comparison mercurial/subrepo.py @ 13417:0748e18be470

subrepos: prompt on conflicts on update with dirty subrepos Consider a repository with a single subrepository. The changesets in the main repository reference the subrepository changesets like this: m0 -> s0 m1 -> s1 m2 -> s2 Starting from a state (m1, s0), doing 'hg update m2' in the main repository will yield a conflict: the subrepo is at revision s0 but the target revision says it should be at revision s2. Before this change, Mercurial would do (m1, s0) -> (m2, s2) and thus ignore the conflict between the working copy and the target revision. With this change, the user is prompted to resolve the conflict by choosing which revision he wants. This is consistent with 'hg merge', which also prompts the user when it detects conflicts in the merged .hgsubstate files. The prompt looks like this: $ hg update tip subrepository sources for my-subrepo differ use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)?
author Erik Zielke <ez@aragost.com>
date Wed, 09 Feb 2011 10:53:09 +0100
parents fa921dcd9993
children 5ef29e0dd418
comparison
equal deleted inserted replaced
13416:5431b3f3e52e 13417:0748e18be470
161 sm[s] = r 161 sm[s] = r
162 162
163 # record merged .hgsubstate 163 # record merged .hgsubstate
164 writestate(repo, sm) 164 writestate(repo, sm)
165 165
166 def _updateprompt(ui, sub, dirty, local, remote):
167 if dirty:
168 msg = (_(' subrepository sources for %s differ\n'
169 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
170 % (subrelpath(sub), local, remote))
171 else:
172 msg = (_(' subrepository sources for %s differ (in checked out version)\n'
173 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
174 % (subrelpath(sub), local, remote))
175 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
176
166 def reporelpath(repo): 177 def reporelpath(repo):
167 """return path to this (sub)repo as seen from outermost repo""" 178 """return path to this (sub)repo as seen from outermost repo"""
168 parent = repo 179 parent = repo
169 while hasattr(parent, '_subparent'): 180 while hasattr(parent, '_subparent'):
170 parent = parent._subparent 181 parent = parent._subparent
440 def merge(self, state): 451 def merge(self, state):
441 self._get(state) 452 self._get(state)
442 cur = self._repo['.'] 453 cur = self._repo['.']
443 dst = self._repo[state[1]] 454 dst = self._repo[state[1]]
444 anc = dst.ancestor(cur) 455 anc = dst.ancestor(cur)
445 if anc == cur: 456
446 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self)) 457 def mergefunc():
447 hg.update(self._repo, state[1]) 458 if anc == cur:
448 elif anc == dst: 459 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
449 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self)) 460 hg.update(self._repo, state[1])
461 elif anc == dst:
462 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
463 else:
464 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
465 hg.merge(self._repo, state[1], remind=False)
466
467 wctx = self._repo[None]
468 if self.dirty():
469 if anc != dst:
470 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
471 mergefunc()
472 else:
473 mergefunc()
450 else: 474 else:
451 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self)) 475 mergefunc()
452 hg.merge(self._repo, state[1], remind=False)
453 476
454 def push(self, force): 477 def push(self, force):
455 # push subrepos depth-first for coherent ordering 478 # push subrepos depth-first for coherent ordering
456 c = self._repo[''] 479 c = self._repo['']
457 subs = c.substate # only repos that are committed 480 subs = c.substate # only repos that are committed
606 if not re.search('Checked out revision [0-9]+.', status): 629 if not re.search('Checked out revision [0-9]+.', status):
607 raise util.Abort(status.splitlines()[-1]) 630 raise util.Abort(status.splitlines()[-1])
608 self._ui.status(status) 631 self._ui.status(status)
609 632
610 def merge(self, state): 633 def merge(self, state):
611 old = int(self._state[1]) 634 old = self._state[1]
612 new = int(state[1]) 635 new = state[1]
613 if new > old: 636 if new != self._wcrev():
614 self.get(state) 637 dirty = old == self._wcrev() or self._wcchanged()[0]
638 if _updateprompt(self._ui, self, dirty, self._wcrev(), new):
639 self.get(state, False)
615 640
616 def push(self, force): 641 def push(self, force):
617 # push is a no-op for SVN 642 # push is a no-op for SVN
618 return True 643 return True
619 644
848 873
849 def merge(self, state): 874 def merge(self, state):
850 source, revision, kind = state 875 source, revision, kind = state
851 self._fetch(source, revision) 876 self._fetch(source, revision)
852 base = self._gitcommand(['merge-base', revision, self._state[1]]) 877 base = self._gitcommand(['merge-base', revision, self._state[1]])
853 if base == revision: 878 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
854 self.get(state) # fast forward merge 879
855 elif base != self._state[1]: 880 def mergefunc():
856 self._gitcommand(['merge', '--no-commit', revision]) 881 if base == revision:
882 self.get(state) # fast forward merge
883 elif base != self._state[1]:
884 self._gitcommand(['merge', '--no-commit', revision])
885
886 if self.dirty():
887 if self._gitstate() != revision:
888 dirty = self._gitstate() == self._state[1] or code != 0
889 if _updateprompt(self._ui, self, dirty, self._state[1], revision):
890 mergefunc()
891 else:
892 mergefunc()
857 893
858 def push(self, force): 894 def push(self, force):
859 # if a branch in origin contains the revision, nothing to do 895 # if a branch in origin contains the revision, nothing to do
860 branch2rev, rev2branch = self._gitbranchmap() 896 branch2rev, rev2branch = self._gitbranchmap()
861 if self._state[1] in rev2branch: 897 if self._state[1] in rev2branch: