diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt --- a/mercurial/help/config.txt +++ b/mercurial/help/config.txt @@ -945,6 +945,15 @@ information about working with phases. Phase of newly-created commits. Default: draft +``checksubrepos`` + + Check phase of state in each subrepositories, allowed values are + "ignore", "follow" or "abort". For settings other than "ignore", + the phase of each subrepository commit is checked before committing + in the parent repository. If there is any greater phase than the parent + ("secret" vs "draft", for example), the commit is either aborted + with "abort" or the higher phase is used with "follow". Default: "follow". + ``profiling`` ------------- diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1382,7 +1382,7 @@ class localrepository(object): parent2=xp2, pending=p) self.changelog.finalize(trp) # set the new commit is proper phase - targetphase = phases.newcommitphase(self.ui) + targetphase = subrepo.newcommitphase(self.ui, ctx) if targetphase: # retract boundary do not alter parent changeset. # if a parent have higher the resulting phase will diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py --- a/mercurial/subrepo.py +++ b/mercurial/subrepo.py @@ -10,6 +10,7 @@ import xml.dom.minidom import stat, subprocess, tarfile from i18n import _ import config, util, node, error, cmdutil, bookmarks, match as matchmod +import phases import pathutil hg = None propertycache = util.propertycache @@ -351,6 +352,37 @@ def subrepo(ctx, path): raise util.Abort(_('unknown subrepo type %s') % state[2]) return types[state[2]](ctx, path, state[:2]) +def newcommitphase(ui, ctx): + commitphase = phases.newcommitphase(ui) + substate = getattr(ctx, "substate", None) + if not substate: + return commitphase + check = ui.config('phases', 'checksubrepos', 'follow') + if check not in ('ignore', 'follow', 'abort'): + raise util.Abort(_('invalid phases.checksubrepos configuration: %s') + % (check)) + if check == 'ignore': + return commitphase + maxphase = phases.public + maxsub = None + for s in sorted(substate): + sub = ctx.sub(s) + subphase = sub.phase(substate[s][1]) + if maxphase < subphase: + maxphase = subphase + maxsub = s + if commitphase < maxphase: + if check == 'abort': + raise util.Abort(_("can't commit in %s phase" + " conflicting %s from subrepository %s") % + (phases.phasenames[commitphase], + phases.phasenames[maxphase], maxsub)) + ui.warn(_("warning: changes are committed in" + " %s phase from subrepository %s\n") % + (phases.phasenames[maxphase], maxsub)) + return maxphase + return commitphase + # subrepo classes need to implement the following abstract class: class abstractsubrepo(object): @@ -385,6 +417,11 @@ class abstractsubrepo(object): """ raise NotImplementedError + def phase(self, state): + """returns phase of specified state in the subrepository. + """ + return phases.public + def remove(self): """remove the subrepo @@ -652,6 +689,10 @@ class hgsubrepo(abstractsubrepo): return node.hex(n) @annotatesubrepoerror + def phase(self, state): + return self._repo[state].phase() + + @annotatesubrepoerror def remove(self): # we can't fully delete the repository as it may contain # local-only history diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t --- a/tests/test-subrepo.t +++ b/tests/test-subrepo.t @@ -1232,4 +1232,69 @@ Courtesy phases synchronisation to publi searching for changes no changes found [1] + $ cd .. +Test phase choice for newly created commit with "phases.subrepochecks" +configuration + + $ cd t + $ hg update -q -r 12 + + $ cat >> s/ss/.hg/hgrc < [phases] + > new-commit = secret + > EOF + $ cat >> s/.hg/hgrc < [phases] + > new-commit = draft + > EOF + $ echo phasecheck1 >> s/ss/a + $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1 + committing subrepository ss + transaction abort! + rollback completed + abort: can't commit in draft phase conflicting secret from subrepository ss + [255] + $ echo phasecheck2 >> s/ss/a + $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2 + committing subrepository ss + $ hg -R s/ss phase tip + 3: secret + $ hg -R s phase tip + 6: draft + $ echo phasecheck3 >> s/ss/a + $ hg -R s commit -S -m phasecheck3 + committing subrepository ss + warning: changes are committed in secret phase from subrepository ss + $ hg -R s/ss phase tip + 4: secret + $ hg -R s phase tip + 7: secret + + $ cat >> t/.hg/hgrc < [phases] + > new-commit = draft + > EOF + $ cat >> .hg/hgrc < [phases] + > new-commit = public + > EOF + $ echo phasecheck4 >> s/ss/a + $ echo phasecheck4 >> t/t + $ hg commit -S -m phasecheck4 + committing subrepository s + committing subrepository s/ss + warning: changes are committed in secret phase from subrepository ss + committing subrepository t + warning: changes are committed in secret phase from subrepository s + created new head + $ hg -R s/ss phase tip + 5: secret + $ hg -R s phase tip + 8: secret + $ hg -R t phase tip + 6: draft + $ hg phase tip + 15: secret + + $ cd ..