bundlerepo: apply phase data stored in the bundle instead of assuming `draft`
The phase information contained in the changegroup part and the explicit
`phase-heads` part are now taken in account.
Initial changes and test by Matt Harbison, code rework by Pierre-Yves David.
--- a/mercurial/bundlerepo.py Thu Feb 23 19:07:58 2023 +0100
+++ b/mercurial/bundlerepo.py Thu Feb 23 23:05:51 2023 +0100
@@ -302,7 +302,9 @@
cgpart = None
for part in bundle.iterparts(seekable=True):
- if part.type == b'changegroup':
+ if part.type == b'phase-heads':
+ self._handle_bundle2_phase_part(bundle, part)
+ elif part.type == b'changegroup':
if cgpart:
raise NotImplementedError(
b"can't process multiple changegroups"
@@ -346,6 +348,17 @@
def _handle_bundle2_cg_part(self, bundle, part):
assert part.type == b'changegroup'
cgstream = part
+ targetphase = part.params.get(b'targetphase')
+ try:
+ targetphase = int(targetphase)
+ except TypeError:
+ pass
+ if targetphase is None:
+ targetphase = phases.draft
+ if targetphase not in phases.allphases:
+ m = _(b'unsupported targetphase: %d')
+ m %= targetphase
+ raise error.Abort(m)
version = part.params.get(b'version', b'01')
legalcgvers = changegroup.supportedincomingversions(self)
if version not in legalcgvers:
@@ -360,10 +373,17 @@
phases.retractboundary(
self,
None,
- phases.draft,
+ targetphase,
[ctx.node() for ctx in self[self.firstnewrev :]],
)
+ def _handle_bundle2_phase_part(self, bundle, part):
+ assert part.type == b'phase-heads'
+
+ unfi = self.unfiltered()
+ headsbyphase = phases.binarydecode(part)
+ phases.updatephases(unfi, lambda: None, headsbyphase)
+
def _writetempbundle(self, readfn, suffix, header=b''):
"""Write a temporary file to disk"""
fdtemp, temp = self.vfs.mkstemp(prefix=b"hg-bundle-", suffix=suffix)
--- a/tests/test-bundle-phases.t Thu Feb 23 19:07:58 2023 +0100
+++ b/tests/test-bundle-phases.t Thu Feb 23 23:05:51 2023 +0100
@@ -33,7 +33,6 @@
|
o A public
-Phases are restored when unbundling
$ hg bundle --base B -r E bundle
3 changesets found
$ hg debugbundle bundle
@@ -46,6 +45,57 @@
phase-heads -- {} (mandatory: True)
26805aba1e600a82e93661149f2313866a221a7b draft
$ hg strip --no-backup C
+
+Phases show on incoming, and are also restored when pulling. Secret commits
+aren't incoming or pulled, following usual incoming/pull semantics.
+
+ $ hg log -R bundle -r 'bundle()^+bundle()' -G -T '{desc} {phase}\n'
+ o E secret
+ |
+ o D secret
+ |
+ o C draft
+ |
+ o B draft
+ |
+ ~
+
+ $ hg incoming bundle -G -T '{desc} {phase}\n'
+ comparing with bundle
+ searching for changes
+ o C draft
+
+ $ hg pull bundle
+ pulling from bundle
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ new changesets 26805aba1e60 (1 drafts)
+ (run 'hg update' to get a working copy)
+ $ hg log -G -T '{desc} {phase}\n'
+ o C draft
+ |
+ o B draft
+ |
+ o A public
+
+ $ hg log -R bundle -r 'bundle()^+bundle()' -G -T '{desc} {phase}\n'
+ o E secret
+ |
+ o D secret
+ |
+ o C draft
+ |
+ o B draft
+ |
+ ~
+
+ $ hg rollback --config ui.rollback=1
+ repository tip rolled back to revision 1 (undo pull)
+
+Phases are restored when unbundling
$ hg unbundle -q bundle
$ rm bundle
$ hg log -G -T '{desc} {phase}\n'
@@ -64,7 +114,27 @@
5 changesets found
$ hg strip --no-backup A
$ hg unbundle -q bundle
- $ rm bundle
+ $ hg log -G -T '{desc} {phase}\n'
+ o E secret
+ |
+ o D secret
+ |
+ o C draft
+ |
+ o B draft
+ |
+ o A public
+
+ $ hg init empty
+ $ hg -R empty pull bundle
+ pulling from bundle
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files
+ new changesets 426bada5c675:26805aba1e60 (2 drafts)
+ (run 'hg update' to get a working copy)
$ hg log -G -T '{desc} {phase}\n'
o E secret
|
@@ -76,8 +146,74 @@
|
o A public
+
+Public repo commits take precedence over phases in the bundle
+ $ hg phase --public E
+ $ hg incoming bundle -G -T '{desc} {phase}\n'
+ comparing with bundle
+ searching for changes
+ no changes found
+ $ hg log -R bundle -r 'bundle()^+bundle()' -G -T '{desc} {phase}\n'
+ o E public
+ |
+ o D public
+ |
+ o C public
+ |
+ o B public
+ |
+ o A public
+
+ $ hg pull bundle
+ pulling from bundle
+ searching for changes
+ no changes found
+ $ hg log -G -T '{desc} {phase}\n'
+ o E public
+ |
+ o D public
+ |
+ o C public
+ |
+ o B public
+ |
+ o A public
+
+ $ rm bundle
+
+A bundle with public phases that are not public in the repo will show as public
+with `hg log`, but will remain not public in the plain repo.
+
+ $ hg bundle --base B -r E bundle
+ 3 changesets found
+ $ hg phase --force --draft -r C
+
+ $ hg log -R bundle -G -T '{desc} {phase}\n'
+ o E public
+ |
+ o D public
+ |
+ o C public
+ |
+ o B public
+ |
+ o A public
+
+ $ hg log -G -T '{desc} {phase}\n'
+ o E draft
+ |
+ o D draft
+ |
+ o C draft
+ |
+ o B public
+ |
+ o A public
+
+ $ hg phase --public -r E
+ $ rm bundle
+
Completely public history can be restored
- $ hg phase --public E
$ hg bundle -a bundle
5 changesets found
$ hg strip --no-backup A