From dc7b24364dcbe9ab74d937c92af9999ac1a2db0b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 Feb 2007 13:12:52 -0800 Subject: Teach 'git apply' to look at $GIT_DIR/config When neither --index nor --cached was used, git-apply did not try calling setup_git_directory(), which means it did not look at configuration files at all. This fixes it to call the setup function but still allow the command to be run in a directory not controlled by git. The bug probably meant that 'git apply', not moving up to the toplevel, did not apply properly formatted diffs from the toplevel when you are inside a subdirectory, even though 'git apply --index' would. As a side effect, this patch fixes it as well. Signed-off-by: Junio C Hamano --- builtin-apply.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 3fefdacd9..fc1d6730d 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2595,9 +2595,18 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) int read_stdin = 1; int inaccurate_eof = 0; int errs = 0; + int is_not_gitdir = 0; const char *whitespace_option = NULL; + prefix = setup_git_directory_gently(&is_not_gitdir); + prefix_length = prefix ? strlen(prefix) : 0; + if (!is_not_gitdir) { + git_config(git_apply_config); + if (apply_default_whitespace) + parse_whitespace_option(apply_default_whitespace); + } + for (i = 1; i < argc; i++) { const char *arg = argv[i]; char *end; @@ -2648,10 +2657,14 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) continue; } if (!strcmp(arg, "--index")) { + if (is_not_gitdir) + die("--index outside a repository"); check_index = 1; continue; } if (!strcmp(arg, "--cached")) { + if (is_not_gitdir) + die("--cached outside a repository"); check_index = 1; cached = 1; continue; @@ -2700,14 +2713,6 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) inaccurate_eof = 1; continue; } - - if (check_index && prefix_length < 0) { - prefix = setup_git_directory(); - prefix_length = prefix ? strlen(prefix) : 0; - git_config(git_apply_config); - if (!whitespace_option && apply_default_whitespace) - parse_whitespace_option(apply_default_whitespace); - } if (0 < prefix_length) arg = prefix_filename(prefix, prefix_length, arg); -- cgit v1.2.1 From 6716027108f426c83038b05baf3f20ceefe6fbd1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 Feb 2007 12:37:25 -0800 Subject: Teach core.autocrlf to 'git apply' This teaches git-apply that the data read from and written to the filesystem might need to get converted to adjust for local line-ending convention. Signed-off-by: Junio C Hamano --- builtin-apply.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 3fefdacd9..45c4acbd2 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1393,28 +1393,39 @@ static void show_stats(struct patch *patch) free(qname); } -static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size) +static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p) { int fd; unsigned long got; + unsigned long nsize; + char *nbuf; + unsigned long size = *size_p; + char *buf = *buf_p; switch (st->st_mode & S_IFMT) { case S_IFLNK: - return readlink(path, buf, size); + return readlink(path, buf, size) != size; case S_IFREG: fd = open(path, O_RDONLY); if (fd < 0) return error("unable to open %s", path); got = 0; for (;;) { - int ret = xread(fd, (char *) buf + got, size - got); + int ret = xread(fd, buf + got, size - got); if (ret <= 0) break; got += ret; } close(fd); - return got; - + nsize = got; + nbuf = buf; + if (convert_to_git(path, &nbuf, &nsize)) { + free(buf); + *buf_p = nbuf; + *alloc_p = nsize; + *size_p = nsize; + } + return got != size; default: return -1; } @@ -1910,7 +1921,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * size = st->st_size; alloc = size + 8192; buf = xmalloc(alloc); - if (read_old_data(st, patch->old_name, buf, alloc) != size) + if (read_old_data(st, patch->old_name, &buf, &alloc, &size)) return error("read of %s failed", patch->old_name); } @@ -2282,12 +2293,22 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { int fd; + char *nbuf; + unsigned long nsize; if (S_ISLNK(mode)) /* Although buf:size is counted string, it also is NUL * terminated. */ return symlink(buf, path); + nsize = size; + nbuf = (char *) buf; + if (convert_to_working_tree(path, &nbuf, &nsize)) { + free((char *) buf); + buf = nbuf; + size = nsize; + } + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666); if (fd < 0) return -1; @@ -2598,6 +2619,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) const char *whitespace_option = NULL; + for (i = 1; i < argc; i++) { const char *arg = argv[i]; char *end; -- cgit v1.2.1 From 700ea47936e393a217f4174f80a19aa01f698b56 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 Feb 2007 18:12:46 -0800 Subject: Teach 'git apply' to look at $HOME/.gitconfig even outside of a repository Signed-off-by: Junio C Hamano --- builtin-apply.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index fc1d6730d..2784db2ef 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2601,11 +2601,9 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) prefix = setup_git_directory_gently(&is_not_gitdir); prefix_length = prefix ? strlen(prefix) : 0; - if (!is_not_gitdir) { - git_config(git_apply_config); - if (apply_default_whitespace) - parse_whitespace_option(apply_default_whitespace); - } + git_config(git_apply_config); + if (apply_default_whitespace) + parse_whitespace_option(apply_default_whitespace); for (i = 1; i < argc; i++) { const char *arg = argv[i]; -- cgit v1.2.1 From aea1945744214bf84908586af8be1c098a6f346d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Feb 2007 17:58:58 -0800 Subject: git-apply: do not lose cwd when run from a subdirectory. When a patch modifies (not deletes) the last file in a directory, because we treat a modification just as deletion followed by creation, and deleting the last file in a directory automatically rmdir(2)'s that directory, we ended up removing the directory, which can potentially be the cwd, and then recreating the same directory to create the patch result. Avoid the rmdir step when remove_file() is called only because we are replacing it with the result by later calling create_file(). Signed-off-by: Junio C Hamano --- builtin-apply.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 2784db2ef..3f829fb66 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2232,7 +2232,7 @@ static void patch_stats(struct patch *patch) } } -static void remove_file(struct patch *patch) +static void remove_file(struct patch *patch, int rmdir_empty) { if (write_index) { if (remove_file_from_cache(patch->old_name) < 0) @@ -2240,7 +2240,7 @@ static void remove_file(struct patch *patch) cache_tree_invalidate_path(active_cache_tree, patch->old_name); } if (!cached) { - if (!unlink(patch->old_name)) { + if (!unlink(patch->old_name) && rmdir_empty) { char *name = xstrdup(patch->old_name); char *end = strrchr(name, '/'); while (end) { @@ -2373,7 +2373,7 @@ static void write_out_one_result(struct patch *patch, int phase) { if (patch->is_delete > 0) { if (phase == 0) - remove_file(patch); + remove_file(patch, 1); return; } if (patch->is_new > 0 || patch->is_copy) { @@ -2386,7 +2386,7 @@ static void write_out_one_result(struct patch *patch, int phase) * thing: remove the old, write the new */ if (phase == 0) - remove_file(patch); + remove_file(patch, 0); if (phase == 1) create_file(patch); } -- cgit v1.2.1 From 56185f49d03cae28048146e902089ea366c6cd6c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Feb 2007 17:57:29 -0800 Subject: git-apply: require -p when working in a subdirectory. git-apply running inside a subdirectory, with or without --index, used to always assume that the patch is formatted in such a way to apply with -p1 from the toplevel, but it is more useful and consistent with the use of "GNU patch -p1" if it defaulted to assume that its input is meant to apply at the level it is invoked in. This changes the behaviour. It used to be that the patch generated this way would apply without any trick: edit Documentation/Makefile git diff >patch.file cd Documentation git apply ../patch.file You need to give an explicit -p2 to git-apply now. On the other hand, if you got a patch from somebody else who did not follow "patch is to apply from the top with -p1" convention, the input patch would start with: diff -u Makefile.old Makefile --- Makefile.old +++ Makefile and in such a case, you can apply it with: git apply -p0 patch.file Signed-off-by: Junio C Hamano --- builtin-apply.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 3f829fb66..053511e2e 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -238,7 +238,7 @@ static int name_terminate(const char *name, int namelen, int c, int terminate) return 1; } -static char * find_name(const char *line, char *def, int p_value, int terminate) +static char *find_name(const char *line, char *def, int p_value, int terminate) { int len; const char *start = line; @@ -362,7 +362,7 @@ 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) { if (!orig_name && !isnull) - return find_name(line, NULL, 1, TERM_TAB); + return find_name(line, NULL, p_value, TERM_TAB); if (orig_name) { int len; @@ -372,7 +372,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, len = strlen(name); if (isnull) die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); - another = find_name(line, NULL, 1, TERM_TAB); + another = find_name(line, NULL, p_value, TERM_TAB); if (!another || memcmp(another, name, len)) die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); free(another); @@ -427,28 +427,28 @@ static int gitdiff_newfile(const char *line, struct patch *patch) static int gitdiff_copysrc(const char *line, struct patch *patch) { patch->is_copy = 1; - patch->old_name = find_name(line, NULL, 0, 0); + patch->old_name = find_name(line, NULL, p_value-1, 0); return 0; } static int gitdiff_copydst(const char *line, struct patch *patch) { patch->is_copy = 1; - patch->new_name = find_name(line, NULL, 0, 0); + patch->new_name = find_name(line, NULL, p_value-1, 0); return 0; } static int gitdiff_renamesrc(const char *line, struct patch *patch) { patch->is_rename = 1; - patch->old_name = find_name(line, NULL, 0, 0); + patch->old_name = find_name(line, NULL, p_value-1, 0); return 0; } static int gitdiff_renamedst(const char *line, struct patch *patch) { patch->is_rename = 1; - patch->new_name = find_name(line, NULL, 0, 0); + patch->new_name = find_name(line, NULL, p_value-1, 0); return 0; } @@ -2499,15 +2499,26 @@ static int use_patch(struct patch *p) return 0; x = x->next; } - if (0 < prefix_length) { - int pathlen = strlen(pathname); - if (pathlen <= prefix_length || - memcmp(prefix, pathname, prefix_length)) - return 0; - } return 1; } +static char *prefix_one(char *name) +{ + if (!name) + return name; + return xstrdup(prefix_filename(prefix, prefix_length, name)); +} + +static void prefix_patches(struct patch *p) +{ + if (!prefix) + return; + for ( ; p; p = p->next) { + p->new_name = prefix_one(p->new_name); + p->old_name = prefix_one(p->old_name); + } +} + static int apply_patch(int fd, const char *filename, int inaccurate_eof) { unsigned long offset, size; @@ -2530,11 +2541,14 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) break; if (apply_in_reverse) reverse_patches(patch); + if (prefix) + prefix_patches(patch); if (use_patch(patch)) { patch_stats(patch); *listp = patch; listp = &patch->next; - } else { + } + else { /* perhaps free it a bit better? */ free(patch); skipped_patch++; -- cgit v1.2.1 From eac70c4f64a618744e05d4a5be61a356c0011033 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 20 Feb 2007 03:45:49 +0100 Subject: apply: fix memory leak in prefix_one() Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-apply.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 053511e2e..2a40af3ff 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2502,11 +2502,13 @@ static int use_patch(struct patch *p) return 1; } -static char *prefix_one(char *name) +static void prefix_one(char **name) { - if (!name) - return name; - return xstrdup(prefix_filename(prefix, prefix_length, 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) @@ -2514,8 +2516,9 @@ static void prefix_patches(struct patch *p) if (!prefix) return; for ( ; p; p = p->next) { - p->new_name = prefix_one(p->new_name); - p->old_name = prefix_one(p->old_name); + if (p->new_name != p->old_name) + prefix_one(&p->new_name); + prefix_one(&p->old_name); } } -- cgit v1.2.1 From 6c912f5b04e3216a5487e03235a8454b754a464e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Feb 2007 00:58:18 -0800 Subject: Fix botched "leak fix" When (new_name == old_name), the previous one prefixed old_name alone, leaving new_name untouched, and worse yet, left it dangling pointing at an already freed memory location. Signed-off-by: Junio C Hamano --- builtin-apply.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 2a40af3ff..1beebe5ff 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2516,9 +2516,15 @@ static void prefix_patches(struct patch *p) if (!prefix) return; for ( ; p; p = p->next) { - if (p->new_name != p->old_name) + if (p->new_name == p->old_name) { + char *prefixed = p->new_name; + prefix_one(&prefixed); + p->new_name = p->old_name = prefixed; + } + else { prefix_one(&p->new_name); - prefix_one(&p->old_name); + prefix_one(&p->old_name); + } } } -- cgit v1.2.1 From 9987d7c58a847ab1605ae3216ff1ca95b19f0ad1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Feb 2007 14:31:10 -0800 Subject: git-apply: notice "diff --git" patch again Earlier one that tried to be too consistent with GNU patch by not stripping the leading path when we _know_ we are in a subdirectory and the patch is relative to the toplevel was a mistake. This fixes it. - No change to behaviour when it is run from the toplevel of the repository. - When run from a subdirectory to apply a git-generated patch, it uses the right -p value automatically, with or without --index nor --cached option. - When run from a subdirectory to apply a randomly generated patch, it wants the right -p value to be given by the user. The second one is a pure improvement to correct inconsistency between --index and non --index case, compared with 1.5.0. The third point could be further improved to guess what the right value for -p should be by looking at the patch, but should be a topic of a separate patch. Signed-off-by: Junio C Hamano --- builtin-apply.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 1beebe5ff..12f00e38d 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -144,6 +144,7 @@ struct patch { unsigned long deflate_origlen; int lines_added, lines_deleted; int score; + unsigned int is_toplevel_relative:1; unsigned int inaccurate_eof:1; unsigned int is_binary:1; unsigned int is_copy:1; @@ -362,7 +363,7 @@ 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) { if (!orig_name && !isnull) - return find_name(line, NULL, p_value, TERM_TAB); + return find_name(line, NULL, 1, TERM_TAB); if (orig_name) { int len; @@ -372,7 +373,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, len = strlen(name); if (isnull) die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); - another = find_name(line, NULL, p_value, TERM_TAB); + another = find_name(line, NULL, 1, TERM_TAB); if (!another || memcmp(another, name, len)) die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); free(another); @@ -427,28 +428,28 @@ static int gitdiff_newfile(const char *line, struct patch *patch) static int gitdiff_copysrc(const char *line, struct patch *patch) { patch->is_copy = 1; - patch->old_name = find_name(line, NULL, p_value-1, 0); + patch->old_name = find_name(line, NULL, 0, 0); return 0; } static int gitdiff_copydst(const char *line, struct patch *patch) { patch->is_copy = 1; - patch->new_name = find_name(line, NULL, p_value-1, 0); + patch->new_name = find_name(line, NULL, 0, 0); return 0; } static int gitdiff_renamesrc(const char *line, struct patch *patch) { patch->is_rename = 1; - patch->old_name = find_name(line, NULL, p_value-1, 0); + patch->old_name = find_name(line, NULL, 0, 0); return 0; } static int gitdiff_renamedst(const char *line, struct patch *patch) { patch->is_rename = 1; - patch->new_name = find_name(line, NULL, p_value-1, 0); + patch->new_name = find_name(line, NULL, 0, 0); return 0; } @@ -787,6 +788,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc { unsigned long offset, len; + patch->is_toplevel_relative = 0; patch->is_rename = patch->is_copy = 0; patch->is_new = patch->is_delete = -1; patch->old_mode = patch->new_mode = 0; @@ -831,6 +833,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc die("git diff header lacks filename information (line %d)", linenr); patch->old_name = patch->new_name = patch->def_name; } + patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; } @@ -2499,6 +2502,12 @@ static int use_patch(struct patch *p) return 0; x = x->next; } + if (0 < prefix_length) { + int pathlen = strlen(pathname); + if (pathlen <= prefix_length || + memcmp(prefix, pathname, prefix_length)) + return 0; + } return 1; } @@ -2513,7 +2522,7 @@ static void prefix_one(char **name) static void prefix_patches(struct patch *p) { - if (!prefix) + if (!prefix || p->is_toplevel_relative) return; for ( ; p; p = p->next) { if (p->new_name == p->old_name) { -- cgit v1.2.1 From 3e8a5db966c26a0a986161103d59683b909a6c78 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Feb 2007 16:05:56 -0800 Subject: git-apply: guess correct -p value for non-git patches. This enhances the third point in the previous commit. When applying a non-git patch that begins like this: --- 2.6.orig/mm/slab.c +++ 2.6/mm/slab.c @@ -N,M +L,K @@@ ... and if you are in 'mm' subdirectory, we notice that -p2 is the right option to use to apply the patch in file slab.c in the current directory (i.e. mm/slab.c) The guess function also knows about this pattern, where you would need to use -p0 if applying from the top-level: --- mm/slab.c +++ mm/slab.c @@ -N,M +L,K @@@ ... Signed-off-by: Junio C Hamano --- builtin-apply.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) (limited to 'builtin-apply.c') diff --git a/builtin-apply.c b/builtin-apply.c index 12f00e38d..c7d4bdd47 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -28,6 +28,7 @@ static int newfd = -1; static int unidiff_zero; static int p_value = 1; +static int p_value_known; static int check_index; static int write_index; static int cached; @@ -312,11 +313,54 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) return name; } +static int count_slashes(const char *cp) +{ + int cnt = 0; + char ch; + + while ((ch = *cp++)) + if (ch == '/') + cnt++; + return cnt; +} + +/* + * Given the string after "--- " or "+++ ", guess the appropriate + * p_value for the given patch. + */ +static int guess_p_value(const char *nameline) +{ + char *name, *cp; + int val = -1; + + if (is_dev_null(nameline)) + return -1; + name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB); + if (!name) + return -1; + cp = strchr(name, '/'); + if (!cp) + val = 0; + else if (prefix) { + /* + * Does it begin with "a/$our-prefix" and such? Then this is + * very likely to apply to our directory. + */ + if (!strncmp(name, prefix, prefix_length)) + val = count_slashes(prefix); + else { + cp++; + if (!strncmp(cp, prefix, prefix_length)) + val = count_slashes(prefix) + 1; + } + } + free(name); + return val; +} + /* * Get the name etc info from the --/+++ lines of a traditional patch header * - * NOTE! This hardcodes "-p1" behaviour in filename detection. - * * FIXME! The end-of-filename heuristics are kind of screwy. For existing * files, we can happily check the index for a match, but for creating a * new file we should try to match whatever "patch" does. I have no idea. @@ -327,6 +371,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc first += 4; /* skip "--- " */ second += 4; /* skip "+++ " */ + if (!p_value_known) { + int p, q; + p = guess_p_value(first); + q = guess_p_value(second); + if (p < 0) p = q; + if (0 <= p && p == q) { + p_value = p; + p_value_known = 1; + } + } if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; @@ -2656,6 +2710,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) } if (!strncmp(arg, "-p", 2)) { p_value = atoi(arg + 2); + p_value_known = 1; continue; } if (!strcmp(arg, "--no-add")) { -- cgit v1.2.1