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) |