[PATCH] subrepo: Force updating subrepos if using overwrite option

Steve Losh steve at stevelosh.com
Thu Feb 18 20:41:31 CST 2010


Saint Germain and I were talking about this patch on IRC.

The main problem is this: updating a master repo when the subrepos have uncommitted changes can fail, and there's no quick way to say "it's okay, just update anyway."

The original patch added some functionality to wipe the subrepo changes when you used 'hg update -C' in the master repo.  Personally I wasn't comfortable with this because running 'hg status' in the master repo does NOT show you any changes in the subrepos.  To me it seems like this makes it too easy to blow away changes in the subrepos without realizing it until it's too late.

This updated version of the patch adds an additional layer of protection.  Running 'hg update -C' when some subrepos have uncommitted changes will make Mercurial abort and tell you to use the '-S' option to force the update of the subrepos.

I think this makes things much safer without sacrificing any functionality.  I'm interested in hearing what other people think of it.

--
Steve Losh
http://stevelosh.com/



On Feb 18, 2010, at 9:31 PM, Saint Germain wrote:

> # HG changeset patch
> # User Saint Germain <saintger at gmail.com>
> # Date 1266546400 -3600
> # Node ID d12e9314a5265b7e2aae44e123a3309a2ac9f03f
> # Parent  b9e44cc97355ff27e05f6cd384061fc12731cec0
> subrepo: Force updating subrepos if using overwrite option
> 
> Even if .hgsubstate doesn't need updating, subrepos may be 'dirty'. So if using -CS (overwrite)
> option, add a check for 'dirty' subrepos and if found, force the update.
> 
> diff -r b9e44cc97355 -r d12e9314a526 mercurial/commands.py
> --- a/mercurial/commands.py	Wed Feb 03 16:09:19 2010 +0000
> +++ b/mercurial/commands.py	Fri Feb 19 03:26:40 2010 +0100
> @@ -3152,7 +3152,7 @@
> 
>     return postincoming(ui, repo, modheads, opts.get('update'), None)
> 
> -def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
> +def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False, clean_subrepos=False):
>     """update working directory
> 
>     Update the repository's working directory to the specified
> @@ -3208,7 +3208,7 @@
>         rev = cmdutil.finddate(ui, repo, date)
> 
>     if clean or check:
> -        return hg.clean(repo, rev)
> +        return hg.clean(repo, rev, clean_subrepo=clean_subrepos)
>     else:
>         return hg.update(repo, rev)
> 
> @@ -3715,6 +3715,7 @@
>     "^update|up|checkout|co":
>         (update,
>          [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
> +          ('S', 'clean-subrepos', None, _('discard uncommitted changes (no backup) in subrepositories')),
>           ('c', 'check', None, _('check for uncommitted changes')),
>           ('d', 'date', '', _('tipmost revision matching date')),
>           ('r', 'rev', '', _('revision'))],
> diff -r b9e44cc97355 -r d12e9314a526 mercurial/hg.py
> --- a/mercurial/hg.py	Wed Feb 03 16:09:19 2010 +0000
> +++ b/mercurial/hg.py	Fri Feb 19 03:26:40 2010 +0100
> @@ -345,9 +345,9 @@
> # naming conflict in clone()
> _update = update
> 
> -def clean(repo, node, show_stats=True):
> +def clean(repo, node, show_stats=True, clean_subrepo=False):
>     """forcibly switch the working directory to node, clobbering changes"""
> -    stats = _merge.update(repo, node, False, True, None)
> +    stats = _merge.update(repo, node, False, True, None, clean_subrepo)
>     if show_stats:
>         _showstats(repo, stats)
>     return stats[3] > 0
> diff -r b9e44cc97355 -r d12e9314a526 mercurial/merge.py
> --- a/mercurial/merge.py	Wed Feb 03 16:09:19 2010 +0000
> +++ b/mercurial/merge.py	Fri Feb 19 03:26:40 2010 +0100
> @@ -117,7 +117,7 @@
> 
>     return action
> 
> -def manifestmerge(repo, p1, p2, pa, overwrite, partial):
> +def manifestmerge(repo, p1, p2, pa, overwrite, partial, overwrite_subrepo=False):
>     """
>     Merge p1 and p2 with ancestor ma and generate merge action list
> 
> @@ -186,6 +186,16 @@
>             if n == m2[f] or m2[f] == a: # same or local newer
>                 if m1.flags(f) != rflags:
>                     act("update permissions", "e", f, rflags)
> +                # in case of subrepos, update if overwrite and subrepos is dirty
> +                if f == '.hgsubstate':
> +                    for s in p1.substate:
> +                        if p1.sub(s).dirty(): # if dirty, force update
> +                            if overwrite_subrepo:
> +                                act("remote is newer", "g", f, rflags)
> +                            elif overwrite:
> +                                raise util.Abort("subrepos have uncommitted"
> +                                " changes, use 'hg update -CS' to overwrite\n")
> +                            break
>             elif n == a: # remote newer
>                 act("remote is newer", "g", f, rflags)
>             else: # both changed
> @@ -402,7 +412,7 @@
>                 if f:
>                     repo.dirstate.forget(f)
> 
> -def update(repo, node, branchmerge, force, partial):
> +def update(repo, node, branchmerge, force, partial, clean_subrepos=False):
>     """
>     Perform a merge between the working directory and the given node
> 
> @@ -451,6 +461,7 @@
>                 else:
>                     raise util.Abort(_("branch %s not found") % wc.branch())
>         overwrite = force and not branchmerge
> +        overwrite_subrepo = overwrite and clean_subrepos
>         pl = wc.parents()
>         p1, p2 = pl[0], repo[node]
>         pa = p1.ancestor(p2)
> @@ -492,7 +503,7 @@
>         if not util.checkcase(repo.path):
>             _checkcollision(p2)
>         action += _forgetremoved(wc, p2, branchmerge)
> -        action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
> +        action += manifestmerge(repo, wc, p2, pa, overwrite, partial, overwrite_subrepo)
> 
>         ### apply phase
>         if not branchmerge: # just jump to the new rev
> diff -r b9e44cc97355 -r d12e9314a526 tests/test-subrepo
> --- a/tests/test-subrepo	Wed Feb 03 16:09:19 2010 +0000
> +++ b/tests/test-subrepo	Fri Feb 19 03:26:40 2010 +0100
> @@ -161,4 +161,20 @@
> hg up 5
> hg merge 4    # try to merge default into br again
> 
> +echo % test repositoy clean/overwrite updating
> +hg init mercurial
> +cd mercurial
> +hg init nested
> +echo test > nested/foo
> +hg -R nested add nested/foo
> +echo nested = nested > .hgsub
> +hg add .hgsub
> +hg ci -mtest
> +echo modification > nested/foo
> +hg -R nested commit -mmodif
> +hg update -C
> +hg update -CS
> +cd ..
> +rm -rf mercurial
> +
> exit 0
> diff -r b9e44cc97355 -r d12e9314a526 tests/test-subrepo.out
> --- a/tests/test-subrepo.out	Wed Feb 03 16:09:19 2010 +0000
> +++ b/tests/test-subrepo.out	Fri Feb 19 03:26:40 2010 +0100
> @@ -243,3 +243,8 @@
> 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
> 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> (branch merge, don't forget to commit)
> +% test repositoy clean/overwrite updating
> +committing subrepository nested
> +abort: subrepos have uncommitted changes, use 'hg update -CS' to overwrite
> +
> +1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel



More information about the Mercurial-devel mailing list