monotone-commits-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Monotone-commits-diffs] net.venge.monotone: 3ea3e8ee8014b9cf0cbe92c98dd


From: code
Subject: [Monotone-commits-diffs] net.venge.monotone: 3ea3e8ee8014b9cf0cbe92c98dd3e4b7bc76378a
Date: Wed, 9 May 2012 12:11:39 +0200 (CEST)

revision:            3ea3e8ee8014b9cf0cbe92c98dd3e4b7bc76378a
date:                2012-05-09T10:11:58
author:              address@hidden
branch:              net.venge.monotone
changelog:
propagate from branch 'net.venge.monotone.nfs_mount_in_workspace' (head 
e326e60813b2c5144716140b432cc601404e2ea9)
            to branch 'net.venge.monotone' (head 
2c06e0c3e391fb5cf255eccb581d586ca6618658)



manifest:
format_version "1"

new_manifest [7756fb94bb19114da4b600f3e6f315e9f170b892]

old_revision [2c06e0c3e391fb5cf255eccb581d586ca6618658]

patch "src/unix/fs.cc"
 from [d34f7ed4b5cf3655d7920568e4e1a146fa670e13]
   to [cd0a6fb223a4351035c3d2c32a5d4dafc609ba91]

old_revision [e326e60813b2c5144716140b432cc601404e2ea9]

add_dir "test/func/automate_erase_descendants"

add_file "test/func/automate_erase_descendants/__driver__.lua"
 content [45830e0a1e9f33e9031ca9953689b3648a9eb04e]

patch ".mtn-ignore"
 from [bd2b1b11006b28d0b1fb079e6b9c77521c704508]
   to [be27b64d4938d68bb89fab2933c1c35d0cebf885]

patch "NEWS"
 from [5514332f7c16430d0c46d3fbac01b0c8a96bc412]
   to [17c819736b0893e4d4667526656138ed23cb4b48]

patch "doc/monotone.texi"
 from [e0b88a30f08867f9e0f593e5004789ca17753a94]
   to [7b7f4e950e7fc284363246761f612d85d65b9744]

patch "src/ancestry.cc"
 from [8b3388b690a5f4878bd29d752c3e6e073411739e]
   to [e673b17a5d2fad2716f7f4efe33c2ca11c7c31f9]

patch "src/automate.cc"
 from [efa4ecceab7f1e31f71778f325da312bf6aefba5]
   to [919e3df52514d0877aeafc67791aae6a3faa33b4]

patch "src/revision.hh"
 from [740c4dd4ee350fcf06af3ba707cef3dadecb46f8]
   to [8d93883e8a6de779aa199d9b2e1aa58589f0626c]

patch "src/selectors.cc"
 from [588c6b5fd3ded29f5f778a78e707352564acbc02]
   to [19a82c64f44ab5433354299f84598174cb15f510]

patch "test/func/extended-selectors/__driver__.lua"
 from [cab379ef6f3439ab7f5a4424202ebf9097171c4c]
   to [e80ed8e831bbc9cc346e7722c9877d32887104ef]

patch "test/unit/tests/xdelta.cc"
 from [4aff975cfd9a9cf18c26f70118e2895880ed6100]
   to [ed5f467ebc4755f7521e74a40833181974b43a8f]

patch "util/audit-includes"
 from [d5757fcf83ab116fba16c53221da0f832307a113]
   to [1c6fc8be30ebfafc67ca03980e7860db35e5d2e9]
============================================================
--- src/unix/fs.cc	d34f7ed4b5cf3655d7920568e4e1a146fa670e13
+++ src/unix/fs.cc	cd0a6fb223a4351035c3d2c32a5d4dafc609ba91
@@ -1,3 +1,4 @@
+// Copyright (C) 2012 Stephe Leake <address@hidden>
 // Copyright (C) 2005 nathaniel smith <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
@@ -28,6 +29,7 @@
 #include "../platform.hh"
 #include "../vector.hh"
 
+using std::malloc;
 using std::string;
 using std::vector;
 
