[Utnubu-maintainers] Bug#382561: bzrtools: New upstream release

Mohammed Adnène Trojette adn+deb at diwi.org
Sat Aug 19 22:55:52 CEST 2006


tag 382561 patch
thanks

On Fri, Aug 11, 2006, Bob Tanner wrote:
> I'm happy to announce the release of Bzrtools 0.9.0.

Hi,

I've tried to package the new upstream version, to fix some of the bugs
filed in the BTS and to switch to the new Python Policy.

The result is the following NMU diff with the following changelog:

+bzrtools (0.9.0-0.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * New upstream release and resync with Ubuntu.
+  * Add rsync and python-medusa to Build-Depends (necessary for tests).
+  * Adjust dependencies
+     + remove bzr (>= 0.8.1), bzr (<< 0.9). (Closes: #382561)
+     + add bzr (>= 0.9), bzr (<< 0.10).
+     + add pybaz. (Closes: #362587)
+  * Remove debian/dirs. (Closes: #368729)
+  * Use python-support to follow new Python Policy
+    thanks to Arnaud Fontaine
+     + install *.py in python-support directory.
+  * Add preamble to debian/copyright.
+
+ -- Mohammed Adnène Trojette <adn+deb at diwi.org>  Sat, 19 Aug 2006 21:50:36 +0200

I don't intend to upload it as is, because there are many changes which
are not "minimal" in the meaning of a NMU. But if this bug is not fixed
soon, I'll upload a Low Threshold NMU.

You'll find the files on http://adn.diwi.org/debian-QA/bzrtools/0.9.0-0.1/

I hope it helps,
-- 
adn
Mohammed Adnène Trojette
-------------- next part --------------
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/baz_import.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/baz_import.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/baz_import.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/baz_import.py	2006-08-11 15:10:54.000000000 +0200
@@ -730,7 +730,7 @@
 
 _global_option('max-count', type = int)
 class cmd_baz_import_branch(Command):
-    """Import an Arch or Baz branch into a bzr branch.  <BZRTOOLS>"""
+    """Import an Arch or Baz branch into a bzr branch."""
     takes_args = ['to_location', 'from_branch?', 'reuse_history*']
     takes_options = ['verbose', 'max-count']
 
@@ -757,7 +757,7 @@
 
 
 class cmd_baz_import(Command):
-    """Import an Arch or Baz archive into a bzr repository.  <BZRTOOLS>
+    """Import an Arch or Baz archive into a bzr repository.
 
     This command should be used on local archives (or mirrors) only.  It is
     quite slow on remote archives.
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/body /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/body
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/body	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/body	1970-01-01 01:00:00.000000000 +0100
@@ -1,3 +0,0 @@
-If tmp is on a separate filesystem, baz2bzr won't be able to rename files.
-Option 1 (preferred): use the same filesystem
-Option 2: use shutils.move instead of os.rename
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/comments/a78766ca-05bb-4875-b716-7f22ec430c52/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-
-
-
-Date=Thu, 26 May 2005 13:14:35 +0000
-
-
-
-
-
-
-From=abentley
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/07f6e226-092a-4e69-8891-307903604c93/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=closed
-
-
-
-
-
-
-summary=baz2bzr should use correct filesystem
-
-
-
-
-
-
-time=Thu, 26 May 2005 13:13:06 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/1e6d3356-5668-47f3-b451-5285bcc0ec3b/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/1e6d3356-5668-47f3-b451-5285bcc0ec3b/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/1e6d3356-5668-47f3-b451-5285bcc0ec3b/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/1e6d3356-5668-47f3-b451-5285bcc0ec3b/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,42 +0,0 @@
-
-
-
-assigned=abentley
-
-
-
-
-
-
-creator=root
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=closed
-
-
-
-
-
-
-summary=shell produces deprecation warnings
-
-
-
-
-
-
-time=Mon, 13 Mar 2006 00:12:18 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/4ddc9130-fe72-4d0c-9848-157ad6f2c14d/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/4ddc9130-fe72-4d0c-9848-157ad6f2c14d/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/4ddc9130-fe72-4d0c-9848-157ad6f2c14d/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/4ddc9130-fe72-4d0c-9848-157ad6f2c14d/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,42 +0,0 @@
-
-
-
-assigned=abentley
-
-
-
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=closed
-
-
-
-
-
-
-summary=push overwrites diverged branches
-
-
-
-
-
-
-time=Tue, 20 Sep 2005 17:08:01 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/7c3b4f3e-33c5-4112-b8df-c209ec6cc8b6/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/7c3b4f3e-33c5-4112-b8df-c209ec6cc8b6/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/7c3b4f3e-33c5-4112-b8df-c209ec6cc8b6/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/7c3b4f3e-33c5-4112-b8df-c209ec6cc8b6/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,42 +0,0 @@
-
-
-
-assigned=abentley
-
-
-
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=closed
-
-
-
-
-
-
-summary=push overwrites existing non-bzr directories
-
-
-
-
-
-
-time=Tue, 20 Sep 2005 17:07:14 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/body /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/body
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/body	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/body	1970-01-01 01:00:00.000000000 +0100
@@ -1,3 +0,0 @@
-Fetch-ghosts only works on ghosts in the ancestry of the branch's 
-last-revision.  It should pull any available ghosts, i.e. it should scan the
-repository for ghosts.
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/comments/906938f4-be68-4d65-9b75-def07743b043/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-
-
-
-Date=Fri, 31 Mar 2006 15:47:26 +0000
-
-
-
-
-
-
-From=abentley
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/9312b4cf-379e-490b-bfb5-dfb81357ee25/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=fetch-ghosts should be more aggressive
-
-
-
-
-
-
-time=Fri, 31 Mar 2006 15:44:44 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/body /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/body
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/body	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/body	1970-01-01 01:00:00.000000000 +0100
@@ -1,61 +0,0 @@
-From Robey Pointer...
-
-This exception:
-
-[ 7114] Tue 22:37:07.305 ERROR: exceptions.TypeError: args must be a  sequence of strings, but was ('cat-log', '--dir', '/home/robey/code/ testing/baz2bzr-618h1D/rd', bzrlib.plugins.bzrtools- baz2bzr.pybaz.Revision('robey at lag.net--2003-public/secsh--dev--1.0-- base-0'))
-  at /home/robey/code/bzrtools/pybaz/backends/forkexec.py line 254  in __init__
-Traceback (most recent call last):
-  File "/home/robey/code/bzr.dev/bzrlib/commands.py", line 557, in  run_bzr_catch_errors
-    return run_bzr(argv)
-  File "/home/robey/code/bzr.dev/bzrlib/commands.py", line 520, in  run_bzr
-    ret = cmd_obj.run_argv(argv)
-  File "/home/robey/code/bzr.dev/bzrlib/commands.py", line 233, in  run_argv
-    return self.run(**all_cmd_args)
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/baz_import.py",  line 713, in run
-    reuse_history_from=reuse_history_list)
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/baz_import.py",  line 398, in import_version
-    max_count=max_count):
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/baz_import.py",  line 545, in iter_import_version
-    tree, baz_inv, log = get_revision(revdir, revision)
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/baz_import.py",  line 643, in get_revision
-    log = get_log(tree, revision)
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/baz_import.py",  line 638, in get_log
-    assert str(log.revision) == str(revision), (log.revision, revision)
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/external/pybaz/ _patchlog.py", line 171, in _get_revision
-    assert self.__revision == self['Archive']+'/'+self['Revision']
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/external/pybaz/ _patchlog.py", line 150, in __getitem__
-    return self._parse()[header]
-  File "/home/robey/.bazaar/plugins/bzrtools-baz2bzr/external/pybaz/ _patchlog.py", line 133, in _parse
-    s = _backend().text_cmd(
-  File "/home/robey/code/bzrtools/pybaz/backends/commandline.py",  line 141, in text_cmd
-    status, text = self._get_spawner().status_text_cmd(args, chdir,  (0,))
-  File "/home/robey/code/bzrtools/pybaz/backends/commandline.py",  line 240, in status_text_cmd
-    return self._guess().status_text_cmd(*args, **kwargs)
-  File "/home/robey/code/bzrtools/pybaz/backends/forkexec.py", line  62, in status_text_cmd
-    logger=self._logger)
-  File "/home/robey/code/bzrtools/pybaz/backends/forkexec.py", line  107, in exec_safe_status_stdout
-    proc = ChildProcess(program, args, expected, chdir, logger)
-  File "/home/robey/code/bzrtools/pybaz/backends/forkexec.py", line  254, in __init__
-    raise TypeError(
-
-
-is fixed by this tiny patch to pybaz:
-
---- orig/pybaz/_patchlog.py
-+++ mod/pybaz/_patchlog.py
-@@ -99,7 +99,7 @@
-             if not p.has_archive() or not p.has_patchlevel:
-                 raise errors.NamespaceError(revision,
-                                             'fully-qualified  revision')
--            self.__revision = revision
-+            self.__revision = str(revision)
-         if tree is None:
-             self.__tree = None
-         else:
-
-
-
-I'm sure that's fixing the symptom and not the bug, but it was  sufficient for me to migrate paramiko, so I thought I'd share it.
-
-robey
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/comments/e53c2ce1-5b28-4e48-8453-bad891786713/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-
-
-
-Date=Sat, 03 Dec 2005 23:53:27 +0000
-
-
-
-
-
-
-From=abentley
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/aa3f38e8-8544-498f-893e-dec21b1b8f13/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,28 +0,0 @@
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=closed
-
-
-
-
-
-
-summary=baz-import fails because cat-log uses a Revision, not a string
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/bad9a00f-50f5-4782-9da6-722fb0a58c93/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/bad9a00f-50f5-4782-9da6-722fb0a58c93/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/bad9a00f-50f5-4782-9da6-722fb0a58c93/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/bad9a00f-50f5-4782-9da6-722fb0a58c93/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=Supplying past convesions doesn't work as expected?
-
-
-
-
-
-
-time=Thu, 23 Mar 2006 16:38:13 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/da0f16ce-1ee0-4f9e-bf1a-9a78c4a7e765/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/da0f16ce-1ee0-4f9e-bf1a-9a78c4a7e765/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/da0f16ce-1ee0-4f9e-bf1a-9a78c4a7e765/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/da0f16ce-1ee0-4f9e-bf1a-9a78c4a7e765/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,42 +0,0 @@
-
-
-
-assigned=abentley
-
-
-
-
-
-
-creator=root
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=closed
-
-
-
-
-
-
-summary=shelf produces deprecation warnings
-
-
-
-
-
-
-time=Mon, 13 Mar 2006 00:12:30 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/dbd10145-51f8-4bda-96d9-06b5d3ef80b8/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/dbd10145-51f8-4bda-96d9-06b5d3ef80b8/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/dbd10145-51f8-4bda-96d9-06b5d3ef80b8/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/dbd10145-51f8-4bda-96d9-06b5d3ef80b8/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=No way to force an encoding for Arch logs
-
-
-
-
-
-
-time=Thu, 23 Mar 2006 16:42:53 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/body /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/body
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/body	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/body	1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-The repo root may not contain the branch, but reconcile uses the repo
-root, not branch.  Fix so reconcile is called properly.
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/comments/f13e3358-1a65-4a45-9472-781548bc3fc3/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-
-
-
-Date=Fri, 31 Mar 2006 15:52:54 +0000
-
-
-
-
-
-
-From=abentley
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/values /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/values
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/values	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/bugs/fdc2b62a-9622-4f08-ac78-bd2603a4e0af/values	1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-
-
-
-creator=abentley
-
-
-
-
-
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=fetch-ghosts should always invoke reconcile on the repo root
-
-
-
-
-
-
-time=Fri, 31 Mar 2006 15:48:55 +0000
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/settings /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/settings
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/settings	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/settings	1970-01-01 01:00:00.000000000 +0100
@@ -1,7 +0,0 @@
-
-
-
-rcs_name=bzr
-
-
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/version /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/version
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/.be/version	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/.be/version	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-Bugs Everywhere Tree 1 0
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/bzrtools.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/bzrtools.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/bzrtools.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/bzrtools.py	2006-08-11 15:10:54.000000000 +0200
@@ -52,24 +52,23 @@
     ([u'foo'], {})
     >>> is_clean(tree)
     (False, [])
-    >>> tree.commit("added file")
+    >>> tree.commit("added file", rev_id='commit-id')
+    'commit-id'
     >>> is_clean(tree)
     (True, [])
     >>> rm_tree(tree)
     """
-    from bzrlib.diff import compare_trees
     old_tree = cur_tree.basis_tree()
     new_tree = cur_tree
     non_source = []
     for path, file_class, kind, file_id, entry in new_tree.list_files():
         if file_class in ('?', 'I'):
             non_source.append(path)
-    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
+    delta = new_tree.changes_from(old_tree, want_unchanged=False)
     return not delta.has_changed(), non_source
 
 def set_push_data(tree, location):
-    push_file = file (tree.branch.control_files.controlfilename("x-push-data"), "wb")
-    push_file.write("%s\n" % location)
+    tree.branch.control_files.put_utf8("x-push-data", "%s\n" % location)
 
 def get_push_data(tree):
     """
@@ -78,15 +77,14 @@
     True
     >>> set_push_data(tree, 'http://somewhere')
     >>> get_push_data(tree)
-    'http://somewhere'
+    u'http://somewhere'
     >>> rm_tree(tree)
     """
-    filename = tree.branch.control_files.controlfilename("x-push-data")
-    if not os.path.exists(filename):
+    try:
+        location = tree.branch.control_files.get_utf8('x-push-data').read()
+    except NoSuchFile:
         return None
-    push_file = file (filename, "rb")
-    (location,) = [f.rstrip('\n') for f in push_file]
-    return location
+    return location.rstrip('\n')
 
 """
 >>> shell_escape('hello')
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/cbranch.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/cbranch.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/cbranch.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/cbranch.py	2006-08-11 15:10:54.000000000 +0200
@@ -15,10 +15,12 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+from bzrlib.branch import Branch
 from bzrlib.builtins import cmd_branch, cmd_checkout
 from bzrlib.config import LocationConfig
 from bzrlib.errors import BzrCommandError
 from bzrlib.osutils import pathjoin, basename, abspath, getcwd
+from bzrlib.urlutils import local_path_to_url
 
 def cbranch(from_location, to_location=None, revision=None, lightweight=False):
     from_location = from_location.rstrip('/')
@@ -31,3 +33,5 @@
     b_loc = pathjoin(b_root, basename(to_location))
     cmd_branch().run(from_location, b_loc, revision=revision)
     cmd_checkout().run(b_loc, to_location, lightweight=lightweight)
+    b = Branch.open(to_location)
+    b.set_parent(local_path_to_url(from_location))
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/clean_tree.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/clean_tree.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/clean_tree.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/clean_tree.py	2006-08-11 15:10:54.000000000 +0200
@@ -18,15 +18,18 @@
 import shutil
 import sys
 
-from bzrlib.bzrdir import BzrDir
 from bzrlib.osutils import has_symlinks, isdir
+from bzrlib.trace import note
 from bzrlib.workingtree import WorkingTree
 
+
 def is_detritus(subp):
+    """Return True if the supplied path is detritus, False otherwise"""
     return subp.endswith('.THIS') or subp.endswith('.BASE') or\
         subp.endswith('.OTHER') or subp.endswith('~') or subp.endswith('.tmp')
 
-def iter_deletables(tree, unknown=True, ignored=False, detritus=False):
+
+def iter_deletables(tree, unknown=False, ignored=False, detritus=False):
     """Iterate through files that may be deleted"""
     for subp in tree.extras():
         if detritus and is_detritus(subp):
@@ -39,67 +42,28 @@
             if unknown:
                 yield tree.abspath(subp), subp
 
-def clean_tree(directory, ignored=False, detritus=False, dry_run=False,
-               to_file=sys.stdout):
+
+def clean_tree(directory, unknown=False, ignored=False, detritus=False, 
+               dry_run=False):
+    """Remove files in the specified classes from the tree"""
     tree = WorkingTree.open_containing(directory)[0]
-    deletables = iter_deletables(tree, ignored=ignored, detritus=detritus)
-    delete_items(deletables, dry_run=dry_run, to_file=to_file)
+    deletables = iter_deletables(tree, unknown=unknown, ignored=ignored, 
+                                 detritus=detritus)
+    delete_items(deletables, dry_run=dry_run)
 
-def delete_items(deletables, dry_run=False, to_file=sys.stdout):
+
+def delete_items(deletables, dry_run=False):
     """Delete files in the deletables iterable"""
     has_deleted = False
     for path, subp in deletables:
         if not has_deleted:
-            print >> to_file, "deleting paths:"
+            note("deleting paths:")
             has_deleted = True
-        print >> to_file, ' ', subp
+        note('  ' + subp)
         if not dry_run:
             if isdir(path):
                 shutil.rmtree(path)
             else:
                 os.unlink(path)
     if not has_deleted:
-        print >> to_file, "No files deleted."
-
-def test_suite():
-    from bzrlib.tests import TestCaseInTempDir
-    from unittest import makeSuite
-    import os.path
-    from StringIO import StringIO
-    class TestCleanTree(TestCaseInTempDir):
-        def test_symlinks(self):
-            if has_symlinks() is False:
-                return
-            os.mkdir('branch')
-            BzrDir.create_standalone_workingtree('branch')
-            os.symlink(os.path.realpath('no-die-please'), 'branch/die-please')
-            os.mkdir('no-die-please')
-            assert os.path.exists('branch/die-please')
-            os.mkdir('no-die-please/child')
-
-            clean_tree('branch', to_file=StringIO())
-            assert os.path.exists('no-die-please')
-            assert os.path.exists('no-die-please/child')
-
-        def test_iter_deletable(self):
-            """Files are selected for deletion appropriately"""
-            os.mkdir('branch')
-            tree = BzrDir.create_standalone_workingtree('branch')
-            file('branch/file.BASE', 'wb').write('contents')
-            self.assertEqual(len(list(iter_deletables(tree))), 1)
-            file('branch/file~', 'wb').write('contents')
-            file('branch/file.pyc', 'wb').write('contents')
-            dels = [r for a,r in iter_deletables(tree)]
-            assert 'file~' not in dels
-            assert 'file.pyc' not in dels
-            dels = [r for a,r in iter_deletables(tree, detritus=True)]
-            assert 'file~' in dels
-            assert 'file.pyc' not in dels
-            dels = [r for a,r in iter_deletables(tree, ignored=True)]
-            assert 'file~' in dels
-            assert 'file.BASE' in dels
-            assert 'file.pyc' in dels
-            dels = [r for a,r in iter_deletables(tree, unknown=False)]
-            assert 'file.BASE' not in dels
-
-    return makeSuite(TestCleanTree)
+        note("No files deleted.")
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/colordiff.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/colordiff.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/colordiff.py	1970-01-01 01:00:00.000000000 +0100
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/colordiff.py	2006-08-11 15:10:54.000000000 +0200
@@ -0,0 +1,118 @@
+# Copyright (C) 2006 Aaron Bentley
+# <aaron.bentley at utoronto.ca>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+from os.path import expanduser
+
+from bzrlib.commands import get_cmd_object
+from bzrlib.patches import (hunk_from_header, InsertLine, RemoveLine,
+                            ContextLine, Hunk)
+
+import terminal
+
+class LineParser(object):
+    def parse_line(self, line):
+        if line.startswith("@"):
+            return hunk_from_header(line)
+        elif line.startswith("+"):
+            return InsertLine(line[1:])
+        elif line.startswith("-"):
+            return RemoveLine(line[1:])
+        elif line.startswith(" "):
+            return ContextLine(line[1:])
+        else:
+            return line
+
+
+class DiffWriter(object):
+
+    colors = {
+        'metaline':    'darkyellow',
+        'plain':       'darkwhite',
+        'newtext':     'darkblue',
+        'oldtext':     'darkred',
+        'diffstuff':   'darkgreen'
+    }
+
+    def __init__(self, target):
+        self.target = target
+        self.lp = LineParser()
+        self.chunks = []
+        self._read_colordiffrc()
+
+    def _read_colordiffrc(self):
+        path = expanduser('~/.colordiffrc')
+        try:
+            f = open(path, 'r')
+        except IOError:
+            return
+
+        for line in f.readlines():
+            try:
+                key, val = line.split('=')
+            except ValueError:
+                continue
+
+            key = key.strip()
+            val = val.strip()
+
+            tmp = val
+            if val.startswith('dark'):
+                tmp = val[4:]
+
+            if tmp not in terminal.colors:
+                continue
+
+            self.colors[key] = val
+
+    def colorstring(self, type, string):
+        color = self.colors[type]
+        if color is not None:
+            string = terminal.colorstring(str(string), color)
+        self.target.write(string)
+
+    def write(self, text):
+        newstuff = text.split('\n')
+        for newchunk in newstuff[:-1]:
+            self._writeline(''.join(self.chunks + [newchunk, '\n']))
+            self.chunks = []
+        self.chunks = [newstuff[-1]]
+
+    def _writeline(self, line):
+        item = self.lp.parse_line(line)
+        if isinstance(item, Hunk):
+            line_class = 'diffstuff'
+        elif isinstance(item, InsertLine):
+            line_class = 'newtext'
+        elif isinstance(item, RemoveLine):
+            line_class = 'oldtext'
+        elif isinstance(item, basestring) and item.startswith('==='):
+            line_class = 'metaline'
+        else:
+            line_class = 'plain'
+        self.colorstring(line_class, str(item))
+
+    def flush(self):
+        self.target.flush()
+
+def colordiff(self, *args, **kwargs):
+    real_stdout = sys.stdout
+    sys.stdout = DiffWriter(real_stdout)
+    try:
+        get_cmd_object('diff').run(*args, **kwargs)
+    finally:
+        sys.stdout = real_stdout
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/changelog /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/changelog
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/changelog	2006-08-19 21:52:01.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/changelog	2006-08-19 21:52:02.000000000 +0200
@@ -1,3 +1,20 @@
+bzrtools (0.9.0-0.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * New upstream release and resync with Ubuntu.
+  * Add rsync and python-medusa to Build-Depends (necessary for tests).
+  * Adjust dependencies
+     + remove bzr (>= 0.8.1), bzr (<< 0.9). (Closes: #382561)
+     + add bzr (>= 0.9), bzr (<< 0.10).
+     + add pybaz. (Closes: #362587)
+  * Remove debian/dirs. (Closes: #368729)
+  * Use python-support to follow new Python Policy
+    thanks to Arnaud Fontaine
+     + install *.py in python-support directory.
+  * Add preamble to debian/copyright.
+
+ -- Mohammed Adnène Trojette <adn+deb at diwi.org>  Sat, 19 Aug 2006 21:50:36 +0200
+
 bzrtools (0.8.1-1.1) unstable; urgency=low
 
   * Low threshold NMU.
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/control /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/control
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/control	2006-08-19 21:52:01.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/control	2006-08-19 21:52:02.000000000 +0200
@@ -3,14 +3,14 @@
 Priority: optional
 Maintainer: Utnubu Team <utnubu-maintainers at lists.alioth.debian.org>
 Uploaders: Gustavo Franco <stratus at debian.org>
-Build-Depends: cdbs (>= 0.4.26-2), debhelper (>= 4.1.0)
-Build-Depends-Indep: bzr (>= 0.8-1), python-dev
+Build-Depends: cdbs (>= 0.4.43), debhelper (>= 5.0.37.2), python-dev (>= 2.3.5-11), python-support (>= 0.4.0), rsync, python-medusa
+Build-Depends-Indep: bzr (>= 0.9)
 Standards-Version: 3.7.2
 
 Package: bzrtools
 Architecture: all
-Depends: ${python:Depends}, bzr (>= 0.8-1), bzr (<< 0.9)
-Suggests: rsync (>= 2.6.6-1), graphviz (>= 2.2.1-1sarge1), librsvg2-bin (>= 2.9.5-6), patch (>= 2.5.9-2), python2.4-paramiko
+Depends: ${python:Depends}, bzr (>= 0.0), bzr (<< 0.10)
+Suggests: rsync (>= 2.6.6-1), graphviz (>= 2.2.1-1sarge1), librsvg2-bin (>= 2.9.5-6), patch (>= 2.5.9-2), python-paramiko, pybaz
 Description: Collection of tools for bzr
  This package contains a collection of plugins for bzr - a distributed 
  version control system. Some of these tools may work their way into 
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/copyright /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/copyright
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/copyright	2006-08-19 21:52:01.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/copyright	2006-08-19 21:52:02.000000000 +0200
@@ -7,6 +7,20 @@
 
 License: GNU GPLv2
 
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License with
+the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+if not, write to the Free Software Foundation, Inc., 51 Franklin St,
+Fifth Floor, Boston, MA 02110-1301, USA.
+
 On Debian systems, the complete text of the GNU General Public
 License can be found in /usr/share/common-licenses/GPL file.
-
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/dirs /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/dirs
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/dirs	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/dirs	1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-usr/bin
-usr/sbin
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/install /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/install
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/install	2006-08-19 21:52:01.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/install	2006-08-11 15:10:54.000000000 +0200
@@ -1 +1 @@
-*.py /usr/lib/python2.4/site-packages/bzrlib/plugins/bzrtools
+tests/*.py /usr/lib/python2.4/site-packages/bzrlib/plugins/bzrtools/tests
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/pycompat /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/pycompat
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/pycompat	1970-01-01 01:00:00.000000000 +0100
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/pycompat	2006-08-19 21:52:02.000000000 +0200
@@ -0,0 +1,2 @@
+2
+2
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/pyversions /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/pyversions
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/pyversions	1970-01-01 01:00:00.000000000 +0100
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/pyversions	2006-08-19 21:52:02.000000000 +0200
@@ -0,0 +1,2 @@
+2.4
+2.4
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/rules /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/rules
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/debian/rules	2006-08-19 21:52:01.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/debian/rules	2006-08-19 21:52:02.000000000 +0200
@@ -1,9 +1,10 @@
 #!/usr/bin/make -f
 # This file is public domain.
   
-include /usr/share/cdbs/1/rules/debhelper.mk
+DEB_PYTHON_SYSTEM=pysupport
 
-# Hook to add dh_python
-$(patsubst %,binary-install/%,$(DEB_PACKAGES)) :: binary-install/%:
-	dh_python -p$(cdbs_curpkg)
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/python-distutils.mk
 
+common-post-build-indep::
+	PYTHONPATH=. BZR_PLUGIN_PATH=build/lib/bzrlib/plugins/ bzr selftest bzrtools -v
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/errors.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/errors.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/errors.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/errors.py	2006-08-11 15:10:54.000000000 +0200
@@ -27,6 +27,15 @@
 class PatchFailed(Exception):
     """Failed applying patch!"""
 
+
+class NoColor(Exception):
+    """Color not available on this terminal."""
+
+
+class NoBzrtoolsColor(Exception):
+    """Bzrtools is required for color display"""
+
+
 class NotCheckout(CommandError):
     """Specified path is not a checkout."""
     def __init__(self, path):
@@ -36,3 +45,16 @@
     """This checkout contains uncommitted changes"""
     def __init__(self):
         CommandError.__init__(self, "This checkout has uncommitted changes")
+
+class ParentMissingRevisions(CommandError):
+    """The parent branch is missing revisions."""
+    def __init__(self, parent):
+        CommandError.__init__(self, 
+            "The parent branch %s is missing revisions from this branch." % 
+            parent)
+        self.parent = parent
+
+class NoParent(CommandError):
+    def __init__(self):
+        CommandError.__init__(self, "There is no parent, so deleting the"
+                                    " branch could destroy data.")
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/hunk_selector.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/hunk_selector.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/hunk_selector.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/hunk_selector.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,12 +1,30 @@
-#!/usr/bin/python
+import sys
 
 from userinteractor import UserInteractor, UserOption
+from errors import NoColor
 import copy
 
 class HunkSelector:
     strings = {}
 
-    def __init__(self, patches):
+    def __init__(self, patches, color=None):
+        if color is True or color is None:
+            try:
+                from colordiff import DiffWriter
+                from terminal import has_ansi_colors
+                if has_ansi_colors():
+                    self.diff_stream = DiffWriter(sys.stdout)
+                else:
+                    if color is True:
+                        raise NoColor()
+                    self.diff_stream = sys.stdout
+            except ImportError:
+                if color is True:
+                    raise NoBzrtoolsColor()
+                self.diff_stream = sys.stdout
+        else:
+            self.diff_stream = sys.stdout
+            
         self.standard_options = [
             UserOption('y', self._selected, self.strings['select_desc'],
                 default=True),
@@ -65,7 +83,8 @@
     # Called once for each hunk
     def _hunk_callback(self, hunk, count):
         if self.last_printed != count:
-            print hunk.patch.get_header(), hunk
+            self.diff_stream.write(str(hunk.patch.get_header()))
+            self.diff_stream.write(str(hunk))
             self.last_printed = count
 
         if hunk.selected:
@@ -153,7 +172,7 @@
         return (selected_patches, unselected_patches)
 
 class ShelveHunkSelector(HunkSelector):
-    def __init__(self, patches):
+    def __init__(self, patches, color=None):
         self.strings = {}
         self.strings['status_selected'] = '%d hunks to be shelved'
         self.strings['status_unselected'] = '%d hunks to be kept'
@@ -162,10 +181,10 @@
         self.strings['finish_desc'] = 'shelve selected changes.'
         self.strings['prompt'] = 'Shelve this change? (%(count)d of %(total)d)'
         self.strings['end_prompt'] = 'Shelve these changes?'
-        HunkSelector.__init__(self, patches)
+        HunkSelector.__init__(self, patches, color)
 
 class UnshelveHunkSelector(HunkSelector):
-    def __init__(self, patches):
+    def __init__(self, patches, color=None):
         self.strings = {}
         self.strings['status_selected'] = '%d hunks to be unshelved'
         self.strings['status_unselected'] = '%d hunks left on shelf'
@@ -175,4 +194,4 @@
         self.strings['prompt'] = 'Unshelve this change? ' \
             '(%(count)d of %(total)d)'
         self.strings['end_prompt'] = 'Unshelve these changes?'
-        HunkSelector.__init__(self, patches)
+        HunkSelector.__init__(self, patches, color)
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/__init__.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/__init__.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/__init__.py	2006-08-19 21:52:01.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/__init__.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,8 +1,6 @@
-#!/usr/bin/python
 """\
 Various useful plugins for working with bzr.
 """
-import bzrlib.commands
 import rspush
 from errors import CommandError
 from patchsource import BzrPatchSource
@@ -10,49 +8,52 @@
 from switch import cmd_switch
 import sys
 import os.path
-from bzrlib.option import Option
+
+import bzrlib.builtins
 import bzrlib.branch
+import bzrlib.commands
+from bzrlib.commands import get_cmd_object
 from bzrlib.errors import BzrCommandError
+from bzrlib.help import command_usage
+import bzrlib.ignores
+from bzrlib.option import Option
 sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), 
                                                  "external")))
 
-try:
-    import bzrlib.ignores
-except ImportError:
-    # Bzr 0.8 we had to modify the DEFAULT_IGNORE list to 
-    # auto ignore certain files
-    from bzrlib import DEFAULT_IGNORE
-    DEFAULT_IGNORE.append('./.shelf')
-    DEFAULT_IGNORE.append('./.bzr-shelf*')
-else:
-    # bzr 0.9 introduced add_runtime_ignores as a more explicit method
-    # of ignoring certain files
-    _add_runtime_ignores = getattr(bzrlib.ignores, 'add_runtime_ignores', None)
-    if _add_runtime_ignores:
-        _add_runtime_ignores(['./.shelf', './.bzr-shelf*'])
-
-
-Option.OPTIONS['ignored'] = Option('ignored',
-        help='delete all ignored files.')
-Option.OPTIONS['detritus'] = Option('detritus',
-        help='delete conflict files merge backups, and failed selftest dirs.' +
-              '(*.THIS, *.BASE, *.OTHER, *~, *.tmp')
-Option.OPTIONS['dry-run'] = Option('dry-run',
-        help='show files to delete instead of deleting them.')
+bzrlib.ignores.add_runtime_ignores(['./.shelf', './.bzr-shelf*'])
+
 
 class cmd_clean_tree(bzrlib.commands.Command):
-    """Remove unwanted files from working tree.  <BZRTOOLS>
-    Normally, ignored files are left alone.
-    """
-    takes_options = ['ignored', 'detritus', 'dry-run']
-    def run(self, ignored=False, detritus=False, dry_run=False):
-        from clean_tree import clean_tree
-        clean_tree('.', ignored=ignored, detritus=detritus, dry_run=dry_run)
+    """Remove unwanted files from working tree.
 
-Option.OPTIONS['merge-branch'] = Option('merge-branch',type=str)
+    By default, only unknown files, not ignored files, are deleted.  Versioned
+    files are never deleted.
+
+    Another class is 'detritus', which includes files emitted by bzr during
+    normal operations and selftests.  (The value of these files decreases with
+    time.)
+
+    If no options are specified, unknown files are deleted.  Otherwise, option
+    flags are respected, and may be combined.
+
+    To check what clean-tree will do, use --dry-run.
+    """
+    takes_options = [Option('ignored', help='delete all ignored files.'), 
+                     Option('detritus', help='delete conflict files, merge'
+                            ' backups, and failed selftest dirs.'), 
+                     Option('unknown', 
+                            help='delete files unknown to bzr.  (default)'),
+                     Option('dry-run', help='show files to delete instead of'
+                            ' deleting them.')]
+    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
+        from clean_tree import clean_tree
+        if not (unknown or ignored or detritus):
+            unknown = True
+        clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus, 
+                   dry_run=dry_run)
 
 class cmd_graph_ancestry(bzrlib.commands.Command):
-    """Produce ancestry graphs using dot.  <BZRTOOLS>
+    """Produce ancestry graphs using dot.
     
     Output format is detected according to file extension.  Some of the more
     common output formats are html, png, gif, svg, ps.  An extension of '.dot'
@@ -104,7 +105,7 @@
                                   not no_antialias, merge_branch, ranking)
 
 class cmd_fetch_ghosts(bzrlib.commands.Command):
-    """Attempt to retrieve ghosts from another branch.  <BZRTOOLS>
+    """Attempt to retrieve ghosts from another branch.
     If the other branch is not supplied, the last-pulled branch is used.
     """
     aliases = ['fetch-missing']
@@ -116,14 +117,13 @@
 
 strip_help="""Strip the smallest prefix containing num leading slashes  from \
 each file name found in the patch file."""
-Option.OPTIONS['strip'] = Option('strip', type=int, help=strip_help)
 Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
                                 help="""Handle extra bzr tags""")
 class cmd_patch(bzrlib.commands.Command):
-    """Apply a named patch to the current tree.  <BZRTOOLS>
+    """Apply a named patch to the current tree.
     """
     takes_args = ['filename?']
-    takes_options = ['strip']
+    takes_options = [Option('strip', type=int, help=strip_help)]
     def run(self, filename=None, strip=-1, bzrdiff=0):
         from patch import patch
         from bzrlib.workingtree import WorkingTree
@@ -135,7 +135,7 @@
         return patch(wt, filename, strip, legacy= not bzrdiff)
 
 class cmd_shelve(bzrlib.commands.Command):
-    """Temporarily set aside some changes from the current tree.  <BZRTOOLS>
+    """Temporarily set aside some changes from the current tree.
 
     Shelve allows you to temporarily put changes you've made "on the shelf",
     ie. out of the way, until a later time when you can bring them back from
@@ -149,20 +149,25 @@
     By default shelve asks you what you want to shelve, press '?' at the
     prompt to get help. To shelve everything run shelve --all.
 
-    You can put multiple items on the shelf, each time you run unshelve the
-    most recently shelved changes will be reinstated.
-
     If filenames are specified, only the changes to those files will be
     shelved, other files will be left untouched.
 
     If a revision is specified, changes since that revision will be shelved.
+
+    You can put multiple items on the shelf. Normally each time you run
+    unshelve the most recently shelved changes will be reinstated. However,
+    you can also unshelve changes in a different order by explicitly
+    specifiying which changes to unshelve. This works best when the changes
+    don't depend on each other.
     """
 
     takes_args = ['file*']
     takes_options = ['message', 'revision',
-            Option('all', help='Shelve all changes without prompting')]
+            Option('all', help='Shelve all changes without prompting'), 
+            Option('no-color', help='Never display changes in color')]
 
-    def run(self, all=False, file_list=None, message=None, revision=None):
+    def run(self, all=False, file_list=None, message=None, revision=None,
+            no_color=False):
         if revision is not None and revision:
             if len(revision) == 1:
                 revision = revision[0]
@@ -172,72 +177,137 @@
 
         source = BzrPatchSource(revision, file_list)
         s = Shelf(source.base)
-        s.shelve(source, all, message)
+        s.shelve(source, all, message, no_color)
         return 0
 
-class cmd_shelf(bzrlib.commands.Command):
-    """Perform various operations on your shelved patches. See also shelve.
 
-    Subcommands:
-        list   (ls)           List the patches on the current shelf.
-        delete (del) <patch>  Delete a patch from the current shelf.
-        switch       <shelf>  Switch to the named shelf, create it if necessary.
-        show         <patch>  Show the contents of the specified patch.
-        upgrade               Upgrade old format shelves.
-    """
+# The following classes are only used as subcommands for 'shelf', they're
+# not to be registered directly with bzr.
+
+class cmd_shelf_list(bzrlib.commands.Command):
+    """List the patches on the current shelf."""
+    aliases = ['list', 'ls']
+    def run(self):
+        self.shelf.list()
+
+
+class cmd_shelf_delete(bzrlib.commands.Command):
+    """Delete the patch from the current shelf."""
+    aliases = ['delete', 'del']
+    takes_args = ['patch']
+    def run(self, patch):
+        self.shelf.delete(patch)
+
+
+class cmd_shelf_switch(bzrlib.commands.Command):
+    """Switch to the other shelf, create it if necessary."""
+    aliases = ['switch']
+    takes_args = ['othershelf']
+    def run(self, othershelf):
+        s = Shelf(self.shelf.base, othershelf)
+        s.make_default()
+
+
+class cmd_shelf_show(bzrlib.commands.Command):
+    """Show the contents of the specified or topmost patch."""
+    aliases = ['show', 'cat', 'display']
+    takes_args = ['patch?']
+    def run(self, patch=None):
+        self.shelf.display(patch)
+
+
+class cmd_shelf_upgrade(bzrlib.commands.Command):
+    """Upgrade old format shelves."""
+    aliases = ['upgrade']
+    def run(self):
+        self.shelf.upgrade()
+
+
+class cmd_shelf(bzrlib.commands.Command):
+    """Perform various operations on your shelved patches. See also shelve."""
     takes_args = ['subcommand', 'args*']
 
+    subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
+        cmd_shelf_show, cmd_shelf_upgrade]
+
     def run(self, subcommand, args_list):
         import sys
 
+        cmd = self._get_cmd_object(subcommand)
         source = BzrPatchSource()
         s = Shelf(source.base)
+        cmd.shelf = s
+        return cmd.run_argv_aliases(args_list)
 
-        if subcommand == 'ls' or subcommand == 'list':
-            self.__check_no_args(args_list, "shelf list takes no arguments!")
-            s.list()
-        elif subcommand == 'delete' or subcommand == 'del':
-            self.__check_one_arg(args_list, "shelf delete takes one argument!")
-            s.delete(args_list[0])
-        elif subcommand == 'switch':
-            self.__check_one_arg(args_list, "shelf switch takes one argument!")
-            s = Shelf(source.base, args_list[0])
-            s.make_default()
-        elif subcommand == 'show':
-            self.__check_one_arg(args_list, "shelf show takes one argument!")
-            s.display(args_list[0])
-        elif subcommand == 'upgrade':
-            self.__check_no_args(args_list, "shelf upgrade takes no arguments!")
-            s.upgrade()
-        else:
-            raise CommandError("Unknown shelf subcommand '%s'" % subcommand)
+    def _get_cmd_object(self, cmd_name):
+        for cmd_class in self.subcommands:
+            for alias in cmd_class.aliases:
+                if alias == cmd_name:
+                    return cmd_class()
+        raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
+
+    def help(self):
+        text = ["%s\n\nSubcommands:\n" % self.__doc__]
+
+        for cmd_class in self.subcommands:
+            text.extend(self.sub_help(cmd_class) + ['\n'])
+
+        return ''.join(text)
+
+    def sub_help(self, cmd_class):
+        text = []
+        cmd_obj = cmd_class()
+        indent = 2 * ' '
+
+        usage = command_usage(cmd_obj)
+        usage = usage.replace('bzr shelf-', '')
+        text.append('%s%s\n' % (indent, usage))
+
+        text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
+
+        # Somewhat copied from bzrlib.help.help_on_command_options
+        option_help = []
+        for option_name, option in sorted(cmd_obj.options().items()):
+            if option_name == 'help':
+                continue
+            option_help.append('%s--%s' % (3 * indent, option_name))
+            if option.type is not None:
+                option_help.append(' %s' % option.argname.upper())
+            if option.short_name():
+                option_help.append(', -%s' % option.short_name())
+            option_help.append('%s%s\n' % (2 * indent, option.help))
+
+        if len(option_help) > 0:
+            text.append('%soptions:\n' % (2 * indent))
+            text.extend(option_help)
+
+        return text
 
-    def __check_one_arg(self, args, msg):
-        if args is None or len(args) != 1:
-            raise CommandError(msg)
-
-    def __check_no_args(self, args, msg):
-        if args is not None:
-            raise CommandError(msg)
 
 
 class cmd_unshelve(bzrlib.commands.Command):
-    """Restore the most recently shelved changes to current tree.  <BZRTOOLS>
+    """Restore shelved changes.
+
+    By default the most recently shelved changes are restored. However if you
+    specify a patch by name those changes will be restored instead.
+
     See 'shelve' for more information.
     """
     takes_options = [
             Option('all', help='Unshelve all changes without prompting'),
             Option('force', help='Force unshelving even if errors occur'),
-    ]
-    def run(self, all=False, force=False):
+            Option('no-color', help='Never display changes in color')
+        ]
+    takes_args = ['patch?']
+    def run(self, patch=None, all=False, force=False, no_color=False):
         source = BzrPatchSource()
         s = Shelf(source.base)
-        s.unshelve(source, all, force)
+        s.unshelve(source, patch, all, force, no_color)
         return 0
 
 
 class cmd_shell(bzrlib.commands.Command):
-    """Begin an interactive shell tailored for bzr.  <BZRTOOLS>
+    """Begin an interactive shell tailored for bzr.
     Bzr commands can be used without typing bzr first, and will be run natively
     when possible.  Tab completion is tailored for bzr.  The shell prompt shows
     the branch nick, revno, and path.
@@ -260,7 +330,7 @@
 
 class cmd_branch_history(bzrlib.commands.Command):
     """\
-    Display the development history of a branch  <BZRTOOLS>.
+    Display the development history of a branch.
 
     Each different committer or branch nick is considered a different line of
     development.  Committers are treated as the same if they have the same
@@ -274,10 +344,14 @@
 
 class cmd_zap(bzrlib.commands.Command):
     """\
-    Remove a checkout, if it can be done safely. <BZRTOOLS>
+    Remove a lightweight checkout, if it can be done safely.
+
+    This command will remove a lightweight checkout without losing data.  That
+    means it only removes lightweight checkouts, and only if they have no
+    uncommitted changes.
 
-    This command will remove a checkout without losing data.  That means
-    it only removes checkouts, and only if they have no uncommitted changes.
+    If --branch is specified, the branch will be deleted too, but only if the
+    the branch has no new commits (relative to its parent).
     """
     takes_options = [Option("branch", help="Remove associtated branch from"
                                            " repository")]
@@ -289,7 +363,7 @@
 
 class cmd_cbranch(bzrlib.commands.Command):
     """
-    Create a new checkout, associated with a new repository branch. <BZRTOOLS>
+    Create a new checkout, associated with a new repository branch.
     
     When you cbranch, bzr looks up the repository associated with your current
     directory in branches.conf.  It creates a new branch in that repository
@@ -307,15 +381,16 @@
     be produced if the source branch is in 0.7 format (or earlier).
     """
     takes_options = [Option("lightweight", 
-                            help="Create a lightweight checkout")]
+                            help="Create a lightweight checkout"), 'revision']
     takes_args = ["source", "target?"]
-    def run(self, source, target=None, lightweight=False):
+    def run(self, source, target=None, lightweight=False, revision=None):
         from cbranch import cbranch
-        return cbranch(source, target, lightweight=lightweight)
+        return cbranch(source, target, lightweight=lightweight, 
+                       revision=revision)
 
 
 class cmd_branches(bzrlib.commands.Command):
-    """Scan a location for branches <BZRTOOLS>"""
+    """Scan a location for branches"""
     takes_args = ["location?"]
     def run(self, location=None):
         from branches import branches
@@ -323,7 +398,7 @@
 
 
 class cmd_multi_pull(bzrlib.commands.Command):
-    """Pull all the branches under a location, e.g. a repository. <BZRTOOLS>
+    """Pull all the branches under a location, e.g. a repository.
     
     Both branches present in the directory and the branches of checkouts are
     pulled.
@@ -379,16 +454,58 @@
         from branch_mark import branch_mark
         branch_mark(mark, branch, delete)
 
+class cmd_import(bzrlib.commands.Command):
+    """Import sources from a tarball
+    
+    This command will import a tarball into a bzr tree, replacing any versioned
+    files already present.  If a directory is specified, it is used as the
+    target.  If the directory does not exist, or is not versioned, it is
+    created.
+
+    Tarballs may be gzip or bzip2 compressed.  This is autodetected.
+
+    If the tarball has a single root directory, that directory is stripped
+    when extracting the tarball.
+    """
+    
+    takes_args = ['source', 'tree?']
+    def run(self, source, tree=None):
+        from upstream_import import do_import
+        do_import(source, tree)
+
+class cmd_shove(bzrlib.commands.Command):
+    """Apply uncommitted changes to another tree
+    
+    This is useful when you start to make changes in one tree, then realize
+    they should really be done in a different tree.
+
+    Shove is implemented using merge, so:
+     - All changes, including renames and adds, will be applied.
+     - No changes that have already been applied will be applied.
+     - If the target is significantly different from the source, conflicts may
+       be produced.
+    """
+
+    takes_args = ['target', 'source?']
+    def run(self, target, source='.'):
+        from shove import do_shove
+        do_shove(source, target)
+
+class cmd_cdiff(bzrlib.commands.Command):
+    """A color version of bzr's diff"""
+    takes_args = property(lambda x: get_cmd_object('diff').takes_args)
+    takes_options = property(lambda x: get_cmd_object('diff').takes_options)
+    def run(*args, **kwargs):
+        from colordiff import colordiff
+        colordiff(*args, **kwargs)
 
 commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
             cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
             cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches, 
-            cmd_multi_pull, cmd_switch, cmd_branch_mark]
-
+            cmd_multi_pull, cmd_switch, cmd_branch_mark, cmd_import, cmd_shove,
+            cmd_cdiff]
 
 
-
-import bzrlib.builtins
 commands.append(rspush.cmd_rspush)
 
 from errors import NoPyBaz
@@ -399,7 +516,7 @@
 
 except NoPyBaz:
     class cmd_baz_import_branch(bzrlib.commands.Command):
-        """Disabled. (Requires PyBaz)   <BZRTOOLS>"""
+        """Disabled. (Requires PyBaz)"""
         takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
         takes_options = ['verbose', Option('max-count', type=int)]
         def run(self, to_location=None, from_branch=None, fast=False, 
@@ -409,7 +526,7 @@
 
 
     class cmd_baz_import(bzrlib.commands.Command):
-        """Disabled. (Requires PyBaz)   <BZRTOOLS>"""
+        """Disabled. (Requires PyBaz)"""
         takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
         takes_options = ['verbose', Option('prefixes', type=str,
                          help="Prefixes of branches to import")]
@@ -425,21 +542,26 @@
 
 
 def test_suite():
-    import baz_import
     from bzrlib.tests.TestUtil import TestLoader
     import tests
     from doctest import DocTestSuite, ELLIPSIS
     from unittest import TestSuite
-    import clean_tree
+    import tests.clean_tree
+    import upstream_import
     import zap
     import tests.blackbox
     import tests.shelf_tests
     result = TestSuite()
     result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
-    result.addTest(clean_tree.test_suite())
-    result.addTest(DocTestSuite(baz_import))
+    result.addTest(tests.clean_tree.test_suite())
+    try:
+        import baz_import
+        result.addTest(DocTestSuite(baz_import))
+    except NoPyBaz:
+        pass
     result.addTest(tests.test_suite())
     result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
     result.addTest(tests.blackbox.test_suite())
+    result.addTest(upstream_import.test_suite())
     result.addTest(zap.test_suite())
     return result
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/NEWS /tmp/2DwbKrAPHs/bzrtools-0.9.0/NEWS
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/NEWS	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/NEWS	2006-08-11 15:10:54.000000000 +0200
@@ -1,3 +1,27 @@
+Aug 11
+* RELEASE: bzrtools 0.9.0
+
+Aug 6
+* Add --no-color option to shelve/unshelve
+
+July 13
+* clean-tree no longer treats --detritus or --ignored as including --unknowns
+
+July 11
+* Shelf colorizing
+
+June 14
+* Add 'shove' command, to move changes to a different tree
+
+June 3
+* clean-tree tweaks
+
+May 30
+* test suite updates
+
+May 18
+* Add 'import' command, to import tarballs
+
 May 11
 * RELEASE: bzrtools 0.8.1
 * Fixed test case failure
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/NEWS.Shelf /tmp/2DwbKrAPHs/bzrtools-0.9.0/NEWS.Shelf
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/NEWS.Shelf	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/NEWS.Shelf	2006-08-11 15:10:54.000000000 +0200
@@ -1,6 +1,25 @@
 NEWS
 
-Shelf version 0.8-beta:
+Shelf version 0.9-beta (WIP):
+
+ * You can now unshelve patches in arbitrary order. This works best if the
+   shelved patches don't depend on each other, otherwise you'll find you have
+   trouble unshelving them (as patch(1) won't apply them cleanly). To work
+   around this you can use the --force option to unshelve. Handle with care.
+
+ * The patch option to 'show' is now optional. If ommitted the top most patch
+   will be displayed.
+
+Shelf version 0.8 (michael at ellerman.id.au-20060508134216-454ada48469c6daf):
+
+ * Shelves are stored in -p0 format to match bzr's native diff format.
+   Crazy -p1 format diffs are also supported.
+
+ * Patches are backed up when you unshelve them. They're stored in xx~ where
+   xx is the id of the patch. They'll be overwritten the next time you shelve
+   to patch xx.
+
+ * Support for bzr version 0.7 and 0.8, w/thanks to marienz on #bzr.
 
  * Shelves are now stored in .shelf. The bzr API for storing things under
    .bzr is not rich enough at the moment to do all the things the shelf code
@@ -14,7 +33,7 @@
     switch       <shelf>  Switch to the named shelf, create it if necessary.
     show         <patch>  Show the contents of the specified patch.
 
- * The question when shelving with is now "Shelve this change", rather than
+ * The question when shelving is now "Shelve this change", rather than
    "Keep this change", and the y/n choices have been reversed to suit
    obviously. This seems like a more logical choice of wording.
 
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/patches.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/patches.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/patches.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/patches.py	1970-01-01 01:00:00.000000000 +0100
@@ -1,562 +0,0 @@
-# Copyright (C) 2004, 2005 Aaron Bentley
-# <aaron.bentley at utoronto.ca>
-#
-#    This program is free software; you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation; either version 2 of the License, or
-#    (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#
-#    You should have received a copy of the GNU General Public License
-#    along with this program; if not, write to the Free Software
-#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-class PatchSyntax(Exception):
-    def __init__(self, msg):
-        Exception.__init__(self, msg)
-
-
-class MalformedPatchHeader(PatchSyntax):
-    def __init__(self, desc, line):
-        self.desc = desc
-        self.line = line
-        msg = "Malformed patch header.  %s\n%r" % (self.desc, self.line)
-        PatchSyntax.__init__(self, msg)
-
-class MalformedHunkHeader(PatchSyntax):
-    def __init__(self, desc, line):
-        self.desc = desc
-        self.line = line
-        msg = "Malformed hunk header.  %s\n%r" % (self.desc, self.line)
-        PatchSyntax.__init__(self, msg)
-
-class MalformedLine(PatchSyntax):
-    def __init__(self, desc, line):
-        self.desc = desc
-        self.line = line
-        msg = "Malformed line.  %s\n%s" % (self.desc, self.line)
-        PatchSyntax.__init__(self, msg)
-
-def get_patch_names(iter_lines):
-    try:
-        line = iter_lines.next()
-        if not line.startswith("--- "):
-            raise MalformedPatchHeader("No orig name", line)
-        else:
-            orig_name = line[4:].rstrip("\n")
-    except StopIteration:
-        raise MalformedPatchHeader("No orig line", "")
-    try:
-        line = iter_lines.next()
-        if not line.startswith("+++ "):
-            raise PatchSyntax("No mod name")
-        else:
-            mod_name = line[4:].rstrip("\n")
-    except StopIteration:
-        raise MalformedPatchHeader("No mod line", "")
-    return (orig_name, mod_name)
-
-def parse_range(textrange):
-    """Parse a patch range, handling the "1" special-case
-
-    :param textrange: The text to parse
-    :type textrange: str
-    :return: the position and range, as a tuple
-    :rtype: (int, int)
-    """
-    tmp = textrange.split(',')
-    if len(tmp) == 1:
-        pos = tmp[0]
-        range = "1"
-    else:
-        (pos, range) = tmp
-    pos = int(pos)
-    range = int(range)
-    return (pos, range)
-
- 
-def hunk_from_header(line):
-    if not line.startswith("@@") or not line.endswith("@@\n") \
-        or not len(line) > 4:
-        raise MalformedHunkHeader("Does not start and end with @@.", line)
-    try:
-        (orig, mod) = line[3:-4].split(" ")
-    except Exception, e:
-        raise MalformedHunkHeader(str(e), line)
-    if not orig.startswith('-') or not mod.startswith('+'):
-        raise MalformedHunkHeader("Positions don't start with + or -.", line)
-    try:
-        (orig_pos, orig_range) = parse_range(orig[1:])
-        (mod_pos, mod_range) = parse_range(mod[1:])
-    except Exception, e:
-        raise MalformedHunkHeader(str(e), line)
-    if mod_range < 0 or orig_range < 0:
-        raise MalformedHunkHeader("Hunk range is negative", line)
-    return Hunk(orig_pos, orig_range, mod_pos, mod_range)
-
-
-class HunkLine:
-    def __init__(self, contents):
-        self.contents = contents
-
-    def get_str(self, leadchar):
-        if self.contents == "\n" and leadchar == " " and False:
-            return "\n"
-        if not self.contents.endswith('\n'):
-            terminator = '\n' + NO_NL
-        else:
-            terminator = ''
-        return leadchar + self.contents + terminator
-
-
-class ContextLine(HunkLine):
-    def __init__(self, contents):
-        HunkLine.__init__(self, contents)
-
-    def __str__(self):
-        return self.get_str(" ")
-
-
-class InsertLine(HunkLine):
-    def __init__(self, contents):
-        HunkLine.__init__(self, contents)
-
-    def __str__(self):
-        return self.get_str("+")
-
-
-class RemoveLine(HunkLine):
-    def __init__(self, contents):
-        HunkLine.__init__(self, contents)
-
-    def __str__(self):
-        return self.get_str("-")
-
-NO_NL = '\\ No newline at end of file\n'
-__pychecker__="no-returnvalues"
-
-def parse_line(line):
-    if line.startswith("\n"):
-        return ContextLine(line)
-    elif line.startswith(" "):
-        return ContextLine(line[1:])
-    elif line.startswith("+"):
-        return InsertLine(line[1:])
-    elif line.startswith("-"):
-        return RemoveLine(line[1:])
-    elif line == NO_NL:
-        return NO_NL
-    else:
-        raise MalformedLine("Unknown line type", line)
-__pychecker__=""
-
-
-class Hunk:
-    def __init__(self, orig_pos, orig_range, mod_pos, mod_range):
-        self.orig_pos = orig_pos
-        self.orig_range = orig_range
-        self.mod_pos = mod_pos
-        self.mod_range = mod_range
-        self.lines = []
-
-    def get_header(self):
-        return "@@ -%s +%s @@\n" % (self.range_str(self.orig_pos, 
-                                                   self.orig_range),
-                                    self.range_str(self.mod_pos, 
-                                                   self.mod_range))
-
-    def range_str(self, pos, range):
-        """Return a file range, special-casing for 1-line files.
-
-        :param pos: The position in the file
-        :type pos: int
-        :range: The range in the file
-        :type range: int
-        :return: a string in the format 1,4 except when range == pos == 1
-        """
-        if range == 1:
-            return "%i" % pos
-        else:
-            return "%i,%i" % (pos, range)
-
-    def __str__(self):
-        lines = [self.get_header()]
-        for line in self.lines:
-            lines.append(str(line))
-        return "".join(lines)
-
-    def shift_to_mod(self, pos):
-        if pos < self.orig_pos-1:
-            return 0
-        elif pos > self.orig_pos+self.orig_range:
-            return self.mod_range - self.orig_range
-        else:
-            return self.shift_to_mod_lines(pos)
-
-    def shift_to_mod_lines(self, pos):
-        assert (pos >= self.orig_pos-1 and pos <= self.orig_pos+self.orig_range)
-        position = self.orig_pos-1
-        shift = 0
-        for line in self.lines:
-            if isinstance(line, InsertLine):
-                shift += 1
-            elif isinstance(line, RemoveLine):
-                if position == pos:
-                    return None
-                shift -= 1
-                position += 1
-            elif isinstance(line, ContextLine):
-                position += 1
-            if position > pos:
-                break
-        return shift
-
-def iter_hunks(iter_lines):
-    hunk = None
-    for line in iter_lines:
-        if line == "\n":
-            if hunk is not None:
-                yield hunk
-                hunk = None
-            continue
-        if hunk is not None:
-            yield hunk
-        hunk = hunk_from_header(line)
-        orig_size = 0
-        mod_size = 0
-        while orig_size < hunk.orig_range or mod_size < hunk.mod_range:
-            hunk_line = parse_line(iter_lines.next())
-            hunk.lines.append(hunk_line)
-            if isinstance(hunk_line, (RemoveLine, ContextLine)):
-                orig_size += 1
-            if isinstance(hunk_line, (InsertLine, ContextLine)):
-                mod_size += 1
-    if hunk is not None:
-        yield hunk
-
-class Patch:
-    def __init__(self, oldname, newname):
-        self.oldname = oldname
-        self.newname = newname
-        self.hunks = []
-
-    def __str__(self):
-        ret = self.get_header() 
-        ret += "".join([str(h) for h in self.hunks])
-        return ret
-
-    def get_header(self):
-        return "--- %s\n+++ %s\n" % (self.oldname, self.newname)
-
-    def stats_str(self):
-        """Return a string of patch statistics"""
-        removes = 0
-        inserts = 0
-        for hunk in self.hunks:
-            for line in hunk.lines:
-                if isinstance(line, InsertLine):
-                     inserts+=1;
-                elif isinstance(line, RemoveLine):
-                     removes+=1;
-        return "%i inserts, %i removes in %i hunks" % \
-            (inserts, removes, len(self.hunks))
-
-    def pos_in_mod(self, position):
-        newpos = position
-        for hunk in self.hunks:
-            shift = hunk.shift_to_mod(position)
-            if shift is None:
-                return None
-            newpos += shift
-        return newpos
-            
-    def iter_inserted(self):
-        """Iteraties through inserted lines
-        
-        :return: Pair of line number, line
-        :rtype: iterator of (int, InsertLine)
-        """
-        for hunk in self.hunks:
-            pos = hunk.mod_pos - 1;
-            for line in hunk.lines:
-                if isinstance(line, InsertLine):
-                    yield (pos, line)
-                    pos += 1
-                if isinstance(line, ContextLine):
-                    pos += 1
-
-def parse_patch(iter_lines):
-    (orig_name, mod_name) = get_patch_names(iter_lines)
-    patch = Patch(orig_name, mod_name)
-    for hunk in iter_hunks(iter_lines):
-        patch.hunks.append(hunk)
-    return patch
-
-
-def iter_file_patch(iter_lines):
-    saved_lines = []
-    for line in iter_lines:
-        if line.startswith('*** '):
-            continue
-        if line.startswith('#'):
-            continue
-        if line.startswith('==='):
-            continue
-        elif line.startswith('--- '):
-            if len(saved_lines) > 0:
-                yield saved_lines
-            saved_lines = []
-        saved_lines.append(line)
-    if len(saved_lines) > 0:
-        yield saved_lines
-
-
-def iter_lines_handle_nl(iter_lines):
-    """
-    Iterates through lines, ensuring that lines that originally had no
-    terminating \n are produced without one.  This transformation may be
-    applied at any point up until hunk line parsing, and is safe to apply
-    repeatedly.
-    """
-    last_line = None
-    for line in iter_lines:
-        if line == NO_NL:
-            assert last_line.endswith('\n')
-            last_line = last_line[:-1]
-            line = None
-        if last_line is not None:
-            yield last_line
-        last_line = line
-    if last_line is not None:
-        yield last_line
-
-
-def parse_patches(iter_lines):
-    iter_lines = iter_lines_handle_nl(iter_lines)
-    return [parse_patch(f.__iter__()) for f in iter_file_patch(iter_lines)]
-
-
-def difference_index(atext, btext):
-    """Find the indext of the first character that differs betweeen two texts
-
-    :param atext: The first text
-    :type atext: str
-    :param btext: The second text
-    :type str: str
-    :return: The index, or None if there are no differences within the range
-    :rtype: int or NoneType
-    """
-    length = len(atext)
-    if len(btext) < length:
-        length = len(btext)
-    for i in range(length):
-        if atext[i] != btext[i]:
-            return i;
-    return None
-
-class PatchConflict(Exception):
-    def __init__(self, line_no, orig_line, patch_line):
-        orig = orig_line.rstrip('\n')
-        patch = str(patch_line).rstrip('\n')
-        msg = 'Text contents mismatch at line %d.  Original has "%s",'\
-            ' but patch says it should be "%s"' % (line_no, orig, patch)
-        Exception.__init__(self, msg)
-
-
-def iter_patched(orig_lines, patch_lines):
-    """Iterate through a series of lines with a patch applied.
-    This handles a single file, and does exact, not fuzzy patching.
-    """
-    if orig_lines is not None:
-        orig_lines = orig_lines.__iter__()
-    seen_patch = []
-    patch_lines = iter_lines_handle_nl(patch_lines.__iter__())
-    get_patch_names(patch_lines)
-    line_no = 1
-    for hunk in iter_hunks(patch_lines):
-        while line_no < hunk.orig_pos:
-            orig_line = orig_lines.next()
-            yield orig_line
-            line_no += 1
-        for hunk_line in hunk.lines:
-            seen_patch.append(str(hunk_line))
-            if isinstance(hunk_line, InsertLine):
-                yield hunk_line.contents
-            elif isinstance(hunk_line, (ContextLine, RemoveLine)):
-                orig_line = orig_lines.next()
-                if orig_line != hunk_line.contents:
-                    raise PatchConflict(line_no, orig_line, "".join(seen_patch))
-                if isinstance(hunk_line, ContextLine):
-                    yield orig_line
-                else:
-                    assert isinstance(hunk_line, RemoveLine)
-                line_no += 1
-                    
-import unittest
-import os.path
-class PatchesTester(unittest.TestCase):
-    def datafile(self, filename):
-        data_path = os.path.join(os.path.dirname(__file__), "testdata", 
-                                 filename)
-        return file(data_path, "rb")
-
-    def testValidPatchHeader(self):
-        """Parse a valid patch header"""
-        lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
-        (orig, mod) = get_patch_names(lines.__iter__())
-        assert(orig == "orig/commands.py")
-        assert(mod == "mod/dommands.py")
-
-    def testInvalidPatchHeader(self):
-        """Parse an invalid patch header"""
-        lines = "-- orig/commands.py\n+++ mod/dommands.py".split('\n')
-        self.assertRaises(MalformedPatchHeader, get_patch_names,
-                          lines.__iter__())
-
-    def testValidHunkHeader(self):
-        """Parse a valid hunk header"""
-        header = "@@ -34,11 +50,6 @@\n"
-        hunk = hunk_from_header(header);
-        assert (hunk.orig_pos == 34)
-        assert (hunk.orig_range == 11)
-        assert (hunk.mod_pos == 50)
-        assert (hunk.mod_range == 6)
-        assert (str(hunk) == header)
-
-    def testValidHunkHeader2(self):
-        """Parse a tricky, valid hunk header"""
-        header = "@@ -1 +0,0 @@\n"
-        hunk = hunk_from_header(header);
-        assert (hunk.orig_pos == 1)
-        assert (hunk.orig_range == 1)
-        assert (hunk.mod_pos == 0)
-        assert (hunk.mod_range == 0)
-        assert (str(hunk) == header)
-
-    def makeMalformed(self, header):
-        self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
-
-    def testInvalidHeader(self):
-        """Parse an invalid hunk header"""
-        self.makeMalformed(" -34,11 +50,6 \n")
-        self.makeMalformed("@@ +50,6 -34,11 @@\n")
-        self.makeMalformed("@@ -34,11 +50,6 @@")
-        self.makeMalformed("@@ -34.5,11 +50,6 @@\n")
-        self.makeMalformed("@@-34,11 +50,6@@\n")
-        self.makeMalformed("@@ 34,11 50,6 @@\n")
-        self.makeMalformed("@@ -34,11 @@\n")
-        self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
-        self.makeMalformed("@@ -34,11 +50,-6 @@\n")
-
-    def lineThing(self,text, type):
-        line = parse_line(text)
-        assert(isinstance(line, type))
-        assert(str(line)==text)
-
-    def makeMalformedLine(self, text):
-        self.assertRaises(MalformedLine, parse_line, text)
-
-    def testValidLine(self):
-        """Parse a valid hunk line"""
-        self.lineThing(" hello\n", ContextLine)
-        self.lineThing("+hello\n", InsertLine)
-        self.lineThing("-hello\n", RemoveLine)
-    
-    def testMalformedLine(self):
-        """Parse invalid valid hunk lines"""
-        self.makeMalformedLine("hello\n")
-    
-    def compare_parsed(self, patchtext):
-        lines = patchtext.splitlines(True)
-        patch = parse_patch(lines.__iter__())
-        pstr = str(patch)
-        i = difference_index(patchtext, pstr)
-        if i is not None:
-            print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
-        self.assertEqual (patchtext, str(patch))
-
-    def testAll(self):
-        """Test parsing a whole patch"""
-        patchtext = """--- orig/commands.py
-+++ mod/commands.py
-@@ -1337,7 +1337,8 @@
- 
-     def set_title(self, command=None):
-         try:
--            version = self.tree.tree_version.nonarch
-+            version = pylon.alias_or_version(self.tree.tree_version, self.tree,
-+                                             full=False)
-         except:
-             version = "[no version]"
-         if command is None:
-@@ -1983,7 +1984,11 @@
-                                          version)
-         if len(new_merges) > 0:
-             if cmdutil.prompt("Log for merge"):
--                mergestuff = cmdutil.log_for_merge(tree, comp_version)
-+                if cmdutil.prompt("changelog for merge"):
-+                    mergestuff = "Patches applied:\\n"
-+                    mergestuff += pylon.changelog_for_merge(new_merges)
-+                else:
-+                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
-                 log.description += mergestuff
-         log.save()
-     try:
-"""
-        self.compare_parsed(patchtext)
-
-    def testInit(self):
-        """Handle patches missing half the position, range tuple"""
-        patchtext = \
-"""--- orig/__init__.py
-+++ mod/__init__.py
-@@ -1 +1,2 @@
- __docformat__ = "restructuredtext en"
-+__doc__ = An alternate Arch commandline interface
-"""
-        self.compare_parsed(patchtext)
-        
-
-
-    def testLineLookup(self):
-        import sys
-        """Make sure we can accurately look up mod line from orig"""
-        patch = parse_patch(self.datafile("diff"))
-        orig = list(self.datafile("orig"))
-        mod = list(self.datafile("mod"))
-        removals = []
-        for i in range(len(orig)):
-            mod_pos = patch.pos_in_mod(i)
-            if mod_pos is None:
-                removals.append(orig[i])
-                continue
-            assert(mod[mod_pos]==orig[i])
-        rem_iter = removals.__iter__()
-        for hunk in patch.hunks:
-            for line in hunk.lines:
-                if isinstance(line, RemoveLine):
-                    next = rem_iter.next()
-                    if line.contents != next:
-                        sys.stdout.write(" orig:%spatch:%s" % (next,
-                                         line.contents))
-                    assert(line.contents == next)
-        self.assertRaises(StopIteration, rem_iter.next)
-
-    def testFirstLineRenumber(self):
-        """Make sure we handle lines at the beginning of the hunk"""
-        patch = parse_patch(self.datafile("insert_top.patch"))
-        assert (patch.pos_in_mod(0)==1)
-
-def test():
-    patchesTestSuite = unittest.makeSuite(PatchesTester,'test')
-    runner = unittest.TextTestRunner(verbosity=0)
-    return runner.run(patchesTestSuite)
-    
-
-if __name__ == "__main__":
-    test()
-# arch-tag: d1541a25-eac5-4de9-a476-08a7cecd5683
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/patchsource.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/patchsource.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/patchsource.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/patchsource.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,4 +1,4 @@
-import patches
+from bzrlib import patches
 
 class PatchSource(object):
     def __iter__(self):
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/README /tmp/2DwbKrAPHs/bzrtools-0.9.0/README
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/README	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/README	2006-08-11 15:10:54.000000000 +0200
@@ -18,6 +18,44 @@
 baz-import-branch will import an Arch or Baz Branch into a bzr standalone tree.
 See baz-import for conversion details.
 
+BRANCHES
+List all the branches present at, or underneath, a location.
+
+BRANCH-HISTORY
+Show a history of the branch, separated by committer and branch nick.
+
+CBRANCH
+Create a branch in a repository and a checkout elsewhere, in one command.
+
+CDIFF
+cdiff mimics bzr diff, but displays the diff in colour. cdiff knows how
+to read a ~/.colordiffrc file, in the same format as for colordiff(1).
+
+# example ~/.colordiffrc
+plain=darkwhite
+newtext=darkblue
+oldtext=darkred
+diffstuff=darkgreen
+
+CLEAN-TREE
+Delete unknown, ignored, or detritus files, leaving versioned files untouched.
+
+FETCH-GHOSTS
+Scan this branch for missing ancestors (aka "ghosts), and attempt to retrieve
+them from a specified branch.
+
+GRAPH-ANCESTRY
+Use dot (from Graphviz) to produce graphics of a tree's ancestry.
+
+IMPORT
+Import a tarball into a bzr branch, or update to a fresh tarball.
+
+PATCH
+Use patch to apply a patch to this tree.
+
+MULTI-PULL
+Pull all branches and checkouts at or underneath the current location.
+
 RSPUSH
 Uses rsync to copy a branch to a remote location.  Remembers the last location
 used.  Will not push if
@@ -25,6 +63,10 @@
 - there are uncommitted changes in the working tree.
 These safeguards are intended to prevent dirty trees from being uploaded.
 
+SHELL
+An interactive shell with bzr commands available as single commands, and
+nice shell completion.
+
 SHELVE/UNSHELVE/SHELF
 Shelve allows you to temporarily put changes you've made "on the shelf",
 ie. out of the way, until a later time when you can bring them back from
@@ -46,22 +88,12 @@
 
 http://bazaar.canonical.com/BzrShelveExample
 
-CLEAN-TREE
-Delete unknown (or, optionally, ignored) files, leaving versioned files
-untouched.
+SHOVE
+Apply all uncommited changes in one working tree to another.
 
-BRANCH-HISTORY
-Show a history of the branch, separated by committer and branch nick.
-
-GRAPH-ANCESTRY
-Use dot (from Graphviz) to produce graphics of a tree's ancestry.
-
-FETCH-GHOSTS
-Scan this branch for missing ancestors (aka "ghosts), and attempt to retrieve
-them from a specified branch.
-
-PATCH
-Use patch to apply a patch to this tree.
+SWITCH
+Change the branch associated with a lightweight checkout, and update the tree
+while retaining local changes.
 
 ZAP
-Safely remove undesired checkouts
+Safely remove undesired checkouts.
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/README.Shelf /tmp/2DwbKrAPHs/bzrtools-0.9.0/README.Shelf
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/README.Shelf	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/README.Shelf	2006-08-11 15:10:54.000000000 +0200
@@ -3,22 +3,36 @@
 
 This is the shelf plugin for bzr.
 
+Temporarily set aside some changes from the current tree.
+
 Shelve allows you to temporarily put changes you've made "on the shelf",
 ie. out of the way, until a later time when you can bring them back from
 the shelf with the 'unshelve' command.
 
-You can put multiple items on the shelf, each time you run unshelve the
-most recently shelved changes will be reinstated.
+Shelve is intended to help separate several sets of text changes that have
+been inappropriately mingled.  If you just want to get rid of all changes
+(text and otherwise) and you don't need to restore them later, use revert.
+
+By default shelve asks you what you want to shelve, press '?' at the
+prompt to get help. To shelve everything run shelve --all.
 
 If filenames are specified, only the changes to those files will be
 shelved, other files will be left untouched.
 
 If a revision is specified, changes since that revision will be shelved.
 
-If you specifiy "--pick" you'll be prompted for each hunk of the diff as
-to whether you want to shelve it or not. Press "?" at the prompt for help.
+You can put multiple items on the shelf. Normally each time you run
+unshelve the most recently shelved changes will be reinstated. However,
+you can also unshelve changes in a different order by explicitly
+specifiying which changes to unshelve. This works best when the changes
+don't depend on each other.
+
+    --all                     Shelve all changes without prompting
+    --message ARG, -m
+    --revision ARG, -r
 
 More Info
 =========
 
 http://bazaar.canonical.com/BzrShelveExample
+
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/shelf.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/shelf.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/shelf.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/shelf.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
 import os
 import sys
 import subprocess
@@ -7,6 +5,7 @@
 from errors import CommandError, PatchFailed
 from hunk_selector import ShelveHunkSelector, UnshelveHunkSelector
 from patchsource import PatchSource, FilePatchSource
+from bzrlib.osutils import rename
 
 class Shelf(object):
     MESSAGE_PREFIX = "# Shelved patch: "
@@ -56,10 +55,13 @@
 
     def delete(self, patch):
         path = self.__path_from_user(patch)
-        os.rename(path, '%s~' % path)
+        rename(path, '%s~' % path)
 
-    def display(self, patch):
-        path = self.__path_from_user(patch)
+    def display(self, patch=None):
+        if patch is None:
+            path = self.last_patch()
+        else:
+            path = self.__path_from_user(patch)
         sys.stdout.write(open(path).read())
 
     def list(self):
@@ -78,7 +80,7 @@
     def __path_from_user(self, patch_id):
         try:
             patch_index = int(patch_id)
-        except TypeError:
+        except (TypeError, ValueError):
             raise CommandError("Invalid patch name '%s'" % patch_id)
 
         path = self.__path(patch_index)
@@ -130,29 +132,38 @@
             return None
         return patch[len(self.MESSAGE_PREFIX):patch.index('\n')]
 
-    def unshelve(self, patch_source, all=False, force=False):
+    def unshelve(self, patch_source, patch_name=None, all=False, force=False,
+                 no_color=False):
         self._check_upgrade()
 
-        patch_name = self.last_patch()
-
+        if no_color is False:
+            color = None
+        else:
+            color = False
         if patch_name is None:
+            patch_path = self.last_patch()
+        else:
+            patch_path = self.__path_from_user(patch_name)
+
+        if patch_path is None:
             raise CommandError("No patch found on shelf %s" % self.name)
 
-        patches = FilePatchSource(patch_name).readpatches()
+        patches = FilePatchSource(patch_path).readpatches()
         if all:
             to_unshelve = patches
             to_remain = []
         else:
-            to_unshelve, to_remain = UnshelveHunkSelector(patches).select()
+            hs = UnshelveHunkSelector(patches, color)
+            to_unshelve, to_remain = hs.select()
 
         if len(to_unshelve) == 0:
             raise CommandError('Nothing to unshelve')
 
-        message = self.get_patch_message(patch_name)
+        message = self.get_patch_message(patch_path)
         if message is None:
             message = "No message saved with patch."
         self.log('Unshelving from %s/%s: "%s"\n' % \
-                (self.name, os.path.basename(patch_name), message))
+                (self.name, os.path.basename(patch_path), message))
 
         try:
             self._run_patch(to_unshelve, dry_run=True)
@@ -174,23 +185,27 @@
                     "longer applies cleanly to the working tree!")
 
         # Backup the shelved patch
-        os.rename(patch_name, '%s~' % patch_name)
+        rename(patch_path, '%s~' % patch_path)
 
         if len(to_remain) > 0:
-            f = open(patch_name, 'w')
+            f = open(patch_path, 'w')
             for patch in to_remain:
                 f.write(str(patch))
             f.close()
 
-    def shelve(self, patch_source, all=False, message=None):
+    def shelve(self, patch_source, all=False, message=None, no_color=False):
         self._check_upgrade()
+        if no_color is False:
+            color = None
+        else:
+            color = False
 
         patches = patch_source.readpatches()
 
         if all:
             to_shelve = patches
         else:
-            to_shelve = ShelveHunkSelector(patches).select()[0]
+            to_shelve = ShelveHunkSelector(patches, color).select()[0]
 
         if len(to_shelve) == 0:
             raise CommandError('Nothing to shelve')
@@ -199,11 +214,11 @@
             timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
             message = "Changes shelved on %s" % timestamp
 
-        patch_name = self.next_patch()
+        patch_path = self.next_patch()
         self.log('Shelving to %s/%s: "%s"\n' % \
-                (self.name, os.path.basename(patch_name), message))
+                (self.name, os.path.basename(patch_path), message))
 
-        f = open(patch_name, 'a')
+        f = open(patch_path, 'a')
 
         assert '\n' not in message
         f.write("%s%s\n" % (self.MESSAGE_PREFIX, message))
@@ -228,6 +243,10 @@
 
     def _run_patch(self, patches, strip=0, reverse=False, dry_run=False):
         args = ['patch', '-d', self.base, '-s', '-p%d' % strip, '-f']
+
+        if sys.platform == "win32":
+            args.append('--binary')
+
         if reverse:
             args.append('-R')
         if dry_run:
@@ -304,4 +323,4 @@
             new_file.close()
             self.log('Copied %s to %s/%s\n' % (os.path.basename(patch),
                 self.name, os.path.basename(new_path)))
-            os.rename(patch, patch + '~')
+            rename(patch, patch + '~')
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/shell.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/shell.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/shell.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/shell.py	2006-08-11 15:10:54.000000000 +0200
@@ -14,25 +14,34 @@
 #    You should have received a copy of the GNU General Public License
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
 import cmd
-import sys
+from itertools import chain
 import os
-import terminal
 import readline
+import shlex
+import stat
 import string
-from itertools import chain
-from bzrlib.errors import BzrError
-from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
+import sys
+
 from bzrlib.branch import Branch
+from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
+from bzrlib.errors import BzrError
+from bzrlib.workingtree import WorkingTree
+
+import terminal
+
 
 SHELL_BLACKLIST = set(['rm', 'ls'])
 COMPLETION_BLACKLIST = set(['shell'])
 
+
 class BlackListedCommand(BzrError):
     def __init__(self, command):
         BzrError.__init__(self, "The command %s is blacklisted for shell use" %
                           command)
 
+
 class CompletionContext(object):
     def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
         self.text = text
@@ -57,8 +66,11 @@
 
     def get_completions_or_raise(self):
         if self.command is None:
-            iter = (c+" " for c in iter_command_names() if
-                    c not in COMPLETION_BLACKLIST)
+            if '/' in self.text:
+                iter = iter_executables(self.text)
+            else:
+                iter = (c+" " for c in iter_command_names() if
+                        c not in COMPLETION_BLACKLIST)
             return list(filter_completions(iter, self.text))
         if self.prev_opt is None:
             completions = self.get_option_completions()
@@ -67,8 +79,7 @@
                 completions.extend(list(filter_completions(iter, self.text)))
             else:
                 iter = iter_file_completions(self.text)
-                completions.extend([f+" " for f in 
-                                    filter_completions(iter, self.text)])
+                completions.extend(filter_completions(iter, self.text))
             return completions 
 
 
@@ -112,7 +123,7 @@
         if self.tree is not None:
             try:
                 prompt_data = (self.tree.branch.nick, self.tree.branch.revno(), 
-                               self.tree.branch.relpath('.'))
+                               self.tree.relpath('.'))
                 prompt = " %s:%d/%s" % prompt_data
             except:
                 prompt = ""
@@ -153,7 +164,7 @@
         self.default("help "+line)
 
     def default(self, line):
-        args = line.split()
+        args = shlex.split(line)
         alias_args = get_alias(args[0])
         if alias_args is not None:
             args[0] = alias_args.pop(0)
@@ -204,6 +215,7 @@
             cmd = None
         return CompletionContext(text, command=cmd).get_completions()
 
+
 def run_shell():
     try:
         prompt = PromptCmd()
@@ -214,6 +226,7 @@
     except StopIteration:
         pass
 
+
 def iter_opt_completions(command_obj):
     for option_name, option in command_obj.options().items():
         yield "--" + option_name
@@ -221,6 +234,7 @@
         if short_name:
             yield "-" + short_name
 
+
 def iter_file_completions(arg, only_dirs = False):
     """Generate an iterator that iterates through filename completions.
 
@@ -249,7 +263,7 @@
                 userfile+='/'
                 yield userfile
             elif not only_dirs:
-                yield userfile
+                yield userfile + ' '
 
 
 def iter_dir_completions(arg):
@@ -260,6 +274,7 @@
     """
     return iter_file_completions(arg, True)
 
+
 def iter_command_names(hidden=False):
     for real_cmd_name, cmd_class in get_all_cmds():
         if not hidden and cmd_class.hidden:
@@ -269,17 +284,31 @@
             if name == real_cmd_name or not real_cmd_name.startswith(name):
                 yield name
 
+
+def iter_executables(path):
+    dirname, partial = os.path.split(path)
+    for filename in os.listdir(dirname):
+        if not filename.startswith(partial):
+            continue
+        fullpath = os.path.join(dirname, filename)
+        mode=os.lstat(fullpath)[stat.ST_MODE]
+        if stat.S_ISREG(mode) and 0111 & mode:
+            yield fullpath + ' '
+
+
 def filter_completions(iter, arg):
     return (c for c in iter if c.startswith(arg))
 
+
 def iter_munged_completions(iter, arg, text):
     for completion in iter:
         completion = str(completion)
         if completion.startswith(arg):
             yield completion[len(arg)-len(text):]+" "
 
+
 def too_complicated(line):
-    for char in '|<>"\"*?':
+    for char in '|<>*?':
         if char in line:
             return True
     return False
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/shove.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/shove.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/shove.py	1970-01-01 01:00:00.000000000 +0100
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/shove.py	2006-08-11 15:10:54.000000000 +0200
@@ -0,0 +1,33 @@
+# Copyright (C) 2006 Aaron Bentley
+# <aaron.bentley at utoronto.ca>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+import os
+
+from bzrlib.errors import NoSuchFile
+from bzrlib.workingtree import WorkingTree
+from bzrlib.merge import merge_inner
+
+def do_shove(source, target):
+    if not os.path.exists(source):
+        raise NoSuchFile(source)
+    if not os.path.exists(target):
+        raise NoSuchFile(target)
+    source_tree = WorkingTree.open_containing(source)[0]
+    target_tree = WorkingTree.open_containing(target)[0]
+    merge_inner(target_tree.branch, source_tree, source_tree.basis_tree(), 
+                this_tree=target_tree)
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/switch.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/switch.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/switch.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/switch.py	2006-08-11 15:10:54.000000000 +0200
@@ -25,7 +25,7 @@
 from bzrlib.workingtree import WorkingTree
 
 class cmd_switch(Command):
-    """Set the branch of a lightweight checkout and update.  <BZRTOOLS>"""
+    """Set the branch of a lightweight checkout and update."""
 
     takes_args = ['to_location']
 
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/terminal.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/terminal.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/terminal.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/terminal.py	2006-08-11 15:10:54.000000000 +0200
@@ -16,6 +16,7 @@
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 import os
+import sys
 
 __docformat__ = "restructuredtext"
 __doc__ = "Terminal control functionality"
@@ -24,6 +25,8 @@
     # XXX The whole color handling should be rewritten to use terminfo
     # XXX before we get there, checking for setaf capability should do.
     # XXX See terminfo(5) for all the gory details.
+    if sys.platform == "win32":
+        return False
     import curses
     curses.setupterm()
     return bool(curses.tigetstr('setaf'))
@@ -39,7 +42,7 @@
 "white": "7"
 }
 
-def colorstring(text, fgcolor=None, bright=False, bgcolor=None):
+def colorstring(text, fgcolor=None, bgcolor=None):
     """
     Returns a string using ANSI control codes to set the text color.
 
@@ -47,20 +50,26 @@
     :type text: string
     :param fgcolor: The foreground color to use
     :type fgcolor: string
-    :param bright: If true, make the foreground color bright
-    :type bright: bool
     :param bgcolor: The background color to use
     :type bgcolor: string
     """
     code = []
-    if (bright): 
-        code.append("1")
-    if (fgcolor): 
-        code.append('3'+colors[fgcolor])
-    if (bgcolor): 
-        code.append("4"+colors[bgcolor])
+
+    if fgcolor:
+        if fgcolor.startswith('dark'):
+            code.append('0')
+            fgcolor = fgcolor[4:]
+        else:
+            code.append('1')
+
+        code.append('3' + colors[fgcolor])
+
+    if bgcolor:
+        code.append('4' + colors[bgcolor])
+
     return "".join(("\033[", ';'.join(code), "m", text, "\033[0m"))
 
+
 def term_title(title):
     term = os.environ.get('TERM', '')
     if term.startswith('xterm') or term == 'dtterm':
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/diff /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/diff
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/diff	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/diff	1970-01-01 01:00:00.000000000 +0100
@@ -1,1154 +0,0 @@
---- orig/commands.py
-+++ mod/commands.py
-@@ -19,25 +19,31 @@
- import arch
- import arch.util
- import arch.arch
-+
-+import pylon.errors
-+from pylon.errors import *
-+from pylon import errors
-+from pylon import util
-+from pylon import arch_core
-+from pylon import arch_compound
-+from pylon import ancillary
-+from pylon import misc
-+from pylon import paths 
-+
- import abacmds
- import cmdutil
- import shutil
- import os
- import options
--import paths 
- import time
- import cmd
- import readline
- import re
- import string
--import arch_core
--from errors import *
--import errors
- import terminal
--import ancillary
--import misc
- import email
- import smtplib
-+import textwrap
- 
- __docformat__ = "restructuredtext"
- __doc__ = "Implementation of user (sub) commands"
-@@ -257,7 +263,7 @@
- 
-         tree=arch.tree_root()
-         if len(args) == 0:
--            a_spec = cmdutil.comp_revision(tree)
-+            a_spec = ancillary.comp_revision(tree)
-         else:
-             a_spec = cmdutil.determine_revision_tree(tree, args[0])
-         cmdutil.ensure_archive_registered(a_spec.archive)
-@@ -284,7 +290,7 @@
-             changeset=options.changeset
-             tmpdir = None
-         else:
--            tmpdir=cmdutil.tmpdir()
-+            tmpdir=util.tmpdir()
-             changeset=tmpdir+"/changeset"
-         try:
-             delta=arch.iter_delta(a_spec, b_spec, changeset)
-@@ -304,14 +310,14 @@
-             if status > 1:
-                 return
-             if (options.perform_diff):
--                chan = cmdutil.ChangesetMunger(changeset)
-+                chan = arch_compound.ChangesetMunger(changeset)
-                 chan.read_indices()
--                if isinstance(b_spec, arch.Revision):
--                    b_dir = b_spec.library_find()
--                else:
--                    b_dir = b_spec
--                a_dir = a_spec.library_find()
-                 if options.diffopts is not None:
-+                    if isinstance(b_spec, arch.Revision):
-+                        b_dir = b_spec.library_find()
-+                    else:
-+                        b_dir = b_spec
-+                    a_dir = a_spec.library_find()
-                     diffopts = options.diffopts.split()
-                     cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
-                 else:
-@@ -517,7 +523,7 @@
-         except arch.errors.TreeRootError, e:
-             print e
-             return
--        from_revision=cmdutil.tree_latest(tree)
-+        from_revision = arch_compound.tree_latest(tree)
-         if from_revision==to_revision:
-             print "Tree is already up to date with:\n"+str(to_revision)+"."
-             return
-@@ -592,6 +598,9 @@
- 
-         if len(args) == 0:
-             args = None
-+        if options.version is None:
-+            return options, tree.tree_version, args
-+
-         revision=cmdutil.determine_revision_arch(tree, options.version)
-         return options, revision.get_version(), args
- 
-@@ -601,11 +610,16 @@
-         """
-         tree=arch.tree_root()
-         options, version, files = self.parse_commandline(cmdargs, tree)
-+        ancestor = None
-         if options.__dict__.has_key("base") and options.base:
-             base = cmdutil.determine_revision_tree(tree, options.base)
-+            ancestor = base
-         else:
--            base = cmdutil.submit_revision(tree)
--        
-+            base = ancillary.submit_revision(tree)
-+            ancestor = base
-+        if ancestor is None:
-+            ancestor = arch_compound.tree_latest(tree, version)
-+
-         writeversion=version
-         archive=version.archive
-         source=cmdutil.get_mirror_source(archive)
-@@ -625,18 +639,26 @@
-         try:
-             last_revision=tree.iter_logs(version, True).next().revision
-         except StopIteration, e:
--            if cmdutil.prompt("Import from commit"):
--                return do_import(version)
--            else:
--                raise NoVersionLogs(version)
--        if last_revision!=version.iter_revisions(True).next():
-+            last_revision = None
-+            if ancestor is None:
-+                if cmdutil.prompt("Import from commit"):
-+                    return do_import(version)
-+                else:
-+                    raise NoVersionLogs(version)
-+        try:
-+            arch_last_revision = version.iter_revisions(True).next()
-+        except StopIteration, e:
-+            arch_last_revision = None
-+ 
-+        if last_revision != arch_last_revision:
-+            print "Tree is not up to date with %s" % str(version)
-             if not cmdutil.prompt("Out of date"):
-                 raise OutOfDate
-             else:
-                 allow_old=True
- 
-         try:
--            if not cmdutil.has_changed(version):
-+            if not cmdutil.has_changed(ancestor):
-                 if not cmdutil.prompt("Empty commit"):
-                     raise EmptyCommit
-         except arch.util.ExecProblem, e:
-@@ -645,15 +667,15 @@
-                 raise MissingID(e)
-             else:
-                 raise
--        log = tree.log_message(create=False)
-+        log = tree.log_message(create=False, version=version)
-         if log is None:
-             try:
-                 if cmdutil.prompt("Create log"):
--                    edit_log(tree)
-+                    edit_log(tree, version)
- 
-             except cmdutil.NoEditorSpecified, e:
-                 raise CommandFailed(e)
--            log = tree.log_message(create=False)
-+            log = tree.log_message(create=False, version=version)
-         if log is None: 
-             raise NoLogMessage
-         if log["Summary"] is None or len(log["Summary"].strip()) == 0:
-@@ -837,23 +859,24 @@
-             if spec is not None:
-                 revision = cmdutil.determine_revision_tree(tree, spec)
-             else:
--                revision = cmdutil.comp_revision(tree)
-+                revision = ancillary.comp_revision(tree)
-         except cmdutil.CantDetermineRevision, e:
-             raise CommandFailedWrapper(e)
-         munger = None
- 
-         if options.file_contents or options.file_perms or options.deletions\
-             or options.additions or options.renames or options.hunk_prompt:
--            munger = cmdutil.MungeOpts()
--            munger.hunk_prompt = options.hunk_prompt
-+            munger = arch_compound.MungeOpts()
-+            munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
-+                                   options.hunk_prompt)
- 
-         if len(args) > 0 or options.logs or options.pattern_files or \
-             options.control:
-             if munger is None:
--                munger = cmdutil.MungeOpts(True)
-+                munger = cmdutil.arch_compound.MungeOpts(True)
-                 munger.all_types(True)
-         if len(args) > 0:
--            t_cwd = cmdutil.tree_cwd(tree)
-+            t_cwd = arch_compound.tree_cwd(tree)
-             for name in args:
-                 if len(t_cwd) > 0:
-                     t_cwd += "/"
-@@ -878,7 +901,7 @@
-         if options.pattern_files:
-             munger.add_keep_pattern(options.pattern_files)
-                 
--        for line in cmdutil.revert(tree, revision, munger, 
-+        for line in arch_compound.revert(tree, revision, munger, 
-                                    not options.no_output):
-             cmdutil.colorize(line)
- 
-@@ -1042,18 +1065,13 @@
-         help_tree_spec()
-         return
- 
--def require_version_exists(version, spec):
--    if not version.exists():
--        raise cmdutil.CantDetermineVersion(spec, 
--                                           "The version %s does not exist." \
--                                           % version)
--
- class Revisions(BaseCommand):
-     """
-     Print a revision name based on a revision specifier
-     """
-     def __init__(self):
-         self.description="Lists revisions"
-+        self.cl_revisions = []
-     
-     def do_command(self, cmdargs):
-         """
-@@ -1066,224 +1084,68 @@
-             self.tree = arch.tree_root()
-         except arch.errors.TreeRootError:
-             self.tree = None
-+        if options.type == "default":
-+            options.type = "archive"
-         try:
--            iter = self.get_iterator(options.type, args, options.reverse, 
--                                     options.modified)
-+            iter = cmdutil.revision_iterator(self.tree, options.type, args, 
-+                                             options.reverse, options.modified,
-+                                             options.shallow)
-         except cmdutil.CantDetermineRevision, e:
-             raise CommandFailedWrapper(e)
--
-+        except cmdutil.CantDetermineVersion, e:
-+            raise CommandFailedWrapper(e)
-         if options.skip is not None:
-             iter = cmdutil.iter_skip(iter, int(options.skip))
- 
--        for revision in iter:
--            log = None
--            if isinstance(revision, arch.Patchlog):
--                log = revision
--                revision=revision.revision
--            print options.display(revision)
--            if log is None and (options.summary or options.creator or 
--                                options.date or options.merges):
--                log = revision.patchlog
--            if options.creator:
--                print "    %s" % log.creator
--            if options.date:
--                print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
--            if options.summary:
--                print "    %s" % log.summary
--            if options.merges:
--                showed_title = False
--                for revision in log.merged_patches:
--                    if not showed_title:
--                        print "    Merged:"
--                        showed_title = True
--                    print "    %s" % revision
--
--    def get_iterator(self, type, args, reverse, modified):
--        if len(args) > 0:
--            spec = args[0]
--        else:
--            spec = None
--        if modified is not None:
--            iter = cmdutil.modified_iter(modified, self.tree)
--            if reverse:
--                return iter
--            else:
--                return cmdutil.iter_reverse(iter)
--        elif type == "archive":
--            if spec is None:
--                if self.tree is None:
--                    raise cmdutil.CantDetermineRevision("", 
--                                                        "Not in a project tree")
--                version = cmdutil.determine_version_tree(spec, self.tree)
--            else:
--                version = cmdutil.determine_version_arch(spec, self.tree)
--                cmdutil.ensure_archive_registered(version.archive)
--                require_version_exists(version, spec)
--            return version.iter_revisions(reverse)
--        elif type == "cacherevs":
--            if spec is None:
--                if self.tree is None:
--                    raise cmdutil.CantDetermineRevision("", 
--                                                        "Not in a project tree")
--                version = cmdutil.determine_version_tree(spec, self.tree)
--            else:
--                version = cmdutil.determine_version_arch(spec, self.tree)
--                cmdutil.ensure_archive_registered(version.archive)
--                require_version_exists(version, spec)
--            return cmdutil.iter_cacherevs(version, reverse)
--        elif type == "library":
--            if spec is None:
--                if self.tree is None:
--                    raise cmdutil.CantDetermineRevision("", 
--                                                        "Not in a project tree")
--                version = cmdutil.determine_version_tree(spec, self.tree)
--            else:
--                version = cmdutil.determine_version_arch(spec, self.tree)
--            return version.iter_library_revisions(reverse)
--        elif type == "logs":
--            if self.tree is None:
--                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
--            return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
--                                  self.tree), reverse)
--        elif type == "missing" or type == "skip-present":
--            if self.tree is None:
--                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
--            skip = (type == "skip-present")
--            version = cmdutil.determine_version_tree(spec, self.tree)
--            cmdutil.ensure_archive_registered(version.archive)
--            require_version_exists(version, spec)
--            return cmdutil.iter_missing(self.tree, version, reverse,
--                                        skip_present=skip)
--
--        elif type == "present":
--            if self.tree is None:
--                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
--            version = cmdutil.determine_version_tree(spec, self.tree)
--            cmdutil.ensure_archive_registered(version.archive)
--            require_version_exists(version, spec)
--            return cmdutil.iter_present(self.tree, version, reverse)
--
--        elif type == "new-merges" or type == "direct-merges":
--            if self.tree is None:
--                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
--            version = cmdutil.determine_version_tree(spec, self.tree)
--            cmdutil.ensure_archive_registered(version.archive)
--            require_version_exists(version, spec)
--            iter = cmdutil.iter_new_merges(self.tree, version, reverse)
--            if type == "new-merges":
--                return iter
--            elif type == "direct-merges":
--                return cmdutil.direct_merges(iter)
--
--        elif type == "missing-from":
--            if self.tree is None:
--                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
--            revision = cmdutil.determine_revision_tree(self.tree, spec)
--            libtree = cmdutil.find_or_make_local_revision(revision)
--            return cmdutil.iter_missing(libtree, self.tree.tree_version,
--                                        reverse)
--
--        elif type == "partner-missing":
--            return cmdutil.iter_partner_missing(self.tree, reverse)
--
--        elif type == "ancestry":
--            revision = cmdutil.determine_revision_tree(self.tree, spec)
--            iter = cmdutil._iter_ancestry(self.tree, revision)
--            if reverse:
--                return iter
--            else:
--                return cmdutil.iter_reverse(iter)
--
--        elif type == "dependencies" or type == "non-dependencies":
--            nondeps = (type == "non-dependencies")
--            revision = cmdutil.determine_revision_tree(self.tree, spec)
--            anc_iter = cmdutil._iter_ancestry(self.tree, revision)
--            iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
--            if reverse:
--                return iter_depends
--            else:
--                return cmdutil.iter_reverse(iter_depends)
--        elif type == "micro":
--            return cmdutil.iter_micro(self.tree)
--
--    
-+        try:
-+            for revision in iter:
-+                log = None
-+                if isinstance(revision, arch.Patchlog):
-+                    log = revision
-+                    revision=revision.revision
-+                out = options.display(revision)
-+                if out is not None:
-+                    print out
-+                if log is None and (options.summary or options.creator or 
-+                                    options.date or options.merges):
-+                    log = revision.patchlog
-+                if options.creator:
-+                    print "    %s" % log.creator
-+                if options.date:
-+                    print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
-+                if options.summary:
-+                    print "    %s" % log.summary
-+                if options.merges:
-+                    showed_title = False
-+                    for revision in log.merged_patches:
-+                        if not showed_title:
-+                            print "    Merged:"
-+                            showed_title = True
-+                        print "    %s" % revision
-+            if len(self.cl_revisions) > 0:
-+                print pylon.changelog_for_merge(self.cl_revisions)
-+        except pylon.errors.TreeRootNone:
-+            raise CommandFailedWrapper(
-+                Exception("This option can only be used in a project tree."))
-+
-+    def changelog_append(self, revision):
-+        if isinstance(revision, arch.Revision):
-+            revision=arch.Patchlog(revision)
-+        self.cl_revisions.append(revision)
-+   
-     def get_parser(self):
-         """
-         Returns the options parser to use for the "revision" command.
- 
-         :rtype: cmdutil.CmdOptionParser
-         """
--        parser=cmdutil.CmdOptionParser("fai revisions [revision]")
-+        parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
-         select = cmdutil.OptionGroup(parser, "Selection options",
-                           "Control which revisions are listed.  These options"
-                           " are mutually exclusive.  If more than one is"
-                           " specified, the last is used.")
--        select.add_option("", "--archive", action="store_const", 
--                          const="archive", dest="type", default="archive",
--                          help="List all revisions in the archive")
--        select.add_option("", "--cacherevs", action="store_const", 
--                          const="cacherevs", dest="type",
--                          help="List all revisions stored in the archive as "
--                          "complete copies")
--        select.add_option("", "--logs", action="store_const", 
--                          const="logs", dest="type",
--                          help="List revisions that have a patchlog in the "
--                          "tree")
--        select.add_option("", "--missing", action="store_const", 
--                          const="missing", dest="type",
--                          help="List revisions from the specified version that"
--                          " have no patchlog in the tree")
--        select.add_option("", "--skip-present", action="store_const", 
--                          const="skip-present", dest="type",
--                          help="List revisions from the specified version that"
--                          " have no patchlogs at all in the tree")
--        select.add_option("", "--present", action="store_const", 
--                          const="present", dest="type",
--                          help="List revisions from the specified version that"
--                          " have no patchlog in the tree, but can't be merged")
--        select.add_option("", "--missing-from", action="store_const", 
--                          const="missing-from", dest="type",
--                          help="List revisions from the specified revision "
--                          "that have no patchlog for the tree version")
--        select.add_option("", "--partner-missing", action="store_const", 
--                          const="partner-missing", dest="type",
--                          help="List revisions in partner versions that are"
--                          " missing")
--        select.add_option("", "--new-merges", action="store_const", 
--                          const="new-merges", dest="type",
--                          help="List revisions that have had patchlogs added"
--                          " to the tree since the last commit")
--        select.add_option("", "--direct-merges", action="store_const", 
--                          const="direct-merges", dest="type",
--                          help="List revisions that have been directly added"
--                          " to tree since the last commit ")
--        select.add_option("", "--library", action="store_const", 
--                          const="library", dest="type",
--                          help="List revisions in the revision library")
--        select.add_option("", "--ancestry", action="store_const", 
--                          const="ancestry", dest="type",
--                          help="List revisions that are ancestors of the "
--                          "current tree version")
--
--        select.add_option("", "--dependencies", action="store_const", 
--                          const="dependencies", dest="type",
--                          help="List revisions that the given revision "
--                          "depends on")
--
--        select.add_option("", "--non-dependencies", action="store_const", 
--                          const="non-dependencies", dest="type",
--                          help="List revisions that the given revision "
--                          "does not depend on")
--
--        select.add_option("--micro", action="store_const", 
--                          const="micro", dest="type",
--                          help="List partner revisions aimed for this "
--                          "micro-branch")
--
--        select.add_option("", "--modified", dest="modified", 
--                          help="List tree ancestor revisions that modified a "
--                          "given file", metavar="FILE[:LINE]")
- 
-+        cmdutil.add_revision_iter_options(select)
-         parser.add_option("", "--skip", dest="skip", 
-                           help="Skip revisions.  Positive numbers skip from "
-                           "beginning, negative skip from end.",
-@@ -1312,6 +1174,9 @@
-         format.add_option("--cacherev", action="store_const", 
-                          const=paths.determine_cacherev_path, dest="display",
-                          help="Show location of cacherev file")
-+        format.add_option("--changelog", action="store_const", 
-+                         const=self.changelog_append, dest="display",
-+                         help="Show location of cacherev file")
-         parser.add_option_group(format)
-         display = cmdutil.OptionGroup(parser, "Display format options",
-                           "These control the display of data")
-@@ -1448,6 +1313,7 @@
-         if os.access(self.history_file, os.R_OK) and \
-             os.path.isfile(self.history_file):
-             readline.read_history_file(self.history_file)
-+        self.cwd = os.getcwd()
- 
-     def write_history(self):
-         readline.write_history_file(self.history_file)
-@@ -1470,16 +1336,21 @@
-     def set_prompt(self):
-         if self.tree is not None:
-             try:
--                version = " "+self.tree.tree_version.nonarch
-+                prompt = pylon.alias_or_version(self.tree.tree_version, 
-+                                                self.tree, 
-+                                                full=False)
-+                if prompt is not None:
-+                    prompt = " " + prompt
-             except:
--                version = ""
-+                prompt = ""
-         else:
--            version = ""
--        self.prompt = "Fai%s> " % version
-+            prompt = ""
-+        self.prompt = "Fai%s> " % prompt
- 
-     def set_title(self, command=None):
-         try:
--            version = self.tree.tree_version.nonarch
-+            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
-+                                             full=False)
-         except:
-             version = "[no version]"
-         if command is None:
-@@ -1489,8 +1360,15 @@
-     def do_cd(self, line):
-         if line == "":
-             line = "~"
-+        line = os.path.expanduser(line)
-+        if os.path.isabs(line):
-+            newcwd = line
-+        else:
-+            newcwd = self.cwd+'/'+line
-+        newcwd = os.path.normpath(newcwd)
-         try:
--            os.chdir(os.path.expanduser(line))
-+            os.chdir(newcwd)
-+            self.cwd = newcwd
-         except Exception, e:
-             print e
-         try:
-@@ -1523,7 +1401,7 @@
-             except cmdutil.CantDetermineRevision, e:
-                 print e
-             except Exception, e:
--                print "Unhandled error:\n%s" % cmdutil.exception_str(e)
-+                print "Unhandled error:\n%s" % errors.exception_str(e)
- 
-         elif suggestions.has_key(args[0]):
-             print suggestions[args[0]]
-@@ -1574,7 +1452,7 @@
-                 arg = line.split()[-1]
-             else:
-                 arg = ""
--            iter = iter_munged_completions(iter, arg, text)
-+            iter = cmdutil.iter_munged_completions(iter, arg, text)
-         except Exception, e:
-             print e
-         return list(iter)
-@@ -1604,10 +1482,11 @@
-                 else:
-                     arg = ""
-                 if arg.startswith("-"):
--                    return list(iter_munged_completions(iter, arg, text))
-+                    return list(cmdutil.iter_munged_completions(iter, arg, 
-+                                                                text))
-                 else:
--                    return list(iter_munged_completions(
--                        iter_file_completions(arg), arg, text))
-+                    return list(cmdutil.iter_munged_completions(
-+                        cmdutil.iter_file_completions(arg), arg, text))
- 
- 
-             elif cmd == "cd":
-@@ -1615,13 +1494,13 @@
-                     arg = args.split()[-1]
-                 else:
-                     arg = ""
--                iter = iter_dir_completions(arg)
--                iter = iter_munged_completions(iter, arg, text)
-+                iter = cmdutil.iter_dir_completions(arg)
-+                iter = cmdutil.iter_munged_completions(iter, arg, text)
-                 return list(iter)
-             elif len(args)>0:
-                 arg = args.split()[-1]
--                return list(iter_munged_completions(iter_file_completions(arg),
--                                                    arg, text))
-+                iter = cmdutil.iter_file_completions(arg)
-+                return list(cmdutil.iter_munged_completions(iter, arg, text))
-             else:
-                 return self.completenames(text, line, begidx, endidx)
-         except Exception, e:
-@@ -1636,44 +1515,8 @@
-             yield entry
- 
- 
--def iter_file_completions(arg, only_dirs = False):
--    """Generate an iterator that iterates through filename completions.
--
--    :param arg: The filename fragment to match
--    :type arg: str
--    :param only_dirs: If true, match only directories
--    :type only_dirs: bool
--    """
--    cwd = os.getcwd()
--    if cwd != "/":
--        extras = [".", ".."]
--    else:
--        extras = []
--    (dir, file) = os.path.split(arg)
--    if dir != "":
--        listingdir = os.path.expanduser(dir)
--    else:
--        listingdir = cwd
--    for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
--        if dir != "":
--            userfile = dir+'/'+file
--        else:
--            userfile = file
--        if userfile.startswith(arg):
--            if os.path.isdir(listingdir+'/'+file):
--                userfile+='/'
--                yield userfile
--            elif not only_dirs:
--                yield userfile
--
--def iter_munged_completions(iter, arg, text):
--    for completion in iter:
--        completion = str(completion)
--        if completion.startswith(arg):
--            yield completion[len(arg)-len(text):]
--
- def iter_source_file_completions(tree, arg):
--    treepath = cmdutil.tree_cwd(tree)
-+    treepath = arch_compound.tree_cwd(tree)
-     if len(treepath) > 0:
-         dirs = [treepath]
-     else:
-@@ -1701,7 +1544,7 @@
-     :return: An iterator of all matching untagged files
-     :rtype: iterator of str
-     """
--    treepath = cmdutil.tree_cwd(tree)
-+    treepath = arch_compound.tree_cwd(tree)
-     if len(treepath) > 0:
-         dirs = [treepath]
-     else:
-@@ -1743,8 +1586,8 @@
-     :param arg: The prefix to match
-     :type arg: str
-     """
--    treepath = cmdutil.tree_cwd(tree)
--    tmpdir = cmdutil.tmpdir()
-+    treepath = arch_compound.tree_cwd(tree)
-+    tmpdir = util.tmpdir()
-     changeset = tmpdir+"/changeset"
-     completions = []
-     revision = cmdutil.determine_revision_tree(tree)
-@@ -1756,14 +1599,6 @@
-     shutil.rmtree(tmpdir)
-     return completions
- 
--def iter_dir_completions(arg):
--    """Generate an iterator that iterates through directory name completions.
--
--    :param arg: The directory name fragment to match
--    :type arg: str
--    """
--    return iter_file_completions(arg, True)
--
- class Shell(BaseCommand):
-     def __init__(self):
-         self.description = "Runs Fai as a shell"
-@@ -1795,7 +1630,11 @@
-         parser=self.get_parser()
-         (options, args) = parser.parse_args(cmdargs)
- 
--        tree = arch.tree_root()
-+        try:
-+            tree = arch.tree_root()
-+        except arch.errors.TreeRootError, e:
-+            raise pylon.errors.CommandFailedWrapper(e)
-+            
- 
-         if (len(args) == 0) == (options.untagged == False):
-             raise cmdutil.GetHelp
-@@ -1809,13 +1648,22 @@
-         if options.id_type == "tagline":
-             if method != "tagline":
-                 if not cmdutil.prompt("Tagline in other tree"):
--                    if method == "explicit":
--                        options.id_type == explicit
-+                    if method == "explicit" or method == "implicit":
-+                        options.id_type == method
-                     else:
-                         print "add-id not supported for \"%s\" tagging method"\
-                             % method 
-                         return
-         
-+        elif options.id_type == "implicit":
-+            if method != "implicit":
-+                if not cmdutil.prompt("Implicit in other tree"):
-+                    if method == "explicit" or method == "tagline":
-+                        options.id_type == method
-+                    else:
-+                        print "add-id not supported for \"%s\" tagging method"\
-+                            % method 
-+                        return
-         elif options.id_type == "explicit":
-             if method != "tagline" and method != explicit:
-                 if not prompt("Explicit in other tree"):
-@@ -1824,7 +1672,8 @@
-                     return
-         
-         if options.id_type == "auto":
--            if method != "tagline" and method != "explicit":
-+            if method != "tagline" and method != "explicit" \
-+                and method !="implicit":
-                 print "add-id not supported for \"%s\" tagging method" % method
-                 return
-             else:
-@@ -1852,10 +1701,12 @@
-             previous_files.extend(files)
-             if id_type == "explicit":
-                 cmdutil.add_id(files)
--            elif id_type == "tagline":
-+            elif id_type == "tagline" or id_type == "implicit":
-                 for file in files:
-                     try:
--                        cmdutil.add_tagline_or_explicit_id(file)
-+                        implicit = (id_type == "implicit")
-+                        cmdutil.add_tagline_or_explicit_id(file, False,
-+                                                           implicit)
-                     except cmdutil.AlreadyTagged:
-                         print "\"%s\" already has a tagline." % file
-                     except cmdutil.NoCommentSyntax:
-@@ -1888,6 +1739,9 @@
-         parser.add_option("--tagline", action="store_const", 
-                          const="tagline", dest="id_type", 
-                          help="Use a tagline id")
-+        parser.add_option("--implicit", action="store_const", 
-+                         const="implicit", dest="id_type", 
-+                         help="Use an implicit id (deprecated)")
-         parser.add_option("--untagged", action="store_true", 
-                          dest="untagged", default=False, 
-                          help="tag all untagged files")
-@@ -1926,27 +1780,7 @@
-     def get_completer(self, arg, index):
-         if self.tree is None:
-             raise arch.errors.TreeRootError
--        completions = list(ancillary.iter_partners(self.tree, 
--                                                   self.tree.tree_version))
--        if len(completions) == 0:
--            completions = list(self.tree.iter_log_versions())
--
--        aliases = []
--        try:
--            for completion in completions:
--                alias = ancillary.compact_alias(str(completion), self.tree)
--                if alias:
--                    aliases.extend(alias)
--
--            for completion in completions:
--                if completion.archive == self.tree.tree_version.archive:
--                    aliases.append(completion.nonarch)
--
--        except Exception, e:
--            print e
--            
--        completions.extend(aliases)
--        return completions
-+        return cmdutil.merge_completions(self.tree, arg, index)
- 
-     def do_command(self, cmdargs):
-         """
-@@ -1961,7 +1795,7 @@
-         
-         if self.tree is None:
-             raise arch.errors.TreeRootError(os.getcwd())
--        if cmdutil.has_changed(self.tree.tree_version):
-+        if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
-             raise UncommittedChanges(self.tree)
- 
-         if len(args) > 0:
-@@ -2027,14 +1861,14 @@
-         :type other_revision: `arch.Revision`
-         :return: 0 if the merge was skipped, 1 if it was applied
-         """
--        other_tree = cmdutil.find_or_make_local_revision(other_revision)
-+        other_tree = arch_compound.find_or_make_local_revision(other_revision)
-         try:
-             if action == "native-merge":
--                ancestor = cmdutil.merge_ancestor2(self.tree, other_tree, 
--                                                   other_revision)
-+                ancestor = arch_compound.merge_ancestor2(self.tree, other_tree, 
-+                                                         other_revision)
-             elif action == "update":
--                ancestor = cmdutil.tree_latest(self.tree, 
--                                               other_revision.version)
-+                ancestor = arch_compound.tree_latest(self.tree, 
-+                                                     other_revision.version)
-         except CantDetermineRevision, e:
-             raise CommandFailedWrapper(e)
-         cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
-@@ -2104,7 +1938,10 @@
-         if self.tree is None:
-             raise arch.errors.TreeRootError
- 
--        edit_log(self.tree)
-+        try:
-+            edit_log(self.tree, self.tree.tree_version)
-+        except pylon.errors.NoEditorSpecified, e:
-+            raise pylon.errors.CommandFailedWrapper(e)
- 
-     def get_parser(self):
-         """
-@@ -2132,7 +1969,7 @@
-         """
-         return
- 
--def edit_log(tree):
-+def edit_log(tree, version):
-     """Makes and edits the log for a tree.  Does all kinds of fancy things
-     like log templates and merge summaries and log-for-merge
-     
-@@ -2141,28 +1978,29 @@
-     """
-     #ensure we have an editor before preparing the log
-     cmdutil.find_editor()
--    log = tree.log_message(create=False)
-+    log = tree.log_message(create=False, version=version)
-     log_is_new = False
-     if log is None or cmdutil.prompt("Overwrite log"):
-         if log is not None:
-            os.remove(log.name)
--        log = tree.log_message(create=True)
-+        log = tree.log_message(create=True, version=version)
-         log_is_new = True
-         tmplog = log.name
--        template = tree+"/{arch}/=log-template"
--        if not os.path.exists(template):
--            template = os.path.expanduser("~/.arch-params/=log-template")
--            if not os.path.exists(template):
--                template = None
-+        template = pylon.log_template_path(tree)
-         if template:
-             shutil.copyfile(template, tmplog)
--        
--        new_merges = list(cmdutil.iter_new_merges(tree, 
--                                                  tree.tree_version))
--        log["Summary"] = merge_summary(new_merges, tree.tree_version)
-+        comp_version = ancillary.comp_revision(tree).version
-+        new_merges = cmdutil.iter_new_merges(tree, comp_version)
-+        new_merges = cmdutil.direct_merges(new_merges)
-+        log["Summary"] = pylon.merge_summary(new_merges, 
-+                                         version)
-         if len(new_merges) > 0:   
-             if cmdutil.prompt("Log for merge"):
--                mergestuff = cmdutil.log_for_merge(tree)
-+                if cmdutil.prompt("changelog for merge"):
-+                    mergestuff = "Patches applied:\n"
-+                    mergestuff += pylon.changelog_for_merge(new_merges)
-+                else:
-+                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
-                 log.description += mergestuff
-         log.save()
-     try:
-@@ -2172,29 +2010,6 @@
-             os.remove(log.name)
-         raise
- 
--def merge_summary(new_merges, tree_version):
--    if len(new_merges) == 0:
--        return ""
--    if len(new_merges) == 1:
--        summary = new_merges[0].summary
--    else:
--        summary = "Merge"
--
--    credits = []
--    for merge in new_merges:
--        if arch.my_id() != merge.creator:
--            name = re.sub("<.*>", "", merge.creator).rstrip(" ");
--            if not name in credits:
--                credits.append(name)
--        else:
--            version = merge.revision.version
--            if version.archive == tree_version.archive:
--                if not version.nonarch in credits:
--                    credits.append(version.nonarch)
--            elif not str(version) in credits:
--                credits.append(str(version))
--
--    return ("%s (%s)") % (summary, ", ".join(credits))
- 
- class MirrorArchive(BaseCommand):
-     """
-@@ -2268,31 +2083,73 @@
- 
- Use "alias" to list available (user and automatic) aliases."""
- 
-+auto_alias = [
-+"acur", 
-+"The latest revision in the archive of the tree-version.  You can specify \
-+a different version like so: acur:foo--bar--0 (aliases can be used)",
-+"tcur",
-+"""(tree current) The latest revision in the tree of the tree-version. \
-+You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
-+used).""",
-+"tprev" , 
-+"""(tree previous) The previous revision in the tree of the tree-version.  To \
-+specify an older revision, use a number, e.g. "tprev:4" """,
-+"tanc" , 
-+"""(tree ancestor) The ancestor revision of the tree To specify an older \
-+revision, use a number, e.g. "tanc:4".""",
-+"tdate" , 
-+"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
-+"tmod" , 
-+""" (tree modified) The latest revision to modify a given file, e.g. \
-+"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
-+"ttag" , 
-+"""(tree tag) The revision that was tagged into the current tree revision, \
-+according to the tree""",
-+"tagcur", 
-+"""(tag current) The latest revision of the version that the current tree \
-+was tagged from.""",
-+"mergeanc" , 
-+"""The common ancestor of the current tree and the specified revision. \
-+Defaults to the first partner-version's latest revision or to tagcur.""",
-+]
-+
-+
-+def is_auto_alias(name):
-+    """Determine whether a name is an auto alias name
-+
-+    :param name: the name to check
-+    :type name: str
-+    :return: True if the name is an auto alias, false if not
-+    :rtype: bool
-+    """
-+    return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
-+
-+
-+def display_def(iter, wrap = 80):
-+    """Display a list of definitions
-+
-+    :param iter: iter of name, definition pairs
-+    :type iter: iter of (str, str)
-+    :param wrap: The width for text wrapping
-+    :type wrap: int
-+    """
-+    vals = list(iter)
-+    maxlen = 0
-+    for (key, value) in vals:
-+        if len(key) > maxlen:
-+            maxlen = len(key)
-+    for (key, value) in vals:
-+        tw=textwrap.TextWrapper(width=wrap, 
-+                                initial_indent=key.rjust(maxlen)+" : ",
-+                                subsequent_indent="".rjust(maxlen+3))
-+        print tw.fill(value)
-+
-+
- def help_aliases(tree):
--    print """Auto-generated aliases
-- acur : The latest revision in the archive of the tree-version.  You can specfy
--        a different version like so: acur:foo--bar--0 (aliases can be used)
-- tcur : (tree current) The latest revision in the tree of the tree-version.
--        You can specify a different version like so: tcur:foo--bar--0 (aliases
--        can be used).
--tprev : (tree previous) The previous revision in the tree of the tree-version.
--        To specify an older revision, use a number, e.g. "tprev:4"
-- tanc : (tree ancestor) The ancestor revision of the tree
--        To specify an older revision, use a number, e.g. "tanc:4"
--tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
-- tmod : (tree modified) The latest revision to modify a given file 
--        (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
-- ttag : (tree tag) The revision that was tagged into the current tree revision,
--        according to the tree.
--tagcur: (tag current) The latest revision of the version that the current tree
--        was tagged from.
--mergeanc : The common ancestor of the current tree and the specified revision.
--        Defaults to the first partner-version's latest revision or to tagcur.
--   """
-+    print """Auto-generated aliases"""
-+    display_def(pylon.util.iter_pairs(auto_alias))
-     print "User aliases"
--    for parts in ancillary.iter_all_alias(tree):
--        print parts[0].rjust(10)+" : "+parts[1]
--
-+    display_def(ancillary.iter_all_alias(tree))
- 
- class Inventory(BaseCommand):
-     """List the status of files in the tree"""
-@@ -2428,6 +2285,11 @@
-         except cmdutil.ForbiddenAliasSyntax, e:
-             raise CommandFailedWrapper(e)
- 
-+    def no_prefix(self, alias):
-+        if alias.startswith("^"):
-+            alias = alias[1:]
-+        return alias
-+        
-     def arg_dispatch(self, args, options):
-         """Add, modify, or list aliases, depending on number of arguments
- 
-@@ -2438,15 +2300,20 @@
-         if len(args) == 0:
-             help_aliases(self.tree)
-             return
--        elif len(args) == 1:
--            self.print_alias(args[0])
--        elif (len(args)) == 2:
--            self.add(args[0], args[1], options)
-         else:
--            raise cmdutil.GetHelp
-+            alias = self.no_prefix(args[0])
-+            if len(args) == 1:
-+                self.print_alias(alias)
-+            elif (len(args)) == 2:
-+                self.add(alias, args[1], options)
-+            else:
-+                raise cmdutil.GetHelp
- 
-     def print_alias(self, alias):
-         answer = None
-+        if is_auto_alias(alias):
-+            raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
-+                "  Use \"revision\" to expand auto aliases." % alias)
-         for pair in ancillary.iter_all_alias(self.tree):
-             if pair[0] == alias:
-                 answer = pair[1]
-@@ -2464,6 +2331,8 @@
-         :type expansion: str
-         :param options: The commandline options
-         """
-+        if is_auto_alias(alias):
-+            raise IsAutoAlias(alias)
-         newlist = ""
-         written = False
-         new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
-@@ -2490,14 +2359,17 @@
-         deleted = False
-         if len(args) != 1:
-             raise cmdutil.GetHelp
-+        alias = self.no_prefix(args[0])
-+        if is_auto_alias(alias):
-+            raise IsAutoAlias(alias)
-         newlist = ""
-         for pair in self.get_iterator(options):
--            if pair[0] != args[0]:
-+            if pair[0] != alias:
-                 newlist+="%s=%s\n" % (pair[0], pair[1])
-             else:
-                 deleted = True
-         if not deleted:
--            raise errors.NoSuchAlias(args[0])
-+            raise errors.NoSuchAlias(alias)
-         self.write_aliases(newlist, options)
- 
-     def get_alias_file(self, options):
-@@ -2526,7 +2398,7 @@
-         :param options: The commandline options
-         """
-         filename = os.path.expanduser(self.get_alias_file(options))
--        file = cmdutil.NewFileVersion(filename)
-+        file = util.NewFileVersion(filename)
-         file.write(newlist)
-         file.commit()
- 
-@@ -2588,10 +2460,13 @@
-         :param cmdargs: The commandline arguments
-         :type cmdargs: list of str
-         """
--        cmdutil.find_editor()
-         parser = self.get_parser()
-         (options, args) = parser.parse_args(cmdargs)
-         try:
-+            cmdutil.find_editor()
-+        except pylon.errors.NoEditorSpecified, e:
-+            raise pylon.errors.CommandFailedWrapper(e)
-+        try:
-             self.tree=arch.tree_root()
-         except:
-             self.tree=None
-@@ -2655,7 +2530,7 @@
-             target_revision = cmdutil.determine_revision_arch(self.tree, 
-                                                               args[0])
-         else:
--            target_revision = cmdutil.tree_latest(self.tree)
-+            target_revision = arch_compound.tree_latest(self.tree)
-         if len(args) > 1:
-             merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
-                        self.tree, f)) for f in args[1:] ]
-@@ -2711,7 +2586,7 @@
- 
-         :param message: The message to send
-         :type message: `email.Message`"""
--        server = smtplib.SMTP()
-+        server = smtplib.SMTP("localhost")
-         server.sendmail(message['From'], message['To'], message.as_string())
-         server.quit()
- 
-@@ -2763,6 +2638,22 @@
- 'alias' : Alias,
- 'request-merge': RequestMerge,
- }
-+
-+def my_import(mod_name):
-+    module = __import__(mod_name)
-+    components = mod_name.split('.')
-+    for comp in components[1:]:
-+        module = getattr(module, comp)
-+    return module
-+
-+def plugin(mod_name):
-+    module = my_import(mod_name)
-+    module.add_command(commands)
-+
-+for file in os.listdir(sys.path[0]+"/command"):
-+    if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
-+        plugin("command."+file[:-3])
-+
- suggestions = {
- 'apply-delta' : "Try \"apply-changes\".",
- 'delta' : "To compare two revisions, use \"changes\".",
-@@ -2784,6 +2675,7 @@
- 'tagline' : "Use add-id.  It uses taglines in tagline trees",
- 'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
- 'library-revisions' : "Use revisions --library",
--'file-revert' : "Use revert FILE"
-+'file-revert' : "Use revert FILE",
-+'join-branch' : "Use replay --logs-only"
- }
- # arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/insert_top.patch /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/insert_top.patch
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/insert_top.patch	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/insert_top.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,7 +0,0 @@
---- orig/pylon/patches.py
-+++ mod/pylon/patches.py
-@@ -1,3 +1,4 @@
-+#test
- import util
- import sys
- class PatchSyntax(Exception):
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/mod /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/mod
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/mod	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/mod	1970-01-01 01:00:00.000000000 +0100
@@ -1,2681 +0,0 @@
-# Copyright (C) 2004 Aaron Bentley
-# <aaron.bentley at utoronto.ca>
-#
-#    This program is free software; you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation; either version 2 of the License, or
-#    (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#
-#    You should have received a copy of the GNU General Public License
-#    along with this program; if not, write to the Free Software
-#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-import sys
-import arch
-import arch.util
-import arch.arch
-
-import pylon.errors
-from pylon.errors import *
-from pylon import errors
-from pylon import util
-from pylon import arch_core
-from pylon import arch_compound
-from pylon import ancillary
-from pylon import misc
-from pylon import paths 
-
-import abacmds
-import cmdutil
-import shutil
-import os
-import options
-import time
-import cmd
-import readline
-import re
-import string
-import terminal
-import email
-import smtplib
-import textwrap
-
-__docformat__ = "restructuredtext"
-__doc__ = "Implementation of user (sub) commands"
-commands = {}
-
-def find_command(cmd):
-    """
-    Return an instance of a command type.  Return None if the type isn't
-    registered.
-
-    :param cmd: the name of the command to look for
-    :type cmd: the type of the command
-    """
-    if commands.has_key(cmd):
-        return commands[cmd]()
-    else:
-        return None
-
-class BaseCommand:
-    def __call__(self, cmdline):
-        try:
-            self.do_command(cmdline.split())
-        except cmdutil.GetHelp, e:
-            self.help()
-        except Exception, e:
-            print e
-
-    def get_completer(index):
-        return None
-
-    def complete(self, args, text):
-        """
-        Returns a list of possible completions for the given text.
-
-        :param args: The complete list of arguments
-        :type args: List of str
-        :param text: text to complete (may be shorter than args[-1])
-        :type text: str
-        :rtype: list of str
-        """
-        matches = []
-        candidates = None
-
-        if len(args) > 0: 
-            realtext = args[-1]
-        else:
-            realtext = ""
-
-        try:
-            parser=self.get_parser()
-            if realtext.startswith('-'):
-                candidates = parser.iter_options()
-            else:
-                (options, parsed_args) = parser.parse_args(args)
-
-                if len (parsed_args) > 0:
-                    candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
-                else:
-                    candidates = self.get_completer("", 0)
-        except:
-            pass
-        if candidates is None:
-            return
-        for candidate in candidates:
-            candidate = str(candidate)
-            if candidate.startswith(realtext):
-                matches.append(candidate[len(realtext)- len(text):])
-        return matches
-
-
-class Help(BaseCommand):
-    """
-    Lists commands, prints help messages.
-    """
-    def __init__(self):
-        self.description="Prints help mesages"
-        self.parser = None
-
-    def do_command(self, cmdargs):
-        """
-        Prints a help message.
-        """
-        options, args = self.get_parser().parse_args(cmdargs)
-        if len(args) > 1:
-            raise cmdutil.GetHelp
-
-        if options.native or options.suggestions or options.external:
-            native = options.native
-            suggestions = options.suggestions
-            external = options.external
-        else:
-            native = True
-            suggestions = False
-            external = True
-        
-        if len(args) == 0:
-            self.list_commands(native, suggestions, external)
-            return
-        elif len(args) == 1:
-            command_help(args[0])
-            return
-
-    def help(self):
-        self.get_parser().print_help()
-        print """
-If no command is specified, commands are listed.  If a command is
-specified, help for that command is listed.
-        """
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        if self.parser is not None:
-            return self.parser
-        parser=cmdutil.CmdOptionParser("fai help [command]")
-        parser.add_option("-n", "--native", action="store_true", 
-                         dest="native", help="Show native commands")
-        parser.add_option("-e", "--external", action="store_true", 
-                         dest="external", help="Show external commands")
-        parser.add_option("-s", "--suggest", action="store_true", 
-                         dest="suggestions", help="Show suggestions")
-        self.parser = parser
-        return parser 
-      
-    def list_commands(self, native=True, suggest=False, external=True):
-        """
-        Lists supported commands.
-
-        :param native: list native, python-based commands
-        :type native: bool
-        :param external: list external aba-style commands
-        :type external: bool
-        """
-        if native:
-            print "Native Fai commands"
-            keys=commands.keys()
-            keys.sort()
-            for k in keys:
-                space=""
-                for i in range(28-len(k)):
-                    space+=" "
-                print space+k+" : "+commands[k]().description
-            print
-        if suggest:
-            print "Unavailable commands and suggested alternatives"
-            key_list = suggestions.keys()
-            key_list.sort()
-            for key in key_list:
-                print "%28s : %s" % (key, suggestions[key])
-            print
-        if external:
-            fake_aba = abacmds.AbaCmds()
-            if (fake_aba.abadir == ""):
-                return
-            print "External commands"
-            fake_aba.list_commands()
-            print
-        if not suggest:
-            print "Use help --suggest to list alternatives to tla and aba"\
-                " commands."
-        if options.tla_fallthrough and (native or external):
-            print "Fai also supports tla commands."
-
-def command_help(cmd):
-    """
-    Prints help for a command.
-
-    :param cmd: The name of the command to print help for
-    :type cmd: str
-    """
-    fake_aba = abacmds.AbaCmds()
-    cmdobj = find_command(cmd)
-    if cmdobj != None:
-        cmdobj.help()
-    elif suggestions.has_key(cmd):
-        print "Not available\n" + suggestions[cmd]
-    else:
-        abacmd = fake_aba.is_command(cmd)
-        if abacmd:
-            abacmd.help()
-        else:
-            print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
-
-
-
-class Changes(BaseCommand):
-    """
-    the "changes" command: lists differences between trees/revisions:
-    """
-    
-    def __init__(self):
-        self.description="Lists what files have changed in the project tree"
-
-    def get_completer(self, arg, index):
-        if index > 1:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-    
-    def parse_commandline(self, cmdline):
-        """
-        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-        if len(args) > 2:
-            raise cmdutil.GetHelp
-
-        tree=arch.tree_root()
-        if len(args) == 0:
-            a_spec = ancillary.comp_revision(tree)
-        else:
-            a_spec = cmdutil.determine_revision_tree(tree, args[0])
-        cmdutil.ensure_archive_registered(a_spec.archive)
-        if len(args) == 2:
-            b_spec = cmdutil.determine_revision_tree(tree, args[1])
-            cmdutil.ensure_archive_registered(b_spec.archive)
-        else:
-            b_spec=tree
-        return options, a_spec, b_spec
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "changes" command.
-        """
-        try:
-            options, a_spec, b_spec = self.parse_commandline(cmdargs);
-        except cmdutil.CantDetermineRevision, e:
-            print e
-            return
-        except arch.errors.TreeRootError, e:
-            print e
-            return
-        if options.changeset:
-            changeset=options.changeset
-            tmpdir = None
-        else:
-            tmpdir=util.tmpdir()
-            changeset=tmpdir+"/changeset"
-        try:
-            delta=arch.iter_delta(a_spec, b_spec, changeset)
-            try:
-                for line in delta:
-                    if cmdutil.chattermatch(line, "changeset:"):
-                        pass
-                    else:
-                        cmdutil.colorize(line, options.suppress_chatter)
-            except arch.util.ExecProblem, e:
-                if e.proc.error and e.proc.error.startswith(
-                    "missing explicit id for file"):
-                    raise MissingID(e)
-                else:
-                    raise
-            status=delta.status
-            if status > 1:
-                return
-            if (options.perform_diff):
-                chan = arch_compound.ChangesetMunger(changeset)
-                chan.read_indices()
-                if options.diffopts is not None:
-                    if isinstance(b_spec, arch.Revision):
-                        b_dir = b_spec.library_find()
-                    else:
-                        b_dir = b_spec
-                    a_dir = a_spec.library_find()
-                    diffopts = options.diffopts.split()
-                    cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
-                else:
-                    cmdutil.show_diffs(delta.changeset)
-        finally:
-            if tmpdir and (os.access(tmpdir, os.X_OK)):
-                shutil.rmtree(tmpdir)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "changes" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
-                                       " [revision]")
-        parser.add_option("-d", "--diff", action="store_true", 
-                          dest="perform_diff", default=False, 
-                          help="Show diffs in summary")
-        parser.add_option("-c", "--changeset", dest="changeset", 
-                          help="Store a changeset in the given directory", 
-                          metavar="DIRECTORY")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        parser.add_option("--diffopts", dest="diffopts", 
-                          help="Use the specified diff options", 
-                          metavar="OPTIONS")
-
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Performs source-tree comparisons
-
-If no revision is specified, the current project tree is compared to the
-last-committed revision.  If one revision is specified, the current project
-tree is compared to that revision.  If two revisions are specified, they are
-compared to each other.
-        """
-        help_tree_spec() 
-        return
-
-
-class ApplyChanges(BaseCommand):
-    """
-    Apply differences between two revisions to a tree
-    """
-    
-    def __init__(self):
-        self.description="Applies changes to a project tree"
-    
-    def get_completer(self, arg, index):
-        if index > 1:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-    def parse_commandline(self, cmdline, tree):
-        """
-        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-        if len(args) != 2:
-            raise cmdutil.GetHelp
-
-        a_spec = cmdutil.determine_revision_tree(tree, args[0])
-        cmdutil.ensure_archive_registered(a_spec.archive)
-        b_spec = cmdutil.determine_revision_tree(tree, args[1])
-        cmdutil.ensure_archive_registered(b_spec.archive)
-        return options, a_spec, b_spec
-
-    def do_command(self, cmdargs):
-        """
-        Master function that performs "apply-changes".
-        """
-        try:
-            tree = arch.tree_root()
-            options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
-        except cmdutil.CantDetermineRevision, e:
-            print e
-            return
-        except arch.errors.TreeRootError, e:
-            print e
-            return
-        delta=cmdutil.apply_delta(a_spec, b_spec, tree)
-        for line in cmdutil.iter_apply_delta_filter(delta):
-            cmdutil.colorize(line, options.suppress_chatter)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "apply-changes" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
-                                       " revision")
-        parser.add_option("-d", "--diff", action="store_true", 
-                          dest="perform_diff", default=False, 
-                          help="Show diffs in summary")
-        parser.add_option("-c", "--changeset", dest="changeset", 
-                          help="Store a changeset in the given directory", 
-                          metavar="DIRECTORY")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Applies changes to a project tree
-
-Compares two revisions and applies the difference between them to the current
-tree.
-        """
-        help_tree_spec() 
-        return
-
-class Update(BaseCommand):
-    """
-    Updates a project tree to a given revision, preserving un-committed hanges. 
-    """
-    
-    def __init__(self):
-        self.description="Apply the latest changes to the current directory"
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-    
-    def parse_commandline(self, cmdline, tree):
-        """
-        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-        if len(args) > 2:
-            raise cmdutil.GetHelp
-
-        spec=None
-        if len(args)>0:
-            spec=args[0]
-        revision=cmdutil.determine_revision_arch(tree, spec)
-        cmdutil.ensure_archive_registered(revision.archive)
-
-        mirror_source = cmdutil.get_mirror_source(revision.archive)
-        if mirror_source != None:
-            if cmdutil.prompt("Mirror update"):
-                cmd=cmdutil.mirror_archive(mirror_source, 
-                    revision.archive, arch.NameParser(revision).get_package_version())
-                for line in arch.chatter_classifier(cmd):
-                    cmdutil.colorize(line, options.suppress_chatter)
-
-                revision=cmdutil.determine_revision_arch(tree, spec)
-
-        return options, revision 
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "update" command.
-        """
-        tree=arch.tree_root()
-        try:
-            options, to_revision = self.parse_commandline(cmdargs, tree);
-        except cmdutil.CantDetermineRevision, e:
-            print e
-            return
-        except arch.errors.TreeRootError, e:
-            print e
-            return
-        from_revision = arch_compound.tree_latest(tree)
-        if from_revision==to_revision:
-            print "Tree is already up to date with:\n"+str(to_revision)+"."
-            return
-        cmdutil.ensure_archive_registered(from_revision.archive)
-        cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
-            options.patch_forward)
-        for line in cmdutil.iter_apply_delta_filter(cmd):
-            cmdutil.colorize(line)
-        if to_revision.version != tree.tree_version:
-            if cmdutil.prompt("Update version"):
-                tree.tree_version = to_revision.version
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "update" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai update [options]"
-                                       " [revision/version]")
-        parser.add_option("-f", "--forward", action="store_true", 
-                          dest="patch_forward", default=False, 
-                          help="pass the --forward option to 'patch'")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Updates a working tree to the current archive revision
-
-If a revision or version is specified, that is used instead 
-        """
-        help_tree_spec() 
-        return
-
-
-class Commit(BaseCommand):
-    """
-    Create a revision based on the changes in the current tree.
-    """
-    
-    def __init__(self):
-        self.description="Write local changes to the archive"
-
-    def get_completer(self, arg, index):
-        if arg is None:
-            arg = ""
-        return iter_modified_file_completions(arch.tree_root(), arg)
-#        return iter_source_file_completions(arch.tree_root(), arg)
-    
-    def parse_commandline(self, cmdline, tree):
-        """
-        Parse commandline arguments.  Raise cmtutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-
-        if len(args) == 0:
-            args = None
-        if options.version is None:
-            return options, tree.tree_version, args
-
-        revision=cmdutil.determine_revision_arch(tree, options.version)
-        return options, revision.get_version(), args
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "commit" command.
-        """
-        tree=arch.tree_root()
-        options, version, files = self.parse_commandline(cmdargs, tree)
-        ancestor = None
-        if options.__dict__.has_key("base") and options.base:
-            base = cmdutil.determine_revision_tree(tree, options.base)
-            ancestor = base
-        else:
-            base = ancillary.submit_revision(tree)
-            ancestor = base
-        if ancestor is None:
-            ancestor = arch_compound.tree_latest(tree, version)
-
-        writeversion=version
-        archive=version.archive
-        source=cmdutil.get_mirror_source(archive)
-        allow_old=False
-        writethrough="implicit"
-
-        if source!=None:
-            if writethrough=="explicit" and \
-                cmdutil.prompt("Writethrough"):
-                writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
-            elif writethrough=="none":
-                raise CommitToMirror(archive)
-
-        elif archive.is_mirror:
-            raise CommitToMirror(archive)
-
-        try:
-            last_revision=tree.iter_logs(version, True).next().revision
-        except StopIteration, e:
-            last_revision = None
-            if ancestor is None:
-                if cmdutil.prompt("Import from commit"):
-                    return do_import(version)
-                else:
-                    raise NoVersionLogs(version)
-        try:
-            arch_last_revision = version.iter_revisions(True).next()
-        except StopIteration, e:
-            arch_last_revision = None
- 
-        if last_revision != arch_last_revision:
-            print "Tree is not up to date with %s" % str(version)
-            if not cmdutil.prompt("Out of date"):
-                raise OutOfDate
-            else:
-                allow_old=True
-
-        try:
-            if not cmdutil.has_changed(ancestor):
-                if not cmdutil.prompt("Empty commit"):
-                    raise EmptyCommit
-        except arch.util.ExecProblem, e:
-            if e.proc.error and e.proc.error.startswith(
-                "missing explicit id for file"):
-                raise MissingID(e)
-            else:
-                raise
-        log = tree.log_message(create=False, version=version)
-        if log is None:
-            try:
-                if cmdutil.prompt("Create log"):
-                    edit_log(tree, version)
-
-            except cmdutil.NoEditorSpecified, e:
-                raise CommandFailed(e)
-            log = tree.log_message(create=False, version=version)
-        if log is None: 
-            raise NoLogMessage
-        if log["Summary"] is None or len(log["Summary"].strip()) == 0:
-            if not cmdutil.prompt("Omit log summary"):
-                raise errors.NoLogSummary
-        try:
-            for line in tree.iter_commit(version, seal=options.seal_version,
-                base=base, out_of_date_ok=allow_old, file_list=files):
-                cmdutil.colorize(line, options.suppress_chatter)
-
-        except arch.util.ExecProblem, e:
-            if e.proc.error and e.proc.error.startswith(
-                "These files violate naming conventions:"):
-                raise LintFailure(e.proc.error)
-            else:
-                raise
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "commit" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-
-        parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
-                                       " [file2...]")
-        parser.add_option("--seal", action="store_true", 
-                          dest="seal_version", default=False, 
-                          help="seal this version")
-        parser.add_option("-v", "--version", dest="version", 
-                          help="Use the specified version", 
-                          metavar="VERSION")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        if cmdutil.supports_switch("commit", "--base"):
-            parser.add_option("--base", dest="base", help="", 
-                              metavar="REVISION")
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Updates a working tree to the current archive revision
-
-If a version is specified, that is used instead 
-        """
-#        help_tree_spec() 
-        return
-
-
-
-class CatLog(BaseCommand):
-    """
-    Print the log of a given file (from current tree)
-    """
-    def __init__(self):
-        self.description="Prints the patch log for a revision"
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "cat-log" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError, e:
-            tree = None
-        spec=None
-        if len(args) > 0:
-            spec=args[0]
-        if len(args) > 1:
-            raise cmdutil.GetHelp()
-        try:
-            if tree:
-                revision = cmdutil.determine_revision_tree(tree, spec)
-            else:
-                revision = cmdutil.determine_revision_arch(tree, spec)
-        except cmdutil.CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        log = None
-        
-        use_tree = (options.source == "tree" or \
-            (options.source == "any" and tree))
-        use_arch = (options.source == "archive" or options.source == "any")
-        
-        log = None
-        if use_tree:
-            for log in tree.iter_logs(revision.get_version()):
-                if log.revision == revision:
-                    break
-                else:
-                    log = None
-        if log is None and use_arch:
-            cmdutil.ensure_revision_exists(revision)
-            log = arch.Patchlog(revision)
-        if log is not None:
-            for item in log.items():
-                print "%s: %s" % item
-            print log.description
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "cat-log" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
-        parser.add_option("--archive", action="store_const", dest="source",
-                          const="archive", default="any",
-                          help="Always get the log from the archive")
-        parser.add_option("--tree", action="store_const", dest="source",
-                          const="tree", help="Always get the log from the tree")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Prints the log for the specified revision
-        """
-        help_tree_spec()
-        return
-
-class Revert(BaseCommand):
-    """ Reverts a tree (or aspects of it) to a revision
-    """
-    def __init__(self):
-        self.description="Reverts a tree (or aspects of it) to a revision "
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return iter_modified_file_completions(tree, arg)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revert" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError, e:
-            raise CommandFailed(e)
-        spec=None
-        if options.revision is not None:
-            spec=options.revision
-        try:
-            if spec is not None:
-                revision = cmdutil.determine_revision_tree(tree, spec)
-            else:
-                revision = ancillary.comp_revision(tree)
-        except cmdutil.CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        munger = None
-
-        if options.file_contents or options.file_perms or options.deletions\
-            or options.additions or options.renames or options.hunk_prompt:
-            munger = arch_compound.MungeOpts()
-            munger.set_hunk_prompt(cmdutil.colorize, cmdutil.user_hunk_confirm,
-                                   options.hunk_prompt)
-
-        if len(args) > 0 or options.logs or options.pattern_files or \
-            options.control:
-            if munger is None:
-                munger = cmdutil.arch_compound.MungeOpts(True)
-                munger.all_types(True)
-        if len(args) > 0:
-            t_cwd = arch_compound.tree_cwd(tree)
-            for name in args:
-                if len(t_cwd) > 0:
-                    t_cwd += "/"
-                name = "./" + t_cwd + name
-                munger.add_keep_file(name);
-
-        if options.file_perms:
-            munger.file_perms = True
-        if options.file_contents:
-            munger.file_contents = True
-        if options.deletions:
-            munger.deletions = True
-        if options.additions:
-            munger.additions = True
-        if options.renames:
-            munger.renames = True
-        if options.logs:
-            munger.add_keep_pattern('^\./\{arch\}/[^=].*')
-        if options.control:
-            munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
-                                    "/\.arch-inventory$")
-        if options.pattern_files:
-            munger.add_keep_pattern(options.pattern_files)
-                
-        for line in arch_compound.revert(tree, revision, munger, 
-                                   not options.no_output):
-            cmdutil.colorize(line)
-
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "cat-log" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
-        parser.add_option("", "--contents", action="store_true", 
-                          dest="file_contents", 
-                          help="Revert file content changes")
-        parser.add_option("", "--permissions", action="store_true", 
-                          dest="file_perms", 
-                          help="Revert file permissions changes")
-        parser.add_option("", "--deletions", action="store_true", 
-                          dest="deletions", 
-                          help="Restore deleted files")
-        parser.add_option("", "--additions", action="store_true", 
-                          dest="additions", 
-                          help="Remove added files")
-        parser.add_option("", "--renames", action="store_true", 
-                          dest="renames", 
-                          help="Revert file names")
-        parser.add_option("--hunks", action="store_true", 
-                          dest="hunk_prompt", default=False,
-                          help="Prompt which hunks to revert")
-        parser.add_option("--pattern-files", dest="pattern_files", 
-                          help="Revert files that match this pattern", 
-                          metavar="REGEX")
-        parser.add_option("--logs", action="store_true", 
-                          dest="logs", default=False,
-                          help="Revert only logs")
-        parser.add_option("--control-files", action="store_true", 
-                          dest="control", default=False,
-                          help="Revert logs and other control files")
-        parser.add_option("-n", "--no-output", action="store_true", 
-                          dest="no_output", 
-                          help="Don't keep an undo changeset")
-        parser.add_option("--revision", dest="revision", 
-                          help="Revert to the specified revision", 
-                          metavar="REVISION")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Reverts changes in the current working tree.  If no flags are specified, all
-types of changes are reverted.  Otherwise, only selected types of changes are
-reverted.  
-
-If a revision is specified on the commandline, differences between the current
-tree and that revision are reverted.  If a version is specified, the current
-tree is used to determine the revision.
-
-If files are specified, only those files listed will have any changes applied.
-To specify a renamed file, you can use either the old or new name. (or both!)
-
-Unless "-n" is specified, reversions can be undone with "redo".
-        """
-        return
-
-class Revision(BaseCommand):
-    """
-    Print a revision name based on a revision specifier
-    """
-    def __init__(self):
-        self.description="Prints the name of a revision"
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError:
-            tree = None
-
-        spec=None
-        if len(args) > 0:
-            spec=args[0]
-        if len(args) > 1:
-            raise cmdutil.GetHelp
-        try:
-            if tree:
-                revision = cmdutil.determine_revision_tree(tree, spec)
-            else:
-                revision = cmdutil.determine_revision_arch(tree, spec)
-        except cmdutil.CantDetermineRevision, e:
-            print str(e)
-            return
-        print options.display(revision)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai revision [revision]")
-        parser.add_option("", "--location", action="store_const", 
-                         const=paths.determine_path, dest="display", 
-                         help="Show location instead of name", default=str)
-        parser.add_option("--import", action="store_const", 
-                         const=paths.determine_import_path, dest="display",  
-                         help="Show location of import file")
-        parser.add_option("--log", action="store_const", 
-                         const=paths.determine_log_path, dest="display", 
-                         help="Show location of log file")
-        parser.add_option("--patch", action="store_const", 
-                         dest="display", const=paths.determine_patch_path,
-                         help="Show location of patchfile")
-        parser.add_option("--continuation", action="store_const", 
-                         const=paths.determine_continuation_path, 
-                         dest="display",
-                         help="Show location of continuation file")
-        parser.add_option("--cacherev", action="store_const", 
-                         const=paths.determine_cacherev_path, dest="display",
-                         help="Show location of cacherev file")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Expands aliases and prints the name of the specified revision.  Instead of
-the name, several options can be used to print locations.  If more than one is
-specified, the last one is used.
-        """
-        help_tree_spec()
-        return
-
-class Revisions(BaseCommand):
-    """
-    Print a revision name based on a revision specifier
-    """
-    def __init__(self):
-        self.description="Lists revisions"
-        self.cl_revisions = []
-    
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-        (options, args) = self.get_parser().parse_args(cmdargs)
-        if len(args) > 1:
-            raise cmdutil.GetHelp
-        try:
-            self.tree = arch.tree_root()
-        except arch.errors.TreeRootError:
-            self.tree = None
-        if options.type == "default":
-            options.type = "archive"
-        try:
-            iter = cmdutil.revision_iterator(self.tree, options.type, args, 
-                                             options.reverse, options.modified,
-                                             options.shallow)
-        except cmdutil.CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        except cmdutil.CantDetermineVersion, e:
-            raise CommandFailedWrapper(e)
-        if options.skip is not None:
-            iter = cmdutil.iter_skip(iter, int(options.skip))
-
-        try:
-            for revision in iter:
-                log = None
-                if isinstance(revision, arch.Patchlog):
-                    log = revision
-                    revision=revision.revision
-                out = options.display(revision)
-                if out is not None:
-                    print out
-                if log is None and (options.summary or options.creator or 
-                                    options.date or options.merges):
-                    log = revision.patchlog
-                if options.creator:
-                    print "    %s" % log.creator
-                if options.date:
-                    print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
-                if options.summary:
-                    print "    %s" % log.summary
-                if options.merges:
-                    showed_title = False
-                    for revision in log.merged_patches:
-                        if not showed_title:
-                            print "    Merged:"
-                            showed_title = True
-                        print "    %s" % revision
-            if len(self.cl_revisions) > 0:
-                print pylon.changelog_for_merge(self.cl_revisions)
-        except pylon.errors.TreeRootNone:
-            raise CommandFailedWrapper(
-                Exception("This option can only be used in a project tree."))
-
-    def changelog_append(self, revision):
-        if isinstance(revision, arch.Revision):
-            revision=arch.Patchlog(revision)
-        self.cl_revisions.append(revision)
-   
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai revisions [version/revision]")
-        select = cmdutil.OptionGroup(parser, "Selection options",
-                          "Control which revisions are listed.  These options"
-                          " are mutually exclusive.  If more than one is"
-                          " specified, the last is used.")
-
-        cmdutil.add_revision_iter_options(select)
-        parser.add_option("", "--skip", dest="skip", 
-                          help="Skip revisions.  Positive numbers skip from "
-                          "beginning, negative skip from end.",
-                          metavar="NUMBER")
-
-        parser.add_option_group(select)
-
-        format = cmdutil.OptionGroup(parser, "Revision format options",
-                          "These control the appearance of listed revisions")
-        format.add_option("", "--location", action="store_const", 
-                         const=paths.determine_path, dest="display", 
-                         help="Show location instead of name", default=str)
-        format.add_option("--import", action="store_const", 
-                         const=paths.determine_import_path, dest="display",  
-                         help="Show location of import file")
-        format.add_option("--log", action="store_const", 
-                         const=paths.determine_log_path, dest="display", 
-                         help="Show location of log file")
-        format.add_option("--patch", action="store_const", 
-                         dest="display", const=paths.determine_patch_path,
-                         help="Show location of patchfile")
-        format.add_option("--continuation", action="store_const", 
-                         const=paths.determine_continuation_path, 
-                         dest="display",
-                         help="Show location of continuation file")
-        format.add_option("--cacherev", action="store_const", 
-                         const=paths.determine_cacherev_path, dest="display",
-                         help="Show location of cacherev file")
-        format.add_option("--changelog", action="store_const", 
-                         const=self.changelog_append, dest="display",
-                         help="Show location of cacherev file")
-        parser.add_option_group(format)
-        display = cmdutil.OptionGroup(parser, "Display format options",
-                          "These control the display of data")
-        display.add_option("-r", "--reverse", action="store_true", 
-                          dest="reverse", help="Sort from newest to oldest")
-        display.add_option("-s", "--summary", action="store_true", 
-                          dest="summary", help="Show patchlog summary")
-        display.add_option("-D", "--date", action="store_true", 
-                          dest="date", help="Show patchlog date")
-        display.add_option("-c", "--creator", action="store_true", 
-                          dest="creator", help="Show the id that committed the"
-                          " revision")
-        display.add_option("-m", "--merges", action="store_true", 
-                          dest="merges", help="Show the revisions that were"
-                          " merged")
-        parser.add_option_group(display)
-        return parser 
-    def help(self, parser=None):
-        """Attempt to explain the revisions command
-        
-        :param parser: If supplied, used to determine options
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """List revisions.
-        """
-        help_tree_spec()
-
-
-class Get(BaseCommand):
-    """
-    Retrieve a revision from the archive
-    """
-    def __init__(self):
-        self.description="Retrieve a revision from the archive"
-        self.parser=self.get_parser()
-
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "get" command.
-        """
-        (options, args) = self.parser.parse_args(cmdargs)
-        if len(args) < 1:
-            return self.help()            
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError:
-            tree = None
-        
-        arch_loc = None
-        try:
-            revision, arch_loc = paths.full_path_decode(args[0])
-        except Exception, e:
-            revision = cmdutil.determine_revision_arch(tree, args[0], 
-                check_existence=False, allow_package=True)
-        if len(args) > 1:
-            directory = args[1]
-        else:
-            directory = str(revision.nonarch)
-        if os.path.exists(directory):
-            raise DirectoryExists(directory)
-        cmdutil.ensure_archive_registered(revision.archive, arch_loc)
-        try:
-            cmdutil.ensure_revision_exists(revision)
-        except cmdutil.NoSuchRevision, e:
-            raise CommandFailedWrapper(e)
-
-        link = cmdutil.prompt ("get link")
-        for line in cmdutil.iter_get(revision, directory, link,
-                                     options.no_pristine,
-                                     options.no_greedy_add):
-            cmdutil.colorize(line)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "get" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai get revision [dir]")
-        parser.add_option("--no-pristine", action="store_true", 
-                         dest="no_pristine", 
-                         help="Do not make pristine copy for reference")
-        parser.add_option("--no-greedy-add", action="store_true", 
-                         dest="no_greedy_add", 
-                         help="Never add to greedy libraries")
-
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Expands aliases and constructs a project tree for a revision.  If the optional
-"dir" argument is provided, the project tree will be stored in this directory.
-        """
-        help_tree_spec()
-        return
-
-class PromptCmd(cmd.Cmd):
-    def __init__(self):
-        cmd.Cmd.__init__(self)
-        self.prompt = "Fai> "
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-        self.set_title()
-        self.set_prompt()
-        self.fake_aba = abacmds.AbaCmds()
-        self.identchars += '-'
-        self.history_file = os.path.expanduser("~/.fai-history")
-        readline.set_completer_delims(string.whitespace)
-        if os.access(self.history_file, os.R_OK) and \
-            os.path.isfile(self.history_file):
-            readline.read_history_file(self.history_file)
-        self.cwd = os.getcwd()
-
-    def write_history(self):
-        readline.write_history_file(self.history_file)
-
-    def do_quit(self, args):
-        self.write_history()
-        sys.exit(0)
-
-    def do_exit(self, args):
-        self.do_quit(args)
-
-    def do_EOF(self, args):
-        print
-        self.do_quit(args)
-
-    def postcmd(self, line, bar):
-        self.set_title()
-        self.set_prompt()
-
-    def set_prompt(self):
-        if self.tree is not None:
-            try:
-                prompt = pylon.alias_or_version(self.tree.tree_version, 
-                                                self.tree, 
-                                                full=False)
-                if prompt is not None:
-                    prompt = " " + prompt
-            except:
-                prompt = ""
-        else:
-            prompt = ""
-        self.prompt = "Fai%s> " % prompt
-
-    def set_title(self, command=None):
-        try:
-            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
-                                             full=False)
-        except:
-            version = "[no version]"
-        if command is None:
-            command = ""
-        sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
-
-    def do_cd(self, line):
-        if line == "":
-            line = "~"
-        line = os.path.expanduser(line)
-        if os.path.isabs(line):
-            newcwd = line
-        else:
-            newcwd = self.cwd+'/'+line
-        newcwd = os.path.normpath(newcwd)
-        try:
-            os.chdir(newcwd)
-            self.cwd = newcwd
-        except Exception, e:
-            print e
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-    def do_help(self, line):
-        Help()(line)
-
-    def default(self, line):
-        args = line.split()
-        if find_command(args[0]):
-            try:
-                find_command(args[0]).do_command(args[1:])
-            except cmdutil.BadCommandOption, e:
-                print e
-            except cmdutil.GetHelp, e:
-                find_command(args[0]).help()
-            except CommandFailed, e:
-                print e
-            except arch.errors.ArchiveNotRegistered, e:
-                print e
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-            except arch.util.ExecProblem, e:
-                print e.proc.error.rstrip('\n')
-            except cmdutil.CantDetermineVersion, e:
-                print e
-            except cmdutil.CantDetermineRevision, e:
-                print e
-            except Exception, e:
-                print "Unhandled error:\n%s" % errors.exception_str(e)
-
-        elif suggestions.has_key(args[0]):
-            print suggestions[args[0]]
-
-        elif self.fake_aba.is_command(args[0]):
-            tree = None
-            try:
-                tree = arch.tree_root()
-            except arch.errors.TreeRootError:
-                pass
-            cmd = self.fake_aba.is_command(args[0])
-            try:
-                cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-
-        elif options.tla_fallthrough and args[0] != "rm" and \
-            cmdutil.is_tla_command(args[0]):
-            try:
-                tree = None
-                try:
-                    tree = arch.tree_root()
-                except arch.errors.TreeRootError:
-                    pass
-                args = cmdutil.expand_prefix_alias(args, tree)
-                arch.util.exec_safe('tla', args, stderr=sys.stderr,
-                expected=(0, 1))
-            except arch.util.ExecProblem, e:
-                pass
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-        else:
-            try:
-                try:
-                    tree = arch.tree_root()
-                except arch.errors.TreeRootError:
-                    tree = None
-                args=line.split()
-                os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-
-    def completenames(self, text, line, begidx, endidx):
-        completions = []
-        iter = iter_command_names(self.fake_aba)
-        try:
-            if len(line) > 0:
-                arg = line.split()[-1]
-            else:
-                arg = ""
-            iter = cmdutil.iter_munged_completions(iter, arg, text)
-        except Exception, e:
-            print e
-        return list(iter)
-
-    def completedefault(self, text, line, begidx, endidx):
-        """Perform completion for native commands.
-        
-        :param text: The text to complete
-        :type text: str
-        :param line: The entire line to complete
-        :type line: str
-        :param begidx: The start of the text in the line
-        :type begidx: int
-        :param endidx: The end of the text in the line
-        :type endidx: int
-        """
-        try:
-            (cmd, args, foo) = self.parseline(line)
-            command_obj=find_command(cmd)
-            if command_obj is not None:
-                return command_obj.complete(args.split(), text)
-            elif not self.fake_aba.is_command(cmd) and \
-                cmdutil.is_tla_command(cmd):
-                iter = cmdutil.iter_supported_switches(cmd)
-                if len(args) > 0:
-                    arg = args.split()[-1]
-                else:
-                    arg = ""
-                if arg.startswith("-"):
-                    return list(cmdutil.iter_munged_completions(iter, arg, 
-                                                                text))
-                else:
-                    return list(cmdutil.iter_munged_completions(
-                        cmdutil.iter_file_completions(arg), arg, text))
-
-
-            elif cmd == "cd":
-                if len(args) > 0:
-                    arg = args.split()[-1]
-                else:
-                    arg = ""
-                iter = cmdutil.iter_dir_completions(arg)
-                iter = cmdutil.iter_munged_completions(iter, arg, text)
-                return list(iter)
-            elif len(args)>0:
-                arg = args.split()[-1]
-                iter = cmdutil.iter_file_completions(arg)
-                return list(cmdutil.iter_munged_completions(iter, arg, text))
-            else:
-                return self.completenames(text, line, begidx, endidx)
-        except Exception, e:
-            print e
-
-
-def iter_command_names(fake_aba):
-    for entry in cmdutil.iter_combine([commands.iterkeys(), 
-                                     fake_aba.get_commands(), 
-                                     cmdutil.iter_tla_commands(False)]):
-        if not suggestions.has_key(str(entry)):
-            yield entry
-
-
-def iter_source_file_completions(tree, arg):
-    treepath = arch_compound.tree_cwd(tree)
-    if len(treepath) > 0:
-        dirs = [treepath]
-    else:
-        dirs = None
-    for file in tree.iter_inventory(dirs, source=True, both=True):
-        file = file_completion_match(file, treepath, arg)
-        if file is not None:
-            yield file
-
-
-def iter_untagged(tree, dirs):
-    for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False, 
-                                                categories=arch_core.non_root,
-                                                control_files=True):
-        yield file.name 
-
-
-def iter_untagged_completions(tree, arg):
-    """Generate an iterator for all visible untagged files that match arg.
-
-    :param tree: The tree to look for untagged files in
-    :type tree: `arch.WorkingTree`
-    :param arg: The argument to match
-    :type arg: str
-    :return: An iterator of all matching untagged files
-    :rtype: iterator of str
-    """
-    treepath = arch_compound.tree_cwd(tree)
-    if len(treepath) > 0:
-        dirs = [treepath]
-    else:
-        dirs = None
-
-    for file in iter_untagged(tree, dirs):
-        file = file_completion_match(file, treepath, arg)
-        if file is not None:
-            yield file
-
-
-def file_completion_match(file, treepath, arg):
-    """Determines whether a file within an arch tree matches the argument.
-
-    :param file: The rooted filename
-    :type file: str
-    :param treepath: The path to the cwd within the tree
-    :type treepath: str
-    :param arg: The prefix to match
-    :return: The completion name, or None if not a match
-    :rtype: str
-    """
-    if not file.startswith(treepath):
-        return None
-    if treepath != "":
-        file = file[len(treepath)+1:]
-
-    if not file.startswith(arg):
-        return None 
-    if os.path.isdir(file):
-        file += '/'
-    return file
-
-def iter_modified_file_completions(tree, arg):
-    """Returns a list of modified files that match the specified prefix.
-
-    :param tree: The current tree
-    :type tree: `arch.WorkingTree`
-    :param arg: The prefix to match
-    :type arg: str
-    """
-    treepath = arch_compound.tree_cwd(tree)
-    tmpdir = util.tmpdir()
-    changeset = tmpdir+"/changeset"
-    completions = []
-    revision = cmdutil.determine_revision_tree(tree)
-    for line in arch.iter_delta(revision, tree, changeset):
-        if isinstance(line, arch.FileModification):
-            file = file_completion_match(line.name[1:], treepath, arg)
-            if file is not None:
-                completions.append(file)
-    shutil.rmtree(tmpdir)
-    return completions
-
-class Shell(BaseCommand):
-    def __init__(self):
-        self.description = "Runs Fai as a shell"
-
-    def do_command(self, cmdargs):
-        if len(cmdargs)!=0:
-            raise cmdutil.GetHelp
-        prompt = PromptCmd()
-        try:
-            prompt.cmdloop()
-        finally:
-            prompt.write_history()
-
-class AddID(BaseCommand):
-    """
-    Adds an inventory id for the given file
-    """
-    def __init__(self):
-        self.description="Add an inventory id for a given file"
-
-    def get_completer(self, arg, index):
-        tree = arch.tree_root()
-        return iter_untagged_completions(tree, arg)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError, e:
-            raise pylon.errors.CommandFailedWrapper(e)
-            
-
-        if (len(args) == 0) == (options.untagged == False):
-            raise cmdutil.GetHelp
-
-       #if options.id and len(args) != 1:
-       #    print "If --id is specified, only one file can be named."
-       #    return
-        
-        method = tree.tagging_method
-        
-        if options.id_type == "tagline":
-            if method != "tagline":
-                if not cmdutil.prompt("Tagline in other tree"):
-                    if method == "explicit" or method == "implicit":
-                        options.id_type == method
-                    else:
-                        print "add-id not supported for \"%s\" tagging method"\
-                            % method 
-                        return
-        
-        elif options.id_type == "implicit":
-            if method != "implicit":
-                if not cmdutil.prompt("Implicit in other tree"):
-                    if method == "explicit" or method == "tagline":
-                        options.id_type == method
-                    else:
-                        print "add-id not supported for \"%s\" tagging method"\
-                            % method 
-                        return
-        elif options.id_type == "explicit":
-            if method != "tagline" and method != explicit:
-                if not prompt("Explicit in other tree"):
-                    print "add-id not supported for \"%s\" tagging method" % \
-                        method
-                    return
-        
-        if options.id_type == "auto":
-            if method != "tagline" and method != "explicit" \
-                and method !="implicit":
-                print "add-id not supported for \"%s\" tagging method" % method
-                return
-            else:
-                options.id_type = method
-        if options.untagged:
-            args = None
-        self.add_ids(tree, options.id_type, args)
-
-    def add_ids(self, tree, id_type, files=()):
-        """Add inventory ids to files.
-        
-        :param tree: the tree the files are in
-        :type tree: `arch.WorkingTree`
-        :param id_type: the type of id to add: "explicit" or "tagline"
-        :type id_type: str
-        :param files: The list of files to add.  If None do all untagged.
-        :type files: tuple of str
-        """
-
-        untagged = (files is None)
-        if untagged:
-            files = list(iter_untagged(tree, None))
-        previous_files = []
-        while len(files) > 0:
-            previous_files.extend(files)
-            if id_type == "explicit":
-                cmdutil.add_id(files)
-            elif id_type == "tagline" or id_type == "implicit":
-                for file in files:
-                    try:
-                        implicit = (id_type == "implicit")
-                        cmdutil.add_tagline_or_explicit_id(file, False,
-                                                           implicit)
-                    except cmdutil.AlreadyTagged:
-                        print "\"%s\" already has a tagline." % file
-                    except cmdutil.NoCommentSyntax:
-                        pass
-            #do inventory after tagging until no untagged files are encountered
-            if untagged:
-                files = []
-                for file in iter_untagged(tree, None):
-                    if not file in previous_files:
-                        files.append(file)
-
-            else:
-                break
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
-# ddaa suggests removing this to promote GUIDs.  Let's see who squalks.
-#        parser.add_option("-i", "--id", dest="id", 
-#                         help="Specify id for a single file", default=None)
-        parser.add_option("--tltl", action="store_true", 
-                         dest="lord_style",  help="Use Tom Lord's style of id.")
-        parser.add_option("--explicit", action="store_const", 
-                         const="explicit", dest="id_type", 
-                         help="Use an explicit id", default="auto")
-        parser.add_option("--tagline", action="store_const", 
-                         const="tagline", dest="id_type", 
-                         help="Use a tagline id")
-        parser.add_option("--implicit", action="store_const", 
-                         const="implicit", dest="id_type", 
-                         help="Use an implicit id (deprecated)")
-        parser.add_option("--untagged", action="store_true", 
-                         dest="untagged", default=False, 
-                         help="tag all untagged files")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Adds an inventory to the specified file(s) and directories.  If --untagged is
-specified, adds inventory to all untagged files and directories.
-        """
-        return
-
-
-class Merge(BaseCommand):
-    """
-    Merges changes from other versions into the current tree
-    """
-    def __init__(self):
-        self.description="Merges changes from other versions"
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-
-    def get_completer(self, arg, index):
-        if self.tree is None:
-            raise arch.errors.TreeRootError
-        return cmdutil.merge_completions(self.tree, arg, index)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "merge" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        if options.diff3:
-            action="star-merge"
-        else:
-            action = options.action
-        
-        if self.tree is None:
-            raise arch.errors.TreeRootError(os.getcwd())
-        if cmdutil.has_changed(ancillary.comp_revision(self.tree)):
-            raise UncommittedChanges(self.tree)
-
-        if len(args) > 0:
-            revisions = []
-            for arg in args:
-                revisions.append(cmdutil.determine_revision_arch(self.tree, 
-                                                                 arg))
-            source = "from commandline"
-        else:
-            revisions = ancillary.iter_partner_revisions(self.tree, 
-                                                         self.tree.tree_version)
-            source = "from partner version"
-        revisions = misc.rewind_iterator(revisions)
-        try:
-            revisions.next()
-            revisions.rewind()
-        except StopIteration, e:
-            revision = cmdutil.tag_cur(self.tree)
-            if revision is None:
-                raise CantDetermineRevision("", "No version specified, no "
-                                            "partner-versions, and no tag"
-                                            " source")
-            revisions = [revision]
-            source = "from tag source"
-        for revision in revisions:
-            cmdutil.ensure_archive_registered(revision.archive)
-            cmdutil.colorize(arch.Chatter("* Merging %s [%s]" % 
-                             (revision, source)))
-            if action=="native-merge" or action=="update":
-                if self.native_merge(revision, action) == 0:
-                    continue
-            elif action=="star-merge":
-                try: 
-                    self.star_merge(revision, options.diff3)
-                except errors.MergeProblem, e:
-                    break
-            if cmdutil.has_changed(self.tree.tree_version):
-                break
-
-    def star_merge(self, revision, diff3):
-        """Perform a star-merge on the current tree.
-        
-        :param revision: The revision to use for the merge
-        :type revision: `arch.Revision`
-        :param diff3: If true, do a diff3 merge
-        :type diff3: bool
-        """
-        try:
-            for line in self.tree.iter_star_merge(revision, diff3=diff3):
-                cmdutil.colorize(line)
-        except arch.util.ExecProblem, e:
-            if e.proc.status is not None and e.proc.status == 1:
-                if e.proc.error:
-                    print e.proc.error
-                raise MergeProblem
-            else:
-                raise
-
-    def native_merge(self, other_revision, action):
-        """Perform a native-merge on the current tree.
-        
-        :param other_revision: The revision to use for the merge
-        :type other_revision: `arch.Revision`
-        :return: 0 if the merge was skipped, 1 if it was applied
-        """
-        other_tree = arch_compound.find_or_make_local_revision(other_revision)
-        try:
-            if action == "native-merge":
-                ancestor = arch_compound.merge_ancestor2(self.tree, other_tree, 
-                                                         other_revision)
-            elif action == "update":
-                ancestor = arch_compound.tree_latest(self.tree, 
-                                                     other_revision.version)
-        except CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
-        if (ancestor == other_revision):
-            cmdutil.colorize(arch.Chatter("* Skipping redundant merge" 
-                                          % ancestor))
-            return 0
-        delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)    
-        for line in cmdutil.iter_apply_delta_filter(delta):
-            cmdutil.colorize(line)
-        return 1
-
-
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "merge" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
-        parser.add_option("-s", "--star-merge", action="store_const",
-                          dest="action", help="Use star-merge",
-                          const="star-merge", default="native-merge")
-        parser.add_option("--update", action="store_const",
-                          dest="action", help="Use update picker",
-                          const="update")
-        parser.add_option("--diff3", action="store_true", 
-                         dest="diff3",  
-                         help="Use diff3 for merge (implies star-merge)")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Performs a merge operation using the specified version.
-        """
-        return
-
-class ELog(BaseCommand):
-    """
-    Produces a raw patchlog and invokes the user's editor
-    """
-    def __init__(self):
-        self.description="Edit a patchlog to commit"
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "elog" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        if self.tree is None:
-            raise arch.errors.TreeRootError
-
-        try:
-            edit_log(self.tree, self.tree.tree_version)
-        except pylon.errors.NoEditorSpecified, e:
-            raise pylon.errors.CommandFailedWrapper(e)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "merge" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai elog")
-        return parser 
-
-
-    def help(self, parser=None):
-        """
-        Invokes $EDITOR to produce a log for committing.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Invokes $EDITOR to produce a log for committing.
-        """
-        return
-
-def edit_log(tree, version):
-    """Makes and edits the log for a tree.  Does all kinds of fancy things
-    like log templates and merge summaries and log-for-merge
-    
-    :param tree: The tree to edit the log for
-    :type tree: `arch.WorkingTree`
-    """
-    #ensure we have an editor before preparing the log
-    cmdutil.find_editor()
-    log = tree.log_message(create=False, version=version)
-    log_is_new = False
-    if log is None or cmdutil.prompt("Overwrite log"):
-        if log is not None:
-           os.remove(log.name)
-        log = tree.log_message(create=True, version=version)
-        log_is_new = True
-        tmplog = log.name
-        template = pylon.log_template_path(tree)
-        if template:
-            shutil.copyfile(template, tmplog)
-        comp_version = ancillary.comp_revision(tree).version
-        new_merges = cmdutil.iter_new_merges(tree, comp_version)
-        new_merges = cmdutil.direct_merges(new_merges)
-        log["Summary"] = pylon.merge_summary(new_merges, 
-                                         version)
-        if len(new_merges) > 0:   
-            if cmdutil.prompt("Log for merge"):
-                if cmdutil.prompt("changelog for merge"):
-                    mergestuff = "Patches applied:\n"
-                    mergestuff += pylon.changelog_for_merge(new_merges)
-                else:
-                    mergestuff = cmdutil.log_for_merge(tree, comp_version)
-                log.description += mergestuff
-        log.save()
-    try:
-        cmdutil.invoke_editor(log.name)
-    except:
-        if log_is_new:
-            os.remove(log.name)
-        raise
-
-
-class MirrorArchive(BaseCommand):
-    """
-    Updates a mirror from an archive
-    """
-    def __init__(self):
-        self.description="Update a mirror from an archive"
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        if len(args) > 1:
-            raise GetHelp
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-
-        if len(args) == 0:
-            if tree is not None:
-                name = tree.tree_version()
-        else:
-            name = cmdutil.expand_alias(args[0], tree)
-            name = arch.NameParser(name)
-
-        to_arch = name.get_archive()
-        from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
-        limit = name.get_nonarch()
-
-        iter = arch_core.mirror_archive(from_arch,to_arch, limit)
-        for line in arch.chatter_classifier(iter):
-            cmdutil.colorize(line)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Updates a mirror from an archive.  If a branch, package, or version is
-supplied, only changes under it are mirrored.
-        """
-        return
-
-def help_tree_spec():
-    print """Specifying revisions (default: tree)
-Revisions may be specified by alias, revision, version or patchlevel.
-Revisions or versions may be fully qualified.  Unqualified revisions, versions, 
-or patchlevels use the archive of the current project tree.  Versions will
-use the latest patchlevel in the tree.  Patchlevels will use the current tree-
-version.
-
-Use "alias" to list available (user and automatic) aliases."""
-
-auto_alias = [
-"acur", 
-"The latest revision in the archive of the tree-version.  You can specify \
-a different version like so: acur:foo--bar--0 (aliases can be used)",
-"tcur",
-"""(tree current) The latest revision in the tree of the tree-version. \
-You can specify a different version like so: tcur:foo--bar--0 (aliases can be \
-used).""",
-"tprev" , 
-"""(tree previous) The previous revision in the tree of the tree-version.  To \
-specify an older revision, use a number, e.g. "tprev:4" """,
-"tanc" , 
-"""(tree ancestor) The ancestor revision of the tree To specify an older \
-revision, use a number, e.g. "tanc:4".""",
-"tdate" , 
-"""(tree date) The latest revision from a given date, e.g. "tdate:July 6".""",
-"tmod" , 
-""" (tree modified) The latest revision to modify a given file, e.g. \
-"tmod:engine.cpp" or "tmod:engine.cpp:16".""",
-"ttag" , 
-"""(tree tag) The revision that was tagged into the current tree revision, \
-according to the tree""",
-"tagcur", 
-"""(tag current) The latest revision of the version that the current tree \
-was tagged from.""",
-"mergeanc" , 
-"""The common ancestor of the current tree and the specified revision. \
-Defaults to the first partner-version's latest revision or to tagcur.""",
-]
-
-
-def is_auto_alias(name):
-    """Determine whether a name is an auto alias name
-
-    :param name: the name to check
-    :type name: str
-    :return: True if the name is an auto alias, false if not
-    :rtype: bool
-    """
-    return name in [f for (f, v) in pylon.util.iter_pairs(auto_alias)]
-
-
-def display_def(iter, wrap = 80):
-    """Display a list of definitions
-
-    :param iter: iter of name, definition pairs
-    :type iter: iter of (str, str)
-    :param wrap: The width for text wrapping
-    :type wrap: int
-    """
-    vals = list(iter)
-    maxlen = 0
-    for (key, value) in vals:
-        if len(key) > maxlen:
-            maxlen = len(key)
-    for (key, value) in vals:
-        tw=textwrap.TextWrapper(width=wrap, 
-                                initial_indent=key.rjust(maxlen)+" : ",
-                                subsequent_indent="".rjust(maxlen+3))
-        print tw.fill(value)
-
-
-def help_aliases(tree):
-    print """Auto-generated aliases"""
-    display_def(pylon.util.iter_pairs(auto_alias))
-    print "User aliases"
-    display_def(ancillary.iter_all_alias(tree))
-
-class Inventory(BaseCommand):
-    """List the status of files in the tree"""
-    def __init__(self):
-        self.description=self.__doc__
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        tree = arch.tree_root()
-        categories = []
-
-        if (options.source):
-            categories.append(arch_core.SourceFile)
-        if (options.precious):
-            categories.append(arch_core.PreciousFile)
-        if (options.backup):
-            categories.append(arch_core.BackupFile)
-        if (options.junk):
-            categories.append(arch_core.JunkFile)
-
-        if len(categories) == 1:
-            show_leading = False
-        else:
-            show_leading = True
-
-        if len(categories) == 0:
-            categories = None
-
-        if options.untagged:
-            categories = arch_core.non_root
-            show_leading = False
-            tagged = False
-        else:
-            tagged = None
-        
-        for file in arch_core.iter_inventory_filter(tree, None, 
-            control_files=options.control_files, 
-            categories = categories, tagged=tagged):
-            print arch_core.file_line(file, 
-                                      category = show_leading, 
-                                      untagged = show_leading,
-                                      id = options.ids)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai inventory [options]")
-        parser.add_option("--ids", action="store_true", dest="ids", 
-                          help="Show file ids")
-        parser.add_option("--control", action="store_true", 
-                          dest="control_files", help="include control files")
-        parser.add_option("--source", action="store_true", dest="source",
-                          help="List source files")
-        parser.add_option("--backup", action="store_true", dest="backup",
-                          help="List backup files")
-        parser.add_option("--precious", action="store_true", dest="precious",
-                          help="List precious files")
-        parser.add_option("--junk", action="store_true", dest="junk",
-                          help="List junk files")
-        parser.add_option("--unrecognized", action="store_true", 
-                          dest="unrecognized", help="List unrecognized files")
-        parser.add_option("--untagged", action="store_true", 
-                          dest="untagged", help="List only untagged files")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Lists the status of files in the archive:
-S source
-P precious
-B backup
-J junk
-U unrecognized
-T tree root
-? untagged-source
-Leading letter are not displayed if only one kind of file is shown
-        """
-        return
-
-
-class Alias(BaseCommand):
-    """List or adjust aliases"""
-    def __init__(self):
-        self.description=self.__doc__
-
-    def get_completer(self, arg, index):
-        if index > 2:
-            return ()
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-        if index == 0:
-            return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
-        elif index == 1:
-            return cmdutil.iter_revision_completions(arg, self.tree)
-
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            self.tree =  arch.tree_root()
-        except:
-            self.tree = None
-
-
-        try:
-            options.action(args, options)
-        except cmdutil.ForbiddenAliasSyntax, e:
-            raise CommandFailedWrapper(e)
-
-    def no_prefix(self, alias):
-        if alias.startswith("^"):
-            alias = alias[1:]
-        return alias
-        
-    def arg_dispatch(self, args, options):
-        """Add, modify, or list aliases, depending on number of arguments
-
-        :param args: The list of commandline arguments
-        :type args: list of str
-        :param options: The commandline options
-        """
-        if len(args) == 0:
-            help_aliases(self.tree)
-            return
-        else:
-            alias = self.no_prefix(args[0])
-            if len(args) == 1:
-                self.print_alias(alias)
-            elif (len(args)) == 2:
-                self.add(alias, args[1], options)
-            else:
-                raise cmdutil.GetHelp
-
-    def print_alias(self, alias):
-        answer = None
-        if is_auto_alias(alias):
-            raise pylon.errors.IsAutoAlias(alias, "\"%s\" is an auto alias."
-                "  Use \"revision\" to expand auto aliases." % alias)
-        for pair in ancillary.iter_all_alias(self.tree):
-            if pair[0] == alias:
-                answer = pair[1]
-        if answer is not None:
-            print answer
-        else:
-            print "The alias %s is not assigned." % alias
-
-    def add(self, alias, expansion, options):
-        """Add or modify aliases
-
-        :param alias: The alias name to create/modify
-        :type alias: str
-        :param expansion: The expansion to assign to the alias name
-        :type expansion: str
-        :param options: The commandline options
-        """
-        if is_auto_alias(alias):
-            raise IsAutoAlias(alias)
-        newlist = ""
-        written = False
-        new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
-            self.tree))
-        ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
-
-        for pair in self.get_iterator(options):
-            if pair[0] != alias:
-                newlist+="%s=%s\n" % (pair[0], pair[1])
-            elif not written:
-                newlist+=new_line
-                written = True
-        if not written:
-            newlist+=new_line
-        self.write_aliases(newlist, options)
-            
-    def delete(self, args, options):
-        """Delete the specified alias
-
-        :param args: The list of arguments
-        :type args: list of str
-        :param options: The commandline options
-        """
-        deleted = False
-        if len(args) != 1:
-            raise cmdutil.GetHelp
-        alias = self.no_prefix(args[0])
-        if is_auto_alias(alias):
-            raise IsAutoAlias(alias)
-        newlist = ""
-        for pair in self.get_iterator(options):
-            if pair[0] != alias:
-                newlist+="%s=%s\n" % (pair[0], pair[1])
-            else:
-                deleted = True
-        if not deleted:
-            raise errors.NoSuchAlias(alias)
-        self.write_aliases(newlist, options)
-
-    def get_alias_file(self, options):
-        """Return the name of the alias file to use
-
-        :param options: The commandline options
-        """
-        if options.tree:
-            if self.tree is None:
-                self.tree == arch.tree_root()
-            return str(self.tree)+"/{arch}/+aliases"
-        else:
-            return "~/.aba/aliases"
-
-    def get_iterator(self, options):
-        """Return the alias iterator to use
-
-        :param options: The commandline options
-        """
-        return ancillary.iter_alias(self.get_alias_file(options))
-
-    def write_aliases(self, newlist, options):
-        """Safely rewrite the alias file
-        :param newlist: The new list of aliases
-        :type newlist: str
-        :param options: The commandline options
-        """
-        filename = os.path.expanduser(self.get_alias_file(options))
-        file = util.NewFileVersion(filename)
-        file.write(newlist)
-        file.commit()
-
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "alias" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
-        parser.add_option("-d", "--delete", action="store_const", dest="action",
-                          const=self.delete, default=self.arg_dispatch, 
-                          help="Delete an alias")
-        parser.add_option("--tree", action="store_true", dest="tree", 
-                          help="Create a per-tree alias", default=False)
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Lists current aliases or modifies the list of aliases.
-
-If no arguments are supplied, aliases will be listed.  If two arguments are
-supplied, the specified alias will be created or modified.  If -d or --delete
-is supplied, the specified alias will be deleted.
-
-You can create aliases that refer to any fully-qualified part of the
-Arch namespace, e.g. 
-archive, 
-archive/category, 
-archive/category--branch, 
-archive/category--branch--version (my favourite)
-archive/category--branch--version--patchlevel
-
-Aliases can be used automatically by native commands.  To use them
-with external or tla commands, prefix them with ^ (you can do this
-with native commands, too).
-"""
-
-
-class RequestMerge(BaseCommand):
-    """Submit a merge request to Bug Goo"""
-    def __init__(self):
-        self.description=self.__doc__
-
-    def do_command(self, cmdargs):
-        """Submit a merge request
-
-        :param cmdargs: The commandline arguments
-        :type cmdargs: list of str
-        """
-        parser = self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            cmdutil.find_editor()
-        except pylon.errors.NoEditorSpecified, e:
-            raise pylon.errors.CommandFailedWrapper(e)
-        try:
-            self.tree=arch.tree_root()
-        except:
-            self.tree=None
-        base, revisions = self.revision_specs(args)
-        message = self.make_headers(base, revisions)
-        message += self.make_summary(revisions)
-        path = self.edit_message(message)
-        message = self.tidy_message(path)
-        if cmdutil.prompt("Send merge"):
-            self.send_message(message)
-            print "Merge request sent"
-
-    def make_headers(self, base, revisions):
-        """Produce email and Bug Goo header strings
-
-        :param base: The base revision to apply merges to
-        :type base: `arch.Revision`
-        :param revisions: The revisions to replay into the base
-        :type revisions: list of `arch.Patchlog`
-        :return: The headers
-        :rtype: str
-        """
-        headers = "To: gnu-arch-users at gnu.org\n"
-        headers += "From: %s\n" % options.fromaddr
-        if len(revisions) == 1:
-            headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
-        else:
-            headers += "Subject: [MERGE REQUEST]\n"
-        headers += "\n"
-        headers += "Base-Revision: %s\n" % base
-        for revision in revisions:
-            headers += "Revision: %s\n" % revision.revision
-        headers += "Bug: \n\n"
-        return headers
-
-    def make_summary(self, logs):
-        """Generate a summary of merges
-
-        :param logs: the patchlogs that were directly added by the merges
-        :type logs: list of `arch.Patchlog`
-        :return: the summary
-        :rtype: str
-        """ 
-        summary = ""
-        for log in logs:
-            summary+=str(log.revision)+"\n"
-            summary+=log.summary+"\n"
-            if log.description.strip():
-                summary+=log.description.strip('\n')+"\n\n"
-        return summary
-
-    def revision_specs(self, args):
-        """Determine the base and merge revisions from tree and arguments.
-
-        :param args: The parsed arguments
-        :type args: list of str
-        :return: The base revision and merge revisions 
-        :rtype: `arch.Revision`, list of `arch.Patchlog`
-        """
-        if len(args) > 0:
-            target_revision = cmdutil.determine_revision_arch(self.tree, 
-                                                              args[0])
-        else:
-            target_revision = arch_compound.tree_latest(self.tree)
-        if len(args) > 1:
-            merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
-                       self.tree, f)) for f in args[1:] ]
-        else:
-            if self.tree is None:
-                raise CantDetermineRevision("", "Not in a project tree")
-            merge_iter = cmdutil.iter_new_merges(self.tree, 
-                                                 target_revision.version, 
-                                                 False)
-            merges = [f for f in cmdutil.direct_merges(merge_iter)]
-        return (target_revision, merges)
-
-    def edit_message(self, message):
-        """Edit an email message in the user's standard editor
-
-        :param message: The message to edit
-        :type message: str
-        :return: the path of the edited message
-        :rtype: str
-        """
-        if self.tree is None:
-            path = os.get_cwd()
-        else:
-            path = self.tree
-        path += "/,merge-request"
-        file = open(path, 'w')
-        file.write(message)
-        file.flush()
-        cmdutil.invoke_editor(path)
-        return path
-
-    def tidy_message(self, path):
-        """Validate and clean up message.
-
-        :param path: The path to the message to clean up
-        :type path: str
-        :return: The parsed message
-        :rtype: `email.Message`
-        """
-        mail = email.message_from_file(open(path))
-        if mail["Subject"].strip() == "[MERGE REQUEST]":
-            raise BlandSubject
-        
-        request = email.message_from_string(mail.get_payload())
-        if request.has_key("Bug"):
-            if request["Bug"].strip()=="":
-                del request["Bug"]
-        mail.set_payload(request.as_string())
-        return mail
-
-    def send_message(self, message):
-        """Send a message, using its headers to address it.
-
-        :param message: The message to send
-        :type message: `email.Message`"""
-        server = smtplib.SMTP("localhost")
-        server.sendmail(message['From'], message['To'], message.as_string())
-        server.quit()
-
-    def help(self, parser=None):
-        """Print a usage message
-
-        :param parser: The options parser to use
-        :type parser: `cmdutil.CmdOptionParser`
-        """
-        if parser is None:
-            parser = self.get_parser()
-        parser.print_help()
-        print """
-Sends a merge request formatted for Bug Goo.  Intended use: get the tree
-you'd like to merge into.  Apply the merges you want.  Invoke request-merge.
-The merge request will open in your $EDITOR.
-
-When no TARGET is specified, it uses the current tree revision.  When
-no MERGE is specified, it uses the direct merges (as in "revisions
---direct-merges").  But you can specify just the TARGET, or all the MERGE
-revisions.
-"""
-
-    def get_parser(self):
-        """Produce a commandline parser for this command.
-
-        :rtype: `cmdutil.CmdOptionParser`
-        """
-        parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
-        return parser
-
-commands = { 
-'changes' : Changes,
-'help' : Help,
-'update': Update,
-'apply-changes':ApplyChanges,
-'cat-log': CatLog,
-'commit': Commit,
-'revision': Revision,
-'revisions': Revisions,
-'get': Get,
-'revert': Revert,
-'shell': Shell,
-'add-id': AddID,
-'merge': Merge,
-'elog': ELog,
-'mirror-archive': MirrorArchive,
-'ninventory': Inventory,
-'alias' : Alias,
-'request-merge': RequestMerge,
-}
-
-def my_import(mod_name):
-    module = __import__(mod_name)
-    components = mod_name.split('.')
-    for comp in components[1:]:
-        module = getattr(module, comp)
-    return module
-
-def plugin(mod_name):
-    module = my_import(mod_name)
-    module.add_command(commands)
-
-for file in os.listdir(sys.path[0]+"/command"):
-    if len(file) > 3 and file[-3:] == ".py" and file != "__init__.py":
-        plugin("command."+file[:-3])
-
-suggestions = {
-'apply-delta' : "Try \"apply-changes\".",
-'delta' : "To compare two revisions, use \"changes\".",
-'diff-rev' : "To compare two revisions, use \"changes\".",
-'undo' : "To undo local changes, use \"revert\".",
-'undelete' : "To undo only deletions, use \"revert --deletions\"",
-'missing-from' : "Try \"revisions --missing-from\".",
-'missing' : "Try \"revisions --missing\".",
-'missing-merge' : "Try \"revisions --partner-missing\".",
-'new-merges' : "Try \"revisions --new-merges\".",
-'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
-'logs' : "Try \"revisions --logs\"",
-'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
-'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
-'change-version' : "Try \"update REVISION\"",
-'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
-'rev-depends' : "Use revisions --dependencies",
-'auto-get' : "Plain get will do archive lookups",
-'tagline' : "Use add-id.  It uses taglines in tagline trees",
-'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
-'library-revisions' : "Use revisions --library",
-'file-revert' : "Use revert FILE",
-'join-branch' : "Use replay --logs-only"
-}
-# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/orig /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/orig
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/testdata/orig	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/testdata/orig	1970-01-01 01:00:00.000000000 +0100
@@ -1,2789 +0,0 @@
-# Copyright (C) 2004 Aaron Bentley
-# <aaron.bentley at utoronto.ca>
-#
-#    This program is free software; you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation; either version 2 of the License, or
-#    (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#
-#    You should have received a copy of the GNU General Public License
-#    along with this program; if not, write to the Free Software
-#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-import sys
-import arch
-import arch.util
-import arch.arch
-import abacmds
-import cmdutil
-import shutil
-import os
-import options
-import paths 
-import time
-import cmd
-import readline
-import re
-import string
-import arch_core
-from errors import *
-import errors
-import terminal
-import ancillary
-import misc
-import email
-import smtplib
-
-__docformat__ = "restructuredtext"
-__doc__ = "Implementation of user (sub) commands"
-commands = {}
-
-def find_command(cmd):
-    """
-    Return an instance of a command type.  Return None if the type isn't
-    registered.
-
-    :param cmd: the name of the command to look for
-    :type cmd: the type of the command
-    """
-    if commands.has_key(cmd):
-        return commands[cmd]()
-    else:
-        return None
-
-class BaseCommand:
-    def __call__(self, cmdline):
-        try:
-            self.do_command(cmdline.split())
-        except cmdutil.GetHelp, e:
-            self.help()
-        except Exception, e:
-            print e
-
-    def get_completer(index):
-        return None
-
-    def complete(self, args, text):
-        """
-        Returns a list of possible completions for the given text.
-
-        :param args: The complete list of arguments
-        :type args: List of str
-        :param text: text to complete (may be shorter than args[-1])
-        :type text: str
-        :rtype: list of str
-        """
-        matches = []
-        candidates = None
-
-        if len(args) > 0: 
-            realtext = args[-1]
-        else:
-            realtext = ""
-
-        try:
-            parser=self.get_parser()
-            if realtext.startswith('-'):
-                candidates = parser.iter_options()
-            else:
-                (options, parsed_args) = parser.parse_args(args)
-
-                if len (parsed_args) > 0:
-                    candidates = self.get_completer(parsed_args[-1], len(parsed_args) -1)
-                else:
-                    candidates = self.get_completer("", 0)
-        except:
-            pass
-        if candidates is None:
-            return
-        for candidate in candidates:
-            candidate = str(candidate)
-            if candidate.startswith(realtext):
-                matches.append(candidate[len(realtext)- len(text):])
-        return matches
-
-
-class Help(BaseCommand):
-    """
-    Lists commands, prints help messages.
-    """
-    def __init__(self):
-        self.description="Prints help mesages"
-        self.parser = None
-
-    def do_command(self, cmdargs):
-        """
-        Prints a help message.
-        """
-        options, args = self.get_parser().parse_args(cmdargs)
-        if len(args) > 1:
-            raise cmdutil.GetHelp
-
-        if options.native or options.suggestions or options.external:
-            native = options.native
-            suggestions = options.suggestions
-            external = options.external
-        else:
-            native = True
-            suggestions = False
-            external = True
-        
-        if len(args) == 0:
-            self.list_commands(native, suggestions, external)
-            return
-        elif len(args) == 1:
-            command_help(args[0])
-            return
-
-    def help(self):
-        self.get_parser().print_help()
-        print """
-If no command is specified, commands are listed.  If a command is
-specified, help for that command is listed.
-        """
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        if self.parser is not None:
-            return self.parser
-        parser=cmdutil.CmdOptionParser("fai help [command]")
-        parser.add_option("-n", "--native", action="store_true", 
-                         dest="native", help="Show native commands")
-        parser.add_option("-e", "--external", action="store_true", 
-                         dest="external", help="Show external commands")
-        parser.add_option("-s", "--suggest", action="store_true", 
-                         dest="suggestions", help="Show suggestions")
-        self.parser = parser
-        return parser 
-      
-    def list_commands(self, native=True, suggest=False, external=True):
-        """
-        Lists supported commands.
-
-        :param native: list native, python-based commands
-        :type native: bool
-        :param external: list external aba-style commands
-        :type external: bool
-        """
-        if native:
-            print "Native Fai commands"
-            keys=commands.keys()
-            keys.sort()
-            for k in keys:
-                space=""
-                for i in range(28-len(k)):
-                    space+=" "
-                print space+k+" : "+commands[k]().description
-            print
-        if suggest:
-            print "Unavailable commands and suggested alternatives"
-            key_list = suggestions.keys()
-            key_list.sort()
-            for key in key_list:
-                print "%28s : %s" % (key, suggestions[key])
-            print
-        if external:
-            fake_aba = abacmds.AbaCmds()
-            if (fake_aba.abadir == ""):
-                return
-            print "External commands"
-            fake_aba.list_commands()
-            print
-        if not suggest:
-            print "Use help --suggest to list alternatives to tla and aba"\
-                " commands."
-        if options.tla_fallthrough and (native or external):
-            print "Fai also supports tla commands."
-
-def command_help(cmd):
-    """
-    Prints help for a command.
-
-    :param cmd: The name of the command to print help for
-    :type cmd: str
-    """
-    fake_aba = abacmds.AbaCmds()
-    cmdobj = find_command(cmd)
-    if cmdobj != None:
-        cmdobj.help()
-    elif suggestions.has_key(cmd):
-        print "Not available\n" + suggestions[cmd]
-    else:
-        abacmd = fake_aba.is_command(cmd)
-        if abacmd:
-            abacmd.help()
-        else:
-            print "No help is available for \""+cmd+"\". Maybe try \"tla "+cmd+" -H\"?"
-
-
-
-class Changes(BaseCommand):
-    """
-    the "changes" command: lists differences between trees/revisions:
-    """
-    
-    def __init__(self):
-        self.description="Lists what files have changed in the project tree"
-
-    def get_completer(self, arg, index):
-        if index > 1:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-    
-    def parse_commandline(self, cmdline):
-        """
-        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-        if len(args) > 2:
-            raise cmdutil.GetHelp
-
-        tree=arch.tree_root()
-        if len(args) == 0:
-            a_spec = cmdutil.comp_revision(tree)
-        else:
-            a_spec = cmdutil.determine_revision_tree(tree, args[0])
-        cmdutil.ensure_archive_registered(a_spec.archive)
-        if len(args) == 2:
-            b_spec = cmdutil.determine_revision_tree(tree, args[1])
-            cmdutil.ensure_archive_registered(b_spec.archive)
-        else:
-            b_spec=tree
-        return options, a_spec, b_spec
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "changes" command.
-        """
-        try:
-            options, a_spec, b_spec = self.parse_commandline(cmdargs);
-        except cmdutil.CantDetermineRevision, e:
-            print e
-            return
-        except arch.errors.TreeRootError, e:
-            print e
-            return
-        if options.changeset:
-            changeset=options.changeset
-            tmpdir = None
-        else:
-            tmpdir=cmdutil.tmpdir()
-            changeset=tmpdir+"/changeset"
-        try:
-            delta=arch.iter_delta(a_spec, b_spec, changeset)
-            try:
-                for line in delta:
-                    if cmdutil.chattermatch(line, "changeset:"):
-                        pass
-                    else:
-                        cmdutil.colorize(line, options.suppress_chatter)
-            except arch.util.ExecProblem, e:
-                if e.proc.error and e.proc.error.startswith(
-                    "missing explicit id for file"):
-                    raise MissingID(e)
-                else:
-                    raise
-            status=delta.status
-            if status > 1:
-                return
-            if (options.perform_diff):
-                chan = cmdutil.ChangesetMunger(changeset)
-                chan.read_indices()
-                if isinstance(b_spec, arch.Revision):
-                    b_dir = b_spec.library_find()
-                else:
-                    b_dir = b_spec
-                a_dir = a_spec.library_find()
-                if options.diffopts is not None:
-                    diffopts = options.diffopts.split()
-                    cmdutil.show_custom_diffs(chan, diffopts, a_dir, b_dir)
-                else:
-                    cmdutil.show_diffs(delta.changeset)
-        finally:
-            if tmpdir and (os.access(tmpdir, os.X_OK)):
-                shutil.rmtree(tmpdir)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "changes" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai changes [options] [revision]"
-                                       " [revision]")
-        parser.add_option("-d", "--diff", action="store_true", 
-                          dest="perform_diff", default=False, 
-                          help="Show diffs in summary")
-        parser.add_option("-c", "--changeset", dest="changeset", 
-                          help="Store a changeset in the given directory", 
-                          metavar="DIRECTORY")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        parser.add_option("--diffopts", dest="diffopts", 
-                          help="Use the specified diff options", 
-                          metavar="OPTIONS")
-
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Performs source-tree comparisons
-
-If no revision is specified, the current project tree is compared to the
-last-committed revision.  If one revision is specified, the current project
-tree is compared to that revision.  If two revisions are specified, they are
-compared to each other.
-        """
-        help_tree_spec() 
-        return
-
-
-class ApplyChanges(BaseCommand):
-    """
-    Apply differences between two revisions to a tree
-    """
-    
-    def __init__(self):
-        self.description="Applies changes to a project tree"
-    
-    def get_completer(self, arg, index):
-        if index > 1:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-    def parse_commandline(self, cmdline, tree):
-        """
-        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-        if len(args) != 2:
-            raise cmdutil.GetHelp
-
-        a_spec = cmdutil.determine_revision_tree(tree, args[0])
-        cmdutil.ensure_archive_registered(a_spec.archive)
-        b_spec = cmdutil.determine_revision_tree(tree, args[1])
-        cmdutil.ensure_archive_registered(b_spec.archive)
-        return options, a_spec, b_spec
-
-    def do_command(self, cmdargs):
-        """
-        Master function that performs "apply-changes".
-        """
-        try:
-            tree = arch.tree_root()
-            options, a_spec, b_spec = self.parse_commandline(cmdargs, tree);
-        except cmdutil.CantDetermineRevision, e:
-            print e
-            return
-        except arch.errors.TreeRootError, e:
-            print e
-            return
-        delta=cmdutil.apply_delta(a_spec, b_spec, tree)
-        for line in cmdutil.iter_apply_delta_filter(delta):
-            cmdutil.colorize(line, options.suppress_chatter)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "apply-changes" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai apply-changes [options] revision"
-                                       " revision")
-        parser.add_option("-d", "--diff", action="store_true", 
-                          dest="perform_diff", default=False, 
-                          help="Show diffs in summary")
-        parser.add_option("-c", "--changeset", dest="changeset", 
-                          help="Store a changeset in the given directory", 
-                          metavar="DIRECTORY")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Applies changes to a project tree
-
-Compares two revisions and applies the difference between them to the current
-tree.
-        """
-        help_tree_spec() 
-        return
-
-class Update(BaseCommand):
-    """
-    Updates a project tree to a given revision, preserving un-committed hanges. 
-    """
-    
-    def __init__(self):
-        self.description="Apply the latest changes to the current directory"
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-    
-    def parse_commandline(self, cmdline, tree):
-        """
-        Parse commandline arguments.  Raises cmdutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-        if len(args) > 2:
-            raise cmdutil.GetHelp
-
-        spec=None
-        if len(args)>0:
-            spec=args[0]
-        revision=cmdutil.determine_revision_arch(tree, spec)
-        cmdutil.ensure_archive_registered(revision.archive)
-
-        mirror_source = cmdutil.get_mirror_source(revision.archive)
-        if mirror_source != None:
-            if cmdutil.prompt("Mirror update"):
-                cmd=cmdutil.mirror_archive(mirror_source, 
-                    revision.archive, arch.NameParser(revision).get_package_version())
-                for line in arch.chatter_classifier(cmd):
-                    cmdutil.colorize(line, options.suppress_chatter)
-
-                revision=cmdutil.determine_revision_arch(tree, spec)
-
-        return options, revision 
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "update" command.
-        """
-        tree=arch.tree_root()
-        try:
-            options, to_revision = self.parse_commandline(cmdargs, tree);
-        except cmdutil.CantDetermineRevision, e:
-            print e
-            return
-        except arch.errors.TreeRootError, e:
-            print e
-            return
-        from_revision=cmdutil.tree_latest(tree)
-        if from_revision==to_revision:
-            print "Tree is already up to date with:\n"+str(to_revision)+"."
-            return
-        cmdutil.ensure_archive_registered(from_revision.archive)
-        cmd=cmdutil.apply_delta(from_revision, to_revision, tree,
-            options.patch_forward)
-        for line in cmdutil.iter_apply_delta_filter(cmd):
-            cmdutil.colorize(line)
-        if to_revision.version != tree.tree_version:
-            if cmdutil.prompt("Update version"):
-                tree.tree_version = to_revision.version
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "update" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai update [options]"
-                                       " [revision/version]")
-        parser.add_option("-f", "--forward", action="store_true", 
-                          dest="patch_forward", default=False, 
-                          help="pass the --forward option to 'patch'")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Updates a working tree to the current archive revision
-
-If a revision or version is specified, that is used instead 
-        """
-        help_tree_spec() 
-        return
-
-
-class Commit(BaseCommand):
-    """
-    Create a revision based on the changes in the current tree.
-    """
-    
-    def __init__(self):
-        self.description="Write local changes to the archive"
-
-    def get_completer(self, arg, index):
-        if arg is None:
-            arg = ""
-        return iter_modified_file_completions(arch.tree_root(), arg)
-#        return iter_source_file_completions(arch.tree_root(), arg)
-    
-    def parse_commandline(self, cmdline, tree):
-        """
-        Parse commandline arguments.  Raise cmtutil.GetHelp if help is needed.
-        
-        :param cmdline: A list of arguments to parse
-        :rtype: (options, Revision, Revision/WorkingTree)
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdline)
-
-        if len(args) == 0:
-            args = None
-        revision=cmdutil.determine_revision_arch(tree, options.version)
-        return options, revision.get_version(), args
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "commit" command.
-        """
-        tree=arch.tree_root()
-        options, version, files = self.parse_commandline(cmdargs, tree)
-        if options.__dict__.has_key("base") and options.base:
-            base = cmdutil.determine_revision_tree(tree, options.base)
-        else:
-            base = cmdutil.submit_revision(tree)
-        
-        writeversion=version
-        archive=version.archive
-        source=cmdutil.get_mirror_source(archive)
-        allow_old=False
-        writethrough="implicit"
-
-        if source!=None:
-            if writethrough=="explicit" and \
-                cmdutil.prompt("Writethrough"):
-                writeversion=arch.Version(str(source)+"/"+str(version.get_nonarch()))
-            elif writethrough=="none":
-                raise CommitToMirror(archive)
-
-        elif archive.is_mirror:
-            raise CommitToMirror(archive)
-
-        try:
-            last_revision=tree.iter_logs(version, True).next().revision
-        except StopIteration, e:
-            if cmdutil.prompt("Import from commit"):
-                return do_import(version)
-            else:
-                raise NoVersionLogs(version)
-        if last_revision!=version.iter_revisions(True).next():
-            if not cmdutil.prompt("Out of date"):
-                raise OutOfDate
-            else:
-                allow_old=True
-
-        try:
-            if not cmdutil.has_changed(version):
-                if not cmdutil.prompt("Empty commit"):
-                    raise EmptyCommit
-        except arch.util.ExecProblem, e:
-            if e.proc.error and e.proc.error.startswith(
-                "missing explicit id for file"):
-                raise MissingID(e)
-            else:
-                raise
-        log = tree.log_message(create=False)
-        if log is None:
-            try:
-                if cmdutil.prompt("Create log"):
-                    edit_log(tree)
-
-            except cmdutil.NoEditorSpecified, e:
-                raise CommandFailed(e)
-            log = tree.log_message(create=False)
-        if log is None: 
-            raise NoLogMessage
-        if log["Summary"] is None or len(log["Summary"].strip()) == 0:
-            if not cmdutil.prompt("Omit log summary"):
-                raise errors.NoLogSummary
-        try:
-            for line in tree.iter_commit(version, seal=options.seal_version,
-                base=base, out_of_date_ok=allow_old, file_list=files):
-                cmdutil.colorize(line, options.suppress_chatter)
-
-        except arch.util.ExecProblem, e:
-            if e.proc.error and e.proc.error.startswith(
-                "These files violate naming conventions:"):
-                raise LintFailure(e.proc.error)
-            else:
-                raise
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "commit" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-
-        parser=cmdutil.CmdOptionParser("fai commit [options] [file1]"
-                                       " [file2...]")
-        parser.add_option("--seal", action="store_true", 
-                          dest="seal_version", default=False, 
-                          help="seal this version")
-        parser.add_option("-v", "--version", dest="version", 
-                          help="Use the specified version", 
-                          metavar="VERSION")
-        parser.add_option("-s", "--silent", action="store_true", 
-                          dest="suppress_chatter", default=False, 
-                          help="Suppress chatter messages")
-        if cmdutil.supports_switch("commit", "--base"):
-            parser.add_option("--base", dest="base", help="", 
-                              metavar="REVISION")
-        return parser
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser is None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Updates a working tree to the current archive revision
-
-If a version is specified, that is used instead 
-        """
-#        help_tree_spec() 
-        return
-
-
-
-class CatLog(BaseCommand):
-    """
-    Print the log of a given file (from current tree)
-    """
-    def __init__(self):
-        self.description="Prints the patch log for a revision"
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "cat-log" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError, e:
-            tree = None
-        spec=None
-        if len(args) > 0:
-            spec=args[0]
-        if len(args) > 1:
-            raise cmdutil.GetHelp()
-        try:
-            if tree:
-                revision = cmdutil.determine_revision_tree(tree, spec)
-            else:
-                revision = cmdutil.determine_revision_arch(tree, spec)
-        except cmdutil.CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        log = None
-        
-        use_tree = (options.source == "tree" or \
-            (options.source == "any" and tree))
-        use_arch = (options.source == "archive" or options.source == "any")
-        
-        log = None
-        if use_tree:
-            for log in tree.iter_logs(revision.get_version()):
-                if log.revision == revision:
-                    break
-                else:
-                    log = None
-        if log is None and use_arch:
-            cmdutil.ensure_revision_exists(revision)
-            log = arch.Patchlog(revision)
-        if log is not None:
-            for item in log.items():
-                print "%s: %s" % item
-            print log.description
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "cat-log" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai cat-log [revision]")
-        parser.add_option("--archive", action="store_const", dest="source",
-                          const="archive", default="any",
-                          help="Always get the log from the archive")
-        parser.add_option("--tree", action="store_const", dest="source",
-                          const="tree", help="Always get the log from the tree")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Prints the log for the specified revision
-        """
-        help_tree_spec()
-        return
-
-class Revert(BaseCommand):
-    """ Reverts a tree (or aspects of it) to a revision
-    """
-    def __init__(self):
-        self.description="Reverts a tree (or aspects of it) to a revision "
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return iter_modified_file_completions(tree, arg)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revert" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError, e:
-            raise CommandFailed(e)
-        spec=None
-        if options.revision is not None:
-            spec=options.revision
-        try:
-            if spec is not None:
-                revision = cmdutil.determine_revision_tree(tree, spec)
-            else:
-                revision = cmdutil.comp_revision(tree)
-        except cmdutil.CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        munger = None
-
-        if options.file_contents or options.file_perms or options.deletions\
-            or options.additions or options.renames or options.hunk_prompt:
-            munger = cmdutil.MungeOpts()
-            munger.hunk_prompt = options.hunk_prompt
-
-        if len(args) > 0 or options.logs or options.pattern_files or \
-            options.control:
-            if munger is None:
-                munger = cmdutil.MungeOpts(True)
-                munger.all_types(True)
-        if len(args) > 0:
-            t_cwd = cmdutil.tree_cwd(tree)
-            for name in args:
-                if len(t_cwd) > 0:
-                    t_cwd += "/"
-                name = "./" + t_cwd + name
-                munger.add_keep_file(name);
-
-        if options.file_perms:
-            munger.file_perms = True
-        if options.file_contents:
-            munger.file_contents = True
-        if options.deletions:
-            munger.deletions = True
-        if options.additions:
-            munger.additions = True
-        if options.renames:
-            munger.renames = True
-        if options.logs:
-            munger.add_keep_pattern('^\./\{arch\}/[^=].*')
-        if options.control:
-            munger.add_keep_pattern("/\.arch-ids|^\./\{arch\}|"\
-                                    "/\.arch-inventory$")
-        if options.pattern_files:
-            munger.add_keep_pattern(options.pattern_files)
-                
-        for line in cmdutil.revert(tree, revision, munger, 
-                                   not options.no_output):
-            cmdutil.colorize(line)
-
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "cat-log" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai revert [options] [FILE...]")
-        parser.add_option("", "--contents", action="store_true", 
-                          dest="file_contents", 
-                          help="Revert file content changes")
-        parser.add_option("", "--permissions", action="store_true", 
-                          dest="file_perms", 
-                          help="Revert file permissions changes")
-        parser.add_option("", "--deletions", action="store_true", 
-                          dest="deletions", 
-                          help="Restore deleted files")
-        parser.add_option("", "--additions", action="store_true", 
-                          dest="additions", 
-                          help="Remove added files")
-        parser.add_option("", "--renames", action="store_true", 
-                          dest="renames", 
-                          help="Revert file names")
-        parser.add_option("--hunks", action="store_true", 
-                          dest="hunk_prompt", default=False,
-                          help="Prompt which hunks to revert")
-        parser.add_option("--pattern-files", dest="pattern_files", 
-                          help="Revert files that match this pattern", 
-                          metavar="REGEX")
-        parser.add_option("--logs", action="store_true", 
-                          dest="logs", default=False,
-                          help="Revert only logs")
-        parser.add_option("--control-files", action="store_true", 
-                          dest="control", default=False,
-                          help="Revert logs and other control files")
-        parser.add_option("-n", "--no-output", action="store_true", 
-                          dest="no_output", 
-                          help="Don't keep an undo changeset")
-        parser.add_option("--revision", dest="revision", 
-                          help="Revert to the specified revision", 
-                          metavar="REVISION")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Reverts changes in the current working tree.  If no flags are specified, all
-types of changes are reverted.  Otherwise, only selected types of changes are
-reverted.  
-
-If a revision is specified on the commandline, differences between the current
-tree and that revision are reverted.  If a version is specified, the current
-tree is used to determine the revision.
-
-If files are specified, only those files listed will have any changes applied.
-To specify a renamed file, you can use either the old or new name. (or both!)
-
-Unless "-n" is specified, reversions can be undone with "redo".
-        """
-        return
-
-class Revision(BaseCommand):
-    """
-    Print a revision name based on a revision specifier
-    """
-    def __init__(self):
-        self.description="Prints the name of a revision"
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError:
-            tree = None
-
-        spec=None
-        if len(args) > 0:
-            spec=args[0]
-        if len(args) > 1:
-            raise cmdutil.GetHelp
-        try:
-            if tree:
-                revision = cmdutil.determine_revision_tree(tree, spec)
-            else:
-                revision = cmdutil.determine_revision_arch(tree, spec)
-        except cmdutil.CantDetermineRevision, e:
-            print str(e)
-            return
-        print options.display(revision)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai revision [revision]")
-        parser.add_option("", "--location", action="store_const", 
-                         const=paths.determine_path, dest="display", 
-                         help="Show location instead of name", default=str)
-        parser.add_option("--import", action="store_const", 
-                         const=paths.determine_import_path, dest="display",  
-                         help="Show location of import file")
-        parser.add_option("--log", action="store_const", 
-                         const=paths.determine_log_path, dest="display", 
-                         help="Show location of log file")
-        parser.add_option("--patch", action="store_const", 
-                         dest="display", const=paths.determine_patch_path,
-                         help="Show location of patchfile")
-        parser.add_option("--continuation", action="store_const", 
-                         const=paths.determine_continuation_path, 
-                         dest="display",
-                         help="Show location of continuation file")
-        parser.add_option("--cacherev", action="store_const", 
-                         const=paths.determine_cacherev_path, dest="display",
-                         help="Show location of cacherev file")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Expands aliases and prints the name of the specified revision.  Instead of
-the name, several options can be used to print locations.  If more than one is
-specified, the last one is used.
-        """
-        help_tree_spec()
-        return
-
-def require_version_exists(version, spec):
-    if not version.exists():
-        raise cmdutil.CantDetermineVersion(spec, 
-                                           "The version %s does not exist." \
-                                           % version)
-
-class Revisions(BaseCommand):
-    """
-    Print a revision name based on a revision specifier
-    """
-    def __init__(self):
-        self.description="Lists revisions"
-    
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-        (options, args) = self.get_parser().parse_args(cmdargs)
-        if len(args) > 1:
-            raise cmdutil.GetHelp
-        try:
-            self.tree = arch.tree_root()
-        except arch.errors.TreeRootError:
-            self.tree = None
-        try:
-            iter = self.get_iterator(options.type, args, options.reverse, 
-                                     options.modified)
-        except cmdutil.CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-
-        if options.skip is not None:
-            iter = cmdutil.iter_skip(iter, int(options.skip))
-
-        for revision in iter:
-            log = None
-            if isinstance(revision, arch.Patchlog):
-                log = revision
-                revision=revision.revision
-            print options.display(revision)
-            if log is None and (options.summary or options.creator or 
-                                options.date or options.merges):
-                log = revision.patchlog
-            if options.creator:
-                print "    %s" % log.creator
-            if options.date:
-                print "    %s" % time.strftime('%Y-%m-%d %H:%M:%S %Z', log.date)
-            if options.summary:
-                print "    %s" % log.summary
-            if options.merges:
-                showed_title = False
-                for revision in log.merged_patches:
-                    if not showed_title:
-                        print "    Merged:"
-                        showed_title = True
-                    print "    %s" % revision
-
-    def get_iterator(self, type, args, reverse, modified):
-        if len(args) > 0:
-            spec = args[0]
-        else:
-            spec = None
-        if modified is not None:
-            iter = cmdutil.modified_iter(modified, self.tree)
-            if reverse:
-                return iter
-            else:
-                return cmdutil.iter_reverse(iter)
-        elif type == "archive":
-            if spec is None:
-                if self.tree is None:
-                    raise cmdutil.CantDetermineRevision("", 
-                                                        "Not in a project tree")
-                version = cmdutil.determine_version_tree(spec, self.tree)
-            else:
-                version = cmdutil.determine_version_arch(spec, self.tree)
-                cmdutil.ensure_archive_registered(version.archive)
-                require_version_exists(version, spec)
-            return version.iter_revisions(reverse)
-        elif type == "cacherevs":
-            if spec is None:
-                if self.tree is None:
-                    raise cmdutil.CantDetermineRevision("", 
-                                                        "Not in a project tree")
-                version = cmdutil.determine_version_tree(spec, self.tree)
-            else:
-                version = cmdutil.determine_version_arch(spec, self.tree)
-                cmdutil.ensure_archive_registered(version.archive)
-                require_version_exists(version, spec)
-            return cmdutil.iter_cacherevs(version, reverse)
-        elif type == "library":
-            if spec is None:
-                if self.tree is None:
-                    raise cmdutil.CantDetermineRevision("", 
-                                                        "Not in a project tree")
-                version = cmdutil.determine_version_tree(spec, self.tree)
-            else:
-                version = cmdutil.determine_version_arch(spec, self.tree)
-            return version.iter_library_revisions(reverse)
-        elif type == "logs":
-            if self.tree is None:
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
-            return self.tree.iter_logs(cmdutil.determine_version_tree(spec, \
-                                  self.tree), reverse)
-        elif type == "missing" or type == "skip-present":
-            if self.tree is None:
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
-            skip = (type == "skip-present")
-            version = cmdutil.determine_version_tree(spec, self.tree)
-            cmdutil.ensure_archive_registered(version.archive)
-            require_version_exists(version, spec)
-            return cmdutil.iter_missing(self.tree, version, reverse,
-                                        skip_present=skip)
-
-        elif type == "present":
-            if self.tree is None:
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
-            version = cmdutil.determine_version_tree(spec, self.tree)
-            cmdutil.ensure_archive_registered(version.archive)
-            require_version_exists(version, spec)
-            return cmdutil.iter_present(self.tree, version, reverse)
-
-        elif type == "new-merges" or type == "direct-merges":
-            if self.tree is None:
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
-            version = cmdutil.determine_version_tree(spec, self.tree)
-            cmdutil.ensure_archive_registered(version.archive)
-            require_version_exists(version, spec)
-            iter = cmdutil.iter_new_merges(self.tree, version, reverse)
-            if type == "new-merges":
-                return iter
-            elif type == "direct-merges":
-                return cmdutil.direct_merges(iter)
-
-        elif type == "missing-from":
-            if self.tree is None:
-                raise cmdutil.CantDetermineRevision("", "Not in a project tree")
-            revision = cmdutil.determine_revision_tree(self.tree, spec)
-            libtree = cmdutil.find_or_make_local_revision(revision)
-            return cmdutil.iter_missing(libtree, self.tree.tree_version,
-                                        reverse)
-
-        elif type == "partner-missing":
-            return cmdutil.iter_partner_missing(self.tree, reverse)
-
-        elif type == "ancestry":
-            revision = cmdutil.determine_revision_tree(self.tree, spec)
-            iter = cmdutil._iter_ancestry(self.tree, revision)
-            if reverse:
-                return iter
-            else:
-                return cmdutil.iter_reverse(iter)
-
-        elif type == "dependencies" or type == "non-dependencies":
-            nondeps = (type == "non-dependencies")
-            revision = cmdutil.determine_revision_tree(self.tree, spec)
-            anc_iter = cmdutil._iter_ancestry(self.tree, revision)
-            iter_depends = cmdutil.iter_depends(anc_iter, nondeps)
-            if reverse:
-                return iter_depends
-            else:
-                return cmdutil.iter_reverse(iter_depends)
-        elif type == "micro":
-            return cmdutil.iter_micro(self.tree)
-
-    
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai revisions [revision]")
-        select = cmdutil.OptionGroup(parser, "Selection options",
-                          "Control which revisions are listed.  These options"
-                          " are mutually exclusive.  If more than one is"
-                          " specified, the last is used.")
-        select.add_option("", "--archive", action="store_const", 
-                          const="archive", dest="type", default="archive",
-                          help="List all revisions in the archive")
-        select.add_option("", "--cacherevs", action="store_const", 
-                          const="cacherevs", dest="type",
-                          help="List all revisions stored in the archive as "
-                          "complete copies")
-        select.add_option("", "--logs", action="store_const", 
-                          const="logs", dest="type",
-                          help="List revisions that have a patchlog in the "
-                          "tree")
-        select.add_option("", "--missing", action="store_const", 
-                          const="missing", dest="type",
-                          help="List revisions from the specified version that"
-                          " have no patchlog in the tree")
-        select.add_option("", "--skip-present", action="store_const", 
-                          const="skip-present", dest="type",
-                          help="List revisions from the specified version that"
-                          " have no patchlogs at all in the tree")
-        select.add_option("", "--present", action="store_const", 
-                          const="present", dest="type",
-                          help="List revisions from the specified version that"
-                          " have no patchlog in the tree, but can't be merged")
-        select.add_option("", "--missing-from", action="store_const", 
-                          const="missing-from", dest="type",
-                          help="List revisions from the specified revision "
-                          "that have no patchlog for the tree version")
-        select.add_option("", "--partner-missing", action="store_const", 
-                          const="partner-missing", dest="type",
-                          help="List revisions in partner versions that are"
-                          " missing")
-        select.add_option("", "--new-merges", action="store_const", 
-                          const="new-merges", dest="type",
-                          help="List revisions that have had patchlogs added"
-                          " to the tree since the last commit")
-        select.add_option("", "--direct-merges", action="store_const", 
-                          const="direct-merges", dest="type",
-                          help="List revisions that have been directly added"
-                          " to tree since the last commit ")
-        select.add_option("", "--library", action="store_const", 
-                          const="library", dest="type",
-                          help="List revisions in the revision library")
-        select.add_option("", "--ancestry", action="store_const", 
-                          const="ancestry", dest="type",
-                          help="List revisions that are ancestors of the "
-                          "current tree version")
-
-        select.add_option("", "--dependencies", action="store_const", 
-                          const="dependencies", dest="type",
-                          help="List revisions that the given revision "
-                          "depends on")
-
-        select.add_option("", "--non-dependencies", action="store_const", 
-                          const="non-dependencies", dest="type",
-                          help="List revisions that the given revision "
-                          "does not depend on")
-
-        select.add_option("--micro", action="store_const", 
-                          const="micro", dest="type",
-                          help="List partner revisions aimed for this "
-                          "micro-branch")
-
-        select.add_option("", "--modified", dest="modified", 
-                          help="List tree ancestor revisions that modified a "
-                          "given file", metavar="FILE[:LINE]")
-
-        parser.add_option("", "--skip", dest="skip", 
-                          help="Skip revisions.  Positive numbers skip from "
-                          "beginning, negative skip from end.",
-                          metavar="NUMBER")
-
-        parser.add_option_group(select)
-
-        format = cmdutil.OptionGroup(parser, "Revision format options",
-                          "These control the appearance of listed revisions")
-        format.add_option("", "--location", action="store_const", 
-                         const=paths.determine_path, dest="display", 
-                         help="Show location instead of name", default=str)
-        format.add_option("--import", action="store_const", 
-                         const=paths.determine_import_path, dest="display",  
-                         help="Show location of import file")
-        format.add_option("--log", action="store_const", 
-                         const=paths.determine_log_path, dest="display", 
-                         help="Show location of log file")
-        format.add_option("--patch", action="store_const", 
-                         dest="display", const=paths.determine_patch_path,
-                         help="Show location of patchfile")
-        format.add_option("--continuation", action="store_const", 
-                         const=paths.determine_continuation_path, 
-                         dest="display",
-                         help="Show location of continuation file")
-        format.add_option("--cacherev", action="store_const", 
-                         const=paths.determine_cacherev_path, dest="display",
-                         help="Show location of cacherev file")
-        parser.add_option_group(format)
-        display = cmdutil.OptionGroup(parser, "Display format options",
-                          "These control the display of data")
-        display.add_option("-r", "--reverse", action="store_true", 
-                          dest="reverse", help="Sort from newest to oldest")
-        display.add_option("-s", "--summary", action="store_true", 
-                          dest="summary", help="Show patchlog summary")
-        display.add_option("-D", "--date", action="store_true", 
-                          dest="date", help="Show patchlog date")
-        display.add_option("-c", "--creator", action="store_true", 
-                          dest="creator", help="Show the id that committed the"
-                          " revision")
-        display.add_option("-m", "--merges", action="store_true", 
-                          dest="merges", help="Show the revisions that were"
-                          " merged")
-        parser.add_option_group(display)
-        return parser 
-    def help(self, parser=None):
-        """Attempt to explain the revisions command
-        
-        :param parser: If supplied, used to determine options
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """List revisions.
-        """
-        help_tree_spec()
-
-
-class Get(BaseCommand):
-    """
-    Retrieve a revision from the archive
-    """
-    def __init__(self):
-        self.description="Retrieve a revision from the archive"
-        self.parser=self.get_parser()
-
-
-    def get_completer(self, arg, index):
-        if index > 0:
-            return None
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-        return cmdutil.iter_revision_completions(arg, tree)
-
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "get" command.
-        """
-        (options, args) = self.parser.parse_args(cmdargs)
-        if len(args) < 1:
-            return self.help()            
-        try:
-            tree = arch.tree_root()
-        except arch.errors.TreeRootError:
-            tree = None
-        
-        arch_loc = None
-        try:
-            revision, arch_loc = paths.full_path_decode(args[0])
-        except Exception, e:
-            revision = cmdutil.determine_revision_arch(tree, args[0], 
-                check_existence=False, allow_package=True)
-        if len(args) > 1:
-            directory = args[1]
-        else:
-            directory = str(revision.nonarch)
-        if os.path.exists(directory):
-            raise DirectoryExists(directory)
-        cmdutil.ensure_archive_registered(revision.archive, arch_loc)
-        try:
-            cmdutil.ensure_revision_exists(revision)
-        except cmdutil.NoSuchRevision, e:
-            raise CommandFailedWrapper(e)
-
-        link = cmdutil.prompt ("get link")
-        for line in cmdutil.iter_get(revision, directory, link,
-                                     options.no_pristine,
-                                     options.no_greedy_add):
-            cmdutil.colorize(line)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "get" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai get revision [dir]")
-        parser.add_option("--no-pristine", action="store_true", 
-                         dest="no_pristine", 
-                         help="Do not make pristine copy for reference")
-        parser.add_option("--no-greedy-add", action="store_true", 
-                         dest="no_greedy_add", 
-                         help="Never add to greedy libraries")
-
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Expands aliases and constructs a project tree for a revision.  If the optional
-"dir" argument is provided, the project tree will be stored in this directory.
-        """
-        help_tree_spec()
-        return
-
-class PromptCmd(cmd.Cmd):
-    def __init__(self):
-        cmd.Cmd.__init__(self)
-        self.prompt = "Fai> "
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-        self.set_title()
-        self.set_prompt()
-        self.fake_aba = abacmds.AbaCmds()
-        self.identchars += '-'
-        self.history_file = os.path.expanduser("~/.fai-history")
-        readline.set_completer_delims(string.whitespace)
-        if os.access(self.history_file, os.R_OK) and \
-            os.path.isfile(self.history_file):
-            readline.read_history_file(self.history_file)
-
-    def write_history(self):
-        readline.write_history_file(self.history_file)
-
-    def do_quit(self, args):
-        self.write_history()
-        sys.exit(0)
-
-    def do_exit(self, args):
-        self.do_quit(args)
-
-    def do_EOF(self, args):
-        print
-        self.do_quit(args)
-
-    def postcmd(self, line, bar):
-        self.set_title()
-        self.set_prompt()
-
-    def set_prompt(self):
-        if self.tree is not None:
-            try:
-                version = " "+self.tree.tree_version.nonarch
-            except:
-                version = ""
-        else:
-            version = ""
-        self.prompt = "Fai%s> " % version
-
-    def set_title(self, command=None):
-        try:
-            version = self.tree.tree_version.nonarch
-        except:
-            version = "[no version]"
-        if command is None:
-            command = ""
-        sys.stdout.write(terminal.term_title("Fai %s %s" % (command, version)))
-
-    def do_cd(self, line):
-        if line == "":
-            line = "~"
-        try:
-            os.chdir(os.path.expanduser(line))
-        except Exception, e:
-            print e
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-    def do_help(self, line):
-        Help()(line)
-
-    def default(self, line):
-        args = line.split()
-        if find_command(args[0]):
-            try:
-                find_command(args[0]).do_command(args[1:])
-            except cmdutil.BadCommandOption, e:
-                print e
-            except cmdutil.GetHelp, e:
-                find_command(args[0]).help()
-            except CommandFailed, e:
-                print e
-            except arch.errors.ArchiveNotRegistered, e:
-                print e
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-            except arch.util.ExecProblem, e:
-                print e.proc.error.rstrip('\n')
-            except cmdutil.CantDetermineVersion, e:
-                print e
-            except cmdutil.CantDetermineRevision, e:
-                print e
-            except Exception, e:
-                print "Unhandled error:\n%s" % cmdutil.exception_str(e)
-
-        elif suggestions.has_key(args[0]):
-            print suggestions[args[0]]
-
-        elif self.fake_aba.is_command(args[0]):
-            tree = None
-            try:
-                tree = arch.tree_root()
-            except arch.errors.TreeRootError:
-                pass
-            cmd = self.fake_aba.is_command(args[0])
-            try:
-                cmd.run(cmdutil.expand_prefix_alias(args[1:], tree))
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-
-        elif options.tla_fallthrough and args[0] != "rm" and \
-            cmdutil.is_tla_command(args[0]):
-            try:
-                tree = None
-                try:
-                    tree = arch.tree_root()
-                except arch.errors.TreeRootError:
-                    pass
-                args = cmdutil.expand_prefix_alias(args, tree)
-                arch.util.exec_safe('tla', args, stderr=sys.stderr,
-                expected=(0, 1))
-            except arch.util.ExecProblem, e:
-                pass
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-        else:
-            try:
-                try:
-                    tree = arch.tree_root()
-                except arch.errors.TreeRootError:
-                    tree = None
-                args=line.split()
-                os.system(" ".join(cmdutil.expand_prefix_alias(args, tree)))
-            except KeyboardInterrupt, e:
-                print "Interrupted"
-
-    def completenames(self, text, line, begidx, endidx):
-        completions = []
-        iter = iter_command_names(self.fake_aba)
-        try:
-            if len(line) > 0:
-                arg = line.split()[-1]
-            else:
-                arg = ""
-            iter = iter_munged_completions(iter, arg, text)
-        except Exception, e:
-            print e
-        return list(iter)
-
-    def completedefault(self, text, line, begidx, endidx):
-        """Perform completion for native commands.
-        
-        :param text: The text to complete
-        :type text: str
-        :param line: The entire line to complete
-        :type line: str
-        :param begidx: The start of the text in the line
-        :type begidx: int
-        :param endidx: The end of the text in the line
-        :type endidx: int
-        """
-        try:
-            (cmd, args, foo) = self.parseline(line)
-            command_obj=find_command(cmd)
-            if command_obj is not None:
-                return command_obj.complete(args.split(), text)
-            elif not self.fake_aba.is_command(cmd) and \
-                cmdutil.is_tla_command(cmd):
-                iter = cmdutil.iter_supported_switches(cmd)
-                if len(args) > 0:
-                    arg = args.split()[-1]
-                else:
-                    arg = ""
-                if arg.startswith("-"):
-                    return list(iter_munged_completions(iter, arg, text))
-                else:
-                    return list(iter_munged_completions(
-                        iter_file_completions(arg), arg, text))
-
-
-            elif cmd == "cd":
-                if len(args) > 0:
-                    arg = args.split()[-1]
-                else:
-                    arg = ""
-                iter = iter_dir_completions(arg)
-                iter = iter_munged_completions(iter, arg, text)
-                return list(iter)
-            elif len(args)>0:
-                arg = args.split()[-1]
-                return list(iter_munged_completions(iter_file_completions(arg),
-                                                    arg, text))
-            else:
-                return self.completenames(text, line, begidx, endidx)
-        except Exception, e:
-            print e
-
-
-def iter_command_names(fake_aba):
-    for entry in cmdutil.iter_combine([commands.iterkeys(), 
-                                     fake_aba.get_commands(), 
-                                     cmdutil.iter_tla_commands(False)]):
-        if not suggestions.has_key(str(entry)):
-            yield entry
-
-
-def iter_file_completions(arg, only_dirs = False):
-    """Generate an iterator that iterates through filename completions.
-
-    :param arg: The filename fragment to match
-    :type arg: str
-    :param only_dirs: If true, match only directories
-    :type only_dirs: bool
-    """
-    cwd = os.getcwd()
-    if cwd != "/":
-        extras = [".", ".."]
-    else:
-        extras = []
-    (dir, file) = os.path.split(arg)
-    if dir != "":
-        listingdir = os.path.expanduser(dir)
-    else:
-        listingdir = cwd
-    for file in cmdutil.iter_combine([os.listdir(listingdir), extras]):
-        if dir != "":
-            userfile = dir+'/'+file
-        else:
-            userfile = file
-        if userfile.startswith(arg):
-            if os.path.isdir(listingdir+'/'+file):
-                userfile+='/'
-                yield userfile
-            elif not only_dirs:
-                yield userfile
-
-def iter_munged_completions(iter, arg, text):
-    for completion in iter:
-        completion = str(completion)
-        if completion.startswith(arg):
-            yield completion[len(arg)-len(text):]
-
-def iter_source_file_completions(tree, arg):
-    treepath = cmdutil.tree_cwd(tree)
-    if len(treepath) > 0:
-        dirs = [treepath]
-    else:
-        dirs = None
-    for file in tree.iter_inventory(dirs, source=True, both=True):
-        file = file_completion_match(file, treepath, arg)
-        if file is not None:
-            yield file
-
-
-def iter_untagged(tree, dirs):
-    for file in arch_core.iter_inventory_filter(tree, dirs, tagged=False, 
-                                                categories=arch_core.non_root,
-                                                control_files=True):
-        yield file.name 
-
-
-def iter_untagged_completions(tree, arg):
-    """Generate an iterator for all visible untagged files that match arg.
-
-    :param tree: The tree to look for untagged files in
-    :type tree: `arch.WorkingTree`
-    :param arg: The argument to match
-    :type arg: str
-    :return: An iterator of all matching untagged files
-    :rtype: iterator of str
-    """
-    treepath = cmdutil.tree_cwd(tree)
-    if len(treepath) > 0:
-        dirs = [treepath]
-    else:
-        dirs = None
-
-    for file in iter_untagged(tree, dirs):
-        file = file_completion_match(file, treepath, arg)
-        if file is not None:
-            yield file
-
-
-def file_completion_match(file, treepath, arg):
-    """Determines whether a file within an arch tree matches the argument.
-
-    :param file: The rooted filename
-    :type file: str
-    :param treepath: The path to the cwd within the tree
-    :type treepath: str
-    :param arg: The prefix to match
-    :return: The completion name, or None if not a match
-    :rtype: str
-    """
-    if not file.startswith(treepath):
-        return None
-    if treepath != "":
-        file = file[len(treepath)+1:]
-
-    if not file.startswith(arg):
-        return None 
-    if os.path.isdir(file):
-        file += '/'
-    return file
-
-def iter_modified_file_completions(tree, arg):
-    """Returns a list of modified files that match the specified prefix.
-
-    :param tree: The current tree
-    :type tree: `arch.WorkingTree`
-    :param arg: The prefix to match
-    :type arg: str
-    """
-    treepath = cmdutil.tree_cwd(tree)
-    tmpdir = cmdutil.tmpdir()
-    changeset = tmpdir+"/changeset"
-    completions = []
-    revision = cmdutil.determine_revision_tree(tree)
-    for line in arch.iter_delta(revision, tree, changeset):
-        if isinstance(line, arch.FileModification):
-            file = file_completion_match(line.name[1:], treepath, arg)
-            if file is not None:
-                completions.append(file)
-    shutil.rmtree(tmpdir)
-    return completions
-
-def iter_dir_completions(arg):
-    """Generate an iterator that iterates through directory name completions.
-
-    :param arg: The directory name fragment to match
-    :type arg: str
-    """
-    return iter_file_completions(arg, True)
-
-class Shell(BaseCommand):
-    def __init__(self):
-        self.description = "Runs Fai as a shell"
-
-    def do_command(self, cmdargs):
-        if len(cmdargs)!=0:
-            raise cmdutil.GetHelp
-        prompt = PromptCmd()
-        try:
-            prompt.cmdloop()
-        finally:
-            prompt.write_history()
-
-class AddID(BaseCommand):
-    """
-    Adds an inventory id for the given file
-    """
-    def __init__(self):
-        self.description="Add an inventory id for a given file"
-
-    def get_completer(self, arg, index):
-        tree = arch.tree_root()
-        return iter_untagged_completions(tree, arg)
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-
-        tree = arch.tree_root()
-
-        if (len(args) == 0) == (options.untagged == False):
-            raise cmdutil.GetHelp
-
-       #if options.id and len(args) != 1:
-       #    print "If --id is specified, only one file can be named."
-       #    return
-        
-        method = tree.tagging_method
-        
-        if options.id_type == "tagline":
-            if method != "tagline":
-                if not cmdutil.prompt("Tagline in other tree"):
-                    if method == "explicit":
-                        options.id_type == explicit
-                    else:
-                        print "add-id not supported for \"%s\" tagging method"\
-                            % method 
-                        return
-        
-        elif options.id_type == "explicit":
-            if method != "tagline" and method != explicit:
-                if not prompt("Explicit in other tree"):
-                    print "add-id not supported for \"%s\" tagging method" % \
-                        method
-                    return
-        
-        if options.id_type == "auto":
-            if method != "tagline" and method != "explicit":
-                print "add-id not supported for \"%s\" tagging method" % method
-                return
-            else:
-                options.id_type = method
-        if options.untagged:
-            args = None
-        self.add_ids(tree, options.id_type, args)
-
-    def add_ids(self, tree, id_type, files=()):
-        """Add inventory ids to files.
-        
-        :param tree: the tree the files are in
-        :type tree: `arch.WorkingTree`
-        :param id_type: the type of id to add: "explicit" or "tagline"
-        :type id_type: str
-        :param files: The list of files to add.  If None do all untagged.
-        :type files: tuple of str
-        """
-
-        untagged = (files is None)
-        if untagged:
-            files = list(iter_untagged(tree, None))
-        previous_files = []
-        while len(files) > 0:
-            previous_files.extend(files)
-            if id_type == "explicit":
-                cmdutil.add_id(files)
-            elif id_type == "tagline":
-                for file in files:
-                    try:
-                        cmdutil.add_tagline_or_explicit_id(file)
-                    except cmdutil.AlreadyTagged:
-                        print "\"%s\" already has a tagline." % file
-                    except cmdutil.NoCommentSyntax:
-                        pass
-            #do inventory after tagging until no untagged files are encountered
-            if untagged:
-                files = []
-                for file in iter_untagged(tree, None):
-                    if not file in previous_files:
-                        files.append(file)
-
-            else:
-                break
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai add-id file1 [file2] [file3]...")
-# ddaa suggests removing this to promote GUIDs.  Let's see who squalks.
-#        parser.add_option("-i", "--id", dest="id", 
-#                         help="Specify id for a single file", default=None)
-        parser.add_option("--tltl", action="store_true", 
-                         dest="lord_style",  help="Use Tom Lord's style of id.")
-        parser.add_option("--explicit", action="store_const", 
-                         const="explicit", dest="id_type", 
-                         help="Use an explicit id", default="auto")
-        parser.add_option("--tagline", action="store_const", 
-                         const="tagline", dest="id_type", 
-                         help="Use a tagline id")
-        parser.add_option("--untagged", action="store_true", 
-                         dest="untagged", default=False, 
-                         help="tag all untagged files")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Adds an inventory to the specified file(s) and directories.  If --untagged is
-specified, adds inventory to all untagged files and directories.
-        """
-        return
-
-
-class Merge(BaseCommand):
-    """
-    Merges changes from other versions into the current tree
-    """
-    def __init__(self):
-        self.description="Merges changes from other versions"
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-
-    def get_completer(self, arg, index):
-        if self.tree is None:
-            raise arch.errors.TreeRootError
-        completions = list(ancillary.iter_partners(self.tree, 
-                                                   self.tree.tree_version))
-        if len(completions) == 0:
-            completions = list(self.tree.iter_log_versions())
-
-        aliases = []
-        try:
-            for completion in completions:
-                alias = ancillary.compact_alias(str(completion), self.tree)
-                if alias:
-                    aliases.extend(alias)
-
-            for completion in completions:
-                if completion.archive == self.tree.tree_version.archive:
-                    aliases.append(completion.nonarch)
-
-        except Exception, e:
-            print e
-            
-        completions.extend(aliases)
-        return completions
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "merge" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        if options.diff3:
-            action="star-merge"
-        else:
-            action = options.action
-        
-        if self.tree is None:
-            raise arch.errors.TreeRootError(os.getcwd())
-        if cmdutil.has_changed(self.tree.tree_version):
-            raise UncommittedChanges(self.tree)
-
-        if len(args) > 0:
-            revisions = []
-            for arg in args:
-                revisions.append(cmdutil.determine_revision_arch(self.tree, 
-                                                                 arg))
-            source = "from commandline"
-        else:
-            revisions = ancillary.iter_partner_revisions(self.tree, 
-                                                         self.tree.tree_version)
-            source = "from partner version"
-        revisions = misc.rewind_iterator(revisions)
-        try:
-            revisions.next()
-            revisions.rewind()
-        except StopIteration, e:
-            revision = cmdutil.tag_cur(self.tree)
-            if revision is None:
-                raise CantDetermineRevision("", "No version specified, no "
-                                            "partner-versions, and no tag"
-                                            " source")
-            revisions = [revision]
-            source = "from tag source"
-        for revision in revisions:
-            cmdutil.ensure_archive_registered(revision.archive)
-            cmdutil.colorize(arch.Chatter("* Merging %s [%s]" % 
-                             (revision, source)))
-            if action=="native-merge" or action=="update":
-                if self.native_merge(revision, action) == 0:
-                    continue
-            elif action=="star-merge":
-                try: 
-                    self.star_merge(revision, options.diff3)
-                except errors.MergeProblem, e:
-                    break
-            if cmdutil.has_changed(self.tree.tree_version):
-                break
-
-    def star_merge(self, revision, diff3):
-        """Perform a star-merge on the current tree.
-        
-        :param revision: The revision to use for the merge
-        :type revision: `arch.Revision`
-        :param diff3: If true, do a diff3 merge
-        :type diff3: bool
-        """
-        try:
-            for line in self.tree.iter_star_merge(revision, diff3=diff3):
-                cmdutil.colorize(line)
-        except arch.util.ExecProblem, e:
-            if e.proc.status is not None and e.proc.status == 1:
-                if e.proc.error:
-                    print e.proc.error
-                raise MergeProblem
-            else:
-                raise
-
-    def native_merge(self, other_revision, action):
-        """Perform a native-merge on the current tree.
-        
-        :param other_revision: The revision to use for the merge
-        :type other_revision: `arch.Revision`
-        :return: 0 if the merge was skipped, 1 if it was applied
-        """
-        other_tree = cmdutil.find_or_make_local_revision(other_revision)
-        try:
-            if action == "native-merge":
-                ancestor = cmdutil.merge_ancestor2(self.tree, other_tree, 
-                                                   other_revision)
-            elif action == "update":
-                ancestor = cmdutil.tree_latest(self.tree, 
-                                               other_revision.version)
-        except CantDetermineRevision, e:
-            raise CommandFailedWrapper(e)
-        cmdutil.colorize(arch.Chatter("* Found common ancestor %s" % ancestor))
-        if (ancestor == other_revision):
-            cmdutil.colorize(arch.Chatter("* Skipping redundant merge" 
-                                          % ancestor))
-            return 0
-        delta = cmdutil.apply_delta(ancestor, other_tree, self.tree)    
-        for line in cmdutil.iter_apply_delta_filter(delta):
-            cmdutil.colorize(line)
-        return 1
-
-
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "merge" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai merge [VERSION]")
-        parser.add_option("-s", "--star-merge", action="store_const",
-                          dest="action", help="Use star-merge",
-                          const="star-merge", default="native-merge")
-        parser.add_option("--update", action="store_const",
-                          dest="action", help="Use update picker",
-                          const="update")
-        parser.add_option("--diff3", action="store_true", 
-                         dest="diff3",  
-                         help="Use diff3 for merge (implies star-merge)")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Performs a merge operation using the specified version.
-        """
-        return
-
-class ELog(BaseCommand):
-    """
-    Produces a raw patchlog and invokes the user's editor
-    """
-    def __init__(self):
-        self.description="Edit a patchlog to commit"
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "elog" command.
-        """
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        if self.tree is None:
-            raise arch.errors.TreeRootError
-
-        edit_log(self.tree)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "merge" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai elog")
-        return parser 
-
-
-    def help(self, parser=None):
-        """
-        Invokes $EDITOR to produce a log for committing.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Invokes $EDITOR to produce a log for committing.
-        """
-        return
-
-def edit_log(tree):
-    """Makes and edits the log for a tree.  Does all kinds of fancy things
-    like log templates and merge summaries and log-for-merge
-    
-    :param tree: The tree to edit the log for
-    :type tree: `arch.WorkingTree`
-    """
-    #ensure we have an editor before preparing the log
-    cmdutil.find_editor()
-    log = tree.log_message(create=False)
-    log_is_new = False
-    if log is None or cmdutil.prompt("Overwrite log"):
-        if log is not None:
-           os.remove(log.name)
-        log = tree.log_message(create=True)
-        log_is_new = True
-        tmplog = log.name
-        template = tree+"/{arch}/=log-template"
-        if not os.path.exists(template):
-            template = os.path.expanduser("~/.arch-params/=log-template")
-            if not os.path.exists(template):
-                template = None
-        if template:
-            shutil.copyfile(template, tmplog)
-        
-        new_merges = list(cmdutil.iter_new_merges(tree, 
-                                                  tree.tree_version))
-        log["Summary"] = merge_summary(new_merges, tree.tree_version)
-        if len(new_merges) > 0:   
-            if cmdutil.prompt("Log for merge"):
-                mergestuff = cmdutil.log_for_merge(tree)
-                log.description += mergestuff
-        log.save()
-    try:
-        cmdutil.invoke_editor(log.name)
-    except:
-        if log_is_new:
-            os.remove(log.name)
-        raise
-
-def merge_summary(new_merges, tree_version):
-    if len(new_merges) == 0:
-        return ""
-    if len(new_merges) == 1:
-        summary = new_merges[0].summary
-    else:
-        summary = "Merge"
-
-    credits = []
-    for merge in new_merges:
-        if arch.my_id() != merge.creator:
-            name = re.sub("<.*>", "", merge.creator).rstrip(" ");
-            if not name in credits:
-                credits.append(name)
-        else:
-            version = merge.revision.version
-            if version.archive == tree_version.archive:
-                if not version.nonarch in credits:
-                    credits.append(version.nonarch)
-            elif not str(version) in credits:
-                credits.append(str(version))
-
-    return ("%s (%s)") % (summary, ", ".join(credits))
-
-class MirrorArchive(BaseCommand):
-    """
-    Updates a mirror from an archive
-    """
-    def __init__(self):
-        self.description="Update a mirror from an archive"
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        if len(args) > 1:
-            raise GetHelp
-        try:
-            tree = arch.tree_root()
-        except:
-            tree = None
-
-        if len(args) == 0:
-            if tree is not None:
-                name = tree.tree_version()
-        else:
-            name = cmdutil.expand_alias(args[0], tree)
-            name = arch.NameParser(name)
-
-        to_arch = name.get_archive()
-        from_arch = cmdutil.get_mirror_source(arch.Archive(to_arch))
-        limit = name.get_nonarch()
-
-        iter = arch_core.mirror_archive(from_arch,to_arch, limit)
-        for line in arch.chatter_classifier(iter):
-            cmdutil.colorize(line)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai mirror-archive ARCHIVE")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Updates a mirror from an archive.  If a branch, package, or version is
-supplied, only changes under it are mirrored.
-        """
-        return
-
-def help_tree_spec():
-    print """Specifying revisions (default: tree)
-Revisions may be specified by alias, revision, version or patchlevel.
-Revisions or versions may be fully qualified.  Unqualified revisions, versions, 
-or patchlevels use the archive of the current project tree.  Versions will
-use the latest patchlevel in the tree.  Patchlevels will use the current tree-
-version.
-
-Use "alias" to list available (user and automatic) aliases."""
-
-def help_aliases(tree):
-    print """Auto-generated aliases
- acur : The latest revision in the archive of the tree-version.  You can specfy
-        a different version like so: acur:foo--bar--0 (aliases can be used)
- tcur : (tree current) The latest revision in the tree of the tree-version.
-        You can specify a different version like so: tcur:foo--bar--0 (aliases
-        can be used).
-tprev : (tree previous) The previous revision in the tree of the tree-version.
-        To specify an older revision, use a number, e.g. "tprev:4"
- tanc : (tree ancestor) The ancestor revision of the tree
-        To specify an older revision, use a number, e.g. "tanc:4"
-tdate : (tree date) The latest revision from a given date (e.g. "tdate:July 6")
- tmod : (tree modified) The latest revision to modify a given file 
-        (e.g. "tmod:engine.cpp" or "tmod:engine.cpp:16")
- ttag : (tree tag) The revision that was tagged into the current tree revision,
-        according to the tree.
-tagcur: (tag current) The latest revision of the version that the current tree
-        was tagged from.
-mergeanc : The common ancestor of the current tree and the specified revision.
-        Defaults to the first partner-version's latest revision or to tagcur.
-   """
-    print "User aliases"
-    for parts in ancillary.iter_all_alias(tree):
-        print parts[0].rjust(10)+" : "+parts[1]
-
-
-class Inventory(BaseCommand):
-    """List the status of files in the tree"""
-    def __init__(self):
-        self.description=self.__doc__
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        tree = arch.tree_root()
-        categories = []
-
-        if (options.source):
-            categories.append(arch_core.SourceFile)
-        if (options.precious):
-            categories.append(arch_core.PreciousFile)
-        if (options.backup):
-            categories.append(arch_core.BackupFile)
-        if (options.junk):
-            categories.append(arch_core.JunkFile)
-
-        if len(categories) == 1:
-            show_leading = False
-        else:
-            show_leading = True
-
-        if len(categories) == 0:
-            categories = None
-
-        if options.untagged:
-            categories = arch_core.non_root
-            show_leading = False
-            tagged = False
-        else:
-            tagged = None
-        
-        for file in arch_core.iter_inventory_filter(tree, None, 
-            control_files=options.control_files, 
-            categories = categories, tagged=tagged):
-            print arch_core.file_line(file, 
-                                      category = show_leading, 
-                                      untagged = show_leading,
-                                      id = options.ids)
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "revision" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai inventory [options]")
-        parser.add_option("--ids", action="store_true", dest="ids", 
-                          help="Show file ids")
-        parser.add_option("--control", action="store_true", 
-                          dest="control_files", help="include control files")
-        parser.add_option("--source", action="store_true", dest="source",
-                          help="List source files")
-        parser.add_option("--backup", action="store_true", dest="backup",
-                          help="List backup files")
-        parser.add_option("--precious", action="store_true", dest="precious",
-                          help="List precious files")
-        parser.add_option("--junk", action="store_true", dest="junk",
-                          help="List junk files")
-        parser.add_option("--unrecognized", action="store_true", 
-                          dest="unrecognized", help="List unrecognized files")
-        parser.add_option("--untagged", action="store_true", 
-                          dest="untagged", help="List only untagged files")
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Lists the status of files in the archive:
-S source
-P precious
-B backup
-J junk
-U unrecognized
-T tree root
-? untagged-source
-Leading letter are not displayed if only one kind of file is shown
-        """
-        return
-
-
-class Alias(BaseCommand):
-    """List or adjust aliases"""
-    def __init__(self):
-        self.description=self.__doc__
-
-    def get_completer(self, arg, index):
-        if index > 2:
-            return ()
-        try:
-            self.tree = arch.tree_root()
-        except:
-            self.tree = None
-
-        if index == 0:
-            return [part[0]+" " for part in ancillary.iter_all_alias(self.tree)]
-        elif index == 1:
-            return cmdutil.iter_revision_completions(arg, self.tree)
-
-
-    def do_command(self, cmdargs):
-        """
-        Master function that perfoms the "revision" command.
-        """
-
-        parser=self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            self.tree =  arch.tree_root()
-        except:
-            self.tree = None
-
-
-        try:
-            options.action(args, options)
-        except cmdutil.ForbiddenAliasSyntax, e:
-            raise CommandFailedWrapper(e)
-
-    def arg_dispatch(self, args, options):
-        """Add, modify, or list aliases, depending on number of arguments
-
-        :param args: The list of commandline arguments
-        :type args: list of str
-        :param options: The commandline options
-        """
-        if len(args) == 0:
-            help_aliases(self.tree)
-            return
-        elif len(args) == 1:
-            self.print_alias(args[0])
-        elif (len(args)) == 2:
-            self.add(args[0], args[1], options)
-        else:
-            raise cmdutil.GetHelp
-
-    def print_alias(self, alias):
-        answer = None
-        for pair in ancillary.iter_all_alias(self.tree):
-            if pair[0] == alias:
-                answer = pair[1]
-        if answer is not None:
-            print answer
-        else:
-            print "The alias %s is not assigned." % alias
-
-    def add(self, alias, expansion, options):
-        """Add or modify aliases
-
-        :param alias: The alias name to create/modify
-        :type alias: str
-        :param expansion: The expansion to assign to the alias name
-        :type expansion: str
-        :param options: The commandline options
-        """
-        newlist = ""
-        written = False
-        new_line = "%s=%s\n" % (alias, cmdutil.expand_alias(expansion, 
-            self.tree))
-        ancillary.check_alias(new_line.rstrip("\n"), [alias, expansion])
-
-        for pair in self.get_iterator(options):
-            if pair[0] != alias:
-                newlist+="%s=%s\n" % (pair[0], pair[1])
-            elif not written:
-                newlist+=new_line
-                written = True
-        if not written:
-            newlist+=new_line
-        self.write_aliases(newlist, options)
-            
-    def delete(self, args, options):
-        """Delete the specified alias
-
-        :param args: The list of arguments
-        :type args: list of str
-        :param options: The commandline options
-        """
-        deleted = False
-        if len(args) != 1:
-            raise cmdutil.GetHelp
-        newlist = ""
-        for pair in self.get_iterator(options):
-            if pair[0] != args[0]:
-                newlist+="%s=%s\n" % (pair[0], pair[1])
-            else:
-                deleted = True
-        if not deleted:
-            raise errors.NoSuchAlias(args[0])
-        self.write_aliases(newlist, options)
-
-    def get_alias_file(self, options):
-        """Return the name of the alias file to use
-
-        :param options: The commandline options
-        """
-        if options.tree:
-            if self.tree is None:
-                self.tree == arch.tree_root()
-            return str(self.tree)+"/{arch}/+aliases"
-        else:
-            return "~/.aba/aliases"
-
-    def get_iterator(self, options):
-        """Return the alias iterator to use
-
-        :param options: The commandline options
-        """
-        return ancillary.iter_alias(self.get_alias_file(options))
-
-    def write_aliases(self, newlist, options):
-        """Safely rewrite the alias file
-        :param newlist: The new list of aliases
-        :type newlist: str
-        :param options: The commandline options
-        """
-        filename = os.path.expanduser(self.get_alias_file(options))
-        file = cmdutil.NewFileVersion(filename)
-        file.write(newlist)
-        file.commit()
-
-
-    def get_parser(self):
-        """
-        Returns the options parser to use for the "alias" command.
-
-        :rtype: cmdutil.CmdOptionParser
-        """
-        parser=cmdutil.CmdOptionParser("fai alias [ALIAS] [NAME]")
-        parser.add_option("-d", "--delete", action="store_const", dest="action",
-                          const=self.delete, default=self.arg_dispatch, 
-                          help="Delete an alias")
-        parser.add_option("--tree", action="store_true", dest="tree", 
-                          help="Create a per-tree alias", default=False)
-        return parser 
-
-    def help(self, parser=None):
-        """
-        Prints a help message.
-
-        :param parser: If supplied, the parser to use for generating help.  If \
-        not supplied, it is retrieved.
-        :type parser: cmdutil.CmdOptionParser
-        """
-        if parser==None:
-            parser=self.get_parser()
-        parser.print_help()
-        print """
-Lists current aliases or modifies the list of aliases.
-
-If no arguments are supplied, aliases will be listed.  If two arguments are
-supplied, the specified alias will be created or modified.  If -d or --delete
-is supplied, the specified alias will be deleted.
-
-You can create aliases that refer to any fully-qualified part of the
-Arch namespace, e.g. 
-archive, 
-archive/category, 
-archive/category--branch, 
-archive/category--branch--version (my favourite)
-archive/category--branch--version--patchlevel
-
-Aliases can be used automatically by native commands.  To use them
-with external or tla commands, prefix them with ^ (you can do this
-with native commands, too).
-"""
-
-
-class RequestMerge(BaseCommand):
-    """Submit a merge request to Bug Goo"""
-    def __init__(self):
-        self.description=self.__doc__
-
-    def do_command(self, cmdargs):
-        """Submit a merge request
-
-        :param cmdargs: The commandline arguments
-        :type cmdargs: list of str
-        """
-        cmdutil.find_editor()
-        parser = self.get_parser()
-        (options, args) = parser.parse_args(cmdargs)
-        try:
-            self.tree=arch.tree_root()
-        except:
-            self.tree=None
-        base, revisions = self.revision_specs(args)
-        message = self.make_headers(base, revisions)
-        message += self.make_summary(revisions)
-        path = self.edit_message(message)
-        message = self.tidy_message(path)
-        if cmdutil.prompt("Send merge"):
-            self.send_message(message)
-            print "Merge request sent"
-
-    def make_headers(self, base, revisions):
-        """Produce email and Bug Goo header strings
-
-        :param base: The base revision to apply merges to
-        :type base: `arch.Revision`
-        :param revisions: The revisions to replay into the base
-        :type revisions: list of `arch.Patchlog`
-        :return: The headers
-        :rtype: str
-        """
-        headers = "To: gnu-arch-users at gnu.org\n"
-        headers += "From: %s\n" % options.fromaddr
-        if len(revisions) == 1:
-            headers += "Subject: [MERGE REQUEST] %s\n" % revisions[0].summary
-        else:
-            headers += "Subject: [MERGE REQUEST]\n"
-        headers += "\n"
-        headers += "Base-Revision: %s\n" % base
-        for revision in revisions:
-            headers += "Revision: %s\n" % revision.revision
-        headers += "Bug: \n\n"
-        return headers
-
-    def make_summary(self, logs):
-        """Generate a summary of merges
-
-        :param logs: the patchlogs that were directly added by the merges
-        :type logs: list of `arch.Patchlog`
-        :return: the summary
-        :rtype: str
-        """ 
-        summary = ""
-        for log in logs:
-            summary+=str(log.revision)+"\n"
-            summary+=log.summary+"\n"
-            if log.description.strip():
-                summary+=log.description.strip('\n')+"\n\n"
-        return summary
-
-    def revision_specs(self, args):
-        """Determine the base and merge revisions from tree and arguments.
-
-        :param args: The parsed arguments
-        :type args: list of str
-        :return: The base revision and merge revisions 
-        :rtype: `arch.Revision`, list of `arch.Patchlog`
-        """
-        if len(args) > 0:
-            target_revision = cmdutil.determine_revision_arch(self.tree, 
-                                                              args[0])
-        else:
-            target_revision = cmdutil.tree_latest(self.tree)
-        if len(args) > 1:
-            merges = [ arch.Patchlog(cmdutil.determine_revision_arch(
-                       self.tree, f)) for f in args[1:] ]
-        else:
-            if self.tree is None:
-                raise CantDetermineRevision("", "Not in a project tree")
-            merge_iter = cmdutil.iter_new_merges(self.tree, 
-                                                 target_revision.version, 
-                                                 False)
-            merges = [f for f in cmdutil.direct_merges(merge_iter)]
-        return (target_revision, merges)
-
-    def edit_message(self, message):
-        """Edit an email message in the user's standard editor
-
-        :param message: The message to edit
-        :type message: str
-        :return: the path of the edited message
-        :rtype: str
-        """
-        if self.tree is None:
-            path = os.get_cwd()
-        else:
-            path = self.tree
-        path += "/,merge-request"
-        file = open(path, 'w')
-        file.write(message)
-        file.flush()
-        cmdutil.invoke_editor(path)
-        return path
-
-    def tidy_message(self, path):
-        """Validate and clean up message.
-
-        :param path: The path to the message to clean up
-        :type path: str
-        :return: The parsed message
-        :rtype: `email.Message`
-        """
-        mail = email.message_from_file(open(path))
-        if mail["Subject"].strip() == "[MERGE REQUEST]":
-            raise BlandSubject
-        
-        request = email.message_from_string(mail.get_payload())
-        if request.has_key("Bug"):
-            if request["Bug"].strip()=="":
-                del request["Bug"]
-        mail.set_payload(request.as_string())
-        return mail
-
-    def send_message(self, message):
-        """Send a message, using its headers to address it.
-
-        :param message: The message to send
-        :type message: `email.Message`"""
-        server = smtplib.SMTP()
-        server.sendmail(message['From'], message['To'], message.as_string())
-        server.quit()
-
-    def help(self, parser=None):
-        """Print a usage message
-
-        :param parser: The options parser to use
-        :type parser: `cmdutil.CmdOptionParser`
-        """
-        if parser is None:
-            parser = self.get_parser()
-        parser.print_help()
-        print """
-Sends a merge request formatted for Bug Goo.  Intended use: get the tree
-you'd like to merge into.  Apply the merges you want.  Invoke request-merge.
-The merge request will open in your $EDITOR.
-
-When no TARGET is specified, it uses the current tree revision.  When
-no MERGE is specified, it uses the direct merges (as in "revisions
---direct-merges").  But you can specify just the TARGET, or all the MERGE
-revisions.
-"""
-
-    def get_parser(self):
-        """Produce a commandline parser for this command.
-
-        :rtype: `cmdutil.CmdOptionParser`
-        """
-        parser=cmdutil.CmdOptionParser("request-merge [TARGET] [MERGE1...]")
-        return parser
-
-commands = { 
-'changes' : Changes,
-'help' : Help,
-'update': Update,
-'apply-changes':ApplyChanges,
-'cat-log': CatLog,
-'commit': Commit,
-'revision': Revision,
-'revisions': Revisions,
-'get': Get,
-'revert': Revert,
-'shell': Shell,
-'add-id': AddID,
-'merge': Merge,
-'elog': ELog,
-'mirror-archive': MirrorArchive,
-'ninventory': Inventory,
-'alias' : Alias,
-'request-merge': RequestMerge,
-}
-suggestions = {
-'apply-delta' : "Try \"apply-changes\".",
-'delta' : "To compare two revisions, use \"changes\".",
-'diff-rev' : "To compare two revisions, use \"changes\".",
-'undo' : "To undo local changes, use \"revert\".",
-'undelete' : "To undo only deletions, use \"revert --deletions\"",
-'missing-from' : "Try \"revisions --missing-from\".",
-'missing' : "Try \"revisions --missing\".",
-'missing-merge' : "Try \"revisions --partner-missing\".",
-'new-merges' : "Try \"revisions --new-merges\".",
-'cachedrevs' : "Try \"revisions --cacherevs\". (no 'd')",
-'logs' : "Try \"revisions --logs\"",
-'tree-source' : "Use the \"^ttag\" alias (\"revision ^ttag\")",
-'latest-revision' : "Use the \"^acur\" alias (\"revision ^acur\")",
-'change-version' : "Try \"update REVISION\"",
-'tree-revision' : "Use the \"^tcur\" alias (\"revision ^tcur\")",
-'rev-depends' : "Use revisions --dependencies",
-'auto-get' : "Plain get will do archive lookups",
-'tagline' : "Use add-id.  It uses taglines in tagline trees",
-'emlog' : "Use elog.  It automatically adds log-for-merge text, if any",
-'library-revisions' : "Use revisions --library",
-'file-revert' : "Use revert FILE"
-}
-# arch-tag: 19d5739d-3708-486c-93ba-deecc3027fc7
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/blackbox.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/blackbox.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/blackbox.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/blackbox.py	2006-08-11 15:10:54.000000000 +0200
@@ -8,6 +8,8 @@
 
     def test_clean_tree(self):
         self.runbzr('init')
+        self.runbzr('ignore *~')
+        self.runbzr('ignore *.pyc')
         self.touch('name')
         self.touch('name~')
         assert os.path.lexists('name~')
@@ -15,10 +17,22 @@
         self.runbzr('clean-tree')
         assert os.path.lexists('name~')
         assert not os.path.lexists('name')
+        self.touch('name')
         self.runbzr('clean-tree --detritus')
+        assert os.path.lexists('name')
         assert not os.path.lexists('name~')
         assert os.path.lexists('name.pyc')
         self.runbzr('clean-tree --ignored')
+        assert os.path.lexists('name')
+        assert not os.path.lexists('name.pyc')
+        self.runbzr('clean-tree --unknown')
+        assert not os.path.lexists('name')
+        self.touch('name')
+        self.touch('name~')
+        self.touch('name.pyc')
+        self.runbzr('clean-tree --unknown --ignored')
+        assert not os.path.lexists('name')
+        assert not os.path.lexists('name~')
         assert not os.path.lexists('name.pyc')
 
     def test_shelve(self):
@@ -80,9 +94,14 @@
     def test_zap_branch(self):
         self.runbzr('init source')
         self.runbzr('checkout --lightweight source checkout')
-        self.runbzr('zap --branch checkout')
-        self.assertIs(False, os.path.exists('checkout'))
-        self.assertIs(False, os.path.exists('source'))
+        self.runbzr('zap --branch checkout', retcode=3)
+        self.assertIs(True, os.path.exists('checkout'))
+        self.assertIs(True, os.path.exists('source'))
+        self.runbzr('branch source source2')
+        self.runbzr('checkout --lightweight source2 checkout2')
+        self.runbzr('zap --branch checkout2')
+        self.assertIs(False, os.path.exists('checkout2'))
+        self.assertIs(False, os.path.exists('source2'))
 
     def test_branches(self):
         self.runbzr('init source')
@@ -98,5 +117,56 @@
         self.assertIs(True, 'checkout' not in lines)
         self.assertIs(True, 'checkout/.bzr/subcheckout' not in lines)
 
+    def test_import_upstream(self):
+        self.runbzr('init source')
+        os.mkdir('source/src')
+        f = file('source/src/myfile', 'wb')
+        f.write('hello?')
+        f.close()
+        os.chdir('source')
+        self.runbzr('add')
+        self.runbzr('commit -m hello')
+        self.runbzr('export ../source-0.1.tar.gz')
+        self.runbzr('export ../source-0.1.tar.bz2')
+        self.runbzr('init ../import')
+        os.chdir('../import')
+        self.runbzr('import ../source-0.1.tar.gz')
+        self.failUnlessExists('src/myfile')
+        result = self.runbzr('import ../source-0.1.tar.gz', retcode=3)[1]
+        self.assertContainsRe(result, 'Working tree has uncommitted changes')
+        self.runbzr('commit -m commit')
+        self.runbzr('import ../source-0.1.tar.gz')
+        os.chdir('..')
+        self.runbzr('init import2')
+        self.runbzr('import source-0.1.tar.gz import2')
+        self.failUnlessExists('import2/src/myfile')
+        self.runbzr('import source-0.1.tar.gz import3')
+        self.failUnlessExists('import3/src/myfile')
+        self.runbzr('import source-0.1.tar.bz2 import4')
+        self.failUnlessExists('import4/src/myfile')
+
+    def test_shove(self):
+        self.runbzr('init source')
+        f = file('source/file', 'wb')
+        f.write('hello\n')
+        f.close()
+        self.runbzr('add source/file')
+        self.runbzr('commit source -m foo')
+        self.runbzr('branch source target1')
+        self.runbzr('branch source target2')
+        f = file('source/file', 'wb')
+        f.write('goodbye\n')
+        f.close()
+        self.runbzr('shove target1 source')
+        f = file('target1/file', 'rb')
+        self.assertEqual(f.read(), 'goodbye\n')
+        f.close()
+        os.chdir('source')
+        self.runbzr('shove target2', retcode=3)
+        self.runbzr('shove ../target2')
+        f = file('../target2/file', 'rb')
+        self.assertEqual(f.read(), 'goodbye\n')
+        f.close()
+
 def test_suite():
     return makeSuite(TestBzrTools)
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/clean_tree.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/clean_tree.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/clean_tree.py	1970-01-01 01:00:00.000000000 +0100
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/clean_tree.py	2006-08-11 15:10:54.000000000 +0200
@@ -0,0 +1,74 @@
+# Copyright (C) 2005 by Aaron Bentley
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+import os
+from StringIO import StringIO
+from unittest import makeSuite
+
+from bzrlib.bzrdir import BzrDir
+try:
+    from bzrlib.plugins.bzrtools.clean_tree import clean_tree, iter_deletables
+except ImportError:
+    from bzrtools.clean_tree import clean_tree, iter_deletables 
+from bzrlib.osutils import has_symlinks
+from bzrlib.tests import TestCaseInTempDir
+
+
+class TestCleanTree(TestCaseInTempDir):
+    def test_symlinks(self):
+        if has_symlinks() is False:
+            return
+        os.mkdir('branch')
+        BzrDir.create_standalone_workingtree('branch')
+        os.symlink(os.path.realpath('no-die-please'), 'branch/die-please')
+        os.mkdir('no-die-please')
+        assert os.path.exists('branch/die-please')
+        os.mkdir('no-die-please/child')
+
+        clean_tree('branch', unknown=True)
+        assert os.path.exists('no-die-please')
+        assert os.path.exists('no-die-please/child')
+
+    def test_iter_deletable(self):
+        """Files are selected for deletion appropriately"""
+        os.mkdir('branch')
+        tree = BzrDir.create_standalone_workingtree('branch')
+        f = file('branch/.bzrignore', 'wb')
+        try:
+            f.write('*~\n*.pyc\n.bzrignore\n')
+        finally:
+            f.close()
+        file('branch/file.BASE', 'wb').write('contents')
+        self.assertEqual(len(list(iter_deletables(tree, unknown=True))), 1)
+        file('branch/file', 'wb').write('contents')
+        file('branch/file~', 'wb').write('contents')
+        file('branch/file.pyc', 'wb').write('contents')
+
+        dels = sorted([r for a,r in iter_deletables(tree, unknown=True)])
+        assert sorted(['file', 'file.BASE']) == dels
+
+        dels = [r for a,r in iter_deletables(tree, detritus=True)]
+        assert sorted(['file~', 'file.BASE']) == dels
+
+        dels = [r for a,r in iter_deletables(tree, ignored=True)]
+        assert sorted(['file~', 'file.pyc', '.bzrignore']) == dels
+
+        dels = [r for a,r in iter_deletables(tree, unknown=False)]
+        assert [] == dels
+
+def test_suite():
+    return makeSuite(TestCleanTree)
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/__init__.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/__init__.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/__init__.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/__init__.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,11 +1,19 @@
 def test_suite():
     try:
+        from bzrtools.tests.errors import NoPyBaz
         import bzrtools.tests.test_baz_import
         return bzrtools.tests.test_baz_import.test_suite()
     except ImportError:
     	try:
+            from bzrlib.plugins.bzrtools.errors import NoPyBaz
             import bzrlib.plugins.bzrtools.tests.test_baz_import
             return bzrlib.plugins.bzrtools.tests.test_baz_import.test_suite()
 	except ImportError:
             from unittest import TestSuite
             return TestSuite()
+        except NoPyBaz:
+            from unittest import TestSuite
+            return TestSuite()
+    except NoPyBaz:
+        from unittest import TestSuite
+        return TestSuite()
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/shelf_tests.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/shelf_tests.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/shelf_tests.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/shelf_tests.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,14 +1,19 @@
-#!/usr/bin/python
-
+from bzrlib.diff import _patch_header_date
 import bzrlib.tests
 import os.path
 
+from bzrlib.plugins.bzrtools.hunk_selector import (
+    ShelveHunkSelector,
+    UnshelveHunkSelector,
+    )
+from bzrlib.plugins.bzrtools.errors import NoColor
+
 class ShelfTests(bzrlib.tests.TestCaseWithTransport):
     ORIGINAL = '\n\nhello test world\n\n'
     MODIFIED = '\n\ngoodbye test world\n\n'
     DIFF_HEADER = "=== modified file '%(filename)s'\n"
-    DIFF_1 = """--- %(filename)s\t
-+++ %(filename)s\t
+    DIFF_1 = """--- %(filename)s\t%(old_date)s
++++ %(filename)s\t%(new_date)s
 @@ -1,4 +1,4 @@
  
  
@@ -16,8 +21,8 @@
 +goodbye test world
  
 """
-    DIFF_2 = """--- test_file\t
-+++ test_file\t
+    DIFF_2 = """--- test_file\t%(old_date)s
++++ test_file\t%(new_date)s
 @@ -1,4 +1,4 @@
  
  
@@ -26,14 +31,28 @@
  
 """
     def _check_diff(self, diff=DIFF_1, filename='test_file'):
-        keys = { 'filename' : filename }
+        old_tree = self.tree.basis_tree()
+        old_date = _patch_header_date(old_tree, 
+                                      old_tree.inventory.path2id(filename),
+                                      filename)
+        new_date = _patch_header_date(self.tree, 
+                                      self.tree.inventory.path2id(filename),
+                                      filename)
+        keys = { 'filename' : filename , 'old_date': old_date, 
+                 'new_date': new_date}
         hdr  = self.DIFF_HEADER % keys
         diff = diff % keys
         self.assertEqual(self.capture('diff', retcode=1), hdr + diff + '\n')
 
-    def _check_shelf(self, idx, diff=DIFF_1, filename='test_file'):
-        diff = diff % { 'filename' : filename }
-        shelf = open(os.path.join(self.tree.branch.base,
+    def _check_shelf(self, idx, diff=DIFF_1, filename='test_file',
+                     new_date=None):
+        old_tree = self.tree.basis_tree()
+        old_date = _patch_header_date(old_tree, 
+                                      old_tree.inventory.path2id(filename),
+                                      filename)
+        diff = diff % { 'filename' : filename, 'old_date': old_date,
+                        'new_date': new_date}
+        shelf = open(os.path.join(self.tree.basedir,
                 '.shelf/shelves/default/' + idx)).read()
         shelf = shelf[shelf.index('\n') + 1:] # skip the message
         self.assertEqual(shelf, diff)
@@ -52,9 +71,15 @@
             count -= 1
 
             # Modify the test file
-            file('test_file', 'w').write(self.MODIFIED)
+            # write in binary mode because on win32 line-endings should be LF
+            f = file('test_file', 'wb')
+            f.write(self.MODIFIED)
+            f.close()
 
             self._check_diff()
+            
+            new_date = _patch_header_date(self.tree, 
+                self.tree.inventory.path2id('test_file'), 'test_file')
 
             # Shelve the changes
             self.run_bzr('shelve', '--all', retcode=0)
@@ -65,7 +90,7 @@
             # Make sure the file is actually back the way it was
             self.assertEqual(file('test_file').read(), self.ORIGINAL)
 
-            self._check_shelf('00')
+            self._check_shelf('00', new_date=new_date)
 
             # Unshelve
             self.run_bzr('unshelve', '--all', retcode=0)
@@ -73,7 +98,7 @@
             self._check_diff()
 
             # Check the shelved patch was backed up
-            self._check_shelf('00~')
+            self._check_shelf('00~', new_date=new_date)
 
             # Make sure the file is back the way it should be
             self.assertEqual(file('test_file').read(), self.MODIFIED)
@@ -91,7 +116,8 @@
             self.fail("Shelf exists, but it shouldn't")
 
     def __create_and_add_test_file(self, filename='test_file'):
-        f = open(filename, 'w')
+        # write in binary mode because on win32 line-endings should be LF
+        f = open(filename, 'wb')
         f.write(self.ORIGINAL)
         f.close()
         self.tree.add(self.tree.relpath(os.path.join(os.getcwd(), filename)))
@@ -145,7 +171,7 @@
         self.assertEqual(self.capture('diff', retcode=0), '')
 
         # Check the shelf is right
-        shelf = open(os.path.join(self.tree.branch.base,
+        shelf = open(os.path.join(self.tree.basedir,
                     '.shelf/shelves/default', patch)).read()
         self.assertTrue('patch %s' % patch in shelf)
 
@@ -161,7 +187,7 @@
         self.__test_show(self.tree, '02')
 
         # Now check we can show a previously shelved patch
-        shelf = open(os.path.join(self.tree.branch.base,
+        shelf = open(os.path.join(self.tree.basedir,
                     '.shelf/shelves/default/00')).read()
         self.assertTrue('patch 00' in shelf)
 
@@ -169,6 +195,22 @@
         shown = self.capture('shelf show 00', retcode=0)
         self.assertEqual(shown, shelf)
 
+    def test_shelf_show_unspecified(self):
+        self.tree = self.make_branch_and_tree('.')
+        self.__create_and_add_test_file()
+        self.__test_show(self.tree, '00')
+        self.__test_show(self.tree, '01')
+        self.__test_show(self.tree, '02')
+
+        # Check that not specifying at patch gets us the most recent
+        shelf = open(os.path.join(self.tree.basedir,
+                    '.shelf/shelves/default/02')).read()
+        self.assertTrue('patch 02' in shelf)
+
+        # Check the shown output is right
+        shown = self.capture('shelf show', retcode=0)
+        self.assertEqual(shown, shelf)
+
     def test_shelf_show_with_no_patch(self):
         self.tree = self.make_branch_and_tree('.')
         stderr = self.run_bzr_captured(['shelf', 'show', '00'], retcode=None)[1]
@@ -186,7 +228,7 @@
         self.run_bzr('shelve', '--all', retcode=0)
 
         # Write an unapplyable patch into the shelf
-        shelf = open(os.path.join(self.tree.branch.base,
+        shelf = open(os.path.join(self.tree.basedir,
                     '.shelf/shelves/default/00'), 'w')
         shelf.write(self.DIFF_2)
         shelf.close()
@@ -195,7 +237,7 @@
         self.run_bzr('unshelve', '--all', retcode=3)
 
         # Make sure the patch is still there, eventhough it's broken
-        shelf = open(os.path.join(self.tree.branch.base,
+        shelf = open(os.path.join(self.tree.basedir,
                     '.shelf/shelves/default/00')).read()
         self.assertEqual(shelf, self.DIFF_2)
 
@@ -254,8 +296,8 @@
         self.assertTrue(os.path.exists('.shelf/shelves/default/01~'))
 
         # Check ls works
-        list = self.capture('shelf ls', retcode=0).split('\n')
-        for line in list:
+        lines = self.capture('shelf ls', retcode=0).split('\n')
+        for line in lines:
             self.assertFalse(line.startswith(' 01'))
 
         # Unshelve, if unshelve is confused by the backup it will fail
@@ -268,14 +310,21 @@
         self.__create_and_add_test_file(filename='test_file2')
 
         # Modify the test files
-        file('test_file', 'w').write(self.MODIFIED)
-        file('test_file2', 'w').write(self.MODIFIED)
+        # write in binary mode because on win32 line-endings should be LF
+        f = file('test_file', 'wb')
+        f.write(self.MODIFIED)
+        f.close()
+        f = file('test_file2', 'wb')
+        f.write(self.MODIFIED)
+        f.close()
+        new_date = _patch_header_date(self.tree, 
+            self.tree.inventory.path2id('test_file'), 'test_file')
 
         # Shelve the changes
         self.run_bzr('shelve', '--all', 'test_file', retcode=0)
         self.run_bzr('shelve', '--all', 'test_file2', retcode=0)
 
-        self._check_shelf('00')
+        self._check_shelf('00', new_date=new_date)
 
         # Delete 00
         self.run_bzr('shelf', 'delete', '00', retcode=0)
@@ -286,11 +335,11 @@
         self.assertTrue(os.path.exists('.shelf/shelves/default/01'))
 
         # Check the backup is right
-        self._check_shelf('00~')
+        self._check_shelf('00~', new_date=new_date)
 
         # Check ls works
-        list = self.capture('shelf ls', retcode=0).split('\n')
-        for line in list:
+        lines = self.capture('shelf ls', retcode=0).split('\n')
+        for line in lines:
             self.assertFalse(line.startswith(' 00'))
 
         # Unshelve should unshelve 01
@@ -363,8 +412,16 @@
         # Run a benign shelf command to setup .shelf for us
         self.run_bzr('shelf', 'ls', retcode=0)
 
+        old_tree = self.tree.basis_tree()
+        old_date = _patch_header_date(old_tree, 
+                                      old_tree.inventory.path2id('test_file'),
+                                      'test_file')
+        new_date = _patch_header_date(self.tree, 
+                                      self.tree.inventory.path2id('test_file'),
+                                      'test_file')
         # Fake a -p0 shelved patch
-        diff = self.DIFF_1 % { 'filename' : 'test_file' }
+        diff = self.DIFF_1 % { 'filename' : 'test_file', 'old_date': old_date,
+                               'new_date' : new_date}
         diff = diff.replace('--- ', '--- a/')
         diff = diff.replace('+++ ', '+++ b/')
         open('.shelf/shelves/default/00', 'w').write(diff)
@@ -385,7 +442,10 @@
         self.__create_and_add_test_file()
 
         # Modify the test file
-        file('test_file', 'w').write(self.MODIFIED)
+        # write in binary mode because on win32 line-endings should be LF
+        f = file('test_file', 'wb')
+        f.write(self.MODIFIED)
+        f.close()
 
         # Shelve the changes
         self.run_bzr('shelve', '--all', retcode=0)
@@ -405,3 +465,131 @@
     def test_shelf_shelf_bogus_subcommand(self):
         self.tree = self.make_branch_and_tree('.')
         self.run_bzr('shelf', 'foo', retcode=3) # <- retcode == 3
+
+    def test_shelf_OOO_unshelve(self):
+        self.tree = self.make_branch_and_tree('.')
+
+        for i in range(1, 5):
+            self.__create_and_add_test_file(filename='test_file%d' % i)
+
+        # Modify the test files
+        for i in range(1, 5):
+            file('test_file%d' % i, 'w').write(self.MODIFIED)
+
+        # Shelve the changes
+        for i in range(1, 5):
+            self.run_bzr('shelve', '--all', 'test_file%d' % i, retcode=0)
+
+        # Check shelving worked
+        for i in range(1, 5):
+            self.assertEqual(file('test_file%d' % i).read(), self.ORIGINAL)
+
+        # We should now have 00-03
+        for i in range(0, 4):
+            self.assertTrue(os.path.exists('.shelf/shelves/default/0%d' % i))
+
+        # Unshelve 00
+        self.run_bzr('unshelve', '--all', '00', retcode=0)
+        self.assertEqual(file('test_file1').read(), self.MODIFIED)
+
+        # Check ls works
+        lines = self.capture('shelf ls', retcode=0).split('\n')
+        for line in lines:
+            self.assertFalse(line.startswith(' 00'))
+
+        # Check we can reshelve once we've unshelved out of order, should be 04
+        self.assertFalse(os.path.exists('.shelf/shelves/default/04'))
+        self.run_bzr('shelve', '--all')
+        self.assertTrue(os.path.exists('.shelf/shelves/default/04'))
+
+        # Check ls works
+        text = self.capture('shelf ls', retcode=0)
+        for line in text.split('\n'):
+            self.assertFalse(line.startswith(' 00'))
+
+        # We now have 01,02,03,04
+        # Unshelve 02
+        self.run_bzr('unshelve', '--all', '02', retcode=0)
+        self.assertEqual(file('test_file3').read(), self.MODIFIED)
+
+        # Unshelve the default, this is the reshelved 00, hence modifies file 1
+        self.run_bzr('unshelve', '--all', retcode=0)
+        self.assertEqual(file('test_file1').read(), self.MODIFIED)
+
+    def test_shelf_switch_basic(self):
+        self.tree = self.make_branch_and_tree('.')
+        self.__create_and_add_test_file()
+
+        # This should go to "default"
+        file('test_file', 'w').write(self.MODIFIED)
+        self.run_bzr('shelve', '--all', 'test_file', retcode=0)
+
+        # Switch to "other"
+        self.run_bzr('shelf', 'switch', 'other', retcode=0)
+        file('test_file', 'w').write(self.MODIFIED)
+        self.run_bzr('shelve', '--all', 'test_file', retcode=0)
+
+        # Check it worked
+        self.assertTrue(os.path.exists('.shelf/shelves/default/00'))
+        self.assertFalse(os.path.exists('.shelf/shelves/default/01'))
+        self.assertTrue(os.path.exists('.shelf/shelves/other/00'))
+
+        # Switch back
+        self.run_bzr('shelf', 'switch', 'default', retcode=0)
+        file('test_file', 'w').write(self.MODIFIED)
+        self.run_bzr('shelve', '--all', 'test_file', retcode=0)
+
+        # Check that worked
+        self.assertTrue(os.path.exists('.shelf/shelves/default/01'))
+        self.assertFalse(os.path.exists('.shelf/shelves/other/01'))
+
+    def test_shelf_bad_patch_arg(self):
+        self.tree = self.make_branch_and_tree('.')
+
+        # Check the bad arg handling
+        stdout, error = self.run_bzr_captured(['unshelve', '01'], retcode=3)
+        self.assertTrue("Patch '01' doesn't exist on shelf" in error)
+
+        stdout, error = self.run_bzr_captured(['unshelve', 'foo'], retcode=3)
+        self.assertTrue("Invalid patch name 'foo'" in error)
+
+        # Hex and is cracky, so it shouldn't work
+        stdout, error = self.run_bzr_captured(['unshelve', '0x00'], retcode=3)
+        self.assertTrue("Invalid patch name '0x00'" in error)
+
+    def test_color_hunk_selector(self):
+        """Make sure NoColor is raised iff terminal.has_ansi_colors is False"""
+        hs = ShelveHunkSelector([])
+        hs = UnshelveHunkSelector([])
+        from bzrlib.plugins.bzrtools import terminal
+        old_has_ansi_colors = terminal.has_ansi_colors
+        terminal.has_ansi_colors = lambda: False
+        try:
+            self.assertRaises(NoColor, ShelveHunkSelector, [], True)
+            self.assertRaises(NoColor, UnshelveHunkSelector, [], True)
+            terminal.has_ansi_colors = lambda: True
+            hs = ShelveHunkSelector([], color=True)
+            hs = UnshelveHunkSelector([], color=True)
+        finally:
+            terminal.has_ansi_colors = old_has_ansi_colors
+
+    def test_no_color(self):
+        """Ensure --no-color switch can be passed"""
+        self.tree = self.make_branch_and_tree('.')
+
+        subdir = 'subdir'
+        os.mkdir(subdir)
+        self.tree.add(subdir)
+        os.chdir(subdir)
+
+        self.__create_and_add_test_file()
+
+        # Modify the test file
+        # write in binary mode because on win32 line-endings should be LF
+        f = file('test_file', 'wb')
+        f.write(self.MODIFIED)
+        f.close()
+        stdout, error = self.run_bzr_captured(['shelve', '--all', 
+                                               '--no-color'])
+        stdout, error = self.run_bzr_captured(['unshelve', '--all', 
+                                               '--no-color'])
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/test_baz_import.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/test_baz_import.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/tests/test_baz_import.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/tests/test_baz_import.py	2006-08-11 15:10:54.000000000 +0200
@@ -135,8 +135,8 @@
         os.mkdir(os.path.join(self._tmpdir, 'tree'))
         tree = pybaz.init_tree(os.path.join(self._tmpdir, 'tree'),
                                self._import_symlink)
-        os.symlink('missing-file-name',
-                   os.path.join(self._tmpdir, 'tree', 'alink'))
+        link_path = os.path.join(self._tmpdir, 'tree', 'alink')
+        os.symlink('missing-file-name', link_path)
         tree.add_tag('alink')
         id_file = open(os.path.join(tree, '.arch-ids', 'alink.id'), 'w')
         id_file.write('symlink_tag\n')
@@ -144,6 +144,13 @@
         msg = tree.log_message()
         msg["summary"] = "Import with a symlink"
         tree.import_(msg)
+        os.unlink(link_path)
+        f = file(link_path, 'w')
+        f.write('Not a symlink no more!')
+        f.close()
+        msg = tree.log_message()
+        msg["summary"] = "Turn a symlink into a file"
+        tree.commit(msg)
         shutil.rmtree(os.path.join(self._tmpdir, 'tree'))
 
     def make_empty_import(self):
@@ -385,16 +392,18 @@
         # self.assertEqual(branch.missing_revisions(branch3), [])
         
     def test_import_symlink(self):
-        import_version('output', pybaz.Version(self._baz._import_symlink))
+        import_version('output', pybaz.Version(self._baz._import_symlink), 
+                       max_count=1)
         # expected results:
-        # one commit, no files, revision identifier of 
+        # two commits, no files, revision identifier of 
         # 'demo at DONOTUSE_c--import--0--base-0'
         branch = Branch.open('output')
         self.assertEqual(branch.revision_history(),
                          [self._baz._import_symlink_bzr])
         rev = branch.repository.get_revision(self._baz._import_symlink_bzr)
         # and again.
-        import_version('output2', pybaz.Version(self._baz._import_symlink))
+        import_version('output2', pybaz.Version(self._baz._import_symlink),
+                       max_count=1)
         branch2 = Branch.open('output2')
         self.assertEqual(branch.revision_history(), branch2.revision_history())
         rev2 = branch2.repository.get_revision(self._baz._import_symlink_bzr)
@@ -413,6 +422,10 @@
         self.assertEqual(entry.kind, 'symlink')
         self.assertEqual(entry.symlink_target, 'missing-file-name')
 
+        # current bzr doesn't handle type changes
+        self.assertRaises(AssertionError, import_version, 'output3',
+                          pybaz.Version(self._baz._import_symlink))
+
     def test_missing_ancestor(self):
         import_version('output', pybaz.Version(self._baz._missing_ancestor))
         # expected results:
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/upstream_import.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/upstream_import.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/upstream_import.py	1970-01-01 01:00:00.000000000 +0100
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/upstream_import.py	2006-08-11 15:10:54.000000000 +0200
@@ -0,0 +1,272 @@
+"""Import upstream source into a branch"""
+
+from bz2 import BZ2File
+import errno
+import os
+from shutil import rmtree
+from StringIO import StringIO
+import tarfile
+from unittest import makeSuite
+
+from bzrlib.bzrdir import BzrDir
+from bzrlib.errors import NoSuchFile, BzrCommandError, NotBranchError
+from bzrlib.osutils import pathjoin, isdir, file_iterator
+from bzrlib.tests import TestCaseInTempDir
+from bzrlib.trace import warning
+from bzrlib.transform import TreeTransform, resolve_conflicts, cook_conflicts
+from bzrlib.workingtree import WorkingTree
+
+
+def top_directory(path):
+    """Return the top directory given in a path."""
+    dirname = os.path.dirname(path)
+    last_dirname = dirname
+    while True:
+        dirname = os.path.dirname(dirname)
+        if dirname == '' or dirname == last_dirname:
+            return last_dirname
+        last_dirname = dirname
+
+
+def common_directory(names):
+    """Determine a single directory prefix from a list of names"""
+    possible_prefix = None
+    for name in names:
+        name_top = top_directory(name)
+        if possible_prefix is None:
+            possible_prefix = name_top
+        else:
+            if name_top != possible_prefix:
+                return None
+    return possible_prefix
+
+
+def do_directory(tt, trans_id, tree, relative_path, path):
+    if isdir(path) and tree.path2id(relative_path) is not None:
+        tt.cancel_deletion(trans_id)
+    else:
+        tt.create_directory(trans_id)
+
+
+def add_implied_parents(implied_parents, path):
+    """Update the set of implied parents from a path"""
+    parent = os.path.dirname(path)
+    if parent in implied_parents:
+        return
+    implied_parents.add(parent)
+    add_implied_parents(implied_parents, parent)
+
+
+def names_of_files(tar_file):
+    for member in tar_file.getmembers():
+        if member.type != "g":
+            yield member.name
+
+
+def import_tar(tree, tar_input):
+    """Replace the contents of a working directory with tarfile contents.
+    The tarfile may be a gzipped stream.  File ids will be updated.
+    """
+    tar_file = tarfile.open('lala', 'r', tar_input)
+    prefix = common_directory(names_of_files(tar_file))
+    tt = TreeTransform(tree)
+
+    removed = set()
+    for path, entry in tree.inventory.iter_entries():
+        trans_id = tt.trans_id_tree_path(path)
+        tt.delete_contents(trans_id)
+        removed.add(path)
+
+    added = set() 
+    implied_parents = set()
+    seen = set()
+    for member in tar_file.getmembers():
+        if member.type == 'g':
+            # type 'g' is a header
+            continue
+        relative_path = member.name 
+        if prefix is not None:
+            relative_path = relative_path[len(prefix)+1:]
+        if relative_path == '':
+            continue
+        add_implied_parents(implied_parents, relative_path)
+        trans_id = tt.trans_id_tree_path(relative_path)
+        added.add(relative_path.rstrip('/'))
+        path = tree.abspath(relative_path)
+        if member.name in seen:
+            tt.cancel_creation(trans_id)
+        seen.add(member.name)
+        if member.isreg():
+            tt.create_file(file_iterator(tar_file.extractfile(member)), 
+                           trans_id)
+        elif member.isdir():
+            do_directory(tt, trans_id, tree, relative_path, path)
+        elif member.issym():
+            tt.create_symlink(member.linkname, trans_id)
+
+    for relative_path in implied_parents.difference(added):
+        if relative_path == "":
+            continue
+        trans_id = tt.trans_id_tree_path(relative_path)
+        path = tree.abspath(relative_path)
+        do_directory(tt, trans_id, tree, relative_path, path)
+        added.add(relative_path)
+
+    for conflict in cook_conflicts(resolve_conflicts(tt), tt):
+        warning(conflict)
+    tt.apply()
+    update_ids(tree, added, removed)
+
+
+def update_ids(tree, added, removed):
+    """Make sure that all present files files have file_ids.
+    """
+    # XXX detect renames
+    new = added.difference(removed)
+    deleted = removed.difference(added)
+    tree.add(sorted(new))
+    tree.remove(sorted(deleted, reverse=True))
+
+
+def do_import(source, tree_directory=None):
+    """Implementation of import command.  Intended for UI only"""
+    if tree_directory is not None:
+        try:
+            tree = WorkingTree.open(tree_directory)
+        except NotBranchError:
+            if not os.path.exists(tree_directory):
+                os.mkdir(tree_directory)
+            branch = BzrDir.create_branch_convenience(tree_directory)
+            tree = branch.bzrdir.open_workingtree()
+    else:
+        tree = WorkingTree.open_containing('.')[0]
+    tree.lock_write()
+    try:
+        if tree.changes_from(tree.basis_tree()).has_changed():
+            raise BzrCommandError("Working tree has uncommitted changes.")
+
+        if (source.endswith('.tar') or source.endswith('.tar.gz') or 
+            source.endswith('.tar.bz2')) or source.endswith('.tgz'):
+            try:
+                if source.endswith('.bz2'):
+                    tar_input = BZ2File(source, 'r')
+                    tar_input = StringIO(tar_input.read())
+                else:
+                    tar_input = file(source, 'rb')
+            except IOError, e:
+                if e.errno == errno.ENOENT:
+                    raise NoSuchFile(source)
+            try:
+                import_tar(tree, tar_input)
+            finally:
+                tar_input.close()
+    finally:
+        tree.unlock()
+
+class TestImport(TestCaseInTempDir):
+
+    def make_tar(self, mode='w'):
+        result = StringIO()
+        tar_file = tarfile.open('project-0.1.tar', mode, result)
+        os.mkdir('project-0.1')
+        tar_file.add('project-0.1')
+        os.mkdir('project-0.1/junk')
+        tar_file.add('project-0.1/junk')
+        
+        f = file('project-0.1/README', 'wb')
+        f.write('What?')
+        f.close()
+        tar_file.add('project-0.1/README')
+
+        f = file('project-0.1/FEEDME', 'wb')
+        f.write('Hungry!!')
+        f.close()
+        tar_file.add('project-0.1/FEEDME')
+
+        tar_file.close()
+        rmtree('project-0.1')
+        result.seek(0)
+        return result
+
+    def make_tar2(self):
+        result = StringIO()
+        tar_file = tarfile.open('project-0.2.tar', 'w', result)
+        os.mkdir('project-0.2')
+        tar_file.add('project-0.2')
+        
+        os.mkdir('project-0.2/junk')
+        tar_file.add('project-0.2/junk')
+
+        f = file('project-0.2/README', 'wb')
+        f.write('Now?')
+        f.close()
+        tar_file.add('project-0.2/README')
+        tar_file.close()
+
+        tar_file = tarfile.open('project-0.2.tar', 'a', result)
+        tar_file.add('project-0.2/README')
+
+        rmtree('project-0.2')
+        return result
+
+    def make_messed_tar(self):
+        result = StringIO()
+        tar_file = tarfile.open('project-0.1.tar', 'w', result)
+        os.mkdir('project-0.1')
+        tar_file.add('project-0.1')
+
+        os.mkdir('project-0.2')
+        tar_file.add('project-0.2')
+        
+        f = file('project-0.1/README', 'wb')
+        f.write('What?')
+        f.close()
+        tar_file.add('project-0.1/README')
+        tar_file.close()
+        rmtree('project-0.1')
+        result.seek(0)
+        return result
+
+    def test_top_directory(self):
+        self.assertEqual(top_directory('ab/b/c'), 'ab')
+        self.assertEqual(top_directory('/etc'), '/')
+
+    def test_common_directory(self):
+        self.assertEqual(common_directory(['ab/c/d', 'ab/c/e']), 'ab')
+        self.assertIs(common_directory(['ab/c/d', 'ac/c/e']), None)
+
+    def test_untar(self):
+        tar_file = self.make_tar()
+        tree = BzrDir.create_standalone_workingtree('tree')
+        import_tar(tree, tar_file)
+        self.assertTrue(tree.path2id('README') is not None) 
+        self.assertTrue(tree.path2id('FEEDME') is not None)
+        self.assertTrue(os.path.isfile(tree.abspath('README')))
+        self.assertEqual(tree.inventory[tree.path2id('README')].kind, 'file')
+        self.assertEqual(tree.inventory[tree.path2id('FEEDME')].kind, 'file')
+        
+        f = file(tree.abspath('junk/food'), 'wb')
+        f.write('I like food\n')
+        f.close()
+
+        tar_file = self.make_tar2()
+        import_tar(tree, tar_file)
+        self.assertTrue(tree.path2id('README') is not None) 
+        self.assertTrue(not os.path.exists(tree.abspath('FEEDME')))
+
+
+    def test_untar2(self):
+        tar_file = self.make_messed_tar()
+        tree = BzrDir.create_standalone_workingtree('tree')
+        import_tar(tree, tar_file)
+        self.assertTrue(tree.path2id('project-0.1/README') is not None) 
+
+    def test_untar_gzip(self):
+        tar_file = self.make_tar(mode='w:gz')
+        tree = BzrDir.create_standalone_workingtree('tree')
+        import_tar(tree, tar_file)
+        self.assertTrue(tree.path2id('README') is not None) 
+
+
+def test_suite():
+    return makeSuite(TestImport)
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/userinteractor.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/userinteractor.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/userinteractor.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/userinteractor.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-
 import sys
 
 class UserOption:
@@ -134,9 +132,9 @@
             print '  %s - %s' % (opt.char, opt.help)
 
     if sys.platform == "win32":
-        import msvcrt
         def __getchar(self):
-            return msvcrt.getche()
+            import msvcrt
+            return msvcrt.getch()
     else:
         def __getchar(self):
             import tty
diff -Nru /tmp/ah3oLyERDM/bzrtools-0.8.1/zap.py /tmp/2DwbKrAPHs/bzrtools-0.9.0/zap.py
--- /tmp/ah3oLyERDM/bzrtools-0.8.1/zap.py	2006-05-11 17:18:39.000000000 +0200
+++ /tmp/2DwbKrAPHs/bzrtools-0.9.0/zap.py	2006-08-11 15:10:54.000000000 +0200
@@ -1,10 +1,11 @@
 from shutil import rmtree
 
+from bzrlib.branch import Branch
 from bzrlib.errors import NoWorkingTree, NotLocalUrl, NotBranchError
-from bzrlib.delta import compare_trees
 from bzrlib.workingtree import WorkingTree
 
-from errors import NotCheckout, UncommittedCheckout
+from errors import (NotCheckout, UncommittedCheckout, ParentMissingRevisions, 
+                    NoParent)
 
 
 def zap(path, remove_branch=False):
@@ -17,9 +18,17 @@
     branch_base = branch.bzrdir.transport.base
     if tree_base == branch_base:
         raise NotCheckout(path)
-    delta = compare_trees(wt.basis_tree(), wt, want_unchanged=False)
+    delta = wt.changes_from(wt.basis_tree(), want_unchanged=False)
     if delta.has_changed():
         raise UncommittedCheckout()
+    if remove_branch:
+        parent_loc = branch.get_parent()
+        if parent_loc is None:
+            raise NoParent()
+        parent = Branch.open(parent_loc)
+        p_ancestry = parent.repository.get_ancestry(parent.last_revision())
+        if branch.last_revision() not in p_ancestry:
+            raise ParentMissingRevisions(branch.get_parent())
     rmtree(path)
     if remove_branch:
         t = branch.bzrdir.transport
@@ -46,6 +55,14 @@
             BranchReferenceFormat().initialize(checkout, wt.branch)
             return checkout.create_workingtree()
 
+        def make_checkout2(self):
+            wt = self.make_checkout()
+            wt2 = wt.branch.bzrdir.sprout('source2').open_workingtree()
+            os.mkdir('checkout2')
+            checkout = BzrDirMetaFormat1().initialize('checkout2')
+            BranchReferenceFormat().initialize(checkout, wt2.branch)
+            return checkout.create_workingtree()
+
         def test_is_checkout(self):
             self.assertRaises(NotCheckout, zap, '.')
             wt = BzrDir.create_standalone_workingtree('.')
@@ -59,12 +76,15 @@
             self.assertIs(True, os.path.exists('source'))
 
 	def test_zap_branch(self):
-            self.make_checkout()
+            self.make_checkout2()
             base = WorkingTree.open('checkout').branch.base
             self.assertIs(True, os.path.exists('checkout'))
-            zap('checkout', remove_branch=True)
-            self.assertIs(False, os.path.exists('checkout'))
-            self.assertIs(False, os.path.exists('source'))
+            self.assertRaises(NoParent, zap, 'checkout', remove_branch=True)
+            self.assertIs(True, os.path.exists('checkout'))
+            self.assertIs(True, os.path.exists('source'))
+            zap('checkout2', remove_branch=True)
+            self.assertIs(False, os.path.exists('checkout2'))
+            self.assertIs(False, os.path.exists('source2'))
 
         def test_checks_modified(self):
             checkout = self.make_checkout()


More information about the Utnubu-maintainers mailing list