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