@@ -288,11 +290,74 @@ rename_clobberingly(string const & from,
 void
 rename_clobberingly(string const & from, string const & to)
 {
+  // rename doesn't work across devices, which can happen if part of the
+  // workspace is NFS mounted.
+  //
+  // We only check for that if rename fails, to avoid slowing down normal
+  // workspaces.
+
   if (rename(from.c_str(), to.c_str()))
     {
-      const int err = errno;
-      E(false, origin::system,
-        F("renaming '%s' to '%s' failed: %s") % from % to % os_strerror(err));
+      // rename failed
+      int err = errno;
+
+      int from_fd = open(from.c_str(), O_RDONLY);
+      int to_fd = open(to.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+      struct stat from_stat;
+      struct stat to_stat;
+      fstat(from_fd, &from_stat);
+      fstat(to_fd, &to_stat);
+
+      if (from_stat.st_dev != to_stat.st_dev)
+        {
+          // different devices; use cp, rm
+          //
+          // except there isn't a C function that does 'cp', so we read in
+          // the file and write it out again.
+
+          char * buffer    = (char * )malloc(from_stat.st_size);
+          char * ptr       = buffer;
+          size_t remaining = from_stat.st_size;
+
+          do
+            {
+              ssize_t read_count = read(from_fd, ptr, remaining);
+
+              err = errno;
+
+              E(read_count >= 0, origin::system,
+                F ("error reading file '%s': %s") % from % os_strerror(err));
+
+              remaining -= read_count;
+              ptr       += read_count;
+            }
+          while (remaining > 0);
+          close(from_fd);
+
+          ptr       = buffer;
+          remaining = from_stat.st_size;
+          do
+            {
+              ssize_t write_count = write(to_fd, ptr, remaining);
+              err = errno;
+              E(write_count >= 0, origin::system,
+                F("error writing file '%s': %s") % to % os_strerror(err));
+
+              remaining -= write_count;
+              ptr       += write_count;
+            }
+          while (remaining > 0);
+          close(to_fd);
+
+          free(buffer);
+
+          remove(from.c_str());
+        }
+      else
+        {
+          E(false, origin::system,
+            F("renaming '%s' to '%s' failed: %s") % from % to % os_strerror(err));
+        }
     }
 }
 
============================================================
--- .mtn-ignore	bd2b1b11006b28d0b1fb079e6b9c77521c704508
+++ .mtn-ignore	be27b64d4938d68bb89fab2933c1c35d0cebf885
@@ -2,6 +2,7 @@
 ^ABOUT-NLS$
 ^Makefile(\.in)?$
 ^aclocal\.m4$
+^autom4te\.cache$
 ^build-arch-stamp$
 ^build-indep-stamp$
 ^compile$
============================================================
--- NEWS	5514332f7c16430d0c46d3fbac01b0c8a96bc412
+++ NEWS	17c819736b0893e4d4667526656138ed23cb4b48
@@ -10,6 +10,14 @@ XXX XXX XX XX:XX:XX UTC 201X
           and returns the attributes for a specific file from the
           revision's manifest
 
+        - New 'erase_descendants' automate command which returns all
+          input revisions, except those that are a descendant of
+          another revision in the input.
+
+        - New 'min(A)' selector is now available which returns all
+          revisions selected by A which are not descendants of other
+          revisions selected by A.
+
         - New 'not(A)' selector is now available which returns all
           revisions not selected by 'A'.
 
============================================================
--- doc/monotone.texi	e0b88a30f08867f9e0f593e5004789ca17753a94
+++ doc/monotone.texi	7b7f4e950e7fc284363246761f612d85d65b9744
@@ -39,7 +39,7 @@
 Copyright @copyright{} 2006 Alex Queiroz @*
 Copyright @copyright{} 2006, 2007 William Uther @*
 Copyright @copyright{} 2006 - 2010 Thomas Keller @*
-Copyright @copyright{} 2007 - 2011 Stephen Leake @*
+Copyright @copyright{} 2007 - 2012 Stephen Leake @*
 
 This manual is made available under the GNU GPL version 2.0 or
 greater.  See the accompanying file COPYING for details.
@@ -2957,6 +2957,11 @@ @heading Composite selectors
 @code{max(b:net.venge.monotone/a:graydon)} would return the latest revision(s)
 on branch @code{net.venge.monotone} which have an @code{author} cert beginning
 with @code{graydon}.
address@hidden min(A)
+Erase descendants; this returns all revisions selected by @code{A} which are not
+descendants of other revisions selected by @code{A}. For example,
address@hidden(b:net.venge.monotone)} would return the earliest revision(s)
+on branch @code{net.venge.monotone}.
 @item ancestors(A)
 Strict ancestors; returns all revisions which are an ancestor of a revision
 selected by @code{A}. For example, @code{ancestors(b:net.venge.monotone)}
@@ -4598,7 +4603,8 @@ @chapter Command Reference
 The Lua hook @address@hidden can change the
 default value for any option.
 
-Revision arguments to commands may be selectors or hex ids.
+Revision arguments to commands (but not to automate commands) may be
+selectors (see @ref{Selectors}) or hex ids.
 
 @menu
 * Global and Common Options::   Options that affect all or many commands
@@ -4853,7 +4859,7 @@ @section Tree
 @anchor{mtn address@hidden mtn checkout [--[no-]move-conflicting-paths] address@hidden @var{directory}
 @itemx mtn checkout [--[no-]move-conflicting-paths] address@hidden @var{directory}
 @itemx mtn co
address@hidden is an alias for @command{checkout}
address@hidden is an alias for @command{checkout}. See @ref{Selectors}.
 
 These commands copy a revision @var{id} out of your database,
 recording the chosen revision (the @dfn{base revision}) in the file
@@ -4902,9 +4908,9 @@ @section Tree
 See @ref{Conflicts}
 
 @anchor{mtn address@hidden mtn explicit_merge [--[no-]update] @var{id} @var{id} @var{destbranch}
-See the online help for options. See @ref{--update}.
+See the online help for options. See @ref{--update}. See @ref{Selectors}.
 
-This command merges exactly the two @var{id}s you give it, and places
+This command merges exactly the two revision @var{id}s you give it, and places
 the result in branch @var{destbranch}.  It is useful when you need more
 control over the merging process than @command{propagate} or @command{merge}
 give you.  For instance, if you have a branch with three heads, and you
@@ -4927,7 +4933,7 @@ @section Tree
 
 @anchor{mtn address@hidden mtn import address@hidden address@hidden [--[no-]dry-run] @var{dir}
 @itemx mtn import address@hidden address@hidden [--[no-]dry-run] @var{dir}
-See the online help for more options.
+See the online help for more options. See @ref{Selectors}.
 
 This command imports the contents of the given directory and commits it
 to the head of the given branch or as a child of the given revision (and
@@ -5002,10 +5008,10 @@ @section Tree
 @ref{Merge Conflicts} can occur. See @ref{--update}.
 
 @anchor{mtn address@hidden mtn merge_into_workspace [--[no]-move-conflicting-paths] @var{revision}
-Merges @var{revision} into the current workspace; the result is not
-committed to the database. There can be no pending changes in the
-current workspace. The workspace's selected branch is not
-changed.
+Merges @var{revision} (see @ref{Selectors}) into the current
+workspace; the result is not committed to the database. There can be
+no pending changes in the current workspace. The workspace's selected
+branch is not changed.
 
 When a later commit is done, both @var{revision} and the workspace's
 base revision will be recorded as parents.
@@ -5100,10 +5106,10 @@ @subheading Commands
 @ftable @command
 @item mtn conflicts store address@hidden address@hidden @var{right_rev_id}]
 Store the conflicts encountered by merging @var{left_rev_id} with
address@hidden, in the specified file (defaults to
address@hidden/conflicts}. If @var{left_rev_id} and @var{right_rev_id} are
-not given, the first two heads that the @command{merge} command would
-merge are used.
address@hidden (revision ids; see @ref{Selectors}), in the
+specified file (defaults to @file{_MTN/conflicts}. If
address@hidden and @var{right_rev_id} are not given, the first two
+heads that the @command{merge} command would merge are used.
 
 The conflicts file format is as output by the @command{automate
 show_conflicts} command; see @address@hidden automate show_conflicts}}.
@@ -5479,7 +5485,7 @@ @section Workspace
 
 @anchor{mtn address@hidden mtn pluck [--[no-]move-conflicting-paths] address@hidden
 @itemx mtn pluck [--[no-]move-conflicting-paths] address@hidden address@hidden
-See the online help for more options.
+See the online help for more options. See @ref{Selectors}.
 
 This command takes changes made at any point in history, and attempts to
 edit your current workspace to include those changes.  The end result is
@@ -5555,7 +5561,7 @@ @section Workspace
 @anchor{mtn address@hidden mtn update [--[no-]move-conflicting-paths] [--branch @var{branchname}]
 @itemx mtn update [--[no-]move-conflicting-paths] address@hidden
 This command changes your workspace to have the a different revision as
-the base revision.
+the base revision. See @ref{Selectors}.
 
 @command{update} performs 3 separate stages. If any of these stages
 fails, the update aborts, doing nothing. The first two stages select
@@ -5769,9 +5775,9 @@ @section Network
 Finally, @command{clone} copies the files out of the newly created
 database into a local directory, just as @command{checkout} would.  If
 no @var{directory} is given, the @var{branchname} is used as
-directory. If @option{--revision} is given, that revision must be on
-the specified branch, and is checked out; otherwise the head of the
-branch is checked out.
+directory. If @option{--revision} is given (see @ref{Selectors}), that
+revision must be on the specified branch, and is checked out;
+otherwise the head of the branch is checked out.
 
 @strong{Important notice:} The @var{address}[:@var{port}] @var{branchname}
 call syntax is deprecated and subject to removal in future versions of monotone!
@@ -5784,6 +5790,7 @@ @section Informative
 @ftable @command
 @item mtn annotate @var{file}
 @itemx mtn annotate address@hidden [--revs-only] @var{file}
+See @ref{Selectors}.
 
 Dumps an annotated copy of the file to stdout. The output is in the
 form:
@@ -5807,10 +5814,10 @@ @section Informative
 
 @item mtn bisect bad address@hidden ...] [--[no-]move-conflicting-paths]
 Mark the specified revisions as ``bad'' for the current bisection
-operation (see @ref{Bisecting}). If no bisection operation is in
-progress a new bisection is initialized. If a bisection operation is
-in progress the next update target is selected and the workspace is
-updated to the selected revision.
+operation (see @ref{Bisecting}, see @ref{Selectors}). If no bisection
+operation is in progress a new bisection is initialized. If a
+bisection operation is in progress the next update target is selected
+and the workspace is updated to the selected revision.
 
 If the update is blocked by conflicting unversioned paths existing in
 the workspace this command may be re-issued with the
@@ -5820,10 +5827,10 @@ @section Informative
 
 @item mtn bisect good address@hidden ...] [--[no-]move-conflicting-paths]
 Mark the specified revisions as ``good'' for the current bisection
-operation (see @ref{Bisecting}). If no bisection operation is in
-progress a new bisection is initialized. If a bisection operation is
-in progress the next update target is selected and the workspace is
-updated to the selected revision.
+operation (see @ref{Bisecting}, see @ref{Selectors}). If no bisection
+operation is in progress a new bisection is initialized. If a
+bisection operation is in progress the next update target is selected
+and the workspace is updated to the selected revision.
 
 If the update is blocked by conflicting unversioned paths existing in
 the workspace this command may be re-issued with the
@@ -5838,10 +5845,10 @@ @section Informative
 
 @item mtn bisect skip address@hidden ...] [--[no-]move-conflicting-paths]
 Mark the specified revisions as ``skipped'' for the current bisection
-operation (see @ref{Bisecting}). If no bisection operation is in
-progress a new bisection is initialized. If a bisection operation is
-in progress the next update target is selected and the workspace is
-updated to the selected revision.
+operation (see @ref{Bisecting}, see @ref{Selectors}). If no bisection
+operation is in progress a new bisection is initialized. If a
+bisection operation is in progress the next update target is selected
+and the workspace is updated to the selected revision.
 
 If the update is blocked by conflicting unversioned paths existing in
 the workspace this command may be re-issued with the
@@ -5863,16 +5870,18 @@ @section Informative
 
 @item mtn cat address@hidden @var{path}
 Write the contents of a specific file @var{path} in revision @var{id}
-(default to workspace base revision) to standard output.
+(see @ref{Selectors}; default to workspace base revision) to standard
+output.
 
 @item mtn complete file @var{partial-id}
 @itemx mtn complete key @var{partial-id}
 @itemx mtn complete revision @var{partial-id}
 
 These commands print out all known completions of a partial @sc{sha1}
-value, listing completions which are @code{file}, @code{manifest} or
address@hidden IDs depending on which variant is used. For
-example, suppose you enter this command and get this result:
+value (@emph{not} a selector), listing completions which are
address@hidden, @code{manifest} or @code{revision} IDs depending on which
+variant is used. For example, suppose you enter this command and get
+this result:
 
 @smallexample
 @group
@@ -5897,7 +5906,8 @@ @section Informative
 @itemx mtn diff address@hidden address@hidden
 @itemx mtn diff address@hidden address@hidden @var{pathname...}
 @itemx mtn di
address@hidden is an alias for @command{diff}. See online help for more options.
address@hidden is an alias for @command{diff}. See online help for more
+options. See @ref{Selectors}.
 
 These commands print out textual difference listings between various
 manifest versions. With no @option{--revision} options, @command{diff}
@@ -6067,7 +6077,8 @@ @section Informative
 
 @item mtn list duplicates address@hidden
 @itemx mtn ls duplicates
address@hidden duplicates} is an alias for @command{list duplicates}.
address@hidden duplicates} is an alias for @command{list duplicates}. See
address@hidden
 
 This command lists duplicate files in a given revision (defaults to
 the workspace base revision). Ignored and unknown files are excluded
@@ -6180,7 +6191,7 @@ @section Informative
 
 @anchor{mtn address@hidden mtn log
 @itemx mtn log address@hidden address@hidden address@hidden [...]] [--clear-from] address@hidden [...]] [--clear-to] address@hidden [...]] [--[no-]brief] [--[no-]merges] [--[no-]files] [--[no-]graph] [--[no-]diffs] address@hidden
