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.get_project().get_revision_certs_by_name(id, cert_name(testresult_cert_name), certs);
59 for (vector< revision<cert> >::const_iterator i = certs.begin();
60 i != certs.end(); ++i)
61 {
62 cert_value cv;
63 decode_base64(i->inner().value, cv);
64 try
65 {
66 bool test_ok = lexical_cast<bool>(cv());
67 results.insert(make_pair(i->inner().key, test_ok));
68 }
69 catch(boost::bad_lexical_cast &)
70 {
71 W(F("failed to decode boolean testresult cert value '%s'") % cv);
72 }
73 }
74}
75
76static bool
77acceptable_descendent(branch_name const & branch,
78 revision_id const & base,
79 map<rsa_keypair_id, bool> & base_results,
80 revision_id const & target,
81 app_state & app)
82{
83 L(FL("Considering update target %s") % target);
84
85 // step 1: check the branch
86 if (!app.get_project().revision_is_in_branch(target, branch))
87 {
88 L(FL("%s not in branch %s") % target % branch);
89 return false;
90 }
91
92 // step 2: check the testresults
93 map<rsa_keypair_id, bool> target_results;
94 get_test_results_for_revision(target, target_results, app);
95 if (app.lua.hook_accept_testresult_change(base_results, target_results))
96 {
97 L(FL("%s is acceptable update candidate") % target);
98 return true;
99 }
100 else
101 {
102 L(FL("%s has unacceptable test results") % target);
103 return false;
104 }
105}
106
107
108static void
109calculate_update_set(revision_id const & base,
110 branch_name const & branch,
111 app_state & app,
112 set<revision_id> & candidates)
113{
114 map<rsa_keypair_id, bool> base_results;
115 get_test_results_for_revision(base, base_results, app);
116
117 candidates.clear();
118 // we possibly insert base into the candidate set as well; returning a set
119 // containing just it means that we are up to date; returning an empty set
120 // means that there is no acceptable update.
121 if (acceptable_descendent(branch, base, base_results, base, app))
122 candidates.insert(base);
123
124 // keep a visited set to avoid repeating work
125 set<revision_id> visited;
126 set<revision_id> children;
127 vector<revision_id> to_traverse;
128
129 app.db.get_revision_children(base, children);
130 copy(children.begin(), children.end(), back_inserter(to_traverse));
131
132 while (!to_traverse.empty())
133 {
134 revision_id target = to_traverse.back();
135 to_traverse.pop_back();
136
137 // If we've traversed this id before via a different path, skip it.
138 if (visited.find(target) != visited.end())
139 continue;
140 visited.insert(target);
141
142 // then, possibly insert this revision as a candidate
143 if (acceptable_descendent(branch, base, base_results, target, app))
144 candidates.insert(target);
145
146 // and traverse its children as well
147 app.db.get_revision_children(target, children);
148 copy(children.begin(), children.end(), back_inserter(to_traverse));
149 }
150
151 erase_ancestors(candidates, app);
152}
153
154
155void pick_update_candidates(revision_id const & base_ident,
156 app_state & app,
157 set<revision_id> & candidates)
158{
159 N(app.opts.branchname() != "",
160 F("cannot determine branch for update"));
161 I(!null_id(base_ident));
162
163 calculate_update_set(base_ident, app.opts.branchname,
164 app, candidates);
165}
166
167
168
169// Local Variables:
170// mode: C++
171// fill-column: 76
172// c-file-style: "gnu"
173// indent-tabs-mode: nil
174// End:
175// 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