comparison mercurial/subrepo.py @ 20176:4c96c50ef937

subrepo: check phase of state in each subrepositories before committing Before this patch, phase of newly created commit is determined by "phases.new-commit" configuration regardless of phase of state in each subrepositories. For example, this may cause the "public" revision in the parent repository referring the "secret" one in subrepository. This patch checks phase of state in each subrepositories before committing in the parent, and aborts or changes phase of newly created commit if subrepositories have more restricted phase than the parent. This patch uses "follow" as default value of "phases.checksubrepos" configuration, because it can keep consistency between phases of the parent and subrepositories without breaking existing tool chains.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Wed, 13 Nov 2013 15:55:30 +0900
parents af12f58e2aa0
children d6939f29b3b3
comparison
equal deleted inserted replaced
20175:5ff0fd023850 20176:4c96c50ef937
8 import errno, os, re, shutil, posixpath, sys 8 import errno, os, re, shutil, posixpath, sys
9 import xml.dom.minidom 9 import xml.dom.minidom
10 import stat, subprocess, tarfile 10 import stat, subprocess, tarfile
11 from i18n import _ 11 from i18n import _
12 import config, util, node, error, cmdutil, bookmarks, match as matchmod 12 import config, util, node, error, cmdutil, bookmarks, match as matchmod
13 import phases
13 import pathutil 14 import pathutil
14 hg = None 15 hg = None
15 propertycache = util.propertycache 16 propertycache = util.propertycache
16 17
17 nullstate = ('', '', 'empty') 18 nullstate = ('', '', 'empty')
349 state = ctx.substate[path] 350 state = ctx.substate[path]
350 if state[2] not in types: 351 if state[2] not in types:
351 raise util.Abort(_('unknown subrepo type %s') % state[2]) 352 raise util.Abort(_('unknown subrepo type %s') % state[2])
352 return types[state[2]](ctx, path, state[:2]) 353 return types[state[2]](ctx, path, state[:2])
353 354
355 def newcommitphase(ui, ctx):
356 commitphase = phases.newcommitphase(ui)
357 substate = getattr(ctx, "substate", None)
358 if not substate:
359 return commitphase
360 check = ui.config('phases', 'checksubrepos', 'follow')
361 if check not in ('ignore', 'follow', 'abort'):
362 raise util.Abort(_('invalid phases.checksubrepos configuration: %s')
363 % (check))
364 if check == 'ignore':
365 return commitphase
366 maxphase = phases.public
367 maxsub = None
368 for s in sorted(substate):
369 sub = ctx.sub(s)
370 subphase = sub.phase(substate[s][1])
371 if maxphase < subphase:
372 maxphase = subphase
373 maxsub = s
374 if commitphase < maxphase:
375 if check == 'abort':
376 raise util.Abort(_("can't commit in %s phase"
377 " conflicting %s from subrepository %s") %
378 (phases.phasenames[commitphase],
379 phases.phasenames[maxphase], maxsub))
380 ui.warn(_("warning: changes are committed in"
381 " %s phase from subrepository %s\n") %
382 (phases.phasenames[maxphase], maxsub))
383 return maxphase
384 return commitphase
385
354 # subrepo classes need to implement the following abstract class: 386 # subrepo classes need to implement the following abstract class:
355 387
356 class abstractsubrepo(object): 388 class abstractsubrepo(object):
357 389
358 def storeclean(self, path): 390 def storeclean(self, path):
382 """commit the current changes to the subrepo with the given 414 """commit the current changes to the subrepo with the given
383 log message. Use given user and date if possible. Return the 415 log message. Use given user and date if possible. Return the
384 new state of the subrepo. 416 new state of the subrepo.
385 """ 417 """
386 raise NotImplementedError 418 raise NotImplementedError
419
420 def phase(self, state):
421 """returns phase of specified state in the subrepository.
422 """
423 return phases.public
387 424
388 def remove(self): 425 def remove(self):
389 """remove the subrepo 426 """remove the subrepo
390 427
391 (should verify the dirstate is not dirty first) 428 (should verify the dirstate is not dirty first)
648 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self)) 685 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
649 n = self._repo.commit(text, user, date) 686 n = self._repo.commit(text, user, date)
650 if not n: 687 if not n:
651 return self._repo['.'].hex() # different version checked out 688 return self._repo['.'].hex() # different version checked out
652 return node.hex(n) 689 return node.hex(n)
690
691 @annotatesubrepoerror
692 def phase(self, state):
693 return self._repo[state].phase()
653 694
654 @annotatesubrepoerror 695 @annotatesubrepoerror
655 def remove(self): 696 def remove(self):
656 # we can't fully delete the repository as it may contain 697 # we can't fully delete the repository as it may contain
657 # local-only history 698 # local-only history