comparison mercurial/commands.py @ 16458:55982f62651f

commit: add option to amend the working dir parent The --amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by "hg status", if there are any. The old commit is stored in a backup bundle in ".hg/strip-backup"(see "hg help bundle" and "hg help unbundle" on how to restore it). Message, user and date are taken from the amended commit unless specified. When a message isn't specified on the command line, the editor will open with the message of the amended commit. It is not possible to amend public changesets (see "hg help phases") or changesets that have children. Behind the scenes, first commit the update (if there is one) as a regular child of the current parent. Then create a new commit on the parent's parent with the updated contents. Then change the working copy parent to this new combined changeset. Finally, strip the amended commit and update commit created in the beginning. An alternative (cleaner?) approach of doing this is suggested here: http://selenic.com/pipermail/mercurial-devel/2012-March/038540.html It is currently not possible to amend merge commits or recursively, this can be added at a later time.
author Idan Kamara <idankk86@gmail.com>
date Wed, 18 Apr 2012 01:20:16 +0300
parents d54d4de56aa7
children b2e1da5db6df
comparison
equal deleted inserted replaced
16457:91196ebcaeed 16458:55982f62651f
1161 @command('^commit|ci', 1161 @command('^commit|ci',
1162 [('A', 'addremove', None, 1162 [('A', 'addremove', None,
1163 _('mark new/missing files as added/removed before committing')), 1163 _('mark new/missing files as added/removed before committing')),
1164 ('', 'close-branch', None, 1164 ('', 'close-branch', None,
1165 _('mark a branch as closed, hiding it from the branch list')), 1165 _('mark a branch as closed, hiding it from the branch list')),
1166 ('', 'amend', None, _('amend the parent of the working dir')),
1166 ] + walkopts + commitopts + commitopts2 + subrepoopts, 1167 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1167 _('[OPTION]... [FILE]...')) 1168 _('[OPTION]... [FILE]...'))
1168 def commit(ui, repo, *pats, **opts): 1169 def commit(ui, repo, *pats, **opts):
1169 """commit the specified files or all outstanding changes 1170 """commit the specified files or all outstanding changes
1170 1171
1180 1181
1181 If no commit message is specified, Mercurial starts your 1182 If no commit message is specified, Mercurial starts your
1182 configured editor where you can enter a message. In case your 1183 configured editor where you can enter a message. In case your
1183 commit fails, you will find a backup of your message in 1184 commit fails, you will find a backup of your message in
1184 ``.hg/last-message.txt``. 1185 ``.hg/last-message.txt``.
1186
1187 The --amend flag can be used to amend the parent of the
1188 working directory with a new commit that contains the changes
1189 in the parent in addition to those currently reported by :hg:`status`,
1190 if there are any. The old commit is stored in a backup bundle in
1191 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1192 on how to restore it).
1193
1194 Message, user and date are taken from the amended commit unless
1195 specified. When a message isn't specified on the command line,
1196 the editor will open with the message of the amended commit.
1197
1198 It is not possible to amend public changesets (see :hg:`help phases`)
1199 or changesets that have children.
1185 1200
1186 See :hg:`help dates` for a list of formats valid for -d/--date. 1201 See :hg:`help dates` for a list of formats valid for -d/--date.
1187 1202
1188 Returns 0 on success, 1 if nothing changed. 1203 Returns 0 on success, 1 if nothing changed.
1189 """ 1204 """
1196 if repo['.'].node() not in repo.branchheads(): 1211 if repo['.'].node() not in repo.branchheads():
1197 # The topo heads set is included in the branch heads set of the 1212 # The topo heads set is included in the branch heads set of the
1198 # current branch, so it's sufficient to test branchheads 1213 # current branch, so it's sufficient to test branchheads
1199 raise util.Abort(_('can only close branch heads')) 1214 raise util.Abort(_('can only close branch heads'))
1200 extra['close'] = 1 1215 extra['close'] = 1
1201 e = cmdutil.commiteditor
1202 if opts.get('force_editor'):
1203 e = cmdutil.commitforceeditor
1204
1205 def commitfunc(ui, repo, message, match, opts):
1206 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1207 editor=e, extra=extra)
1208 1216
1209 branch = repo[None].branch() 1217 branch = repo[None].branch()
1210 bheads = repo.branchheads(branch) 1218 bheads = repo.branchheads(branch)
1211 1219
1212 node = cmdutil.commit(ui, repo, commitfunc, pats, opts) 1220 if opts.get('amend'):
1213 if not node: 1221 if ui.config('ui', 'commitsubrepos'):
1214 stat = repo.status(match=scmutil.match(repo[None], pats, opts)) 1222 raise util.Abort(_('cannot amend recursively'))
1215 if stat[3]: 1223
1216 ui.status(_("nothing changed (%d missing files, see 'hg status')\n") 1224 old = repo['.']
1217 % len(stat[3])) 1225 if old.phase() == phases.public:
1218 else: 1226 raise util.Abort(_('cannot amend public changesets'))
1227 if len(old.parents()) > 1:
1228 raise util.Abort(_('cannot amend merge changesets'))
1229 if len(repo[None].parents()) > 1:
1230 raise util.Abort(_('cannot amend while merging'))
1231 if old.children():
1232 raise util.Abort(_('cannot amend changeset with children'))
1233
1234 e = cmdutil.commiteditor
1235 if opts.get('force_editor'):
1236 e = cmdutil.commitforceeditor
1237
1238 def commitfunc(ui, repo, message, match, opts):
1239 editor = e
1240 # message contains text from -m or -l, if it's empty,
1241 # open the editor with the old message
1242 if not message:
1243 message = old.description()
1244 editor = cmdutil.commitforceeditor
1245 return repo.commit(message,
1246 opts.get('user') or old.user(),
1247 opts.get('date') or old.date(),
1248 match,
1249 editor=editor,
1250 extra=extra)
1251
1252 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1253 if node == old.node():
1219 ui.status(_("nothing changed\n")) 1254 ui.status(_("nothing changed\n"))
1220 return 1 1255 return 1
1256 else:
1257 e = cmdutil.commiteditor
1258 if opts.get('force_editor'):
1259 e = cmdutil.commitforceeditor
1260
1261 def commitfunc(ui, repo, message, match, opts):
1262 return repo.commit(message, opts.get('user'), opts.get('date'),
1263 match, editor=e, extra=extra)
1264
1265 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1266
1267 if not node:
1268 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1269 if stat[3]:
1270 ui.status(_("nothing changed (%d missing files, see "
1271 "'hg status')\n") % len(stat[3]))
1272 else:
1273 ui.status(_("nothing changed\n"))
1274 return 1
1221 1275
1222 ctx = repo[node] 1276 ctx = repo[node]
1223 parents = ctx.parents() 1277 parents = ctx.parents()
1224 1278
1225 if (bheads and node not in bheads and not 1279 if (not opts.get('amend') and bheads and node not in bheads and not
1226 [x for x in parents if x.node() in bheads and x.branch() == branch]): 1280 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1227 ui.status(_('created new head\n')) 1281 ui.status(_('created new head\n'))
1228 # The message is not printed for initial roots. For the other 1282 # The message is not printed for initial roots. For the other
1229 # changesets, it is printed in the following situations: 1283 # changesets, it is printed in the following situations:
1230 # 1284 #