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: ed8d3ac01eadc609d02f8c14f9b


From: code
Subject: [Monotone-commits-diffs] net.venge.monotone: ed8d3ac01eadc609d02f8c14f9b40cb6f6d8ee50
Date: Thu, 26 Apr 2012 22:40:14 +0200 (CEST)

revision:            ed8d3ac01eadc609d02f8c14f9b40cb6f6d8ee50
date:                2012-04-26T20:33:04
author:              address@hidden
branch:              net.venge.monotone
changelog:
explicit merge of '0dbadd976b0d29ec72c15be03a68a700f412cd0f'
              and '7338faf653922c1c9ce0eb944f8c7c00f5e54bfd'



manifest:
format_version "1"

new_manifest [f6a7530e713ecabbf8bb1b5739b89f8839af5bad]

old_revision [0dbadd976b0d29ec72c15be03a68a700f412cd0f]

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

old_revision [7338faf653922c1c9ce0eb944f8c7c00f5e54bfd]

patch "NEWS"
 from [5514332f7c16430d0c46d3fbac01b0c8a96bc412]
   to [e2f75fe4e775336683ea15030378d9c771ead705]

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

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

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]
============================================================
--- src/unix/fs.cc	030ebb81f78bc138de3cf89e3ace8e9dcde2309c
+++ src/unix/fs.cc	d34f7ed4b5cf3655d7920568e4e1a146fa670e13
@@ -1,4 +1,3 @@
-// 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
@@ -29,7 +28,6 @@
 #include "../platform.hh"
 #include "../vector.hh"
 
-using std::malloc;
 using std::string;
 using std::vector;
 
@@ -290,74 +288,11 @@ 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()))
     {
-      // 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);
-      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));
-        }
+      const int err = errno;
+      E(false, origin::system,
+        F("renaming '%s' to '%s' failed: %s") % from % to % os_strerror(err));
     }
 }
 
============================================================
--- NEWS	5514332f7c16430d0c46d3fbac01b0c8a96bc412
+++ NEWS	e2f75fe4e775336683ea15030378d9c771ead705
@@ -10,6 +10,10 @@ XXX XXX XX XX:XX:XX UTC 201X
           and returns the attributes for a specific file from the
           revision's manifest
 
+        - 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	cba3f164198269415f0996a7211dea6e7e24a3dc
@@ -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)}
============================================================
--- 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/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());
============================================================
--- 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)

reply via email to

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