[PATCH 1 of 8] commands: add abandon command

Martin Geisler mg at lazybytes.net
Wed Jun 1 16:42:47 UTC 2011


# HG changeset patch
# User Martin Geisler <mg at lazybytes.net>
# Date 1306919141 -7200
# Node ID 9b14fa930acae9dc802d7cec87780ba1a3a9aa8b
# Parent  1ffeeb91c55d0b00445ceabde14a0d0faf906a33
commands: add abandon command

Abandoned changesets are hidden from 'hg log' by default.

diff --git a/contrib/perf.py b/contrib/perf.py
--- a/contrib/perf.py
+++ b/contrib/perf.py
@@ -48,6 +48,9 @@
 def perfheads(ui, repo):
     timer(lambda: len(repo.changelog.heads()))
 
+def perfabandoned(ui, repo):
+    timer(lambda: repo.changelog.abandoned(0))
+
 def perftags(ui, repo):
     import mercurial.changelog, mercurial.manifest
     def t():
@@ -153,6 +156,7 @@
     'perfmanifest': (perfmanifest, []),
     'perfindex': (perfindex, []),
     'perfheads': (perfheads, []),
+    'perfabandoned': (perfabandoned, []),
     'perftags': (perftags, []),
     'perfdirstate': (perfdirstate, []),
     'perfdirstatedirs': (perfdirstate, []),
diff --git a/mercurial/changelog.py b/mercurial/changelog.py
--- a/mercurial/changelog.py
+++ b/mercurial/changelog.py
@@ -240,3 +240,24 @@
         l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
         text = "\n".join(l)
         return self.addrevision(text, transaction, len(self), p1, p2)
+
+    def abandoned(self, node):
+        # TODO: need cache
+        abandoned = set()
+
+        for head in self.heads():
+            extra = self.read(head)[5]
+            if 'abandon' in extra:
+                abandoned.update(map(bin, extra['abandon'].split(' ')))
+                abandoned.add(head)
+
+        if node in abandoned:
+            for head in self.heads():
+                if head not in abandoned and self.ancestor(head, node) == node:
+                    return None
+
+            return [h for h in self.heads() if
+                    hex(node) in self.read(h)[5].get('abandon', '').split(' ')
+                    or node == h][0]
+
+        return None
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -130,6 +130,18 @@
 
 # Commands start here, listed alphabetically
 
+ at command('abandon', commitopts2, _('REV'))
+def abandon(ui, repo, rev, **opts):
+    """abandon specified changeset and its descendents"""
+    root = scmutil.revsingle(repo, rev)
+    abandoned = repo.changelog.descendants(root.rev())
+    revlist = root.hex() + " ".join(a.hex() for a in abandoned)
+    ctx = context.memctx(repo, [root.rev(), None], "abandoned",
+                         [], None, user=opts.get('user'),
+                         date=opts.get('date'),
+                         extra=dict(abandon=revlist))
+    ctx.commit()
+
 @command('^add',
     walkopts + subrepoopts + dryrunopts,
     _('[OPTION]... [FILE]...'))
@@ -3281,6 +3293,7 @@
      _('show changesets within the given named branch'), _('BRANCH')),
     ('P', 'prune', [],
      _('do not display revision or any of its ancestors'), _('REV')),
+    ('', 'abandoned', False, _('show abandoned changesets')),
     ] + logopts + walkopts,
     _('[OPTION]... [FILE]'))
 def log(ui, repo, *pats, **opts):
@@ -3336,6 +3349,8 @@
         rev = ctx.rev()
         parents = [p for p in repo.changelog.parentrevs(rev)
                    if p != nullrev]
+        if not opts.get('abandoned') and ctx.abandoned():
+            return
         if opts.get('no_merges') and len(parents) == 2:
             return
         if opts.get('only_merges') and len(parents) != 2:
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -110,6 +110,8 @@
         return self._changeset[4]
     def branch(self):
         return encoding.tolocal(self._changeset[5].get("branch"))
+    def abandoned(self):
+        return self._repo.abandoned(self._node)
     def extra(self):
         return self._changeset[5]
     def tags(self):
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -517,6 +517,9 @@
                     bheads = [b for b in bheads if b not in reachable]
             partial[branch] = bheads
 
