~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to testdata/diff

  • Committer: Aaron Bentley
  • Date: 2012-01-20 02:07:15 UTC
  • Revision ID: aaron@aaronbentley.com-20120120020715-ar6jbqnrjcuebggz
Tags: release-2.5
Update for 2.5 release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
--- orig/commands.py
2
 
+++ mod/commands.py
3
 
@@ -19,25 +19,31 @@
4
 
 import arch
5
 
 import arch.util
6
 
 import arch.arch
7
 
+
8
 
+import pylon.errors
9
 
+from pylon.errors import *
10
 
+from pylon import errors
11
 
+from pylon import util
12
 
+from pylon import arch_core
13
 
+from pylon import arch_compound
14
 
+from pylon import ancillary
15
 
+from pylon import misc
16
 
+from pylon import paths 
17
 
+
18
 
 import abacmds
19
 
 import cmdutil
20
 
 import shutil
21
 
 import os
22
 
 import options
23
 
-import paths 
24
 
 import time
25
 
 import cmd
26
 
 import readline
27
 
 import re
28
 
 import string
29
 
-import arch_core
30
 
-from errors import *
31
 
-import errors
32
 
 import terminal
33
 
-import ancillary
34
 
-import misc
35
 
 import email
36
 
 import smtplib
37
 
+import textwrap
38
 
 
39
 
 __docformat__ = "restructuredtext"
40
 
 __doc__ = "Implementation of user (sub) commands"
41
 
@@ -257,7 +263,7 @@
42
 
 
43
 
         tree=arch.tree_root()
44
 
         if len(args) == 0:
45
 
-            a_spec = cmdutil.comp_revision(tree)
46
 
+            a_spec = ancillary.comp_revision(tree)
47
 
         else:
48
 
             a_spec = cmdutil.determine_revision_tree(tree, args[0])
49
 
         cmdutil.ensure_archive_registered(a_spec.archive)
50
 
@@ -284,7 +290,7 @@
51
 
             changeset=options.changeset
52
 
             tmpdir = None
53
 
         else:
54
 
-            tmpdir=cmdutil.tmpdir()
55
 
+            tmpdir=util.tmpdir()
56
 
             changeset=tmpdir+"/changeset"
57
 
         try:
58
 
             delta=arch.iter_delta(a_spec, b_spec, changeset)
59
 
@@ -304,14 +310,14 @@
60
 
             if status > 1:
61
 
                 return
62
 
             if (options.perform_diff):
63
 
-                chan = cmdutil.ChangesetMunger(changeset)
64
 
+                chan = arch_compound.ChangesetMunger(changeset)
65
 
                 chan.read_indices()
66
 
-                if isinstance(b_spec, arch.Revision):
67
 
-                    b_dir = b_spec.library_find()
68
 
-                else:
69
 
-                    b_dir = b_spec
70
 
-                a_dir = a_spec.library_find()
71
 
                 if options.diffopts is not None:
72
 
+                    if isinstance(b_spec, arch.Revision):
73
 
+                        b_dir = b_spec.library_find()
74
 
+                    else:
75
 
+                        b_dir = b_spec
76
 
+                    a_dir = a_spec.library_find()
77
 
                     diffopts = options.diffopts.split()
78
 
                     cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
79
 
                 else:
80
 
@@ -517,7 +523,7 @@
81
 
         except arch.errors.TreeRootError, e:
82
 
             print e
83
 
             return
84
 
-        from_revision=cmdutil.tree_latest(tree)
85
 
+        from_revision = arch_compound.tree_latest(tree)
86
 
         if from_revision==to_revision:
87
 
             print "Tree is already up to date with:\n"+str(to_revision)+"."
88
 
             return
89
 
@@ -592,6 +598,9 @@
90
 
 
91
 
         if len(args) == 0:
92
 
             args = None
93
 
+        if options.version is None:
94
 
+            return options, tree.tree_version, args
95
 
+
96
 
         revision=cmdutil.determine_revision_arch(tree, options.version)
97
 
         return options, revision.get_version(), args
98
 
 
99
 
