#
# 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)