diff options
Diffstat (limited to 'builtin/apply.c')
-rw-r--r-- | builtin/apply.c | 4861 |
1 files changed, 9 insertions, 4852 deletions
diff --git a/builtin/apply.c b/builtin/apply.c index 1a488f9e88..81b9a61c37 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1,4793 +1,16 @@ -/* - * apply.c - * - * Copyright (C) Linus Torvalds, 2005 - * - * This applies patches on top of some (arbitrary) version of the SCM. - * - */ #include "cache.h" -#include "lockfile.h" -#include "cache-tree.h" -#include "quote.h" -#include "blob.h" -#include "delta.h" #include "builtin.h" -#include "string-list.h" -#include "dir.h" -#include "diff.h" #include "parse-options.h" -#include "xdiff-interface.h" -#include "ll-merge.h" -#include "rerere.h" - -enum ws_error_action { - nowarn_ws_error, - warn_on_ws_error, - die_on_ws_error, - correct_ws_error -}; - - -enum ws_ignore { - ignore_ws_none, - ignore_ws_change -}; - -/* - * We need to keep track of how symlinks in the preimage are - * manipulated by the patches. A patch to add a/b/c where a/b - * is a symlink should not be allowed to affect the directory - * the symlink points at, but if the same patch removes a/b, - * it is perfectly fine, as the patch removes a/b to make room - * to create a directory a/b so that a/b/c can be created. - * - * See also "struct string_list symlink_changes" in "struct - * apply_state". - */ -#define SYMLINK_GOES_AWAY 01 -#define SYMLINK_IN_RESULT 02 - -struct apply_state { - const char *prefix; - int prefix_length; - - /* These are lock_file related */ - struct lock_file *lock_file; - int newfd; - - /* These control what gets looked at and modified */ - int apply; /* this is not a dry-run */ - int cached; /* apply to the index only */ - int check; /* preimage must match working tree, don't actually apply */ - int check_index; /* preimage must match the indexed version */ - int update_index; /* check_index && apply */ - - /* These control cosmetic aspect of the output */ - int diffstat; /* just show a diffstat, and don't actually apply */ - int numstat; /* just show a numeric diffstat, and don't actually apply */ - int summary; /* just report creation, deletion, etc, and don't actually apply */ - - /* These boolean parameters control how the apply is done */ - int allow_overlap; - int apply_in_reverse; - int apply_with_reject; - int apply_verbosely; - int no_add; - int threeway; - int unidiff_zero; - int unsafe_paths; - - /* Other non boolean parameters */ - const char *fake_ancestor; - const char *patch_input_file; - int line_termination; - struct strbuf root; - int p_value; - int p_value_known; - unsigned int p_context; - - /* Exclude and include path parameters */ - struct string_list limit_by_name; - int has_include; - - /* Various "current state" */ - int linenr; /* current line number */ - struct string_list symlink_changes; /* we have to track symlinks */ - - /* - * For "diff-stat" like behaviour, we keep track of the biggest change - * we've seen, and the longest filename. That allows us to do simple - * scaling. - */ - int max_change; - int max_len; - - /* - * Records filenames that have been touched, in order to handle - * the case where more than one patches touch the same file. - */ - struct string_list fn_table; - - /* These control whitespace errors */ - enum ws_error_action ws_error_action; - enum ws_ignore ws_ignore_action; - const char *whitespace_option; - int whitespace_error; - int squelch_whitespace_errors; - int applied_after_fixing_ws; -}; +#include "lockfile.h" +#include "apply.h" static const char * const apply_usage[] = { N_("git apply [<options>] [<patch>...]"), NULL }; -static void parse_whitespace_option(struct apply_state *state, const char *option) -{ - if (!option) { - state->ws_error_action = warn_on_ws_error; - return; - } - if (!strcmp(option, "warn")) { - state->ws_error_action = warn_on_ws_error; - return; - } - if (!strcmp(option, "nowarn")) { - state->ws_error_action = nowarn_ws_error; - return; - } - if (!strcmp(option, "error")) { - state->ws_error_action = die_on_ws_error; - return; - } - if (!strcmp(option, "error-all")) { - state->ws_error_action = die_on_ws_error; - state->squelch_whitespace_errors = 0; - return; - } - if (!strcmp(option, "strip") || !strcmp(option, "fix")) { - state->ws_error_action = correct_ws_error; - return; - } - die(_("unrecognized whitespace option '%s'"), option); -} - -static void parse_ignorewhitespace_option(struct apply_state *state, - const char *option) -{ - if (!option || !strcmp(option, "no") || - !strcmp(option, "false") || !strcmp(option, "never") || - !strcmp(option, "none")) { - state->ws_ignore_action = ignore_ws_none; - return; - } - if (!strcmp(option, "change")) { - state->ws_ignore_action = ignore_ws_change; - return; - } - die(_("unrecognized whitespace ignore option '%s'"), option); -} - -static void set_default_whitespace_mode(struct apply_state *state) -{ - if (!state->whitespace_option && !apply_default_whitespace) - state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); -} - -/* - * This represents one "hunk" from a patch, starting with - * "@@ -oldpos,oldlines +newpos,newlines @@" marker. The - * patch text is pointed at by patch, and its byte length - * is stored in size. leading and trailing are the number - * of context lines. - */ -struct fragment { - unsigned long leading, trailing; - unsigned long oldpos, oldlines; - unsigned long newpos, newlines; - /* - * 'patch' is usually borrowed from buf in apply_patch(), - * but some codepaths store an allocated buffer. - */ - const char *patch; - unsigned free_patch:1, - rejected:1; - int size; - int linenr; - struct fragment *next; -}; - -/* - * When dealing with a binary patch, we reuse "leading" field - * to store the type of the binary hunk, either deflated "delta" - * or deflated "literal". - */ -#define binary_patch_method leading -#define BINARY_DELTA_DEFLATED 1 -#define BINARY_LITERAL_DEFLATED 2 - -/* - * This represents a "patch" to a file, both metainfo changes - * such as creation/deletion, filemode and content changes represented - * as a series of fragments. - */ -struct patch { - char *new_name, *old_name, *def_name; - unsigned int old_mode, new_mode; - int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ - int rejected; - unsigned ws_rule; - 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; - unsigned int is_rename:1; - unsigned int recount:1; - unsigned int conflicted_threeway:1; - unsigned int direct_to_threeway:1; - struct fragment *fragments; - char *result; - size_t resultsize; - char old_sha1_prefix[41]; - char new_sha1_prefix[41]; - struct patch *next; - - /* three-way fallback result */ - struct object_id threeway_stage[3]; -}; - -static void free_fragment_list(struct fragment *list) -{ - while (list) { - struct fragment *next = list->next; - if (list->free_patch) - free((char *)list->patch); - free(list); - list = next; - } -} - -static void free_patch(struct patch *patch) -{ - free_fragment_list(patch->fragments); - free(patch->def_name); - free(patch->old_name); - free(patch->new_name); - free(patch->result); - free(patch); -} - -static void free_patch_list(struct patch *list) -{ - while (list) { - struct patch *next = list->next; - free_patch(list); - list = next; - } -} - -/* - * A line in a file, len-bytes long (includes the terminating LF, - * except for an incomplete line at the end if the file ends with - * one), and its contents hashes to 'hash'. - */ -struct line { - size_t len; - unsigned hash : 24; - unsigned flag : 8; -#define LINE_COMMON 1 -#define LINE_PATCHED 2 -}; - -/* - * This represents a "file", which is an array of "lines". - */ -struct image { - char *buf; - size_t len; - size_t nr; - size_t alloc; - struct line *line_allocated; - struct line *line; -}; - -static uint32_t hash_line(const char *cp, size_t len) -{ - size_t i; - uint32_t h; - for (i = 0, h = 0; i < len; i++) { - if (!isspace(cp[i])) { - h = h * 3 + (cp[i] & 0xff); - } - } - return h; -} - -/* - * Compare lines s1 of length n1 and s2 of length n2, ignoring - * whitespace difference. Returns 1 if they match, 0 otherwise - */ -static int fuzzy_matchlines(const char *s1, size_t n1, - const char *s2, size_t n2) -{ - const char *last1 = s1 + n1 - 1; - const char *last2 = s2 + n2 - 1; - int result = 0; - - /* ignore line endings */ - while ((*last1 == '\r') || (*last1 == '\n')) - last1--; - while ((*last2 == '\r') || (*last2 == '\n')) - last2--; - - /* skip leading whitespaces, if both begin with whitespace */ - if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) { - while (isspace(*s1) && (s1 <= last1)) - s1++; - while (isspace(*s2) && (s2 <= last2)) - s2++; - } - /* early return if both lines are empty */ - if ((s1 > last1) && (s2 > last2)) - return 1; - while (!result) { - result = *s1++ - *s2++; - /* - * Skip whitespace inside. We check for whitespace on - * both buffers because we don't want "a b" to match - * "ab" - */ - if (isspace(*s1) && isspace(*s2)) { - while (isspace(*s1) && s1 <= last1) - s1++; - while (isspace(*s2) && s2 <= last2) - s2++; - } - /* - * If we reached the end on one side only, - * lines don't match - */ - if ( - ((s2 > last2) && (s1 <= last1)) || - ((s1 > last1) && (s2 <= last2))) - return 0; - if ((s1 > last1) && (s2 > last2)) - break; - } - - return !result; -} - -static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag) -{ - ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc); - img->line_allocated[img->nr].len = len; - img->line_allocated[img->nr].hash = hash_line(bol, len); - img->line_allocated[img->nr].flag = flag; - img->nr++; -} - -/* - * "buf" has the file contents to be patched (read from various sources). - * attach it to "image" and add line-based index to it. - * "image" now owns the "buf". - */ -static void prepare_image(struct image *image, char *buf, size_t len, - int prepare_linetable) -{ - const char *cp, *ep; - - memset(image, 0, sizeof(*image)); - image->buf = buf; - image->len = len; - - if (!prepare_linetable) - return; - - ep = image->buf + image->len; - cp = image->buf; - while (cp < ep) { - const char *next; - for (next = cp; next < ep && *next != '\n'; next++) - ; - if (next < ep) - next++; - add_line_info(image, cp, next - cp, 0); - cp = next; - } - image->line = image->line_allocated; -} - -static void clear_image(struct image *image) -{ - free(image->buf); - free(image->line_allocated); - memset(image, 0, sizeof(*image)); -} - -/* fmt must contain _one_ %s and no other substitution */ -static void say_patch_name(FILE *output, const char *fmt, struct patch *patch) -{ - struct strbuf sb = STRBUF_INIT; - - if (patch->old_name && patch->new_name && - strcmp(patch->old_name, patch->new_name)) { - quote_c_style(patch->old_name, &sb, NULL, 0); - strbuf_addstr(&sb, " => "); - quote_c_style(patch->new_name, &sb, NULL, 0); - } else { - const char *n = patch->new_name; - if (!n) - n = patch->old_name; - quote_c_style(n, &sb, NULL, 0); - } - fprintf(output, fmt, sb.buf); - fputc('\n', output); - strbuf_release(&sb); -} - -#define SLOP (16) - -static void read_patch_file(struct strbuf *sb, int fd) -{ - if (strbuf_read(sb, fd, 0) < 0) - die_errno("git apply: failed to read"); - - /* - * Make sure that we have some slop in the buffer - * so that we can do speculative "memcmp" etc, and - * see to it that it is NUL-filled. - */ - strbuf_grow(sb, SLOP); - memset(sb->buf + sb->len, 0, SLOP); -} - -static unsigned long linelen(const char *buffer, unsigned long size) -{ - unsigned long len = 0; - while (size--) { - len++; - if (*buffer++ == '\n') - break; - } - return len; -} - -static int is_dev_null(const char *str) -{ - return skip_prefix(str, "/dev/null", &str) && isspace(*str); -} - -#define TERM_SPACE 1 -#define TERM_TAB 2 - -static int name_terminate(int c, int terminate) -{ - if (c == ' ' && !(terminate & TERM_SPACE)) - return 0; - if (c == '\t' && !(terminate & TERM_TAB)) - return 0; - - return 1; -} - -/* remove double slashes to make --index work with such filenames */ -static char *squash_slash(char *name) -{ - int i = 0, j = 0; - - if (!name) - return NULL; - - while (name[i]) { - if ((name[j++] = name[i++]) == '/') - while (name[i] == '/') - i++; - } - name[j] = '\0'; - return name; -} - -static char *find_name_gnu(struct apply_state *state, - const char *line, - const char *def, - int p_value) -{ - struct strbuf name = STRBUF_INIT; - char *cp; - - /* - * Proposed "new-style" GNU patch/diff format; see - * http://marc.info/?l=git&m=112927316408690&w=2 - */ - if (unquote_c_style(&name, line, NULL)) { - strbuf_release(&name); - return NULL; - } - - for (cp = name.buf; p_value; p_value--) { - cp = strchr(cp, '/'); - if (!cp) { - strbuf_release(&name); - return NULL; - } - cp++; - } - - strbuf_remove(&name, 0, cp - name.buf); - if (state->root.len) - strbuf_insert(&name, 0, state->root.buf, state->root.len); - return squash_slash(strbuf_detach(&name, NULL)); -} - -static size_t sane_tz_len(const char *line, size_t len) -{ - const char *tz, *p; - - if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ') - return 0; - tz = line + len - strlen(" +0500"); - - if (tz[1] != '+' && tz[1] != '-') - return 0; - - for (p = tz + 2; p != line + len; p++) - if (!isdigit(*p)) - return 0; - - return line + len - tz; -} - -static size_t tz_with_colon_len(const char *line, size_t len) -{ - const char *tz, *p; - - if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':') - return 0; - tz = line + len - strlen(" +08:00"); - - if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-')) - return 0; - p = tz + 2; - if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || - !isdigit(*p++) || !isdigit(*p++)) - return 0; - - return line + len - tz; -} - -static size_t date_len(const char *line, size_t len) -{ - const char *date, *p; - - if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-') - return 0; - p = date = line + len - strlen("72-02-05"); - - if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' || - !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' || - !isdigit(*p++) || !isdigit(*p++)) /* Not a date. */ - return 0; - - if (date - line >= strlen("19") && - isdigit(date[-1]) && isdigit(date[-2])) /* 4-digit year */ - date -= strlen("19"); - - return line + len - date; -} - -static size_t short_time_len(const char *line, size_t len) -{ - const char *time, *p; - - if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':') - return 0; - p = time = line + len - strlen(" 07:01:32"); - - /* Permit 1-digit hours? */ - if (*p++ != ' ' || - !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || - !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || - !isdigit(*p++) || !isdigit(*p++)) /* Not a time. */ - return 0; - - return line + len - time; -} - -static size_t fractional_time_len(const char *line, size_t len) -{ - const char *p; - size_t n; - - /* Expected format: 19:41:17.620000023 */ - if (!len || !isdigit(line[len - 1])) - return 0; - p = line + len - 1; - - /* Fractional seconds. */ - while (p > line && isdigit(*p)) - p--; - if (*p != '.') - return 0; - - /* Hours, minutes, and whole seconds. */ - n = short_time_len(line, p - line); - if (!n) - return 0; - - return line + len - p + n; -} - -static size_t trailing_spaces_len(const char *line, size_t len) -{ - const char *p; - - /* Expected format: ' ' x (1 or more) */ - if (!len || line[len - 1] != ' ') - return 0; - - p = line + len; - while (p != line) { - p--; - if (*p != ' ') - return line + len - (p + 1); - } - - /* All spaces! */ - return len; -} - -static size_t diff_timestamp_len(const char *line, size_t len) -{ - const char *end = line + len; - size_t n; - - /* - * Posix: 2010-07-05 19:41:17 - * GNU: 2010-07-05 19:41:17.620000023 -0500 - */ - - if (!isdigit(end[-1])) - return 0; - - n = sane_tz_len(line, end - line); - if (!n) - n = tz_with_colon_len(line, end - line); - end -= n; - - n = short_time_len(line, end - line); - if (!n) - n = fractional_time_len(line, end - line); - end -= n; - - n = date_len(line, end - line); - if (!n) /* No date. Too bad. */ - return 0; - end -= n; - - if (end == line) /* No space before date. */ - return 0; - if (end[-1] == '\t') { /* Success! */ - end--; - return line + len - end; - } - if (end[-1] != ' ') /* No space before date. */ - return 0; - - /* Whitespace damage. */ - end -= trailing_spaces_len(line, end - line); - return line + len - end; -} - -static char *find_name_common(struct apply_state *state, - const char *line, - const char *def, - int p_value, - const char *end, - int terminate) -{ - int len; - const char *start = NULL; - - if (p_value == 0) - start = line; - while (line != end) { - char c = *line; - - if (!end && isspace(c)) { - if (c == '\n') - break; - if (name_terminate(c, terminate)) - break; - } - line++; - if (c == '/' && !--p_value) - start = line; - } - if (!start) - return squash_slash(xstrdup_or_null(def)); - len = line - start; - if (!len) - return squash_slash(xstrdup_or_null(def)); - - /* - * Generally we prefer the shorter name, especially - * if the other one is just a variation of that with - * something else tacked on to the end (ie "file.orig" - * or "file~"). - */ - if (def) { - int deflen = strlen(def); - if (deflen < len && !strncmp(start, def, deflen)) - return squash_slash(xstrdup(def)); - } - - if (state->root.len) { - char *ret = xstrfmt("%s%.*s", state->root.buf, len, start); - return squash_slash(ret); - } - - return squash_slash(xmemdupz(start, len)); -} - -static char *find_name(struct apply_state *state, - const char *line, - char *def, - int p_value, - int terminate) -{ - if (*line == '"') { - char *name = find_name_gnu(state, line, def, p_value); - if (name) - return name; - } - - return find_name_common(state, line, def, p_value, NULL, terminate); -} - -static char *find_name_traditional(struct apply_state *state, - const char *line, - char *def, - int p_value) -{ - size_t len; - size_t date_len; - - if (*line == '"') { - char *name = find_name_gnu(state, line, def, p_value); - if (name) - return name; - } - - len = strchrnul(line, '\n') - line; - date_len = diff_timestamp_len(line, len); - if (!date_len) - return find_name_common(state, line, def, p_value, NULL, TERM_TAB); - len -= date_len; - - return find_name_common(state, line, def, p_value, line + len, 0); -} - -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(struct apply_state *state, const char *nameline) -{ - char *name, *cp; - int val = -1; - - if (is_dev_null(nameline)) - return -1; - name = find_name_traditional(state, nameline, NULL, 0); - if (!name) - return -1; - cp = strchr(name, '/'); - if (!cp) - val = 0; - else if (state->prefix) { - /* - * Does it begin with "a/$our-prefix" and such? Then this is - * very likely to apply to our directory. - */ - if (!strncmp(name, state->prefix, state->prefix_length)) - val = count_slashes(state->prefix); - else { - cp++; - if (!strncmp(cp, state->prefix, state->prefix_length)) - val = count_slashes(state->prefix) + 1; - } - } - free(name); - return val; -} - -/* - * Does the ---/+++ line have the POSIX timestamp after the last HT? - * GNU diff puts epoch there to signal a creation/deletion event. Is - * this such a timestamp? - */ -static int has_epoch_timestamp(const char *nameline) -{ - /* - * We are only interested in epoch timestamp; any non-zero - * fraction cannot be one, hence "(\.0+)?" in the regexp below. - * For the same reason, the date must be either 1969-12-31 or - * 1970-01-01, and the seconds part must be "00". - */ - const char stamp_regexp[] = - "^(1969-12-31|1970-01-01)" - " " - "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?" - " " - "([-+][0-2][0-9]:?[0-5][0-9])\n"; - const char *timestamp = NULL, *cp, *colon; - static regex_t *stamp; - regmatch_t m[10]; - int zoneoffset; - int hourminute; - int status; - - for (cp = nameline; *cp != '\n'; cp++) { - if (*cp == '\t') - timestamp = cp + 1; - } - if (!timestamp) - return 0; - if (!stamp) { - stamp = xmalloc(sizeof(*stamp)); - if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) { - warning(_("Cannot prepare timestamp regexp %s"), - stamp_regexp); - return 0; - } - } - - status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0); - if (status) { - if (status != REG_NOMATCH) - warning(_("regexec returned %d for input: %s"), - status, timestamp); - return 0; - } - - zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10); - if (*colon == ':') - zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10); - else - zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100); - if (timestamp[m[3].rm_so] == '-') - zoneoffset = -zoneoffset; - - /* - * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31 - * (west of GMT) or 1970-01-01 (east of GMT) - */ - if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) || - (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10))) - return 0; - - hourminute = (strtol(timestamp + 11, NULL, 10) * 60 + - strtol(timestamp + 14, NULL, 10) - - zoneoffset); - - return ((zoneoffset < 0 && hourminute == 1440) || - (0 <= zoneoffset && !hourminute)); -} - -/* - * Get the name etc info from the ---/+++ lines of a traditional patch header - * - * 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. - */ -static void parse_traditional_patch(struct apply_state *state, - const char *first, - const char *second, - struct patch *patch) -{ - char *name; - - first += 4; /* skip "--- " */ - second += 4; /* skip "+++ " */ - if (!state->p_value_known) { - int p, q; - p = guess_p_value(state, first); - q = guess_p_value(state, second); - if (p < 0) p = q; - if (0 <= p && p == q) { - state->p_value = p; - state->p_value_known = 1; - } - } - if (is_dev_null(first)) { - patch->is_new = 1; - patch->is_delete = 0; - name = find_name_traditional(state, second, NULL, state->p_value); - patch->new_name = name; - } else if (is_dev_null(second)) { - patch->is_new = 0; - patch->is_delete = 1; - name = find_name_traditional(state, first, NULL, state->p_value); - patch->old_name = name; - } else { - char *first_name; - first_name = find_name_traditional(state, first, NULL, state->p_value); - name = find_name_traditional(state, second, first_name, state->p_value); - free(first_name); - if (has_epoch_timestamp(first)) { - patch->is_new = 1; - patch->is_delete = 0; - patch->new_name = name; - } else if (has_epoch_timestamp(second)) { - patch->is_new = 0; - patch->is_delete = 1; - patch->old_name = name; - } else { - patch->old_name = name; - patch->new_name = xstrdup_or_null(name); - } - } - if (!name) - die(_("unable to find filename in patch at line %d"), state->linenr); -} - -static int gitdiff_hdrend(struct apply_state *state, - const char *line, - struct patch *patch) -{ - return -1; -} - -/* - * We're anal about diff header consistency, to make - * sure that we don't end up having strange ambiguous - * patches floating around. - * - * As a result, gitdiff_{old|new}name() will check - * their names against any previous information, just - * to make sure.. - */ -#define DIFF_OLD_NAME 0 -#define DIFF_NEW_NAME 1 - -static void gitdiff_verify_name(struct apply_state *state, - const char *line, - int isnull, - char **name, - int side) -{ - if (!*name && !isnull) { - *name = find_name(state, line, NULL, state->p_value, TERM_TAB); - return; - } - - if (*name) { - int len = strlen(*name); - char *another; - if (isnull) - die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), - *name, state->linenr); - another = find_name(state, line, NULL, state->p_value, TERM_TAB); - if (!another || memcmp(another, *name, len + 1)) - die((side == DIFF_NEW_NAME) ? - _("git apply: bad git-diff - inconsistent new filename on line %d") : - _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); - free(another); - } 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"), state->linenr); - } -} - -static int gitdiff_oldname(struct apply_state *state, - const char *line, - struct patch *patch) -{ - gitdiff_verify_name(state, line, - patch->is_new, &patch->old_name, - DIFF_OLD_NAME); - return 0; -} - -static int gitdiff_newname(struct apply_state *state, - const char *line, - struct patch *patch) -{ - gitdiff_verify_name(state, line, - patch->is_delete, &patch->new_name, - DIFF_NEW_NAME); - return 0; -} - -static int gitdiff_oldmode(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->old_mode = strtoul(line, NULL, 8); - return 0; -} - -static int gitdiff_newmode(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->new_mode = strtoul(line, NULL, 8); - return 0; -} - -static int gitdiff_delete(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_delete = 1; - free(patch->old_name); - patch->old_name = xstrdup_or_null(patch->def_name); - return gitdiff_oldmode(state, line, patch); -} - -static int gitdiff_newfile(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_new = 1; - free(patch->new_name); - patch->new_name = xstrdup_or_null(patch->def_name); - return gitdiff_newmode(state, line, patch); -} - -static int gitdiff_copysrc(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_copy = 1; - free(patch->old_name); - patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_copydst(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_copy = 1; - free(patch->new_name); - patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_renamesrc(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_rename = 1; - free(patch->old_name); - patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_renamedst(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_rename = 1; - free(patch->new_name); - patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_similarity(struct apply_state *state, - const char *line, - struct patch *patch) -{ - unsigned long val = strtoul(line, NULL, 10); - if (val <= 100) - patch->score = val; - return 0; -} - -static int gitdiff_dissimilarity(struct apply_state *state, - const char *line, - struct patch *patch) -{ - unsigned long val = strtoul(line, NULL, 10); - if (val <= 100) - patch->score = val; - return 0; -} - -static int gitdiff_index(struct apply_state *state, - const char *line, - struct patch *patch) -{ - /* - * index line is N hexadecimal, "..", N hexadecimal, - * and optional space with octal mode. - */ - const char *ptr, *eol; - int len; - - ptr = strchr(line, '.'); - if (!ptr || ptr[1] != '.' || 40 < ptr - line) - return 0; - len = ptr - line; - memcpy(patch->old_sha1_prefix, line, len); - patch->old_sha1_prefix[len] = 0; - - line = ptr + 2; - ptr = strchr(line, ' '); - eol = strchrnul(line, '\n'); - - if (!ptr || eol < ptr) - ptr = eol; - len = ptr - line; - - if (40 < len) - return 0; - memcpy(patch->new_sha1_prefix, line, len); - patch->new_sha1_prefix[len] = 0; - if (*ptr == ' ') - patch->old_mode = strtoul(ptr+1, NULL, 8); - return 0; -} - -/* - * This is normal for a diff that doesn't change anything: we'll fall through - * into the next diff. Tell the parser to break out. - */ -static int gitdiff_unrecognized(struct apply_state *state, - const char *line, - struct patch *patch) -{ - return -1; -} - -/* - * Skip p_value leading components from "line"; as we do not accept - * absolute paths, return NULL in that case. - */ -static const char *skip_tree_prefix(struct apply_state *state, - const char *line, - int llen) -{ - int nslash; - int i; - - if (!state->p_value) - return (llen && line[0] == '/') ? NULL : line; - - nslash = state->p_value; - for (i = 0; i < llen; i++) { - int ch = line[i]; - if (ch == '/' && --nslash <= 0) - return (i == 0) ? NULL : &line[i + 1]; - } - 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(struct apply_state *state, - const char *line, - int llen) -{ - const char *name; - const char *second = NULL; - size_t len, line_len; - - line += strlen("diff --git "); - llen -= strlen("diff --git "); - - if (*line == '"') { - const char *cp; - struct strbuf first = STRBUF_INIT; - struct strbuf sp = STRBUF_INIT; - - if (unquote_c_style(&first, line, &second)) - goto free_and_fail1; - - /* strip the a/b prefix including trailing slash */ - cp = skip_tree_prefix(state, first.buf, first.len); - if (!cp) - goto free_and_fail1; - strbuf_remove(&first, 0, cp - first.buf); - - /* - * 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_and_fail1; - if (*second == '"') { - if (unquote_c_style(&sp, second, NULL)) - goto free_and_fail1; - cp = skip_tree_prefix(state, sp.buf, sp.len); - if (!cp) - goto free_and_fail1; - /* They must match, otherwise ignore */ - if (strcmp(cp, first.buf)) - goto free_and_fail1; - strbuf_release(&sp); - return strbuf_detach(&first, NULL); - } - - /* unquoted second */ - cp = skip_tree_prefix(state, second, line + llen - second); - if (!cp) - goto free_and_fail1; - if (line + llen - cp != first.len || - memcmp(first.buf, cp, first.len)) - goto free_and_fail1; - return strbuf_detach(&first, NULL); - - free_and_fail1: |