@@ -601,11 +610,16 @@
100
 
         """
101
 
         tree=arch.tree_root()
102
 
         options, version, files = self.parse_commandline(cmdargs, tree)
103
 
+        ancestor = None
104
 
         if options.__dict__.has_key("base") and options.base:
105
 
             base = cmdutil.determine_revision_tree(tree, options.base)
106
 
+            ancestor = base
107
 
         else:
108
 
-            base = cmdutil.submit_revision(tree)
109
 
-        
110
 
+            base = ancillary.submit_revision(tree)
111
 
+            ancestor = base
112
 
+        if ancestor is None:
113
 
+            ancestor = arch_compound.tree_latest(tree, version)
114
 
+
115
 
         writeversion=version
116
 
         archive=version.archive
117
 
         source=cmdutil.get_mirror_source(archive)
118
 
@@ -625,18 +639,26 @@
119
 
         try:
120
 
             last_revision=tree.iter_logs(version, True).next().revision
121
 
         except StopIteration, e:
122
 
-            if cmdutil.prompt("Import from commit"):
123
 
-                return do_import(version)
124
 
-            else:
125
 
-                raise NoVersionLogs(version)
126
 
-        if last_revision!=version.iter_revisions(True).next():
127
 
+            last_revision = None
128
 
+            if ancestor is None:
129
 
+                if cmdutil.prompt("Import from commit"):
130
 
+                    return do_import(version)
131
 
+                else:
132
 
+                    raise NoVersionLogs(version)
133
 
+        try:
134
 
+            arch_last_revision = version.iter_revisions(True).next()
135
 
+        except StopIteration, e:
136
 
+            arch_last_revision = None
137
 
138
 
+        if last_revision != arch_last_revision:
139
 
+            print "Tree is not up to date with %s" % str(version)
140
 
             if not cmdutil.prompt("Out of date"):
141
 
                 raise OutOfDate
142
 
             else:
143
 
                 allow_old=True
144
 
 
145
 
         try:
146
 
-            if not cmdutil.has_changed(version):
147
 
+            if not cmdutil.has_changed(ancestor):
148
 
                 if not cmdutil.prompt("Empty commit"):
149
 
                     raise EmptyCommit
150
 
         except arch.util.ExecProblem, e:
151
 
@@ -645,15 +667,15 @@
152
 
                 raise MissingID(e)
153
 
             else:
154
 
                 raise
155
 
-        log = tree.log_message(create=False)
156
 
+        log = tree.log_message(create=False, version=version)
157
 
         if log is None:
158
 
             try:
159
 
                 if cmdutil.prompt("Create log"):
160
 
-                    edit_log(tree)
161
 
+                    edit_log(tree, version)
162
 
 
163
 
             except cmdutil.NoEditorSpecified, e:
164
 
                 raise CommandFailed(e)
165
 
-            log = tree.log_message(create=False)
166
 
+            log = tree.log_message(create=False, version=version)
167
 
         if log is None: 
168
 
             raise NoLogMessage
169
 
         if log["Summary"] is None or len(log["Summary"].strip()) == 0:
170
 
@@ -837,23 +859,24 @@
171
 
             if spec is not None:
172
 
                 revision = cmdutil.determine_revision_tree(tree, spec)
173
 
             else:
174
 
-                revision = cmdutil.comp_revision(tree)
175
 
+                revision = ancillary.comp_revision(tree)
176
 
         except cmdutil.CantDetermineRevision, e:
177
 
             raise CommandFailedWrapper(e)
178
 
         munger = None
179
 
 
180
 
         if options.file_contents or options.file_perms or options.deletions\
181
 
             or options.additions or options.renames or options.hunk_prompt:
182
 
-            munger = cmdutil.MungeOpts()
183
 
-            munger.hunk_prompt = options.hunk_prompt
184
 
+            munger = arch_compound.MungeOpts()
185
 
+            munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
186
 
+                                   options.hunk_prompt)
187
 
 
188
 
         if len(args) > 0 or options.logs or options.pattern_files or \
189
 
             options.control:
190
 
             if munger is None:
191
 
-                munger = cmdutil.MungeOpts(True)
192
 
+                munger = cmdutil.arch_compound.MungeOpts(True)
193
 
                 munger.all_types(True)
194
 
         if len(args) > 0:
195
 
-            t_cwd = cmdutil.tree_cwd(tree)
196
 
+            t_cwd = arch_compound.tree_cwd(tree)
197
 
             for name in args:
198
 
                 if len(t_cwd) > 0:
199
 
                     t_cwd += "/"
200
 
@@ -878,7 +901,7 @@
201
 
         if options.pattern_files:
202
 
             munger.add_keep_pattern(options.pattern_files)
203
 
                 
204
 
-        for line in cmdutil.revert(tree, revision, munger, 
205
 
+        for line in arch_compound.revert(tree, revision, munger, 
206
 
                                    not options.no_output):
207
 
             cmdutil.colorize(line)
208
 
 
209
 
@@ -1042,18 +1065,13 @@
210
 
         help_tree_spec()
211
 
         return
212
 
 
213
 
-def require_version_exists(version, spec):
214
 
-    if not version.exists():
215
 
-        raise cmdutil.CantDetermineVersion(spec, 
216
 
-                                           "The version %s does not exist." \
217
 
-                                           % version)
218
 
-
219
 
 class Revisions(BaseCommand):
220
 
     """
221
 
     Print a revision name based on a revision specifier
222
 
     """
223
 
     def __init__(self):
224
 
         self.description="Lists revisions"
225
 
+        self.cl_revisions = []
226
 
     
227
 
     def do_command(self, cmdargs):
228
 
         """
229
 
@@ -1066,224 +1084,68 @@
230
 
             self.tree = arch.tree_root()
231
 
         except arch.errors.TreeRootError:
232
 
             self.tree = None
233
 
+        if options.type == "default":
234
 
+            options.type = "archive"
235
 
         try:
236
 
-            iter = self.get_iterator(options.type, args, options.reverse, 
237
 
-                                     options.modified)
238
 
+            iter = cmdutil.revision_iterator(self.tree, options.type, args, 
239
 
+                                             options.reverse, options.modified,
240
 
+                                             options.shallow)
241
 
         except cmdutil.CantDetermineRevision, e:
242
 
             raise CommandFailedWrapper(e)
243
 
-
244
 
+        except cmdutil.CantDetermineVersion, e:
245
 
+            raise CommandFailedWrapper(e)
246
 
         if options.skip is not None:
247
 
             iter = cmdutil.iter_skip(iter, int(options.skip))
248
 
 
249
 
-        for revision in iter:
250
 
-            log = None
251
 
-            if isinstance(revision, arch.Patchlog):
252
 
-                log = revision
253
 
-                revision=revision.revision
254
 
-            print options.display(revision)
255
 
-            if log is None and (options.summary or options.creator or 
256
 
-                                options.date or options.merges):
257
 
-                log = revision.patchlog
258
 
-            if options.creator:
259
 
-                print "    %s" % log.creator
260
 
-            if options.date:
261
 
-                print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
262
 
-            if options.summary:
263
 
-                print "    %s" % log.summary
264
 
-            if options.merges:
265
 
-                showed_title = False
266
 
-                for revision in log.merged_patches:
267
 
-                    if not showed_title:
268
 
-                        print "    Merged:"
269
 
-                        showed_title = True
270
 
-                    print "    %s" % revision
271
 
-
272
 
-    def get_iterator(self, type, args, reverse, modified):
273
 
-        if len(args) > 0:
274
 
-            spec = args[0]
275
 
-        else:
276
 
-            spec = None
277
 
-        if modified is not None:
278
 
-            iter = cmdutil.modified_iter(modified, self.tree)
279
 
-            if reverse:
280
 
-                return iter
281
 
-            else:
282
 
-                return cmdutil.iter_reverse(iter)
283
 
-        elif type == "archive":
284
 
-            if spec is None:
285
 
-                if self.tree is None:
286
 
-                    raise cmdutil.CantDetermineRevision("", 
287
 
-                                                        "Not in a project tree")
288
 
-                version = cmdutil.determine_version_tree(spec, self.tree)
289
 
-            else:
290
 
-                version = cmdutil.determine_version_arch(spec, self.tree)
291
 
-                cmdutil.ensure_archive_registered(version.archive)
292
 
-                require_version_exists(version, spec)
293
 
-            return version.iter_revisions(reverse)
294
 
-        elif type == "cacherevs":
295
 
-            if spec is None:
296
 
-                if self.tree is None:
297
 
-                    raise cmdutil.CantDetermineRevision("", 
298
 
-                                                        "Not in a project tree")
299
 
-                version = cmdutil.determine_version_tree(spec, self.tree)
300
 
-            else:
301
 
-                version = cmdutil.determine_version_arch(spec, self.tree)
302
 
