зеркало из https://github.com/microsoft/git.git
Update git-apply to use C-style quoting for funny pathnames.
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Родитель
4f6fbcdcf9
Коммит
22943f1a52
228
apply.c
228
apply.c
|
@ -8,12 +8,13 @@
|
|||
*/
|
||||
#include <fnmatch.h>
|
||||
#include "cache.h"
|
||||
#include "quote.h"
|
||||
|
||||
// --check turns on checking that the working tree matches the
|
||||
// files that are being modified, but doesn't apply the patch
|
||||
// --stat does just a diffstat, and doesn't actually apply
|
||||
// --show-files shows the directory changes
|
||||
// --show-index-info shows the old and new index info for paths if available.
|
||||
// --index-info shows the old and new index info for paths if available.
|
||||
//
|
||||
static int check_index = 0;
|
||||
static int write_index = 0;
|
||||
|
@ -23,8 +24,9 @@ static int check = 0;
|
|||
static int apply = 1;
|
||||
static int show_files = 0;
|
||||
static int show_index_info = 0;
|
||||
static int line_termination = '\n';
|
||||
static const char apply_usage[] =
|
||||
"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--show-index-info] <patch>...";
|
||||
"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--index-info] [-z] <patch>...";
|
||||
|
||||
/*
|
||||
* For "diff-stat" like behaviour, we keep track of the biggest change
|
||||
|
@ -135,6 +137,35 @@ static char * find_name(const char *line, char *def, int p_value, int terminate)
|
|||
const char *start = line;
|
||||
char *name;
|
||||
|
||||
if (*line == '"') {
|
||||
/* Proposed "new-style" GNU patch/diff format; see
|
||||
* http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
|
||||
*/
|
||||
name = unquote_c_style(line, NULL);
|
||||
if (name) {
|
||||
char *cp = name;
|
||||
while (p_value) {
|
||||
cp = strchr(name, '/');
|
||||
if (!cp)
|
||||
break;
|
||||
cp++;
|
||||
p_value--;
|
||||
}
|
||||
if (cp) {
|
||||
/* name can later be freed, so we need
|
||||
* to memmove, not just return cp
|
||||
*/
|
||||
memmove(name, cp, strlen(cp) + 1);
|
||||
free(def);
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
char c = *line;
|
||||
|
||||
|
@ -224,37 +255,29 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
|
|||
*/
|
||||
static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
|
||||
{
|
||||
int len;
|
||||
const char *name;
|
||||
|
||||
if (!orig_name && !isnull)
|
||||
return find_name(line, NULL, 1, 0);
|
||||
|
||||
name = "/dev/null";
|
||||
len = 9;
|
||||
if (orig_name) {
|
||||
int len;
|
||||
const char *name;
|
||||
char *another;
|
||||
name = orig_name;
|
||||
len = strlen(name);
|
||||
if (isnull)
|
||||
die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
|
||||
}
|
||||
|
||||
if (*name == '/')
|
||||
goto absolute_path;
|
||||
|
||||
for (;;) {
|
||||
char c = *line++;
|
||||
if (c == '\n')
|
||||
break;
|
||||
if (c != '/')
|
||||
continue;
|
||||
absolute_path:
|
||||
if (memcmp(line, name, len) || line[len] != '\n')
|
||||
break;
|
||||
another = find_name(line, NULL, 1, 0);
|
||||
if (!another || memcmp(another, name, len))
|
||||
die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
|
||||
free(another);
|
||||
return orig_name;
|
||||
}
|
||||
die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
|
||||
return NULL;
|
||||
else {
|
||||
/* expect "/dev/null" */
|
||||
if (memcmp("/dev/null", line, 9) || line[9] != '\n')
|
||||
die("git-apply: bad git-diff - expected /dev/null on line %d", linenr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gitdiff_oldname(const char *line, struct patch *patch)
|
||||
|
@ -378,29 +401,124 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static char *git_header_name(char *line)
|
||||
static const char *stop_at_slash(const char *line, int llen)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < llen; i++) {
|
||||
int ch = line[i];
|
||||
if (ch == '/')
|
||||
return line + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is to extract the same name that appears on "diff --git"
|
||||
* line. We do not find and return anything if it is a rename
|
||||
* patch, and it is OK because we will find the name elsewhere.
|
||||
* We need to reliably find name only when it is mode-change only,
|
||||
* creation or deletion of an empty file. In any of these cases,
|
||||
* both sides are the same name under a/ and b/ respectively.
|
||||
*/
|
||||
static char *git_header_name(char *line, int llen)
|
||||
{
|
||||
int len;
|
||||
char *name, *second;
|
||||
const char *name;
|
||||
const char *second = NULL;
|
||||
|
||||
/*
|
||||
* Find the first '/'
|
||||
*/
|
||||
name = line;
|
||||
for (;;) {
|
||||
char c = *name++;
|
||||
if (c == '\n')
|
||||
line += strlen("diff --git ");
|
||||
llen -= strlen("diff --git ");
|
||||
|
||||
if (*line == '"') {
|
||||
const char *cp;
|
||||
char *first = unquote_c_style(line, &second);
|
||||
if (!first)
|
||||
return NULL;
|
||||
if (c == '/')
|
||||
break;
|
||||
|
||||
/* advance to the first slash */
|
||||
cp = stop_at_slash(first, strlen(first));
|
||||
if (!cp || cp == first) {
|
||||
/* we do not accept absolute paths */
|
||||
free_first_and_fail:
|
||||
free(first);
|
||||
return NULL;
|
||||
}
|
||||
len = strlen(cp+1);
|
||||
memmove(first, cp+1, len+1); /* including NUL */
|
||||
|
||||
/* second points at one past closing dq of name.
|
||||
* find the second name.
|
||||
*/
|
||||
while ((second < line + llen) && isspace(*second))
|
||||
second++;
|
||||
|
||||
if (line + llen <= second)
|
||||
goto free_first_and_fail;
|
||||
if (*second == '"') {
|
||||
char *sp = unquote_c_style(second, NULL);
|
||||
if (!sp)
|
||||
goto free_first_and_fail;
|
||||
cp = stop_at_slash(sp, strlen(sp));
|
||||
if (!cp || cp == sp) {
|
||||
free_both_and_fail:
|
||||
free(sp);
|
||||
goto free_first_and_fail;
|
||||
}
|
||||
/* They must match, otherwise ignore */
|
||||
if (strcmp(cp+1, first))
|
||||
goto free_both_and_fail;
|
||||
free(sp);
|
||||
return first;
|
||||
}
|
||||
|
||||
/* unquoted second */
|
||||
cp = stop_at_slash(second, line + llen - second);
|
||||
if (!cp || cp == second)
|
||||
goto free_first_and_fail;
|
||||
cp++;
|
||||
if (line + llen - cp != len + 1 ||
|
||||
memcmp(first, cp, len))
|
||||
goto free_first_and_fail;
|
||||
return first;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't accept absolute paths (/dev/null) as possibly valid
|
||||
*/
|
||||
if (name == line+1)
|
||||
/* unquoted first name */
|
||||
name = stop_at_slash(line, llen);
|
||||
if (!name || name == line)
|
||||
return NULL;
|
||||
|
||||
name++;
|
||||
|
||||
/* since the first name is unquoted, a dq if exists must be
|
||||
* the beginning of the second name.
|
||||
*/
|
||||
for (second = name; second < line + llen; second++) {
|
||||
if (*second == '"') {
|
||||
const char *cp = second;
|
||||
const char *np;
|
||||
char *sp = unquote_c_style(second, NULL);
|
||||
|
||||
if (!sp)
|
||||
return NULL;
|
||||
np = stop_at_slash(sp, strlen(sp));
|
||||
if (!np || np == sp) {
|
||||
free_second_and_fail:
|
||||
free(sp);
|
||||
return NULL;
|
||||
}
|
||||
np++;
|
||||
len = strlen(np);
|
||||
if (len < cp - name &&
|
||||
!strncmp(np, name, len) &&
|
||||
isspace(name[len])) {
|
||||
/* Good */
|
||||
memmove(sp, np, len + 1);
|
||||
return sp;
|
||||
}
|
||||
goto free_second_and_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Accept a name only if it shows up twice, exactly the same
|
||||
* form.
|
||||
|
@ -448,7 +566,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
|
|||
* or removing or adding empty files), so we get
|
||||
* the default name from the header.
|
||||
*/
|
||||
patch->def_name = git_header_name(line + strlen("diff --git "));
|
||||
patch->def_name = git_header_name(line, len);
|
||||
|
||||
line += len;
|
||||
size -= len;
|
||||
|
@ -785,11 +903,18 @@ static void show_stats(struct patch *patch)
|
|||
{
|
||||
const char *prefix = "";
|
||||
char *name = patch->new_name;
|
||||
char *qname = NULL;
|
||||
int len, max, add, del, total;
|
||||
|
||||
if (!name)
|
||||
name = patch->old_name;
|
||||
|
||||
if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
|
||||
qname = xmalloc(len + 1);
|
||||
quote_c_style(name, qname, NULL, 0);
|
||||
name = qname;
|
||||
}
|
||||
|
||||
/*
|
||||
* "scale" the filename
|
||||
*/
|
||||
|
@ -827,6 +952,8 @@ static void show_stats(struct patch *patch)
|
|||
printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
|
||||
len, name, patch->lines_added + patch->lines_deleted,
|
||||
add, pluses, del, minuses);
|
||||
if (qname)
|
||||
free(qname);
|
||||
}
|
||||
|
||||
static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
|
||||
|
@ -1197,8 +1324,13 @@ static void show_index_list(struct patch *list)
|
|||
name);
|
||||
else
|
||||
sha1_ptr = sha1;
|
||||
printf("%06o %s %s\n",patch->old_mode,
|
||||
sha1_to_hex(sha1_ptr), name);
|
||||
|
||||
printf("%06o %s ",patch->old_mode, sha1_to_hex(sha1_ptr));
|
||||
if (line_termination && quote_c_style(name, NULL, NULL, 0))
|
||||
quote_c_style(name, NULL, stdout, 0);
|
||||
else
|
||||
fputs(name, stdout);
|
||||
putchar(line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1301,12 +1433,16 @@ static void patch_stats(struct patch *patch)
|
|||
if (lines > max_change)
|
||||
max_change = lines;
|
||||
if (patch->old_name) {
|
||||
int len = strlen(patch->old_name);
|
||||
int len = quote_c_style(patch->old_name, NULL, NULL, 0);
|
||||
if (!len)
|
||||
len = strlen(patch->old_name);
|
||||
if (len > max_len)
|
||||
max_len = len;
|
||||
}
|
||||
if (patch->new_name) {
|
||||
int len = strlen(patch->new_name);
|
||||
int len = quote_c_style(patch->new_name, NULL, NULL, 0);
|
||||
if (!len)
|
||||
len = strlen(patch->new_name);
|
||||
if (len > max_len)
|
||||
max_len = len;
|
||||
}
|
||||
|
@ -1603,11 +1739,15 @@ int main(int argc, char **argv)
|
|||
show_files = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--show-index-info")) {
|
||||
if (!strcmp(arg, "--index-info")) {
|
||||
apply = 0;
|
||||
show_index_info = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-z")) {
|
||||
line_termination = 0;
|
||||
continue;
|
||||
}
|
||||
fd = open(arg, O_RDONLY);
|
||||
if (fd < 0)
|
||||
usage(apply_usage);
|
||||
|
|
|
@ -35,10 +35,10 @@ fall_back_3way () {
|
|||
mkdir "$dotest/patch-merge-tmp-dir"
|
||||
|
||||
# First see if the patch records the index info that we can use.
|
||||
if git-apply --show-index-info "$dotest/patch" \
|
||||
if git-apply -z --index-info "$dotest/patch" \
|
||||
>"$dotest/patch-merge-index-info" 2>/dev/null &&
|
||||
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git-update-index --index-info <"$dotest/patch-merge-index-info" &&
|
||||
git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
|
||||
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git-write-tree >"$dotest/patch-merge-base+" &&
|
||||
# index has the base tree now.
|
||||
|
|
Загрузка…
Ссылка в новой задаче