comparison mercurial/commands.py @ 14611:adbf5e7df96d

import: add --bypass option This feature is more a way to test patching without a working directory than something people asked about. Adding a --rev option to specify the parent patch revision would make it a little more useful. What this change introduces is patch.repobackend class which let patches be applied against repository revisions. The caller must supply a filestore object to receive patched content, which can be turned into a memctx with patch.makememctx() helper.
author Patrick Mezard <pmezard@gmail.com>
date Tue, 14 Jun 2011 23:26:35 +0200
parents 5d6244930559
children 234b9795d74e
comparison
equal deleted inserted replaced
14610:5d6244930559 14611:adbf5e7df96d
3000 'meaning as the corresponding patch option'), _('NUM')), 3000 'meaning as the corresponding patch option'), _('NUM')),
3001 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')), 3001 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3002 ('f', 'force', None, _('skip check for outstanding uncommitted changes')), 3002 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3003 ('', 'no-commit', None, 3003 ('', 'no-commit', None,
3004 _("don't commit, just update the working directory")), 3004 _("don't commit, just update the working directory")),
3005 ('', 'bypass', None,
3006 _("apply patch without touching the working directory")),
3005 ('', 'exact', None, 3007 ('', 'exact', None,
3006 _('apply patch to the nodes from which it was generated')), 3008 _('apply patch to the nodes from which it was generated')),
3007 ('', 'import-branch', None, 3009 ('', 'import-branch', None,
3008 _('use any branch information in patch (implied by --exact)'))] + 3010 _('use any branch information in patch (implied by --exact)'))] +
3009 commitopts + commitopts2 + similarityopts, 3011 commitopts + commitopts2 + similarityopts,
3033 the parent of each patch before applying it, and will abort if the 3035 the parent of each patch before applying it, and will abort if the
3034 resulting changeset has a different ID than the one recorded in 3036 resulting changeset has a different ID than the one recorded in
3035 the patch. This may happen due to character set problems or other 3037 the patch. This may happen due to character set problems or other
3036 deficiencies in the text patch format. 3038 deficiencies in the text patch format.
3037 3039
3040 Use --bypass to apply and commit patches directly to the
3041 repository, not touching the working directory. Without --exact,
3042 patches will be applied on top of the working directory parent
3043 revision.
3044
3038 With -s/--similarity, hg will attempt to discover renames and 3045 With -s/--similarity, hg will attempt to discover renames and
3039 copies in the patch in the same way as 'addremove'. 3046 copies in the patch in the same way as 'addremove'.
3040 3047
3041 To read a patch from standard input, use "-" as the patch name. If 3048 To read a patch from standard input, use "-" as the patch name. If
3042 a URL is specified, the patch will be downloaded from it. 3049 a URL is specified, the patch will be downloaded from it.
3048 3055
3049 date = opts.get('date') 3056 date = opts.get('date')
3050 if date: 3057 if date:
3051 opts['date'] = util.parsedate(date) 3058 opts['date'] = util.parsedate(date)
3052 3059
3060 update = not opts.get('bypass')
3061 if not update and opts.get('no_commit'):
3062 raise util.Abort(_('cannot use --no-commit with --bypass'))
3053 try: 3063 try:
3054 sim = float(opts.get('similarity') or 0) 3064 sim = float(opts.get('similarity') or 0)
3055 except ValueError: 3065 except ValueError:
3056 raise util.Abort(_('similarity must be a number')) 3066 raise util.Abort(_('similarity must be a number'))
3057 if sim < 0 or sim > 100: 3067 if sim < 0 or sim > 100:
3058 raise util.Abort(_('similarity must be between 0 and 100')) 3068 raise util.Abort(_('similarity must be between 0 and 100'))
3059 3069 if sim and not update:
3060 if opts.get('exact') or not opts.get('force'): 3070 raise util.Abort(_('cannot use --similarity with --bypass'))
3071
3072 if (opts.get('exact') or not opts.get('force')) and update:
3061 cmdutil.bailifchanged(repo) 3073 cmdutil.bailifchanged(repo)
3062 3074
3063 d = opts["base"] 3075 d = opts["base"]
3064 strip = opts["strip"] 3076 strip = opts["strip"]
3065 wlock = lock = None 3077 wlock = lock = None
3066 msgs = [] 3078 msgs = []
3067 3079
3068 def tryone(ui, hunk): 3080 def checkexact(repo, n, nodeid):
3081 if opts.get('exact') and hex(n) != nodeid:
3082 repo.rollback()
3083 raise util.Abort(_('patch is damaged or loses information'))
3084
3085 def tryone(ui, hunk, parents):
3069 tmpname, message, user, date, branch, nodeid, p1, p2 = \ 3086 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3070 patch.extract(ui, hunk) 3087 patch.extract(ui, hunk)
3071 3088
3072 if not tmpname: 3089 if not tmpname:
3073 return None 3090 return None
3084 else: 3101 else:
3085 # launch the editor 3102 # launch the editor
3086 message = None 3103 message = None
3087 ui.debug('message:\n%s\n' % message) 3104 ui.debug('message:\n%s\n' % message)
3088 3105
3089 wp = repo.parents() 3106 if len(parents) == 1:
3090 if len(wp) == 1: 3107 parents.append(repo[nullid])
3091 wp.append(repo[nullid])
3092 if opts.get('exact'): 3108 if opts.get('exact'):
3093 if not nodeid or not p1: 3109 if not nodeid or not p1:
3094 raise util.Abort(_('not a Mercurial patch')) 3110 raise util.Abort(_('not a Mercurial patch'))
3095 p1 = repo[p1] 3111 p1 = repo[p1]
3096 p2 = repo[p2 or nullid] 3112 p2 = repo[p2 or nullid]
3097 elif p2: 3113 elif p2:
3098 try: 3114 try:
3099 p1 = repo[p1] 3115 p1 = repo[p1]
3100 p2 = repo[p2] 3116 p2 = repo[p2]
3101 except error.RepoError: 3117 except error.RepoError:
3102 p1, p2 = wp 3118 p1, p2 = parents
3103 else: 3119 else:
3104 p1, p2 = wp 3120 p1, p2 = parents
3105 3121
3106 if opts.get('exact') and p1 != wp[0]: 3122 n = None
3107 hg.clean(repo, p1.node()) 3123 if update:
3108 if p1 != wp[0] and p2 != wp[1]: 3124 if opts.get('exact') and p1 != parents[0]:
3109 repo.dirstate.setparents(p1.node(), p2.node()) 3125 hg.clean(repo, p1.node())
3110 3126 if p1 != parents[0] and p2 != parents[1]:
3111 if opts.get('exact') or opts.get('import_branch'): 3127 repo.dirstate.setparents(p1.node(), p2.node())
3112 repo.dirstate.setbranch(branch or 'default') 3128
3113 3129 if opts.get('exact') or opts.get('import_branch'):
3114 files = set() 3130 repo.dirstate.setbranch(branch or 'default')
3115 patch.patch(ui, repo, tmpname, strip=strip, files=files, 3131
3116 eolmode=None, similarity=sim / 100.0) 3132 files = set()
3117 files = list(files) 3133 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3118 if opts.get('no_commit'): 3134 eolmode=None, similarity=sim / 100.0)
3119 if message: 3135 files = list(files)
3120 msgs.append(message) 3136 if opts.get('no_commit'):
3137 if message:
3138 msgs.append(message)
3139 else:
3140 if opts.get('exact'):
3141 m = None
3142 else:
3143 m = scmutil.matchfiles(repo, files or [])
3144 n = repo.commit(message, opts.get('user') or user,
3145 opts.get('date') or date, match=m,
3146 editor=cmdutil.commiteditor)
3147 checkexact(repo, n, nodeid)
3148 # Force a dirstate write so that the next transaction
3149 # backups an up-to-date file.
3150 repo.dirstate.write()
3121 else: 3151 else:
3122 if opts.get('exact'): 3152 if opts.get('exact') or opts.get('import_branch'):
3123 m = None 3153 branch = branch or 'default'
3124 else: 3154 else:
3125 m = scmutil.matchfiles(repo, files or []) 3155 branch = p1.branch()
3126 n = repo.commit(message, opts.get('user') or user, 3156 store = patch.filestore()
3127 opts.get('date') or date, match=m, 3157 try:
3128 editor=cmdutil.commiteditor) 3158 files = set()
3129 if opts.get('exact'): 3159 try:
3130 if hex(n) != nodeid: 3160 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3131 repo.rollback() 3161 files, eolmode=None)
3132 raise util.Abort(_('patch is damaged' 3162 except patch.PatchError, e:
3133 ' or loses information')) 3163 raise util.Abort(str(e))
3134 # Force a dirstate write so that the next transaction 3164 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3135 # backups an up-do-date file. 3165 message,
3136 repo.dirstate.write() 3166 opts.get('user') or user,
3137 if n: 3167 opts.get('date') or date,
3138 commitid = short(n) 3168 branch, files, store,
3139 3169 editor=cmdutil.commiteditor)
3170 repo.savecommitmessage(memctx.description())
3171 n = memctx.commit()
3172 checkexact(repo, n, nodeid)
3173 finally:
3174 store.close()
3175 if n:
3176 commitid = short(n)
3140 return commitid 3177 return commitid
3141 finally: 3178 finally:
3142 os.unlink(tmpname) 3179 os.unlink(tmpname)
3143 3180
3144 try: 3181 try:
3145 wlock = repo.wlock() 3182 wlock = repo.wlock()
3146 lock = repo.lock() 3183 lock = repo.lock()
3184 parents = repo.parents()
3147 lastcommit = None 3185 lastcommit = None
3148 for p in patches: 3186 for p in patches:
3149 pf = os.path.join(d, p) 3187 pf = os.path.join(d, p)
3150 3188
3151 if pf == '-': 3189 if pf == '-':
3155 ui.status(_("applying %s\n") % p) 3193 ui.status(_("applying %s\n") % p)
3156 pf = url.open(ui, pf) 3194 pf = url.open(ui, pf)
3157 3195
3158 haspatch = False 3196 haspatch = False
3159 for hunk in patch.split(pf): 3197 for hunk in patch.split(pf):
3160 commitid = tryone(ui, hunk) 3198 commitid = tryone(ui, hunk, parents)
3161 if commitid: 3199 if commitid:
3162 haspatch = True 3200 haspatch = True
3163 if lastcommit: 3201 if lastcommit:
3164 ui.status(_('applied %s\n') % lastcommit) 3202 ui.status(_('applied %s\n') % lastcommit)
3165 lastcommit = commitid 3203 lastcommit = commitid
3204 if update or opts.get('exact'):
3205 parents = repo.parents()
3206 else:
3207 parents = [repo[commitid]]
3166 3208
3167 if not haspatch: 3209 if not haspatch:
3168 raise util.Abort(_('no diffs found')) 3210 raise util.Abort(_('no diffs found'))
3169 3211
3170 if msgs: 3212 if msgs: