Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/cmdutil.py @ 17472:965fbe04fd96
amend: wrap all commit operations in a single transaction
This allows proper recovery of an interrupted amend process.
No changes are made to the logic besides:
- indent operations into a single try-except clause,
- some comment and code wrapping to 80 chars,
- strip logic should not be contained in the transaction and is extracted from
the main code.
author | Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
---|---|
date | Mon, 10 Sep 2012 23:44:24 +0200 |
parents | ad1561723dde |
children | 9732473aa24b |
comparison
equal
deleted
inserted
replaced
17471:ad1561723dde | 17472:965fbe04fd96 |
---|---|
1578 | 1578 |
1579 wlock = lock = None | 1579 wlock = lock = None |
1580 try: | 1580 try: |
1581 wlock = repo.wlock() | 1581 wlock = repo.wlock() |
1582 lock = repo.lock() | 1582 lock = repo.lock() |
1583 # First, do a regular commit to record all changes in the working | 1583 tr = repo.transaction('amend') |
1584 # directory (if there are any) | |
1585 ui.callhooks = False | |
1586 try: | 1584 try: |
1587 node = commit(ui, repo, commitfunc, pats, opts) | 1585 # First, do a regular commit to record all changes in the working |
1586 # directory (if there are any) | |
1587 ui.callhooks = False | |
1588 try: | |
1589 node = commit(ui, repo, commitfunc, pats, opts) | |
1590 finally: | |
1591 ui.callhooks = True | |
1592 ctx = repo[node] | |
1593 | |
1594 # Participating changesets: | |
1595 # | |
1596 # node/ctx o - new (intermediate) commit that contains changes | |
1597 # | from working dir to go into amending commit | |
1598 # | (or a workingctx if there were no changes) | |
1599 # | | |
1600 # old o - changeset to amend | |
1601 # | | |
1602 # base o - parent of amending changeset | |
1603 | |
1604 # Update extra dict from amended commit (e.g. to preserve graft | |
1605 # source) | |
1606 extra.update(old.extra()) | |
1607 | |
1608 # Also update it from the intermediate commit or from the wctx | |
1609 extra.update(ctx.extra()) | |
1610 | |
1611 files = set(old.files()) | |
1612 | |
1613 # Second, we use either the commit we just did, or if there were no | |
1614 # changes the parent of the working directory as the version of the | |
1615 # files in the final amend commit | |
1616 if node: | |
1617 ui.note(_('copying changeset %s to %s\n') % (ctx, base)) | |
1618 | |
1619 user = ctx.user() | |
1620 date = ctx.date() | |
1621 message = ctx.description() | |
1622 # Recompute copies (avoid recording a -> b -> a) | |
1623 copied = copies.pathcopies(base, ctx) | |
1624 | |
1625 # Prune files which were reverted by the updates: if old | |
1626 # introduced file X and our intermediate commit, node, | |
1627 # renamed that file, then those two files are the same and | |
1628 # we can discard X from our list of files. Likewise if X | |
1629 # was deleted, it's no longer relevant | |
1630 files.update(ctx.files()) | |
1631 | |
1632 def samefile(f): | |
1633 if f in ctx.manifest(): | |
1634 a = ctx.filectx(f) | |
1635 if f in base.manifest(): | |
1636 b = base.filectx(f) | |
1637 return (not a.cmp(b) | |
1638 and a.flags() == b.flags()) | |
1639 else: | |
1640 return False | |
1641 else: | |
1642 return f not in base.manifest() | |
1643 files = [f for f in files if not samefile(f)] | |
1644 | |
1645 def filectxfn(repo, ctx_, path): | |
1646 try: | |
1647 fctx = ctx[path] | |
1648 flags = fctx.flags() | |
1649 mctx = context.memfilectx(fctx.path(), fctx.data(), | |
1650 islink='l' in flags, | |
1651 isexec='x' in flags, | |
1652 copied=copied.get(path)) | |
1653 return mctx | |
1654 except KeyError: | |
1655 raise IOError | |
1656 else: | |
1657 ui.note(_('copying changeset %s to %s\n') % (old, base)) | |
1658 | |
1659 # Use version of files as in the old cset | |
1660 def filectxfn(repo, ctx_, path): | |
1661 try: | |
1662 return old.filectx(path) | |
1663 except KeyError: | |
1664 raise IOError | |
1665 | |
1666 # See if we got a message from -m or -l, if not, open the editor | |
1667 # with the message of the changeset to amend | |
1668 user = opts.get('user') or old.user() | |
1669 date = opts.get('date') or old.date() | |
1670 message = logmessage(ui, opts) | |
1671 if not message: | |
1672 cctx = context.workingctx(repo, old.description(), | |
1673 user, date, extra, | |
1674 repo.status(base.node(), | |
1675 old.node())) | |
1676 message = commitforceeditor(repo, cctx, []) | |
1677 | |
1678 new = context.memctx(repo, | |
1679 parents=[base.node(), nullid], | |
1680 text=message, | |
1681 files=files, | |
1682 filectxfn=filectxfn, | |
1683 user=user, | |
1684 date=date, | |
1685 extra=extra) | |
1686 ph = repo.ui.config('phases', 'new-commit', phases.draft) | |
1687 try: | |
1688 repo.ui.setconfig('phases', 'new-commit', old.phase()) | |
1689 newid = repo.commitctx(new) | |
1690 finally: | |
1691 repo.ui.setconfig('phases', 'new-commit', ph) | |
1692 if newid != old.node(): | |
1693 # Reroute the working copy parent to the new changeset | |
1694 repo.setparents(newid, nullid) | |
1695 | |
1696 # Move bookmarks from old parent to amend commit | |
1697 bms = repo.nodebookmarks(old.node()) | |
1698 if bms: | |
1699 for bm in bms: | |
1700 repo._bookmarks[bm] = newid | |
1701 bookmarks.write(repo) | |
1702 #commit the whole amend process | |
1703 tr.close() | |
1588 finally: | 1704 finally: |
1589 ui.callhooks = True | 1705 tr.release() |
1590 ctx = repo[node] | 1706 # Strip the intermediate commit (if there was one) and the amended |
1591 | 1707 # commit |
1592 # Participating changesets: | |
1593 # | |
1594 # node/ctx o - new (intermediate) commit that contains changes from | |
1595 # | working dir to go into amending commit (or a workingctx | |
1596 # | if there were no changes) | |
1597 # | | |
1598 # old o - changeset to amend | |
1599 # | | |
1600 # base o - parent of amending changeset | |
1601 | |
1602 # Update extra dict from amended commit (e.g. to preserve graft source) | |
1603 extra.update(old.extra()) | |
1604 | |
1605 # Also update it from the intermediate commit or from the wctx | |
1606 extra.update(ctx.extra()) | |
1607 | |
1608 files = set(old.files()) | |
1609 | |
1610 # Second, we use either the commit we just did, or if there were no | |
1611 # changes the parent of the working directory as the version of the | |
1612 # files in the final amend commit | |
1613 if node: | |
1614 ui.note(_('copying changeset %s to %s\n') % (ctx, base)) | |
1615 | |
1616 user = ctx.user() | |
1617 date = ctx.date() | |
1618 message = ctx.description() | |
1619 # Recompute copies (avoid recording a -> b -> a) | |
1620 copied = copies.pathcopies(base, ctx) | |
1621 | |
1622 # Prune files which were reverted by the updates: if old introduced | |
1623 # file X and our intermediate commit, node, renamed that file, then | |
1624 # those two files are the same and we can discard X from our list | |
1625 # of files. Likewise if X was deleted, it's no longer relevant | |
1626 files.update(ctx.files()) | |
1627 | |
1628 def samefile(f): | |
1629 if f in ctx.manifest(): | |
1630 a = ctx.filectx(f) | |
1631 if f in base.manifest(): | |
1632 b = base.filectx(f) | |
1633 return (not a.cmp(b) | |
1634 and a.flags() == b.flags()) | |
1635 else: | |
1636 return False | |
1637 else: | |
1638 return f not in base.manifest() | |
1639 files = [f for f in files if not samefile(f)] | |
1640 | |
1641 def filectxfn(repo, ctx_, path): | |
1642 try: | |
1643 fctx = ctx[path] | |
1644 flags = fctx.flags() | |
1645 mctx = context.memfilectx(fctx.path(), fctx.data(), | |
1646 islink='l' in flags, | |
1647 isexec='x' in flags, | |
1648 copied=copied.get(path)) | |
1649 return mctx | |
1650 except KeyError: | |
1651 raise IOError | |
1652 else: | |
1653 ui.note(_('copying changeset %s to %s\n') % (old, base)) | |
1654 | |
1655 # Use version of files as in the old cset | |
1656 def filectxfn(repo, ctx_, path): | |
1657 try: | |
1658 return old.filectx(path) | |
1659 except KeyError: | |
1660 raise IOError | |
1661 | |
1662 # See if we got a message from -m or -l, if not, open the editor | |
1663 # with the message of the changeset to amend | |
1664 user = opts.get('user') or old.user() | |
1665 date = opts.get('date') or old.date() | |
1666 message = logmessage(ui, opts) | |
1667 if not message: | |
1668 cctx = context.workingctx(repo, old.description(), user, date, | |
1669 extra, | |
1670 repo.status(base.node(), old.node())) | |
1671 message = commitforceeditor(repo, cctx, []) | |
1672 | |
1673 new = context.memctx(repo, | |
1674 parents=[base.node(), nullid], | |
1675 text=message, | |
1676 files=files, | |
1677 filectxfn=filectxfn, | |
1678 user=user, | |
1679 date=date, | |
1680 extra=extra) | |
1681 ph = repo.ui.config('phases', 'new-commit', phases.draft) | |
1682 try: | |
1683 repo.ui.setconfig('phases', 'new-commit', old.phase()) | |
1684 newid = repo.commitctx(new) | |
1685 finally: | |
1686 repo.ui.setconfig('phases', 'new-commit', ph) | |
1687 if newid != old.node(): | 1708 if newid != old.node(): |
1688 # Reroute the working copy parent to the new changeset | |
1689 repo.setparents(newid, nullid) | |
1690 | |
1691 # Move bookmarks from old parent to amend commit | |
1692 bms = repo.nodebookmarks(old.node()) | |
1693 if bms: | |
1694 for bm in bms: | |
1695 repo._bookmarks[bm] = newid | |
1696 bookmarks.write(repo) | |
1697 | |
1698 # Strip the intermediate commit (if there was one) and the amended | |
1699 # commit | |
1700 if node: | 1709 if node: |
1701 ui.note(_('stripping intermediate changeset %s\n') % ctx) | 1710 ui.note(_('stripping intermediate changeset %s\n') % ctx) |
1702 ui.note(_('stripping amended changeset %s\n') % old) | 1711 ui.note(_('stripping amended changeset %s\n') % old) |
1703 repair.strip(ui, repo, old.node(), topic='amend-backup') | 1712 repair.strip(ui, repo, old.node(), topic='amend-backup') |
1704 finally: | 1713 finally: |