D3187: phase: add dry-run functionality
khanchi97 (Sushil khanchi)
phabricator at mercurial-scm.org
Fri May 25 16:34:20 EDT 2018
khanchi97 updated this revision to Diff 8896.
khanchi97 edited the summary of this revision.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D3187?vs=7877&id=8896
REVISION DETAIL
https://phab.mercurial-scm.org/D3187
AFFECTED FILES
mercurial/commands.py
mercurial/phases.py
tests/test-completion.t
tests/test-phases.t
CHANGE DETAILS
diff --git a/tests/test-phases.t b/tests/test-phases.t
--- a/tests/test-phases.t
+++ b/tests/test-phases.t
@@ -826,3 +826,107 @@
rollback completed
abort: pretxnclose-phase.nopublish_D hook exited with status 1
[255]
+
+Test dry-run functionality
+
+ $ hg init dryrunrepo
+ $ cd dryrunrepo
+ $ echo a > a
+ $ hg ci -qAm 0
+ test-debug-phase: new rev 0: x -> 1
+ test-hook-close-phase: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac: -> draft
+ $ echo b > b
+ $ hg ci -qAm 1
+ test-debug-phase: new rev 1: x -> 1
+ test-hook-close-phase: 925d80f479bb026b0fb3deb27503780b13f74123: -> draft
+ $ echo c > c
+ $ hg ci -qAm 2
+ test-debug-phase: new rev 2: x -> 1
+ test-hook-close-phase: 0316ce92851d493393d2181900388caa05d256c3: -> draft
+ $ echo d > d
+ $ hg ci -qAm 3
+ test-debug-phase: new rev 3: x -> 1
+ test-hook-close-phase: 14b465a7e25bf201e963c055be0e780414cff648: -> draft
+ $ echo e > e
+ $ hg ci -qAm 4
+ test-debug-phase: new rev 4: x -> 1
+ test-hook-close-phase: b385d13d5ed4ceb2b67ced172470734a60187cd1: -> draft
+ $ echo f > f
+ $ hg ci -qAm 5
+ test-debug-phase: new rev 5: x -> 1
+ test-hook-close-phase: fdc0253c25cfd67fe42b7be81e3abc9f92bebbd5: -> draft
+ $ hg up 3 -q
+ $ echo g > g
+ $ hg ci -qAm 6
+ test-debug-phase: new rev 6: x -> 1
+ test-hook-close-phase: f19b7f89f44eee9ffe34ba58b4e4ee3b3cea1f34: -> draft
+ $ echo h > h
+ $ hg ci -qAm 7
+ test-debug-phase: new rev 7: x -> 1
+ test-hook-close-phase: 4ccc844d545402eb0f39cd294227cd38de3ece20: -> draft
+
+
+ $ hg phase --public 1
+ test-debug-phase: move rev 0: 1 -> 0
+ test-debug-phase: move rev 1: 1 -> 0
+ test-hook-close-phase: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac: draft -> public
+ test-hook-close-phase: 925d80f479bb026b0fb3deb27503780b13f74123: draft -> public
+ $ hg phase --secret 4 --force
+ test-debug-phase: move rev 2: 1 -> 2
+ test-debug-phase: move rev 3: 1 -> 2
+ test-debug-phase: move rev 4: 1 -> 2
+ test-debug-phase: move rev 5: 1 -> 2
+ test-hook-close-phase: 0316ce92851d493393d2181900388caa05d256c3: draft -> secret
+ test-hook-close-phase: 14b465a7e25bf201e963c055be0e780414cff648: draft -> secret
+ test-hook-close-phase: b385d13d5ed4ceb2b67ced172470734a60187cd1: draft -> secret
+ test-hook-close-phase: fdc0253c25cfd67fe42b7be81e3abc9f92bebbd5: draft -> secret
+
+ $ hg log -G -T "{rev} : {node | short} : {phase}"
+ @ 7 : 4ccc844d5454 : draft
+ |
+ o 6 : f19b7f89f44e : draft
+ |
+ | o 5 : fdc0253c25cf : secret
+ | |
+ | o 4 : b385d13d5ed4 : secret
+ |/
+ o 3 : 14b465a7e25b : draft
+ |
+ o 2 : 0316ce92851d : draft
+ |
+ o 1 : 925d80f479bb : public
+ |
+ o 0 : f7b1eb17ad24 : public
+
+ $ hg phase --secret --force 1 -n
+ 925d80f479bb 1 public -> secret
+ 0316ce92851d 2 draft -> secret
+ 14b465a7e25b 3 draft -> secret
+ f19b7f89f44e 6 draft -> secret
+ 4ccc844d5454 7 draft -> secret
+
+ $ hg phase --public 5 7 -n
+ 0316ce92851d 2 draft -> public
+ 14b465a7e25b 3 draft -> public
+ f19b7f89f44e 6 draft -> public
+ 4ccc844d5454 7 draft -> public
+ b385d13d5ed4 4 secret -> public
+ fdc0253c25cf 5 secret -> public
+
+ $ hg log -G -T "{rev} : {node | short} : {phase}"
+ @ 7 : 4ccc844d5454 : draft
+ |
+ o 6 : f19b7f89f44e : draft
+ |
+ | o 5 : fdc0253c25cf : secret
+ | |
+ | o 4 : b385d13d5ed4 : secret
+ |/
+ o 3 : 14b465a7e25b : draft
+ |
+ o 2 : 0316ce92851d : draft
+ |
+ o 1 : 925d80f479bb : public
+ |
+ o 0 : f7b1eb17ad24 : public
+
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -323,7 +323,7 @@
outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
parents: rev, style, template
paths: template
- phase: public, draft, secret, force, rev
+ phase: public, draft, secret, force, rev, dry-run
recover:
rename: after, force, include, exclude, dry-run
resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
diff --git a/mercurial/phases.py b/mercurial/phases.py
--- a/mercurial/phases.py
+++ b/mercurial/phases.py
@@ -352,7 +352,7 @@
_trackphasechange(phasetracking, rev, None, revphase)
repo.invalidatevolatilesets()
- def advanceboundary(self, repo, tr, targetphase, nodes):
+ def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None):
"""Set all 'nodes' to phase 'targetphase'
Nodes with a phase lower than 'targetphase' are not affected.
@@ -366,6 +366,13 @@
repo = repo.unfiltered()
+ rejected = list()
+ changes = [set(), set(), set()]
+ if dryrun:
+ getphase = repo._phasecache.phase
+ rejected = [repo[n].rev() for n in nodes
+ if getphase(repo, repo[n].rev()) < targetphase]
+
delroots = [] # set of root deleted by this path
for phase in xrange(targetphase + 1, len(allphases)):
# filter nodes that are not in a compatible phase already
@@ -377,47 +384,68 @@
olds = self.phaseroots[phase]
affected = repo.revs('%ln::%ln', olds, nodes)
- for r in affected:
- _trackphasechange(phasetracking, r, self.phase(repo, r),
- targetphase)
+ if dryrun:
+ faffected = filter(lambda x: getphase(repo,
+ repo[x].rev()) == phase,
+ affected)
+ changes[phase].update(faffected)
+ else:
+ for r in affected:
+ _trackphasechange(phasetracking, r, self.phase(repo, r),
+ targetphase)
- roots = set(ctx.node() for ctx in repo.set(
- 'roots((%ln::) - %ld)', olds, affected))
- if olds != roots:
- self._updateroots(phase, roots, tr)
- # some roots may need to be declared for lower phases
- delroots.extend(olds - roots)
- # declare deleted root in the target phase
- if targetphase != 0:
- self._retractboundary(repo, tr, targetphase, delroots)
- repo.invalidatevolatilesets()
+ roots = set(ctx.node() for ctx in repo.set(
+ 'roots((%ln::) - %ld)', olds, affected))
+ if olds != roots:
+ self._updateroots(phase, roots, tr)
+ # some roots may need to be declared for lower phases
+ delroots.extend(olds - roots)
+ if not dryrun:
+ # declare deleted root in the target phase
+ if targetphase != 0:
+ self._retractboundary(repo, tr, targetphase, delroots)
+ repo.invalidatevolatilesets()
+ return rejected, changes
- def retractboundary(self, repo, tr, targetphase, nodes):
+ def retractboundary(self, repo, tr, targetphase, nodes, dryrun=None):
oldroots = self.phaseroots[:targetphase + 1]
if tr is None:
phasetracking = None
else:
phasetracking = tr.changes.get('phases')
repo = repo.unfiltered()
- if (self._retractboundary(repo, tr, targetphase, nodes)
- and phasetracking is not None):
-
- # find the affected revisions
- new = self.phaseroots[targetphase]
- old = oldroots[targetphase]
- affected = set(repo.revs('(%ln::) - (%ln::)', new, old))
+ changes = [set(), set(), set()]
+ if dryrun:
+ getphase = repo._phasecache.phase
+ nds = [n for n in nodes
+ if getphase(repo, repo[n].rev()) < targetphase]
+ targetphroots = oldroots[-1]
+ affected = set(repo.revs('(%ln::) - (%ln::)', nds,
+ targetphroots))
+ for rev in affected:
+ revphase = getphase(repo, rev)
+ changes[revphase].update((rev,))
+ else:
+ if (self._retractboundary(repo, tr, targetphase, nodes)
+ and phasetracking is not None):
- # find the phase of the affected revision
- for phase in xrange(targetphase, -1, -1):
- if phase:
- roots = oldroots[phase]
- revs = set(repo.revs('%ln::%ld', roots, affected))
- affected -= revs
- else: # public phase
- revs = affected
- for r in revs:
- _trackphasechange(phasetracking, r, phase, targetphase)
- repo.invalidatevolatilesets()
+ # find the affected revisions
+ new = self.phaseroots[targetphase]
+ old = oldroots[targetphase]
+ affected = set(repo.revs('(%ln::) - (%ln::)', new, old))
+
+ # find the phase of the affected revision
+ for phase in xrange(targetphase, -1, -1):
+ if phase:
+ roots = oldroots[phase]
+ revs = set(repo.revs('%ln::%ld', roots, affected))
+ affected -= revs
+ else: # public phase
+ revs = affected
+ for r in revs:
+ _trackphasechange(phasetracking, r, phase, targetphase)
+ repo.invalidatevolatilesets()
+ return changes
def _retractboundary(self, repo, tr, targetphase, nodes):
# Be careful to preserve shallow-copied values: do not update
@@ -478,28 +506,34 @@
# (see branchmap one)
self.invalidate()
-def advanceboundary(repo, tr, targetphase, nodes):
+def advanceboundary(repo, tr, targetphase, nodes, dryrun=None):
"""Add nodes to a phase changing other nodes phases if necessary.
This function move boundary *forward* this means that all nodes
are set in the target phase or kept in a *lower* phase.
Simplify boundary to contains phase roots only."""
phcache = repo._phasecache.copy()
- phcache.advanceboundary(repo, tr, targetphase, nodes)
- repo._phasecache.replace(phcache)
+ rejected, changes = phcache.advanceboundary(repo, tr, targetphase, nodes,
+ dryrun=dryrun)
+ if not dryrun:
+ repo._phasecache.replace(phcache)
+ return rejected, changes
-def retractboundary(repo, tr, targetphase, nodes):
+def retractboundary(repo, tr, targetphase, nodes, dryrun=None):
"""Set nodes back to a phase changing other nodes phases if
necessary.
This function move boundary *backward* this means that all nodes
are set in the target phase or kept in a *higher* phase.
Simplify boundary to contains phase roots only."""
phcache = repo._phasecache.copy()
- phcache.retractboundary(repo, tr, targetphase, nodes)
- repo._phasecache.replace(phcache)
+ changes = phcache.retractboundary(repo, tr, targetphase, nodes,
+ dryrun=dryrun)
+ if not dryrun:
+ repo._phasecache.replace(phcache)
+ return changes
def registernew(repo, tr, targetphase, nodes):
"""register a new revision and its phase
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -3828,7 +3828,7 @@
('s', 'secret', False, _('set changeset phase to secret')),
('f', 'force', False, _('allow to move boundary backward')),
('r', 'rev', [], _('target revision'), _('REV')),
- ],
+ ] + dryrunopts,
_('[-p|-d|-s] [-f] [-r] [REV...]'))
def phase(ui, repo, *revs, **opts):
"""set or show the current phase name
@@ -3848,6 +3848,7 @@
(For more information about the phases concept, see :hg:`help phases`.)
"""
opts = pycompat.byteskwargs(opts)
+ dryrun = opts.get('dry_run')
# search for a unique phase argument
targetphase = None
for idx, name in enumerate(phases.phasenames):
@@ -3868,6 +3869,8 @@
ret = 0
if targetphase is None:
+ if dryrun:
+ raise error.Abort(_("cannot use --dry-run without target phase"))
# display
for r in revs:
ctx = repo[r]
@@ -3883,27 +3886,48 @@
unfi = repo.unfiltered()
getphase = unfi._phasecache.phase
olddata = [getphase(unfi, r) for r in unfi]
- phases.advanceboundary(repo, tr, targetphase, nodes)
+ rejected, advanced = phases.advanceboundary(repo, tr, targetphase,
+ nodes, dryrun=dryrun)
+ if opts['force']:
+ retracted = phases.retractboundary(repo, tr, targetphase,
+ nodes, dryrun=dryrun)
+ if dryrun:
+ changes = advanced[:]
if opts['force']:
- phases.retractboundary(repo, tr, targetphase, nodes)
- getphase = unfi._phasecache.phase
- newdata = [getphase(unfi, r) for r in unfi]
- changes = sum(newdata[r] != olddata[r] for r in unfi)
- cl = unfi.changelog
- rejected = [n for n in nodes
- if newdata[cl.rev(n)] < targetphase]
- if rejected:
- ui.warn(_('cannot move %i changesets to a higher '
- 'phase, use --force\n') % len(rejected))
- ret = 1
- if changes:
- msg = _('phase changed for %i changesets\n') % changes
- if ret:
- ui.status(msg)
+ for phase in xrange(3):
+ changes[phase] = changes[phase].union(retracted[phase])
+ rejected = []
+ if rejected:
+ ui.warn(_('cannot move %i changesets to a higher '
+ 'phase, use --force\n') % len(rejected))
+
+ allphases = ['public', 'draft', 'secret']
+ for phase in xrange(len(allphases)):
+ if changes[phase]:
+ for rev in changes[phase]:
+ ui.status(_('{} {} {} -> {}\n'.format(
+ repo[rev].hex()[:12], rev, allphases[phase],
+ allphases[targetphase])))
+
+ else:
+ getphase = unfi._phasecache.phase
+ newdata = [getphase(unfi, r) for r in unfi]
+ changes = sum(newdata[r] != olddata[r] for r in unfi)
+ cl = unfi.changelog
+ rejected = [n for n in nodes
+ if newdata[cl.rev(n)] < targetphase]
+ if rejected:
+ ui.warn(_('cannot move %i changesets to a higher '
+ 'phase, use --force\n') % len(rejected))
+ ret = 1
+ if changes:
+ msg = _('phase changed for %i changesets\n') % changes
+ if ret:
+ ui.status(msg)
+ else:
+ ui.note(msg)
else:
- ui.note(msg)
- else:
- ui.warn(_('no phases changed\n'))
+ ui.warn(_('no phases changed\n'))
return ret
def postincoming(ui, repo, modheads, optupdate, checkout, brev):
To: khanchi97, pulkit, #hg-reviewers
Cc: av6, mercurial-devel
More information about the Mercurial-devel
mailing list