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
519
+class Progress(object):
520
+ def __init__(self, units, current, total=None):
522
+ self.current = current
524
+ self.percent = None
525
+ if self.total is not None:
526
+ self.percent = 100.0 * current / total
529
+ if self.total is not None:
530
+ return "%i of %i %s %.1f%%" % (self.current, self.total, self.units,
533
+ return "%i %s" (self.current, self.units)
536
+def progress_bar(progress):
537
+ fmt = " %i of %i %s (%.1f%%)"
538
+ f = fmt % (progress.total, progress.total, progress.units, 100.0)
541
+ markers = int (float(cols) * progress.current / progress.total)
542
+ txt = fmt % (progress.current, progress.total, progress.units,
544
+ sys.stderr.write("\r[%s%s]%s" % ('='*markers, ' '*(cols-markers), txt))
546
+def clear_progress_bar():
547
+ sys.stderr.write('\r%s\r' % (' '*79))
549
+def spinner_str(progress, show_text=False):
551
+ Produces the string for a textual "spinner" progress indicator
552
+ :param progress: an object represinting current progress
553
+ :param show_text: If true, show progress text as well
554
+ :return: The spinner string
556
+ >>> spinner_str(Progress("baloons", 0))
558
+ >>> spinner_str(Progress("baloons", 5))
560
+ >>> spinner_str(Progress("baloons", 6), show_text=True)
563
+ positions = ('|', '/', '-', '\\')
564
+ text = positions[progress.current % 4]
566
+ text+=" %i %s" % (progress.current, progress.units)
569
+def spinner(progress, show_text=False, output=sys.stderr):
571
+ Update a spinner progress indicator on an output
572
+ :param progress: The progress to display
573
+ :param show_text: If true, show text as well as spinner
574
+ :param output: The output to write to
576
+ >>> spinner(Progress("baloons", 6), show_text=True, output=sys.stdout)
579
+ output.write('\r%s' % spinner_str(progress, show_text))
583
+ result = doctest.testmod()
586
+ print "All tests passed"
588
+ print "No tests to run"
589
+if __name__ == "__main__":
592
*** added directory 'testdata'
593
*** added file 'testdata/diff'
597
+--- orig/commands.py
604
++import pylon.errors
605
++from pylon.errors import *
606
++from pylon import errors
607
++from pylon import util
608
++from pylon import arch_core
609
++from pylon import arch_compound
610
++from pylon import ancillary
611
++from pylon import misc
612
++from pylon import paths
626
+-from errors import *
635
+ __docformat__ = "restructuredtext"
636
+ __doc__ = "Implementation of user (sub) commands"
639
+ tree=arch.tree_root()
641
+- a_spec = cmdutil.comp_revision(tree)
643
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
644
+ cmdutil.ensure_archive_registered(a_spec.archive)
646
+ changeset=options.changeset
649
+- tmpdir=cmdutil.tmpdir()
650
+ changeset=tmpdir+"/changeset"
652
+ delta=arch.iter_delta(a_spec, b_spec, changeset)
653
+@@ -304,14 +310,14 @@
656
+ if (options.perform_diff):
657
+- chan = cmdutil.ChangesetMunger(changeset)
658
+ chan.read_indices()
659
+- if isinstance(b_spec, arch.Revision):
660
+- b_dir = b_spec.library_find()
663
+- a_dir = a_spec.library_find()
664
+ if options.diffopts is not None:
665
+ diffopts = options.diffopts.split()
666
+ cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
669
+ except arch.errors.TreeRootError, e:
672
+- from_revision=cmdutil.tree_latest(tree)
673
+ if from_revision==to_revision:
674
+ print "Tree is already up to date with:\n"+str(to_revision)+"."
681
+ revision=cmdutil.determine_revision_arch(tree, options.version)
682
+ return options, revision.get_version(), args
684
+@@ -601,11 +610,16 @@
686
+ tree=arch.tree_root()
687
+ options, version, files = self.parse_commandline(cmdargs, tree)
688
+ if options.__dict__.has_key("base") and options.base:
689
+ base = cmdutil.determine_revision_tree(tree, options.base)
691
+- base = cmdutil.submit_revision(tree)
694
+ writeversion=version
695
+ archive=version.archive
696
+ source=cmdutil.get_mirror_source(archive)
697
+@@ -625,18 +639,26 @@
699
+ last_revision=tree.iter_logs(version, True).next().revision
700
+ except StopIteration, e:
701
+- if cmdutil.prompt("Import from commit"):
702
+- return do_import(version)
704
+- raise NoVersionLogs(version)
705
+- if last_revision!=version.iter_revisions(True).next():
706
+ if not cmdutil.prompt("Out of date"):
712
+- if not cmdutil.has_changed(version):
713
+ if not cmdutil.prompt("Empty commit"):
715
+ except arch.util.ExecProblem, e:
716
+@@ -645,15 +667,15 @@
720
+- log = tree.log_message(create=False)
723
+ if cmdutil.prompt("Create log"):
726
+ except cmdutil.NoEditorSpecified, e:
727
+ raise CommandFailed(e)
728
+- log = tree.log_message(create=False)
731
+ if log["Summary"] is None or len(log["Summary"].strip()) == 0:
732
+@@ -837,23 +859,24 @@
733
+ if spec is not None:
734
+ revision = cmdutil.determine_revision_tree(tree, spec)
736
+- revision = cmdutil.comp_revision(tree)
737
+ except cmdutil.CantDetermineRevision, e:
738
+ raise CommandFailedWrapper(e)
741
+ if options.file_contents or options.file_perms or options.deletions\
742
+ or options.additions or options.renames or options.hunk_prompt:
743
+- munger = cmdutil.MungeOpts()
744
+- munger.hunk_prompt = options.hunk_prompt
746
+ if len(args) > 0 or options.logs or options.pattern_files or \
749
+- munger = cmdutil.MungeOpts(True)
750
+ munger.all_types(True)
752
+- t_cwd = cmdutil.tree_cwd(tree)
757
+ if options.pattern_files:
758
+ munger.add_keep_pattern(options.pattern_files)
760
+- for line in cmdutil.revert(tree, revision, munger,
761
+ not options.no_output):
762
+ cmdutil.colorize(line)
764
+@@ -1042,18 +1065,13 @@
768
+-def require_version_exists(version, spec):
769
+- if not version.exists():
770
+- raise cmdutil.CantDetermineVersion(spec,
771
+- "The version %s does not exist." \
774
+ class Revisions(BaseCommand):
776
+ Print a revision name based on a revision specifier
778
+ def __init__(self):
779
+ self.description="Lists revisions"
781
+ def do_command(self, cmdargs):
783
+@@ -1066,224 +1084,68 @@
784
+ self.tree = arch.tree_root()
785
+ except arch.errors.TreeRootError:
788
+- iter = self.get_iterator(options.type, args, options.reverse,
790
+ except cmdutil.CantDetermineRevision, e:
791
+ raise CommandFailedWrapper(e)
793
+ if options.skip is not None:
794
+ iter = cmdutil.iter_skip(iter, int(options.skip))
796
+- for revision in iter:
798
+- if isinstance(revision, arch.Patchlog):
800
+- revision=revision.revision
801
+- print options.display(revision)
802
+- if log is None and (options.summary or options.creator or
803
+- options.date or options.merges):
804
+- log = revision.patchlog
805
+- if options.creator:
806
+- print " %s" % log.creator
808
+- print " %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
809
+- if options.summary:
810
+- print " %s" % log.summary
811
+- if options.merges:
812
+- showed_title = False
813
+- for revision in log.merged_patches:
814
+- if not showed_title:
816
+- showed_title = True
817
+- print " %s" % revision
819
+- def get_iterator(self, type, args, reverse, modified):
824
+- if modified is not None:
825
+- iter = cmdutil.modified_iter(modified, self.tree)
829
+- return cmdutil.iter_reverse(iter)
830
+- elif type == "archive":
832
+- if self.tree is None:
833
+- raise cmdutil.CantDetermineRevision("",
834
+- "Not in a project tree")
835
+- version = cmdutil.determine_version_tree(spec, self.tree)
837
+- version = cmdutil.determine_version_arch(spec, self.tree)
838
+- cmdutil.ensure_archive_registered(version.archive)
839
+- require_version_exists(version, spec)
840
+- return version.iter_revisions(reverse)
841
+- elif type == "cacherevs":
843
+- if self.tree is None:
844
+- raise cmdutil.CantDetermineRevision("",
845
+- "Not in a project tree")
846
+- version = cmdutil.determine_version_tree(spec, self.tree)
848
+- version = cmdutil.determine_version_arch(spec, self.tree)
849
+- cmdutil.ensure_archive_registered(version.archive)
850
+- require_version_exists(version, spec)
851
+- return cmdutil.iter_cacherevs(version, reverse)
852
+- elif type == "library":
854
+- if self.tree is None:
855
+- raise cmdutil.CantDetermineRevision("",
856
+- "Not in a project tree")
857
+- version = cmdutil.determine_version_tree(spec, self.tree)
859
+- version = cmdutil.determine_version_arch(spec, self.tree)
860
+- return version.iter_library_revisions(reverse)
861
+- elif type == "logs":
862
+- if self.tree is None:
863
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
864
+- return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
865
+- self.tree), reverse)
866
+- elif type == "missing" or type == "skip-present":
867
+- if self.tree is None:
868
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
869
+- skip = (type == "skip-present")
870
+- version = cmdutil.determine_version_tree(spec, self.tree)
871
+- cmdutil.ensure_archive_registered(version.archive)
872
+- require_version_exists(version, spec)
873
+- return cmdutil.iter_missing(self.tree, version, reverse,
874
+- skip_present=skip)
876
+- elif type == "present":
877
+- if self.tree is None:
878
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
879
+- version = cmdutil.determine_version_tree(spec, self.tree)
880
+- cmdutil.ensure_archive_registered(version.archive)
881
+- require_version_exists(version, spec)
882
+- return cmdutil.iter_present(self.tree, version, reverse)
884
+- elif type == "new-merges" or type == "direct-merges":
885
+- if self.tree is None:
886
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
887
+- version = cmdutil.determine_version_tree(spec, self.tree)
888
+- cmdutil.ensure_archive_registered(version.archive)
889
+- require_version_exists(version, spec)
890
+- iter = cmdutil.iter_new_merges(self.tree, version, reverse)
891
+- if type == "new-merges":
893
+- elif type == "direct-merges":
894
+- return cmdutil.direct_merges(iter)
896
+- elif type == "missing-from":
897
+- if self.tree is None:
898
+- raise cmdutil.CantDetermineRevision("", "Not in a project tree")
899
+- revision = cmdutil.determine_revision_tree(self.tree, spec)
900
+- libtree = cmdutil.find_or_make_local_revision(revision)
901
+- return cmdutil.iter_missing(libtree, self.tree.tree_version,
904
+- elif type == "partner-missing":
905
+- return cmdutil.iter_partner_missing(self.tree, reverse)
907
+- elif type == "ancestry":
908
+- revision = cmdutil.determine_revision_tree(self.tree, spec)
909
+- iter = cmdutil._iter_ancestry(self.tree, revision)
913
+- return cmdutil.iter_reverse(iter)
915
+- elif type == "dependencies" or type == "non-dependencies":
916
+- nondeps = (type == "non-dependencies")
917
+- revision = cmdutil.determine_revision_tree(self.tree, spec)
918
+- anc_iter = cmdutil._iter_ancestry(self.tree, revision)
919
+- iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
921
+- return iter_depends
923
+- return cmdutil.iter_reverse(iter_depends)
924
+- elif type == "micro":
925
+- return cmdutil.iter_micro(self.tree)
929
+ def get_parser(self):
931
+ Returns the options parser to use for the "revision" command.
933
+ :rtype: cmdutil.CmdOptionParser
935
+- parser=cmdutil.CmdOptionParser("fai revisions [revision]")
936
+ select = cmdutil.OptionGroup(parser, "Selection options",
937
+ "Control which revisions are listed. These options"
938
+ " are mutually exclusive. If more than one is"
939
+ " specified, the last is used.")
940
+- select.add_option("", "--archive", action="store_const",
941
+- const="archive", dest="type", default="archive",
942
+- help="List all revisions in the archive")
943
+- select.add_option("", "--cacherevs", action="store_const",
944
+- const="cacherevs", dest="type",
945
+- help="List all revisions stored in the archive as "
946
+- "complete copies")
947
+- select.add_option("", "--logs", action="store_const",
948
+- const="logs", dest="type",
949
+- help="List revisions that have a patchlog in the "
951
+- select.add_option("", "--missing", action="store_const",
952
+- const="missing", dest="type",
953
+- help="List revisions from the specified version that"
954
+- " have no patchlog in the tree")
955
+- select.add_option("", "--skip-present", action="store_const",
956
+- const="skip-present", dest="type",
957
+- help="List revisions from the specified version that"
958
+- " have no patchlogs at all in the tree")
959
+- select.add_option("", "--present", action="store_const",
960
+- const="present", dest="type",
961
+- help="List revisions from the specified version that"
962
+- " have no patchlog in the tree, but can't be merged")
963
+- select.add_option("", "--missing-from", action="store_const",
964
+- const="missing-from", dest="type",
965
+- help="List revisions from the specified revision "
966
+- "that have no patchlog for the tree version")
967
+- select.add_option("", "--partner-missing", action="store_const",
968
+- const="partner-missing", dest="type",
969
+- help="List revisions in partner versions that are"
971
+- select.add_option("", "--new-merges", action="store_const",
972
+- const="new-merges", dest="type",
973
+- help="List revisions that have had patchlogs added"
974
+- " to the tree since the last commit")
975
+- select.add_option("", "--direct-merges", action="store_const",
976
+- const="direct-merges", dest="type",
977
+- help="List revisions that have been directly added"
978
+- " to tree since the last commit ")
979
+- select.add_option("", "--library", action="store_const",
980
+- const="library", dest="type",
981
+- help="List revisions in the revision library")
982
+- select.add_option("", "--ancestry", action="store_const",
983
+- const="ancestry", dest="type",
984
+- help="List revisions that are ancestors of the "
985
+- "current tree version")
987
+- select.add_option("", "--dependencies", action="store_const",
988
+- const="dependencies", dest="type",
989
+- help="List revisions that the given revision "
992
+- select.add_option("", "--non-dependencies", action="store_const",
993
+- const="non-dependencies", dest="type",
994
+- help="List revisions that the given revision "
995
+- "does not depend on")
997
+- select.add_option("--micro", action="store_const",
998
+- const="micro", dest="type",
999
+- help="List partner revisions aimed for this "
1002
+- select.add_option("", "--modified", dest="modified",
1003
+- help="List tree ancestor revisions that modified a "
1004
+- "given file", metavar="FILE[:LINE]")
1006
+ parser.add_option("", "--skip", dest="skip",
1007
+ help="Skip revisions. Positive numbers skip from "
1008
+ "beginning, negative skip from end.",
1009
+@@ -1312,6 +1174,9 @@
1010
+ format.add_option("--cacherev", action="store_const",
1011
+ const=paths.determine_cacherev_path, dest="display",
1012
+ help="Show location of cacherev file")
1013
+ parser.add_option_group(format)
1014
+ display = cmdutil.OptionGroup(parser, "Display format options",
1015
+ "These control the display of data")
1016
+@@ -1448,6 +1313,7 @@
1017
+ if os.access(self.history_file, os.R_OK) and \
1018
+ os.path.isfile(self.history_file):
1019
+ readline.read_history_file(self.history_file)
1021
+ def write_history(self):
1022
+ readline.write_history_file(self.history_file)
1023
+@@ -1470,16 +1336,21 @@
1024
+ def set_prompt(self):
1025
+ if self.tree is not None:
1027
+- version = " "+self.tree.tree_version.nonarch
1032
+- self.prompt = "Fai%s> " % version
1034
+ def set_title(self, command=None):
1036
+- version = self.tree.tree_version.nonarch
1038
+ version = "[no version]"
1039
+ if command is None:
1040
+@@ -1489,8 +1360,15 @@
1041
+ def do_cd(self, line):
1045
+- os.chdir(os.path.expanduser(line))
1046
+ except Exception, e:
1049
+@@ -1523,7 +1401,7 @@
1050
+ except cmdutil.CantDetermineRevision, e:
1052
+ except Exception, e:
1053
+- print "Unhandled error:\n%s" % cmdutil.exception_str(e)
1055
+ elif suggestions.has_key(args[0]):
1056
+ print suggestions[args[0]]
1057
+@@ -1574,7 +1452,7 @@
1058
+ arg = line.split()[-1]
1061
+- iter = iter_munged_completions(iter, arg, text)
1062
+ except Exception, e:
1065
+@@ -1604,10 +1482,11 @@
1068
+ if arg.startswith("-"):
1069
+- return list(iter_munged_completions(iter, arg, text))
1071
+- return list(iter_munged_completions(
1072
+- iter_file_completions(arg), arg, text))
1076
+@@ -1615,13 +1494,13 @@
1077
+ arg = args.split()[-1]
1080
+- iter = iter_dir_completions(arg)
1081
+- iter = iter_munged_completions(iter, arg, text)
1084
+ arg = args.split()[-1]
1085
+- return list(iter_munged_completions(iter_file_completions(arg),
1088
+ return self.completenames(text, line, begidx, endidx)
1089
+ except Exception, e:
1090
+@@ -1636,44 +1515,8 @@
1094
+-def iter_file_completions(arg, only_dirs = False):
1095
+- """Generate an iterator that iterates through filename completions.
1097
+- :param arg: The filename fragment to match
1099
+- :param only_dirs: If true, match only directories
1100
+- :type only_dirs: bool
1102
+- cwd = os.getcwd()
1104
+- extras = [".", ".."]
1107
+- (dir, file) = os.path.split(arg)
1109
+- listingdir = os.path.expanduser(dir)
1112
+- for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
1114
+- userfile = dir+'/'+file
1117
+- if userfile.startswith(arg):
1118
+- if os.path.isdir(listingdir+'/'+file):
1121
+- elif not only_dirs:
1124
+-def iter_munged_completions(iter, arg, text):
1125
+- for completion in iter:
1126
+- completion = str(completion)
1127
+- if completion.startswith(arg):
1128
+- yield completion[len(arg)-len(text):]
1130
+ def iter_source_file_completions(tree, arg):
1131
+- treepath = cmdutil.tree_cwd(tree)
1132
+ if len(treepath) > 0:
1135
+@@ -1701,7 +1544,7 @@
1136
+ :return: An iterator of all matching untagged files
1137
+ :rtype: iterator of str
1139
+- treepath = cmdutil.tree_cwd(tree)
1140
+ if len(treepath) > 0:
1143
+@@ -1743,8 +1586,8 @@
1144
+ :param arg: The prefix to match
1147
+- treepath = cmdutil.tree_cwd(tree)
1148
+- tmpdir = cmdutil.tmpdir()
1149
+ changeset = tmpdir+"/changeset"
1151
+ revision = cmdutil.determine_revision_tree(tree)
1152
+@@ -1756,14 +1599,6 @@
1153
+ shutil.rmtree(tmpdir)
1154
+ return completions
1156
+-def iter_dir_completions(arg):
1157
+- """Generate an iterator that iterates through directory name completions.
1159
+- :param arg: The directory name fragment to match
1162
+- return iter_file_completions(arg, True)
1164
+ class Shell(BaseCommand):
1165
+ def __init__(self):
1166
+ self.description = "Runs Fai as a shell"
1167
+@@ -1795,7 +1630,11 @@
1168
+ parser=self.get_parser()
1169
+ (options, args) = parser.parse_args(cmdargs)
1171
+- tree = arch.tree_root()
1173
+ if (len(args) == 0) == (options.untagged == False):
1174
+ raise cmdutil.GetHelp
1175
+@@ -1809,13 +1648,22 @@
1176
+ if options.id_type == "tagline":
1177
+ if method != "tagline":
1178
+ if not cmdutil.prompt("Tagline in other tree"):
1179
+- if method == "explicit":
1180
+- options.id_type == explicit
1182
+ print "add-id not supported for \"%s\" tagging method"\
1186
+ elif options.id_type == "explicit":
1187
+ if method != "tagline" and method != explicit:
1188
+ if not prompt("Explicit in other tree"):
1189
+@@ -1824,7 +1672,8 @@
1192
+ if options.id_type == "auto":
1193
+- if method != "tagline" and method != "explicit":
1194
+ print "add-id not supported for \"%s\" tagging method" % method
1197
+@@ -1852,10 +1701,12 @@
1198
+ previous_files.extend(files)
1199
+ if id_type == "explicit":
1200
+ cmdutil.add_id(files)
1201
+- elif id_type == "tagline":
1202
+ for file in files:
1204
+- cmdutil.add_tagline_or_explicit_id(file)
1205
+ except cmdutil.AlreadyTagged:
1206
+ print "\"%s\" already has a tagline." % file
1207
+ except cmdutil.NoCommentSyntax:
1208
+@@ -1888,6 +1739,9 @@
1209
+ parser.add_option("--tagline", action="store_const",
1210
+ const="tagline", dest="id_type",
1211
+ help="Use a tagline id")
1212
+ parser.add_option("--untagged", action="store_true",
1213
+ dest="untagged", default=False,
1214
+ help="tag all untagged files")
1215
+@@ -1926,27 +1780,7 @@
1216
+ def get_completer(self, arg, index):
1217
+ if self.tree is None:
1218
+ raise arch.errors.TreeRootError
1219
+- completions = list(ancillary.iter_partners(self.tree,
1220
+- self.tree.tree_version))
1221
+- if len(completions) == 0:
1222
+- completions = list(self.tree.iter_log_versions())
1226
+- for completion in completions:
1227
+- alias = ancillary.compact_alias(str(completion), self.tree)
1229
+- aliases.extend(alias)
1231
+- for completion in completions:
1232
+- if completion.archive == self.tree.tree_version.archive:
1233
+- aliases.append(completion.nonarch)
1235
+- except Exception, e:
1238
+- completions.extend(aliases)
1239
+- return completions
1241
+ def do_command(self, cmdargs):
1243
+@@ -1961,7 +1795,7 @@
1245
+ if self.tree is None:
1246
+ raise arch.errors.TreeRootError(os.getcwd())
1247
+- if cmdutil.has_changed(self.tree.tree_version):
1248
+ raise UncommittedChanges(self.tree)
1251
+@@ -2027,14 +1861,14 @@
1252
+ :type other_revision: `arch.Revision`
1253
+ :return: 0 if the merge was skipped, 1 if it was applied
1255
+- other_tree = cmdutil.find_or_make_local_revision(other_revision)
1257
+ if action == "native-merge":
1258
+- ancestor = cmdutil.merge_ancestor2(self.tree, other_tree,
1260
+ elif action == "update":
1261
+- ancestor = cmdutil.tree_latest(self.tree,
1262
+- other_revision.version)
1263
+ except CantDetermineRevision, e:
1264
+ raise CommandFailedWrapper(e)
1265
+ cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
1266
+@@ -2104,7 +1938,10 @@
1267
+ if self.tree is None:
1268
+ raise arch.errors.TreeRootError
1270
+- edit_log(self.tree)
1272
+ def get_parser(self):
1274
+@@ -2132,7 +1969,7 @@
1278
+-def edit_log(tree):
1279
++def edit_log(tree, version):
1280
+ """Makes and edits the log for a tree. Does all kinds of fancy things
1281
+ like log templates and merge summaries and log-for-merge
1283
+@@ -2141,28 +1978,29 @@
1285
+ #ensure we have an editor before preparing the log
1286
+ cmdutil.find_editor()
1287
+- log = tree.log_message(create=False)
1288
+ log_is_new = False
1289
+ if log is None or cmdutil.prompt("Overwrite log"):
1290
+ if log is not None:
1291
+ os.remove(log.name)
1292
+- log = tree.log_message(create=True)
1295
+- template = tree+"/{arch}/=log-template"
1296
+- if not os.path.exists(template):
1297
+- template = os.path.expanduser("~/.arch-params/=log-template")
1298
+- if not os.path.exists(template):
1301
+ shutil.copyfile(template, tmplog)
1303
+- new_merges = list(cmdutil.iter_new_merges(tree,
1304
+- tree.tree_version))
1305
+- log["Summary"] = merge_summary(new_merges, tree.tree_version)
1306
+ if len(new_merges) > 0:
1307
+ if cmdutil.prompt("Log for merge"):
1308
+- mergestuff = cmdutil.log_for_merge(tree)
1309
+ log.description += mergestuff
1312
+@@ -2172,29 +2010,6 @@
1313
+ os.remove(log.name)
1316
+-def merge_summary(new_merges, tree_version):
1317
+- if len(new_merges) == 0:
1319
+- if len(new_merges) == 1:
1320
+- summary = new_merges[0].summary
1322
+- summary = "Merge"
1325
+- for merge in new_merges:
1326
+- if arch.my_id() != merge.creator:
1327
+- name = re.sub("<.*>", "", merge.creator).rstrip(" ");
1328
+- if not name in credits:
1329
+- credits.append(name)
1331
+- version = merge.revision.version
1332
+- if version.archive == tree_version.archive:
1333
+- if not version.nonarch in credits:
1334
+- credits.append(version.nonarch)
1335
+- elif not str(version) in credits:
1336
+- credits.append(str(version))
1338
+- return ("%s (%s)") % (summary, ", ".join(credits))
1340
+ class MirrorArchive(BaseCommand):
1342
+@@ -2268,31 +2083,73 @@
1344
+ Use "alias" to list available (user and automatic) aliases."""
1348
++"The latest revision in the archive of the tree-version. You can specify \
1349
++a different version like so: acur:foo--bar--0 (aliases can be used)",
1351
++"""(tree current) The latest revision in the tree of the tree-version. \
1352
++You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
1355
++"""(tree previous) The previous revision in the tree of the tree-version. To \
1356
++specify an older revision, use a number, e.g. "tprev:4" """,
1358
++"""(tree ancestor) The ancestor revision of the tree To specify an older \
1359
++revision, use a number, e.g. "tanc:4".""",
1361
++"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
1363
++""" (tree modified) The latest revision to modify a given file, e.g. \
1364
++"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
1366
++"""(tree tag) The revision that was tagged into the current tree revision, \
1367
++according to the tree""",
1369
++"""(tag current) The latest revision of the version that the current tree \
1370
++was tagged from.""",
1372
++"""The common ancestor of the current tree and the specified revision. \
1373
++Defaults to the first partner-version's latest revision or to tagcur.""",
1377
++def is_auto_alias(name):
1381
++def display_def(iter, wrap = 80):
1385
+ def help_aliases(tree):
1386
+- print """Auto-generated aliases
1387
+- acur : The latest revision in the archive of the tree-version. You can specfy
1388
+- a different version like so: acur:foo--bar--0 (aliases can be used)
1389
+- tcur : (tree current) The latest revision in the tree of the tree-version.
1390
+- You can specify a different version like so: tcur:foo--bar--0 (aliases
1392
+-tprev : (tree previous) The previous revision in the tree of the tree-version.
1393
+- To specify an older revision, use a number, e.g. "tprev:4"
1394
+- tanc : (tree ancestor) The ancestor revision of the tree
1395
+- To specify an older revision, use a number, e.g. "tanc:4"
1396
+-tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
1397
+- tmod : (tree modified) The latest revision to modify a given file
1398
+- (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
1399
+- ttag : (tree tag) The revision that was tagged into the current tree revision,
1400
+- according to the tree.
1401
+-tagcur: (tag current) The latest revision of the version that the current tree
1403
+-mergeanc : The common ancestor of the current tree and the specified revision.
1404
+- Defaults to the first partner-version's latest revision or to tagcur.
1406
+ print "User aliases"
1407
+- for parts in ancillary.iter_all_alias(tree):
1408
+- print parts[0].rjust(10)+" : "+parts[1]
1411
+ class Inventory(BaseCommand):
1412
+ """List the status of files in the tree"""
1413
+@@ -2428,6 +2285,11 @@
1414
+ except cmdutil.ForbiddenAliasSyntax, e:
1415
+ raise CommandFailedWrapper(e)
1417
+ def arg_dispatch(self, args, options):
1418
+ """Add, modify, or list aliases, depending on number of arguments
1420
+@@ -2438,15 +2300,20 @@
1421
+ if len(args) == 0:
1422
+ help_aliases(self.tree)
1424
+- elif len(args) == 1:
1425
+- self.print_alias(args[0])
1426
+- elif (len(args)) == 2:
1427
+- self.add(args[0], args[1], options)
1429
+- raise cmdutil.GetHelp
1431
+ def print_alias(self, alias):
1433
+ for pair in ancillary.iter_all_alias(self.tree):
1434
+ if pair[0] == alias:
1436
+@@ -2464,6 +2331,8 @@
1437
+ :type expansion: str
1438
+ :param options: The commandline options
1442
+ new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion,
1443
+@@ -2490,14 +2359,17 @@
1445
+ if len(args) != 1:
1446
+ raise cmdutil.GetHelp
1448
+ for pair in self.get_iterator(options):
1449
+- if pair[0] != args[0]:
1450
+ newlist+="%s=%s\n" % (pair[0], pair[1])
1454
+- raise errors.NoSuchAlias(args[0])
1455
+ self.write_aliases(newlist, options)
1457
+ def get_alias_file(self, options):
1458
+@@ -2526,7 +2398,7 @@
1459
+ :param options: The commandline options
1461
+ filename = os.path.expanduser(self.get_alias_file(options))
1462
+- file = cmdutil.NewFileVersion(filename)
1463
+ file.write(newlist)
1466
+@@ -2588,10 +2460,13 @@
1467
+ :param cmdargs: The commandline arguments
1468
+ :type cmdargs: list of str
1470
+- cmdutil.find_editor()
1471
+ parser = self.get_parser()
1472
+ (options, args) = parser.parse_args(cmdargs)
1474
+ self.tree=arch.tree_root()
1477
+@@ -2655,7 +2530,7 @@
1478
+ target_revision = cmdutil.determine_revision_arch(self.tree,
1481
+- target_revision = cmdutil.tree_latest(self.tree)
1483
+ merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
1484
+ self.tree, f)) for f in args[1:] ]
1485
+@@ -2711,7 +2586,7 @@
1487
+ :param message: The message to send
1488
+ :type message: `email.Message`"""
1489
+- server = smtplib.SMTP()
1490
+ server.sendmail(message['From'], message['To'], message.as_string())
1493
+@@ -2763,6 +2638,22 @@
1495
+ 'request-merge': RequestMerge,
1498
++def my_import(mod_name):
1500
++def plugin(mod_name):
1502
++for file in os.listdir(sys.path[0]+"/command"):
1505
+ 'apply-delta' : "Try \"apply-changes\".",
1506
+ 'delta' : "To compare two revisions, use \"changes\".",
1507
+@@ -2784,6 +2675,7 @@
1508
+ 'tagline' : "Use add-id. It uses taglines in tagline trees",
1509
+ 'emlog' : "Use elog. It automatically adds log-for-merge text, if any",
1510
+ 'library-revisions' : "Use revisions --library",
1511
+-'file-revert' : "Use revert FILE"
1512
++'file-revert' : "Use revert FILE",
1513
++'join-branch' : "Use replay --logs-only"
1515
+ # arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
1517
*** added file 'testdata/insert_top.patch'
1519
+++ testdata/insert_top.patch
1521
+--- orig/pylon/patches.py
1522
++++ mod/pylon/patches.py
1527
+ class PatchSyntax(Exception):
1529
*** added file 'testdata/mod'
1533
+# Copyright (C) 2004 Aaron Bentley
1534
+# <aaron.bentley@utoronto.ca>
1536
+# This program is free software; you can redistribute it and/or modify
1537
+# it under the terms of the GNU General Public License as published by
1538
+# the Free Software Foundation; either version 2 of the License, or
1539
+# (at your option) any later version.
1541
+# This program is distributed in the hope that it will be useful,
1542
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1543
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1544
+# GNU General Public License for more details.
1546
+# You should have received a copy of the GNU General Public License
1547
+# along with this program; if not, write to the Free Software
1548
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1555
+import pylon.errors
1556
+from pylon.errors import *
1557
+from pylon import errors
1558
+from pylon import util
1559
+from pylon import arch_core
1560
+from pylon import arch_compound
1561
+from pylon import ancillary
1562
+from pylon import misc
1563
+from pylon import paths
1580
+__docformat__ = "restructuredtext"
1581
+__doc__ = "Implementation of user (sub) commands"
1584
+def find_command(cmd):
1586
+ Return an instance of a command type. Return None if the type isn't
1589
+ :param cmd: the name of the command to look for
1590
+ :type cmd: the type of the command
1592
+ if commands.has_key(cmd):
1593
+ return commands[cmd]()
1598
+ def __call__(self, cmdline):
1600
+ self.do_command(cmdline.split())
1601
+ except cmdutil.GetHelp, e:
1603
+ except Exception, e:
1606
+ def get_completer(index):
1609
+ def complete(self, args, text):
1611
+ Returns a list of possible completions for the given text.
1613
+ :param args: The complete list of arguments
1614
+ :type args: List of str
1615
+ :param text: text to complete (may be shorter than args[-1])
1617
+ :rtype: list of str
1623
+ realtext = args[-1]
1628
+ parser=self.get_parser()
1629
+ if realtext.startswith('-'):
1630
+ candidates = parser.iter_options()
1632
+ (options, parsed_args) = parser.parse_args(args)
1634
+ if len (parsed_args) > 0:
1635
+ candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
1637
+ candidates = self.get_completer("", 0)
1640
+ if candidates is None:
1642
+ for candidate in candidates:
1643
+ candidate = str(candidate)
1644
+ if candidate.startswith(realtext):
1645
+ matches.append(candidate[len(realtext)- len(text):])
1649
+class Help(BaseCommand):
1651
+ Lists commands, prints help messages.
1653
+ def __init__(self):
1654
+ self.description="Prints help mesages"
1655
+ self.parser = None
1657
+ def do_command(self, cmdargs):
1659
+ Prints a help message.
1661
+ options, args = self.get_parser().parse_args(cmdargs)
1663
+ raise cmdutil.GetHelp
1665
+ if options.native or options.suggestions or options.external:
1666
+ native = options.native
1667
+ suggestions = options.suggestions
1668
+ external = options.external
1671
+ suggestions = False
1674
+ if len(args) == 0:
1675
+ self.list_commands(native, suggestions, external)
1677
+ elif len(args) == 1:
1678
+ command_help(args[0])
1682
+ self.get_parser().print_help()
1684
+If no command is specified, commands are listed. If a command is
1685
+specified, help for that command is listed.
1688
+ def get_parser(self):
1690
+ Returns the options parser to use for the "revision" command.
1692
+ :rtype: cmdutil.CmdOptionParser
1694
+ if self.parser is not None:
1695
+ return self.parser
1696
+ parser=cmdutil.CmdOptionParser("fai help [command]")
1697
+ parser.add_option("-n", "--native", action="store_true",
1698
+ dest="native", help="Show native commands")
1699
+ parser.add_option("-e", "--external", action="store_true",
1700
+ dest="external", help="Show external commands")
1701
+ parser.add_option("-s", "--suggest", action="store_true",
1702
+ dest="suggestions", help="Show suggestions")
1703
+ self.parser = parser
1706
+ def list_commands(self, native=True, suggest=False, external=True):
1708
+ Lists supported commands.
1710
+ :param native: list native, python-based commands
1711
+ :type native: bool
1712
+ :param external: list external aba-style commands
1713
+ :type external: bool
1716
+ print "Native Fai commands"
1717
+ keys=commands.keys()
1721
+ for i in range(28-len(k)):
1723
+ print space+k+" : "+commands[k]().description
1726
+ print "Unavailable commands and suggested alternatives"
1727
+ key_list = suggestions.keys()
1729
+ for key in key_list:
1730
+ print "%28s : %s" % (key, suggestions[key])
1733
+ fake_aba = abacmds.AbaCmds()
1734
+ if (fake_aba.abadir == ""):
1736
+ print "External commands"
1737
+ fake_aba.list_commands()
1740
+ print "Use help --suggest to list alternatives to tla and aba"\
1742
+ if options.tla_fallthrough and (native or external):
1743
+ print "Fai also supports tla commands."
1745
+def command_help(cmd):
1747
+ Prints help for a command.
1749
+ :param cmd: The name of the command to print help for
1752
+ fake_aba = abacmds.AbaCmds()
1753
+ cmdobj = find_command(cmd)
1754
+ if cmdobj != None:
1756
+ elif suggestions.has_key(cmd):
1757
+ print "Not available\n" + suggestions[cmd]
1759
+ abacmd = fake_aba.is_command(cmd)
1763
+ print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
1767
+class Changes(BaseCommand):
1769
+ the "changes" command: lists differences between trees/revisions:
1772
+ def __init__(self):
1773
+ self.description="Lists what files have changed in the project tree"
1775
+ def get_completer(self, arg, index):
1779
+ tree = arch.tree_root()
1782
+ return cmdutil.iter_revision_completions(arg, tree)
1784
+ def parse_commandline(self, cmdline):
1786
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
1788
+ :param cmdline: A list of arguments to parse
1789
+ :rtype: (options, Revision, Revision/WorkingTree)
1791
+ parser=self.get_parser()
1792
+ (options, args) = parser.parse_args(cmdline)
1794
+ raise cmdutil.GetHelp
1796
+ tree=arch.tree_root()
1797
+ if len(args) == 0:
1798
+ a_spec = ancillary.comp_revision(tree)
1800
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
1801
+ cmdutil.ensure_archive_registered(a_spec.archive)
1802
+ if len(args) == 2:
1803
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
1804
+ cmdutil.ensure_archive_registered(b_spec.archive)
1807
+ return options, a_spec, b_spec
1809
+ def do_command(self, cmdargs):
1811
+ Master function that perfoms the "changes" command.
1814
+ options, a_spec, b_spec = self.parse_commandline(cmdargs);
1815
+ except cmdutil.CantDetermineRevision, e:
1818
+ except arch.errors.TreeRootError, e:
1821
+ if options.changeset:
1822
+ changeset=options.changeset
1825
+ tmpdir=util.tmpdir()
1826
+ changeset=tmpdir+"/changeset"
1828
+ delta=arch.iter_delta(a_spec, b_spec, changeset)
1830
+ for line in delta:
1831
+ if cmdutil.chattermatch(line, "changeset:"):
1834
+ cmdutil.colorize(line, options.suppress_chatter)
1835
+ except arch.util.ExecProblem, e:
1836
+ if e.proc.error and e.proc.error.startswith(
1837
+ "missing explicit id for file"):
1838
+ raise MissingID(e)
1841
+ status=delta.status
1844
+ if (options.perform_diff):
1845
+ chan = arch_compound.ChangesetMunger(changeset)
1846
+ chan.read_indices()
1847
+ if options.diffopts is not None:
1848
+ if isinstance(b_spec, arch.Revision):
1849
+ b_dir = b_spec.library_find()
1852
+ a_dir = a_spec.library_find()
1853
+ diffopts = options.diffopts.split()
1854
+ cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
1856
+ cmdutil.show_diffs(delta.changeset)
1858
+ if tmpdir and (os.access(tmpdir, os.X_OK)):
1859
+ shutil.rmtree(tmpdir)
1861
+ def get_parser(self):
1863
+ Returns the options parser to use for the "changes" command.
1865
+ :rtype: cmdutil.CmdOptionParser
1867
+ parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
1869
+ parser.add_option("-d", "--diff", action="store_true",
1870
+ dest="perform_diff", default=False,
1871
+ help="Show diffs in summary")
1872
+ parser.add_option("-c", "--changeset", dest="changeset",
1873
+ help="Store a changeset in the given directory",
1874
+ metavar="DIRECTORY")
1875
+ parser.add_option("-s", "--silent", action="store_true",
1876
+ dest="suppress_chatter", default=False,
1877
+ help="Suppress chatter messages")
1878
+ parser.add_option("--diffopts", dest="diffopts",
1879
+ help="Use the specified diff options",
1880
+ metavar="OPTIONS")
1884
+ def help(self, parser=None):
1886
+ Prints a help message.
1888
+ :param parser: If supplied, the parser to use for generating help. If \
1889
+ not supplied, it is retrieved.
1890
+ :type parser: cmdutil.CmdOptionParser
1892
+ if parser is None:
1893
+ parser=self.get_parser()
1894
+ parser.print_help()
1896
+Performs source-tree comparisons
1898
+If no revision is specified, the current project tree is compared to the
1899
+last-committed revision. If one revision is specified, the current project
1900
+tree is compared to that revision. If two revisions are specified, they are
1901
+compared to each other.
1907
+class ApplyChanges(BaseCommand):
1909
+ Apply differences between two revisions to a tree
1912
+ def __init__(self):
1913
+ self.description="Applies changes to a project tree"
1915
+ def get_completer(self, arg, index):
1919
+ tree = arch.tree_root()
1922
+ return cmdutil.iter_revision_completions(arg, tree)
1924
+ def parse_commandline(self, cmdline, tree):
1926
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
1928
+ :param cmdline: A list of arguments to parse
1929
+ :rtype: (options, Revision, Revision/WorkingTree)
1931
+ parser=self.get_parser()
1932
+ (options, args) = parser.parse_args(cmdline)
1933
+ if len(args) != 2:
1934
+ raise cmdutil.GetHelp
1936
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
1937
+ cmdutil.ensure_archive_registered(a_spec.archive)
1938
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
1939
+ cmdutil.ensure_archive_registered(b_spec.archive)
1940
+ return options, a_spec, b_spec
1942
+ def do_command(self, cmdargs):
1944
+ Master function that performs "apply-changes".
1947
+ tree = arch.tree_root()
1948
+ options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
1949
+ except cmdutil.CantDetermineRevision, e:
1952
+ except arch.errors.TreeRootError, e:
1955
+ delta=cmdutil.apply_delta(a_spec, b_spec, tree)
1956
+ for line in cmdutil.iter_apply_delta_filter(delta):
1957
+ cmdutil.colorize(line, options.suppress_chatter)
1959
+ def get_parser(self):
1961
+ Returns the options parser to use for the "apply-changes" command.
1963
+ :rtype: cmdutil.CmdOptionParser
1965
+ parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
1967
+ parser.add_option("-d", "--diff", action="store_true",
1968
+ dest="perform_diff", default=False,
1969
+ help="Show diffs in summary")
1970
+ parser.add_option("-c", "--changeset", dest="changeset",
1971
+ help="Store a changeset in the given directory",
1972
+ metavar="DIRECTORY")
1973
+ parser.add_option("-s", "--silent", action="store_true",
1974
+ dest="suppress_chatter", default=False,
1975
+ help="Suppress chatter messages")
1978
+ def help(self, parser=None):
1980
+ Prints a help message.
1982
+ :param parser: If supplied, the parser to use for generating help. If \
1983
+ not supplied, it is retrieved.
1984
+ :type parser: cmdutil.CmdOptionParser
1986
+ if parser is None:
1987
+ parser=self.get_parser()
1988
+ parser.print_help()
1990
+Applies changes to a project tree
1992
+Compares two revisions and applies the difference between them to the current
1998
+class Update(BaseCommand):
2000
+ Updates a project tree to a given revision, preserving un-committed hanges.
2003
+ def __init__(self):
2004
+ self.description="Apply the latest changes to the current directory"
2006
+ def get_completer(self, arg, index):
2010
+ tree = arch.tree_root()
2013
+ return cmdutil.iter_revision_completions(arg, tree)
2015
+ def parse_commandline(self, cmdline, tree):
2017
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
2019
+ :param cmdline: A list of arguments to parse
2020
+ :rtype: (options, Revision, Revision/WorkingTree)
2022
+ parser=self.get_parser()
2023
+ (options, args) = parser.parse_args(cmdline)
2025
+ raise cmdutil.GetHelp
2030
+ revision=cmdutil.determine_revision_arch(tree, spec)
2031
+ cmdutil.ensure_archive_registered(revision.archive)
2033
+ mirror_source = cmdutil.get_mirror_source(revision.archive)
2034
+ if mirror_source != None:
2035
+ if cmdutil.prompt("Mirror update"):
2036
+ cmd=cmdutil.mirror_archive(mirror_source,
2037
+ revision.archive, arch.NameParser(revision).get_package_version())
2038
+ for line in arch.chatter_classifier(cmd):
2039
+ cmdutil.colorize(line, options.suppress_chatter)
2041
+ revision=cmdutil.determine_revision_arch(tree, spec)
2043
+ return options, revision
2045
+ def do_command(self, cmdargs):
2047
+ Master function that perfoms the "update" command.
2049
+ tree=arch.tree_root()
2051
+ options, to_revision = self.parse_commandline(cmdargs, tree);
2052
+ except cmdutil.CantDetermineRevision, e:
2055
+ except arch.errors.TreeRootError, e:
2058
+ from_revision = arch_compound.tree_latest(tree)
2059
+ if from_revision==to_revision:
2060
+ print "Tree is already up to date with:\n"+str(to_revision)+"."
2062
+ cmdutil.ensure_archive_registered(from_revision.archive)
2063
+ cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
2064
+ options.patch_forward)
2065
+ for line in cmdutil.iter_apply_delta_filter(cmd):
2066
+ cmdutil.colorize(line)
2067
+ if to_revision.version != tree.tree_version:
2068
+ if cmdutil.prompt("Update version"):
2069
+ tree.tree_version = to_revision.version
2071
+ def get_parser(self):
2073
+ Returns the options parser to use for the "update" command.
2075
+ :rtype: cmdutil.CmdOptionParser
2077
+ parser=cmdutil.CmdOptionParser("fai update [options]"
2078
+ " [revision/version]")
2079
+ parser.add_option("-f", "--forward", action="store_true",
2080
+ dest="patch_forward", default=False,
2081
+ help="pass the --forward option to 'patch'")
2082
+ parser.add_option("-s", "--silent", action="store_true",
2083
+ dest="suppress_chatter", default=False,
2084
+ help="Suppress chatter messages")
2087
+ def help(self, parser=None):
2089
+ Prints a help message.
2091
+ :param parser: If supplied, the parser to use for generating help. If \
2092
+ not supplied, it is retrieved.
2093
+ :type parser: cmdutil.CmdOptionParser
2095
+ if parser is None:
2096
+ parser=self.get_parser()
2097
+ parser.print_help()
2099
+Updates a working tree to the current archive revision
2101
+If a revision or version is specified, that is used instead
2107
+class Commit(BaseCommand):
2109
+ Create a revision based on the changes in the current tree.
2112
+ def __init__(self):
2113
+ self.description="Write local changes to the archive"
2115
+ def get_completer(self, arg, index):
2118
+ return iter_modified_file_completions(arch.tree_root(), arg)
2119
+# return iter_source_file_completions(arch.tree_root(), arg)
2121
+ def parse_commandline(self, cmdline, tree):
2123
+ Parse commandline arguments. Raise cmtutil.GetHelp if help is needed.
2125
+ :param cmdline: A list of arguments to parse
2126
+ :rtype: (options, Revision, Revision/WorkingTree)
2128
+ parser=self.get_parser()
2129
+ (options, args) = parser.parse_args(cmdline)
2131
+ if len(args) == 0:
2133
+ if options.version is None:
2134
+ return options, tree.tree_version, args
2136
+ revision=cmdutil.determine_revision_arch(tree, options.version)
2137
+ return options, revision.get_version(), args
2139
+ def do_command(self, cmdargs):
2141
+ Master function that perfoms the "commit" command.
2143
+ tree=arch.tree_root()
2144
+ options, version, files = self.parse_commandline(cmdargs, tree)
2146
+ if options.__dict__.has_key("base") and options.base:
2147
+ base = cmdutil.determine_revision_tree(tree, options.base)
2150
+ base = ancillary.submit_revision(tree)
2152
+ if ancestor is None:
2153
+ ancestor = arch_compound.tree_latest(tree, version)
2155
+ writeversion=version
2156
+ archive=version.archive
2157
+ source=cmdutil.get_mirror_source(archive)
2159
+ writethrough="implicit"
2162
+ if writethrough=="explicit" and \
2163
+ cmdutil.prompt("Writethrough"):
2164
+ writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
2165
+ elif writethrough=="none":
2166
+ raise CommitToMirror(archive)
2168
+ elif archive.is_mirror:
2169
+ raise CommitToMirror(archive)
2172
+ last_revision=tree.iter_logs(version, True).next().revision
2173
+ except StopIteration, e:
2174
+ last_revision = None
2175
+ if ancestor is None:
2176
+ if cmdutil.prompt("Import from commit"):
2177
+ return do_import(version)
2179
+ raise NoVersionLogs(version)
2181
+ arch_last_revision = version.iter_revisions(True).next()
2182
+ except StopIteration, e:
2183
+ arch_last_revision = None
2185
+ if last_revision != arch_last_revision:
2186
+ print "Tree is not up to date with %s" % str(version)
2187
+ if not cmdutil.prompt("Out of date"):
2193
+ if not cmdutil.has_changed(ancestor):
2194
+ if not cmdutil.prompt("Empty commit"):
2196
+ except arch.util.ExecProblem, e:
2197
+ if e.proc.error and e.proc.error.startswith(
2198
+ "missing explicit id for file"):
2199
+ raise MissingID(e)
2202
+ log = tree.log_message(create=False, version=version)
2205
+ if cmdutil.prompt("Create log"):
2206
+ edit_log(tree, version)
2208
+ except cmdutil.NoEditorSpecified, e:
2209
+ raise CommandFailed(e)
2210
+ log = tree.log_message(create=False, version=version)
2212
+ raise NoLogMessage
2213
+ if log["Summary"] is None or len(log["Summary"].strip()) == 0:
2214
+ if not cmdutil.prompt("Omit log summary"):
2215
+ raise errors.NoLogSummary
2217
+ for line in tree.iter_commit(version, seal=options.seal_version,
2218
+ base=base, out_of_date_ok=allow_old, file_list=files):
2219
+ cmdutil.colorize(line, options.suppress_chatter)
2221
+ except arch.util.ExecProblem, e:
2222
+ if e.proc.error and e.proc.error.startswith(
2223
+ "These files violate naming conventions:"):
2224
+ raise LintFailure(e.proc.error)
2228
+ def get_parser(self):
2230
+ Returns the options parser to use for the "commit" command.
2232
+ :rtype: cmdutil.CmdOptionParser
2235
+ parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
2237
+ parser.add_option("--seal", action="store_true",
2238
+ dest="seal_version", default=False,
2239
+ help="seal this version")
2240
+ parser.add_option("-v", "--version", dest="version",
2241
+ help="Use the specified version",
2242
+ metavar="VERSION")
2243
+ parser.add_option("-s", "--silent", action="store_true",
2244
+ dest="suppress_chatter", default=False,
2245
+ help="Suppress chatter messages")
2246
+ if cmdutil.supports_switch("commit", "--base"):
2247
+ parser.add_option("--base", dest="base", help="",
2248
+ metavar="REVISION")
2251
+ def help(self, parser=None):
2253
+ Prints a help message.
2255
+ :param parser: If supplied, the parser to use for generating help. If \
2256
+ not supplied, it is retrieved.
2257
+ :type parser: cmdutil.CmdOptionParser
2259
+ if parser is None:
2260
+ parser=self.get_parser()
2261
+ parser.print_help()
2263
+Updates a working tree to the current archive revision
2265
+If a version is specified, that is used instead
2272
+class CatLog(BaseCommand):
2274
+ Print the log of a given file (from current tree)
2276
+ def __init__(self):
2277
+ self.description="Prints the patch log for a revision"
2279
+ def get_completer(self, arg, index):
2283
+ tree = arch.tree_root()
2286
+ return cmdutil.iter_revision_completions(arg, tree)
2288
+ def do_command(self, cmdargs):
2290
+ Master function that perfoms the "cat-log" command.
2292
+ parser=self.get_parser()
2293
+ (options, args) = parser.parse_args(cmdargs)
2295
+ tree = arch.tree_root()
2296
+ except arch.errors.TreeRootError, e:
2302
+ raise cmdutil.GetHelp()
2305
+ revision = cmdutil.determine_revision_tree(tree, spec)
2307
+ revision = cmdutil.determine_revision_arch(tree, spec)
2308
+ except cmdutil.CantDetermineRevision, e:
2309
+ raise CommandFailedWrapper(e)
2312
+ use_tree = (options.source == "tree" or \
2313
+ (options.source == "any" and tree))
2314
+ use_arch = (options.source == "archive" or options.source == "any")
2318
+ for log in tree.iter_logs(revision.get_version()):
2319
+ if log.revision == revision:
2323
+ if log is None and use_arch:
2324
+ cmdutil.ensure_revision_exists(revision)
2325
+ log = arch.Patchlog(revision)
2326
+ if log is not None:
2327
+ for item in log.items():
2328
+ print "%s: %s" % item
2329
+ print log.description
2331
+ def get_parser(self):
2333
+ Returns the options parser to use for the "cat-log" command.
2335
+ :rtype: cmdutil.CmdOptionParser
2337
+ parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
2338
+ parser.add_option("--archive", action="store_const", dest="source",
2339
+ const="archive", default="any",
2340
+ help="Always get the log from the archive")
2341
+ parser.add_option("--tree", action="store_const", dest="source",
2342
+ const="tree", help="Always get the log from the tree")
2345
+ def help(self, parser=None):
2347
+ Prints a help message.
2349
+ :param parser: If supplied, the parser to use for generating help. If \
2350
+ not supplied, it is retrieved.
2351
+ :type parser: cmdutil.CmdOptionParser
2354
+ parser=self.get_parser()
2355
+ parser.print_help()
2357
+Prints the log for the specified revision
2362
+class Revert(BaseCommand):
2363
+ """ Reverts a tree (or aspects of it) to a revision
2365
+ def __init__(self):
2366
+ self.description="Reverts a tree (or aspects of it) to a revision "
2368
+ def get_completer(self, arg, index):
2372
+ tree = arch.tree_root()
2375
+ return iter_modified_file_completions(tree, arg)
2377
+ def do_command(self, cmdargs):
2379
+ Master function that perfoms the "revert" command.
2381
+ parser=self.get_parser()
2382
+ (options, args) = parser.parse_args(cmdargs)
2384
+ tree = arch.tree_root()
2385
+ except arch.errors.TreeRootError, e:
2386
+ raise CommandFailed(e)
2388
+ if options.revision is not None:
2389
+ spec=options.revision
2391
+ if spec is not None:
2392
+ revision = cmdutil.determine_revision_tree(tree, spec)
2394
+ revision = ancillary.comp_revision(tree)
2395
+ except cmdutil.CantDetermineRevision, e:
2396
+ raise CommandFailedWrapper(e)
2399
+ if options.file_contents or options.file_perms or options.deletions\
2400
+ or options.additions or options.renames or options.hunk_prompt:
2401
+ munger = arch_compound.MungeOpts()
2402
+ munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
2403
+ options.hunk_prompt)
2405
+ if len(args) > 0 or options.logs or options.pattern_files or \
2407
+ if munger is None:
2408
+ munger = cmdutil.arch_compound.MungeOpts(True)
2409
+ munger.all_types(True)
2411
+ t_cwd = arch_compound.tree_cwd(tree)
2413
+ if len(t_cwd) > 0:
2415
+ name = "./" + t_cwd + name
2416
+ munger.add_keep_file(name);
2418
+ if options.file_perms:
2419
+ munger.file_perms = True
2420
+ if options.file_contents:
2421
+ munger.file_contents = True
2422
+ if options.deletions:
2423
+ munger.deletions = True
2424
+ if options.additions:
2425
+ munger.additions = True
2426
+ if options.renames:
2427
+ munger.renames = True
2429
+ munger.add_keep_pattern('^\./\{arch\}/[^=].*')
2430
+ if options.control:
2431
+ munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
2432
+ "/\.arch-inventory$")
2433
+ if options.pattern_files:
2434
+ munger.add_keep_pattern(options.pattern_files)
2436
+ for line in arch_compound.revert(tree, revision, munger,
2437
+ not options.no_output):
2438
+ cmdutil.colorize(line)
2441
+ def get_parser(self):
2443
+ Returns the options parser to use for the "cat-log" command.
2445
+ :rtype: cmdutil.CmdOptionParser
2447
+ parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
2448
+ parser.add_option("", "--contents", action="store_true",
2449
+ dest="file_contents",
2450
+ help="Revert file content changes")
2451
+ parser.add_option("", "--permissions", action="store_true",
2452
+ dest="file_perms",
2453
+ help="Revert file permissions changes")
2454
+ parser.add_option("", "--deletions", action="store_true",
2456
+ help="Restore deleted files")
2457
+ parser.add_option("", "--additions", action="store_true",
2459
+ help="Remove added files")
2460
+ parser.add_option("", "--renames", action="store_true",
2462
+ help="Revert file names")
2463
+ parser.add_option("--hunks", action="store_true",
2464
+ dest="hunk_prompt", default=False,
2465
+ help="Prompt which hunks to revert")
2466
+ parser.add_option("--pattern-files", dest="pattern_files",
2467
+ help="Revert files that match this pattern",
2469
+ parser.add_option("--logs", action="store_true",
2470
+ dest="logs", default=False,
2471
+ help="Revert only logs")
2472
+ parser.add_option("--control-files", action="store_true",
2473
+ dest="control", default=False,
2474
+ help="Revert logs and other control files")
2475
+ parser.add_option("-n", "--no-output", action="store_true",
2477
+ help="Don't keep an undo changeset")
2478
+ parser.add_option("--revision", dest="revision",
2479
+ help="Revert to the specified revision",
2480
+ metavar="REVISION")
2483
+ def help(self, parser=None):
2485
+ Prints a help message.
2487
+ :param parser: If supplied, the parser to use for generating help. If \
2488
+ not supplied, it is retrieved.
2489
+ :type parser: cmdutil.CmdOptionParser
2492
+ parser=self.get_parser()
2493
+ parser.print_help()
2495
+Reverts changes in the current working tree. If no flags are specified, all
2496
+types of changes are reverted. Otherwise, only selected types of changes are
2499
+If a revision is specified on the commandline, differences between the current
2500
+tree and that revision are reverted. If a version is specified, the current
2501
+tree is used to determine the revision.
2503
+If files are specified, only those files listed will have any changes applied.
2504
+To specify a renamed file, you can use either the old or new name. (or both!)
2506
+Unless "-n" is specified, reversions can be undone with "redo".
2510
+class Revision(BaseCommand):
2512
+ Print a revision name based on a revision specifier
2514
+ def __init__(self):
2515
+ self.description="Prints the name of a revision"
2517
+ def get_completer(self, arg, index):
2521
+ tree = arch.tree_root()
2524
+ return cmdutil.iter_revision_completions(arg, tree)
2526
+ def do_command(self, cmdargs):
2528
+ Master function that perfoms the "revision" command.
2530
+ parser=self.get_parser()
2531
+ (options, args) = parser.parse_args(cmdargs)
2534
+ tree = arch.tree_root()
2535
+ except arch.errors.TreeRootError:
2542
+ raise cmdutil.GetHelp
2545
+ revision = cmdutil.determine_revision_tree(tree, spec)
2547
+ revision = cmdutil.determine_revision_arch(tree, spec)
2548
+ except cmdutil.CantDetermineRevision, e:
2551
+ print options.display(revision)
2553
+ def get_parser(self):
2555
+ Returns the options parser to use for the "revision" command.
2557
+ :rtype: cmdutil.CmdOptionParser
2559
+ parser=cmdutil.CmdOptionParser("fai revision [revision]")
2560
+ parser.add_option("", "--location", action="store_const",
2561
+ const=paths.determine_path, dest="display",
2562
+ help="Show location instead of name", default=str)
2563
+ parser.add_option("--import", action="store_const",
2564
+ const=paths.determine_import_path, dest="display",
2565
+ help="Show location of import file")
2566
+ parser.add_option("--log", action="store_const",
2567
+ const=paths.determine_log_path, dest="display",
2568
+ help="Show location of log file")
2569
+ parser.add_option("--patch", action="store_const",
2570
+ dest="display", const=paths.determine_patch_path,
2571
+ help="Show location of patchfile")
2572
+ parser.add_option("--continuation", action="store_const",
2573
+ const=paths.determine_continuation_path,
2575
+ help="Show location of continuation file")
2576
+ parser.add_option("--cacherev", action="store_const",
2577
+ const=paths.determine_cacherev_path, dest="display",
2578
+ help="Show location of cacherev file")
2581
+ def help(self, parser=None):
2583
+ Prints a help message.
2585
+ :param parser: If supplied, the parser to use for generating help. If \
2586
+ not supplied, it is retrieved.
2587
+ :type parser: cmdutil.CmdOptionParser
2590
+ parser=self.get_parser()
2591
+ parser.print_help()
2593
+Expands aliases and prints the name of the specified revision. Instead of
2594
+the name, several options can be used to print locations. If more than one is
2595
+specified, the last one is used.
2600
+class Revisions(BaseCommand):
2602
+ Print a revision name based on a revision specifier
2604
+ def __init__(self):
2605
+ self.description="Lists revisions"
2606
+ self.cl_revisions = []
2608
+ def do_command(self, cmdargs):
2610
+ Master function that perfoms the "revision" command.
2612
+ (options, args) = self.get_parser().parse_args(cmdargs)
2614
+ raise cmdutil.GetHelp
2616
+ self.tree = arch.tree_root()
2617
+ except arch.errors.TreeRootError:
2619
+ if options.type == "default":
2620
+ options.type = "archive"
2622
+ iter = cmdutil.revision_iterator(self.tree, options.type, args,
2623
+ options.reverse, options.modified,
2625
+ except cmdutil.CantDetermineRevision, e:
2626
+ raise CommandFailedWrapper(e)
2627
+ except cmdutil.CantDetermineVersion, e:
2628
+ raise CommandFailedWrapper(e)
2629
+ if options.skip is not None:
2630
+ iter = cmdutil.iter_skip(iter, int(options.skip))
2633
+ for revision in iter:
2635
+ if isinstance(revision, arch.Patchlog):
2637
+ revision=revision.revision
2638
+ out = options.display(revision)
2639
+ if out is not None:
2641
+ if log is None and (options.summary or options.creator or
2642
+ options.date or options.merges):
2643
+ log = revision.patchlog
2644
+ if options.creator:
2645
+ print " %s" % log.creator
2647
+ print " %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
2648
+ if options.summary:
2649
+ print " %s" % log.summary
2650
+ if options.merges:
2651
+ showed_title = False
2652
+ for revision in log.merged_patches:
2653
+ if not showed_title:
2655
+ showed_title = True
2656
+ print " %s" % revision
2657
+ if len(self.cl_revisions) > 0:
2658
+ print pylon.changelog_for_merge(self.cl_revisions)
2659
+ except pylon.errors.TreeRootNone:
2660
+ raise CommandFailedWrapper(
2661
+ Exception("This option can only be used in a project tree."))
2663
+ def changelog_append(self, revision):
2664
+ if isinstance(revision, arch.Revision):
2665
+ revision=arch.Patchlog(revision)
2666
+ self.cl_revisions.append(revision)
2668
+ def get_parser(self):
2670
+ Returns the options parser to use for the "revision" command.
2672
+ :rtype: cmdutil.CmdOptionParser
2674
+ parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
2675
+ select = cmdutil.OptionGroup(parser, "Selection options",
2676
+ "Control which revisions are listed. These options"
2677
+ " are mutually exclusive. If more than one is"
2678
+ " specified, the last is used.")
2680
+ cmdutil.add_revision_iter_options(select)
2681
+ parser.add_option("", "--skip", dest="skip",
2682
+ help="Skip revisions. Positive numbers skip from "
2683
+ "beginning, negative skip from end.",
2686
+ parser.add_option_group(select)
2688
+ format = cmdutil.OptionGroup(parser, "Revision format options",
2689
+ "These control the appearance of listed revisions")
2690
+ format.add_option("", "--location", action="store_const",
2691
+ const=paths.determine_path, dest="display",
2692
+ help="Show location instead of name", default=str)
2693
+ format.add_option("--import", action="store_const",
2694
+ const=paths.determine_import_path, dest="display",
2695
+ help="Show location of import file")
2696
+ format.add_option("--log", action="store_const",
2697
+ const=paths.determine_log_path, dest="display",
2698
+ help="Show location of log file")
2699
+ format.add_option("--patch", action="store_const",
2700
+ dest="display", const=paths.determine_patch_path,
2701
+ help="Show location of patchfile")
2702
+ format.add_option("--continuation", action="store_const",
2703
+ const=paths.determine_continuation_path,
2705
+ help="Show location of continuation file")
2706
+ format.add_option("--cacherev", action="store_const",
2707
+ const=paths.determine_cacherev_path, dest="display",
2708
+ help="Show location of cacherev file")
2709
+ format.add_option("--changelog", action="store_const",
2710
+ const=self.changelog_append, dest="display",
2711
+ help="Show location of cacherev file")
2712
+ parser.add_option_group(format)
2713
+ display = cmdutil.OptionGroup(parser, "Display format options",
2714
+ "These control the display of data")
2715
+ display.add_option("-r", "--reverse", action="store_true",
2716
+ dest="reverse", help="Sort from newest to oldest")
2717
+ display.add_option("-s", "--summary", action="store_true",
2718
+ dest="summary", help="Show patchlog summary")
2719
+ display.add_option("-D", "--date", action="store_true",
2720
+ dest="date", help="Show patchlog date")
2721
+ display.add_option("-c", "--creator", action="store_true",
2722
+ dest="creator", help="Show the id that committed the"
2724
+ display.add_option("-m", "--merges", action="store_true",
2725
+ dest="merges", help="Show the revisions that were"
2727
+ parser.add_option_group(display)
2729
+ def help(self, parser=None):
2730
+ """Attempt to explain the revisions command
2732
+ :param parser: If supplied, used to determine options
2735
+ parser=self.get_parser()
2736
+ parser.print_help()
2737
+ print """List revisions.
2742
+class Get(BaseCommand):
2744
+ Retrieve a revision from the archive
2746
+ def __init__(self):
2747
+ self.description="Retrieve a revision from the archive"
2748
+ self.parser=self.get_parser()
2751
+ def get_completer(self, arg, index):
2755
+ tree = arch.tree_root()
2758
+ return cmdutil.iter_revision_completions(arg, tree)
2761
+ def do_command(self, cmdargs):
2763
+ Master function that perfoms the "get" command.
2765
+ (options, args) = self.parser.parse_args(cmdargs)
2767
+ return self.help()
2769
+ tree = arch.tree_root()
2770
+ except arch.errors.TreeRootError:
2775
+ revision, arch_loc = paths.full_path_decode(args[0])
2776
+ except Exception, e:
2777
+ revision = cmdutil.determine_revision_arch(tree, args[0],
2778
+ check_existence=False, allow_package=True)
2780
+ directory = args[1]
2782
+ directory = str(revision.nonarch)
2783
+ if os.path.exists(directory):
2784
+ raise DirectoryExists(directory)
2785
+ cmdutil.ensure_archive_registered(revision.archive, arch_loc)
2787
+ cmdutil.ensure_revision_exists(revision)
2788
+ except cmdutil.NoSuchRevision, e:
2789
+ raise CommandFailedWrapper(e)
2791
+ link = cmdutil.prompt ("get link")
2792
+ for line in cmdutil.iter_get(revision, directory, link,
2793
+ options.no_pristine,
2794
+ options.no_greedy_add):
2795
+ cmdutil.colorize(line)
2797
+ def get_parser(self):
2799
+ Returns the options parser to use for the "get" command.
2801
+ :rtype: cmdutil.CmdOptionParser
2803
+ parser=cmdutil.CmdOptionParser("fai get revision [dir]")
2804
+ parser.add_option("--no-pristine", action="store_true",
2805
+ dest="no_pristine",
2806
+ help="Do not make pristine copy for reference")
2807
+ parser.add_option("--no-greedy-add", action="store_true",
2808
+ dest="no_greedy_add",
2809
+ help="Never add to greedy libraries")
2813
+ def help(self, parser=None):
2815
+ Prints a help message.
2817
+ :param parser: If supplied, the parser to use for generating help. If \
2818
+ not supplied, it is retrieved.
2819
+ :type parser: cmdutil.CmdOptionParser
2822
+ parser=self.get_parser()
2823
+ parser.print_help()
2825
+Expands aliases and constructs a project tree for a revision. If the optional
2826
+"dir" argument is provided, the project tree will be stored in this directory.
2831
+class PromptCmd(cmd.Cmd):
2832
+ def __init__(self):
2833
+ cmd.Cmd.__init__(self)
2834
+ self.prompt = "Fai> "
2836
+ self.tree = arch.tree_root()
2841
+ self.fake_aba = abacmds.AbaCmds()
2842
+ self.identchars += '-'
2843
+ self.history_file = os.path.expanduser("~/.fai-history")
2844
+ readline.set_completer_delims(string.whitespace)
2845
+ if os.access(self.history_file, os.R_OK) and \
2846
+ os.path.isfile(self.history_file):
2847
+ readline.read_history_file(self.history_file)
2848
+ self.cwd = os.getcwd()
2850
+ def write_history(self):
2851
+ readline.write_history_file(self.history_file)
2853
+ def do_quit(self, args):
2854
+ self.write_history()
2857
+ def do_exit(self, args):
2858
+ self.do_quit(args)
2860
+ def do_EOF(self, args):
2862
+ self.do_quit(args)
2864
+ def postcmd(self, line, bar):
2868
+ def set_prompt(self):
2869
+ if self.tree is not None:
2871
+ prompt = pylon.alias_or_version(self.tree.tree_version,
2874
+ if prompt is not None:
2875
+ prompt = " " + prompt
2880
+ self.prompt = "Fai%s> " % prompt
2882
+ def set_title(self, command=None):
2884
+ version = pylon.alias_or_version(self.tree.tree_version, self.tree,
2887
+ version = "[no version]"
2888
+ if command is None:
2890
+ sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
2892
+ def do_cd(self, line):
2895
+ line = os.path.expanduser(line)
2896
+ if os.path.isabs(line):
2899
+ newcwd = self.cwd+'/'+line
2900
+ newcwd = os.path.normpath(newcwd)
2904
+ except Exception, e:
2907
+ self.tree = arch.tree_root()
2911
+ def do_help(self, line):
2914
+ def default(self, line):
2915
+ args = line.split()
2916
+ if find_command(args[0]):
2918
+ find_command(args[0]).do_command(args[1:])
2919
+ except cmdutil.BadCommandOption, e:
2921
+ except cmdutil.GetHelp, e:
2922
+ find_command(args[0]).help()
2923
+ except CommandFailed, e:
2925
+ except arch.errors.ArchiveNotRegistered, e:
2927
+ except KeyboardInterrupt, e:
2928
+ print "Interrupted"
2929
+ except arch.util.ExecProblem, e:
2930
+ print e.proc.error.rstrip('\n')
2931
+ except cmdutil.CantDetermineVersion, e:
2933
+ except cmdutil.CantDetermineRevision, e:
2935
+ except Exception, e:
2936
+ print "Unhandled error:\n%s" % errors.exception_str(e)
2938
+ elif suggestions.has_key(args[0]):
2939
+ print suggestions[args[0]]
2941
+ elif self.fake_aba.is_command(args[0]):
2944
+ tree = arch.tree_root()
2945
+ except arch.errors.TreeRootError:
2947
+ cmd = self.fake_aba.is_command(args[0])
2949
+ cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
2950
+ except KeyboardInterrupt, e:
2951
+ print "Interrupted"
2953
+ elif options.tla_fallthrough and args[0] != "rm" and \
2954
+ cmdutil.is_tla_command(args[0]):
2958
+ tree = arch.tree_root()
2959
+ except arch.errors.TreeRootError:
2961
+ args = cmdutil.expand_prefix_alias(args, tree)
2962
+ arch.util.exec_safe('tla', args, stderr=sys.stderr,
2964
+ except arch.util.ExecProblem, e:
2966
+ except KeyboardInterrupt, e:
2967
+ print "Interrupted"
2971
+ tree = arch.tree_root()
2972
+ except arch.errors.TreeRootError:
2975
+ os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
2976
+ except KeyboardInterrupt, e:
2977
+ print "Interrupted"
2979
+ def completenames(self, text, line, begidx, endidx):
2981
+ iter = iter_command_names(self.fake_aba)
2984
+ arg = line.split()[-1]
2987
+ iter = cmdutil.iter_munged_completions(iter, arg, text)
2988
+ except Exception, e:
2992
+ def completedefault(self, text, line, begidx, endidx):
2993
+ """Perform completion for native commands.
2995
+ :param text: The text to complete
2997
+ :param line: The entire line to complete
2999
+ :param begidx: The start of the text in the line
3001
+ :param endidx: The end of the text in the line
3005
+ (cmd, args, foo) = self.parseline(line)
3006
+ command_obj=find_command(cmd)
3007
+ if command_obj is not None:
3008
+ return command_obj.complete(args.split(), text)
3009
+ elif not self.fake_aba.is_command(cmd) and \
3010
+ cmdutil.is_tla_command(cmd):
3011
+ iter = cmdutil.iter_supported_switches(cmd)
3013
+ arg = args.split()[-1]
3016
+ if arg.startswith("-"):
3017
+ return list(cmdutil.iter_munged_completions(iter, arg,
3020
+ return list(cmdutil.iter_munged_completions(
3021
+ cmdutil.iter_file_completions(arg), arg, text))
3026
+ arg = args.split()[-1]
3029
+ iter = cmdutil.iter_dir_completions(arg)
3030
+ iter = cmdutil.iter_munged_completions(iter, arg, text)
3033
+ arg = args.split()[-1]
3034
+ iter = cmdutil.iter_file_completions(arg)
3035
+ return list(cmdutil.iter_munged_completions(iter, arg, text))
3037
+ return self.completenames(text, line, begidx, endidx)
3038
+ except Exception, e:
3042
+def iter_command_names(fake_aba):
3043
+ for entry in cmdutil.iter_combine([commands.iterkeys(),
3044
+ fake_aba.get_commands(),
3045
+ cmdutil.iter_tla_commands(False)]):
3046
+ if not suggestions.has_key(str(entry)):
3050
+def iter_source_file_completions(tree, arg):
3051
+ treepath = arch_compound.tree_cwd(tree)
3052
+ if len(treepath) > 0:
3056
+ for file in tree.iter_inventory(dirs, source=True, both=True):
3057
+ file = file_completion_match(file, treepath, arg)
3058
+ if file is not None:
3062
+def iter_untagged(tree, dirs):
3063
+ for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False,
3064
+ categories=arch_core.non_root,
3065
+ control_files=True):
3069
+def iter_untagged_completions(tree, arg):
3070
+ """Generate an iterator for all visible untagged files that match arg.
3072
+ :param tree: The tree to look for untagged files in
3073
+ :type tree: `arch.WorkingTree`
3074
+ :param arg: The argument to match
3076
+ :return: An iterator of all matching untagged files
3077
+ :rtype: iterator of str
3079
+ treepath = arch_compound.tree_cwd(tree)
3080
+ if len(treepath) > 0:
3085
+ for file in iter_untagged(tree, dirs):
3086
+ file = file_completion_match(file, treepath, arg)
3087
+ if file is not None:
3091
+def file_completion_match(file, treepath, arg):
3092
+ """Determines whether a file within an arch tree matches the argument.
3094
+ :param file: The rooted filename
3096
+ :param treepath: The path to the cwd within the tree
3097
+ :type treepath: str
3098
+ :param arg: The prefix to match
3099
+ :return: The completion name, or None if not a match
3102
+ if not file.startswith(treepath):
3104
+ if treepath != "":
3105
+ file = file[len(treepath)+1:]
3107
+ if not file.startswith(arg):
3109
+ if os.path.isdir(file):
3113
+def iter_modified_file_completions(tree, arg):
3114
+ """Returns a list of modified files that match the specified prefix.
3116
+ :param tree: The current tree
3117
+ :type tree: `arch.WorkingTree`
3118
+ :param arg: The prefix to match
3121
+ treepath = arch_compound.tree_cwd(tree)
3122
+ tmpdir = util.tmpdir()
3123
+ changeset = tmpdir+"/changeset"
3125
+ revision = cmdutil.determine_revision_tree(tree)
3126
+ for line in arch.iter_delta(revision, tree, changeset):
3127
+ if isinstance(line, arch.FileModification):
3128
+ file = file_completion_match(line.name[1:], treepath, arg)
3129
+ if file is not None:
3130
+ completions.append(file)
3131
+ shutil.rmtree(tmpdir)
3132
+ return completions
3134
+class Shell(BaseCommand):
3135
+ def __init__(self):
3136
+ self.description = "Runs Fai as a shell"
3138
+ def do_command(self, cmdargs):
3139
+ if len(cmdargs)!=0:
3140
+ raise cmdutil.GetHelp
3141
+ prompt = PromptCmd()
3145
+ prompt.write_history()
3147
+class AddID(BaseCommand):
3149
+ Adds an inventory id for the given file
3151
+ def __init__(self):
3152
+ self.description="Add an inventory id for a given file"
3154
+ def get_completer(self, arg, index):
3155
+ tree = arch.tree_root()
3156
+ return iter_untagged_completions(tree, arg)
3158
+ def do_command(self, cmdargs):
3160
+ Master function that perfoms the "revision" command.
3162
+ parser=self.get_parser()
3163
+ (options, args) = parser.parse_args(cmdargs)
3166
+ tree = arch.tree_root()
3167
+ except arch.errors.TreeRootError, e:
3168
+ raise pylon.errors.CommandFailedWrapper(e)
3171
+ if (len(args) == 0) == (options.untagged == False):
3172
+ raise cmdutil.GetHelp
3174
+ #if options.id and len(args) != 1:
3175
+ # print "If --id is specified, only one file can be named."
3178
+ method = tree.tagging_method
3180
+ if options.id_type == "tagline":
3181
+ if method != "tagline":
3182
+ if not cmdutil.prompt("Tagline in other tree"):
3183
+ if method == "explicit" or method == "implicit":
3184
+ options.id_type == method
3186
+ print "add-id not supported for \"%s\" tagging method"\
3190
+ elif options.id_type == "implicit":
3191
+ if method != "implicit":
3192
+ if not cmdutil.prompt("Implicit in other tree"):
3193
+ if method == "explicit" or method == "tagline":
3194
+ options.id_type == method
3196
+ print "add-id not supported for \"%s\" tagging method"\
3199
+ elif options.id_type == "explicit":
3200
+ if method != "tagline" and method != explicit:
3201
+ if not prompt("Explicit in other tree"):
3202
+ print "add-id not supported for \"%s\" tagging method" % \
3206
+ if options.id_type == "auto":
3207
+ if method != "tagline" and method != "explicit" \
3208
+ and method !="implicit":
3209
+ print "add-id not supported for \"%s\" tagging method" % method
3212
+ options.id_type = method
3213
+ if options.untagged:
3215
+ self.add_ids(tree, options.id_type, args)
3217
+ def add_ids(self, tree, id_type, files=()):
3218
+ """Add inventory ids to files.
3220
+ :param tree: the tree the files are in
3221
+ :type tree: `arch.WorkingTree`
3222
+ :param id_type: the type of id to add: "explicit" or "tagline"
3223
+ :type id_type: str
3224
+ :param files: The list of files to add. If None do all untagged.
3225
+ :type files: tuple of str
3228
+ untagged = (files is None)
3230
+ files = list(iter_untagged(tree, None))
3231
+ previous_files = []
3232
+ while len(files) > 0:
3233
+ previous_files.extend(files)
3234
+ if id_type == "explicit":
3235
+ cmdutil.add_id(files)
3236
+ elif id_type == "tagline" or id_type == "implicit":
3237
+ for file in files:
3239
+ implicit = (id_type == "implicit")
3240
+ cmdutil.add_tagline_or_explicit_id(file, False,
3242
+ except cmdutil.AlreadyTagged:
3243
+ print "\"%s\" already has a tagline." % file
3244
+ except cmdutil.NoCommentSyntax:
3246
+ #do inventory after tagging until no untagged files are encountered
3249
+ for file in iter_untagged(tree, None):
3250
+ if not file in previous_files:
3251
+ files.append(file)
3256
+ def get_parser(self):
3258
+ Returns the options parser to use for the "revision" command.
3260
+ :rtype: cmdutil.CmdOptionParser
3262
+ parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
3263
+# ddaa suggests removing this to promote GUIDs. Let's see who squalks.
3264
+# parser.add_option("-i", "--id", dest="id",
3265
+# help="Specify id for a single file", default=None)
3266
+ parser.add_option("--tltl", action="store_true",
3267
+ dest="lord_style", help="Use Tom Lord's style of id.")
3268
+ parser.add_option("--explicit", action="store_const",
3269
+ const="explicit", dest="id_type",
3270
+ help="Use an explicit id", default="auto")
3271
+ parser.add_option("--tagline", action="store_const",
3272
+ const="tagline", dest="id_type",
3273
+ help="Use a tagline id")
3274
+ parser.add_option("--implicit", action="store_const",
3275
+ const="implicit", dest="id_type",
3276
+ help="Use an implicit id (deprecated)")
3277
+ parser.add_option("--untagged", action="store_true",
3278
+ dest="untagged", default=False,
3279
+ help="tag all untagged files")
3282
+ def help(self, parser=None):
3284
+ Prints a help message.
3286
+ :param parser: If supplied, the parser to use for generating help. If \
3287
+ not supplied, it is retrieved.
3288
+ :type parser: cmdutil.CmdOptionParser
3291
+ parser=self.get_parser()
3292
+ parser.print_help()
3294
+Adds an inventory to the specified file(s) and directories. If --untagged is
3295
+specified, adds inventory to all untagged files and directories.
3300
+class Merge(BaseCommand):
3302
+ Merges changes from other versions into the current tree
3304
+ def __init__(self):
3305
+ self.description="Merges changes from other versions"
3307
+ self.tree = arch.tree_root()
3312
+ def get_completer(self, arg, index):
3313
+ if self.tree is None:
3314
+ raise arch.errors.TreeRootError
3315
+ return cmdutil.merge_completions(self.tree, arg, index)
3317
+ def do_command(self, cmdargs):
3319
+ Master function that perfoms the "merge" command.
3321
+ parser=self.get_parser()
3322
+ (options, args) = parser.parse_args(cmdargs)
3324
+ action="star-merge"
3326
+ action = options.action
3328
+ if self.tree is None:
3329
+ raise arch.errors.TreeRootError(os.getcwd())
3330
+ if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
3331
+ raise UncommittedChanges(self.tree)
3336
+ revisions.append(cmdutil.determine_revision_arch(self.tree,
3338
+ source = "from commandline"
3340
+ revisions = ancillary.iter_partner_revisions(self.tree,
3341
+ self.tree.tree_version)
3342
+ source = "from partner version"
3343
+ revisions = misc.rewind_iterator(revisions)
3346
+ revisions.rewind()
3347
+ except StopIteration, e:
3348
+ revision = cmdutil.tag_cur(self.tree)
3349
+ if revision is None:
3350
+ raise CantDetermineRevision("", "No version specified, no "
3351
+ "partner-versions, and no tag"
3353
+ revisions = [revision]
3354
+ source = "from tag source"
3355
+ for revision in revisions:
3356
+ cmdutil.ensure_archive_registered(revision.archive)
3357
+ cmdutil.colorize(arch.Chatter("* Merging %s [%s]" %
3358
+ (revision, source)))
3359
+ if action=="native-merge" or action=="update":
3360
+ if self.native_merge(revision, action) == 0:
3362
+ elif action=="star-merge":
3364
+ self.star_merge(revision, options.diff3)
3365
+ except errors.MergeProblem, e:
3367
+ if cmdutil.has_changed(self.tree.tree_version):
3370
+ def star_merge(self, revision, diff3):
3371
+ """Perform a star-merge on the current tree.
3373
+ :param revision: The revision to use for the merge
3374
+ :type revision: `arch.Revision`
3375
+ :param diff3: If true, do a diff3 merge
3379
+ for line in self.tree.iter_star_merge(revision, diff3=diff3):
3380
+ cmdutil.colorize(line)
3381
+ except arch.util.ExecProblem, e:
3382
+ if e.proc.status is not None and e.proc.status == 1:
3384
+ print e.proc.error
3385
+ raise MergeProblem
3389
+ def native_merge(self, other_revision, action):
3390
+ """Perform a native-merge on the current tree.
3392
+ :param other_revision: The revision to use for the merge
3393
+ :type other_revision: `arch.Revision`
3394
+ :return: 0 if the merge was skipped, 1 if it was applied
3396
+ other_tree = arch_compound.find_or_make_local_revision(other_revision)
3398
+ if action == "native-merge":
3399
+ ancestor = arch_compound.merge_ancestor2(self.tree, other_tree,
3401
+ elif action == "update":
3402
+ ancestor = arch_compound.tree_latest(self.tree,
3403
+ other_revision.version)
3404
+ except CantDetermineRevision, e:
3405
+ raise CommandFailedWrapper(e)
3406
+ cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
3407
+ if (ancestor == other_revision):
3408
+ cmdutil.colorize(arch.Chatter("* Skipping redundant merge"
3411
+ delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)
3412
+ for line in cmdutil.iter_apply_delta_filter(delta):
3413
+ cmdutil.colorize(line)
3418
+ def get_parser(self):
3420
+ Returns the options parser to use for the "merge" command.
3422
+ :rtype: cmdutil.CmdOptionParser
3424
+ parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
3425
+ parser.add_option("-s", "--star-merge", action="store_const",
3426
+ dest="action", help="Use star-merge",
3427
+ const="star-merge", default="native-merge")
3428
+ parser.add_option("--update", action="store_const",
3429
+ dest="action", help="Use update picker",
3431
+ parser.add_option("--diff3", action="store_true",
3433
+ help="Use diff3 for merge (implies star-merge)")
3436
+ def help(self, parser=None):
3438
+ Prints a help message.
3440
+ :param parser: If supplied, the parser to use for generating help. If \
3441
+ not supplied, it is retrieved.
3442
+ :type parser: cmdutil.CmdOptionParser
3445
+ parser=self.get_parser()
3446
+ parser.print_help()
3448
+Performs a merge operation using the specified version.
3452
+class ELog(BaseCommand):
3454
+ Produces a raw patchlog and invokes the user's editor
3456
+ def __init__(self):
3457
+ self.description="Edit a patchlog to commit"
3459
+ self.tree = arch.tree_root()
3464
+ def do_command(self, cmdargs):
3466
+ Master function that perfoms the "elog" command.
3468
+ parser=self.get_parser()
3469
+ (options, args) = parser.parse_args(cmdargs)
3470
+ if self.tree is None:
3471
+ raise arch.errors.TreeRootError
3474
+ edit_log(self.tree, self.tree.tree_version)
3475
+ except pylon.errors.NoEditorSpecified, e:
3476
+ raise pylon.errors.CommandFailedWrapper(e)
3478
+ def get_parser(self):
3480
+ Returns the options parser to use for the "merge" command.
3482
+ :rtype: cmdutil.CmdOptionParser
3484
+ parser=cmdutil.CmdOptionParser("fai elog")
3488
+ def help(self, parser=None):
3490
+ Invokes $EDITOR to produce a log for committing.
3492
+ :param parser: If supplied, the parser to use for generating help. If \
3493
+ not supplied, it is retrieved.
3494
+ :type parser: cmdutil.CmdOptionParser
3497
+ parser=self.get_parser()
3498
+ parser.print_help()
3500
+Invokes $EDITOR to produce a log for committing.
3504
+def edit_log(tree, version):
3505
+ """Makes and edits the log for a tree. Does all kinds of fancy things
3506
+ like log templates and merge summaries and log-for-merge
3508
+ :param tree: The tree to edit the log for
3509
+ :type tree: `arch.WorkingTree`
3511
+ #ensure we have an editor before preparing the log
3512
+ cmdutil.find_editor()
3513
+ log = tree.log_message(create=False, version=version)
3514
+ log_is_new = False
3515
+ if log is None or cmdutil.prompt("Overwrite log"):
3516
+ if log is not None:
3517
+ os.remove(log.name)
3518
+ log = tree.log_message(create=True, version=version)
3521
+ template = pylon.log_template_path(tree)
3523
+ shutil.copyfile(template, tmplog)
3524
+ comp_version = ancillary.comp_revision(tree).version
3525
+ new_merges = cmdutil.iter_new_merges(tree, comp_version)
3526
+ new_merges = cmdutil.direct_merges(new_merges)
3527
+ log["Summary"] = pylon.merge_summary(new_merges,
3529
+ if len(new_merges) > 0:
3530
+ if cmdutil.prompt("Log for merge"):
3531
+ if cmdutil.prompt("changelog for merge"):
3532
+ mergestuff = "Patches applied:\n"
3533
+ mergestuff += pylon.changelog_for_merge(new_merges)
3535
+ mergestuff = cmdutil.log_for_merge(tree, comp_version)
3536
+ log.description += mergestuff
3539
+ cmdutil.invoke_editor(log.name)
3542
+ os.remove(log.name)
3546
+class MirrorArchive(BaseCommand):
3548
+ Updates a mirror from an archive
3550
+ def __init__(self):
3551
+ self.description="Update a mirror from an archive"
3553
+ def do_command(self, cmdargs):
3555
+ Master function that perfoms the "revision" command.
3558
+ parser=self.get_parser()
3559
+ (options, args) = parser.parse_args(cmdargs)
3563
+ tree = arch.tree_root()
3567
+ if len(args) == 0:
3568
+ if tree is not None:
3569
+ name = tree.tree_version()
3571
+ name = cmdutil.expand_alias(args[0], tree)
3572
+ name = arch.NameParser(name)
3574
+ to_arch = name.get_archive()
3575
+ from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
3576
+ limit = name.get_nonarch()
3578
+ iter = arch_core.mirror_archive(from_arch,to_arch, limit)
3579
+ for line in arch.chatter_classifier(iter):
3580
+ cmdutil.colorize(line)
3582
+ def get_parser(self):
3584
+ Returns the options parser to use for the "revision" command.
3586
+ :rtype: cmdutil.CmdOptionParser
3588
+ parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
3591
+ def help(self, parser=None):
3593
+ Prints a help message.
3595
+ :param parser: If supplied, the parser to use for generating help. If \
3596
+ not supplied, it is retrieved.
3597
+ :type parser: cmdutil.CmdOptionParser
3600
+ parser=self.get_parser()
3601
+ parser.print_help()
3603
+Updates a mirror from an archive. If a branch, package, or version is
3604
+supplied, only changes under it are mirrored.
3608
+def help_tree_spec():
3609
+ print """Specifying revisions (default: tree)
3610
+Revisions may be specified by alias, revision, version or patchlevel.
3611
+Revisions or versions may be fully qualified. Unqualified revisions, versions,
3612
+or patchlevels use the archive of the current project tree. Versions will
3613
+use the latest patchlevel in the tree. Patchlevels will use the current tree-
3616
+Use "alias" to list available (user and automatic) aliases."""
3620
+"The latest revision in the archive of the tree-version. You can specify \
3621
+a different version like so: acur:foo--bar--0 (aliases can be used)",
3623
+"""(tree current) The latest revision in the tree of the tree-version. \
3624
+You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
3627
+"""(tree previous) The previous revision in the tree of the tree-version. To \
3628
+specify an older revision, use a number, e.g. "tprev:4" """,
3630
+"""(tree ancestor) The ancestor revision of the tree To specify an older \
3631
+revision, use a number, e.g. "tanc:4".""",
3633
+"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
3635
+""" (tree modified) The latest revision to modify a given file, e.g. \
3636
+"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
3638
+"""(tree tag) The revision that was tagged into the current tree revision, \
3639
+according to the tree""",
3641
+"""(tag current) The latest revision of the version that the current tree \
3642
+was tagged from.""",
3644
+"""The common ancestor of the current tree and the specified revision. \
3645
+Defaults to the first partner-version's latest revision or to tagcur.""",
3649
+def is_auto_alias(name):
3650
+ """Determine whether a name is an auto alias name
3652
+ :param name: the name to check
3654
+ :return: True if the name is an auto alias, false if not
3657
+ return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
3660
+def display_def(iter, wrap = 80):
3661
+ """Display a list of definitions
3663
+ :param iter: iter of name, definition pairs
3664
+ :type iter: iter of (str, str)
3665
+ :param wrap: The width for text wrapping
3670
+ for (key, value) in vals:
3671
+ if len(key) > maxlen:
3673
+ for (key, value) in vals:
3674
+ tw=textwrap.TextWrapper(width=wrap,
3675
+ initial_indent=key.rjust(maxlen)+" : ",
3676
+ subsequent_indent="".rjust(maxlen+3))
3677
+ print tw.fill(value)
3680
+def help_aliases(tree):
3681
+ print """Auto-generated aliases"""
3682
+ display_def(pylon.util.iter_pairs(auto_alias))
3683
+ print "User aliases"
3684
+ display_def(ancillary.iter_all_alias(tree))
3686
+class Inventory(BaseCommand):
3687
+ """List the status of files in the tree"""
3688
+ def __init__(self):
3689
+ self.description=self.__doc__
3691
+ def do_command(self, cmdargs):
3693
+ Master function that perfoms the "revision" command.
3696
+ parser=self.get_parser()
3697
+ (options, args) = parser.parse_args(cmdargs)
3698
+ tree = arch.tree_root()
3701
+ if (options.source):
3702
+ categories.append(arch_core.SourceFile)
3703
+ if (options.precious):
3704
+ categories.append(arch_core.PreciousFile)
3705
+ if (options.backup):
3706
+ categories.append(arch_core.BackupFile)
3707
+ if (options.junk):
3708
+ categories.append(arch_core.JunkFile)
3710
+ if len(categories) == 1:
3711
+ show_leading = False
3713
+ show_leading = True
3715
+ if len(categories) == 0:
3718
+ if options.untagged:
3719
+ categories = arch_core.non_root
3720
+ show_leading = False
3725
+ for file in arch_core.iter_inventory_filter(tree, None,
3726
+ control_files=options.control_files,
3727
+ categories = categories, tagged=tagged):
3728
+ print arch_core.file_line(file,
3729
+ category = show_leading,
3730
+ untagged = show_leading,
3733
+ def get_parser(self):
3735
+ Returns the options parser to use for the "revision" command.
3737
+ :rtype: cmdutil.CmdOptionParser
3739
+ parser=cmdutil.CmdOptionParser("fai inventory [options]")
3740
+ parser.add_option("--ids", action="store_true", dest="ids",
3741
+ help="Show file ids")
3742
+ parser.add_option("--control", action="store_true",
3743
+ dest="control_files", help="include control files")
3744
+ parser.add_option("--source", action="store_true", dest="source",
3745
+ help="List source files")
3746
+ parser.add_option("--backup", action="store_true", dest="backup",
3747
+ help="List backup files")
3748
+ parser.add_option("--precious", action="store_true", dest="precious",
3749
+ help="List precious files")
3750
+ parser.add_option("--junk", action="store_true", dest="junk",
3751
+ help="List junk files")
3752
+ parser.add_option("--unrecognized", action="store_true",
3753
+ dest="unrecognized", help="List unrecognized files")
3754
+ parser.add_option("--untagged", action="store_true",
3755
+ dest="untagged", help="List only untagged files")
3758
+ def help(self, parser=None):
3760
+ Prints a help message.
3762
+ :param parser: If supplied, the parser to use for generating help. If \
3763
+ not supplied, it is retrieved.
3764
+ :type parser: cmdutil.CmdOptionParser
3767
+ parser=self.get_parser()
3768
+ parser.print_help()
3770
+Lists the status of files in the archive:
3778
+Leading letter are not displayed if only one kind of file is shown
3783
+class Alias(BaseCommand):
3784
+ """List or adjust aliases"""
3785
+ def __init__(self):
3786
+ self.description=self.__doc__
3788
+ def get_completer(self, arg, index):
3792
+ self.tree = arch.tree_root()
3797
+ return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
3799
+ return cmdutil.iter_revision_completions(arg, self.tree)
3802
+ def do_command(self, cmdargs):
3804
+ Master function that perfoms the "revision" command.
3807
+ parser=self.get_parser()
3808
+ (options, args) = parser.parse_args(cmdargs)
3810
+ self.tree = arch.tree_root()
3816
+ options.action(args, options)
3817
+ except cmdutil.ForbiddenAliasSyntax, e:
3818
+ raise CommandFailedWrapper(e)
3820
+ def no_prefix(self, alias):
3821
+ if alias.startswith("^"):
3825
+ def arg_dispatch(self, args, options):
3826
+ """Add, modify, or list aliases, depending on number of arguments
3828
+ :param args: The list of commandline arguments
3829
+ :type args: list of str
3830
+ :param options: The commandline options
3832
+ if len(args) == 0:
3833
+ help_aliases(self.tree)
3836
+ alias = self.no_prefix(args[0])
3837
+ if len(args) == 1:
3838
+ self.print_alias(alias)
3839
+ elif (len(args)) == 2:
3840
+ self.add(alias, args[1], options)
3842
+ raise cmdutil.GetHelp
3844
+ def print_alias(self, alias):
3846
+ if is_auto_alias(alias):
3847
+ raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
3848
+ " Use \"revision\" to expand auto aliases." % alias)
3849
+ for pair in ancillary.iter_all_alias(self.tree):
3850
+ if pair[0] == alias:
3852
+ if answer is not None:
3855
+ print "The alias %s is not assigned." % alias
3857
+ def add(self, alias, expansion, options):
3858
+ """Add or modify aliases
3860
+ :param alias: The alias name to create/modify
3862
+ :param expansion: The expansion to assign to the alias name
3863
+ :type expansion: str
3864
+ :param options: The commandline options
3866
+ if is_auto_alias(alias):
3867
+ raise IsAutoAlias(alias)
3870
+ new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion,
3872
+ ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
3874
+ for pair in self.get_iterator(options):
3875
+ if pair[0] != alias:
3876
+ newlist+="%s=%s\n" % (pair[0], pair[1])
3882
+ self.write_aliases(newlist, options)
3884
+ def delete(self, args, options):
3885
+ """Delete the specified alias
3887
+ :param args: The list of arguments
3888
+ :type args: list of str
3889
+ :param options: The commandline options
3892
+ if len(args) != 1:
3893
+ raise cmdutil.GetHelp
3894
+ alias = self.no_prefix(args[0])
3895
+ if is_auto_alias(alias):
3896
+ raise IsAutoAlias(alias)
3898
+ for pair in self.get_iterator(options):
3899
+ if pair[0] != alias:
3900
+ newlist+="%s=%s\n" % (pair[0], pair[1])
3904
+ raise errors.NoSuchAlias(alias)
3905
+ self.write_aliases(newlist, options)
3907
+ def get_alias_file(self, options):
3908
+ """Return the name of the alias file to use
3910
+ :param options: The commandline options
3913
+ if self.tree is None:
3914
+ self.tree == arch.tree_root()
3915
+ return str(self.tree)+"/{arch}/+aliases"
3917
+ return "~/.aba/aliases"
3919
+ def get_iterator(self, options):
3920
+ """Return the alias iterator to use
3922
+ :param options: The commandline options
3924
+ return ancillary.iter_alias(self.get_alias_file(options))
3926
+ def write_aliases(self, newlist, options):
3927
+ """Safely rewrite the alias file
3928
+ :param newlist: The new list of aliases
3929
+ :type newlist: str
3930
+ :param options: The commandline options
3932
+ filename = os.path.expanduser(self.get_alias_file(options))
3933
+ file = util.NewFileVersion(filename)
3934
+ file.write(newlist)
3938
+ def get_parser(self):
3940
+ Returns the options parser to use for the "alias" command.
3942
+ :rtype: cmdutil.CmdOptionParser
3944
+ parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
3945
+ parser.add_option("-d", "--delete", action="store_const", dest="action",
3946
+ const=self.delete, default=self.arg_dispatch,
3947
+ help="Delete an alias")
3948
+ parser.add_option("--tree", action="store_true", dest="tree",
3949
+ help="Create a per-tree alias", default=False)
3952
+ def help(self, parser=None):
3954
+ Prints a help message.
3956
+ :param parser: If supplied, the parser to use for generating help. If \
3957
+ not supplied, it is retrieved.
3958
+ :type parser: cmdutil.CmdOptionParser
3961
+ parser=self.get_parser()
3962
+ parser.print_help()
3964
+Lists current aliases or modifies the list of aliases.
3966
+If no arguments are supplied, aliases will be listed. If two arguments are
3967
+supplied, the specified alias will be created or modified. If -d or --delete
3968
+is supplied, the specified alias will be deleted.
3970
+You can create aliases that refer to any fully-qualified part of the
3971
+Arch namespace, e.g.
3974
+archive/category--branch,
3975
+archive/category--branch--version (my favourite)
3976
+archive/category--branch--version--patchlevel
3978
+Aliases can be used automatically by native commands. To use them
3979
+with external or tla commands, prefix them with ^ (you can do this
3980
+with native commands, too).
3984
+class RequestMerge(BaseCommand):
3985
+ """Submit a merge request to Bug Goo"""
3986
+ def __init__(self):
3987
+ self.description=self.__doc__
3989
+ def do_command(self, cmdargs):
3990
+ """Submit a merge request
3992
+ :param cmdargs: The commandline arguments
3993
+ :type cmdargs: list of str
3995
+ parser = self.get_parser()
3996
+ (options, args) = parser.parse_args(cmdargs)
3998
+ cmdutil.find_editor()
3999
+ except pylon.errors.NoEditorSpecified, e:
4000
+ raise pylon.errors.CommandFailedWrapper(e)
4002
+ self.tree=arch.tree_root()
4005
+ base, revisions = self.revision_specs(args)
4006
+ message = self.make_headers(base, revisions)
4007
+ message += self.make_summary(revisions)
4008
+ path = self.edit_message(message)
4009
+ message = self.tidy_message(path)
4010
+ if cmdutil.prompt("Send merge"):
4011
+ self.send_message(message)
4012
+ print "Merge request sent"
4014
+ def make_headers(self, base, revisions):
4015
+ """Produce email and Bug Goo header strings
4017
+ :param base: The base revision to apply merges to
4018
+ :type base: `arch.Revision`
4019
+ :param revisions: The revisions to replay into the base
4020
+ :type revisions: list of `arch.Patchlog`
4021
+ :return: The headers
4024
+ headers = "To: gnu-arch-users@gnu.org\n"
4025
+ headers += "From: %s\n" % options.fromaddr
4026
+ if len(revisions) == 1:
4027
+ headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
4029
+ headers += "Subject: [MERGE REQUEST]\n"
4031
+ headers += "Base-Revision: %s\n" % base
4032
+ for revision in revisions:
4033
+ headers += "Revision: %s\n" % revision.revision
4034
+ headers += "Bug: \n\n"
4037
+ def make_summary(self, logs):
4038
+ """Generate a summary of merges
4040
+ :param logs: the patchlogs that were directly added by the merges
4041
+ :type logs: list of `arch.Patchlog`
4042
+ :return: the summary
4047
+ summary+=str(log.revision)+"\n"
4048
+ summary+=log.summary+"\n"
4049
+ if log.description.strip():
4050
+ summary+=log.description.strip('\n')+"\n\n"
4053
+ def revision_specs(self, args):
4054
+ """Determine the base and merge revisions from tree and arguments.
4056
+ :param args: The parsed arguments
4057
+ :type args: list of str
4058
+ :return: The base revision and merge revisions
4059
+ :rtype: `arch.Revision`, list of `arch.Patchlog`
4062
+ target_revision = cmdutil.determine_revision_arch(self.tree,
4065
+ target_revision = arch_compound.tree_latest(self.tree)
4067
+ merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
4068
+ self.tree, f)) for f in args[1:] ]
4070
+ if self.tree is None:
4071
+ raise CantDetermineRevision("", "Not in a project tree")
4072
+ merge_iter = cmdutil.iter_new_merges(self.tree,
4073
+ target_revision.version,
4075
+ merges = [f for f in cmdutil.direct_merges(merge_iter)]
4076
+ return (target_revision, merges)
4078
+ def edit_message(self, message):
4079
+ """Edit an email message in the user's standard editor
4081
+ :param message: The message to edit
4082
+ :type message: str
4083
+ :return: the path of the edited message
4086
+ if self.tree is None:
4087
+ path = os.get_cwd()
4090
+ path += "/,merge-request"
4091
+ file = open(path, 'w')
4092
+ file.write(message)
4094
+ cmdutil.invoke_editor(path)
4097
+ def tidy_message(self, path):
4098
+ """Validate and clean up message.
4100
+ :param path: The path to the message to clean up
4102
+ :return: The parsed message
4103
+ :rtype: `email.Message`
4105
+ mail = email.message_from_file(open(path))
4106
+ if mail["Subject"].strip() == "[MERGE REQUEST]":
4107
+ raise BlandSubject
4109
+ request = email.message_from_string(mail.get_payload())
4110
+ if request.has_key("Bug"):
4111
+ if request["Bug"].strip()=="":
4112
+ del request["Bug"]
4113
+ mail.set_payload(request.as_string())
4116
+ def send_message(self, message):
4117
+ """Send a message, using its headers to address it.
4119
+ :param message: The message to send
4120
+ :type message: `email.Message`"""
4121
+ server = smtplib.SMTP("localhost")
4122
+ server.sendmail(message['From'], message['To'], message.as_string())
4125
+ def help(self, parser=None):
4126
+ """Print a usage message
4128
+ :param parser: The options parser to use
4129
+ :type parser: `cmdutil.CmdOptionParser`
4131
+ if parser is None:
4132
+ parser = self.get_parser()
4133
+ parser.print_help()
4135
+Sends a merge request formatted for Bug Goo. Intended use: get the tree
4136
+you'd like to merge into. Apply the merges you want. Invoke request-merge.
4137
+The merge request will open in your $EDITOR.
4139
+When no TARGET is specified, it uses the current tree revision. When
4140
+no MERGE is specified, it uses the direct merges (as in "revisions
4141
+--direct-merges"). But you can specify just the TARGET, or all the MERGE
4145
+ def get_parser(self):
4146
+ """Produce a commandline parser for this command.
4148
+ :rtype: `cmdutil.CmdOptionParser`
4150
+ parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
4154
+'changes' : Changes,
4157
+'apply-changes':ApplyChanges,
4160
+'revision': Revision,
4161
+'revisions': Revisions,
4168
+'mirror-archive': MirrorArchive,
4169
+'ninventory': Inventory,
4171
+'request-merge': RequestMerge,
4174
+def my_import(mod_name):
4175
+ module = __import__(mod_name)
4176
+ components = mod_name.split('.')
4177
+ for comp in components[1:]:
4178
+ module = getattr(module, comp)
4181
+def plugin(mod_name):
4182
+ module = my_import(mod_name)
4183
+ module.add_command(commands)
4185
+for file in os.listdir(sys.path[0]+"/command"):
4186
+ if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
4187
+ plugin("command."+file[:-3])
4190
+'apply-delta' : "Try \"apply-changes\".",
4191
+'delta' : "To compare two revisions, use \"changes\".",
4192
+'diff-rev' : "To compare two revisions, use \"changes\".",
4193
+'undo' : "To undo local changes, use \"revert\".",
4194
+'undelete' : "To undo only deletions, use \"revert --deletions\"",
4195
+'missing-from' : "Try \"revisions --missing-from\".",
4196
+'missing' : "Try \"revisions --missing\".",
4197
+'missing-merge' : "Try \"revisions --partner-missing\".",
4198
+'new-merges' : "Try \"revisions --new-merges\".",
4199
+'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
4200
+'logs' : "Try \"revisions --logs\"",
4201
+'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
4202
+'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
4203
+'change-version' : "Try \"update REVISION\"",
4204
+'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
4205
+'rev-depends' : "Use revisions --dependencies",
4206
+'auto-get' : "Plain get will do archive lookups",
4207
+'tagline' : "Use add-id. It uses taglines in tagline trees",
4208
+'emlog' : "Use elog. It automatically adds log-for-merge text, if any",
4209
+'library-revisions' : "Use revisions --library",
4210
+'file-revert' : "Use revert FILE",
4211
+'join-branch' : "Use replay --logs-only"
4213
+# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
4215
*** added file 'testdata/orig'
4219
+# Copyright (C) 2004 Aaron Bentley
4220
+# <aaron.bentley@utoronto.ca>
4222
+# This program is free software; you can redistribute it and/or modify
4223
+# it under the terms of the GNU General Public License as published by
4224
+# the Free Software Foundation; either version 2 of the License, or
4225
+# (at your option) any later version.
4227
+# This program is distributed in the hope that it will be useful,
4228
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4229
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4230
+# GNU General Public License for more details.
4232
+# You should have received a copy of the GNU General Public License
4233
+# along with this program; if not, write to the Free Software
4234
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
4252
+from errors import *
4260
+__docformat__ = "restructuredtext"
4261
+__doc__ = "Implementation of user (sub) commands"
4264
+def find_command(cmd):
4266
+ Return an instance of a command type. Return None if the type isn't
4269
+ :param cmd: the name of the command to look for
4270
+ :type cmd: the type of the command
4272
+ if commands.has_key(cmd):
4273
+ return commands[cmd]()
4278
+ def __call__(self, cmdline):
4280
+ self.do_command(cmdline.split())
4281
+ except cmdutil.GetHelp, e:
4283
+ except Exception, e:
4286
+ def get_completer(index):
4289
+ def complete(self, args, text):
4291
+ Returns a list of possible completions for the given text.
4293
+ :param args: The complete list of arguments
4294
+ :type args: List of str
4295
+ :param text: text to complete (may be shorter than args[-1])
4297
+ :rtype: list of str
4303
+ realtext = args[-1]
4308
+ parser=self.get_parser()
4309
+ if realtext.startswith('-'):
4310
+ candidates = parser.iter_options()
4312
+ (options, parsed_args) = parser.parse_args(args)
4314
+ if len (parsed_args) > 0:
4315
+ candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
4317
+ candidates = self.get_completer("", 0)
4320
+ if candidates is None:
4322
+ for candidate in candidates:
4323
+ candidate = str(candidate)
4324
+ if candidate.startswith(realtext):
4325
+ matches.append(candidate[len(realtext)- len(text):])
4329
+class Help(BaseCommand):
4331
+ Lists commands, prints help messages.
4333
+ def __init__(self):
4334
+ self.description="Prints help mesages"
4335
+ self.parser = None
4337
+ def do_command(self, cmdargs):
4339
+ Prints a help message.
4341
+ options, args = self.get_parser().parse_args(cmdargs)
4343
+ raise cmdutil.GetHelp
4345
+ if options.native or options.suggestions or options.external:
4346
+ native = options.native
4347
+ suggestions = options.suggestions
4348
+ external = options.external
4351
+ suggestions = False
4354
+ if len(args) == 0:
4355
+ self.list_commands(native, suggestions, external)
4357
+ elif len(args) == 1:
4358
+ command_help(args[0])
4362
+ self.get_parser().print_help()
4364
+If no command is specified, commands are listed. If a command is
4365
+specified, help for that command is listed.
4368
+ def get_parser(self):
4370
+ Returns the options parser to use for the "revision" command.
4372
+ :rtype: cmdutil.CmdOptionParser
4374
+ if self.parser is not None:
4375
+ return self.parser
4376
+ parser=cmdutil.CmdOptionParser("fai help [command]")
4377
+ parser.add_option("-n", "--native", action="store_true",
4378
+ dest="native", help="Show native commands")
4379
+ parser.add_option("-e", "--external", action="store_true",
4380
+ dest="external", help="Show external commands")
4381
+ parser.add_option("-s", "--suggest", action="store_true",
4382
+ dest="suggestions", help="Show suggestions")
4383
+ self.parser = parser
4386
+ def list_commands(self, native=True, suggest=False, external=True):
4388
+ Lists supported commands.
4390
+ :param native: list native, python-based commands
4391
+ :type native: bool
4392
+ :param external: list external aba-style commands
4393
+ :type external: bool
4396
+ print "Native Fai commands"
4397
+ keys=commands.keys()
4401
+ for i in range(28-len(k)):
4403
+ print space+k+" : "+commands[k]().description
4406
+ print "Unavailable commands and suggested alternatives"
4407
+ key_list = suggestions.keys()
4409
+ for key in key_list:
4410
+ print "%28s : %s" % (key, suggestions[key])
4413
+ fake_aba = abacmds.AbaCmds()
4414
+ if (fake_aba.abadir == ""):
4416
+ print "External commands"
4417
+ fake_aba.list_commands()
4420
+ print "Use help --suggest to list alternatives to tla and aba"\
4422
+ if options.tla_fallthrough and (native or external):
4423
+ print "Fai also supports tla commands."
4425
+def command_help(cmd):
4427
+ Prints help for a command.
4429
+ :param cmd: The name of the command to print help for
4432
+ fake_aba = abacmds.AbaCmds()
4433
+ cmdobj = find_command(cmd)
4434
+ if cmdobj != None:
4436
+ elif suggestions.has_key(cmd):
4437
+ print "Not available\n" + suggestions[cmd]
4439
+ abacmd = fake_aba.is_command(cmd)
4443
+ print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
4447
+class Changes(BaseCommand):
4449
+ the "changes" command: lists differences between trees/revisions:
4452
+ def __init__(self):
4453
+ self.description="Lists what files have changed in the project tree"
4455
+ def get_completer(self, arg, index):
4459
+ tree = arch.tree_root()
4462
+ return cmdutil.iter_revision_completions(arg, tree)
4464
+ def parse_commandline(self, cmdline):
4466
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
4468
+ :param cmdline: A list of arguments to parse
4469
+ :rtype: (options, Revision, Revision/WorkingTree)
4471
+ parser=self.get_parser()
4472
+ (options, args) = parser.parse_args(cmdline)
4474
+ raise cmdutil.GetHelp
4476
+ tree=arch.tree_root()
4477
+ if len(args) == 0:
4478
+ a_spec = cmdutil.comp_revision(tree)
4480
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
4481
+ cmdutil.ensure_archive_registered(a_spec.archive)
4482
+ if len(args) == 2:
4483
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
4484
+ cmdutil.ensure_archive_registered(b_spec.archive)
4487
+ return options, a_spec, b_spec
4489
+ def do_command(self, cmdargs):
4491
+ Master function that perfoms the "changes" command.
4494
+ options, a_spec, b_spec = self.parse_commandline(cmdargs);
4495
+ except cmdutil.CantDetermineRevision, e:
4498
+ except arch.errors.TreeRootError, e:
4501
+ if options.changeset:
4502
+ changeset=options.changeset
4505
+ tmpdir=cmdutil.tmpdir()
4506
+ changeset=tmpdir+"/changeset"
4508
+ delta=arch.iter_delta(a_spec, b_spec, changeset)
4510
+ for line in delta:
4511
+ if cmdutil.chattermatch(line, "changeset:"):
4514
+ cmdutil.colorize(line, options.suppress_chatter)
4515
+ except arch.util.ExecProblem, e:
4516
+ if e.proc.error and e.proc.error.startswith(
4517
+ "missing explicit id for file"):
4518
+ raise MissingID(e)
4521
+ status=delta.status
4524
+ if (options.perform_diff):
4525
+ chan = cmdutil.ChangesetMunger(changeset)
4526
+ chan.read_indices()
4527
+ if isinstance(b_spec, arch.Revision):
4528
+ b_dir = b_spec.library_find()
4531
+ a_dir = a_spec.library_find()
4532
+ if options.diffopts is not None:
4533
+ diffopts = options.diffopts.split()
4534
+ cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
4536
+ cmdutil.show_diffs(delta.changeset)
4538
+ if tmpdir and (os.access(tmpdir, os.X_OK)):
4539
+ shutil.rmtree(tmpdir)
4541
+ def get_parser(self):
4543
+ Returns the options parser to use for the "changes" command.
4545
+ :rtype: cmdutil.CmdOptionParser
4547
+ parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
4549
+ parser.add_option("-d", "--diff", action="store_true",
4550
+ dest="perform_diff", default=False,
4551
+ help="Show diffs in summary")
4552
+ parser.add_option("-c", "--changeset", dest="changeset",
4553
+ help="Store a changeset in the given directory",
4554
+ metavar="DIRECTORY")
4555
+ parser.add_option("-s", "--silent", action="store_true",
4556
+ dest="suppress_chatter", default=False,
4557
+ help="Suppress chatter messages")
4558
+ parser.add_option("--diffopts", dest="diffopts",
4559
+ help="Use the specified diff options",
4560
+ metavar="OPTIONS")
4564
+ def help(self, parser=None):
4566
+ Prints a help message.
4568
+ :param parser: If supplied, the parser to use for generating help. If \
4569
+ not supplied, it is retrieved.
4570
+ :type parser: cmdutil.CmdOptionParser
4572
+ if parser is None:
4573
+ parser=self.get_parser()
4574
+ parser.print_help()
4576
+Performs source-tree comparisons
4578
+If no revision is specified, the current project tree is compared to the
4579
+last-committed revision. If one revision is specified, the current project
4580
+tree is compared to that revision. If two revisions are specified, they are
4581
+compared to each other.
4587
+class ApplyChanges(BaseCommand):
4589
+ Apply differences between two revisions to a tree
4592
+ def __init__(self):
4593
+ self.description="Applies changes to a project tree"
4595
+ def get_completer(self, arg, index):
4599
+ tree = arch.tree_root()
4602
+ return cmdutil.iter_revision_completions(arg, tree)
4604
+ def parse_commandline(self, cmdline, tree):
4606
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
4608
+ :param cmdline: A list of arguments to parse
4609
+ :rtype: (options, Revision, Revision/WorkingTree)
4611
+ parser=self.get_parser()
4612
+ (options, args) = parser.parse_args(cmdline)
4613
+ if len(args) != 2:
4614
+ raise cmdutil.GetHelp
4616
+ a_spec = cmdutil.determine_revision_tree(tree, args[0])
4617
+ cmdutil.ensure_archive_registered(a_spec.archive)
4618
+ b_spec = cmdutil.determine_revision_tree(tree, args[1])
4619
+ cmdutil.ensure_archive_registered(b_spec.archive)
4620
+ return options, a_spec, b_spec
4622
+ def do_command(self, cmdargs):
4624
+ Master function that performs "apply-changes".
4627
+ tree = arch.tree_root()
4628
+ options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
4629
+ except cmdutil.CantDetermineRevision, e:
4632
+ except arch.errors.TreeRootError, e:
4635
+ delta=cmdutil.apply_delta(a_spec, b_spec, tree)
4636
+ for line in cmdutil.iter_apply_delta_filter(delta):
4637
+ cmdutil.colorize(line, options.suppress_chatter)
4639
+ def get_parser(self):
4641
+ Returns the options parser to use for the "apply-changes" command.
4643
+ :rtype: cmdutil.CmdOptionParser
4645
+ parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
4647
+ parser.add_option("-d", "--diff", action="store_true",
4648
+ dest="perform_diff", default=False,
4649
+ help="Show diffs in summary")
4650
+ parser.add_option("-c", "--changeset", dest="changeset",
4651
+ help="Store a changeset in the given directory",
4652
+ metavar="DIRECTORY")
4653
+ parser.add_option("-s", "--silent", action="store_true",
4654
+ dest="suppress_chatter", default=False,
4655
+ help="Suppress chatter messages")
4658
+ def help(self, parser=None):
4660
+ Prints a help message.
4662
+ :param parser: If supplied, the parser to use for generating help. If \
4663
+ not supplied, it is retrieved.
4664
+ :type parser: cmdutil.CmdOptionParser
4666
+ if parser is None:
4667
+ parser=self.get_parser()
4668
+ parser.print_help()
4670
+Applies changes to a project tree
4672
+Compares two revisions and applies the difference between them to the current
4678
+class Update(BaseCommand):
4680
+ Updates a project tree to a given revision, preserving un-committed hanges.
4683
+ def __init__(self):
4684
+ self.description="Apply the latest changes to the current directory"
4686
+ def get_completer(self, arg, index):
4690
+ tree = arch.tree_root()
4693
+ return cmdutil.iter_revision_completions(arg, tree)
4695
+ def parse_commandline(self, cmdline, tree):
4697
+ Parse commandline arguments. Raises cmdutil.GetHelp if help is needed.
4699
+ :param cmdline: A list of arguments to parse
4700
+ :rtype: (options, Revision, Revision/WorkingTree)
4702
+ parser=self.get_parser()
4703
+ (options, args) = parser.parse_args(cmdline)
4705
+ raise cmdutil.GetHelp
4710
+ revision=cmdutil.determine_revision_arch(tree, spec)
4711
+ cmdutil.ensure_archive_registered(revision.archive)
4713
+ mirror_source = cmdutil.get_mirror_source(revision.archive)
4714
+ if mirror_source != None:
4715
+ if cmdutil.prompt("Mirror update"):
4716
+ cmd=cmdutil.mirror_archive(mirror_source,
4717
+ revision.archive, arch.NameParser(revision).get_package_version())
4718
+ for line in arch.chatter_classifier(cmd):
4719
+ cmdutil.colorize(line, options.suppress_chatter)
4721
+ revision=cmdutil.determine_revision_arch(tree, spec)
4723
+ return options, revision
4725
+ def do_command(self, cmdargs):
4727
+ Master function that perfoms the "update" command.
4729
+ tree=arch.tree_root()
4731
+ options, to_revision = self.parse_commandline(cmdargs, tree);
4732
+ except cmdutil.CantDetermineRevision, e:
4735
+ except arch.errors.TreeRootError, e:
4738
+ from_revision=cmdutil.tree_latest(tree)
4739
+ if from_revision==to_revision:
4740
+ print "Tree is already up to date with:\n"+str(to_revision)+"."
4742
+ cmdutil.ensure_archive_registered(from_revision.archive)
4743
+ cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
4744
+ options.patch_forward)
4745
+ for line in cmdutil.iter_apply_delta_filter(cmd):
4746
+ cmdutil.colorize(line)
4747
+ if to_revision.version != tree.tree_version:
4748
+ if cmdutil.prompt("Update version"):
4749
+ tree.tree_version = to_revision.version
4751
+ def get_parser(self):
4753
+ Returns the options parser to use for the "update" command.
4755
+ :rtype: cmdutil.CmdOptionParser
4757
+ parser=cmdutil.CmdOptionParser("fai update [options]"
4758
+ " [revision/version]")
4759
+ parser.add_option("-f", "--forward", action="store_true",
4760
+ dest="patch_forward", default=False,
4761
+ help="pass the --forward option to 'patch'")
4762
+ parser.add_option("-s", "--silent", action="store_true",
4763
+ dest="suppress_chatter", default=False,
4764
+ help="Suppress chatter messages")
4767
+ def help(self, parser=None):
4769
+ Prints a help message.
4771
+ :param parser: If supplied, the parser to use for generating help. If \
4772
+ not supplied, it is retrieved.
4773
+ :type parser: cmdutil.CmdOptionParser
4775
+ if parser is None:
4776
+ parser=self.get_parser()
4777
+ parser.print_help()
4779
+Updates a working tree to the current archive revision
4781
+If a revision or version is specified, that is used instead
4787
+class Commit(BaseCommand):
4789
+ Create a revision based on the changes in the current tree.
4792
+ def __init__(self):
4793
+ self.description="Write local changes to the archive"
4795
+ def get_completer(self, arg, index):
4798
+ return iter_modified_file_completions(arch.tree_root(), arg)
4799
+# return iter_source_file_completions(arch.tree_root(), arg)
4801
+ def parse_commandline(self, cmdline, tree):
4803
+ Parse commandline arguments. Raise cmtutil.GetHelp if help is needed.
4805
+ :param cmdline: A list of arguments to parse
4806
+ :rtype: (options, Revision, Revision/WorkingTree)
4808
+ parser=self.get_parser()
4809
+ (options, args) = parser.parse_args(cmdline)
4811
+ if len(args) == 0:
4813
+ revision=cmdutil.determine_revision_arch(tree, options.version)
4814
+ return options, revision.get_version(), args
4816
+ def do_command(self, cmdargs):
4818
+ Master function that perfoms the "commit" command.
4820
+ tree=arch.tree_root()
4821
+ options, version, files = self.parse_commandline(cmdargs, tree)
4822
+ if options.__dict__.has_key("base") and options.base:
4823
+ base = cmdutil.determine_revision_tree(tree, options.base)
4825
+ base = cmdutil.submit_revision(tree)
4827
+ writeversion=version
4828
+ archive=version.archive
4829
+ source=cmdutil.get_mirror_source(archive)
4831
+ writethrough="implicit"
4834
+ if writethrough=="explicit" and \
4835
+ cmdutil.prompt("Writethrough"):
4836
+ writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
4837
+ elif writethrough=="none":
4838
+ raise CommitToMirror(archive)
4840
+ elif archive.is_mirror:
4841
+ raise CommitToMirror(archive)
4844
+ last_revision=tree.iter_logs(version, True).next().revision
4845
+ except StopIteration, e:
4846
+ if cmdutil.prompt("Import from commit"):
4847
+ return do_import(version)
4849
+ raise NoVersionLogs(version)
4850
+ if last_revision!=version.iter_revisions(True).next():
4851
+ if not cmdutil.prompt("Out of date"):
4857
+ if not cmdutil.has_changed(version):
4858
+ if not cmdutil.prompt("Empty commit"):
4860
+ except arch.util.ExecProblem, e:
4861
+ if e.proc.error and e.proc.error.startswith(
4862
+ "missing explicit id for file"):
4863
+ raise MissingID(e)
4866
+ log = tree.log_message(create=False)
4869
+ if cmdutil.prompt("Create log"):
4872
+ except cmdutil.NoEditorSpecified, e:
4873
+ raise CommandFailed(e)
4874
+ log = tree.log_message(create=False)
4876
+ raise NoLogMessage
4877
+ if log["Summary"] is None or len(log["Summary"].strip()) == 0:
4878
+ if not cmdutil.prompt("Omit log summary"):
4879
+ raise errors.NoLogSummary
4881
+ for line in tree.iter_commit(version, seal=options.seal_version,
4882
+ base=base, out_of_date_ok=allow_old, file_list=files):
4883
+ cmdutil.colorize(line, options.suppress_chatter)
4885
+ except arch.util.ExecProblem, e:
4886
+ if e.proc.error and e.proc.error.startswith(
4887
+ "These files violate naming conventions:"):
4888
+ raise LintFailure(e.proc.error)
4892
+ def get_parser(self):
4894
+ Returns the options parser to use for the "commit" command.
4896
+ :rtype: cmdutil.CmdOptionParser
4899
+ parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
4901
+ parser.add_option("--seal", action="store_true",
4902
+ dest="seal_version", default=False,
4903
+ help="seal this version")
4904
+ parser.add_option("-v", "--version", dest="version",
4905
+ help="Use the specified version",
4906
+ metavar="VERSION")
4907
+ parser.add_option("-s", "--silent", action="store_true",
4908
+ dest="suppress_chatter", default=False,
4909
+ help="Suppress chatter messages")
4910
+ if cmdutil.supports_switch("commit", "--base"):
4911
+ parser.add_option("--base", dest="base", help="",
4912
+ metavar="REVISION")
4915
+ def help(self, parser=None):
4917
+ Prints a help message.
4919
+ :param parser: If supplied, the parser to use for generating help. If \
4920
+ not supplied, it is retrieved.
4921
+ :type parser: cmdutil.CmdOptionParser
4923
+ if parser is None:
4924
+ parser=self.get_parser()
4925
+ parser.print_help()
4927
+Updates a working tree to the current archive revision
4929
+If a version is specified, that is used instead
4936
+class CatLog(BaseCommand):
4938
+ Print the log of a given file (from current tree)
4940
+ def __init__(self):
4941
+ self.description="Prints the patch log for a revision"
4943
+ def get_completer(self, arg, index):
4947
+ tree = arch.tree_root()
4950
+ return cmdutil.iter_revision_completions(arg, tree)
4952
+ def do_command(self, cmdargs):
4954
+ Master function that perfoms the "cat-log" command.
4956
+ parser=self.get_parser()
4957
+ (options, args) = parser.parse_args(cmdargs)
4959
+ tree = arch.tree_root()
4960
+ except arch.errors.TreeRootError, e:
4966
+ raise cmdutil.GetHelp()
4969
+ revision = cmdutil.determine_revision_tree(tree, spec)
4971
+ revision = cmdutil.determine_revision_arch(tree, spec)
4972
+ except cmdutil.CantDetermineRevision, e:
4973
+ raise CommandFailedWrapper(e)
4976
+ use_tree = (options.source == "tree" or \
4977
+ (options.source == "any" and tree))
4978
+ use_arch = (options.source == "archive" or options.source == "any")
4982
+ for log in tree.iter_logs(revision.get_version()):
4983
+ if log.revision == revision:
4987
+ if log is None and use_arch:
4988
+ cmdutil.ensure_revision_exists(revision)
4989
+ log = arch.Patchlog(revision)
4990
+ if log is not None:
4991
+ for item in log.items():
4992
+ print "%s: %s" % item
4993
+ print log.description
4995
+ def get_parser(self):
4997
+ Returns the options parser to use for the "cat-log" command.
4999
+ :rtype: cmdutil.CmdOptionParser
5001
+ parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
5002
+ parser.add_option("--archive", action="store_const", dest="source",
5003
+ const="archive", default="any",
5004
+ help="Always get the log from the archive")
5005
+ parser.add_option("--tree", action="store_const", dest="source",
5006
+ const="tree", help="Always get the log from the tree")
5009
+ def help(self, parser=None):
5011
+ Prints a help message.
5013
+ :param parser: If supplied, the parser to use for generating help. If \
5014
+ not supplied, it is retrieved.
5015
+ :type parser: cmdutil.CmdOptionParser
5018
+ parser=self.get_parser()
5019
+ parser.print_help()
5021
+Prints the log for the specified revision
5026
+class Revert(BaseCommand):
5027
+ """ Reverts a tree (or aspects of it) to a revision
5029
+ def __init__(self):
5030
+ self.description="Reverts a tree (or aspects of it) to a revision "
5032
+ def get_completer(self, arg, index):
5036
+ tree = arch.tree_root()
5039
+ return iter_modified_file_completions(tree, arg)
5041
+ def do_command(self, cmdargs):
5043
+ Master function that perfoms the "revert" command.
5045
+ parser=self.get_parser()
5046
+ (options, args) = parser.parse_args(cmdargs)
5048
+ tree = arch.tree_root()
5049
+ except arch.errors.TreeRootError, e:
5050
+ raise CommandFailed(e)
5052
+ if options.revision is not None:
5053
+ spec=options.revision
5055
+ if spec is not None:
5056
+ revision = cmdutil.determine_revision_tree(tree, spec)
5058
+ revision = cmdutil.comp_revision(tree)
5059
+ except cmdutil.CantDetermineRevision, e:
5060
+ raise CommandFailedWrapper(e)
5063
+ if options.file_contents or options.file_perms or options.deletions\
5064
+ or options.additions or options.renames or options.hunk_prompt:
5065
+ munger = cmdutil.MungeOpts()
5066
+ munger.hunk_prompt = options.hunk_prompt
5068
+ if len(args) > 0 or options.logs or options.pattern_files or \
5070
+ if munger is None:
5071
+ munger = cmdutil.MungeOpts(True)
5072
+ munger.all_types(True)
5074
+ t_cwd = cmdutil.tree_cwd(tree)
5076
+ if len(t_cwd) > 0:
5078
+ name = "./" + t_cwd + name
5079
+ munger.add_keep_file(name);
5081
+ if options.file_perms:
5082
+ munger.file_perms = True
5083
+ if options.file_contents:
5084
+ munger.file_contents = True
5085
+ if options.deletions:
5086
+ munger.deletions = True
5087
+ if options.additions:
5088
+ munger.additions = True
5089
+ if options.renames:
5090
+ munger.renames = True
5092
+ munger.add_keep_pattern('^\./\{arch\}/[^=].*')
5093
+ if options.control:
5094
+ munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
5095
+ "/\.arch-inventory$")
5096
+ if options.pattern_files:
5097
+ munger.add_keep_pattern(options.pattern_files)
5099
+ for line in cmdutil.revert(tree, revision, munger,
5100
+ not options.no_output):
5101
+ cmdutil.colorize(line)
5104
+ def get_parser(self):
5106
+ Returns the options parser to use for the "cat-log" command.
5108
+ :rtype: cmdutil.CmdOptionParser
5110
+ parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
5111
+ parser.add_option("", "--contents", action="store_true",
5112
+ dest="file_contents",
5113
+ help="Revert file content changes")
5114
+ parser.add_option("", "--permissions", action="store_true",
5115
+ dest="file_perms",
5116
+ help="Revert file permissions changes")
5117
+ parser.add_option("", "--deletions", action="store_true",
5119
+ help="Restore deleted files")
5120
+ parser.add_option("", "--additions", action="store_true",
5122
+ help="Remove added files")
5123
+ parser.add_option("", "--renames", action="store_true",
5125
+ help="Revert file names")
5126
+ parser.add_option("--hunks", action="store_true",
5127
+ dest="hunk_prompt", default=False,
5128
+ help="Prompt which hunks to revert")
5129
+ parser.add_option("--pattern-files", dest="pattern_files",
5130
+ help="Revert files that match this pattern",
5132
+ parser.add_option("--logs", action="store_true",
5133
+ dest="logs", default=False,
5134
+ help="Revert only logs")
5135
+ parser.add_option("--control-files", action="store_true",
5136
+ dest="control", default=False,
5137
+ help="Revert logs and other control files")
5138
+ parser.add_option("-n", "--no-output", action="store_true",
5140
+ help="Don't keep an undo changeset")
5141
+ parser.add_option("--revision", dest="revision",
5142
+ help="Revert to the specified revision",
5143
+ metavar="REVISION")
5146
+ def help(self, parser=None):
5148
+ Prints a help message.
5150
+ :param parser: If supplied, the parser to use for generating help. If \
5151
+ not supplied, it is retrieved.
5152
+ :type parser: cmdutil.CmdOptionParser
5155
+ parser=self.get_parser()
5156
+ parser.print_help()
5158
+Reverts changes in the current working tree. If no flags are specified, all
5159
+types of changes are reverted. Otherwise, only selected types of changes are
5162
+If a revision is specified on the commandline, differences between the current
5163
+tree and that revision are reverted. If a version is specified, the current
5164
+tree is used to determine the revision.
5166
+If files are specified, only those files listed will have any changes applied.
5167
+To specify a renamed file, you can use either the old or new name. (or both!)
5169
+Unless "-n" is specified, reversions can be undone with "redo".
5173
+class Revision(BaseCommand):
5175
+ Print a revision name based on a revision specifier
5177
+ def __init__(self):
5178
+ self.description="Prints the name of a revision"
5180
+ def get_completer(self, arg, index):
5184
+ tree = arch.tree_root()
5187
+ return cmdutil.iter_revision_completions(arg, tree)
5189
+ def do_command(self, cmdargs):
5191
+ Master function that perfoms the "revision" command.
5193
+ parser=self.get_parser()
5194
+ (options, args) = parser.parse_args(cmdargs)
5197
+ tree = arch.tree_root()
5198
+ except arch.errors.TreeRootError:
5205
+ raise cmdutil.GetHelp
5208
+ revision = cmdutil.determine_revision_tree(tree, spec)
5210
+ revision = cmdutil.determine_revision_arch(tree, spec)
5211
+ except cmdutil.CantDetermineRevision, e:
5214
+ print options.display(revision)
5216
+ def get_parser(self):
5218
+ Returns the options parser to use for the "revision" command.
5220
+ :rtype: cmdutil.CmdOptionParser
5222
+ parser=cmdutil.CmdOptionParser("fai revision [revision]")
5223
+ parser.add_option("", "--location", action="store_const",
5224
+ const=paths.determine_path, dest="display",
5225
+ help="Show location instead of name", default=str)
5226
+ parser.add_option("--import", action="store_const",
5227
+ const=paths.determine_import_path, dest="display",
5228
+ help="Show location of import file")
5229
+ parser.add_option("--log", action="store_const",
5230
+ const=paths.determine_log_path, dest="display",
5231
+ help="Show location of log file")
5232
+ parser.add_option("--patch", action="store_const",
5233
+ dest="display", const=paths.determine_patch_path,
5234
+ help="Show location of patchfile")
5235
+ parser.add_option("--continuation", action="store_const",
5236
+ const=paths.determine_continuation_path,
5238
+ help="Show location of continuation file")
5239
+ parser.add_option("--cacherev", action="store_const",
5240
+ const=paths.determine_cacherev_path, dest="display",
5241
+ help="Show location of cacherev file")
5244
+ def help(self, parser=None):
5246
+ Prints a help message.
5248
+ :param parser: If supplied, the parser to use for generating help. If \
5249
+ not supplied, it is retrieved.
5250
+ :type parser: cmdutil.CmdOptionParser
5253
+ parser=self.get_parser()
5254
+ parser.print_help()
5256
+Expands aliases and prints the name of the specified revision. Instead of
5257
+the name, several options can be used to print locations. If more than one is
5258
+specified, the last one is used.
5263
+def require_version_exists(version, spec):
5264
+ if not version.exists():
5265
+ raise cmdutil.CantDetermineVersion(spec,
5266
+ "The version %s does not exist." \
5269
+class Revisions(BaseCommand):
5271
+ Print a revision name based on a revision specifier
5273
+ def __init__(self):
5274
+ self.description="Lists revisions"
5276
+ def do_command(self, cmdargs):
5278
+ Master function that perfoms the "revision" command.
5280
+ (options, args) = self.get_parser().parse_args(cmdargs)
5282
+ raise cmdutil.GetHelp
5284
+ self.tree = arch.tree_root()
5285
+ except arch.errors.TreeRootError:
5288
+ iter = self.get_iterator(options.type, args, options.reverse,
5290
+ except cmdutil.CantDetermineRevision, e:
5291
+ raise CommandFailedWrapper(e)
5293
+ if options.skip is not None:
5294
+ iter = cmdutil.iter_skip(iter, int(options.skip))
5296
+ for revision in iter:
5298
+ if isinstance(revision, arch.Patchlog):
5300
+ revision=revision.revision
5301
+ print options.display(revision)
5302
+ if log is None and (options.summary or options.creator or
5303
+ options.date or options.merges):
5304
+ log = revision.patchlog
5305
+ if options.creator:
5306
+ print " %s" % log.creator
5308
+ print " %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
5309
+ if options.summary:
5310
+ print " %s" % log.summary
5311
+ if options.merges:
5312
+ showed_title = False
5313
+ for revision in log.merged_patches:
5314
+ if not showed_title:
5316
+ showed_title = True
5317
+ print " %s" % revision
5319
+ def get_iterator(self, type, args, reverse, modified):
5324
+ if modified is not None:
5325
+ iter = cmdutil.modified_iter(modified, self.tree)
5329
+ return cmdutil.iter_reverse(iter)
5330
+ elif type == "archive":
5332
+ if self.tree is None:
5333
+ raise cmdutil.CantDetermineRevision("",
5334
+ "Not in a project tree")
5335
+ version = cmdutil.determine_version_tree(spec, self.tree)
5337
+ version = cmdutil.determine_version_arch(spec, self.tree)
5338
+ cmdutil.ensure_archive_registered(version.archive)
5339
+ require_version_exists(version, spec)
5340
+ return version.iter_revisions(reverse)
5341
+ elif type == "cacherevs":
5343
+ if self.tree is None:
5344
+ raise cmdutil.CantDetermineRevision("",
5345
+ "Not in a project tree")
5346
+ version = cmdutil.determine_version_tree(spec, self.tree)
5348
+ version = cmdutil.determine_version_arch(spec, self.tree)
5349
+ cmdutil.ensure_archive_registered(version.archive)
5350
+ require_version_exists(version, spec)
5351
+ return cmdutil.iter_cacherevs(version, reverse)
5352
+ elif type == "library":
5354
+ if self.tree is None:
5355
+ raise cmdutil.CantDetermineRevision("",
5356
+ "Not in a project tree")
5357
+ version = cmdutil.determine_version_tree(spec, self.tree)
5359
+ version = cmdutil.determine_version_arch(spec, self.tree)
5360
+ return version.iter_library_revisions(reverse)
5361
+ elif type == "logs":
5362
+ if self.tree is None:
5363
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5364
+ return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
5365
+ self.tree), reverse)
5366
+ elif type == "missing" or type == "skip-present":
5367
+ if self.tree is None:
5368
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5369
+ skip = (type == "skip-present")
5370
+ version = cmdutil.determine_version_tree(spec, self.tree)
5371
+ cmdutil.ensure_archive_registered(version.archive)
5372
+ require_version_exists(version, spec)
5373
+ return cmdutil.iter_missing(self.tree, version, reverse,
5374
+ skip_present=skip)
5376
+ elif type == "present":
5377
+ if self.tree is None:
5378
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5379
+ version = cmdutil.determine_version_tree(spec, self.tree)
5380
+ cmdutil.ensure_archive_registered(version.archive)
5381
+ require_version_exists(version, spec)
5382
+ return cmdutil.iter_present(self.tree, version, reverse)
5384
+ elif type == "new-merges" or type == "direct-merges":
5385
+ if self.tree is None:
5386
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5387
+ version = cmdutil.determine_version_tree(spec, self.tree)
5388
+ cmdutil.ensure_archive_registered(version.archive)
5389
+ require_version_exists(version, spec)
5390
+ iter = cmdutil.iter_new_merges(self.tree, version, reverse)
5391
+ if type == "new-merges":
5393
+ elif type == "direct-merges":
5394
+ return cmdutil.direct_merges(iter)
5396
+ elif type == "missing-from":
5397
+ if self.tree is None:
5398
+ raise cmdutil.CantDetermineRevision("", "Not in a project tree")
5399
+ revision = cmdutil.determine_revision_tree(self.tree, spec)
5400
+ libtree = cmdutil.find_or_make_local_revision(revision)
5401
+ return cmdutil.iter_missing(libtree, self.tree.tree_version,
5404
+ elif type == "partner-missing":
5405
+ return cmdutil.iter_partner_missing(self.tree, reverse)
5407
+ elif type == "ancestry":
5408
+ revision = cmdutil.determine_revision_tree(self.tree, spec)
5409
+ iter = cmdutil._iter_ancestry(self.tree, revision)
5413
+ return cmdutil.iter_reverse(iter)
5415
+ elif type == "dependencies" or type == "non-dependencies":
5416
+ nondeps = (type == "non-dependencies")
5417
+ revision = cmdutil.determine_revision_tree(self.tree, spec)
5418
+ anc_iter = cmdutil._iter_ancestry(self.tree, revision)
5419
+ iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
5421
+ return iter_depends
5423
+ return cmdutil.iter_reverse(iter_depends)
5424
+ elif type == "micro":
5425
+ return cmdutil.iter_micro(self.tree)
5428
+ def get_parser(self):
5430
+ Returns the options parser to use for the "revision" command.
5432
+ :rtype: cmdutil.CmdOptionParser
5434
+ parser=cmdutil.CmdOptionParser("fai revisions [revision]")
5435
+ select = cmdutil.OptionGroup(parser, "Selection options",
5436
+ "Control which revisions are listed. These options"
5437
+ " are mutually exclusive. If more than one is"
5438
+ " specified, the last is used.")
5439
+ select.add_option("", "--archive", action="store_const",
5440
+ const="archive", dest="type", default="archive",
5441
+ help="List all revisions in the archive")
5442
+ select.add_option("", "--cacherevs", action="store_const",
5443
+ const="cacherevs", dest="type",
5444
+ help="List all revisions stored in the archive as "
5445
+ "complete copies")
5446
+ select.add_option("", "--logs", action="store_const",
5447
+ const="logs", dest="type",
5448
+ help="List revisions that have a patchlog in the "
5450
+ select.add_option("", "--missing", action="store_const",
5451
+ const="missing", dest="type",
5452
+ help="List revisions from the specified version that"
5453
+ " have no patchlog in the tree")
5454
+ select.add_option("", "--skip-present", action="store_const",
5455
+ const="skip-present", dest="type",
5456
+ help="List revisions from the specified version that"
5457
+ " have no patchlogs at all in the tree")
5458
+ select.add_option("", "--present", action="store_const",
5459
+ const="present", dest="type",
5460
+ help="List revisions from the specified version that"
5461
+ " have no patchlog in the tree, but can't be merged")
5462
+ select.add_option("", "--missing-from", action="store_const",
5463
+ const="missing-from", dest="type",
5464
+ help="List revisions from the specified revision "
5465
+ "that have no patchlog for the tree version")
5466
+ select.add_option("", "--partner-missing", action="store_const",
5467
+ const="partner-missing", dest="type",
5468
+ help="List revisions in partner versions that are"
5470
+ select.add_option("", "--new-merges", action="store_const",
5471
+ const="new-merges", dest="type",
5472
+ help="List revisions that have had patchlogs added"
5473
+ " to the tree since the last commit")
5474
+ select.add_option("", "--direct-merges", action="store_const",
5475
+ const="direct-merges", dest="type",
5476
+ help="List revisions that have been directly added"
5477
+ " to tree since the last commit ")
5478
+ select.add_option("", "--library", action="store_const",
5479
+ const="library", dest="type",
5480
+ help="List revisions in the revision library")
5481
+ select.add_option("", "--ancestry", action="store_const",
5482
+ const="ancestry", dest="type",
5483
+ help="List revisions that are ancestors of the "
5484
+ "current tree version")
5486
+ select.add_option("", "--dependencies", action="store_const",
5487
+ const="dependencies", dest="type",
5488
+ help="List revisions that the given revision "
5491
+ select.add_option("", "--non-dependencies", action="store_const",
5492
+ const="non-dependencies", dest="type",
5493
+ help="List revisions that the given revision "
5494
+ "does not depend on")
5496
+ select.add_option("--micro", action="store_const",
5497
+ const="micro", dest="type",
5498
+ help="List partner revisions aimed for this "
5501
+ select.add_option("", "--modified", dest="modified",
5502
+ help="List tree ancestor revisions that modified a "
5503
+ "given file", metavar="FILE[:LINE]")
5505
+ parser.add_option("", "--skip", dest="skip",
5506
+ help="Skip revisions. Positive numbers skip from "
5507
+ "beginning, negative skip from end.",
5510
+ parser.add_option_group(select)
5512
+ format = cmdutil.OptionGroup(parser, "Revision format options",
5513
+ "These control the appearance of listed revisions")
5514
+ format.add_option("", "--location", action="store_const",
5515
+ const=paths.determine_path, dest="display",
5516
+ help="Show location instead of name", default=str)
5517
+ format.add_option("--import", action="store_const",
5518
+ const=paths.determine_import_path, dest="display",
5519
+ help="Show location of import file")
5520
+ format.add_option("--log", action="store_const",
5521
+ const=paths.determine_log_path, dest="display",
5522
+ help="Show location of log file")
5523
+ format.add_option("--patch", action="store_const",
5524
+ dest="display", const=paths.determine_patch_path,
5525
+ help="Show location of patchfile")
5526
+ format.add_option("--continuation", action="store_const",
5527
+ const=paths.determine_continuation_path,
5529
+ help="Show location of continuation file")
5530
+ format.add_option("--cacherev", action="store_const",
5531
+ const=paths.determine_cacherev_path, dest="display",
5532
+ help="Show location of cacherev file")
5533
+ parser.add_option_group(format)
5534
+ display = cmdutil.OptionGroup(parser, "Display format options",
5535
+ "These control the display of data")
5536
+ display.add_option("-r", "--reverse", action="store_true",
5537
+ dest="reverse", help="Sort from newest to oldest")
5538
+ display.add_option("-s", "--summary", action="store_true",
5539
+ dest="summary", help="Show patchlog summary")
5540
+ display.add_option("-D", "--date", action="store_true",
5541
+ dest="date", help="Show patchlog date")
5542
+ display.add_option("-c", "--creator", action="store_true",
5543
+ dest="creator", help="Show the id that committed the"
5545
+ display.add_option("-m", "--merges", action="store_true",
5546
+ dest="merges", help="Show the revisions that were"
5548
+ parser.add_option_group(display)
5550
+ def help(self, parser=None):
5551
+ """Attempt to explain the revisions command
5553
+ :param parser: If supplied, used to determine options
5556
+ parser=self.get_parser()
5557
+ parser.print_help()
5558
+ print """List revisions.
5563
+class Get(BaseCommand):
5565
+ Retrieve a revision from the archive
5567
+ def __init__(self):
5568
+ self.description="Retrieve a revision from the archive"
5569
+ self.parser=self.get_parser()
5572
+ def get_completer(self, arg, index):
5576
+ tree = arch.tree_root()
5579
+ return cmdutil.iter_revision_completions(arg, tree)
5582
+ def do_command(self, cmdargs):
5584
+ Master function that perfoms the "get" command.
5586
+ (options, args) = self.parser.parse_args(cmdargs)
5588
+ return self.help()
5590
+ tree = arch.tree_root()
5591
+ except arch.errors.TreeRootError:
5596
+ revision, arch_loc = paths.full_path_decode(args[0])
5597
+ except Exception, e:
5598
+ revision = cmdutil.determine_revision_arch(tree, args[0],
5599
+ check_existence=False, allow_package=True)
5601
+ directory = args[1]
5603
+ directory = str(revision.nonarch)
5604
+ if os.path.exists(directory):
5605
+ raise DirectoryExists(directory)
5606
+ cmdutil.ensure_archive_registered(revision.archive, arch_loc)
5608
+ cmdutil.ensure_revision_exists(revision)
5609
+ except cmdutil.NoSuchRevision, e:
5610
+ raise CommandFailedWrapper(e)
5612
+ link = cmdutil.prompt ("get link")
5613
+ for line in cmdutil.iter_get(revision, directory, link,
5614
+ options.no_pristine,
5615
+ options.no_greedy_add):
5616
+ cmdutil.colorize(line)
5618
+ def get_parser(self):
5620
+ Returns the options parser to use for the "get" command.
5622
+ :rtype: cmdutil.CmdOptionParser
5624
+ parser=cmdutil.CmdOptionParser("fai get revision [dir]")
5625
+ parser.add_option("--no-pristine", action="store_true",
5626
+ dest="no_pristine",
5627
+ help="Do not make pristine copy for reference")
5628
+ parser.add_option("--no-greedy-add", action="store_true",
5629
+ dest="no_greedy_add",
5630
+ help="Never add to greedy libraries")
5634
+ def help(self, parser=None):
5636
+ Prints a help message.
5638
+ :param parser: If supplied, the parser to use for generating help. If \
5639
+ not supplied, it is retrieved.
5640
+ :type parser: cmdutil.CmdOptionParser
5643
+ parser=self.get_parser()
5644
+ parser.print_help()
5646
+Expands aliases and constructs a project tree for a revision. If the optional
5647
+"dir" argument is provided, the project tree will be stored in this directory.
5652
+class PromptCmd(cmd.Cmd):
5653
+ def __init__(self):
5654
+ cmd.Cmd.__init__(self)
5655
+ self.prompt = "Fai> "
5657
+ self.tree = arch.tree_root()
5662
+ self.fake_aba = abacmds.AbaCmds()
5663
+ self.identchars += '-'
5664
+ self.history_file = os.path.expanduser("~/.fai-history")
5665
+ readline.set_completer_delims(string.whitespace)
5666
+ if os.access(self.history_file, os.R_OK) and \
5667
+ os.path.isfile(self.history_file):
5668
+ readline.read_history_file(self.history_file)
5670
+ def write_history(self):
5671
+ readline.write_history_file(self.history_file)
5673
+ def do_quit(self, args):
5674
+ self.write_history()
5677
+ def do_exit(self, args):
5678
+ self.do_quit(args)
5680
+ def do_EOF(self, args):
5682
+ self.do_quit(args)
5684
+ def postcmd(self, line, bar):
5688
+ def set_prompt(self):
5689
+ if self.tree is not None:
5691
+ version = " "+self.tree.tree_version.nonarch
5696
+ self.prompt = "Fai%s> " % version
5698
+ def set_title(self, command=None):
5700
+ version = self.tree.tree_version.nonarch
5702
+ version = "[no version]"
5703
+ if command is None:
5705
+ sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
5707
+ def do_cd(self, line):
5711
+ os.chdir(os.path.expanduser(line))
5712
+ except Exception, e:
5715
+ self.tree = arch.tree_root()
5719
+ def do_help(self, line):
5722
+ def default(self, line):
5723
+ args = line.split()
5724
+ if find_command(args[0]):
5726
+ find_command(args[0]).do_command(args[1:])
5727
+ except cmdutil.BadCommandOption, e:
5729
+ except cmdutil.GetHelp, e:
5730
+ find_command(args[0]).help()
5731
+ except CommandFailed, e:
5733
+ except arch.errors.ArchiveNotRegistered, e:
5735
+ except KeyboardInterrupt, e:
5736
+ print "Interrupted"
5737
+ except arch.util.ExecProblem, e:
5738
+ print e.proc.error.rstrip('\n')
5739
+ except cmdutil.CantDetermineVersion, e:
5741
+ except cmdutil.CantDetermineRevision, e:
5743
+ except Exception, e:
5744
+ print "Unhandled error:\n%s" % cmdutil.exception_str(e)
5746
+ elif suggestions.has_key(args[0]):
5747
+ print suggestions[args[0]]
5749
+ elif self.fake_aba.is_command(args[0]):
5752
+ tree = arch.tree_root()
5753
+ except arch.errors.TreeRootError:
5755
+ cmd = self.fake_aba.is_command(args[0])
5757
+ cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
5758
+ except KeyboardInterrupt, e:
5759
+ print "Interrupted"
5761
+ elif options.tla_fallthrough and args[0] != "rm" and \
5762
+ cmdutil.is_tla_command(args[0]):
5766
+ tree = arch.tree_root()
5767
+ except arch.errors.TreeRootError:
5769
+ args = cmdutil.expand_prefix_alias(args, tree)
5770
+ arch.util.exec_safe('tla', args, stderr=sys.stderr,
5772
+ except arch.util.ExecProblem, e:
5774
+ except KeyboardInterrupt, e:
5775
+ print "Interrupted"
5779
+ tree = arch.tree_root()
5780
+ except arch.errors.TreeRootError:
5783
+ os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
5784
+ except KeyboardInterrupt, e:
5785
+ print "Interrupted"
5787
+ def completenames(self, text, line, begidx, endidx):
5789
+ iter = iter_command_names(self.fake_aba)
5792
+ arg = line.split()[-1]
5795
+ iter = iter_munged_completions(iter, arg, text)
5796
+ except Exception, e:
5800
+ def completedefault(self, text, line, begidx, endidx):
5801
+ """Perform completion for native commands.
5803
+ :param text: The text to complete
5805
+ :param line: The entire line to complete
5807
+ :param begidx: The start of the text in the line
5809
+ :param endidx: The end of the text in the line
5813
+ (cmd, args, foo) = self.parseline(line)
5814
+ command_obj=find_command(cmd)
5815
+ if command_obj is not None:
5816
+ return command_obj.complete(args.split(), text)
5817
+ elif not self.fake_aba.is_command(cmd) and \
5818
+ cmdutil.is_tla_command(cmd):
5819
+ iter = cmdutil.iter_supported_switches(cmd)
5821
+ arg = args.split()[-1]
5824
+ if arg.startswith("-"):
5825
+ return list(iter_munged_completions(iter, arg, text))
5827
+ return list(iter_munged_completions(
5828
+ iter_file_completions(arg), arg, text))
5833
+ arg = args.split()[-1]
5836
+ iter = iter_dir_completions(arg)
5837
+ iter = iter_munged_completions(iter, arg, text)
5840
+ arg = args.split()[-1]
5841
+ return list(iter_munged_completions(iter_file_completions(arg),
5844
+ return self.completenames(text, line, begidx, endidx)
5845
+ except Exception, e:
5849
+def iter_command_names(fake_aba):
5850
+ for entry in cmdutil.iter_combine([commands.iterkeys(),
5851
+ fake_aba.get_commands(),
5852
+ cmdutil.iter_tla_commands(False)]):
5853
+ if not suggestions.has_key(str(entry)):
5857
+def iter_file_completions(arg, only_dirs = False):
5858
+ """Generate an iterator that iterates through filename completions.
5860
+ :param arg: The filename fragment to match
5862
+ :param only_dirs: If true, match only directories
5863
+ :type only_dirs: bool
5867
+ extras = [".", ".."]
5870
+ (dir, file) = os.path.split(arg)
5872
+ listingdir = os.path.expanduser(dir)
5875
+ for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
5877
+ userfile = dir+'/'+file
5880
+ if userfile.startswith(arg):
5881
+ if os.path.isdir(listingdir+'/'+file):
5884
+ elif not only_dirs:
5887
+def iter_munged_completions(iter, arg, text):
5888
+ for completion in iter:
5889
+ completion = str(completion)
5890
+ if completion.startswith(arg):
5891
+ yield completion[len(arg)-len(text):]
5893
+def iter_source_file_completions(tree, arg):
5894
+ treepath = cmdutil.tree_cwd(tree)
5895
+ if len(treepath) > 0:
5899
+ for file in tree.iter_inventory(dirs, source=True, both=True):
5900
+ file = file_completion_match(file, treepath, arg)
5901
+ if file is not None:
5905
+def iter_untagged(tree, dirs):
5906
+ for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False,
5907
+ categories=arch_core.non_root,
5908
+ control_files=True):
5912
+def iter_untagged_completions(tree, arg):
5913
+ """Generate an iterator for all visible untagged files that match arg.
5915
+ :param tree: The tree to look for untagged files in
5916
+ :type tree: `arch.WorkingTree`
5917
+ :param arg: The argument to match
5919
+ :return: An iterator of all matching untagged files
5920
+ :rtype: iterator of str
5922
+ treepath = cmdutil.tree_cwd(tree)
5923
+ if len(treepath) > 0:
5928
+ for file in iter_untagged(tree, dirs):
5929
+ file = file_completion_match(file, treepath, arg)
5930
+ if file is not None:
5934
+def file_completion_match(file, treepath, arg):
5935
+ """Determines whether a file within an arch tree matches the argument.
5937
+ :param file: The rooted filename
5939
+ :param treepath: The path to the cwd within the tree
5940
+ :type treepath: str
5941
+ :param arg: The prefix to match
5942
+ :return: The completion name, or None if not a match
5945
+ if not file.startswith(treepath):
5947
+ if treepath != "":
5948
+ file = file[len(treepath)+1:]
5950
+ if not file.startswith(arg):
5952
+ if os.path.isdir(file):
5956
+def iter_modified_file_completions(tree, arg):
5957
+ """Returns a list of modified files that match the specified prefix.
5959
+ :param tree: The current tree
5960
+ :type tree: `arch.WorkingTree`
5961
+ :param arg: The prefix to match
5964
+ treepath = cmdutil.tree_cwd(tree)
5965
+ tmpdir = cmdutil.tmpdir()
5966
+ changeset = tmpdir+"/changeset"
5968
+ revision = cmdutil.determine_revision_tree(tree)
5969
+ for line in arch.iter_delta(revision, tree, changeset):
5970
+ if isinstance(line, arch.FileModification):
5971
+ file = file_completion_match(line.name[1:], treepath, arg)
5972
+ if file is not None:
5973
+ completions.append(file)
5974
+ shutil.rmtree(tmpdir)
5975
+ return completions
5977
+def iter_dir_completions(arg):
5978
+ """Generate an iterator that iterates through directory name completions.
5980
+ :param arg: The directory name fragment to match
5983
+ return iter_file_completions(arg, True)
5985
+class Shell(BaseCommand):
5986
+ def __init__(self):
5987
+ self.description = "Runs Fai as a shell"
5989
+ def do_command(self, cmdargs):
5990
+ if len(cmdargs)!=0:
5991
+ raise cmdutil.GetHelp
5992
+ prompt = PromptCmd()
5996
+ prompt.write_history()
5998
+class AddID(BaseCommand):
6000
+ Adds an inventory id for the given file
6002
+ def __init__(self):
6003
+ self.description="Add an inventory id for a given file"
6005
+ def get_completer(self, arg, index):
6006
+ tree = arch.tree_root()
6007
+ return iter_untagged_completions(tree, arg)
6009
+ def do_command(self, cmdargs):
6011
+ Master function that perfoms the "revision" command.
6013
+ parser=self.get_parser()
6014
+ (options, args) = parser.parse_args(cmdargs)
6016
+ tree = arch.tree_root()
6018
+ if (len(args) == 0) == (options.untagged == False):
6019
+ raise cmdutil.GetHelp
6021
+ #if options.id and len(args) != 1:
6022
+ # print "If --id is specified, only one file can be named."
6025
+ method = tree.tagging_method
6027
+ if options.id_type == "tagline":
6028
+ if method != "tagline":
6029
+ if not cmdutil.prompt("Tagline in other tree"):
6030
+ if method == "explicit":
6031
+ options.id_type == explicit
6033
+ print "add-id not supported for \"%s\" tagging method"\
6037
+ elif options.id_type == "explicit":
6038
+ if method != "tagline" and method != explicit:
6039
+ if not prompt("Explicit in other tree"):
6040
+ print "add-id not supported for \"%s\" tagging method" % \
6044
+ if options.id_type == "auto":
6045
+ if method != "tagline" and method != "explicit":
6046
+ print "add-id not supported for \"%s\" tagging method" % method
6049
+ options.id_type = method
6050
+ if options.untagged:
6052
+ self.add_ids(tree, options.id_type, args)
6054
+ def add_ids(self, tree, id_type, files=()):
6055
+ """Add inventory ids to files.
6057
+ :param tree: the tree the files are in
6058
+ :type tree: `arch.WorkingTree`
6059
+ :param id_type: the type of id to add: "explicit" or "tagline"
6060
+ :type id_type: str
6061
+ :param files: The list of files to add. If None do all untagged.
6062
+ :type files: tuple of str
6065
+ untagged = (files is None)
6067
+ files = list(iter_untagged(tree, None))
6068
+ previous_files = []
6069
+ while len(files) > 0:
6070
+ previous_files.extend(files)
6071
+ if id_type == "explicit":
6072
+ cmdutil.add_id(files)
6073
+ elif id_type == "tagline":
6074
+ for file in files:
6076
+ cmdutil.add_tagline_or_explicit_id(file)
6077
+ except cmdutil.AlreadyTagged:
6078
+ print "\"%s\" already has a tagline." % file
6079
+ except cmdutil.NoCommentSyntax:
6081
+ #do inventory after tagging until no untagged files are encountered
6084
+ for file in iter_untagged(tree, None):
6085
+ if not file in previous_files:
6086
+ files.append(file)
6091
+ def get_parser(self):
6093
+ Returns the options parser to use for the "revision" command.
6095
+ :rtype: cmdutil.CmdOptionParser
6097
+ parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
6098
+# ddaa suggests removing this to promote GUIDs. Let's see who squalks.
6099
+# parser.add_option("-i", "--id", dest="id",
6100
+# help="Specify id for a single file", default=None)
6101
+ parser.add_option("--tltl", action="store_true",
6102
+ dest="lord_style", help="Use Tom Lord's style of id.")
6103
+ parser.add_option("--explicit", action="store_const",
6104
+ const="explicit", dest="id_type",
6105
+ help="Use an explicit id", default="auto")
6106
+ parser.add_option("--tagline", action="store_const",
6107
+ const="tagline", dest="id_type",
6108
+ help="Use a tagline id")
6109
+ parser.add_option("--untagged", action="store_true",
6110
+ dest="untagged", default=False,
6111
+ help="tag all untagged files")
6114
+ def help(self, parser=None):
6116
+ Prints a help message.
6118
+ :param parser: If supplied, the parser to use for generating help. If \
6119
+ not supplied, it is retrieved.
6120
+ :type parser: cmdutil.CmdOptionParser
6123
+ parser=self.get_parser()
6124
+ parser.print_help()
6126
+Adds an inventory to the specified file(s) and directories. If --untagged is
6127
+specified, adds inventory to all untagged files and directories.
6132
+class Merge(BaseCommand):
6134
+ Merges changes from other versions into the current tree
6136
+ def __init__(self):
6137
+ self.description="Merges changes from other versions"
6139
+ self.tree = arch.tree_root()
6144
+ def get_completer(self, arg, index):
6145
+ if self.tree is None:
6146
+ raise arch.errors.TreeRootError
6147
+ completions = list(ancillary.iter_partners(self.tree,
6148
+ self.tree.tree_version))
6149
+ if len(completions) == 0:
6150
+ completions = list(self.tree.iter_log_versions())
6154
+ for completion in completions:
6155
+ alias = ancillary.compact_alias(str(completion), self.tree)
6157
+ aliases.extend(alias)
6159
+ for completion in completions:
6160
+ if completion.archive == self.tree.tree_version.archive:
6161
+ aliases.append(completion.nonarch)
6163
+ except Exception, e:
6166
+ completions.extend(aliases)
6167
+ return completions
6169
+ def do_command(self, cmdargs):
6171
+ Master function that perfoms the "merge" command.
6173
+ parser=self.get_parser()
6174
+ (options, args) = parser.parse_args(cmdargs)
6176
+ action="star-merge"
6178
+ action = options.action
6180
+ if self.tree is None:
6181
+ raise arch.errors.TreeRootError(os.getcwd())
6182
+ if cmdutil.has_changed(self.tree.tree_version):
6183
+ raise UncommittedChanges(self.tree)
6188
+ revisions.append(cmdutil.determine_revision_arch(self.tree,
6190
+ source = "from commandline"
6192
+ revisions = ancillary.iter_partner_revisions(self.tree,
6193
+ self.tree.tree_version)
6194
+ source = "from partner version"
6195
+ revisions = misc.rewind_iterator(revisions)
6198
+ revisions.rewind()
6199
+ except StopIteration, e:
6200
+ revision = cmdutil.tag_cur(self.tree)
6201
+ if revision is None:
6202
+ raise CantDetermineRevision("", "No version specified, no "
6203
+ "partner-versions, and no tag"
6205
+ revisions = [revision]
6206
+ source = "from tag source"
6207
+ for revision in revisions:
6208
+ cmdutil.ensure_archive_registered(revision.archive)
6209
+ cmdutil.colorize(arch.Chatter("* Merging %s [%s]" %
6210
+ (revision, source)))
6211
+ if action=="native-merge" or action=="update":
6212
+ if self.native_merge(revision, action) == 0:
6214
+ elif action=="star-merge":
6216
+ self.star_merge(revision, options.diff3)
6217
+ except errors.MergeProblem, e:
6219
+ if cmdutil.has_changed(self.tree.tree_version):
6222
+ def star_merge(self, revision, diff3):
6223
+ """Perform a star-merge on the current tree.
6225
+ :param revision: The revision to use for the merge
6226
+ :type revision: `arch.Revision`
6227
+ :param diff3: If true, do a diff3 merge
6231
+ for line in self.tree.iter_star_merge(revision, diff3=diff3):
6232
+ cmdutil.colorize(line)
6233
+ except arch.util.ExecProblem, e:
6234
+ if e.proc.status is not None and e.proc.status == 1:
6236
+ print e.proc.error
6237
+ raise MergeProblem
6241
+ def native_merge(self, other_revision, action):
6242
+ """Perform a native-merge on the current tree.
6244
+ :param other_revision: The revision to use for the merge
6245
+ :type other_revision: `arch.Revision`
6246
+ :return: 0 if the merge was skipped, 1 if it was applied
6248
+ other_tree = cmdutil.find_or_make_local_revision(other_revision)
6250
+ if action == "native-merge":
6251
+ ancestor = cmdutil.merge_ancestor2(self.tree, other_tree,
6253
+ elif action == "update":
6254
+ ancestor = cmdutil.tree_latest(self.tree,
6255
+ other_revision.version)
6256
+ except CantDetermineRevision, e:
6257
+ raise CommandFailedWrapper(e)
6258
+ cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
6259
+ if (ancestor == other_revision):
6260
+ cmdutil.colorize(arch.Chatter("* Skipping redundant merge"
6263
+ delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)
6264
+ for line in cmdutil.iter_apply_delta_filter(delta):
6265
+ cmdutil.colorize(line)
6270
+ def get_parser(self):
6272
+ Returns the options parser to use for the "merge" command.
6274
+ :rtype: cmdutil.CmdOptionParser
6276
+ parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
6277
+ parser.add_option("-s", "--star-merge", action="store_const",
6278
+ dest="action", help="Use star-merge",
6279
+ const="star-merge", default="native-merge")
6280
+ parser.add_option("--update", action="store_const",
6281
+ dest="action", help="Use update picker",
6283
+ parser.add_option("--diff3", action="store_true",
6285
+ help="Use diff3 for merge (implies star-merge)")
6288
+ def help(self, parser=None):
6290
+ Prints a help message.
6292
+ :param parser: If supplied, the parser to use for generating help. If \
6293
+ not supplied, it is retrieved.
6294
+ :type parser: cmdutil.CmdOptionParser
6297
+ parser=self.get_parser()
6298
+ parser.print_help()
6300
+Performs a merge operation using the specified version.
6304
+class ELog(BaseCommand):
6306
+ Produces a raw patchlog and invokes the user's editor
6308
+ def __init__(self):
6309
+ self.description="Edit a patchlog to commit"
6311
+ self.tree = arch.tree_root()
6316
+ def do_command(self, cmdargs):
6318
+ Master function that perfoms the "elog" command.
6320
+ parser=self.get_parser()
6321
+ (options, args) = parser.parse_args(cmdargs)
6322
+ if self.tree is None:
6323
+ raise arch.errors.TreeRootError
6325
+ edit_log(self.tree)
6327
+ def get_parser(self):
6329
+ Returns the options parser to use for the "merge" command.
6331
+ :rtype: cmdutil.CmdOptionParser
6333
+ parser=cmdutil.CmdOptionParser("fai elog")
6337
+ def help(self, parser=None):
6339
+ Invokes $EDITOR to produce a log for committing.
6341
+ :param parser: If supplied, the parser to use for generating help. If \
6342
+ not supplied, it is retrieved.
6343
+ :type parser: cmdutil.CmdOptionParser
6346
+ parser=self.get_parser()
6347
+ parser.print_help()
6349
+Invokes $EDITOR to produce a log for committing.
6353
+def edit_log(tree):
6354
+ """Makes and edits the log for a tree. Does all kinds of fancy things
6355
+ like log templates and merge summaries and log-for-merge
6357
+ :param tree: The tree to edit the log for
6358
+ :type tree: `arch.WorkingTree`
6360
+ #ensure we have an editor before preparing the log
6361
+ cmdutil.find_editor()
6362
+ log = tree.log_message(create=False)
6363
+ log_is_new = False
6364
+ if log is None or cmdutil.prompt("Overwrite log"):
6365
+ if log is not None:
6366
+ os.remove(log.name)
6367
+ log = tree.log_message(create=True)
6370
+ template = tree+"/{arch}/=log-template"
6371
+ if not os.path.exists(template):
6372
+ template = os.path.expanduser("~/.arch-params/=log-template")
6373
+ if not os.path.exists(template):
6376
+ shutil.copyfile(template, tmplog)
6378
+ new_merges = list(cmdutil.iter_new_merges(tree,
6379
+ tree.tree_version))
6380
+ log["Summary"] = merge_summary(new_merges, tree.tree_version)
6381
+ if len(new_merges) > 0:
6382
+ if cmdutil.prompt("Log for merge"):
6383
+ mergestuff = cmdutil.log_for_merge(tree)
6384
+ log.description += mergestuff
6387
+ cmdutil.invoke_editor(log.name)
6390
+ os.remove(log.name)
6393
+def merge_summary(new_merges, tree_version):
6394
+ if len(new_merges) == 0:
6396
+ if len(new_merges) == 1:
6397
+ summary = new_merges[0].summary
6402
+ for merge in new_merges:
6403
+ if arch.my_id() != merge.creator:
6404
+ name = re.sub("<.*>", "", merge.creator).rstrip(" ");
6405
+ if not name in credits:
6406
+ credits.append(name)
6408
+ version = merge.revision.version
6409
+ if version.archive == tree_version.archive:
6410
+ if not version.nonarch in credits:
6411
+ credits.append(version.nonarch)
6412
+ elif not str(version) in credits:
6413
+ credits.append(str(version))
6415
+ return ("%s (%s)") % (summary, ", ".join(credits))
6417
+class MirrorArchive(BaseCommand):
6419
+ Updates a mirror from an archive
6421
+ def __init__(self):
6422
+ self.description="Update a mirror from an archive"
6424
+ def do_command(self, cmdargs):
6426
+ Master function that perfoms the "revision" command.
6429
+ parser=self.get_parser()
6430
+ (options, args) = parser.parse_args(cmdargs)
6434
+ tree = arch.tree_root()
6438
+ if len(args) == 0:
6439
+ if tree is not None:
6440
+ name = tree.tree_version()
6442
+ name = cmdutil.expand_alias(args[0], tree)
6443
+ name = arch.NameParser(name)
6445
+ to_arch = name.get_archive()
6446
+ from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
6447
+ limit = name.get_nonarch()
6449
+ iter = arch_core.mirror_archive(from_arch,to_arch, limit)
6450
+ for line in arch.chatter_classifier(iter):
6451
+ cmdutil.colorize(line)
6453
+ def get_parser(self):
6455
+ Returns the options parser to use for the "revision" command.
6457
+ :rtype: cmdutil.CmdOptionParser
6459
+ parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
6462
+ def help(self, parser=None):
6464
+ Prints a help message.
6466
+ :param parser: If supplied, the parser to use for generating help. If \
6467
+ not supplied, it is retrieved.
6468
+ :type parser: cmdutil.CmdOptionParser
6471
+ parser=self.get_parser()
6472
+ parser.print_help()
6474
+Updates a mirror from an archive. If a branch, package, or version is
6475
+supplied, only changes under it are mirrored.
6479
+def help_tree_spec():
6480
+ print """Specifying revisions (default: tree)
6481
+Revisions may be specified by alias, revision, version or patchlevel.
6482
+Revisions or versions may be fully qualified. Unqualified revisions, versions,
6483
+or patchlevels use the archive of the current project tree. Versions will
6484
+use the latest patchlevel in the tree. Patchlevels will use the current tree-
6487
+Use "alias" to list available (user and automatic) aliases."""
6489
+def help_aliases(tree):
6490
+ print """Auto-generated aliases
6491
+ acur : The latest revision in the archive of the tree-version. You can specfy
6492
+ a different version like so: acur:foo--bar--0 (aliases can be used)
6493
+ tcur : (tree current) The latest revision in the tree of the tree-version.
6494
+ You can specify a different version like so: tcur:foo--bar--0 (aliases
6496
+tprev : (tree previous) The previous revision in the tree of the tree-version.
6497
+ To specify an older revision, use a number, e.g. "tprev:4"
6498
+ tanc : (tree ancestor) The ancestor revision of the tree
6499
+ To specify an older revision, use a number, e.g. "tanc:4"
6500
+tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
6501
+ tmod : (tree modified) The latest revision to modify a given file
6502
+ (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
6503
+ ttag : (tree tag) The revision that was tagged into the current tree revision,
6504
+ according to the tree.
6505
+tagcur: (tag current) The latest revision of the version that the current tree
6507
+mergeanc : The common ancestor of the current tree and the specified revision.
6508
+ Defaults to the first partner-version's latest revision or to tagcur.
6510
+ print "User aliases"
6511
+ for parts in ancillary.iter_all_alias(tree):
6512
+ print parts[0].rjust(10)+" : "+parts[1]
6515
+class Inventory(BaseCommand):
6516
+ """List the status of files in the tree"""
6517
+ def __init__(self):
6518
+ self.description=self.__doc__
6520
+ def do_command(self, cmdargs):
6522
+ Master function that perfoms the "revision" command.
6525
+ parser=self.get_parser()
6526
+ (options, args) = parser.parse_args(cmdargs)
6527
+ tree = arch.tree_root()
6530
+ if (options.source):
6531
+ categories.append(arch_core.SourceFile)
6532
+ if (options.precious):
6533
+ categories.append(arch_core.PreciousFile)
6534
+ if (options.backup):
6535
+ categories.append(arch_core.BackupFile)
6536
+ if (options.junk):
6537
+ categories.append(arch_core.JunkFile)
6539
+ if len(categories) == 1:
6540
+ show_leading = False
6542
+ show_leading = True
6544
+ if len(categories) == 0:
6547
+ if options.untagged:
6548
+ categories = arch_core.non_root
6549
+ show_leading = False
6554
+ for file in arch_core.iter_inventory_filter(tree, None,
6555
+ control_files=options.control_files,
6556
+ categories = categories, tagged=tagged):
6557
+ print arch_core.file_line(file,
6558
+ category = show_leading,
6559
+ untagged = show_leading,
6562
+ def get_parser(self):
6564
+ Returns the options parser to use for the "revision" command.
6566
+ :rtype: cmdutil.CmdOptionParser
6568
+ parser=cmdutil.CmdOptionParser("fai inventory [options]")
6569
+ parser.add_option("--ids", action="store_true", dest="ids",
6570
+ help="Show file ids")
6571
+ parser.add_option("--control", action="store_true",
6572
+ dest="control_files", help="include control files")
6573
+ parser.add_option("--source", action="store_true", dest="source",
6574
+ help="List source files")
6575
+ parser.add_option("--backup", action="store_true", dest="backup",
6576
+ help="List backup files")
6577
+ parser.add_option("--precious", action="store_true", dest="precious",
6578
+ help="List precious files")
6579
+ parser.add_option("--junk", action="store_true", dest="junk",
6580
+ help="List junk files")
6581
+ parser.add_option("--unrecognized", action="store_true",
6582
+ dest="unrecognized", help="List unrecognized files")
6583
+ parser.add_option("--untagged", action="store_true",
6584
+ dest="untagged", help="List only untagged files")
6587
+ def help(self, parser=None):
6589
+ Prints a help message.
6591
+ :param parser: If supplied, the parser to use for generating help. If \
6592
+ not supplied, it is retrieved.
6593
+ :type parser: cmdutil.CmdOptionParser
6596
+ parser=self.get_parser()
6597
+ parser.print_help()
6599
+Lists the status of files in the archive:
6607
+Leading letter are not displayed if only one kind of file is shown
6612
+class Alias(BaseCommand):
6613
+ """List or adjust aliases"""
6614
+ def __init__(self):
6615
+ self.description=self.__doc__
6617
+ def get_completer(self, arg, index):
6621
+ self.tree = arch.tree_root()
6626
+ return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
6628
+ return cmdutil.iter_revision_completions(arg, self.tree)
6631
+ def do_command(self, cmdargs):
6633
+ Master function that perfoms the "revision" command.
6636
+ parser=self.get_parser()
6637
+ (options, args) = parser.parse_args(cmdargs)
6639
+ self.tree = arch.tree_root()
6645
+ options.action(args, options)
6646
+ except cmdutil.ForbiddenAliasSyntax, e:
6647
+ raise CommandFailedWrapper(e)
6649
+ def arg_dispatch(self, args, options):
6650
+ """Add, modify, or list aliases, depending on number of arguments
6652
+ :param args: The list of commandline arguments
6653
+ :type args: list of str
6654
+ :param options: The commandline options
6656
+ if len(args) == 0:
6657
+ help_aliases(self.tree)
6659
+ elif len(args) == 1:
6660
+ self.print_alias(args[0])
6661
+ elif (len(args)) == 2:
6662
+ self.add(args[0], args[1], options)
6664
+ raise cmdutil.GetHelp
6666
+ def print_alias(self, alias):
6668
+ for pair in ancillary.iter_all_alias(self.tree):
6669
+ if pair[0] == alias:
6671
+ if answer is not None:
6674
+ print "The alias %s is not assigned." % alias
6676
+ def add(self, alias, expansion, options):
6677
+ """Add or modify aliases
6679
+ :param alias: The alias name to create/modify
6681
+ :param expansion: The expansion to assign to the alias name
6682
+ :type expansion: str
6683
+ :param options: The commandline options
6687
+ new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion,
6689
+ ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
6691
+ for pair in self.get_iterator(options):
6692
+ if pair[0] != alias:
6693
+ newlist+="%s=%s\n" % (pair[0], pair[1])
6699
+ self.write_aliases(newlist, options)
6701
+ def delete(self, args, options):
6702
+ """Delete the specified alias
6704
+ :param args: The list of arguments
6705
+ :type args: list of str
6706
+ :param options: The commandline options
6709
+ if len(args) != 1:
6710
+ raise cmdutil.GetHelp
6712
+ for pair in self.get_iterator(options):
6713
+ if pair[0] != args[0]:
6714
+ newlist+="%s=%s\n" % (pair[0], pair[1])
6718
+ raise errors.NoSuchAlias(args[0])
6719
+ self.write_aliases(newlist, options)
6721
+ def get_alias_file(self, options):
6722
+ """Return the name of the alias file to use
6724
+ :param options: The commandline options
6727
+ if self.tree is None:
6728
+ self.tree == arch.tree_root()
6729
+ return str(self.tree)+"/{arch}/+aliases"
6731
+ return "~/.aba/aliases"
6733
+ def get_iterator(self, options):
6734
+ """Return the alias iterator to use
6736
+ :param options: The commandline options
6738
+ return ancillary.iter_alias(self.get_alias_file(options))
6740
+ def write_aliases(self, newlist, options):
6741
+ """Safely rewrite the alias file
6742
+ :param newlist: The new list of aliases
6743
+ :type newlist: str
6744
+ :param options: The commandline options
6746
+ filename = os.path.expanduser(self.get_alias_file(options))
6747
+ file = cmdutil.NewFileVersion(filename)
6748
+ file.write(newlist)
6752
+ def get_parser(self):
6754
+ Returns the options parser to use for the "alias" command.
6756
+ :rtype: cmdutil.CmdOptionParser
6758
+ parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
6759
+ parser.add_option("-d", "--delete", action="store_const", dest="action",
6760
+ const=self.delete, default=self.arg_dispatch,
6761
+ help="Delete an alias")
6762
+ parser.add_option("--tree", action="store_true", dest="tree",
6763
+ help="Create a per-tree alias", default=False)
6766
+ def help(self, parser=None):
6768
+ Prints a help message.
6770
+ :param parser: If supplied, the parser to use for generating help. If \
6771
+ not supplied, it is retrieved.
6772
+ :type parser: cmdutil.CmdOptionParser
6775
+ parser=self.get_parser()
6776
+ parser.print_help()
6778
+Lists current aliases or modifies the list of aliases.
6780
+If no arguments are supplied, aliases will be listed. If two arguments are
6781
+supplied, the specified alias will be created or modified. If -d or --delete
6782
+is supplied, the specified alias will be deleted.
6784
+You can create aliases that refer to any fully-qualified part of the
6785
+Arch namespace, e.g.
6788
+archive/category--branch,
6789
+archive/category--branch--version (my favourite)
6790
+archive/category--branch--version--patchlevel
6792
+Aliases can be used automatically by native commands. To use them
6793
+with external or tla commands, prefix them with ^ (you can do this
6794
+with native commands, too).
6798
+class RequestMerge(BaseCommand):
6799
+ """Submit a merge request to Bug Goo"""
6800
+ def __init__(self):
6801
+ self.description=self.__doc__
6803
+ def do_command(self, cmdargs):
6804
+ """Submit a merge request
6806
+ :param cmdargs: The commandline arguments
6807
+ :type cmdargs: list of str
6809
+ cmdutil.find_editor()
6810
+ parser = self.get_parser()
6811
+ (options, args) = parser.parse_args(cmdargs)
6813
+ self.tree=arch.tree_root()
6816
+ base, revisions = self.revision_specs(args)
6817
+ message = self.make_headers(base, revisions)
6818
+ message += self.make_summary(revisions)
6819
+ path = self.edit_message(message)
6820
+ message = self.tidy_message(path)
6821
+ if cmdutil.prompt("Send merge"):
6822
+ self.send_message(message)
6823
+ print "Merge request sent"
6825
+ def make_headers(self, base, revisions):
6826
+ """Produce email and Bug Goo header strings
6828
+ :param base: The base revision to apply merges to
6829
+ :type base: `arch.Revision`
6830
+ :param revisions: The revisions to replay into the base
6831
+ :type revisions: list of `arch.Patchlog`
6832
+ :return: The headers
6835
+ headers = "To: gnu-arch-users@gnu.org\n"
6836
+ headers += "From: %s\n" % options.fromaddr
6837
+ if len(revisions) == 1:
6838
+ headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
6840
+ headers += "Subject: [MERGE REQUEST]\n"
6842
+ headers += "Base-Revision: %s\n" % base
6843
+ for revision in revisions:
6844
+ headers += "Revision: %s\n" % revision.revision
6845
+ headers += "Bug: \n\n"
6848
+ def make_summary(self, logs):
6849
+ """Generate a summary of merges
6851
+ :param logs: the patchlogs that were directly added by the merges
6852
+ :type logs: list of `arch.Patchlog`
6853
+ :return: the summary
6858
+ summary+=str(log.revision)+"\n"
6859
+ summary+=log.summary+"\n"
6860
+ if log.description.strip():
6861
+ summary+=log.description.strip('\n')+"\n\n"
6864
+ def revision_specs(self, args):
6865
+ """Determine the base and merge revisions from tree and arguments.
6867
+ :param args: The parsed arguments
6868
+ :type args: list of str
6869
+ :return: The base revision and merge revisions
6870
+ :rtype: `arch.Revision`, list of `arch.Patchlog`
6873
+ target_revision = cmdutil.determine_revision_arch(self.tree,
6876
+ target_revision = cmdutil.tree_latest(self.tree)
6878
+ merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
6879
+ self.tree, f)) for f in args[1:] ]
6881
+ if self.tree is None:
6882
+ raise CantDetermineRevision("", "Not in a project tree")
6883
+ merge_iter = cmdutil.iter_new_merges(self.tree,
6884
+ target_revision.version,
6886
+ merges = [f for f in cmdutil.direct_merges(merge_iter)]
6887
+ return (target_revision, merges)
6889
+ def edit_message(self, message):
6890
+ """Edit an email message in the user's standard editor
6892
+ :param message: The message to edit
6893
+ :type message: str
6894
+ :return: the path of the edited message
6897
+ if self.tree is None:
6898
+ path = os.get_cwd()
6901
+ path += "/,merge-request"
6902
+ file = open(path, 'w')
6903
+ file.write(message)
6905
+ cmdutil.invoke_editor(path)
6908
+ def tidy_message(self, path):
6909
+ """Validate and clean up message.
6911
+ :param path: The path to the message to clean up
6913
+ :return: The parsed message
6914
+ :rtype: `email.Message`
6916
+ mail = email.message_from_file(open(path))
6917
+ if mail["Subject"].strip() == "[MERGE REQUEST]":
6918
+ raise BlandSubject
6920
+ request = email.message_from_string(mail.get_payload())
6921
+ if request.has_key("Bug"):
6922
+ if request["Bug"].strip()=="":
6923
+ del request["Bug"]
6924
+ mail.set_payload(request.as_string())
6927
+ def send_message(self, message):
6928
+ """Send a message, using its headers to address it.
6930
+ :param message: The message to send
6931
+ :type message: `email.Message`"""
6932
+ server = smtplib.SMTP()
6933
+ server.sendmail(message['From'], message['To'], message.as_string())
6936
+ def help(self, parser=None):
6937
+ """Print a usage message
6939
+ :param parser: The options parser to use
6940
+ :type parser: `cmdutil.CmdOptionParser`
6942
+ if parser is None:
6943
+ parser = self.get_parser()
6944
+ parser.print_help()
6946
+Sends a merge request formatted for Bug Goo. Intended use: get the tree
6947
+you'd like to merge into. Apply the merges you want. Invoke request-merge.
6948
+The merge request will open in your $EDITOR.
6950
+When no TARGET is specified, it uses the current tree revision. When
6951
+no MERGE is specified, it uses the direct merges (as in "revisions
6952
+--direct-merges"). But you can specify just the TARGET, or all the MERGE
6956
+ def get_parser(self):
6957
+ """Produce a commandline parser for this command.
6959
+ :rtype: `cmdutil.CmdOptionParser`
6961
+ parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
6965
+'changes' : Changes,
6968
+'apply-changes':ApplyChanges,
6971
+'revision': Revision,
6972
+'revisions': Revisions,
6979
+'mirror-archive': MirrorArchive,
6980
+'ninventory': Inventory,
6982
+'request-merge': RequestMerge,
6985
+'apply-delta' : "Try \"apply-changes\".",
6986
+'delta' : "To compare two revisions, use \"changes\".",
6987
+'diff-rev' : "To compare two revisions, use \"changes\".",
6988
+'undo' : "To undo local changes, use \"revert\".",
6989
+'undelete' : "To undo only deletions, use \"revert --deletions\"",
6990
+'missing-from' : "Try \"revisions --missing-from\".",
6991
+'missing' : "Try \"revisions --missing\".",
6992
+'missing-merge' : "Try \"revisions --partner-missing\".",
6993
+'new-merges' : "Try \"revisions --new-merges\".",
6994
+'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
6995
+'logs' : "Try \"revisions --logs\"",
6996
+'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
6997
+'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
6998
+'change-version' : "Try \"update REVISION\"",
6999
+'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
7000
+'rev-depends' : "Use revisions --dependencies",
7001
+'auto-get' : "Plain get will do archive lookups",
7002
+'tagline' : "Use add-id. It uses taglines in tagline trees",
7003
+'emlog' : "Use elog. It automatically adds log-for-merge text, if any",
7004
+'library-revisions' : "Use revisions --library",
7005
+'file-revert' : "Use revert FILE"
7007
+# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
7009
*** modified file 'bzrlib/branch.py'
7010
--- bzrlib/branch.py
7011
+++ bzrlib/branch.py
7013
from revision import Revision
7014
from errors import bailout, BzrError
7015
from textui import show_status
7017
+from bzrlib import progress
7019
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
7020
## TODO: Maybe include checks for common corruption of newlines, etc?
7021
@@ -802,3 +804,36 @@
7023
s = hexlify(rand_bytes(8))
7024
return '-'.join((name, compact_date(time.time()), s))
7027
+def iter_anno_data(branch, file_id):
7028
+ later_revision = branch.revno()
7029
+ q = range(branch.revno())
7031
+ later_text_id = branch.basis_tree().inventory[file_id].text_id
7035
+ cur_tree = branch.revision_tree(branch.lookup_revision(revno))
7036
+ if file_id not in cur_tree.inventory:
7039
+ text_id = cur_tree.inventory[file_id].text_id
7040
+ if text_id != later_text_id:
7041
+ patch = get_patch(branch, revno, later_revision, file_id)
7042
+ yield revno, patch.iter_inserted(), patch
7043
+ later_revision = revno
7044
+ later_text_id = text_id
7045
+ yield progress.Progress("revisions", i)
7047
+def get_patch(branch, old_revno, new_revno, file_id):
7048
+ old_tree = branch.revision_tree(branch.lookup_revision(old_revno))
7049
+ new_tree = branch.revision_tree(branch.lookup_revision(new_revno))
7050
+ if file_id in old_tree.inventory:
7051
+ old_file = old_tree.get_file(file_id).readlines()
7054
+ ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
7055
+ return patches.parse_patch(ud)
7059
*** modified file 'bzrlib/commands.py'
7060
--- bzrlib/commands.py
7061
+++ bzrlib/commands.py
7063
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
7065
from bzrlib import merge
7066
+from bzrlib.branch import iter_anno_data
7067
+from bzrlib import patches
7068
+from bzrlib import progress
7071
def _squish_command_name(cmd):
7072
@@ -882,7 +885,15 @@
7073
print '%3d FAILED!' % mf
7077
+ result = bzrlib.patches.test()
7078
+ resultFailed = len(result.errors) + len(result.failures)
7079
+ print '%-40s %3d tests' % ('bzrlib.patches', result.testsRun),
7081
+ print '%3d FAILED!' % resultFailed
7084
+ tests += result.testsRun
7085
+ failures += resultFailed
7086
print '%-40s %3d tests' % ('total', tests),
7088
print '%3d FAILED!' % failures
7089
@@ -897,6 +908,27 @@
7090
"""Show version of bzr"""
7094
+class cmd_annotate(Command):
7095
+ """Show which revision added each line in a file"""
7097
+ takes_args = ['filename']
7098
+ def run(self, filename):
7099
+ branch = (Branch(filename))
7100
+ file_id = branch.working_tree().path2id(filename)
7101
+ lines = branch.basis_tree().get_file(file_id)
7102
+ total = branch.revno()
7103
+ anno_d_iter = iter_anno_data(branch, file_id)
7104
+ for result in patches.iter_annotate_file(lines, anno_d_iter):
7105
+ if isinstance(result, progress.Progress):
7106
+ result.total = total
7107
+ progress.progress_bar(result)
7109
+ progress.clear_progress_bar()
7110
+ anno_lines = result
7111
+ for line in anno_lines:
7112
+ sys.stdout.write("%4s:%s" % (str(line.log), line.text))
7116
print "bzr (bazaar-ng) %s" % bzrlib.__version__