-                cmdutil.ensure_archive_registered(version.archive)
303
 
-                require_version_exists(version, spec)
304
 
-            return cmdutil.iter_cacherevs(version, reverse)
305
 
-        elif type == "library":
306
 
-            if spec is None:
307
 
-                if self.tree is None:
308
 
-                    raise cmdutil.CantDetermineRevision("", 
309
 
-                                                        "Not in a project tree")
310
 
-                version = cmdutil.determine_version_tree(spec, self.tree)
311
 
-            else:
312
 
-                version = cmdutil.determine_version_arch(spec, self.tree)
313
 
-            return version.iter_library_revisions(reverse)
314
 
-        elif type == "logs":
315
 
-            if self.tree is None:
316
 
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
317
 
-            return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
318
 
-                                  self.tree), reverse)
319
 
-        elif type == "missing" or type == "skip-present":
320
 
-            if self.tree is None:
321
 
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
322
 
-            skip = (type == "skip-present")
323
 
-            version = cmdutil.determine_version_tree(spec, self.tree)
324
 
-            cmdutil.ensure_archive_registered(version.archive)
325
 
-            require_version_exists(version, spec)
326
 
-            return cmdutil.iter_missing(self.tree, version, reverse,
327
 
-                                        skip_present=skip)
328
 
-
329
 
-        elif type == "present":
330
 
-            if self.tree is None:
331
 
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
332
 
-            version = cmdutil.determine_version_tree(spec, self.tree)
333
 
-            cmdutil.ensure_archive_registered(version.archive)
334
 
-            require_version_exists(version, spec)
335
 
-            return cmdutil.iter_present(self.tree, version, reverse)
336
 
-
337
 
-        elif type == "new-merges" or type == "direct-merges":
338
 
-            if self.tree is None:
339
 
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
340
 
-            version = cmdutil.determine_version_tree(spec, self.tree)
341
 
-            cmdutil.ensure_archive_registered(version.archive)
342
 
-            require_version_exists(version, spec)
343
 
-            iter = cmdutil.iter_new_merges(self.tree, version, reverse)
344
 
-            if type == "new-merges":
345
 
-                return iter
346
 
-            elif type == "direct-merges":
347
 
-                return cmdutil.direct_merges(iter)
348
 
-
349
 
-        elif type == "missing-from":
350
 
-            if self.tree is None:
351
 
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
352
 
-            revision = cmdutil.determine_revision_tree(self.tree, spec)
353
 
-            libtree = cmdutil.find_or_make_local_revision(revision)
354
 
-            return cmdutil.iter_missing(libtree, self.tree.tree_version,
355
 
-                                        reverse)
356
 
-
357
 
-        elif type == "partner-missing":
358
 
-            return cmdutil.iter_partner_missing(self.tree, reverse)
359
 
-
360
 
-        elif type == "ancestry":
361
 
-            revision = cmdutil.determine_revision_tree(self.tree, spec)
362
 
-            iter = cmdutil._iter_ancestry(self.tree, revision)
363
 
-            if reverse:
364
 
-                return iter
365
 
-            else:
366
 
-                return cmdutil.iter_reverse(iter)
367
 
-
368
 
-        elif type == "dependencies" or type == "non-dependencies":
369
 
-            nondeps = (type == "non-dependencies")
370
 
-            revision = cmdutil.determine_revision_tree(self.tree, spec)
371
 
-            anc_iter = cmdutil._iter_ancestry(self.tree, revision)
372
 
-            iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
373
 
-            if reverse:
374
 
-                return iter_depends
375
 
-            else:
376
 
-                return cmdutil.iter_reverse(iter_depends)
377
 
-        elif type == "micro":
378
 
-            return cmdutil.iter_micro(self.tree)
379
 
-
380
 
-    
381
 
+        try:
382
 
+            for revision in iter:
383
 
+                log = None
384
 
+                if isinstance(revision, arch.Patchlog):
385
 
+                    log = revision
386
 
+                    revision=revision.revision
387
 
+                out = options.display(revision)
388
 
+                if out is not None:
389
 
+                    print out
390
 
+                if log is None and (options.summary or options.creator or 
391
 
+                                    options.date or options.merges):
392
 
+                    log = revision.patchlog
393
 
+                if options.creator:
394
 
+                    print "    %s" % log.creator
395
 
+                if options.date:
396
 
+                    print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
397
 
+                if options.summary:
398
 
+                    print "    %s" % log.summary
399
 
+                if options.merges:
400
 
+                    showed_title = False
401
 
+                    for revision in log.merged_patches:
402
 
+                        if not showed_title:
403
 
+                            print "    Merged:"
404
 
+                            showed_title = True
405
 
+                        print "    %s" % revision
406
 
+            if len(self.cl_revisions) > 0:
407
 
+                print pylon.changelog_for_merge(self.cl_revisions)
408
 
+        except pylon.errors.TreeRootNone:
409
 
+            raise CommandFailedWrapper(
410
 
+                Exception("This option can only be used in a project tree."))
411
 
+
412
 
+    def changelog_append(self, revision):
413
 
+        if isinstance(revision, arch.Revision):
414
 
+            revision=arch.Patchlog(revision)
415
 
+        self.cl_revisions.append(revision)
416
 
+   
417
 
     def get_parser(self):
418
 
         """
419
 
         Returns the options parser to use for the "revision" command.
420
 
 
421
 
         :rtype: cmdutil.CmdOptionParser
422
 
         """
423
 
-        parser=cmdutil.CmdOptionParser("fai revisions [revision]")
424
 
+        parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
425
 
         select = cmdutil.OptionGroup(parser, "Selection options",
426
 
                           "Control which revisions are listed.  These options"
427
 
                           " are mutually exclusive.  If more than one is"
428
 
                           " specified, the last is used.")
429
 
-        select.add_option("", "--archive", action="store_const", 
430
 
-                          const="archive", dest="type", default="archive",
431
 
-                          help="List all revisions in the archive")
432
 
-        select.add_option("", "--cacherevs", action="store_const", 
433
 
-                          const="cacherevs", dest="type",
434
 
-                          help="List all revisions stored in the archive as "
435
 
-                          "complete copies")
436
 
-        select.add_option("", "--logs", action="store_const", 
437
 
-                          const="logs", dest="type",
438
 
-                          help="List revisions that have a patchlog in the "
439
 
-                          "tree")
440
 