+    def abandoned(self, rev):
+        return self.changelog.abandoned(self[rev].node())
+
     def lookup(self, key):
         if isinstance(key, int):
             return self.changelog.node(key)
diff --git a/tests/test-abandoned.t b/tests/test-abandoned.t
new file mode 100644
--- /dev/null
+++ b/tests/test-abandoned.t
@@ -0,0 +1,45 @@
+setting up
+
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > logtemplate = '{rev} {node|short} {desc|firstline|strip}\n'
+  > [defaults]
+  > abandon = -d "0 0"
+  > [extensions]
+  > graphlog =
+  > EOF
+
+  $ hg init repo
+  $ cd repo
+  $ touch i x
+  $ hg commit -A -m init i
+  $ hg commit -A -m x x
+  $ hg glog
+  @  1 0b00c28422ee x
+  |
+  o  0 54dbcd775ef0 init
+  
+  $ hg init ../empty
+  $ hg clone -q -r 0 . ../base-i
+  $ hg clone -q -r 1 . ../base-x
+
+abandon x
+
+  $ hg abandon 1
+  $ hg glog
+  o  2 339976ff5010 abandoned
+  |
+  @  1 0b00c28422ee x
+  |
+  o  0 54dbcd775ef0 init
+  
+
+abandoned changeset is hidden in log
+
+  $ hg log
+  0 54dbcd775ef0 init
+
+  $ hg log --abandoned
+  2 339976ff5010 abandoned
+  1 0b00c28422ee x
+  0 54dbcd775ef0 init
diff --git a/tests/test-debugcomplete.t b/tests/test-debugcomplete.t
--- a/tests/test-debugcomplete.t
+++ b/tests/test-debugcomplete.t
@@ -1,5 +1,6 @@
 Show all commands except debug commands
   $ hg debugcomplete
+  abandon
   add
   addremove
   annotate
@@ -54,6 +55,7 @@
 
 Show all commands that start with "a"
   $ hg debugcomplete a
+  abandon
   add
   addremove
   annotate
@@ -193,7 +195,7 @@
   export: output, switch-parent, rev, text, git, nodates
   forget: include, exclude
   init: ssh, remotecmd, insecure
-  log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
+  log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, abandoned, patch, git, limit, no-merges, stat, style, template, include, exclude
   merge: force, tool, rev, preview
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
@@ -202,6 +204,7 @@
   status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
   summary: remote
   update: clean, check, date, rev
+  abandon: date, user
   addremove: similarity, include, exclude, dry-run
   archive: no-decode, prefix, rev, type, subrepos, include, exclude
   backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
diff --git a/tests/test-globalopts.t b/tests/test-globalopts.t
--- a/tests/test-globalopts.t
+++ b/tests/test-globalopts.t
@@ -279,6 +279,7 @@
   
   list of commands:
   
+   abandon      abandon specified changeset and its descendents
    add          add the specified files on the next commit
    addremove    add all new files, delete all missing files
    annotate     show changeset information by line for each file
@@ -359,6 +360,7 @@
   
   list of commands:
   
+   abandon      abandon specified changeset and its descendents
    add          add the specified files on the next commit
    addremove    add all new files, delete all missing files
    annotate     show changeset information by line for each file
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -49,6 +49,7 @@
   
   list of commands:
   
+   abandon      abandon specified changeset and its descendents
    add          add the specified files on the next commit
    addremove    add all new files, delete all missing files
    annotate     show changeset information by line for each file
@@ -123,6 +124,7 @@
   use "hg -v help" to show builtin aliases and global options
 
   $ hg -q help
+   abandon      abandon specified changeset and its descendents
    add          add the specified files on the next commit
    addremove    add all new files, delete all missing files
    annotate     show changeset information by line for each file
@@ -636,6 +638,7 @@
   
   list of commands:
   
+   abandon      abandon specified changeset and its descendents
    add          add the specified files on the next commit
    addremove    add all new files, delete all missing files
    annotate     show changeset information by line for each file



More information about the Mercurial-devel mailing list