Commits

Peter Arrenbrecht committed fdf7045

better bookmark handling, handle amdend*2+evolve

  • Participants
  • Parent commits 4c6c57f

Comments (0)

Files changed (3)

File hgext/evolution.py

         self._supercedes = {}
         self._replacedby = {}
         self._readmeta()
+        self._bookmarktargets = dict((id, name) for name, id
+                                     in repo._bookmarks.iteritems())
 
     def _readmeta(self):
         cl = self.repo.changelog
                 res = next
         return res
 
-    def printstatus(self, printfn, revs):
-        needevolve = False
-        for revspec in revs:
-            cset = self.repo[revspec]
-            canrebase = True
-
-            replrev = self.replacedby(cset.rev())
-            if replrev:
-                printfn(_('rev %s has been replaced by rev %s\n')
-                        % (cset.rev(), replrev))
-                canrebase = False
-
-            sup = self.supercedes(cset.rev())
-            if sup:
-                for type, tgtrev in sup:
-                    if type == UPDATES:
-                        replrev = self.replacedby(tgtrev) or '?'
-                        printfn(_('rev %s is an update of rev %s, which was replaced by rev %s\n')
-                                % (cset.rev(), tgtrev, replrev))
-                        canrebase = False
-                        break
-
-            if canrebase:
-                replrev = self.replacedby(cset.p1().rev())
-                if replrev:
-                    printfn(_('rev %s needs rebase on rev %s, which replaces rev %s\n')
-                            % (cset.rev(), replrev, cset.p1().rev()))
-                    needevolve = True
-
-        return needevolve
-
     def amendby(self, old, updates, head, newbases, commitopts):
         repo = self.repo
         if len(old.parents()) > 1:
 
         return newid
 
+    # FIXME need version that walks back the parent chain
 
-    def childrenof(self, cset):
-        res = []
-        for chd in cset.children():
-            # TODO might simply use !hidden here?
-            sup = self.supercedes(chd.rev())
-            repl = self.replacedby(chd.rev(), transitive=False)
-            if not repl and (not sup or sup[0][0] != UPDATES):
-                res.append(chd)
-        return res
+    def checkstatus(self, cset, printfn=None):
+        if not printfn:
+            def printfn(s):
+                pass
+        replrev = self.replacedby(cset.rev())
+        if replrev:
+            printfn(_('rev %s has been replaced by rev %s\n')
+                    % (cset.rev(), replrev))
+            return False
+
+        sup = self.supercedes(cset.rev())
+        if sup:
+            for type, tgtrev in sup:
+                if type == UPDATES:
+                    replrev = self.replacedby(tgtrev) or '?'
+                    printfn(_('rev %s is an update of rev %s, which was replaced by rev %s\n')
+                            % (cset.rev(), tgtrev, replrev))
+                    return False
+
+        replrev = self.replacedby(cset.p1().rev())
+        if replrev:
+            printfn(_('rev %s needs rebase on rev %s, which replaces rev %s\n')
+                    % (cset.rev(), replrev, cset.p1().rev()))
+            return True
+
+        return False
 
     def evolveonehop(self, old, commitopts):
         repo = self.repo
         oldbase = old.p1()
         newbaserev = self.replacedby(oldbase.rev(), transitive=False)
         if not newbaserev:
-            return None
+            return old.node()
         self.ui.status(_('rebasing rev %s onto %s\n') % (old.rev(), newbaserev))
         newbase = repo[newbaserev]
         sup = self.supercedes(newbase.rev())
         headupdrev = max(rev for type, rev in sup if type == SUBSUMES)
 
-        # FIXME need to find and maintain the bookmark at old
-
+        ui.status(_('updating to rev %s\n') % old.rev())
         if commands.update(ui, repo, old.rev(), clean=True):
             return 1
+        bm = self._bookmarktargets.get(old.node())
+        if bm:
+            bookmarks.setcurrent(repo, bm)
+        ui.status(_('merging with rev %s\n') % headupdrev)
         if commands.merge(ui, repo, headupdrev):
             return 1
         if commands.commit(ui, repo, message=_('rebasing %s on %s\n')
                            % (old.hex(), newbase.hex()), **commitopts):
             return 1
         merge = repo['tip']
