[PATCH STABLE RFC] transplant: avoid a dirstate race when transplanting multiple changesets
Mads Kiilerich
mads at kiilerich.com
Tue Jan 25 09:29:56 CST 2011
On 01/25/2011 03:18 PM, Greg Ward wrote:
> # HG changeset patch
> # User Greg Ward<greg-hg at gerg.ca>
> # Date 1295964871 18000
> # Branch stable
> # Node ID f501e673e1188511f3bd22959dd8ed693d458011
> # Parent d0e0d3d43e1439d63564ab4dddfe0daa69ae2d86
> transplant: avoid a dirstate race when transplanting multiple changesets
> (issue2264, issue2516)
>
> The race happens when adjacent transplanted changesets change the same
> file but keep its size the same. When those changesets are applied to
> the working dir before re-committing them, the mtime is almost
> certainly the same, so dirstate fails to notice the change. The
> result depends on circumstances: if the transplanted changesets affect
> only one file, then we get a big noisy crash: "RuntimeError: nothing
> committed after transplant". But if other files are included, we get
> subtle data loss, i.e. the file whose size does not change is silently
> dropped from the second transplant.
>
> I couldn't think of a way to fix this in dirstate, which means other
> extensions or scripts that do multiple commits in rapid succession
> could suffer the same problem. But it's not too hard to fix in
> transplant: just mark each file involved in a patch as "normal
> lookup", forcing repo.status() to work harder when committing the next
> transplant in the series.
>
> diff --git a/hgext/transplant.py b/hgext/transplant.py
> --- a/hgext/transplant.py
> +++ b/hgext/transplant.py
> @@ -177,6 +177,16 @@
> lock.release()
> wlock.release()
>
> + # Extra after-the-fact check for one of the symptoms of
> + # issue2264: if this is raised, it's too late and we've already
> + # committed bad changesets. Not sure if it's worth the overhead
> + # of a status() call.
> + (modified, added, removed) = repo.status()[0:3]
> + if modified or added or removed:
> + raise RuntimeError('uncommitted changes left in working dir '
> + 'after transplanting %d changesets\n'
> + % len(revs))
> +
Wouldn't util.Abort be better? (I guess the existing use of RuntimeError
should be changed too.)
> @@ -252,6 +262,12 @@
> m = match.exact(repo.root, '', files)
>
> n = repo.commit(message, user, date, extra=extra, match=m)
> +
> + # force repo.status() to look harder at each file in this patch
> + # when committing the next patch (avoids a dirstate race)
> + for fn in files:
> + repo.dirstate.normallookup(fn)
> +
Shouldn't this be done before commit so commit is guaranteed to pick up
all these patched files and we get a 100% reliable fix?
/Mads
More information about the Mercurial-devel
mailing list