-        select.add_option("", "--missing", action="store_const", 
441
 
-                          const="missing", dest="type",
442
 
-                          help="List revisions from the specified version that"
443
 
-                          " have no patchlog in the tree")
444
 
-        select.add_option("", "--skip-present", action="store_const", 
445
 
-                          const="skip-present", dest="type",
446
 
-                          help="List revisions from the specified version that"
447
 
-                          " have no patchlogs at all in the tree")
448
 
-        select.add_option("", "--present", action="store_const", 
449
 
-                          const="present", dest="type",
450
 
-                          help="List revisions from the specified version that"
451
 
-                          " have no patchlog in the tree, but can't be merged")
452
 
-        select.add_option("", "--missing-from", action="store_const", 
453
 
-                          const="missing-from", dest="type",
454
 
-                          help="List revisions from the specified revision "
455
 
-                          "that have no patchlog for the tree version")
456
 
-        select.add_option("", "--partner-missing", action="store_const", 
457
 
-                          const="partner-missing", dest="type",
458
 
-                          help="List revisions in partner versions that are"
459
 
-                          " missing")
460
 
-        select.add_option("", "--new-merges", action="store_const", 
461
 
-                          const="new-merges", dest="type",
462
 
-                          help="List revisions that have had patchlogs added"
463
 
-                          " to the tree since the last commit")
464
 
-        select.add_option("", "--direct-merges", action="store_const", 
465
 
-                          const="direct-merges", dest="type",
466
 
-                          help="List revisions that have been directly added"
467
 
-                          " to tree since the last commit ")
468
 
-        select.add_option("", "--library", action="store_const", 
469
 
-                          const="library", dest="type",
470
 
-                          help="List revisions in the revision library")
471
 
-        select.add_option("", "--ancestry", action="store_const", 
472
 
-                          const="ancestry", dest="type",
473
 
-                          help="List revisions that are ancestors of the "
474
 
-                          "current tree version")
475
 
-
476
 
-        select.add_option("", "--dependencies", action="store_const", 
477
 
-                          const="dependencies", dest="type",
478
 
-                          help="List revisions that the given revision "
479
 
-                          "depends on")
480
 
-
481
 
-        select.add_option("", "--non-dependencies", action="store_const", 
482
 
-                          const="non-dependencies", dest="type",
483
 
-                          help="List revisions that the given revision "
484
 
-                          "does not depend on")
485
 
-
486
 
-        select.add_option("--micro", action="store_const", 
487
 
-                          const="micro", dest="type",
488
 
-                          help="List partner revisions aimed for this "
489
 
-                          "micro-branch")
490
 
-
491
 
-        select.add_option("", "--modified", dest="modified", 
492
 
-                          help="List tree ancestor revisions that modified a "
493
 
-                          "given file", metavar="FILE[:LINE]")
494
 
 
495
 
+        cmdutil.add_revision_iter_options(select)
496
 
         parser.add_option("", "--skip", dest="skip", 
497
 
                           help="Skip revisions.  Positive numbers skip from "
498
 
                           "beginning, negative skip from end.",
499
 
@@ -1312,6 +1174,9 @@
500
 
         format.add_option("--cacherev", action="store_const", 
501
 
                          const=paths.determine_cacherev_path, dest="display",
502
 
                          help="Show location of cacherev file")
503
 
+        format.add_option("--changelog", action="store_const", 
504
 
+                         const=self.changelog_append, dest="display",
505
 
+                         help="Show location of cacherev file")
506
 
         parser.add_option_group(format)
507
 
         display = cmdutil.OptionGroup(parser, "Display format options",
508
 
                           "These control the display of data")
509
 
@@ -1448,6 +1313,7 @@
510
 
         if os.access(self.history_file, os.R_OK) and \
511
 
             os.path.isfile(self.history_file):
512
 
             readline.read_history_file(self.history_file)
513
 
+        self.cwd = os.getcwd()
514
 
 
515
 
     def write_history(self):
516
 
         readline.write_history_file(self.history_file)
517
 
@@ -1470,16 +1336,21 @@
518
 
     def set_prompt(self):
519
 
         if self.tree is not None:
520
 
             try:
521
 
-                version = " "+self.tree.tree_version.nonarch
522
 
+                prompt = pylon.alias_or_version(self.tree.tree_version, 
523
 
+                                                self.tree, 
524
 
+                                                full=False)
525
 
+                if prompt is not None:
526
 
+                    prompt = " " + prompt
527
 
             except:
528
 
-                version = ""
529
 
+                prompt = ""
530
 
         else:
531
 
-            version = ""
532
 
-        self.prompt = "Fai%s> " % version
533
 
+            prompt = ""
534
 
+        self.prompt = "Fai%s> " % prompt
535
 
 
536
 
     def set_title(self, command=None):
537
 
         try:
538
 
-            version = self.tree.tree_version.nonarch
539
 
+            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
540
 
+                                             full=False)
541
 
         except:
542
 
             version = "[no version]"
543
 
         if command is None:
544
 
@@ -1489,8 +1360,15 @@
545
 
     def do_cd(self, line):
546
 
         if line == "":
547
 
             line = "~"
548
 
+        line = os.path.expanduser(line)
549
 
+        if os.path.isabs(line):
550
 
+            newcwd = line
551
 
+        else:
552
 
+            newcwd = self.cwd+'/'+line
553
 
+        newcwd = os.path.normpath(newcwd)
554
 
         try:
555
 
-            os.chdir(os.path.expanduser(line))
556
 
+            os.chdir(newcwd)
557
 
+            self.cwd = newcwd
558
 
         except Exception, e:
559
 
             print e
560
 
         try:
561
 
@@ -1523,7 +1401,7 @@
562
 
             except cmdutil.CantDetermineRevision, e:
563
 
                 print e
564
 
             except Exception, e:
565
 
-                print "Unhandled error:\n%s" % cmdutil.exception_str(e)
566
 
+                print "Unhandled error:\n%s" % errors.exception_str(e)
567
 
 
568
 
         elif suggestions.has_key(args[0]):
569
 
             print suggestions[args[0]]
570
 
@@ -1574,7 +1452,7 @@
571
 
                 arg = line.split()[-1]
572
 
             else:
573
 
                 arg = ""
574
 
-            iter = iter_munged_completions(iter, arg, text)
575
 
+            iter = cmdutil.iter_munged_completions(iter, arg, text)
576
 
         except Exception, e:
577
 
             print e
578
 
         return list(iter)
579
 
@@ -1604,10 +1482,11 @@
580
 
                 else:
