monotone

monotone Mtn Source Tree

Root/src/work.cc

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
38using std::deque;
39using std::exception;
40using std::make_pair;
41using std::map;
42using std::pair;
43using std::set;
44using std::string;
45using std::vector;
46
47using boost::lexical_cast;
48
49// workspace / book-keeping file code
50
51static char const inodeprints_file_name[] = "inodeprints";
52static char const local_dump_file_name[] = "debug";
53static char const options_file_name[] = "options";
54static char const user_log_file_name[] = "log";
55static char const commit_file_name[] = "commit";
56static char const revision_file_name[] = "revision";
57static char const update_file_name[] = "update";
58static char const bisect_file_name[] = "bisect";
59
60static void
61get_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
67static void
68get_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
74static void
75get_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
81static void
82get_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
88static void
89get_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
95static void
96get_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
102static void
103get_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
109static void
110get_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
118bool
119directory_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
126bool workspace::found;
127bool workspace::used;
128bool workspace::branch_is_sticky;
129
130void
131workspace::require_workspace()
132{
133 E(workspace::found, origin::user,
134 F("workspace required but not found"));
135 workspace::used = true;
136}
137
138void
139workspace::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
146void
147workspace::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.
193workspace::workspace(app_state & app)
194 : lua(app.lua)
195{
196 require_workspace();
197}
198
199workspace::workspace(app_state & app, i18n_format const & explanation)
200 : lua(app.lua)
201{
202 require_workspace(explanation);
203}
204
205workspace::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
214void
215workspace::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
237void
238workspace::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
252void
253workspace::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
269void
270workspace::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
282static void
283get_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
304void
305workspace::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
318void
319workspace::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
335void
336workspace::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
360bool
361workspace::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
382void
383workspace::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
396void
397workspace::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
407void
408workspace::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
416bool
417workspace::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
426void
427workspace::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
440void
441workspace::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
451void
452workspace::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
461static void
462read_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
528static void
529write_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
564void
565workspace::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
609void
610workspace::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
619void
620workspace::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
630void
631workspace::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
713void
714workspace::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
738namespace syms
739{
740 symbol const start("start");
741 symbol const good("good");
742 symbol const bad("bad");
743 symbol const skipped("skipped");
744};
745
746void
747workspace::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
800void
801workspace::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
844void
845workspace::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
854void
855workspace::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
865bool
866workspace::in_inodeprints_mode()
867{
868 bookkeeping_path ip_path;
869 get_inodeprints_path(ip_path);
870 return file_exists(ip_path);
871}
872
873void
874workspace::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
882void
883workspace::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
891void
892workspace::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
900void
901workspace::maybe_update_inodeprints(database & db)
902{
903 maybe_update_inodeprints(db, node_restriction());
904}
905
906void
907workspace::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
975bool
976workspace::ignore_file(file_path const & path)
977{
978 return lua.hook_ignore_file(path);
979}
980
981bool
982ignored_file::operator()(file_path const & f) const
983{
984 return work.ignore_file(f);
985}
986
987void
988workspace::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
1000namespace {
1001
1002struct 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;
1011 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
1023bool
1024file_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
1031void
1032file_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
1044struct 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
1056workspace_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
1065bool
1066workspace_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
1073void
1074workspace_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
1082class
1083addition_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;
1092public:
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
1103void
1104addition_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
1144bool
1145addition_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
1153void
1154addition_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
1173struct editable_working_tree : public editable_tree
1174{
1175 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();
1201private:
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
1212struct 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
1222 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
1243 virtual bool has_conflicting_paths() const { return conflicting_paths.size() > 0; }
1244 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
1252static inline bookkeeping_path
1253path_for_detached_nids()
1254{
1255 return bookkeeping_root / "detached";
1256}
1257
1258static inline bookkeeping_path
1259path_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
1283node_id
1284editable_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
1314void
1315editable_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
1326node_id
1327editable_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
1337node_id
1338editable_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
1351void
1352editable_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
1390void
1391editable_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
1409void
1410editable_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
1417void
1418editable_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
1426void
1427editable_working_tree::commit()
1428{
1429 I(rename_add_drop_map.empty());
1430 I(root_dir_attached);
1431}
1432
1433editable_working_tree::~editable_working_tree()
1434{
1435}
1436
1437
1438node_id
1439simulated_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
1446void
1447simulated_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
1466node_id
1467simulated_working_tree::create_dir_node()
1468{
1469 return workspace.create_dir_node(nis);
1470}
1471
1472node_id
1473simulated_working_tree::create_file_node(file_id const & content)
1474{
1475 return workspace.create_file_node(content, nis);
1476}
1477
1478void
1479simulated_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
1516void
1517simulated_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
1525void
1526simulated_working_tree::clear_attr(file_path const & path,
1527 attr_key const & key)
1528{
1529}
1530
1531void
1532simulated_working_tree::set_attr(file_path const & path,
1533 attr_key const & key,
1534 attr_value const & val)
1535{
1536}
1537
1538void
1539simulated_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
1548simulated_working_tree::~simulated_working_tree()
1549{
1550}
1551
1552
1553}; // anonymous namespace
1554
1555static void
1556move_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
1602static void
1603add_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
1615void
1616workspace::update_current_roster_from_filesystem(roster_t & ros)
1617{
1618 update_current_roster_from_filesystem(ros, node_restriction());
1619}
1620
1621void
1622workspace::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
1713void
1714workspace::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
1734void
1735workspace::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
1757void
1758workspace::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
1814static bool
1815in_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
1828void
1829workspace::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
1920void
1921workspace::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
2077void
2078workspace::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
2144void
2145workspace::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:

Archive Download this file

Branches

Tags

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