# HG changeset patch # User Patrick Mezard # Date 1339500519 -7200 # Node ID 24943df310d47a503222331d700e205d291ada8f # Parent 1c21865bf8ba6fcc99ae1be4e4a1a12b290dcab3 amend: do not traceback on no-ops If rewrite() generated changeset happened to be an existing one, the call would traceback when trying to obsolete the changeset with itself. Instead, leave gracefully, marking any intermediate changeset extinct. diff -r 1c21865bf8ba -r 24943df310d4 hgext/evolve.py --- a/hgext/evolve.py Tue Jun 12 11:53:02 2012 +0200 +++ b/hgext/evolve.py Tue Jun 12 13:28:39 2012 +0200 @@ -57,6 +57,11 @@ ############################# def rewrite(repo, old, updates, head, newbases, commitopts): + """Return (nodeid, created) where nodeid is the identifier of the + changeset generated by the rewrite process, and created is True if + nodeid was actually created. If created is False, nodeid + references a changeset existing before the rewrite call. + """ if len(old.parents()) > 1: #XXX remove this unecessary limitation. raise error.Abort(_('cannot amend merge changesets')) base = old.p1() @@ -126,29 +131,35 @@ if commitopts.get('edit'): new._text = cmdutil.commitforceeditor(repo, new, []) + revcount = len(repo) newid = repo.commitctx(new) new = repo[newid] - - # update the bookmark - if bm: - repo._bookmarks[bm] = newid - bookmarks.write(repo) + created = len(repo) != revcount + if created: + # update the bookmark + if bm: + repo._bookmarks[bm] = newid + bookmarks.write(repo) - # add evolution metadata - repo.addobsolete(new.node(), old.node()) - for u in updates: - repo.addobsolete(u.node(), old.node()) - repo.addobsolete(new.node(), u.node()) - oldbookmarks = repo.nodebookmarks(old.node()) - for book in oldbookmarks: - repo._bookmarks[book] = new.node() - if oldbookmarks: - bookmarks.write(repo) - + # add evolution metadata + repo.addobsolete(new.node(), old.node()) + for u in updates: + repo.addobsolete(u.node(), old.node()) + repo.addobsolete(new.node(), u.node()) + oldbookmarks = repo.nodebookmarks(old.node()) + for book in oldbookmarks: + repo._bookmarks[book] = new.node() + if oldbookmarks: + bookmarks.write(repo) + else: + # newid is an existing revision. It could make sense to + # replace revisions with existing ones but probably not by + # default. + pass finally: wlock.release() - return newid + return newid, created def relocate(repo, orig, dest): """rewrite on dest""" @@ -407,7 +418,11 @@ def commitfunc(ui, repo, message, match, opts): return repo.commit(message, opts.get('user'), opts.get('date'), match, editor=e) - cmdutil.commit(ui, repo, commitfunc, pats, ciopts) + revcount = len(repo) + tempid = cmdutil.commit(ui, repo, commitfunc, pats, ciopts) + if len(repo) == revcount: + # No revision created + tempid = None # find all changesets to be considered updates cl = repo.changelog @@ -429,13 +444,20 @@ # perform amend if opts.get('edit'): opts['force_editor'] = True - newid = rewrite(repo, old, updates, head, - [old.p1().node(), old.p2().node()], opts) - - # reroute the working copy parent to the new changeset - phases.retractboundary(repo, oldphase, [newid]) - repo.dirstate.setparents(newid, node.nullid) - + newid, created = rewrite(repo, old, updates, head, + [old.p1().node(), old.p2().node()], opts) + if created: + # reroute the working copy parent to the new changeset + phases.retractboundary(repo, oldphase, [newid]) + repo.dirstate.setparents(newid, node.nullid) + else: + # rewrite() recreated an existing revision, discard + # the intermediate revision if any. No need to update + # phases or parents. + if tempid is not None: + repo.addobsolete(node.nullid, tempid) + # XXX: need another message in collapse case. + raise error.Abort(_('no updates found')) finally: wlock.release() finally: diff -r 1c21865bf8ba -r 24943df310d4 tests/test-amend.t --- a/tests/test-amend.t Tue Jun 12 11:53:02 2012 +0200 +++ b/tests/test-amend.t Tue Jun 12 13:28:39 2012 +0200 @@ -8,6 +8,10 @@ $ echo "obsolete=$(echo $(dirname $TESTDIR))/hgext/obsolete.py" >> $HGRCPATH $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + $ glog() { + > hg glog --template '{rev}@{branch}({phase}) {desc|firstline}\n' "$@" + > } + $ hg init repo $ cd repo $ echo a > a @@ -25,7 +29,67 @@ $ hg branches foo 2:a34b93d251e4 default 0:07f494440405 (inactive) - $ hg glog --template '{rev}@{branch} {desc|firstline}\n' - @ 2@foo adda + $ glog + @ 2@foo(draft) adda + +Test no-op + + $ hg amend + abort: no updates found + [255] + $ glog + @ 2@foo(draft) adda + + +Test forcing the message to the same value, no intermediate revision. + + $ hg amend -m 'adda' + abort: no updates found + [255] + $ glog + @ 2@foo(draft) adda +Test collapsing into an existing revision, no intermediate revision. + + $ echo a >> a + $ hg ci -m changea + $ echo a > a + $ hg ci -m reseta + $ hg amend --change 2 + abort: no updates found + [255] + $ hg phase 2 + 2: draft + $ glog + @ 4@foo(draft) reseta + | + o 3@foo(draft) changea + | + o 2@foo(draft) adda + + +Test collapsing into an existing rev, with an intermediate revision. + + $ hg branch --force default + marked working directory as branch default + (branches are permanent and global, did you want a bookmark?) + $ hg ci -m resetbranch + created new head + $ hg branch --force foo + marked working directory as branch foo + (branches are permanent and global, did you want a bookmark?) + $ hg amend --change 2 + abort: no updates found + [255] + $ glog + @ 6@foo(secret) amends a34b93d251e49c93d5685ebacad785c73a7e8605 + | + o 5@default(draft) resetbranch + | + o 4@foo(draft) reseta + | + o 3@foo(draft) changea + | + o 2@foo(draft) adda +