1
*** added file 'bzrlib/patches.py'
5
+# Copyright (C) 2004, 2005 Aaron Bentley
6
+# <aaron.bentley@utoronto.ca>
8
+# This program is free software; you can redistribute it and/or modify
9
+# it under the terms of the GNU General Public License as published by
10
+# the Free Software Foundation; either version 2 of the License, or
11
+# (at your option) any later version.
13
+# This program is distributed in the hope that it will be useful,
14
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+# GNU General Public License for more details.
18
+# You should have received a copy of the GNU General Public License
19
+# along with this program; if not, write to the Free Software
20
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
+class PatchSyntax(Exception):
24
+ def __init__(self, msg):
25
+ Exception.__init__(self, msg)
28
+class MalformedPatchHeader(PatchSyntax):
29
+ def __init__(self, desc, line):
32
+ msg = "Malformed patch header. %s\n%s" % (self.desc, self.line)
33
+ PatchSyntax.__init__(self, msg)
35
+class MalformedHunkHeader(PatchSyntax):
36
+ def __init__(self, desc, line):
39
+ msg = "Malformed hunk header. %s\n%s" % (self.desc, self.line)
40
+ PatchSyntax.__init__(self, msg)
42
+class MalformedLine(PatchSyntax):
43
+ def __init__(self, desc, line):
46
+ msg = "Malformed line. %s\n%s" % (self.desc, self.line)
47
+ PatchSyntax.__init__(self, msg)
49
+def get_patch_names(iter_lines):
51
+ line = iter_lines.next()
52
+ if not line.startswith("--- "):
53
+ raise MalformedPatchHeader("No orig name", line)
55
+ orig_name = line[4:].rstrip("\n")
56
+ except StopIteration:
57
+ raise MalformedPatchHeader("No orig line", "")
59
+ line = iter_lines.next()
60
+ if not line.startswith("+++ "):
61
+ raise PatchSyntax("No mod name")
63
+ mod_name = line[4:].rstrip("\n")
64
+ except StopIteration:
65
+ raise MalformedPatchHeader("No mod line", "")
66
+ return (orig_name, mod_name)
68
+def parse_range(textrange):
69
+ """Parse a patch range, handling the "1" special-case
71
+ :param textrange: The text to parse
72
+ :type textrange: str
73
+ :return: the position and range, as a tuple
76
+ tmp = textrange.split(',')
87
+def hunk_from_header(line):
88
+ if not line.startswith("@@") or not line.endswith("@@\n") \
89
+ or not len(line) > 4:
90
+ raise MalformedHunkHeader("Does not start and end with @@.", line)
92
+ (orig, mod) = line[3:-4].split(" ")
93
+ except Exception, e:
94
+ raise MalformedHunkHeader(str(e), line)
95
+ if not orig.startswith('-') or not mod.startswith('+'):
96
+ raise MalformedHunkHeader("Positions don't start with + or -.", line)
98
+ (orig_pos, orig_range) = parse_range(orig[1:])
99
+ (mod_pos, mod_range) = parse_range(mod[1:])
100
+ except Exception, e:
101
+ raise MalformedHunkHeader(str(e), line)
102
+ if mod_range < 0 or orig_range < 0:
103
+ raise MalformedHunkHeader("Hunk range is negative", line)
104
+ return Hunk(orig_pos, orig_range, mod_pos, mod_range)
108
+ def __init__(self, contents):
109
+ self.contents = contents
111
+ def get_str(self, leadchar):
112
+ if self.contents == "\n" and leadchar == " " and False:
114
+ return leadchar + self.contents
116
+class ContextLine(HunkLine):
117
+ def __init__(self, contents):
118
+ HunkLine.__init__(self, contents)
121
+ return self.get_str(" ")
124
+class InsertLine(HunkLine):
125
+ def __init__(self, contents):
126
+ HunkLine.__init__(self, contents)
129
+ return self.get_str("+")
132
+class RemoveLine(HunkLine):
133
+ def __init__(self, contents):
134
+ HunkLine.__init__(self, contents)
137
+ return self.get_str("-")
139
+__pychecker__="no-returnvalues"
140
+def parse_line(line):
141
+ if line.startswith("\n"):
142
+ return ContextLine(line)
143
+ elif line.startswith(" "):
144
+ return ContextLine(line[1:])
145
+ elif line.startswith("+"):
146
+ return InsertLine(line[1:])
147
+ elif line.startswith("-"):
148
+ return RemoveLine(line[1:])
150
+ raise MalformedLine("Unknown line type", line)
155
+ def __init__(self, orig_pos, orig_range, mod_pos, mod_range):
156
+ self.orig_pos = orig_pos
157
+ self.orig_range = orig_range
158
+ self.mod_pos = mod_pos
159
+ self.mod_range = mod_range
162
+ def get_header(self):
163
+ return "@@ -%s +%s @@\n" % (self.range_str(self.orig_pos,
165
+ self.range_str(self.mod_pos,
168
+ def range_str(self, pos, range):
169
+ """Return a file range, special-casing for 1-line files.
171
+ :param pos: The position in the file
173
+ :range: The range in the file
175
+ :return: a string in the format 1,4 except when range == pos == 1
180
+ return "%i,%i" % (pos, range)
183
+ lines = [self.get_header()]
184
+ for line in self.lines:
185
+ lines.append(str(line))
186
+ return "".join(lines)
188
+ def shift_to_mod(self, pos):
189
+ if pos < self.orig_pos-1:
191
+ elif pos > self.orig_pos+self.orig_range:
192
+ return self.mod_range - self.orig_range
194
+ return self.shift_to_mod_lines(pos)
196
+ def shift_to_mod_lines(self, pos):
197
+ assert (pos >= self.orig_pos-1 and pos <= self.orig_pos+self.orig_range)
198
+ position = self.orig_pos-1
200
+ for line in self.lines:
201
+ if isinstance(line, InsertLine):
203
+ elif isinstance(line, RemoveLine):
204
+ if position == pos:
208
+ elif isinstance(line, ContextLine):
214
+def iter_hunks(iter_lines):
216
+ for line in iter_lines:
217
+ if line.startswith("@@"):
218
+ if hunk is not None:
220
+ hunk = hunk_from_header(line)
222
+ hunk.lines.append(parse_line(line))
224
+ if hunk is not None:
228
+ def __init__(self, oldname, newname):
229
+ self.oldname = oldname
230
+ self.newname = newname
234
+ ret = "--- %s\n+++ %s\n" % (self.oldname, self.newname)
235
+ ret += "".join([str(h) for h in self.hunks])
238
+ def stats_str(self):
239
+ """Return a string of patch statistics"""
242
+ for hunk in self.hunks:
243
+ for line in hunk.lines:
244
+ if isinstance(line, InsertLine):
246
+ elif isinstance(line, RemoveLine):
248
+ return "%i inserts, %i removes in %i hunks" % \
249
+ (inserts, removes, len(self.hunks))
251
+ def pos_in_mod(self, position):
253
+ for hunk in self.hunks:
254
+ shift = hunk.shift_to_mod(position)
260
+ def iter_inserted(self):
261
+ """Iteraties through inserted lines
263
+ :return: Pair of line number, line
264
+ :rtype: iterator of (int, InsertLine)
266
+ for hunk in self.hunks:
267
+ pos = hunk.mod_pos - 1;
268
+ for line in hunk.lines:
269
+ if isinstance(line, InsertLine):
272
+ if isinstance(line, ContextLine):
275
+def parse_patch(iter_lines):
276
+ (orig_name, mod_name) = get_patch_names(iter_lines)
277
+ patch = Patch(orig_name, mod_name)
278
+ for hunk in iter_hunks(iter_lines):
279
+ patch.hunks.append(hunk)
284
+ """A line associated with the log that produced it"""
285
+ def __init__(self, text, log=None):
289
+class CantGetRevisionData(Exception):
290
+ def __init__(self, revision):
291
+ Exception.__init__(self, "Can't get data for revision %s" % revision)
293
+def annotate_file2(file_lines, anno_iter):
294
+ for result in iter_annotate_file(file_lines, anno_iter):
299
+def iter_annotate_file(file_lines, anno_iter):
300
+ lines = [AnnotateLine(f) for f in file_lines]
303
+ for result in anno_iter:
304
+ if isinstance(result, progress.Progress):
307
+ log, iter_inserted, patch = result
308
+ for (num, line) in iter_inserted:
311
+ for cur_patch in patches:
312
+ num = cur_patch.pos_in_mod(num)
316
+ if num >= len(lines):
318
+ if num is not None and lines[num].log is None:
319
+ lines[num].log = log
320
+ patches=[patch]+patches
321
+ except CantGetRevisionData:
326
+def difference_index(atext, btext):
327
+ """Find the indext of the first character that differs betweeen two texts
329
+ :param atext: The first text
331
+ :param btext: The second text
333
+ :return: The index, or None if there are no differences within the range
334
+ :rtype: int or NoneType
336
+ length = len(atext)
337
+ if len(btext) < length:
338
+ length = len(btext)
339
+ for i in range(length):
340
+ if atext[i] != btext[i]:
347
+ class PatchesTester(unittest.TestCase):
348
+ def testValidPatchHeader(self):
349
+ """Parse a valid patch header"""
350
+ lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
351
+ (orig, mod) = get_patch_names(lines.__iter__())
352
+ assert(orig == "orig/commands.py")
353
+ assert(mod == "mod/dommands.py")
355
+ def testInvalidPatchHeader(self):
356
+ """Parse an invalid patch header"""
357
+ lines = "-- orig/commands.py\n+++ mod/dommands.py".split('\n')
358
+ self.assertRaises(MalformedPatchHeader, get_patch_names,
361
+ def testValidHunkHeader(self):
362
+ """Parse a valid hunk header"""
363
+ header = "@@ -34,11 +50,6 @@\n"
364
+ hunk = hunk_from_header(header);
365
+ assert (hunk.orig_pos == 34)
366
+ assert (hunk.orig_range == 11)
367
+ assert (hunk.mod_pos == 50)
368
+ assert (hunk.mod_range == 6)
369
+ assert (str(hunk) == header)
371
+ def testValidHunkHeader2(self):
372
+ """Parse a tricky, valid hunk header"""
373
+ header = "@@ -1 +0,0 @@\n"
374
+ hunk = hunk_from_header(header);
375
+ assert (hunk.orig_pos == 1)
376
+ assert (hunk.orig_range == 1)
377
+ assert (hunk.mod_pos == 0)
378
+ assert (hunk.mod_range == 0)
379
+ assert (str(hunk) == header)
381
+ def makeMalformed(self, header):
382
+ self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
384
+ def testInvalidHeader(self):
385
+ """Parse an invalid hunk header"""
386
+ self.makeMalformed(" -34,11 +50,6 \n")
387
+ self.makeMalformed("@@ +50,6 -34,11 @@\n")
388
+ self.makeMalformed("@@ -34,11 +50,6 @@")
389
+ self.makeMalformed("@@ -34.5,11 +50,6 @@\n")
390
+ self.makeMalformed("@@-34,11 +50,6@@\n")
391
+ self.makeMalformed("@@ 34,11 50,6 @@\n")
392
+ self.makeMalformed("@@ -34,11 @@\n")
393
+ self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
394
+ self.makeMalformed("@@ -34,11 +50,-6 @@\n")
396
+ def lineThing(self,text, type):
397
+ line = parse_line(text)
398
+ assert(isinstance(line, type))
399
+ assert(str(line)==text)
401
+ def makeMalformedLine(self, text):
402
+ self.assertRaises(MalformedLine, parse_line, text)
404
+ def testValidLine(self):
405
+ """Parse a valid hunk line"""
406
+ self.lineThing(" hello\n", ContextLine)
407
+ self.lineThing("+hello\n", InsertLine)
408
+ self.lineThing("-hello\n", RemoveLine)
410
+ def testMalformedLine(self):
411
+ """Parse invalid valid hunk lines"""
412
+ self.makeMalformedLine("hello\n")
414
+ def compare_parsed(self, patchtext):
415
+ lines = patchtext.splitlines(True)
416
+ patch = parse_patch(lines.__iter__())
418
+ i = difference_index(patchtext, pstr)
420
+ print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
421
+ assert (patchtext == str(patch))
424
+ """Test parsing a whole patch"""
425
+ patchtext = """--- orig/commands.py
427
+@@ -1337,7 +1337,8 @@
429
+ def set_title(self, command=None):
431
+- version = self.tree.tree_version.nonarch
433
+ version = "[no version]"
434
+ if command is None:
435
+@@ -1983,7 +1984,11 @@
437
+ if len(new_merges) > 0:
438
+ if cmdutil.prompt("Log for merge"):
439
+- mergestuff = cmdutil.log_for_merge(tree, comp_version)
440
+ log.description += mergestuff
444
+ self.compare_parsed(patchtext)
446
+ def testInit(self):
447
+ """Handle patches missing half the position, range tuple"""
449
+"""--- orig/__init__.py
452
+ __docformat__ = "restructuredtext en"
453
++__doc__ = An alternate Arch commandline interface"""
454
+ self.compare_parsed(patchtext)
458
+ def testLineLookup(self):
459
+ """Make sure we can accurately look up mod line from orig"""
460
+ patch = parse_patch(open("testdata/diff"))
461
+ orig = list(open("testdata/orig"))
462
+ mod = list(open("testdata/mod"))
464
+ for i in range(len(orig)):
465
+ mod_pos = patch.pos_in_mod(i)
466
+ if mod_pos is None:
467
+ removals.append(orig[i])
469
+ assert(mod[mod_pos]==orig[i])
470
+ rem_iter = removals.__iter__()
471
+ for hunk in patch.hunks:
472
+ for line in hunk.lines:
473
+ if isinstance(line, RemoveLine):
474
+ next = rem_iter.next()
475
+ if line.contents != next:
476
+ sys.stdout.write(" orig:%spatch:%s" % (next,
478
+ assert(line.contents == next)
479
+ self.assertRaises(StopIteration, rem_iter.next)
481
+ def testFirstLineRenumber(self):
482
+ """Make sure we handle lines at the beginning of the hunk"""
483
+ patch = parse_patch(open("testdata/insert_top.patch"))
484
+ assert (patch.pos_in_mod(0)==1)
487
+ patchesTestSuite = unittest.makeSuite(PatchesTester,'test')
488
+ runner = unittest.TextTestRunner(verbosity=0)
489
+ return runner.run(patchesTestSuite)
492
+if __name__ == "__main__":
494
+# arch-tag: d1541a25-eac5-4de9-a476-08a7cecd5683
496
*** added file 'bzrlib/progress.py'
498
+++ bzrlib/progress.py
500
+# Copyright (C) 2005 Aaron Bentley
501
+# <aaron.bentley@utoronto.ca>
503
+# This program is free software; you can redistribute it and/or modify
504
+# it under the terms of the GNU General Public License as published by
505
+# the Free Software Foundation; either version 2 of the License, or
506
+# (at your option) any later version.
508
+# This program is distributed in the hope that it will be useful,
509
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
510
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
511
+# GNU General Public License for more details.
513
+# You should have received a copy of the GNU General Public License
514
+# along with this program; if not, write to the Free Software
515
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
520
+class Progress(object):
521
+ def __init__(self, units, current, total=None):
523
+ self.current = current
526
+ def _get_percent(self):
527
+ if self.total is not None and self.current is not None:
528
+ return 100.0 * self.current / self.total
530
+ percent = property(_get_percent)
533
+ if self.total is not None:
534
+ return "%i of %i %s %.1f%%" % (self.current, self.total, self.units,
537
+ return "%i %s" (self.current, self.units)
539
+class ProgressBar(object):
540
+ def __init__(self):
542
+ object.__init__(self)
544
+ def __call__(self, progress):
545
+ if self.start is None:
546
+ self.start = datetime.datetime.now()
547
+ progress_bar(progress, start_time=self.start)
549
+def divide_timedelta(delt, divisor):
550
+ """Divides a timedelta object"""
551
+ return datetime.timedelta(float(delt.days)/divisor,
552
+ float(delt.seconds)/divisor,
553
+ float(delt.microseconds)/divisor)
555
+def str_tdelta(delt):
558
+ return str(datetime.timedelta(delt.days, delt.seconds))
560
+def get_eta(start_time, progress, enough_samples=20):
561
+ if start_time is None or progress.current == 0:
563
+ elif progress.current < enough_samples:
565
+ elapsed = datetime.datetime.now() - start_time
566
+ total_duration = divide_timedelta((elapsed) * long(progress.total),
568
+ if elapsed < total_duration:
569
+ eta = total_duration - elapsed
571
+ eta = total_duration - total_duration
574
+def progress_bar(progress, start_time=None):
575
+ eta = get_eta(start_time, progress)
576
+ if start_time is not None:
577
+ eta_str = " "+str_tdelta(eta)
581
+ fmt = " %i of %i %s (%.1f%%)"
582
+ f = fmt % (progress.total, progress.total, progress.units, 100.0)
585
+ if start_time is not None:
586
+ cols -= len(eta_str)
587
+ markers = int (float(cols) * progress.current / progress.total)
588
+ txt = fmt % (progress.current, progress.total, progress.units,
590
+ sys.stderr.write("\r[%s%s]%s%s" % ('='*markers, ' '*(cols-markers), txt,
593
+def clear_progress_bar():
594
+ sys.stderr.write('\r%s\r' % (' '*79))
596
+def spinner_str(progress, show_text=False):
598
+ Produces the string for a textual "spinner" progress indicator
599
+ :param progress: an object represinting current progress
600
+ :param show_text: If true, show progress text as well
601
+ :return: The spinner string
603
+ >>> spinner_str(Progress("baloons", 0))
605
+ >>> spinner_str(Progress("baloons", 5))
607
+ >>> spinner_str(Progress("baloons", 6), show_text=True)
610
+ positions = ('|', '/', '-', '\\')
611
+ text = positions[progress.current % 4]
613
+ text+=" %i %s" % (progress.current, progress.units)
616
+def spinner(progress, show_text=False, output=sys.stderr):
618
+ Update a spinner progress indicator on an output
619
+ :param progress: The progress to display
620
+ :param show_text: If true, show text as well as spinner
621
+ :param output: The output to write to
623
+ >>> spinner(Progress("baloons", 6), show_text=True, output=sys.stdout)
626
+ output.write('\r%s' % spinner_str(progress, show_text))
630
+ result = doctest.testmod()
633
+ print "All tests passed"
635
+ print "No tests to run"
636
+if __name__ == "__main__":
639
*** added directory 'testdata'
640
*** added file 'testdata/diff'
644
+--- orig/commands.py
651
++import pylon.errors
652
++from pylon.errors import *
653
++from pylon import errors
654
++from pylon import util
655
++from pylon import arch_core
656
++from pylon import arch_compound
657
++from pylon import ancillary
658
++from pylon import misc
659
++from pylon import paths
673
+-from errors import *
682
+ __docformat__ = "restructuredtext"
683
+ __doc__ = "Implementation of user (sub) commands"
686
+ tree=arch.tree_root()
688
+- a_spec = cmdutil.comp_revision(tree)
690
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
691
+ cmdutil.ensure_archive_registered(a_spec.archive)
693
+ changeset=options.changeset
696
+- tmpdir=cmdutil.tmpdir()
697
+ changeset=tmpdir+"/changeset"
699
+ delta=arch.iter_delta(a_spec, b_spec, changeset)
700
+@@ -304,14 +310,14 @@
703
+ if (options.perform_diff):
704
+- chan = cmdutil.ChangesetMunger(changeset)
705
+ chan.read_indices()
706
+- if isinstance(b_spec, arch.Revision):
707
+- b_dir = b_spec.library_find()
710
+- a_dir = a_spec.library_find()
711
+ if options.diffopts is not None:
712
+ diffopts = options.diffopts.split()
713
+ cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
716
+ except arch.errors.TreeRootError, e:
719
+- from_revision=cmdutil.tree_latest(tree)
720
+ if from_revision==to_revision:
721
+ print "Tree is already up to date with:\n"+str(to_revision)+"."
728
+ revision=cmdutil.determine_revision_arch(tree, options.version)
729
+ return options, revision.get_version(), args
731
+@@ -601,11 +610,16 @@
733
+ tree=arch.tree_root()
734
+ options, version, files = self.parse_commandline(cmdargs, tree)
735
+ if options.__dict__.has_key("base") and options.base:
736
+ base = cmdutil.determine_revision_tree(tree, options.base)
738
+- base = cmdutil.submit_revision(tree)
741
+ writeversion=version
742
+ archive=version.archive
743
+ source=cmdutil.get_mirror_source(archive)
744
+@@ -625,18 +639,26 @@
746
+ last_revision=tree.iter_logs(version, True).next().revision
747
+ except StopIteration, e:
748
+- if cmdutil.prompt("Import from commit"):
749
+- return do_import(version)
751
+- raise NoVersionLogs(version)
752
+- if last_revision!=version.iter_revisions(True).next():
753
+ if not cmdutil.prompt("Out of date"):
759
+- if not cmdutil.has_changed(version):
760
+ if not cmdutil.prompt("Empty commit"):
762
+ except arch.util.ExecProblem, e:
763
+@@ -645,15 +667,15 @@
767
+- log = tree.log_message(create=False)
770
+ if cmdutil.prompt("Create log"):
773
+ except cmdutil.NoEditorSpecified, e:
774
+ raise CommandFailed(e)
775
+- log = tree.log_message(create=False)
778
+ if log["Summary"] is None or len(log["Summary"].strip()) == 0:
779
+@@ -837,23 +859,24 @@
780
+ if spec is not None:
781
+ revision = cmdutil.determine_revision_tree(tree, spec)
783
+- revision = cmdutil.comp_revision(tree)
784
+ except cmdutil.CantDetermineRevision, e:
785
+ raise CommandFailedWrapper(e)
788
+ if options.file_contents or options.file_perms or options.deletions\
789
+ or options.additions or options.renames or options.hunk_prompt:
790
+- munger = cmdutil.MungeOpts()
791
+- munger.hunk_prompt = options.hunk_prompt
793
+ if len(args) > 0 or options.logs or options.pattern_files or \
796
+- munger = cmdutil.MungeOpts(True)
797
+ munger.all_types(True)
799
+- t_cwd = cmdutil.tree_cwd(tree)
804
+ if options.pattern_files:
805
+ munger.add_keep_pattern(options.pattern_files)
807
+- for line in cmdutil.revert(tree, revision, munger,
808
+ not options.no_output):
809
+ cmdutil.colorize(line)
811
+@@ -1042,18 +1065,13 @@
815
+-def require_version_exists(version, spec):
816
+- if not version.exists():
817
+- raise cmdutil.CantDetermineVersion(spec,
818
+- "The version %s does not exist." \
821
+ class Revisions(BaseCommand):
823
+ Print a revision name based on a revision specifier
825
+ def __init__(self):
826
+ self.description="Lists revisions"
828
+ def do_command(self, cmdargs):
830
+@@ -1066,224 +1084,68 @@
831
+ self.tree = arch.tree_root()
832
+ except arch.errors.TreeRootError:
835
+- iter = self.get_iterator(options.type, args, options.reverse,
837
+ except cmdutil.CantDetermineRevision, e:
838
+ raise CommandFailedWrapper(e)
840
+ if options.skip is not None:
841
+ iter = cmdutil.iter_skip(iter, int(options.skip))
843
+- for revision in iter:
845
+- if isinstance(revision, arch.Patchlog):
847
+- revision=revision.revision
848
+- print options.display(revision)
849
+- if log is None and (options.summary or options.creator or
850
+- options.date or options.merges):
851
+- log = revision.patchlog
852
+- if options.creator:
853
+- print " %s" % log.creator
855
+- print " %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
856
+- if options.summary:
857
+- print " %s" % log.summary
858
+- if options.merges:
859
+- showed_title = False
860
+- for revision in log.merged_patches:
861
+- if not showed_title:
863
+- showed_title = True
864
+- print " %s" % revision
866
+- def get_iterator(self, type, args, reverse, modified):
871
+- if modified is not None:
872
+- iter = cmdutil.modified_iter(modified, self.tree)
876
+- return cmdutil.iter_reverse(iter)
877
+- elif type == "archive":
879
+- if self.tree is None:
880
+- raise cmdutil.CantDetermineRevision("",
881
+- "Not in a project tree")
882
+- version = cmdutil.determine_version_tree(spec, self.tree)
884
+- version = cmdutil.determine_version_arch(spec, self.tree)
885
+- cmdutil.ensure_archive_registered(version.archive)
886
+- require_version_exists(version, spec)
887
+- return version.iter_revisions(reverse)
888
+- elif type == "cacherevs":
890
+- if self.tree is None:
891
+- raise cmdutil.CantDetermineRevision("",
892
+- "Not in a project tree")
893
+- version = cmdutil.determine_version_tree(spec, self.tree)
895
+- version = cmdutil.determine_version_arch(spec, self.tree)
896
+- cmdutil.ensure_archive_registered(version.archive)
897
+- require_version_exists(version, spec)
898
+- return cmdutil.iter_cacherevs(version, reverse)
899
+- elif type == "library":
901
+- if self.tree is None:
902
+- raise cmdutil.CantDetermineRevision("",
903
+- "Not in a project tree")
904
+- version = cmdutil.determine_version_tree(spec, self.tree)
906
+- version = cmdutil.determine_version_arch(spec, self.tree)
907
+- return version.iter_library_revisions(reverse)
908
+- elif type == "logs":
909
+- if self.tree is None:
910
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
911
+- return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
912
+- self.tree), reverse)
913
+- elif type == "missing" or type == "skip-present":
914
+- if self.tree is None:
915
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
916
+- skip = (type == "skip-present")
917
+- version = cmdutil.determine_version_tree(spec, self.tree)
918
+- cmdutil.ensure_archive_registered(version.archive)
919
+- require_version_exists(version, spec)
920
+- return cmdutil.iter_missing(self.tree, version, reverse,
921
+- skip_present=skip)
923
+- elif type == "present":
924
+- if self.tree is None:
925
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
926
+- version = cmdutil.determine_version_tree(spec, self.tree)
927
+- cmdutil.ensure_archive_registered(version.archive)
928
+- require_version_exists(version, spec)
929
+- return cmdutil.iter_present(self.tree, version, reverse)
931
+- elif type == "new-merges" or type == "direct-merges":
932
+- if self.tree is None:
933
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
934
+- version = cmdutil.determine_version_tree(spec, self.tree)
935
+- cmdutil.ensure_archive_registered(version.archive)
936
+- require_version_exists(version, spec)
937
+- iter = cmdutil.iter_new_merges(self.tree, version, reverse)
938
+- if type == "new-merges":
940
+- elif type == "direct-merges":
941
+- return cmdutil.direct_merges(iter)
943
+- elif type == "missing-from":
944
+- if self.tree is None:
945
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
946
+- revision = cmdutil.determine_revision_tree(self.tree, spec)
947
+- libtree = cmdutil.find_or_make_local_revision(revision)
948
+- return cmdutil.iter_missing(libtree, self.tree.tree_version,
951
+- elif type == "partner-missing":
952
+- return cmdutil.iter_partner_missing(self.tree, reverse)
954
+- elif type == "ancestry":
955
+- revision = cmdutil.determine_revision_tree(self.tree, spec)
956
+- iter = cmdutil._iter_ancestry(self.tree, revision)
960
+- return cmdutil.iter_reverse(iter)
962
+- elif type == "dependencies" or type == "non-dependencies":
963
+- nondeps = (type == "non-dependencies")
964
+- revision = cmdutil.determine_revision_tree(self.tree, spec)
965
+- anc_iter = cmdutil._iter_ancestry(self.tree, revision)
966
+- iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
968
+- return iter_depends
970
+- return cmdutil.iter_reverse(iter_depends)
971
+- elif type == "micro":
972
+- return cmdutil.iter_micro(self.tree)
976
+ def get_parser(self):
978
+ Returns the options parser to use for the "revision" command.
980
+ :rtype: cmdutil.CmdOptionParser
982
+- parser=cmdutil.CmdOptionParser("fai revisions [revision]")
983
+ select = cmdutil.OptionGroup(parser, "Selection options",
984
+ "Control which revisions are listed. These options"
985
+ " are mutually exclusive. If more than one is"
986
+ " specified, the last is used.")
987
+- select.add_option("", "--archive", action="store_const",
988
+- const="archive", dest="type", default="archive",
989
+- help="List all revisions in the archive")
990
+- select.add_option("", "--cacherevs", action="store_const",
991
+- const="cacherevs", dest="type",
992
+- help="List all revisions stored in the archive as "
993
+- "complete copies")
994
+- select.add_option("", "--logs", action="store_const",
995
+- const="logs", dest="type",
996
+- help="List revisions that have a patchlog in the "
998
+- select.add_option("", "--missing", action="store_const",
999
+- const="missing", dest="type",
1000
+- help="List revisions from the specified version that"
1001
+- " have no patchlog in the tree")
1002
+- select.add_option("", "--skip-present", action="store_const",
1003
+- const="skip-present", dest="type",
1004
+- help="List revisions from the specified version that"
1005
+- " have no patchlogs at all in the tree")
1006
+- select.add_option("", "--present", action="store_const",
1007
+- const="present", dest="type",
1008
+- help="List revisions from the specified version that"
1009
+- " have no patchlog in the tree, but can't be merged")
1010
+- select.add_option("", "--missing-from", action="store_const",
1011
+- const="missing-from", dest="type",
1012
+- help="List revisions from the specified revision "
1013
+- "that have no patchlog for the tree version")
1014
+- select.add_option("", "--partner-missing", action="store_const",
1015
+- const="partner-missing", dest="type",
1016
+- help="List revisions in partner versions that are"
1018
+- select.add_option("", "--new-merges", action="store_const",
1019
+- const="new-merges", dest="type",
1020
+- help="List revisions that have had patchlogs added"
1021
+- " to the tree since the last commit")
1022
+- select.add_option("", "--direct-merges", action="store_const",
1023
+- const="direct-merges", dest="type",
1024
+- help="List revisions that have been directly added"
1025
+- " to tree since the last commit ")
1026
+- select.add_option("", "--library", action="store_const",
1027
+- const="library", dest="type",
1028
+- help="List revisions in the revision library")
1029
+- select.add_option("", "--ancestry", action="store_const",
1030
+- const="ancestry", dest="type",
1031
+- help="List revisions that are ancestors of the "
1032
+- "current tree version")
1034
+- select.add_option("", "--dependencies", action="store_const",
1035
+- const="dependencies", dest="type",
1036
+- help="List revisions that the given revision "
1039
+- select.add_option("", "--non-dependencies", action="store_const",
1040
+- const="non-dependencies", dest="type",
1041
+- help="List revisions that the given revision "
1042
+- "does not depend on")
1044
+- select.add_option("--micro", action="store_const",
1045
+- const="micro", dest="type",
1046
+- help="List partner revisions aimed for this "
1049
+- select.add_option("", "--modified", dest="modified",
1050
+- help="List tree ancestor revisions that modified a "
1051
+- "given file", metavar="FILE[:LINE]")
1053
+ parser.add_option("", "--skip", dest="skip",
1054
+ help="Skip revisions. Positive numbers skip from "
1055
+ "beginning, negative skip from end.",
1056
+@@ -1312,6 +1174,9 @@
1057
+ format.add_option("--cacherev", action="store_const",
1058
+ const=paths.determine_cacherev_path, dest="display",
1059
+ help="Show location of cacherev file")
1060
+ parser.add_option_group(format)
1061
+ display = cmdutil.OptionGroup(parser, "Display format options",
1062
+ "These control the display of data")
1063
+@@ -1448,6 +1313,7 @@
1064
+ if os.access(self.history_file, os.R_OK) and \
1065
+ os.path.isfile(self.history_file):
1066
+ readline.read_history_file(self.history_file)
1068
+ def write_history(self):
1069
+ readline.write_history_file(self.history_file)
1070
+@@ -1470,16 +1336,21 @@
1071
+ def set_prompt(self):
1072
+ if self.tree is not None:
1074
+- version = " "+self.tree.tree_version.nonarch
1079
+- self.prompt = "Fai%s> " % version
1081
+ def set_title(self, command=None):
1083
+- version = self.tree.tree_version.nonarch
1085
+ version = "[no version]"
1086
+ if command is None:
1087
+@@ -1489,8 +1360,15 @@
1088
+ def do_cd(self, line):
1092
+- os.chdir(os.path.expanduser(line))
1093
+ except Exception, e:
1096
+@@ -1523,7 +1401,7 @@
1097
+ except cmdutil.CantDetermineRevision, e:
1099
+ except Exception, e:
1100
+- print "Unhandled error:\n%s" % cmdutil.exception_str(e)
1102
+ elif suggestions.has_key(args[0]):
1103
+ print suggestions[args[0]]
1104
+@@ -1574,7 +1452,7 @@
1105
+ arg = line.split()[-1]
1108
+- iter = iter_munged_completions(iter, arg, text)
1109
+ except Exception, e:
1112
+@@ -1604,10 +1482,11 @@
1115
+ if arg.startswith("-"):
1116
+- return list(iter_munged_completions(iter, arg, text))
1118
+- return list(iter_munged_completions(
1119
+- iter_file_completions(arg), arg, text))
1123
+@@ -1615,13 +1494,13 @@
1124
+ arg = args.split()[-1]
1127
+- iter = iter_dir_completions(arg)
1128
+- iter = iter_munged_completions(iter, arg, text)
1131
+ arg = args.split()[-1]
1132
+- return list(iter_munged_completions(iter_file_completions(arg),
1135
+ return self.completenames(text, line, begidx, endidx)
1136
+ except Exception, e:
1137
+@@ -1636,44 +1515,8 @@
1141
+-def iter_file_completions(arg, only_dirs = False):
1142
+- """Generate an iterator that iterates through filename completions.
1144
+- :param arg: The filename fragment to match
1146
+- :param only_dirs: If true, match only directories
1147
+- :type only_dirs: bool
1149
+- cwd = os.getcwd()
1151
+- extras = [".", ".."]
1154
+- (dir, file) = os.path.split(arg)
1156
+- listingdir = os.path.expanduser(dir)
1159
+- for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
1161
+- userfile = dir+'/'+file
1164
+- if userfile.startswith(arg):
1165
+- if os.path.isdir(listingdir+'/'+file):
1168
+- elif not only_dirs:
1171
+-def iter_munged_completions(iter, arg, text):
1172
+- for completion in iter:
1173
+- completion = str(completion)
1174
+- if completion.startswith(arg):
1175
+- yield completion[len(arg)-len(text):]
1177
+ def iter_source_file_completions(tree, arg):
1178
+- treepath = cmdutil.tree_cwd(tree)
1179
+ if len(treepath) > 0:
1182
+@@ -1701,7 +1544,7 @@
1183
+ :return: An iterator of all matching untagged files
1184
+ :rtype: iterator of str
1186
+- treepath = cmdutil.tree_cwd(tree)
1187
+ if len(treepath) > 0:
1190
+@@ -1743,8 +1586,8 @@
1191
+ :param arg: The prefix to match
1194
+- treepath = cmdutil.tree_cwd(tree)
1195
+- tmpdir = cmdutil.tmpdir()
1196
+ changeset = tmpdir+"/changeset"
1198
+ revision = cmdutil.determine_revision_tree(tree)
1199
+@@ -1756,14 +1599,6 @@
1200
+ shutil.rmtree(tmpdir)
1201
+ return completions
1203
+-def iter_dir_completions(arg):
1204
+- """Generate an iterator that iterates through directory name completions.
1206
+- :param arg: The directory name fragment to match
1209
+- return iter_file_completions(arg, True)
1211
+ class Shell(BaseCommand):
1212
+ def __init__(self):
1213
+ self.description = "Runs Fai as a shell"
1214
+@@ -1795,7 +1630,11 @@
1215
+ parser=self.get_parser()
1216
+ (options, args) = parser.parse_args(cmdargs)
1218
+- tree = arch.tree_root()
1220
+ if (len(args) == 0) == (options.untagged == False):
1221
+ raise cmdutil.GetHelp
1222
+@@ -1809,13 +1648,22 @@
1223
+ if options.id_type == "tagline":
1224
+ if method != "tagline":
1225
+ if not cmdutil.prompt("Tagline in other tree"):
1226
+- if method == "explicit":
1227
+- options.id_type == explicit
1229
+ print "add-id not supported for \"%s\" tagging method"\
1233
+ elif options.id_type == "explicit":
1234
+ if method != "tagline" and method != explicit:
1235
+ if not prompt("Explicit in other tree"):
1236
+@@ -1824,7 +1672,8 @@
1239
+ if options.id_type == "auto":
1240
+- if method != "tagline" and method != "explicit":
1241
+ print "add-id not supported for \"%s\" tagging method" % method
1244
+@@ -1852,10 +1701,12 @@
1245
+ previous_files.extend(files)
1246
+ if id_type == "explicit":
1247
+ cmdutil.add_id(files)
1248
+- elif id_type == "tagline":
1249
+ for file in files:
1251
+- cmdutil.add_tagline_or_explicit_id(file)
1252
+ except cmdutil.AlreadyTagged:
1253
+ print "\"%s\" already has a tagline." % file
1254
+ except cmdutil.NoCommentSyntax:
1255
+@@ -1888,6 +1739,9 @@
1256
+ parser.add_option("--tagline", action="store_const",
1257
+ const="tagline", dest="id_type",
1258
+ help="Use a tagline id")
1259
+ parser.add_option("--untagged", action="store_true",
1260
+ dest="untagged", default=False,
1261
+ help="tag all untagged files")
1262
+@@ -1926,27 +1780,7 @@
1263
+ def get_completer(self, arg, index):
1264
+ if self.tree is None:
1265
+ raise arch.errors.TreeRootError
1266
+- completions = list(ancillary.iter_partners(self.tree,
1267
+- self.tree.tree_version))
1268
+- if len(completions) == 0:
1269
+- completions = list(self.tree.iter_log_versions())
1273
+- for completion in completions:
1274
+- alias = ancillary.compact_alias(str(completion), self.tree)
1276
+- aliases.extend(alias)
1278
+- for completion in completions:
1279
+- if completion.archive == self.tree.tree_version.archive:
1280
+- aliases.append(completion.nonarch)
1282
+- except Exception, e:
1285
+- completions.extend(aliases)
1286
+- return completions
1288
+ def do_command(self, cmdargs):
1290
+@@ -1961,7 +1795,7 @@
1292
+ if self.tree is None:
1293
+ raise arch.errors.TreeRootError(os.getcwd())
1294
+- if cmdutil.has_changed(self.tree.tree_version):
1295
+ raise UncommittedChanges(self.tree)
1298
+@@ -2027,14 +1861,14 @@
1299
+ :type other_revision: `arch.Revision`
1300
+ :return: 0 if the merge was skipped, 1 if it was applied
1302
+- other_tree = cmdutil.find_or_make_local_revision(other_revision)
1304
+ if action == "native-merge":
1305
+- ancestor = cmdutil.merge_ancestor2(self.tree, other_tree,
1307
+ elif action == "update":
1308
+- ancestor = cmdutil.tree_latest(self.tree,
1309
+- other_revision.version)
1310
+ except CantDetermineRevision, e:
1311
+ raise CommandFailedWrapper(e)
1312
+ cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
1313
+@@ -2104,7 +1938,10 @@
1314
+ if self.tree is None:
1315
+ raise arch.errors.TreeRootError
1317
+- edit_log(self.tree)
1319
+ def get_parser(self):
1321
+@@ -2132,7 +1969,7 @@
1325
+-def edit_log(tree):
1326
++def edit_log(tree, version):
1327
+ """Makes and edits the log for a tree. Does all kinds of fancy things
1328
+ like log templates and merge summaries and log-for-merge
1330
+@@ -2141,28 +1978,29 @@
1332
+ #ensure we have an editor before preparing the log
1333
+ cmdutil.find_editor()
1334
+- log = tree.log_message(create=False)
1335
+ log_is_new = False
1336
+ if log is None or cmdutil.prompt("Overwrite log"):
1337
+ if log is not None:
1338
+ os.remove(log.name)
1339
+- log = tree.log_message(create=True)
1342
+- template = tree+"/{arch}/=log-template"
1343
+- if not os.path.exists(template):
1344
+- template = os.path.expanduser("~/.arch-params/=log-template")
1345
+- if not os.path.exists(template):
1348
+ shutil.copyfile(template, tmplog)
1350
+- new_merges = list(cmdutil.iter_new_merges(tree,
1351
+- tree.tree_version))
1352
+- log["Summary"] = merge_summary(new_merges, tree.tree_version)
1353
+ if len(new_merges) > 0:
1354
+ if cmdutil.prompt("Log for merge"):
1355
+- mergestuff = cmdutil.log_for_merge(tree)
1356
+ log.description += mergestuff
1359
+@@ -2172,29 +2010,6 @@
1360
+ os.remove(log.name)
1363
+-def merge_summary(new_merges, tree_version):
1364
+- if len(new_merges) == 0:
1366
+- if len(new_merges) == 1:
1367
+- summary = new_merges[0].summary
1369
+- summary = "Merge"
1372
+- for merge in new_merges:
1373
+- if arch.my_id() != merge.creator:
1374
+- name = re.sub("<.*>", "", merge.creator).rstrip(" ");
1375
+- if not name in credits:
1376
+- credits.append(name)
1378
+- version = merge.revision.version
1379
+- if version.archive == tree_version.archive:
1380
+- if not version.nonarch in credits:
1381
+- credits.append(version.nonarch)
1382
+- elif not str(version) in credits:
1383
+- credits.append(str(version))
1385
+- return ("%s (%s)") % (summary, ", ".join(credits))
1387
+ class MirrorArchive(BaseCommand):
1389
+@@ -2268,31 +2083,73 @@
1391
+ Use "alias" to list available (user and automatic) aliases."""
1395
++"The latest revision in the archive of the tree-version. You can specify \
1396
++a different version like so: acur:foo--bar--0 (aliases can be used)",
1398
++"""(tree current) The latest revision in the tree of the tree-version. \
1399
++You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
1402
++"""(tree previous) The previous revision in the tree of the tree-version. To \
1403
++specify an older revision, use a number, e.g. "tprev:4" """,
1405
++"""(tree ancestor) The ancestor revision of the tree To specify an older \
1406
++revision, use a number, e.g. "tanc:4".""",
1408
++"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
1410
++""" (tree modified) The latest revision to modify a given file, e.g. \
1411
++"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
1413
++"""(tree tag) The revision that was tagged into the current tree revision, \
1414
++according to the tree""",
1416
++"""(tag current) The latest revision of the version that the current tree \
1417
++was tagged from.""",
1419
++"""The common ancestor of the current tree and the specified revision. \
1420
++Defaults to the first partner-version's latest revision or to tagcur.""",
1424
++def is_auto_alias(name):
1428
++def display_def(iter, wrap = 80):
1432
+ def help_aliases(tree):
1433
+- print """Auto-generated aliases
1434
+- acur : The latest revision in the archive of the tree-version. You can specfy
1435
+- a different version like so: acur:foo--bar--0 (aliases can be used)
1436
+- tcur : (tree current) The latest revision in the tree of the tree-version.
1437
+- You can specify a different version like so: tcur:foo--bar--0 (aliases
1439
+-tprev : (tree previous) The previous revision in the tree of the tree-version.
1440
+- To specify an older revision, use a number, e.g. "tprev:4"
1441
+- tanc : (tree ancestor) The ancestor revision of the tree
1442
+- To specify an older revision, use a number, e.g. "tanc:4"
1443
+-tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
1444
+- tmod : (tree modified) The latest revision to modify a given file
1445
+- (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
1446
+- ttag : (tree tag) The revision that was tagged into the current tree revision,
1447
+- according to the tree.
1448
+-tagcur: (tag current) The latest revision of the version that the current tree
1450
+-mergeanc : The common ancestor of the current tree and the specified revision.
1451
+- Defaults to the first partner-version's latest revision or to tagcur.
1453
+ print "User aliases"
1454
+- for parts in ancillary.iter_all_alias(tree):
1455
+- print parts[0].rjust(10)+" : "+parts[1]
1458
+ class Inventory(BaseCommand):
1459
+ """List the status of files in the tree"""
1460
+@@ -2428,6 +2285,11 @@
1461
+ except cmdutil.ForbiddenAliasSyntax, e:
1462
+ raise CommandFailedWrapper(e)
1464
+ def arg_dispatch(self, args, options):
1465
+ """Add, modify, or list aliases, depending on number of arguments
1467
+@@ -2438,15 +2300,20 @@
1468
+ if len(args) == 0:
1469
+ help_aliases(self.tree)
1471
+- elif len(args) == 1:
1472
+- self.print_alias(args[0])
1473
+- elif (len(args)) == 2:
1474
+- self.add(args[0], args[1], options)
1476
+- raise cmdutil.GetHelp
1478
+ def print_alias(self, alias):
1480
+ for pair in ancillary.iter_all_alias(self.tree):
1481
+ if pair[0] == alias:
1483
+@@ -2464,6 +2331,8 @@
1484
+ :type expansion: str
1485
+ :param options: The commandline options
1489
+ new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion,
1490
+@@ -2490,14 +2359,17 @@
1492
+ if len(args) != 1:
1493
+ raise cmdutil.GetHelp
1495
+ for pair in self.get_iterator(options):
1496
+- if pair[0] != args[0]:
1497
+ newlist+="%s=%s\n" % (pair[0], pair[1])
1501
+- raise errors.NoSuchAlias(args[0])
1502
+ self.write_aliases(newlist, options)
1504
+ def get_alias_file(self, options):
1505
+@@ -2526,7 +2398,7 @@
1506
+ :param options: The commandline options
1508
+ filename = os.path.expanduser(self.get_alias_file(options))
1509
+- file = cmdutil.NewFileVersion(filename)
1510
+ file.write(newlist)
1513
+@@ -2588,10 +2460,13 @@
1514
+ :param cmdargs: The commandline arguments
1515
+ :type cmdargs: list of str
1517
+- cmdutil.find_editor()
1518
+ parser = self.get_parser()
1519
+ (options, args) = parser.parse_args(cmdargs)
1521
+ self.tree=arch.tree_root()
1524
+@@ -2655,7 +2530,7 @@
1525
+ target_revision = cmdutil.determine_revision_arch(self.tree,
1528
+- target_revision = cmdutil.tree_latest(self.tree)
1530
+ merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
1531
+ self.tree, f)) for f in args[1:] ]
1532
+@@ -2711,7 +2586,7 @@
1534
+ :param message: The message to send
1535
+ :type message: `email.Message`"""
1536
+- server = smtplib.SMTP()
1537
+ server.sendmail(message['From'], message['To'], message.as_string())
1540
+@@ -2763,6 +2638,22 @@
1542
+ 'request-merge': RequestMerge,
1545
++def my_import(mod_name):
1547
++def plugin(mod_name):
1549
++for file in os.listdir(sys.path[0]+"/command"):
1552
+ 'apply-delta' : "Try \"apply-changes\".",
1553
+ 'delta' : "To compare two revisions, use \"changes\".",
1554
+@@ -2784,6 +2675,7 @@
1555
+ 'tagline' : "Use add-id. It uses taglines in tagline trees",
1556
+ 'emlog' : "Use elog. It automatically adds log-for-merge text, if any",
1557
+ 'library-revisions' : "Use revisions --library",
1558
+-'file-revert' : "Use revert FILE"
1559
++'file-revert' : "Use revert FILE",
1560
++'join-branch' : "Use replay --logs-only"
1562
+ # arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
1564
*** added file 'testdata/insert_top.patch'
1566
+++ testdata/insert_top.patch
1568
+--- orig/pylon/patches.py
1569
++++ mod/pylon/patches.py
1574
+ class PatchSyntax(Exception):
1576
*** added file 'testdata/mod'
1580
+# Copyright (C) 2004 Aaron Bentley
1581
+# <aaron.bentley@utoronto.ca>
1583
+# This program is free software; you can redistribute it and/or modify
1584
+# it under the terms of the GNU General Public License as published by
1585
+# the Free Software Foundation; either version 2 of the License, or
1586
+# (at your option) any later version.
1588
+# This program is distributed in the hope that it will be useful,
1589
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1590
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1591
+# GNU General Public License for more details.
1593
+# You should have received a copy of the GNU General Public License
1594
+# along with this program; if not, write to the Free Software
1595
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1602
+import pylon.errors
1603
+from pylon.errors import *
1604
+from pylon import errors
1605
+from pylon import util
1606
+from pylon import arch_core
1607
+from pylon import arch_compound
1608
+from pylon import ancillary
1609
+from pylon import misc
1610
+from pylon import paths
1627
+__docformat__ = "restructuredtext"
1628
+__doc__ = "Implementation of user (sub) commands"
1631
+def find_command(cmd):
1633
+ Return an instance of a command type. Return None if the type isn't
1636
+ :param cmd: the name of the command to look for
1637
+ :type cmd: the type of the command
1639
+ if commands.has_key(cmd):
1640
+ return commands[cmd]()
1645
+ def __call__(self, cmdline):
1647
+ self.do_command(cmdline.split())
1648
+ except cmdutil.GetHelp, e:
1650
+ except Exception, e:
1653
+ def get_completer(index):
1656
+ def complete(self, args, text):
1658
+ Returns a list of possible completions for the given text.
1660
+ :param args: The complete list of arguments
1661
+ :type args: List of str
1662
+ :param text: text to complete (may be shorter than args[-1])
1664
+ :rtype: list of str
1670
+ realtext = args[-1]
1675
+ parser=self.get_parser()
1676
+ if realtext.startswith('-'):
1677
+ candidates = parser.iter_options()
1679
+ (options, parsed_args) = parser.parse_args(args)
1681
+ if len (parsed_args) > 0:
1682
+ candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
1684
+ candidates = self.get_completer("", 0)
1687
+ if candidates is None:
1689
+ for candidate in candidates:
1690
+ candidate = str(candidate)
1691
+ if candidate.startswith(realtext):
1692
+ matches.append(candidate[len(realtext)- len(text):])
1696
+class Help(BaseCommand):
1698
+ Lists commands, prints help messages.
1700
+ def __init__(self):
1701
+ self.description="Prints help mesages"
1702
+ self.parser = None
1704
+ def do_command(self, cmdargs):
1706
+ Prints a help message.
1708
+ options, args = self.get_parser().parse_args(cmdargs)
1710
+ raise cmdutil.GetHelp
1712
+ if options.native or options.suggestions or options.external:
1713
+ native = options.native
1714
+ suggestions = options.suggestions
1715
+ external = options.external
1718
+ suggestions = False
1721
+ if len(args) == 0:
1722
+ self.list_commands(native, suggestions, external)
1724
+ elif len(args) == 1:
1725
+ command_help(args[0])
1729
+ self.get_parser().print_help()
1731
+If no command is specified, commands are listed. If a command is
1732
+specified, help for that command is listed.
1735
+ def get_parser(self):
1737
+ Returns the options parser to use for the "revision" command.
1739
+ :rtype: cmdutil.CmdOptionParser
1741
+ if self.parser is not None:
1742
+ return self.parser
1743
+ parser=cmdutil.CmdOptionParser("fai help [command]")
1744
+ parser.add_option("-n", "--native", action="store_true",
1745
+ dest="native", help="Show native commands")
1746
+ parser.add_option("-e", "--external", action="store_true",
1747
+ dest="external", help="Show external commands")
1748
+ parser.add_option("-s", "--suggest", action="store_true",
1749
+ dest="suggestions", help="Show suggestions")
1750
+ self.parser = parser
1753
+ def list_commands(self, native=True, suggest=False, external=True):
1755
+ Lists supported commands.
1757
+ :param native: list native, python-based commands
1758
+ :type native: bool
1759
+ :param external: list external aba-style commands
1760
+ :type external: bool
1763
+ print "Native Fai commands"
1764
+ keys=commands.keys()
1768
+ for i in range(28-len(k)):
1770
+ print space+k+" : "+commands[k]().description
1773
+ print "Unavailable commands and suggested alternatives"
1774
+ key_list = suggestions.keys()
1776
+ for key in key_list:
1777
+ print "%28s : %s" % (key, suggestions[key])
1780
+ fake_aba = abacmds.AbaCmds()
1781
+ if (fake_aba.abadir == ""):
1783
+ print "External commands"
1784
+ fake_aba.list_commands()
1787
+ print "Use help --suggest to list alternatives to tla and aba"\
1789
+ if options.tla_fallthrough and (native or external):
1790
+ print "Fai also supports tla commands."
1792
+def command_help(cmd):
1794
+ Prints help for a command.
1796
+ :param cmd: The name of the command to print help for
1799
+ fake_aba = abacmds.AbaCmds()
1800
+ cmdobj = find_command(cmd)
1801
+ if cmdobj != None:
1803
+ elif suggestions.has_key(cmd):
1804
+ print "Not available\n" + suggestions[cmd]
1806
+ abacmd = fake_aba.is_command(cmd)
1810
+ print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
1814
+class Changes(BaseCommand):
1816
+ the "changes" command: lists differences between trees/revisions:
1819
+ def __init__(self):
1820
+ self.description="Lists what files have changed in the project tree"
1822
+ def get_completer(self, arg, index):
1826
+ tree = arch.tree_root()
1829
+ return cmdutil.iter_revision_completions(arg, tree)
1831
+ def parse_commandline(self, cmdline):
1833
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
1835
+ :param cmdline: A list of arguments to parse
1836
+ :rtype: (options, Revision, Revision/WorkingTree)
1838
+ parser=self.get_parser()
1839
+ (options, args) = parser.parse_args(cmdline)
1841
+ raise cmdutil.GetHelp
1843
+ tree=arch.tree_root()
1844
+ if len(args) == 0:
1845
+ a_spec = ancillary.comp_revision(tree)
1847
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
1848
+ cmdutil.ensure_archive_registered(a_spec.archive)
1849
+ if len(args) == 2:
1850
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
1851
+ cmdutil.ensure_archive_registered(b_spec.archive)
1854
+ return options, a_spec, b_spec
1856
+ def do_command(self, cmdargs):
1858
+ Master function that perfoms the "changes" command.
1861
+ options, a_spec, b_spec = self.parse_commandline(cmdargs);
1862
+ except cmdutil.CantDetermineRevision, e:
1865
+ except arch.errors.TreeRootError, e:
1868
+ if options.changeset:
1869
+ changeset=options.changeset
1872
+ tmpdir=util.tmpdir()
1873
+ changeset=tmpdir+"/changeset"
1875
+ delta=arch.iter_delta(a_spec, b_spec, changeset)
1877
+ for line in delta:
1878
+ if cmdutil.chattermatch(line, "changeset:"):
1881
+ cmdutil.colorize(line, options.suppress_chatter)
1882
+ except arch.util.ExecProblem, e:
1883
+ if e.proc.error and e.proc.error.startswith(
1884
+ "missing explicit id for file"):
1885
+ raise MissingID(e)
1888
+ status=delta.status
1891
+ if (options.perform_diff):
1892
+ chan = arch_compound.ChangesetMunger(changeset)
1893
+ chan.read_indices()
1894
+ if options.diffopts is not None:
1895
+ if isinstance(b_spec, arch.Revision):
1896
+ b_dir = b_spec.library_find()
1899
+ a_dir = a_spec.library_find()
1900
+ diffopts = options.diffopts.split()
1901
+ cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
1903
+ cmdutil.show_diffs(delta.changeset)
1905
+ if tmpdir and (os.access(tmpdir, os.X_OK)):
1906
+ shutil.rmtree(tmpdir)
1908
+ def get_parser(self):
1910
+ Returns the options parser to use for the "changes" command.
1912
+ :rtype: cmdutil.CmdOptionParser
1914
+ parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
1916
+ parser.add_option("-d", "--diff", action="store_true",
1917
+ dest="perform_diff", default=False,
1918
+ help="Show diffs in summary")
1919
+ parser.add_option("-c", "--changeset", dest="changeset",
1920
+ help="Store a changeset in the given directory",
1921
+ metavar="DIRECTORY")
1922
+ parser.add_option("-s", "--silent", action="store_true",
1923
+ dest="suppress_chatter", default=False,
1924
+ help="Suppress chatter messages")
1925
+ parser.add_option("--diffopts", dest="diffopts",
1926
+ help="Use the specified diff options",
1927
+ metavar="OPTIONS")
1931
+ def help(self, parser=None):
1933
+ Prints a help message.
1935
+ :param parser: If supplied, the parser to use for generating help. If \
1936
+ not supplied, it is retrieved.
1937
+ :type parser: cmdutil.CmdOptionParser
1939
+ if parser is None:
1940
+ parser=self.get_parser()
1941
+ parser.print_help()
1943
+Performs source-tree comparisons
1945
+If no revision is specified, the current project tree is compared to the
1946
+last-committed revision. If one revision is specified, the current project
1947
+tree is compared to that revision. If two revisions are specified, they are
1948
+compared to each other.
1954
+class ApplyChanges(BaseCommand):
1956
+ Apply differences between two revisions to a tree
1959
+ def __init__(self):
1960
+ self.description="Applies changes to a project tree"
1962
+ def get_completer(self, arg, index):
1966
+ tree = arch.tree_root()
1969
+ return cmdutil.iter_revision_completions(arg, tree)
1971
+ def parse_commandline(self, cmdline, tree):
1973
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
1975
+ :param cmdline: A list of arguments to parse
1976
+ :rtype: (options, Revision, Revision/WorkingTree)
1978
+ parser=self.get_parser()
1979
+ (options, args) = parser.parse_args(cmdline)
1980
+ if len(args) != 2:
1981
+ raise cmdutil.GetHelp
1983
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
1984
+ cmdutil.ensure_archive_registered(a_spec.archive)
1985
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
1986
+ cmdutil.ensure_archive_registered(b_spec.archive)
1987
+ return options, a_spec, b_spec
1989
+ def do_command(self, cmdargs):
1991
+ Master function that performs "apply-changes".
1994
+ tree = arch.tree_root()
1995
+ options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
1996
+ except cmdutil.CantDetermineRevision, e:
1999
+ except arch.errors.TreeRootError, e:
2002
+ delta=cmdutil.apply_delta(a_spec, b_spec, tree)
2003
+ for line in cmdutil.iter_apply_delta_filter(delta):
2004
+ cmdutil.colorize(line, options.suppress_chatter)
2006
+ def get_parser(self):
2008
+ Returns the options parser to use for the "apply-changes" command.
2010
+ :rtype: cmdutil.CmdOptionParser
2012
+ parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
2014
+ parser.add_option("-d", "--diff", action="store_true",
2015
+ dest="perform_diff", default=False,
2016
+ help="Show diffs in summary")
2017
+ parser.add_option("-c", "--changeset", dest="changeset",
2018
+ help="Store a changeset in the given directory",
2019
+ metavar="DIRECTORY")
2020
+ parser.add_option("-s", "--silent", action="store_true",
2021
+ dest="suppress_chatter", default=False,
2022
+ help="Suppress chatter messages")
2025
+ def help(self, parser=None):
2027
+ Prints a help message.
2029
+ :param parser: If supplied, the parser to use for generating help. If \
2030
+ not supplied, it is retrieved.
2031
+ :type parser: cmdutil.CmdOptionParser
2033
+ if parser is None:
2034
+ parser=self.get_parser()
2035
+ parser.print_help()
2037
+Applies changes to a project tree
2039
+Compares two revisions and applies the difference between them to the current
2045
+class Update(BaseCommand):
2047
+ Updates a project tree to a given revision, preserving un-committed hanges.
2050
+ def __init__(self):
2051
+ self.description="Apply the latest changes to the current directory"
2053
+ def get_completer(self, arg, index):
2057
+ tree = arch.tree_root()
2060
+ return cmdutil.iter_revision_completions(arg, tree)
2062
+ def parse_commandline(self, cmdline, tree):
2064
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
2066
+ :param cmdline: A list of arguments to parse
2067
+ :rtype: (options, Revision, Revision/WorkingTree)
2069
+ parser=self.get_parser()
2070
+ (options, args) = parser.parse_args(cmdline)
2072
+ raise cmdutil.GetHelp
2077
+ revision=cmdutil.determine_revision_arch(tree, spec)
2078
+ cmdutil.ensure_archive_registered(revision.archive)
2080
+ mirror_source = cmdutil.get_mirror_source(revision.archive)
2081
+ if mirror_source != None:
2082
+ if cmdutil.prompt("Mirror update"):
2083
+ cmd=cmdutil.mirror_archive(mirror_source,
2084
+ revision.archive, arch.NameParser(revision).get_package_version())
2085
+ for line in arch.chatter_classifier(cmd):
2086
+ cmdutil.colorize(line, options.suppress_chatter)
2088
+ revision=cmdutil.determine_revision_arch(tree, spec)
2090
+ return options, revision
2092
+ def do_command(self, cmdargs):
2094
+ Master function that perfoms the "update" command.
2096
+ tree=arch.tree_root()
2098
+ options, to_revision = self.parse_commandline(cmdargs, tree);
2099
+ except cmdutil.CantDetermineRevision, e:
2102
+ except arch.errors.TreeRootError, e:
2105
+ from_revision = arch_compound.tree_latest(tree)
2106
+ if from_revision==to_revision:
2107
+ print "Tree is already up to date with:\n"+str(to_revision)+"."
2109
+ cmdutil.ensure_archive_registered(from_revision.archive)
2110
+ cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
2111
+ options.patch_forward)
2112
+ for line in cmdutil.iter_apply_delta_filter(cmd):
2113
+ cmdutil.colorize(line)
2114
+ if to_revision.version != tree.tree_version:
2115
+ if cmdutil.prompt("Update version"):
2116
+ tree.tree_version = to_revision.version
2118
+ def get_parser(self):
2120
+ Returns the options parser to use for the "update" command.
2122
+ :rtype: cmdutil.CmdOptionParser
2124
+ parser=cmdutil.CmdOptionParser("fai update [options]"
2125
+ " [revision/version]")
2126
+ parser.add_option("-f", "--forward", action="store_true",
2127
+ dest="patch_forward", default=False,
2128
+ help="pass the --forward option to 'patch'")
2129
+ parser.add_option("-s", "--silent", action="store_true",
2130
+ dest="suppress_chatter", default=False,
2131
+ help="Suppress chatter messages")
2134
+ def help(self, parser=None):
2136
+ Prints a help message.
2138
+ :param parser: If supplied, the parser to use for generating help. If \
2139
+ not supplied, it is retrieved.
2140
+ :type parser: cmdutil.CmdOptionParser
2142
+ if parser is None:
2143
+ parser=self.get_parser()
2144
+ parser.print_help()
2146
+Updates a working tree to the current archive revision
2148
+If a revision or version is specified, that is used instead
2154
+class Commit(BaseCommand):
2156
+ Create a revision based on the changes in the current tree.
2159
+ def __init__(self):
2160
+ self.description="Write local changes to the archive"
2162
+ def get_completer(self, arg, index):
2165
+ return iter_modified_file_completions(arch.tree_root(), arg)
2166
+# return iter_source_file_completions(arch.tree_root(), arg)
2168
+ def parse_commandline(self, cmdline, tree):
2170
+ Parse commandline arguments. Raise cmtutil.GetHelp if help is needed.
2172
+ :param cmdline: A list of arguments to parse
2173
+ :rtype: (options, Revision, Revision/WorkingTree)
2175
+ parser=self.get_parser()
2176
+ (options, args) = parser.parse_args(cmdline)
2178
+ if len(args) == 0:
2180
+ if options.version is None:
2181
+ return options, tree.tree_version, args
2183
+ revision=cmdutil.determine_revision_arch(tree, options.version)
2184
+ return options, revision.get_version(), args
2186
+ def do_command(self, cmdargs):
2188
+ Master function that perfoms the "commit" command.
2190
+ tree=arch.tree_root()
2191
+ options, version, files = self.parse_commandline(cmdargs, tree)
2193
+ if options.__dict__.has_key("base") and options.base:
2194
+ base = cmdutil.determine_revision_tree(tree, options.base)
2197
+ base = ancillary.submit_revision(tree)
2199
+ if ancestor is None:
2200
+ ancestor = arch_compound.tree_latest(tree, version)
2202
+ writeversion=version
2203
+ archive=version.archive
2204
+ source=cmdutil.get_mirror_source(archive)
2206
+ writethrough="implicit"
2209
+ if writethrough=="explicit" and \
2210
+ cmdutil.prompt("Writethrough"):
2211
+ writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
2212
+ elif writethrough=="none":
2213
+ raise CommitToMirror(archive)
2215
+ elif archive.is_mirror:
2216
+ raise CommitToMirror(archive)
2219
+ last_revision=tree.iter_logs(version, True).next().revision
2220
+ except StopIteration, e:
2221
+ last_revision = None
2222
+ if ancestor is None:
2223
+ if cmdutil.prompt("Import from commit"):
2224
+ return do_import(version)
2226
+ raise NoVersionLogs(version)
2228
+ arch_last_revision = version.iter_revisions(True).next()
2229
+ except StopIteration, e:
2230
+ arch_last_revision = None
2232
+ if last_revision != arch_last_revision:
2233
+ print "Tree is not up to date with %s" % str(version)
2234
+ if not cmdutil.prompt("Out of date"):
2240
+ if not cmdutil.has_changed(ancestor):
2241
+ if not cmdutil.prompt("Empty commit"):
2243
+ except arch.util.ExecProblem, e:
2244
+ if e.proc.error and e.proc.error.startswith(
2245
+ "missing explicit id for file"):
2246
+ raise MissingID(e)
2249
+ log = tree.log_message(create=False, version=version)
2252
+ if cmdutil.prompt("Create log"):
2253
+ edit_log(tree, version)
2255
+ except cmdutil.NoEditorSpecified, e:
2256
+ raise CommandFailed(e)
2257
+ log = tree.log_message(create=False, version=version)
2259
+ raise NoLogMessage
2260
+ if log["Summary"] is None or len(log["Summary"].strip()) == 0:
2261
+ if not cmdutil.prompt("Omit log summary"):
2262
+ raise errors.NoLogSummary
2264
+ for line in tree.iter_commit(version, seal=options.seal_version,
2265
+ base=base, out_of_date_ok=allow_old, file_list=files):
2266
+ cmdutil.colorize(line, options.suppress_chatter)
2268
+ except arch.util.ExecProblem, e:
2269
+ if e.proc.error and e.proc.error.startswith(
2270
+ "These files violate naming conventions:"):
2271
+ raise LintFailure(e.proc.error)
2275
+ def get_parser(self):
2277
+ Returns the options parser to use for the "commit" command.
2279
+ :rtype: cmdutil.CmdOptionParser
2282
+ parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
2284
+ parser.add_option("--seal", action="store_true",
2285
+ dest="seal_version", default=False,
2286
+ help="seal this version")
2287
+ parser.add_option("-v", "--version", dest="version",
2288
+ help="Use the specified version",
2289
+ metavar="VERSION")
2290
+ parser.add_option("-s", "--silent", action="store_true",
2291
+ dest="suppress_chatter", default=False,
2292
+ help="Suppress chatter messages")
2293
+ if cmdutil.supports_switch("commit", "--base"):
2294
+ parser.add_option("--base", dest="base", help="",
2295
+ metavar="REVISION")
2298
+ def help(self, parser=None):
2300
+ Prints a help message.
2302
+ :param parser: If supplied, the parser to use for generating help. If \
2303
+ not supplied, it is retrieved.
2304
+ :type parser: cmdutil.CmdOptionParser
2306
+ if parser is None:
2307
+ parser=self.get_parser()
2308
+ parser.print_help()
2310
+Updates a working tree to the current archive revision
2312
+If a version is specified, that is used instead
2319
+class CatLog(BaseCommand):
2321
+ Print the log of a given file (from current tree)
2323
+ def __init__(self):
2324
+ self.description="Prints the patch log for a revision"
2326
+ def get_completer(self, arg, index):
2330
+ tree = arch.tree_root()
2333
+ return cmdutil.iter_revision_completions(arg, tree)
2335
+ def do_command(self, cmdargs):
2337
+ Master function that perfoms the "cat-log" command.
2339
+ parser=self.get_parser()
2340
+ (options, args) = parser.parse_args(cmdargs)
2342
+ tree = arch.tree_root()
2343
+ except arch.errors.TreeRootError, e:
2349
+ raise cmdutil.GetHelp()
2352
+ revision = cmdutil.determine_revision_tree(tree, spec)
2354
+ revision = cmdutil.determine_revision_arch(tree, spec)
2355
+ except cmdutil.CantDetermineRevision, e:
2356
+ raise CommandFailedWrapper(e)
2359
+ use_tree = (options.source == "tree" or \
2360
+ (options.source == "any" and tree))
2361
+ use_arch = (options.source == "archive" or options.source == "any")
2365
+ for log in tree.iter_logs(revision.get_version()):
2366
+ if log.revision == revision:
2370
+ if log is None and use_arch:
2371
+ cmdutil.ensure_revision_exists(revision)
2372
+ log = arch.Patchlog(revision)
2373
+ if log is not None:
2374
+ for item in log.items():
2375
+ print "%s: %s" % item
2376
+ print log.description
2378
+ def get_parser(self):
2380
+ Returns the options parser to use for the "cat-log" command.
2382
+ :rtype: cmdutil.CmdOptionParser
2384
+ parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
2385
+ parser.add_option("--archive", action="store_const", dest="source",
2386
+ const="archive", default="any",
2387
+ help="Always get the log from the archive")
2388
+ parser.add_option("--tree", action="store_const", dest="source",
2389
+ const="tree", help="Always get the log from the tree")
2392
+ def help(self, parser=None):
2394
+ Prints a help message.
2396
+ :param parser: If supplied, the parser to use for generating help. If \
2397
+ not supplied, it is retrieved.
2398
+ :type parser: cmdutil.CmdOptionParser
2401
+ parser=self.get_parser()
2402
+ parser.print_help()
2404
+Prints the log for the specified revision
2409
+class Revert(BaseCommand):
2410
+ """ Reverts a tree (or aspects of it) to a revision
2412
+ def __init__(self):
2413
+ self.description="Reverts a tree (or aspects of it) to a revision "
2415
+ def get_completer(self, arg, index):
2419
+ tree = arch.tree_root()
2422
+ return iter_modified_file_completions(tree, arg)
2424
+ def do_command(self, cmdargs):
2426
+ Master function that perfoms the "revert" command.
2428
+ parser=self.get_parser()
2429
+ (options, args) = parser.parse_args(cmdargs)
2431
+ tree = arch.tree_root()
2432
+ except arch.errors.TreeRootError, e:
2433
+ raise CommandFailed(e)
2435
+ if options.revision is not None:
2436
+ spec=options.revision
2438
+ if spec is not None:
2439
+ revision = cmdutil.determine_revision_tree(tree, spec)
2441
+ revision = ancillary.comp_revision(tree)
2442
+ except cmdutil.CantDetermineRevision, e:
2443
+ raise CommandFailedWrapper(e)
2446
+ if options.file_contents or options.file_perms or options.deletions\
2447
+ or options.additions or options.renames or options.hunk_prompt:
2448
+ munger = arch_compound.MungeOpts()
2449
+ munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
2450
+ options.hunk_prompt)
2452
+ if len(args) > 0 or options.logs or options.pattern_files or \
2454
+ if munger is None:
2455
+ munger = cmdutil.arch_compound.MungeOpts(True)
2456
+ munger.all_types(True)
2458
+ t_cwd = arch_compound.tree_cwd(tree)
2460
+ if len(t_cwd) > 0:
2462
+ name = "./" + t_cwd + name
2463
+ munger.add_keep_file(name);
2465
+ if options.file_perms:
2466
+ munger.file_perms = True
2467
+ if options.file_contents:
2468
+ munger.file_contents = True
2469
+ if options.deletions:
2470
+ munger.deletions = True
2471
+ if options.additions:
2472
+ munger.additions = True
2473
+ if options.renames:
2474
+ munger.renames = True
2476
+ munger.add_keep_pattern('^\./\{arch\}/[^=].*')
2477
+ if options.control:
2478
+ munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
2479
+ "/\.arch-inventory$")
2480
+ if options.pattern_files:
2481
+ munger.add_keep_pattern(options.pattern_files)
2483
+ for line in arch_compound.revert(tree, revision, munger,
2484
+ not options.no_output):
2485
+ cmdutil.colorize(line)
2488
+ def get_parser(self):
2490
+ Returns the options parser to use for the "cat-log" command.
2492
+ :rtype: cmdutil.CmdOptionParser
2494
+ parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
2495
+ parser.add_option("", "--contents", action="store_true",
2496
+ dest="file_contents",
2497
+ help="Revert file content changes")
2498
+ parser.add_option("", "--permissions", action="store_true",
2499
+ dest="file_perms",
2500
+ help="Revert file permissions changes")
2501
+ parser.add_option("", "--deletions", action="store_true",
2503
+ help="Restore deleted files")
2504
+ parser.add_option("", "--additions", action="store_true",
2506
+ help="Remove added files")
2507
+ parser.add_option("", "--renames", action="store_true",
2509
+ help="Revert file names")
2510
+ parser.add_option("--hunks", action="store_true",
2511
+ dest="hunk_prompt", default=False,
2512
+ help="Prompt which hunks to revert")
2513
+ parser.add_option("--pattern-files", dest="pattern_files",
2514
+ help="Revert files that match this pattern",
2516
+ parser.add_option("--logs", action="store_true",
2517
+ dest="logs", default=False,
2518
+ help="Revert only logs")
2519
+ parser.add_option("--control-files", action="store_true",
2520
+ dest="control", default=False,
2521
+ help="Revert logs and other control files")
2522
+ parser.add_option("-n", "--no-output", action="store_true",
2524
+ help="Don't keep an undo changeset")
2525
+ parser.add_option("--revision", dest="revision",
2526
+ help="Revert to the specified revision",
2527
+ metavar="REVISION")
2530
+ def help(self, parser=None):
2532
+ Prints a help message.
2534
+ :param parser: If supplied, the parser to use for generating help. If \
2535
+ not supplied, it is retrieved.
2536
+ :type parser: cmdutil.CmdOptionParser
2539
+ parser=self.get_parser()
2540
+ parser.print_help()
2542
+Reverts changes in the current working tree. If no flags are specified, all
2543
+types of changes are reverted. Otherwise, only selected types of changes are
2546
+If a revision is specified on the commandline, differences between the current
2547
+tree and that revision are reverted. If a version is specified, the current
2548
+tree is used to determine the revision.
2550
+If files are specified, only those files listed will have any changes applied.
2551
+To specify a renamed file, you can use either the old or new name. (or both!)
2553
+Unless "-n" is specified, reversions can be undone with "redo".
2557
+class Revision(BaseCommand):
2559
+ Print a revision name based on a revision specifier
2561
+ def __init__(self):
2562
+ self.description="Prints the name of a revision"
2564
+ def get_completer(self, arg, index):
2568
+ tree = arch.tree_root()
2571
+ return cmdutil.iter_revision_completions(arg, tree)
2573
+ def do_command(self, cmdargs):
2575
+ Master function that perfoms the "revision" command.
2577
+ parser=self.get_parser()
2578
+ (options, args) = parser.parse_args(cmdargs)
2581
+ tree = arch.tree_root()
2582
+ except arch.errors.TreeRootError:
2589
+ raise cmdutil.GetHelp
2592
+ revision = cmdutil.determine_revision_tree(tree, spec)
2594
+ revision = cmdutil.determine_revision_arch(tree, spec)
2595
+ except cmdutil.CantDetermineRevision, e:
2598
+ print options.display(revision)
2600
+ def get_parser(self):
2602
+ Returns the options parser to use for the "revision" command.
2604
+ :rtype: cmdutil.CmdOptionParser
2606
+ parser=cmdutil.CmdOptionParser("fai revision [revision]")
2607
+ parser.add_option("", "--location", action="store_const",
2608
+ const=paths.determine_path, dest="display",
2609
+ help="Show location instead of name", default=str)
2610
+ parser.add_option("--import", action="store_const",
2611
+ const=paths.determine_import_path, dest="display",
2612
+ help="Show location of import file")
2613
+ parser.add_option("--log", action="store_const",
2614
+ const=paths.determine_log_path, dest="display",
2615
+ help="Show location of log file")
2616
+ parser.add_option("--patch", action="store_const",
2617
+ dest="display", const=paths.determine_patch_path,
2618
+ help="Show location of patchfile")
2619
+ parser.add_option("--continuation", action="store_const",
2620
+ const=paths.determine_continuation_path,
2622
+ help="Show location of continuation file")
2623
+ parser.add_option("--cacherev", action="store_const",
2624
+ const=paths.determine_cacherev_path, dest="display",
2625
+ help="Show location of cacherev file")
2628
+ def help(self, parser=None):
2630
+ Prints a help message.
2632
+ :param parser: If supplied, the parser to use for generating help. If \
2633
+ not supplied, it is retrieved.
2634
+ :type parser: cmdutil.CmdOptionParser
2637
+ parser=self.get_parser()
2638
+ parser.print_help()
2640
+Expands aliases and prints the name of the specified revision. Instead of
2641
+the name, several options can be used to print locations. If more than one is
2642
+specified, the last one is used.
2647
+class Revisions(BaseCommand):
2649
+ Print a revision name based on a revision specifier
2651
+ def __init__(self):
2652
+ self.description="Lists revisions"
2653
+ self.cl_revisions = []
2655
+ def do_command(self, cmdargs):
2657
+ Master function that perfoms the "revision" command.
2659
+ (options, args) = self.get_parser().parse_args(cmdargs)
2661
+ raise cmdutil.GetHelp
2663
+ self.tree = arch.tree_root()
2664
+ except arch.errors.TreeRootError:
2666
+ if options.type == "default":
2667
+ options.type = "archive"
2669
+ iter = cmdutil.revision_iterator(self.tree, options.type, args,
2670
+ options.reverse, options.modified,
2672
+ except cmdutil.CantDetermineRevision, e:
2673
+ raise CommandFailedWrapper(e)
2674
+ except cmdutil.CantDetermineVersion, e:
2675
+ raise CommandFailedWrapper(e)
2676
+ if options.skip is not None:
2677
+ iter = cmdutil.iter_skip(iter, int(options.skip))
2680
+ for revision in iter:
2682
+ if isinstance(revision, arch.Patchlog):
2684
+ revision=revision.revision
2685
+ out = options.display(revision)
2686
+ if out is not None:
2688
+ if log is None and (options.summary or options.creator or
2689
+ options.date or options.merges):
2690
+ log = revision.patchlog
2691
+ if options.creator:
2692
+ print " %s" % log.creator
2694
+ print " %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
2695
+ if options.summary:
2696
+ print " %s" % log.summary
2697
+ if options.merges:
2698
+ showed_title = False
2699
+ for revision in log.merged_patches:
2700
+ if not showed_title:
2702
+ showed_title = True
2703
+ print " %s" % revision
2704
+ if len(self.cl_revisions) > 0:
2705
+ print pylon.changelog_for_merge(self.cl_revisions)
2706
+ except pylon.errors.TreeRootNone:
2707
+ raise CommandFailedWrapper(
2708
+ Exception("This option can only be used in a project tree."))
2710
+ def changelog_append(self, revision):
2711
+ if isinstance(revision, arch.Revision):
2712
+ revision=arch.Patchlog(revision)
2713
+ self.cl_revisions.append(revision)
2715
+ def get_parser(self):
2717
+ Returns the options parser to use for the "revision" command.
2719
+ :rtype: cmdutil.CmdOptionParser
2721
+ parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
2722
+ select = cmdutil.OptionGroup(parser, "Selection options",
2723
+ "Control which revisions are listed. These options"
2724
+ " are mutually exclusive. If more than one is"
2725
+ " specified, the last is used.")
2727
+ cmdutil.add_revision_iter_options(select)
2728
+ parser.add_option("", "--skip", dest="skip",
2729
+ help="Skip revisions. Positive numbers skip from "
2730
+ "beginning, negative skip from end.",
2733
+ parser.add_option_group(select)
2735
+ format = cmdutil.OptionGroup(parser, "Revision format options",
2736
+ "These control the appearance of listed revisions")
2737
+ format.add_option("", "--location", action="store_const",
2738
+ const=paths.determine_path, dest="display",
2739
+ help="Show location instead of name", default=str)
2740
+ format.add_option("--import", action="store_const",
2741
+ const=paths.determine_import_path, dest="display",
2742
+ help="Show location of import file")
2743
+ format.add_option("--log", action="store_const",
2744
+ const=paths.determine_log_path, dest="display",
2745
+ help="Show location of log file")
2746
+ format.add_option("--patch", action="store_const",
2747
+ dest="display", const=paths.determine_patch_path,
2748
+ help="Show location of patchfile")
2749
+ format.add_option("--continuation", action="store_const",
2750
+ const=paths.determine_continuation_path,
2752
+ help="Show location of continuation file")
2753
+ format.add_option("--cacherev", action="store_const",
2754
+ const=paths.determine_cacherev_path, dest="display",
2755
+ help="Show location of cacherev file")
2756
+ format.add_option("--changelog", action="store_const",
2757
+ const=self.changelog_append, dest="display",
2758
+ help="Show location of cacherev file")
2759
+ parser.add_option_group(format)
2760
+ display = cmdutil.OptionGroup(parser, "Display format options",
2761
+ "These control the display of data")
2762
+ display.add_option("-r", "--reverse", action="store_true",
2763
+ dest="reverse", help="Sort from newest to oldest")
2764
+ display.add_option("-s", "--summary", action="store_true",
2765
+ dest="summary", help="Show patchlog summary")
2766
+ display.add_option("-D", "--date", action="store_true",
2767
+ dest="date", help="Show patchlog date")
2768
+ display.add_option("-c", "--creator", action="store_true",
2769
+ dest="creator", help="Show the id that committed the"
2771
+ display.add_option("-m", "--merges", action="store_true",
2772
+ dest="merges", help="Show the revisions that were"
2774
+ parser.add_option_group(display)
2776
+ def help(self, parser=None):
2777
+ """Attempt to explain the revisions command
2779
+ :param parser: If supplied, used to determine options
2782
+ parser=self.get_parser()
2783
+ parser.print_help()
2784
+ print """List revisions.
2789
+class Get(BaseCommand):
2791
+ Retrieve a revision from the archive
2793
+ def __init__(self):
2794
+ self.description="Retrieve a revision from the archive"
2795
+ self.parser=self.get_parser()
2798
+ def get_completer(self, arg, index):
2802
+ tree = arch.tree_root()
2805
+ return cmdutil.iter_revision_completions(arg, tree)
2808
+ def do_command(self, cmdargs):
2810
+ Master function that perfoms the "get" command.
2812
+ (options, args) = self.parser.parse_args(cmdargs)
2814
+ return self.help()
2816
+ tree = arch.tree_root()
2817
+ except arch.errors.TreeRootError:
2822
+ revision, arch_loc = paths.full_path_decode(args[0])
2823
+ except Exception, e:
2824
+ revision = cmdutil.determine_revision_arch(tree, args[0],
2825
+ check_existence=False, allow_package=True)
2827
+ directory = args[1]
2829
+ directory = str(revision.nonarch)
2830
+ if os.path.exists(directory):
2831
+ raise DirectoryExists(directory)
2832
+ cmdutil.ensure_archive_registered(revision.archive, arch_loc)
2834
+ cmdutil.ensure_revision_exists(revision)
2835
+ except cmdutil.NoSuchRevision, e:
2836
+ raise CommandFailedWrapper(e)
2838
+ link = cmdutil.prompt ("get link")
2839
+ for line in cmdutil.iter_get(revision, directory, link,
2840
+ options.no_pristine,
2841
+ options.no_greedy_add):
2842
+ cmdutil.colorize(line)
2844
+ def get_parser(self):
2846
+ Returns the options parser to use for the "get" command.
2848
+ :rtype: cmdutil.CmdOptionParser
2850
+ parser=cmdutil.CmdOptionParser("fai get revision [dir]")
2851
+ parser.add_option("--no-pristine", action="store_true",
2852
+ dest="no_pristine",
2853
+ help="Do not make pristine copy for reference")
2854
+ parser.add_option("--no-greedy-add", action="store_true",
2855
+ dest="no_greedy_add",
2856
+ help="Never add to greedy libraries")
2860
+ def help(self, parser=None):
2862
+ Prints a help message.
2864
+ :param parser: If supplied, the parser to use for generating help. If \
2865
+ not supplied, it is retrieved.
2866
+ :type parser: cmdutil.CmdOptionParser
2869
+ parser=self.get_parser()
2870
+ parser.print_help()
2872
+Expands aliases and constructs a project tree for a revision. If the optional
2873
+"dir" argument is provided, the project tree will be stored in this directory.
2878
+class PromptCmd(cmd.Cmd):
2879
+ def __init__(self):
2880
+ cmd.Cmd.__init__(self)
2881
+ self.prompt = "Fai> "
2883
+ self.tree = arch.tree_root()
2888
+ self.fake_aba = abacmds.AbaCmds()
2889
+ self.identchars += '-'
2890
+ self.history_file = os.path.expanduser("~/.fai-history")
2891
+ readline.set_completer_delims(string.whitespace)
2892
+ if os.access(self.history_file, os.R_OK) and \
2893
+ os.path.isfile(self.history_file):
2894
+ readline.read_history_file(self.history_file)
2895
+ self.cwd = os.getcwd()
2897
+ def write_history(self):
2898
+ readline.write_history_file(self.history_file)
2900
+ def do_quit(self, args):
2901
+ self.write_history()
2904
+ def do_exit(self, args):
2905
+ self.do_quit(args)
2907
+ def do_EOF(self, args):
2909
+ self.do_quit(args)
2911
+ def postcmd(self, line, bar):
2915
+ def set_prompt(self):
2916
+ if self.tree is not None:
2918
+ prompt = pylon.alias_or_version(self.tree.tree_version,
2921
+ if prompt is not None:
2922
+ prompt = " " + prompt
2927
+ self.prompt = "Fai%s> " % prompt
2929
+ def set_title(self, command=None):
2931
+ version = pylon.alias_or_version(self.tree.tree_version, self.tree,
2934
+ version = "[no version]"
2935
+ if command is None:
2937
+ sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
2939
+ def do_cd(self, line):
2942
+ line = os.path.expanduser(line)
2943
+ if os.path.isabs(line):
2946
+ newcwd = self.cwd+'/'+line
2947
+ newcwd = os.path.normpath(newcwd)
2951
+ except Exception, e:
2954
+ self.tree = arch.tree_root()
2958
+ def do_help(self, line):
2961
+ def default(self, line):
2962
+ args = line.split()
2963
+ if find_command(args[0]):
2965
+ find_command(args[0]).do_command(args[1:])
2966
+ except cmdutil.BadCommandOption, e:
2968
+ except cmdutil.GetHelp, e:
2969
+ find_command(args[0]).help()
2970
+ except CommandFailed, e:
2972
+ except arch.errors.ArchiveNotRegistered, e:
2974
+ except KeyboardInterrupt, e:
2975
+ print "Interrupted"
2976
+ except arch.util.ExecProblem, e:
2977
+ print e.proc.error.rstrip('\n')
2978
+ except cmdutil.CantDetermineVersion, e:
2980
+ except cmdutil.CantDetermineRevision, e:
2982
+ except Exception, e:
2983
+ print "Unhandled error:\n%s" % errors.exception_str(e)
2985
+ elif suggestions.has_key(args[0]):
2986
+ print suggestions[args[0]]
2988
+ elif self.fake_aba.is_command(args[0]):
2991
+ tree = arch.tree_root()
2992
+ except arch.errors.TreeRootError:
2994
+ cmd = self.fake_aba.is_command(args[0])
2996
+ cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
2997
+ except KeyboardInterrupt, e:
2998
+ print "Interrupted"
3000
+ elif options.tla_fallthrough and args[0] != "rm" and \
3001
+ cmdutil.is_tla_command(args[0]):
3005
+ tree = arch.tree_root()
3006
+ except arch.errors.TreeRootError:
3008
+ args = cmdutil.expand_prefix_alias(args, tree)
3009
+ arch.util.exec_safe('tla', args, stderr=sys.stderr,
3011
+ except arch.util.ExecProblem, e:
3013
+ except KeyboardInterrupt, e:
3014
+ print "Interrupted"
3018
+ tree = arch.tree_root()
3019
+ except arch.errors.TreeRootError:
3022
+ os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
3023
+ except KeyboardInterrupt, e:
3024
+ print "Interrupted"
3026
+ def completenames(self, text, line, begidx, endidx):
3028
+ iter = iter_command_names(self.fake_aba)
3031
+ arg = line.split()[-1]
3034
+ iter = cmdutil.iter_munged_completions(iter, arg, text)
3035
+ except Exception, e:
3039
+ def completedefault(self, text, line, begidx, endidx):
3040
+ """Perform completion for native commands.
3042
+ :param text: The text to complete
3044
+ :param line: The entire line to complete
3046
+ :param begidx: The start of the text in the line
3048
+ :param endidx: The end of the text in the line
3052
+ (cmd, args, foo) = self.parseline(line)
3053
+ command_obj=find_command(cmd)
3054
+ if command_obj is not None:
3055
+ return command_obj.complete(args.split(), text)
3056
+ elif not self.fake_aba.is_command(cmd) and \
3057
+ cmdutil.is_tla_command(cmd):
3058
+ iter = cmdutil.iter_supported_switches(cmd)
3060
+ arg = args.split()[-1]
3063
+ if arg.startswith("-"):
3064
+ return list(cmdutil.iter_munged_completions(iter, arg,
3067
+ return list(cmdutil.iter_munged_completions(
3068
+ cmdutil.iter_file_completions(arg), arg, text))
3073
+ arg = args.split()[-1]
3076
+ iter = cmdutil.iter_dir_completions(arg)
3077
+ iter = cmdutil.iter_munged_completions(iter, arg, text)
3080
+ arg = args.split()[-1]
3081
+ iter = cmdutil.iter_file_completions(arg)
3082
+ return list(cmdutil.iter_munged_completions(iter, arg, text))
3084
+ return self.completenames(text, line, begidx, endidx)
3085
+ except Exception, e:
3089
+def iter_command_names(fake_aba):
3090
+ for entry in cmdutil.iter_combine([commands.iterkeys(),
3091
+ fake_aba.get_commands(),
3092
+ cmdutil.iter_tla_commands(False)]):
3093
+ if not suggestions.has_key(str(entry)):
3097
+def iter_source_file_completions(tree, arg):
3098
+ treepath = arch_compound.tree_cwd(tree)
3099
+ if len(treepath) > 0:
3103
+ for file in tree.iter_inventory(dirs, source=True, both=True):
3104
+ file = file_completion_match(file, treepath, arg)
3105
+ if file is not None:
3109
+def iter_untagged(tree, dirs):
3110
+ for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False,
3111
+ categories=arch_core.non_root,
3112
+ control_files=True):
3116
+def iter_untagged_completions(tree, arg):
3117
+ """Generate an iterator for all visible untagged files that match arg.
3119
+ :param tree: The tree to look for untagged files in
3120
+ :type tree: `arch.WorkingTree`
3121
+ :param arg: The argument to match
3123
+ :return: An iterator of all matching untagged files
3124
+ :rtype: iterator of str
3126
+ treepath = arch_compound.tree_cwd(tree)
3127
+ if len(treepath) > 0:
3132
+ for file in iter_untagged(tree, dirs):
3133
+ file = file_completion_match(file, treepath, arg)
3134
+ if file is not None:
3138
+def file_completion_match(file, treepath, arg):
3139
+ """Determines whether a file within an arch tree matches the argument.
3141
+ :param file: The rooted filename
3143
+ :param treepath: The path to the cwd within the tree
3144
+ :type treepath: str
3145
+ :param arg: The prefix to match
3146
+ :return: The completion name, or None if not a match
3149
+ if not file.startswith(treepath):
3151
+ if treepath != "":
3152
+ file = file[len(treepath)+1:]
3154
+ if not file.startswith(arg):
3156
+ if os.path.isdir(file):
3160
+def iter_modified_file_completions(tree, arg):
3161
+ """Returns a list of modified files that match the specified prefix.
3163
+ :param tree: The current tree
3164
+ :type tree: `arch.WorkingTree`
3165
+ :param arg: The prefix to match
3168
+ treepath = arch_compound.tree_cwd(tree)
3169
+ tmpdir = util.tmpdir()
3170
+ changeset = tmpdir+"/changeset"
3172
+ revision = cmdutil.determine_revision_tree(tree)
3173
+ for line in arch.iter_delta(revision, tree, changeset):
3174
+ if isinstance(line, arch.FileModification):
3175
+ file = file_completion_match(line.name[1:], treepath, arg)
3176
+ if file is not None:
3177
+ completions.append(file)
3178
+ shutil.rmtree(tmpdir)
3179
+ return completions
3181
+class Shell(BaseCommand):
3182
+ def __init__(self):
3183
+ self.description = "Runs Fai as a shell"
3185
+ def do_command(self, cmdargs):
3186
+ if len(cmdargs)!=0:
3187
+ raise cmdutil.GetHelp
3188
+ prompt = PromptCmd()
3192
+ prompt.write_history()
3194
+class AddID(BaseCommand):
3196
+ Adds an inventory id for the given file
3198
+ def __init__(self):
3199
+ self.description="Add an inventory id for a given file"
3201
+ def get_completer(self, arg, index):
3202
+ tree = arch.tree_root()
3203
+ return iter_untagged_completions(tree, arg)
3205
+ def do_command(self, cmdargs):
3207
+ Master function that perfoms the "revision" command.
3209
+ parser=self.get_parser()
3210
+ (options, args) = parser.parse_args(cmdargs)
3213
+ tree = arch.tree_root()
3214
+ except arch.errors.TreeRootError, e:
3215
+ raise pylon.errors.CommandFailedWrapper(e)
3218
+ if (len(args) == 0) == (options.untagged == False):
3219
+ raise cmdutil.GetHelp
3221
+ #if options.id and len(args) != 1:
3222
+ # print "If --id is specified, only one file can be named."
3225
+ method = tree.tagging_method
3227
+ if options.id_type == "tagline":
3228
+ if method != "tagline":
3229
+ if not cmdutil.prompt("Tagline in other tree"):
3230
+ if method == "explicit" or method == "implicit":
3231
+ options.id_type == method
3233
+ print "add-id not supported for \"%s\" tagging method"\
3237
+ elif options.id_type == "implicit":
3238
+ if method != "implicit":
3239
+ if not cmdutil.prompt("Implicit in other tree"):
3240
+ if method == "explicit" or method == "tagline":
3241
+ options.id_type == method
3243
+ print "add-id not supported for \"%s\" tagging method"\
3246
+ elif options.id_type == "explicit":
3247
+ if method != "tagline" and method != explicit:
3248
+ if not prompt("Explicit in other tree"):
3249
+ print "add-id not supported for \"%s\" tagging method" % \
3253
+ if options.id_type == "auto":
3254
+ if method != "tagline" and method != "explicit" \
3255
+ and method !="implicit":
3256
+ print "add-id not supported for \"%s\" tagging method" % method
3259
+ options.id_type = method
3260
+ if options.untagged:
3262
+ self.add_ids(tree, options.id_type, args)
3264
+ def add_ids(self, tree, id_type, files=()):
3265
+ """Add inventory ids to files.
3267
+ :param tree: the tree the files are in
3268
+ :type tree: `arch.WorkingTree`
3269
+ :param id_type: the type of id to add: "explicit" or "tagline"
3270
+ :type id_type: str
3271
+ :param files: The list of files to add. If None do all untagged.
3272
+ :type files: tuple of str
3275
+ untagged = (files is None)
3277
+ files = list(iter_untagged(tree, None))
3278
+ previous_files = []
3279
+ while len(files) > 0:
3280
+ previous_files.extend(files)
3281
+ if id_type == "explicit":
3282
+ cmdutil.add_id(files)
3283
+ elif id_type == "tagline" or id_type == "implicit":
3284
+ for file in files:
3286
+ implicit = (id_type == "implicit")
3287
+ cmdutil.add_tagline_or_explicit_id(file, False,
3289
+ except cmdutil.AlreadyTagged:
3290
+ print "\"%s\" already has a tagline." % file
3291
+ except cmdutil.NoCommentSyntax:
3293
+ #do inventory after tagging until no untagged files are encountered
3296
+ for file in iter_untagged(tree, None):
3297
+ if not file in previous_files:
3298
+ files.append(file)
3303
+ def get_parser(self):
3305
+ Returns the options parser to use for the "revision" command.
3307
+ :rtype: cmdutil.CmdOptionParser
3309
+ parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
3310
+# ddaa suggests removing this to promote GUIDs. Let's see who squalks.
3311
+# parser.add_option("-i", "--id", dest="id",
3312
+# help="Specify id for a single file", default=None)
3313
+ parser.add_option("--tltl", action="store_true",
3314
+ dest="lord_style", help="Use Tom Lord's style of id.")
3315
+ parser.add_option("--explicit", action="store_const",
3316
+ const="explicit", dest="id_type",
3317
+ help="Use an explicit id", default="auto")
3318
+ parser.add_option("--tagline", action="store_const",
3319
+ const="tagline", dest="id_type",
3320
+ help="Use a tagline id")
3321
+ parser.add_option("--implicit", action="store_const",
3322
+ const="implicit", dest="id_type",
3323
+ help="Use an implicit id (deprecated)")
3324
+ parser.add_option("--untagged", action="store_true",
3325
+ dest="untagged", default=False,
3326
+ help="tag all untagged files")
3329
+ def help(self, parser=None):
3331
+ Prints a help message.
3333
+ :param parser: If supplied, the parser to use for generating help. If \
3334
+ not supplied, it is retrieved.
3335
+ :type parser: cmdutil.CmdOptionParser
3338
+ parser=self.get_parser()
3339
+ parser.print_help()
3341
+Adds an inventory to the specified file(s) and directories. If --untagged is
3342
+specified, adds inventory to all untagged files and directories.
3347
+class Merge(BaseCommand):
3349
+ Merges changes from other versions into the current tree
3351
+ def __init__(self):
3352
+ self.description="Merges changes from other versions"
3354
+ self.tree = arch.tree_root()
3359
+ def get_completer(self, arg, index):
3360
+ if self.tree is None:
3361
+ raise arch.errors.TreeRootError
3362
+ return cmdutil.merge_completions(self.tree, arg, index)
3364
+ def do_command(self, cmdargs):
3366
+ Master function that perfoms the "merge" command.
3368
+ parser=self.get_parser()
3369
+ (options, args) = parser.parse_args(cmdargs)
3371
+ action="star-merge"
3373
+ action = options.action
3375
+ if self.tree is None:
3376
+ raise arch.errors.TreeRootError(os.getcwd())
3377
+ if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
3378
+ raise UncommittedChanges(self.tree)
3383
+ revisions.append(cmdutil.determine_revision_arch(self.tree,
3385
+ source = "from commandline"
3387
+ revisions = ancillary.iter_partner_revisions(self.tree,
3388
+ self.tree.tree_version)
3389
+ source = "from partner version"
3390
+ revisions = misc.rewind_iterator(revisions)
3393
+ revisions.rewind()
3394
+ except StopIteration, e:
3395
+ revision = cmdutil.tag_cur(self.tree)
3396
+ if revision is None:
3397
+ raise CantDetermineRevision("", "No version specified, no "
3398
+ "partner-versions, and no tag"
3400
+ revisions = [revision]
3401
+ source = "from tag source"
3402
+ for revision in revisions:
3403
+ cmdutil.ensure_archive_registered(revision.archive)
3404
+ cmdutil.colorize(arch.Chatter("* Merging %s [%s]" %
3405
+ (revision, source)))
3406
+ if action=="native-merge" or action=="update":
3407
+ if self.native_merge(revision, action) == 0:
3409
+ elif action=="star-merge":
3411
+ self.star_merge(revision, options.diff3)
3412
+ except errors.MergeProblem, e:
3414
+ if cmdutil.has_changed(self.tree.tree_version):
3417
+ def star_merge(self, revision, diff3):
3418
+ """Perform a star-merge on the current tree.
3420
+ :param revision: The revision to use for the merge
3421
+ :type revision: `arch.Revision`
3422
+ :param diff3: If true, do a diff3 merge
3426
+ for line in self.tree.iter_star_merge(revision, diff3=diff3):
3427
+ cmdutil.colorize(line)
3428
+ except arch.util.ExecProblem, e:
3429
+ if e.proc.status is not None and e.proc.status == 1:
3431
+ print e.proc.error
3432
+ raise MergeProblem
3436
+ def native_merge(self, other_revision, action):
3437
+ """Perform a native-merge on the current tree.
3439
+ :param other_revision: The revision to use for the merge
3440
+ :type other_revision: `arch.Revision`
3441
+ :return: 0 if the merge was skipped, 1 if it was applied
3443
+ other_tree = arch_compound.find_or_make_local_revision(other_revision)
3445
+ if action == "native-merge":
3446
+ ancestor = arch_compound.merge_ancestor2(self.tree, other_tree,
3448
+ elif action == "update":
3449
+ ancestor = arch_compound.tree_latest(self.tree,
3450
+ other_revision.version)
3451
+ except CantDetermineRevision, e:
3452
+ raise CommandFailedWrapper(e)
3453
+ cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
3454
+ if (ancestor == other_revision):
3455
+ cmdutil.colorize(arch.Chatter("* Skipping redundant merge"
3458
+ delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)
3459
+ for line in cmdutil.iter_apply_delta_filter(delta):
3460
+ cmdutil.colorize(line)
3465
+ def get_parser(self):
3467
+ Returns the options parser to use for the "merge" command.
3469
+ :rtype: cmdutil.CmdOptionParser
3471
+ parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
3472
+ parser.add_option("-s", "--star-merge", action="store_const",
3473
+ dest="action", help="Use star-merge",
3474
+ const="star-merge", default="native-merge")
3475
+ parser.add_option("--update", action="store_const",
3476
+ dest="action", help="Use update picker",
3478
+ parser.add_option("--diff3", action="store_true",
3480
+ help="Use diff3 for merge (implies star-merge)")
3483
+ def help(self, parser=None):
3485
+ Prints a help message.
3487
+ :param parser: If supplied, the parser to use for generating help. If \
3488
+ not supplied, it is retrieved.
3489
+ :type parser: cmdutil.CmdOptionParser
3492
+ parser=self.get_parser()
3493
+ parser.print_help()
3495
+Performs a merge operation using the specified version.
3499
+class ELog(BaseCommand):
3501
+ Produces a raw patchlog and invokes the user's editor
3503
+ def __init__(self):
3504
+ self.description="Edit a patchlog to commit"
3506
+ self.tree = arch.tree_root()
3511
+ def do_command(self, cmdargs):
3513
+ Master function that perfoms the "elog" command.
3515
+ parser=self.get_parser()
3516
+ (options, args) = parser.parse_args(cmdargs)
3517
+ if self.tree is None:
3518
+ raise arch.errors.TreeRootError
3521
+ edit_log(self.tree, self.tree.tree_version)
3522
+ except pylon.errors.NoEditorSpecified, e:
3523
+ raise pylon.errors.CommandFailedWrapper(e)
3525
+ def get_parser(self):
3527
+ Returns the options parser to use for the "merge" command.
3529
+ :rtype: cmdutil.CmdOptionParser
3531
+ parser=cmdutil.CmdOptionParser("fai elog")
3535
+ def help(self, parser=None):
3537
+ Invokes $EDITOR to produce a log for committing.
3539
+ :param parser: If supplied, the parser to use for generating help. If \
3540
+ not supplied, it is retrieved.
3541
+ :type parser: cmdutil.CmdOptionParser
3544
+ parser=self.get_parser()
3545
+ parser.print_help()
3547
+Invokes $EDITOR to produce a log for committing.
3551
+def edit_log(tree, version):
3552
+ """Makes and edits the log for a tree. Does all kinds of fancy things
3553
+ like log templates and merge summaries and log-for-merge
3555
+ :param tree: The tree to edit the log for
3556
+ :type tree: `arch.WorkingTree`
3558
+ #ensure we have an editor before preparing the log
3559
+ cmdutil.find_editor()
3560
+ log = tree.log_message(create=False, version=version)
3561
+ log_is_new = False
3562
+ if log is None or cmdutil.prompt("Overwrite log"):
3563
+ if log is not None:
3564
+ os.remove(log.name)
3565
+ log = tree.log_message(create=True, version=version)
3568
+ template = pylon.log_template_path(tree)
3570
+ shutil.copyfile(template, tmplog)
3571
+ comp_version = ancillary.comp_revision(tree).version
3572
+ new_merges = cmdutil.iter_new_merges(tree, comp_version)
3573
+ new_merges = cmdutil.direct_merges(new_merges)
3574
+ log["Summary"] = pylon.merge_summary(new_merges,
3576
+ if len(new_merges) > 0:
3577
+ if cmdutil.prompt("Log for merge"):
3578
+ if cmdutil.prompt("changelog for merge"):
3579
+ mergestuff = "Patches applied:\n"
3580
+ mergestuff += pylon.changelog_for_merge(new_merges)
3582
+ mergestuff = cmdutil.log_for_merge(tree, comp_version)
3583
+ log.description += mergestuff
3586
+ cmdutil.invoke_editor(log.name)
3589
+ os.remove(log.name)
3593
+class MirrorArchive(BaseCommand):
3595
+ Updates a mirror from an archive
3597
+ def __init__(self):
3598
+ self.description="Update a mirror from an archive"
3600
+ def do_command(self, cmdargs):
3602
+ Master function that perfoms the "revision" command.
3605
+ parser=self.get_parser()
3606
+ (options, args) = parser.parse_args(cmdargs)
3610
+ tree = arch.tree_root()
3614
+ if len(args) == 0:
3615
+ if tree is not None:
3616
+ name = tree.tree_version()
3618
+ name = cmdutil.expand_alias(args[0], tree)
3619
+ name = arch.NameParser(name)
3621
+ to_arch = name.get_archive()
3622
+ from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
3623
+ limit = name.get_nonarch()
3625
+ iter = arch_core.mirror_archive(from_arch,to_arch, limit)
3626
+ for line in arch.chatter_classifier(iter):
3627
+ cmdutil.colorize(line)
3629
+ def get_parser(self):
3631
+ Returns the options parser to use for the "revision" command.
3633
+ :rtype: cmdutil.CmdOptionParser
3635
+ parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
3638
+ def help(self, parser=None):
3640
+ Prints a help message.
3642
+ :param parser: If supplied, the parser to use for generating help. If \
3643
+ not supplied, it is retrieved.
3644
+ :type parser: cmdutil.CmdOptionParser
3647
+ parser=self.get_parser()
3648
+ parser.print_help()
3650
+Updates a mirror from an archive. If a branch, package, or version is
3651
+supplied, only changes under it are mirrored.
3655
+def help_tree_spec():
3656
+ print """Specifying revisions (default: tree)
3657
+Revisions may be specified by alias, revision, version or patchlevel.
3658
+Revisions or versions may be fully qualified. Unqualified revisions, versions,
3659
+or patchlevels use the archive of the current project tree. Versions will
3660
+use the latest patchlevel in the tree. Patchlevels will use the current tree-
3663
+Use "alias" to list available (user and automatic) aliases."""
3667
+"The latest revision in the archive of the tree-version. You can specify \
3668
+a different version like so: acur:foo--bar--0 (aliases can be used)",
3670
+"""(tree current) The latest revision in the tree of the tree-version. \
3671
+You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
3674
+"""(tree previous) The previous revision in the tree of the tree-version. To \
3675
+specify an older revision, use a number, e.g. "tprev:4" """,
3677
+"""(tree ancestor) The ancestor revision of the tree To specify an older \
3678
+revision, use a number, e.g. "tanc:4".""",
3680
+"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
3682
+""" (tree modified) The latest revision to modify a given file, e.g. \
3683
+"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
3685
+"""(tree tag) The revision that was tagged into the current tree revision, \
3686
+according to the tree""",
3688
+"""(tag current) The latest revision of the version that the current tree \
3689
+was tagged from.""",
3691
+"""The common ancestor of the current tree and the specified revision. \
3692
+Defaults to the first partner-version's latest revision or to tagcur.""",
3696
+def is_auto_alias(name):
3697
+ """Determine whether a name is an auto alias name
3699
+ :param name: the name to check
3701
+ :return: True if the name is an auto alias, false if not
3704
+ return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
3707
+def display_def(iter, wrap = 80):
3708
+ """Display a list of definitions
3710
+ :param iter: iter of name, definition pairs
3711
+ :type iter: iter of (str, str)
3712
+ :param wrap: The width for text wrapping
3717
+ for (key, value) in vals:
3718
+ if len(key) > maxlen:
3720
+ for (key, value) in vals:
3721
+ tw=textwrap.TextWrapper(width=wrap,
3722
+ initial_indent=key.rjust(maxlen)+" : ",
3723
+ subsequent_indent="".rjust(maxlen+3))
3724
+ print tw.fill(value)
3727
+def help_aliases(tree):
3728
+ print """Auto-generated aliases"""
3729
+ display_def(pylon.util.iter_pairs(auto_alias))
3730
+ print "User aliases"
3731
+ display_def(ancillary.iter_all_alias(tree))
3733
+class Inventory(BaseCommand):
3734
+ """List the status of files in the tree"""
3735
+ def __init__(self):
3736
+ self.description=self.__doc__
3738
+ def do_command(self, cmdargs):
3740
+ Master function that perfoms the "revision" command.
3743
+ parser=self.get_parser()
3744
+ (options, args) = parser.parse_args(cmdargs)
3745
+ tree = arch.tree_root()
3748
+ if (options.source):
3749
+ categories.append(arch_core.SourceFile)
3750
+ if (options.precious):
3751
+ categories.append(arch_core.PreciousFile)
3752
+ if (options.backup):
3753
+ categories.append(arch_core.BackupFile)
3754
+ if (options.junk):
3755
+ categories.append(arch_core.JunkFile)
3757
+ if len(categories) == 1:
3758
+ show_leading = False
3760
+ show_leading = True
3762
+ if len(categories) == 0:
3765
+ if options.untagged:
3766
+ categories = arch_core.non_root
3767
+ show_leading = False
3772
+ for file in arch_core.iter_inventory_filter(tree, None,
3773
+ control_files=options.control_files,
3774
+ categories = categories, tagged=tagged):
3775
+ print arch_core.file_line(file,
3776
+ category = show_leading,
3777
+ untagged = show_leading,
3780
+ def get_parser(self):
3782
+ Returns the options parser to use for the "revision" command.
3784
+ :rtype: cmdutil.CmdOptionParser
3786
+ parser=cmdutil.CmdOptionParser("fai inventory [options]")
3787
+ parser.add_option("--ids", action="store_true", dest="ids",
3788
+ help="Show file ids")
3789
+ parser.add_option("--control", action="store_true",
3790
+ dest="control_files", help="include control files")
3791
+ parser.add_option("--source", action="store_true", dest="source",
3792
+ help="List source files")
3793
+ parser.add_option("--backup", action="store_true", dest="backup",
3794
+ help="List backup files")
3795
+ parser.add_option("--precious", action="store_true", dest="precious",
3796
+ help="List precious files")
3797
+ parser.add_option("--junk", action="store_true", dest="junk",
3798
+ help="List junk files")
3799
+ parser.add_option("--unrecognized", action="store_true",
3800
+ dest="unrecognized", help="List unrecognized files")
3801
+ parser.add_option("--untagged", action="store_true",
3802
+ dest="untagged", help="List only untagged files")
3805
+ def help(self, parser=None):
3807
+ Prints a help message.
3809
+ :param parser: If supplied, the parser to use for generating help. If \
3810
+ not supplied, it is retrieved.
3811
+ :type parser: cmdutil.CmdOptionParser
3814
+ parser=self.get_parser()
3815
+ parser.print_help()
3817
+Lists the status of files in the archive:
3825
+Leading letter are not displayed if only one kind of file is shown
3830
+class Alias(BaseCommand):
3831
+ """List or adjust aliases"""
3832
+ def __init__(self):
3833
+ self.description=self.__doc__
3835
+ def get_completer(self, arg, index):
3839
+ self.tree = arch.tree_root()
3844
+ return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
3846
+ return cmdutil.iter_revision_completions(arg, self.tree)
3849
+ def do_command(self, cmdargs):
3851
+ Master function that perfoms the "revision" command.
3854
+ parser=self.get_parser()
3855
+ (options, args) = parser.parse_args(cmdargs)
3857
+ self.tree = arch.tree_root()
3863
+ options.action(args, options)
3864
+ except cmdutil.ForbiddenAliasSyntax, e:
3865
+ raise CommandFailedWrapper(e)
3867
+ def no_prefix(self, alias):
3868
+ if alias.startswith("^"):
3872
+ def arg_dispatch(self, args, options):
3873
+ """Add, modify, or list aliases, depending on number of arguments
3875
+ :param args: The list of commandline arguments
3876
+ :type args: list of str
3877
+ :param options: The commandline options
3879
+ if len(args) == 0:
3880
+ help_aliases(self.tree)
3883
+ alias = self.no_prefix(args[0])
3884
+ if len(args) == 1:
3885
+ self.print_alias(alias)
3886
+ elif (len(args)) == 2:
3887
+ self.add(alias, args[1], options)
3889
+ raise cmdutil.GetHelp
3891
+ def print_alias(self, alias):
3893
+ if is_auto_alias(alias):
3894
+ raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
3895
+ " Use \"revision\" to expand auto aliases." % alias)
3896
+ for pair in ancillary.iter_all_alias(self.tree):
3897
+ if pair[0] == alias:
3899
+ if answer is not None:
3902
+ print "The alias %s is not assigned." % alias
3904
+ def add(self, alias, expansion, options):
3905
+ """Add or modify aliases
3907
+ :param alias: The alias name to create/modify
3909
+ :param expansion: The expansion to assign to the alias name
3910
+ :type expansion: str
3911
+ :param options: The commandline options
3913
+ if is_auto_alias(alias):
3914
+ raise IsAutoAlias(alias)
3917
+ new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion,
3919
+ ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
3921
+ for pair in self.get_iterator(options):
3922
+ if pair[0] != alias:
3923
+ newlist+="%s=%s\n" % (pair[0], pair[1])
3929
+ self.write_aliases(newlist, options)
3931
+ def delete(self, args, options):
3932
+ """Delete the specified alias
3934
+ :param args: The list of arguments
3935
+ :type args: list of str
3936
+ :param options: The commandline options
3939
+ if len(args) != 1:
3940
+ raise cmdutil.GetHelp
3941
+ alias = self.no_prefix(args[0])
3942
+ if is_auto_alias(alias):
3943
+ raise IsAutoAlias(alias)
3945
+ for pair in self.get_iterator(options):
3946
+ if pair[0] != alias:
3947
+ newlist+="%s=%s\n" % (pair[0], pair[1])
3951
+ raise errors.NoSuchAlias(alias)
3952
+ self.write_aliases(newlist, options)
3954
+ def get_alias_file(self, options):
3955
+ """Return the name of the alias file to use
3957
+ :param options: The commandline options
3960
+ if self.tree is None:
3961
+ self.tree == arch.tree_root()
3962
+ return str(self.tree)+"/{arch}/+aliases"
3964
+ return "~/.aba/aliases"
3966
+ def get_iterator(self, options):
3967
+ """Return the alias iterator to use
3969
+ :param options: The commandline options
3971
+ return ancillary.iter_alias(self.get_alias_file(options))
3973
+ def write_aliases(self, newlist, options):
3974
+ """Safely rewrite the alias file
3975
+ :param newlist: The new list of aliases
3976
+ :type newlist: str
3977
+ :param options: The commandline options
3979
+ filename = os.path.expanduser(self.get_alias_file(options))
3980
+ file = util.NewFileVersion(filename)
3981
+ file.write(newlist)
3985
+ def get_parser(self):
3987
+ Returns the options parser to use for the "alias" command.
3989
+ :rtype: cmdutil.CmdOptionParser
3991
+ parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
3992
+ parser.add_option("-d", "--delete", action="store_const", dest="action",
3993
+ const=self.delete, default=self.arg_dispatch,
3994
+ help="Delete an alias")
3995
+ parser.add_option("--tree", action="store_true", dest="tree",
3996
+ help="Create a per-tree alias", default=False)
3999
+ def help(self, parser=None):
4001
+ Prints a help message.
4003
+ :param parser: If supplied, the parser to use for generating help. If \
4004
+ not supplied, it is retrieved.
4005
+ :type parser: cmdutil.CmdOptionParser
4008
+ parser=self.get_parser()
4009
+ parser.print_help()
4011
+Lists current aliases or modifies the list of aliases.
4013
+If no arguments are supplied, aliases will be listed. If two arguments are
4014
+supplied, the specified alias will be created or modified. If -d or --delete
4015
+is supplied, the specified alias will be deleted.
4017
+You can create aliases that refer to any fully-qualified part of the
4018
+Arch namespace, e.g.
4021
+archive/category--branch,
4022
+archive/category--branch--version (my favourite)
4023
+archive/category--branch--version--patchlevel
4025
+Aliases can be used automatically by native commands. To use them
4026
+with external or tla commands, prefix them with ^ (you can do this
4027
+with native commands, too).
4031
+class RequestMerge(BaseCommand):
4032
+ """Submit a merge request to Bug Goo"""
4033
+ def __init__(self):
4034
+ self.description=self.__doc__
4036
+ def do_command(self, cmdargs):
4037
+ """Submit a merge request
4039
+ :param cmdargs: The commandline arguments
4040
+ :type cmdargs: list of str
4042
+ parser = self.get_parser()
4043
+ (options, args) = parser.parse_args(cmdargs)
4045
+ cmdutil.find_editor()
4046
+ except pylon.errors.NoEditorSpecified, e:
4047
+ raise pylon.errors.CommandFailedWrapper(e)
4049
+ self.tree=arch.tree_root()
4052
+ base, revisions = self.revision_specs(args)
4053
+ message = self.make_headers(base, revisions)
4054
+ message += self.make_summary(revisions)
4055
+ path = self.edit_message(message)
4056
+ message = self.tidy_message(path)
4057
+ if cmdutil.prompt("Send merge"):
4058
+ self.send_message(message)
4059
+ print "Merge request sent"
4061
+ def make_headers(self, base, revisions):
4062
+ """Produce email and Bug Goo header strings
4064
+ :param base: The base revision to apply merges to
4065
+ :type base: `arch.Revision`
4066
+ :param revisions: The revisions to replay into the base
4067
+ :type revisions: list of `arch.Patchlog`
4068
+ :return: The headers
4071
+ headers = "To: gnu-arch-users@gnu.org\n"
4072
+ headers += "From: %s\n" % options.fromaddr
4073
+ if len(revisions) == 1:
4074
+ headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
4076
+ headers += "Subject: [MERGE REQUEST]\n"
4078
+ headers += "Base-Revision: %s\n" % base
4079
+ for revision in revisions:
4080
+ headers += "Revision: %s\n" % revision.revision
4081
+ headers += "Bug: \n\n"
4084
+ def make_summary(self, logs):
4085
+ """Generate a summary of merges
4087
+ :param logs: the patchlogs that were directly added by the merges
4088
+ :type logs: list of `arch.Patchlog`
4089
+ :return: the summary
4094
+ summary+=str(log.revision)+"\n"
4095
+ summary+=log.summary+"\n"
4096
+ if log.description.strip():
4097
+ summary+=log.description.strip('\n')+"\n\n"
4100
+ def revision_specs(self, args):
4101
+ """Determine the base and merge revisions from tree and arguments.
4103
+ :param args: The parsed arguments
4104
+ :type args: list of str
4105
+ :return: The base revision and merge revisions
4106
+ :rtype: `arch.Revision`, list of `arch.Patchlog`
4109
+ target_revision = cmdutil.determine_revision_arch(self.tree,
4112
+ target_revision = arch_compound.tree_latest(self.tree)
4114
+ merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
4115
+ self.tree, f)) for f in args[1:] ]
4117
+ if self.tree is None:
4118
+ raise CantDetermineRevision("", "Not in a project tree")
4119
+ merge_iter = cmdutil.iter_new_merges(self.tree,
4120
+ target_revision.version,
4122
+ merges = [f for f in cmdutil.direct_merges(merge_iter)]
4123
+ return (target_revision, merges)
4125
+ def edit_message(self, message):
4126
+ """Edit an email message in the user's standard editor
4128
+ :param message: The message to edit
4129
+ :type message: str
4130
+ :return: the path of the edited message
4133
+ if self.tree is None:
4134
+ path = os.get_cwd()
4137
+ path += "/,merge-request"
4138
+ file = open(path, 'w')
4139
+ file.write(message)
4141
+ cmdutil.invoke_editor(path)
4144
+ def tidy_message(self, path):
4145
+ """Validate and clean up message.
4147
+ :param path: The path to the message to clean up
4149
+ :return: The parsed message
4150
+ :rtype: `email.Message`
4152
+ mail = email.message_from_file(open(path))
4153
+ if mail["Subject"].strip() == "[MERGE REQUEST]":
4154
+ raise BlandSubject
4156
+ request = email.message_from_string(mail.get_payload())
4157
+ if request.has_key("Bug"):
4158
+ if request["Bug"].strip()=="":
4159
+ del request["Bug"]
4160
+ mail.set_payload(request.as_string())
4163
+ def send_message(self, message):
4164
+ """Send a message, using its headers to address it.
4166
+ :param message: The message to send
4167
+ :type message: `email.Message`"""
4168
+ server = smtplib.SMTP("localhost")
4169
+ server.sendmail(message['From'], message['To'], message.as_string())
4172
+ def help(self, parser=None):
4173
+ """Print a usage message
4175
+ :param parser: The options parser to use
4176
+ :type parser: `cmdutil.CmdOptionParser`
4178
+ if parser is None:
4179
+ parser = self.get_parser()
4180
+ parser.print_help()
4182
+Sends a merge request formatted for Bug Goo. Intended use: get the tree
4183
+you'd like to merge into. Apply the merges you want. Invoke request-merge.
4184
+The merge request will open in your $EDITOR.
4186
+When no TARGET is specified, it uses the current tree revision. When
4187
+no MERGE is specified, it uses the direct merges (as in "revisions
4188
+--direct-merges"). But you can specify just the TARGET, or all the MERGE
4192
+ def get_parser(self):
4193
+ """Produce a commandline parser for this command.
4195
+ :rtype: `cmdutil.CmdOptionParser`
4197
+ parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
4201
+'changes' : Changes,
4204
+'apply-changes':ApplyChanges,
4207
+'revision': Revision,
4208
+'revisions': Revisions,
4215
+'mirror-archive': MirrorArchive,
4216
+'ninventory': Inventory,
4218
+'request-merge': RequestMerge,
4221
+def my_import(mod_name):
4222
+ module = __import__(mod_name)
4223
+ components = mod_name.split('.')
4224
+ for comp in components[1:]:
4225
+ module = getattr(module, comp)
4228
+def plugin(mod_name):
4229
+ module = my_import(mod_name)
4230
+ module.add_command(commands)
4232
+for file in os.listdir(sys.path[0]+"/command"):
4233
+ if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
4234
+ plugin("command."+file[:-3])
4237
+'apply-delta' : "Try \"apply-changes\".",
4238
+'delta' : "To compare two revisions, use \"changes\".",
4239
+'diff-rev' : "To compare two revisions, use \"changes\".",
4240
+'undo' : "To undo local changes, use \"revert\".",
4241
+'undelete' : "To undo only deletions, use \"revert --deletions\"",
4242
+'missing-from' : "Try \"revisions --missing-from\".",
4243
+'missing' : "Try \"revisions --missing\".",
4244
+'missing-merge' : "Try \"revisions --partner-missing\".",
4245
+'new-merges' : "Try \"revisions --new-merges\".",
4246
+'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
4247
+'logs' : "Try \"revisions --logs\"",
4248
+'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
4249
+'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
4250
+'change-version' : "Try \"update REVISION\"",
4251
+'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
4252
+'rev-depends' : "Use revisions --dependencies",
4253
+'auto-get' : "Plain get will do archive lookups",
4254
+'tagline' : "Use add-id. It uses taglines in tagline trees",
4255
+'emlog' : "Use elog. It automatically adds log-for-merge text, if any",
4256
+'library-revisions' : "Use revisions --library",
4257
+'file-revert' : "Use revert FILE",
4258
+'join-branch' : "Use replay --logs-only"
4260
+# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
4262
*** added file 'testdata/orig'
4266
+# Copyright (C) 2004 Aaron Bentley
4267
+# <aaron.bentley@utoronto.ca>
4269
+# This program is free software; you can redistribute it and/or modify
4270
+# it under the terms of the GNU General Public License as published by
4271
+# the Free Software Foundation; either version 2 of the License, or
4272
+# (at your option) any later version.
4274
+# This program is distributed in the hope that it will be useful,
4275
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4276
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4277
+# GNU General Public License for more details.
4279
+# You should have received a copy of the GNU General Public License
4280
+# along with this program; if not, write to the Free Software
4281
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
4299
+from errors import *
4307
+__docformat__ = "restructuredtext"
4308
+__doc__ = "Implementation of user (sub) commands"
4311
+def find_command(cmd):
4313
+ Return an instance of a command type. Return None if the type isn't
4316
+ :param cmd: the name of the command to look for
4317
+ :type cmd: the type of the command
4319
+ if commands.has_key(cmd):
4320
+ return commands[cmd]()
4325
+ def __call__(self, cmdline):
4327
+ self.do_command(cmdline.split())
4328
+ except cmdutil.GetHelp, e:
4330
+ except Exception, e:
4333
+ def get_completer(index):
4336
+ def complete(self, args, text):
4338
+ Returns a list of possible completions for the given text.
4340
+ :param args: The complete list of arguments
4341
+ :type args: List of str
4342
+ :param text: text to complete (may be shorter than args[-1])
4344
+ :rtype: list of str
4350
+ realtext = args[-1]
4355
+ parser=self.get_parser()
4356
+ if realtext.startswith('-'):
4357
+ candidates = parser.iter_options()
4359
+ (options, parsed_args) = parser.parse_args(args)
4361
+ if len (parsed_args) > 0:
4362
+ candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
4364
+ candidates = self.get_completer("", 0)
4367
+ if candidates is None:
4369
+ for candidate in candidates:
4370
+ candidate = str(candidate)
4371
+ if candidate.startswith(realtext):
4372
+ matches.append(candidate[len(realtext)- len(text):])
4376
+class Help(BaseCommand):
4378
+ Lists commands, prints help messages.
4380
+ def __init__(self):
4381
+ self.description="Prints help mesages"
4382
+ self.parser = None
4384
+ def do_command(self, cmdargs):
4386
+ Prints a help message.
4388
+ options, args = self.get_parser().parse_args(cmdargs)
4390
+ raise cmdutil.GetHelp
4392
+ if options.native or options.suggestions or options.external:
4393
+ native = options.native
4394
+ suggestions = options.suggestions
4395
+ external = options.external
4398
+ suggestions = False
4401
+ if len(args) == 0:
4402
+ self.list_commands(native, suggestions, external)
4404
+ elif len(args) == 1:
4405
+ command_help(args[0])
4409
+ self.get_parser().print_help()
4411
+If no command is specified, commands are listed. If a command is
4412
+specified, help for that command is listed.
4415
+ def get_parser(self):
4417
+ Returns the options parser to use for the "revision" command.
4419
+ :rtype: cmdutil.CmdOptionParser
4421
+ if self.parser is not None:
4422
+ return self.parser
4423
+ parser=cmdutil.CmdOptionParser("fai help [command]")
4424
+ parser.add_option("-n", "--native", action="store_true",
4425
+ dest="native", help="Show native commands")
4426
+ parser.add_option("-e", "--external", action="store_true",
4427
+ dest="external", help="Show external commands")
4428
+ parser.add_option("-s", "--suggest", action="store_true",
4429
+ dest="suggestions", help="Show suggestions")
4430
+ self.parser = parser
4433
+ def list_commands(self, native=True, suggest=False, external=True):
4435
+ Lists supported commands.
4437
+ :param native: list native, python-based commands
4438
+ :type native: bool
4439
+ :param external: list external aba-style commands
4440
+ :type external: bool
4443
+ print "Native Fai commands"
4444
+ keys=commands.keys()
4448
+ for i in range(28-len(k)):
4450
+ print space+k+" : "+commands[k]().description
4453
+ print "Unavailable commands and suggested alternatives"
4454
+ key_list = suggestions.keys()
4456
+ for key in key_list:
4457
+ print "%28s : %s" % (key, suggestions[key])
4460
+ fake_aba = abacmds.AbaCmds()
4461
+ if (fake_aba.abadir == ""):
4463
+ print "External commands"
4464
+ fake_aba.list_commands()
4467
+ print "Use help --suggest to list alternatives to tla and aba"\
4469
+ if options.tla_fallthrough and (native or external):
4470
+ print "Fai also supports tla commands."
4472
+def command_help(cmd):
4474
+ Prints help for a command.
4476
+ :param cmd: The name of the command to print help for
4479
+ fake_aba = abacmds.AbaCmds()
4480
+ cmdobj = find_command(cmd)
4481
+ if cmdobj != None:
4483
+ elif suggestions.has_key(cmd):
4484
+ print "Not available\n" + suggestions[cmd]
4486
+ abacmd = fake_aba.is_command(cmd)
4490
+ print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
4494
+class Changes(BaseCommand):
4496
+ the "changes" command: lists differences between trees/revisions:
4499
+ def __init__(self):
4500
+ self.description="Lists what files have changed in the project tree"
4502
+ def get_completer(self, arg, index):
4506
+ tree = arch.tree_root()
4509
+ return cmdutil.iter_revision_completions(arg, tree)
4511
+ def parse_commandline(self, cmdline):
4513
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
4515
+ :param cmdline: A list of arguments to parse
4516
+ :rtype: (options, Revision, Revision/WorkingTree)
4518
+ parser=self.get_parser()
4519
+ (options, args) = parser.parse_args(cmdline)
4521
+ raise cmdutil.GetHelp
4523
+ tree=arch.tree_root()
4524
+ if len(args) == 0:
4525
+ a_spec = cmdutil.comp_revision(tree)
4527
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
4528
+ cmdutil.ensure_archive_registered(a_spec.archive)
4529
+ if len(args) == 2:
4530
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
4531
+ cmdutil.ensure_archive_registered(b_spec.archive)
4534
+ return options, a_spec, b_spec
4536
+ def do_command(self, cmdargs):
4538
+ Master function that perfoms the "changes" command.
4541
+ options, a_spec, b_spec = self.parse_commandline(cmdargs);
4542
+ except cmdutil.CantDetermineRevision, e:
4545
+ except arch.errors.TreeRootError, e:
4548
+ if options.changeset:
4549
+ changeset=options.changeset
4552
+ tmpdir=cmdutil.tmpdir()
4553
+ changeset=tmpdir+"/changeset"
4555
+ delta=arch.iter_delta(a_spec, b_spec, changeset)
4557
+ for line in delta:
4558
+ if cmdutil.chattermatch(line, "changeset:"):
4561
+ cmdutil.colorize(line, options.suppress_chatter)
4562
+ except arch.util.ExecProblem, e:
4563
+ if e.proc.error and e.proc.error.startswith(
4564
+ "missing explicit id for file"):
4565
+ raise MissingID(e)
4568
+ status=delta.status
4571
+ if (options.perform_diff):
4572
+ chan = cmdutil.ChangesetMunger(changeset)
4573
+ chan.read_indices()
4574
+ if isinstance(b_spec, arch.Revision):
4575
+ b_dir = b_spec.library_find()
4578
+ a_dir = a_spec.library_find()
4579
+ if options.diffopts is not None:
4580
+ diffopts = options.diffopts.split()
4581
+ cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
4583
+ cmdutil.show_diffs(delta.changeset)
4585
+ if tmpdir and (os.access(tmpdir, os.X_OK)):
4586
+ shutil.rmtree(tmpdir)
4588
+ def get_parser(self):
4590
+ Returns the options parser to use for the "changes" command.
4592
+ :rtype: cmdutil.CmdOptionParser
4594
+ parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
4596
+ parser.add_option("-d", "--diff", action="store_true",
4597
+ dest="perform_diff", default=False,
4598
+ help="Show diffs in summary")
4599
+ parser.add_option("-c", "--changeset", dest="changeset",
4600
+ help="Store a changeset in the given directory",
4601
+ metavar="DIRECTORY")
4602
+ parser.add_option("-s", "--silent", action="store_true",
4603
+ dest="suppress_chatter", default=False,
4604
+ help="Suppress chatter messages")
4605
+ parser.add_option("--diffopts", dest="diffopts",
4606
+ help="Use the specified diff options",
4607
+ metavar="OPTIONS")
4611
+ def help(self, parser=None):
4613
+ Prints a help message.
4615
+ :param parser: If supplied, the parser to use for generating help. If \
4616
+ not supplied, it is retrieved.
4617
+ :type parser: cmdutil.CmdOptionParser
4619
+ if parser is None:
4620
+ parser=self.get_parser()
4621
+ parser.print_help()
4623
+Performs source-tree comparisons
4625
+If no revision is specified, the current project tree is compared to the
4626
+last-committed revision. If one revision is specified, the current project
4627
+tree is compared to that revision. If two revisions are specified, they are
4628
+compared to each other.
4634
+class ApplyChanges(BaseCommand):
4636
+ Apply differences between two revisions to a tree
4639
+ def __init__(self):
4640
+ self.description="Applies changes to a project tree"
4642
+ def get_completer(self, arg, index):
4646
+ tree = arch.tree_root()
4649
+ return cmdutil.iter_revision_completions(arg, tree)
4651
+ def parse_commandline(self, cmdline, tree):
4653
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
4655
+ :param cmdline: A list of arguments to parse
4656
+ :rtype: (options, Revision, Revision/WorkingTree)
4658
+ parser=self.get_parser()
4659
+ (options, args) = parser.parse_args(cmdline)
4660
+ if len(args) != 2:
4661
+ raise cmdutil.GetHelp
4663
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
4664
+ cmdutil.ensure_archive_registered(a_spec.archive)
4665
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
4666
+ cmdutil.ensure_archive_registered(b_spec.archive)
4667
+ return options, a_spec, b_spec
4669
+ def do_command(self, cmdargs):
4671
+ Master function that performs "apply-changes".
4674
+ tree = arch.tree_root()
4675
+ options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
4676
+ except cmdutil.CantDetermineRevision, e:
4679
+ except arch.errors.TreeRootError, e:
4682
+ delta=cmdutil.apply_delta(a_spec, b_spec, tree)
4683
+ for line in cmdutil.iter_apply_delta_filter(delta):
4684
+ cmdutil.colorize(line, options.suppress_chatter)
4686
+ def get_parser(self):
4688
+ Returns the options parser to use for the "apply-changes" command.
4690
+ :rtype: cmdutil.CmdOptionParser
4692
+ parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
4694
+ parser.add_option("-d", "--diff", action="store_true",
4695
+ dest="perform_diff", default=False,
4696
+ help="Show diffs in summary")
4697
+ parser.add_option("-c", "--changeset", dest="changeset",
4698
+ help="Store a changeset in the given directory",
4699
+ metavar="DIRECTORY")
4700
+ parser.add_option("-s", "--silent", action="store_true",
4701
+ dest="suppress_chatter", default=False,
4702
+ help="Suppress chatter messages")
4705
+ def help(self, parser=None):
4707
+ Prints a help message.
4709
+ :param parser: If supplied, the parser to use for generating help. If \
4710
+ not supplied, it is retrieved.
4711
+ :type parser: cmdutil.CmdOptionParser
4713
+ if parser is None:
4714
+ parser=self.get_parser()
4715
+ parser.print_help()
4717
+Applies changes to a project tree
4719
+Compares two revisions and applies the difference between them to the current
4725
+class Update(BaseCommand):
4727
+ Updates a project tree to a given revision, preserving un-committed hanges.
4730
+ def __init__(self):
4731
+ self.description="Apply the latest changes to the current directory"
4733
+ def get_completer(self, arg, index):
4737
+ tree = arch.tree_root()
4740
+ return cmdutil.iter_revision_completions(arg, tree)
4742
+ def parse_commandline(self, cmdline, tree):
4744
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
4746
+ :param cmdline: A list of arguments to parse
4747
+ :rtype: (options, Revision, Revision/WorkingTree)
4749
+ parser=self.get_parser()
4750
+ (options, args) = parser.parse_args(cmdline)
4752
+ raise cmdutil.GetHelp
4757
+ revision=cmdutil.determine_revision_arch(tree, spec)
4758
+ cmdutil.ensure_archive_registered(revision.archive)
4760
+ mirror_source = cmdutil.get_mirror_source(revision.archive)
4761
+ if mirror_source != None:
4762
+ if cmdutil.prompt("Mirror update"):
4763
+ cmd=cmdutil.mirror_archive(mirror_source,
4764
+ revision.archive, arch.NameParser(revision).get_package_version())
4765
+ for line in arch.chatter_classifier(cmd):
4766
+ cmdutil.colorize(line, options.suppress_chatter)
4768
+ revision=cmdutil.determine_revision_arch(tree, spec)
4770
+ return options, revision
4772
+ def do_command(self, cmdargs):
4774
+ Master function that perfoms the "update" command.
4776
+ tree=arch.tree_root()
4778
+ options, to_revision = self.parse_commandline(cmdargs, tree);
4779
+ except cmdutil.CantDetermineRevision, e:
4782
+ except arch.errors.TreeRootError, e:
4785
+ from_revision=cmdutil.tree_latest(tree)
4786
+ if from_revision==to_revision:
4787
+ print "Tree is already up to date with:\n"+str(to_revision)+"."
4789
+ cmdutil.ensure_archive_registered(from_revision.archive)
4790
+ cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
4791
+ options.patch_forward)
4792
+ for line in cmdutil.iter_apply_delta_filter(cmd):
4793
+ cmdutil.colorize(line)
4794
+ if to_revision.version != tree.tree_version:
4795
+ if cmdutil.prompt("Update version"):
4796
+ tree.tree_version = to_revision.version
4798
+ def get_parser(self):
4800
+ Returns the options parser to use for the "update" command.
4802
+ :rtype: cmdutil.CmdOptionParser
4804
+ parser=cmdutil.CmdOptionParser("fai update [options]"
4805
+ " [revision/version]")
4806
+ parser.add_option("-f", "--forward", action="store_true",
4807
+ dest="patch_forward", default=False,
4808
+ help="pass the --forward option to 'patch'")
4809
+ parser.add_option("-s", "--silent", action="store_true",
4810
+ dest="suppress_chatter", default=False,
4811
+ help="Suppress chatter messages")
4814
+ def help(self, parser=None):
4816
+ Prints a help message.
4818
+ :param parser: If supplied, the parser to use for generating help. If \
4819
+ not supplied, it is retrieved.
4820
+ :type parser: cmdutil.CmdOptionParser
4822
+ if parser is None:
4823
+ parser=self.get_parser()
4824
+ parser.print_help()
4826
+Updates a working tree to the current archive revision
4828
+If a revision or version is specified, that is used instead
4834
+class Commit(BaseCommand):
4836
+ Create a revision based on the changes in the current tree.
4839
+ def __init__(self):
4840
+ self.description="Write local changes to the archive"
4842
+ def get_completer(self, arg, index):
4845
+ return iter_modified_file_completions(arch.tree_root(), arg)
4846
+# return iter_source_file_completions(arch.tree_root(), arg)
4848
+ def parse_commandline(self, cmdline, tree):
4850
+ Parse commandline arguments. Raise cmtutil.GetHelp if help is needed.
4852
+ :param cmdline: A list of arguments to parse
4853
+ :rtype: (options, Revision, Revision/WorkingTree)
4855
+ parser=self.get_parser()
4856
+ (options, args) = parser.parse_args(cmdline)
4858
+ if len(args) == 0:
4860
+ revision=cmdutil.determine_revision_arch(tree, options.version)
4861
+ return options, revision.get_version(), args
4863
+ def do_command(self, cmdargs):
4865
+ Master function that perfoms the "commit" command.
4867
+ tree=arch.tree_root()
4868
+ options, version, files = self.parse_commandline(cmdargs, tree)
4869
+ if options.__dict__.has_key("base") and options.base:
4870
+ base = cmdutil.determine_revision_tree(tree, options.base)
4872
+ base = cmdutil.submit_revision(tree)
4874
+ writeversion=version
4875
+ archive=version.archive
4876
+ source=cmdutil.get_mirror_source(archive)
4878
+ writethrough="implicit"
4881
+ if writethrough=="explicit" and \
4882
+ cmdutil.prompt("Writethrough"):
4883
+ writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
4884
+ elif writethrough=="none":
4885
+ raise CommitToMirror(archive)
4887
+ elif archive.is_mirror:
4888
+ raise CommitToMirror(archive)
4891
+ last_revision=tree.iter_logs(version, True).next().revision
4892
+ except StopIteration, e:
4893
+ if cmdutil.prompt("Import from commit"):
4894
+ return do_import(version)
4896
+ raise NoVersionLogs(version)
4897
+ if last_revision!=version.iter_revisions(True).next():
4898
+ if not cmdutil.prompt("Out of date"):
4904
+ if not cmdutil.has_changed(version):
4905
+ if not cmdutil.prompt("Empty commit"):
4907
+ except arch.util.ExecProblem, e:
4908
+ if e.proc.error and e.proc.error.startswith(
4909
+ "missing explicit id for file"):
4910
+ raise MissingID(e)
4913
+ log = tree.log_message(create=False)
4916
+ if cmdutil.prompt("Create log"):
4919
+ except cmdutil.NoEditorSpecified, e:
4920
+ raise CommandFailed(e)
4921
+ log = tree.log_message(create=False)
4923
+ raise NoLogMessage
4924
+ if log["Summary"] is None or len(log["Summary"].strip()) == 0:
4925
+ if not cmdutil.prompt("Omit log summary"):
4926
+ raise errors.NoLogSummary
4928
+ for line in tree.iter_commit(version, seal=options.seal_version,
4929
+ base=base, out_of_date_ok=allow_old, file_list=files):
4930
+ cmdutil.colorize(line, options.suppress_chatter)
4932
+ except arch.util.ExecProblem, e:
4933
+ if e.proc.error and e.proc.error.startswith(
4934
+ "These files violate naming conventions:"):
4935
+ raise LintFailure(e.proc.error)
4939
+ def get_parser(self):
4941
+ Returns the options parser to use for the "commit" command.
4943
+ :rtype: cmdutil.CmdOptionParser
4946
+ parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
4948
+ parser.add_option("--seal", action="store_true",
4949
+ dest="seal_version", default=False,
4950
+ help="seal this version")
4951
+ parser.add_option("-v", "--version", dest="version",
4952
+ help="Use the specified version",
4953
+ metavar="VERSION")
4954
+ parser.add_option("-s", "--silent", action="store_true",
4955
+ dest="suppress_chatter", default=False,
4956
+ help="Suppress chatter messages")
4957
+ if cmdutil.supports_switch("commit", "--base"):
4958
+ parser.add_option("--base", dest="base", help="",
4959
+ metavar="REVISION")
4962
+ def help(self, parser=None):
4964
+ Prints a help message.
4966
+ :param parser: If supplied, the parser to use for generating help. If \
4967
+ not supplied, it is retrieved.
4968
+ :type parser: cmdutil.CmdOptionParser
4970
+ if parser is None:
4971
+ parser=self.get_parser()
4972
+ parser.print_help()
4974
+Updates a working tree to the current archive revision
4976
+If a version is specified, that is used instead
4983
+class CatLog(BaseCommand):
4985
+ Print the log of a given file (from current tree)
4987
+ def __init__(self):
4988
+ self.description="Prints the patch log for a revision"
4990
+ def get_completer(self, arg, index):
4994
+ tree = arch.tree_root()
4997
+ return cmdutil.iter_revision_completions(arg, tree)
4999
+ def do_command(self, cmdargs):
5001
+ Master function that perfoms the "cat-log" command.
5003
+ parser=self.get_parser()
5004
+ (options, args) = parser.parse_args(cmdargs)
5006
+ tree = arch.tree_root()
5007
+ except arch.errors.TreeRootError, e:
5013
+ raise cmdutil.GetHelp()
5016
+ revision = cmdutil.determine_revision_tree(tree, spec)
5018
+ revision = cmdutil.determine_revision_arch(tree, spec)
5019
+ except cmdutil.CantDetermineRevision, e:
5020
+ raise CommandFailedWrapper(e)
5023
+ use_tree = (options.source == "tree" or \
5024
+ (options.source == "any" and tree))
5025
+ use_arch = (options.source == "archive" or options.source == "any")
5029
+ for log in tree.iter_logs(revision.get_version()):
5030
+ if log.revision == revision:
5034
+ if log is None and use_arch:
5035
+ cmdutil.ensure_revision_exists(revision)
5036
+ log = arch.Patchlog(revision)
5037
+ if log is not None:
5038
+ for item in log.items():
5039
+ print "%s: %s" % item
5040
+ print log.description
5042
+ def get_parser(self):
5044
+ Returns the options parser to use for the "cat-log" command.
5046
+ :rtype: cmdutil.CmdOptionParser
5048
+ parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
5049
+ parser.add_option("--archive", action="store_const", dest="source",
5050
+ const="archive", default="any",
5051
+ help="Always get the log from the archive")
5052
+ parser.add_option("--tree", action="store_const", dest="source",
5053
+ const="tree", help="Always get the log from the tree")
5056
+ def help(self, parser=None):
5058
+ Prints a help message.
5060
+ :param parser: If supplied, the parser to use for generating help. If \
5061
+ not supplied, it is retrieved.
5062
+ :type parser: cmdutil.CmdOptionParser
5065
+ parser=self.get_parser()
5066
+ parser.print_help()
5068
+Prints the log for the specified revision
5073
+class Revert(BaseCommand):
5074
+ """ Reverts a tree (or aspects of it) to a revision
5076
+ def __init__(self):
5077
+ self.description="Reverts a tree (or aspects of it) to a revision "
5079
+ def get_completer(self, arg, index):
5083
+ tree = arch.tree_root()
5086
+ return iter_modified_file_completions(tree, arg)
5088
+ def do_command(self, cmdargs):
5090
+ Master function that perfoms the "revert" command.
5092
+ parser=self.get_parser()
5093
+ (options, args) = parser.parse_args(cmdargs)
5095
+ tree = arch.tree_root()
5096
+ except arch.errors.TreeRootError, e:
5097
+ raise CommandFailed(e)
5099
+ if options.revision is not None:
5100
+ spec=options.revision
5102
+ if spec is not None:
5103
+ revision = cmdutil.determine_revision_tree(tree, spec)
5105
+ revision = cmdutil.comp_revision(tree)
5106
+ except cmdutil.CantDetermineRevision, e:
5107
+ raise CommandFailedWrapper(e)
5110
+ if options.file_contents or options.file_perms or options.deletions\
5111
+ or options.additions or options.renames or options.hunk_prompt:
5112
+ munger = cmdutil.MungeOpts()
5113
+ munger.hunk_prompt = options.hunk_prompt
5115
+ if len(args) > 0 or options.logs or options.pattern_files or \
5117
+ if munger is None:
5118
+ munger = cmdutil.MungeOpts(True)
5119
+ munger.all_types(True)
5121
+ t_cwd = cmdutil.tree_cwd(tree)
5123
+ if len(t_cwd) > 0:
5125
+ name = "./" + t_cwd + name
5126
+ munger.add_keep_file(name);
5128
+ if options.file_perms:
5129
+ munger.file_perms = True
5130
+ if options.file_contents:
5131
+ munger.file_contents = True
5132
+ if options.deletions:
5133
+ munger.deletions = True
5134
+ if options.additions:
5135
+ munger.additions = True
5136
+ if options.renames:
5137
+ munger.renames = True
5139
+ munger.add_keep_pattern('^\./\{arch\}/[^=].*')
5140
+ if options.control:
5141
+ munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
5142
+ "/\.arch-inventory$")
5143
+ if options.pattern_files:
5144
+ munger.add_keep_pattern(options.pattern_files)
5146
+ for line in cmdutil.revert(tree, revision, munger,
5147
+ not options.no_output):
5148
+ cmdutil.colorize(line)
5151
+ def get_parser(self):
5153
+ Returns the options parser to use for the "cat-log" command.
5155
+ :rtype: cmdutil.CmdOptionParser
5157
+ parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
5158
+ parser.add_option("", "--contents", action="store_true",
5159
+ dest="file_contents",
5160
+ help="Revert file content changes")
5161
+ parser.add_option("", "--permissions", action="store_true",
5162
+ dest="file_perms",
5163
+ help="Revert file permissions changes")
5164
+ parser.add_option("", "--deletions", action="store_true",
5166
+ help="Restore deleted files")
5167
+ parser.add_option("", "--additions", action="store_true",
5169
+ help="Remove added files")
5170
+ parser.add_option("", "--renames", action="store_true",
5172
+ help="Revert file names")
5173
+ parser.add_option("--hunks", action="store_true",
5174
+ dest="hunk_prompt", default=False,
5175
+ help="Prompt which hunks to revert")
5176
+ parser.add_option("--pattern-files", dest="pattern_files",
5177
+ help="Revert files that match this pattern",
5179
+ parser.add_option("--logs", action="store_true",
5180
+ dest="logs", default=False,
5181
+ help="Revert only logs")
5182
+ parser.add_option("--control-files", action="store_true",
5183
+ dest="control", default=False,
5184
+ help="Revert logs and other control files")
5185
+ parser.add_option("-n", "--no-output", action="store_true",
5187
+ help="Don't keep an undo changeset")
5188
+ parser.add_option("--revision", dest="revision",
5189
+ help="Revert to the specified revision",
5190
+ metavar="REVISION")
5193
+ def help(self, parser=None):
5195
+ Prints a help message.
5197
+ :param parser: If supplied, the parser to use for generating help. If \
5198
+ not supplied, it is retrieved.
5199
+ :type parser: cmdutil.CmdOptionParser
5202
+ parser=self.get_parser()
5203
+ parser.print_help()
5205
+Reverts changes in the current working tree. If no flags are specified, all
5206
+types of changes are reverted. Otherwise, only selected types of changes are
5209
+If a revision is specified on the commandline, differences between the current
5210
+tree and that revision are reverted. If a version is specified, the current
5211
+tree is used to determine the revision.
5213
+If files are specified, only those files listed will have any changes applied.
5214
+To specify a renamed file, you can use either the old or new name. (or both!)
5216
+Unless "-n" is specified, reversions can be undone with "redo".
5220
+class Revision(BaseCommand):
5222
+ Print a revision name based on a revision specifier
5224
+ def __init__(self):
5225
+ self.description="Prints the name of a revision"
5227
+ def get_completer(self, arg, index):
5231
+ tree = arch.tree_root()
5234
+ return cmdutil.iter_revision_completions(arg, tree)
5236
+ def do_command(self, cmdargs):
5238
+ Master function that perfoms the "revision" command.
5240
+ parser=self.get_parser()
5241
+ (options, args) = parser.parse_args(cmdargs)
5244
+ tree = arch.tree_root()
5245
+ except arch.errors.TreeRootError:
5252
+ raise cmdutil.GetHelp
5255
+ revision = cmdutil.determine_revision_tree(tree, spec)
5257
+ revision = cmdutil.determine_revision_arch(tree, spec)
5258
+ except cmdutil.CantDetermineRevision, e:
5261
+ print options.display(revision)
5263
+ def get_parser(self):
5265
+ Returns the options parser to use for the "revision" command.
5267
+ :rtype: cmdutil.CmdOptionParser
5269
+ parser=cmdutil.CmdOptionParser("fai revision [revision]")
5270
+ parser.add_option("", "--location", action="store_const",
5271
+ const=paths.determine_path, dest="display",
5272
+ help="Show location instead of name", default=str)
5273
+ parser.add_option("--import", action="store_const",
5274
+ const=paths.determine_import_path, dest="display",
5275
+ help="Show location of import file")
5276
+ parser.add_option("--log", action="store_const",
5277
+ const=paths.determine_log_path, dest="display",
5278
+ help="Show location of log file")
5279
+ parser.add_option("--patch", action="store_const",
5280
+ dest="display", const=paths.determine_patch_path,
5281
+ help="Show location of patchfile")
5282
+ parser.add_option("--continuation", action="store_const",
5283
+ const=paths.determine_continuation_path,
5285
+ help="Show location of continuation file")
5286
+ parser.add_option("--cacherev", action="store_const",
5287
+ const=paths.determine_cacherev_path, dest="display",
5288
+ help="Show location of cacherev file")
5291
+ def help(self, parser=None):
5293
+ Prints a help message.
5295
+ :param parser: If supplied, the parser to use for generating help. If \
5296
+ not supplied, it is retrieved.
5297
+ :type parser: cmdutil.CmdOptionParser
5300
+ parser=self.get_parser()
5301
+ parser.print_help()
5303
+Expands aliases and prints the name of the specified revision. Instead of
5304
+the name, several options can be used to print locations. If more than one is
5305
+specified, the last one is used.
5310
+def require_version_exists(version, spec):
5311
+ if not version.exists():
5312
+ raise cmdutil.CantDetermineVersion(spec,
5313
+ "The version %s does not exist." \
5316
+class Revisions(BaseCommand):
5318
+ Print a revision name based on a revision specifier
5320
+ def __init__(self):
5321
+ self.description="Lists revisions"
5323
+ def do_command(self, cmdargs):
5325
+ Master function that perfoms the "revision" command.
5327
+ (options, args) = self.get_parser().parse_args(cmdargs)
5329
+ raise cmdutil.GetHelp
5331
+ self.tree = arch.tree_root()
5332
+ except arch.errors.TreeRootError:
5335
+ iter = self.get_iterator(options.type, args, options.reverse,
5337
+ except cmdutil.CantDetermineRevision, e:
5338
+ raise CommandFailedWrapper(e)
5340
+ if options.skip is not None:
5341
+ iter = cmdutil.iter_skip(iter, int(options.skip))
5343
+ for revision in iter:
5345
+ if isinstance(revision, arch.Patchlog):
5347
+ revision=revision.revision
5348
+ print options.display(revision)
5349
+ if log is None and (options.summary or options.creator or
5350
+ options.date or options.merges):
5351
+ log = revision.patchlog
5352
+ if options.creator:
5353
+ print " %s" % log.creator
5355
+ print " %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
5356
+ if options.summary:
5357
+ print " %s" % log.summary
5358
+ if options.merges:
5359
+ showed_title = False
5360
+ for revision in log.merged_patches:
5361
+ if not showed_title:
5363
+ showed_title = True
5364
+ print " %s" % revision
5366
+ def get_iterator(self, type, args, reverse, modified):
5371
+ if modified is not None:
5372
+ iter = cmdutil.modified_iter(modified, self.tree)
5376
+ return cmdutil.iter_reverse(iter)
5377
+ elif type == "archive":
5379
+ if self.tree is None:
5380
+ raise cmdutil.CantDetermineRevision("",
5381
+ "Not in a project tree")
5382
+ version = cmdutil.determine_version_tree(spec, self.tree)
5384
+ version = cmdutil.determine_version_arch(spec, self.tree)
5385
+ cmdutil.ensure_archive_registered(version.archive)
5386
+ require_version_exists(version, spec)
5387
+ return version.iter_revisions(reverse)
5388
+ elif type == "cacherevs":
5390
+ if self.tree is None:
5391
+ raise cmdutil.CantDetermineRevision("",
5392
+ "Not in a project tree")
5393
+ version = cmdutil.determine_version_tree(spec, self.tree)
5395
+ version = cmdutil.determine_version_arch(spec, self.tree)
5396
+ cmdutil.ensure_archive_registered(version.archive)
5397
+ require_version_exists(version, spec)
5398
+ return cmdutil.iter_cacherevs(version, reverse)
5399
+ elif type == "library":
5401
+ if self.tree is None:
5402
+ raise cmdutil.CantDetermineRevision("",
5403
+ "Not in a project tree")
5404
+ version = cmdutil.determine_version_tree(spec, self.tree)
5406
+ version = cmdutil.determine_version_arch(spec, self.tree)
5407
+ return version.iter_library_revisions(reverse)
5408
+ elif type == "logs":
5409
+ if self.tree is None:
5410
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5411
+ return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
5412
+ self.tree), reverse)
5413
+ elif type == "missing" or type == "skip-present":
5414
+ if self.tree is None:
5415
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5416
+ skip = (type == "skip-present")
5417
+ version = cmdutil.determine_version_tree(spec, self.tree)
5418
+ cmdutil.ensure_archive_registered(version.archive)
5419
+ require_version_exists(version, spec)
5420
+ return cmdutil.iter_missing(self.tree, version, reverse,
5421
+ skip_present=skip)
5423
+ elif type == "present":
5424
+ if self.tree is None:
5425
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5426
+ version = cmdutil.determine_version_tree(spec, self.tree)
5427
+ cmdutil.ensure_archive_registered(version.archive)
5428
+ require_version_exists(version, spec)
5429
+ return cmdutil.iter_present(self.tree, version, reverse)
5431
+ elif type == "new-merges" or type == "direct-merges":
5432
+ if self.tree is None:
5433
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5434
+ version = cmdutil.determine_version_tree(spec, self.tree)
5435
+ cmdutil.ensure_archive_registered(version.archive)
5436
+ require_version_exists(version, spec)
5437
+ iter = cmdutil.iter_new_merges(self.tree, version, reverse)
5438
+ if type == "new-merges":
5440
+ elif type == "direct-merges":
5441
+ return cmdutil.direct_merges(iter)
5443
+ elif type == "missing-from":
5444
+ if self.tree is None:
5445
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5446
+ revision = cmdutil.determine_revision_tree(self.tree, spec)
5447
+ libtree = cmdutil.find_or_make_local_revision(revision)
5448
+ return cmdutil.iter_missing(libtree, self.tree.tree_version,
5451
+ elif type == "partner-missing":
5452
+ return cmdutil.iter_partner_missing(self.tree, reverse)
5454
+ elif type == "ancestry":
5455
+ revision = cmdutil.determine_revision_tree(self.tree, spec)
5456
+ iter = cmdutil._iter_ancestry(self.tree, revision)
5460
+ return cmdutil.iter_reverse(iter)
5462
+ elif type == "dependencies" or type == "non-dependencies":
5463
+ nondeps = (type == "non-dependencies")
5464
+ revision = cmdutil.determine_revision_tree(self.tree, spec)
5465
+ anc_iter = cmdutil._iter_ancestry(self.tree, revision)
5466
+ iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
5468
+ return iter_depends
5470
+ return cmdutil.iter_reverse(iter_depends)
5471
+ elif type == "micro":
5472
+ return cmdutil.iter_micro(self.tree)
5475
+ def get_parser(self):
5477
+ Returns the options parser to use for the "revision" command.
5479
+ :rtype: cmdutil.CmdOptionParser
5481
+ parser=cmdutil.CmdOptionParser("fai revisions [revision]")
5482
+ select = cmdutil.OptionGroup(parser, "Selection options",
5483
+ "Control which revisions are listed. These options"
5484
+ " are mutually exclusive. If more than one is"
5485
+ " specified, the last is used.")
5486
+ select.add_option("", "--archive", action="store_const",
5487
+ const="archive", dest="type", default="archive",
5488
+ help="List all revisions in the archive")
5489
+ select.add_option("", "--cacherevs", action="store_const",
5490
+ const="cacherevs", dest="type",
5491
+ help="List all revisions stored in the archive as "
5492
+ "complete copies")
5493
+ select.add_option("", "--logs", action="store_const",
5494
+ const="logs", dest="type",
5495
+ help="List revisions that have a patchlog in the "
5497
+ select.add_option("", "--missing", action="store_const",
5498
+ const="missing", dest="type",
5499
+ help="List revisions from the specified version that"
5500
+ " have no patchlog in the tree")
5501
+ select.add_option("", "--skip-present", action="store_const",
5502
+ const="skip-present", dest="type",
5503
+ help="List revisions from the specified version that"
5504
+ " have no patchlogs at all in the tree")
5505
+ select.add_option("", "--present", action="store_const",
5506
+ const="present", dest="type",
5507
+ help="List revisions from the specified version that"
5508
+ " have no patchlog in the tree, but can't be merged")
5509
+ select.add_option("", "--missing-from", action="store_const",
5510
+ const="missing-from", dest="type",
5511
+ help="List revisions from the specified revision "
5512
+ "that have no patchlog for the tree version")
5513
+ select.add_option("", "--partner-missing", action="store_const",
5514
+ const="partner-missing", dest="type",
5515
+ help="List revisions in partner versions that are"
5517
+ select.add_option("", "--new-merges", action="store_const",
5518
+ const="new-merges", dest="type",
5519
+ help="List revisions that have had patchlogs added"
5520
+ " to the tree since the last commit")
5521
+ select.add_option("", "--direct-merges", action="store_const",
5522
+ const="direct-merges", dest="type",
5523
+ help="List revisions that have been directly added"
5524
+ " to tree since the last commit ")
5525
+ select.add_option("", "--library", action="store_const",
5526
+ const="library", dest="type",
5527
+ help="List revisions in the revision library")
5528
+ select.add_option("", "--ancestry", action="store_const",
5529
+ const="ancestry", dest="type",
5530
+ help="List revisions that are ancestors of the "
5531
+ "current tree version")
5533
+ select.add_option("", "--dependencies", action="store_const",
5534
+ const="dependencies", dest="type",
5535
+ help="List revisions that the given revision "
5538
+ select.add_option("", "--non-dependencies", action="store_const",
5539
+ const="non-dependencies", dest="type",
5540
+ help="List revisions that the given revision "
5541
+ "does not depend on")
5543
+ select.add_option("--micro", action="store_const",
5544
+ const="micro", dest="type",
5545
+ help="List partner revisions aimed for this "
5548
+ select.add_option("", "--modified", dest="modified",
5549
+ help="List tree ancestor revisions that modified a "
5550
+ "given file", metavar="FILE[:LINE]")
5552
+ parser.add_option("", "--skip", dest="skip",
5553
+ help="Skip revisions. Positive numbers skip from "
5554
+ "beginning, negative skip from end.",
5557
+ parser.add_option_group(select)
5559
+ format = cmdutil.OptionGroup(parser, "Revision format options",
5560
+ "These control the appearance of listed revisions")
5561
+ format.add_option("", "--location", action="store_const",
5562
+ const=paths.determine_path, dest="display",
5563
+ help="Show location instead of name", default=str)
5564
+ format.add_option("--import", action="store_const",
5565
+ const=paths.determine_import_path, dest="display",
5566
+ help="Show location of import file")
5567
+ format.add_option("--log", action="store_const",
5568
+ const=paths.determine_log_path, dest="display",
5569
+ help="Show location of log file")
5570
+ format.add_option("--patch", action="store_const",
5571
+ dest="display", const=paths.determine_patch_path,
5572
+ help="Show location of patchfile")
5573
+ format.add_option("--continuation", action="store_const",
5574
+ const=paths.determine_continuation_path,
5576
+ help="Show location of continuation file")
5577
+ format.add_option("--cacherev", action="store_const",
5578
+ const=paths.determine_cacherev_path, dest="display",
5579
+ help="Show location of cacherev file")
5580
+ parser.add_option_group(format)
5581
+ display = cmdutil.OptionGroup(parser, "Display format options",
5582
+ "These control the display of data")
5583
+ display.add_option("-r", "--reverse", action="store_true",
5584
+ dest="reverse", help="Sort from newest to oldest")
5585
+ display.add_option("-s", "--summary", action="store_true",
5586
+ dest="summary", help="Show patchlog summary")
5587
+ display.add_option("-D", "--date", action="store_true",
5588
+ dest="date", help="Show patchlog date")
5589
+ display.add_option("-c", "--creator", action="store_true",
5590
+ dest="creator", help="Show the id that committed the"
5592
+ display.add_option("-m", "--merges", action="store_true",
5593
+ dest="merges", help="Show the revisions that were"
5595
+ parser.add_option_group(display)
5597
+ def help(self, parser=None):
5598
+ """Attempt to explain the revisions command
5600
+ :param parser: If supplied, used to determine options
5603
+ parser=self.get_parser()
5604
+ parser.print_help()
5605
+ print """List revisions.
5610
+class Get(BaseCommand):
5612
+ Retrieve a revision from the archive
5614
+ def __init__(self):
5615
+ self.description="Retrieve a revision from the archive"
5616
+ self.parser=self.get_parser()
5619
+ def get_completer(self, arg, index):
5623
+ tree = arch.tree_root()
5626
+ return cmdutil.iter_revision_completions(arg, tree)
5629
+ def do_command(self, cmdargs):
5631
+ Master function that perfoms the "get" command.
5633
+ (options, args) = self.parser.parse_args(cmdargs)
5635
+ return self.help()
5637
+ tree = arch.tree_root()
5638
+ except arch.errors.TreeRootError:
5643
+ revision, arch_loc = paths.full_path_decode(args[0])
5644
+ except Exception, e:
5645
+ revision = cmdutil.determine_revision_arch(tree, args[0],
5646
+ check_existence=False, allow_package=True)
5648
+ directory = args[1]
5650
+ directory = str(revision.nonarch)
5651
+ if os.path.exists(directory):
5652
+ raise DirectoryExists(directory)
5653
+ cmdutil.ensure_archive_registered(revision.archive, arch_loc)
5655
+ cmdutil.ensure_revision_exists(revision)
5656
+ except cmdutil.NoSuchRevision, e:
5657
+ raise CommandFailedWrapper(e)
5659
+ link = cmdutil.prompt ("get link")
5660
+ for line in cmdutil.iter_get(revision, directory, link,
5661
+ options.no_pristine,
5662
+ options.no_greedy_add):
5663
+ cmdutil.colorize(line)
5665
+ def get_parser(self):
5667
+ Returns the options parser to use for the "get" command.
5669
+ :rtype: cmdutil.CmdOptionParser
5671
+ parser=cmdutil.CmdOptionParser("fai get revision [dir]")
5672
+ parser.add_option("--no-pristine", action="store_true",
5673
+ dest="no_pristine",
5674
+ help="Do not make pristine copy for reference")
5675
+ parser.add_option("--no-greedy-add", action="store_true",
5676
+ dest="no_greedy_add",
5677
+ help="Never add to greedy libraries")
5681
+ def help(self, parser=None):
5683
+ Prints a help message.
5685
+ :param parser: If supplied, the parser to use for generating help. If \
5686
+ not supplied, it is retrieved.
5687
+ :type parser: cmdutil.CmdOptionParser
5690
+ parser=self.get_parser()
5691
+ parser.print_help()
5693
+Expands aliases and constructs a project tree for a revision. If the optional
5694
+"dir" argument is provided, the project tree will be stored in this directory.
5699
+class PromptCmd(cmd.Cmd):
5700
+ def __init__(self):
5701
+ cmd.Cmd.__init__(self)
5702
+ self.prompt = "Fai> "
5704
+ self.tree = arch.tree_root()
5709
+ self.fake_aba = abacmds.AbaCmds()
5710
+ self.identchars += '-'
5711
+ self.history_file = os.path.expanduser("~/.fai-history")
5712
+ readline.set_completer_delims(string.whitespace)
5713
+ if os.access(self.history_file, os.R_OK) and \
5714
+ os.path.isfile(self.history_file):
5715
+ readline.read_history_file(self.history_file)
5717
+ def write_history(self):
5718
+ readline.write_history_file(self.history_file)
5720
+ def do_quit(self, args):
5721
+ self.write_history()
5724
+ def do_exit(self, args):
5725
+ self.do_quit(args)
5727
+ def do_EOF(self, args):
5729
+ self.do_quit(args)
5731
+ def postcmd(self, line, bar):
5735
+ def set_prompt(self):
5736
+ if self.tree is not None:
5738
+ version = " "+self.tree.tree_version.nonarch
5743
+ self.prompt = "Fai%s> " % version
5745
+ def set_title(self, command=None):
5747
+ version = self.tree.tree_version.nonarch
5749
+ version = "[no version]"
5750
+ if command is None:
5752
+ sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
5754
+ def do_cd(self, line):
5758
+ os.chdir(os.path.expanduser(line))
5759
+ except Exception, e:
5762
+ self.tree = arch.tree_root()
5766
+ def do_help(self, line):
5769
+ def default(self, line):
5770
+ args = line.split()
5771
+ if find_command(args[0]):
5773
+ find_command(args[0]).do_command(args[1:])
5774
+ except cmdutil.BadCommandOption, e:
5776
+ except cmdutil.GetHelp, e:
5777
+ find_command(args[0]).help()
5778
+ except CommandFailed, e:
5780
+ except arch.errors.ArchiveNotRegistered, e:
5782
+ except KeyboardInterrupt, e:
5783
+ print "Interrupted"
5784
+ except arch.util.ExecProblem, e:
5785
+ print e.proc.error.rstrip('\n')
5786
+ except cmdutil.CantDetermineVersion, e:
5788
+ except cmdutil.CantDetermineRevision, e:
5790
+ except Exception, e:
5791
+ print "Unhandled error:\n%s" % cmdutil.exception_str(e)
5793
+ elif suggestions.has_key(args[0]):
5794
+ print suggestions[args[0]]
5796
+ elif self.fake_aba.is_command(args[0]):
5799
+ tree = arch.tree_root()
5800
+ except arch.errors.TreeRootError:
5802
+ cmd = self.fake_aba.is_command(args[0])
5804
+ cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
5805
+ except KeyboardInterrupt, e:
5806
+ print "Interrupted"
5808
+ elif options.tla_fallthrough and args[0] != "rm" and \
5809
+ cmdutil.is_tla_command(args[0]):
5813
+ tree = arch.tree_root()
5814
+ except arch.errors.TreeRootError:
5816
+ args = cmdutil.expand_prefix_alias(args, tree)
5817
+ arch.util.exec_safe('tla', args, stderr=sys.stderr,
5819
+ except arch.util.ExecProblem, e:
5821
+ except KeyboardInterrupt, e:
5822
+ print "Interrupted"
5826
+ tree = arch.tree_root()
5827
+ except arch.errors.TreeRootError:
5830
+ os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
5831
+ except KeyboardInterrupt, e:
5832
+ print "Interrupted"
5834
+ def completenames(self, text, line, begidx, endidx):
5836
+ iter = iter_command_names(self.fake_aba)
5839
+ arg = line.split()[-1]
5842
+ iter = iter_munged_completions(iter, arg, text)
5843
+ except Exception, e:
5847
+ def completedefault(self, text, line, begidx, endidx):
5848
+ """Perform completion for native commands.
5850
+ :param text: The text to complete
5852
+ :param line: The entire line to complete
5854
+ :param begidx: The start of the text in the line
5856
+ :param endidx: The end of the text in the line
5860
+ (cmd, args, foo) = self.parseline(line)
5861
+ command_obj=find_command(cmd)
5862
+ if command_obj is not None:
5863
+ return command_obj.complete(args.split(), text)
5864
+ elif not self.fake_aba.is_command(cmd) and \
5865
+ cmdutil.is_tla_command(cmd):
5866
+ iter = cmdutil.iter_supported_switches(cmd)
5868
+ arg = args.split()[-1]
5871
+ if arg.startswith("-"):
5872
+ return list(iter_munged_completions(iter, arg, text))
5874
+ return list(iter_munged_completions(
5875
+ iter_file_completions(arg), arg, text))
5880
+ arg = args.split()[-1]
5883
+ iter = iter_dir_completions(arg)
5884
+ iter = iter_munged_completions(iter, arg, text)
5887
+ arg = args.split()[-1]
5888
+ return list(iter_munged_completions(iter_file_completions(arg),
5891
+ return self.completenames(text, line, begidx, endidx)
5892
+ except Exception, e:
5896
+def iter_command_names(fake_aba):
5897
+ for entry in cmdutil.iter_combine([commands.iterkeys(),
5898
+ fake_aba.get_commands(),
5899
+ cmdutil.iter_tla_commands(False)]):
5900
+ if not suggestions.has_key(str(entry)):
5904
+def iter_file_completions(arg, only_dirs = False):
5905
+ """Generate an iterator that iterates through filename completions.
5907
+ :param arg: The filename fragment to match
5909
+ :param only_dirs: If true, match only directories
5910
+ :type only_dirs: bool
5914
+ extras = [".", ".."]
5917
+ (dir, file) = os.path.split(arg)
5919
+ listingdir = os.path.expanduser(dir)
5922
+ for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
5924
+ userfile = dir+'/'+file
5927
+ if userfile.startswith(arg):
5928
+ if os.path.isdir(listingdir+'/'+file):
5931
+ elif not only_dirs:
5934
+def iter_munged_completions(iter, arg, text):
5935
+ for completion in iter:
5936
+ completion = str(completion)
5937
+ if completion.startswith(arg):
5938
+ yield completion[len(arg)-len(text):]
5940
+def iter_source_file_completions(tree, arg):
5941
+ treepath = cmdutil.tree_cwd(tree)
5942
+ if len(treepath) > 0:
5946
+ for file in tree.iter_inventory(dirs, source=True, both=True):
5947
+ file = file_completion_match(file, treepath, arg)
5948
+ if file is not None:
5952
+def iter_untagged(tree, dirs):
5953
+ for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False,
5954
+ categories=arch_core.non_root,
5955
+ control_files=True):
5959
+def iter_untagged_completions(tree, arg):
5960
+ """Generate an iterator for all visible untagged files that match arg.
5962
+ :param tree: The tree to look for untagged files in
5963
+ :type tree: `arch.WorkingTree`
5964
+ :param arg: The argument to match
5966
+ :return: An iterator of all matching untagged files
5967
+ :rtype: iterator of str
5969
+ treepath = cmdutil.tree_cwd(tree)
5970
+ if len(treepath) > 0:
5975
+ for file in iter_untagged(tree, dirs):
5976
+ file = file_completion_match(file, treepath, arg)
5977
+ if file is not None:
5981
+def file_completion_match(file, treepath, arg):
5982
+ """Determines whether a file within an arch tree matches the argument.
5984
+ :param file: The rooted filename
5986
+ :param treepath: The path to the cwd within the tree
5987
+ :type treepath: str
5988
+ :param arg: The prefix to match
5989
+ :return: The completion name, or None if not a match
5992
+ if not file.startswith(treepath):
5994
+ if treepath != "":
5995
+ file = file[len(treepath)+1:]
5997
+ if not file.startswith(arg):
5999
+ if os.path.isdir(file):
6003
+def iter_modified_file_completions(tree, arg):
6004
+ """Returns a list of modified files that match the specified prefix.
6006
+ :param tree: The current tree
6007
+ :type tree: `arch.WorkingTree`
6008
+ :param arg: The prefix to match
6011
+ treepath = cmdutil.tree_cwd(tree)
6012
+ tmpdir = cmdutil.tmpdir()
6013
+ changeset = tmpdir+"/changeset"
6015
+ revision = cmdutil.determine_revision_tree(tree)
6016
+ for line in arch.iter_delta(revision, tree, changeset):
6017
+ if isinstance(line, arch.FileModification):
6018
+ file = file_completion_match(line.name[1:], treepath, arg)
6019
+ if file is not None:
6020
+ completions.append(file)
6021
+ shutil.rmtree(tmpdir)
6022
+ return completions
6024
+def iter_dir_completions(arg):
6025
+ """Generate an iterator that iterates through directory name completions.
6027
+ :param arg: The directory name fragment to match
6030
+ return iter_file_completions(arg, True)
6032
+class Shell(BaseCommand):
6033
+ def __init__(self):
6034
+ self.description = "Runs Fai as a shell"
6036
+ def do_command(self, cmdargs):
6037
+ if len(cmdargs)!=0:
6038
+ raise cmdutil.GetHelp
6039
+ prompt = PromptCmd()
6043
+ prompt.write_history()
6045
+class AddID(BaseCommand):
6047
+ Adds an inventory id for the given file
6049
+ def __init__(self):
6050
+ self.description="Add an inventory id for a given file"
6052
+ def get_completer(self, arg, index):
6053
+ tree = arch.tree_root()
6054
+ return iter_untagged_completions(tree, arg)
6056
+ def do_command(self, cmdargs):
6058
+ Master function that perfoms the "revision" command.
6060
+ parser=self.get_parser()
6061
+ (options, args) = parser.parse_args(cmdargs)
6063
+ tree = arch.tree_root()
6065
+ if (len(args) == 0) == (options.untagged == False):
6066
+ raise cmdutil.GetHelp
6068
+ #if options.id and len(args) != 1:
6069
+ # print "If --id is specified, only one file can be named."
6072
+ method = tree.tagging_method
6074
+ if options.id_type == "tagline":
6075
+ if method != "tagline":
6076
+ if not cmdutil.prompt("Tagline in other tree"):
6077
+ if method == "explicit":
6078
+ options.id_type == explicit
6080
+ print "add-id not supported for \"%s\" tagging method"\
6084
+ elif options.id_type == "explicit":
6085
+ if method != "tagline" and method != explicit:
6086
+ if not prompt("Explicit in other tree"):
6087
+ print "add-id not supported for \"%s\" tagging method" % \
6091
+ if options.id_type == "auto":
6092
+ if method != "tagline" and method != "explicit":
6093
+ print "add-id not supported for \"%s\" tagging method" % method
6096
+ options.id_type = method
6097
+ if options.untagged:
6099
+ self.add_ids(tree, options.id_type, args)
6101
+ def add_ids(self, tree, id_type, files=()):
6102
+ """Add inventory ids to files.
6104
+ :param tree: the tree the files are in
6105
+ :type tree: `arch.WorkingTree`
6106
+ :param id_type: the type of id to add: "explicit" or "tagline"
6107
+ :type id_type: str
6108
+ :param files: The list of files to add. If None do all untagged.
6109
+ :type files: tuple of str
6112
+ untagged = (files is None)
6114
+ files = list(iter_untagged(tree, None))
6115
+ previous_files = []
6116
+ while len(files) > 0:
6117
+ previous_files.extend(files)
6118
+ if id_type == "explicit":
6119
+ cmdutil.add_id(files)
6120
+ elif id_type == "tagline":
6121
+ for file in files:
6123
+ cmdutil.add_tagline_or_explicit_id(file)
6124
+ except cmdutil.AlreadyTagged:
6125
+ print "\"%s\" already has a tagline." % file
6126
+ except cmdutil.NoCommentSyntax:
6128
+ #do inventory after tagging until no untagged files are encountered
6131
+ for file in iter_untagged(tree, None):
6132
+ if not file in previous_files:
6133
+ files.append(file)
6138
+ def get_parser(self):
6140
+ Returns the options parser to use for the "revision" command.
6142
+ :rtype: cmdutil.CmdOptionParser
6144
+ parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
6145
+# ddaa suggests removing this to promote GUIDs. Let's see who squalks.
6146
+# parser.add_option("-i", "--id", dest="id",
6147
+# help="Specify id for a single file", default=None)
6148
+ parser.add_option("--tltl", action="store_true",
6149
+ dest="lord_style", help="Use Tom Lord's style of id.")
6150
+ parser.add_option("--explicit", action="store_const",
6151
+ const="explicit", dest="id_type",
6152
+ help="Use an explicit id", default="auto")
6153
+ parser.add_option("--tagline", action="store_const",
6154
+ const="tagline", dest="id_type",
6155
+ help="Use a tagline id")
6156
+ parser.add_option("--untagged", action="store_true",
6157
+ dest="untagged", default=False,
6158
+ help="tag all untagged files")
6161
+ def help(self, parser=None):
6163
+ Prints a help message.
6165
+ :param parser: If supplied, the parser to use for generating help. If \
6166
+ not supplied, it is retrieved.
6167
+ :type parser: cmdutil.CmdOptionParser
6170
+ parser=self.get_parser()
6171
+ parser.print_help()
6173
+Adds an inventory to the specified file(s) and directories. If --untagged is
6174
+specified, adds inventory to all untagged files and directories.
6179
+class Merge(BaseCommand):
6181
+ Merges changes from other versions into the current tree
6183
+ def __init__(self):
6184
+ self.description="Merges changes from other versions"
6186
+ self.tree = arch.tree_root()
6191
+ def get_completer(self, arg, index):
6192
+ if self.tree is None:
6193
+ raise arch.errors.TreeRootError
6194
+ completions = list(ancillary.iter_partners(self.tree,
6195
+ self.tree.tree_version))
6196
+ if len(completions) == 0:
6197
+ completions = list(self.tree.iter_log_versions())
6201
+ for completion in completions:
6202
+ alias = ancillary.compact_alias(str(completion), self.tree)
6204
+ aliases.extend(alias)
6206
+ for completion in completions:
6207
+ if completion.archive == self.tree.tree_version.archive:
6208
+ aliases.append(completion.nonarch)
6210
+ except Exception, e:
6213
+ completions.extend(aliases)
6214
+ return completions
6216
+ def do_command(self, cmdargs):
6218
+ Master function that perfoms the "merge" command.
6220
+ parser=self.get_parser()
6221
+ (options, args) = parser.parse_args(cmdargs)
6223
+ action="star-merge"
6225
+ action = options.action
6227
+ if self.tree is None:
6228
+ raise arch.errors.TreeRootError(os.getcwd())
6229
+ if cmdutil.has_changed(self.tree.tree_version):
6230
+ raise UncommittedChanges(self.tree)
6235
+ revisions.append(cmdutil.determine_revision_arch(self.tree,
6237
+ source = "from commandline"
6239
+ revisions = ancillary.iter_partner_revisions(self.tree,
6240
+ self.tree.tree_version)
6241
+ source = "from partner version"
6242
+ revisions = misc.rewind_iterator(revisions)
6245
+ revisions.rewind()
6246
+ except StopIteration, e:
6247
+ revision = cmdutil.tag_cur(self.tree)
6248
+ if revision is None:
6249
+ raise CantDetermineRevision("", "No version specified, no "
6250
+ "partner-versions, and no tag"
6252
+ revisions = [revision]
6253
+ source = "from tag source"
6254
+ for revision in revisions:
6255
+ cmdutil.ensure_archive_registered(revision.archive)
6256
+ cmdutil.colorize(arch.Chatter("* Merging %s [%s]" %
6257
+ (revision, source)))
6258
+ if action=="native-merge" or action=="update":
6259
+ if self.native_merge(revision, action) == 0:
6261
+ elif action=="star-merge":
6263
+ self.star_merge(revision, options.diff3)
6264
+ except errors.MergeProblem, e:
6266
+ if cmdutil.has_changed(self.tree.tree_version):
6269
+ def star_merge(self, revision, diff3):
6270
+ """Perform a star-merge on the current tree.
6272
+ :param revision: The revision to use for the merge
6273
+ :type revision: `arch.Revision`
6274
+ :param diff3: If true, do a diff3 merge
6278
+ for line in self.tree.iter_star_merge(revision, diff3=diff3):
6279
+ cmdutil.colorize(line)
6280
+ except arch.util.ExecProblem, e:
6281
+ if e.proc.status is not None and e.proc.status == 1:
6283
+ print e.proc.error
6284
+ raise MergeProblem
6288
+ def native_merge(self, other_revision, action):
6289
+ """Perform a native-merge on the current tree.
6291
+ :param other_revision: The revision to use for the merge
6292
+ :type other_revision: `arch.Revision`
6293
+ :return: 0 if the merge was skipped, 1 if it was applied
6295
+ other_tree = cmdutil.find_or_make_local_revision(other_revision)
6297
+ if action == "native-merge":
6298
+ ancestor = cmdutil.merge_ancestor2(self.tree, other_tree,
6300
+ elif action == "update":
6301
+ ancestor = cmdutil.tree_latest(self.tree,
6302
+ other_revision.version)
6303
+ except CantDetermineRevision, e:
6304
+ raise CommandFailedWrapper(e)
6305
+ cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
6306
+ if (ancestor == other_revision):
6307
+ cmdutil.colorize(arch.Chatter("* Skipping redundant merge"
6310
+ delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)
6311
+ for line in cmdutil.iter_apply_delta_filter(delta):
6312
+ cmdutil.colorize(line)
6317
+ def get_parser(self):
6319
+ Returns the options parser to use for the "merge" command.
6321
+ :rtype: cmdutil.CmdOptionParser
6323
+ parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
6324
+ parser.add_option("-s", "--star-merge", action="store_const",
6325
+ dest="action", help="Use star-merge",
6326
+ const="star-merge", default="native-merge")
6327
+ parser.add_option("--update", action="store_const",
6328
+ dest="action", help="Use update picker",
6330
+ parser.add_option("--diff3", action="store_true",
6332
+ help="Use diff3 for merge (implies star-merge)")
6335
+ def help(self, parser=None):
6337
+ Prints a help message.
6339
+ :param parser: If supplied, the parser to use for generating help. If \
6340
+ not supplied, it is retrieved.
6341
+ :type parser: cmdutil.CmdOptionParser
6344
+ parser=self.get_parser()
6345
+ parser.print_help()
6347
+Performs a merge operation using the specified version.
6351
+class ELog(BaseCommand):
6353
+ Produces a raw patchlog and invokes the user's editor
6355
+ def __init__(self):
6356
+ self.description="Edit a patchlog to commit"
6358
+ self.tree = arch.tree_root()
6363
+ def do_command(self, cmdargs):
6365
+ Master function that perfoms the "elog" command.
6367
+ parser=self.get_parser()
6368
+ (options, args) = parser.parse_args(cmdargs)
6369
+ if self.tree is None:
6370
+ raise arch.errors.TreeRootError
6372
+ edit_log(self.tree)
6374
+ def get_parser(self):
6376
+ Returns the options parser to use for the "merge" command.
6378
+ :rtype: cmdutil.CmdOptionParser
6380
+ parser=cmdutil.CmdOptionParser("fai elog")
6384
+ def help(self, parser=None):
6386
+ Invokes $EDITOR to produce a log for committing.
6388
+ :param parser: If supplied, the parser to use for generating help. If \
6389
+ not supplied, it is retrieved.
6390
+ :type parser: cmdutil.CmdOptionParser
6393
+ parser=self.get_parser()
6394
+ parser.print_help()
6396
+Invokes $EDITOR to produce a log for committing.
6400
+def edit_log(tree):
6401
+ """Makes and edits the log for a tree. Does all kinds of fancy things
6402
+ like log templates and merge summaries and log-for-merge
6404
+ :param tree: The tree to edit the log for
6405
+ :type tree: `arch.WorkingTree`
6407
+ #ensure we have an editor before preparing the log
6408
+ cmdutil.find_editor()
6409
+ log = tree.log_message(create=False)
6410
+ log_is_new = False
6411
+ if log is None or cmdutil.prompt("Overwrite log"):
6412
+ if log is not None:
6413
+ os.remove(log.name)
6414
+ log = tree.log_message(create=True)
6417
+ template = tree+"/{arch}/=log-template"
6418
+ if not os.path.exists(template):
6419
+ template = os.path.expanduser("~/.arch-params/=log-template")
6420
+ if not os.path.exists(template):
6423
+ shutil.copyfile(template, tmplog)
6425
+ new_merges = list(cmdutil.iter_new_merges(tree,
6426
+ tree.tree_version))
6427
+ log["Summary"] = merge_summary(new_merges, tree.tree_version)
6428
+ if len(new_merges) > 0:
6429
+ if cmdutil.prompt("Log for merge"):
6430
+ mergestuff = cmdutil.log_for_merge(tree)
6431
+ log.description += mergestuff
6434
+ cmdutil.invoke_editor(log.name)
6437
+ os.remove(log.name)
6440
+def merge_summary(new_merges, tree_version):
6441
+ if len(new_merges) == 0:
6443
+ if len(new_merges) == 1:
6444
+ summary = new_merges[0].summary
6449
+ for merge in new_merges:
6450
+ if arch.my_id() != merge.creator:
6451
+ name = re.sub("<.*>", "", merge.creator).rstrip(" ");
6452
+ if not name in credits:
6453
+ credits.append(name)
6455
+ version = merge.revision.version
6456
+ if version.archive == tree_version.archive:
6457
+ if not version.nonarch in credits:
6458
+ credits.append(version.nonarch)
6459
+ elif not str(version) in credits:
6460
+ credits.append(str(version))
6462
+ return ("%s (%s)") % (summary, ", ".join(credits))
6464
+class MirrorArchive(BaseCommand):
6466
+ Updates a mirror from an archive
6468
+ def __init__(self):
6469
+ self.description="Update a mirror from an archive"
6471
+ def do_command(self, cmdargs):
6473
+ Master function that perfoms the "revision" command.
6476
+ parser=self.get_parser()
6477
+ (options, args) = parser.parse_args(cmdargs)
6481
+ tree = arch.tree_root()
6485
+ if len(args) == 0:
6486
+ if tree is not None:
6487
+ name = tree.tree_version()
6489
+ name = cmdutil.expand_alias(args[0], tree)
6490
+ name = arch.NameParser(name)
6492
+ to_arch = name.get_archive()
6493
+ from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
6494
+ limit = name.get_nonarch()
6496
+ iter = arch_core.mirror_archive(from_arch,to_arch, limit)
6497
+ for line in arch.chatter_classifier(iter):
6498
+ cmdutil.colorize(line)
6500
+ def get_parser(self):
6502
+ Returns the options parser to use for the "revision" command.
6504
+ :rtype: cmdutil.CmdOptionParser
6506
+ parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
6509
+ def help(self, parser=None):
6511
+ Prints a help message.
6513
+ :param parser: If supplied, the parser to use for generating help. If \
6514
+ not supplied, it is retrieved.
6515
+ :type parser: cmdutil.CmdOptionParser
6518
+ parser=self.get_parser()
6519
+ parser.print_help()
6521
+Updates a mirror from an archive. If a branch, package, or version is
6522
+supplied, only changes under it are mirrored.
6526
+def help_tree_spec():
6527
+ print """Specifying revisions (default: tree)
6528
+Revisions may be specified by alias, revision, version or patchlevel.
6529
+Revisions or versions may be fully qualified. Unqualified revisions, versions,
6530
+or patchlevels use the archive of the current project tree. Versions will
6531
+use the latest patchlevel in the tree. Patchlevels will use the current tree-
6534
+Use "alias" to list available (user and automatic) aliases."""
6536
+def help_aliases(tree):
6537
+ print """Auto-generated aliases
6538
+ acur : The latest revision in the archive of the tree-version. You can specfy
6539
+ a different version like so: acur:foo--bar--0 (aliases can be used)
6540
+ tcur : (tree current) The latest revision in the tree of the tree-version.
6541
+ You can specify a different version like so: tcur:foo--bar--0 (aliases
6543
+tprev : (tree previous) The previous revision in the tree of the tree-version.
6544
+ To specify an older revision, use a number, e.g. "tprev:4"
6545
+ tanc : (tree ancestor) The ancestor revision of the tree
6546
+ To specify an older revision, use a number, e.g. "tanc:4"
6547
+tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
6548
+ tmod : (tree modified) The latest revision to modify a given file
6549
+ (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
6550
+ ttag : (tree tag) The revision that was tagged into the current tree revision,
6551
+ according to the tree.
6552
+tagcur: (tag current) The latest revision of the version that the current tree
6554
+mergeanc : The common ancestor of the current tree and the specified revision.
6555
+ Defaults to the first partner-version's latest revision or to tagcur.
6557
+ print "User aliases"
6558
+ for parts in ancillary.iter_all_alias(tree):
6559
+ print parts[0].rjust(10)+" : "+parts[1]
6562
+class Inventory(BaseCommand):
6563
+ """List the status of files in the tree"""
6564
+ def __init__(self):
6565
+ self.description=self.__doc__
6567
+ def do_command(self, cmdargs):
6569
+ Master function that perfoms the "revision" command.
6572
+ parser=self.get_parser()
6573
+ (options, args) = parser.parse_args(cmdargs)
6574
+ tree = arch.tree_root()
6577
+ if (options.source):
6578
+ categories.append(arch_core.SourceFile)
6579
+ if (options.precious):
6580
+ categories.append(arch_core.PreciousFile)
6581
+ if (options.backup):
6582
+ categories.append(arch_core.BackupFile)
6583
+ if (options.junk):
6584
+ categories.append(arch_core.JunkFile)
6586
+ if len(categories) == 1:
6587
+ show_leading = False
6589
+ show_leading = True
6591
+ if len(categories) == 0:
6594
+ if options.untagged:
6595
+ categories = arch_core.non_root
6596
+ show_leading = False
6601
+ for file in arch_core.iter_inventory_filter(tree, None,
6602
+ control_files=options.control_files,
6603
+ categories = categories, tagged=tagged):
6604
+ print arch_core.file_line(file,
6605
+ category = show_leading,
6606
+ untagged = show_leading,
6609
+ def get_parser(self):
6611
+ Returns the options parser to use for the "revision" command.
6613
+ :rtype: cmdutil.CmdOptionParser
6615
+ parser=cmdutil.CmdOptionParser("fai inventory [options]")
6616
+ parser.add_option("--ids", action="store_true", dest="ids",
6617
+ help="Show file ids")
6618
+ parser.add_option("--control", action="store_true",
6619
+ dest="control_files", help="include control files")
6620
+ parser.add_option("--source", action="store_true", dest="source",
6621
+ help="List source files")
6622
+ parser.add_option("--backup", action="store_true", dest="backup",
6623
+ help="List backup files")
6624
+ parser.add_option("--precious", action="store_true", dest="precious",
6625
+ help="List precious files")
6626
+ parser.add_option("--junk", action="store_true", dest="junk",
6627
+ help="List junk files")
6628
+ parser.add_option("--unrecognized", action="store_true",
6629
+ dest="unrecognized", help="List unrecognized files")
6630
+ parser.add_option("--untagged", action="store_true",
6631
+ dest="untagged", help="List only untagged files")
6634
+ def help(self, parser=None):
6636
+ Prints a help message.
6638
+ :param parser: If supplied, the parser to use for generating help. If \
6639
+ not supplied, it is retrieved.
6640
+ :type parser: cmdutil.CmdOptionParser
6643
+ parser=self.get_parser()
6644
+ parser.print_help()
6646
+Lists the status of files in the archive:
6654
+Leading letter are not displayed if only one kind of file is shown
6659
+class Alias(BaseCommand):
6660
+ """List or adjust aliases"""
6661
+ def __init__(self):
6662
+ self.description=self.__doc__
6664
+ def get_completer(self, arg, index):
6668
+ self.tree = arch.tree_root()
6673
+ return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
6675
+ return cmdutil.iter_revision_completions(arg, self.tree)
6678
+ def do_command(self, cmdargs):
6680
+ Master function that perfoms the "revision" command.
6683
+ parser=self.get_parser()
6684
+ (options, args) = parser.parse_args(cmdargs)
6686
+ self.tree = arch.tree_root()
6692
+ options.action(args, options)
6693
+ except cmdutil.ForbiddenAliasSyntax, e:
6694
+ raise CommandFailedWrapper(e)
6696
+ def arg_dispatch(self, args, options):
6697
+ """Add, modify, or list aliases, depending on number of arguments
6699
+ :param args: The list of commandline arguments
6700
+ :type args: list of str
6701
+ :param options: The commandline options
6703
+ if len(args) == 0:
6704
+ help_aliases(self.tree)
6706
+ elif len(args) == 1:
6707
+ self.print_alias(args[0])
6708
+ elif (len(args)) == 2:
6709
+ self.add(args[0], args[1], options)
6711
+ raise cmdutil.GetHelp
6713
+ def print_alias(self, alias):
6715
+ for pair in ancillary.iter_all_alias(self.tree):
6716
+ if pair[0] == alias:
6718
+ if answer is not None:
6721
+ print "The alias %s is not assigned." % alias
6723
+ def add(self, alias, expansion, options):
6724
+ """Add or modify aliases
6726
+ :param alias: The alias name to create/modify
6728
+ :param expansion: The expansion to assign to the alias name
6729
+ :type expansion: str
6730
+ :param options: The commandline options
6734
+ new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion,
6736
+ ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
6738
+ for pair in self.get_iterator(options):
6739
+ if pair[0] != alias:
6740
+ newlist+="%s=%s\n" % (pair[0], pair[1])
6746
+ self.write_aliases(newlist, options)
6748
+ def delete(self, args, options):
6749
+ """Delete the specified alias
6751
+ :param args: The list of arguments
6752
+ :type args: list of str
6753
+ :param options: The commandline options
6756
+ if len(args) != 1:
6757
+ raise cmdutil.GetHelp
6759
+ for pair in self.get_iterator(options):
6760
+ if pair[0] != args[0]:
6761
+ newlist+="%s=%s\n" % (pair[0], pair[1])
6765
+ raise errors.NoSuchAlias(args[0])
6766
+ self.write_aliases(newlist, options)
6768
+ def get_alias_file(self, options):
6769
+ """Return the name of the alias file to use
6771
+ :param options: The commandline options
6774
+ if self.tree is None:
6775
+ self.tree == arch.tree_root()
6776
+ return str(self.tree)+"/{arch}/+aliases"
6778
+ return "~/.aba/aliases"
6780
+ def get_iterator(self, options):
6781
+ """Return the alias iterator to use
6783
+ :param options: The commandline options
6785
+ return ancillary.iter_alias(self.get_alias_file(options))
6787
+ def write_aliases(self, newlist, options):
6788
+ """Safely rewrite the alias file
6789
+ :param newlist: The new list of aliases
6790
+ :type newlist: str
6791
+ :param options: The commandline options
6793
+ filename = os.path.expanduser(self.get_alias_file(options))
6794
+ file = cmdutil.NewFileVersion(filename)
6795
+ file.write(newlist)
6799
+ def get_parser(self):
6801
+ Returns the options parser to use for the "alias" command.
6803
+ :rtype: cmdutil.CmdOptionParser
6805
+ parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
6806
+ parser.add_option("-d", "--delete", action="store_const", dest="action",
6807
+ const=self.delete, default=self.arg_dispatch,
6808
+ help="Delete an alias")
6809
+ parser.add_option("--tree", action="store_true", dest="tree",
6810
+ help="Create a per-tree alias", default=False)
6813
+ def help(self, parser=None):
6815
+ Prints a help message.
6817
+ :param parser: If supplied, the parser to use for generating help. If \
6818
+ not supplied, it is retrieved.
6819
+ :type parser: cmdutil.CmdOptionParser
6822
+ parser=self.get_parser()
6823
+ parser.print_help()
6825
+Lists current aliases or modifies the list of aliases.
6827
+If no arguments are supplied, aliases will be listed. If two arguments are
6828
+supplied, the specified alias will be created or modified. If -d or --delete
6829
+is supplied, the specified alias will be deleted.
6831
+You can create aliases that refer to any fully-qualified part of the
6832
+Arch namespace, e.g.
6835
+archive/category--branch,
6836
+archive/category--branch--version (my favourite)
6837
+archive/category--branch--version--patchlevel
6839
+Aliases can be used automatically by native commands. To use them
6840
+with external or tla commands, prefix them with ^ (you can do this
6841
+with native commands, too).
6845
+class RequestMerge(BaseCommand):
6846
+ """Submit a merge request to Bug Goo"""
6847
+ def __init__(self):
6848
+ self.description=self.__doc__
6850
+ def do_command(self, cmdargs):
6851
+ """Submit a merge request
6853
+ :param cmdargs: The commandline arguments
6854
+ :type cmdargs: list of str
6856
+ cmdutil.find_editor()
6857
+ parser = self.get_parser()
6858
+ (options, args) = parser.parse_args(cmdargs)
6860
+ self.tree=arch.tree_root()
6863
+ base, revisions = self.revision_specs(args)
6864
+ message = self.make_headers(base, revisions)
6865
+ message += self.make_summary(revisions)
6866
+ path = self.edit_message(message)
6867
+ message = self.tidy_message(path)
6868
+ if cmdutil.prompt("Send merge"):
6869
+ self.send_message(message)
6870
+ print "Merge request sent"
6872
+ def make_headers(self, base, revisions):
6873
+ """Produce email and Bug Goo header strings
6875
+ :param base: The base revision to apply merges to
6876
+ :type base: `arch.Revision`
6877
+ :param revisions: The revisions to replay into the base
6878
+ :type revisions: list of `arch.Patchlog`
6879
+ :return: The headers
6882
+ headers = "To: gnu-arch-users@gnu.org\n"
6883
+ headers += "From: %s\n" % options.fromaddr
6884
+ if len(revisions) == 1:
6885
+ headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
6887
+ headers += "Subject: [MERGE REQUEST]\n"
6889
+ headers += "Base-Revision: %s\n" % base
6890
+ for revision in revisions:
6891
+ headers += "Revision: %s\n" % revision.revision
6892
+ headers += "Bug: \n\n"
6895
+ def make_summary(self, logs):
6896
+ """Generate a summary of merges
6898
+ :param logs: the patchlogs that were directly added by the merges
6899
+ :type logs: list of `arch.Patchlog`
6900
+ :return: the summary
6905
+ summary+=str(log.revision)+"\n"
6906
+ summary+=log.summary+"\n"
6907
+ if log.description.strip():
6908
+ summary+=log.description.strip('\n')+"\n\n"
6911
+ def revision_specs(self, args):
6912
+ """Determine the base and merge revisions from tree and arguments.
6914
+ :param args: The parsed arguments
6915
+ :type args: list of str
6916
+ :return: The base revision and merge revisions
6917
+ :rtype: `arch.Revision`, list of `arch.Patchlog`
6920
+ target_revision = cmdutil.determine_revision_arch(self.tree,
6923
+ target_revision = cmdutil.tree_latest(self.tree)
6925
+ merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
6926
+ self.tree, f)) for f in args[1:] ]
6928
+ if self.tree is None:
6929
+ raise CantDetermineRevision("", "Not in a project tree")
6930
+ merge_iter = cmdutil.iter_new_merges(self.tree,
6931
+ target_revision.version,
6933
+ merges = [f for f in cmdutil.direct_merges(merge_iter)]
6934
+ return (target_revision, merges)
6936
+ def edit_message(self, message):
6937
+ """Edit an email message in the user's standard editor
6939
+ :param message: The message to edit
6940
+ :type message: str
6941
+ :return: the path of the edited message
6944
+ if self.tree is None:
6945
+ path = os.get_cwd()
6948
+ path += "/,merge-request"
6949
+ file = open(path, 'w')
6950
+ file.write(message)
6952
+ cmdutil.invoke_editor(path)
6955
+ def tidy_message(self, path):
6956
+ """Validate and clean up message.
6958
+ :param path: The path to the message to clean up
6960
+ :return: The parsed message
6961
+ :rtype: `email.Message`
6963
+ mail = email.message_from_file(open(path))
6964
+ if mail["Subject"].strip() == "[MERGE REQUEST]":
6965
+ raise BlandSubject
6967
+ request = email.message_from_string(mail.get_payload())
6968
+ if request.has_key("Bug"):
6969
+ if request["Bug"].strip()=="":
6970
+ del request["Bug"]
6971
+ mail.set_payload(request.as_string())
6974
+ def send_message(self, message):
6975
+ """Send a message, using its headers to address it.
6977
+ :param message: The message to send
6978
+ :type message: `email.Message`"""
6979
+ server = smtplib.SMTP()
6980
+ server.sendmail(message['From'], message['To'], message.as_string())
6983
+ def help(self, parser=None):
6984
+ """Print a usage message
6986
+ :param parser: The options parser to use
6987
+ :type parser: `cmdutil.CmdOptionParser`
6989
+ if parser is None:
6990
+ parser = self.get_parser()
6991
+ parser.print_help()
6993
+Sends a merge request formatted for Bug Goo. Intended use: get the tree
6994
+you'd like to merge into. Apply the merges you want. Invoke request-merge.
6995
+The merge request will open in your $EDITOR.
6997
+When no TARGET is specified, it uses the current tree revision. When
6998
+no MERGE is specified, it uses the direct merges (as in "revisions
6999
+--direct-merges"). But you can specify just the TARGET, or all the MERGE
7003
+ def get_parser(self):
7004
+ """Produce a commandline parser for this command.
7006
+ :rtype: `cmdutil.CmdOptionParser`
7008
+ parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
7012
+'changes' : Changes,
7015
+'apply-changes':ApplyChanges,
7018
+'revision': Revision,
7019
+'revisions': Revisions,
7026
+'mirror-archive': MirrorArchive,
7027
+'ninventory': Inventory,
7029
+'request-merge': RequestMerge,
7032
+'apply-delta' : "Try \"apply-changes\".",
7033
+'delta' : "To compare two revisions, use \"changes\".",
7034
+'diff-rev' : "To compare two revisions, use \"changes\".",
7035
+'undo' : "To undo local changes, use \"revert\".",
7036
+'undelete' : "To undo only deletions, use \"revert --deletions\"",
7037
+'missing-from' : "Try \"revisions --missing-from\".",
7038
+'missing' : "Try \"revisions --missing\".",
7039
+'missing-merge' : "Try \"revisions --partner-missing\".",
7040
+'new-merges' : "Try \"revisions --new-merges\".",
7041
+'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
7042
+'logs' : "Try \"revisions --logs\"",
7043
+'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
7044
+'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
7045
+'change-version' : "Try \"update REVISION\"",
7046
+'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
7047
+'rev-depends' : "Use revisions --dependencies",
7048
+'auto-get' : "Plain get will do archive lookups",
7049
+'tagline' : "Use add-id. It uses taglines in tagline trees",
7050
+'emlog' : "Use elog. It automatically adds log-for-merge text, if any",
7051
+'library-revisions' : "Use revisions --library",
7052
+'file-revert' : "Use revert FILE"
7054
+# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
7056
*** modified file 'bzrlib/branch.py'
7057
--- bzrlib/branch.py
7058
+++ bzrlib/branch.py
7060
from revision import Revision
7061
from errors import bailout, BzrError
7062
from textui import show_status
7064
+from bzrlib import progress
7066
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
7067
## TODO: Maybe include checks for common corruption of newlines, etc?
7068
@@ -864,3 +866,36 @@
7070
s = hexlify(rand_bytes(8))
7071
return '-'.join((name, compact_date(time.time()), s))
7074
+def iter_anno_data(branch, file_id):
7075
+ later_revision = branch.revno()
7076
+ q = range(branch.revno())
7078
+ later_text_id = branch.basis_tree().inventory[file_id].text_id
7082
+ cur_tree = branch.revision_tree(branch.lookup_revision(revno))
7083
+ if file_id not in cur_tree.inventory:
7086
+ text_id = cur_tree.inventory[file_id].text_id
7087
+ if text_id != later_text_id:
7088
+ patch = get_patch(branch, revno, later_revision, file_id)
7089
+ yield revno, patch.iter_inserted(), patch
7090
+ later_revision = revno
7091
+ later_text_id = text_id
7092
+ yield progress.Progress("revisions", i)
7094
+def get_patch(branch, old_revno, new_revno, file_id):
7095
+ old_tree = branch.revision_tree(branch.lookup_revision(old_revno))
7096
+ new_tree = branch.revision_tree(branch.lookup_revision(new_revno))
7097
+ if file_id in old_tree.inventory:
7098
+ old_file = old_tree.get_file(file_id).readlines()
7101
+ ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
7102
+ return patches.parse_patch(ud)
7106
*** modified file 'bzrlib/commands.py'
7107
--- bzrlib/commands.py
7108
+++ bzrlib/commands.py
7110
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
7112
from bzrlib import merge
7113
+from bzrlib.branch import iter_anno_data
7114
+from bzrlib import patches
7115
+from bzrlib import progress
7118
def _squish_command_name(cmd):
7119
@@ -882,7 +885,15 @@
7120
print '%3d FAILED!' % mf
7124
+ result = bzrlib.patches.test()
7125
+ resultFailed = len(result.errors) + len(result.failures)
7126
+ print '%-40s %3d tests' % ('bzrlib.patches', result.testsRun),
7128
+ print '%3d FAILED!' % resultFailed
7131
+ tests += result.testsRun
7132
+ failures += resultFailed
7133
print '%-40s %3d tests' % ('total', tests),
7135
print '%3d FAILED!' % failures
7136
@@ -897,6 +908,34 @@
7137
"""Show version of bzr"""
7141
+class cmd_annotate(Command):
7142
+ """Show which revision added each line in a file"""
7144
+ takes_args = ['filename']
7145
+ def run(self, filename):
7146
+ if not os.path.exists(filename):
7147
+ raise BzrCommandError("The file %s does not exist." % filename)
7148
+ branch = (Branch(filename))
7149
+ file_id = branch.working_tree().path2id(filename)
7150
+ if file_id is None:
7151
+ raise BzrCommandError("The file %s is not versioned." % filename)
7152
+ lines = branch.basis_tree().get_file(file_id)
7153
+ total = branch.revno()
7154
+ anno_d_iter = iter_anno_data(branch, file_id)
7155
+ progress_bar = progress.ProgressBar()
7157
+ for result in patches.iter_annotate_file(lines, anno_d_iter):
7158
+ if isinstance(result, progress.Progress):
7159
+ result.total = total
7160
+ progress_bar(result)
7162
+ anno_lines = result
7164
+ progress.clear_progress_bar()
7165
+ for line in anno_lines:
7166
+ sys.stdout.write("%4s:%s" % (str(line.log), line.text))
7170
print "bzr (bazaar-ng) %s" % bzrlib.__version__