Bug 2403 - Empty commit with dirty .hgsubstate file
Summary: Empty commit with dirty .hgsubstate file
Status: RESOLVED FIXED
Alias: None
Product: Mercurial
Classification: Unclassified
Component: Mercurial (show other bugs)
Version: unspecified
Hardware: All All
: normal bug
Assignee: Bugzilla
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-09-27 03:52 UTC by Martin Geisler
Modified: 2012-05-13 04:53 UTC (History)
7 users (show)

See Also:
Python Version: ---


Attachments
(34 bytes, application/x-troff)
2010-11-04 05:12 UTC, Martin Geisler
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Geisler 2010-09-27 03:52 UTC
The following test script shows a problem where we can make an empty commit
because Mercurial forgot to update th .hgsubstate file.

Create three changesets with outer and inner repos moving in lock-step

  $ hg init
  $ hg init sub
  $ echo 'sub = sub' > .hgsub
  $ hg add .hgsub
  $ echo x0 > x
  $ echo y0 > sub/y
  $ hg add -S
  adding x
  adding sub/y
  $ hg commit -m r0
  committing subrepository sub

  $ echo x1 > x
  $ echo y1 > sub/y
  $ hg commit -m r1
  committing subrepository sub

  $ echo x2 > x
  $ echo y2 > sub/y
  $ hg commit -m r2
  committing subrepository sub

  $ cat x sub/y .hgsubstate
  x2
  y2
  97dc77eda23d16886b839deb0e8ff655b431b9f7 sub

  $ hg -R sub log --template '{rev}: {node|short}\n'
  2: 97dc77eda23d
  1: 8514e0ba703c
  0: a2eda9ba59e6

Now update to an earlier version:

  $ hg update 1
  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ cat x sub/y .hgsubstate
  x1
  y1
  8514e0ba703cdb0dcc5c92beb6c534ec8e7cb34b sub
  $ hg -R sub id
  8514e0ba703c

Update the subrepository to an even earlier version:

  $ hg -R sub update 0
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ cat x sub/y .hgsubstate
  x1
  y0
  8514e0ba703cdb0dcc5c92beb6c534ec8e7cb34b sub
  $ hg -R sub id
  a2eda9ba59e6

What happens when we update the outer repository?

  $ hg update 2
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ cat x sub/y .hgsubstate
  x2
  y2
  8514e0ba703cdb0dcc5c92beb6c534ec8e7cb34b sub
  $ hg -R sub id
  97dc77eda23d tip

The .hgsubstate file still records the old revision, but the subrepo
was updated correctly.

  $ hg summary
  parent: 2:0fc3cdfccbc8 tip
   r2
  branch: default
  commit: 1 modified, 1 subrepos
  update: (current)

  $ hg commit -m 'r3 - empty substate commit!'
  committing subrepository sub

  $ cat x sub/y .hgsubstate
  x2
  y2
  97dc77eda23d16886b839deb0e8ff655b431b9f7 sub
  $ hg -R sub id
  97dc77eda23d tip

  $ hg log
  changeset:   3:9313ee30238c
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     r3 - empty substate commit!
  
  changeset:   2:0fc3cdfccbc8
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     r2
  
  changeset:   1:5d202505eb64
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     r1
  
  changeset:   0:56b51e4cf504
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     r0
  
  $ hg export tip
  # HG changeset patch
  # User test
  # Date 0 0
  # Node ID 9313ee30238c88d124ef38e8f2f3a7daa83618b5
  # Parent  0fc3cdfccbc805c2bb4f2a09a354ff037583bdab
  r3 - empty substate commit!
Comment 1 kiilerix 2010-09-27 05:01 UTC
I tried to track down a similar case some months ago. (Discussed on IRC July
22-23 2010.) That did however involve a commit that failed after .hgsubstate
had been updated:
[mk@dev-mk subtest]$ hg up -R sub -r tip
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[mk@dev-mk subtest]$ hg commit --config hooks.precommit=true -m hey
committing subrepository sub
[mk@dev-mk subtest]$ hg up -R sub -r null
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
[mk@dev-mk subtest]$ hg commit --config hooks.precommit=false -m hey
committing subrepository sub
abort: precommit hook exited with status 1
[mk@dev-mk subtest]$ hg diff
diff --git a/.hgsubstate b/.hgsubstate
--- a/.hgsubstate
+++ b/.hgsubstate
@@ -1,1 +1,1 @@
-2a1db703cc4c9e7441592191e4ff2367708120e4 sub
+0000000000000000000000000000000000000000 sub
[mk@dev-mk subtest]$ hg up -R sub -r tip
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[mk@dev-mk subtest]$ hg commit --config hooks.precommit=true -m hey
committing subrepository sub
[mk@dev-mk subtest]$ hg log -p -v -r tip
changeset:   9:ccdb16ca32c0
tag:         tip
user:        x
date:        Thu Jul 22 16:54:44 2010 +0200
description:
hey


I think these shows a general problem with the invisible substate. It is
almost invisible and works fine in most cases, but the hardcoded magic makes
it hard to debug and workaround issues.


I tried a patch like
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -779,6 +779,10 @@
             changelist.append(fname)
             return flog.add(text, meta, tr, linkrev, fparent1, fparent2)

+        # any reason to commit this file? did something go wrong?
+        if fname == '.hgsubstate' and fparent2o == nullid and
manifest1.flags(fname) == fctx.flags():
+            raise util.Abort(_('no change to %s') % fname)
+
         # are just the flags changed during merge?
         if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
             changelist.append(fname)

- but the problem is that we do allow empty patches - especially for mq.

I also tried to catch and abort in localrepo commitctx when we get as far as
trying to commit an unmodified file. That also didn't work out - and 
nothing more happened.

Now I guess the best/simplest way to solve this is to add a ugly hack for
special handling of commits that only makes a noop-change to .hgsubstate.
Comment 2 kiilerix 2010-09-27 07:07 UTC
Regarding .hgsubstate handling see also issue1771
Comment 3 Martin Geisler 2010-11-04 05:12 UTC
Hi Mads, so by setting precommit=false you can trick Mercurial into updating
the .hgsubstate file thereby making it dirty, and when you make a commit we
don't realize that nothing is dirty after all since the subrepo is at its
correct revision...

I've attached an easier way to provoke this by simply overwriting the
.hgsubstate file.
Comment 4 Martin Geisler 2010-11-04 05:13 UTC
Matt, do you have any good ideas for where we should handle this?
Comment 5 Matt Mackall 2010-11-05 16:17 UTC
Probably in localrepo.commit? Perhaps we can simply ignore the status on
.hgsubstate earlier on.
Comment 6 Erik Zielke 2010-11-18 10:48 UTC
Have looked at it. One solution could be to just 'revert' whatever a user
have done with the .hgsubstate file when starting a commit.
Comment 7 HG Bot 2012-02-06 16:00 UTC
Fixed by http://selenic.com/repo/hg/rev/b254f827b7a6
Matt Mackall <mpm@selenic.com>
subrepo: rewrite handling of subrepo state at commit (issue2403)

(please test the fix)
Comment 8 Bugzilla 2012-05-12 09:12 UTC

--- Bug imported by bugzilla@serpentine.com 2012-05-12 09:12 EDT  ---

This bug was previously known as _bug_ 2403 at http://mercurial.selenic.com/bts/issue2403
Imported an attachment (id=1459)