diff options
Diffstat (limited to 'builtin')
46 files changed, 3632 insertions, 2691 deletions
diff --git a/builtin/add.c b/builtin/add.c index b79336d712..89dce56a24 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -281,7 +281,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) argc = setup_revisions(argc, argv, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_PATCH; DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES); - out = open(file, O_CREAT | O_WRONLY, 0644); + out = open(file, O_CREAT | O_WRONLY, 0666); if (out < 0) die (_("Could not open '%s' for writing."), file); rev.diffopt.file = xfdopen(out, "w"); @@ -443,6 +443,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (pathspec) { int i; + struct path_exclude_check check; + + path_exclude_check_init(&check, &dir); if (!seen) seen = find_used_pathspec(pathspec); for (i = 0; pathspec[i]; i++) { @@ -450,7 +453,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) && !file_exists(pathspec[i])) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (excluded(&dir, pathspec[i], &dtype)) + if (path_excluded(&check, pathspec[i], -1, &dtype)) dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); } else die(_("pathspec '%s' did not match any files"), @@ -458,6 +461,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } } free(seen); + path_exclude_check_clear(&check); } plug_bulk_checkin(); diff --git a/builtin/apply.c b/builtin/apply.c index 389898f133..d453c83378 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -16,6 +16,9 @@ #include "dir.h" #include "diff.h" #include "parse-options.h" +#include "xdiff-interface.h" +#include "ll-merge.h" +#include "rerere.h" /* * --check turns on checking that the working tree matches the @@ -46,11 +49,12 @@ static int apply_with_reject; static int apply_verbosely; static int allow_overlap; static int no_add; +static int threeway; static const char *fake_ancestor; static int line_termination = '\n'; static unsigned int p_context = UINT_MAX; static const char * const apply_usage[] = { - "git apply [options] [<patch>...]", + N_("git apply [options] [<patch>...]"), NULL }; @@ -103,7 +107,7 @@ static void parse_whitespace_option(const char *option) ws_error_action = correct_ws_error; return; } - die("unrecognized whitespace option '%s'", option); + die(_("unrecognized whitespace option '%s'"), option); } static void parse_ignorewhitespace_option(const char *option) @@ -118,7 +122,7 @@ static void parse_ignorewhitespace_option(const char *option) ws_ignore_action = ignore_ws_change; return; } - die("unrecognized whitespace ignore option '%s'", option); + die(_("unrecognized whitespace ignore option '%s'"), option); } static void set_default_whitespace_mode(const char *whitespace_option) @@ -152,9 +156,14 @@ 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 rejected; int linenr; struct fragment *next; }; @@ -188,14 +197,49 @@ struct patch { 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 */ + unsigned char threeway_stage[3][20]; }; +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 @@ -302,6 +346,11 @@ static void add_line_info(struct image *img, const char *bol, size_t len, unsign 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) { @@ -331,29 +380,31 @@ static void prepare_image(struct image *image, char *buf, size_t len, static void clear_image(struct image *image) { free(image->buf); - image->buf = NULL; - image->len = 0; + free(image->line_allocated); + memset(image, 0, sizeof(*image)); } -static void say_patch_name(FILE *output, const char *pre, - struct patch *patch, const char *post) +/* fmt must contain _one_ %s and no other substitution */ +static void say_patch_name(FILE *output, const char *fmt, struct patch *patch) { - fputs(pre, output); + 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, NULL, output, 0); - fputs(" => ", output); - quote_c_style(patch->new_name, NULL, output, 0); + 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, NULL, output, 0); + quote_c_style(n, &sb, NULL, 0); } - fputs(post, output); + fprintf(output, fmt, sb.buf); + fputc('\n', output); + strbuf_release(&sb); } -#define CHUNKSIZE (8192) #define SLOP (16) static void read_patch_file(struct strbuf *sb, int fd) @@ -416,7 +467,7 @@ static char *squash_slash(char *name) return name; } -static char *find_name_gnu(const char *line, char *def, int p_value) +static char *find_name_gnu(const char *line, const char *def, int p_value) { struct strbuf name = STRBUF_INIT; char *cp; @@ -439,11 +490,7 @@ static char *find_name_gnu(const char *line, char *def, int p_value) cp++; } - /* name can later be freed, so we need - * to memmove, not just return cp - */ strbuf_remove(&name, 0, cp - name.buf); - free(def); if (root) strbuf_insert(&name, 0, root, root_len); return squash_slash(strbuf_detach(&name, NULL)); @@ -608,8 +655,13 @@ static size_t diff_timestamp_len(const char *line, size_t len) return line + len - end; } -static char *find_name_common(const char *line, char *def, int p_value, - const char *end, int terminate) +static char *null_strdup(const char *s) +{ + return s ? xstrdup(s) : NULL; +} + +static char *find_name_common(const char *line, const char *def, + int p_value, const char *end, int terminate) { int len; const char *start = NULL; @@ -630,10 +682,10 @@ static char *find_name_common(const char *line, char *def, int p_value, start = line; } if (!start) - return squash_slash(def); + return squash_slash(null_strdup(def)); len = line - start; if (!len) - return squash_slash(def); + return squash_slash(null_strdup(def)); /* * Generally we prefer the shorter name, especially @@ -644,8 +696,7 @@ static char *find_name_common(const char *line, char *def, int p_value, if (def) { int deflen = strlen(def); if (deflen < len && !strncmp(start, def, deflen)) - return squash_slash(def); - free(def); + return squash_slash(xstrdup(def)); } if (root) { @@ -770,7 +821,7 @@ static int has_epoch_timestamp(const char *nameline) if (!stamp) { stamp = xmalloc(sizeof(*stamp)); if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) { - warning("Cannot prepare timestamp regexp %s", + warning(_("Cannot prepare timestamp regexp %s"), stamp_regexp); return 0; } @@ -779,7 +830,7 @@ static int has_epoch_timestamp(const char *nameline) status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0); if (status) { if (status != REG_NOMATCH) - warning("regexec returned %d for input: %s", + warning(_("regexec returned %d for input: %s"), status, timestamp); return 0; } @@ -842,8 +893,10 @@ static void parse_traditional_patch(const char *first, const char *second, struc name = find_name_traditional(first, NULL, p_value); patch->old_name = name; } else { - name = find_name_traditional(first, NULL, p_value); - name = find_name_traditional(second, name, p_value); + char *first_name; + first_name = find_name_traditional(first, NULL, p_value); + name = find_name_traditional(second, first_name, p_value); + free(first_name); if (has_epoch_timestamp(first)) { patch->is_new = 1; patch->is_delete = 0; @@ -853,11 +906,12 @@ static void parse_traditional_patch(const char *first, const char *second, struc patch->is_delete = 1; patch->old_name = name; } else { - patch->old_name = patch->new_name = name; + patch->old_name = name; + patch->new_name = xstrdup(name); } } if (!name) - die("unable to find filename in patch at line %d", linenr); + die(_("unable to find filename in patch at line %d"), linenr); } static int gitdiff_hdrend(const char *line, struct patch *patch) @@ -874,7 +928,10 @@ static int gitdiff_hdrend(const char *line, struct patch *patch) * their names against any previous information, just * to make sure.. */ -static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew) +#define DIFF_OLD_NAME 0 +#define DIFF_NEW_NAME 1 + +static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side) { if (!orig_name && !isnull) return find_name(line, NULL, p_value, TERM_TAB); @@ -886,30 +943,40 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, name = orig_name; len = strlen(name); if (isnull) - die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); + 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); if (!another || memcmp(another, name, len + 1)) - die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); + 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"), linenr); free(another); return orig_name; } 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); + 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) { - patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old"); + char *orig = patch->old_name; + patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, + DIFF_OLD_NAME); + if (orig != patch->old_name) + free(orig); return 0; } static int gitdiff_newname(const char *line, struct patch *patch) { - patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new"); + char *orig = patch->new_name; + patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, + DIFF_NEW_NAME); + if (orig != patch->new_name) + free(orig); return 0; } @@ -928,20 +995,23 @@ static int gitdiff_newmode(const char *line, struct patch *patch) static int gitdiff_delete(const char *line, struct patch *patch) { patch->is_delete = 1; - patch->old_name = patch->def_name; + free(patch->old_name); + patch->old_name = null_strdup(patch->def_name); return gitdiff_oldmode(line, patch); } static int gitdiff_newfile(const char *line, struct patch *patch) { patch->is_new = 1; - patch->new_name = patch->def_name; + free(patch->new_name); + patch->new_name = null_strdup(patch->def_name); return gitdiff_newmode(line, patch); } static int gitdiff_copysrc(const char *line, struct patch *patch) { patch->is_copy = 1; + free(patch->old_name); patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); return 0; } @@ -949,6 +1019,7 @@ static int gitdiff_copysrc(const char *line, struct patch *patch) static int gitdiff_copydst(const char *line, struct patch *patch) { patch->is_copy = 1; + free(patch->new_name); patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); return 0; } @@ -956,6 +1027,7 @@ static int gitdiff_copydst(const char *line, struct patch *patch) static int gitdiff_renamesrc(const char *line, struct patch *patch) { patch->is_rename = 1; + free(patch->old_name); patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); return 0; } @@ -963,6 +1035,7 @@ static int gitdiff_renamesrc(const char *line, struct patch *patch) static int gitdiff_renamedst(const char *line, struct patch *patch) { patch->is_rename = 1; + free(patch->new_name); patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); return 0; } @@ -1044,7 +1117,7 @@ static const char *stop_at_slash(const char *line, int llen) * 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) +static char *git_header_name(const char *line, int llen) { const char *name; const char *second = NULL; @@ -1171,7 +1244,7 @@ static char *git_header_name(char *line, int llen) } /* Verify that we recognize the lines following a git header */ -static int parse_git_header(char *line, int len, unsigned int size, struct patch *patch) +static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch) { unsigned long offset; @@ -1287,7 +1360,7 @@ static int parse_range(const char *line, int len, int offset, const char *expect return offset + ex; } -static void recount_diff(char *line, int size, struct fragment *fragment) +static void recount_diff(const char *line, int size, struct fragment *fragment) { int oldlines = 0, newlines = 0, ret = 0; @@ -1327,7 +1400,7 @@ static void recount_diff(char *line, int size, struct fragment *fragment) break; } if (ret) { - warning("recount: unexpected line: %.*s", + warning(_("recount: unexpected line: %.*s"), (int)linelen(line, size), line); return; } @@ -1341,7 +1414,7 @@ static void recount_diff(char *line, int size, struct fragment *fragment) * Parse a unified diff fragment header of the * form "@@ -a,b +c,d @@" */ -static int parse_fragment_header(char *line, int len, struct fragment *fragment) +static int parse_fragment_header(const char *line, int len, struct fragment *fragment) { int offset; @@ -1355,7 +1428,7 @@ static int parse_fragment_header(char *line, int len, struct fragment *fragment) return offset; } -static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch) +static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch) { unsigned long offset, len; @@ -1384,7 +1457,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc struct fragment dummy; if (parse_fragment_header(line, len, &dummy) < 0) continue; - die("patch fragment without header at line %d: %.*s", + die(_("patch fragment without header at line %d: %.*s"), linenr, (int)len-1, line); } @@ -1401,9 +1474,14 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc continue; if (!patch->old_name && !patch->new_name) { if (!patch->def_name) - die("git diff header lacks filename information when removing " - "%d leading pathname components (line %d)" , p_value, linenr); - patch->old_name = patch->new_name = patch->def_name; + die(Q_("git diff header lacks filename information when removing " + "%d leading pathname component (line %d)", + "git diff header lacks filename information when removing " + "%d leading pathname components (line %d)", + p_value), + p_value, linenr); + patch->old_name = xstrdup(patch->def_name); + patch->new_name = xstrdup(patch->def_name); } if (!patch->is_delete && !patch->new_name) die("git diff header lacks filename information " @@ -1466,7 +1544,7 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule) * between a "---" that is part of a patch, and a "---" that starts * the next patch is to look at the line counts.. */ -static int parse_fragment(char *line, unsigned long size, +static int parse_fragment(const char *line, unsigned long size, struct patch *patch, struct fragment *fragment) { int added, deleted; @@ -1556,13 +1634,21 @@ static int parse_fragment(char *line, unsigned long size, patch->lines_deleted += deleted; if (0 < patch->is_new && oldlines) - return error("new file depends on old contents"); + return error(_("new file depends on old contents")); if (0 < patch->is_delete && newlines) - return error("deleted file still has contents"); + return error(_("deleted file still has contents")); return offset; } -static int parse_single_patch(char *line, unsigned long size, struct patch *patch) +/* + * We have seen "diff --git a/... b/..." header (or a traditional patch + * header). Read hunks that belong to this patch into fragments and hang + * them to the given patch structure. + * + * The (fragment->patch, fragment->size) pair points into the memory given + * by the caller, not a copy, when we return. + */ +static int parse_single_patch(const char *line, unsigned long size, struct patch *patch) { unsigned long offset = 0; unsigned long oldlines = 0, newlines = 0, context = 0; @@ -1576,7 +1662,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc fragment->linenr = linenr; len = parse_fragment(line, size, patch, fragment); if (len <= 0) - die("corrupt patch at line %d", linenr); + die(_("corrupt patch at line %d"), linenr); fragment->patch = line; fragment->size = len; oldlines += fragment->oldlines; @@ -1612,12 +1698,14 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc patch->is_delete = 0; if (0 < patch->is_new && oldlines) - die("new file %s depends on old contents", patch->new_name); + die(_("new file %s depends on old contents"), patch->new_name); if (0 < patch->is_delete && newlines) - die("deleted file %s still has contents", patch->old_name); + die(_("deleted file %s still has contents"), patch->old_name); if (!patch->is_delete && !newlines && context) - fprintf(stderr, "** warning: file %s becomes empty but " - "is not deleted\n", patch->new_name); + fprintf_ln(stderr, + _("** warning: " + "file %s becomes empty but is not deleted"), + patch->new_name); return offset; } @@ -1655,6 +1743,11 @@ static char *inflate_it(const void *data, unsigned long size, return out; } +/* + * Read a binary hunk and return a new fragment; fragment->patch + * points at an allocated memory that the caller must free, so + * it is marked as "->free_patch = 1". + */ static struct fragment *parse_binary_hunk(char **buf_p, unsigned long *sz_p, int *status_p, @@ -1742,6 +1835,7 @@ static struct fragment *parse_binary_hunk(char **buf_p, frag = xcalloc(1, sizeof(*frag)); frag->patch = inflate_it(data, hunk_size, origlen); + frag->free_patch = 1; if (!frag->patch) goto corrupt; free(data); @@ -1755,7 +1849,7 @@ static struct fragment *parse_binary_hunk(char **buf_p, corrupt: free(data); *status_p = -1; - error("corrupt binary patch at line %d: %.*s", + error(_("corrupt binary patch at line %d: %.*s"), linenr-1, llen-1, buffer); return NULL; } @@ -1784,7 +1878,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) forward = parse_binary_hunk(&buffer, &size, &status, &used); if (!forward && !status) /* there has to be one hunk (forward hunk) */ - return error("unrecognized binary patch at line %d", linenr-1); + return error(_("unrecognized binary patch at line %d"), linenr-1); if (status) /* otherwise we already gave an error message */ return status; @@ -1807,6 +1901,13 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) return used; } +/* + * Read the patch text in "buffer" taht extends for "size" bytes; stop + * reading after seeing a single patch (i.e. changes to a single file). + * Create fragments (i.e. patch hunks) and hang them to the given patch. + * Return the number of bytes consumed, so that the caller can call us + * again for the next patch. + */ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) { int hdrsize, patchsize; @@ -1863,7 +1964,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) */ if ((apply || check) && (!patch->is_binary && !metadata_changes(patch))) - die("patch with only garbage at line %d", linenr); + die(_("patch with only garbage at line %d"), linenr); } return offset + hdrsize + patchsize; @@ -1953,11 +2054,11 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) switch (st->st_mode & S_IFMT) { case S_IFLNK: if (strbuf_readlink(buf, path, st->st_size) < 0) - return error("unable to read symlink %s", path); + return error(_("unable to read symlink %s"), path); return 0; case S_IFREG: if (strbuf_read_file(buf, path, st->st_size) != st->st_size) - return error("unable to open or read %s", path); + return error(_("unable to open or read %s"), path); convert_to_git(path, buf->buf, buf->len, buf, 0); return 0; default: @@ -2028,7 +2129,7 @@ static void update_pre_post_images(struct image *preimage, ctx++; } if (preimage->nr <= ctx) - die("oops"); + die(_("oops")); /* and copy it in, while fixing the line length */ len = preimage->line[ctx].len; @@ -2367,6 +2468,11 @@ static void remove_last_line(struct image *img) img->len -= img->line[--img->nr].len; } +/* + * The change from "preimage" and "postimage" has been found to + * apply at applied_pos (counts in line numbers) in "img". + * Update "img" to remove "preimage" and replace it with "postimage". + */ static void update_image(struct image *img, int applied_pos, struct image *preimage, @@ -2438,6 +2544,11 @@ static void update_image(struct image *img, img->nr = nr; } +/* + * Use the patch-hunk text in "frag" to prepare two images (preimage and + * postimage) for the hunk. Find lines that match "preimage" in "img" and + * replace the part of "img" with "postimage" text. + */ static int apply_one_fragment(struct image *img, struct fragment *frag, int inaccurate_eof, unsigned ws_rule, int nth_fragment) @@ -2540,7 +2651,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, break; default: if (apply_verbosely) - error("invalid start of line: '%c'", first); + error(_("invalid start of line: '%c'"), first); return -1; } |