[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Monotone-commits-diffs] net.venge.monotone, net.venge.monotone.min-sele
From: |
code |
Subject: |
[Monotone-commits-diffs] net.venge.monotone, net.venge.monotone.min-selector: 0dbadd976b0d29ec72c15be03a68a700f412cd0f |
Date: |
Thu, 26 Apr 2012 21:03:48 +0200 (CEST) |
revision: 0dbadd976b0d29ec72c15be03a68a700f412cd0f
date: 2012-04-26T19:01:48
date: 2012-04-26T19:03:05
author: Richard Hopkins <address@hidden>
branch: net.venge.monotone
branch: net.venge.monotone.min-selector
changelog:
merge of '672d62dfdd81252d581a8941a69ea4f99b2359c8'
and 'd238d73a0c7bce35c65d1dd4612d8eae104612e9'
changelog:
propagate from branch 'net.venge.monotone' (head
d238d73a0c7bce35c65d1dd4612d8eae104612e9)
to branch 'net.venge.monotone.min-selector' (head
672d62dfdd81252d581a8941a69ea4f99b2359c8)
manifest:
format_version "1"
new_manifest [2746684653a096b99f000a3fdcbd4395033a7a3e]
old_revision [672d62dfdd81252d581a8941a69ea4f99b2359c8]
patch "src/unix/fs.cc"
from [d34f7ed4b5cf3655d7920568e4e1a146fa670e13]
to [030ebb81f78bc138de3cf89e3ace8e9dcde2309c]
old_revision [d238d73a0c7bce35c65d1dd4612d8eae104612e9]
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 d34f7ed4b5cf3655d7920568e4e1a146fa670e13
+++ src/unix/fs.cc 030ebb81f78bc138de3cf89e3ace8e9dcde2309c
@@ -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);
+ 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));
+ }
}
}
============================================================
--- 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)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Monotone-commits-diffs] net.venge.monotone, net.venge.monotone.min-selector: 0dbadd976b0d29ec72c15be03a68a700f412cd0f,
code <=