~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to testdata/diff

  • Committer: Aaron Bentley
  • Date: 2005-07-27 17:57:32 UTC
  • Revision ID: abentley@panoramicfeedback.com-20050727175732-3637e3a9c1af9c33
Added iter_patched, handled files with no terminating newline

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