# HG changeset patch # User Pierre-Yves David # Date 1348248271 -7200 # Node ID 9ae073f10572be67bb809f4e9100f3e9d9a56e1d # Parent 64e0f0cfb569da2c7221f36da6816ecb667dc1f8 histedit: fold in memory Update the folding code to works in memory instead of applying patches on the working directory. This is cleaner, faster and prepare the removal of the whole patching logic. This new collapse function will probably move into core sooner or later. A lot of other rewriting operation may benefit from it. diff -r 64e0f0cfb569 -r 9ae073f10572 hgext/histedit.py --- a/hgext/histedit.py Fri Sep 21 00:33:30 2012 +0200 +++ b/hgext/histedit.py Fri Sep 21 19:24:31 2012 +0200 @@ -149,6 +149,8 @@ from mercurial import cmdutil from mercurial import discovery from mercurial import error +from mercurial import copies +from mercurial import context from mercurial import hg from mercurial import lock as lockmod from mercurial import node @@ -195,6 +197,76 @@ os.unlink(patchfile) return files +def collapse(repo, first, last, commitopts): + """collapse the set of revisions from first to last as new one. + + Expected commit options are: + - message + - date + - username + Edition of commit message is trigered in all case. + + This function works in memory.""" + ctxs = list(repo.set('%d::%d', first, last)) + if not ctxs: + return None + base = first.parents()[0] + + # commit a new version of the old changeset, including the update + # collect all files which might be affected + files = set() + for ctx in ctxs: + files.update(ctx.files()) + + # Recompute copies (avoid recording a -> b -> a) + copied = copies.pathcopies(first, last) + + # prune files which were reverted by the updates + def samefile(f): + if f in last.manifest(): + a = last.filectx(f) + if f in base.manifest(): + b = base.filectx(f) + return (a.data() == b.data() + and a.flags() == b.flags()) + else: + return False + else: + return f not in base.manifest() + files = [f for f in files if not samefile(f)] + # commit version of these files as defined by head + headmf = last.manifest() + def filectxfn(repo, ctx, path): + if path in headmf: + fctx = last[path] + flags = fctx.flags() + mctx = context.memfilectx(fctx.path(), fctx.data(), + islink='l' in flags, + isexec='x' in flags, + copied=copied.get(path)) + return mctx + raise IOError() + + if commitopts.get('message'): + message = commitopts['message'] + else: + message = first.description() + user = commitopts.get('user') + date = commitopts.get('date') + extra = first.extra() + + parents = (first.p1().node(), first.p2().node()) + new = context.memctx(repo, + parents=parents, + text=message, + files=files, + filectxfn=filectxfn, + user=user, + date=date, + extra=extra) + new._text = cmdutil.commitforceeditor(repo, new, []) + return repo.commitctx(new) + def pick(ui, repo, ctx, ha, opts): oldctx = repo[ha] if oldctx.parents()[0] == ctx: @@ -245,19 +317,26 @@ def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): parent = ctx.parents()[0].node() hg.update(repo, parent) - foldchanges(ui, repo, parent, newnode, opts) + ### prepare new commit data + commitopts = opts.copy() + # username + if ctx.user() == oldctx.user(): + username = ctx.user() + else: + username = ui.username() + commitopts['user'] = username + # commit message newmessage = '\n***\n'.join( [ctx.description()] + [repo[r].description() for r in internalchanges] + [oldctx.description()]) + '\n' - # If the changesets are from the same author, keep it. - if ctx.user() == oldctx.user(): - username = ctx.user() - else: - username = ui.username() - newmessage = ui.edit(newmessage, username) - n = repo.commit(text=newmessage, user=username, - date=max(ctx.date(), oldctx.date()), extra=oldctx.extra()) + commitopts['message'] = newmessage + # date + commitopts['date'] = max(ctx.date(), oldctx.date()) + n = collapse(repo, ctx, repo[newnode], commitopts) + if n is None: + return ctx, [], [], [] + hg.update(repo, n) return repo[n], [n], [oldctx.node(), ctx.node()], [newnode] def drop(ui, repo, ctx, ha, opts): diff -r 64e0f0cfb569 -r 9ae073f10572 tests/test-histedit-fold-non-commute.t --- a/tests/test-histedit-fold-non-commute.t Fri Sep 21 00:33:30 2012 +0200 +++ b/tests/test-histedit-fold-non-commute.t Fri Sep 21 19:24:31 2012 +0200 @@ -92,6 +92,17 @@ + HG: Enter commit message. Lines beginning with 'HG:' are removed. + HG: Leave message empty to abort commit. + HG: -- + HG: user: test + HG: branch 'default' + HG: changed d + HG: changed e + + + + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved file e already exists 1 out of 1 hunks FAILED -- saving rejects to file e.rej diff -r 64e0f0cfb569 -r 9ae073f10572 tests/test-histedit-fold.t --- a/tests/test-histedit-fold.t Fri Sep 21 00:33:30 2012 +0200 +++ b/tests/test-histedit-fold.t Fri Sep 21 19:24:31 2012 +0200 @@ -66,6 +66,7 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -232,6 +233,16 @@ +5.2 *** +6 + + + + HG: Enter commit message. Lines beginning with 'HG:' are removed. + HG: Leave message empty to abort commit. + HG: -- + HG: user: test + HG: branch 'default' + HG: changed file + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob) $ cd ..