[PATCH] convert: Add 'close' as a possible splicemap option

John Peacock john.peacock at messagesystems.com
Wed Jan 2 09:51:09 CST 2013

# HG changeset patch
# User John Peacock <john.peacock at messagesystems.com>
# Date 1357141812 18000
# Branch stable
# Node ID 326d97248e8b753f01b41349e2e6e3163e26fae4
# Parent  6d2ba9875d083e7acad3c1674d6b922a4b7bdb76
convert: Add 'close' as a possible splicemap option.

Sometimes a shared repo that makes extensive use of feature branches
is not managed appropriately and a growing number of open but inactive
branches litter the branches listing.  Since there is no way to close
multiple branches in a single commit (even though the act of
closing is just altering metadata), cleaning up a repository like
this can be an exercise in frustration and makes the history unclear.

This patch extends the splicemap to accept a second type of line which
signals a changeset to mark as being closed as part of the conversion.
During the scanning of the source, the changeset matching the key has
the changeset marked as closed in the extra metadata.  Then later when
that changeset is actually processed, the original source parents are
used for history and the 'close' value is just ignored.

It seems unlikely that a single changeset would require both synthetic
history as well as being closed, so that case is not handled.  Either
new parent(s) can be synthesized OR a branch can be closed, but not both.

diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py
--- a/hgext/convert/__init__.py
+++ b/hgext/convert/__init__.py
@@ -122,6 +122,20 @@
     specify the revision on "trunk" as the first parent and the one on
     the "release-1.0" branch as the second.
+    You can also use a splicemap to close inactive branches during the
+    conversion (if the source repo was not well managed).  In this case,
+    the entry contains a key, followed by a space, and literal 'close'::
+      key close
+    It is not possible to combine both synthetic history and marking a
+    branch as closed.  A branch doesn't need to strictly be inactive
+    (an inactive branch is one whose head revision has already been
+    merged); any branches that have not been merged but are no longer 
+    needed or valid can be close in this fashion as well.  Note that a
+    closed branch is still reachable by updating to that branch and will
+    be reopened automatically when a new commit is made.
     The branchmap is a file that allows you to rename a branch when it is
     being brought in from whatever external repository. When used in
     conjunction with a splicemap, it allows for a powerful combination
diff --git a/hgext/convert/convcmd.py b/hgext/convert/convcmd.py
--- a/hgext/convert/convcmd.py
+++ b/hgext/convert/convcmd.py
@@ -159,6 +159,11 @@
                 # We do not have to wait for nodes already in dest.
                 if self.dest.hascommit(self.map.get(p, p)):
+                # Mark the commit as closed and ignore "parent"
+                if p == 'close':
+                    commit = self.cachecommit(c)
+                    commit.extra['close'] = 1
+                    continue
                 # Parent is not in dest and not being converted, not good
                 if p not in parents:
                     raise util.Abort(_('unknown splice map parent: %s') % p)
@@ -343,9 +348,15 @@
         self.dest.setbranch(commit.branch, pbranches)
             parents = self.splicemap[rev]
-            self.ui.status(_('spliced in %s as parents of %s\n') %
-                           (parents, rev))
-            parents = [self.map.get(p, p) for p in parents]
+            # Commit is already marked closed, so use original parents
+            if 'close' in parents:
+              self.ui.status(_('splice map closed %s\n') %
+                             (commit.branch))
+              parents = [self.map.get(p, p) for p in commit.parents]
+            else:
+              self.ui.status(_('spliced in %s as parents of %s\n') %
+                             (parents, rev))
+              parents = [self.map.get(p, p) for p in parents]
         except KeyError:
             parents = [b[0] for b in pbranches]
         source = progresssource(self.ui, self.source, len(files))
diff --git a/tests/test-convert-splicemap.t b/tests/test-convert-splicemap.t
--- a/tests/test-convert-splicemap.t
+++ b/tests/test-convert-splicemap.t
@@ -220,3 +220,46 @@
   scanning source...
   abort: unknown splice map parent: deadbeef102a90ea7b4a3361e4082ed620918c26
+Test branch closing
+  $ hg init closebranch
+  $ cd closebranch
+  $ echo a > a
+  $ hg ci -Am createa
+  adding a
+  $ hg branch newa
+  marked working directory as branch newa
+  (branches are permanent and global, did you want a bookmark?)
+  $ hg echo aa > a
+  hg: unknown command 'echo'
+  [255]
+  $ hg ci -m newa
+  $ hg up default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge newa
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m mergenewa
+  $ hg branches
+  default                        2:5502a03b9720
+  newa                           1:86d3d4ecdf64 (inactive)
+  $ hg log -r1 --template "{node} close\n" > ../splicemap.cb
+  $ cd ..
+  $ hg convert --splicemap=splicemap.cb closebranch closebranch.closed
+  initializing destination closebranch.closed repository
+  scanning source...
+  sorting...
+  converting...
+  2 createa
+  1 newa
+  splice map closed newa
+  0 mergenewa
+  $ cd closebranch.closed
+  $ hg branches
+  default                        2:7c1b32ad8d42
+  $ hg branches -c
+  default                        2:7c1b32ad8d42
+  newa                           1:57ad6ec6adcc (closed)

More information about the Mercurial-devel mailing list