# # add_file "tests/t_automate_graph.at" # # add_file "tests/t_parents_children.at" # # patch "ChangeLog" # from [a5a7e41a12215c696298c98eef41ba0c62c0270e] # to [350d85f7e17626dcd73730329db818a481a4aefe] # # patch "automate.cc" # from [dc6c34d27c31057be28379b197a9ba535716e713] # to [9a12b01deba6551f2bb0cce3d99c44d6d4dd472b] # # patch "commands.cc" # from [6374417a8d763aec13e45054cf672798f542db2a] # to [f6b48a62d2a217ee741c7b74bac60bef7c42c5eb] # # patch "monotone.texi" # from [3c4010df98b95f235503cd19bce964957e2c74b7] # to [f7d83e5be3cc6d8fc54f11c20dbd4ae672ceb113] # # patch "tests/t_automate_ancestors.at" # from [6a71ddfc43a245c500a1274d0954f942f2a5672a] # to [3c5baf5757a08e4fe41d17acf8d2a1956319fc9e] # # patch "tests/t_automate_graph.at" # from [] # to [4f4ea120d3eccd2d6f5edf21cdaf3ce8d2f92f2c] # # patch "tests/t_parents_children.at" # from [] # to [3834a2ec99dff28eb1dfa20edd22c04c964ec9c5] # # patch "testsuite.at" # from [34f3168e47516f1224add46b67a6e7c1455de85b] # to [54835ea5c1a08c1c69ab1427b8b51652e0b76f05] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,16 @@ +2005-04-25 Nathaniel Smith + + * automate.cc (automate_parents, automate_children) + (automate_graph): New automate commands. + (automate_command): Add them. + * commands.cc (automate): Synopsisfy them. + * monotone.texi (Automation): Document them. + * tests/t_automate_graph.at, test/t_parents_children.at: Test + them. + * testsuite.at: Add the tests. + + * tests/t_automate_ancestors.at: Remove obsolete comment. + 2005-04-24 Nathaniel Smith * monotone.texi (Database): Document 'db kill_rev_locally'. --- automate.cc +++ automate.cc @@ -94,17 +94,17 @@ revision_id rid = frontier.back(); frontier.pop_back(); if(!null_id(rid)) { - std::set parents; - app.db.get_revision_parents(rid, parents); - for (std::set::const_iterator i = parents.begin(); - i != parents.end(); ++i) - { - if (ancestors.find(*i) == ancestors.end()) - { - frontier.push_back(*i); - ancestors.insert(*i); - } - } + std::set parents; + app.db.get_revision_parents(rid, parents); + for (std::set::const_iterator i = parents.begin(); + i != parents.end(); ++i) + { + if (ancestors.find(*i) == ancestors.end()) + { + frontier.push_back(*i); + ancestors.insert(*i); + } + } } } for (std::set::const_iterator i = ancestors.begin(); @@ -303,6 +303,120 @@ output << (*i).inner()() << std::endl; } +// Name: parents +// Arguments: +// 1: a revision id +// Added in: 0.2 +// Purpose: Prints the immediate ancestors of the given revision, i.e., the +// parents. +// Output format: A list of revision ids, in hexadecimal, each followed by a +// newline. Revision ids are printed in alphabetically sorted order. +// Error conditions: If the revision does not exist, prints nothing to stdout, +// prints an error message to stderr, and exits with status 1. +static void +automate_parents(std::vector args, + std::string const & help_name, + app_state & app, + std::ostream & output) +{ + if (args.size() != 1) + throw usage(help_name); + revision_id rid(idx(args, 0)()); + N(app.db.revision_exists(rid), F("No such revision %s") % rid); + std::set parents; + app.db.get_revision_parents(rid, parents); + for (std::set::const_iterator i = parents.begin(); + i != parents.end(); ++i) + if (!null_id(*i)) + output << (*i).inner()() << std::endl; +} + +// Name: children +// Arguments: +// 1: a revision id +// Added in: 0.2 +// Purpose: Prints the immediate descendents of the given revision, i.e., the +// children. +// Output format: A list of revision ids, in hexadecimal, each followed by a +// newline. Revision ids are printed in alphabetically sorted order. +// Error conditions: If the revision does not exist, prints nothing to stdout, +// prints an error message to stderr, and exits with status 1. +static void +automate_children(std::vector args, + std::string const & help_name, + app_state & app, + std::ostream & output) +{ + if (args.size() != 1) + throw usage(help_name); + revision_id rid(idx(args, 0)()); + N(app.db.revision_exists(rid), F("No such revision %s") % rid); + std::set children; + app.db.get_revision_children(rid, children); + for (std::set::const_iterator i = children.begin(); + i != children.end(); ++i) + if (!null_id(*i)) + output << (*i).inner()() << std::endl; +} + +// Name: graph +// Arguments: +// None +// Added in: 0.2 +// Purpose: Prints out the complete ancestry graph of this database. +// Output format: +// Each line begins with a revision id. Following this are zero or more +// space-prefixed revision ids. Each revision id after the first is a +// parent (in the sense of 'automate parents') of the first. For instance, +// the following are valid lines: +// 07804171823d963f78d6a0ff1763d694dd74ff40 +// 07804171823d963f78d6a0ff1763d694dd74ff40 79d755c197e54dd3db65751d3803833d4cbf0d01 +// 07804171823d963f78d6a0ff1763d694dd74ff40 79d755c197e54dd3db65751d3803833d4cbf0d01 a02e7a1390e3e4745c31be922f03f56450c13dce +// The first would indicate that 07804171823d963f78d6a0ff1763d694dd74ff40 +// was a root node; the second would indicate that it had one parent, and +// the third would indicate that it had two parents, i.e., was a merge. +// +// The output as a whole is alphabetically sorted; additionally, the parents +// within each line are alphabetically sorted. +// Error conditions: None. +static void +automate_graph(std::vector args, + std::string const & help_name, + app_state & app, + std::ostream & output) +{ + if (args.size() != 0) + throw usage(help_name); + + std::multimap edges_mmap; + std::map > child_to_parents; + + app.db.get_revision_ancestry(edges_mmap); + + for (std::multimap::const_iterator i = edges_mmap.begin(); + i != edges_mmap.end(); ++i) + { + if (child_to_parents.find(i->second) == child_to_parents.end()) + child_to_parents.insert(std::make_pair(i->second, std::set())); + if (null_id(i->first)) + continue; + std::map >::iterator + j = child_to_parents.find(i->second); + I(j->first == i->second); + j->second.insert(i->first); + } + + for (std::map >::const_iterator i = child_to_parents.begin(); + i != child_to_parents.end(); ++i) + { + output << (i->first).inner()(); + for (std::set::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) + output << " " << (*j).inner()(); + output << std::endl; + } +} + void automate_command(utf8 cmd, std::vector args, std::string const & root_cmd_name, @@ -325,6 +439,12 @@ automate_ancestry_difference(args, root_cmd_name, app, output); else if (cmd() == "leaves") automate_leaves(args, root_cmd_name, app, output); + else if (cmd() == "parents") + automate_parents(args, root_cmd_name, app, output); + else if (cmd() == "children") + automate_children(args, root_cmd_name, app, output); + else if (cmd() == "graph") + automate_graph(args, root_cmd_name, app, output); else throw usage(root_cmd_name); } --- commands.cc +++ commands.cc @@ -3918,7 +3918,10 @@ "interface_version\n" "heads [BRANCH]\n" "ancestors REV1 [REV2 [REV3 [...]]]\n" + "parents REV\n" "descendents REV1 [REV2 [REV3 [...]]]\n" + "children REV\n" + "graph\n" "erase_ancestors [REV1 [REV2 [REV3 [...]]]]\n" "toposort [REV1 [REV2 [REV3 [...]]]]\n" "ancestry_difference NEW_REV [OLD_REV1 [OLD_REV2 [...]]]\n" --- monotone.texi +++ monotone.texi @@ -4377,6 +4377,44 @@ @end table address@hidden monotone automate parents @var{rev} + address@hidden @strong address@hidden Arguments: + +One revision id, @var{rev}. + address@hidden Added in: + +0.2 + address@hidden Purpose: + +Prints the immediate parents of a revision. This is like a +non-recursive version of @command{automate ancestors}. + address@hidden Sample output: + address@hidden +28ce076c69eadb9b1ca7bdf9d40ce95fe2f29b61 +75156724e0e2e3245838f356ec373c50fa469f1f address@hidden verbatim + address@hidden Output format: + +Zero or more lines, each giving the id of one parent of the given +revision. Each line consists of a revision id, in hexadecimal, +followed by a newline. The lines are printed in alphabetically sorted +order. + address@hidden Error conditions: + +If the given revision @var{rev} does not exist, prints nothing to +stdout, prints an error message to stderr, and exits with status 1. + address@hidden table + + @item monotone automate descendents @var{rev1} address@hidden [...]] @table @strong @@ -4418,6 +4456,89 @@ @end table address@hidden monotone automate children @var{rev} + address@hidden @strong address@hidden Arguments: + +One revision id, @var{rev}. + address@hidden Added in: + +0.2 + address@hidden Purpose: + +Prints the immediate children of a revision. This is like a +non-recursive version of @command{automate descendents}. + address@hidden Sample output: + address@hidden +28ce076c69eadb9b1ca7bdf9d40ce95fe2f29b61 +75156724e0e2e3245838f356ec373c50fa469f1f address@hidden verbatim + address@hidden Output format: + +Zero or more lines, each giving the id of one child of the given +revision. Each line consists of a revision id, in hexadecimal, +followed by a newline. The lines are printed in alphabetically sorted +order. + address@hidden Error conditions: + +If the given revision @var{rev} does not exist, prints nothing to +stdout, prints an error message to stderr, and exits with status 1. + address@hidden table + + address@hidden monotone automate graph + address@hidden @strong address@hidden Arguments: + +None. + address@hidden Added in: + +0.2 + address@hidden Purpose: + +Prints out the complete ancestry graph of this database. + address@hidden Sample output: + address@hidden +0c05e8ec9c6af4224672c7cc4c9ef05ae8bdb794 +27ebcae50e1814e35274cb89b5031a423c29f95a 5830984dec5c41d994bcadfeab4bf1bf67747b89 +4e284617c80bec7da03925062a84f715c1b042bd 27ebcae50e1814e35274cb89b5031a423c29f95a 657c756d24fb65213d59f4ae07e117d830dcc95b address@hidden verbatim + address@hidden Output format: + +Zero or more lines, each giving ancestry information for one revision. +Each line begins with a revision id. Following this are zero or more +space-prefixed revision ids. Each revision id after the first is a +parent (in the sense of @command{automate parents}) of the first. For +instance, in the above sample output, +0c05e8ec9c6af4224672c7cc4c9ef05ae8bdb794 is a root node, +27ebcae50e1814e35274cb89b5031a423c29f95a has one parent, and +4e284617c80bec7da03925062a84f715c1b042bd has two parents, i.e., is a +merge node. + +The output as a whole is alphabetically sorted by line; additionally, +the parents within each line are alphabetically sorted. + address@hidden Error conditions: + +None. + address@hidden table + + @item monotone automate erase_ancestors address@hidden address@hidden [...]]] @table @strong --- tests/t_automate_ancestors.at +++ tests/t_automate_ancestors.at @@ -49,7 +49,6 @@ # Now do some checks -#next test would still fail as it outputs an empty line. Why? descendents does not output an empty line and this is just copy'n paste??? AT_CHECK(MONOTONE automate ancestors $REV_A, [], [], [ignore]) AT_CHECK(MONOTONE automate ancestors $REV_B, [], [stdout], [ignore]) --- tests/t_automate_graph.at +++ tests/t_automate_graph.at @@ -0,0 +1,65 @@ +AT_SETUP([automate graph]) +MONOTONE_SETUP + +AT_CHECK(MONOTONE automate graph, [], [], [ignore]) + +# A +# / \ +# B C +# |\ +# D E +# \/ +# F + +ADD_FILE(testfile, [A +]) +COMMIT(testbranch) +REV_A=`BASE_REVISION` + +SET_FILE(testfile, [B +]) +COMMIT(testbranch) +REV_B=`BASE_REVISION` + +REVERT_TO($REV_A) + +SET_FILE(testfile, [C +]) +COMMIT(testbranch) +REV_C=`BASE_REVISION` + +SET_FILE(testfile, [D +]) +COMMIT(testbranch) +REV_D=`BASE_REVISION` + +REVERT_TO($REV_C) + +ADD_FILE(otherfile, [E +]) +COMMIT(testbranch) +REV_E=`BASE_REVISION` + +AT_CHECK(MONOTONE explicit_merge $REV_D $REV_E testbranch, [], [ignore], [ignore]) +AT_CHECK(MONOTONE update, [], [ignore], [ignore]) +REV_F=`BASE_REVISION` + +AT_CHECK(test $REV_F != $REV_D) +AT_CHECK(test $REV_F != $REV_E) + +# Now do some checks + +AT_CHECK(MONOTONE automate graph, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) + +AT_CHECK(echo $REV_A >>graph) +AT_CHECK(echo $REV_B $REV_A >>graph) +AT_CHECK(echo $REV_C $REV_A >>graph) +AT_CHECK(echo $REV_D $REV_C >>graph) +AT_CHECK(echo $REV_E $REV_C >>graph) +AT_CHECK(echo $REV_F `(echo $REV_D; echo $REV_E) | sort` >>graph) +AT_CHECK(sort graph > graph.sorted) + +AT_CHECK(cmp stdout graph.sorted) + +AT_CLEANUP --- tests/t_parents_children.at +++ tests/t_parents_children.at @@ -0,0 +1,105 @@ +AT_SETUP([automate parents, automate children]) +MONOTONE_SETUP + +AT_CHECK(MONOTONE automate parents c7539264e83c5d6af4c792f079b5d46e9c128665, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE automate children c7539264e83c5d6af4c792f079b5d46e9c128665, [1], [ignore], [ignore]) + +# A +# / \ +# B C +# |\ +# D E +# \/ +# F + +ADD_FILE(testfile, [A +]) +COMMIT(testbranch) +REV_A=`BASE_REVISION` + +SET_FILE(testfile, [B +]) +COMMIT(testbranch) +REV_B=`BASE_REVISION` + +REVERT_TO($REV_A) + +SET_FILE(testfile, [C +]) +COMMIT(testbranch) +REV_C=`BASE_REVISION` + +SET_FILE(testfile, [D +]) +COMMIT(testbranch) +REV_D=`BASE_REVISION` + +REVERT_TO($REV_C) + +ADD_FILE(otherfile, [E +]) +COMMIT(testbranch) +REV_E=`BASE_REVISION` + +AT_CHECK(MONOTONE explicit_merge $REV_D $REV_E testbranch, [], [ignore], [ignore]) +AT_CHECK(MONOTONE update, [], [ignore], [ignore]) +REV_F=`BASE_REVISION` + +AT_CHECK(test $REV_F != $REV_D) +AT_CHECK(test $REV_F != $REV_E) + +# Now do some checks + +AT_CHECK(MONOTONE automate parents $REV_A, [], [], [ignore]) +AT_CHECK(MONOTONE automate children $REV_B, [], [], [ignore]) +AT_CHECK(MONOTONE automate children $REV_F, [], [], [ignore]) + +AT_CHECK(echo $REV_A > tmp) +AT_CHECK(MONOTONE automate parents $REV_B, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp tmp stdout) + +AT_CHECK(echo $REV_A > tmp) +AT_CHECK(MONOTONE automate parents $REV_C, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp tmp stdout) + +AT_CHECK(echo $REV_C > tmp) +AT_CHECK(MONOTONE automate parents $REV_D, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp tmp stdout) + +AT_CHECK(echo $REV_C > tmp) +AT_CHECK(MONOTONE automate parents $REV_E, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp tmp stdout) + +AT_CHECK(echo $REV_D >revs_de.unsorted) +AT_CHECK(echo $REV_E >>revs_de.unsorted) +AT_CHECK(sort revs_de.unsorted > revs_de) +AT_CHECK(MONOTONE automate parents $REV_F, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp revs_de stdout) + +AT_CHECK(echo $REV_F > tmp) +AT_CHECK(MONOTONE automate children $REV_D, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp tmp stdout) + +AT_CHECK(echo $REV_F > tmp) +AT_CHECK(MONOTONE automate children $REV_E, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp tmp stdout) + +AT_CHECK(MONOTONE automate children $REV_C, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp revs_de stdout) + +AT_CHECK(echo $REV_B >revs_bc.unsorted) +AT_CHECK(echo $REV_C >>revs_bc.unsorted) +AT_CHECK(sort revs_bc.unsorted > revs_bc) +AT_CHECK(MONOTONE automate children $REV_A, [], [stdout], [ignore]) +AT_CHECK(CANONICALISE(stdout)) +AT_CHECK(cmp revs_bc stdout) + +AT_CLEANUP --- testsuite.at +++ testsuite.at @@ -578,3 +578,5 @@ m4_include(tests/t_multiple_heads_msg.at) m4_include(tests/t_diff_currev.at) m4_include(tests/t_normalized_filenames.at) +m4_include(tests/t_parents_children.at) +m4_include(tests/t_automate_graph.at)