# HG changeset patch # User Matt Mackall # Date 1328562601 21600 # Node ID b254f827b7a67534962a68c9e22d4564231ddce0 # Parent bcb973abcc0b71817389c11d167beb9d0d83fce2 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. diff -r bcb973abcc0b -r b254f827b7a6 mercurial/localrepo.py --- 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 diff -r bcb973abcc0b -r b254f827b7a6 tests/test-check-code-hg.t --- a/tests/test-check-code-hg.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-check-code-hg.t Mon Feb 06 15:10:01 2012 -0600 @@ -474,9 +474,6 @@ > except: warning: naked except clause mercurial/localrepo.py:0: - > hint=_("use --subrepos for recursive commit")) - warning: line over 80 characters - mercurial/localrepo.py:0: > # we return an integer indicating remote head count change warning: line over 80 characters mercurial/localrepo.py:0: diff -r bcb973abcc0b -r b254f827b7a6 tests/test-mq-subrepo-svn.t --- a/tests/test-mq-subrepo-svn.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-mq-subrepo-svn.t Mon Feb 06 15:10:01 2012 -0600 @@ -37,7 +37,6 @@ $ hg status -S -X '**/format' A .hgsub $ hg qnew -m0 0.diff - committing subrepository sub $ cd sub $ echo a > a $ svn add a diff -r bcb973abcc0b -r b254f827b7a6 tests/test-mq-subrepo.t --- a/tests/test-mq-subrepo.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-mq-subrepo.t Mon Feb 06 15:10:01 2012 -0600 @@ -105,7 +105,6 @@ % update substate when adding .hgsub w/clean updated subrepo A .hgsub % qnew -m0 0.diff - committing subrepository sub path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31 @@ -121,7 +120,6 @@ % update substate when modifying .hgsub w/clean updated subrepo M .hgsub % qnew -m1 1.diff - committing subrepository sub2 path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31 @@ -166,7 +164,6 @@ % update substate when adding .hgsub w/clean updated subrepo A .hgsub % qrefresh - committing subrepository sub path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31 @@ -183,7 +180,6 @@ % update substate when modifying .hgsub w/clean updated subrepo M .hgsub % qrefresh - committing subrepository sub2 path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31 @@ -225,7 +221,6 @@ $ echo sub = sub > .hgsub $ hg add .hgsub $ hg qnew -m0 0.diff - committing subrepository sub $ hg debugsub path sub source sub @@ -277,7 +272,6 @@ diff --git a/.hgsub b/.hgsub new file mode 100644 examine changes to '.hgsub'? [Ynsfdaq?] - committing subrepository sub path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31 @@ -310,7 +304,6 @@ sub = sub +sub2 = sub2 record this change to '.hgsub'? [Ynsfdaq?] - committing subrepository sub2 path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31 @@ -360,4 +353,3 @@ $ echo sub = sub >> .hgsub $ hg add .hgsub $ hg qnew 0.diff - committing subrepository sub diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo-deep-nested-change.t --- a/tests/test-subrepo-deep-nested-change.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo-deep-nested-change.t Mon Feb 06 15:10:01 2012 -0600 @@ -18,7 +18,6 @@ adding sub1/.hgsub (glob) adding sub1/sub1 (glob) $ hg commit -R sub1 -m "sub1 import" - committing subrepository sub2 Preparing the 'main' repo which depends on the subrepo 'sub1' @@ -33,7 +32,6 @@ adding main/.hgsub (glob) adding main/main (glob) $ hg commit -R main -m "main import" - committing subrepository sub1 Cleaning both repositories, just as a clone -U diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo-git.t --- a/tests/test-subrepo-git.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo-git.t Mon Feb 06 15:10:01 2012 -0600 @@ -34,7 +34,6 @@ $ git clone -q ../gitroot s $ hg add .hgsub $ hg commit -m 'new git subrepo' - committing subrepository s $ hg debugsub path s source ../gitroot @@ -55,7 +54,6 @@ $ hg status --subrepos M s/g $ hg commit -m 'update git subrepo' - committing subrepository s $ hg debugsub path s source ../gitroot @@ -222,7 +220,6 @@ $ git pull -q >/dev/null 2>/dev/null $ cd .. $ hg commit -m 'git upstream sync' - committing subrepository s $ hg debugsub path s source ../gitroot @@ -287,7 +284,6 @@ $ echo inner = inner > .hgsub $ hg add .hgsub $ hg commit -m 'nested sub' - committing subrepository inner nested commit @@ -339,27 +335,32 @@ $ hg update 1 -q $ hg rm .hgsubstate $ hg commit .hgsubstate -m 'no substate' - created new head + nothing changed + [1] $ hg tag -l nosubstate $ hg manifest .hgsub + .hgsubstate a $ hg status -S + R .hgsubstate $ hg sum | grep commit - commit: 1 subrepos + commit: 1 removed, 1 subrepos (new branch head) $ hg commit -m 'restore substate' - committing subrepository s + nothing changed + [1] $ hg manifest .hgsub .hgsubstate a $ hg sum | grep commit - commit: (clean) + commit: 1 removed, 1 subrepos (new branch head) $ hg update -qC nosubstate $ ls s + g issue3109: false positives in git diff-index diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo-missing.t --- a/tests/test-subrepo-missing.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo-missing.t Mon Feb 06 15:10:01 2012 -0600 @@ -7,12 +7,10 @@ $ echo 'subrepo = subrepo' > .hgsub $ hg ci -Am addsubrepo adding .hgsub - committing subrepository subrepo $ echo b > subrepo/b $ hg -R subrepo ci -Am addb adding b $ hg ci -m updatedsub - committing subrepository subrepo delete .hgsub and revert it diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo-recursion.t --- a/tests/test-subrepo-recursion.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo-recursion.t Mon Feb 06 15:10:01 2012 -0600 @@ -79,11 +79,9 @@ $ cd .. $ hg commit -m 0-2-1 - committing subrepository bar $ cd .. $ hg commit -m 1-2-1 - committing subrepository foo Change working directory: diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo-relative-path.t --- a/tests/test-subrepo-relative-path.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo-relative-path.t Mon Feb 06 15:10:01 2012 -0600 @@ -20,7 +20,6 @@ adding main/.hgsub (glob) adding main/main (glob) $ hg commit -R main -m "main import" - committing subrepository sub Cleaning both repositories, just as a clone -U diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo-svn.t --- a/tests/test-subrepo-svn.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo-svn.t Mon Feb 06 15:10:01 2012 -0600 @@ -69,8 +69,6 @@ $ svn co --quiet "$SVNREPO"/src subdir/s $ hg add .hgsub $ hg ci -m1 - committing subrepository s - committing subrepository subdir/s make sure we avoid empty commits (issue2445) @@ -432,7 +430,6 @@ $ echo "s = [svn] $SVNREPO/src" >> .hgsub $ hg add .hgsub $ hg ci -m addsub - committing subrepository s $ echo a > a $ hg ci -Am adda adding a @@ -440,7 +437,6 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ svn up -qr6 s $ hg ci -m updatesub - committing subrepository s created new head $ echo pyc > s/dir/epsilon.pyc $ hg up 1 @@ -462,14 +458,12 @@ $ echo "obstruct = [svn] $SVNREPO/externals" >> .hgsub $ svn co -r5 --quiet "$SVNREPO"/externals obstruct $ hg commit -m 'Start making obstructed working copy' - committing subrepository obstruct $ hg book other $ hg co -r 'p1(tip)' 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo "obstruct = [svn] $SVNREPO/src" >> .hgsub $ svn co -r5 --quiet "$SVNREPO"/src obstruct $ hg commit -m 'Other branch which will be obstructed' - committing subrepository obstruct created new head Switching back to the head where we have another path mapped to the @@ -530,12 +524,10 @@ Checked out revision 10. $ echo "recreated = [svn] $SVNREPO/branch" >> .hgsub $ hg ci -m addsub - committing subrepository recreated $ cd recreated $ svn up -q $ cd .. $ hg ci -m updatesub - committing subrepository recreated $ hg up -r-2 D *recreated/somethingnew (glob) A *recreated/somethingold (glob) diff -r bcb973abcc0b -r b254f827b7a6 tests/test-subrepo.t --- a/tests/test-subrepo.t Mon Feb 06 15:00:08 2012 -0600 +++ b/tests/test-subrepo.t Mon Feb 06 15:10:01 2012 -0600 @@ -37,7 +37,6 @@ commit: 1 added, 1 subrepos update: (current) $ hg ci -m1 - committing subrepository s Revert can't (yet) revert subrepos: @@ -105,7 +104,6 @@ $ echo b > s/a $ hg -R s ci -ms1 $ hg --config ui.commitsubrepos=no ci -m3 - committing subrepository s leave sub dirty (and check ui.commitsubrepos=no aborts the commit) @@ -455,7 +453,6 @@ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg ci -Am1 adding .hgsub - committing subrepository s $ hg branch br marked working directory as branch br (branches are permanent and global, did you want a bookmark?) @@ -464,7 +461,6 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg ci -Am1 adding b - committing subrepository s $ hg up default 1 files updated, 0 files merged, 1 files removed, 0 files unresolved $ echo c > c @@ -483,7 +479,6 @@ $ echo d > d $ hg ci -Am1 adding d - committing subrepository s $ hg up 3 2 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg -R s up 5 @@ -491,7 +486,6 @@ $ echo e > e $ hg ci -Am1 adding e - committing subrepository s $ hg up 5 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -519,8 +513,6 @@ $ hg -R testdelete add adding testdelete/.hgsub (glob) $ hg -R testdelete ci -m "nested 1 & 2 added" - committing subrepository nested - committing subrepository nested2 $ echo nested = nested > testdelete/.hgsub $ hg -R testdelete ci -m "nested 2 deleted" $ cat testdelete/.hgsubstate @@ -550,8 +542,6 @@ $ hg -R main add adding main/.hgsub (glob) $ hg -R main ci -m "add subrepos" - committing subrepository nested_absolute - committing subrepository nested_relative $ cd .. $ hg clone mercurial/main mercurial2/main updating to branch default @@ -574,7 +564,6 @@ $ echo s = s > repo/.hgsub $ hg -R repo ci -Am1 adding .hgsub - committing subrepository s $ hg clone repo repo2 updating to branch default cloning subrepo s from $TESTTMP/sub/repo/s (glob) @@ -590,7 +579,6 @@ $ hg -R repo2/s ci -m3 created new head $ hg -R repo2 ci -m3 - committing subrepository s $ hg -q -R repo2 push abort: push creates new remote head 9d66565e64e1! (did you forget to merge? use push -f to force) @@ -701,7 +689,6 @@ $ echo subrepo-2 = subrepo-2 >> .hgsub $ hg add .hgsub $ hg ci -m 'Added subrepos' - committing subrepository subrepo-1 committing subrepository subrepo-2 $ hg st subrepo-2/file @@ -859,17 +846,16 @@ $ hg rm -f .hgsubstate $ hg ci -mrm - committing subrepository s - committing subrepository t - created new head + nothing changed + [1] $ hg log -vr tip - changeset: 14:3941e0aa5236 + changeset: 13:925c17564ef8 tag: tip - parent: 11:365661e5936a user: test date: Thu Jan 01 00:00:00 1970 +0000 + files: .hgsubstate description: - rm + 13 @@ -877,9 +863,11 @@ $ hg rm .hgsub $ hg ci -mrm2 + created new head $ hg log -vr tip - changeset: 15:8b31de9d13d1 + changeset: 14:2400bccd50af tag: tip + parent: 11:365661e5936a user: test date: Thu Jan 01 00:00:00 1970 +0000 files: .hgsub .hgsubstate @@ -890,13 +878,13 @@ Test issue3153: diff -S with deleted subrepos $ hg diff --nodates -S -c . - diff -r 3941e0aa5236 -r 8b31de9d13d1 .hgsub + diff -r 365661e5936a -r 2400bccd50af .hgsub --- a/.hgsub +++ /dev/null @@ -1,2 +0,0 @@ -s = s -t = t - diff -r 3941e0aa5236 -r 8b31de9d13d1 .hgsubstate + diff -r 365661e5936a -r 2400bccd50af .hgsubstate --- a/.hgsubstate +++ /dev/null @@ -1,2 +0,0 @@ @@ -911,7 +899,6 @@ $ hg add .hgsub $ hg init s $ hg ci -m0 - committing subrepository s Adding with an explicit path in a subrepo adds the file $ echo c1 > f1 $ echo c2 > s/f2 @@ -925,7 +912,6 @@ $ hg ci -R s -m0 $ hg ci -Am1 adding f1 - committing subrepository s Adding with an explicit path in a subrepo with -S has the same behavior $ echo c3 > f3 $ echo c4 > s/f4 @@ -939,7 +925,6 @@ $ hg ci -R s -m1 $ hg ci -Ama2 adding f3 - committing subrepository s Adding without a path or pattern silently ignores subrepos $ echo c5 > f5 $ echo c6 > s/f6 @@ -958,7 +943,6 @@ adding f6 adding f7 $ hg ci -m3 - committing subrepository s Adding without a path or pattern with -S also adds files in subrepos $ echo c8 > f8 $ echo c9 > s/f9 @@ -977,7 +961,6 @@ A s/f9 $ hg ci -R s -m3 $ hg ci -m4 - committing subrepository s Adding with a pattern silently ignores subrepos $ echo c11 > fm11 $ echo c12 > fn12 @@ -1000,7 +983,6 @@ adding fn14 $ hg ci -Am5 adding fn12 - committing subrepository s Adding with a pattern with -S also adds matches in subrepos $ echo c15 > fm15 $ echo c16 > fn16 @@ -1023,7 +1005,6 @@ adding fn18 $ hg ci -Am6 adding fn16 - committing subrepository s Test behavior of forget for explicit path in subrepo: Forgetting an explicit path in a subrepo untracks the file