-See the online help for more options.
+See the online help for more options. See @ref{Selectors}.
 
 This command prints out a log, in forward ancestry order by default
 but optionally in reverse ancestry order, of small history summaries.
@@ -6277,7 +6288,7 @@ @section Informative
 
 @item mtn show_conflicts @var{rev} @var{rev}
 This command shows what conflicts would need to be resolved in order to merge
-the given revisions; see @ref{Merge Conflicts}.
+the given revisions; see @ref{Merge Conflicts}, see @ref{Selectors}.
 
 Note that this does not show conflicts due to update commands, since
 in that case one revision is the workspace.
@@ -6336,8 +6347,8 @@ @section Review
 
 @anchor{mtn address@hidden mtn comment @var{rev} address@hidden
 
-This adds a new comment to a committed revision.  If @var{comment} is
-not provided, it is obtained from the Lua hook
+This adds a new comment to a committed revision (see @ref{Selectors}).
+If @var{comment} is not provided, it is obtained from the Lua hook
 @address@hidden; the hook is passed an empty string.
 
 This command is a synonym for @command{mtn cert @var{rev} comment
@@ -6346,10 +6357,12 @@ @section Review
 @item mtn disapprove [--[no-]update] address@hidden @var{child}
 See online help for more options; see @ref{Common Options}. See @ref{--update}.
 
-This command records a disapproval of the changes between @var{parent}'s
-ancestor and @var{child}.  If @var{parent} is omitted, only @var{child}
-is disapproved.  The command does the disapproval by committing
-the @i{inverse} changes as a new revision descending from @var{child}.
+This command records a disapproval of the changes between
address@hidden's ancestor and @var{child}. @var{parent} and @var{child}
+are revision ids (see @ref{Selectors}). If @var{parent} is omitted,
+only @var{child} is disapproved.  The command does the disapproval by
+committing the @i{inverse} changes as a new revision descending from
address@hidden
 
 Conceptually, @command{disapprove}'s contract is that disapprove(A) gives a
 revision B such that whenever B is merged with a descendant D of A the merge
@@ -6364,10 +6377,11 @@ @section Review
 @anchor{mtn address@hidden mtn suspend [--[no-]update] [--branch @var{branchname}] @var{rev}
 See @ref{--update}.
 
-This makes @var{rev} invisible as a head of branch @var{branchname}
-(defaults to the current workspace branch).  Any operation that looks
-for heads will not count @var{rev}; this includes @address@hidden
-list branches}} as well as @address@hidden merge}} etc.
+This makes @var{rev} (a revision id; see @ref{Selectors}) invisible as
+a head of branch @var{branchname} (defaults to the current workspace
+branch).  Any operation that looks for heads will not count @var{rev};
+this includes @address@hidden list branches}} as well as
address@hidden@ref{mtn merge}} etc.
 
 If @var{rev} is not a head, @command{suspend} has no effect.
 