+        ui.status(_('committing new version\n'))
         return self.amendby(old, [merge], merge, [newbase.node(), nullid],
                             commitopts)
 
-    def evolvetotop(self, rev, commitopts):
-        repo = self.repo
-        res = repo['.'].node()
-        csets = [repo[rev]]
-        while csets:
-            cset = csets.pop()
-            res = self.evolveonehop(cset, commitopts)
-            csets += self.childrenof(cset)
+    def evolveontonewest(self, old, commitopts):
+        while True:
+            newid = self.evolveonehop(old, commitopts)
+            if newid == old.node():
+                return newid
+            old = self.repo[newid]
+
+    def evolveupto(self, old, commitopts):
+        # scan back to earliest parent possibly needing evolve
+        csets = [old]
+        while True:
+            old = old.p1()
+            if not self.checkstatus(old):
+                break
+            csets.append(old)
+        # evolve them, parents first
+        for old in reversed(csets):
+            res = self.evolveontonewest(old, commitopts)
         return res
 
 
     revs = list(revs) + list(opts.get('change') or [])
     if not revs:
         revs = ['.']
-    evolution(ui, repo).printstatus(repo, ui.status, revs)
+    evo = evolution(ui, repo)
+    for revspec in revs:
+        evo.checkstatus(repo[revspec], ui.status)
 
 
 # FIXME add commit options (user, date, etc.)
     evo = evolution(ui, repo)
     resid = None
     for rev in revs:
-        resid = evo.evolvetotop(rev, ciopts)
+        resid = evo.evolveupto(repo[rev], ciopts)
     if resid:
         hg.update(repo, resid)
 
     res = origfn(ui, repo, *args, **opts)
     def printfn(s):
         ui.warn(_('WARNING: %s') % s)
-    if evo.printstatus(printfn, ['.']):
+    if evo.checkstatus(repo['.'], printfn):
         ui.status(_("use 'hg evolve'\n"))
     return res
 

File tut/src/quick.rextile

-
-h1. Quick Test
-
-	$ hg init foo
-	$ cd foo
-	$ echo Z >z
-	$ hg add z
-	$ hg book z
-	$ hg ci -m "add z"
-	$ echo A >a
-	$ hg add a
-	$ hg book a
-	$ hg ci -m "add a"
-	$ echo B >b
-	$ echo C >c
-	$ hg add b c
-	$ hg amend -n "add b" b --traceback
-
-	$ hg log
-	3	a: add a - john
-	0	z: add z - john
-
-	$ hg log --hidden
-	3	a: add a - john
-	2	: add b - john
-	1	: add a - john
-	0	z: add z - john
-
-	$ cat .hg/evolution
-	34e8f08430127133f67fa129d6d4a62b80616230
-	 U 90944eeb31f5785032c994342e29ea351caed407
-	2167fbc8ad0556b23fc564960e7eafbb298b8656
-	 R 90944eeb31f5785032c994342e29ea351caed407
-	 S 34e8f08430127133f67fa129d6d4a62b80616230
-
-	$ hg glog -p
-	@  3	a: add a - john
-	|  diff --git a/a b/a
-	|  new file mode 100644
-	|  --- /dev/null
-	|  +++ b/a
-	|  @@ -0,0 +1,1 @@
-	|  +A
-	|  diff --git a/b b/b
-	|  new file mode 100644
-	|  --- /dev/null
-	|  +++ b/b
-	|  @@ -0,0 +1,1 @@
-	|  +B
-	|
-	| o  2	: add b - john
-	| |  diff --git a/b b/b
-	| |  new file mode 100644
-	| |  --- /dev/null
-	| |  +++ b/b
-	| |  @@ -0,0 +1,1 @@
-	| |  +B
-	| |
-	| o  1	: add a - john
-	|/   diff --git a/a b/a
-	|    new file mode 100644
-	|    --- /dev/null
-	|    +++ b/a
-	|    @@ -0,0 +1,1 @@
-	|    +A
-	|
-	o  0	z: add z - john
-	   diff --git a/z b/z
-	   new file mode 100644
-	   --- /dev/null
-	   +++ b/z
-	   @@ -0,0 +1,1 @@
-	   +Z
-
-	$ hg parents
-	3	a: add a - john
-
-	$ hg book
-	 * a                         3:2167fbc8ad05
-	   z                         0:8370c3f4bb68
-
-	$ hg stat
-	A c
-
-	$ hg diff
-	diff --git a/c b/c
-	new file mode 100644
-	--- /dev/null
-	+++ b/c
-	@@ -0,0 +1,1 @@
-	+C
-

