Skip to content

Conversation

dscho
Copy link
Member

@dscho dscho commented Apr 8, 2020

This corner case came up a couple of times in my personal usage of git stash -p: I wanted to stash only part of a split hunk, and it didn't work.

I originally intended to do a full fix (for details, see the commit message of the second patch), but in the year since I wrote this patch pair, I found the current work-around plenty satisfactory.

dscho added 2 commits April 8, 2020 19:58
In 7e9e048 (stash -p: demonstrate failure of split with mixed y/n,
2015-04-16), a regression test for a known breakage that was added to
the test script `t3904-stash-patch.sh` that demonstrated that splitting
a hunk and trying to stash only part of that split hunk fails (but
shouldn't).

As expected, it still fails, but for the wrong reason: once the bug is
fixed, we would expect stderr to show nothing, yet the regression test
expects stderr to show something.

Let's fix that by telling that regression test case to expect nothing to
be printed to stderr.

While at it, also drop the obvious left-over from debugging where the
regression test did not mind `git stash -p` to return a non-zero exit
status.

Of course, the regression test still fails, but this time for the
correct reason.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When trying to stash part of the worktree changes by splitting a hunk
and then only partially accepting the split bits and pieces, the user
is presented with a rather cryptic error:

	error: patch failed: <file>:<line>
	error: test: patch does not apply
	Cannot remove worktree changes

and the command would fail to stash the desired parts of the worktree
changes (even if the `stash` ref was actually updated correctly).

We even have a test case demonstrating that failure, carrying it for
four years already.

The explanation: when splitting a hunk, the changed lines are no longer
separated by more than 3 lines (which is the amount of context lines
Git's diffs use by default), but less than that. So when staging only
part of the diff hunk for stashing, the resulting diff that we want to
apply to the worktree in reverse will contain those changes to be
dropped surrounded by three context lines, but since the diff is
relative to HEAD rather than to the worktree, these context lines will
not match.

Example time. Let's assume that the file README contains these lines:

	We
	the
	people

and the worktree added some lines so that it contains these lines
instead:

	We
	are
	the
	kind
	people

and the user tries to stash the line containing "are", then the command
will internally stage this line to a temporary index file and try to
revert the diff between HEAD and that index file. The diff hunk that
`git stash` tries to revert will look somewhat like this:

	@@ -1776,3 +1776,4
	 We
	+are
	 the
	 people

It is obvious, now, that the trailing context lines overlap with the
part of the original diff hunk that the user did *not* want to stash.

Keeping in mind that context lines in diffs serve the primary purpose of
finding the exact location when the diff does not apply precisely (but
when the exact line number in the file to be patched differs from the
line number indicated in the diff), we work around this by reducing the
amount of context lines: the diff was just generated.

Note: this is not a *full* fix for the issue. Just as demonstrated in
t3701's 'add -p works with pathological context lines' test case, there
are ambiguities in the diff format. It is very rare in practice, of
course, to encounter such repeated lines.

The full solution for such cases would be to replace the approach of
generating a diff from the stash and then applying it in reverse by
emulating `git revert` (i.e. doing a 3-way merge). However, in `git
stash -p` it would not apply to `HEAD` but instead to the worktree,
which makes this non-trivial to implement as long as we also maintain a
scripted version of `add -i`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho
Copy link
Member Author

dscho commented Apr 8, 2020

/submit

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 8, 2020

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 8, 2020

This branch is now known as js/stash-p-fix.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 8, 2020

This patch series was integrated into pu via git@5e55a9f.

@gitgitgadget gitgitgadget bot added the pu label Apr 8, 2020
@gitgitgadget
Copy link

gitgitgadget bot commented Apr 9, 2020

This patch series was integrated into pu via git@503f577.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 9, 2020

This patch series was integrated into pu via git@88ec648.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 10, 2020

This patch series was integrated into pu via git@c363b17.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 10, 2020

This patch series was integrated into pu via git@67bf5c6.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 10, 2020

This patch series was integrated into pu via git@aba56a1.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 11, 2020

This patch series was integrated into pu via git@3368f88.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 12, 2020

This patch series was integrated into pu via git@07b9d9a.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 13, 2020

This patch series was integrated into pu via git@c68ca94.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 14, 2020

This patch series was integrated into pu via git@f6b2d52.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 15, 2020

This patch series was integrated into pu via git@6dd0025.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 15, 2020

This patch series was integrated into pu via git@c58d47d.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 18, 2020

This patch series was integrated into pu via git@13a8e7d.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 20, 2020

This patch series was integrated into pu via git@e71e511.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 21, 2020

This patch series was integrated into pu via git@0eedf3e.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 21, 2020

This patch series was integrated into next via git@435bf60.

@gitgitgadget gitgitgadget bot added the next label Apr 21, 2020
@gitgitgadget
Copy link

gitgitgadget bot commented Apr 24, 2020

This patch series was integrated into pu via git@2fe0e92.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 28, 2020

This patch series was integrated into pu via git@0b6b30e.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 28, 2020

This patch series was integrated into pu via git@44e7ce4.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 29, 2020

This patch series was integrated into pu via git@e81ecff.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 29, 2020

This patch series was integrated into next via git@e81ecff.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 29, 2020

This patch series was integrated into master via git@e81ecff.

@gitgitgadget gitgitgadget bot added the master label Apr 29, 2020
@gitgitgadget gitgitgadget bot closed this Apr 29, 2020
@gitgitgadget
Copy link

gitgitgadget bot commented Apr 29, 2020

Closed via e81ecff.

@dscho dscho deleted the stash-p-corner-case branch April 29, 2020 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant