Mercurial > public > mercurial-scm > hg
comparison mercurial/cmdutil.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 | 6883c2363f44 |
children | e596a631210e |
comparison
equal
deleted
inserted
replaced
16457:91196ebcaeed | 16458:55982f62651f |
---|---|
8 from node import hex, nullid, nullrev, short | 8 from node import hex, nullid, nullrev, short |
9 from i18n import _ | 9 from i18n import _ |
10 import os, sys, errno, re, tempfile | 10 import os, sys, errno, re, tempfile |
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies | 11 import util, scmutil, templater, patch, error, templatekw, revlog, copies |
12 import match as matchmod | 12 import match as matchmod |
13 import subrepo | 13 import subrepo, context, repair, bookmarks |
14 | 14 |
15 def parsealiases(cmd): | 15 def parsealiases(cmd): |
16 return cmd.lstrip("^").split("|") | 16 return cmd.lstrip("^").split("|") |
17 | 17 |
18 def findpossible(cmd, table, strict=False): | 18 def findpossible(cmd, table, strict=False): |
1282 if opts.get('addremove'): | 1282 if opts.get('addremove'): |
1283 scmutil.addremove(repo, pats, opts) | 1283 scmutil.addremove(repo, pats, opts) |
1284 | 1284 |
1285 return commitfunc(ui, repo, message, | 1285 return commitfunc(ui, repo, message, |
1286 scmutil.match(repo[None], pats, opts), opts) | 1286 scmutil.match(repo[None], pats, opts), opts) |
1287 | |
1288 def amend(ui, repo, commitfunc, old, extra, pats, opts): | |
1289 ui.note(_('amending changeset %s\n') % old) | |
1290 base = old.p1() | |
1291 | |
1292 wlock = repo.wlock() | |
1293 try: | |
1294 # Fix up dirstate for copies and renames | |
1295 duplicatecopies(repo, None, base.node()) | |
1296 | |
1297 # First, do a regular commit to record all changes in the working | |
1298 # directory (if there are any) | |
1299 node = commit(ui, repo, commitfunc, pats, opts) | |
1300 ctx = repo[node] | |
1301 | |
1302 # Participating changesets: | |
1303 # | |
1304 # node/ctx o - new (intermediate) commit that contains changes from | |
1305 # | working dir to go into amending commit (or a workingctx | |
1306 # | if there were no changes) | |
1307 # | | |
1308 # old o - changeset to amend | |
1309 # | | |
1310 # base o - parent of amending changeset | |
1311 | |
1312 files = set(old.files()) | |
1313 | |
1314 # Second, we use either the commit we just did, or if there were no | |
1315 # changes the parent of the working directory as the version of the | |
1316 # files in the final amend commit | |
1317 if node: | |
1318 ui.note(_('copying changeset %s to %s\n') % (ctx, base)) | |
1319 | |
1320 user = ctx.user() | |
1321 date = ctx.date() | |
1322 message = ctx.description() | |
1323 extra = ctx.extra() | |
1324 | |
1325 # Prune files which were reverted by the updates: if old introduced | |
1326 # file X and our intermediate commit, node, renamed that file, then | |
1327 # those two files are the same and we can discard X from our list | |
1328 # of files. Likewise if X was deleted, it's no longer relevant | |
1329 files.update(ctx.files()) | |
1330 | |
1331 def samefile(f): | |
1332 if f in ctx.manifest(): | |
1333 a = ctx.filectx(f) | |
1334 if f in base.manifest(): | |
1335 b = base.filectx(f) | |
1336 return (a.data() == b.data() | |
1337 and a.flags() == b.flags() | |
1338 and a.renamed() == b.renamed()) | |
1339 else: | |
1340 return False | |
1341 else: | |
1342 return f not in base.manifest() | |
1343 files = [f for f in files if not samefile(f)] | |
1344 | |
1345 def filectxfn(repo, ctx_, path): | |
1346 try: | |
1347 return ctx.filectx(path) | |
1348 except KeyError: | |
1349 raise IOError() | |
1350 else: | |
1351 ui.note(_('copying changeset %s to %s\n') % (old, base)) | |
1352 | |
1353 # Use version of files as in the old cset | |
1354 def filectxfn(repo, ctx_, path): | |
1355 try: | |
1356 return old.filectx(path) | |
1357 except KeyError: | |
1358 raise IOError() | |
1359 | |
1360 # See if we got a message from -m or -l, if not, open the editor | |
1361 # with the message of the changeset to amend | |
1362 user = opts.get('user') or old.user() | |
1363 date = opts.get('date') or old.date() | |
1364 message = logmessage(ui, opts) | |
1365 if not message: | |
1366 cctx = context.workingctx(repo, old.description(), user, date, | |
1367 extra, | |
1368 repo.status(base.node(), old.node())) | |
1369 message = commitforceeditor(repo, cctx, []) | |
1370 | |
1371 new = context.memctx(repo, | |
1372 parents=[base.node(), nullid], | |
1373 text=message, | |
1374 files=files, | |
1375 filectxfn=filectxfn, | |
1376 user=user, | |
1377 date=date, | |
1378 extra=extra) | |
1379 newid = repo.commitctx(new) | |
1380 if newid != old.node(): | |
1381 # Reroute the working copy parent to the new changeset | |
1382 repo.dirstate.setparents(newid, nullid) | |
1383 | |
1384 # Move bookmarks from old parent to amend commit | |
1385 bms = repo.nodebookmarks(old.node()) | |
1386 if bms: | |
1387 for bm in bms: | |
1388 repo._bookmarks[bm] = newid | |
1389 bookmarks.write(repo) | |
1390 | |
1391 # Strip the intermediate commit (if there was one) and the amended | |
1392 # commit | |
1393 lock = repo.lock() | |
1394 try: | |
1395 if node: | |
1396 ui.note(_('stripping intermediate changeset %s\n') % ctx) | |
1397 ui.note(_('stripping amended changeset %s\n') % old) | |
1398 repair.strip(ui, repo, old.node(), topic='amend-backup') | |
1399 finally: | |
1400 lock.release() | |
1401 finally: | |
1402 wlock.release() | |
1403 return newid | |
1287 | 1404 |
1288 def commiteditor(repo, ctx, subs): | 1405 def commiteditor(repo, ctx, subs): |
1289 if ctx.description(): | 1406 if ctx.description(): |
1290 return ctx.description() | 1407 return ctx.description() |
1291 return commitforceeditor(repo, ctx, subs) | 1408 return commitforceeditor(repo, ctx, subs) |