@@ -6380,8 +6394,9 @@ @section Review
 
 @item mtn tag @var{rev} @var{tagname}
 This command associates the symbolic name @var{tagname} with the
-revision @var{rev}, so that symbolic name can later be used in
address@hidden for specifying revisions.
+revision @var{rev} (a revision id; see @ref{Selectors}), so that
+symbolic name can later be used in selectors for specifying
+revisions.
 
 This command is a synonym for @command{mtn cert @var{rev} tag
 @var{tagname}}.
@@ -6434,7 +6449,7 @@ @section Key and Cert
 @anchor{mtn address@hidden mtn cert @var{selector} @var{certname} address@hidden
 
 Create a new certificate with name @var{certname}, for all
-revisions matching @var{selector}.
+revisions matching @var{selector} (see @ref{Selectors}).
 
 If @var{certval} is provided, it is the value of the certificate.
 Otherwise the certificate value is read from @code{stdin}.
@@ -6555,11 +6570,11 @@ @section Key and Cert
 
 @item mtn trusted @var{id} @var{certname} @var{certval} @var{signers}
 This command lets you test your revision trust hook
address@hidden  You pass it a revision ID, a
-certificate name, a certificate value, and one or more key IDs or key
-names, and it will tell you whether, under your current settings,
-Monotone would trust a cert on that revision with that value signed by
-those keys.
address@hidden  You pass it a revision ID (see
address@hidden), a certificate name, a certificate value, and one or
+more key IDs or key names, and it will tell you whether, under your
+current settings, Monotone would trust a cert on that revision with
+that value signed by those keys.
 
 The specified keys must exist either in your keystore or in the database.
 
@@ -6821,15 +6836,15 @@ @section Database
 
 @item mtn local kill_certs @var{selector} @var{certname} address@hidden
 This command deletes certs with the given name on revisions that match
-the given selector. If a value is given, it restricts itself to only
-delete certs that also have that same value. Like @address@hidden
-local kill_revision}}, it is a very dangerous command; it permanently
-and irrevocably deletes historical information from your
-database. Also like @command{kill_revision}, this only deletes the
-certs from your local database; if there are other databases that you
-sync with which have these certs they will reappear when you sync,
-unless the owners of those databases also delete those certificates
-locally.
+the given selector (see @ref{Selectors}). If a value is given, it
+restricts itself to only delete certs that also have that same
+value. Like @address@hidden local kill_revision}}, it is a very
+dangerous command; it permanently and irrevocably deletes historical
+information from your database. Also like @command{kill_revision},
+this only deletes the certs from your local database; if there are
+other databases that you sync with which have these certs they will
+reappear when you sync, unless the owners of those databases also
+delete those certificates locally.
 
 Early versions of monotone had @command{db kill_tag_locally} and
 @command{db kill_branch_certs_locally} commands. These can be emulated with
