1 // Copyright (C) 2004 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 "revision.hh"
12 #include "roster.hh"
13
14 #include "sanity.hh"
15 #include "basic_io.hh"
16 #include "transforms.hh"
17
18 #include "safe_map.hh"
19 #include <boost/shared_ptr.hpp>
20
21 using std::make_pair;
22 using std::map;
23 using std::string;
24 using boost::shared_ptr;
25
check_sane() const26 void revision_t::check_sane() const
27 {
28 E(!null_id(new_manifest), made_from, F("revision has no manifest id"));
29
30 if (edges.size() == 1)
31 {
32 // no particular checks to be done right now
33 }
34 else if (edges.size() == 2)
35 {
36 // merge nodes cannot have null revisions
37 for (edge_map::const_iterator i = edges.begin(); i != edges.end(); ++i)
38 E(!null_id(edge_old_revision(i)), made_from,
39 F("merge revision has a null parent"));
40 }
41 else
42 // revisions must always have either 1 or 2 edges
43 E(false, made_from, F("revision has %d edges, not 1 or 2") % edges.size());
44
45 // we used to also check that if there were multiple edges that had patches
46 // for the same file, then the new hashes on each edge matched each other.
47 // this is not ported over to roster-style revisions because it's an
48 // inadequate check, and the real check, that the new manifest id is correct
49 // (done in put_revision, for instance) covers this case automatically.
50 }
51
52 bool
is_merge_node() const53 revision_t::is_merge_node() const
54 {
55 return edges.size() > 1;
56 }
57
58 bool
is_nontrivial() const59 revision_t::is_nontrivial() const
60 {
61 check_sane();
62 // merge revisions are never trivial, because even if the resulting node
63 // happens to be identical to both parents, the merge is still recording
64 // that fact.
65 if (is_merge_node())
66 return true;
67 else
68 return !edge_changes(edges.begin()).empty();
69 }
70
revision_t(revision_t const & other)71 revision_t::revision_t(revision_t const & other)
72 : origin_aware(other)
73 {
74 /* behave like normal constructor if other is empty */
75 made_for = made_for_nobody;
76 if (null_id(other.new_manifest) && other.edges.empty()) return;
77 other.check_sane();
78 new_manifest = other.new_manifest;
79 edges = other.edges;
80 made_for = other.made_for;
81 }
82
83 revision_t const &
operator =(revision_t const & other)84 revision_t::operator=(revision_t const & other)
85 {
86 other.check_sane();
87 new_manifest = other.new_manifest;
88 edges = other.edges;
89 made_for = other.made_for;
90 return *this;
91 }
92
93 void
make_revision(revision_id const & old_rev_id,roster_t const & old_roster,roster_t const & new_roster,revision_t & rev)94 make_revision(revision_id const & old_rev_id,
95 roster_t const & old_roster,
96 roster_t const & new_roster,
97 revision_t & rev)
98 {
99 shared_ptr<cset> cs(new cset());
100
101 rev.edges.clear();
102 make_cset(old_roster, new_roster, *cs);
103
104 calculate_ident(new_roster, rev.new_manifest);
105
106 if (global_sanity.debug_p())
107 L(FL("new manifest_id is %s")
108 % rev.new_manifest);
109
110 safe_insert(rev.edges, make_pair(old_rev_id, cs));
111 rev.made_for = made_for_database;
112 }
113
114 void
make_revision(revision_id const & old_rev_id,roster_t const & old_roster,cset const & changes,revision_t & rev)115 make_revision(revision_id const & old_rev_id,
116 roster_t const & old_roster,
117 cset const & changes,
118 revision_t & rev)
119 {
120 roster_t new_roster = old_roster;
121 {
122 temp_node_id_source nis;
123 editable_roster_base er(new_roster, nis);
124 changes.apply_to(er);
125 }
126
127 shared_ptr<cset> cs(new cset(changes));
128 rev.edges.clear();
129
130 calculate_ident(new_roster, rev.new_manifest);
131
132 if (global_sanity.debug_p())
133 L(FL("new manifest_id is %s")
134 % rev.new_manifest);
135
136 safe_insert(rev.edges, make_pair(old_rev_id, cs));
137 rev.made_for = made_for_database;
138 }
139
140 void
make_revision(parent_map const & old_rosters,roster_t const & new_roster,revision_t & rev)141 make_revision(parent_map const & old_rosters,
142 roster_t const & new_roster,
143 revision_t & rev)
144 {
145 edge_map edges;
146 for (parent_map::const_iterator i = old_rosters.begin();
147 i != old_rosters.end();
148 i++)
149 {
150 shared_ptr<cset> cs(new cset());
151 make_cset(parent_roster(i), new_roster, *cs);
152 safe_insert(edges, make_pair(parent_id(i), cs));
153 }
154
155 rev.edges = edges;
156 calculate_ident(new_roster, rev.new_manifest);
157
158 if (global_sanity.debug_p())
159 L(FL("new manifest_id is %s")
160 % rev.new_manifest);
161 }
162
163 static void
recalculate_manifest_id_for_restricted_rev(parent_map const & old_rosters,edge_map & edges,revision_t & rev)164 recalculate_manifest_id_for_restricted_rev(parent_map const & old_rosters,
165 edge_map & edges,
166 revision_t & rev)
167 {
168 // In order to get the correct manifest ID, recalculate the new roster
169 // using one of the restricted csets. It doesn't matter which of the
170 // parent roster/cset pairs we use for this; by construction, they must
171 // all produce the same result.
172 revision_id rid = parent_id(old_rosters.begin());
173 roster_t restricted_roster = *(safe_get(old_rosters, rid).first);
174
175 temp_node_id_source nis;
176 editable_roster_base er(restricted_roster, nis);
177 safe_get(edges, rid)->apply_to(er);
178
179 calculate_ident(restricted_roster, rev.new_manifest);
180 rev.edges = edges;
181
182 if (global_sanity.debug_p())
183 L(FL("new manifest_id is %s")
184 % rev.new_manifest);
185 }
186
187 void
make_restricted_revision(parent_map const & old_rosters,roster_t const & new_roster,node_restriction const & mask,revision_t & rev)188 make_restricted_revision(parent_map const & old_rosters,
189 roster_t const & new_roster,
190 node_restriction const & mask,
191 revision_t & rev)
192 {
193 edge_map edges;
194 for (parent_map::const_iterator i = old_rosters.begin();
195 i != old_rosters.end();
196 i++)
197 {
198 shared_ptr<cset> included(new cset());
199 roster_t restricted_roster;
200
201 make_restricted_roster(parent_roster(i), new_roster,
202 restricted_roster, mask);
203 make_cset(parent_roster(i), restricted_roster, *included);
204 safe_insert(edges, make_pair(parent_id(i), included));
205 }
206
207 recalculate_manifest_id_for_restricted_rev(old_rosters, edges, rev);
208 }
209
210 void
make_restricted_revision(parent_map const & old_rosters,roster_t const & new_roster,node_restriction const & mask,revision_t & rev,cset & excluded,utf8 const & cmd_name)211 make_restricted_revision(parent_map const & old_rosters,
212 roster_t const & new_roster,
213 node_restriction const & mask,
214 revision_t & rev,
215 cset & excluded,
216 utf8 const & cmd_name)
217 {
218 edge_map edges;
219 bool no_excludes = true;
220 for (parent_map::const_iterator i = old_rosters.begin();
221 i != old_rosters.end();
222 i++)
223 {
224 shared_ptr<cset> included(new cset());
225 roster_t restricted_roster;
226
227 make_restricted_roster(parent_roster(i), new_roster,
228 restricted_roster, mask);
229 make_cset(parent_roster(i), restricted_roster, *included);
230 make_cset(restricted_roster, new_roster, excluded);
231 safe_insert(edges, make_pair(parent_id(i), included));
232 if (!excluded.empty())
233 no_excludes = false;
234 }
235
236 E(old_rosters.size() == 1 || no_excludes, origin::user,
237 F("the command '%s %s' cannot be restricted in a two-parent workspace")
238 % prog_name % cmd_name);
239
240 recalculate_manifest_id_for_restricted_rev(old_rosters, edges, rev);
241 }
242
243 // Workspace-only revisions, with fake rev.new_manifest and content
244 // changes suppressed.
245 void
make_revision_for_workspace(revision_id const & old_rev_id,cset const & changes,revision_t & rev)246 make_revision_for_workspace(revision_id const & old_rev_id,
247 cset const & changes,
248 revision_t & rev)
249 {
250 MM(old_rev_id);
251 MM(changes);
252 MM(rev);
253 shared_ptr<cset> cs(new cset(changes));
254 cs->deltas_applied.clear();
255
256 rev.edges.clear();
257 safe_insert(rev.edges, make_pair(old_rev_id, cs));
258 rev.new_manifest = manifest_id(fake_id());
259 rev.made_for = made_for_workspace;
260 }
261
262 void
make_revision_for_workspace(revision_id const & old_rev_id,roster_t const & old_roster,roster_t const & new_roster,revision_t & rev)263 make_revision_for_workspace(revision_id const & old_rev_id,
264 roster_t const & old_roster,
265 roster_t const & new_roster,
266 revision_t & rev)
267 {
268 MM(old_rev_id);
269 MM(old_roster);
270 MM(new_roster);
271 MM(rev);
272 cset changes;
273 make_cset(old_roster, new_roster, changes);
274 make_revision_for_workspace(old_rev_id, changes, rev);
275 }
276
277 void
make_revision_for_workspace(parent_map const & old_rosters,roster_t const & new_roster,revision_t & rev)278 make_revision_for_workspace(parent_map const & old_rosters,
279 roster_t const & new_roster,
280 revision_t & rev)
281 {
282 edge_map edges;
283 for (parent_map::const_iterator i = old_rosters.begin();
284 i != old_rosters.end();
285 i++)
286 {
287 shared_ptr<cset> cs(new cset());
288 make_cset(parent_roster(i), new_roster, *cs);
289 cs->deltas_applied.clear();
290 safe_insert(edges, make_pair(parent_id(i), cs));
291 }
292
293 rev.edges = edges;
294 rev.new_manifest = manifest_id(fake_id());
295 rev.made_for = made_for_workspace;
296 }
297
298 // i/o stuff
299
300 namespace
301 {
302 namespace syms
303 {
304 symbol const format_version("format_version");
305 symbol const old_revision("old_revision");
306 symbol const new_manifest("new_manifest");
307 }
308 }
309
310 void
print_edge(basic_io::printer & printer,edge_entry const & e)311 print_edge(basic_io::printer & printer,
312 edge_entry const & e)
313 {
314 basic_io::stanza st;
315 st.push_binary_pair(syms::old_revision, edge_old_revision(e).inner());
316 printer.print_stanza(st);
317 print_cset(printer, edge_changes(e));
318 }
319
320 static void
print_insane_revision(basic_io::printer & printer,revision_t const & rev)321 print_insane_revision(basic_io::printer & printer,
322 revision_t const & rev)
323 {
324
325 basic_io::stanza format_stanza;
326 format_stanza.push_str_pair(syms::format_version, "1");
327 printer.print_stanza(format_stanza);
328
329 basic_io::stanza manifest_stanza;
330 manifest_stanza.push_binary_pair(syms::new_manifest, rev.new_manifest.inner());
331 printer.print_stanza(manifest_stanza);
332
333 for (edge_map::const_iterator edge = rev.edges.begin();
334 edge != rev.edges.end(); ++edge)
335 print_edge(printer, *edge);
336 }
337
338 void
print_revision(basic_io::printer & printer,revision_t const & rev)339 print_revision(basic_io::printer & printer,
340 revision_t const & rev)
341 {
342 rev.check_sane();
343 print_insane_revision(printer, rev);
344 }
345
346
347 void
parse_edge(basic_io::parser & parser,revision_t & rev)348 parse_edge(basic_io::parser & parser,
349 revision_t & rev)
350 {
351 shared_ptr<cset> cs(new cset());
352 MM(*cs);
353 manifest_id old_man;
354 revision_id old_rev;
355 string tmp;
356
357 parser.esym(syms::old_revision);
358 parser.hex(tmp);
359 old_rev = decode_hexenc_as<revision_id>(tmp, parser.tok.in.made_from);
360
361 parse_cset(parser, *cs);
362
363 rev.edges.insert(make_pair(old_rev, cs));
364 }
365
366
367 void
parse_revision(basic_io::parser & parser,revision_t & rev)368 parse_revision(basic_io::parser & parser,
369 revision_t & rev)
370 {
371 MM(rev);
372 rev.edges.clear();
373 rev.made_for = made_for_database;
374 rev.made_from = parser.tok.in.made_from;
375 string tmp;
376 parser.esym(syms::format_version);
377 parser.str(tmp);
378 E(tmp == "1", parser.tok.in.made_from,
379 F("encountered a revision with unknown format, version %s.\n"
380 "I only know how to understand the version 1 format.\n"
381 "A newer version of monotone is required to complete this operation")
382 % tmp);
383 parser.esym(syms::new_manifest);
384 parser.hex(tmp);
385 rev.new_manifest = decode_hexenc_as<manifest_id>(tmp, parser.tok.in.made_from);
386 while (parser.symp(syms::old_revision))
387 parse_edge(parser, rev);
388 rev.check_sane();
389 }
390
391 void
read_revision(data const & dat,revision_t & rev)392 read_revision(data const & dat,
393 revision_t & rev)
394 {
395 MM(rev);
396 basic_io::input_source src(dat(), "revision");
397 src.made_from = dat.made_from;
398 basic_io::tokenizer tok(src);
399 basic_io::parser pars(tok);
400 parse_revision(pars, rev);
401 E(src.lookahead == EOF, rev.made_from,
402 F("failed to parse revision"));
403 rev.check_sane();
404 }
405
406 void
read_revision(revision_data const & dat,revision_t & rev)407 read_revision(revision_data const & dat,
408 revision_t & rev)
409 {
410 read_revision(dat.inner(), rev);
411 rev.check_sane();
412 }
413
write_insane_revision(revision_t const & rev,data & dat)414 static void write_insane_revision(revision_t const & rev,
415 data & dat)
416 {
417 basic_io::printer pr;
418 print_insane_revision(pr, rev);
419 dat = data(pr.buf, origin::internal);
420 }
421
422 template <> void
dump(revision_t const & rev,string & out)423 dump(revision_t const & rev, string & out)
424 {
425 data dat;
426 write_insane_revision(rev, dat);
427 out = dat();
428 }
429
430 void
write_revision(revision_t const & rev,data & dat)431 write_revision(revision_t const & rev,
432 data & dat)
433 {
434 rev.check_sane();
435 write_insane_revision(rev, dat);
436 }
437
438 void
write_revision(revision_t const & rev,revision_data & dat)439 write_revision(revision_t const & rev,
440 revision_data & dat)
441 {
442 data d;
443 write_revision(rev, d);
444 dat = revision_data(d);
445 }
446
calculate_ident(revision_t const & cs,revision_id & ident)447 void calculate_ident(revision_t const & cs,
448 revision_id & ident)
449 {
450 data tmp;
451 id tid;
452 write_revision(cs, tmp);
453 calculate_ident(tmp, tid);
454 ident = revision_id(tid);
455 }
456
457 // Local Variables:
458 // mode: C++
459 // fill-column: 76
460 // c-file-style: "gnu"
461 // indent-tabs-mode: nil
462 // End:
463 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
464