File tut/src/simple.rextile

 h3. TODO
 
 Single user
+* Introduce roots managed by amend and pull
 * Amend multiple times in a row, then evolve
 * Pull changes up (move some diffs into parent csets)
 * Push changes down (move some diffs into child csets and back them out of the parents)
 
 h3. Rebase The Later Patches
 
-The later patches _patchB_ and _patchC_ are now based on a replaced version of _patchA_. This is not fixed until we actually update to them. To make it easier to follow, we'll do it step by step:
+The later patches _patchB_ and _patchC_ are now based on a replaced version of _patchA_. This is not fixed until we actually update to them. To make it easier to follow, we'll do it one patch at a time. I also clone the repo so we can see what happens when do both in one go:
+
+	$ hg clone . ../evolve-in-one-go
+	updating to branch default
+	3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 h5. hg update
 
 
 	$ hg evolve
 	rebasing rev 4 onto 7
+	updating to rev 4
 	0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	merging with rev 6
 	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 	(branch merge, don't forget to commit)
+	committing new version
+	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+The result is:
+
+	$ hg glog -r 'visible()'
+	@  9	patchB: another patch - john
+	|
+	o  7	patchA: a nifty patch - john
+	|
+	| o  5	patchC: yet another patch - john
+	|/
+	o  0	: base - john
+
+With full details:
+
+	$ hg glog # --hidden
+	@  9	patchB: another patch - john
+	|
+	| o    8	: rebasing c61663dae6ca5e66751b16255a515eef55532954 on f1cb1a5426300904c5373a775343697e3b28d0ed - john
+	| |\
+	o | |  7	patchA: a nifty patch - john
+	| | |
+	| | o  6	: second try in A - john
+	| | |
+	| +---o  5	patchC: yet another patch - john
+	| | |
+	| o |  4	: another patch - john
+	| |/
+	| | o  3	: fix spelling of Zwei - john
+	| | |
+	| | o  2	: another patch - john
+	| |/
+	| o  1	: a nifty patch - john
+	|/
+	o  0	: base - john
+
+And the patch data is:
+
+	$ hg log --patch --rev patchB
+	9	patchB: another patch - john
+	diff --git a/file-from-B b/file-from-B
+	new file mode 100644
+	--- /dev/null
+	+++ b/file-from-B
+	@@ -0,0 +1,1 @@
+	+Two
+	diff --git a/main-file-1 b/main-file-1
+	--- a/main-file-1
+	+++ b/main-file-1
+	@@ -3,1 +3,1 @@
+	-Two
+	+Zwei
+
+The same happens if we now update to patchC:
+
+	$ hg update patchC
+	3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	WARNING: rev 5 needs rebase on rev 9, which replaces rev 4
+	use 'hg evolve'
+
+	$ hg evolve
 	rebasing rev 5 onto 9
-	3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	updating to rev 5
+	0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	merging with rev 8
 	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 	(branch merge, don't forget to commit)
+	committing new version
 	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-Here's the visible result:
-
 	$ hg glog -r 'visible()'
 	@  11	patchC: yet another patch - john
 	|
 	|
 	o  0	: base - john
 
-and the internal one:
+	$ hg glog # --hidden
+	@  11	patchC: yet another patch - john
+	|
+	| o    10	: rebasing f104d3a2a305f7513b0bd0e6bb430ce451da2739 on 219017dabfc267a50d727d09b347a784a8628663 - john
+	| |\
+	o | |  9	patchB: another patch - john
+	| | |
+	| | o    8	: rebasing c61663dae6ca5e66751b16255a515eef55532954 on f1cb1a5426300904c5373a775343697e3b28d0ed - john
+	| | |\
+	o | | |  7	patchA: a nifty patch - john
+	| | | |
+	| | | o  6	: second try in A - john
+	| | | |
+	| o | |  5	: yet another patch - john
+	| |/ /
+	| o /  4	: another patch - john
+	| |/
+	| | o  3	: fix spelling of Zwei - john
+	| | |
+	| | o  2	: another patch - john
+	| |/
+	| o  1	: a nifty patch - john
+	|/
+	o  0	: base - john
+
+	$ hg log --rev patchC --patch
+	11	patchC: yet another patch - john
+	diff --git a/file-from-C b/file-from-C
+	new file mode 100644
+	--- /dev/null
+	+++ b/file-from-C
+	@@ -0,0 +1,1 @@
+	+Three
+	diff --git a/main-file-1 b/main-file-1
+	--- a/main-file-1
+	+++ b/main-file-1
+	@@ -5,1 +5,1 @@
+	-Three
+	+Drei
+
+h4. Rebase All Patches In One Go
+
+Let's now switch to the repo I saved and test evolving all of the patches in one go:
+
+	$ cd ../evolve-in-one-go
+
+	$ hg update patchC
+	4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+	$ hg evolve
+	rebasing rev 4 onto 7
+	0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	(branch merge, don't forget to commit)
+	rebasing rev 5 onto 9
+	3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+	(branch merge, don't forget to commit)
+	1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Again, the details:
 
 	$ hg glog
 	@  11	patchC: yet another patch - john
 	| | | |
 	| | | o  6	: second try in A - john
 	| | | |
-	| o | |  5	patchC: yet another patch - john
+	| o | |  5	: yet another patch - john
 	| |/ /
 	| o /  4	: another patch - john
 	| |/
 	|/
 	o  0	: base - john
 
- 
+And to verify the patch data:
+
+	$ hg log --patch
+	11	patchC: yet another patch - john
+	diff --git a/file-from-C b/file-from-C
+	new file mode 100644
+	--- /dev/null
+	+++ b/file-from-C
+	@@ -0,0 +1,1 @@
+	+Three
+	diff --git a/main-file-1 b/main-file-1
+	--- a/main-file-1
+	+++ b/main-file-1
+	@@ -5,1 +5,1 @@
+	-Three
+	+Drei
+	_
+	9	patchB: another patch - john
+	diff --git a/file-from-B b/file-from-B
+	new file mode 100644
+	--- /dev/null
+	+++ b/file-from-B
+	@@ -0,0 +1,1 @@
+	+Two
+	diff --git a/main-file-1 b/main-file-1
+	--- a/main-file-1
+	+++ b/main-file-1
+	@@ -3,1 +3,1 @@
+	-Two
+	+Zwei
+	_
+	7	patchA: a nifty patch - john
+	diff --git a/file-from-A b/file-from-A
+	new file mode 100644
+	--- /dev/null
+	+++ b/file-from-A
+	@@ -0,0 +1,2 @@
+	+One
+	+Later
+	diff --git a/main-file-1 b/main-file-1
+	--- a/main-file-1
+	+++ b/main-file-1
+	@@ -1,1 +1,1 @@
+	-One
+	+Eins
+	_
+	0	: base - john
+	diff --git a/main-file-1 b/main-file-1
+	new file mode 100644
+	--- /dev/null
+	+++ b/main-file-1
+	@@ -0,0 +1,5 @@
+	+One
+	+
+	+Two
+	+
+	+Three
+	diff --git a/main-file-2 b/main-file-2
+	new file mode 100644
+	--- /dev/null
+	+++ b/main-file-2
+	@@ -0,0 +1,1 @@
+	+Two
+
+All good, so let's switch back:
+
+	$ cd ../work
+
+