@@ -6837,14 +6852,15 @@ @section Database
 @command{local kill_certs i: branch BRANCH}, respectively.
 
 @anchor{mtn local address@hidden mtn local kill_revision @var{id}
-This command ``kills'', i.e., deletes, a given revision, as well as
-any certs attached to it.  It is a very dangerous command; it
-permanently and irrevocably deletes historical information from your
-database.  If you execute this command in a workspace, whose parent
-revision is the one you are about to delete, the killed revision is
-re-applied to this workspace which makes it possible for you to fix a
-problem and commit again later on easily. For this to work, the
-workspace may not have any changes and/or missing files.
+This command ``kills'', i.e., deletes, a given revision (see
address@hidden), as well as any certs attached to it.  It is a very
+dangerous command; it permanently and irrevocably deletes historical
+information from your database.  If you execute this command in a
+workspace, whose parent revision is the one you are about to delete,
+the killed revision is re-applied to this workspace which makes it
+possible for you to fix a problem and commit again later on
+easily. For this to work, the workspace may not have any changes
+and/or missing files.
 
 There are a number of other caveats with this command:
 @itemize
@@ -7507,7 +7523,48 @@ @section Automation
 
 @end table
 
address@hidden mtn automate erase_descendants address@hidden
 
address@hidden @strong
address@hidden Arguments:
+
+One or more revision IDs.
+
address@hidden Added in:
+
+13.1
+
address@hidden Purpose:
+
+Prints all arguments, except those that are a descendant of some other
+argument.
+
+One way to think about this is that it prints the minimal elements of
+the given set, under the ordering imposed by the ``parent of''
+relation.  Another way to think of it is if the arguments formed a
+branch, then we would print the roots of that branch.  If there are no
+arguments, prints nothing.
+
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 of the given revisions.
+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 any of the revisions do not exist, prints nothing to stdout, prints
+an error message to stderr, and exits with status 1.
+
address@hidden table
+
 @item mtn automate file_merge @var{left-rid} @var{left-path} @var{right-rid} @var{right-path}
 
 @table @strong
============================================================
--- src/ancestry.cc	8b3388b690a5f4878bd29d752c3e6e073411739e
+++ src/ancestry.cc	e673b17a5d2fad2716f7f4efe33c2ca11c7c31f9
@@ -374,6 +374,121 @@ erase_ancestors(database & db, set<revis
   erase_ancestors_and_failures(db, revisions, p);
 }
 
+static void
+accumulate_strict_descendants(database & db,
+                              revision_id const & start,
+                              set<revision_id> & all_descendants,
+                              multimap<revision_id, revision_id> const & graph,
+                              rev_height const & max_height)
+{
+  typedef multimap<revision_id, revision_id>::const_iterator gi;
+
+  vector<revision_id> frontier;
+  frontier.push_back(start);
+
+  while (!frontier.empty())
+    {
+      revision_id rid = frontier.back();
+      frontier.pop_back();
+      pair<gi, gi> parents = graph.equal_range(rid);
+      for (gi i = parents.first; i != parents.second; ++i)
+        {
+          revision_id const & parent = i->second;
+          if (all_descendants.find(parent) == all_descendants.end())
+            {
+              // prune if we're above max_height
+              rev_height h;
+              db.get_rev_height(parent, h);
+              if (h <= max_height)
+                {
+                  all_descendants.insert(parent);
+                  frontier.push_back(parent);
+                }
+            }
+        }
+    }
+}
+
+// this call is equivalent to running:
+//   erase(remove_if(candidates.begin(), candidates.end(), p));
+//   erase_descendants(candidates, db);
+// however, by interleaving the two operations, it can in common cases make
+// many fewer calls to the predicate, which can be a significant speed win.
+
+void
+erase_descendants_and_failures(database & db,
+                               std::set<revision_id> & candidates,
+                               is_failure & p,
+                               multimap<revision_id, revision_id> *graph_cache_ptr)
+{
+  // Load up the ancestry graph
+  multimap<revision_id, revision_id> graph;
+
+  if (candidates.empty())
+    return;
+
+  if (graph_cache_ptr == NULL)
+    graph_cache_ptr = &graph;
+  if (graph_cache_ptr->empty())
+  {
+    db.get_forward_ancestry(*graph_cache_ptr);
+  }
+
+  // Keep a set of all descendants that we've traversed -- to avoid
+  // combinatorial explosion.
+  set<revision_id> all_descendants;
+
+  rev_height max_height;
+  db.get_rev_height(*candidates.begin(), max_height);
+  for (std::set<revision_id>::const_iterator it = candidates.begin(); it != candidates.end(); it++)
+    {
+      rev_height h;
+      db.get_rev_height(*it, h);
+      if (h > max_height)
+        max_height = h;
+    }
+
+  vector<revision_id> todo(candidates.begin(), candidates.end());
+  std::random_shuffle(todo.begin(), todo.end());
+
+  size_t predicates = 0;
+  while (!todo.empty())
+    {
+      revision_id rid = todo.back();
+      todo.pop_back();
+      // check if this one has already been eliminated
+      if (all_descendants.find(rid) != all_descendants.end())
+        continue;
+      // and then whether it actually should stay in the running:
+      ++predicates;
+      if (p(rid))
+        {
+          candidates.erase(rid);
+          continue;
+        }
+      // okay, it is good enough that all its descendants should be
+      // eliminated
+      accumulate_strict_descendants(db, rid, all_descendants, *graph_cache_ptr, max_height);
+    }
+
+  // now go and eliminate the ancestors
+  for (set<revision_id>::const_iterator i = all_descendants.begin();
+       i != all_descendants.end(); ++i)
+    candidates.erase(*i);
+
+  L(FL("called predicate %s times") % predicates);
+}
+
+// This function looks at a set of revisions, and for every pair A, B in that
+// set such that A is an descendant of B, it erases A.
+
+void
+erase_descendants(database & db, set<revision_id> & revisions)
+{
+  no_failures p;
+  erase_descendants_and_failures(db, revisions, p);
+}
+
 // This function takes a revision A and a set of revision Bs, calculates the
 // ancestry of each, and returns the set of revisions that are in A's ancestry
 // but not in the ancestry of any of the Bs.  It tells you 'what's new' in A
============================================================
--- src/automate.cc	efa4ecceab7f1e31f71778f325da312bf6aefba5
+++ src/automate.cc	919e3df52514d0877aeafc67791aae6a3faa33b4
@@ -240,6 +240,39 @@ CMD_AUTOMATE(erase_ancestors, N_("[REV1 
     output << *i << '\n';
 }
 
+// Name: erase_descendants
+// Arguments:
+//   0 or more: revision ids
+// Added in: 13.1
+// Purpose: Prints all arguments, except those that are a descendant of some
+//   other argument.  One way to think about this is that it prints the
+//   minimal elements of the given set, under the ordering imposed by the
+//   "parent of" relation.  Another way to think of it is if the arguments were
+//   a branch, then we print the roots of that branch.
+// 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 any of the revisions do not exist, prints nothing to
+//   stdout, prints an error message to stderr, and exits with status 1.
+CMD_AUTOMATE(erase_descendants, N_("[REV1 [REV2 [REV3 [...]]]]"),
+             N_("Erases the descendants in a list of revisions"),
+             "",
+             options::opts::none)
+{
+  database db(app);
+
+  set<revision_id> revs;
+  for (args_vector::const_iterator i = args.begin(); i != args.end(); ++i)
+    {
+      revision_id rid(decode_hexenc_as<revision_id>((*i)(), origin::user));
+      E(db.revision_exists(rid), origin::user,
+        F("no revision %s found in database") % rid);
+      revs.insert(rid);
+    }
+  erase_descendants(db, revs);
+  for (set<revision_id>::const_iterator i = revs.begin(); i != revs.end(); ++i)
+    output << *i << '\n';
+}
+
 // Name: toposort
 // Arguments:
 //   0 or more: revision ids
============================================================
--- src/revision.hh	740c4dd4ee350fcf06af3ba707cef3dadecb46f8
+++ src/revision.hh	8d93883e8a6de779aa199d9b2e1aa58589f0626c
@@ -162,6 +162,15 @@ void
                              std::multimap<revision_id, revision_id> *inverse_graph_cache_ptr = NULL);
 
 void
+erase_descendants(database & db, std::set<revision_id> & revisions);
+
+void
+erase_descendants_and_failures(database & db,
+                               std::set<revision_id> & revisions,
+                               is_failure & p,
+                               std::multimap<revision_id, revision_id> *inverse_graph_cache_ptr = NULL);
+
+void
 ancestry_difference(database & db, revision_id const & a,
                     std::set<revision_id> const & bs,
                     std::set<revision_id> & new_stuff);
============================================================
--- src/selectors.cc	588c6b5fd3ded29f5f778a78e707352564acbc02
+++ src/selectors.cc	19a82c64f44ab5433354299f84598174cb15f510
@@ -559,6 +559,13 @@ public:
         erase_ancestors(project.db, ret);
         return ret;
       }
