comparison hgext/mq.py @ 19826:4b1cbcfdabf7

mq: extract strip function as its standalone extension (issue3824) Strip now lives in its own extension reminder: The extension is surprisingly called `strip`. The `mq` extension force the use of the strip extension when its enabled. This is both necessary for backward compatibility (people expect `mq` to comes with strip) and become some utility function used by `mq` are now in the strip extension.
author Pierre-Yves David <pierre-yves.david@ens-lyon.org>
date Thu, 26 Sep 2013 23:57:21 +0200
parents 4b4997068143
children 28b1b7b9b4a9
comparison
equal deleted inserted replaced
19825:4b4997068143 19826:4b1cbcfdabf7
55 keepchanges = True 55 keepchanges = True
56 56
57 make them behave as if --keep-changes were passed, and non-conflicting 57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options 58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored. 59 such as -f/--force or --exact are passed, this setting is ignored.
60
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
60 ''' 63 '''
61 64
62 from mercurial.i18n import _ 65 from mercurial.i18n import _
63 from mercurial.node import bin, hex, short, nullid, nullrev 66 from mercurial.node import bin, hex, short, nullid, nullrev
64 from mercurial.lock import release 67 from mercurial.lock import release
65 from mercurial import commands, cmdutil, hg, scmutil, util, revset 68 from mercurial import commands, cmdutil, hg, scmutil, util, revset
66 from mercurial import extensions, error, phases, bookmarks 69 from mercurial import extensions, error, phases
67 from mercurial import patch as patchmod 70 from mercurial import patch as patchmod
68 from mercurial import localrepo 71 from mercurial import localrepo
69 from mercurial import subrepo 72 from mercurial import subrepo
70 import os, re, errno, shutil 73 import os, re, errno, shutil
71 74
2910 del q.applied[:] 2913 del q.applied[:]
2911 q.applieddirty = True 2914 q.applieddirty = True
2912 q.savedirty() 2915 q.savedirty()
2913 return 0 2916 return 0
2914 2917
2915 @command("strip",
2916 [
2917 ('r', 'rev', [], _('strip specified revision (optional, '
2918 'can specify revisions without this '
2919 'option)'), _('REV')),
2920 ('f', 'force', None, _('force removal of changesets, discard '
2921 'uncommitted changes (no backup)')),
2922 ('b', 'backup', None, _('bundle only changesets with local revision'
2923 ' number greater than REV which are not'
2924 ' descendants of REV (DEPRECATED)')),
2925 ('', 'no-backup', None, _('no backups')),
2926 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2927 ('n', '', None, _('ignored (DEPRECATED)')),
2928 ('k', 'keep', None, _("do not modify working copy during strip")),
2929 ('B', 'bookmark', '', _("remove revs only reachable from given"
2930 " bookmark"))],
2931 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
2932 def stripcmd(ui, repo, *revs, **opts):
2933 """strip changesets and all their descendants from the repository
2934
2935 The strip command removes the specified changesets and all their
2936 descendants. If the working directory has uncommitted changes, the
2937 operation is aborted unless the --force flag is supplied, in which
2938 case changes will be discarded.
2939
2940 If a parent of the working directory is stripped, then the working
2941 directory will automatically be updated to the most recent
2942 available ancestor of the stripped parent after the operation
2943 completes.
2944
2945 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2946 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2947 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2948 where BUNDLE is the bundle file created by the strip. Note that
2949 the local revision numbers will in general be different after the
2950 restore.
2951
2952 Use the --no-backup option to discard the backup bundle once the
2953 operation completes.
2954
2955 Strip is not a history-rewriting operation and can be used on
2956 changesets in the public phase. But if the stripped changesets have
2957 been pushed to a remote repository you will likely pull them again.
2958
2959 Return 0 on success.
2960 """
2961 backup = 'all'
2962 if opts.get('backup'):
2963 backup = 'strip'
2964 elif opts.get('no_backup') or opts.get('nobackup'):
2965 backup = 'none'
2966
2967 cl = repo.changelog
2968 revs = list(revs) + opts.get('rev')
2969 revs = set(scmutil.revrange(repo, revs))
2970
2971 if opts.get('bookmark'):
2972 mark = opts.get('bookmark')
2973 marks = repo._bookmarks
2974 if mark not in marks:
2975 raise util.Abort(_("bookmark '%s' not found") % mark)
2976
2977 # If the requested bookmark is not the only one pointing to a
2978 # a revision we have to only delete the bookmark and not strip
2979 # anything. revsets cannot detect that case.
2980 uniquebm = True
2981 for m, n in marks.iteritems():
2982 if m != mark and n == repo[mark].node():
2983 uniquebm = False
2984 break
2985 if uniquebm:
2986 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2987 "ancestors(head() and not bookmark(%s)) - "
2988 "ancestors(bookmark() and not bookmark(%s))",
2989 mark, mark, mark)
2990 revs.update(set(rsrevs))
2991 if not revs:
2992 del marks[mark]
2993 marks.write()
2994 ui.write(_("bookmark '%s' deleted\n") % mark)
2995
2996 if not revs:
2997 raise util.Abort(_('empty revision set'))
2998
2999 descendants = set(cl.descendants(revs))
3000 strippedrevs = revs.union(descendants)
3001 roots = revs.difference(descendants)
3002
3003 update = False
3004 # if one of the wdir parent is stripped we'll need
3005 # to update away to an earlier revision
3006 for p in repo.dirstate.parents():
3007 if p != nullid and cl.rev(p) in strippedrevs:
3008 update = True
3009 break
3010
3011 rootnodes = set(cl.node(r) for r in roots)
3012
3013 q = getattr(repo, 'mq', None)
3014 if q is not None and q.applied:
3015 # refresh queue state if we're about to strip
3016 # applied patches
3017 if cl.rev(repo.lookup('qtip')) in strippedrevs:
3018 q.applieddirty = True
3019 start = 0
3020 end = len(q.applied)
3021 for i, statusentry in enumerate(q.applied):
3022 if statusentry.node in rootnodes:
3023 # if one of the stripped roots is an applied
3024 # patch, only part of the queue is stripped
3025 start = i
3026 break
3027 del q.applied[start:end]
3028 q.savedirty()
3029
3030 revs = sorted(rootnodes)
3031 if update and opts.get('keep'):
3032 wlock = repo.wlock()
3033 try:
3034 urev, p2 = repo.changelog.parents(revs[0])
3035 if (util.safehasattr(repo, 'mq') and p2 != nullid
3036 and p2 in [x.node for x in repo.mq.applied]):
3037 urev = p2
3038 uctx = repo[urev]
3039
3040 # only reset the dirstate for files that would actually change
3041 # between the working context and uctx
3042 descendantrevs = repo.revs("%s::." % uctx.rev())
3043 changedfiles = []
3044 for rev in descendantrevs:
3045 # blindly reset the files, regardless of what actually changed
3046 changedfiles.extend(repo[rev].files())
3047
3048 # reset files that only changed in the dirstate too
3049 dirstate = repo.dirstate
3050 dirchanges = [f for f in dirstate if dirstate[f] != 'n']
3051 changedfiles.extend(dirchanges)
3052
3053 repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
3054 repo.dirstate.write()
3055 update = False
3056 finally:
3057 wlock.release()
3058
3059 if opts.get('bookmark'):
3060 if mark == repo._bookmarkcurrent:
3061 bookmarks.setcurrent(repo, None)
3062 del marks[mark]
3063 marks.write()
3064 ui.write(_("bookmark '%s' deleted\n") % mark)
3065
3066 strip(ui, repo, revs, backup=backup, update=update, force=opts.get('force'))
3067
3068 return 0
3069 2918
3070 @command("qselect", 2919 @command("qselect",
3071 [('n', 'none', None, _('disable all guards')), 2920 [('n', 'none', None, _('disable all guards')),
3072 ('s', 'series', None, _('list all guards in series file')), 2921 ('s', 'series', None, _('list all guards in series file')),
3073 ('', 'pop', None, _('pop to before first guarded applied patch')), 2922 ('', 'pop', None, _('pop to before first guarded applied patch')),