diff options
Diffstat (limited to 'builtin')
47 files changed, 1000 insertions, 773 deletions
diff --git a/builtin/add.c b/builtin/add.c index 2a2722fa10..459208a326 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -15,6 +15,7 @@ #include "diffcore.h" #include "revision.h" #include "bulk-checkin.h" +#include "argv-array.h" static const char * const builtin_add_usage[] = { N_("git add [options] [--] <pathspec>..."), @@ -26,55 +27,8 @@ static int take_worktree_changes; struct update_callback_data { int flags; int add_errors; - const char *implicit_dot; - size_t implicit_dot_len; - - /* only needed for 2.0 transition preparation */ - int warn_add_would_remove; }; -static const char *option_with_implicit_dot; -static const char *short_option_with_implicit_dot; - -static void warn_pathless_add(void) -{ - static int shown; - assert(option_with_implicit_dot && short_option_with_implicit_dot); - - if (shown) - return; - shown = 1; - - /* - * To be consistent with "git add -p" and most Git - * commands, we should default to being tree-wide, but - * this is not the original behavior and can't be - * changed until users trained themselves not to type - * "git add -u" or "git add -A". For now, we warn and - * keep the old behavior. Later, the behavior can be changed - * to tree-wide, keeping the warning for a while, and - * eventually we can drop the warning. - */ - warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n" - "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n" - "To add content for the whole tree, run:\n" - "\n" - " git add %s :/\n" - " (or git add %s :/)\n" - "\n" - "To restrict the command to the current directory, run:\n" - "\n" - " git add %s .\n" - " (or git add %s .)\n" - "\n" - "With the current Git version, the command is restricted to " - "the current directory.\n" - ""), - option_with_implicit_dot, short_option_with_implicit_dot, - option_with_implicit_dot, short_option_with_implicit_dot, - option_with_implicit_dot, short_option_with_implicit_dot); -} - static int fix_unmerged_status(struct diff_filepair *p, struct update_callback_data *data) { @@ -96,49 +50,15 @@ static int fix_unmerged_status(struct diff_filepair *p, return DIFF_STATUS_MODIFIED; } -static const char *add_would_remove_warning = N_( - "You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n" -"whose behaviour will change in Git 2.0 with respect to paths you removed.\n" -"Paths like '%s' that are\n" -"removed from your working tree are ignored with this version of Git.\n" -"\n" -"* 'git add --ignore-removal <pathspec>', which is the current default,\n" -" ignores paths you removed from your working tree.\n" -"\n" -"* 'git add --all <pathspec>' will let you also record the removals.\n" -"\n" -"Run 'git status' to check the paths you removed from your working tree.\n"); - -static void warn_add_would_remove(const char *path) -{ - warning(_(add_would_remove_warning), path); -} - static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { int i; struct update_callback_data *data = cbdata; - const char *implicit_dot = data->implicit_dot; - size_t implicit_dot_len = data->implicit_dot_len; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; - /* - * Check if "git add -A" or "git add -u" was run from a - * subdirectory with a modified file outside that directory, - * and warn if so. - * - * "git add -u" will behave like "git add -u :/" instead of - * "git add -u ." in the future. This warning prepares for - * that change. - */ - if (implicit_dot && - strncmp_icase(path, implicit_dot, implicit_dot_len)) { - warn_pathless_add(); - continue; - } switch (fix_unmerged_status(p, data)) { default: die(_("unexpected diff status %c"), p->status); @@ -151,10 +71,6 @@ static void update_callback(struct diff_queue_struct *q, } break; case DIFF_STATUS_DELETED: - if (data->warn_add_would_remove) { - warn_add_would_remove(path); - data->warn_add_would_remove = 0; - } if (data->flags & ADD_CACHE_IGNORE_REMOVAL) break; if (!(data->flags & ADD_CACHE_PRETEND)) @@ -166,37 +82,28 @@ static void update_callback(struct diff_queue_struct *q, } } -static void update_files_in_cache(const char *prefix, - const struct pathspec *pathspec, - struct update_callback_data *data) +int add_files_to_cache(const char *prefix, + const struct pathspec *pathspec, int flags) { + struct update_callback_data data; struct rev_info rev; + memset(&data, 0, sizeof(data)); + data.flags = flags; + init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); if (pathspec) copy_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = data; + rev.diffopt.format_callback_data = &data; rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); -} - -int add_files_to_cache(const char *prefix, - const struct pathspec *pathspec, int flags) -{ - struct update_callback_data data; - - memset(&data, 0, sizeof(data)); - data.flags = flags; - update_files_in_cache(prefix, pathspec, &data); return !!data.add_errors; } -#define WARN_IMPLICIT_DOT (1u << 0) -static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, - int prefix, unsigned flag) +static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix) { char *seen; int i; @@ -208,19 +115,8 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (match_pathspec_depth(pathspec, entry->name, entry->len, - prefix, seen)) + if (dir_path_match(entry, pathspec, prefix, seen)) *dst++ = entry; - else if (flag & WARN_IMPLICIT_DOT) - /* - * "git add -A" was run from a subdirectory with a - * new file outside that directory. - * - * "git add -A" will behave like "git add -A :/" - * instead of "git add -A ." in the future. - * Warn about the coming behavior change. - */ - warn_pathless_add(); } dir->nr = dst - dir->entries; add_pathspec_matches_against_index(pathspec, seen); @@ -246,23 +142,21 @@ static void refresh(int verbose, const struct pathspec *pathspec) int run_add_interactive(const char *revision, const char *patch_mode, const struct pathspec *pathspec) { - int status, ac, i; - const char **args; + int status, i; + struct argv_array argv = ARGV_ARRAY_INIT; - args = xcalloc(sizeof(const char *), (pathspec->nr + 6)); - ac = 0; - args[ac++] = "add--interactive"; + argv_array_push(&argv, "add--interactive"); if (patch_mode) - args[ac++] = patch_mode; + argv_array_push(&argv, patch_mode); if (revision) - args[ac++] = revision; - args[ac++] = "--"; + argv_array_push(&argv, revision); + argv_array_push(&argv, "--"); for (i = 0; i < pathspec->nr; i++) /* pass original pathspec, to be re-parsed */ - args[ac++] = pathspec->items[i].original; + argv_array_push(&argv, pathspec->items[i].original); - status = run_command_v_opt(args, RUN_GIT_CMD); - free(args); + status = run_command_v_opt(argv.argv, RUN_GIT_CMD); + argv_array_clear(&argv); return status; } @@ -339,7 +233,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n"); static int verbose, show_only, ignored_too, refresh_only; static int ignore_add_errors, intent_to_add, ignore_missing; -#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */ +#define ADDREMOVE_DEFAULT 1 static int addremove = ADDREMOVE_DEFAULT; static int addremove_explicit = -1; /* unspecified */ @@ -412,8 +306,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; char *seen = NULL; - int implicit_dot = 0; - struct update_callback_data update_data; git_config(add_config, NULL); @@ -437,36 +329,17 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); - /* - * Warn when "git add pathspec..." was given without "-u" or "-A" - * and pathspec... covers a removed path. - */ - memset(&update_data, 0, sizeof(update_data)); - if (!take_worktree_changes && addremove_explicit < 0) - update_data.warn_add_would_remove = 1; - if (!take_worktree_changes && addremove_explicit < 0 && argc) - /* - * Turn "git add pathspec..." to "git add -A pathspec..." - * in Git 2.0 but not yet - */ - ; /* addremove = 1; */ + /* Turn "git add pathspec..." to "git add -A pathspec..." */ + addremove = 1; if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); - if (addremove) { - option_with_implicit_dot = "--all"; - short_option_with_implicit_dot = "-A"; - } - if (take_worktree_changes) { - option_with_implicit_dot = "--update"; - short_option_with_implicit_dot = "-u"; - } - if (option_with_implicit_dot && !argc) { - static const char *here[2] = { ".", NULL }; + + if ((0 < addremove_explicit || take_worktree_changes) && !argc) { + static const char *whole[2] = { ":/", NULL }; argc = 1; - argv = here; - implicit_dot = 1; + argv = whole; } add_new_files = !take_worktree_changes && !refresh_only; @@ -479,8 +352,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) - ? ADD_CACHE_IGNORE_REMOVAL : 0)) | - (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0); + ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); @@ -514,18 +386,15 @@ int cmd_add(int argc, const char **argv, const char *prefix) memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); + baselen = fill_directory(&dir, &pathspec); if (pathspec.nr) - seen = prune_directory(&dir, &pathspec, baselen, - implicit_dot ? WARN_IMPLICIT_DOT : 0); + seen = prune_directory(&dir, &pathspec, baselen); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } - if (implicit_dot && prefix) - refresh_cache(REFRESH_QUIET); if (pathspec.nr) { int i; @@ -565,21 +434,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) plug_bulk_checkin(); - if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) { - /* - * Check for modified files throughout the worktree so - * update_callback has a chance to warn about changes - * outside the cwd. - */ - update_data.implicit_dot = prefix; - update_data.implicit_dot_len = strlen(prefix); - free_pathspec(&pathspec); - memset(&pathspec, 0, sizeof(pathspec)); - } - update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; - update_files_in_cache(prefix, &pathspec, &update_data); + exit_status |= add_files_to_cache(prefix, &pathspec, flags); - exit_status |= !!update_data.add_errors; if (add_new_files) exit_status |= add_files(&dir, flags); diff --git a/builtin/apply.c b/builtin/apply.c index b0d0986226..9c5724eacc 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -300,11 +300,13 @@ static int fuzzy_matchlines(const char *s1, size_t n1, while ((*last2 == '\r') || (*last2 == '\n')) last2--; - /* skip leading whitespace */ - while (isspace(*s1) && (s1 <= last1)) - s1++; - while (isspace(*s2) && (s2 <= last2)) - s2++; + /* skip leading whitespaces, if both begin with whitespace */ + if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) { + while (isspace(*s1) && (s1 <= last1)) + s1++; + while (isspace(*s2) && (s2 <= last2)) + s2++; + } /* early return if both lines are empty */ if ((s1 > last1) && (s2 > last2)) return 1; @@ -1943,13 +1945,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) size - offset - hdrsize, patch); if (!patchsize) { - static const char *binhdr[] = { - "Binary files ", - "Files ", - NULL, - }; static const char git_binary[] = "GIT binary patch\n"; - int i; int hd = hdrsize + offset; unsigned long llen = linelen(buffer + hd, size - hd); @@ -1965,6 +1961,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) patchsize = 0; } else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) { + static const char *binhdr[] = { + "Binary files ", + "Files ", + NULL, + }; + int i; for (i = 0; binhdr[i]; i++) { int len = strlen(binhdr[i]); if (len < size - hd && @@ -4061,7 +4063,7 @@ static int write_out_one_reject(struct patch *patch) 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 + * this is a git patch by saying --git or giving extended * headers. While at it, maybe please "kompare" that wants * the trailing TAB and some garbage at the end of line ;-). */ @@ -4152,7 +4154,7 @@ static int use_patch(struct patch *p) /* See if it matches any of exclude/include rule */ for (i = 0; i < limit_by_name.nr; i++) { struct string_list_item *it = &limit_by_name.items[i]; - if (!fnmatch(it->string, pathname, 0)) + if (!wildmatch(it->string, pathname, 0, NULL)) return (it->util != NULL); } diff --git a/builtin/blame.c b/builtin/blame.c index e44a6bb30a..d8b276048e 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -74,7 +74,7 @@ static unsigned blame_copy_score; #define BLAME_DEFAULT_MOVE_SCORE 20 #define BLAME_DEFAULT_COPY_SCORE 40 -/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */ +/* Remember to update object flag allocation in object.h */ #define METAINFO_SHOWN (1u<<12) #define MORE_THAN_ONE_PATH (1u<<13) @@ -197,7 +197,6 @@ static void drop_origin_blob(struct origin *o) * scoreboard structure, sorted by the target line number. */ struct blame_entry { - struct blame_entry *prev; struct blame_entry *next; /* the first line of this group in the final image; @@ -256,15 +255,6 @@ struct scoreboard { int *lineno; }; -static inline int same_suspect(struct origin *a, struct origin *b) -{ - if (a == b) - return 1; - if (a->commit != b->commit) - return 0; - return !strcmp(a->path, b->path); -} - static void sanity_check_refcnt(struct scoreboard *); /* @@ -277,13 +267,11 @@ static void coalesce(struct scoreboard *sb) struct blame_entry *ent, *next; for (ent = sb->ent; ent && (next = ent->next); ent = next) { - if (same_suspect(ent->suspect, next->suspect) && + if (ent->suspect == next->suspect && ent->guilty == next->guilty && ent->s_lno + ent->num_lines == next->s_lno) { ent->num_lines += next->num_lines; ent->next = next->next; - if (ent->next) - ent->next->prev = ent; origin_decref(next->suspect); free(next); ent->score = 0; @@ -534,7 +522,7 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e) prev = ent; /* prev, if not NULL, is the last one that is below e */ - e->prev = prev; + if (prev) { e->next = prev->next; prev->next = e; @@ -543,8 +531,6 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e) e->next = sb->ent; sb->ent = e; } - if (e->next) - e->next->prev = e; } /* @@ -555,14 +541,12 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e) */ static void dup_entry(struct blame_entry *dst, struct blame_entry *src) { - struct blame_entry *p, *n; + struct blame_entry *n; - p = dst->prev; n = dst->next; origin_incref(src->suspect); origin_decref(dst->suspect); memcpy(dst, src, sizeof(*src)); - dst->prev = p; dst->next = n; dst->score = 0; } @@ -742,7 +726,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target) int last_in_target = -1; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target)) + if (e->guilty || e->suspect != target) continue; if (last_in_target < e->s_lno + e->num_lines) last_in_target = e->s_lno + e->num_lines; @@ -762,7 +746,7 @@ static void blame_chunk(struct scoreboard *sb, struct blame_entry *e; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target)) + if (e->guilty || e->suspect != target) continue; if (same <= e->s_lno) continue; @@ -939,7 +923,6 @@ static void find_copy_in_blob(struct scoreboard *sb, mmfile_t *file_p) { const char *cp; - int cnt; mmfile_t file_o; struct handle_split_cb_data d; @@ -950,13 +933,7 @@ static void find_copy_in_blob(struct scoreboard *sb, */ cp = nth_line(sb, ent->lno); file_o.ptr = (char *) cp; - cnt = ent->num_lines; - - while (cnt && cp < sb->final_buf + sb->final_buf_size) { - if (*cp++ == '\n') - cnt--; - } - file_o.size = cp - file_o.ptr; + file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp; /* * file_o is a part of final image we are annotating. @@ -992,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb, while (made_progress) { made_progress = 0; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target) || + if (e->guilty || e->suspect != target || ent_score(sb, e) < blame_move_score) continue; find_copy_in_blob(sb, e, parent, split, &file_p); @@ -1027,14 +1004,14 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb, for (e = sb->ent, num_ents = 0; e; e = e->next) if (!e->scanned && !e->guilty && - same_suspect(e->suspect, target) && + e->suspect == target && min_score < ent_score(sb, e)) num_ents++; if (num_ents) { blame_list = xcalloc(num_ents, sizeof(struct blame_list)); for (e = sb->ent, i = 0; e; e = e->next) if (!e->scanned && !e->guilty && - same_suspect(e->suspect, target) && + e->suspect == target && min_score < ent_score(sb, e)) blame_list[i++].ent = e; } @@ -1178,7 +1155,7 @@ static void pass_whole_blame(struct scoreboard *sb, origin->file.ptr = NULL; } for (e = sb->ent; e; e = e->next) { - if (!same_suspect(e->suspect, origin)) + if (e->suspect != origin) continue; origin_incref(porigin); origin_decref(e->suspect); @@ -1567,7 +1544,7 @@ static void assign_blame(struct scoreboard *sb, int opt) /* Take responsibility for the remaining entries */ for (ent = sb->ent; ent; ent = ent->next) - if (same_suspect(ent->suspect, suspect)) + if (ent->suspect == suspect) found_guilty_entry(ent); origin_decref(suspect); @@ -1579,22 +1556,29 @@ static void assign_blame(struct scoreboard *sb, int opt) static const char *format_time(unsigned long time, const char *tz_str, int show_raw_time) { - static char time_buf[128]; - const char *time_str; - int time_len; - int tz; + static struct strbuf time_buf = STRBUF_INIT; + strbuf_reset(&time_buf); if (show_raw_time) { - snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str); + strbuf_addf(&time_buf, "%lu %s", time, tz_str); } else { + const char *time_str; + size_t time_width; + int tz; tz = atoi(tz_str); time_str = show_date(time, tz, blame_date_mode); - time_len = strlen(time_str); - memcpy(time_buf, time_str, time_len); - memset(time_buf + time_len, ' ', blame_date_width - time_len); + strbuf_addstr(&time_buf, time_str); + /* + * Add space paddings to time_buf to display a fixed width + * string, and use time_width for display width calibration. + */ + for (time_width = utf8_strwidth(time_str); + time_width < blame_date_width; + time_width++) + strbuf_addch(&time_buf, ' '); } - return time_buf; + return time_buf.buf; } #define OUTPUT_ANNOTATE_COMPAT 001 @@ -1772,25 +1756,41 @@ static int prepare_lines(struct scoreboard *sb) { const char *buf = sb->final_buf; unsigned long len = sb->final_buf_size; - int num = 0, incomplete = 0, bol = 1; + const char *end = buf + len; + const char *p; + int *lineno; + int num = 0, incomplete = 0; - if (len && buf[len-1] != '\n') - incomplete++; /* incomplete line at the end */ - while (len--) { - if (bol) { - sb->lineno = xrealloc(sb->lineno, - sizeof(int *) * (num + 1)); - sb->lineno[num] = buf - sb->final_buf; - bol = 0; - } - if (*buf++ == '\n') { + for (p = buf;;) { + p = memchr(p, '\n', end - p); + if (p) { + p++; num++; - bol = 1; + continue; } + break; } - sb->lineno = xrealloc(sb->lineno, - sizeof(int *) * (num + incomplete + 1)); - sb->lineno[num + incomplete] = buf - sb->final_buf; + + if (len && end[-1] != '\n') + incomplete++; /* incomplete line at the end */ + + sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1)); + lineno = sb->lineno; + + *lineno++ = 0; + for (p = buf;;) { + p = memchr(p, '\n', end - p); + if (p) { + p++; + *lineno++ = p - buf; + continue; + } + break; + } + + if (incomplete) + *lineno++ = len; + sb->num_lines = num + incomplete; return sb->num_lines; } @@ -2095,7 +2095,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, if (strbuf_read(&buf, 0, 0) < 0) die_errno("failed to read from stdin"); } - convert_to_git(path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); @@ -2338,7 +2337,14 @@ parse_done: blame_date_width = sizeof("2006-10-19"); break; case DATE_RELATIVE: - /* "normal" is used as the fallback for "relative" */ + /* TRANSLATORS: This string is used to tell us the maximum + display width for a relative timestamp in "git blame" + output. For C locale, "4 years, 11 months ago", which + takes 22 places, is the longest among various forms of + relative timestamps, but your language may need more or + fewer display columns. */ + blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */ + break; case DATE_LOCAL: case DATE_NORMAL: blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); @@ -2502,8 +2508,6 @@ parse_done: ent->suspect = o; ent->s_lno = bottom; ent->next = next; - if (next) - next->prev = ent; origin_incref(o); } origin_decref(o); diff --git a/builtin/branch.c b/builtin/branch.c index b4d771673e..652b1d2d14 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -315,7 +315,7 @@ static int match_patterns(const char **pattern, const char *refname) if (!*pattern) return 1; /* no pattern always matches */ while (*pattern) { - if (!fnmatch(*pattern, refname, 0)) + if (!wildmatch(*pattern, refname, 0, NULL)) return 1; pattern++; } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index d5a93e0e91..707330499f 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -269,6 +269,8 @@ static int batch_objects(struct batch_options *opt) { struct strbuf buf = STRBUF_INIT; struct expand_data data; + int save_warning; + int retval = 0; if (!opt->format) opt->format = "%(objectname) %(objecttype) %(objectsize)"; @@ -297,11 +299,10 @@ static int batch_objects(struct batch_options *opt) * warn) ends up dwarfing the actual cost of the object lookups * themselves. We can work around it by just turning off the warning. */ + save_warning = warn_on_object_refname_ambiguity; warn_on_object_refname_ambiguity = 0; while (strbuf_getline(&buf, stdin, '\n') != EOF) { - int error; - if (data.split_on_whitespace) { /* * Split at first whitespace, tying off the beginning @@ -316,12 +317,14 @@ static int batch_objects(struct batch_options *opt) data.rest = p; } - error = batch_one_object(buf.buf, opt, &data); - if (error) - return error; + retval = batch_one_object(buf.buf, opt, &data); + if (retval) + break; } - return 0; + strbuf_release(&buf); + warn_on_object_refname_ambiguity = save_warning; + return retval; } static const char * const cat_file_usage[] = { diff --git a/builtin/check-attr.c b/builtin/check-attr.c index e9af7b2bfb..5600ec3f61 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -102,6 +102,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) struct git_attr_check *check; int cnt, i, doubledash, filei; + if (!is_bare_repository()) + setup_work_tree(); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, check_attr_options, diff --git a/builtin/checkout.c b/builtin/checkout.c index 5df3837e31..07cf555309 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -53,10 +53,10 @@ struct checkout_opts { static int post_checkout_hook(struct commit *old, struct commit *new, int changed) { - return run_hook(NULL, "post-checkout", - sha1_to_hex(old ? old->object.sha1 : null_sha1), - sha1_to_hex(new ? new->object.sha1 : null_sha1), - changed ? "1" : "0", NULL); + return run_hook_le(NULL, "post-checkout", + sha1_to_hex(old ? old->object.sha1 : null_sha1), + sha1_to_hex(new ? new->object.sha1 : null_sha1), + changed ? "1" : "0", NULL); /* "new" can be NULL when checking out from the index before a commit exists. */ @@ -297,8 +297,7 @@ static int checkout_paths(const struct checkout_opts *opts, * match_pathspec() for _all_ entries when * opts->source_tree != NULL. */ - if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce), - 0, ps_matched)) + if (ce_path_match(ce, &opts->pathspec, ps_matched)) ce->ce_flags |= CE_MATCHED; } @@ -896,7 +895,7 @@ static int parse_branchname_arg(int argc, const char **argv, * between A and B, A...B names that merge base. * * (b) If <something> is _not_ a commit, either "--" is present - * or <something> is not a path, no -t nor -b was given, and + * or <something> is not a path, no -t or -b was given, and * and there is a tracking branch whose name is <something> * in one and only one remote, then this is a short-hand to * fork local <something> from that remote-tracking branch. @@ -1096,7 +1095,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")), OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"), BRANCH_TRACK_EXPLICIT), - OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")), + OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")), OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"), 2), OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), diff --git a/builtin/clean.c b/builtin/clean.c index 2f26297142..9a9151575d 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -154,7 +154,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, DIR *dir; struct strbuf quoted = STRBUF_INIT; struct dirent *e; - int res = 0, ret = 0, gone = 1, original_len = path->len, len, i; + int res = 0, ret = 0, gone = 1, original_len = path->len, len; unsigned char submodule_head[20]; struct string_list dels = STRING_LIST_INIT_DUP; @@ -242,6 +242,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, } if (!*dir_gone && !quiet) { + int i; for (i = 0; i < dels.nr; i++) printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string); } @@ -903,11 +904,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (!interactive && !dry_run && !force) { if (config_set) - die(_("clean.requireForce set to true and neither -i, -n nor -f given; " + die(_("clean.requireForce set to true and neither -i, -n, nor -f given; " "refusing to clean")); else - die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; " - "refusing to clean")); + die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;" + " refusing to clean")); } if (force > 1) @@ -933,48 +934,28 @@ int cmd_clean(int argc, const char **argv, const char *prefix) for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; - int len, pos; int matches = 0; - const struct cache_entry *ce; struct stat st; const char *rel; - /* - * Remove the '/' at the end that directory - * walking adds for directory entries. - */ - len = ent->len; - if (len && ent->name[len-1] == '/') - len--; - pos = cache_name_pos(ent->name, len); - if (0 <= pos) - continue; /* exact match */ - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == len && - !memcmp(ce->name, ent->name, len)) - continue; /* Yup, this one exists unmerged */ - } + if (!cache_name_is_other(ent->name, ent->len)) + continue; if (lstat(ent->name, &st)) die_errno("Cannot lstat '%s'", ent->name); if (pathspec.nr) - matches = match_pathspec_depth(&pathspec, ent->name, - len, 0, NULL); + matches = dir_path_match(ent, &pathspec, 0, NULL); - if (S_ISDIR(st.st_mode)) { - if (remove_directories || (matches == MATCHED_EXACTLY)) { - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } - } else { - if (pathspec.nr && !matches) - continue; - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } + if (pathspec.nr && !matches) + continue; + + if (S_ISDIR(st.st_mode) && !remove_directories && + matches != MATCHED_EXACTLY) + continue; + + rel = relative_path(ent->name, prefix, &buf); + string_list_append(&del_list, rel); } if (interactive && del_list.nr > 0) diff --git a/builtin/clone.c b/builtin/clone.c index 43e772ccdb..9b3c04d914 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -660,8 +660,8 @@ static int checkout(void) commit_locked_index(lock_file)) die(_("unable to write new index file")); - err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1), - sha1_to_hex(sha1), "1", NULL); + err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), + sha1_to_hex(sha1), "1", NULL); if (!err && option_recursive) err = run_command_v_opt(argv_submodule, RUN_GIT_CMD); diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index f641ff2a89..987a4c3d73 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -12,6 +12,8 @@ static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog"; +static const char *sign_commit; + static void new_parent(struct commit *parent, struct commit_list **parents_p) { unsigned char *sha1 = parent->object.sha1; @@ -31,6 +33,10 @@ static int commit_tree_config(const char *var, const char *value, void *cb) int status = git_gpg_config(var, value, NULL); if (status) return status; + if (!strcmp(var, "commit.gpgsign")) { + sign_commit = git_config_bool(var, value) ? "" : NULL; + return 0; + } return git_default_config(var, value, cb); } @@ -41,7 +47,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; struct strbuf buffer = STRBUF_INIT; - const char *sign_commit = NULL; git_config(commit_tree_config, NULL); @@ -66,6 +71,11 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) continue; } + if (!strcmp(arg, "--no-gpg-sign")) { + sign_commit = NULL; + continue; + } + if (!strcmp(arg, "-m")) { if (argc <= ++i) usage(commit_tree_usage); diff --git a/builtin/commit.c b/builtin/commit.c index 3767478c6d..65c069d2cb 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -113,6 +113,7 @@ static char *sign_commit; static enum { CLEANUP_SPACE, CLEANUP_NONE, + CLEANUP_SCISSORS, CLEANUP_ALL } cleanup_mode; static const char *cleanup_arg; @@ -234,7 +235,7 @@ static int list_paths(struct string_list *list, const char *with_tree, if (ce->ce_flags & CE_UPDATE) continue; - if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m)) + if (!ce_path_match(ce, pattern, m)) continue; item = string_list_insert(list, ce->name); if (ce_skip_worktree(ce)) @@ -307,7 +308,6 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int fd; struct string_list partial; struct pathspec pathspec; - char *old_index_env = NULL; int refresh_flags = REFRESH_QUIET; if (is_status) @@ -320,6 +320,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, die(_("index file corrupt")); if (interactive) { + char *old_index_env = NULL; fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); @@ -600,19 +601,17 @@ static int prepare_to_commit(const char *index_file, const char *prefix, { struct stat statbuf; struct strbuf committer_ident = STRBUF_INIT; - int commitable, saved_color_setting; + int commitable; struct strbuf sb = STRBUF_INIT; - char *buffer; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; - int ident_shown = 0; int clean_message_contents = (cleanup_mode != CLEANUP_NONE); int old_display_comment_prefix; /* This checks and barfs if author is badly specified */ determine_author_info(author_ident); - if (!no_verify && run_hook(index_file, "pre-commit", NULL)) + if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL)) return 0; if (squash_message) { @@ -649,10 +648,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix, logfile); hook_arg1 = "message"; } else if (use_message) { + char *buffer; buffer = strstr(use_message_buffer, "\n\n"); - if (!use_editor && (!buffer || buffer[2] == '\0')) - die(_("commit has empty message")); - strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); + if (buffer) + strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); hook_arg1 = "commit"; hook_arg2 = use_message; } else if (fixup_message) { @@ -753,8 +752,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix, /* This checks if committer ident is explicitly given */ strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT)); if (use_editor && include_status) { + int ident_shown = 0; + int saved_color_setting; char *ai_tmp, *ci_tmp; - if (whence != FROM_COMMIT) + if (whence != FROM_COMMIT) { + if (cleanup_mode == CLEANUP_SCISSORS) + wt_status_add_cut_line(s->fp); status_printf_ln(s, GIT_COLOR_NORMAL, whence == FROM_MERGE ? _("\n" @@ -770,6 +773,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, git_path(whence == FROM_MERGE ? "MERGE_HEAD" : "CHERRY_PICK_HEAD")); + } fprintf(s->fp, "\n"); if (cleanup_mode == CLEANUP_ALL) @@ -777,6 +781,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, _("Please enter the commit message for your changes." " Lines starting\nwith '%c' will be ignored, and an empty" " message aborts the commit.\n"), comment_line_char); + else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT) + wt_status_add_cut_line(s->fp); else /* CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." @@ -866,8 +872,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, return 0; } - if (run_hook(index_file, "prepare-commit-msg", - git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) + if (run_commit_hook(use_editor, index_file, "prepare-commit-msg", + git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) return 0; if (use_editor) { @@ -883,7 +889,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } if (!no_verify && - run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) { + run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) { return 0; } @@ -1067,8 +1073,6 @@ static int parse_and_validate_options(int argc, const char *argv[], use_editor = 0; if (0 <= edit_flag) use_editor = edit_flag; - if (!use_editor) - setenv("GIT_EDITOR", ":", 1); /* Sanity check options */ if (amend && !current_head) @@ -1123,7 +1127,7 @@ static int parse_and_validate_options(int argc, const char *argv[], if (argc == 0 && only && amend) only_include_assumed = _("Clever... amending the last one with dirty index."); if (argc > 0 && !also && !only) - only_include_assumed = _("Explicit paths specified without -i nor -o; assuming --only paths..."); + only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths..."); if (!cleanup_arg || !strcmp(cleanup_arg, "default")) cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) @@ -1132,6 +1136,8 @@ static int parse_and_validate_options(int argc, const char *argv[], cleanup_mode = CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "strip")) cleanup_mode = CLEANUP_ALL; + else if (!strcmp(cleanup_arg, "scissors")) + cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE; else die(_("Invalid cleanup mode %s"), cleanup_arg); @@ -1406,6 +1412,10 @@ static int git_commit_config(const char *k, const char *v, void *cb) } if (!strcmp(k, "commit.cleanup")) return git_config_string(&cleanup_arg, k, v); + if (!strcmp(k, "commit.gpgsign")) { + sign_commit = git_config_bool(k, v) ? "" : NULL; + return 0; + } status = git_gpg_config(k, v, NULL); if (status) @@ -1445,6 +1455,29 @@ static int run_rewrite_hook(const unsigned char *oldsha1, return finish_command(&proc); } +int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...) +{ + const char *hook_env[3] = { NULL }; + char index[PATH_MAX]; + va_list args; + int ret; + + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + hook_env[0] = index; + + /* + * Let the hook know that no editor will be launched. + */ + if (!editor_is_used) + hook_env[1] = "GIT_EDITOR=:"; + + va_start(args, name); + ret = run_hook_ve(hook_env, name, args); + va_end(args); + + return ret; +} + int cmd_commit(int argc, const char **argv, const char *prefix) { static struct wt_status s; @@ -1467,7 +1500,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")), OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), + { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, /* end commit message options */ @@ -1510,7 +1543,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; - int allow_fast_forward = 1; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; @@ -1558,6 +1590,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; + int allow_fast_forward = 1; if (!reflog_msg) reflog_msg = "commit (merge)"; @@ -1600,8 +1633,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) die(_("could not read commit message: %s"), strerror(saved_errno)); } - /* Truncate the message just before the diff, if any. */ - if (verbose) + if (verbose || /* Truncate the message just before the diff, if any. */ + cleanup_mode == CLEANUP_SCISSORS) wt_status_truncate_message_at_cut_line(&sb); if (cleanup_mode != CLEANUP_NONE) @@ -1669,7 +1702,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); - run_hook(get_index_file(), "post-commit", NULL); + run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { struct notes_rewrite_cfg *cfg; cfg = init_copy_notes_for_rewrite("amend"); diff --git a/builtin/config.c b/builtin/config.c index 92ebf23f0a..5677c942b6 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -21,8 +21,7 @@ static char key_delim = ' '; static char term = '\n'; static int use_global_config, use_system_config, use_local_config; -static const char *given_config_file; -static const char *given_config_blob; +static struct git_config_source given_config_source; static int actions, types; static const char *get_color_slot, *get_colorbool_slot; static int end_null; @@ -55,8 +54,8 @@ static struct option builtin_config_options[] = { OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), - OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")), - OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")), + OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), + OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), @@ -224,8 +223,7 @@ static int get_value(const char *key_, const char *regex_) } git_config_with_options(collect_config, &values, - given_config_file, given_config_blob, - respect_includes); + &given_config_source, respect_includes); ret = !values.nr; @@ -309,8 +307,7 @@ static void get_color(const char *def_color) get_color_found = 0; parsed_color[0] = '\0'; git_config_with_options(git_get_color_config, NULL, - given_config_file, given_config_blob, - respect_includes); + &given_config_source, respect_includes); if (!get_color_found && def_color) color_parse(def_color, "command line", parsed_color); @@ -339,8 +336,7 @@ static int get_colorbool(int print) get_diff_color_found = -1; get_color_ui_found = -1; git_config_with_options(git_get_colorbool_config, NULL, - given_config_file, given_config_blob, - respect_includes); + &given_config_source, respect_includes); if (get_colorbool_found < 0) { if (!strcmp(get_colorbool_slot, "color.diff")) @@ -362,9 +358,12 @@ static int get_colorbool(int print) return get_colorbool_found ? 0 : 1; } -static void check_blob_write(void) +static void check_write(void) { - if (given_config_blob) + if (given_config_source.use_stdin) + die("writing to stdin is not supported"); + + if (given_config_source.blob) die("writing config blobs is not supported"); } @@ -435,7 +434,7 @@ static int get_urlmatch(const char *var, const char *url) } git_config_with_options(urlmatch_config_entry, &config, - given_config_file, NULL, respect_includes); + &given_config_source, respect_includes); for_each_string_list_item(item, &values) { struct urlmatch_current_candidate_value *matched = item->util; @@ -464,18 +463,24 @@ int cmd_config(int argc, const char **argv, const char *prefix) int nongit = !startup_info->have_repository; char *value; - given_config_file = getenv(CONFIG_ENVIRONMENT); + given_config_source.file = getenv(CONFIG_ENVIRONMENT); argc = parse_options(argc, argv, prefix, builtin_config_options, builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (use_global_config + use_system_config + use_local_config + - !!given_config_file + !!given_config_blob > 1) { + !!given_config_source.file + !!given_config_source.blob > 1) { error("only one config file at a time."); usage_with_options(builtin_config_usage, builtin_config_options); } + if (given_config_source.file && + !strcmp(given_config_source.file, "-")) { + given_config_source.file = NULL; + given_config_source.use_stdin = 1; + } + if (use_global_config) { char *user_config = NULL; char *xdg_config = NULL; @@ -493,24 +498,24 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (access_or_warn(user_config, R_OK, 0) && xdg_config && !access_or_warn(xdg_config, R_OK, 0)) - given_config_file = xdg_config; + given_config_source.file = xdg_config; else - given_config_file = user_config; + given_config_source.file = user_config; } else if (use_system_config) - given_config_file = git_etc_gitconfig(); + given_config_source.file = git_etc_gitconfig(); else if (use_local_config) - given_config_file = git_pathdup("config"); - else if (given_config_file) { - if (!is_absolute_path(given_config_file) && prefix) - given_config_file = + given_config_source.file = git_pathdup("config"); + else if (given_config_source.file) { + if (!is_absolute_path(given_config_source.file) && prefix) + given_config_source.file = xstrdup(prefix_filename(prefix, strlen(prefix), - given_config_file)); + given_config_source.file)); } if (respect_includes == -1) - respect_includes = !given_config_file; + respect_includes = !given_config_source.file; if (end_null) { term = '\0'; @@ -549,57 +554,58 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (git_config_with_options(show_all_config, NULL, - given_config_file, - given_config_blob, + &given_config_source, respect_includes) < 0) { - if (given_config_file) + if (given_config_source.file) die_errno("unable to read config file '%s'", - given_config_file); + given_config_source.file); else die("error processing config file(s)"); } } else if (actions == ACTION_EDIT) { check_argc(argc, 0, 0); - if (!given_config_file && nongit) + if (!given_config_source.file && nongit) die("not in a git directory"); - if (given_config_blob) + if (given_config_source.use_stdin) + die("editing stdin is not supported"); + if (given_config_source.blob) die("editing blobs is not supported"); git_config(git_default_config, NULL); - launch_editor(given_config_file ? - given_config_file : git_path("config"), + launch_editor(given_config_source.file ? + given_config_source.file : git_path("config"), NULL, NULL); } else if (actions == ACTION_SET) { int ret; - check_blob_write(); + check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - ret = git_config_set_in_file(given_config_file, argv[0], value); + ret = git_config_set_in_file(given_config_source.file, argv[0], value); if (ret == CONFIG_NOTHING_SET) error("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s.", argv[0]); return ret; } else if (actions == ACTION_SET_ALL) { - check_blob_write(); + check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], value, argv[2], 0); } else if (actions == ACTION_ADD) { - check_blob_write(); + check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], value, "^$", 0); } else if (actions == ACTION_REPLACE_ALL) { - check_blob_write(); + check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], value, argv[2], 1); } else if (actions == ACTION_GET) { @@ -623,26 +629,26 @@ int cmd_config(int argc, const char **argv, const char *prefix) return get_urlmatch(argv[0], argv[1]); } else if (actions == ACTION_UNSET) { - check_blob_write(); + check_write(); check_argc(argc, 1, 2); if (argc == 2) - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], NULL, argv[1], 0); else - return git_config_set_in_file(given_config_file, + return git_config_set_in_file(given_config_source.file, argv[0], NULL); } else if (actions == ACTION_UNSET_ALL) { - check_blob_write(); + check_write(); check_argc(argc, 1, 2); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], NULL, argv[1], 1); } else if (actions == ACTION_RENAME_SECTION) { int ret; - check_blob_write(); + check_write(); check_argc(argc, 2, 2); - ret = git_config_rename_section_in_file(given_config_file, + ret = git_config_rename_section_in_file(given_config_source.file, argv[0], argv[1]); if (ret < 0) return ret; @@ -651,9 +657,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) } else if (actions == ACTION_REMOVE_SECTION) { int ret; - check_blob_write(); + check_write(); check_argc(argc, 1, 1); - ret = git_config_rename_section_in_file(given_config_file, + ret = git_config_rename_section_in_file(given_config_source.file, argv[0], NULL); if (ret < 0) return ret; diff --git a/builtin/describe.c b/builtin/describe.c index 7db43dae1b..24d740c8b1 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -6,7 +6,7 @@ #include "exec_cmd.h" #include "parse-options.h" #include "diff.h" -#include "hash.h" +#include "hashmap.h" #include "argv-array.h" #define SEEN (1u << 0) @@ -25,7 +25,7 @@ static int longformat; static int first_parent; static int abbrev = -1; /* unspecified */ static int max_candidates = 10; -static struct hash_table names; +static struct hashmap names; static int have_util; static const char *pattern; static int always; @@ -37,7 +37,7 @@ static const char *diff_index_args[] = { }; struct commit_name { - struct commit_name *next; + struct hashmap_entry entry; unsigned char peeled[20]; struct tag *tag; unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ @@ -50,6 +50,12 @@ static const char *prio_names[] = { "head", "lightweight", "annotated", }; +static int commit_name_cmp(const struct commit_name *cn1, + const struct commit_name *cn2, const void *peeled) +{ + return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled); +} + static inline unsigned int hash_sha1(const unsigned char *sha1) { unsigned int hash; @@ -59,21 +65,9 @@ static inline unsigned int hash_sha1(const unsigned char *sha1) static inline struct commit_name *find_commit_name(const unsigned char *peeled) { - struct commit_name *n = lookup_hash(hash_sha1(peeled), &names); - while (n && !!hashcmp(peeled, n->peeled)) - n = n->next; - return n; -} - -static int set_util(void *chain, void *data) -{ - struct commit_name *n; - for (n = chain; n; n = n->next) { - struct commit *c = lookup_commit_reference_gently(n->peeled, 1); - if (c) - c->util = n; - } - return 0; + struct commit_name key; + hashmap_entry_init(&key, hash_sha1(peeled)); + return hashmap_get(&names, &key, peeled); } static int replace_name(struct commit_name *e, @@ -118,16 +112,10 @@ static void add_to_known_names(const char *path, struct tag *tag = NULL; if (replace_name(e, prio, sha1, &tag)) { if (!e) { - void **pos; e = xmalloc(sizeof(struct commit_name)); hashcpy(e->peeled, peeled); - pos = insert_hash(hash_sha1(peeled), e, &names); - if (pos) { - e->next = *pos; - *pos = e; - } else { - e->next = NULL; - } + hashmap_entry_init(e, hash_sha1(peeled)); + hashmap_add(&names, e); e->path = NULL; } e->tag = tag; @@ -150,7 +138,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void return 0; /* Accept only tags that match the pattern, if given */ - if (pattern && (!is_tag || fnmatch(pattern, path + 10, 0))) + if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL))) return 0; /* Is it annotated? */ @@ -292,7 +280,14 @@ static void describe(const char *arg, int last_one) fprintf(stderr, _("searching to describe %s\n"), arg); if (!have_util) { - for_each_hash(&names, set_util, NULL); + struct hashmap_iter iter; + struct commit *c; + struct commit_name *n = hashmap_iter_first(&names, &iter); + for (; n; n = hashmap_iter_next(&iter)) { + c = lookup_commit_reference_gently(n->peeled, 1); + if (c) + c->util = n; + } have_util = 1; } @@ -463,9 +458,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) return cmd_name_rev(args.argc, args.argv, prefix); } - init_hash(&names); + hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0); for_each_rawref(get_name, NULL); - if (!names.nr && !always) + if (!names.size && !always) die(_("No names found, cannot describe anything.")); if (argc == 0) { diff --git a/builtin/fetch.c b/builtin/fetch.c index 025bc3e38d..55f457c04f 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1026,7 +1026,6 @@ static int fetch_multiple(struct string_list *list) static int fetch_one(struct remote *remote, int argc, const char **argv) { - int i; static const char **refs = NULL; struct refspec *refspec; int ref_nr = 0; @@ -1050,6 +1049,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) if (argc > 0) { int j = 0; + int i; refs = xcalloc(argc + 1, sizeof(const char *)); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "tag")) { diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 51798b48b5..3e1d5c3334 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -864,7 +864,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f refname[plen] == '/' || p[plen-1] == '/')) break; - if (!fnmatch(p, refname, FNM_PATHNAME)) + if (!wildmatch(p, refname, WM_PATHNAME, NULL)) break; } if (!*pattern) diff --git a/builtin/fsck.c b/builtin/fsck.c index 1affdd5e92..fc150c8821 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -144,7 +144,7 @@ static int traverse_reachable(void) unsigned int nr = 0; int result = 0; if (show_progress) - progress = start_progress_delay("Checking connectivity", 0, 0, 2); + progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); while (pending.nr) { struct object_array_entry *entry; struct object *obj; @@ -541,7 +541,7 @@ static void fsck_object_dir(const char *path) fprintf(stderr, "Checking object directory\n"); if (show_progress) - progress = start_progress("Checking object directories", 256); + progress = start_progress(_("Checking object directories"), 256); for (i = 0; i < 256; i++) { static char dir[4096]; sprintf(dir, "%s/%02x", path, i); @@ -630,7 +630,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) struct alternate_object_database *alt; errors_found = 0; - read_replace_refs = 0; + check_replace_refs = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); @@ -670,7 +670,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) total += p->num_objects; } - progress = start_progress("Checking objects", total); + progress = start_progress(_("Checking objects"), total); } for (p = packed_git; p; p = p->next) { /* verify gives error messages itself */ diff --git a/builtin/gc.c b/builtin/gc.c index 4588af70df..8d219d8c42 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -27,6 +27,7 @@ static const char * const builtin_gc_usage[] = { static int pack_refs = 1; static int prune_reflogs = 1; +static int aggressive_depth = 250; static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; @@ -67,6 +68,10 @@ static int gc_config(const char *var, const char *value, void *cb) aggressive_window = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.aggressivedepth")) { + aggressive_depth = git_config_int(var, value); + return 0; + } if (!strcmp(var, "gc.auto")) { gc_auto_threshold = git_config_int(var, value); return 0; @@ -185,7 +190,7 @@ static int need_to_gc(void) else if (!too_many_loose_objects()) return 0; - if (run_hook(NULL, "pre-auto-gc", NULL)) + if (run_hook_le(NULL, "pre-auto-gc", NULL)) return 0; return 1; } @@ -194,13 +199,12 @@ static int need_to_gc(void) static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { static struct lock_file lock; - static char locking_host[128]; char my_host[128]; struct strbuf sb = STRBUF_INIT; struct stat st; uintmax_t pid; FILE *fp; - int fd, should_exit; + int fd; if (pidfile) /* already locked */ @@ -212,6 +216,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) fd = hold_lock_file_for_update(&lock, git_path("gc.pid"), LOCK_DIE_ON_ERROR); if (!force) { + static char locking_host[128]; + int should_exit; fp = fopen(git_path("gc.pid"), "r"); memset(locking_host, 0, sizeof(locking_host)); should_exit = @@ -307,7 +313,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (aggressive) { argv_array_push(&repack, "-f"); - argv_array_push(&repack, "--depth=250"); + if (aggressive_depth > 0) + argv_array_pushf(&repack, "--depth=%d", aggressive_depth); if (aggressive_window > 0) argv_array_pushf(&repack, "--window=%d", aggressive_window); } diff --git a/builtin/grep.c b/builtin/grep.c index 63f86032d9..69ac2d8797 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -379,7 +379,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int const struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) + if (!ce_path_match(ce, pathspec, NULL)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -524,9 +524,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, fill_directory(&dir, pathspec); for (i = 0; i < dir.nr; i++) { - const char *name = dir.entries[i]->name; - int namelen = strlen(name); - if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL)) + if (!dir_path_match(dir.entries[i], pathspec, 0, NULL)) continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 2f37a38fbc..b9f6e12c0e 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1291,7 +1291,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'"), - keep_name); + keep_name ? keep_name : name); } else { if (keep_msg_len > 0) { write_or_die(keep_fd, keep_msg, keep_msg_len); @@ -1299,7 +1299,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } if (close(keep_fd) != 0) die_errno(_("cannot close written keep file '%s'"), - keep_name); + keep_name ? keep_name : name); report = "keep"; } } @@ -1502,7 +1502,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage(index_pack_usage); - read_replace_refs = 0; + check_replace_refs = 0; reset_pack_idx_option(&opts); git_config(git_index_pack_config, &opts); diff --git a/builtin/init-db.c b/builtin/init-db.c index b3f03cf0d6..56f85e239a 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -412,11 +412,9 @@ int init_db(const char *template_dir, unsigned int flags) if (!(flags & INIT_DB_QUIET)) { int len = strlen(git_dir); - /* - * TRANSLATORS: The first '%s' is either "Reinitialized - * existing" or "Initialized empty", the second " shared" or - * "", and the last '%s%s' is the verbatim directory name. - */ + /* TRANSLATORS: The first '%s' is either "Reinitialized + existing" or "Initialized empty", the second " shared" or + "", and the last '%s%s' is the verbatim directory name. */ printf(_("%s%s Git repository in %s%s\n"), reinit ? _("Reinitialized existing") : _("Initialized empty"), shared_repository ? _(" shared") : "", @@ -515,13 +513,14 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) saved = shared_repository; shared_repository = 0; switch (safe_create_leading_directories_const(argv[0])) { - case -3: + case SCLD_OK: + case SCLD_PERMS: + break; + case SCLD_EXISTS: errno = EEXIST; /* fallthru */ - case -1: - die_errno(_("cannot mkdir %s"), argv[0]); - break; default: + die_errno(_("cannot mkdir %s"), argv[0]); break; } shared_repository = saved; diff --git a/builtin/log.c b/builtin/log.c index b97373da3b..3b6a6bbadd 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -158,13 +158,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (rev->show_notes) init_display_notes(&rev->notes_opt); - if (rev->diffopt.pickaxe || rev->diffopt.filter) + if (rev->diffopt.pickaxe || rev->diffopt.filter || + DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) rev->always_show_header = 0; - if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { - rev->always_show_header = 0; - if (rev->diffopt.pathspec.nr != 1) - usage("git logs can only follow renames on one pathname at a time"); - } if (source) rev->show_source = 1; @@ -503,7 +499,7 @@ static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt * /* There was no "-m" on the command line */ rev->ignore_merges = 0; if (!rev->first_parent_only && !rev->combine_merges) { - /* No "--first-parent", "-c", nor "--cc" */ + /* No "--first-parent", "-c", or "--cc" */ rev->combine_merges = 1; rev->dense_combined_merges = 1; } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index e1cf6d8547..47c38808a2 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -64,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (len >= ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); - if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched)) + if (!dir_path_match(ent, &pathspec, len, ps_matched)) return; fputs(tag, stdout); @@ -139,7 +139,9 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git ls-files: internal error - cache entry not superset of prefix"); - if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched)) + if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce), + len, ps_matched, + S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode))) return; if (tag && *tag && show_valid_bit && @@ -195,7 +197,8 @@ static void show_ru_info(void) len = strlen(path); if (len < max_prefix_len) continue; /* outside of the prefix */ - if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched)) + if (!match_pathspec(&pathspec, path, len, + max_prefix_len, ps_matched, 0)) continue; /* uninterested */ for (i = 0; i < 3; i++) { if (!ui->mode[i]) diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 39e5144b9e..3e9eefb091 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -22,7 +22,7 @@ static int tail_match(const char **pattern, const char *path) if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf)) return error("insanely long ref %.*s...", 20, path); while ((p = *(pattern++)) != NULL) { - if (!fnmatch(p, pathbuf, 0)) + if (!wildmatch(p, pathbuf, 0, NULL)) return 1; } return 0; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 65ec931846..51184dfa2e 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -171,7 +171,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * show_recursive() rolls its own matching code and is * generally ignorant of 'struct pathspec'. The magic mask * cannot be lifted until it is converted to use - * match_pathspec_depth() or tree_entry_interesting() + * match_pathspec() or tree_entry_interesting() */ parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE, PATHSPEC_PREFER_CWD, diff --git a/builtin/merge.c b/builtin/merge.c index 4941a6c36a..66d8843301 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -220,7 +220,7 @@ static struct option builtin_merge_options[] = { OPT_BOOL(0, "abort", &abort_current_merge, N_("abort the current in-progress merge")), OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), + { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_END() @@ -367,7 +367,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead sha1_to_hex(commit->object.sha1)); pretty_print_commit(&ctx, commit, &out); } - if (write(fd, out.buf, out.len) < 0) + if (write_in_full(fd, out.buf, out.len) != out.len) die_errno(_("Writing SQUASH_MSG")); if (close(fd)) die_errno(_("Finishing SQUASH_MSG")); @@ -421,7 +421,7 @@ static void finish(struct commit *head_commit, } /* Run a post-merge hook */ - run_hook(NULL, "post-merge", squash ? "1" : "0", NULL); + run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL); strbuf_release(&reflog_message); } @@ -597,6 +597,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) } else if (!strcmp(k, "merge.defaulttoupstream")) { default_to_upstream = git_config_bool(k, v); return 0; + } else if (!strcmp(k, "commit.gpgsign")) { + sign_commit = git_config_bool(k, v) ? "" : NULL; + return 0; } status = fmt_merge_msg_config(k, v, cb); @@ -821,8 +824,8 @@ static void prepare_to_commit(struct commit_list *remoteheads) if (0 < option_edit) strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char); write_merge_msg(&msg); - if (run_hook(get_index_file(), "prepare-commit-msg", - git_path("MERGE_MSG"), "merge", NULL, NULL)) + if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", + git_path("MERGE_MSG"), "merge", NULL)) abort_commit(remoteheads, NULL); if (0 < option_edit) { if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) diff --git a/builtin/mktree.c b/builtin/mktree.c index f92ba404ab..a964d6be52 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -23,10 +23,7 @@ static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) if (strchr(path, '/')) die("path %s contains slash", path); - if (alloc <= used) { - alloc = alloc_nr(used); - entries = xrealloc(entries, sizeof(*entries) * alloc); - } + ALLOC_GROW(entries, used + 1, alloc); ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1); ent->mode = mode; ent->len = len; diff --git a/builtin/mv.c b/builtin/mv.c index 21c46d1636..2a7243f52e 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -162,7 +162,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (strncmp(path, src_w_slash, len_w_slash)) break; } - free((char *)src_w_slash); + if (src_w_slash != src) + free((char *)src_w_slash); if (last - first < 1) bad = _("source directory is empty"); @@ -179,6 +180,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix) modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); + submodule_gitfile = xrealloc(submodule_gitfile, + (argc + last - first) + * sizeof(char *)); } dst = add_slash(dst); @@ -192,6 +196,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; + submodule_gitfile[argc + j] = NULL; } argc += last - first; } @@ -227,6 +232,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix) memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); + memmove(modes + i, modes + i + 1, + (argc - i) * sizeof(enum update_mode)); + memmove(submodule_gitfile + i, + submodule_gitfile + i + 1, + (argc - i) * sizeof(char *)); i--; } } else diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 0b21d7e5b2..c824d4ec5f 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -87,7 +87,7 @@ static int subpath_matches(const char *path, const char *filter) const char *subpath = path; while (subpath) { - if (!fnmatch(filter, subpath, 0)) + if (!wildmatch(filter, subpath, 0, NULL)) return subpath - path; subpath = strchr(subpath, '/'); if (subpath) diff --git a/builtin/notes.c b/builtin/notes.c index 2b24d059b5..39c8573cde 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -269,7 +269,11 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) die(_("Failed to resolve '%s' as a valid ref."), arg); if (!(buf = read_sha1_file(object, &type, &len)) || !len) { free(buf); - die(_("Failed to read object '%s'."), arg);; + die(_("Failed to read object '%s'."), arg); + } + if (type != OBJ_BLOB) { + free(buf); + die(_("Cannot read note data from non-blob object '%s'."), arg); } strbuf_add(&(msg->buf), buf, len); free(buf); @@ -935,7 +939,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix) int result; const char *override_notes_ref = NULL; struct option options[] = { - OPT_STRING(0, "ref", &override_notes_ref, N_("notes_ref"), + OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"), N_("use notes from <notes_ref>")), OPT_END() }; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 541667f102..de36c60ca1 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -14,10 +14,12 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "pack-objects.h" #include "progress.h" #include "refs.h" #include "streaming.h" #include "thread-utils.h" +#include "pack-bitmap.h" static const char *pack_usage[] = { N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"), @@ -25,42 +27,15 @@ static const char *pack_usage[] = { NULL }; -struct object_entry { - struct pack_idx_entry idx; - unsigned long size; /* uncompressed size */ - struct packed_git *in_pack; /* already in pack */ - off_t in_pack_offset; - struct object_entry *delta; /* delta base object */ - struct object_entry *delta_child; /* deltified objects who bases me */ - struct object_entry *delta_sibling; /* other deltified objects who - * uses the same base as me - */ - void *delta_data; /* cached delta (uncompressed) */ - unsigned long delta_size; /* delta data size (uncompressed) */ - unsigned long z_delta_size; /* delta data size (compressed) */ - enum object_type type; - enum object_type in_pack_type; /* could be delta */ - uint32_t hash; /* name hint hash */ - unsigned char in_pack_header_size; - unsigned preferred_base:1; /* - * we do not pack this, but is available - * to be used as the base object to delta - * objects against. - */ - unsigned no_try_delta:1; - unsigned tagged:1; /* near the very tip of refs */ - unsigned filled:1; /* assigned write-order */ -}; - /* - * Objects we are going to pack are collected in objects array (dynamically - * expanded). nr_objects & nr_alloc controls this array. They are stored - * in the order we see -- typically rev-list --objects order that gives us - * nice "minimum seek" order. + * Objects we are going to pack are collected in the `to_pack` structure. + * It contains an array (dynamically expanded) of the object data, and a map + * that can resolve SHA1s to their position in the array. */ -static struct object_entry *objects; +static struct packing_data to_pack; + static struct pack_idx_entry **written_list; -static uint32_t nr_objects, nr_alloc, nr_result, nr_written; +static uint32_t nr_result, nr_written; static int non_empty; static int reuse_delta = 1, reuse_object = 1; @@ -83,6 +58,14 @@ static struct progress *progress_state; static int pack_compression_level = Z_DEFAULT_COMPRESSION; static int pack_compression_seen; +static struct packed_git *reuse_packfile; +static uint32_t reuse_packfile_objects; +static off_t reuse_packfile_offset; + +static int use_bitmap_index = 1; +static int write_bitmap_index; +static uint16_t write_bitmap_options; + static unsigned long delta_cache_size = 0; static unsigned long max_delta_cache_size = 256 * 1024 * 1024; static unsigned long cache_max_small_delta_size = 1000; @@ -90,20 +73,28 @@ static unsigned long cache_max_small_delta_size = 1000; static unsigned long window_memory_limit = 0; /* - * The object names in objects array are hashed with this hashtable, - * to help looking up the entry by object name. - * This hashtable is built after all the objects are seen. - */ -static int *object_ix; -static int object_ix_hashsz; -static struct object_entry *locate_object_entry(const unsigned char *sha1); - -/* * stats */ static uint32_t written, written_delta; static uint32_t reused, reused_delta; +/* + * Indexed commits + */ +static struct commit **indexed_commits; +static unsigned int indexed_commits_nr; +static unsigned int indexed_commits_alloc; + +static void index_commit_for_bitmap(struct commit *commit) +{ + if (indexed_commits_nr >= indexed_commits_alloc) { + indexed_commits_alloc = (indexed_commits_alloc + 32) * 2; + indexed_commits = xrealloc(indexed_commits, + indexed_commits_alloc * sizeof(struct commit *)); + } + + indexed_commits[indexed_commits_nr++] = commit; +} static void *get_delta(struct object_entry *entry) { @@ -553,12 +544,12 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag, void *cb_data) { unsigned char peeled[20]; - struct object_entry *entry = locate_object_entry(sha1); + struct object_entry *entry = packlist_find(&to_pack, sha1, NULL); if (entry) entry->tagged = 1; if (!peel_ref(path, peeled)) { - entry = locate_object_entry(peeled); + entry = packlist_find(&to_pack, peeled, NULL); if (entry) entry->tagged = 1; } @@ -633,9 +624,10 @@ static struct object_entry **compute_write_order(void) { unsigned int i, wo_end, last_untagged; - struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo)); + struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo)); + struct object_entry *objects = to_pack.objects; - for (i = 0; i < nr_objects; i++) { + for (i = 0; i < to_pack.nr_objects; i++) { objects[i].tagged = 0; objects[i].filled = 0; objects[i].delta_child = NULL; @@ -647,7 +639,7 @@ static struct object_entry **compute_write_order(void) * Make sure delta_sibling is sorted in the original * recency order. */ - for (i = nr_objects; i > 0;) { + for (i = to_pack.nr_objects; i > 0;) { struct object_entry *e = &objects[--i]; if (!e->delta) continue; @@ -665,7 +657,7 @@ static struct object_entry **compute_write_order(void) * Give the objects in the original recency order until * we see a tagged tip. */ - for (i = wo_end = 0; i < nr_objects; i++) { + for (i = wo_end = 0; i < to_pack.nr_objects; i++) { if (objects[i].tagged) break; add_to_write_order(wo, &wo_end, &objects[i]); @@ -675,7 +667,7 @@ static struct object_entry **compute_write_order(void) /* * Then fill all the tagged tips. */ - for (; i < nr_objects; i++) { + for (; i < to_pack.nr_objects; i++) { if (objects[i].tagged) add_to_write_order(wo, &wo_end, &objects[i]); } @@ -683,7 +675,7 @@ static struct object_entry **compute_write_order(void) /* * And then all remaining commits and tags. */ - for (i = last_untagged; i < nr_objects; i++) { + for (i = last_untagged; i < to_pack.nr_objects; i++) { if (objects[i].type != OBJ_COMMIT && objects[i].type != OBJ_TAG) continue; @@ -693,7 +685,7 @@ static struct object_entry **compute_write_order(void) /* * And then all the trees. */ - for (i = last_untagged; i < nr_objects; i++) { + for (i = last_untagged; i < to_pack.nr_objects; i++) { if (objects[i].type != OBJ_TREE) continue; add_to_write_order(wo, &wo_end, &objects[i]); @@ -702,17 +694,70 @@ static struct object_entry **compute_write_order(void) /* * Finally all the rest in really tight order */ - for (i = last_untagged; i < nr_objects; i++) { + for (i = last_untagged; i < to_pack.nr_objects; i++) { if (!objects[i].filled) add_family_to_write_order(wo, &wo_end, &objects[i]); } - if (wo_end != nr_objects) - die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects); + if (wo_end != to_pack.nr_objects) + die("ordered %u objects, expected %"PRIu32, wo_end, to_pack.nr_objects); return wo; } +static off_t write_reused_pack(struct sha1file *f) +{ + unsigned char buffer[8192]; + off_t to_write, total; + int fd; + + if (!is_pack_valid(reuse_packfile)) + die("packfile is invalid: %s", reuse_packfile->pack_name); + + fd = git_open_noatime(reuse_packfile->pack_name); + if (fd < 0) + die_errno("unable to open packfile for reuse: %s", + reuse_packfile->pack_name); + + if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1) + die_errno("unable to seek in reused packfile"); + + if (reuse_packfile_offset < 0) + reuse_packfile_offset = reuse_packfile->pack_size - 20; + + total = to_write = reuse_packfile_offset - sizeof(struct pack_header); + + while (to_write) { + int read_pack = xread(fd, buffer, sizeof(buffer)); + + if (read_pack <= 0) + die_errno("unable to read from reused packfile"); + + if (read_pack > to_write) + read_pack = to_write; + + sha1write(f, buffer, read_pack); + to_write -= read_pack; + + /* + * We don't know the actual number of objects written, + * only how many bytes written, how many bytes total, and + * how many objects total. So we can fake it by pretending all + * objects we are writing are the same size. This gives us a + * smooth progress meter, and at the end it matches the true + * answer. + */ + written = reuse_packfile_objects * + (((double)(total - to_write)) / total); + display_progress(progress_state, written); + } + + close(fd); + written = reuse_packfile_objects; + display_progress(progress_state, written); + return reuse_packfile_offset - sizeof(struct pack_header); +} + static void write_pack_file(void) { uint32_t i = 0, j; @@ -723,8 +768,8 @@ static void write_pack_file(void) struct object_entry **write_order; if (progress > pack_to_stdout) - progress_state = start_progress("Writing objects", nr_result); - written_list = xmalloc(nr_objects * sizeof(*written_list)); + progress_state = start_progress(_("Writing objects"), nr_result); + written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list)); write_order = compute_write_order(); do { @@ -737,8 +782,17 @@ static void write_pack_file(void) f = create_tmp_packfile(&pack_tmp_name); offset = write_pack_header(f, nr_remaining); + + if (reuse_packfile) { + off_t packfile_size; + assert(pack_to_stdout); + + packfile_size = write_reused_pack(f); + offset += packfile_size; + } + nr_written = 0; - for (; i < nr_objects; i++) { + for (; i < to_pack.nr_objects; i++) { struct object_entry *e = write_order[i]; if (write_one(f, e, &offset) == WRITE_ONE_BREAK) break; @@ -762,7 +816,7 @@ static void write_pack_file(void) if (!pack_to_stdout) { struct stat st; - char tmpname[PATH_MAX]; + struct strbuf tmpname = STRBUF_INIT; /* * Packs are runtime accessed in their mtime @@ -782,16 +836,35 @@ static void write_pack_file(void) utb.modtime = --last_mtime; if (utime(pack_tmp_name, &utb) < 0) warning("failed utime() on %s: %s", - tmpname, strerror(errno)); + pack_tmp_name, strerror(errno)); } - /* Enough space for "-<sha-1>.pack"? */ - if (sizeof(tmpname) <= strlen(base_name) + 50) - die("pack base name '%s' too long", base_name); - snprintf(tmpname, sizeof(tmpname), "%s-", base_name); - finish_tmp_packfile(tmpname, pack_tmp_name, + strbuf_addf(&tmpname, "%s-", base_name); + + if (write_bitmap_index) { + bitmap_writer_set_checksum(sha1); + bitmap_writer_build_type_index(written_list, nr_written); + } + + finish_tmp_packfile(&tmpname, pack_tmp_name, written_list, nr_written, &pack_idx_opts, sha1); + + if (write_bitmap_index) { + strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1)); + + stop_progress(&progress_state); + + bitmap_writer_show_progress(progress); + bitmap_writer_reuse_bitmaps(&to_pack); + bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1); + bitmap_writer_build(&to_pack); + bitmap_writer_finish(written_list, nr_written, + tmpname.buf, write_bitmap_options); + write_bitmap_index = 0; + } + + strbuf_release(&tmpname); free(pack_tmp_name); puts(sha1_to_hex(sha1)); } @@ -801,7 +874,7 @@ static void write_pack_file(void) written_list[j]->offset = (off_t)-1; } nr_remaining -= nr_written; - } while (nr_remaining && i < nr_objects); + } while (nr_remaining && i < to_pack.nr_objects); free(written_list); free(write_order); @@ -811,73 +884,6 @@ static void write_pack_file(void) written, nr_result); } -static int locate_object_entry_hash(const unsigned char *sha1) -{ - int i; - unsigned int ui; - memcpy(&ui, sha1, sizeof(unsigned int)); - i = ui % object_ix_hashsz; - while (0 < object_ix[i]) { - if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1)) - return i; - if (++i == object_ix_hashsz) - i = 0; - } - return -1 - i; -} - -static struct object_entry *locate_object_entry(const unsigned char *sha1) -{ - int i; - - if (!object_ix_hashsz) - return NULL; - - i = locate_object_entry_hash(sha1); - if (0 <= i) - return &objects[object_ix[i]-1]; - return NULL; -} - -static void rehash_objects(void) -{ - uint32_t i; - struct object_entry *oe; - - object_ix_hashsz = nr_objects * 3; - if (object_ix_hashsz < 1024) - object_ix_hashsz = 1024; - object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz); - memset(object_ix, 0, sizeof(int) * object_ix_hashsz); - for (i = 0, oe = objects; i < nr_objects; i++, oe++) { - int ix = locate_object_entry_hash(oe->idx.sha1); - if (0 <= ix) - continue; - ix = -1 - ix; - object_ix[ix] = i + 1; - } -} - -static uint32_t name_hash(const char *name) -{ - uint32_t c, hash = 0; - - if (!name) - return 0; - - /* - * This effectively just creates a sortable number from the - * last sixteen non-whitespace characters. Last characters - * count "most", so things that end in ".c" sort together. - */ - while ((c = *name++) != 0) { - if (isspace(c)) - continue; - hash = (hash >> 2) + (c << 24); - } - return hash; -} - static void setup_delta_attr_check(struct git_attr_check *check) { static struct git_attr *attr_delta; @@ -900,42 +906,69 @@ static int no_try_delta(const char *path) return 0; } -static int add_object_entry(const unsigned char *sha1, enum object_type type, - const char *name, int exclude) +/* + * When adding an object, check whether we have already added it + * to our packing list. If so, we can skip. However, if we are + * being asked to excludei t, but the previous mention was to include + * it, make sure to adjust its flags and tweak our numbers accordingly. + * + * As an optimization, we pass out the index position where we would have + * found the item, since that saves us from having to look it up again a + * few lines later when we want to add the new entry. + */ +static int have_duplicate_entry(const unsigned char *sha1, + int exclude, + uint32_t *index_pos) { struct object_entry *entry; - struct packed_git *p, *found_pack = NULL; - off_t found_offset = 0; - int ix; - uint32_t hash = name_hash(name); - - ix = nr_objects ? locate_object_entry_hash(sha1) : -1; - if (ix >= 0) { - if (exclude) { - entry = objects + object_ix[ix] - 1; - if (!entry->preferred_base) - nr_result--; - entry->preferred_base = 1; - } + + entry = packlist_find(&to_pack, sha1, index_pos); + if (!entry) return 0; + + if (exclude) { + if (!entry->preferred_base) + nr_result--; + entry->preferred_base = 1; } + return 1; +} + +/* + * Check whether we want the object in the pack (e.g., we do not want + * objects found in non-local stores if the "--local" option was used). + * + * As a side effect of this check, we will find the packed version of this + * object, if any. We therefore pass out the pack information to avoid having + * to look it up again later. + */ +static int want_object_in_pack(const unsigned char *sha1, + int exclude, + struct packed_git **found_pack, + off_t *found_offset) +{ + struct packed_git *p; + if (!exclude && local && has_loose_object_nonlocal(sha1)) return 0; + *found_pack = NULL; + *found_offset = 0; + for (p = packed_git; p; p = p->next) { off_t offset = find_pack_entry_one(sha1, p); if (offset) { - if (!found_pack) { + if (!*found_pack) { if (!is_pack_valid(p)) { warning("packfile %s cannot be accessed", p->pack_name); continue; } - found_offset = offset; - found_pack = p; + *found_offset = offset; + *found_pack = p; } if (exclude) - break; + return 1; if (incremental) return 0; if (local && !p->pack_local) @@ -945,14 +978,21 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, } } - if (nr_objects >= nr_alloc) { - nr_alloc = (nr_alloc + 1024) * 3 / 2; - objects = xrealloc(objects, nr_alloc * sizeof(*entry)); - } + return 1; +} - entry = objects + nr_objects++; - memset(entry, 0, sizeof(*entry)); - hashcpy(entry->idx.sha1, sha1); +static void create_object_entry(const unsigned char *sha1, + enum object_type type, + uint32_t hash, + int exclude, + int no_try_delta, + uint32_t index_pos, + struct packed_git *found_pack, + off_t found_offset) +{ + struct object_entry *entry; + + entry = packlist_alloc(&to_pack, sha1, index_pos); entry->hash = hash; if (type) entry->type = type; @@ -965,16 +1005,53 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, entry->in_pack_offset = found_offset; } - if (object_ix_hashsz * 3 <= nr_objects * 4) - rehash_objects(); - else - object_ix[-1 - ix] = nr_objects; + entry->no_try_delta = no_try_delta; +} + +static const char no_closure_warning[] = N_( +"disabling bitmap writing, as some objects are not being packed" +); - display_progress(progress_state, nr_objects); +static int add_object_entry(const unsigned char *sha1, enum object_type type, + const char *name, int exclude) +{ + struct packed_git *found_pack; + off_t found_offset; + uint32_t index_pos; + + if (have_duplicate_entry(sha1, exclude, &index_pos)) + return 0; + + if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) { + /* The pack is missing an object, so it will not have closure */ + if (write_bitmap_index) { + warning(_(no_closure_warning)); + write_bitmap_index = 0; + } + return 0; + } - if (name && no_try_delta(name)) - entry->no_try_delta = 1; + create_object_entry(sha1, type, pack_name_hash(name), + exclude, name && no_try_delta(name), + index_pos, found_pack, found_offset); + display_progress(progress_state, nr_result); + return 1; +} + +static int add_object_entry_from_bitmap(const unsigned char *sha1, + enum object_type type, + int flags, uint32_t name_hash, + struct packed_git *pack, off_t offset) +{ + uint32_t index_pos; + + if (have_duplicate_entry(sha1, 0, &index_pos)) + return 0; + + create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset); + + display_progress(progress_state, nr_result); return 1; } @@ -999,7 +1076,7 @@ static int pbase_tree_cache_ix_incr(int ix) static struct pbase_tree { struct pbase_tree *next; /* This is a phony "cache" entry; we are not - * going to evict it nor find it through _get() + * going to evict it or find it through _get() * mechanism -- this is for the toplevel node that * would almost always change with any commit. */ @@ -1156,12 +1233,9 @@ static int check_pbase_path(unsigned hash) if (0 <= pos) return 1; pos = -pos - 1; - if (done_pbase_paths_alloc <= done_pbase_paths_num) { - done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc); - done_pbase_paths = xrealloc(done_pbase_paths, - done_pbase_paths_alloc * - sizeof(unsigned)); - } + ALLOC_GROW(done_pbase_paths, + done_pbase_paths_num + 1, + done_pbase_paths_alloc); done_pbase_paths_num++; if (pos < done_pbase_paths_num) memmove(done_pbase_paths + pos + 1, @@ -1175,7 +1249,7 @@ static void add_preferred_base_object(const char *name) { struct pbase_tree *it; int cmplen; - unsigned hash = name_hash(name); + unsigned hash = pack_name_hash(name); if (!num_preferred_base || check_pbase_path(hash)) return; @@ -1327,7 +1401,7 @@ static void check_object(struct object_entry *entry) break; } - if (base_ref && (base_entry = locate_object_entry(base_ref))) { + if (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) { /* * If base_ref was set above that means we wish to * reuse delta data, and we even found that base @@ -1401,12 +1475,12 @@ static void get_object_details(void) uint32_t i; struct object_entry **sorted_by_offset; - sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *)); - for (i = 0; i < nr_objects; i++) - sorted_by_offset[i] = objects + i; - qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort); + sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *)); + for (i = 0; i < to_pack.nr_objects; i++) + sorted_by_offset[i] = to_pack.objects + i; + qsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort); - for (i = 0; i < nr_objects; i++) { + for (i = 0; i < to_pack.nr_objects; i++) { struct object_entry *entry = sorted_by_offset[i]; check_object(entry); if (big_file_threshold < entry->size) @@ -2032,7 +2106,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo if (starts_with(path, "refs/tags/") && /* is a tag? */ !peel_ref(path, peeled) && /* peelable? */ - locate_object_entry(peeled)) /* object packed? */ + packlist_find(&to_pack, peeled, NULL)) /* object packed? */ add_object_entry(sha1, OBJ_TAG, NULL, 0); return 0; } @@ -2055,14 +2129,14 @@ static void prepare_pack(int window, int depth) if (!pack_to_stdout) do_check_packed_object_crc = 1; - if (!nr_objects || !window || !depth) + if (!to_pack.nr_objects || !window || !depth) return; - delta_list = xmalloc(nr_objects * sizeof(*delta_list)); + delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list)); nr_deltas = n = 0; - for (i = 0; i < nr_objects; i++) { - struct object_entry *entry = objects + i; + for (i = 0; i < to_pack.nr_objects; i++) { + struct object_entry *entry = to_pack.objects + i; if (entry->delta) /* This happens if we decided to reuse existing @@ -2097,7 +2171,7 @@ static void prepare_pack(int window, int depth) if (nr_deltas && n > 1) { unsigned nr_done = 0; if (progress) - progress_state = start_progress("Compressing objects", + progress_state = start_progress(_("Compressing objects"), nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); @@ -2140,6 +2214,20 @@ static int git_pack_config(const char *k, const char *v, void *cb) cache_max_small_delta_size = git_config_int(k, v); return 0; } + if (!strcmp(k, "pack.writebitmaps")) { + write_bitmap_index = git_config_bool(k, v); + return 0; + } + if (!strcmp(k, "pack.writebitmaphashcache")) { + if (git_config_bool(k, v)) + write_bitmap_options |= BITMAP_OPT_HASH_CACHE; + else + write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE; + } + if (!strcmp(k, "pack.usebitmaps")) { + use_bitmap_index = git_config_bool(k, v); + return 0; + } if (!strcmp(k, "pack.threads")) { delta_search_threads = git_config_int(k, v); if (delta_search_threads < 0) @@ -2198,6 +2286,9 @@ static void show_commit(struct commit *commit, void *data) { add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0); commit->object.flags |= OBJECT_ADDED; + + if (write_bitmap_index) + index_commit_for_bitmap(commit); } static void show_object(struct object *obj, @@ -2340,7 +2431,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) for (i = 0; i < p->num_objects; i++) { sha1 = nth_packed_object_sha1(p, i); - if (!locate_object_entry(sha1) && + if (!packlist_find(&to_pack, sha1, NULL) && !has_sha1_pack_kept_or_nonlocal(sha1)) if (force_object_loose(sha1, p->mtime)) die("unable to force loose object"); @@ -2348,6 +2439,35 @@ static void loosen_unused_packed_objects(struct rev_info *revs) } } +/* + * This tracks any options which a reader of the pack might + * not understand, and which would therefore prevent blind reuse + * of what we have on disk. + */ +static int pack_options_allow_reuse(void) +{ + return allow_ofs_delta; +} + +static int get_object_list_from_bitmap(struct rev_info *revs) +{ + if (prepare_bitmap_walk(revs) < 0) + return -1; + + if (pack_options_allow_reuse() && + !reuse_partial_packfile_from_bitmap( + &reuse_packfile, + &reuse_packfile_objects, + &reuse_packfile_offset)) { + assert(reuse_packfile_objects); + nr_result += reuse_packfile_objects; + display_progress(progress_state, nr_result); + } + + traverse_bitmap_commit_list(&add_object_entry_from_bitmap); + return 0; +} + static void get_object_list(int ac, const char **av) { struct rev_info revs; @@ -2358,6 +2478,9 @@ static void get_object_list(int ac, const char **av) save_commit_buffer = 0; setup_revisions(ac, av, &revs, NULL); + /* make sure shallows are read */ + is_repository_shallow(); + while (fgets(line, sizeof(line), stdin) != NULL) { int len = strlen(line); if (len && line[len - 1] == '\n') @@ -2367,6 +2490,14 @@ static void get_object_list(int ac, const char **av) if (*line == '-') { if (!strcmp(line, "--not")) { flags ^= UNINTERESTING; + write_bitmap_index = 0; + continue; + } + if (starts_with(line, "--shallow ")) { + unsigned char sha1[20]; + if (get_sha1_hex(line + 10, sha1)) + die("not an SHA-1 '%s'", line + 10); + register_shallow(sha1); continue; } die("not a rev '%s'", line); @@ -2375,6 +2506,9 @@ static void get_object_list(int ac, const char **av) die("bad revision '%s'", line); } + if (use_bitmap_index && !get_object_list_from_bitmap(&revs)) + return; + if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(&revs, show_edge); @@ -2504,10 +2638,14 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("pack compression level")), OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, N_("do not hide commits by grafts"), 0), + OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index, + N_("use a bitmap index if available to speed up counting objects")), + OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index, + N_("write a bitmap index together with the pack index")), OPT_END(), }; - read_replace_refs = 0; + check_replace_refs = 0; reset_pack_idx_option(&pack_idx_opts); git_config(git_pack_config, NULL); @@ -2570,13 +2708,19 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (keep_unreachable && unpack_unreachable) die("--keep-unreachable and --unpack-unreachable are incompatible."); + if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow()) + use_bitmap_index = 0; + + if (pack_to_stdout || !rev_list_all) + write_bitmap_index = 0; + if (progress && all_progress_implied) progress = 2; prepare_packed_git(); if (progress) - progress_state = start_progress("Counting objects", 0); + progress_state = start_progress(_("Counting objects"), 0); if (!use_internal_rev_list) read_object_list_from_stdin(); else { diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index fcf5fb6129..6879468c46 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -46,7 +46,7 @@ void prune_packed_objects(int opts) strbuf_addstr(&pathname, dir); if (opts & PRUNE_PACKED_VERBOSE) - progress = start_progress_delay("Removing duplicate objects", + progress = start_progress_delay(_("Removing duplicate objects"), 256, 95, 2); if (pathname.len && pathname.buf[pathname.len - 1] != '/') diff --git a/builtin/prune.c b/builtin/prune.c index de43b26cfe..144a3bdb33 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -150,7 +150,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = ULONG_MAX; save_commit_buffer = 0; - read_replace_refs = 0; + check_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); @@ -169,7 +169,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) if (show_progress == -1) show_progress = isatty(2); if (show_progress) - progress = start_progress_delay("Checking connectivity", 0, 0, 2); + progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); mark_reachable_objects(&revs, 1, progress); stop_progress(&progress); diff --git a/builtin/push.c b/builtin/push.c index 0e50ddbb01..f8dfea41e1 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -26,7 +26,6 @@ static struct push_cas_option cas; static const char **refspec; static int refspec_nr; static int refspec_alloc; -static int default_matching_used; static void add_refspec(const char *ref) { @@ -59,7 +58,7 @@ static const char *map_refspec(const char *ref, } if (push_default == PUSH_DEFAULT_UPSTREAM && - !prefixcmp(matched->name, "refs/heads/")) { + starts_with(matched->name, "refs/heads/")) { struct branch *branch = branch_get(matched->name + 11); if (branch->merge_nr == 1 && branch->merge[0]->src) { struct strbuf buf = STRBUF_INIT; @@ -204,9 +203,9 @@ static void setup_push_current(struct remote *remote, struct branch *branch) } static char warn_unspecified_push_default_msg[] = -N_("push.default is unset; its implicit value is changing in\n" +N_("push.default is unset; its implicit value has changed in\n" "Git 2.0 from 'matching' to 'simple'. To squelch this message\n" - "and maintain the current behavior after the default changes, use:\n" + "and maintain the traditional behavior, use:\n" "\n" " git config --global push.default matching\n" "\n" @@ -217,7 +216,7 @@ N_("push.default is unset; its implicit value is changing in\n" "When push.default is set to 'matching', git will push local branches\n" "to the remote branches that already exist with the same name.\n" "\n" - "In Git 2.0, Git will default to the more conservative 'simple'\n" + "Since Git 2.0, Git defaults to the more conservative 'simple'\n" "behavior, which only pushes the current branch to the corresponding\n" "remote branch that 'git pull' uses to update the current branch.\n" "\n" @@ -247,14 +246,14 @@ static void setup_default_push_refspecs(struct remote *remote) switch (push_default) { default: - case PUSH_DEFAULT_UNSPECIFIED: - default_matching_used = 1; - warn_unspecified_push_default_configuration(); - /* fallthru */ case PUSH_DEFAULT_MATCHING: add_refspec(":"); break; + case PUSH_DEFAULT_UNSPECIFIED: + warn_unspecified_push_default_configuration(); + /* fallthru */ + case PUSH_DEFAULT_SIMPLE: if (triangular) setup_push_current(remote, branch); @@ -283,12 +282,6 @@ static const char message_advice_pull_before_push[] = "'git pull ...') before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); -static const char message_advice_use_upstream[] = - N_("Updates were rejected because a pushed branch tip is behind its remote\n" - "counterpart. If you did not intend to push that branch, you may want to\n" - "specify branches to push or set the 'push.default' configuration variable\n" - "to 'simple', 'current' or 'upstream' to push only the current branch."); - static const char message_advice_checkout_pull_push[] = N_("Updates were rejected because a pushed branch tip is behind its remote\n" "counterpart. Check out this branch and integrate the remote changes\n" @@ -317,13 +310,6 @@ static void advise_pull_before_push(void) advise(_(message_advice_pull_before_push)); } -static void advise_use_upstream(void) -{ - if (!advice_push_non_ff_default || !advice_push_update_rejected) - return; - advise(_(message_advice_use_upstream)); -} - static void advise_checkout_pull_push(void) { if (!advice_push_non_ff_matching || !advice_push_update_rejected) @@ -385,10 +371,7 @@ static int push_with_options(struct transport *transport, int flags) if (reject_reasons & REJECT_NON_FF_HEAD) { advise_pull_before_push(); } else if (reject_reasons & REJECT_NON_FF_OTHER) { - if (default_matching_used) - advise_use_upstream(); - else - advise_checkout_pull_push(); + advise_checkout_pull_push(); } else if (reject_reasons & REJECT_ALREADY_EXISTS) { advise_ref_already_exists(); } else if (reject_reasons & REJECT_FETCH_FIRST) { diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 85bba356fa..c3230817db 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -828,14 +828,10 @@ static void execute_commands(struct command *commands, } } - if (shallow_update) { - if (!checked_connectivity) - error("BUG: run 'git fsck' for safety.\n" - "If there are errors, try to remove " - "the reported refs above"); - if (alt_shallow_file && *alt_shallow_file) - unlink(alt_shallow_file); - } + if (shallow_update && !checked_connectivity) + error("BUG: run 'git fsck' for safety.\n" + "If there are errors, try to remove " + "the reported refs above"); } static struct command *read_head_info(struct sha1_array *shallow) @@ -1087,10 +1083,6 @@ static void update_shallow_info(struct command *commands, cmd->skip_update = 1; } } - if (alt_shallow_file && *alt_shallow_file) { - unlink(alt_shallow_file); - alt_shallow_file = NULL; - } free(ref_status); } diff --git a/builtin/reflog.c b/builtin/reflog.c index 852cff60b7..c12a9784e6 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -561,7 +561,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c return; /* both given explicitly -- nothing to tweak */ for (ent = reflog_expire_cfg; ent; ent = ent->next) { - if (!fnmatch(ent->pattern, ref, 0)) { + if (!wildmatch(ent->pattern, ref, 0, NULL)) { if (!(slot & EXPIRE_TOTAL)) cb->expire_total = ent->expire_total; if (!(slot & EXPIRE_UNREACH)) diff --git a/builtin/repack.c b/builtin/repack.c index ba66c6e377..6b0b62dcb2 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -9,6 +9,7 @@ #include "argv-array.h" static int delta_base_offset = 1; +static int pack_kept_objects = -1; static char *packdir, *packtmp; static const char *const git_repack_usage[] = { @@ -22,6 +23,10 @@ static int repack_config(const char *var, const char *value, void *cb) delta_base_offset = git_config_bool(var, value); return 0; } + if (!strcmp(var, "repack.packkeptobjects")) { + pack_kept_objects = git_config_bool(var, value); + return 0; + } return git_default_config(var, value, cb); } @@ -94,7 +99,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list) static void remove_redundant_pack(const char *dir_name, const char *base_name) { - const char *exts[] = {".pack", ".idx", ".keep"}; + const char *exts[] = {".pack", ".idx", ".keep", ".bitmap"}; int i; struct strbuf buf = STRBUF_INIT; size_t plen; @@ -115,7 +120,14 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name) int cmd_repack(int argc, const char **argv, const char *prefix) { - const char *exts[2] = {".pack", ".idx"}; + struct { + const char *name; + unsigned optional:1; + } exts[] = { + {".pack"}, + {".idx"}, + {".bitmap", 1}, + }; struct child_process cmd; struct string_list_item *item; struct argv_array cmd_args = ARGV_ARRAY_INIT; @@ -129,14 +141,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix) /* variables to be filled by option parsing */ int pack_everything = 0; int delete_redundant = 0; - char *unpack_unreachable = NULL; - int window = 0, window_memory = 0; - int depth = 0; - int max_pack_size = 0; + const char *unpack_unreachable = NULL; + const char *window = NULL, *window_memory = NULL; + const char *depth = NULL; + const char *max_pack_size = NULL; int no_reuse_delta = 0, no_reuse_object = 0; int no_update_server_info = 0; int quiet = 0; int local = 0; + int write_bitmap = -1; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -155,16 +168,20 @@ int cmd_repack(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("be quiet")), OPT_BOOL('l', "local", &local, N_("pass --local to git-pack-objects")), + OPT_BOOL('b', "write-bitmap-index", &write_bitmap, + N_("write bitmap index")), OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), N_("with -A, do not loosen objects older than this")), - OPT_INTEGER(0, "window", &window, + OPT_STRING(0, "window", &window, N_("n"), N_("size of the window used for delta compression")), - OPT_INTEGER(0, "window-memory", &window_memory, + OPT_STRING(0, "window-memory", &window_memory, N_("bytes"), N_("same as the above, but limit memory size instead of entries count")), - OPT_INTEGER(0, "depth", &depth, + OPT_STRING(0, "depth", &depth, N_("n"), N_("limits the maximum delta depth")), - OPT_INTEGER(0, "max-pack-size", &max_pack_size, + OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"), N_("maximum size of each packfile")), + OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, + N_("repack objects in packs marked with .keep")), OPT_END() }; @@ -173,6 +190,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_repack_options, git_repack_usage, 0); + if (pack_kept_objects < 0) + pack_kept_objects = write_bitmap; + packdir = mkpathdup("%s/pack", get_object_directory()); packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid()); @@ -180,22 +200,26 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd_args, "pack-objects"); argv_array_push(&cmd_args, "--keep-true-parents"); - argv_array_push(&cmd_args, "--honor-pack-keep"); + if (!pack_kept_objects) + argv_array_push(&cmd_args, "--honor-pack-keep"); argv_array_push(&cmd_args, "--non-empty"); argv_array_push(&cmd_args, "--all"); argv_array_push(&cmd_args, "--reflog"); if (window) - argv_array_pushf(&cmd_args, "--window=%u", window); + argv_array_pushf(&cmd_args, "--window=%s", window); if (window_memory) - argv_array_pushf(&cmd_args, "--window-memory=%u", window_memory); + argv_array_pushf(&cmd_args, "--window-memory=%s", window_memory); if (depth) - argv_array_pushf(&cmd_args, "--depth=%u", depth); + argv_array_pushf(&cmd_args, "--depth=%s", depth); if (max_pack_size) - argv_array_pushf(&cmd_args, "--max_pack_size=%u", max_pack_size); + argv_array_pushf(&cmd_args, "--max-pack-size=%s", max_pack_size); if (no_reuse_delta) argv_array_pushf(&cmd_args, "--no-reuse-delta"); if (no_reuse_object) argv_array_pushf(&cmd_args, "--no-reuse-object"); + if (write_bitmap >= 0) + argv_array_pushf(&cmd_args, "--%swrite-bitmap-index", + write_bitmap ? "" : "no-"); if (pack_everything & ALL_INTO_ONE) { get_non_kept_pack_filenames(&existing_packs); @@ -256,17 +280,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix) */ failed = 0; for_each_string_list_item(item, &names) { - for (ext = 0; ext < 2; ext++) { + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname, *fname_old; - fname = mkpathdup("%s/%s%s", packdir, - item->string, exts[ext]); + fname = mkpathdup("%s/pack-%s%s", packdir, + item->string, exts[ext].name); if (!file_exists(fname)) { free(fname); continue; } fname_old = mkpath("%s/old-%s%s", packdir, - item->string, exts[ext]); + item->string, exts[ext].name); if (file_exists(fname_old)) if (unlink(fname_old)) failed = 1; @@ -313,19 +337,23 @@ int cmd_repack(int argc, const char **argv, const char *prefix) /* Now the ones with the same name are out of the way... */ for_each_string_list_item(item, &names) { - for (ext = 0; ext < 2; ext++) { + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname, *fname_old; struct stat statbuffer; + int exists = 0; fname = mkpathdup("%s/pack-%s%s", - packdir, item->string, exts[ext]); + packdir, item->string, exts[ext].name); fname_old = mkpathdup("%s-%s%s", - packtmp, item->string, exts[ext]); + packtmp, item->string, exts[ext].name); if (!stat(fname_old, &statbuffer)) { statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); chmod(fname_old, statbuffer.st_mode); + exists = 1; + } + if (exists || !exts[ext].optional) { + if (rename(fname_old, fname)) + die_errno(_("renaming '%s' failed"), fname_old); } - if (rename(fname_old, fname)) - die_errno(_("renaming '%s' failed"), fname_old); free(fname); free(fname_old); } @@ -333,12 +361,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) /* Remove the "old-" files */ for_each_string_list_item(item, &names) { - for (ext = 0; ext < 2; ext++) { + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname; - fname = mkpath("%s/old-pack-%s%s", + fname = mkpath("%s/old-%s%s", packdir, item->string, - exts[ext]); + exts[ext].name); if (remove_path(fname)) warning(_("removing '%s' failed"), fname); } diff --git a/builtin/replace.c b/builtin/replace.c index 2336325ce3..b62420a01a 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -36,7 +36,7 @@ static int show_reference(const char *refname, const unsigned char *sha1, { struct show_data *data = cb_data; - if (!fnmatch(data->pattern, refname, 0)) { + if (!wildmatch(data->pattern, refname, 0, NULL)) { if (data->format == REPLACE_FORMAT_SHORT) printf("%s\n", refname); else if (data->format == REPLACE_FORMAT_MEDIUM) @@ -178,7 +178,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) OPT_END() }; - read_replace_refs = 0; + check_replace_refs = 0; argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); diff --git a/builtin/reset.c b/builtin/reset.c index 60048030dd..f4e087596b 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -116,25 +116,34 @@ static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; + int intent_to_add = *(int *)data; for (i = 0; i < q->nr; i++) { struct diff_filespec *one = q->queue[i]->one; - if (one->mode && !is_null_sha1(one->sha1)) { - struct cache_entry *ce; - ce = make_cache_entry(one->mode, one->sha1, one->path, - 0, 0); - if (!ce) - die(_("make_cache_entry failed for path '%s'"), - one->path); - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | - ADD_CACHE_OK_TO_REPLACE); - } else + int is_missing = !(one->mode && !is_null_sha1(one->sha1)); + struct cache_entry *ce; + + if (is_missing && !intent_to_add) { remove_file_from_cache(one->path); + continue; + } + + ce = make_cache_entry(one->mode, one->sha1, one->path, + 0, 0); + if (!ce) + die(_("make_cache_entry failed for path '%s'"), + one->path); + if (is_missing) { + ce->ce_flags |= CE_INTENT_TO_ADD; + set_object_name_for_intent_to_add_entry(ce); + } + add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } } static int read_from_tree(const struct pathspec *pathspec, - unsigned char *tree_sha1) + unsigned char *tree_sha1, + int intent_to_add) { struct diff_options opt; @@ -142,6 +151,7 @@ static int read_from_tree(const struct pathspec *pathspec, copy_pathspec(&opt.pathspec, pathspec); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; + opt.format_callback_data = &intent_to_add; if (do_diff_cache(tree_sha1, &opt)) return 1; @@ -258,6 +268,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) const char *rev; unsigned char sha1[20]; struct pathspec pathspec; + int intent_to_add = 0; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, @@ -270,6 +281,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_BOOL('N', "intent-to-add", &intent_to_add, + N_("record only the fact that removed paths will be added later")), OPT_END() }; @@ -320,13 +333,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type == NONE) reset_type = MIXED; /* by default */ - if (reset_type != SOFT && reset_type != MIXED) + if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree())) setup_work_tree(); if (reset_type == MIXED && is_bare_repository()) die(_("%s reset is not allowed in a bare repository"), _(reset_type_names[reset_type])); + if (intent_to_add && reset_type != MIXED) + die(_("-N can only be used with --mixed")); + /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset * the index file to the tree object we are switching to. */ @@ -338,10 +354,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix) int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; - if (read_from_tree(&pathspec, sha1)) + if (read_from_tree(&pathspec, sha1, intent_to_add)) return 1; - refresh_index(&the_index, flags, NULL, NULL, - _("Unstaged changes after reset:")); + if (get_git_work_tree()) + refresh_index(&the_index, flags, NULL, NULL, + _("Unstaged changes after reset:")); } else { int err = reset_index(sha1, reset_type, quiet); if (reset_type == KEEP && !err) diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 0745e2d053..9f92905379 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -3,6 +3,8 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "pack.h" +#include "pack-bitmap.h" #include "builtin.h" #include "log-tree.h" #include "graph.h" @@ -257,6 +259,18 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all) return 0; } +static int show_object_fast( + const unsigned char *sha1, + enum object_type type, + int exclude, + uint32_t name_hash, + struct packed_git *found_pack, + off_t found_offset) +{ + fprintf(stdout, "%s\n", sha1_to_hex(sha1)); + return 1; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -265,6 +279,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; + int use_bitmap_index = 0; git_config(git_default_config, NULL); init_revisions(&revs, prefix); @@ -306,6 +321,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) bisect_show_vars = 1; continue; } + if (!strcmp(arg, "--use-bitmap-index")) { + use_bitmap_index = 1; + continue; + } + if (!strcmp(arg, "--test-bitmap")) { + test_bitmap_walk(&revs); + return 0; + } usage(rev_list_usage); } @@ -333,6 +356,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) revs.limited = 1; + if (use_bitmap_index) { + if (revs.count && !revs.left_right && !revs.cherry_mark) { + uint32_t commit_count; + if (!prepare_bitmap_walk(&revs)) { + count_bitmap_commit_list(&commit_count, NULL, NULL, NULL); + printf("%d\n", commit_count); + return 0; + } + } else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) { + if (!prepare_bitmap_walk(&revs)) { + traverse_bitmap_commit_list(&show_object_fast); + return 0; + } + } + } + if (prepare_revision_walk(&revs)) die("revision walk setup failed"); if (revs.tree_objects) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index aaeb611a97..1a6122d3ae 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -395,9 +395,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) usage[unb++] = strbuf_detach(&sb, NULL); } - /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */ + /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */ while (strbuf_getline(&sb, stdin, '\n') != EOF) { const char *s; + const char *end; struct option *o; if (!sb.len) @@ -419,6 +420,16 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) o->value = &parsed; o->flags = PARSE_OPT_NOARG; o->callback = &parseopt_dump; + + /* Possible argument name hint */ + end = s; + while (s > sb.buf && strchr("*=?!", s[-1]) == NULL) + --s; + if (s != sb.buf && s != end) + o->argh = xmemdupz(s, end - s); + if (s == sb.buf) + s = end; + while (s > sb.buf && strchr("*=?!", s[-1])) { switch (*--s) { case '=': @@ -547,15 +558,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--default")) { - def = argv[i+1]; - i++; + def = argv[++i]; + if (!def) + die("--default requires an argument"); continue; } if (!strcmp(arg, "--prefix")) { - prefix = argv[i+1]; + prefix = argv[++i]; + if (!prefix) + die("--prefix requires an argument"); startup_info->prefix = prefix; output_prefix = 1; - i++; continue; } if (!strcmp(arg, "--revs-only")) { @@ -738,9 +751,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--resolve-git-dir")) { - const char *gitdir = resolve_gitdir(argv[i+1]); + const char *gitdir = argv[++i]; + if (!gitdir) + die("--resolve-git-dir requires an argument"); + gitdir = resolve_gitdir(gitdir); if (!gitdir) - die("not a gitdir '%s'", argv[i+1]); + die("not a gitdir '%s'", argv[i]); puts(gitdir); continue; } diff --git a/builtin/revert.c b/builtin/revert.c index 87659c9fdb..f9ed5bd5d0 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -89,6 +89,8 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")), OPT_CALLBACK('X', "strategy-option", &opts, N_("option"), N_("option for merge strategy"), option_parse_x), + { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"), + N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_END(), OPT_END(), OPT_END(), diff --git a/builtin/rm.c b/builtin/rm.c index 3a0e0eaab7..960634dd0c 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -308,10 +308,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix) for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; - if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen)) + if (!ce_path_match(ce, &pathspec, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); - list.entry[list.nr].name = ce->name; + list.entry[list.nr].name = xstrdup(ce->name); list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); if (list.entry[list.nr++].is_submodule && !is_staging_gitmodules_ok()) diff --git a/builtin/show-branch.c b/builtin/show-branch.c index d9217ce1e1..d87317290c 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -450,7 +450,7 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i slash--; if (!*tail) return 0; - if (fnmatch(match_ref_pattern, tail, 0)) + if (wildmatch(match_ref_pattern, tail, 0, NULL)) return 0; if (starts_with(refname, "refs/heads/")) return append_head_ref(refname, sha1, flag, cb_data); diff --git a/builtin/tag.c b/builtin/tag.c index 74d3780b77..6c7c6bde9d 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -27,9 +27,16 @@ static const char * const git_tag_usage[] = { NULL }; +#define STRCMP_SORT 0 /* must be zero */ +#define VERCMP_SORT 1 +#define SORT_MASK 0x7fff +#define REVERSE_SORT 0x8000 + struct tag_filter { const char **patterns; int lines; + int sort; + struct string_list tags; struct commit_list *with_commit; }; @@ -42,7 +49,7 @@ static int match_pattern(const char **patterns, const char *ref) if (!*patterns) return 1; for (; *patterns; patterns++) - if (!fnmatch(*patterns, ref, 0)) + if (!wildmatch(*patterns, ref, 0, NULL)) return 1; return 0; } @@ -166,7 +173,10 @@ static int show_reference(const char *refname, const unsigned char *sha1, return 0; if (!filter->lines) { - printf("%s\n", refname); + if (filter->sort) + string_list_append(&filter->tags, refname); + else + printf("%s\n", refname); return 0; } printf("%-15s ", refname); @@ -177,17 +187,39 @@ static int show_reference(const char *refname, const unsigned char *sha1, return 0; } +static int sort_by_version(const void *a_, const void *b_) +{ + const struct string_list_item *a = a_; + const struct string_list_item *b = b_; + return versioncmp(a->string, b->string); +} + static int list_tags(const char **patterns, int lines, - struct commit_list *with_commit) + struct commit_list *with_commit, int sort) { struct tag_filter filter; filter.patterns = patterns; filter.lines = lines; + filter.sort = sort; filter.with_commit = with_commit; + memset(&filter.tags, 0, sizeof(filter.tags)); + filter.tags.strdup_strings = 1; for_each_tag_ref(show_reference, (void *) &filter); - + if (sort) { + int i; + if ((sort & SORT_MASK) == VERCMP_SORT) + qsort(filter.tags.items, filter.tags.nr, + sizeof(struct string_list_item), sort_by_version); + if (sort & REVERSE_SORT) + for (i = filter.tags.nr - 1; i >= 0; i--) + printf("%s\n", filter.tags.items[i].string); + else + for (i = 0; i < filter.tags.nr; i++) + printf("%s\n", filter.tags.items[i].string); + string_list_clear(&filter.tags, 0); + } return 0; } @@ -427,6 +459,29 @@ static int parse_opt_points_at(const struct option *opt __attribute__((unused)), return 0; } +static int parse_opt_sort(const struct option *opt, const char *arg, int unset) +{ + int *sort = opt->value; + int flags = 0; + + if (*arg == '-') { + flags |= REVERSE_SORT; + arg++; + } + if (starts_with(arg, "version:")) { + *sort = VERCMP_SORT; + arg += 8; + } else if (starts_with(arg, "v:")) { + *sort = VERCMP_SORT; + arg += 2; + } else + *sort = STRCMP_SORT; + if (strcmp(arg, "refname")) + die(_("unsupported sort specification %s"), arg); + *sort |= flags; + return 0; +} + int cmd_tag(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; @@ -437,7 +492,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct create_tag_options opt; char *cleanup_arg = NULL; int annotate = 0, force = 0, lines = -1; - int cmdmode = 0; + int cmdmode = 0, sort = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct commit_list *with_commit = NULL; @@ -458,10 +513,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"), N_("how to strip spaces and #comments from message")), - OPT_STRING('u', "local-user", &keyid, N_("key id"), + OPT_STRING('u', "local-user", &keyid, N_("key-id"), N_("use another key to sign the tag")), OPT__FORCE(&force, N_("replace the tag if exists")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), + { + OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"), + PARSE_OPT_NONEG, parse_opt_sort + }, OPT_GROUP(N_("Tag listing options")), { @@ -471,6 +530,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) parse_opt_with_commit, (intptr_t)"HEAD", }, { + OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"), + N_("print only tags that contain the commit"), + PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT, + parse_opt_with_commit, (intptr_t)"HEAD", + }, + { OPTION_CALLBACK, 0, "points-at", NULL, N_("object"), N_("print only tags of the object"), 0, parse_opt_points_at }, @@ -509,7 +574,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix) copts.padding = 2; run_column_filter(colopts, &copts); } - ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit); + if (lines != -1 && sort) + die(_("--sort and -n are incompatible")); + ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort); if (column_active(colopts)) stop_column_filter(); return ret; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 62ff673f68..99cde45879 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -480,7 +480,7 @@ static void unpack_all(void) use(sizeof(struct pack_header)); if (!quiet) - progress = start_progress("Unpacking objects", nr_objects); + progress = start_progress(_("Unpacking objects"), nr_objects); obj_list = xcalloc(nr_objects, sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); @@ -497,7 +497,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) int i; unsigned char sha1[20]; - read_replace_refs = 0; + check_replace_refs = 0; git_config(git_default_config, NULL); diff --git a/builtin/update-index.c b/builtin/update-index.c index e3a10d706d..ebea285e1b 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -12,6 +12,7 @@ #include "resolve-undo.h" #include "parse-options.h" #include "pathspec.h" +#include "dir.h" /* * Default to not allowing changes to the list of files. The @@ -274,36 +275,32 @@ static void chmod_path(int flip, const char *path) die("git update-index: cannot chmod %cx '%s'", flip, path); } -static void update_one(const char *path, const char *prefix, int prefix_length) +static void update_one(const char *path) { - const char *p = prefix_path(prefix, prefix_length, path); - if (!verify_path(p)) { + if (!verify_path(path)) { fprintf(stderr, "Ignoring path %s\n", path); - goto free_return; + return; } if (mark_valid_only) { - if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG)) + if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG)) die("Unable to mark file %s", path); - goto free_return; + return; } if (mark_skip_worktree_only) { - if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG)) + if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG)) die("Unable to mark file %s", path); - goto free_return; + return; } if (force_remove) { - if (remove_file_from_cache(p)) + if (remove_file_from_cache(path)) die("git update-index: unable to remove %s", path); report("remove '%s'", path); - goto free_return; + return; } - if (process_path(p)) + if (process_path(path)) die("Unable to process path %s", path); report("add '%s'", path); - free_return: - if (p < path || p > path + strlen(path)) - free((char *)p); } static void read_index_info(int line_termination) @@ -563,8 +560,9 @@ static int do_reupdate(int ac, const char **av, const struct cache_entry *ce = active_cache[pos]; struct cache_entry *old = NULL; int save_nr; + char *path; - if (ce_stage(ce) || !ce_path_match(ce, &pathspec)) + if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL)) continue; if (has_head) old = read_one_ent(NULL, head_sha1, @@ -579,7 +577,9 @@ static int do_reupdate(int ac, const char **av, * or worse yet 'allow_replace', active_nr may decrease. */ save_nr = active_nr; - update_one(ce->name + prefix_length, prefix, prefix_length); + path = xstrdup(ce->name); + update_one(path); + free(path); if (save_nr != active_nr) goto redo; } @@ -629,14 +629,45 @@ static int resolve_undo_clear_callback(const struct option *opt, return 0; } +static int parse_new_style_cacheinfo(const char *arg, + unsigned int *mode, + unsigned char sha1[], + const char **path) +{ + unsigned long ul; + char *endp; + + if (!arg) + return -1; + + errno = 0; + ul = strtoul(arg, &endp, 8); + if (errno || endp == arg || *endp != ',' || (unsigned int) ul != ul) + return -1; /* not a new-style cacheinfo */ + *mode = ul; + endp++; + if (get_sha1_hex(endp, sha1) || endp[40] != ',') + return -1; + *path = endp + 41; + return 0; +} + static int cacheinfo_callback(struct parse_opt_ctx_t *ctx, const struct option *opt, int unset) { unsigned char sha1[20]; unsigned int mode; + const char *path; + if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, sha1, &path)) { + if (add_cacheinfo(mode, sha1, path, 0)) + die("git update-index: --cacheinfo cannot add %s", path); + ctx->argv++; + ctx->argc--; + return 0; + } if (ctx->argc <= 3) - return error("option 'cacheinfo' expects three arguments"); + return error("option 'cacheinfo' expects <mode>,<sha1>,<path>"); if (strtoul_ui(*++ctx->argv, 8, &mode) || get_sha1_hex(*++ctx->argv, sha1) || add_cacheinfo(mode, sha1, *++ctx->argv, 0)) @@ -740,9 +771,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_NONEG, really_refresh_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL, - N_("<mode> <object> <path>"), + N_("<mode>,<object>,<path>"), N_("add the specified entry to the index"), - PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ + PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, (parse_opt_cb *) cacheinfo_callback}, {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, N_("(+/-)x"), @@ -836,11 +867,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) setup_work_tree(); p = prefix_path(prefix, prefix_length, path); - update_one(p, NULL, 0); + update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); - if (p < path || p > path + strlen(path)) - free((char *)p); + free((char *)p); ctx.argc--; ctx.argv++; break; @@ -879,11 +909,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); - update_one(p, NULL, 0); + update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); - if (p < buf.buf || p > buf.buf + buf.len) - free((char *)p); + free((char *)p); } strbuf_release(&nbuf); strbuf_release(&buf); diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 1292cfea11..5c208bb1fc 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -147,6 +147,7 @@ static void parse_cmd_create(const char *next) struct ref_update *update; update = update_alloc(); + update->have_old = 1; if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0]) update_store_ref_name(update, ref.buf); |