581
 
                     arg = ""
582
 
                 if arg.startswith("-"):
583
 
-                    return list(iter_munged_completions(iter, arg, text))
584
 
+                    return list(cmdutil.iter_munged_completions(iter, arg, 
585
 
+                                                                text))
586
 
                 else:
587
 
-                    return list(iter_munged_completions(
588
 
-                        iter_file_completions(arg), arg, text))
589
 
+                    return list(cmdutil.iter_munged_completions(
590
 
+                        cmdutil.iter_file_completions(arg), arg, text))
591
 
 
592
 
 
593
 
             elif cmd == "cd":
594
 
@@ -1615,13 +1494,13 @@
595
 
                     arg = args.split()[-1]
596
 
                 else:
597
 
                     arg = ""
598
 
-                iter = iter_dir_completions(arg)
599
 
-                iter = iter_munged_completions(iter, arg, text)
600
 
+                iter = cmdutil.iter_dir_completions(arg)
601
 
+                iter = cmdutil.iter_munged_completions(iter, arg, text)
602
 
                 return list(iter)
603
 
             elif len(args)>0:
604
 
                 arg = args.split()[-1]
605
 
-                return list(iter_munged_completions(iter_file_completions(arg),
606
 
-                                                    arg, text))
607
 
+                iter = cmdutil.iter_file_completions(arg)
608
 
+                return list(cmdutil.iter_munged_completions(iter, arg, text))
609
 
             else:
610
 
                 return self.completenames(text, line, begidx, endidx)
611
 
         except Exception, e:
612
 
@@ -1636,44 +1515,8 @@
613
 
             yield entry
614
 
 
615
 
 
616
 
-def iter_file_completions(arg, only_dirs = False):
617
 
-    """Generate an iterator that iterates through filename completions.
618
 
-
619
 
-    :param arg: The filename fragment to match
620
 
-    :type arg: str
621
 
-    :param only_dirs: If true, match only directories
622
 
-    :type only_dirs: bool
623
 
-    """
624
 
-    cwd = os.getcwd()
625
 
-    if cwd != "/":
626
 
-        extras = [".", ".."]
627
 
-    else:
628
 
-        extras = []
629
 
-    (dir, file) = os.path.split(arg)
630
 
-    if dir != "":
631
 
-        listingdir = os.path.expanduser(dir)
632
 
-    else:
633
 
-        listingdir = cwd
634
 
-    for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
635
 
-        if dir != "":
636
 
-            userfile = dir+'/'+file
637
 
-        else:
638
 
-            userfile = file
639
 
-        if userfile.startswith(arg):
640
 
-            if os.path.isdir(listingdir+'/'+file):
641
 
-                userfile+='/'
642
 
-                yield userfile
643
 
-            elif not only_dirs:
644
 
-                yield userfile
645
 
-
646
 
-def iter_munged_completions(iter, arg, text):
647
 
-    for completion in iter:
648
 
-        completion = str(completion)
649
 
-        if completion.startswith(arg):
650
 
-            yield completion[len(arg)-len(text):]
651
 
-
652
 
 def iter_source_file_completions(tree, arg):
653
 
-    treepath = cmdutil.tree_cwd(tree)
654
 
+    treepath = arch_compound.tree_cwd(tree)
655
 
     if len(treepath) > 0:
656
 
         dirs = [treepath]
657
 
     else:
658
 
@@ -1701,7 +1544,7 @@
659
 
     :return: An iterator of all matching untagged files
660
 
     :rtype: iterator of str
661
 
     """
662
 
-    treepath = cmdutil.tree_cwd(tree)
663
 
+    treepath = arch_compound.tree_cwd(tree)
664
 
     if len(treepath) > 0:
665
 
         dirs = [treepath]
666
 
     else:
667
 
@@ -1743,8 +1586,8 @@
668
 
     :param arg: The prefix to match
669
 
     :type arg: str
670
 
     """
671
 
-    treepath = cmdutil.tree_cwd(tree)
672
 
-    tmpdir = cmdutil.tmpdir()
673
 
+    treepath = arch_compound.tree_cwd(tree)
674
 
+    tmpdir = util.tmpdir()
675
 
     changeset = tmpdir+"/changeset"
676
 
     completions = []
677
 
     revision = cmdutil.determine_revision_tree(tree)
678
 
@@ -1756,14 +1599,6 @@
679
 
     shutil.rmtree(tmpdir)
680
 
     return completions
681
 
 
682
 
-def iter_dir_completions(arg):
683
 
-    """Generate an iterator that iterates through directory name completions.
684
 
-
685
 
-    :param arg: The directory name fragment to match
686
 
-    :type arg: str
687
 
-    """
688
 
-    return iter_file_completions(arg, True)
689
 
-
690
 
 class Shell(BaseCommand):
691
 
     def __init__(self):
692
 
         self.description = "Runs Fai as a shell"
693
 
@@ -1795,7 +1630,11 @@
694
 
         parser=self.get_parser()
695
 
         (options, args) = parser.parse_args(cmdargs)
696
 
 
697
 
-        tree = arch.tree_root()
698
 
+        try:
699
 
+            tree = arch.tree_root()
700
 
+        except arch.errors.TreeRootError, e:
701
 
+            raise pylon.errors.CommandFailedWrapper(e)
702
 
+            
703
 
 
704
 
         if (len(args) == 0) == (options.untagged == False):
705
 
             raise cmdutil.GetHelp
706
 
@@ -1809,13 +1648,22 @@
707
 
         if options.id_type == "tagline":
708
 
             if method != "tagline":
709
 
                 if not cmdutil.prompt("Tagline in other tree"):
710
 
-                    if method == "explicit":
711
 
-                        options.id_type == explicit
712
 
+                    if method == "explicit" or method == "implicit":
713
 
+                        options.id_type == method
714
 
                     else:
715
 
                         print "add-id not supported for \"%s\" tagging method"\
716
 
                             % method 
717
 
                         return
718
 
         
719
 
+        elif options.id_type == "implicit":
720
 
+            if method != "implicit":
721
 
+                if not cmdutil.prompt("Implicit in other tree"):
722
 
+                    if method == "explicit" or method == "tagline":
723
 
+                        options.id_type == method
724
 
+                    else:
725
 
+                        print "add-id not supported for \"%s\" tagging method"\
726
 
+                            % method 
727
 
+                        return
728
 
         elif options.id_type == "explicit":
729
 
             if method != "tagline" and method != explicit:
730
 
                 if not prompt("Explicit in other tree"):
731
 
@@ -1824,7 +1672,8 @@
732
 
                     return
733
 
         
734
 
         if options.id_type == "auto":
735
 
-            if method != "tagline" and method != "explicit":
736
 
+            if method != "tagline" and method != "explicit" \
737
 
+                and method !="implicit":
738
 
                 print "add-id not supported for \"%s\" tagging method" % method
739
 
                 return
740
 
             else:
741
 
@@ -1852,10 +1701,12 @@
742
 
             previous_files.extend(files)
743
 
             if id_type == "explicit":
744
 
                 cmdutil.add_id(files)
745
 
-            elif id_type == "tagline":
746
 
+            elif id_type == "tagline" or id_type == "implicit":
747
 
                 for file in files:
748
 
                     try:
749
 
-                        cmdutil.add_tagline_or_explicit_id(file)
750
 
+                        implicit = (id_type == "implicit")
751
 
+                        cmdutil.add_tagline_or_explicit_id(file, False,
752
 
+                                                           implicit)
753
 
                     except cmdutil.AlreadyTagged:
754
 
                         print "\"%s\" already has a tagline." % file
755
 
                     except cmdutil.NoCommentSyntax:
756
 
@@ -1888,6 +1739,9 @@
757
 
         parser.add_option("--tagline", action="store_const", 
758
 
                          const="tagline", dest="id_type", 
759
 
                          help="Use a tagline id")
760
 
+        parser.add_option("--implicit", action="store_const", 
761
 
+                         const="implicit", dest="id_type", 
762
 
+                         help="Use an implicit id (deprecated)")
763
 
         parser.add_option("--untagged", action="store_true", 
764
 
                          dest="untagged", default=False, 
765
 
                          help="tag all untagged files")
766
 
@@ -1926,27 +1780,7 @@
767
 
     def get_completer(self, arg, index):
768
 
         if self.tree is None:
769
 
             raise arch.errors.TreeRootError
770
 
-        completions = list(ancillary.iter_partners(self.tree, 
771
 
-                                                   self.tree.tree_version))
772
 
-        if len(completions) == 0:
773
 
-            completions = list(self.tree.iter_log_versions())
774
 
-
775
 
-        aliases = []
776
 
-        try:
777
 
-            for completion in completions:
778
 
-                alias = ancillary.compact_alias(str(completion), self.tree)
779
 
-                if alias:
780
 
-                    aliases.extend(alias)
781
 
-
782
 
-            for completion in completions:
783
 
-                if completion.archive == self.tree.tree_version.archive:
784
 
-                    aliases.append(completion.nonarch)
785
 
-
786
 
-        except Exception, e:
787
 
-            print e
788
 
-            
789
 
-        completions.extend(aliases)
790
 
-        return completions
791
 
+        return cmdutil.merge_completions(self.tree, arg, index)
792
 
 
793
 
     def do_command(self, cmdargs):
794
 
         """
795
 
@@ -1961,7 +1795,7 @@
796
 
         
797
 
         if self.tree is None:
798
 
             raise arch.errors.TreeRootError(os.getcwd())
799
 
-        if cmdutil.has_changed(self.tree.tree_version):
800
 
+        if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
801
 
             raise UncommittedChanges(self.tree)
802
 
 
803
 
         if len(args) > 0:
804
 
@@ -2027,14 +1861,14 @@
805
 
         :type other_revision: `arch.Revision`
806
 
         :return: 0 if the merge was skipped, 1 if it was applied
807
 
         """
808
 
-        other_tree = cmdutil.find_or_make_local_revision(other_revision)
809
 
+        other_tree = arch_compound.find_or_make_local_revision(other_revision)
810
 
         try:
811
 
             if action == "native-merge":
812
 
-                ancestor = cmdutil.merge_ancestor2(self.tree, other_tree, 
813
 
-                                                   other_revision)
814
 
+                ancestor = arch_compound.merge_ancestor2(self.tree, other_tree, 
815
 
+                                                         other_revision)
816
 
             elif action == "update":
817
 
-                ancestor = cmdutil.tree_latest(self.tree, 
818
 
-                                               other_revision.version)
819
 
+                ancestor = arch_compound.tree_latest(self.tree, 
820
 
+                                                     other_revision.version)
821
 
         except CantDetermineRevision, e:
822
 
             raise CommandFailedWrapper(e)
823
 
         cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
824
 
@@ -2104,7 +1938,10 @@
825
 
         if self.tree is None:
826
 
             raise arch.errors.TreeRootError
827
 
 
828
 
-        edit_log(self.tree)
829
 
+        try:
830
 
+            edit_log(self.tree, self.tree.tree_version)
831
 
+        except pylon.errors.NoEditorSpecified, e:
832
 
+            raise pylon.errors.CommandFailedWrapper(e)
833
 
 
834
 
     def get_parser(self):
835
 
         """
836
 
@@ -2132,7 +1969,7 @@
837
 
         """
838
 
         return
839
 
 
840
 
-def edit_log(tree):
841
 
+def edit_log(tree, version):
842
 
     """Makes and edits the log for a tree.  Does all kinds of fancy things
843
 
     like log templates and merge summaries and log-for-merge
844
 
     
845
 
@@ -2141,28 +1978,29 @@
846
 
     """
847
 
     #ensure we have an editor before preparing the log
848
 
     cmdutil.find_editor()
849
 
-    log = tree.log_message(create=False)
850
 
+    log = tree.log_message(create=False, version=version)
851
 
     log_is_new = False
852
 
     if log is None or cmdutil.prompt("Overwrite log"):
853
 
         if log is not None:
854
 
            os.remove(log.name)
855
 
-        log = tree.log_message(create=True)
856
 
+        log = tree.log_message(create=True, version=version)
857
 
         log_is_new = True
858
 
         tmplog = log.name
859
 
-        template = tree+"/{arch}/=log-template"
860
 
-        if not os.path.exists(template):
861
 
-            template = os.path.expanduser("~/.arch-params/=log-template")
862
 
-            if not os.path.exists(template):
863
 
-                template = None
864
 
+        template = pylon.log_template_path(tree)
865
 
         if template:
866
 
             shutil.copyfile(template, tmplog)
867
 
-        
868
 
-        new_merges = list(cmdutil.iter_new_merges(tree, 
869
 
-                                                  tree.tree_version))
870
 
-        log["Summary"] = merge_summary(new_merges, tree.tree_version)
871
 
