From: Junio C Hamano Date: Sun, 22 Jul 2012 19:55:07 +0000 (-0700) Subject: Merge branch 'jc/sha1-name-more' X-Git-Tag: v1.7.12-rc0~22 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/0958a24d7360759902f802744c0839c8029659d7?ds=inline;hp=-c Merge branch 'jc/sha1-name-more' Teaches the object name parser things like a "git describe" output is always a commit object, "A" in "git log A" must be a committish, and "A" and "B" in "git log A...B" both must be committish, etc., to prolong the lifetime of abbreviated object names. * jc/sha1-name-more: (27 commits) t1512: match the "other" object names t1512: ignore whitespaces in wc -l output rev-parse --disambiguate= rev-parse: A and B in "rev-parse A..B" refer to committish reset: the command takes committish commit-tree: the command wants a tree and commits apply: --build-fake-ancestor expects blobs sha1_name.c: add support for disambiguating other types revision.c: the "log" family, except for "show", takes committish revision.c: allow handle_revision_arg() to take other flags sha1_name.c: introduce get_sha1_committish() sha1_name.c: teach lookup context to get_sha1_with_context() sha1_name.c: many short names can only be committish sha1_name.c: get_sha1_1() takes lookup flags sha1_name.c: get_describe_name() by definition groks only commits sha1_name.c: teach get_short_sha1() a commit-only option sha1_name.c: allow get_short_sha1() to take other flags get_sha1(): fix error status regression sha1_name.c: restructure disambiguation of short names sha1_name.c: correct misnamed "canonical" and "res" ... --- 0958a24d7360759902f802744c0839c8029659d7 diff --combined Documentation/git-rev-parse.txt index 4cc3e9586f,8d90863ee3..3c63561f02 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@@ -101,6 -101,12 +101,12 @@@ OPTION The option core.warnAmbiguousRefs is used to select the strict abbreviation mode. + --disambiguate=:: + Show every object whose name begins with the given prefix. + The must be at least 4 hexadecimal digits long to + avoid listing each and every object in the repository by + mistake. + --all:: Show all refs found in `refs/`. @@@ -113,14 -119,15 +119,14 @@@ + If a `pattern` is given, only refs matching the given shell glob are shown. If the pattern does not contain a globbing character (`?`, -`{asterisk}`, or `[`), it is turned into a prefix match by -appending `/{asterisk}`. +`*`, or `[`), it is turned into a prefix match by appending `/*`. --glob=pattern:: Show all refs matching the shell glob pattern `pattern`. If the pattern does not start with `refs/`, this is automatically prepended. If the pattern does not contain a globbing - character (`?`, `{asterisk}`, or `[`), it is turned into a prefix - match by appending `/{asterisk}`. + character (`?`, `*`, or `[`), it is turned into a prefix + match by appending `/*`. --show-toplevel:: Show the absolute path of the top-level directory. @@@ -137,8 -144,7 +143,8 @@@ --git-dir:: Show `$GIT_DIR` if defined. Otherwise show the path to - the .git directory, relative to the current directory. + the .git directory. The path shown, when relative, is + relative to the current working directory. + If `$GIT_DIR` is not defined and the current directory is not detected to lie in a git repository or work tree diff --combined builtin/apply.c index ace04c453b,8b2db1d76b..069cf341b6 --- a/builtin/apply.c +++ b/builtin/apply.c @@@ -14,11 -14,7 +14,11 @@@ #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" /* * --check turns on checking that the working tree matches the @@@ -49,12 -45,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] [...]", + N_("git apply [options] [...]"), NULL }; @@@ -107,7 -102,7 +107,7 @@@ static void parse_whitespace_option(con 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) @@@ -122,7 -117,7 +122,7 @@@ 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) @@@ -156,14 -151,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; }; @@@ -197,49 -187,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 @@@ -346,11 -301,6 +346,11 @@@ static void add_line_info(struct image 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) { @@@ -380,31 -330,29 +380,31 @@@ 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) @@@ -467,7 -415,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; @@@ -490,7 -438,11 +490,7 @@@ 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)); @@@ -655,13 -607,8 +655,13 @@@ static size_t diff_timestamp_len(const 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; @@@ -682,10 -629,10 +682,10 @@@ 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 @@@ -696,7 -643,8 +696,7 @@@ 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) { @@@ -821,7 -769,7 +821,7 @@@ static int has_epoch_timestamp(const ch 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; } @@@ -830,7 -778,7 +830,7 @@@ 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; } @@@ -893,10 -841,8 +893,10 @@@ static void parse_traditional_patch(con 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; @@@ -906,12 -852,11 +906,12 @@@ 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) @@@ -928,10 -873,7 +928,10 @@@ * 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); @@@ -943,40 -885,30 +943,40 @@@ 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; } @@@ -995,23 -927,20 +995,23 @@@ static int gitdiff_newmode(const char * 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; } @@@ -1019,7 -948,6 +1019,7 @@@ 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; } @@@ -1027,7 -955,6 +1027,7 @@@ 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; } @@@ -1035,7 -962,6 +1035,7 @@@ 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; } @@@ -1117,7 -1043,7 +1117,7 @@@ static const char *stop_at_slash(const * 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; @@@ -1244,7 -1170,7 +1244,7 @@@ } /* 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; @@@ -1360,7 -1286,7 +1360,7 @@@ static int parse_range(const char *line 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; @@@ -1400,7 -1326,7 +1400,7 @@@ break; } if (ret) { - warning("recount: unexpected line: %.*s", + warning(_("recount: unexpected line: %.*s"), (int)linelen(line, size), line); return; } @@@ -1414,7 -1340,7 +1414,7 @@@ * 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; @@@ -1428,7 -1354,7 +1428,7 @@@ 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; @@@ -1457,7 -1383,7 +1457,7 @@@ 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); } @@@ -1474,14 -1400,9 +1474,14 @@@ 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 " @@@ -1544,7 -1465,7 +1544,7 @@@ static void check_whitespace(const cha * 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; @@@ -1634,21 -1555,13 +1634,21 @@@ 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; @@@ -1662,7 -1575,7 +1662,7 @@@ 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; @@@ -1698,14 -1611,12 +1698,14 @@@ 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; } @@@ -1743,11 -1654,6 +1743,11 @@@ static char *inflate_it(const void *dat 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, @@@ -1835,7 -1741,6 +1835,7 @@@ frag = xcalloc(1, sizeof(*frag)); frag->patch = inflate_it(data, hunk_size, origlen); + frag->free_patch = 1; if (!frag->patch) goto corrupt; free(data); @@@ -1849,7 -1754,7 +1849,7 @@@ 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; } @@@ -1878,7 -1783,7 +1878,7 @@@ static int parse_binary(char *buffer, u 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; @@@ -1901,13 -1806,6 +1901,13 @@@ 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; @@@ -1964,7 -1862,7 +1964,7 @@@ */ 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; @@@ -2054,11 -1952,11 +2054,11 @@@ static int read_old_data(struct stat *s 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: @@@ -2129,7 -2027,7 +2129,7 @@@ static void update_pre_post_images(stru ctx++; } if (preimage->nr <= ctx) - die("oops"); + die(_("oops")); /* and copy it in, while fixing the line length */ len = preimage->line[ctx].len; @@@ -2468,11 -2366,6 +2468,11 @@@ static void remove_last_line(struct ima 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, @@@ -2544,11 -2437,6 +2544,11 @@@ 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) @@@ -2651,7 -2539,7 +2651,7 @@@ break; default: if (apply_verbosely) - error("invalid start of line: '%c'", first); + error(_("invalid start of line: '%c'"), first); return -1; } if (added_blank_line) { @@@ -2768,11 -2656,9 +2768,11 @@@ int offset = applied_pos - pos; if (apply_in_reverse) offset = 0 - offset; - fprintf(stderr, - "Hunk #%d succeeded at %d (offset %d lines).\n", - nth_fragment, applied_pos + 1, offset); + fprintf_ln(stderr, + Q_("Hunk #%d succeeded at %d (offset %d line).", + "Hunk #%d succeeded at %d (offset %d lines).", + offset), + nth_fragment, applied_pos + 1, offset); } /* @@@ -2781,13 -2667,13 +2781,13 @@@ */ if ((leading != frag->leading) || (trailing != frag->trailing)) - fprintf(stderr, "Context reduced to (%ld/%ld)" - " to apply fragment at %d\n", - leading, trailing, applied_pos+1); + fprintf_ln(stderr, _("Context reduced to (%ld/%ld)" + " to apply fragment at %d"), + leading, trailing, applied_pos+1); update_image(img, applied_pos, &preimage, &postimage); } else { if (apply_verbosely) - error("while searching for:\n%.*s", + error(_("while searching for:\n%.*s"), (int)(old - oldlines), oldlines); } @@@ -2806,7 -2692,7 +2806,7 @@@ static int apply_binary_fragment(struc void *dst; if (!fragment) - return error("missing binary patch data for '%s'", + return error(_("missing binary patch data for '%s'"), patch->new_name ? patch->new_name : patch->old_name); @@@ -2841,12 -2727,6 +2841,12 @@@ return -1; } +/* + * Replace "img" with the result of applying the binary patch. + * The binary patch data itself in patch->fragment is still kept + * but the preimage prepared by the caller in "img" is freed here + * or in the helper function apply_binary_fragment() this calls. + */ static int apply_binary(struct image *img, struct patch *patch) { const char *name = patch->old_name ? patch->old_name : patch->new_name; @@@ -2909,13 -2789,13 +2909,13 @@@ * in the patch->fragments->{patch,size}. */ if (apply_binary_fragment(img, patch)) - return error("binary patch does not apply to '%s'", + return error(_("binary patch does not apply to '%s'"), name); /* verify that the result matches */ hash_sha1_file(img->buf, img->len, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) - return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", + return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"), name, patch->new_sha1_prefix, sha1_to_hex(sha1)); } @@@ -2936,7 -2816,7 +2936,7 @@@ static int apply_fragments(struct imag while (frag) { nth++; if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) { - error("patch failed: %s:%ld", name, frag->oldpos); + error(_("patch failed: %s:%ld"), name, frag->oldpos); if (!apply_with_reject) return -1; frag->rejected = 1; @@@ -2946,17 -2826,20 +2946,17 @@@ return 0; } -static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) +static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode) { - if (!ce) - return 0; - - if (S_ISGITLINK(ce->ce_mode)) { + if (S_ISGITLINK(mode)) { strbuf_grow(buf, 100); - strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1)); + strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1)); } else { enum object_type type; unsigned long sz; char *result; - result = read_sha1_file(ce->sha1, &type, &sz); + result = read_sha1_file(sha1, &type, &sz); if (!result) return -1; /* XXX read_sha1_file NUL-terminates */ @@@ -2965,13 -2848,6 +2965,13 @@@ return 0; } +static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) +{ + if (!ce) + return 0; + return read_blob_object(buf, ce->sha1, ce->ce_mode); +} + static struct patch *in_fn_table(const char *name) { struct string_list_item *item; @@@ -2990,15 -2866,9 +2990,15 @@@ * item->util in the filename table records the status of the path. * Usually it points at a patch (whose result records the contents * of it after applying it), but it could be PATH_WAS_DELETED for a - * path that a previously applied patch has already removed. + * path that a previously applied patch has already removed, or + * PATH_TO_BE_DELETED for a path that a later patch would remove. + * + * The latter is needed to deal with a case where two paths A and B + * are swapped by first renaming A to B and then renaming B to A; + * moving A to B should not be prevented due to presense of B as we + * will remove it in a later patch. */ - #define PATH_TO_BE_DELETED ((struct patch *) -2) +#define PATH_TO_BE_DELETED ((struct patch *) -2) #define PATH_WAS_DELETED ((struct patch *) -1) static int to_be_deleted(struct patch *patch) @@@ -3050,346 -2920,152 +3050,346 @@@ static void prepare_fn_table(struct pat } } -static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) +static int checkout_target(struct cache_entry *ce, struct stat *st) +{ + struct checkout costate; + + memset(&costate, 0, sizeof(costate)); + costate.base_dir = ""; + costate.refresh_cache = 1; + if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st)) + return error(_("cannot checkout %s"), ce->name); + return 0; +} + +static struct patch *previous_patch(struct patch *patch, int *gone) +{ + struct patch *previous; + + *gone = 0; + if (patch->is_copy || patch->is_rename) + return NULL; /* "git" patches do not depend on the order */ + + previous = in_fn_table(patch->old_name); + if (!previous) + return NULL; + + if (to_be_deleted(previous)) + return NULL; /* the deletion hasn't happened yet */ + + if (was_deleted(previous)) + *gone = 1; + + return previous; +} + +static int verify_index_match(struct cache_entry *ce, struct stat *st) +{ + if (S_ISGITLINK(ce->ce_mode)) { + if (!S_ISDIR(st->st_mode)) + return -1; + return 0; + } + return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); +} + +#define SUBMODULE_PATCH_WITHOUT_INDEX 1 + +static int load_patch_target(struct strbuf *buf, + struct cache_entry *ce, + struct stat *st, + const char *name, + unsigned expected_mode) +{ + if (cached) { + if (read_file_or_gitlink(ce, buf)) + return error(_("read of %s failed"), name); + } else if (name) { + if (S_ISGITLINK(expected_mode)) { + if (ce) + return read_file_or_gitlink(ce, buf); + else + return SUBMODULE_PATCH_WITHOUT_INDEX; + } else { + if (read_old_data(st, name, buf)) + return error(_("read of %s failed"), name); + } + } + return 0; +} + +/* + * We are about to apply "patch"; populate the "image" with the + * current version we have, from the working tree or from the index, + * depending on the situation e.g. --cached/--index. If we are + * applying a non-git patch that incrementally updates the tree, + * we read from the result of a previous diff. + */ +static int load_preimage(struct image *image, + struct patch *patch, struct stat *st, struct cache_entry *ce) { struct strbuf buf = STRBUF_INIT; - struct image image; size_t len; char *img; - struct patch *tpatch; + struct patch *previous; + int status; - if (!(patch->is_copy || patch->is_rename) && - (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) { - if (was_deleted(tpatch)) { - return error("patch %s has been renamed/deleted", - patch->old_name); - } - /* We have a patched copy in memory use that */ - strbuf_add(&buf, tpatch->result, tpatch->resultsize); - } else if (cached) { - if (read_file_or_gitlink(ce, &buf)) - return error("read of %s failed", patch->old_name); - } else if (patch->old_name) { - if (S_ISGITLINK(patch->old_mode)) { - if (ce) { - read_file_or_gitlink(ce, &buf); - } else { - /* - * There is no way to apply subproject - * patch without looking at the index. - */ - patch->fragments = NULL; - } - } else { - if (read_old_data(st, patch->old_name, &buf)) - return error("read of %s failed", patch->old_name); + previous = previous_patch(patch, &status); + if (status) + return error(_("path %s has been renamed/deleted"), + patch->old_name); + if (previous) { + /* We have a patched copy in memory; use that. */ + strbuf_add(&buf, previous->result, previous->resultsize); + } else { + status = load_patch_target(&buf, ce, st, + patch->old_name, patch->old_mode); + if (status < 0) + return status; + else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) { + /* + * There is no way to apply subproject + * patch without looking at the index. + * NEEDSWORK: shouldn't this be flagged + * as an error??? + */ + free_fragment_list(patch->fragments); + patch->fragments = NULL; + } else if (status) { + return error(_("read of %s failed"), patch->old_name); } } img = strbuf_detach(&buf, &len); - prepare_image(&image, img, len, !patch->is_binary); + prepare_image(image, img, len, !patch->is_binary); + return 0; +} - if (apply_fragments(&image, patch) < 0) - return -1; /* note with --reject this succeeds. */ - patch->result = image.buf; - patch->resultsize = image.len; - add_to_fn_table(patch); - free(image.line_allocated); +static int three_way_merge(struct image *image, + char *path, + const unsigned char *base, + const unsigned char *ours, + const unsigned char *theirs) +{ + mmfile_t base_file, our_file, their_file; + mmbuffer_t result = { NULL }; + int status; - if (0 < patch->is_delete && patch->resultsize) - return error("removal patch leaves file contents"); + read_mmblob(&base_file, base); + read_mmblob(&our_file, ours); + read_mmblob(&their_file, theirs); + status = ll_merge(&result, path, + &base_file, "base", + &our_file, "ours", + &their_file, "theirs", NULL); + free(base_file.ptr); + free(our_file.ptr); + free(their_file.ptr); + if (status < 0 || !result.ptr) { + free(result.ptr); + return -1; + } + clear_image(image); + image->buf = result.ptr; + image->len = result.size; + + return status; +} + +/* + * When directly falling back to add/add three-way merge, we read from + * the current contents of the new_name. In no cases other than that + * this function will be called. + */ +static int load_current(struct image *image, struct patch *patch) +{ + struct strbuf buf = STRBUF_INIT; + int status, pos; + size_t len; + char *img; + struct stat st; + struct cache_entry *ce; + char *name = patch->new_name; + unsigned mode = patch->new_mode; + + if (!patch->is_new) + die("BUG: patch to %s is not a creation", patch->old_name); + + pos = cache_name_pos(name, strlen(name)); + if (pos < 0) + return error(_("%s: does not exist in index"), name); + ce = active_cache[pos]; + if (lstat(name, &st)) { + if (errno != ENOENT) + return error(_("%s: %s"), name, strerror(errno)); + if (checkout_target(ce, &st)) + return -1; + } + if (verify_index_match(ce, &st)) + return error(_("%s: does not match index"), name); + status = load_patch_target(&buf, ce, &st, name, mode); + if (status < 0) + return status; + else if (status) + return -1; + img = strbuf_detach(&buf, &len); + prepare_image(image, img, len, !patch->is_binary); return 0; } -static int check_to_create_blob(const char *new_name, int ok_if_exists) +static int try_threeway(struct image *image, struct patch *patch, + struct stat *st, struct cache_entry *ce) { - struct stat nst; - if (!lstat(new_name, &nst)) { - if (S_ISDIR(nst.st_mode) || ok_if_exists) - return 0; - /* - * A leading component of new_name might be a symlink - * that is going to be removed with this patch, but - * still pointing at somewhere that has the path. - * In such a case, path "new_name" does not exist as - * far as git is concerned. - */ - if (has_symlink_leading_path(new_name, strlen(new_name))) - return 0; + unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; + struct strbuf buf = STRBUF_INIT; + size_t len; + int status; + char *img; + struct image tmp_image; + + /* No point falling back to 3-way merge in these cases */ + if (patch->is_delete || + S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode)) + return -1; + + /* Preimage the patch was prepared for */ + if (patch->is_new) + write_sha1_file("", 0, blob_type, pre_sha1); + else if (get_sha1(patch->old_sha1_prefix, pre_sha1) || + read_blob_object(&buf, pre_sha1, patch->old_mode)) + return error("repository lacks the necessary blob to fall back on 3-way merge."); + + fprintf(stderr, "Falling back to three-way merge...\n"); - return error("%s: already exists in working directory", new_name); + img = strbuf_detach(&buf, &len); + prepare_image(&tmp_image, img, len, 1); + /* Apply the patch to get the post image */ + if (apply_fragments(&tmp_image, patch) < 0) { + clear_image(&tmp_image); + return -1; + } + /* post_sha1[] is theirs */ + write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1); + clear_image(&tmp_image); + + /* our_sha1[] is ours */ + if (patch->is_new) { + if (load_current(&tmp_image, patch)) + return error("cannot read the current contents of '%s'", + patch->new_name); + } else { + if (load_preimage(&tmp_image, patch, st, ce)) + return error("cannot read the current contents of '%s'", + patch->old_name); + } + write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1); + clear_image(&tmp_image); + + /* in-core three-way merge between post and our using pre as base */ + status = three_way_merge(image, patch->new_name, + pre_sha1, our_sha1, post_sha1); + if (status < 0) { + fprintf(stderr, "Failed to fall back on three-way merge...\n"); + return status; + } + + if (status) { + patch->conflicted_threeway = 1; + if (patch->is_new) + hashclr(patch->threeway_stage[0]); + else + hashcpy(patch->threeway_stage[0], pre_sha1); + hashcpy(patch->threeway_stage[1], our_sha1); + hashcpy(patch->threeway_stage[2], post_sha1); + fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); + } else { + fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name); } - else if ((errno != ENOENT) && (errno != ENOTDIR)) - return error("%s: %s", new_name, strerror(errno)); return 0; } -static int verify_index_match(struct cache_entry *ce, struct stat *st) +static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { - if (S_ISGITLINK(ce->ce_mode)) { - if (!S_ISDIR(st->st_mode)) + struct image image; + + if (load_preimage(&image, patch, st, ce) < 0) + return -1; + + if (patch->direct_to_threeway || + apply_fragments(&image, patch) < 0) { + /* Note: with --reject, apply_fragments() returns 0 */ + if (!threeway || try_threeway(&image, patch, st, ce) < 0) return -1; - return 0; } - return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); + patch->result = image.buf; + patch->resultsize = image.len; + add_to_fn_table(patch); + free(image.line_allocated); + + if (0 < patch->is_delete && patch->resultsize) + return error(_("removal patch leaves file contents")); + + return 0; } +/* + * If "patch" that we are looking at modifies or deletes what we have, + * we would want it not to lose any local modification we have, either + * in the working tree or in the index. + * + * This also decides if a non-git patch is a creation patch or a + * modification to an existing empty file. We do not check the state + * of the current tree for a creation patch in this function; the caller + * check_patch() separately makes sure (and errors out otherwise) that + * the path the patch creates does not exist in the current tree. + */ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) { const char *old_name = patch->old_name; - struct patch *tpatch = NULL; - int stat_ret = 0; + struct patch *previous = NULL; + int stat_ret = 0, status; unsigned st_mode = 0; - /* - * Make sure that we do not have local modifications from the - * index when we are looking at the index. Also make sure - * we have the preimage file to be patched in the work tree, - * unless --cached, which tells git to apply only in the index. - */ if (!old_name) return 0; assert(patch->is_new <= 0); + previous = previous_patch(patch, &status); - if (!(patch->is_copy || patch->is_rename) && - (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) { - if (was_deleted(tpatch)) - return error("%s: has been deleted/renamed", old_name); - st_mode = tpatch->new_mode; + if (status) + return error(_("path %s has been renamed/deleted"), old_name); + if (previous) { + st_mode = previous->new_mode; } else if (!cached) { stat_ret = lstat(old_name, st); if (stat_ret && errno != ENOENT) - return error("%s: %s", old_name, strerror(errno)); + return error(_("%s: %s"), old_name, strerror(errno)); } - if (to_be_deleted(tpatch)) - tpatch = NULL; - - if (check_index && !tpatch) { + if (check_index && !previous) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { if (patch->is_new < 0) goto is_new; - return error("%s: does not exist in index", old_name); + return error(_("%s: does not exist in index"), old_name); } *ce = active_cache[pos]; if (stat_ret < 0) { - struct checkout costate; - /* checkout */ - memset(&costate, 0, sizeof(costate)); - costate.base_dir = ""; - costate.refresh_cache = 1; - if (checkout_entry(*ce, &costate, NULL) || - lstat(old_name, st)) + if (checkout_target(*ce, st)) return -1; } if (!cached && verify_index_match(*ce, st)) - return error("%s: does not match index", old_name); + return error(_("%s: does not match index"), old_name); if (cached) st_mode = (*ce)->ce_mode; } else if (stat_ret < 0) { if (patch->is_new < 0) goto is_new; - return error("%s: %s", old_name, strerror(errno)); + return error(_("%s: %s"), old_name, strerror(errno)); } - if (!cached && !tpatch) + if (!cached && !previous) st_mode = ce_mode_from_stat(*ce, st->st_mode); if (patch->is_new < 0) @@@ -3397,9 -3073,9 +3397,9 @@@ if (!patch->old_mode) patch->old_mode = st_mode; if ((st_mode ^ patch->old_mode) & S_IFMT) - return error("%s: wrong type", old_name); + return error(_("%s: wrong type"), old_name); if (st_mode != patch->old_mode) - warning("%s has type %o, expected %o", + warning(_("%s has type %o, expected %o"), old_name, st_mode, patch->old_mode); if (!patch->new_mode && !patch->is_delete) patch->new_mode = st_mode; @@@ -3408,50 -3084,10 +3408,50 @@@ is_new: patch->is_new = 1; patch->is_delete = 0; + free(patch->old_name); patch->old_name = NULL; return 0; } + +#define EXISTS_IN_INDEX 1 +#define EXISTS_IN_WORKTREE 2 + +static int check_to_create(const char *new_name, int ok_if_exists) +{ + struct stat nst; + + if (check_index && + cache_name_pos(new_name, strlen(new_name)) >= 0 && + !ok_if_exists) + return EXISTS_IN_INDEX; + if (cached) + return 0; + + if (!lstat(new_name, &nst)) { + if (S_ISDIR(nst.st_mode) || ok_if_exists) + return 0; + /* + * A leading component of new_name might be a symlink + * that is going to be removed with this patch, but + * still pointing at somewhere that has the path. + * In such a case, path "new_name" does not exist as + * far as git is concerned. + */ + if (has_symlink_leading_path(new_name, strlen(new_name))) + return 0; + + return EXISTS_IN_WORKTREE; + } else if ((errno != ENOENT) && (errno != ENOTDIR)) { + return error("%s: %s", new_name, strerror(errno)); + } + return 0; +} + +/* + * Check and apply the patch in-core; leave the result in patch->result + * for the caller to write it out to the final destination. + */ static int check_patch(struct patch *patch) { struct stat st; @@@ -3470,45 -3106,31 +3470,45 @@@ return status; old_name = patch->old_name; + /* + * A type-change diff is always split into a patch to delete + * old, immediately followed by a patch to create new (see + * diff.c::run_diff()); in such a case it is Ok that the entry + * to be deleted by the previous patch is still in the working + * tree and in the index. + * + * A patch to swap-rename between A and B would first rename A + * to B and then rename B to A. While applying the first one, + * the presense of B should not stop A from getting renamed to + * B; ask to_be_deleted() about the later rename. Removal of + * B and rename from A to B is handled the same way by asking + * was_deleted(). + */ if ((tpatch = in_fn_table(new_name)) && - (was_deleted(tpatch) || to_be_deleted(tpatch))) - /* - * A type-change diff is always split into a patch to - * delete old, immediately followed by a patch to - * create new (see diff.c::run_diff()); in such a case - * it is Ok that the entry to be deleted by the - * previous patch is still in the working tree and in - * the index. - */ + (was_deleted(tpatch) || to_be_deleted(tpatch))) ok_if_exists = 1; else ok_if_exists = 0; if (new_name && ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) { - if (check_index && - cache_name_pos(new_name, strlen(new_name)) >= 0 && - !ok_if_exists) - return error("%s: already exists in index", new_name); - if (!cached) { - int err = check_to_create_blob(new_name, ok_if_exists); - if (err) - return err; + int err = check_to_create(new_name, ok_if_exists); + + if (err && threeway) { + patch->direct_to_threeway = 1; + } else switch (err) { + case 0: + break; /* happy */ + case EXISTS_IN_INDEX: + return error(_("%s: already exists in index"), new_name); + break; + case EXISTS_IN_WORKTREE: + return error(_("%s: already exists in working directory"), + new_name); + default: + return err; } + if (!patch->new_mode) { if (0 < patch->is_new) patch->new_mode = S_IFREG | 0644; @@@ -3521,22 -3143,14 +3521,22 @@@ int same = !strcmp(old_name, new_name); if (!patch->new_mode) patch->new_mode = patch->old_mode; - if ((patch->old_mode ^ patch->new_mode) & S_IFMT) - return error("new mode (%o) of %s does not match old mode (%o)%s%s", - patch->new_mode, new_name, patch->old_mode, - same ? "" : " of ", same ? "" : old_name); + if ((patch->old_mode ^ patch->new_mode) & S_IFMT) { + if (same) + return error(_("new mode (%o) of %s does not " + "match old mode (%o)"), + patch->new_mode, new_name, + patch->old_mode); + else + return error(_("new mode (%o) of %s does not " + "match old mode (%o) of %s"), + patch->new_mode, new_name, + patch->old_mode, old_name); + } } if (apply_data(patch, &st, ce) < 0) - return error("%s: patch does not apply", name); + return error(_("%s: patch does not apply"), name); patch->rejected = 0; return 0; } @@@ -3549,7 -3163,7 +3549,7 @@@ static int check_patch_list(struct patc while (patch) { if (apply_verbosely) say_patch_name(stderr, - "Checking patch ", patch, "...\n"); + _("Checking patch %s..."), patch); err |= check_patch(patch); patch = patch->next; } @@@ -3589,7 -3203,7 +3589,7 @@@ static void build_fake_ancestor(struct name = patch->old_name ? patch->old_name : patch->new_name; if (0 < patch->is_new) continue; - else if (get_sha1(patch->old_sha1_prefix, sha1)) + else if (get_sha1_blob(patch->old_sha1_prefix, sha1)) /* git diff has no index line for mode/type changes */ if (!patch->lines_added && !patch->lines_deleted) { if (get_current_sha1(patch->old_name, sha1)) @@@ -3604,7 -3218,7 +3604,7 @@@ ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0); if (!ce) - die("make_cache_entry failed for path '%s'", name); + die(_("make_cache_entry failed for path '%s'"), name); if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) die ("Could not add %s to temporary index", name); } @@@ -3627,7 -3241,7 +3627,7 @@@ static void stat_patch_list(struct patc show_stats(patch); } - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels); + print_stat_summary(stdout, files, adds, dels); } static void numstat_patch_list(struct patch *patch) @@@ -3747,7 -3361,7 +3747,7 @@@ static void remove_file(struct patch *p { if (update_index) { if (remove_file_from_cache(patch->old_name) < 0) - die("unable to remove %s from index", patch->old_name); + die(_("unable to remove %s from index"), patch->old_name); } if (!cached) { if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { @@@ -3774,19 -3388,19 +3774,19 @@@ static void add_index_file(const char * const char *s = buf; if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1)) - die("corrupt patch for subproject %s", path); + die(_("corrupt patch for subproject %s"), path); } else { if (!cached) { if (lstat(path, &st) < 0) - die_errno("unable to stat newly created file '%s'", + die_errno(_("unable to stat newly created file '%s'"), path); fill_stat_cache_info(ce, &st); } if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) - die("unable to create backing store for newly created file %s", path); + die(_("unable to create backing store for newly created file %s"), path); } if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) - die("unable to add cache entry for %s", path); + die(_("unable to add cache entry for %s"), path); } static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) @@@ -3819,7 -3433,7 +3819,7 @@@ strbuf_release(&nbuf); if (close(fd) < 0) - die_errno("closing file '%s'", path); + die_errno(_("closing file '%s'"), path); return 0; } @@@ -3868,33 -3482,7 +3868,33 @@@ static void create_one_file(char *path ++nr; } } - die_errno("unable to write file '%s' mode %o", path, mode); + die_errno(_("unable to write file '%s' mode %o"), path, mode); +} + +static void add_conflicted_stages_file(struct patch *patch) +{ + int stage, namelen; + unsigned ce_size, mode; + struct cache_entry *ce; + + if (!update_index) + return; + namelen = strlen(patch->new_name); + ce_size = cache_entry_size(namelen); + mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644); + + remove_file_from_cache(patch->new_name); + for (stage = 1; stage < 4; stage++) { + if (is_null_sha1(patch->threeway_stage[stage - 1])) + continue; + ce = xcalloc(1, ce_size); + memcpy(ce->name, patch->new_name, namelen); + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(namelen, stage); + hashcpy(ce->sha1, patch->threeway_stage[stage - 1]); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) + die(_("unable to add cache entry for %s"), patch->new_name); + } } static void create_file(struct patch *patch) @@@ -3907,11 -3495,7 +3907,11 @@@ if (!mode) mode = S_IFREG | 0644; create_one_file(path, mode, buf, size); - add_index_file(path, mode, buf, size); + + if (patch->conflicted_threeway) + add_conflicted_stages_file(patch); + else + add_index_file(path, mode, buf, size); } /* phase zero is to remove, phase one is to create */ @@@ -3943,7 -3527,6 +3943,7 @@@ static int write_out_one_reject(struct char namebuf[PATH_MAX]; struct fragment *frag; int cnt = 0; + struct strbuf sb = STRBUF_INIT; for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) { if (!frag->rejected) @@@ -3954,7 -3537,7 +3954,7 @@@ if (!cnt) { if (apply_verbosely) say_patch_name(stderr, - "Applied patch ", patch, " cleanly.\n"); + _("Applied patch %s cleanly."), patch); return 0; } @@@ -3962,20 -3545,16 +3962,20 @@@ * contents are marked "rejected" at the patch level. */ if (!patch->new_name) - die("internal error"); + die(_("internal error")); /* Say this even without --verbose */ - say_patch_name(stderr, "Applying patch ", patch, " with"); - fprintf(stderr, " %d rejects...\n", cnt); + strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...", + "Applying patch %%s with %d rejects...", + cnt), + cnt); + say_patch_name(stderr, sb.buf, patch); + strbuf_release(&sb); cnt = strlen(patch->new_name); if (ARRAY_SIZE(namebuf) <= cnt + 5) { cnt = ARRAY_SIZE(namebuf) - 5; - warning("truncating .rej filename to %.*s.rej", + warning(_("truncating .rej filename to %.*s.rej"), cnt - 1, patch->new_name); } memcpy(namebuf, patch->new_name, cnt); @@@ -3983,7 -3562,7 +3983,7 @@@ rej = fopen(namebuf, "w"); if (!rej) - return error("cannot open %s: %s", namebuf, strerror(errno)); + return error(_("cannot open %s: %s"), namebuf, strerror(errno)); /* Normal git tools never deal with .rej, so do not pretend * this is a git patch by saying --git nor give extended @@@ -3996,10 -3575,10 +3996,10 @@@ frag; cnt++, frag = frag->next) { if (!frag->rejected) { - fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt); + fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt); continue; } - fprintf(stderr, "Rejected hunk #%d.\n", cnt); + fprintf_ln(stderr, _("Rejected hunk #%d."), cnt); fprintf(rej, "%.*s", frag->size, frag->patch); if (frag->patch[frag->size-1] != '\n') fputc('\n', rej); @@@ -4013,7 -3592,6 +4013,7 @@@ static int write_out_results(struct pat int phase; int errs = 0; struct patch *l; + struct string_list cpath = STRING_LIST_INIT_DUP; for (phase = 0; phase < 2; phase++) { l = list; @@@ -4022,30 -3600,12 +4022,30 @@@ errs = 1; else { write_out_one_result(l, phase); - if (phase == 1 && write_out_one_reject(l)) - errs = 1; + if (phase == 1) { + if (write_out_one_reject(l)) + errs = 1; + if (l->conflicted_threeway) { + string_list_append(&cpath, l->new_name); + errs = 1; + } + } } l = l->next; } } + + if (cpath.nr) { + struct string_list_item *item; + + sort_string_list(&cpath); + for_each_string_list_item(item, &cpath) + fprintf(stderr, "U %s\n", item->string); + string_list_clear(&cpath, 0); + + rerere(0); + } + return errs; } @@@ -4104,8 -3664,15 +4104,8 @@@ static void prefix_patches(struct patc if (!prefix || p->is_toplevel_relative) return; for ( ; p; p = p->next) { - 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->new_name); + prefix_one(&p->old_name); } } @@@ -4115,10 -3682,12 +4115,10 @@@ static int apply_patch(int fd, const char *filename, int options) { size_t offset; - struct strbuf buf = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; /* owns the patch text */ struct patch *list = NULL, **listp = &list; int skipped_patch = 0; - /* FIXME - memory leak when using multiple patch files as inputs */ - memset(&fn_table, 0, sizeof(struct string_list)); patch_input_file = filename; read_patch_file(&buf, fd); offset = 0; @@@ -4142,14 -3711,15 +4142,14 @@@ listp = &patch->next; } else { - /* perhaps free it a bit better? */ - free(patch); + free_patch(patch); skipped_patch++; } offset += nr; } if (!list && !skipped_patch) - die("unrecognized input"); + die(_("unrecognized input")); if (whitespace_error && (ws_error_action == die_on_ws_error)) apply = 0; @@@ -4160,7 -3730,7 +4160,7 @@@ if (check_index) { if (read_cache() < 0) - die("unable to read index file"); + die(_("unable to read index file")); } if ((check || apply) && @@@ -4168,12 -3738,8 +4168,12 @@@ !apply_with_reject) exit(1); - if (apply && write_out_results(list)) - exit(1); + if (apply && write_out_results(list)) { + if (apply_with_reject) + exit(1); + /* with --3way, we still need to write the index out */ + return 1; + } if (fake_ancestor) build_fake_ancestor(list, fake_ancestor); @@@ -4187,9 -3753,7 +4187,9 @@@ if (summary) summary_patch_list(list); + free_patch_list(list); strbuf_release(&buf); + string_list_clear(&fn_table, 0); return 0; } @@@ -4279,68 -3843,66 +4279,68 @@@ int cmd_apply(int argc, const char **ar const char *whitespace_option = NULL; struct option builtin_apply_options[] = { - { OPTION_CALLBACK, 0, "exclude", NULL, "path", - "don't apply changes matching the given path", + { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"), + N_("don't apply changes matching the given path"), 0, option_parse_exclude }, - { OPTION_CALLBACK, 0, "include", NULL, "path", - "apply changes matching the given path", + { OPTION_CALLBACK, 0, "include", NULL, N_("path"), + N_("apply changes matching the given path"), 0, option_parse_include }, - { OPTION_CALLBACK, 'p', NULL, NULL, "num", - "remove leading slashes from traditional diff paths", + { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), + N_("remove leading slashes from traditional diff paths"), 0, option_parse_p }, OPT_BOOLEAN(0, "no-add", &no_add, - "ignore additions made by the patch"), + N_("ignore additions made by the patch")), OPT_BOOLEAN(0, "stat", &diffstat, - "instead of applying the patch, output diffstat for the input"), + N_("instead of applying the patch, output diffstat for the input")), OPT_NOOP_NOARG(0, "allow-binary-replacement"), OPT_NOOP_NOARG(0, "binary"), OPT_BOOLEAN(0, "numstat", &numstat, - "shows number of added and deleted lines in decimal notation"), + N_("shows number of added and deleted lines in decimal notation")), OPT_BOOLEAN(0, "summary", &summary, - "instead of applying the patch, output a summary for the input"), + N_("instead of applying the patch, output a summary for the input")), OPT_BOOLEAN(0, "check", &check, - "instead of applying the patch, see if the patch is applicable"), + N_("instead of applying the patch, see if the patch is applicable")), OPT_BOOLEAN(0, "index", &check_index, - "make sure the patch is applicable to the current index"), + N_("make sure the patch is applicable to the current index")), OPT_BOOLEAN(0, "cached", &cached, - "apply a patch without touching the working tree"), + N_("apply a patch without touching the working tree")), OPT_BOOLEAN(0, "apply", &force_apply, - "also apply the patch (use with --stat/--summary/--check)"), + N_("also apply the patch (use with --stat/--summary/--check)")), + OPT_BOOL('3', "3way", &threeway, + N_( "attempt three-way merge if a patch does not apply")), OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, - "build a temporary index based on embedded index information"), + N_("build a temporary index based on embedded index information")), { OPTION_CALLBACK, 'z', NULL, NULL, NULL, - "paths are separated with NUL character", + N_("paths are separated with NUL character"), PARSE_OPT_NOARG, option_parse_z }, OPT_INTEGER('C', NULL, &p_context, - "ensure at least lines of context match"), - { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action", - "detect new or modified lines that have whitespace errors", + N_("ensure at least lines of context match")), + { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"), + N_("detect new or modified lines that have whitespace errors"), 0, option_parse_whitespace }, { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL, - "ignore changes in whitespace when finding context", + N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL, - "ignore changes in whitespace when finding context", + N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, OPT_BOOLEAN('R', "reverse", &apply_in_reverse, - "apply the patch in reverse"), + N_("apply the patch in reverse")), OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero, - "don't expect at least one line of context"), + N_("don't expect at least one line of context")), OPT_BOOLEAN(0, "reject", &apply_with_reject, - "leave the rejected hunks in corresponding *.rej files"), + N_("leave the rejected hunks in corresponding *.rej files")), OPT_BOOLEAN(0, "allow-overlap", &allow_overlap, - "allow overlapping hunks"), - OPT__VERBOSE(&apply_verbosely, "be verbose"), + N_("allow overlapping hunks")), + OPT__VERBOSE(&apply_verbosely, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, - "tolerate incorrectly detected missing new-line at the end of file", + N_("tolerate incorrectly detected missing new-line at the end of file"), INACCURATE_EOF), OPT_BIT(0, "recount", &options, - "do not trust the line counts in the hunk headers", + N_("do not trust the line counts in the hunk headers"), RECOUNT), - { OPTION_CALLBACK, 0, "directory", NULL, "root", - "prepend to all filenames", + { OPTION_CALLBACK, 0, "directory", NULL, N_("root"), + N_("prepend to all filenames"), 0, option_parse_directory }, OPT_END() }; @@@ -4356,24 -3918,15 +4356,24 @@@ argc = parse_options(argc, argv, prefix, builtin_apply_options, apply_usage, 0); + if (apply_with_reject && threeway) + die("--reject and --3way cannot be used together."); + if (cached && threeway) + die("--cached and --3way cannot be used together."); + if (threeway) { + if (is_not_gitdir) + die(_("--3way outside a repository")); + check_index = 1; + } if (apply_with_reject) apply = apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) apply = 0; if (check_index && is_not_gitdir) - die("--index outside a repository"); + die(_("--index outside a repository")); if (cached) { if (is_not_gitdir) - die("--cached outside a repository"); + die(_("--cached outside a repository")); check_index = 1; } for (i = 0; i < argc; i++) { @@@ -4389,7 -3942,7 +4389,7 @@@ fd = open(arg, O_RDONLY); if (fd < 0) - die_errno("can't open patch '%s'", arg); + die_errno(_("can't open patch '%s'"), arg); read_stdin = 0; set_default_whitespace_mode(whitespace_option); errs |= apply_patch(fd, arg, options); @@@ -4403,32 -3956,32 +4403,32 @@@ squelch_whitespace_errors < whitespace_error) { int squelched = whitespace_error - squelch_whitespace_errors; - warning("squelched %d " - "whitespace error%s", - squelched, - squelched == 1 ? "" : "s"); + warning(Q_("squelched %d whitespace error", + "squelched %d whitespace errors", + squelched), + squelched); } if (ws_error_action == die_on_ws_error) - die("%d line%s add%s whitespace errors.", - whitespace_error, - whitespace_error == 1 ? "" : "s", - whitespace_error == 1 ? "s" : ""); + die(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + whitespace_error), + whitespace_error); if (applied_after_fixing_ws && apply) warning("%d line%s applied after" " fixing whitespace errors.", applied_after_fixing_ws, applied_after_fixing_ws == 1 ? "" : "s"); else if (whitespace_error) - warning("%d line%s add%s whitespace errors.", - whitespace_error, - whitespace_error == 1 ? "" : "s", - whitespace_error == 1 ? "s" : ""); + warning(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + whitespace_error), + whitespace_error); } if (update_index) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) - die("Unable to write new index file"); + die(_("Unable to write new index file")); } return !!errs; diff --combined builtin/cat-file.c index 36a9104433,c27268ff30..af74e775a1 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@@ -11,7 -11,6 +11,7 @@@ #include "parse-options.h" #include "diff.h" #include "userdiff.h" +#include "streaming.h" #define BATCH 1 #define BATCH_CHECK 2 @@@ -91,7 -90,7 +91,7 @@@ static int cat_one_file(int opt, const unsigned long size; struct object_context obj_context; - if (get_sha1_with_context(obj_name, sha1, &obj_context)) + if (get_sha1_with_context(obj_name, 0, sha1, &obj_context)) die("Not a valid object name %s", obj_name); buf = NULL; @@@ -128,8 -127,6 +128,8 @@@ return cmd_ls_tree(2, ls_args, NULL); } + if (type == OBJ_BLOB) + return stream_blob_to_fd(1, sha1, NULL, 0); buf = read_sha1_file(sha1, &type, &size); if (!buf) die("Cannot read object %s", obj_name); @@@ -152,28 -149,6 +152,28 @@@ break; case 0: + if (type_from_string(exp_type) == OBJ_BLOB) { + unsigned char blob_sha1[20]; + if (sha1_object_info(sha1, NULL) == OBJ_TAG) { + enum object_type type; + unsigned long size; + char *buffer = read_sha1_file(sha1, &type, &size); + if (memcmp(buffer, "object ", 7) || + get_sha1_hex(buffer + 7, blob_sha1)) + die("%s not a valid tag", sha1_to_hex(sha1)); + free(buffer); + } else + hashcpy(blob_sha1, sha1); + + if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB) + return stream_blob_to_fd(1, blob_sha1, NULL, 0); + /* + * we attempted to dereference a tag to a blob + * and failed; there may be new dereference + * mechanisms this code is not aware of. + * fall-back to the usual case. + */ + } buf = read_object_with_reference(sha1, exp_type, &size, NULL); break; @@@ -251,8 -226,14 +251,8 @@@ static const char * const cat_file_usag static int git_cat_file_config(const char *var, const char *value, void *cb) { - switch (userdiff_config(var, value)) { - case 0: - break; - case -1: + if (userdiff_config(var, value) < 0) return -1; - default: - return 0; - } return git_default_config(var, value, cb); } diff --combined builtin/log.c index adcbcf1f24,9363f3929c..ecc2793690 --- a/builtin/log.c +++ b/builtin/log.c @@@ -20,8 -20,6 +20,8 @@@ #include "string-list.h" #include "parse-options.h" #include "branch.h" +#include "streaming.h" +#include "version.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@@ -79,8 -77,6 +79,8 @@@ static void cmd_log_init_defaults(struc get_commit_format(fmt_pretty, rev); rev->verbose_header = 1; DIFF_OPT_SET(&rev->diffopt, RECURSIVE); + rev->diffopt.stat_width = -1; /* use full terminal width */ + rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; @@@ -367,6 -363,7 +367,7 @@@ int cmd_whatchanged(int argc, const cha rev.simplify_history = 0; memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; + opt.revarg_opt = REVARG_COMMITTISH; cmd_log_init(argc, argv, prefix, &rev, &opt); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; @@@ -385,13 -382,8 +386,13 @@@ static void show_tagger(char *buf, int strbuf_release(&out); } -static int show_object(const unsigned char *sha1, int show_tag_object, - struct rev_info *rev) +static int show_blob_object(const unsigned char *sha1, struct rev_info *rev) +{ + fflush(stdout); + return stream_blob_to_fd(1, sha1, NULL, 0); +} + +static int show_tag_object(const unsigned char *sha1, struct rev_info *rev) { unsigned long size; enum object_type type; @@@ -401,16 -393,16 +402,16 @@@ if (!buf) return error(_("Could not read object %s"), sha1_to_hex(sha1)); - if (show_tag_object) - while (offset < size && buf[offset] != '\n') { - int new_offset = offset + 1; - while (new_offset < size && buf[new_offset++] != '\n') - ; /* do nothing */ - if (!prefixcmp(buf + offset, "tagger ")) - show_tagger(buf + offset + 7, - new_offset - offset - 7, rev); - offset = new_offset; - } + assert(type == OBJ_TAG); + while (offset < size && buf[offset] != '\n') { + int new_offset = offset + 1; + while (new_offset < size && buf[new_offset++] != '\n') + ; /* do nothing */ + if (!prefixcmp(buf + offset, "tagger ")) + show_tagger(buf + offset + 7, + new_offset - offset - 7, rev); + offset = new_offset; + } if (offset < size) fwrite(buf + offset, size - offset, 1, stdout); @@@ -456,16 -448,11 +457,16 @@@ int cmd_show(int argc, const char **arg rev.diff = 1; rev.always_show_header = 1; rev.no_walk = 1; + rev.diffopt.stat_width = -1; /* Scale to real terminal size */ + memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.tweak = show_rev_tweak_rev; cmd_log_init(argc, argv, prefix, &rev, &opt); + if (!rev.no_walk) + return cmd_log_walk(&rev); + count = rev.pending.nr; objects = rev.pending.objects; for (i = 0; i < count && !ret; i++) { @@@ -473,7 -460,7 +474,7 @@@ const char *name = objects[i].name; switch (o->type) { case OBJ_BLOB: - ret = show_object(o->sha1, 0, NULL); + ret = show_blob_object(o->sha1, NULL); break; case OBJ_TAG: { struct tag *t = (struct tag *)o; @@@ -484,7 -471,7 +485,7 @@@ diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); - ret = show_object(o->sha1, 1, &rev); + ret = show_tag_object(o->sha1, &rev); rev.shown_one = 1; if (ret) break; @@@ -557,6 -544,7 +558,7 @@@ int cmd_log(int argc, const char **argv rev.always_show_header = 1; memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; + opt.revarg_opt = REVARG_COMMITTISH; cmd_log_init(argc, argv, prefix, &rev, &opt); return cmd_log_walk(&rev); } @@@ -667,8 -655,7 +669,8 @@@ static FILE *realstdout = NULL static const char *output_directory = NULL; static int outdir_offset; -static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet) +static int reopen_stdout(struct commit *commit, const char *subject, + struct rev_info *rev, int quiet) { struct strbuf filename = STRBUF_INIT; int suffix_len = strlen(fmt_patch_suffix) + 1; @@@ -682,7 -669,7 +684,7 @@@ strbuf_addch(&filename, '/'); } - get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename); + get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename); if (!quiet) fprintf(realstdout, "%s\n", filename.buf + outdir_offset); @@@ -742,10 -729,15 +744,10 @@@ static void get_patch_ids(struct rev_in static void gen_message_id(struct rev_info *info, char *base) { - const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME); - const char *email_start = strrchr(committer, '<'); - const char *email_end = strrchr(committer, '>'); struct strbuf buf = STRBUF_INIT; - if (!email_start || !email_end || email_start > email_end - 1) - die(_("Could not extract email from committer identity.")); - strbuf_addf(&buf, "%s.%lu.git.%.*s", base, + strbuf_addf(&buf, "%s.%lu.git.%s", base, (unsigned long) time(NULL), - (int)(email_end - email_start - 1), email_start + 1); + git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT)); info->message_id = strbuf_detach(&buf, NULL); } @@@ -784,6 -776,7 +786,6 @@@ static void make_cover_letter(struct re const char *encoding = "UTF-8"; struct diff_options opts; int need_8bit_cte = 0; - struct commit *commit = NULL; struct pretty_print_context pp = {0}; if (rev->commit_format != CMIT_FMT_EMAIL) @@@ -791,10 -784,31 +793,10 @@@ committer = git_committer_info(0); - if (!numbered_files) { - /* - * We fake a commit for the cover letter so we get the filename - * desired. - */ - commit = xcalloc(1, sizeof(*commit)); - commit->buffer = xmalloc(400); - snprintf(commit->buffer, 400, - "tree 0000000000000000000000000000000000000000\n" - "parent %s\n" - "author %s\n" - "committer %s\n\n" - "cover letter\n", - sha1_to_hex(head->object.sha1), committer, committer); - } - - if (!use_stdout && reopen_stdout(commit, rev, quiet)) + if (!use_stdout && + reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet)) return; - if (commit) { - - free(commit->buffer); - free(commit); - } - log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, &need_8bit_cte); @@@ -1132,6 -1146,7 +1134,7 @@@ int cmd_format_patch(int argc, const ch rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; + s_r_opt.revarg_opt = REVARG_COMMITTISH; if (default_attach) { rev.mime_boundary = default_attach; @@@ -1151,7 -1166,7 +1154,7 @@@ if (do_signoff) { const char *committer; const char *endpos; - committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); + committer = git_committer_info(IDENT_STRICT); endpos = strchr(committer, '>'); if (!endpos) die(_("bogus committer info %s"), committer); @@@ -1389,8 -1404,8 +1392,8 @@@ gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } - if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, - &rev, quiet)) + if (!use_stdout && + reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); diff --combined builtin/pack-objects.c index f3348208d8,48ccaddadf..782e7d0c38 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@@ -16,14 -16,18 +16,14 @@@ #include "list-objects.h" #include "progress.h" #include "refs.h" +#include "streaming.h" #include "thread-utils.h" -static const char pack_usage[] = - "git pack-objects [ -q | --progress | --all-progress ]\n" - " [--all-progress-implied]\n" - " [--max-pack-size=] [--local] [--incremental]\n" - " [--window=] [--window-memory=] [--depth=]\n" - " [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset]\n" - " [--threads=] [--non-empty] [--revs [--unpacked | --all]]\n" - " [--reflog] [--stdout | base-name] [--include-tag]\n" - " [--keep-unreachable | --unpack-unreachable]\n" - " [< ref-list | < object-list]"; +static const char *pack_usage[] = { + "git pack-objects --stdout [options...] [< ref-list | < object-list]", + "git pack-objects [options...] base-name [< ref-list | < object-list]", + NULL +}; struct object_entry { struct pack_idx_entry idx; @@@ -64,7 -68,6 +64,7 @@@ static uint32_t nr_objects, nr_alloc, n static int non_empty; static int reuse_delta = 1, reuse_object = 1; static int keep_unreachable, unpack_unreachable, include_tag; +static unsigned long unpack_unreachable_expiration; static int local; static int incremental; static int ignore_packed_keep; @@@ -151,46 -154,6 +151,46 @@@ static unsigned long do_compress(void * return stream.total_out; } +static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f, + const unsigned char *sha1) +{ + git_zstream stream; + unsigned char ibuf[1024 * 16]; + unsigned char obuf[1024 * 16]; + unsigned long olen = 0; + + memset(&stream, 0, sizeof(stream)); + git_deflate_init(&stream, pack_compression_level); + + for (;;) { + ssize_t readlen; + int zret = Z_OK; + readlen = read_istream(st, ibuf, sizeof(ibuf)); + if (readlen == -1) + die(_("unable to read %s"), sha1_to_hex(sha1)); + + stream.next_in = ibuf; + stream.avail_in = readlen; + while ((stream.avail_in || readlen == 0) && + (zret == Z_OK || zret == Z_BUF_ERROR)) { + stream.next_out = obuf; + stream.avail_out = sizeof(obuf); + zret = git_deflate(&stream, readlen ? 0 : Z_FINISH); + sha1write(f, obuf, stream.next_out - obuf); + olen += stream.next_out - obuf; + } + if (stream.avail_in) + die(_("deflate error (%d)"), zret); + if (readlen == 0) { + if (zret != Z_STREAM_END) + die(_("deflate error (%d)"), zret); + break; + } + } + git_deflate_end(&stream); + return olen; +} + /* * we are going to reuse the existing object data as is. make * sure it is not corrupt. @@@ -241,198 -204,22 +241,198 @@@ static void copy_pack_data(struct sha1f } /* Return 0 if we will bust the pack-size limit */ -static unsigned long write_object(struct sha1file *f, - struct object_entry *entry, - off_t write_offset) +static unsigned long write_no_reuse_object(struct sha1file *f, struct object_entry *entry, + unsigned long limit, int usable_delta) { - unsigned long size, limit, datalen; - void *buf; + unsigned long size, datalen; unsigned char header[10], dheader[10]; unsigned hdrlen; enum object_type type; + void *buf; + struct git_istream *st = NULL; + + if (!usable_delta) { + if (entry->type == OBJ_BLOB && + entry->size > big_file_threshold && + (st = open_istream(entry->idx.sha1, &type, &size, NULL)) != NULL) + buf = NULL; + else { + buf = read_sha1_file(entry->idx.sha1, &type, &size); + if (!buf) + die(_("unable to read %s"), sha1_to_hex(entry->idx.sha1)); + } + /* + * make sure no cached delta data remains from a + * previous attempt before a pack split occurred. + */ + free(entry->delta_data); + entry->delta_data = NULL; + entry->z_delta_size = 0; + } else if (entry->delta_data) { + size = entry->delta_size; + buf = entry->delta_data; + entry->delta_data = NULL; + type = (allow_ofs_delta && entry->delta->idx.offset) ? + OBJ_OFS_DELTA : OBJ_REF_DELTA; + } else { + buf = get_delta(entry); + size = entry->delta_size; + type = (allow_ofs_delta && entry->delta->idx.offset) ? + OBJ_OFS_DELTA : OBJ_REF_DELTA; + } + + if (st) /* large blob case, just assume we don't compress well */ + datalen = size; + else if (entry->z_delta_size) + datalen = entry->z_delta_size; + else + datalen = do_compress(&buf, size); + + /* + * The object header is a byte of 'type' followed by zero or + * more bytes of length. + */ + hdrlen = encode_in_pack_object_header(type, size, header); + + if (type == OBJ_OFS_DELTA) { + /* + * Deltas with relative base contain an additional + * encoding of the relative offset for the delta + * base from this object's position in the pack. + */ + off_t ofs = entry->idx.offset - entry->delta->idx.offset; + unsigned pos = sizeof(dheader) - 1; + dheader[pos] = ofs & 127; + while (ofs >>= 7) + dheader[--pos] = 128 | (--ofs & 127); + if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { + if (st) + close_istream(st); + free(buf); + return 0; + } + sha1write(f, header, hdrlen); + sha1write(f, dheader + pos, sizeof(dheader) - pos); + hdrlen += sizeof(dheader) - pos; + } else if (type == OBJ_REF_DELTA) { + /* + * Deltas with a base reference contain + * an additional 20 bytes for the base sha1. + */ + if (limit && hdrlen + 20 + datalen + 20 >= limit) { + if (st) + close_istream(st); + free(buf); + return 0; + } + sha1write(f, header, hdrlen); + sha1write(f, entry->delta->idx.sha1, 20); + hdrlen += 20; + } else { + if (limit && hdrlen + datalen + 20 >= limit) { + if (st) + close_istream(st); + free(buf); + return 0; + } + sha1write(f, header, hdrlen); + } + if (st) { + datalen = write_large_blob_data(st, f, entry->idx.sha1); + close_istream(st); + } else { + sha1write(f, buf, datalen); + free(buf); + } + + return hdrlen + datalen; +} + +/* Return 0 if we will bust the pack-size limit */ +static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry, + unsigned long limit, int usable_delta) +{ + struct packed_git *p = entry->in_pack; + struct pack_window *w_curs = NULL; + struct revindex_entry *revidx; + off_t offset; + enum object_type type = entry->type; + unsigned long datalen; + unsigned char header[10], dheader[10]; + unsigned hdrlen; + + if (entry->delta) + type = (allow_ofs_delta && entry->delta->idx.offset) ? + OBJ_OFS_DELTA : OBJ_REF_DELTA; + hdrlen = encode_in_pack_object_header(type, entry->size, header); + + offset = entry->in_pack_offset; + revidx = find_pack_revindex(p, offset); + datalen = revidx[1].offset - offset; + if (!pack_to_stdout && p->index_version > 1 && + check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) { + error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1)); + unuse_pack(&w_curs); + return write_no_reuse_object(f, entry, limit, usable_delta); + } + + offset += entry->in_pack_header_size; + datalen -= entry->in_pack_header_size; + + if (!pack_to_stdout && p->index_version == 1 && + check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) { + error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1)); + unuse_pack(&w_curs); + return write_no_reuse_object(f, entry, limit, usable_delta); + } + + if (type == OBJ_OFS_DELTA) { + off_t ofs = entry->idx.offset - entry->delta->idx.offset; + unsigned pos = sizeof(dheader) - 1; + dheader[pos] = ofs & 127; + while (ofs >>= 7) + dheader[--pos] = 128 | (--ofs & 127); + if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { + unuse_pack(&w_curs); + return 0; + } + sha1write(f, header, hdrlen); + sha1write(f, dheader + pos, sizeof(dheader) - pos); + hdrlen += sizeof(dheader) - pos; + reused_delta++; + } else if (type == OBJ_REF_DELTA) { + if (limit && hdrlen + 20 + datalen + 20 >= limit) { + unuse_pack(&w_curs); + return 0; + } + sha1write(f, header, hdrlen); + sha1write(f, entry->delta->idx.sha1, 20); + hdrlen += 20; + reused_delta++; + } else { + if (limit && hdrlen + datalen + 20 >= limit) { + unuse_pack(&w_curs); + return 0; + } + sha1write(f, header, hdrlen); + } + copy_pack_data(f, p, &w_curs, offset, datalen); + unuse_pack(&w_curs); + reused++; + return hdrlen + datalen; +} + +/* Return 0 if we will bust the pack-size limit */ +static unsigned long write_object(struct sha1file *f, + struct object_entry *entry, + off_t write_offset) +{ + unsigned long limit, len; int usable_delta, to_reuse; if (!pack_to_stdout) crc32_begin(f); - type = entry->type; - /* apply size limit if limited packsize and not first object */ if (!pack_size_limit || !nr_written) limit = 0; @@@ -460,11 -247,11 +460,11 @@@ to_reuse = 0; /* explicit */ else if (!entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ - else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) + else if (entry->type == OBJ_REF_DELTA || entry->type == OBJ_OFS_DELTA) /* check_object() decided it for us ... */ to_reuse = usable_delta; /* ... but pack split may override that */ - else if (type != entry->in_pack_type) + else if (entry->type != entry->in_pack_type) to_reuse = 0; /* pack has delta which is unusable */ else if (entry->delta) to_reuse = 0; /* we want to pack afresh */ @@@ -473,19 -260,153 +473,19 @@@ * and we do not need to deltify it. */ - if (!to_reuse) { - no_reuse: - if (!usable_delta) { - buf = read_sha1_file(entry->idx.sha1, &type, &size); - if (!buf) - die("unable to read %s", sha1_to_hex(entry->idx.sha1)); - /* - * make sure no cached delta data remains from a - * previous attempt before a pack split occurred. - */ - free(entry->delta_data); - entry->delta_data = NULL; - entry->z_delta_size = 0; - } else if (entry->delta_data) { - size = entry->delta_size; - buf = entry->delta_data; - entry->delta_data = NULL; - type = (allow_ofs_delta && entry->delta->idx.offset) ? - OBJ_OFS_DELTA : OBJ_REF_DELTA; - } else { - buf = get_delta(entry); - size = entry->delta_size; - type = (allow_ofs_delta && entry->delta->idx.offset) ? - OBJ_OFS_DELTA : OBJ_REF_DELTA; - } - - if (entry->z_delta_size) - datalen = entry->z_delta_size; - else - datalen = do_compress(&buf, size); - - /* - * The object header is a byte of 'type' followed by zero or - * more bytes of length. - */ - hdrlen = encode_in_pack_object_header(type, size, header); - - if (type == OBJ_OFS_DELTA) { - /* - * Deltas with relative base contain an additional - * encoding of the relative offset for the delta - * base from this object's position in the pack. - */ - off_t ofs = entry->idx.offset - entry->delta->idx.offset; - unsigned pos = sizeof(dheader) - 1; - dheader[pos] = ofs & 127; - while (ofs >>= 7) - dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { - free(buf); - return 0; - } - sha1write(f, header, hdrlen); - sha1write(f, dheader + pos, sizeof(dheader) - pos); - hdrlen += sizeof(dheader) - pos; - } else if (type == OBJ_REF_DELTA) { - /* - * Deltas with a base reference contain - * an additional 20 bytes for the base sha1. - */ - if (limit && hdrlen + 20 + datalen + 20 >= limit) { - free(buf); - return 0; - } - sha1write(f, header, hdrlen); - sha1write(f, entry->delta->idx.sha1, 20); - hdrlen += 20; - } else { - if (limit && hdrlen + datalen + 20 >= limit) { - free(buf); - return 0; - } - sha1write(f, header, hdrlen); - } - sha1write(f, buf, datalen); - free(buf); - } - else { - struct packed_git *p = entry->in_pack; - struct pack_window *w_curs = NULL; - struct revindex_entry *revidx; - off_t offset; - - if (entry->delta) - type = (allow_ofs_delta && entry->delta->idx.offset) ? - OBJ_OFS_DELTA : OBJ_REF_DELTA; - hdrlen = encode_in_pack_object_header(type, entry->size, header); - - offset = entry->in_pack_offset; - revidx = find_pack_revindex(p, offset); - datalen = revidx[1].offset - offset; - if (!pack_to_stdout && p->index_version > 1 && - check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) { - error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1)); - unuse_pack(&w_curs); - goto no_reuse; - } - - offset += entry->in_pack_header_size; - datalen -= entry->in_pack_header_size; - if (!pack_to_stdout && p->index_version == 1 && - check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) { - error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1)); - unuse_pack(&w_curs); - goto no_reuse; - } + if (!to_reuse) + len = write_no_reuse_object(f, entry, limit, usable_delta); + else + len = write_reuse_object(f, entry, limit, usable_delta); + if (!len) + return 0; - if (type == OBJ_OFS_DELTA) { - off_t ofs = entry->idx.offset - entry->delta->idx.offset; - unsigned pos = sizeof(dheader) - 1; - dheader[pos] = ofs & 127; - while (ofs >>= 7) - dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { - unuse_pack(&w_curs); - return 0; - } - sha1write(f, header, hdrlen); - sha1write(f, dheader + pos, sizeof(dheader) - pos); - hdrlen += sizeof(dheader) - pos; - reused_delta++; - } else if (type == OBJ_REF_DELTA) { - if (limit && hdrlen + 20 + datalen + 20 >= limit) { - unuse_pack(&w_curs); - return 0; - } - sha1write(f, header, hdrlen); - sha1write(f, entry->delta->idx.sha1, 20); - hdrlen += 20; - reused_delta++; - } else { - if (limit && hdrlen + datalen + 20 >= limit) { - unuse_pack(&w_curs); - return 0; - } - sha1write(f, header, hdrlen); - } - copy_pack_data(f, p, &w_curs, offset, datalen); - unuse_pack(&w_curs); - reused++; - } if (usable_delta) written_delta++; written++; if (!pack_to_stdout) entry->idx.crc32 = crc32_end(f); - return hdrlen + datalen; + return len; } enum write_one_status { @@@ -1410,7 -1331,7 +1410,7 @@@ static void get_object_details(void for (i = 0; i < nr_objects; i++) { struct object_entry *entry = sorted_by_offset[i]; check_object(entry); - if (big_file_threshold <= entry->size) + if (big_file_threshold < entry->size) entry->no_try_delta = 1; } @@@ -2333,10 -2254,6 +2333,10 @@@ static void loosen_unused_packed_object if (!p->pack_local || p->pack_keep) continue; + if (unpack_unreachable_expiration && + p->mtime < unpack_unreachable_expiration) + continue; + if (open_pack_index(p)) die("cannot open pack index"); @@@ -2373,7 -2290,7 +2373,7 @@@ static void get_object_list(int ac, con } die("not a rev '%s'", line); } - if (handle_revision_arg(line, &revs, flags, 1)) + if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME)) die("bad revision '%s'", line); } @@@ -2388,175 -2305,204 +2388,175 @@@ loosen_unused_packed_objects(&revs); } +static int option_parse_index_version(const struct option *opt, + const char *arg, int unset) +{ + char *c; + const char *val = arg; + pack_idx_opts.version = strtoul(val, &c, 10); + if (pack_idx_opts.version > 2) + die(_("unsupported index version %s"), val); + if (*c == ',' && c[1]) + pack_idx_opts.off32_limit = strtoul(c+1, &c, 0); + if (*c || pack_idx_opts.off32_limit & 0x80000000) + die(_("bad index version '%s'"), val); + return 0; +} + +static int option_parse_unpack_unreachable(const struct option *opt, + const char *arg, int unset) +{ + if (unset) { + unpack_unreachable = 0; + unpack_unreachable_expiration = 0; + } + else { + unpack_unreachable = 1; + if (arg) + unpack_unreachable_expiration = approxidate(arg); + } + return 0; +} + +static int option_parse_ulong(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + die(_("option %s does not accept negative form"), + opt->long_name); + + if (!git_parse_ulong(arg, opt->value)) + die(_("unable to parse value '%s' for option %s"), + arg, opt->long_name); + return 0; +} + +#define OPT_ULONG(s, l, v, h) \ + { OPTION_CALLBACK, (s), (l), (v), "n", (h), \ + PARSE_OPT_NONEG, option_parse_ulong } + int cmd_pack_objects(int argc, const char **argv, const char *prefix) { int use_internal_rev_list = 0; int thin = 0; int all_progress_implied = 0; - uint32_t i; - const char **rp_av; - int rp_ac_alloc = 64; - int rp_ac; + const char *rp_av[6]; + int rp_ac = 0; + int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; + struct option pack_objects_options[] = { + OPT_SET_INT('q', "quiet", &progress, + "do not show progress meter", 0), + OPT_SET_INT(0, "progress", &progress, + "show progress meter", 1), + OPT_SET_INT(0, "all-progress", &progress, + "show progress meter during object writing phase", 2), + OPT_BOOL(0, "all-progress-implied", + &all_progress_implied, + "similar to --all-progress when progress meter is shown"), + { OPTION_CALLBACK, 0, "index-version", NULL, "version[,offset]", + "write the pack index file in the specified idx format version", + 0, option_parse_index_version }, + OPT_ULONG(0, "max-pack-size", &pack_size_limit, + "maximum size of each output pack file"), + OPT_BOOL(0, "local", &local, + "ignore borrowed objects from alternate object store"), + OPT_BOOL(0, "incremental", &incremental, + "ignore packed objects"), + OPT_INTEGER(0, "window", &window, + "limit pack window by objects"), + OPT_ULONG(0, "window-memory", &window_memory_limit, + "limit pack window by memory in addition to object limit"), + OPT_INTEGER(0, "depth", &depth, + "maximum length of delta chain allowed in the resulting pack"), + OPT_BOOL(0, "reuse-delta", &reuse_delta, + "reuse existing deltas"), + OPT_BOOL(0, "reuse-object", &reuse_object, + "reuse existing objects"), + OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta, + "use OFS_DELTA objects"), + OPT_INTEGER(0, "threads", &delta_search_threads, + "use threads when searching for best delta matches"), + OPT_BOOL(0, "non-empty", &non_empty, + "do not create an empty pack output"), + OPT_BOOL(0, "revs", &use_internal_rev_list, + "read revision arguments from standard input"), + { OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL, + "limit the objects to those that are not yet packed", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + { OPTION_SET_INT, 0, "all", &rev_list_all, NULL, + "include objects reachable from any reference", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL, + "include objects referred by reflog entries", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + OPT_BOOL(0, "stdout", &pack_to_stdout, + "output pack to stdout"), + OPT_BOOL(0, "include-tag", &include_tag, + "include tag objects that refer to objects to be packed"), + OPT_BOOL(0, "keep-unreachable", &keep_unreachable, + "keep unreachable objects"), + { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, "time", + "unpack unreachable objects newer than