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