+        comp_version = ancillary.comp_revision(tree).version
872
 
+        new_merges = cmdutil.iter_new_merges(tree, comp_version)
873
 
+        new_merges = cmdutil.direct_merges(new_merges)
874
 
+        log["Summary"] = pylon.merge_summary(new_merges, 
875
 
+                                         version)
876
 
         if len(new_merges) > 0:   
877
 
             if cmdutil.prompt("Log for merge"):
878
 
-                mergestuff = cmdutil.log_for_merge(tree)
879
 
+                if cmdutil.prompt("changelog for merge"):
880
 
+                    mergestuff = "Patches applied:\n"
881
 
+                    mergestuff += pylon.changelog_for_merge(new_merges)
882
 
+                else:
883
 
+                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
884
 
                 log.description += mergestuff
885
 
         log.save()
886
 
     try:
887
 
@@ -2172,29 +2010,6 @@
888
 
             os.remove(log.name)
889
 
         raise
890
 
 
891
 
-def merge_summary(new_merges, tree_version):
892
 
-    if len(new_merges) == 0:
893
 
-        return ""
894
 
-    if len(new_merges) == 1:
895
 
-        summary = new_merges[0].summary
896
 
-    else:
897
 
-        summary = "Merge"
898
 
-
899
 
-    credits = []
900
 
-    for merge in new_merges:
901
 
-        if arch.my_id() != merge.creator:
902
 
-            name = re.sub("<.*>", "", merge.creator).rstrip(" ");
903
 
-            if not name in credits:
904
 
-                credits.append(name)
905
 
-        else:
906
 
-            version = merge.revision.version
907
 
-            if version.archive == tree_version.archive:
908
 
-                if not version.nonarch in credits:
909
 
-                    credits.append(version.nonarch)
910
 
-            elif not str(version) in credits:
911
 
-                credits.append(str(version))
912
 
-
913
 
-    return ("%s (%s)") % (summary, ", ".join(credits))
914
 
 
915
 
 class MirrorArchive(BaseCommand):
916
 
     """
917
 
@@ -2268,31 +2083,73 @@
918
 
 
919
 
 Use "alias" to list available (user and automatic) aliases."""
920
 
 
921
 
+auto_alias = [
922
 
+"acur", 
923
 
+"The latest revision in the archive of the tree-version.  You can specify \
924
 
+a different version like so: acur:foo--bar--0 (aliases can be used)",
925
 
+"tcur",
926
 
+"""(tree current) The latest revision in the tree of the tree-version. \
927
 
+You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
928
 
+used).""",
929
 
+"tprev" , 
930
 
+"""(tree previous) The previous revision in the tree of the tree-version.  To \
931
 
+specify an older revision, use a number, e.g. "tprev:4" """,
932
 
+"tanc" , 
933
 
+"""(tree ancestor) The ancestor revision of the tree To specify an older \
934
 
+revision, use a number, e.g. "tanc:4".""",
935
 
+"tdate" , 
936
 
+"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
937
 
+"tmod" , 
938
 
+""" (tree modified) The latest revision to modify a given file, e.g. \
939
 
+"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
940
 
+"ttag" , 
941
 
+"""(tree tag) The revision that was tagged into the current tree revision, \
942
 
+according to the tree""",
943
 
+"tagcur", 
944
 
+"""(tag current) The latest revision of the version that the current tree \
945
 
+was tagged from.""",
946
 
+"mergeanc" , 
947
 
+"""The common ancestor of the current tree and the specified revision. \
948
 
+Defaults to the first partner-version's latest revision or to tagcur.""",
949
 
+]
950
 
+
951
 
+
952
 
+def is_auto_alias(name):
953
 
+    """Determine whether a name is an auto alias name
954
 
+
955
 
+    :param name: the name to check
956
 
+    :type name: str
957
 
+    :return: True if the name is an auto alias, false if not
958
 
+    :rtype: bool
959
 
+    """
960
 
+    return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
961
 
+
962
 
+
963
 
+def display_def(iter, wrap = 80):
964
 
+    """Display a list of definitions
965
 
+
966
 
+    :param iter: iter of name, definition pairs
967
 
+    :type iter: iter of (str, str)
968
 
+    :param wrap: The width for text wrapping
969
 
+    :type wrap: int
970
 
+    """
971
 
+    vals = list(iter)
972
 
+    maxlen = 0
973
 
+    for (key, value) in vals:
974
 
+        if len(key) > maxlen:
975
 
+            maxlen = len(key)
976
 
+    for (key, value) in vals:
977
 
+        tw=textwrap.TextWrapper(width=wrap, 
978
 
+                                initial_indent=key.rjust(maxlen)+" : ",
979
 
+                                subsequent_indent="".rjust(maxlen+3))
980
 
+        print tw.fill(value)
981
 
+
982
 
+
983
 
 def help_aliases(tree):
984
 
-    print """Auto-generated aliases
985
 
- acur : The latest revision in the archive of the tree-version.  You can specfy
986
 
-        a different version like so: acur:foo--bar--0 (aliases can be used)
987
 
- tcur : (tree current) The latest revision in the tree of the tree-version.
988
 
-        You can specify a different version like so: tcur:foo--bar--0 (aliases
989
 
-        can be used).
990
 
-tprev : (tree previous) The previous revision in the tree of the tree-version.
991
 
-        To specify an older revision, use a number, e.g. "tprev:4"
992
 
- tanc : (tree ancestor) The ancestor revision of the tree
993
 
-        To specify an older revision, use a number, e.g. "tanc:4"
994
 
-tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
995
 
- tmod : (tree modified) The latest revision to modify a given file 
996
 
-        (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
997
 
- ttag : (tree tag) The revision that was tagged into the current tree revision,
998
 
-        according to the tree.
999
 
-tagcur: (tag current) The latest revision of the version that the current tree
1000
 
-        was tagged from.
1001
 
-mergeanc : The common ancestor of the current tree and the specified revision.
1002
 
-        Defaults to the first partner-version's latest revision or to tagcur.
1003
 
-   """
1004
 
+    print """Auto-generated aliases"""
1005
 
+    display_def(pylon.util.iter_pairs(auto_alias))
1006
 
     print "User aliases"
1007
 
-    for parts in ancillary.iter_all_alias(tree):
1008
 
-        print parts[0].rjust(10)+" : "+parts[1]
1009
 
-
1010
 