+    else if (name == "min")
+      {
+        diagnose_wrong_arg_count("min", 1, args.size());
+        set<revision_id> ret = args[0]->complete(project);
+        erase_descendants(project.db, ret);
+        return ret;
+      }
     else if (name == "ancestors")
       {
         diagnose_wrong_arg_count("ancestors", 1, args.size());
============================================================
--- /dev/null	
+++ test/func/automate_erase_descendants/__driver__.lua	45830e0a1e9f33e9031ca9953689b3648a9eb04e
@@ -0,0 +1,46 @@
+
+mtn_setup()
+
+-- Make sure we fail when given a non-existent revision
+check(mtn("automate", "erase_descendants", "c7539264e83c5d6af4c792f079b5d46e9c128665"), 1, false, false)
+
+--   A
+--  / \
+-- B   C
+--     |\
+--     D E
+--     \/
+--      F
+includecommon("automate_ancestry.lua")
+
+revs = make_graph()
+
+-- Now do some checks
+
+-- Empty input gives empty output
+revmap("erase_descendants", {}, {})
+
+-- Single revision input gives the same single revision output
+for _,x in pairs(revs) do
+  revmap("erase_descendants", {x}, {x})
+end
+
+-- Whole revision graph should give roots - A in this case
+revmap("erase_descendants", {revs.a, revs.b, revs.c, revs.d, revs.e, revs.f}, {revs.a})
+
+-- Sibling only inputs should give the same output
+revmap("erase_descendants", {revs.b, revs.c}, {revs.b, revs.c})
+revmap("erase_descendants", {revs.d, revs.e}, {revs.d, revs.e})
+
+-- Siblings with descendants should give just the siblings
+revmap("erase_descendants", {revs.b, revs.c, revs.d, revs.e, revs.f}, {revs.b, revs.c})
+revmap("erase_descendants", {revs.d, revs.e, revs.f}, {revs.d, revs.e})
+
+-- Leaves only input should give the same output
+revmap("erase_descendants", {revs.b, revs.f}, {revs.b, revs.f})
+
+-- Revision with its descendants should give just the revision
+revmap("erase_descendants", {revs.c, revs.d, revs.e, revs.f}, {revs.c})
+revmap("erase_descendants", {revs.e, revs.f}, {revs.e})
+
+
============================================================
--- test/func/extended-selectors/__driver__.lua	cab379ef6f3439ab7f5a4424202ebf9097171c4c
+++ test/func/extended-selectors/__driver__.lua	e80ed8e831bbc9cc346e7722c9877d32887104ef
@@ -3,6 +3,7 @@
 --   not(a)
 --   lca(a,b)
 --   max(a)
+--   min(a)
 --   ancestors(a)
 --   descendants(a)
 --   parents(a)
@@ -79,6 +80,12 @@ expect("b:testbranch|b:otherbranch", roo
 expect("b:testbranch/b:otherbranch", lhs)
 expect("b:testbranch|b:otherbranch", root, lhs, rhs, m, other, other_2)
 
+expect("min(*)", root)
+expect("min(b:testbranch)", root)
+expect("min(b:testbranch/a:Anne)", rhs)
+expect("min(b:otherbranch)", lhs)
+expect("min(a:Jim)", other)
+
 -- now do same tests again with a double not - should get same results
 expect("not(not(b:testbranch))", root, lhs, rhs, m)
 expect("not(not(b:otherbranch))", lhs, other, other_2)
============================================================
--- test/unit/tests/xdelta.cc	4aff975cfd9a9cf18c26f70118e2895880ed6100
+++ test/unit/tests/xdelta.cc	ed5f467ebc4755f7521e74a40833181974b43a8f
@@ -8,11 +8,16 @@
 // PURPOSE.
 
 #include "../../../src/base.hh"
+
+// <boost/math/special_functions/detail/lgamma_small.hpp> uses L().
+// This conflicts with a #define in "../../../src/sanity.hh".
+// Workaround: Include BOOST header before "../../../src/xdelta.hh".
+#include <boost/random.hpp>
+
 #include "../unit_tests.hh"
 #include "../../../src/xdelta.hh"
 
 #include "../../../src/adler32.hh"
-#include <boost/random.hpp>
 
 boost::mt19937 xdelta_prng;
 boost::uniform_smallint<char> xdelta_chargen('a', 'z');
============================================================
--- util/audit-includes	d5757fcf83ab116fba16c53221da0f832307a113
+++ util/audit-includes	1c6fc8be30ebfafc67ca03980e7860db35e5d2e9
@@ -11,6 +11,7 @@ egrep -H "$(printf '^[ \t]*#[ \t]*includ
 # Check all C++ source files to make sure they obey the header file rules.
 
 egrep -H "$(printf '^[ \t]*#[ \t]*include\\>')" "$@" |
+  sed -e "$(printf 's/\r$//')" |
   sed -e "$(printf 's/:[ \t]*#[ \t]*include[ \t]*[<\"]/ /')" -e 's/[>"]$//' |
   {
     current=""

reply via email to

[Prev in Thread] Current Thread [Next in Thread]