diff options
Diffstat (limited to 'builtin')
38 files changed, 2425 insertions, 1051 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..3bf71dc452 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; }; @@ -179,7 +188,6 @@ struct patch { int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ int rejected; unsigned ws_rule; - unsigned long deflate_origlen; int lines_added, lines_deleted; int score; unsigned int is_toplevel_relative:1; @@ -188,14 +196,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 +345,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 +379,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 +466,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 +489,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 +654,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 +681,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 +695,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 +820,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 +829,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 +892,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 +905,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 +927,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 +942,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 +994,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 +1018,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 +1026,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 +1034,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 +1116,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 +1243,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 +1359,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 +1399,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 +1413,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 +1427,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 +1456,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 +1473,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 +1543,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 +1633,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 +1661,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 +1697,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 +1742,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 +1834,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 +1848,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 +1877,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 +1900,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 +1963,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 +2053,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 +2128,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 +2467,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 +2543,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 +2650,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; } if (added_blank_line) { @@ -2657,9 +2767,11 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, 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); } /* @@ -2668,13 +2780,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, */ 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); } @@ -2693,7 +2805,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch) 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); @@ -2728,6 +2840,12 @@ static int apply_binary_fragment(struct image *img, struct patch *patch) 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; @@ -2790,13 +2908,13 @@ static int apply_binary(struct image *img, struct patch *patch) * 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)); } @@ -2817,7 +2935,7 @@ static int apply_fragments(struct image *img, struct patch *patch) 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; @@ -2827,20 +2945,17 @@ static int apply_fragments(struct image *img, struct patch *patch) 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 */ @@ -2849,6 +2964,13 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) 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; @@ -2867,9 +2989,15 @@ static struct patch *in_fn_table(const char *name) * 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) @@ -2921,152 +3049,346 @@ static void prepare_fn_table(struct patch *patch) } } -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) @@ -3074,9 +3396,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s 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; @@ -3085,10 +3407,50 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s 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; @@ -3107,31 +3469,45 @@ static int check_patch(struct patch *patch) 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; @@ -3144,14 +3520,22 @@ static int check_patch(struct patch *patch) 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; } @@ -3164,7 +3548,7 @@ static int check_patch_list(struct patch *patch) while (patch) { if (apply_verbosely) say_patch_name(stderr, - "Checking patch ", patch, "...\n"); + _("Checking patch %s..."), patch); err |= check_patch(patch); patch = patch->next; } @@ -3204,7 +3588,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename) 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)) @@ -3219,7 +3603,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename) 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); } @@ -3362,7 +3746,7 @@ static void remove_file(struct patch *patch, int rmdir_empty) { 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) { @@ -3384,24 +3768,25 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned ce = xcalloc(1, ce_size); memcpy(ce->name, path, namelen); ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = namelen; + ce->ce_flags = create_ce_flags(0); + ce->ce_namelen = namelen; if (S_ISGITLINK(mode)) { 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) @@ -3434,7 +3819,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, strbuf_release(&nbuf); if (close(fd) < 0) - die_errno("closing file '%s'", path); + die_errno(_("closing file '%s'"), path); return 0; } @@ -3483,7 +3868,34 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned ++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(stage); + ce->ce_namelen = namelen; + 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) @@ -3496,7 +3908,11 @@ static void create_file(struct patch *patch) 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 */ @@ -3528,6 +3944,7 @@ static int write_out_one_reject(struct patch *patch) 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) @@ -3538,7 +3955,7 @@ static int write_out_one_reject(struct patch *patch) if (!cnt) { if (apply_verbosely) say_patch_name(stderr, - "Applied patch ", patch, " cleanly.\n"); + _("Applied patch %s cleanly."), patch); return 0; } @@ -3546,16 +3963,20 @@ static int write_out_one_reject(struct patch *patch) * 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); @@ -3563,7 +3984,7 @@ static int write_out_one_reject(struct patch *patch) 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 @@ -3576,10 +3997,10 @@ static int write_out_one_reject(struct patch *patch) 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); @@ -3593,6 +4014,7 @@ static int write_out_results(struct patch *list) int phase; int errs = 0; struct patch *l; + struct string_list cpath = STRING_LIST_INIT_DUP; for (phase = 0; phase < 2; phase++) { l = list; @@ -3601,12 +4023,30 @@ static int write_out_results(struct patch *list) 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; } @@ -3665,15 +4105,8 @@ static void prefix_patches(struct patch *p) 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); } } @@ -3683,12 +4116,10 @@ static void prefix_patches(struct patch *p) 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; @@ -3712,15 +4143,14 @@ static int apply_patch(int fd, const char *filename, int options) 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; @@ -3731,7 +4161,7 @@ static int apply_patch(int fd, const char *filename, int options) if (check_index) { if (read_cache() < 0) - die("unable to read index file"); + die(_("unable to read index file")); } if ((check || apply) && @@ -3739,8 +4169,12 @@ static int apply_patch(int fd, const char *filename, int options) !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); @@ -3754,7 +4188,9 @@ static int apply_patch(int fd, const char *filename, int options) if (summary) summary_patch_list(list); + free_patch_list(list); strbuf_release(&buf); + string_list_clear(&fn_table, 0); return 0; } @@ -3844,66 +4280,68 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) 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 <num> leading slashes from traditional diff paths", + { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), + N_("remove <num> 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 <n> lines of context match"), - { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action", - "detect new or modified lines that have whitespace errors", + N_("ensure at least <n> 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 <root> to all filenames", + { OPTION_CALLBACK, 0, "directory", NULL, N_("root"), + N_("prepend <root> to all filenames"), 0, option_parse_directory }, OPT_END() }; @@ -3919,15 +4357,24 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) 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++) { @@ -3943,7 +4390,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) 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); @@ -3957,32 +4404,32 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) 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 --git a/builtin/blame.c b/builtin/blame.c index 324d476abf..48dbfa4304 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -88,6 +88,20 @@ struct origin { char path[FLEX_ARRAY]; }; +static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen, + xdl_emit_hunk_consume_func_t hunk_func, void *cb_data) +{ + xpparam_t xpp = {0}; + xdemitconf_t xecfg = {0}; + xdemitcb_t ecb = {NULL}; + + xpp.flags = xdl_opts; + xecfg.ctxlen = ctxlen; + xecfg.hunk_func = hunk_func; + ecb.priv = cb_data; + return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb); +} + /* * Prepare diff_filespec and convert it using diff textconv API * if the textconv driver exists. @@ -392,8 +406,7 @@ static struct origin *find_origin(struct scoreboard *sb, paths[1] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("diff-setup"); + diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) do_diff_cache(parent->tree->object.sha1, &diff_opts); @@ -479,8 +492,7 @@ static struct origin *find_rename(struct scoreboard *sb, diff_opts.single_follow = origin->path; paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("diff-setup"); + diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) do_diff_cache(parent->tree->object.sha1, &diff_opts); @@ -759,12 +771,14 @@ struct blame_chunk_cb_data { long tlno; }; -static void blame_chunk_cb(void *data, long same, long p_next, long t_next) +static int blame_chunk_cb(long start_a, long count_a, + long start_b, long count_b, void *data) { struct blame_chunk_cb_data *d = data; - blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent); - d->plno = p_next; - d->tlno = t_next; + blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent); + d->plno = start_a + count_a; + d->tlno = start_b + count_b; + return 0; } /* @@ -779,8 +793,7 @@ static int pass_blame_to_parent(struct scoreboard *sb, int last_in_target; mmfile_t file_p, file_o; struct blame_chunk_cb_data d; - xpparam_t xpp; - xdemitconf_t xecfg; + memset(&d, 0, sizeof(d)); d.sb = sb; d.target = target; d.parent = parent; last_in_target = find_last_in_target(sb, target); @@ -791,11 +804,7 @@ static int pass_blame_to_parent(struct scoreboard *sb, fill_origin_blob(&sb->revs->diffopt, target, &file_o); num_get_patch++; - memset(&xpp, 0, sizeof(xpp)); - xpp.flags = xdl_opts; - memset(&xecfg, 0, sizeof(xecfg)); - xecfg.ctxlen = 0; - xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg); + diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d); /* The rest (i.e. anything after tlno) are the same as the parent */ blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent); @@ -899,12 +908,15 @@ struct handle_split_cb_data { long tlno; }; -static void handle_split_cb(void *data, long same, long p_next, long t_next) +static int handle_split_cb(long start_a, long count_a, + long start_b, long count_b, void *data) { struct handle_split_cb_data *d = data; - handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split); - d->plno = p_next; - d->tlno = t_next; + handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent, + d->split); + d->plno = start_a + count_a; + d->tlno = start_b + count_b; + return 0; } /* @@ -922,8 +934,7 @@ static void find_copy_in_blob(struct scoreboard *sb, int cnt; mmfile_t file_o; struct handle_split_cb_data d; - xpparam_t xpp; - xdemitconf_t xecfg; + memset(&d, 0, sizeof(d)); d.sb = sb; d.ent = ent; d.parent = parent; d.split = split; /* @@ -943,12 +954,8 @@ static void find_copy_in_blob(struct scoreboard *sb, * file_o is a part of final image we are annotating. * file_p partially may match that image. */ - memset(&xpp, 0, sizeof(xpp)); - xpp.flags = xdl_opts; - memset(&xecfg, 0, sizeof(xecfg)); - xecfg.ctxlen = 1; memset(split, 0, sizeof(struct blame_entry [3])); - xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg); + diff_hunks(file_p, &file_o, 1, handle_split_cb, &d); /* remainder, if any, all match the preimage */ handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split); } @@ -1065,8 +1072,7 @@ static int find_copy_in_parent(struct scoreboard *sb, paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); - if (diff_setup_done(&diff_opts) < 0) - die("diff-setup"); + diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; * we do not want to use diffcore_rename() actually to @@ -1828,6 +1834,16 @@ static int read_ancestry(const char *graft_file) return 0; } +static int update_auto_abbrev(int auto_abbrev, struct origin *suspect) +{ + const char *uniq = find_unique_abbrev(suspect->commit->object.sha1, + auto_abbrev); + int len = strlen(uniq); + if (auto_abbrev < len) + return len; + return auto_abbrev; +} + /* * How many columns do we need to show line numbers, authors, * and filenames? @@ -1838,12 +1854,16 @@ static void find_alignment(struct scoreboard *sb, int *option) int longest_dst_lines = 0; unsigned largest_score = 0; struct blame_entry *e; + int compute_auto_abbrev = (abbrev < 0); + int auto_abbrev = default_abbrev; for (e = sb->ent; e; e = e->next) { struct origin *suspect = e->suspect; struct commit_info ci; int num; + if (compute_auto_abbrev) + auto_abbrev = update_auto_abbrev(auto_abbrev, suspect); if (strcmp(suspect->path, sb->path)) *option |= OUTPUT_SHOW_NAME; num = strlen(suspect->path); @@ -1871,6 +1891,10 @@ static void find_alignment(struct scoreboard *sb, int *option) max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); max_score_digits = decimal_width(largest_score); + + if (compute_auto_abbrev) + /* one more abbrev length is needed for the boundary commit */ + abbrev = auto_abbrev + 1; } /* @@ -2144,7 +2168,8 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, ce = xcalloc(1, size); hashcpy(ce->sha1, origin->blob_sha1); memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(len, 0); + ce->ce_flags = create_ce_flags(0); + ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); @@ -2344,10 +2369,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_done: argc = parse_options_end(&ctx); - if (abbrev == -1) - abbrev = default_abbrev; - /* one more abbrev length is needed for the boundary commit */ - abbrev++; + if (0 < abbrev) + /* one more abbrev length is needed for the boundary commit */ + abbrev++; if (revs_file && read_ancestry(revs_file)) die_errno("reading graft file '%s' failed", revs_file); diff --git a/builtin/branch.c b/builtin/branch.c index d8cccf725d..0e060f2e4a 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -15,6 +15,8 @@ #include "branch.h" #include "diff.h" #include "revision.h" +#include "string-list.h" +#include "column.h" static const char * const builtin_branch_usage[] = { "git branch [options] [-r | -a] [--merged | --no-merged]", @@ -53,6 +55,9 @@ static enum merge_filter { } merge_filter; static unsigned char merge_filter_ref[20]; +static struct string_list output = STRING_LIST_INIT_DUP; +static unsigned int colopts; + static int parse_branch_color_slot(const char *var, int ofs) { if (!strcasecmp(var+ofs, "plain")) @@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs) static int git_branch_config(const char *var, const char *value, void *cb) { + if (!prefixcmp(var, "column.")) + return git_column_config(var, value, "branch", &colopts); if (!strcmp(var, "color.branch")) { branch_use_color = git_config_colorbool(var, value); return 0; @@ -146,26 +153,28 @@ static int branch_merged(int kind, const char *name, return merged; } -static int delete_branches(int argc, const char **argv, int force, int kinds) +static int delete_branches(int argc, const char **argv, int force, int kinds, + int quiet) { struct commit *rev, *head_rev = NULL; unsigned char sha1[20]; char *name = NULL; - const char *fmt, *remote; + const char *fmt; int i; int ret = 0; + int remote_branch = 0; struct strbuf bname = STRBUF_INIT; switch (kinds) { case REF_REMOTE_BRANCH: fmt = "refs/remotes/%s"; - /* TRANSLATORS: This is "remote " in "remote branch '%s' not found" */ - remote = _("remote "); + /* For subsequent UI messages */ + remote_branch = 1; + force = 1; break; case REF_LOCAL_BRANCH: fmt = "refs/heads/%s"; - remote = ""; break; default: die(_("cannot use -a with -d")); @@ -189,8 +198,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) name = xstrdup(mkpath(fmt, bname.buf)); if (read_ref(name, sha1)) { - error(_("%sbranch '%s' not found."), - remote, bname.buf); + error(remote_branch + ? _("remote branch '%s' not found.") + : _("branch '%s' not found."), bname.buf); ret = 1; continue; } @@ -211,14 +221,19 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) } if (delete_ref(name, sha1, 0)) { - error(_("Error deleting %sbranch '%s'"), remote, + error(remote_branch + ? _("Error deleting remote branch '%s'") + : _("Error deleting branch '%s'"), bname.buf); ret = 1; } else { struct strbuf buf = STRBUF_INIT; - printf(_("Deleted %sbranch %s (was %s).\n"), remote, - bname.buf, - find_unique_abbrev(sha1, DEFAULT_ABBREV)); + if (!quiet) + printf(remote_branch + ? _("Deleted remote branch %s (was %s).\n") + : _("Deleted branch %s (was %s).\n"), + bname.buf, + find_unique_abbrev(sha1, DEFAULT_ABBREV)); strbuf_addf(&buf, "branch.%s", bname.buf); if (git_config_rename_section(buf.buf, NULL) < 0) warning(_("Update of config-file failed")); @@ -376,6 +391,7 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name, int show_upstream_ref) { int ours, theirs; + char *ref = NULL; struct branch *branch = branch_get(branch_name); if (!stat_tracking_info(branch, &ours, &theirs)) { @@ -386,16 +402,29 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name, return; } - strbuf_addch(stat, '['); if (show_upstream_ref) - strbuf_addf(stat, "%s: ", - shorten_unambiguous_ref(branch->merge[0]->dst, 0)); - if (!ours) - strbuf_addf(stat, _("behind %d] "), theirs); - else if (!theirs) - strbuf_addf(stat, _("ahead %d] "), ours); - else - strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs); + ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0); + if (!ours) { + if (ref) + strbuf_addf(stat, _("[%s: behind %d]"), ref, theirs); + else + strbuf_addf(stat, _("[behind %d]"), theirs); + + } else if (!theirs) { + if (ref) + strbuf_addf(stat, _("[%s: ahead %d]"), ref, ours); + else + strbuf_addf(stat, _("[ahead %d]"), ours); + } else { + if (ref) + strbuf_addf(stat, _("[%s: ahead %d, behind %d]"), + ref, ours, theirs); + else + strbuf_addf(stat, _("[ahead %d, behind %d]"), + ours, theirs); + } + strbuf_addch(stat, ' '); + free(ref); } static int matches_merge_filter(struct commit *commit) @@ -474,7 +503,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, else if (verbose) /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ add_verbose_info(&out, item, verbose, abbrev); - printf("%s\n", out.buf); + if (column_active(colopts)) { + assert(!verbose && "--column and --verbose are incompatible"); + string_list_append(&output, out.buf); + } else { + printf("%s\n", out.buf); + } strbuf_release(&name); strbuf_release(&out); } @@ -655,7 +689,7 @@ static int edit_branch_description(const char *branch_name) fp = fopen(git_path(edit_description), "w"); if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) { strbuf_release(&buf); - return error(_("could not write branch description template: %s\n"), + return error(_("could not write branch description template: %s"), strerror(errno)); } strbuf_reset(&buf); @@ -678,6 +712,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int delete = 0, rename = 0, force_create = 0, list = 0; int verbose = 0, abbrev = -1, detached = 0; int reflog = 0, edit_description = 0; + int quiet = 0; enum branch_track track; int kinds = REF_LOCAL_BRANCH; struct commit_list *with_commit = NULL; @@ -686,6 +721,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_GROUP("Generic options"), OPT__VERBOSE(&verbose, "show hash and subject, give twice for upstream branch"), + OPT__QUIET(&quiet, "suppress informational messages"), OPT_SET_INT('t', "track", &track, "set up tracking mode (see git-pull(1))", BRANCH_TRACK_EXPLICIT), OPT_SET_INT( 0, "set-upstream", &track, "change upstream info", @@ -731,6 +767,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, opt_parse_merge_filter, (intptr_t) "HEAD", }, + OPT_COLUMN(0, "column", &colopts, "list branches in columns"), OPT_END(), }; @@ -753,6 +790,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } hashcpy(merge_filter_ref, head_sha1); + argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, 0); @@ -764,12 +802,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (abbrev == -1) abbrev = DEFAULT_ABBREV; + finalize_colopts(&colopts, -1); + if (verbose) { + if (explicitly_enable_column(colopts)) + die(_("--column and --verbose are incompatible")); + colopts = 0; + } if (delete) - return delete_branches(argc, argv, delete > 1, kinds); - else if (list) - return print_ref_list(kinds, detached, verbose, abbrev, - with_commit, argv); + return delete_branches(argc, argv, delete > 1, kinds, quiet); + else if (list) { + int ret = print_ref_list(kinds, detached, verbose, abbrev, + with_commit, argv); + print_columns(&output, colopts, NULL); + string_list_clear(&output, 0); + return ret; + } else if (edit_description) { const char *branch_name; struct strbuf branch_ref = STRBUF_INIT; @@ -808,7 +856,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); create_branch(head, argv[0], (argc == 2) ? argv[1] : head, - force_create, reflog, 0, track); + force_create, reflog, 0, quiet, track); } else usage_with_options(builtin_branch_usage, options); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 8ed501f220..af74e775a1 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -11,6 +11,7 @@ #include "parse-options.h" #include "diff.h" #include "userdiff.h" +#include "streaming.h" #define BATCH 1 #define BATCH_CHECK 2 @@ -90,7 +91,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) 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; @@ -127,6 +128,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) 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); @@ -149,6 +152,28 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) 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; diff --git a/builtin/checkout.c b/builtin/checkout.c index f9f7f40afb..7d922c612a 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -73,7 +73,8 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, hashcpy(ce->sha1, sha1); memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len - baselen); - ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE; + ce->ce_flags = create_ce_flags(0) | CE_UPDATE; + ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; @@ -315,8 +316,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts) init_revisions(&rev, NULL); rev.diffopt.flags = opts->flags; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; - if (diff_setup_done(&rev.diffopt) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&rev.diffopt); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); } @@ -543,6 +543,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, opts->new_branch_force ? 1 : 0, opts->new_branch_log, opts->new_branch_force ? 1 : 0, + opts->quiet, opts->track); new->name = opts->new_branch; setup_branch_path(new); @@ -604,7 +605,7 @@ static int add_pending_uninteresting_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { - add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING); + add_pending_sha1(cb_data, refname, sha1, UNINTERESTING); return 0; } @@ -914,6 +915,8 @@ static int switch_unborn_to_new_branch(struct checkout_opts *opts) int status; struct strbuf branch_ref = STRBUF_INIT; + if (!opts->new_branch) + die(_("You are on a branch yet to be born")); strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch); status = create_symref("HEAD", branch_ref.buf, "checkout -b"); strbuf_release(&branch_ref); diff --git a/builtin/clone.c b/builtin/clone.c index a4d8d25ee3..e314b0b6d2 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -38,7 +38,7 @@ static const char * const builtin_clone_usage[] = { }; static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; -static int option_local, option_no_hardlinks, option_shared, option_recursive; +static int option_local = -1, option_no_hardlinks, option_shared, option_recursive; static char *option_template, *option_depth; static char *option_origin = NULL; static char *option_branch = NULL; @@ -70,8 +70,8 @@ static struct option builtin_clone_options[] = { PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_BOOLEAN(0, "mirror", &option_mirror, "create a mirror repository (implies bare)"), - OPT_BOOLEAN('l', "local", &option_local, - "to clone from a local repository"), + OPT_BOOL('l', "local", &option_local, + "to clone from a local repository"), OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks, "don't use local hardlinks, always copy"), OPT_BOOLEAN('s', "shared", &option_shared, @@ -342,7 +342,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, if (!option_no_hardlinks) { if (!link(src->buf, dest->buf)) continue; - if (option_local) + if (option_local > 0) die_errno(_("failed to create link '%s'"), dest->buf); option_no_hardlinks = 1; } @@ -433,8 +433,11 @@ static struct ref *wanted_peer_refs(const struct ref *refs, if (!option_branch) remote_head = guess_remote_head(head, refs, 0); - else - remote_head = find_remote_branch(refs, option_branch); + else { + local_refs = NULL; + tail = &local_refs; + remote_head = copy_ref(find_remote_branch(refs, option_branch)); + } if (!remote_head && option_branch) warning(_("Could not find remote branch %s to clone."), @@ -668,7 +671,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) die(_("repository '%s' does not exist"), repo_name); else repo = repo_name; - is_local = path && !is_bundle; + is_local = option_local != 0 && path && !is_bundle; if (is_local && option_depth) warning(_("--depth is ignored in local clones; use file:// instead.")); @@ -705,7 +708,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); - if (!dest_exists && mkdir(work_tree, 0755)) + if (!dest_exists && mkdir(work_tree, 0777)) die_errno(_("could not create work tree dir '%s'."), work_tree); set_git_work_tree(work_tree); diff --git a/builtin/column.c b/builtin/column.c new file mode 100644 index 0000000000..5ea798a7ca --- /dev/null +++ b/builtin/column.c @@ -0,0 +1,59 @@ +#include "builtin.h" +#include "cache.h" +#include "strbuf.h" +#include "parse-options.h" +#include "string-list.h" +#include "column.h" + +static const char * const builtin_column_usage[] = { + "git column [options]", + NULL +}; +static unsigned int colopts; + +static int column_config(const char *var, const char *value, void *cb) +{ + return git_column_config(var, value, cb, &colopts); +} + +int cmd_column(int argc, const char **argv, const char *prefix) +{ + struct string_list list = STRING_LIST_INIT_DUP; + struct strbuf sb = STRBUF_INIT; + struct column_options copts; + const char *command = NULL, *real_command = NULL; + struct option options[] = { + OPT_STRING(0, "command", &real_command, "name", "lookup config vars"), + OPT_COLUMN(0, "mode", &colopts, "layout to use"), + OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"), + OPT_INTEGER(0, "width", &copts.width, "Maximum width"), + OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"), + OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"), + OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"), + OPT_END() + }; + + /* This one is special and must be the first one */ + if (argc > 1 && !prefixcmp(argv[1], "--command=")) { + command = argv[1] + 10; + git_config(column_config, (void *)command); + } else + git_config(column_config, NULL); + + memset(&copts, 0, sizeof(copts)); + copts.width = term_columns(); + copts.padding = 1; + argc = parse_options(argc, argv, "", options, builtin_column_usage, 0); + if (argc) + usage_with_options(builtin_column_usage, options); + if (real_command || command) { + if (!real_command || !command || strcmp(real_command, command)) + die(_("--command must be the first argument")); + } + finalize_colopts(&colopts, -1); + while (!strbuf_getline(&sb, stdin, '\n')) + string_list_append(&list, sb.buf); + + print_columns(&list, colopts, &copts); + return 0; +} diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 164b655df9..eac901a0ee 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -48,16 +48,13 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) if (argc < 2 || !strcmp(argv[1], "-h")) usage(commit_tree_usage); - if (get_sha1(argv[1], tree_sha1)) - die("Not a valid object name %s", argv[1]); - for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "-p")) { unsigned char sha1[20]; if (argc <= ++i) usage(commit_tree_usage); - if (get_sha1(argv[i], sha1)) + if (get_sha1_commit(argv[i], sha1)) die("Not a valid object name %s", argv[i]); assert_sha1_type(sha1, OBJ_COMMIT); new_parent(lookup_commit(sha1), &parents); @@ -104,7 +101,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) continue; } - if (get_sha1(arg, tree_sha1)) + if (get_sha1_tree(arg, tree_sha1)) die("Not a valid object name %s", arg); if (got_tree) die("Cannot give more than one trees"); diff --git a/builtin/commit.c b/builtin/commit.c index e2d9cbe3e3..20cef95d60 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -27,6 +27,7 @@ #include "quote.h" #include "submodule.h" #include "gpg-interface.h" +#include "column.h" static const char * const builtin_commit_usage[] = { "git commit [options] [--] <filepattern>...", @@ -139,24 +140,6 @@ static void determine_whence(struct wt_status *s) s->whence = whence; } -static const char *whence_s(void) -{ - const char *s = ""; - - switch (whence) { - case FROM_COMMIT: - break; - case FROM_MERGE: - s = _("merge"); - break; - case FROM_CHERRY_PICK: - s = _("cherry-pick"); - break; - } - - return s; -} - static void rollback_index_files(void) { switch (commit_style) { @@ -201,6 +184,9 @@ static int list_paths(struct string_list *list, const char *with_tree, int i; char *m; + if (!pattern) + return 0; + for (i = 0; pattern[i]; i++) ; m = xcalloc(1, i); @@ -362,7 +348,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, * and create commit from the_index. * We still need to refresh the index here. */ - if (!pathspec || !*pathspec) { + if (!only && (!pathspec || !*pathspec)) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { @@ -398,8 +384,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, */ commit_style = COMMIT_PARTIAL; - if (whence != FROM_COMMIT) - die(_("cannot do a partial commit during a %s."), whence_s()); + if (whence != FROM_COMMIT) { + if (whence == FROM_MERGE) + die(_("cannot do a partial commit during a merge.")); + else if (whence == FROM_CHERRY_PICK) + die(_("cannot do a partial commit during a cherry-pick.")); + } memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; @@ -539,8 +529,7 @@ static void determine_author_info(struct strbuf *author_ident) if (force_date) date = force_date; - strbuf_addstr(author_ident, fmt_ident(name, email, date, - IDENT_ERROR_ON_NO_NAME)); + strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT)); if (!split_ident_line(&author, author_ident->buf, author_ident->len)) { export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); @@ -654,7 +643,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, hook_arg1 = "message"; } else if (use_message) { buffer = strstr(use_message_buffer, "\n\n"); - if (!buffer || buffer[2] == '\0') + if (!use_editor && (!buffer || buffer[2] == '\0')) die(_("commit has empty message")); strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); hook_arg1 = "commit"; @@ -736,33 +725,36 @@ static int prepare_to_commit(const char *index_file, const char *prefix, strbuf_release(&sb); /* This checks if committer ident is explicitly given */ - strbuf_addstr(&committer_ident, git_committer_info(0)); + strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT)); if (use_editor && include_status) { char *ai_tmp, *ci_tmp; if (whence != FROM_COMMIT) status_printf_ln(s, GIT_COLOR_NORMAL, - _("\n" - "It looks like you may be committing a %s.\n" - "If this is not correct, please remove the file\n" - " %s\n" - "and try again.\n" - ""), - whence_s(), + whence == FROM_MERGE + ? _("\n" + "It looks like you may be committing a merge.\n" + "If this is not correct, please remove the file\n" + " %s\n" + "and try again.\n") + : _("\n" + "It looks like you may be committing a cherry-pick.\n" + "If this is not correct, please remove the file\n" + " %s\n" + "and try again.\n"), git_path(whence == FROM_MERGE ? "MERGE_HEAD" : "CHERRY_PICK_HEAD")); fprintf(s->fp, "\n"); - status_printf(s, GIT_COLOR_NORMAL, - _("Please enter the commit message for your changes.")); if (cleanup_mode == CLEANUP_ALL) - status_printf_more(s, GIT_COLOR_NORMAL, - _(" Lines starting\n" - "with '#' will be ignored, and an empty" + status_printf(s, GIT_COLOR_NORMAL, + _("Please enter the commit message for your changes." + " Lines starting\nwith '#' will be ignored, and an empty" " message aborts the commit.\n")); else /* CLEANUP_SPACE, that is. */ - status_printf_more(s, GIT_COLOR_NORMAL, - _(" Lines starting\n" + status_printf(s, GIT_COLOR_NORMAL, + _("Please enter the commit message for your changes." + " Lines starting\n" "with '#' will be kept; you may remove them" " yourself if you want to.\n" "An empty message aborts the commit.\n")); @@ -1017,8 +1009,12 @@ static int parse_and_validate_options(int argc, const char *argv[], /* Sanity check options */ if (amend && !current_head) die(_("You have nothing to amend.")); - if (amend && whence != FROM_COMMIT) - die(_("You are in the middle of a %s -- cannot amend."), whence_s()); + if (amend && whence != FROM_COMMIT) { + if (whence == FROM_MERGE) + die(_("You are in the middle of a merge -- cannot amend.")); + else if (whence == FROM_CHERRY_PICK) + die(_("You are in the middle of a cherry-pick -- cannot amend.")); + } if (fixup_message && squash_message) die(_("Options --squash and --fixup cannot be used together")); if (use_message) @@ -1125,6 +1121,8 @@ static int git_status_config(const char *k, const char *v, void *cb) { struct wt_status *s = cb; + if (!prefixcmp(k, "column.")) + return git_column_config(k, v, "status", &s->colopts); if (!strcmp(k, "status.submodulesummary")) { int is_bool; s->submodule_summary = git_config_bool_or_int(k, v, &is_bool); @@ -1190,6 +1188,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when", "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + OPT_COLUMN(0, "column", &s.colopts, "list untracked files in columns"), OPT_END(), }; @@ -1203,6 +1202,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_status_options, builtin_status_usage, 0); + finalize_colopts(&s.colopts, -1); if (s.null_termination && status_format == STATUS_FORMAT_LONG) status_format = STATUS_FORMAT_PORCELAIN; @@ -1439,6 +1439,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) wt_status_prepare(&s); git_config(git_commit_config, &s); determine_whence(&s); + s.colopts = 0; if (get_sha1("HEAD", sha1)) current_head = NULL; diff --git a/builtin/config.c b/builtin/config.c index 33c8820af6..8cd08da991 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -161,7 +161,7 @@ static int show_config(const char *key_, const char *value_, void *cb) static int get_value(const char *key_, const char *regex_) { int ret = -1; - char *global = NULL, *repo_config = NULL; + char *global = NULL, *xdg = NULL, *repo_config = NULL; const char *system_wide = NULL, *local; struct config_include_data inc = CONFIG_INCLUDE_INIT; config_fn_t fn; @@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_) local = given_config_file; if (!local) { - const char *home = getenv("HOME"); local = repo_config = git_pathdup("config"); - if (home) - global = xstrdup(mkpath("%s/.gitconfig", home)); if (git_config_system()) system_wide = git_etc_gitconfig(); + home_config_paths(&global, &xdg, "config"); } if (use_key_regexp) { @@ -229,6 +227,8 @@ static int get_value(const char *key_, const char *regex_) if (do_all && system_wide) git_config_from_file(fn, system_wide, data); + if (do_all && xdg) + git_config_from_file(fn, xdg, data); if (do_all && global) git_config_from_file(fn, global, data); if (do_all) @@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_) git_config_from_file(fn, local, data); if (!do_all && !seen && global) git_config_from_file(fn, global, data); + if (!do_all && !seen && xdg) + git_config_from_file(fn, xdg, data); if (!do_all && !seen && system_wide) git_config_from_file(fn, system_wide, data); @@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_) free_strings: free(repo_config); free(global); + free(xdg); return ret; } @@ -379,13 +382,25 @@ int cmd_config(int argc, const char **argv, const char *prefix) } if (use_global_config) { - char *home = getenv("HOME"); - if (home) { - char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - given_config_file = user_config; - } else { + char *user_config = NULL; + char *xdg_config = NULL; + + home_config_paths(&user_config, &xdg_config, "config"); + + if (!user_config) + /* + * It is unknown if HOME/.gitconfig exists, so + * we do not know if we should write to XDG + * location; error out even if XDG_CONFIG_HOME + * is set and points at a sane location. + */ die("$HOME not set"); - } + + if (access(user_config, R_OK) && + xdg_config && !access(xdg_config, R_OK)) + given_config_file = xdg_config; + else + given_config_file = user_config; } else if (use_system_config) given_config_file = git_etc_gitconfig(); diff --git a/builtin/credential.c b/builtin/credential.c new file mode 100644 index 0000000000..0412fa00f0 --- /dev/null +++ b/builtin/credential.c @@ -0,0 +1,31 @@ +#include "git-compat-util.h" +#include "credential.h" +#include "builtin.h" + +static const char usage_msg[] = + "git credential [fill|approve|reject]"; + +int cmd_credential(int argc, const char **argv, const char *prefix) +{ + const char *op; + struct credential c = CREDENTIAL_INIT; + + op = argv[1]; + if (!op) + usage(usage_msg); + + if (credential_read(&c, stdin) < 0) + die("unable to read credential from stdin"); + + if (!strcmp(op, "fill")) { + credential_fill(&c); + credential_write(&c, stdout); + } else if (!strcmp(op, "approve")) { + credential_approve(&c); + } else if (!strcmp(op, "reject")) { + credential_reject(&c); + } else { + usage(usage_msg); + } + return 0; +} diff --git a/builtin/diff.c b/builtin/diff.c index 9069dc41be..ecfdd1b6df 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -298,19 +298,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix) argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; - if (diff_setup_done(&rev.diffopt) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&rev.diffopt); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); - /* - * If the user asked for our exit code then don't start a - * pager or we would end up reporting its exit code instead. - */ - if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) && - check_pager_config("diff") != 0) - setup_pager(); + setup_diff_pager(&rev.diffopt); /* * Do we have --cached and not have a pending object, then @@ -421,3 +414,19 @@ int cmd_diff(int argc, const char **argv, const char *prefix) refresh_index_quietly(); return result; } + +void setup_diff_pager(struct diff_options *opt) +{ + /* + * If the user asked for our exit code, then either they want --quiet + * or --exit-code. We should definitely not bother with a pager in the + * former case, as we will generate no output. Since we still properly + * report our exit code even when a pager is run, we _could_ run a + * pager with --exit-code. But since we have not done so historically, + * and because it is easy to find people oneline advising "git diff + * --exit-code" in hooks and other scripts, we do not do so. + */ + if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + check_pager_config("diff") != 0) + setup_pager(); +} diff --git a/builtin/fast-export.c b/builtin/fast-export.c index ef7c012094..9ab6db3fb0 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -185,6 +185,8 @@ static void print_path(const char *path) int need_quote = quote_c_style(path, NULL, NULL, 0); if (need_quote) quote_c_style(path, NULL, stdout, 0); + else if (strchr(path, ' ')) + printf("\"%s\"", path); else printf("%s", path); } diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 10db15b184..149db88726 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -528,6 +528,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match) struct ref **newtail = &newlist; struct ref *ref, *next; struct ref *fastarray[32]; + int match_pos; if (nr_match && !args.fetch_all) { if (ARRAY_SIZE(fastarray) < nr_match) @@ -540,6 +541,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match) else return_refs = NULL; + match_pos = 0; for (ref = *refs; ref; ref = next) { next = ref->next; if (!memcmp(ref->name, "refs/", 5) && @@ -553,15 +555,20 @@ static void filter_refs(struct ref **refs, int nr_match, char **match) continue; } else { - int i; - for (i = 0; i < nr_match; i++) { - if (!strcmp(ref->name, match[i])) { - match[i][0] = '\0'; - return_refs[i] = ref; + int cmp = -1; + while (match_pos < nr_match) { + cmp = strcmp(ref->name, match[match_pos]); + if (cmp < 0) /* definitely do not have it */ + break; + else if (cmp == 0) { /* definitely have it */ + match[match_pos][0] = '\0'; + return_refs[match_pos] = ref; break; } + else /* might have it; keep looking */ + match_pos++; } - if (i < nr_match) + if (!cmp) continue; /* we will link it later */ } free(ref); @@ -777,6 +784,8 @@ static struct ref *do_fetch_pack(int fd[2], struct ref *ref = copy_ref_list(orig_ref); unsigned char sha1[20]; + sort_ref_list(&ref, ref_compare_name); + if (is_repository_shallow() && !server_supports("shallow")) die("Server does not support shallow clients"); if (server_supports("multi_ack_detailed")) { @@ -834,21 +843,12 @@ static int remove_duplicates(int nr_heads, char **heads) { int src, dst; - for (src = dst = 0; src < nr_heads; src++) { - /* If heads[src] is different from any of - * heads[0..dst], push it in. - */ - int i; - for (i = 0; i < dst; i++) { - if (!strcmp(heads[i], heads[src])) - break; - } - if (i < dst) - continue; - if (src != dst) - heads[dst] = heads[src]; - dst++; - } + if (!nr_heads) + return 0; + + for (src = dst = 1; src < nr_heads; src++) + if (strcmp(heads[src], heads[dst-1])) + heads[dst++] = heads[src]; return dst; } @@ -899,9 +899,11 @@ static void fetch_pack_setup(void) int cmd_fetch_pack(int argc, const char **argv, const char *prefix) { - int i, ret, nr_heads; + int i, ret; struct ref *ref = NULL; - char *dest = NULL, **heads; + const char *dest = NULL; + int alloc_heads = 0, nr_heads = 0; + char **heads = NULL; int fd[2]; char *pack_lockfile = NULL; char **pack_lockfile_ptr = NULL; @@ -909,84 +911,79 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("fetch-pack"); - nr_heads = 0; - heads = NULL; - for (i = 1; i < argc; i++) { + for (i = 1; i < argc && *argv[i] == '-'; i++) { const char *arg = argv[i]; - if (*arg == '-') { - if (!prefixcmp(arg, "--upload-pack=")) { - args.uploadpack = arg + 14; - continue; - } - if (!prefixcmp(arg, "--exec=")) { - args.uploadpack = arg + 7; - continue; - } - if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { - args.quiet = 1; - continue; - } - if (!strcmp("--keep", arg) || !strcmp("-k", arg)) { - args.lock_pack = args.keep_pack; - args.keep_pack = 1; - continue; - } - if (!strcmp("--thin", arg)) { - args.use_thin_pack = 1; - continue; - } - if (!strcmp("--include-tag", arg)) { - args.include_tag = 1; - continue; - } - if (!strcmp("--all", arg)) { - args.fetch_all = 1; - continue; - } - if (!strcmp("--stdin", arg)) { - args.stdin_refs = 1; - continue; - } - if (!strcmp("-v", arg)) { - args.verbose = 1; - continue; - } - if (!prefixcmp(arg, "--depth=")) { - args.depth = strtol(arg + 8, NULL, 0); - continue; - } - if (!strcmp("--no-progress", arg)) { - args.no_progress = 1; - continue; - } - if (!strcmp("--stateless-rpc", arg)) { - args.stateless_rpc = 1; - continue; - } - if (!strcmp("--lock-pack", arg)) { - args.lock_pack = 1; - pack_lockfile_ptr = &pack_lockfile; - continue; - } - usage(fetch_pack_usage); + if (!prefixcmp(arg, "--upload-pack=")) { + args.uploadpack = arg + 14; + continue; } - dest = (char *)arg; - heads = (char **)(argv + i + 1); - nr_heads = argc - i - 1; - break; + if (!prefixcmp(arg, "--exec=")) { + args.uploadpack = arg + 7; + continue; + } + if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { + args.quiet = 1; + continue; + } + if (!strcmp("--keep", arg) || !strcmp("-k", arg)) { + args.lock_pack = args.keep_pack; + args.keep_pack = 1; + continue; + } + if (!strcmp("--thin", arg)) { + args.use_thin_pack = 1; + continue; + } + if (!strcmp("--include-tag", arg)) { + args.include_tag = 1; + continue; + } + if (!strcmp("--all", arg)) { + args.fetch_all = 1; + continue; + } + if (!strcmp("--stdin", arg)) { + args.stdin_refs = 1; + continue; + } + if (!strcmp("-v", arg)) { + args.verbose = 1; + continue; + } + if (!prefixcmp(arg, "--depth=")) { + args.depth = strtol(arg + 8, NULL, 0); + continue; + } + if (!strcmp("--no-progress", arg)) { + args.no_progress = 1; + continue; + } + if (!strcmp("--stateless-rpc", arg)) { + args.stateless_rpc = 1; + continue; + } + if (!strcmp("--lock-pack", arg)) { + args.lock_pack = 1; + pack_lockfile_ptr = &pack_lockfile; + continue; + } + usage(fetch_pack_usage); } - if (!dest) + + if (i < argc) + dest = argv[i++]; + else usage(fetch_pack_usage); + /* + * Copy refs from cmdline to growable list, then append any + * refs from the standard input: + */ + ALLOC_GROW(heads, argc - i, alloc_heads); + for (; i < argc; i++) + heads[nr_heads++] = xstrdup(argv[i]); if (args.stdin_refs) { - /* - * Copy refs from cmdline to new growable list, then - * append the refs from the standard input. - */ - int alloc_heads = nr_heads; - int size = nr_heads * sizeof(*heads); - heads = memcpy(xmalloc(size), heads, size); if (args.stateless_rpc) { /* in stateless RPC mode we use pkt-line to read * from stdin, until we get a flush packet @@ -1018,7 +1015,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) fd[0] = 0; fd[1] = 1; } else { - conn = git_connect(fd, (char *)dest, args.uploadpack, + conn = git_connect(fd, dest, args.uploadpack, args.verbose ? CONNECT_VERBOSE : 0); } @@ -1057,6 +1054,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) return ret; } +static int compare_heads(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + struct ref *fetch_pack(struct fetch_pack_args *my_args, int fd[], struct child_process *conn, const struct ref *ref, @@ -1076,8 +1078,11 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, st.st_mtime = 0; } - if (heads && nr_heads) + if (heads && nr_heads) { + qsort(heads, nr_heads, sizeof(*heads), compare_heads); nr_heads = remove_duplicates(nr_heads, heads); + } + if (!ref) { packet_flush(fd[1]); die("no matching remote head"); diff --git a/builtin/fetch.c b/builtin/fetch.c index 1c8cb62445..bb9a0743ff 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -546,8 +546,8 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) int result = 0; struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map); const char *dangling_msg = dry_run - ? _(" (%s will become dangling)\n") - : _(" (%s has become dangling)\n"); + ? _(" (%s will become dangling)") + : _(" (%s has become dangling)"); for (ref = stale_refs; ref; ref = ref->next) { if (!dry_run) diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index fb1ebcbe68..2c4d435da1 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -27,6 +27,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb) merge_log_config = DEFAULT_MERGE_LOG_LEN; } else if (!strcmp(key, "merge.branchdesc")) { use_branch_desc = git_config_bool(key, value); + } else { + return git_default_config(key, value, cb); } return 0; } @@ -223,6 +225,101 @@ static void add_branch_desc(struct strbuf *out, const char *name) strbuf_release(&desc); } +#define util_as_integral(elem) ((intptr_t)((elem)->util)) + +static void record_person(int which, struct string_list *people, + struct commit *commit) +{ + char *name_buf, *name, *name_end; + struct string_list_item *elem; + const char *field = (which == 'a') ? "\nauthor " : "\ncommitter "; + + name = strstr(commit->buffer, field); + if (!name) + return; + name += strlen(field); + name_end = strchrnul(name, '<'); + if (*name_end) + name_end--; + while (isspace(*name_end) && name <= name_end) + name_end--; + if (name_end < name) + return; + name_buf = xmemdupz(name, name_end - name + 1); + + elem = string_list_lookup(people, name_buf); + if (!elem) { + elem = string_list_insert(people, name_buf); + elem->util = (void *)0; + } + elem->util = (void*)(util_as_integral(elem) + 1); + free(name_buf); +} + +static int cmp_string_list_util_as_integral(const void *a_, const void *b_) +{ + const struct string_list_item *a = a_, *b = b_; + return util_as_integral(b) - util_as_integral(a); +} + +static void add_people_count(struct strbuf *out, struct string_list *people) +{ + if (people->nr == 1) + strbuf_addf(out, "%s", people->items[0].string); + else if (people->nr == 2) + strbuf_addf(out, "%s (%d) and %s (%d)", + people->items[0].string, + (int)util_as_integral(&people->items[0]), + people->items[1].string, + (int)util_as_integral(&people->items[1])); + else if (people->nr) + strbuf_addf(out, "%s (%d) and others", + people->items[0].string, + (int)util_as_integral(&people->items[0])); +} + +static void credit_people(struct strbuf *out, + struct string_list *them, + int kind) +{ + const char *label; + const char *me; + + if (kind == 'a') { + label = "\n# By "; + me = git_author_info(IDENT_NO_DATE); + } else { + label = "\n# Via "; + me = git_committer_info(IDENT_NO_DATE); + } + + if (!them->nr || + (them->nr == 1 && + me && + (me = skip_prefix(me, them->items->string)) != NULL && + skip_prefix(me, " <"))) + return; + strbuf_addstr(out, label); + add_people_count(out, them); +} + +static void add_people_info(struct strbuf *out, + struct string_list *authors, + struct string_list *committers) +{ + if (authors->nr) + qsort(authors->items, + authors->nr, sizeof(authors->items[0]), + cmp_string_list_util_as_integral); + if (committers->nr) + qsort(committers->items, + committers->nr, sizeof(committers->items[0]), + cmp_string_list_util_as_integral); + + credit_people(out, authors, 'a'); + credit_people(out, committers, 'c'); +} + static void shortlog(const char *name, struct origin_data *origin_data, struct commit *head, @@ -233,6 +330,8 @@ static void shortlog(const char *name, struct commit *commit; struct object *branch; struct string_list subjects = STRING_LIST_INIT_DUP; + struct string_list authors = STRING_LIST_INIT_DUP; + struct string_list committers = STRING_LIST_INIT_DUP; int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; struct strbuf sb = STRBUF_INIT; const unsigned char *sha1 = origin_data->sha1; @@ -242,7 +341,6 @@ static void shortlog(const char *name, return; setup_revisions(0, NULL, rev, NULL); - rev->ignore_merges = 1; add_pending_object(rev, branch, name); add_pending_object(rev, &head->object, "^HEAD"); head->object.flags |= UNINTERESTING; @@ -251,10 +349,15 @@ static void shortlog(const char *name, while ((commit = get_revision(rev)) != NULL) { struct pretty_print_context ctx = {0}; - /* ignore merges */ - if (commit->parents && commit->parents->next) + if (commit->parents && commit->parents->next) { + /* do not list a merge but count committer */ + record_person('c', &committers, commit); continue; - + } + if (!count) + /* the 'tip' committer */ + record_person('c', &committers, commit); + record_person('a', &authors, commit); count++; if (subjects.nr > limit) continue; @@ -269,6 +372,7 @@ static void shortlog(const char *name, string_list_append(&subjects, strbuf_detach(&sb, NULL)); } + add_people_info(out, &authors, &committers); if (count > limit) strbuf_addf(out, "\n* %s: (%d commits)\n", name, count); else @@ -289,6 +393,8 @@ static void shortlog(const char *name, rev->commits = NULL; rev->pending.nr = 0; + string_list_clear(&authors, 0); + string_list_clear(&committers, 0); string_list_clear(&subjects, 0); } diff --git a/builtin/fsck.c b/builtin/fsck.c index 67eb553c7d..a710227a64 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -12,6 +12,7 @@ #include "parse-options.h" #include "dir.h" #include "progress.h" +#include "streaming.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -238,13 +239,8 @@ static void check_unreachable_object(struct object *obj) if (!(f = fopen(filename, "w"))) die_errno("Could not open '%s'", filename); if (obj->type == OBJ_BLOB) { - enum object_type type; - unsigned long size; - char *buf = read_sha1_file(obj->sha1, - &type, &size); - if (buf && fwrite(buf, 1, size, f) != size) + if (stream_blob_to_fd(fileno(f), obj->sha1, NULL, 1)) die_errno("Could not write '%s'", filename); - free(buf); } else fprintf(f, "%s\n", sha1_to_hex(obj->sha1)); if (fclose(f)) diff --git a/builtin/grep.c b/builtin/grep.c index fe1726f5ef..29adb0ac93 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -928,7 +928,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!seen_dashdash) { int j; for (j = i; j < argc; j++) - verify_filename(prefix, argv[j]); + verify_filename(prefix, argv[j], j == i); } paths = get_pathspec(prefix, argv + i); diff --git a/builtin/help.c b/builtin/help.c index 61ff79839b..efea4f55e1 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -9,8 +9,13 @@ #include "common-cmds.h" #include "parse-options.h" #include "run-command.h" +#include "column.h" #include "help.h" +#ifndef DEFAULT_HELP_FORMAT +#define DEFAULT_HELP_FORMAT "man" +#endif + static struct man_viewer_list { struct man_viewer_list *next; char name[FLEX_ARRAY]; @@ -29,7 +34,10 @@ enum help_format { HELP_FORMAT_WEB }; +static const char *html_path; + static int show_all = 0; +static unsigned int colopts; static enum help_format help_format = HELP_FORMAT_NONE; static struct option builtin_help_options[] = { OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), @@ -54,7 +62,7 @@ static enum help_format parse_help_format(const char *format) return HELP_FORMAT_INFO; if (!strcmp(format, "web") || !strcmp(format, "html")) return HELP_FORMAT_WEB; - die("unrecognized help format '%s'", format); + die(_("unrecognized help format '%s'"), format); } static const char *get_man_viewer_info(const char *name) @@ -82,7 +90,7 @@ static int check_emacsclient_version(void) ec_process.err = -1; ec_process.stdout_to_stderr = 1; if (start_command(&ec_process)) - return error("Failed to start emacsclient."); + return error(_("Failed to start emacsclient.")); strbuf_read(&buffer, ec_process.err, 20); close(ec_process.err); @@ -95,7 +103,7 @@ static int check_emacsclient_version(void) if (prefixcmp(buffer.buf, "emacsclient")) { strbuf_release(&buffer); - return error("Failed to parse emacsclient version."); + return error(_("Failed to parse emacsclient version.")); } strbuf_remove(&buffer, 0, strlen("emacsclient")); @@ -103,7 +111,7 @@ static int check_emacsclient_version(void) if (version < 22) { strbuf_release(&buffer); - return error("emacsclient version '%d' too old (< 22).", + return error(_("emacsclient version '%d' too old (< 22)."), version); } @@ -121,7 +129,7 @@ static void exec_woman_emacs(const char *path, const char *page) path = "emacsclient"; strbuf_addf(&man_page, "(woman \"%s\")", page); execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL); - warning("failed to exec '%s': %s", path, strerror(errno)); + warning(_("failed to exec '%s': %s"), path, strerror(errno)); } } @@ -149,7 +157,7 @@ static void exec_man_konqueror(const char *path, const char *page) path = "kfmclient"; strbuf_addf(&man_page, "man:%s(1)", page); execlp(path, filename, "newTab", man_page.buf, (char *)NULL); - warning("failed to exec '%s': %s", path, strerror(errno)); + warning(_("failed to exec '%s': %s"), path, strerror(errno)); } } @@ -158,7 +166,7 @@ static void exec_man_man(const char *path, const char *page) if (!path) path = "man"; execlp(path, "man", page, (char *)NULL); - warning("failed to exec '%s': %s", path, strerror(errno)); + warning(_("failed to exec '%s': %s"), path, strerror(errno)); } static void exec_man_cmd(const char *cmd, const char *page) @@ -166,7 +174,7 @@ static void exec_man_cmd(const char *cmd, const char *page) struct strbuf shell_cmd = STRBUF_INIT; strbuf_addf(&shell_cmd, "%s %s", cmd, page); execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL); - warning("failed to exec '%s': %s", cmd, strerror(errno)); + warning(_("failed to exec '%s': %s"), cmd, strerror(errno)); } static void add_man_viewer(const char *name) @@ -206,8 +214,8 @@ static int add_man_viewer_path(const char *name, if (supported_man_viewer(name, len)) do_add_man_viewer_info(name, len, value); else - warning("'%s': path for unsupported man viewer.\n" - "Please consider using 'man.<tool>.cmd' instead.", + warning(_("'%s': path for unsupported man viewer.\n" + "Please consider using 'man.<tool>.cmd' instead."), name); return 0; @@ -218,8 +226,8 @@ static int add_man_viewer_cmd(const char *name, const char *value) { if (supported_man_viewer(name, len)) - warning("'%s': cmd for supported man viewer.\n" - "Please consider using 'man.<tool>.path' instead.", + warning(_("'%s': cmd for supported man viewer.\n" + "Please consider using 'man.<tool>.path' instead."), name); else do_add_man_viewer_info(name, len, value); @@ -251,12 +259,20 @@ static int add_man_viewer_info(const char *var, const char *value) static int git_help_config(const char *var, const char *value, void *cb) { + if (!prefixcmp(var, "column.")) + return git_column_config(var, value, "help", &colopts); if (!strcmp(var, "help.format")) { if (!value) return config_error_nonbool(var); help_format = parse_help_format(value); return 0; } + if (!strcmp(var, "help.htmlpath")) { + if (!value) + return config_error_nonbool(var); + html_path = xstrdup(value); + return 0; + } if (!strcmp(var, "man.viewer")) { if (!value) return config_error_nonbool(var); @@ -280,11 +296,11 @@ void list_common_cmds_help(void) longest = strlen(common_cmds[i].name); } - puts("The most commonly used git commands are:"); + puts(_("The most commonly used git commands are:")); for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { printf(" %s ", common_cmds[i].name); mput_char(' ', longest - strlen(common_cmds[i].name)); - puts(common_cmds[i].help); + puts(_(common_cmds[i].help)); } } @@ -348,7 +364,7 @@ static void exec_viewer(const char *name, const char *page) else if (info) exec_man_cmd(info, page); else - warning("'%s': unknown man viewer.", name); + warning(_("'%s': unknown man viewer."), name); } static void show_man_page(const char *git_cmd) @@ -365,7 +381,7 @@ static void show_man_page(const char *git_cmd) if (fallback) exec_viewer(fallback, page); exec_viewer("man", page); - die("no man viewer handled the request"); + die(_("no man viewer handled the request")); } static void show_info_page(const char *git_cmd) @@ -373,18 +389,21 @@ static void show_info_page(const char *git_cmd) const char *page = cmd_to_page(git_cmd); setenv("INFOPATH", system_path(GIT_INFO_PATH), 1); execlp("info", "info", "gitman", page, (char *)NULL); - die("no info viewer handled the request"); + die(_("no info viewer handled the request")); } static void get_html_page_path(struct strbuf *page_path, const char *page) { struct stat st; - const char *html_path = system_path(GIT_HTML_PATH); + if (!html_path) + html_path = system_path(GIT_HTML_PATH); /* Check that we have a git documentation directory. */ - if (stat(mkpath("%s/git.html", html_path), &st) - || !S_ISREG(st.st_mode)) - die("'%s': not a documentation directory.", html_path); + if (!strstr(html_path, "://")) { + if (stat(mkpath("%s/git.html", html_path), &st) + || !S_ISREG(st.st_mode)) + die("'%s': not a documentation directory.", html_path); + } strbuf_init(page_path, 0); strbuf_addf(page_path, "%s/%s.html", html_path, page); @@ -424,16 +443,17 @@ int cmd_help(int argc, const char **argv, const char *prefix) parsed_help_format = help_format; if (show_all) { - printf("usage: %s\n\n", git_usage_string); - list_commands("git commands", &main_cmds, &other_cmds); - printf("%s\n", git_more_info_string); + git_config(git_help_config, NULL); + printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); + list_commands(colopts, &main_cmds, &other_cmds); + printf("%s\n", _(git_more_info_string)); return 0; } if (!argv[0]) { - printf("usage: %s\n\n", git_usage_string); + printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); list_common_cmds_help(); - printf("\n%s\n", git_more_info_string); + printf("\n%s\n", _(git_more_info_string)); return 0; } @@ -442,10 +462,12 @@ int cmd_help(int argc, const char **argv, const char *prefix) if (parsed_help_format != HELP_FORMAT_NONE) help_format = parsed_help_format; + if (help_format == HELP_FORMAT_NONE) + help_format = parse_help_format(DEFAULT_HELP_FORMAT); alias = alias_lookup(argv[0]); if (alias && !is_git_command(argv[0])) { - printf("`git %s' is aliased to `%s'\n", argv[0], alias); + printf_ln(_("`git %s' is aliased to `%s'"), argv[0], alias); return 0; } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index dd1c5c961d..953dd3004e 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -9,6 +9,8 @@ #include "progress.h" #include "fsck.h" #include "exec_cmd.h" +#include "streaming.h" +#include "thread-utils.h" static const char index_pack_usage[] = "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; @@ -38,6 +40,19 @@ struct base_data { int ofs_first, ofs_last; }; +#if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD) +/* pread() emulation is not thread-safe. Disable threading. */ +#define NO_PTHREADS +#endif + +struct thread_local { +#ifndef NO_PTHREADS + pthread_t thread; +#endif + struct base_data *base_cache; + size_t base_cache_used; +}; + /* * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want * to memcmp() only the first 20 bytes. @@ -54,11 +69,11 @@ struct delta_entry { static struct object_entry *objects; static struct delta_entry *deltas; -static struct base_data *base_cache; -static size_t base_cache_used; +static struct thread_local nothread_data; static int nr_objects; static int nr_deltas; static int nr_resolved_deltas; +static int nr_threads; static int from_stdin; static int strict; @@ -75,13 +90,84 @@ static git_SHA_CTX input_ctx; static uint32_t input_crc32; static int input_fd, output_fd, pack_fd; +#ifndef NO_PTHREADS + +static struct thread_local *thread_data; +static int nr_dispatched; +static int threads_active; + +static pthread_mutex_t read_mutex; +#define read_lock() lock_mutex(&read_mutex) +#define read_unlock() unlock_mutex(&read_mutex) + +static pthread_mutex_t counter_mutex; +#define counter_lock() lock_mutex(&counter_mutex) +#define counter_unlock() unlock_mutex(&counter_mutex) + +static pthread_mutex_t work_mutex; +#define work_lock() lock_mutex(&work_mutex) +#define work_unlock() unlock_mutex(&work_mutex) + +static pthread_key_t key; + +static inline void lock_mutex(pthread_mutex_t *mutex) +{ + if (threads_active) + pthread_mutex_lock(mutex); +} + +static inline void unlock_mutex(pthread_mutex_t *mutex) +{ + if (threads_active) + pthread_mutex_unlock(mutex); +} + +/* + * Mutex and conditional variable can't be statically-initialized on Windows. + */ +static void init_thread(void) +{ + init_recursive_mutex(&read_mutex); + pthread_mutex_init(&counter_mutex, NULL); + pthread_mutex_init(&work_mutex, NULL); + pthread_key_create(&key, NULL); + thread_data = xcalloc(nr_threads, sizeof(*thread_data)); + threads_active = 1; +} + +static void cleanup_thread(void) +{ + if (!threads_active) + return; + threads_active = 0; + pthread_mutex_destroy(&read_mutex); + pthread_mutex_destroy(&counter_mutex); + pthread_mutex_destroy(&work_mutex); + pthread_key_delete(key); + free(thread_data); +} + +#else + +#define read_lock() +#define read_unlock() + +#define counter_lock() +#define counter_unlock() + +#define work_lock() +#define work_unlock() + +#endif + + static int mark_link(struct object *obj, int type, void *data) { if (!obj) return -1; if (type != OBJ_ANY && obj->type != type) - die("object type mismatch at %s", sha1_to_hex(obj->sha1)); + die(_("object type mismatch at %s"), sha1_to_hex(obj->sha1)); obj->flags |= FLAG_LINK; return 0; @@ -101,7 +187,7 @@ static void check_object(struct object *obj) unsigned long size; int type = sha1_object_info(obj->sha1, &size); if (type != obj->type || type <= 0) - die("object of unexpected type"); + die(_("object of unexpected type")); obj->flags |= FLAG_CHECKED; return; } @@ -138,15 +224,18 @@ static void *fill(int min) if (min <= input_len) return input_buffer + input_offset; if (min > sizeof(input_buffer)) - die("cannot fill %d bytes", min); + die(Q_("cannot fill %d byte", + "cannot fill %d bytes", + min), + min); flush(); do { ssize_t ret = xread(input_fd, input_buffer + input_len, sizeof(input_buffer) - input_len); if (ret <= 0) { if (!ret) - die("early EOF"); - die_errno("read error on input"); + die(_("early EOF")); + die_errno(_("read error on input")); } input_len += ret; if (from_stdin) @@ -158,14 +247,14 @@ static void *fill(int min) static void use(int bytes) { if (bytes > input_len) - die("used more bytes than were available"); + die(_("used more bytes than were available")); input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes); input_len -= bytes; input_offset += bytes; /* make sure off_t is sufficiently large not to wrap */ if (signed_add_overflows(consumed_bytes, bytes)) - die("pack too large for current definition of off_t"); + die(_("pack too large for current definition of off_t")); consumed_bytes += bytes; } @@ -181,12 +270,12 @@ static const char *open_pack_file(const char *pack_name) } else output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600); if (output_fd < 0) - die_errno("unable to create '%s'", pack_name); + die_errno(_("unable to create '%s'"), pack_name); pack_fd = output_fd; } else { input_fd = open(pack_name, O_RDONLY); if (input_fd < 0) - die_errno("cannot open packfile '%s'", pack_name); + die_errno(_("cannot open packfile '%s'"), pack_name); output_fd = -1; pack_fd = input_fd; } @@ -200,7 +289,7 @@ static void parse_pack_header(void) /* Header consistency check */ if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) - die("pack signature mismatch"); + die(_("pack signature mismatch")); if (!pack_version_ok(hdr->hdr_version)) die("pack version %"PRIu32" unsupported", ntohl(hdr->hdr_version)); @@ -220,9 +309,28 @@ static NORETURN void bad_object(unsigned long offset, const char *format, ...) va_start(params, format); vsnprintf(buf, sizeof(buf), format, params); va_end(params); - die("pack has bad object at offset %lu: %s", offset, buf); + die(_("pack has bad object at offset %lu: %s"), offset, buf); +} + +static inline struct thread_local *get_thread_data(void) +{ +#ifndef NO_PTHREADS + if (threads_active) + return pthread_getspecific(key); + assert(!threads_active && + "This should only be reached when all threads are gone"); +#endif + return ¬hread_data; } +#ifndef NO_PTHREADS +static void set_thread_data(struct thread_local *data) +{ + if (threads_active) + pthread_setspecific(key, data); +} +#endif + static struct base_data *alloc_base_data(void) { struct base_data *base = xmalloc(sizeof(struct base_data)); @@ -237,15 +345,16 @@ static void free_base_data(struct base_data *c) if (c->data) { free(c->data); c->data = NULL; - base_cache_used -= c->size; + get_thread_data()->base_cache_used -= c->size; } } static void prune_base_data(struct base_data *retain) { struct base_data *b; - for (b = base_cache; - base_cache_used > delta_base_cache_limit && b; + struct thread_local *data = get_thread_data(); + for (b = data->base_cache; + data->base_cache_used > delta_base_cache_limit && b; b = b->child) { if (b->data && b != retain) free_base_data(b); @@ -257,12 +366,12 @@ static void link_base_data(struct base_data *base, struct base_data *c) if (base) base->child = c; else - base_cache = c; + get_thread_data()->base_cache = c; c->base = base; c->child = NULL; if (c->data) - base_cache_used += c->size; + get_thread_data()->base_cache_used += c->size; prune_base_data(c); } @@ -272,34 +381,66 @@ static void unlink_base_data(struct base_data *c) if (base) base->child = NULL; else - base_cache = NULL; + get_thread_data()->base_cache = NULL; free_base_data(c); } -static void *unpack_entry_data(unsigned long offset, unsigned long size) +static int is_delta_type(enum object_type type) +{ + return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA); +} + +static void *unpack_entry_data(unsigned long offset, unsigned long size, + enum object_type type, unsigned char *sha1) { + static char fixed_buf[8192]; int status; git_zstream stream; - void *buf = xmalloc(size); + void *buf; + git_SHA_CTX c; + char hdr[32]; + int hdrlen; + + if (!is_delta_type(type)) { + hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1; + git_SHA1_Init(&c); + git_SHA1_Update(&c, hdr, hdrlen); + } else + sha1 = NULL; + if (type == OBJ_BLOB && size > big_file_threshold) + buf = fixed_buf; + else + buf = xmalloc(size); memset(&stream, 0, sizeof(stream)); git_inflate_init(&stream); stream.next_out = buf; - stream.avail_out = size; + stream.avail_out = buf == fixed_buf ? sizeof(fixed_buf) : size; do { + unsigned char *last_out = stream.next_out; stream.next_in = fill(1); stream.avail_in = input_len; status = git_inflate(&stream, 0); use(input_len - stream.avail_in); + if (sha1) + git_SHA1_Update(&c, last_out, stream.next_out - last_out); + if (buf == fixed_buf) { + stream.next_out = buf; + stream.avail_out = sizeof(fixed_buf); + } } while (status == Z_OK); if (stream.total_out != size || status != Z_STREAM_END) - bad_object(offset, "inflate returned %d", status); + bad_object(offset, _("inflate returned %d"), status); git_inflate_end(&stream); - return buf; + if (sha1) + git_SHA1_Final(sha1, &c); + return buf == fixed_buf ? NULL : buf; } -static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base) +static void *unpack_raw_entry(struct object_entry *obj, + union delta_base *delta_base, + unsigned char *sha1) { unsigned char *p; unsigned long size, c; @@ -339,7 +480,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ while (c & 128) { base_offset += 1; if (!base_offset || MSB(base_offset, 7)) - bad_object(obj->idx.offset, "offset value overflow for delta base object"); + bad_object(obj->idx.offset, _("offset value overflow for delta base object")); p = fill(1); c = *p; use(1); @@ -347,7 +488,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ } delta_base->offset = obj->idx.offset - base_offset; if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset) - bad_object(obj->idx.offset, "delta base offset is out of bound"); + bad_object(obj->idx.offset, _("delta base offset is out of bound")); break; case OBJ_COMMIT: case OBJ_TREE: @@ -355,16 +496,18 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ case OBJ_TAG: break; default: - bad_object(obj->idx.offset, "unknown object type %d", obj->type); + bad_object(obj->idx.offset, _("unknown object type %d"), obj->type); } obj->hdr_size = consumed_bytes - obj->idx.offset; - data = unpack_entry_data(obj->idx.offset, obj->size); + data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, sha1); obj->idx.crc32 = input_crc32; return data; } -static void *get_data_from_pack(struct object_entry *obj) +static void *unpack_data(struct object_entry *obj, + int (*consume)(const unsigned char *, unsigned long, void *), + void *cb_data) { off_t from = obj[0].idx.offset + obj[0].hdr_size; unsigned long len = obj[1].idx.offset - from; @@ -372,37 +515,62 @@ static void *get_data_from_pack(struct object_entry *obj) git_zstream stream; int status; - data = xmalloc(obj->size); + data = xmalloc(consume ? 64*1024 : obj->size); inbuf = xmalloc((len < 64*1024) ? len : 64*1024); memset(&stream, 0, sizeof(stream)); git_inflate_init(&stream); stream.next_out = data; - stream.avail_out = obj->size; + stream.avail_out = consume ? 64*1024 : obj->size; do { ssize_t n = (len < 64*1024) ? len : 64*1024; n = pread(pack_fd, inbuf, n, from); if (n < 0) - die_errno("cannot pread pack file"); + die_errno(_("cannot pread pack file")); if (!n) - die("premature end of pack file, %lu bytes missing", len); + die(Q_("premature end of pack file, %lu byte missing", + "premature end of pack file, %lu bytes missing", + len), + len); from += n; len -= n; stream.next_in = inbuf; stream.avail_in = n; - status = git_inflate(&stream, 0); + if (!consume) + status = git_inflate(&stream, 0); + else { + do { + status = git_inflate(&stream, 0); + if (consume(data, stream.next_out - data, cb_data)) { + free(inbuf); + free(data); + return NULL; + } + stream.next_out = data; + stream.avail_out = 64*1024; + } while (status == Z_OK && stream.avail_in); + } } while (len && status == Z_OK && !stream.avail_in); /* This has been inflated OK when first encountered, so... */ if (status != Z_STREAM_END || stream.total_out != obj->size) - die("serious inflate inconsistency"); + die(_("serious inflate inconsistency")); git_inflate_end(&stream); free(inbuf); + if (consume) { + free(data); + data = NULL; + } return data; } +static void *get_data_from_pack(struct object_entry *obj) +{ + return unpack_data(obj, NULL, NULL); +} + static int compare_delta_bases(const union delta_base *base1, const union delta_base *base2, enum object_type type1, @@ -457,45 +625,130 @@ static void find_delta_children(const union delta_base *base, *last_index = last; } -static void sha1_object(const void *data, unsigned long size, - enum object_type type, unsigned char *sha1) +struct compare_data { + struct object_entry *entry; + struct git_istream *st; + unsigned char *buf; + unsigned long buf_size; +}; + +static int compare_objects(const unsigned char *buf, unsigned long size, + void *cb_data) +{ + struct compare_data *data = cb_data; + + if (data->buf_size < size) { + free(data->buf); + data->buf = xmalloc(size); + data->buf_size = size; + } + + while (size) { + ssize_t len = read_istream(data->st, data->buf, size); + if (len == 0) + die(_("SHA1 COLLISION FOUND WITH %s !"), + sha1_to_hex(data->entry->idx.sha1)); + if (len < 0) + die(_("unable to read %s"), + sha1_to_hex(data->entry->idx.sha1)); + if (memcmp(buf, data->buf, len)) + die(_("SHA1 COLLISION FOUND WITH %s !"), + sha1_to_hex(data->entry->idx.sha1)); + size -= len; + buf += len; + } + return 0; +} + +static int check_collison(struct object_entry *entry) +{ + struct compare_data data; + enum object_type type; + unsigned long size; + + if (entry->size <= big_file_threshold || entry->type != OBJ_BLOB) + return -1; + + memset(&data, 0, sizeof(data)); + data.entry = entry; + data.st = open_istream(entry->idx.sha1, &type, &size, NULL); + if (!data.st) + return -1; + if (size != entry->size || type != entry->type) + die(_("SHA1 COLLISION FOUND WITH %s !"), + sha1_to_hex(entry->idx.sha1)); + unpack_data(entry, compare_objects, &data); + close_istream(data.st); + free(data.buf); + return 0; +} + +static void sha1_object(const void *data, struct object_entry *obj_entry, + unsigned long size, enum object_type type, + const unsigned char *sha1) { - hash_sha1_file(data, size, typename(type), sha1); - if (has_sha1_file(sha1)) { + void *new_data = NULL; + int collision_test_needed; + + assert(data || obj_entry); + + read_lock(); + collision_test_needed = has_sha1_file(sha1); + read_unlock(); + + if (collision_test_needed && !data) { + read_lock(); + if (!check_collison(obj_entry)) + collision_test_needed = 0; + read_unlock(); + } + if (collision_test_needed) { void *has_data; enum object_type has_type; unsigned long has_size; + read_lock(); + has_type = sha1_object_info(sha1, &has_size); + if (has_type != type || has_size != size) + die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1)); has_data = read_sha1_file(sha1, &has_type, &has_size); + read_unlock(); + if (!data) + data = new_data = get_data_from_pack(obj_entry); if (!has_data) - die("cannot read existing object %s", sha1_to_hex(sha1)); + die(_("cannot read existing object %s"), sha1_to_hex(sha1)); if (size != has_size || type != has_type || memcmp(data, has_data, size) != 0) - die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1)); + die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1)); free(has_data); } + if (strict) { + read_lock(); if (type == OBJ_BLOB) { struct blob *blob = lookup_blob(sha1); if (blob) blob->object.flags |= FLAG_CHECKED; else - die("invalid blob object %s", sha1_to_hex(sha1)); + die(_("invalid blob object %s"), sha1_to_hex(sha1)); } else { struct object *obj; int eaten; void *buf = (void *) data; + if (!buf) + buf = new_data = get_data_from_pack(obj_entry); + /* * we do not need to free the memory here, as the * buf is deleted by the caller. */ obj = parse_object_buffer(sha1, type, size, buf, &eaten); if (!obj) - die("invalid %s", typename(type)); + die(_("invalid %s"), typename(type)); if (fsck_object(obj, 1, fsck_error_function)) - die("Error in object"); + die(_("Error in object")); if (fsck_walk(obj, mark_link, NULL)) - die("Not all child objects of %s are reachable", sha1_to_hex(obj->sha1)); + die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1)); if (obj->type == OBJ_TREE) { struct tree *item = (struct tree *) obj; @@ -507,12 +760,10 @@ static void sha1_object(const void *data, unsigned long size, } obj->flags |= FLAG_CHECKED; } + read_unlock(); } -} -static int is_delta_type(enum object_type type) -{ - return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA); + free(new_data); } /* @@ -552,7 +803,7 @@ static void *get_base_data(struct base_data *c) if (!delta_nr) { c->data = get_data_from_pack(obj); c->size = obj->size; - base_cache_used += c->size; + get_thread_data()->base_cache_used += c->size; prune_base_data(c); } for (; delta_nr > 0; delta_nr--) { @@ -567,8 +818,8 @@ static void *get_base_data(struct base_data *c) &c->size); free(raw); if (!c->data) - bad_object(obj->idx.offset, "failed to apply delta"); - base_cache_used += c->size; + bad_object(obj->idx.offset, _("failed to apply delta")); + get_thread_data()->base_cache_used += c->size; prune_base_data(c); } free(delta); @@ -593,10 +844,14 @@ static void resolve_delta(struct object_entry *delta_obj, delta_data, delta_obj->size, &result->size); free(delta_data); if (!result->data) - bad_object(delta_obj->idx.offset, "failed to apply delta"); - sha1_object(result->data, result->size, delta_obj->real_type, + bad_object(delta_obj->idx.offset, _("failed to apply delta")); + hash_sha1_file(result->data, result->size, + typename(delta_obj->real_type), delta_obj->idx.sha1); + sha1_object(result->data, NULL, result->size, delta_obj->real_type, delta_obj->idx.sha1); + counter_lock(); nr_resolved_deltas++; + counter_unlock(); } static struct base_data *find_unresolved_deltas_1(struct base_data *base, @@ -682,33 +937,68 @@ static int compare_delta_entry(const void *a, const void *b) objects[delta_b->obj_no].type); } -/* Parse all objects and return the pack content SHA1 hash */ +static void resolve_base(struct object_entry *obj) +{ + struct base_data *base_obj = alloc_base_data(); + base_obj->obj = obj; + base_obj->data = NULL; + find_unresolved_deltas(base_obj); +} + +#ifndef NO_PTHREADS +static void *threaded_second_pass(void *data) +{ + set_thread_data(data); + for (;;) { + int i; + work_lock(); + display_progress(progress, nr_resolved_deltas); + while (nr_dispatched < nr_objects && + is_delta_type(objects[nr_dispatched].type)) + nr_dispatched++; + if (nr_dispatched >= nr_objects) { + work_unlock(); + break; + } + i = nr_dispatched++; + work_unlock(); + + resolve_base(&objects[i]); + } + return NULL; +} +#endif + +/* + * First pass: + * - find locations of all objects; + * - calculate SHA1 of all non-delta objects; + * - remember base (SHA1 or offset) for all deltas. + */ static void parse_pack_objects(unsigned char *sha1) { - int i; + int i, nr_delays = 0; struct delta_entry *delta = deltas; struct stat st; - /* - * First pass: - * - find locations of all objects; - * - calculate SHA1 of all non-delta objects; - * - remember base (SHA1 or offset) for all deltas. - */ if (verbose) progress = start_progress( - from_stdin ? "Receiving objects" : "Indexing objects", + from_stdin ? _("Receiving objects") : _("Indexing objects"), nr_objects); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; - void *data = unpack_raw_entry(obj, &delta->base); + void *data = unpack_raw_entry(obj, &delta->base, obj->idx.sha1); obj->real_type = obj->type; if (is_delta_type(obj->type)) { nr_deltas++; delta->obj_no = i; delta++; + } else if (!data) { + /* large blobs, check later */ + obj->real_type = OBJ_BAD; + nr_delays++; } else - sha1_object(data, obj->size, obj->type, obj->idx.sha1); + sha1_object(data, NULL, obj->size, obj->type, obj->idx.sha1); free(data); display_progress(progress, i+1); } @@ -719,15 +1009,39 @@ static void parse_pack_objects(unsigned char *sha1) flush(); git_SHA1_Final(sha1, &input_ctx); if (hashcmp(fill(20), sha1)) - die("pack is corrupted (SHA1 mismatch)"); + die(_("pack is corrupted (SHA1 mismatch)")); use(20); /* If input_fd is a file, we should have reached its end now. */ if (fstat(input_fd, &st)) - die_errno("cannot fstat packfile"); + die_errno(_("cannot fstat packfile")); if (S_ISREG(st.st_mode) && lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size) - die("pack has junk at the end"); + die(_("pack has junk at the end")); + + for (i = 0; i < nr_objects; i++) { + struct object_entry *obj = &objects[i]; + if (obj->real_type != OBJ_BAD) + continue; + obj->real_type = obj->type; + sha1_object(NULL, obj, obj->size, obj->type, obj->idx.sha1); + nr_delays--; + } + if (nr_delays) + die(_("confusion beyond insanity in parse_pack_objects()")); +} + +/* + * Second pass: + * - for all non-delta objects, look if it is used as a base for + * deltas; + * - if used as a base, uncompress the object and apply all deltas, + * recursively checking if the resulting object is used as a base + * for some more deltas. + */ +static void resolve_deltas(void) +{ + int i; if (!nr_deltas) return; @@ -736,29 +1050,83 @@ static void parse_pack_objects(unsigned char *sha1) qsort(deltas, nr_deltas, sizeof(struct delta_entry), compare_delta_entry); - /* - * Second pass: - * - for all non-delta objects, look if it is used as a base for - * deltas; - * - if used as a base, uncompress the object and apply all deltas, - * recursively checking if the resulting object is used as a base - * for some more deltas. - */ if (verbose) - progress = start_progress("Resolving deltas", nr_deltas); + progress = start_progress(_("Resolving deltas"), nr_deltas); + +#ifndef NO_PTHREADS + nr_dispatched = 0; + if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) { + init_thread(); + for (i = 0; i < nr_threads; i++) { + int ret = pthread_create(&thread_data[i].thread, NULL, + threaded_second_pass, thread_data + i); + if (ret) + die("unable to create thread: %s", strerror(ret)); + } + for (i = 0; i < nr_threads; i++) + pthread_join(thread_data[i].thread, NULL); + cleanup_thread(); + return; + } +#endif + for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; - struct base_data *base_obj = alloc_base_data(); if (is_delta_type(obj->type)) continue; - base_obj->obj = obj; - base_obj->data = NULL; - find_unresolved_deltas(base_obj); + resolve_base(obj); display_progress(progress, nr_resolved_deltas); } } +/* + * Third pass: + * - append objects to convert thin pack to full pack if required + * - write the final 20-byte SHA-1 + */ +static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved); +static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1) +{ + if (nr_deltas == nr_resolved_deltas) { + stop_progress(&progress); + /* Flush remaining pack final 20-byte SHA1. */ + flush(); + return; + } + + if (fix_thin_pack) { + struct sha1file *f; + unsigned char read_sha1[20], tail_sha1[20]; + char msg[48]; + int nr_unresolved = nr_deltas - nr_resolved_deltas; + int nr_objects_initial = nr_objects; + if (nr_unresolved <= 0) + die(_("confusion beyond insanity")); + objects = xrealloc(objects, + (nr_objects + nr_unresolved + 1) + * sizeof(*objects)); + f = sha1fd(output_fd, curr_pack); + fix_unresolved_deltas(f, nr_unresolved); + sprintf(msg, "completed with %d local objects", + nr_objects - nr_objects_initial); + stop_progress_msg(&progress, msg); + sha1close(f, tail_sha1, 0); + hashcpy(read_sha1, pack_sha1); + fixup_pack_header_footer(output_fd, pack_sha1, + curr_pack, nr_objects, + read_sha1, consumed_bytes-20); + if (hashcmp(read_sha1, tail_sha1) != 0) + die("Unexpected tail checksum for %s " + "(disk corruption?)", curr_pack); + } + if (nr_deltas != nr_resolved_deltas) + die(Q_("pack has %d unresolved delta", + "pack has %d unresolved deltas", + nr_deltas - nr_resolved_deltas), + nr_deltas - nr_resolved_deltas); +} + static int write_compressed(struct sha1file *f, void *in, unsigned int size) { git_zstream stream; @@ -778,7 +1146,7 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size) } while (status == Z_OK); if (status != Z_STREAM_END) - die("unable to deflate appended object (%d)", status); + die(_("unable to deflate appended object (%d)"), status); size = stream.total_out; git_deflate_end(&stream); return size; @@ -857,7 +1225,7 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved) if (check_sha1_signature(d->base.sha1, base_obj->data, base_obj->size, typename(type))) - die("local object %s is corrupt", sha1_to_hex(d->base.sha1)); + die(_("local object %s is corrupt"), sha1_to_hex(d->base.sha1)); base_obj->obj = append_obj_to_pack(f, d->base.sha1, base_obj->data, base_obj->size, type); find_unresolved_deltas(base_obj); @@ -881,7 +1249,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, fsync_or_die(output_fd, curr_pack_name); err = close(output_fd); if (err) - die_errno("error while closing pack file"); + die_errno(_("error while closing pack file")); } if (keep_msg) { @@ -894,7 +1262,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, if (keep_fd < 0) { if (errno != EEXIST) - die_errno("cannot write keep file '%s'", + die_errno(_("cannot write keep file '%s'"), keep_name); } else { if (keep_msg_len > 0) { @@ -902,7 +1270,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, write_or_die(keep_fd, "\n", 1); } if (close(keep_fd) != 0) - die_errno("cannot close written keep file '%s'", + die_errno(_("cannot close written keep file '%s'"), keep_name); report = "keep"; } @@ -915,7 +1283,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, final_pack_name = name; } if (move_temp_to_file(curr_pack_name, final_pack_name)) - die("cannot store pack file"); + die(_("cannot store pack file")); } else if (from_stdin) chmod(final_pack_name, 0444); @@ -926,7 +1294,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, final_index_name = name; } if (move_temp_to_file(curr_index_name, final_index_name)) - die("cannot store index file"); + die(_("cannot store index file")); } else chmod(final_index_name, 0444); @@ -962,6 +1330,18 @@ static int git_index_pack_config(const char *k, const char *v, void *cb) die("bad pack.indexversion=%"PRIu32, opts->version); return 0; } + if (!strcmp(k, "pack.threads")) { + nr_threads = git_config_int(k, v); + if (nr_threads < 0) + die("invalid number of threads specified (%d)", + nr_threads); +#ifdef NO_PTHREADS + if (nr_threads != 1) + warning("no threads support, ignoring %s", k); + nr_threads = 1; +#endif + return 0; + } return git_default_config(k, v, cb); } @@ -1015,9 +1395,9 @@ static void read_idx_option(struct pack_idx_option *opts, const char *pack_name) struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1); if (!p) - die("Cannot open existing pack file '%s'", pack_name); + die(_("Cannot open existing pack file '%s'"), pack_name); if (open_pack_index(p)) - die("Cannot open existing pack idx file for '%s'", pack_name); + die(_("Cannot open existing pack idx file for '%s'"), pack_name); /* Read the attributes from the existing idx file */ opts->version = p->index_version; @@ -1064,15 +1444,18 @@ static void show_pack_info(int stat_only) } if (baseobjects) - printf("non delta: %d object%s\n", - baseobjects, baseobjects > 1 ? "s" : ""); + printf_ln(Q_("non delta: %d object", + "non delta: %d objects", + baseobjects), + baseobjects); for (i = 0; i < deepest_delta; i++) { if (!chain_histogram[i]) continue; - printf("chain length = %d: %lu object%s\n", - i + 1, - chain_histogram[i], - chain_histogram[i] > 1 ? "s" : ""); + printf_ln(Q_("chain length = %d: %lu object", + "chain length = %d: %lu objects", + chain_histogram[i]), + i + 1, + chain_histogram[i]); } } @@ -1095,7 +1478,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) reset_pack_idx_option(&opts); git_config(git_index_pack_config, &opts); if (prefix && chdir(prefix)) - die("Cannot come back to cwd"); + die(_("Cannot come back to cwd")); for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -1120,6 +1503,17 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) keep_msg = ""; } else if (!prefixcmp(arg, "--keep=")) { keep_msg = arg + 7; + } else if (!prefixcmp(arg, "--threads=")) { + char *end; + nr_threads = strtoul(arg+10, &end, 0); + if (!arg[10] || *end || nr_threads < 0) + usage(index_pack_usage); +#ifdef NO_PTHREADS + if (nr_threads != 1) + warning("no threads support, " + "ignoring %s", arg); + nr_threads = 1; +#endif } else if (!prefixcmp(arg, "--pack_header=")) { struct pack_header *hdr; char *c; @@ -1128,10 +1522,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) hdr->hdr_signature = htonl(PACK_SIGNATURE); hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10)); if (*c != ',') - die("bad %s", arg); + die(_("bad %s"), arg); hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10)); if (*c) - die("bad %s", arg); + die(_("bad %s"), arg); input_len = sizeof(*hdr); } else if (!strcmp(arg, "-v")) { verbose = 1; @@ -1143,11 +1537,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) char *c; opts.version = strtoul(arg + 16, &c, 10); if (opts.version > 2) - die("bad %s", arg); + die(_("bad %s"), arg); if (*c == ',') opts.off32_limit = strtoul(c+1, &c, 0); if (*c || opts.off32_limit & 0x80000000) - die("bad %s", arg); + die(_("bad %s"), arg); } else usage(index_pack_usage); continue; @@ -1161,11 +1555,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (!pack_name && !from_stdin) usage(index_pack_usage); if (fix_thin_pack && !from_stdin) - die("--fix-thin cannot be used without --stdin"); + die(_("--fix-thin cannot be used without --stdin")); if (!index_name && pack_name) { int len = strlen(pack_name); if (!has_extension(pack_name, ".pack")) - die("packfile name '%s' does not end with '.pack'", + die(_("packfile name '%s' does not end with '.pack'"), pack_name); index_name_buf = xmalloc(len); memcpy(index_name_buf, pack_name, len - 5); @@ -1175,7 +1569,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (keep_msg && !keep_name && pack_name) { int len = strlen(pack_name); if (!has_extension(pack_name, ".pack")) - die("packfile name '%s' does not end with '.pack'", + die(_("packfile name '%s' does not end with '.pack'"), pack_name); keep_name_buf = xmalloc(len); memcpy(keep_name_buf, pack_name, len - 5); @@ -1184,52 +1578,29 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) } if (verify) { if (!index_name) - die("--verify with no packfile name given"); + die(_("--verify with no packfile name given")); read_idx_option(&opts, index_name); opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT; } if (strict) opts.flags |= WRITE_IDX_STRICT; +#ifndef NO_PTHREADS + if (!nr_threads) { + nr_threads = online_cpus(); + /* An experiment showed that more threads does not mean faster */ + if (nr_threads > 3) + nr_threads = 3; + } +#endif + curr_pack = open_pack_file(pack_name); parse_pack_header(); objects = xcalloc(nr_objects + 1, sizeof(struct object_entry)); deltas = xcalloc(nr_objects, sizeof(struct delta_entry)); parse_pack_objects(pack_sha1); - if (nr_deltas == nr_resolved_deltas) { - stop_progress(&progress); - /* Flush remaining pack final 20-byte SHA1. */ - flush(); - } else { - if (fix_thin_pack) { - struct sha1file *f; - unsigned char read_sha1[20], tail_sha1[20]; - char msg[48]; - int nr_unresolved = nr_deltas - nr_resolved_deltas; - int nr_objects_initial = nr_objects; - if (nr_unresolved <= 0) - die("confusion beyond insanity"); - objects = xrealloc(objects, - (nr_objects + nr_unresolved + 1) - * sizeof(*objects)); - f = sha1fd(output_fd, curr_pack); - fix_unresolved_deltas(f, nr_unresolved); - sprintf(msg, "completed with %d local objects", - nr_objects - nr_objects_initial); - stop_progress_msg(&progress, msg); - sha1close(f, tail_sha1, 0); - hashcpy(read_sha1, pack_sha1); - fixup_pack_header_footer(output_fd, pack_sha1, - curr_pack, nr_objects, - read_sha1, consumed_bytes-20); - if (hashcmp(read_sha1, tail_sha1) != 0) - die("Unexpected tail checksum for %s " - "(disk corruption?)", curr_pack); - } - if (nr_deltas != nr_resolved_deltas) - die("pack has %d unresolved deltas", - nr_deltas - nr_resolved_deltas); - } + resolve_deltas(); + conclude_pack(fix_thin_pack, curr_pack, pack_sha1); free(deltas); if (strict) check_objects(); diff --git a/builtin/init-db.c b/builtin/init-db.c index 0dacb8b79c..244fb7fc32 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -290,6 +290,7 @@ static int create_default_files(const char *template_path) strcpy(path + len, "CoNfIg"); if (!access(path, F_OK)) git_config_set("core.ignorecase", "true"); + probe_utf8_pathname_composition(path, len); } return reinit; diff --git a/builtin/log.c b/builtin/log.c index 8a47012b0b..ecc2793690 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -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; @@ -365,6 +367,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) 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; @@ -383,8 +386,13 @@ static void show_tagger(char *buf, int len, struct rev_info *rev) 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; @@ -394,16 +402,16 @@ static int show_object(const unsigned char *sha1, int show_tag_object, 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,6 +464,9 @@ int cmd_show(int argc, const char **argv, const char *prefix) 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++) { @@ -463,7 +474,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) 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; @@ -474,7 +485,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) 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; @@ -547,6 +558,7 @@ int cmd_log(int argc, const char **argv, const char *prefix) 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); } @@ -657,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; @@ -671,7 +684,7 @@ static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet) 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); @@ -731,15 +744,10 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha 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); } @@ -778,7 +786,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, 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) @@ -786,31 +793,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, 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); @@ -1148,6 +1134,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) 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; @@ -1167,7 +1154,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) 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); @@ -1405,8 +1392,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) 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 --git a/builtin/ls-files.c b/builtin/ls-files.c index 7cff175745..31b3f2d900 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -200,9 +200,19 @@ static void show_ru_info(void) } } +static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce) +{ + int dtype = ce_to_dtype(ce); + return path_excluded(check, ce->name, ce_namelen(ce), &dtype); +} + static void show_files(struct dir_struct *dir) { int i; + struct path_exclude_check check; + + if ((dir->flags & DIR_SHOW_IGNORED)) + path_exclude_check_init(&check, dir); /* For cached/deleted files we don't need to even do the readdir */ if (show_others || show_killed) { @@ -215,9 +225,8 @@ static void show_files(struct dir_struct *dir) if (show_cached | show_stage) { for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - int dtype = ce_to_dtype(ce); - if (dir->flags & DIR_SHOW_IGNORED && - !excluded(dir, ce->name, &dtype)) + if ((dir->flags & DIR_SHOW_IGNORED) && + !ce_excluded(&check, ce)) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -232,9 +241,8 @@ static void show_files(struct dir_struct *dir) struct cache_entry *ce = active_cache[i]; struct stat st; int err; - int dtype = ce_to_dtype(ce); - if (dir->flags & DIR_SHOW_IGNORED && - !excluded(dir, ce->name, &dtype)) + if ((dir->flags & DIR_SHOW_IGNORED) && + !ce_excluded(&check, ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; @@ -247,6 +255,9 @@ static void show_files(struct dir_struct *dir) show_ce_entry(tag_modified, ce); } } + + if ((dir->flags & DIR_SHOW_IGNORED)) + path_exclude_check_clear(&check); } /* diff --git a/builtin/merge-file.c b/builtin/merge-file.c index 237abd3c0b..6f0efef43c 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -63,7 +63,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) if (quiet) { if (!freopen("/dev/null", "w", stderr)) return error("failed to redirect stderr to /dev/null: " - "%s\n", strerror(errno)); + "%s", strerror(errno)); } if (prefix) @@ -76,7 +76,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) if (read_mmfile(mmfs + i, fname)) return -1; if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size)) - return error("Cannot merge binary files: %s\n", + return error("Cannot merge binary files: %s", argv[i]); } diff --git a/builtin/merge.c b/builtin/merge.c index 470fc57c5d..e81fde6d79 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -404,8 +404,7 @@ static void finish(struct commit *head_commit, opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; - if (diff_setup_done(&opts) < 0) - die(_("diff_setup_done failed")); + diff_setup_done(&opts); diff_tree_sha1(head, new_head, "", &opts); diffcore_std(&opts); diff_flush(&opts); @@ -1447,7 +1446,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) refresh_cache(REFRESH_QUIET); if (allow_trivial && !fast_forward_only) { /* See if it is really trivial. */ - git_committer_info(IDENT_ERROR_ON_NO_NAME); + git_committer_info(IDENT_STRICT); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, head_commit->object.sha1, @@ -1490,7 +1489,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) die(_("Not possible to fast-forward, aborting.")); /* We are going to make a new commit. */ - git_committer_info(IDENT_ERROR_ON_NO_NAME); + git_committer_info(IDENT_STRICT); /* * At this point, we need a real merge. No matter what strategy diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 1861093e9d..782e7d0c38 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -16,6 +16,7 @@ #include "list-objects.h" #include "progress.h" #include "refs.h" +#include "streaming.h" #include "thread-utils.h" static const char *pack_usage[] = { @@ -150,6 +151,46 @@ static unsigned long do_compress(void **pptr, unsigned long size) 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. @@ -200,22 +241,198 @@ static void copy_pack_data(struct sha1file *f, } /* 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; @@ -243,11 +460,11 @@ static unsigned long write_object(struct sha1file *f, 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 */ @@ -256,153 +473,19 @@ static unsigned long write_object(struct sha1file *f, * 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 { @@ -1327,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; } @@ -2290,7 +2373,7 @@ static void get_object_list(int ac, const char **av) } 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); } diff --git a/builtin/push.c b/builtin/push.c index 693671315e..fdfcc6c716 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -76,7 +76,44 @@ static int push_url_of_remote(struct remote *remote, const char ***url_p) return remote->url_nr; } -static void setup_push_upstream(struct remote *remote) +static NORETURN int die_push_simple(struct branch *branch, struct remote *remote) { + /* + * There's no point in using shorten_unambiguous_ref here, + * as the ambiguity would be on the remote side, not what + * we have locally. Plus, this is supposed to be the simple + * mode. If the user is doing something crazy like setting + * upstream to a non-branch, we should probably be showing + * them the big ugly fully qualified ref. + */ + const char *advice_maybe = ""; + const char *short_upstream = + skip_prefix(branch->merge[0]->src, "refs/heads/"); + + if (!short_upstream) + short_upstream = branch->merge[0]->src; + /* + * Don't show advice for people who explicitely set + * push.default. + */ + if (push_default == PUSH_DEFAULT_UNSPECIFIED) + advice_maybe = _("\n" + "To choose either option permanently, " + "see push.default in 'git help config'."); + die(_("The upstream branch of your current branch does not match\n" + "the name of your current branch. To push to the upstream branch\n" + "on the remote, use\n" + "\n" + " git push %s HEAD:%s\n" + "\n" + "To push to the branch of the same name on the remote, use\n" + "\n" + " git push %s %s\n" + "%s"), + remote->name, short_upstream, + remote->name, branch->name, advice_maybe); +} + +static void setup_push_upstream(struct remote *remote, int simple) { struct strbuf refspec = STRBUF_INIT; struct branch *branch = branch_get(NULL); @@ -103,6 +140,8 @@ static void setup_push_upstream(struct remote *remote) "your current branch '%s', without telling me what to push\n" "to update which remote branch."), remote->name, branch->name); + if (simple && strcmp(branch->refname, branch->merge[0]->src)) + die_push_simple(branch, remote); strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); add_refspec(refspec.buf); @@ -119,8 +158,12 @@ static void setup_default_push_refspecs(struct remote *remote) add_refspec(":"); break; + case PUSH_DEFAULT_SIMPLE: + setup_push_upstream(remote, 1); + break; + case PUSH_DEFAULT_UPSTREAM: - setup_push_upstream(remote); + setup_push_upstream(remote, 0); break; case PUSH_DEFAULT_CURRENT: @@ -284,13 +327,21 @@ static int option_parse_recurse_submodules(const struct option *opt, const char *arg, int unset) { int *flags = opt->value; + + if (*flags & (TRANSPORT_RECURSE_SUBMODULES_CHECK | + TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND)) + die("%s can only be used once.", opt->long_name); + if (arg) { if (!strcmp(arg, "check")) *flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK; + else if (!strcmp(arg, "on-demand")) + *flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND; else die("bad %s argument: %s", opt->long_name, arg); } else - die("option %s needs an argument (check)", opt->long_name); + die("option %s needs an argument (check|on-demand)", + opt->long_name); return 0; } diff --git a/builtin/reflog.c b/builtin/reflog.c index 062d7dad1b..b3c9e27bde 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -330,8 +330,10 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, printf("keep %s", message); return 0; prune: - if (!cb->newlog || cb->cmd->verbose) - printf("%sprune %s", cb->newlog ? "" : "would ", message); + if (!cb->newlog) + printf("would prune %s", message); + else if (cb->cmd->verbose) + printf("prune %s", message); return 0; } diff --git a/builtin/remote.c b/builtin/remote.c index fec92bc66e..920262d76e 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -9,7 +9,7 @@ static const char * const builtin_remote_usage[] = { "git remote [-v | --verbose]", - "git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>", + "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>", "git remote rename <old> <new>", "git remote rm <name>", "git remote set-head <name> (-a | -d | <branch>)", @@ -17,7 +17,7 @@ static const char * const builtin_remote_usage[] = { "git remote prune [-n | --dry-run] <name>", "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]", "git remote set-branches [--add] <name> <branch>...", - "git remote set-url <name> <newurl> [<oldurl>]", + "git remote set-url [--push] <name> <newurl> [<oldurl>]", "git remote set-url --add <name> <newurl>", "git remote set-url --delete <name> <url>", NULL @@ -95,9 +95,9 @@ static int fetch_remote(const char *name) argv[1] = "-v"; argv[2] = name; } - printf("Updating %s\n", name); + printf_ln(_("Updating %s"), name); if (run_command_v_opt(argv, RUN_GIT_CMD)) - return error("Could not fetch %s", name); + return error(_("Could not fetch %s"), name); return 0; } @@ -127,8 +127,8 @@ static int add_branch(const char *key, const char *branchname, } static const char mirror_advice[] = -"--mirror is dangerous and deprecated; please\n" -"\t use --mirror=fetch or --mirror=push instead"; +N_("--mirror is dangerous and deprecated; please\n" + "\t use --mirror=fetch or --mirror=push instead"); static int parse_mirror_opt(const struct option *opt, const char *arg, int not) { @@ -136,7 +136,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) if (not) *mirror = MIRROR_NONE; else if (!arg) { - warning("%s", mirror_advice); + warning("%s", _(mirror_advice)); *mirror = MIRROR_BOTH; } else if (!strcmp(arg, "fetch")) @@ -144,7 +144,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) else if (!strcmp(arg, "push")) *mirror = MIRROR_PUSH; else - return error("unknown mirror argument: %s", arg); + return error(_("unknown mirror argument: %s"), arg); return 0; } @@ -182,9 +182,9 @@ static int add(int argc, const char **argv) usage_with_options(builtin_remote_add_usage, options); if (mirror && master) - die("specifying a master branch makes no sense with --mirror"); + die(_("specifying a master branch makes no sense with --mirror")); if (mirror && !(mirror & MIRROR_FETCH) && track.nr) - die("specifying branches to track makes sense only with fetch mirrors"); + die(_("specifying branches to track makes sense only with fetch mirrors")); name = argv[0]; url = argv[1]; @@ -192,11 +192,11 @@ static int add(int argc, const char **argv) remote = remote_get(name); if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) || remote->fetch_refspec_nr)) - die("remote %s already exists.", name); + die(_("remote %s already exists."), name); strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name); if (!valid_fetch_refspec(buf2.buf)) - die("'%s' is not a valid remote name", name); + die(_("'%s' is not a valid remote name"), name); strbuf_addf(&buf, "remote.%s.url", name); if (git_config_set(buf.buf, url)) @@ -240,7 +240,7 @@ static int add(int argc, const char **argv) strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master); if (create_symref(buf.buf, buf2.buf, "remote add")) - return error("Could not setup master '%s'", master); + return error(_("Could not setup master '%s'"), master); } strbuf_release(&buf); @@ -296,7 +296,7 @@ static int config_read_branches(const char *key, const char *value, void *cb) info = item->util; if (type == REMOTE) { if (info->remote_name) - warning("more than one %s", orig_key); + warning(_("more than one %s"), orig_key); info->remote_name = xstrdup(value); } else if (type == MERGE) { char *space = strchr(value, ' '); @@ -336,7 +336,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat for (i = 0; i < states->remote->fetch_refspec_nr; i++) if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1)) - die("Could not get fetch map for refspec %s", + die(_("Could not get fetch map for refspec %s"), states->remote->fetch_refspec[i]); states->new.strdup_strings = 1; @@ -437,7 +437,7 @@ static int get_push_ref_states_noquery(struct ref_states *states) states->push.strdup_strings = 1; if (!remote->push_refspec_nr) { - item = string_list_append(&states->push, "(matching)"); + item = string_list_append(&states->push, _("(matching)")); info = item->util = xcalloc(sizeof(struct push_info), 1); info->status = PUSH_STATUS_NOTQUERIED; info->dest = xstrdup(item->string); @@ -445,11 +445,11 @@ static int get_push_ref_states_noquery(struct ref_states *states) for (i = 0; i < remote->push_refspec_nr; i++) { struct refspec *spec = remote->push + i; if (spec->matching) - item = string_list_append(&states->push, "(matching)"); + item = string_list_append(&states->push, _("(matching)")); else if (strlen(spec->src)) item = string_list_append(&states->push, spec->src); else - item = string_list_append(&states->push, "(delete)"); + item = string_list_append(&states->push, _("(delete)")); info = item->util = xcalloc(sizeof(struct push_info), 1); info->forced = spec->force; @@ -592,19 +592,19 @@ static int migrate_file(struct remote *remote) strbuf_addf(&buf, "remote.%s.url", remote->name); for (i = 0; i < remote->url_nr; i++) if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0)) - return error("Could not append '%s' to '%s'", + return error(_("Could not append '%s' to '%s'"), remote->url[i], buf.buf); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); for (i = 0; i < remote->push_refspec_nr; i++) if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0)) - return error("Could not append '%s' to '%s'", + return error(_("Could not append '%s' to '%s'"), remote->push_refspec[i], buf.buf); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); for (i = 0; i < remote->fetch_refspec_nr; i++) if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0)) - return error("Could not append '%s' to '%s'", + return error(_("Could not append '%s' to '%s'"), remote->fetch_refspec[i], buf.buf); if (remote->origin == REMOTE_REMOTES) path = git_path("remotes/%s", remote->name); @@ -636,30 +636,30 @@ static int mv(int argc, const char **argv) oldremote = remote_get(rename.old); if (!oldremote) - die("No such remote: %s", rename.old); + die(_("No such remote: %s"), rename.old); if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG) return migrate_file(oldremote); newremote = remote_get(rename.new); if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr)) - die("remote %s already exists.", rename.new); + die(_("remote %s already exists."), rename.new); strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new); if (!valid_fetch_refspec(buf.buf)) - die("'%s' is not a valid remote name", rename.new); + die(_("'%s' is not a valid remote name"), rename.new); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s", rename.old); strbuf_addf(&buf2, "remote.%s", rename.new); if (git_config_rename_section(buf.buf, buf2.buf) < 1) - return error("Could not rename config section '%s' to '%s'", + return error(_("Could not rename config section '%s' to '%s'"), buf.buf, buf2.buf); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new); if (git_config_set_multivar(buf.buf, NULL, NULL, 1)) - return error("Could not remove config section '%s'", buf.buf); + return error(_("Could not remove config section '%s'"), buf.buf); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old); for (i = 0; i < oldremote->fetch_refspec_nr; i++) { char *ptr; @@ -674,13 +674,13 @@ static int mv(int argc, const char **argv) strlen(rename.old), rename.new, strlen(rename.new)); } else - warning("Not updating non-default fetch respec\n" - "\t%s\n" - "\tPlease update the configuration manually if necessary.", + warning(_("Not updating non-default fetch refspec\n" + "\t%s\n" + "\tPlease update the configuration manually if necessary."), buf2.buf); if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0)) - return error("Could not append '%s'", buf.buf); + return error(_("Could not append '%s'"), buf.buf); } read_branches(); @@ -691,7 +691,7 @@ static int mv(int argc, const char **argv) strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); if (git_config_set(buf.buf, rename.new)) { - return error("Could not set '%s'", buf.buf); + return error(_("Could not set '%s'"), buf.buf); } } } @@ -713,7 +713,7 @@ static int mv(int argc, const char **argv) if (!(flag & REF_ISSYMREF)) continue; if (delete_ref(item->string, NULL, REF_NODEREF)) - die("deleting '%s' failed", item->string); + die(_("deleting '%s' failed"), item->string); } for (i = 0; i < remote_branches.nr; i++) { struct string_list_item *item = remote_branches.items + i; @@ -728,7 +728,7 @@ static int mv(int argc, const char **argv) strbuf_addf(&buf2, "remote: renamed %s to %s", item->string, buf.buf); if (rename_ref(item->string, buf.buf, buf2.buf)) - die("renaming '%s' failed", item->string); + die(_("renaming '%s' failed"), item->string); } for (i = 0; i < remote_branches.nr; i++) { struct string_list_item *item = remote_branches.items + i; @@ -747,7 +747,7 @@ static int mv(int argc, const char **argv) strbuf_addf(&buf3, "remote: renamed %s to %s", item->string, buf.buf); if (create_symref(buf.buf, buf2.buf, buf3.buf)) - die("creating '%s' failed", buf.buf); + die(_("creating '%s' failed"), buf.buf); } return 0; } @@ -761,7 +761,7 @@ static int remove_branches(struct string_list *branches) unsigned char *sha1 = item->util; if (delete_ref(refname, sha1, 0)) - result |= error("Could not remove branch %s", refname); + result |= error(_("Could not remove branch %s"), refname); } return result; } @@ -789,14 +789,14 @@ static int rm(int argc, const char **argv) remote = remote_get(argv[1]); if (!remote) - die("No such remote: %s", argv[1]); + die(_("No such remote: %s"), argv[1]); known_remotes.to_delete = remote; for_each_remote(add_known_remote, &known_remotes); strbuf_addf(&buf, "remote.%s", remote->name); if (git_config_rename_section(buf.buf, NULL) < 1) - return error("Could not remove config section '%s'", buf.buf); + return error(_("Could not remove config section '%s'"), buf.buf); read_branches(); for (i = 0; i < branch_list.nr; i++) { @@ -830,11 +830,12 @@ static int rm(int argc, const char **argv) string_list_clear(&branches, 1); if (skipped.nr) { - fprintf(stderr, skipped.nr == 1 ? - "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n" - "to delete it, use:\n" : - "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n" - "to delete them, use:\n"); + fprintf_ln(stderr, + Q_("Note: A branch outside the refs/remotes/ hierarchy was not removed;\n" + "to delete it, use:", + "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n" + "to delete them, use:", + skipped.nr)); for (i = 0; i < skipped.nr; i++) fprintf(stderr, " git branch -d %s\n", skipped.items[i].string); @@ -886,7 +887,7 @@ static int get_remote_ref_states(const char *name, states->remote = remote_get(name); if (!states->remote) - return error("No such remote: %s", name); + return error(_("No such remote: %s"), name); read_branches(); @@ -939,14 +940,14 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data) const char *fmt = "%s"; const char *arg = ""; if (string_list_has_string(&states->new, name)) { - fmt = " new (next fetch will store in remotes/%s)"; + fmt = _(" new (next fetch will store in remotes/%s)"); arg = states->remote->name; } else if (string_list_has_string(&states->tracked, name)) - arg = " tracked"; + arg = _(" tracked"); else if (string_list_has_string(&states->stale, name)) - arg = " stale (use 'git remote prune' to remove)"; + arg = _(" stale (use 'git remote prune' to remove)"); else - arg = " ???"; + arg = _(" ???"); printf(" %-*s", info->width, name); printf(fmt, arg); printf("\n"); @@ -987,21 +988,21 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data) int i; if (branch_info->rebase && branch_info->merge.nr > 1) { - error("invalid branch.%s.merge; cannot rebase onto > 1 branch", + error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"), item->string); return 0; } printf(" %-*s ", show_info->width, item->string); if (branch_info->rebase) { - printf("rebases onto remote %s\n", merge->items[0].string); + printf_ln(_("rebases onto remote %s"), merge->items[0].string); return 0; } else if (show_info->any_rebase) { - printf(" merges with remote %s\n", merge->items[0].string); - also = " and with remote"; + printf_ln(_(" merges with remote %s"), merge->items[0].string); + also = _(" and with remote"); } else { - printf("merges with remote %s\n", merge->items[0].string); - also = " and with remote"; + printf_ln(_("merges with remote %s"), merge->items[0].string); + also = _(" and with remote"); } for (i = 1; i < merge->nr; i++) printf(" %-*s %s %s\n", show_info->width, "", also, @@ -1043,36 +1044,43 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data) { struct show_info *show_info = cb_data; struct push_info *push_info = item->util; - char *src = item->string, *status = NULL; + const char *src = item->string, *status = NULL; switch (push_info->status) { case PUSH_STATUS_CREATE: - status = "create"; + status = _("create"); break; case PUSH_STATUS_DELETE: - status = "delete"; - src = "(none)"; + status = _("delete"); + src = _("(none)"); break; case PUSH_STATUS_UPTODATE: - status = "up to date"; + status = _("up to date"); break; case PUSH_STATUS_FASTFORWARD: - status = "fast-forwardable"; + status = _("fast-forwardable"); break; case PUSH_STATUS_OUTOFDATE: - status = "local out of date"; + status = _("local out of date"); break; case PUSH_STATUS_NOTQUERIED: break; } - if (status) - printf(" %-*s %s to %-*s (%s)\n", show_info->width, src, - push_info->forced ? "forces" : "pushes", - show_info->width2, push_info->dest, status); - else - printf(" %-*s %s to %s\n", show_info->width, src, - push_info->forced ? "forces" : "pushes", - push_info->dest); + if (status) { + if (push_info->forced) + printf_ln(_(" %-*s forces to %-*s (%s)"), show_info->width, src, + show_info->width2, push_info->dest, status); + else + printf_ln(_(" %-*s pushes to %-*s (%s)"), show_info->width, src, + show_info->width2, push_info->dest, status); + } else { + if (push_info->forced) + printf_ln(_(" %-*s forces to %s"), show_info->width, src, + push_info->dest); + else + printf_ln(_(" %-*s pushes to %s"), show_info->width, src, + push_info->dest); + } return 0; } @@ -1107,9 +1115,9 @@ static int show(int argc, const char **argv) get_remote_ref_states(*argv, &states, query_flag); - printf("* remote %s\n", *argv); - printf(" Fetch URL: %s\n", states.remote->url_nr > 0 ? - states.remote->url[0] : "(no URL)"); + printf_ln(_("* remote %s"), *argv); + printf_ln(_(" Fetch URL: %s"), states.remote->url_nr > 0 ? + states.remote->url[0] : _("(no URL)")); if (states.remote->pushurl_nr) { url = states.remote->pushurl; url_nr = states.remote->pushurl_nr; @@ -1118,18 +1126,18 @@ static int show(int argc, const char **argv) url_nr = states.remote->url_nr; } for (i = 0; i < url_nr; i++) - printf(" Push URL: %s\n", url[i]); + printf_ln(_(" Push URL: %s"), url[i]); if (!i) - printf(" Push URL: %s\n", "(no URL)"); + printf_ln(_(" Push URL: %s"), "(no URL)"); if (no_query) - printf(" HEAD branch: (not queried)\n"); + printf_ln(_(" HEAD branch: %s"), "(not queried)"); else if (!states.heads.nr) - printf(" HEAD branch: (unknown)\n"); + printf_ln(_(" HEAD branch: %s"), "(unknown)"); else if (states.heads.nr == 1) - printf(" HEAD branch: %s\n", states.heads.items[0].string); + printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string); else { - printf(" HEAD branch (remote HEAD is ambiguous," - " may be one of the following):\n"); + printf(_(" HEAD branch (remote HEAD is ambiguous," + " may be one of the following):\n")); for (i = 0; i < states.heads.nr; i++) printf(" %s\n", states.heads.items[i].string); } @@ -1140,9 +1148,10 @@ static int show(int argc, const char **argv) for_each_string_list(&states.tracked, add_remote_to_show_info, &info); for_each_string_list(&states.stale, add_remote_to_show_info, &info); if (info.list->nr) - printf(" Remote branch%s:%s\n", - info.list->nr > 1 ? "es" : "", - no_query ? " (status not queried)" : ""); + printf_ln(Q_(" Remote branch:%s", + " Remote branches:%s", + info.list->nr), + no_query ? _(" (status not queried)") : ""); for_each_string_list(info.list, show_remote_info_item, &info); string_list_clear(info.list, 0); @@ -1151,23 +1160,25 @@ static int show(int argc, const char **argv) info.any_rebase = 0; for_each_string_list(&branch_list, add_local_to_show_info, &info); if (info.list->nr) - printf(" Local branch%s configured for 'git pull':\n", - info.list->nr > 1 ? "es" : ""); + printf_ln(Q_(" Local branch configured for 'git pull':", + " Local branches configured for 'git pull':", + info.list->nr)); for_each_string_list(info.list, show_local_info_item, &info); string_list_clear(info.list, 0); /* git push info */ if (states.remote->mirror) - printf(" Local refs will be mirrored by 'git push'\n"); + printf_ln(_(" Local refs will be mirrored by 'git push'")); info.width = info.width2 = 0; for_each_string_list(&states.push, add_push_to_show_info, &info); qsort(info.list->items, info.list->nr, sizeof(*info.list->items), cmp_string_with_push); if (info.list->nr) - printf(" Local ref%s configured for 'git push'%s:\n", - info.list->nr > 1 ? "s" : "", - no_query ? " (status not queried)" : ""); + printf_ln(Q_(" Local ref configured for 'git push'%s:", + " Local refs configured for 'git push'%s:", + info.list->nr), + no_query ? _(" (status not queried)") : ""); for_each_string_list(info.list, show_push_info_item, &info); string_list_clear(info.list, 0); @@ -1202,10 +1213,10 @@ static int set_head(int argc, const char **argv) memset(&states, 0, sizeof(states)); get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES); if (!states.heads.nr) - result |= error("Cannot determine remote HEAD"); + result |= error(_("Cannot determine remote HEAD")); else if (states.heads.nr > 1) { - result |= error("Multiple remote HEAD branches. " - "Please choose one explicitly with:"); + result |= error(_("Multiple remote HEAD branches. " + "Please choose one explicitly with:")); for (i = 0; i < states.heads.nr; i++) fprintf(stderr, " git remote set-head %s %s\n", argv[0], states.heads.items[i].string); @@ -1214,7 +1225,7 @@ static int set_head(int argc, const char **argv) free_remote_ref_states(&states); } else if (opt_d && !opt_a && argc == 1) { if (delete_ref(buf.buf, NULL, REF_NODEREF)) - result |= error("Could not delete %s", buf.buf); + result |= error(_("Could not delete %s"), buf.buf); } else usage_with_options(builtin_remote_sethead_usage, options); @@ -1222,9 +1233,9 @@ static int set_head(int argc, const char **argv) strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name); /* make sure it's valid */ if (!ref_exists(buf2.buf)) - result |= error("Not a valid ref: %s", buf2.buf); + result |= error(_("Not a valid ref: %s"), buf2.buf); else if (create_symref(buf.buf, buf2.buf, "remote set-head")) - result |= error("Could not setup %s", buf.buf); + result |= error(_("Could not setup %s"), buf.buf); if (opt_a) printf("%s/HEAD set to %s\n", argv[0], head_name); free(head_name); @@ -1260,18 +1271,18 @@ static int prune_remote(const char *remote, int dry_run) int result = 0, i; struct ref_states states; const char *dangling_msg = dry_run - ? " %s will become dangling!\n" - : " %s has become dangling!\n"; + ? _(" %s will become dangling!") + : _(" %s has become dangling!"); memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); if (states.stale.nr) { - printf("Pruning %s\n", remote); - printf("URL: %s\n", + printf_ln(_("Pruning %s"), remote); + printf_ln(_("URL: %s"), states.remote->url_nr ? states.remote->url[0] - : "(no URL)"); + : _("(no URL)")); } for (i = 0; i < states.stale.nr; i++) { @@ -1280,8 +1291,12 @@ static int prune_remote(const char *remote, int dry_run) if (!dry_run) result |= delete_ref(refname, NULL, 0); - printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", - abbrev_ref(refname, "refs/remotes/")); + if (dry_run) + printf_ln(_(" * [would prune] %s"), + abbrev_ref(refname, "refs/remotes/")); + else + printf_ln(_(" * [pruned] %s"), + abbrev_ref(refname, "refs/remotes/")); warn_dangling_symref(stdout, dangling_msg, refname); } @@ -1369,7 +1384,7 @@ static int set_remote_branches(const char *remotename, const char **branches, strbuf_addf(&key, "remote.%s.fetch", remotename); if (!remote_is_configured(remotename)) - die("No such remote '%s'", remotename); + die(_("No such remote '%s'"), remotename); remote = remote_get(remotename); if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) { @@ -1396,7 +1411,7 @@ static int set_branches(int argc, const char **argv) argc = parse_options(argc, argv, NULL, options, builtin_remote_setbranches_usage, 0); if (argc == 0) { - error("no remote specified"); + error(_("no remote specified")); usage_with_options(builtin_remote_setbranches_usage, options); } argv[argc] = NULL; @@ -1429,7 +1444,7 @@ static int set_url(int argc, const char **argv) PARSE_OPT_KEEP_ARGV0); if (add_mode && delete_mode) - die("--add --delete doesn't make sense"); + die(_("--add --delete doesn't make sense")); if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3)) usage_with_options(builtin_remote_seturl_usage, options); @@ -1443,7 +1458,7 @@ static int set_url(int argc, const char **argv) oldurl = newurl; if (!remote_is_configured(remotename)) - die("No such remote '%s'", remotename); + die(_("No such remote '%s'"), remotename); remote = remote_get(remotename); if (push_mode) { @@ -1469,7 +1484,7 @@ static int set_url(int argc, const char **argv) /* Old URL specified. Demand that one matches. */ if (regcomp(&old_regex, oldurl, REG_EXTENDED)) - die("Invalid old URL pattern: %s", oldurl); + die(_("Invalid old URL pattern: %s"), oldurl); for (i = 0; i < urlset_nr; i++) if (!regexec(&old_regex, urlset[i], 0, NULL, 0)) @@ -1477,9 +1492,9 @@ static int set_url(int argc, const char **argv) else negative_matches++; if (!delete_mode && !matches) - die("No such URL found: %s", oldurl); + die(_("No such URL found: %s"), oldurl); if (delete_mode && !negative_matches && !push_mode) - die("Will not delete all non-push URLs"); + die(_("Will not delete all non-push URLs")); regfree(&old_regex); @@ -1580,7 +1595,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[0], "update")) result = update(argc, argv); else { - error("Unknown subcommand: %s", argv[0]); + error(_("Unknown subcommand: %s"), argv[0]); usage_with_options(builtin_remote_usage, options); } diff --git a/builtin/reset.c b/builtin/reset.c index 8c2c1d52a2..74442bd766 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -276,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) * Otherwise, argv[i] could be either <rev> or <paths> and * has to be unambiguous. */ - else if (!get_sha1(argv[i], sha1)) { + else if (!get_sha1_committish(argv[i], sha1)) { /* * Ok, argv[i] looks like a rev; it should not * be a filename. @@ -285,13 +285,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix) rev = argv[i++]; } else { /* Otherwise we treat this as a filename */ - verify_filename(prefix, argv[i]); + verify_filename(prefix, argv[i], 1); } } - if (get_sha1(rev, sha1)) + if (get_sha1_committish(rev, sha1)) die(_("Failed to resolve '%s' as a valid ref."), rev); + /* + * NOTE: As "git reset $treeish -- $path" should be usable on + * any tree-ish, this is not strictly correct. We are not + * moving the HEAD to any commit; we are merely resetting the + * entries in the index to that of a treeish. + */ commit = lookup_commit_reference(sha1); if (!commit) die(_("Could not parse object '%s'."), rev); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 733f626f6c..32788a9f86 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -195,6 +195,12 @@ static int anti_reference(const char *refname, const unsigned char *sha1, int fl return 0; } +static int show_abbrev(const unsigned char *sha1, void *cb_data) +{ + show_rev(NORMAL, sha1, NULL); + return 0; +} + static void show_datestring(const char *flag, const char *datestr) { static char buffer[100]; @@ -238,7 +244,7 @@ static int try_difference(const char *arg) next = "HEAD"; if (dotdot == arg) this = "HEAD"; - if (!get_sha1(this, sha1) && !get_sha1(next, end)) { + if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) { show_rev(NORMAL, end, next); show_rev(symmetric ? NORMAL : REVERSED, sha1, this); if (symmetric) { @@ -278,7 +284,7 @@ static int try_parent_shorthands(const char *arg) return 0; *dotdot = 0; - if (get_sha1(arg, sha1)) + if (get_sha1_committish(arg, sha1)) return 0; if (!parents_only) @@ -486,7 +492,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (as_is) { if (show_file(arg) && as_is < 2) - verify_filename(prefix, arg); + verify_filename(prefix, arg, 0); continue; } if (!strcmp(arg,"-n")) { @@ -589,6 +595,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_ref(show_reference, NULL); continue; } + if (!prefixcmp(arg, "--disambiguate=")) { + for_each_abbrev(arg + 15, show_abbrev, NULL); + continue; + } if (!strcmp(arg, "--bisect")) { for_each_ref_in("refs/bisect/bad", show_reference, NULL); for_each_ref_in("refs/bisect/good", anti_reference, NULL); @@ -734,7 +744,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) as_is = 1; if (!show_file(arg)) continue; - verify_filename(prefix, arg); + verify_filename(prefix, arg, 1); } if (verify) { if (revs_count == 1) { diff --git a/builtin/revert.c b/builtin/revert.c index 5462e676e2..82d1bf844b 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -115,12 +115,16 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) OPT_END(), OPT_END(), OPT_END(), + OPT_END(), + OPT_END(), }; if (opts->action == REPLAY_PICK) { struct option cp_extra[] = { OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"), OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"), + OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"), + OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"), OPT_END(), }; if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra)) @@ -138,6 +142,10 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) "--abort", rollback, NULL); + /* implies allow_empty */ + if (opts->keep_redundant_commits) + opts->allow_empty = 1; + /* Set the subcommand */ if (remove_state) opts->subcommand = REPLAY_REMOVE_STATE; diff --git a/builtin/tag.c b/builtin/tag.c index fe7e5e5b3d..7b1be85e48 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -16,6 +16,7 @@ #include "revision.h" #include "gpg-interface.h" #include "sha1-array.h" +#include "column.h" static const char * const git_tag_usage[] = { "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]", @@ -33,6 +34,7 @@ struct tag_filter { }; static struct sha1_array points_at; +static unsigned int colopts; static int match_pattern(const char **patterns, const char *ref) { @@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb) int status = git_gpg_config(var, value, cb); if (status) return status; + if (!prefixcmp(var, "column.")) + return git_column_config(var, value, "tag", &colopts); return git_default_config(var, value, cb); } @@ -328,7 +332,7 @@ static void create_tag(const unsigned char *object, const char *tag, sha1_to_hex(object), typename(type), tag, - git_committer_info(IDENT_ERROR_ON_NO_NAME)); + git_committer_info(IDENT_STRICT)); if (header_len > sizeof(header_buf) - 1) die(_("tag header too big.")); @@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_STRING('u', "local-user", &keyid, "key-id", "use another key to sign the tag"), OPT__FORCE(&force, "replace the tag if exists"), + OPT_COLUMN(0, "column", &colopts, "show tag list in columns"), OPT_GROUP("Tag listing options"), { @@ -495,9 +500,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (list + delete + verify > 1) usage_with_options(git_tag_usage, options); - if (list) - return list_tags(argv, lines == -1 ? 0 : lines, - with_commit); + finalize_colopts(&colopts, -1); + if (list && lines != -1) { + if (explicitly_enable_column(colopts)) + die(_("--column and -n are incompatible")); + colopts = 0; + } + if (list) { + int ret; + if (column_active(colopts)) { + struct column_options copts; + memset(&copts, 0, sizeof(copts)); + copts.padding = 2; + run_column_filter(colopts, &copts); + } + ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit); + if (column_active(colopts)) + stop_column_filter(); + return ret; + } if (lines != -1) die(_("-n option is only allowed with -l.")); if (with_commit) diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 14e04e6795..2217d7b3ae 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -107,7 +107,7 @@ static void *get_data(unsigned long size) if (stream.total_out == size && ret == Z_STREAM_END) break; if (ret != Z_OK) { - error("inflate returned %d\n", ret); + error("inflate returned %d", ret); free(buf); buf = NULL; if (!recover) diff --git a/builtin/update-index.c b/builtin/update-index.c index a6a23fa1f3..4ce341ceeb 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -95,7 +95,8 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru size = cache_entry_size(len); ce = xcalloc(1, size); memcpy(ce->name, path, len); - ce->ce_flags = len; + ce->ce_flags = create_ce_flags(0); + ce->ce_namelen = len; fill_stat_cache_info(ce, st); ce->ce_mode = ce_mode_from_stat(old, st->st_mode); @@ -211,12 +212,6 @@ static int process_path(const char *path) if (S_ISDIR(st.st_mode)) return process_directory(path, len, &st); - /* - * Process a regular file - */ - if (ce && S_ISGITLINK(ce->ce_mode)) - return error("%s is already a gitlink, not replacing", path); - return add_one_path(ce, path, len, &st); } @@ -235,7 +230,8 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, hashcpy(ce->sha1, sha1); memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(len, stage); + ce->ce_flags = create_ce_flags(stage); + ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); if (assume_unchanged) ce->ce_flags |= CE_VALID; @@ -433,7 +429,8 @@ static struct cache_entry *read_one_ent(const char *which, hashcpy(ce->sha1, sha1); memcpy(ce->name, path, namelen); - ce->ce_flags = create_ce_flags(namelen, stage); + ce->ce_flags = create_ce_flags(stage); + ce->ce_namelen = namelen; ce->ce_mode = create_ce_mode(mode); return ce; } @@ -708,6 +705,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) int newfd, entries, has_errors = 0, line_termination = '\n'; int read_from_stdin = 0; int prefix_length = prefix ? strlen(prefix) : 0; + int preferred_index_format = 0; char set_executable_bit = 0; struct refresh_params refresh_args = {0, &has_errors}; int lock_error = 0; @@ -791,6 +789,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) "(for porcelains) forget saved unresolved conflicts", PARSE_OPT_NOARG | PARSE_OPT_NONEG, resolve_undo_clear_callback}, + OPT_INTEGER(0, "index-version", &preferred_index_format, + "write index in this format"), OPT_END() }; @@ -851,6 +851,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) } } argc = parse_options_end(&ctx); + if (preferred_index_format) { + if (preferred_index_format < INDEX_FORMAT_LB || + INDEX_FORMAT_UB < preferred_index_format) + die("index-version %d not in range: %d..%d", + preferred_index_format, + INDEX_FORMAT_LB, INDEX_FORMAT_UB); + + if (the_index.version != preferred_index_format) + active_cache_changed = 1; + the_index.version = preferred_index_format; + } if (read_from_stdin) { struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index b90dce6358..0d63c4498c 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -15,6 +15,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix) OPT_END() }; + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, update_server_info_usage, 0); if (argc > 0) diff --git a/builtin/var.c b/builtin/var.c index 99d068a532..aedbb53a2d 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -11,7 +11,7 @@ static const char *editor(int flag) { const char *pgm = git_editor(); - if (!pgm && flag & IDENT_ERROR_ON_NO_NAME) + if (!pgm && flag & IDENT_STRICT) die("Terminal is dumb, but EDITOR unset"); return pgm; @@ -55,7 +55,7 @@ static const char *read_var(const char *var) val = NULL; for (ptr = git_vars; ptr->read; ptr++) { if (strcmp(var, ptr->name) == 0) { - val = ptr->read(IDENT_ERROR_ON_NO_NAME); + val = ptr->read(IDENT_STRICT); break; } } |