1 // Copyright (C) 2009, 2010, 2012 Stephen Leake <stephen_leake@stephe-leake.org>
2 // Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
3 //
4 // This program is made available under the GNU GPL version 2.0 or
5 // greater. See the accompanying file COPYING for details.
6 //
7 // This program is distributed WITHOUT ANY WARRANTY; without even the
8 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 // PURPOSE.
10 
11 #include "base.hh"
12 #include "work.hh"
13 
14 #include <ostream>
15 #include <sstream>
16 #include <cstring>
17 #include <cerrno>
18 #include <queue>
19 
20 #include "lexical_cast.hh"
21 #include "basic_io.hh"
22 #include "cset.hh"
23 #include "file_io.hh"
24 #include "platform-wrapped.hh"
25 #include "restrictions.hh"
26 #include "sanity.hh"
27 #include "safe_map.hh"
28 #include "revision.hh"
29 #include "inodeprint.hh"
30 #include "merge_content.hh"
31 #include "charset.hh"
32 #include "app_state.hh"
33 #include "database.hh"
34 #include "roster.hh"
35 #include "transforms.hh"
36 #include "vocab_cast.hh"
37 
38 using std::deque;
39 using std::exception;
40 using std::make_pair;
41 using std::map;
42 using std::pair;
43 using std::set;
44 using std::string;
45 using std::vector;
46 
47 using boost::lexical_cast;
48 
49 // workspace / book-keeping file code
50 
51 static char const inodeprints_file_name[] = "inodeprints";
52 static char const local_dump_file_name[] = "debug";
53 static char const options_file_name[] = "options";
54 static char const user_log_file_name[] = "log";
55 static char const commit_file_name[] = "commit";
56 static char const revision_file_name[] = "revision";
57 static char const update_file_name[] = "update";
58 static char const bisect_file_name[] = "bisect";
59 
60 static void
get_revision_path(bookkeeping_path & m_path)61 get_revision_path(bookkeeping_path & m_path)
62 {
63   m_path = bookkeeping_root / revision_file_name;
64   L(FL("revision path is %s") % m_path);
65 }
66 
67 static void
get_options_path(bookkeeping_path & o_path)68 get_options_path(bookkeeping_path & o_path)
69 {
70   o_path = bookkeeping_root / options_file_name;
71   L(FL("options path is %s") % o_path);
72 }
73 
74 static void
get_options_path(system_path const & workspace,system_path & o_path)75 get_options_path(system_path const & workspace, system_path & o_path)
76 {
77   o_path = workspace / bookkeeping_root_component / options_file_name;
78   L(FL("options path is %s") % o_path);
79 }
80 
81 static void
get_inodeprints_path(bookkeeping_path & ip_path)82 get_inodeprints_path(bookkeeping_path & ip_path)
83 {
84   ip_path = bookkeeping_root / inodeprints_file_name;
85   L(FL("inodeprints path is %s") % ip_path);
86 }
87 
88 static void
get_user_log_path(bookkeeping_path & ul_path)89 get_user_log_path(bookkeeping_path & ul_path)
90 {
91   ul_path = bookkeeping_root / user_log_file_name;
92   L(FL("user log path is %s") % ul_path);
93 }
94 
95 static void
get_commit_path(bookkeeping_path & commit_path)96 get_commit_path(bookkeeping_path & commit_path)
97 {
98   commit_path = bookkeeping_root / commit_file_name;
99   L(FL("commit path is %s") % commit_path);
100 }
101 
102 static void
get_update_path(bookkeeping_path & update_path)103 get_update_path(bookkeeping_path & update_path)
104 {
105   update_path = bookkeeping_root / update_file_name;
106   L(FL("update path is %s") % update_path);
107 }
108 
109 static void
get_bisect_path(bookkeeping_path & bisect_path)110 get_bisect_path(bookkeeping_path & bisect_path)
111 {
112   bisect_path = bookkeeping_root / bisect_file_name;
113   L(FL("bisect path is %s") % bisect_path);
114 }
115 
116 //
117 
118 bool
directory_is_workspace(system_path const & dir)119 directory_is_workspace(system_path const & dir)
120 {
121   // as far as the users of this function are concerned, a version 0
122   // workspace (MT directory instead of _MTN) does not count.
123   return directory_exists(dir / bookkeeping_root_component);
124 }
125 
126 bool workspace::found;
127 bool workspace::used;
128 bool workspace::branch_is_sticky;
129 
130 void
require_workspace()131 workspace::require_workspace()
132 {
133   E(workspace::found, origin::user,
134     F("workspace required but not found"));
135   workspace::used = true;
136 }
137 
138 void
require_workspace(i18n_format const & explanation)139 workspace::require_workspace(i18n_format const & explanation)
140 {
141   E(workspace::found, origin::user,
142     F("workspace required but not found\n%s") % explanation.str());
143   workspace::used = true;
144 }
145 
146 void
create_workspace(options const & opts,lua_hooks & lua,system_path const & new_dir)147 workspace::create_workspace(options const & opts,
148                             lua_hooks & lua,
149                             system_path const & new_dir)
150 {
151   E(!new_dir.empty(), origin::user, F("invalid directory ''"));
152 
153   L(FL("creating workspace in %s") % new_dir);
154 
155   mkdir_p(new_dir);
156   go_to_workspace(new_dir);
157   mark_std_paths_used();
158 
159   E(!directory_exists(bookkeeping_root), origin::user,
160     F("monotone bookkeeping directory '%s' already exists in '%s'")
161     % bookkeeping_root % new_dir);
162 
163   L(FL("creating bookkeeping directory '%s' for workspace in '%s'")
164     % bookkeeping_root % new_dir);
165 
166   mkdir_p(bookkeeping_root);
167 
168   workspace::found = true;
169   workspace::set_options(opts, lua, true);
170   workspace::write_format();
171 
172   data empty;
173   bookkeeping_path ul_path;
174   get_user_log_path(ul_path);
175   write_data(ul_path, empty);
176 
177   if (lua.hook_use_inodeprints())
178     {
179       bookkeeping_path ip_path;
180       get_inodeprints_path(ip_path);
181       write_data(ip_path, empty);
182     }
183 
184   bookkeeping_path dump_path;
185   workspace::get_local_dump_path(dump_path);
186   // The 'false' means that, e.g., if we're running checkout,
187   // then it's okay for dumps to go into our starting working
188   // dir's _MTN rather than the new workspace dir's _MTN.
189   global_sanity.set_dump_path(system_path(dump_path, false).as_external());
190 }
191 
192 // Normal-use constructor.
workspace(app_state & app)193 workspace::workspace(app_state & app)
194   : lua(app.lua)
195 {
196   require_workspace();
197 }
198 
workspace(app_state & app,i18n_format const & explanation)199 workspace::workspace(app_state & app, i18n_format const & explanation)
200   : lua(app.lua)
201 {
202   require_workspace(explanation);
203 }
204 
workspace(lua_hooks & lua,i18n_format const & explanation)205 workspace::workspace(lua_hooks & lua, i18n_format const & explanation)
206   : lua(lua)
207 {
208   require_workspace(explanation);
209 }
210 
211 // routines for manipulating the bookkeeping directory
212 
213 // revision file contains a partial revision describing the workspace
214 void
get_work_rev(revision_t & rev)215 workspace::get_work_rev(revision_t & rev)
216 {
217   bookkeeping_path rev_path;
218   get_revision_path(rev_path);
219   data rev_data;
220   MM(rev_data);
221   try
222     {
223       read_data(rev_path, rev_data);
224     }
225   catch(exception & e)
226     {
227       E(false, origin::system,
228         F("workspace is corrupt: reading '%s': %s")
229         % rev_path % e.what());
230     }
231 
232   read_revision(rev_data, rev);
233   // Mark it so it doesn't creep into the database.
234   rev.made_for = made_for_workspace;
235 }
236 
237 void
put_work_rev(revision_t const & rev)238 workspace::put_work_rev(revision_t const & rev)
239 {
240   MM(rev);
241   I(rev.made_for == made_for_workspace);
242   rev.check_sane();
243 
244   data rev_data;
245   write_revision(rev, rev_data);
246 
247   bookkeeping_path rev_path;
248   get_revision_path(rev_path);
249   write_data(rev_path, rev_data);
250 }
251 
252 void
get_update_id(revision_id & update_id)253 workspace::get_update_id(revision_id & update_id)
254 {
255   data update_data;
256   bookkeeping_path update_path;
257   get_update_path(update_path);
258   E(file_exists(update_path), origin::user,
259     F("no update has occurred in this workspace"));
260 
261   read_data(update_path, update_data);
262 
263   update_id = revision_id(decode_hexenc(update_data(), origin::internal),
264                           origin::internal);
265   E(!null_id(update_id), origin::internal,
266     F("no update revision available"));
267 }
268 
269 void
put_update_id(revision_id const & update_id)270 workspace::put_update_id(revision_id const & update_id)
271 {
272   data update_data(encode_hexenc(update_id.inner()(), origin::internal),
273                    origin::internal);
274   bookkeeping_path update_path;
275   get_update_path(update_path);
276   write_data(update_path, update_data);
277 }
278 
279 // structures derived from the work revision, the database, and possibly
280 // the workspace
281 
282 static void
get_roster_for_rid(database & db,revision_id const & rid,cached_roster & cr)283 get_roster_for_rid(database & db,
284                    revision_id const & rid,
285                    cached_roster & cr)
286 {
287   // We may be asked for a roster corresponding to the null rid, which
288   // is not in the database.  In this situation, what is wanted is an empty
289   // roster (and marking map).
290   if (null_id(rid))
291     {
292       cr.first = boost::shared_ptr<roster_t const>(new roster_t);
293       cr.second = boost::shared_ptr<marking_map const>(new marking_map);
294     }
295   else
296     {
297       E(db.revision_exists(rid), origin::user,
298         F("base revision %s does not exist in database") % rid);
299       db.get_roster(rid, cr);
300     }
301   L(FL("base roster has %d entries") % cr.first->all_nodes().size());
302 }
303 
304 void
require_parents_in_db(database & db,revision_t const & rev)305 workspace::require_parents_in_db(database & db,
306                                  revision_t const & rev)
307 {
308   for (edge_map::const_iterator i = rev.edges.begin();
309        i != rev.edges.end(); i++)
310     {
311       revision_id const & parent(edge_old_revision(i));
312       E(null_id(parent) || db.revision_exists(parent), origin::user,
313         F("parent revision %s does not exist, did you specify the wrong database?")
314         % parent);
315     }
316 }
317 
318 void
get_parent_rosters(database & db,parent_map & parents)319 workspace::get_parent_rosters(database & db, parent_map & parents)
320 {
321   revision_t rev;
322   get_work_rev(rev);
323   require_parents_in_db(db, rev);
324 
325   parents.clear();
326   for (edge_map::const_iterator i = rev.edges.begin();
327        i != rev.edges.end(); i++)
328     {
329       cached_roster cr;
330       get_roster_for_rid(db, edge_old_revision(i), cr);
331       safe_insert(parents, make_pair(edge_old_revision(i), cr));
332     }
333 }
334 
335 void
get_current_roster_shape(database & db,node_id_source & nis,roster_t & ros)336 workspace::get_current_roster_shape(database & db,
337                                     node_id_source & nis,
338                                     roster_t & ros)
339 {
340   revision_t rev;
341   get_work_rev(rev);
342   require_parents_in_db(db, rev);
343   revision_id new_rid(fake_id());
344 
345   // If there is just one parent, it might be the null ID, which
346   // make_roster_for_revision does not handle correctly.
347   if (rev.edges.size() == 1 && null_id(edge_old_revision(rev.edges.begin())))
348     {
349       I(ros.all_nodes().empty());
350       editable_roster_base er(ros, nis);
351       edge_changes(rev.edges.begin()).apply_to(er);
352     }
353   else
354     {
355       marking_map dummy;
356       make_roster_for_revision(db, nis, rev, new_rid, ros, dummy);
357     }
358 }
359 
360 bool
has_changes(database & db)361 workspace::has_changes(database & db)
362 {
363   parent_map parents;
364   get_parent_rosters(db, parents);
365 
366   // if we have more than one parent roster then this workspace contains
367   // a merge which means this is always a committable change
368   if (parents.size() > 1)
369     return true;
370 
371   temp_node_id_source nis;
372   roster_t new_roster, old_roster = parent_roster(parents.begin());
373 
374   get_current_roster_shape(db, nis, new_roster);
375   update_current_roster_from_filesystem(new_roster);
376 
377   return !(old_roster == new_roster);
378 }
379 
380 // user log file
381 
382 void
read_user_log(utf8 & dat)383 workspace::read_user_log(utf8 & dat)
384 {
385   bookkeeping_path ul_path;
386   get_user_log_path(ul_path);
387 
388   if (file_exists(ul_path))
389     {
390       data tmp;
391       read_data(ul_path, tmp);
392       system_to_utf8(typecast_vocab<external>(tmp), dat);
393     }
394 }
395 
396 void
write_user_log(utf8 const & dat)397 workspace::write_user_log(utf8 const & dat)
398 {
399   bookkeeping_path ul_path;
400   get_user_log_path(ul_path);
401 
402   external tmp;
403   utf8_to_system_best_effort(dat, tmp);
404   write_data(ul_path, typecast_vocab<data>(tmp));
405 }
406 
407 void
blank_user_log()408 workspace::blank_user_log()
409 {
410   data empty;
411   bookkeeping_path ul_path;
412   get_user_log_path(ul_path);
413   write_data(ul_path, empty);
414 }
415 
416 bool
has_contents_user_log()417 workspace::has_contents_user_log()
418 {
419   utf8 user_log_message;
420   read_user_log(user_log_message);
421   return user_log_message().length() > 0;
422 }
423 
424 // commit buffer backup file
425 
426 void
load_commit_text(utf8 & dat)427 workspace::load_commit_text(utf8 & dat)
428 {
429   bookkeeping_path commit_path;
430   get_commit_path(commit_path);
431 
432   if (file_exists(commit_path))
433     {
434       data tmp;
435       read_data(commit_path, tmp);
436       system_to_utf8(typecast_vocab<external>(tmp), dat);
437     }
438 }
439 
440 void
save_commit_text(utf8 const & dat)441 workspace::save_commit_text(utf8 const & dat)
442 {
443   bookkeeping_path commit_path;
444   get_commit_path(commit_path);
445 
446   external tmp;
447   utf8_to_system_best_effort(dat, tmp);
448   write_data(commit_path, typecast_vocab<data>(tmp));
449 }
450 
451 void
clear_commit_text()452 workspace::clear_commit_text()
453 {
454   bookkeeping_path commit_path;
455   get_commit_path(commit_path);
456   delete_file(commit_path);
457 }
458 
459 // _MTN/options handling.
460 
461 static void
read_options_file(any_path const & optspath,options & opts)462 read_options_file(any_path const & optspath,
463                   options & opts)
464 {
465   data dat;
466   try
467     {
468       read_data(optspath, dat);
469     }
470   catch (exception & e)
471     {
472       W(F("Failed to read options file '%s': %s") % optspath % e.what());
473       return;
474     }
475 
476   basic_io::input_source src(dat(), optspath.as_external(), origin::workspace);
477   basic_io::tokenizer tok(src);
478   basic_io::parser parser(tok);
479 
480   while (parser.symp())
481     {
482       string opt, val;
483       parser.sym(opt);
484       parser.str(val);
485 
486       if (opt == "database")
487         {
488           E(val != memory_db_identifier, origin::user,
489             F("a memory database '%s' cannot be used in a workspace")
490                 % memory_db_identifier);
491 
492           if (val.find(':') == 0)
493             {
494               opts.dbname_alias = val;
495               opts.dbname_given = true;
496               opts.dbname_type = managed_db;
497             }
498           else
499             {
500               opts.dbname = system_path(val, origin::workspace);
501               opts.dbname_given = true;
502               opts.dbname_type = unmanaged_db;
503             }
504         }
505       else if (opt == "branch")
506         {
507           opts.branch = branch_name(val, origin::workspace);
508           opts.branch_given = true;
509         }
510       else if (opt == "key")
511         {
512           opts.key = external_key_name(val, origin::workspace);
513           opts.key_given = true;
514         }
515       else if (opt == "keydir")
516         {
517           opts.key_dir = system_path(val, origin::workspace);
518           opts.key_dir_given = true;
519         }
520       else
521         W(F("unrecognized key '%s' in options file '%s' - ignored")
522           % opt % optspath);
523     }
524   E(src.lookahead == EOF, src.made_from,
525     F("Could not parse entire options file '%s'") % optspath);
526 }
527 
528 static void
write_options_file(bookkeeping_path const & optspath,options const & opts)529 write_options_file(bookkeeping_path const & optspath,
530                    options const & opts)
531 {
532   basic_io::stanza st;
533 
534   E(opts.dbname_type != memory_db, origin::user,
535     F("a memory database '%s' cannot be used in a workspace")
536       % memory_db_identifier);
537 
538   // if we have both, alias and full path, prefer the alias
539   if (opts.dbname_type == managed_db && !opts.dbname_alias.empty())
540     st.push_str_pair(symbol("database"), opts.dbname_alias);
541   else
542   if (opts.dbname_type == unmanaged_db && !opts.dbname.as_internal().empty())
543     st.push_str_pair(symbol("database"), opts.dbname.as_internal());
544 
545   if (!opts.branch().empty())
546     st.push_str_pair(symbol("branch"), opts.branch());
547   if (!opts.key().empty())
548     st.push_str_pair(symbol("key"), opts.key());
549   if (!opts.key_dir.as_internal().empty())
550     st.push_str_pair(symbol("keydir"), opts.key_dir.as_internal());
551 
552   basic_io::printer pr;
553   pr.print_stanza(st);
554   try
555     {
556       write_data(optspath, data(pr.buf, origin::internal));
557     }
558   catch(exception & e)
559     {
560       W(F("Failed to write options file '%s': %s") % optspath % e.what());
561     }
562 }
563 
564 void
get_options(options & opts)565 workspace::get_options(options & opts)
566 {
567   if (!workspace::found)
568     return;
569 
570   options cur_opts;
571   bookkeeping_path o_path;
572   get_options_path(o_path);
573   read_options_file(o_path, cur_opts);
574 
575   // Workspace options are not to override the command line.
576   if (!opts.dbname_given)
577     {
578       opts.dbname = cur_opts.dbname;
579       opts.dbname_alias = cur_opts.dbname_alias;
580       opts.dbname_type = cur_opts.dbname_type;
581       opts.dbname_given = cur_opts.dbname_type;
582     }
583 
584   if (!opts.key_dir_given && !opts.conf_dir_given && cur_opts.key_dir_given)
585     { // if empty/missing, we want to keep the default
586       opts.key_dir = cur_opts.key_dir;
587       // one would expect that we should set the key_dir_given flag here, but
588       // we do not because of the interaction between --confdir and --keydir.
589       // If --keydir is not given and --confdir is, then --keydir will default
590       // to the "keys" subdirectory of the given confdir. This works by the
591       // --confdir option body looking at key_dir_given; if reading the keydir
592       // from _MTN/options set that, then --confdir would stop setting the
593       // default keydir when in a workspace.
594       //opts.key_dir_given = true;
595     }
596 
597   if (opts.branch().empty() && cur_opts.branch_given)
598     {
599       opts.branch = cur_opts.branch;
600       branch_is_sticky = true;
601     }
602 
603   L(FL("branch name is '%s'") % opts.branch);
604 
605   if (!opts.key_given)
606     opts.key = cur_opts.key;
607 }
608 
609 void
get_options(system_path const & workspace_root,options & opts)610 workspace::get_options(system_path const & workspace_root,
611                        options & opts)
612 {
613   system_path o_path = (workspace_root
614                         / bookkeeping_root_component
615                         / options_file_name);
616   read_options_file(o_path, opts);
617 }
618 
619 void
maybe_set_options(options const & opts,lua_hooks & lua)620 workspace::maybe_set_options(options const & opts, lua_hooks & lua)
621 {
622   if (workspace::found && workspace::used)
623       set_options(opts, lua, false);
624 }
625 
626 // This function should usually be called at the (successful)
627 // execution of a function, because we don't do many checks here, f.e.
628 // if this is a valid sqlite file and if it contains the correct identifier,
629 // so be warned that you do not call this too early
630 void
set_options(options const & opts,lua_hooks & lua,bool branch_is_sticky)631 workspace::set_options(options const & opts, lua_hooks & lua, bool branch_is_sticky)
632 {
633   E(workspace::found, origin::user, F("workspace required but not found"));
634 
635   bookkeeping_path o_path;
636   get_options_path(o_path);
637 
638   database_path_helper helper(lua);
639   system_path old_db_path, new_db_path;
640 
641   helper.get_database_path(opts, new_db_path);
642 
643   // If any of the incoming options was empty, we want to leave that option
644   // as is in _MTN/options, not write out an empty option.
645   options cur_opts;
646   if (file_exists(o_path))
647   {
648     read_options_file(o_path, cur_opts);
649     helper.get_database_path(cur_opts, old_db_path);
650   }
651 
652   bool options_changed = false;
653   if (old_db_path != new_db_path && file_exists(new_db_path))
654     {
655       // remove the currently registered workspace from the old
656       // database and add it to the new one
657       system_path current_workspace;
658       get_current_workspace(current_workspace);
659 
660       if (cur_opts.dbname_given)
661         {
662           try
663             {
664               database old_db(cur_opts, lua);
665               old_db.unregister_workspace(current_workspace);
666             }
667           catch (recoverable_failure & rf)
668             {
669               W(F("could not unregister workspace from old database '%s'")
670                 % old_db_path);
671             }
672         }
673 
674       database new_db(opts, lua);
675       new_db.register_workspace(current_workspace);
676 
677       cur_opts.dbname_type = opts.dbname_type;
678       cur_opts.dbname_alias = opts.dbname_alias;
679       cur_opts.dbname = opts.dbname;
680       options_changed = true;
681     }
682 
683   if (!opts.key_dir.as_internal().empty() &&
684       directory_exists(opts.key_dir) &&
685       cur_opts.key_dir != opts.key_dir)
686     {
687       cur_opts.key_dir = opts.key_dir;
688       options_changed = true;
689     }
690 
691   if ((branch_is_sticky || workspace::branch_is_sticky) &&
692       !opts.branch().empty() &&
693       cur_opts.branch != opts.branch)
694     {
695       cur_opts.branch = opts.branch;
696       options_changed = true;
697     }
698 
699   if (opts.key_given && cur_opts.key != opts.key)
700     {
701       cur_opts.key = opts.key;
702       options_changed = true;
703     }
704 
705   // only rewrite the options file if there are actual changes
706   if (options_changed)
707     {
708       L(FL("workspace options changed - writing back to _MTN/options"));
709       write_options_file(o_path, cur_opts);
710     }
711 }
712 
713 void
print_option(utf8 const & opt,std::ostream & output)714 workspace::print_option(utf8 const & opt, std::ostream & output)
715 {
716   E(workspace::found, origin::user, F("workspace required but not found"));
717 
718   bookkeeping_path o_path;
719   get_options_path(o_path);
720 
721   options opts;
722   read_options_file(o_path, opts);
723 
724   if (opt() == "database")
725     output << opts.dbname << '\n';
726   else if (opt() == "branch")
727     output << opts.branch << '\n';
728   else if (opt() == "key")
729     output << opts.key << '\n';
730   else if (opt() == "keydir")
731     output << opts.key_dir << '\n';
732   else
733     E(false, origin::user, F("'%s' is not a recognized workspace option") % opt);
734 }
735 
736 // _MTN/bisect handling.
737 
738 namespace syms
739 {
740     symbol const start("start");
741     symbol const good("good");
742     symbol const bad("bad");
743     symbol const skipped("skipped");
744 };
745 
746 void
get_bisect_info(vector<bisect::entry> & bisect)747 workspace::get_bisect_info(vector<bisect::entry> & bisect)
748 {
749   bookkeeping_path bisect_path;
750   get_bisect_path(bisect_path);
751 
752   if (!file_exists(bisect_path))
753     return;
754 
755   data dat;
756   read_data(bisect_path, dat);
757 
758   string name("bisect");
759   basic_io::input_source src(dat(), name, origin::workspace);
760   basic_io::tokenizer tok(src);
761   basic_io::parser parser(tok);
762 
763   while (parser.symp())
764     {
765       string rev;
766       bisect::type type;
767       if (parser.symp(syms::start))
768         {
769           parser.sym();
770           parser.hex(rev);
771           type = bisect::start;
772         }
773       else if (parser.symp(syms::good))
774         {
775           parser.sym();
776           parser.hex(rev);
777           type = bisect::good;
778         }
779       else if (parser.symp(syms::bad))
780         {
781           parser.sym();
782           parser.hex(rev);
783           type = bisect::bad;
784         }
785       else if (parser.symp(syms::skipped))
786         {
787           parser.sym();
788           parser.hex(rev);
789           type = bisect::skipped;
790         }
791       else
792         I(false);
793 
794       revision_id rid =
795         decode_hexenc_as<revision_id>(rev, parser.tok.in.made_from);
796       bisect.push_back(make_pair(type, rid));
797     }
798 }
799 
800 void
put_bisect_info(vector<bisect::entry> const & bisect)801 workspace::put_bisect_info(vector<bisect::entry> const & bisect)
802 {
803   bookkeeping_path bisect_path;
804   get_bisect_path(bisect_path);
805 
806   basic_io::stanza st;
807   for (vector<bisect::entry>::const_iterator i = bisect.begin();
808        i != bisect.end(); ++i)
809     {
810       switch (i->first)
811         {
812         case bisect::start:
813           st.push_binary_pair(syms::start, i->second.inner());
814           break;
815 
816         case bisect::good:
817           st.push_binary_pair(syms::good, i->second.inner());
818           break;
819 
820         case bisect::bad:
821           st.push_binary_pair(syms::bad, i->second.inner());
822           break;
823 
824         case bisect::skipped:
825           st.push_binary_pair(syms::skipped, i->second.inner());
826           break;
827 
828         case bisect::update:
829           // this value is not persisted, it is only used by the bisect
830           // update command to rerun a selection and update based on current
831           // bisect information
832           I(false);
833           break;
834         }
835     }
836 
837   basic_io::printer pr;
838   pr.print_stanza(st);
839   data dat(pr.buf, origin::internal);
840 
841   write_data(bisect_path, dat);
842 }
843 
844 void
remove_bisect_info()845 workspace::remove_bisect_info()
846 {
847   bookkeeping_path bisect_path;
848   get_bisect_path(bisect_path);
849   delete_file(bisect_path);
850 }
851 
852 // local dump file
853 
854 void
get_local_dump_path(bookkeeping_path & d_path)855 workspace::get_local_dump_path(bookkeeping_path & d_path)
856 {
857   E(workspace::found, origin::user, F("workspace required but not found"));
858 
859   d_path = bookkeeping_root / local_dump_file_name;
860   L(FL("local dump path is %s") % d_path);
861 }
862 
863 // inodeprint file
864 
865 bool
in_inodeprints_mode()866 workspace::in_inodeprints_mode()
867 {
868   bookkeeping_path ip_path;
869   get_inodeprints_path(ip_path);
870   return file_exists(ip_path);
871 }
872 
873 void
read_inodeprints(data & dat)874 workspace::read_inodeprints(data & dat)
875 {
876   I(in_inodeprints_mode());
877   bookkeeping_path ip_path;
878   get_inodeprints_path(ip_path);
879   read_data(ip_path, dat);
880 }
881 
882 void
write_inodeprints(data const & dat)883 workspace::write_inodeprints(data const & dat)
884 {
885   I(in_inodeprints_mode());
886   bookkeeping_path ip_path;
887   get_inodeprints_path(ip_path);
888   write_data(ip_path, dat);
889 }
890 
891 void
enable_inodeprints()892 workspace::enable_inodeprints()
893 {
894   bookkeeping_path ip_path;
895   get_inodeprints_path(ip_path);
896   data dat;
897   write_data(ip_path, dat);
898 }
899 
900 void
maybe_update_inodeprints(database & db)901 workspace::maybe_update_inodeprints(database & db)
902 {
903   maybe_update_inodeprints(db, node_restriction());
904 }
905 
906 void
maybe_update_inodeprints(database & db,node_restriction const & mask)907 workspace::maybe_update_inodeprints(database & db,
908                                     node_restriction const & mask)
909 {
910   if (!in_inodeprints_mode())
911     return;
912 
913   // We update the cache only for files that are included in the
914   // restriction. The only guarantee that inodeprints mode makes is that if
915   // a file's current inodeprint matches its cached inodeprint then it has
916   // not changed. i.e. for a missing file, the cache would not be updated
917   // but the old cached value can't possibly equal the current value since
918   // the file does not exist and cannot have an inodeprint.
919 
920   inodeprint_map ipm_new;
921   temp_node_id_source nis;
922   roster_t new_roster;
923 
924   get_current_roster_shape(db, nis, new_roster);
925   update_current_roster_from_filesystem(new_roster, mask);
926 
927   parent_map parents;
928   get_parent_rosters(db, parents);
929 
930   node_map const & new_nodes = new_roster.all_nodes();
931   for (node_map::const_iterator i = new_nodes.begin(); i != new_nodes.end(); ++i)
932     {
933       node_id nid = i->first;
934 
935       if (!mask.includes(new_roster, nid))
936         continue;
937 
938       if (!is_file_t(i->second))
939         continue;
940       file_t new_file = downcast_to_file_t(i->second);
941       bool all_same = true;
942 
943       for (parent_map::const_iterator parent = parents.begin();
944            parent != parents.end(); ++parent)
945         {
946           roster_t const & parent_ros = parent_roster(parent);
947           if (parent_ros.has_node(nid))
948             {
949               const_node_t old_node = parent_ros.get_node(nid);
950               I(is_file_t(old_node));
951               const_file_t old_file = downcast_to_file_t(old_node);
952 
953               if (new_file->content != old_file->content)
954                 {
955                   all_same = false;
956                   break;
957                 }
958             }
959         }
960 
961       if (all_same)
962         {
963           file_path fp;
964           new_roster.get_name(nid, fp);
965           hexenc<inodeprint> ip;
966           if (inodeprint_file(fp, ip))
967             ipm_new.insert(inodeprint_entry(fp, ip));
968         }
969     }
970   data dat;
971   write_inodeprint_map(ipm_new, dat);
972   write_inodeprints(dat);
973 }
974 
975 bool
ignore_file(file_path const & path)976 workspace::ignore_file(file_path const & path)
977 {
978   return lua.hook_ignore_file(path);
979 }
980 
981 bool
operator ()(file_path const & f) const982 ignored_file::operator()(file_path const & f) const
983 {
984   return work.ignore_file(f);
985 }
986 
987 void
init_attributes(file_path const & path,editable_roster_base & er)988 workspace::init_attributes(file_path const & path, editable_roster_base & er)
989 {
990   map<string, string> attrs;
991   lua.hook_init_attributes(path, attrs);
992   if (!attrs.empty())
993     for (map<string, string>::const_iterator i = attrs.begin();
994          i != attrs.end(); ++i)
995       er.set_attr(path, attr_key(i->first, origin::user),
996                   attr_value(i->second, origin::user));
997 }
998 
999 // objects and routines for manipulating the workspace itself
1000 namespace {
1001 
1002 struct file_itemizer : public tree_walker
1003 {
1004   database & db;
1005   workspace & work;
1006   set<file_path> & known;
1007   set<file_path> & unknown;
1008   set<file_path> & ignored;
1009   path_restriction const & mask;
1010   bool const recurse;
file_itemizer__anonf0f3fe300111::file_itemizer1011   file_itemizer(database & db, workspace & work,
1012                 set<file_path> & k,
1013                 set<file_path> & u,
1014                 set<file_path> & i,
1015                 path_restriction const & r,
1016                 bool recurse)
1017     : db(db), work(work), known(k), unknown(u), ignored(i), mask(r), recurse(recurse) {}
1018   virtual bool visit_dir(file_path const & path);
1019   virtual void visit_file(file_path const & path);
1020 };
1021 
1022 
1023 bool
visit_dir(file_path const & path)1024 file_itemizer::visit_dir(file_path const & path)
1025 {
1026   this->visit_file(path);
1027   // Don't recurse into ignored directories, even for 'ls ignored'.
1028   return recurse && ignored.find(path) == ignored.end();
1029 }
1030 
1031 void
visit_file(file_path const & path)1032 file_itemizer::visit_file(file_path const & path)
1033 {
1034   if (mask.includes(path) && known.find(path) == known.end())
1035     {
1036       if (work.ignore_file(path) || db.is_dbfile(path))
1037         ignored.insert(path);
1038       else
1039         unknown.insert(path);
1040     }
1041 }
1042 
1043 
1044 struct workspace_itemizer : public tree_walker
1045 {
1046   roster_t & roster;
1047   set<file_path> const & known;
1048   node_id_source & nis;
1049 
1050   workspace_itemizer(roster_t & roster, set<file_path> const & paths,
1051                      node_id_source & nis);
1052   virtual bool visit_dir(file_path const & path);
1053   virtual void visit_file(file_path const & path);
1054 };
1055 
workspace_itemizer(roster_t & roster,set<file_path> const & paths,node_id_source & nis)1056 workspace_itemizer::workspace_itemizer(roster_t & roster,
1057                                        set<file_path> const & paths,
1058                                        node_id_source & nis)
1059     : roster(roster), known(paths), nis(nis)
1060 {
1061   node_id root_nid = roster.create_dir_node(nis);
1062   roster.attach_node(root_nid, file_path_internal(""));
1063 }
1064 
1065 bool
visit_dir(file_path const & path)1066 workspace_itemizer::visit_dir(file_path const & path)
1067 {
1068   node_id nid = roster.create_dir_node(nis);
1069   roster.attach_node(nid, path);
1070   return known.find(path) != known.end();
1071 }
1072 
1073 void
visit_file(file_path const & path)1074 workspace_itemizer::visit_file(file_path const & path)
1075 {
1076   file_id fid;
1077   node_id nid = roster.create_file_node(fid, nis);
1078   roster.attach_node(nid, path);
1079 }
1080 
1081 
1082 class
1083 addition_builder
1084   : public tree_walker
1085 {
1086   database & db;
1087   workspace & work;
1088   roster_t & ros;
1089   editable_roster_base & er;
1090   bool respect_ignore;
1091   bool recursive;
1092 public:
addition_builder(database & db,workspace & work,roster_t & r,editable_roster_base & e,bool i,bool rec)1093   addition_builder(database & db, workspace & work,
1094                    roster_t & r, editable_roster_base & e,
1095                    bool i, bool rec)
1096     : db(db), work(work), ros(r), er(e), respect_ignore(i), recursive(rec)
1097   {}
1098   virtual bool visit_dir(file_path const & path);
1099   virtual void visit_file(file_path const & path);
1100   void add_nodes_for(file_path const & path, file_path const & goal);
1101 };
1102 
1103 void
add_nodes_for(file_path const & path,file_path const & goal)1104 addition_builder::add_nodes_for(file_path const & path,
1105                                 file_path const & goal)
1106 {
1107   // this check suffices to terminate the recursion; our caller guarantees
1108   // that the roster has a root node, which will be a directory.
1109   if (ros.has_node(path))
1110     {
1111       E(is_dir_t(ros.get_node(path)), origin::user,
1112         F("cannot add '%s', because '%s' is recorded as a file "
1113           "in the workspace manifest") % goal % path);
1114       return;
1115     }
1116 
1117   add_nodes_for(path.dirname(), goal);
1118   P(F("adding '%s' to workspace manifest") % path);
1119 
1120   node_id nid = the_null_node;
1121   switch (get_path_status(path))
1122     {
1123     case path::nonexistent:
1124       return;
1125     case path::file:
1126       {
1127         file_id ident;
1128         I(ident_existing_file(path, ident));
1129         nid = er.create_file_node(ident);
1130       }
1131       break;
1132     case path::directory:
1133       nid = er.create_dir_node();
1134       break;
1135     }
1136 
1137   I(nid != the_null_node);
1138   er.attach_node(nid, path);
1139 
1140   work.init_attributes(path, er);
1141 }
1142 
1143 
1144 bool
visit_dir(file_path const & path)1145 addition_builder::visit_dir(file_path const & path)
1146 {
1147   this->visit_file(path);
1148   // when --recursive, don't recurse into ignored dirs (it would just waste time)
1149   // when --no-recursive, this result is ignored (see workspace::perform_additions)
1150   return !work.ignore_file(path);
1151 }
1152 
1153 void
visit_file(file_path const & path)1154 addition_builder::visit_file(file_path const & path)
1155 {
1156   if ((respect_ignore && work.ignore_file(path)) || db.is_dbfile(path))
1157     {
1158       P(F("skipping ignorable file '%s'") % path);
1159       return;
1160     }
1161 
1162   if (ros.has_node(path))
1163     {
1164       if (!path.empty())
1165         P(F("skipping '%s', already accounted for in workspace") % path);
1166       return;
1167     }
1168 
1169   I(ros.has_root());
1170   add_nodes_for(path, path);
1171 }
1172 
1173 struct editable_working_tree : public editable_tree
1174 {
editable_working_tree__anonf0f3fe300111::editable_working_tree1175   editable_working_tree(workspace & work, lua_hooks & lua,
1176                         content_merge_adaptor const & source,
1177                         bool const messages)
1178     : work(work), lua(lua), source(source), next_nid(1),
1179       root_dir_attached(true), messages(messages)
1180   {};
1181 
1182   virtual node_id detach_node(file_path const & src);
1183   virtual void drop_detached_node(node_id nid);
1184 
1185   virtual node_id create_dir_node();
1186   virtual node_id create_file_node(file_id const & content);
1187   virtual void attach_node(node_id nid, file_path const & dst);
1188 
1189   virtual void apply_delta(file_path const & pth,
1190                            file_id const & old_id,
1191                            file_id const & new_id);
1192   virtual void clear_attr(file_path const & path,
1193                           attr_key const & key);
1194   virtual void set_attr(file_path const & path,
1195                         attr_key const & key,
1196                         attr_value const & val);
1197 
1198   virtual void commit();
1199 
1200   virtual ~editable_working_tree();
1201 private:
1202   workspace & work;
1203   lua_hooks & lua;
1204   content_merge_adaptor const & source;
1205   node_id next_nid;
1206   std::map<bookkeeping_path, file_path> rename_add_drop_map;
1207   bool root_dir_attached;
1208   bool messages;
1209 };
1210 
1211 
1212 struct simulated_working_tree : public editable_tree
1213 {
1214   roster_t & workspace;
1215   node_id_source & nis;
1216 
1217   set<file_path> blocked_paths;
1218   set<file_path> conflicting_paths;
1219   int conflicts;
1220   map<node_id, file_path> nid_map;
1221 
simulated_working_tree__anonf0f3fe300111::simulated_working_tree1222   simulated_working_tree(roster_t & r, temp_node_id_source & n)
1223     : workspace(r), nis(n), conflicts(0) {}
1224 
1225   virtual node_id detach_node(file_path const & src);
1226   virtual void drop_detached_node(node_id nid);
1227 
1228   virtual node_id create_dir_node();
1229   virtual node_id create_file_node(file_id const & content);
1230   virtual void attach_node(node_id nid, file_path const & dst);
1231 
1232   virtual void apply_delta(file_path const & pth,
1233                            file_id const & old_id,
1234                            file_id const & new_id);
1235   virtual void clear_attr(file_path const & path,
1236                           attr_key const & key);
1237   virtual void set_attr(file_path const & path,
1238                         attr_key const & key,
1239                         attr_value const & val);
1240 
1241   virtual void commit();
1242 
has_conflicting_paths__anonf0f3fe300111::simulated_working_tree1243   virtual bool has_conflicting_paths() const { return conflicting_paths.size() > 0; }
get_conflicting_paths__anonf0f3fe300111::simulated_working_tree1244   virtual set<file_path> get_conflicting_paths() const { return conflicting_paths; }
1245 
1246   virtual ~simulated_working_tree();
1247 };
1248 
1249 
1250 // editable_working_tree implementation
1251 
1252 static inline bookkeeping_path
path_for_detached_nids()1253 path_for_detached_nids()
1254 {
1255   return bookkeeping_root / "detached";
1256 }
1257 
1258 static inline bookkeeping_path
path_for_detached_nid(node_id nid)1259 path_for_detached_nid(node_id nid)
1260 {
1261   return path_for_detached_nids() / path_component(lexical_cast<string>(nid),
1262                                                    origin::internal);
1263 }
1264 
1265 // Attaching/detaching the root directory:
1266 //   This is tricky, because we don't want to simply move it around, like
1267 // other directories.  That would require some very snazzy handling of the
1268 // _MTN directory, and never be possible on windows anyway[1].  So, what we do
1269 // is fake it -- whenever we want to move the root directory into the
1270 // temporary dir, we instead create a new dir in the temporary dir, move
1271 // all of the root's contents into this new dir, and make a note that the root
1272 // directory is logically non-existent.  Whenever we want to move some
1273 // directory out of the temporary dir and onto the root directory, we instead
1274 // check that the root is logically nonexistent, move its contents, and note
1275 // that it exists again.
1276 //
1277 // [1] Because the root directory is our working directory, and thus locked in
1278 // place.  We _could_ chdir out, then move _MTN out, then move the real root
1279 // directory into our newly-moved _MTN, etc., but aside from being very finicky,
1280 // this would require that we know our root directory's name relative to its
1281 // parent.
1282 
1283 node_id
detach_node(file_path const & src_pth)1284 editable_working_tree::detach_node(file_path const & src_pth)
1285 {
1286   I(root_dir_attached);
1287   node_id nid = next_nid++;
1288   bookkeeping_path dst_pth = path_for_detached_nid(nid);
1289   safe_insert(rename_add_drop_map, make_pair(dst_pth, src_pth));
1290   if (src_pth == file_path())
1291     {
1292       // root dir detach, so we move contents, rather than the dir itself
1293       mkdir_p(dst_pth);
1294 
1295       vector<file_path> files, dirs;
1296       fill_path_vec<file_path> fill_files(src_pth, files, false);
1297       fill_path_vec<file_path> fill_dirs(src_pth, dirs, true);
1298       read_directory(src_pth, fill_files, fill_dirs);
1299 
1300       for (vector<file_path>::const_iterator i = files.begin();
1301            i != files.end(); ++i)
1302         move_file(*i, dst_pth / (*i).basename());
1303       for (vector<file_path>::const_iterator i = dirs.begin();
1304            i != dirs.end(); ++i)
1305         move_dir(*i, dst_pth / (*i).basename());
1306 
1307       root_dir_attached = false;
1308     }
1309   else
1310     move_path(src_pth, dst_pth);
1311   return nid;
1312 }
1313 
1314 void
drop_detached_node(node_id nid)1315 editable_working_tree::drop_detached_node(node_id nid)
1316 {
1317   bookkeeping_path pth = path_for_detached_nid(nid);
1318   map<bookkeeping_path, file_path>::const_iterator i
1319     = rename_add_drop_map.find(pth);
1320   I(i != rename_add_drop_map.end());
1321   P(F("dropping '%s'") % i->second);
1322   safe_erase(rename_add_drop_map, pth);
1323   delete_file_or_dir_shallow(pth);
1324 }
1325 
1326 node_id
create_dir_node()1327 editable_working_tree::create_dir_node()
1328 {
1329   node_id nid = next_nid++;
1330   bookkeeping_path pth = path_for_detached_nid(nid);
1331   require_path_is_nonexistent(pth,
1332                               F("path '%s' already exists") % pth);
1333   mkdir_p(pth);
1334   return nid;
1335 }
1336 
1337 node_id
create_file_node(file_id const & content)1338 editable_working_tree::create_file_node(file_id const & content)
1339 {
1340   node_id nid = next_nid++;
1341   bookkeeping_path pth = path_for_detached_nid(nid);
1342   require_path_is_nonexistent(pth,
1343                               F("path '%s' already exists") % pth);
1344   file_data dat;
1345   source.get_version(content, dat);
1346   write_data(pth, dat.inner());
1347 
1348   return nid;
1349 }
1350 
1351 void
attach_node(node_id nid,file_path const & dst_pth)1352 editable_working_tree::attach_node(node_id nid, file_path const & dst_pth)
1353 {
1354   bookkeeping_path src_pth = path_for_detached_nid(nid);
1355 
1356   map<bookkeeping_path, file_path>::const_iterator i
1357     = rename_add_drop_map.find(src_pth);
1358   if (i != rename_add_drop_map.end())
1359     {
1360       if (messages)
1361         P(F("renaming '%s' to '%s'") % i->second % dst_pth);
1362       safe_erase(rename_add_drop_map, src_pth);
1363     }
1364   else if (messages)
1365      P(F("adding '%s'") % dst_pth);
1366 
1367   if (dst_pth == file_path())
1368     {
1369       // root dir attach, so we move contents, rather than the dir itself
1370       vector<bookkeeping_path> files, dirs;
1371       fill_path_vec<bookkeeping_path> fill_files(src_pth, files, false);
1372       fill_path_vec<bookkeeping_path> fill_dirs(src_pth, dirs, true);
1373       read_directory(src_pth, fill_files, fill_dirs);
1374 
1375       for (vector<bookkeeping_path>::const_iterator i = files.begin();
1376            i != files.end(); ++i)
1377         move_file(*i, dst_pth / (*i).basename());
1378       for (vector<bookkeeping_path>::const_iterator i = dirs.begin();
1379            i != dirs.end(); ++i)
1380         move_dir(*i, dst_pth / (*i).basename());
1381 
1382       delete_dir_shallow(src_pth);
1383       root_dir_attached = true;
1384     }
1385   else
1386     // This will complain if the move is actually impossible
1387     move_path(src_pth, dst_pth);
1388 }
1389 
1390 void
apply_delta(file_path const & pth,file_id const & old_id,file_id const & new_id)1391 editable_working_tree::apply_delta(file_path const & pth,
1392                                    file_id const & old_id,
1393                                    file_id const & new_id)
1394 {
1395   require_path_is_file(pth,
1396                        F("file '%s' does not exist") % pth,
1397                        F("file '%s' is a directory") % pth);
1398   file_id curr_id;
1399   calculate_ident(pth, curr_id);
1400   E(curr_id == old_id, origin::system,
1401     F("content of file '%s' has changed, not overwriting") % pth);
1402   P(F("updating '%s'") % pth);
1403 
1404   file_data dat;
1405   source.get_version(new_id, dat);
1406   write_data(pth, dat.inner());
1407 }
1408 
1409 void
clear_attr(file_path const & path,attr_key const & key)1410 editable_working_tree::clear_attr(file_path const & path,
1411                                   attr_key const & key)
1412 {
1413   L(FL("calling hook to clear attribute %s on %s") % key % path);
1414   lua.hook_clear_attribute(key(), path);
1415 }
1416 
1417 void
set_attr(file_path const & path,attr_key const & key,attr_value const & value)1418 editable_working_tree::set_attr(file_path const & path,
1419                                 attr_key const & key,
1420                                 attr_value const & value)
1421 {
1422   L(FL("calling hook to set attribute %s on %s to %s") % key % path % value);
1423   lua.hook_set_attribute(key(), path, value());
1424 }
1425 
1426 void
commit()1427 editable_working_tree::commit()
1428 {
1429   I(rename_add_drop_map.empty());
1430   I(root_dir_attached);
1431 }
1432 
~editable_working_tree()1433 editable_working_tree::~editable_working_tree()
1434 {
1435 }
1436 
1437 
1438 node_id
detach_node(file_path const & src)1439 simulated_working_tree::detach_node(file_path const & src)
1440 {
1441   node_id nid = workspace.detach_node(src);
1442   nid_map.insert(make_pair(nid, src));
1443   return nid;
1444 }
1445 
1446 void
drop_detached_node(node_id nid)1447 simulated_working_tree::drop_detached_node(node_id nid)
1448 {
1449   const_node_t node = workspace.get_node(nid);
1450   if (is_dir_t(node))
1451     {
1452       const_dir_t dir = downcast_to_dir_t(node);
1453       if (!dir->children.empty())
1454         {
1455           map<node_id, file_path>::const_iterator i = nid_map.find(nid);
1456           I(i != nid_map.end());
1457           W(F("cannot drop non-empty directory '%s'") % i->second);
1458           conflicts++;
1459           for (dir_map::const_iterator j = dir->children.begin();
1460                j != dir->children.end(); ++j)
1461             conflicting_paths.insert(i->second / j->first);
1462         }
1463     }
1464 }
1465 
1466 node_id
create_dir_node()1467 simulated_working_tree::create_dir_node()
1468 {
1469   return workspace.create_dir_node(nis);
1470 }
1471 
1472 node_id
create_file_node(file_id const & content)1473 simulated_working_tree::create_file_node(file_id const & content)
1474 {
1475   return workspace.create_file_node(content, nis);
1476 }
1477 
1478 void
attach_node(node_id nid,file_path const & dst)1479 simulated_working_tree::attach_node(node_id nid, file_path const & dst)
1480 {
1481   // this check is needed for checkout because we're using a roster to
1482   // represent paths that *may* block the checkout. however to represent
1483   // these we *must* have a root node in the roster which will *always*
1484   // block us. so here we check for that case and avoid it.
1485   if (dst.empty() && workspace.has_root())
1486     return;
1487 
1488   if (workspace.has_node(dst))
1489     {
1490       W(F("attach node %d blocked by unversioned path '%s'") % nid % dst);
1491       blocked_paths.insert(dst);
1492       conflicting_paths.insert(dst);
1493       conflicts++;
1494     }
1495   else if (dst.empty())
1496     {
1497       // the parent of the workspace root cannot be in the blocked set
1498       // this attach would have been caught above if it were a problem
1499       workspace.attach_node(nid, dst);
1500     }
1501   else
1502     {
1503       file_path parent = dst.dirname();
1504 
1505       if (blocked_paths.find(parent) == blocked_paths.end())
1506         workspace.attach_node(nid, dst);
1507       else
1508         {
1509           W(F("attach node %d blocked by blocked parent '%s'")
1510             % nid % parent);
1511           blocked_paths.insert(dst);
1512         }
1513     }
1514 }
1515 
1516 void
apply_delta(file_path const & path,file_id const & old_id,file_id const & new_id)1517 simulated_working_tree::apply_delta(file_path const & path,
1518                                     file_id const & old_id,
1519                                     file_id const & new_id)
1520 {
1521   // this may fail if path is not a file but that will be caught
1522   // earlier in update_current_roster_from_filesystem
1523 }
1524 
1525 void
clear_attr(file_path const & path,attr_key const & key)1526 simulated_working_tree::clear_attr(file_path const & path,
1527                                    attr_key const & key)
1528 {
1529 }
1530 
1531 void
set_attr(file_path const & path,attr_key const & key,attr_value const & val)1532 simulated_working_tree::set_attr(file_path const & path,
1533                                  attr_key const & key,
1534                                  attr_value const & val)
1535 {
1536 }
1537 
1538 void
commit()1539 simulated_working_tree::commit()
1540 {
1541   // This used to error out on any conflicts, but now some can be resolved
1542   // (by --move-conflicting-paths), so we just warn. The non-resolved
1543   // conflicts generate other errors downstream.
1544   if (conflicts > 0)
1545     F("%d workspace conflicts") % conflicts;
1546 }
1547 
~simulated_working_tree()1548 simulated_working_tree::~simulated_working_tree()
1549 {
1550 }
1551 
1552 
1553 }; // anonymous namespace
1554 
1555 static void
move_conflicting_paths_into_bookkeeping(set<file_path> const & leftover_paths)1556 move_conflicting_paths_into_bookkeeping(set<file_path> const & leftover_paths)
1557 {
1558   I(leftover_paths.size() > 0);
1559 
1560   // There is some concern that this fixed bookkeeping path will cause
1561   // problems, if a user forgets to clean up, and then does something that
1562   // involves the same name again. However, I can't think of a reasonable
1563   // use case that does that, so I can't think of a reasonable solution. One
1564   // solution is to generate a random directory name, another is to use the
1565   // current time in some format to generate a directory name.
1566   //
1567   // now().as_iso_8601_extended doesn't work on Windows, because it has
1568   // colons in it.
1569   //
1570   // Random or time based directory names significantly complicate testing,
1571   // since you can't predict the directory name.
1572   //
1573   // If this turns out to be a problem, a modification of
1574   // now().as_iso_8601_extended to eliminate the colons, or some appropriate
1575   // format for now().as_formatted_localtime would be simple and
1576   // probably adequate.
1577   bookkeeping_path leftover_path = bookkeeping_resolutions_dir;
1578 
1579   mkdir_p(leftover_path);
1580 
1581   for (set<file_path>::const_iterator i = leftover_paths.begin();
1582         i != leftover_paths.end(); ++i)
1583     {
1584       L(FL("processing %s") % *i);
1585 
1586       file_path basedir = (*i).dirname();
1587       if (!basedir.empty())
1588         mkdir_p(leftover_path / basedir);
1589 
1590       bookkeeping_path new_path = leftover_path / *i;
1591       if (directory_exists(*i))
1592         move_dir(*i, new_path);
1593       else if (file_exists(*i))
1594         move_file(*i, new_path);
1595       else
1596         I(false);
1597 
1598       P(F("moved conflicting path '%s' to '%s'") % *i % new_path);
1599     }
1600 }
1601 
1602 static void
add_parent_dirs(database & db,node_id_source & nis,workspace & work,file_path const & dst,roster_t & ros)1603 add_parent_dirs(database & db, node_id_source & nis, workspace & work,
1604                 file_path const & dst, roster_t & ros)
1605 {
1606   editable_roster_base er(ros, nis);
1607   addition_builder build(db, work, ros, er, false, true);
1608 
1609   // FIXME: this is a somewhat odd way to use the builder
1610   build.visit_dir(dst.dirname());
1611 }
1612 
1613 // updating rosters from the workspace
1614 
1615 void
update_current_roster_from_filesystem(roster_t & ros)1616 workspace::update_current_roster_from_filesystem(roster_t & ros)
1617 {
1618   update_current_roster_from_filesystem(ros, node_restriction());
1619 }
1620 
1621 void
update_current_roster_from_filesystem(roster_t & ros,node_restriction const & mask)1622 workspace::update_current_roster_from_filesystem(roster_t & ros,
1623                                                  node_restriction const & mask)
1624 {
1625   temp_node_id_source nis;
1626   inodeprint_map ipm;
1627 
1628   if (in_inodeprints_mode())
1629     {
1630       data dat;
1631       read_inodeprints(dat);
1632       read_inodeprint_map(dat, ipm);
1633     }
1634 
1635   size_t missing_items = 0;
1636 
1637   // this code is speed critical, hence the use of inode fingerprints so be
1638   // careful when making changes in here and preferably do some timing tests
1639 
1640   if (!ros.has_root())
1641     return;
1642 
1643   node_map const & nodes = ros.all_nodes();
1644   for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
1645     {
1646       node_id nid = i->first;
1647       node_t node = i->second;
1648 
1649       // Only analyze restriction-included files and dirs
1650       if (!mask.includes(ros, nid))
1651         continue;
1652 
1653       file_path fp;
1654       ros.get_name(nid, fp);
1655 
1656       const path::status status(get_path_status(fp));
1657 
1658       if (is_dir_t(node))
1659         {
1660           if (status == path::nonexistent)
1661             {
1662               W(F("missing directory '%s'") % (fp));
1663               missing_items++;
1664             }
1665           else if (status != path::directory)
1666             {
1667               W(F("not a directory '%s'") % (fp));
1668               missing_items++;
1669             }
1670         }
1671       else
1672         {
1673           // Only analyze changed files (or all files if inodeprints mode
1674           // is disabled).
1675           if (inodeprint_unchanged(ipm, fp))
1676             continue;
1677 
1678           if (status == path::nonexistent)
1679             {
1680               W(F("missing file '%s'") % (fp));
1681               missing_items++;
1682             }
1683           else if (status != path::file)
1684             {
1685               W(F("not a file '%s'") % (fp));
1686               missing_items++;
1687             }
1688 
1689           file_id fid;
1690           ident_existing_file(fp, fid, status);
1691           file_t file = downcast_to_file_t(node);
1692           if (file->content != fid)
1693             {
1694               ros.unshare(node);
1695               downcast_to_file_t(node)->content = fid;
1696             }
1697         }
1698 
1699     }
1700 
1701   E(missing_items == 0, origin::user,
1702     F("%d missing items; use '%s ls missing' to view.\n"
1703       "To restore consistency, on each missing item run either\n"
1704       " '%s drop ITEM' to remove it permanently, or\n"
1705       " '%s revert ITEM' to restore it.\n"
1706       "To handle all at once, simply use\n"
1707       " '%s drop --missing' or\n"
1708       " '%s revert --missing'")
1709     % missing_items % prog_name % prog_name % prog_name
1710     % prog_name % prog_name);
1711 }
1712 
1713 void
find_missing(roster_t const & new_roster_shape,node_restriction const & mask,set<file_path> & missing)1714 workspace::find_missing(roster_t const & new_roster_shape,
1715                         node_restriction const & mask,
1716                         set<file_path> & missing)
1717 {
1718   node_map const & nodes = new_roster_shape.all_nodes();
1719   for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
1720     {
1721       node_id nid = i->first;
1722 
1723       if (!new_roster_shape.is_root(nid)
1724           && mask.includes(new_roster_shape, nid))
1725         {
1726           file_path fp;
1727           new_roster_shape.get_name(nid, fp);
1728           if (!path_exists(fp))
1729             missing.insert(fp);
1730         }
1731     }
1732 }
1733 
1734 void
find_unknown_and_ignored(database & db,path_restriction const & mask,bool recurse,vector<file_path> const & roots,set<file_path> & unknown,set<file_path> & ignored)1735 workspace::find_unknown_and_ignored(database & db,
1736                                     path_restriction const & mask,
1737                                     bool recurse,
1738                                     vector<file_path> const & roots,
1739                                     set<file_path> & unknown,
1740                                     set<file_path> & ignored)
1741 {
1742   set<file_path> known;
1743   roster_t new_roster;
1744   temp_node_id_source nis;
1745 
1746   get_current_roster_shape(db, nis, new_roster);
1747   new_roster.extract_path_set(known);
1748 
1749   file_itemizer u(db, *this, known, unknown, ignored, mask, recurse);
1750   for (vector<file_path>::const_iterator
1751          i = roots.begin(); i != roots.end(); ++i)
1752     {
1753       walk_tree(*i, u);
1754     }
1755 }
1756 
1757 void
perform_additions(database & db,set<file_path> const & paths,bool recursive,bool respect_ignore)1758 workspace::perform_additions(database & db, set<file_path> const & paths,
1759                              bool recursive, bool respect_ignore)
1760 {
1761   if (paths.empty())
1762     return;
1763 
1764   temp_node_id_source nis;
1765   roster_t new_roster;
1766   MM(new_roster);
1767   get_current_roster_shape(db, nis, new_roster);
1768 
1769   editable_roster_base er(new_roster, nis);
1770 
1771   if (!new_roster.has_root())
1772     {
1773       er.attach_node(er.create_dir_node(), file_path_internal(""));
1774     }
1775 
1776   I(new_roster.has_root());
1777   addition_builder build(db, *this, new_roster, er, respect_ignore, recursive);
1778 
1779   for (set<file_path>::const_iterator i = paths.begin(); i != paths.end(); ++i)
1780     {
1781       if (recursive)
1782         {
1783           // NB.: walk_tree will handle error checking for non-existent paths
1784           walk_tree(*i, build);
1785         }
1786       else
1787         {
1788           // in the case where we're just handed a set of paths, we use the
1789           // builder in this strange way.
1790           switch (get_path_status(*i))
1791             {
1792             case path::nonexistent:
1793               E(false, origin::user,
1794                 F("no such file or directory: '%s'") % *i);
1795               break;
1796             case path::file:
1797               build.visit_file(*i);
1798               break;
1799             case path::directory:
1800               build.visit_dir(*i);
1801               break;
1802             }
1803         }
1804     }
1805 
1806   parent_map parents;
1807   get_parent_rosters(db, parents);
1808 
1809   revision_t new_work;
1810   make_revision_for_workspace(parents, new_roster, new_work);
1811   put_work_rev(new_work);
1812 }
1813 
1814 static bool
in_parent_roster(const parent_map & parents,const node_id & nid)1815 in_parent_roster(const parent_map & parents, const node_id & nid)
1816 {
1817   for (parent_map::const_iterator i = parents.begin();
1818        i != parents.end();
1819        i++)
1820     {
1821       if (parent_roster(i).has_node(nid))
1822         return true;
1823     }
1824 
1825   return false;
1826 }
1827 
1828 void
perform_deletions(database & db,set<file_path> const & paths,bool recursive,bool bookkeep_only)1829 workspace::perform_deletions(database & db,
1830                              set<file_path> const & paths,
1831                              bool recursive, bool bookkeep_only)
1832 {
1833   if (paths.empty())
1834     return;
1835 
1836   temp_node_id_source nis;
1837   roster_t new_roster;
1838   MM(new_roster);
1839   get_current_roster_shape(db, nis, new_roster);
1840 
1841   parent_map parents;
1842   get_parent_rosters(db, parents);
1843 
1844   // we traverse the the paths backwards, so that we always hit deep paths
1845   // before shallow paths (because set<file_path> is lexicographically
1846   // sorted).  this is important in cases like
1847   //    monotone drop foo/bar foo foo/baz
1848   // where, when processing 'foo', we need to know whether or not it is empty
1849   // (and thus legal to remove)
1850 
1851   deque<file_path> todo;
1852   set<file_path>::const_reverse_iterator i = paths.rbegin();
1853   todo.push_back(*i);
1854   ++i;
1855 
1856   while (todo.size())
1857     {
1858       file_path const & name(todo.front());
1859 
1860       E(!name.empty(), origin::user,
1861         F("unable to drop the root directory"));
1862 
1863       if (!new_roster.has_node(name))
1864         P(F("skipping '%s', not currently tracked") % name);
1865       else
1866         {
1867           const_node_t n = new_roster.get_node(name);
1868           if (is_dir_t(n))
1869             {
1870               const_dir_t d = downcast_to_dir_t(n);
1871               if (!d->children.empty())
1872                 {
1873                   E(recursive, origin::user,
1874                     F("cannot remove '%s/', it is not empty") % name);
1875                   for (dir_map::const_iterator j = d->children.begin();
1876                        j != d->children.end(); ++j)
1877                     todo.push_front(name / j->first);
1878                   continue;
1879                 }
1880             }
1881           if (!bookkeep_only && path_exists(name)
1882               && in_parent_roster(parents, n->self))
1883             {
1884               if (is_dir_t(n))
1885                 {
1886                   if (directory_empty(name))
1887                     delete_file_or_dir_shallow(name);
1888                   else
1889                     W(F("directory '%s' not empty - "
1890                         "it will be dropped but not deleted") % name);
1891                 }
1892               else
1893                 {
1894                   const_file_t file = downcast_to_file_t(n);
1895                   file_id fid;
1896                   I(ident_existing_file(name, fid));
1897                   if (file->content == fid)
1898                     delete_file_or_dir_shallow(name);
1899                   else
1900                     W(F("file '%s' changed - "
1901                         "it will be dropped but not deleted") % name);
1902                 }
1903             }
1904           P(F("dropping '%s' from workspace manifest") % name);
1905           new_roster.drop_detached_node(new_roster.detach_node(name));
1906         }
1907       todo.pop_front();
1908       if (i != paths.rend())
1909         {
1910           todo.push_back(*i);
1911           ++i;
1912         }
1913     }
1914 
1915   revision_t new_work;
1916   make_revision_for_workspace(parents, new_roster, new_work);
1917   put_work_rev(new_work);
1918 }
1919 
1920 void
perform_rename(database & db,set<file_path> const & srcs,file_path const & dst,bool bookkeep_only)1921 workspace::perform_rename(database & db,
1922                           set<file_path> const & srcs,
1923                           file_path const & dst,
1924                           bool bookkeep_only)
1925 {
1926   temp_node_id_source nis;
1927   roster_t new_roster;
1928   MM(new_roster);
1929   set< pair<file_path, file_path> > renames;
1930 
1931   I(!srcs.empty());
1932 
1933   get_current_roster_shape(db, nis, new_roster);
1934 
1935   // validation.  it's okay if the target exists as a file; we just won't
1936   // clobber it (in !--bookkeep-only mode).  similarly, it's okay if the
1937   // source does not exist as a file.
1938   if (srcs.size() == 1 && !new_roster.has_node(dst))
1939     {
1940       // "rename SRC DST", DST is a file
1941       file_path const & src = *srcs.begin();
1942       file_path dpath = dst;
1943 
1944       E(!src.empty(), origin::user,
1945         F("cannot rename the workspace root (try '%s pivot_root' instead)")
1946         % prog_name);
1947       E(new_roster.has_node(src), origin::user,
1948         F("source file '%s' is not versioned") % src);
1949 
1950       if (src == dst || dst.is_beneath_of(src))
1951         {
1952           if (get_path_status(dst) == path::directory)
1953             W(F("cannot move '%s' to a subdirectory of itself, '%s/%s'") % src % dst % src);
1954           else
1955             W(F("'%s' and '%s' are the same file") % src % dst);
1956         }
1957       else
1958         {
1959           //this allows the 'magic add' of a non-versioned directory to happen in
1960           //all cases.  previously, mtn mv fileA dir/ woudl fail if dir/ wasn't
1961           //versioned whereas mtn mv fileA dir/fileA would add dir/ if necessary
1962           //and then reparent fileA.
1963           //
1964           //Note that we checked above that dst is not a directory
1965 
1966           //this handles the case where:
1967           // touch foo
1968           // mtn mv foo bar/foo where bar doesn't exist
1969           file_path parent = dst.dirname();
1970           E(get_path_status(parent) == path::directory, origin::user,
1971             F("destination path's parent directory '%s/' doesn't exist") % parent);
1972 
1973           renames.insert(make_pair(src, dpath));
1974           add_parent_dirs(db, nis, *this, dpath, new_roster);
1975         }
1976     }
1977   else
1978     {
1979       // Either srcs has more than one element, or dst is an existing
1980       // directory (or both). So we have one of:
1981       //
1982       // 1) rename SRC1 [SRC2 ...] DSTDIR
1983       //
1984       // 2) mv foo bar
1985       //    mtn mv --bookkeep-only foo bar
1986 
1987       E(get_path_status(dst) == path::directory, origin::user,
1988         F("destination '%s/' is not a directory") % dst);
1989 
1990       for (set<file_path>::const_iterator i = srcs.begin();
1991            i != srcs.end(); i++)
1992         {
1993           E(!i->empty(), origin::user,
1994             F("cannot rename the workspace root (try '%s pivot_root' instead)")
1995             % prog_name);
1996           E(new_roster.has_node(*i), origin::user,
1997             F("source file '%s' is not versioned") % *i);
1998 
1999           file_path d = dst / i->basename();
2000           if (bookkeep_only &&
2001               srcs.size() == 1 &&
2002               get_path_status(*srcs.begin()) == path::directory &&
2003               get_path_status(dst) == path::directory)
2004             {
2005               // case 2)
2006               d = dst;
2007             }
2008           else
2009             {
2010               // case 1)
2011               d = dst / i->basename();
2012 
2013               E(!new_roster.has_node(d), origin::user,
2014                 F("destination '%s' already exists in the workspace manifest") % d);
2015             }
2016 
2017           if (*i == dst || dst.is_beneath_of(*i))
2018             {
2019               W(F("cannot move '%s' to a subdirectory of itself, '%s/%s'")
2020                 % *i % dst % *i);
2021             }
2022           else
2023             {
2024               renames.insert(make_pair(*i, d));
2025 
2026               add_parent_dirs(db, nis, *this, d, new_roster);
2027             }
2028         }
2029     }
2030 
2031   // do the attach/detaching
2032   for (set< pair<file_path, file_path> >::const_iterator i = renames.begin();
2033        i != renames.end(); i++)
2034     {
2035       node_id nid = new_roster.detach_node(i->first);
2036       new_roster.attach_node(nid, i->second);
2037       P(F("renaming '%s' to '%s' in workspace manifest") % i->first % i->second);
2038     }
2039 
2040   parent_map parents;
2041   get_parent_rosters(db, parents);
2042 
2043   revision_t new_work;
2044   make_revision_for_workspace(parents, new_roster, new_work);
2045   put_work_rev(new_work);
2046 
2047   if (!bookkeep_only)
2048     for (set< pair<file_path, file_path> >::const_iterator i = renames.begin();
2049          i != renames.end(); i++)
2050       {
2051         file_path const & s(i->first);
2052         file_path const & d(i->second);
2053         // silently skip files where src doesn't exist or dst does
2054         bool have_src = path_exists(s);
2055         bool have_dst = path_exists(d);
2056         if (have_src && !have_dst)
2057           {
2058             move_path(s, d);
2059           }
2060         else if (!have_src && !have_dst)
2061           {
2062             W(F("'%s' doesn't exist in workspace, skipping") % s);
2063           }
2064         else if (have_src && have_dst)
2065           {
2066             W(F("destination '%s' already exists in workspace, "
2067                 "skipping filesystem rename") % d);
2068           }
2069         else
2070           {
2071             W(F("'%s' doesn't exist in workspace and '%s' does, "
2072                 "skipping filesystem rename") % s % d);
2073           }
2074       }
2075 }
2076 
2077 void
perform_pivot_root(database & db,file_path const & new_root,file_path const & put_old,bool bookkeep_only,bool move_conflicting_paths)2078 workspace::perform_pivot_root(database & db,
2079                               file_path const & new_root,
2080                               file_path const & put_old,
2081                               bool bookkeep_only,
2082                               bool move_conflicting_paths)
2083 {
2084   temp_node_id_source nis;
2085   roster_t old_roster, new_roster;
2086   MM(old_roster);
2087   MM(new_roster);
2088   get_current_roster_shape(db, nis, old_roster);
2089 
2090   I(old_roster.has_root());
2091   E(old_roster.has_node(new_root), origin::user,
2092     F("proposed new root directory '%s' is not versioned or does not exist")
2093     % new_root);
2094   E(is_dir_t(old_roster.get_node(new_root)), origin::user,
2095     F("proposed new root directory '%s' is not a directory") % new_root);
2096   {
2097     E(!old_roster.has_node(new_root / bookkeeping_root_component), origin::user,
2098       F("proposed new root directory '%s' contains illegal path '%s'")
2099       % new_root % bookkeeping_root);
2100   }
2101 
2102   {
2103     file_path current_path_to_put_old = (new_root / put_old);
2104     file_path current_path_to_put_old_parent
2105       = current_path_to_put_old.dirname();
2106 
2107     E(old_roster.has_node(current_path_to_put_old_parent), origin::user,
2108       F("directory '%s' is not versioned or does not exist")
2109       % current_path_to_put_old_parent);
2110     E(is_dir_t(old_roster.get_node(current_path_to_put_old_parent)),
2111       origin::user,
2112       F("'%s' is not a directory")
2113       % current_path_to_put_old_parent);
2114     E(!old_roster.has_node(current_path_to_put_old),
2115       origin::user,
2116       F("'%s' is in the way") % current_path_to_put_old);
2117   }
2118 
2119   cset cs;
2120   safe_insert(cs.nodes_renamed, make_pair(file_path_internal(""), put_old));
2121   safe_insert(cs.nodes_renamed, make_pair(new_root, file_path_internal("")));
2122 
2123   {
2124     new_roster = old_roster;
2125     editable_roster_base e(new_roster, nis);
2126     cs.apply_to(e);
2127   }
2128 
2129   {
2130     parent_map parents;
2131     get_parent_rosters(db, parents);
2132 
2133     revision_t new_work;
2134     make_revision_for_workspace(parents, new_roster, new_work);
2135     put_work_rev(new_work);
2136   }
2137   if (!bookkeep_only)
2138     {
2139       content_merge_empty_adaptor cmea;
2140       perform_content_update(old_roster, new_roster, cs, cmea, true, move_conflicting_paths);
2141     }
2142 }
2143 
2144 void
perform_content_update(roster_t const & old_roster,roster_t const & new_roster,cset const & update,content_merge_adaptor const & ca,bool const messages,bool const move_conflicting_paths)2145 workspace::perform_content_update(roster_t const & old_roster,
2146                                   roster_t const & new_roster,
2147                                   cset const & update,
2148                                   content_merge_adaptor const & ca,
2149                                   bool const messages,
2150                                   bool const move_conflicting_paths)
2151 {
2152   roster_t test_roster;
2153   temp_node_id_source nis;
2154   set<file_path> known;
2155   bookkeeping_path detached = path_for_detached_nids();
2156   bool moved_conflicting = false;
2157 
2158   E(!directory_exists(detached), origin::user,
2159     F("workspace is locked\n"
2160       "you must clean up and remove the %s directory")
2161     % detached);
2162 
2163   old_roster.extract_path_set(known);
2164 
2165   workspace_itemizer itemizer(test_roster, known, nis);
2166   walk_tree(file_path(), itemizer);
2167 
2168   simulated_working_tree swt(test_roster, nis);
2169   update.apply_to(swt);
2170 
2171   // if we have found paths during the test-run which will conflict with
2172   // newly attached or to-be-dropped nodes, move these paths out of the way
2173   // into _MTN while keeping the path to these paths intact in case the user
2174   // wants them back
2175   if (swt.has_conflicting_paths())
2176     {
2177       E(move_conflicting_paths, origin::user,
2178         F("re-run this command with '--move-conflicting-paths' to move "
2179           "conflicting paths out of the way"));
2180       move_conflicting_paths_into_bookkeeping(swt.get_conflicting_paths());
2181       moved_conflicting = true;
2182     }
2183 
2184   mkdir_p(detached);
2185 
2186   editable_working_tree ewt(*this, this->lua, ca, messages);
2187   update.apply_to(ewt);
2188 
2189   // attributes on updated files must be reset because apply_delta writes
2190   // new versions of files to _MTN/tmp and then renames them over top of the
2191   // old versions and doesn't reset attributes (mtn:execute).
2192 
2193   for (map<file_path, pair<file_id, file_id> >::const_iterator
2194          i = update.deltas_applied.begin(); i != update.deltas_applied.end();
2195        ++i)
2196     {
2197       const_node_t node = new_roster.get_node(i->first);
2198       for (attr_map_t::const_iterator a = node->attrs.begin();
2199            a != node->attrs.end(); ++a)
2200         {
2201           if (a->second.first)
2202             this->lua.hook_set_attribute(a->first(), i->first,
2203                                          a->second.second());
2204         }
2205     }
2206 
2207   delete_dir_shallow(detached);
2208 
2209   if (moved_conflicting)
2210     P(F("moved some conflicting files into '%s'") % bookkeeping_resolutions_dir);
2211 }
2212 
2213 // Local Variables:
2214 // mode: C++
2215 // fill-column: 76
2216 // c-file-style: "gnu"
2217 // indent-tabs-mode: nil
2218 // End:
2219 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
2220