Skip to content

Commit

Permalink
apply: use the right attribute for paths in non-Git patches
Browse files Browse the repository at this point in the history
We parse each patchfile and find the name of the path the patch
applies to, and then use that name to consult the attribute system
to find the whitespace rules to be used, and also the target file
(either in the working tree or in the index) to replay the changes
against.

Unlike a Git-generated patch, a non-Git patch is taken to have the
pathnames relative to the current working directory.  The names
found in such a patch are modified by prepending the prefix by the
prefix_patches() helper function introduced in 56185f4 (git-apply:
require -p<n> when working in a subdirectory., 2007-02-19).

However, this prefixing is done after the patch is fully parsed and
affects only what target files are patched.  Because the attributes
are checked against the names found in the patch during the parsing,
not against the final pathname, the whitespace check that is done
during parsing ends up using attributes for a wrong path for non-Git
patches.

Fix this by doing the prefix much earlier, immediately after the
header part of each patch is parsed and we learn the name of the
path the patch affects.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Junio C Hamano committed Aug 7, 2014
1 parent d31f3ad commit d487b0b
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 22 deletions.
41 changes: 19 additions & 22 deletions builtin/apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -1920,6 +1920,23 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
return used;
}

static void prefix_one(char **name)
{
char *old_name = *name;
if (!old_name)
return;
*name = xstrdup(prefix_filename(prefix, prefix_length, *name));
free(old_name);
}

static void prefix_patch(struct patch *p)
{
if (!prefix || p->is_toplevel_relative)
return;
prefix_one(&p->new_name);
prefix_one(&p->old_name);
}

/*
* Read the patch text in "buffer" that extends for "size" bytes; stop
* reading after seeing a single patch (i.e. changes to a single file).
Expand All @@ -1935,6 +1952,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
if (offset < 0)
return offset;

prefix_patch(patch);

patch->ws_rule = whitespace_rule(patch->new_name
? patch->new_name
: patch->old_name);
Expand Down Expand Up @@ -4164,26 +4183,6 @@ static int use_patch(struct patch *p)
return !has_include;
}


static void prefix_one(char **name)
{
char *old_name = *name;
if (!old_name)
return;
*name = xstrdup(prefix_filename(prefix, prefix_length, *name));
free(old_name);
}

static void prefix_patches(struct patch *p)
{
if (!prefix || p->is_toplevel_relative)
return;
for ( ; p; p = p->next) {
prefix_one(&p->new_name);
prefix_one(&p->old_name);
}
}

#define INACCURATE_EOF (1<<0)
#define RECOUNT (1<<1)

Expand All @@ -4209,8 +4208,6 @@ static int apply_patch(int fd, const char *filename, int options)
break;
if (apply_in_reverse)
reverse_patches(patch);
if (prefix)
prefix_patches(patch);
if (use_patch(patch)) {
patch_stats(patch);
*listp = patch;
Expand Down
17 changes: 17 additions & 0 deletions t/t4119-apply-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,21 @@ test_expect_success 'same but with traditional patch input of depth 2' '
check_result sub/file1
'

test_expect_success 'in subdir with traditional patch input' '
cd "$D" &&
git config apply.whitespace strip &&
cat >.gitattributes <<-EOF &&
/* whitespace=blank-at-eol
sub/* whitespace=-blank-at-eol
EOF
rm -f sub/file1 &&
cp saved sub/file1 &&
git update-index --refresh &&
cd sub &&
git apply ../gpatch.file &&
echo "B " >expect &&
test_cmp expect file1
'

test_done

0 comments on commit d487b0b

Please sign in to comment.