# HG changeset patch # User Daniel Ploch # Date 2021-07-02 18:44:13 # Node ID 00ae1fb6c4591818e65d8fa2a27ddcb930dc711a # Parent 57bdecf4322ca0d901f17955c7c8372cdc310593 cmdutil: fix newandmodified file accounting for --interactive commits `originalchunks` is a misleading name, because it only contains header objects, which are flattened to selected hunks by the filter function. As such, `chunks not in originalchunks` is always True and misleading, because hunk objects never compare equal to header objects. This change fixes the internal naming and removes the useless parameter from the method. This change also fixes issue6533, by considering the filtered headers, rather than the hunks, when determining new and modified files. If a file is renamed + edited, and the edited hunks are deselected (but the file is not), the filtered chunks will contain a header for the file (because it's .special()) but but no hunks. Differential Revision: https://phab.mercurial-scm.org/D10936 diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -346,19 +346,18 @@ def ishunk(x): return isinstance(x, hunkclasses) -def newandmodified(chunks, originalchunks): +def isheader(x): + headerclasses = (crecordmod.uiheader, patch.header) + return isinstance(x, headerclasses) + + +def newandmodified(chunks): newlyaddedandmodifiedfiles = set() alsorestore = set() for chunk in chunks: - if ( - ishunk(chunk) - and chunk.header.isnewfile() - and chunk not in originalchunks - ): - newlyaddedandmodifiedfiles.add(chunk.header.filename()) - alsorestore.update( - set(chunk.header.files()) - {chunk.header.filename()} - ) + if isheader(chunk) and chunk.isnewfile(): + newlyaddedandmodifiedfiles.add(chunk.filename()) + alsorestore.update(set(chunk.files()) - {chunk.filename()}) return newlyaddedandmodifiedfiles, alsorestore @@ -517,12 +516,12 @@ def dorecord( diffopts.git = True diffopts.showfunc = True originaldiff = patch.diff(repo, changes=status, opts=diffopts) - originalchunks = patch.parsepatch(originaldiff) + original_headers = patch.parsepatch(originaldiff) match = scmutil.match(repo[None], pats) # 1. filter patch, since we are intending to apply subset of it try: - chunks, newopts = filterfn(ui, originalchunks, match) + chunks, newopts = filterfn(ui, original_headers, match) except error.PatchError as err: raise error.InputError(_(b'error parsing patch: %s') % err) opts.update(newopts) @@ -532,15 +531,11 @@ def dorecord( # version without the edit in the workdir. We also will need to restore # files that were the sources of renames so that the patch application # works. - newlyaddedandmodifiedfiles, alsorestore = newandmodified( - chunks, originalchunks - ) + newlyaddedandmodifiedfiles, alsorestore = newandmodified(chunks) contenders = set() for h in chunks: - try: + if isheader(h): contenders.update(set(h.files())) - except AttributeError: - pass changed = status.modified + status.added + status.removed newfiles = [f for f in changed if f in contenders] @@ -3630,12 +3625,12 @@ def _performrevert( diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts) else: diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts) - originalchunks = patch.parsepatch(diff) + original_headers = patch.parsepatch(diff) try: chunks, opts = recordfilter( - repo.ui, originalchunks, match, operation=operation + repo.ui, original_headers, match, operation=operation ) if operation == b'discard': chunks = patch.reversehunks(chunks) @@ -3648,9 +3643,7 @@ def _performrevert( # "remove added file (Yn)?", so we don't need to worry about the # alsorestore value. Ideally we'd be able to partially revert # copied/renamed files. - newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified( - chunks, originalchunks - ) + newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(chunks) if tobackup is None: tobackup = set() # Apply changes diff --git a/tests/test-commit-interactive.t b/tests/test-commit-interactive.t --- a/tests/test-commit-interactive.t +++ b/tests/test-commit-interactive.t @@ -1713,20 +1713,58 @@ Moving files record this change to 'plain3'? (enter ? for help) [Ynesfdaq?] y + +Rename file but discard edits + + $ echo content > new-file + $ hg add -q new-file + $ hg commit -qm 'new file' + $ hg mv new-file renamed-file + $ echo new-content >> renamed-file + $ hg commit -i -d '24 0' -m content-rename< y + > n + > EOF + diff --git a/new-file b/renamed-file + rename from new-file + rename to renamed-file + 1 hunks, 1 lines changed + examine changes to 'new-file' and 'renamed-file'? + (enter ? for help) [Ynesfdaq?] y + + @@ -1,1 +1,2 @@ + content + +new-content + record this change to 'renamed-file'? + (enter ? for help) [Ynesfdaq?] n + + $ hg status + M renamed-file + ? editedfile.orig + ? editedfile.rej + ? editor.sh + $ hg diff + diff -r * renamed-file (glob) + --- a/renamed-file Thu Jan 01 00:00:24 1970 +0000 + +++ b/renamed-file Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,2 @@ + content + +new-content + The #if execbit block above changes the hash here on some systems $ hg status -A plain3 C plain3 $ hg tip - changeset: 32:* (glob) + changeset: 34:* (glob) tag: tip user: test - date: Thu Jan 01 00:00:23 1970 +0000 - summary: moving_files + date: Thu Jan 01 00:00:24 1970 +0000 + summary: content-rename Editing patch of newly added file $ hg update -C . - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat > editor.sh << '__EOF__' > cat "$1" | sed "s/first/very/g" > tt > mv tt "$1" @@ -1737,7 +1775,7 @@ Editing patch of newly added file > This is the third line > __EOF__ $ hg add newfile - $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -d '23 0' -medit-patch-new < y > e > EOF @@ -1770,7 +1808,7 @@ Add new file from within a subdirectory $ cd folder $ echo "foo" > bar $ hg add bar - $ hg commit -i -d '23 0' -mnewfilesubdir < y > y > EOF @@ -1786,15 +1824,15 @@ Add new file from within a subdirectory The #if execbit block above changes the hashes here on some systems $ hg tip -p - changeset: 34:* (glob) + changeset: 36:* (glob) tag: tip user: test - date: Thu Jan 01 00:00:23 1970 +0000 + date: Thu Jan 01 00:00:26 1970 +0000 summary: newfilesubdir diff -r * -r * folder/bar (glob) --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/folder/bar Thu Jan 01 00:00:23 1970 +0000 + +++ b/folder/bar Thu Jan 01 00:00:26 1970 +0000 @@ -0,0 +1,1 @@ +foo