monotone

monotone Mtn Source Tree

Root/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 <map>
13#include "vector.hh"
14#include "lexical_cast.hh"
15
16#include "app_state.hh"
17#include "database.hh"
18#include "sanity.hh"
19#include "cert.hh"
20#include "transforms.hh"
21#include "ui.hh"
22#include "update.hh"
23#include "vocab.hh"
24#include "revision.hh"
25
26// these functions just encapsulate the (somewhat complex) logic behind
27// picking an update target. the actual updating takes place in
28// commands.cc, along with most other file-modifying actions.
29
30// the algorithm is:
31// - do a depth-first traversal of the current revision's descendent set
32// - for each revision, check to see whether it is
33// - in the correct branch
34// - has acceptable test results
35// and add it to the candidate set if so
36// - this gives a set containing every descendent that we might want to
37// update to.
38// - run erase_ancestors on that set, to get just the heads
39// - If there are any non-suspended revisions in the set, then remove the
40// suspended revisions.
41// the idea is that this should be correct even in the presence of
42// discontinuous branches, test results that go from good to bad to good to
43// bad to good, etc.
44// it may be somewhat inefficient to use erase_ancestors here, but deal with
45// that when and if the time comes...
46
47using std::make_pair;
48using std::map;
49using std::set;
50using std::vector;
51
52using boost::lexical_cast;
53
54static void
55get_test_results_for_revision(revision_id const & id,
56 map<rsa_keypair_id, bool> & results,
57 app_state & app)
58{
59 vector< revision<cert> > certs;
60 app.get_project().get_revision_certs_by_name(id, cert_name(testresult_cert_name), certs);
61 for (vector< revision<cert> >::const_iterator i = certs.begin();
62 i != certs.end(); ++i)
63 {
64 cert_value cv;
65 decode_base64(i->inner().value, cv);
66 try
67 {
68 bool test_ok = lexical_cast<bool>(cv());
69 results.insert(make_pair(i->inner().key, test_ok));
70 }
71 catch(boost::bad_lexical_cast &)
72 {
73 W(F("failed to decode boolean testresult cert value '%s'") % cv);
74 }
75 }
76}
77
78static bool
79acceptable_descendent(branch_name const & branch,
80 revision_id const & base,
81 map<rsa_keypair_id, bool> & base_results,
82 revision_id const & target,
83 app_state & app)
84{
85 L(FL("Considering update target %s") % target);
86
87 // step 1: check the branch
88 if (!app.get_project().revision_is_in_branch(target, branch))
89 {
90 L(FL("%s not in branch %s") % target % branch);
91 return false;
92 }
93
94 // step 2: check the testresults
95 map<rsa_keypair_id, bool> target_results;
96 get_test_results_for_revision(target, target_results, app);
97 if (app.lua.hook_accept_testresult_change(base_results, target_results))
98 {
99 L(FL("%s is acceptable update candidate") % target);
100 return true;
101 }
102 else
103 {
104 L(FL("%s has unacceptable test results") % target);
105 return false;
106 }
107}
108
109namespace
110{
111 struct suspended_in_branch : public is_failure
112 {
113 app_state & app;
114 base64<cert_value > const & branch_encoded;
115 suspended_in_branch(app_state & app,
116 base64<cert_value> const & branch_encoded)
117 : app(app), branch_encoded(branch_encoded)
118 {}
119 virtual bool operator()(revision_id const & rid)
120 {
121 vector< revision<cert> > certs;
122 app.db.get_revision_certs(rid,
123 cert_name(suspend_cert_name),
124 branch_encoded,
125 certs);
126 erase_bogus_certs(certs, app);
127 return !certs.empty();
128 }
129 };
130}
131
132
133static void
134calculate_update_set(revision_id const & base,
135 branch_name const & branch,
136 app_state & app,
137 set<revision_id> & candidates)
138{
139 map<rsa_keypair_id, bool> base_results;
140 get_test_results_for_revision(base, base_results, app);
141
142 candidates.clear();
143 // we possibly insert base into the candidate set as well; returning a set
144 // containing just it means that we are up to date; returning an empty set
145 // means that there is no acceptable update.
146 if (acceptable_descendent(branch, base, base_results, base, app))
147 candidates.insert(base);
148
149 // keep a visited set to avoid repeating work
150 set<revision_id> visited;
151 set<revision_id> children;
152 vector<revision_id> to_traverse;
153
154 app.db.get_revision_children(base, children);
155 copy(children.begin(), children.end(), back_inserter(to_traverse));
156
157 while (!to_traverse.empty())
158 {
159 revision_id target = to_traverse.back();
160 to_traverse.pop_back();
161
162 // If we've traversed this id before via a different path, skip it.
163 if (visited.find(target) != visited.end())
164 continue;
165 visited.insert(target);
166
167 // then, possibly insert this revision as a candidate
168 if (acceptable_descendent(branch, base, base_results, target, app))
169 candidates.insert(target);
170
171 // and traverse its children as well
172 app.db.get_revision_children(target, children);
173 copy(children.begin(), children.end(), back_inserter(to_traverse));
174 }
175
176 erase_ancestors(candidates, app);
177
178 bool have_non_suspended_rev = false;
179
180 for (set<revision_id>::const_iterator it = candidates.begin(); it != candidates.end(); it++)
181 {
182 if (!app.get_project().revision_is_suspended_in_branch(*it, branch))
183 {
184 have_non_suspended_rev = true;
185 break;
186 }
187 }
188 if (!app.opts.ignore_suspend_certs && have_non_suspended_rev)
189 {
190 // remove all suspended revisions
191 base64<cert_value> branch_encoded;
192 encode_base64(cert_value(branch()), branch_encoded);
193 suspended_in_branch s(app, branch_encoded);
194 for(std::set<revision_id>::iterator it = candidates.begin(); it != candidates.end(); it++)
195 if (s(*it))
196 candidates.erase(*it);
197 }
198}
199
200
201void pick_update_candidates(revision_id const & base_ident,
202 app_state & app,
203 set<revision_id> & candidates)
204{
205 N(app.opts.branchname() != "",
206 F("cannot determine branch for update"));
207 I(!null_id(base_ident));
208
209 calculate_update_set(base_ident, app.opts.branchname,
210 app, candidates);
211}
212
213
214
215// Local Variables:
216// mode: C++
217// fill-column: 76
218// c-file-style: "gnu"
219// indent-tabs-mode: nil
220// End:
221// 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