Mercurial > public > mercurial-scm > hg
diff mercurial/localrepo.py @ 16073:b254f827b7a6
subrepo: rewrite handling of subrepo state at commit (issue2403)
When the contents of .hgsubstate are stale (either because they've
manually been tweaked or partial updates have confused it), we get
confused about whether it actually needs committing.
So instead, we actively consult the parent's substate and compare it
the actual current state when deciding whether it needs committing.
Side effect: lots of "committing subrepo" messages that didn't
correspond with real commits disappear.
This change is fairly invasive for a fairly obscure condition, so it's
kept on the default branch.
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Mon, 06 Feb 2012 15:10:01 -0600 |
parents | 308406677e9d |
children | 004982e5d782 |
line wrap: on
line diff
--- a/mercurial/localrepo.py Mon Feb 06 15:00:08 2012 -0600 +++ b/mercurial/localrepo.py Mon Feb 06 15:10:01 2012 -0600 @@ -1096,37 +1096,58 @@ # check subrepos subs = [] - removedsubs = set() + commitsubs = set() + newstate = wctx.substate.copy() + # only manage subrepos and .hgsubstate if .hgsub is present if '.hgsub' in wctx: - # only manage subrepos and .hgsubstate if .hgsub is present + # we'll decide whether to track this ourselves, thanks + if '.hgsubstate' in changes[0]: + changes[0].remove('.hgsubstate') + if '.hgsubstate' in changes[2]: + changes[2].remove('.hgsubstate') + + # compare current state to last committed state + # build new substate based on last committed state + oldstate = wctx.p1().substate + for s in sorted(newstate.keys()): + if not match(s): + # ignore working copy, use old state if present + if s in oldstate: + newstate[s] = oldstate[s] + continue + if not force: + raise util.Abort( + _("commit with new subrepo %s excluded") % s) + if wctx.sub(s).dirty(True): + if not self.ui.configbool('ui', 'commitsubrepos'): + raise util.Abort( + _("uncommitted changes in subrepo %s") % s, + hint=_("use --subrepos for recursive commit")) + subs.append(s) + commitsubs.add(s) + else: + bs = wctx.sub(s).basestate() + newstate[s] = (newstate[s][0], bs, newstate[s][2]) + if oldstate.get(s, (None, None, None))[1] != bs: + subs.append(s) + + # check for removed subrepos for p in wctx.parents(): - removedsubs.update(s for s in p.substate if match(s)) - for s in wctx.substate: - removedsubs.discard(s) - if match(s) and wctx.sub(s).dirty(): - subs.append(s) - if (subs or removedsubs): + r = [s for s in p.substate if s not in newstate] + subs += [s for s in r if match(s)] + if subs: if (not match('.hgsub') and '.hgsub' in (wctx.modified() + wctx.added())): raise util.Abort( _("can't commit subrepos without .hgsub")) - if '.hgsubstate' not in changes[0]: - changes[0].insert(0, '.hgsubstate') - if '.hgsubstate' in changes[2]: - changes[2].remove('.hgsubstate') + changes[0].insert(0, '.hgsubstate') + elif '.hgsub' in changes[2]: # clean up .hgsubstate when .hgsub is removed if ('.hgsubstate' in wctx and '.hgsubstate' not in changes[0] + changes[1] + changes[2]): changes[2].insert(0, '.hgsubstate') - if subs and not self.ui.configbool('ui', 'commitsubrepos', False): - changedsubs = [s for s in subs if wctx.sub(s).dirty(True)] - if changedsubs: - raise util.Abort(_("uncommitted changes in subrepo %s") - % changedsubs[0], - hint=_("use --subrepos for recursive commit")) - # make sure all explicit patterns are matched if not force and match.files(): matched = set(changes[0] + changes[1] + changes[2]) @@ -1162,16 +1183,15 @@ cctx._text = editor(self, cctx, subs) edited = (text != cctx._text) - # commit subs - if subs or removedsubs: - state = wctx.substate.copy() - for s in sorted(subs): + # commit subs and write new state + if subs: + for s in sorted(commitsubs): sub = wctx.sub(s) self.ui.status(_('committing subrepository %s\n') % subrepo.subrelpath(sub)) sr = sub.commit(cctx._text, user, date) - state[s] = (state[s][0], sr) - subrepo.writestate(self, state) + newstate[s] = (newstate[s][0], sr) + subrepo.writestate(self, newstate) # Save commit message in case this transaction gets rolled back # (e.g. by a pretxncommit hook). Leave the content alone on