monotone

monotone Mtn Source Tree

Root/work.cc

1// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
2// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
3// all rights reserved.
4// licensed to the public under the terms of the GNU GPL (>= 2)
5// see the file COPYING for details
6
7#include <sstream>
8#include <cstdio>
9#include <cstring>
10#include <cerrno>
11
12#include "app_state.hh"
13#include "basic_io.hh"
14#include "change_set.hh"
15#include "file_io.hh"
16#include "sanity.hh"
17#include "transforms.hh"
18#include "vocab.hh"
19#include "work.hh"
20
21// working copy / book-keeping file code
22
23using namespace std;
24
25// attribute map file
26
27string const attr_file_name(".mt-attrs");
28
29void
30file_itemizer::visit_file(file_path const & path)
31{
32 if (app.restriction_includes(path) && known.find(path) == known.end())
33 {
34 if (app.lua.hook_ignore_file(path))
35 ignored.insert(path);
36 else
37 unknown.insert(path);
38 }
39}
40
41class
42addition_builder
43 : public tree_walker
44{
45 app_state & app;
46 change_set::path_rearrangement & pr;
47 path_set ps;
48 attr_map & am_attrs;
49public:
50 addition_builder(app_state & a,
51 change_set::path_rearrangement & pr,
52 path_set & p,
53 attr_map & am)
54 : app(a), pr(pr), ps(p), am_attrs(am)
55 {}
56 virtual void visit_file(file_path const & path);
57};
58
59void
60addition_builder::visit_file(file_path const & path)
61{
62 if (app.lua.hook_ignore_file(path))
63 {
64 P(F("skipping ignorable file %s\n") % path);
65 return;
66 }
67
68 if (ps.find(path) != ps.end())
69 {
70 P(F("skipping %s, already accounted for in working copy\n") % path);
71 return;
72 }
73
74 P(F("adding %s to working copy add set\n") % path);
75 ps.insert(path);
76 pr.added_files.insert(path);
77
78 map<string, string> attrs;
79 app.lua.hook_init_attributes(path, attrs);
80 if (attrs.size() > 0)
81 am_attrs[path] = attrs;
82}
83
84void
85build_additions(vector<file_path> const & paths,
86 manifest_map const & man,
87 app_state & app,
88 change_set::path_rearrangement & pr)
89{
90 change_set::path_rearrangement pr_new, pr_concatenated;
91 change_set cs_new;
92
93 path_set ps;
94 attr_map am_attrs;
95 extract_path_set(man, ps);
96 apply_path_rearrangement(pr, ps);
97
98 addition_builder build(app, pr_new, ps, am_attrs);
99
100 for (vector<file_path>::const_iterator i = paths.begin(); i != paths.end(); ++i)
101 // NB.: walk_tree will handle error checking for non-existent paths
102 walk_tree(*i, build);
103
104 if (am_attrs.size () > 0)
105 {
106 // add .mt-attrs to manifest if not already registered
107 file_path attr_path;
108 get_attr_path(attr_path);
109
110 if ((man.find(attr_path) == man.end ()) &&
111 (pr_new.added_files.find(attr_path) == pr_new.added_files.end()))
112 {
113 P(F("registering %s file in working copy\n") % attr_path);
114 pr.added_files.insert(attr_path);
115 }
116
117 // read attribute map if available
118 data attr_data;
119 attr_map attrs;
120
121 if (path_exists(attr_path))
122 {
123 read_data(attr_path, attr_data);
124 read_attr_map(attr_data, attrs);
125 }
126
127 // add new attribute entries
128 for (attr_map::const_iterator i = am_attrs.begin();
129 i != am_attrs.end(); ++i)
130 {
131 map<string, string> m = i->second;
132
133 for (map<string, string>::const_iterator j = m.begin();
134 j != m.end(); ++j)
135 {
136 P(F("adding attribute '%s' on file %s to %s\n") % j->first % i->first % attr_file_name);
137 attrs[i->first][j->first] = j->second;
138 }
139 }
140
141 // write out updated map
142 write_attr_map(attr_data, attrs);
143 write_data(attr_path, attr_data);
144 }
145
146 normalize_path_rearrangement(pr_new);
147 concatenate_rearrangements(pr, pr_new, pr_concatenated);
148 pr = pr_concatenated;
149}
150
151static bool
152known_path(file_path const & p,
153 path_set const & ps,
154 bool & path_is_directory)
155{
156 std::string path_as_dir = p.as_internal() + "/";
157 for (path_set::const_iterator i = ps.begin(); i != ps.end(); ++i)
158 {
159 if (*i == p)
160 {
161 path_is_directory = false;
162 return true;
163 }
164 else if (i->as_internal().find(path_as_dir) == 0)
165 {
166 path_is_directory = true;
167 return true;
168 }
169 }
170 return false;
171}
172
173void
174build_deletions(vector<file_path> const & paths,
175 manifest_map const & man,
176 app_state & app,
177 change_set::path_rearrangement & pr)
178{
179 change_set::path_rearrangement pr_new, pr_concatenated;
180 path_set ps;
181 extract_path_set(man, ps);
182 apply_path_rearrangement(pr, ps);
183
184 // read attribute map if available
185 file_path attr_path;
186 get_attr_path(attr_path);
187
188 data attr_data;
189 attr_map attrs;
190
191 if (path_exists(attr_path))
192 {
193 read_data(attr_path, attr_data);
194 read_attr_map(attr_data, attrs);
195 }
196
197 bool updated_attr_map = false;
198
199 for (vector<file_path>::const_iterator i = paths.begin(); i != paths.end(); ++i)
200 {
201 bool dir_p = false;
202
203 N(!i->empty(), F("invalid path ''"));
204
205 if (! known_path(*i, ps, dir_p))
206 {
207 P(F("skipping %s, not currently tracked\n") % *i);
208 continue;
209 }
210
211 P(F("adding %s to working copy delete set\n") % *i);
212
213 if (dir_p)
214 {
215 E(false, F("sorry -- 'drop <directory>' is currently broken.\n"
216 "try 'find %s -type f | monotone drop -@-'\n") % (*i));
217 pr_new.deleted_dirs.insert(*i);
218 }
219 else
220 {
221 pr_new.deleted_files.insert(*i);
222
223 // delete any associated attributes for this file
224 if (1 == attrs.erase(*i))
225 {
226 updated_attr_map = true;
227 P(F("dropped attributes for file %s from %s\n") % (*i) % attr_file_name);
228 }
229 if (app.execute && path_exists(*i))
230 delete_file(*i);
231 }
232 }
233
234 normalize_path_rearrangement(pr_new);
235 concatenate_rearrangements(pr, pr_new, pr_concatenated);
236 pr = pr_concatenated;
237
238 // write out updated map if necessary
239 if (updated_attr_map)
240 {
241 write_attr_map(attr_data, attrs);
242 write_data(attr_path, attr_data);
243 }
244}
245
246void
247build_rename(file_path const & src,
248 file_path const & dst,
249 manifest_map const & man,
250 app_state & app,
251 change_set::path_rearrangement & pr)
252{
253 N(!src.empty(), F("invalid source path ''"));
254 N(!dst.empty(), F("invalid destination path ''"));
255
256 change_set::path_rearrangement pr_new, pr_concatenated;
257 path_set ps;
258 extract_path_set(man, ps);
259 apply_path_rearrangement(pr, ps);
260
261 bool src_dir_p = false;
262 bool dst_dir_p = false;
263
264 N(known_path(src, ps, src_dir_p),
265 F("%s does not exist in current revision\n") % src);
266
267 N(!known_path(dst, ps, dst_dir_p),
268 F("%s already exists in current revision\n") % dst);
269
270 P(F("adding %s -> %s to working copy rename set\n") % src % dst);
271 if (src_dir_p)
272 pr_new.renamed_dirs.insert(std::make_pair(src, dst));
273 else
274 pr_new.renamed_files.insert(std::make_pair(src, dst));
275
276 if (app.execute && (path_exists(src) || !path_exists(dst)))
277 move_path(src, dst);
278
279 // read attribute map if available
280 file_path attr_path;
281 get_attr_path(attr_path);
282
283 if (path_exists(attr_path))
284 {
285 data attr_data;
286 read_data(attr_path, attr_data);
287 attr_map attrs;
288 read_attr_map(attr_data, attrs);
289
290 // make sure there aren't pre-existing attributes that we'd accidentally
291 // pick up
292 N(attrs.find(dst) == attrs.end(),
293 F("%s has existing attributes in %s; clean them up first") % dst % attr_file_name);
294
295 // only write out a new attribute map if we find attrs to move
296 attr_map::iterator a = attrs.find(src);
297 if (a != attrs.end())
298 {
299 attrs[dst] = (*a).second;
300 attrs.erase(a);
301
302 P(F("moving attributes for %s to %s\n") % src % dst);
303
304 write_attr_map(attr_data, attrs);
305 write_data(attr_path, attr_data);
306 }
307 }
308
309 normalize_path_rearrangement(pr_new);
310 concatenate_rearrangements(pr, pr_new, pr_concatenated);
311 pr = pr_concatenated;
312}
313
314// work file containing rearrangement from uncommitted adds/drops/renames
315
316std::string const work_file_name("work");
317
318static void get_work_path(bookkeeping_path & w_path)
319{
320 w_path = bookkeeping_root / work_file_name;
321 L(F("work path is %s\n") % w_path);
322}
323
324void get_path_rearrangement(change_set::path_rearrangement & w)
325{
326 bookkeeping_path w_path;
327 get_work_path(w_path);
328 if (path_exists(w_path))
329 {
330 L(F("checking for un-committed work file %s\n") % w_path);
331 data w_data;
332 read_data(w_path, w_data);
333 read_path_rearrangement(w_data, w);
334 L(F("read rearrangement from %s\n") % w_path);
335 }
336 else
337 {
338 L(F("no un-committed work file %s\n") % w_path);
339 }
340}
341
342void remove_path_rearrangement()
343{
344 bookkeeping_path w_path;
345 get_work_path(w_path);
346 if (file_exists(w_path))
347 delete_file(w_path);
348}
349
350void put_path_rearrangement(change_set::path_rearrangement & w)
351{
352 bookkeeping_path w_path;
353 get_work_path(w_path);
354
355 if (w.empty())
356 {
357 if (file_exists(w_path))
358 delete_file(w_path);
359 }
360 else
361 {
362 data w_data;
363 write_path_rearrangement(w, w_data);
364 write_data(w_path, w_data);
365 }
366}
367
368// revision file name
369
370std::string revision_file_name("revision");
371
372static void get_revision_path(bookkeeping_path & m_path)
373{
374 m_path = bookkeeping_root / revision_file_name;
375 L(F("revision path is %s\n") % m_path);
376}
377
378void get_revision_id(revision_id & c)
379{
380 c = revision_id();
381 bookkeeping_path c_path;
382 get_revision_path(c_path);
383
384 require_path_is_file(c_path,
385 F("working copy is corrupt: %s does not exist") % c_path,
386 F("working copy is corrupt: %s is a directory") % c_path);
387
388 data c_data;
389 L(F("loading revision id from %s\n") % c_path);
390 try
391 {
392 read_data(c_path, c_data);
393 }
394 catch(std::exception & e)
395 {
396 N(false, F("Problem with working directory: %s is unreadable") % c_path);
397 }
398 c = revision_id(remove_ws(c_data()));
399}
400
401void put_revision_id(revision_id const & rev)
402{
403 bookkeeping_path c_path;
404 get_revision_path(c_path);
405 L(F("writing revision id to %s\n") % c_path);
406 data c_data(rev.inner()() + "\n");
407 write_data(c_path, c_data);
408}
409
410void
411get_base_revision(app_state & app,
412 revision_id & rid,
413 manifest_id & mid,
414 manifest_map & man)
415{
416 man.clear();
417
418 get_revision_id(rid);
419
420 if (!null_id(rid))
421 {
422
423 N(app.db.revision_exists(rid),
424 F("base revision %s does not exist in database\n") % rid);
425
426 app.db.get_revision_manifest(rid, mid);
427 L(F("old manifest is %s\n") % mid);
428
429 N(app.db.manifest_version_exists(mid),
430 F("base manifest %s does not exist in database\n") % mid);
431
432 app.db.get_manifest(mid, man);
433 }
434
435 L(F("old manifest has %d entries\n") % man.size());
436}
437
438void
439get_base_manifest(app_state & app,
440 manifest_map & man)
441{
442 revision_id rid;
443 manifest_id mid;
444 get_base_revision(app, rid, mid, man);
445}
446
447
448// user log file
449
450string const user_log_file_name("log");
451
452void
453get_user_log_path(bookkeeping_path & ul_path)
454{
455 ul_path = bookkeeping_root / user_log_file_name;
456 L(F("user log path is %s\n") % ul_path);
457}
458
459void
460read_user_log(data & dat)
461{
462 bookkeeping_path ul_path;
463 get_user_log_path(ul_path);
464
465 if (file_exists(ul_path))
466 {
467 read_data(ul_path, dat);
468 }
469}
470
471void
472write_user_log(data const & dat)
473{
474 bookkeeping_path ul_path;
475 get_user_log_path(ul_path);
476
477 write_data(ul_path, dat);
478}
479
480void
481blank_user_log()
482{
483 data empty;
484 bookkeeping_path ul_path;
485 get_user_log_path(ul_path);
486 write_data(ul_path, empty);
487}
488
489bool
490has_contents_user_log()
491{
492 data user_log_message;
493 read_user_log(user_log_message);
494 return user_log_message().length() > 0;
495}
496
497// options map file
498
499string const options_file_name("options");
500
501void
502get_options_path(bookkeeping_path & o_path)
503{
504 o_path = bookkeeping_root / options_file_name;
505 L(F("options path is %s\n") % o_path);
506}
507
508void
509read_options_map(data const & dat, options_map & options)
510{
511 std::istringstream iss(dat());
512 basic_io::input_source src(iss, "MT/options");
513 basic_io::tokenizer tok(src);
514 basic_io::parser parser(tok);
515
516 // don't clear the options which will have settings from the command line
517 // options.clear();
518
519 std::string opt, val;
520 while (parser.symp())
521 {
522 parser.sym(opt);
523 parser.str(val);
524 // options[opt] = val;
525 // use non-replacing insert verses replacing with options[opt] = val;
526 options.insert(make_pair(opt, val));
527 }
528}
529
530void
531write_options_map(data & dat, options_map const & options)
532{
533 std::ostringstream oss;
534 basic_io::printer pr(oss);
535
536 basic_io::stanza st;
537 for (options_map::const_iterator i = options.begin();
538 i != options.end(); ++i)
539 st.push_str_pair(i->first, i->second());
540
541 pr.print_stanza(st);
542 dat = oss.str();
543}
544
545// local dump file
546
547static string const local_dump_file_name("debug");
548
549void get_local_dump_path(bookkeeping_path & d_path)
550{
551 d_path = bookkeeping_root / local_dump_file_name;
552 L(F("local dump path is %s\n") % d_path);
553}
554
555// inodeprint file
556
557static string const inodeprints_file_name("inodeprints");
558
559void
560get_inodeprints_path(bookkeeping_path & ip_path)
561{
562 ip_path = bookkeeping_root / inodeprints_file_name;
563}
564
565bool
566in_inodeprints_mode()
567{
568 bookkeeping_path ip_path;
569 get_inodeprints_path(ip_path);
570 return file_exists(ip_path);
571}
572
573void
574read_inodeprints(data & dat)
575{
576 I(in_inodeprints_mode());
577 bookkeeping_path ip_path;
578 get_inodeprints_path(ip_path);
579 read_data(ip_path, dat);
580}
581
582void
583write_inodeprints(data const & dat)
584{
585 I(in_inodeprints_mode());
586 bookkeeping_path ip_path;
587 get_inodeprints_path(ip_path);
588 write_data(ip_path, dat);
589}
590
591void
592enable_inodeprints()
593{
594 bookkeeping_path ip_path;
595 get_inodeprints_path(ip_path);
596 data dat;
597 write_data(ip_path, dat);
598}
599
600void
601get_attr_path(file_path & a_path)
602{
603 a_path = file_path_internal(attr_file_name);
604 L(F("attribute map path is %s\n") % a_path);
605}
606
607namespace
608{
609 namespace syms
610 {
611 std::string const file("file");
612 }
613}
614
615void
616read_attr_map(data const & dat, attr_map & attr)
617{
618 std::istringstream iss(dat());
619 basic_io::input_source src(iss, attr_file_name);
620 basic_io::tokenizer tok(src);
621 basic_io::parser parser(tok);
622
623 std::string file, name, value;
624
625 attr.clear();
626
627 while (parser.symp(syms::file))
628 {
629 parser.sym();
630 parser.str(file);
631 file_path fp = file_path_internal(file);
632
633 while (parser.symp() &&
634 !parser.symp(syms::file))
635 {
636 parser.sym(name);
637 parser.str(value);
638 attr[fp][name] = value;
639 }
640 }
641}
642
643void
644write_attr_map(data & dat, attr_map const & attr)
645{
646 std::ostringstream oss;
647 basic_io::printer pr(oss);
648
649 for (attr_map::const_iterator i = attr.begin();
650 i != attr.end(); ++i)
651 {
652 basic_io::stanza st;
653 st.push_str_pair(syms::file, i->first.as_internal());
654
655 for (std::map<std::string, std::string>::const_iterator j = i->second.begin();
656 j != i->second.end(); ++j)
657 st.push_str_pair(j->first, j->second);
658
659 pr.print_stanza(st);
660 }
661
662 dat = oss.str();
663}
664
665
666static void
667apply_attributes(app_state & app, attr_map const & attr)
668{
669 for (attr_map::const_iterator i = attr.begin();
670 i != attr.end(); ++i)
671 for (std::map<std::string, std::string>::const_iterator j = i->second.begin();
672 j != i->second.end(); ++j)
673 app.lua.hook_apply_attribute (j->first,
674 i->first,
675 j->second);
676}
677
678string const encoding_attribute("encoding");
679string const binary_encoding("binary");
680string const default_encoding("default");
681
682string const manual_merge_attribute("manual_merge");
683
684static bool find_in_attr_map(attr_map const & attr,
685 file_path const & file,
686 std::string const & attr_key,
687 std::string & attr_val)
688{
689 attr_map::const_iterator f = attr.find(file);
690 if (f == attr.end())
691 return false;
692
693 std::map<std::string, std::string>::const_iterator a = f->second.find(attr_key);
694 if (a == f->second.end())
695 return false;
696
697 attr_val = a->second;
698 return true;
699}
700
701bool get_attribute_from_db(file_path const & file,
702 std::string const & attr_key,
703 manifest_map const & man,
704 std::string & attr_val,
705 app_state & app)
706{
707 file_path fp;
708 get_attr_path(fp);
709 manifest_map::const_iterator i = man.find(fp);
710 if (i == man.end())
711 return false;
712
713 file_id fid = manifest_entry_id(i);
714 if (!app.db.file_version_exists(fid))
715 return false;
716
717 file_data attr_data;
718 app.db.get_file_version(fid, attr_data);
719
720 attr_map attr;
721 read_attr_map(data(attr_data.inner()()), attr);
722
723 return find_in_attr_map(attr, file, attr_key, attr_val);
724}
725
726bool get_attribute_from_working_copy(file_path const & file,
727 std::string const & attr_key,
728 std::string & attr_val)
729{
730 file_path fp;
731 get_attr_path(fp);
732 if (!file_exists(fp))
733 return false;
734
735 data attr_data;
736 read_data(fp, attr_data);
737
738 attr_map attr;
739 read_attr_map(attr_data, attr);
740
741 return find_in_attr_map(attr, file, attr_key, attr_val);
742}
743
744void update_any_attrs(app_state & app)
745{
746 file_path fp;
747 data attr_data;
748 attr_map attr;
749
750 get_attr_path(fp);
751 if (!file_exists(fp))
752 return;
753
754 read_data(fp, attr_data);
755 read_attr_map(attr_data, attr);
756 apply_attributes(app, attr);
757}

Archive Download this file

Branches

Tags

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