+    display_def(ancillary.iter_all_alias(tree))
1011
 
 
1012
 
 class Inventory(BaseCommand):
1013
 
     """List the status of files in the tree"""
1014
 
@@ -2428,6 +2285,11 @@
1015
 
         except cmdutil.ForbiddenAliasSyntax, e:
1016
 
             raise CommandFailedWrapper(e)
1017
 
 
1018
 
+    def no_prefix(self, alias):
1019
 
+        if alias.startswith("^"):
1020
 
+            alias = alias[1:]
1021
 
+        return alias
1022
 
+        
1023
 
     def arg_dispatch(self, args, options):
1024
 
         """Add, modify, or list aliases, depending on number of arguments
1025
 
 
1026
 
@@ -2438,15 +2300,20 @@
1027
 
         if len(args) == 0:
1028
 
             help_aliases(self.tree)
1029
 
             return
1030
 
-        elif len(args) == 1:
1031
 
-            self.print_alias(args[0])
1032
 
-        elif (len(args)) == 2:
1033
 
-            self.add(args[0], args[1], options)
1034
 
         else:
1035
 
-            raise cmdutil.GetHelp
1036
 
+            alias = self.no_prefix(args[0])
1037
 
+            if len(args) == 1:
1038
 
+                self.print_alias(alias)
1039
 
+            elif (len(args)) == 2:
1040
 
+                self.add(alias, args[1], options)
1041
 
+            else:
1042
 
+                raise cmdutil.GetHelp
1043
 
 
1044
 
     def print_alias(self, alias):
1045
 
         answer = None
1046
 
+        if is_auto_alias(alias):
1047
 
+            raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
1048
 
+                "  Use \"revision\" to expand auto aliases." % alias)
1049
 
         for pair in ancillary.iter_all_alias(self.tree):
1050
 
             if pair[0] == alias:
1051
 
                 answer = pair[1]
1052
 
@@ -2464,6 +2331,8 @@
1053
 
         :type expansion: str
1054
 
         :param options: The commandline options
1055
 
         """
1056
 
+        if is_auto_alias(alias):
1057
 
+            raise IsAutoAlias(alias)
1058
 
         newlist = ""
1059
 
         written = False
1060
 
         new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
1061
 
@@ -2490,14 +2359,17 @@
1062
 
         deleted = False
1063
 
         if len(args) != 1:
1064
 
             raise cmdutil.GetHelp
1065
 
+        alias = self.no_prefix(args[0])
1066
 
+        if is_auto_alias(alias):
1067
 
+            raise IsAutoAlias(alias)
1068
 
         newlist = ""
1069
 
         for pair in self.get_iterator(options):
1070
 
-            if pair[0] != args[0]:
1071
 
+            if pair[0] != alias:
1072
 
                 newlist+="%s=%s\n" % (pair[0], pair[1])
1073
 
             else:
1074
 
                 deleted = True
1075
 
         if not deleted:
1076
 
-            raise errors.NoSuchAlias(args[0])
1077
 
+            raise errors.NoSuchAlias(alias)
1078
 
         self.write_aliases(newlist, options)
1079
 
 
1080
 
     def get_alias_file(self, options):
1081
 
@@ -2526,7 +2398,7 @@
1082
 
         :param options: The commandline options
1083
 
         """
1084
 
         filename = os.path.expanduser(self.get_alias_file(options))
1085
 
-        file = cmdutil.NewFileVersion(filename)
1086
 
+        file = util.NewFileVersion(filename)
1087
 
         file.write(newlist)
1088
 
         file.commit()
1089
 
 
1090
 
@@ -2588,10 +2460,13 @@
1091
 
         :param cmdargs: The commandline arguments
1092
 
         :type cmdargs: list of str
1093
 
         """
1094
 
-        cmdutil.find_editor()
1095
 
         parser = self.get_parser()
1096
 
         (options, args) = parser.parse_args(cmdargs)
1097
 
         try:
1098
 
+            cmdutil.find_editor()
1099
 
+        except pylon.errors.NoEditorSpecified, e:
1100
 
+            raise pylon.errors.CommandFailedWrapper(e)
1101
 
+        try:
1102
 
             self.tree=arch.tree_root()
1103
 
         except:
1104
 
             self.tree=None
1105
 
@@ -2655,7 +2530,7 @@
1106
 
             target_revision = cmdutil.determine_revision_arch(self.tree, 
1107
 
                                                               args[0])
1108
 
         else:
1109
 
-            target_revision = cmdutil.tree_latest(self.tree)
1110
 
+            target_revision = arch_compound.tree_latest(self.tree)
1111
 
         if len(args) > 1:
1112
 
             merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
1113
 
                        self.tree, f)) for f in args[1:] ]
1114
 
@@ -2711,7 +2586,7 @@
1115
 
 
1116
 
         :param message: The message to send
1117
 
         :type message: `email.Message`"""
1118
 
-        server = smtplib.SMTP()
1119
 
+        server = smtplib.SMTP("localhost")
1120
 
         server.sendmail(message['From'], message['To'], message.as_string())
1121
 
         server.quit()
1122
 
 
1123
 
@@ -2763,6 +2638,22 @@
1124
 
 'alias' : Alias,
1125
 
 'request-merge': RequestMerge,
1126
 
 }
1127
 
+
1128
 
+def my_import(mod_name):
1129
 
+    module = __import__(mod_name)
1130
 
+    components = mod_name.split('.')
1131
 
+    for comp in components[1:]:
1132
 
+        module = getattr(module, comp)
1133
 
+    return module
1134
 
+
1135
 
+def plugin(mod_name):
1136
 
+    module = my_import(mod_name)
1137
 
+    module.add_command(commands)
1138
 
+
1139
 
+for file in os.listdir(sys.path[0]+"/command"):
1140
 
+    if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
1141
 
+        plugin("command."+file[:-3])
1142
 
+
1143
 
 suggestions = {
1144
 
 'apply-delta' : "Try \"apply-changes\".",
1145
 
 'delta' : "To compare two revisions, use \"changes\".",
1146
 
@@ -2784,6 +2675,7 @@
1147
 
 'tagline' : "Use add-id.  It uses taglines in tagline trees",
1148
 
 'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
1149
 
 'library-revisions' : "Use revisions --library",
1150
 
-'file-revert' : "Use revert FILE"
1151
 
+'file-revert' : "Use revert FILE",
1152
 
+'join-branch' : "Use replay --logs-only"
1153
 
 }
1154
 
 # arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7