Hi,

When two hunks in a patch have the exact same context, and in the
file that the patch applies to one of the two contexts has changed
a bit (in such a way that hunk #1 would apply fine with fuzz 1),
then *both* hunks mistakenly get applied to the place where the
context hasn't changed.

For an example, see the attached file and patch.  Apply the patch
with 'patch -p1 <goes-wrong.patch' and see the output:

patching file testor.c
Hunk #1 succeeded at 44 (offset 37 lines).

It does not say anything about hunk #2 -- which is wrong, because
after applying hunk #1 in the wrong place, the context for hunk #2
has changed, and it can only apply the second hunk with fuzz 2.


Expected behavior: at the very least, 'patch' should mention that
hunk #2 was applied with fuzz 2.  And maybe patching should fail
entirely, because when both hunks get applied to the same spot,
also hunk #1 has in the end result factually been applied with
fuzz 2.

The ideal behavior would be that 'patch' tries to minimize the
overall amount of fuzz.  When it notices that applying hunk #1
with an offset of 37 lines results in a total fuzz of 2 (or 4),
it should try to apply hunk #1 with fuzz 1.  When it does that,
it will see that hunk #2 applies cleanly, so the total amount
of fuzz will be just 1, which is better than 2.  So the fuzz 1
alternative should be chosen.

(I could also imagine that patch would make use of the function
name mentioned in the @@ line: applying a hunk outside of that
function would count as fuzz 1.5, so that it becomes better to
accept a little fuzz within a function than to accept a perfect
match outside of that function.)

Benno
/* Try to move up nrows softwrapped chunks from the given line and the
 * given column (leftedge).  After moving, leftedge will be set to the
 * starting column of the current chunk.  Return the number of chunks we
 * couldn't move up, which will be zero if we completely succeeded. */
int go_back_chunks(int nrows, filestruct **line, size_t *leftedge)
{
    int i;

#ifndef NANO_TINY
    if (ISSET(SOFTWRAP)) {
	/* Recede through the requested number of chunks. */
	for (i = nrows; i > 0; i--) {
	    size_t chunk = chunk_for(*leftedge, *line);

	    *leftedge = 0;

	    if (chunk >= i)
		return go_forward_chunks(chunk - i, line, leftedge);

	    if (*line == openfile->fileage)
		break;

	    i -= chunk;
	    *line = (*line)->prev;
	    *leftedge = HIGHEST_POSITIVE;
	}

	if (*leftedge == HIGHEST_POSITIVE)
	    *leftedge = leftedge_for(*leftedge, *line);
    } else
#endif
	for (i = nrows; i > 0 && (*line)->prev != NULL; i--)
	    *line = (*line)->prev;

    return i;
}

/* Try to move down nrows softwrapped chunks from the given line and the
 * given column (leftedge).  After moving, leftedge will be set to the
 * starting column of the current chunk.  Return the number of chunks we
 * couldn't move down, which will be zero if we completely succeeded. */
int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge)
{
    int i;

    /* This is the FIRST hunk, meant for go_back... */
    if (xxx)
	yyy;

    /* This is the SECOND hunk, meant for go_forward... */
    if (oops)
	not again;

#ifndef NANO_TINY
    if (ISSET(SOFTWRAP)) {
	size_t current_leftedge = *leftedge;

	/* Advance through the requested number of chunks. */
	for (i = nrows; i > 0; i--) {
	    bool end_of_line = FALSE;

	    current_leftedge = get_softwrap_breakpoint((*line)->data,
					current_leftedge, &end_of_line);

	    if (!end_of_line)
		continue;

	    if (*line == openfile->filebot)
		break;

	    *line = (*line)->next;
	    current_leftedge = 0;
	}

	/* Only change leftedge when we actually could move. */
	if (i < nrows)
	    *leftedge = current_leftedge;
    } else
#endif
	for (i = nrows; i > 0 && (*line)->next != NULL; i--)
	    *line = (*line)->next;

    return i;
}

/* Return TRUE if there are fewer than a screen's worth of lines between
 * the line at line number was_lineno (and column was_leftedge, if we're
 * in softwrap mode) and the line at current[current_x]. */
bool less_than_a_screenful(size_t was_lineno, size_t was_leftedge)
{
#ifndef NANO_TINY
    if (ISSET(SOFTWRAP)) {
	filestruct *line = openfile->current;
	size_t leftedge = leftedge_for(xplustabs(), openfile->current);
	int rows_left = go_back_chunks(editwinrows - 1, &line, &leftedge);

	return (rows_left > 0 || line->lineno < was_lineno ||
		(line->lineno == was_lineno && leftedge <= was_leftedge));
    } else
#endif
	return (openfile->current->lineno - was_lineno < editwinrows);
}
diff --git a/testor.c b/testor.c
index d9bde426..7b309d73 100644
--- a/testor.c
+++ b/testor.c
@@ -7,6 +7,10 @@ int go_back_chunks(int nrows, filestruct **line, size_t *leftedge)
 {
     int i;
 
+    /* This is the FIRST hunk, meant for go_back... */
+    if (xxx)
+	yyy;
+
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
 	size_t current_leftedge = *leftedge;
@@ -44,6 +48,10 @@ int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge)
 {
     int i;
 
+    /* This is the SECOND hunk, meant for go_forward... */
+    if (oops)
+	not again;
+
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
 	size_t current_leftedge = *leftedge;

Reply via email to