monotone

monotone Mtn Source Tree

Root/update.cc

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

Archive Download this file

Branches

Tags

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