monotone

monotone Mtn Source Tree

Root/src/update.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <set>
12#include "safe_map.hh"
13#include "vector.hh"
14#include "lexical_cast.hh"
15
16#include "database.hh"
17#include "sanity.hh"
18#include "cert.hh"
19#include "project.hh"
20#include "transforms.hh"
21#include "ui.hh"
22#include "update.hh"
23#include "vocab.hh"
24#include "revision.hh"
25#include "lua_hooks.hh"
26
27// these functions just encapsulate the (somewhat complex) logic behind
28// picking an update target. the actual updating takes place in
29// commands.cc, along with most other file-modifying actions.
30
31// the algorithm is:
32// - do a depth-first traversal of the current revision's descendent set
33// - for each revision, check to see whether it is
34// - in the correct branch
35// - has acceptable test results
36// and add it to the candidate set if so
37// - this gives a set containing every descendent that we might want to
38// update to.
39// - run erase_ancestors on that set, to get just the heads
40// - If there are any non-suspended revisions in the set, then remove the
41// suspended revisions.
42// the idea is that this should be correct even in the presence of
43// discontinuous branches, test results that go from good to bad to good to
44// bad to good, etc.
45// it may be somewhat inefficient to use erase_ancestors here, but deal with
46// that when and if the time comes...
47
48using std::make_pair;
49using std::map;
50using std::set;
51using std::vector;
52
53using boost::lexical_cast;
54
55static void
56get_test_results_for_revision(project_t & project,
57 revision_id const & id,
58 map<key_id, bool> & results)
59{
60 vector<cert> certs;
61 project.get_revision_certs_by_name(id, cert_name(testresult_cert_name),
62 certs);
63 for (vector<cert>::const_iterator i = certs.begin();
64 i != certs.end(); ++i)
65 {
66 cert_value cv = i->value;
67 try
68 {
69 bool test_ok = lexical_cast<bool>(cv());
70 results.insert(make_pair(i->key, test_ok));
71 }
72 catch(boost::bad_lexical_cast &)
73 {
74 W(F("failed to decode boolean testresult cert value '%s'") % cv);
75 }
76 }
77}
78
79static bool
80acceptable_descendent(lua_hooks & lua,
81 project_t & project,
82 branch_name const & branch,
83 revision_id const & base,
84 map<key_id, bool> & base_results,
85 revision_id const & target)
86{
87 L(FL("Considering update target %s") % target);
88
89 // step 1: check the branch
90 if (!project.revision_is_in_branch(target, branch))
91 {
92 L(FL("%s not in branch %s") % target % branch);
93 return false;
94 }
95
96 // step 2: check the testresults
97 map<key_id, bool> target_results;
98 get_test_results_for_revision(project, target, target_results);
99 if (lua.hook_accept_testresult_change(base_results, target_results))
100 {
101 L(FL("%s is acceptable update candidate") % target);
102 return true;
103 }
104 else
105 {
106 L(FL("%s has unacceptable test results") % target);
107 return false;
108 }
109}
110
111void
112pick_update_candidates(lua_hooks & lua,
113 project_t & project,
114 set<revision_id> & candidates,
115 revision_id const & base,
116 branch_name const & branch,
117 bool ignore_suspend_certs)
118{
119 I(!null_id(base));
120 I(!branch().empty());
121
122 map<key_id, bool> base_results;
123 get_test_results_for_revision(project, base, base_results);
124
125 candidates.clear();
126 // we possibly insert base into the candidate set as well; returning a set
127 // containing just it means that we are up to date; returning an empty set
128 // means that there is no acceptable update.
129 if (acceptable_descendent(lua, project, branch, base, base_results, base))
130 candidates.insert(base);
131
132 // keep a visited set to avoid repeating work
133 set<revision_id> visited;
134 set<revision_id> children;
135 vector<revision_id> to_traverse;
136
137 project.db.get_revision_children(base, children);
138 copy(children.begin(), children.end(), back_inserter(to_traverse));
139
140 while (!to_traverse.empty())
141 {
142 revision_id target = to_traverse.back();
143 to_traverse.pop_back();
144
145 // If we've traversed this id before via a different path, skip it.
146 if (visited.find(target) != visited.end())
147 continue;
148 visited.insert(target);
149
150 // then, possibly insert this revision as a candidate
151 if (acceptable_descendent(lua, project, branch, base, base_results,
152 target))
153 candidates.insert(target);
154
155 // and traverse its children as well
156 project.db.get_revision_children(target, children);
157 copy(children.begin(), children.end(), back_inserter(to_traverse));
158 }
159
160 erase_ancestors(project.db, candidates);
161
162 if (ignore_suspend_certs)
163 return;
164
165 set<revision_id> active_candidates;
166 for (set<revision_id>::const_iterator i = candidates.begin();
167 i != candidates.end(); i++)
168 if (!project.revision_is_suspended_in_branch(*i, branch))
169 safe_insert(active_candidates, *i);
170
171 if (!active_candidates.empty())
172 candidates = active_candidates;
173}
174
175// Local Variables:
176// mode: C++
177// fill-column: 76
178// c-file-style: "gnu"
179// indent-tabs-mode: nil
180// End:
181// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status