diff options
Diffstat (limited to 'builtin')
| -rw-r--r-- | builtin/add.c | 4 | ||||
| -rw-r--r-- | builtin/am.c | 2 | ||||
| -rw-r--r-- | builtin/bisect--helper.c | 282 | ||||
| -rw-r--r-- | builtin/blame.c | 27 | ||||
| -rw-r--r-- | builtin/clone.c | 2 | ||||
| -rw-r--r-- | builtin/commit-graph.c | 65 | ||||
| -rw-r--r-- | builtin/commit.c | 12 | ||||
| -rw-r--r-- | builtin/diff.c | 3 | ||||
| -rw-r--r-- | builtin/env--helper.c | 13 | ||||
| -rw-r--r-- | builtin/fast-export.c | 10 | ||||
| -rw-r--r-- | builtin/fetch.c | 12 | ||||
| -rw-r--r-- | builtin/init-db.c | 6 | ||||
| -rw-r--r-- | builtin/log.c | 1 | ||||
| -rw-r--r-- | builtin/pull.c | 3 | ||||
| -rw-r--r-- | builtin/push.c | 4 | ||||
| -rw-r--r-- | builtin/shortlog.c | 213 | ||||
| -rw-r--r-- | builtin/sparse-checkout.c | 37 |
17 files changed, 609 insertions, 87 deletions
diff --git a/builtin/add.c b/builtin/add.c index 26b6ced09e..a825887c50 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -239,7 +239,7 @@ int run_add_interactive(const char *revision, const char *patch_mode, return status; } -int interactive_add(int argc, const char **argv, const char *prefix, int patch) +int interactive_add(const char **argv, const char *prefix, int patch) { struct pathspec pathspec; @@ -451,7 +451,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (add_interactive) { if (pathspec_from_file) die(_("--pathspec-from-file is incompatible with --interactive/--patch")); - exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); + exit(interactive_add(argv + 1, prefix, patch_interactive)); } if (legacy_stash_p) { struct pathspec pathspec; diff --git a/builtin/am.c b/builtin/am.c index 7259186408..2c7673f74e 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2180,6 +2180,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar }; int new_value = SHOW_PATCH_RAW; + BUG_ON_OPT_NEG(unset); + if (arg) { for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) { if (!strcmp(arg, valid_modes[new_value])) diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 7dcc1b5188..7512b880f0 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -8,6 +8,7 @@ #include "run-command.h" #include "prompt.h" #include "quote.h" +#include "revision.h" static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") @@ -29,9 +30,17 @@ static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"), N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]" " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"), + N_("git bisect--helper --bisect-next"), + N_("git bisect--helper --bisect-auto-next"), + N_("git bisect--helper --bisect-autostart"), NULL }; +struct add_bisect_ref_data { + struct rev_info *revs; + unsigned int object_flags; +}; + struct bisect_terms { char *term_good; char *term_bad; @@ -55,6 +64,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad, static const char vocab_bad[] = "bad|new"; static const char vocab_good[] = "good|old"; +static int bisect_autostart(struct bisect_terms *terms); + /* * Check whether the string `term` belongs to the set of strings * included in the variable arguments. @@ -74,6 +85,52 @@ static int one_of(const char *term, ...) return res; } +static int write_in_file(const char *path, const char *mode, const char *format, va_list args) +{ + FILE *fp = NULL; + int res = 0; + + if (strcmp(mode, "w") && strcmp(mode, "a")) + BUG("write-in-file does not support '%s' mode", mode); + fp = fopen(path, mode); + if (!fp) + return error_errno(_("cannot open file '%s' in mode '%s'"), path, mode); + res = vfprintf(fp, format, args); + + if (res < 0) { + int saved_errno = errno; + fclose(fp); + errno = saved_errno; + return error_errno(_("could not write to file '%s'"), path); + } + + return fclose(fp); +} + +static int write_to_file(const char *path, const char *format, ...) +{ + int res; + va_list args; + + va_start(args, format); + res = write_in_file(path, "w", format, args); + va_end(args); + + return res; +} + +static int append_to_file(const char *path, const char *format, ...) +{ + int res; + va_list args; + + va_start(args, format); + res = write_in_file(path, "a", format, args); + va_end(args); + + return res; +} + static int check_term_format(const char *term, const char *orig_term) { int res; @@ -104,7 +161,6 @@ static int check_term_format(const char *term, const char *orig_term) static int write_terms(const char *bad, const char *good) { - FILE *fp = NULL; int res; if (!strcmp(bad, good)) @@ -113,13 +169,9 @@ static int write_terms(const char *bad, const char *good) if (check_term_format(bad, "bad") || check_term_format(good, "good")) return -1; - fp = fopen(git_path_bisect_terms(), "w"); - if (!fp) - return error_errno(_("could not open the file BISECT_TERMS")); + res = write_to_file(git_path_bisect_terms(), "%s\n%s\n", bad, good); - res = fprintf(fp, "%s\n%s\n", bad, good); - res |= fclose(fp); - return (res < 0) ? -1 : 0; + return res; } static int is_expected_rev(const char *expected_hex) @@ -421,6 +473,142 @@ finish: return res; } +static int add_bisect_ref(const char *refname, const struct object_id *oid, + int flags, void *cb) +{ + struct add_bisect_ref_data *data = cb; + + add_pending_oid(data->revs, refname, oid, data->object_flags); + + return 0; +} + +static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs) +{ + int res = 0; + struct add_bisect_ref_data cb = { revs }; + char *good = xstrfmt("%s-*", terms->term_good); + + /* + * We cannot use terms->term_bad directly in + * for_each_glob_ref_in() and we have to append a '*' to it, + * otherwise for_each_glob_ref_in() will append '/' and '*'. + */ + char *bad = xstrfmt("%s*", terms->term_bad); + + /* + * It is important to reset the flags used by revision walks + * as the previous call to bisect_next_all() in turn + * sets up a revision walk. + */ + reset_revision_walk(); + init_revisions(revs, NULL); + setup_revisions(0, NULL, revs, NULL); + for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb); + cb.object_flags = UNINTERESTING; + for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb); + if (prepare_revision_walk(revs)) + res = error(_("revision walk setup failed\n")); + + free(good); + free(bad); + return res; +} + +static int bisect_skipped_commits(struct bisect_terms *terms) +{ + int res; + FILE *fp = NULL; + struct rev_info revs; + struct commit *commit; + struct pretty_print_context pp = {0}; + struct strbuf commit_name = STRBUF_INIT; + + res = prepare_revs(terms, &revs); + if (res) + return res; + + fp = fopen(git_path_bisect_log(), "a"); + if (!fp) + return error_errno(_("could not open '%s' for appending"), + git_path_bisect_log()); + + if (fprintf(fp, "# only skipped commits left to test\n") < 0) + return error_errno(_("failed to write to '%s'"), git_path_bisect_log()); + + while ((commit = get_revision(&revs)) != NULL) { + strbuf_reset(&commit_name); + format_commit_message(commit, "%s", + &commit_name, &pp); + fprintf(fp, "# possible first %s commit: [%s] %s\n", + terms->term_bad, oid_to_hex(&commit->object.oid), + commit_name.buf); + } + + /* + * Reset the flags used by revision walks in case + * there is another revision walk after this one. + */ + reset_revision_walk(); + + strbuf_release(&commit_name); + fclose(fp); + return 0; +} + +static int bisect_successful(struct bisect_terms *terms) +{ + struct object_id oid; + struct commit *commit; + struct pretty_print_context pp = {0}; + struct strbuf commit_name = STRBUF_INIT; + char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad); + int res; + + read_ref(bad_ref, &oid); + commit = lookup_commit_reference_by_name(bad_ref); + format_commit_message(commit, "%s", &commit_name, &pp); + + res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n", + terms->term_bad, oid_to_hex(&commit->object.oid), + commit_name.buf); + + strbuf_release(&commit_name); + free(bad_ref); + return res; +} + +static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix) +{ + enum bisect_error res; + + if (bisect_autostart(terms)) + return BISECT_FAILED; + + if (bisect_next_check(terms, terms->term_good)) + return BISECT_FAILED; + + /* Perform all bisection computation */ + res = bisect_next_all(the_repository, prefix); + + if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { + res = bisect_successful(terms); + return res ? res : BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND; + } else if (res == BISECT_ONLY_SKIPPED_LEFT) { + res = bisect_skipped_commits(terms); + return res ? res : BISECT_ONLY_SKIPPED_LEFT; + } + return res; +} + +static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix) +{ + if (bisect_next_check(terms, NULL)) + return BISECT_OK; + + return bisect_next(terms, prefix); +} + static int bisect_start(struct bisect_terms *terms, const char **argv, int argc) { int no_checkout = 0; @@ -484,14 +672,13 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc) terms->term_bad = xstrdup(arg); } else if (starts_with(arg, "--")) { return error(_("unrecognized option: '%s'"), arg); - } else { - char *commit_id = xstrfmt("%s^{commit}", arg); - if (get_oid(commit_id, &oid) && has_double_dash) - die(_("'%s' does not appear to be a valid " - "revision"), arg); - + } else if (!get_oidf(&oid, "%s^{commit}", arg)) { string_list_append(&revs, oid_to_hex(&oid)); - free(commit_id); + } else if (has_double_dash) { + die(_("'%s' does not appear to be a valid " + "revision"), arg); + } else { + break; } } pathspec_pos = i; @@ -624,6 +811,38 @@ finish: return res; } +static inline int file_is_not_empty(const char *path) +{ + return !is_empty_or_missing_file(path); +} + +static int bisect_autostart(struct bisect_terms *terms) +{ + int res; + const char *yesno; + + if (file_is_not_empty(git_path_bisect_start())) + return 0; + + fprintf_ln(stderr, _("You need to start by \"git bisect " + "start\"\n")); + + if (!isatty(STDIN_FILENO)) + return -1; + + /* + * TRANSLATORS: Make sure to include [Y] and [n] in your + * translation. The program will only accept English input + * at this point. + */ + yesno = git_prompt(_("Do you want me to do it for you " + "[Y/n]? "), PROMPT_ECHO); + res = tolower(*yesno) == 'n' ? + -1 : bisect_start(terms, empty_strvec, 0); + + return res; +} + int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { enum { @@ -636,7 +855,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) CHECK_AND_SET_TERMS, BISECT_NEXT_CHECK, BISECT_TERMS, - BISECT_START + BISECT_START, + BISECT_AUTOSTART, + BISECT_NEXT, + BISECT_AUTO_NEXT } cmdmode = 0; int res = 0, nolog = 0; struct option options[] = { @@ -660,6 +882,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) N_("print out the bisect terms"), BISECT_TERMS), OPT_CMDMODE(0, "bisect-start", &cmdmode, N_("start the bisect session"), BISECT_START), + OPT_CMDMODE(0, "bisect-next", &cmdmode, + N_("find the next bisection commit"), BISECT_NEXT), + OPT_CMDMODE(0, "bisect-auto-next", &cmdmode, + N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT), + OPT_CMDMODE(0, "bisect-autostart", &cmdmode, + N_("start the bisection if it has not yet been started"), BISECT_AUTOSTART), OPT_BOOL(0, "no-log", &nolog, N_("no log for BISECT_WRITE")), OPT_END() @@ -719,8 +947,26 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) set_terms(&terms, "bad", "good"); res = bisect_start(&terms, argv, argc); break; + case BISECT_NEXT: + if (argc) + return error(_("--bisect-next requires 0 arguments")); + get_terms(&terms); + res = bisect_next(&terms, prefix); + break; + case BISECT_AUTO_NEXT: + if (argc) + return error(_("--bisect-auto-next requires 0 arguments")); + get_terms(&terms); + res = bisect_auto_next(&terms, prefix); + break; + case BISECT_AUTOSTART: + if (argc) + return error(_("--bisect-autostart does not accept arguments")); + set_terms(&terms, "bad", "good"); + res = bisect_autostart(&terms); + break; default: - return error("BUG: unknown subcommand '%d'", cmdmode); + BUG("unknown subcommand %d", cmdmode); } free_terms(&terms); @@ -728,8 +974,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) * Handle early success * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all */ - if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) + if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND)) res = BISECT_OK; - return abs(res); + return -res; } diff --git a/builtin/blame.c b/builtin/blame.c index eb513fbe60..bb0f29300e 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -27,6 +27,7 @@ #include "object-store.h" #include "blame.h" #include "refs.h" +#include "tag.h" static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"); @@ -803,6 +804,26 @@ static int is_a_rev(const char *name) return OBJ_NONE < oid_object_info(the_repository, &oid, NULL); } +static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata) +{ + struct repository *r = ((struct blame_scoreboard *)cbdata)->repo; + struct object_id oid; + + oidcpy(&oid, oid_ret); + while (1) { + struct object *obj; + int kind = oid_object_info(r, &oid, NULL); + if (kind == OBJ_COMMIT) { + oidcpy(oid_ret, &oid); + return 0; + } + if (kind != OBJ_TAG) + return -1; + obj = deref_tag(r, parse_object(r, &oid), NULL, 0); + oidcpy(&oid, &obj->oid); + } +} + static void build_ignorelist(struct blame_scoreboard *sb, struct string_list *ignore_revs_file_list, struct string_list *ignore_rev_list) @@ -815,10 +836,12 @@ static void build_ignorelist(struct blame_scoreboard *sb, if (!strcmp(i->string, "")) oidset_clear(&sb->ignore_list); else - oidset_parse_file(&sb->ignore_list, i->string); + oidset_parse_file_carefully(&sb->ignore_list, i->string, + peel_to_commit_oid, sb); } for_each_string_list_item(i, ignore_rev_list) { - if (get_oid_committish(i->string, &oid)) + if (get_oid_committish(i->string, &oid) || + peel_to_commit_oid(&oid, sb)) die(_("cannot find revision %s to ignore"), i->string); oidset_insert(&sb->ignore_list, &oid); } diff --git a/builtin/clone.c b/builtin/clone.c index fbfd6568cd..391aa41075 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -1233,7 +1233,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) * Now that we know what algorithm the remote side is using, * let's set ours to the same thing. */ - initialize_repository_version(hash_algo); + initialize_repository_version(hash_algo, 1); repo_set_hash_algo(the_repository, hash_algo); mapped_refs = wanted_peer_refs(refs, &remote->fetch); diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 523501f217..78fa08f43a 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -13,7 +13,8 @@ static char const * const builtin_commit_graph_usage[] = { N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"), N_("git commit-graph write [--object-dir <objdir>] [--append] " "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " - "[--changed-paths] [--[no-]progress] <split options>"), + "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " + "<split options>"), NULL }; @@ -25,7 +26,8 @@ static const char * const builtin_commit_graph_verify_usage[] = { static const char * const builtin_commit_graph_write_usage[] = { N_("git commit-graph write [--object-dir <objdir>] [--append] " "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " - "[--changed-paths] [--[no-]progress] <split options>"), + "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " + "<split options>"), NULL }; @@ -106,7 +108,7 @@ static int graph_verify(int argc, const char **argv) FREE_AND_NULL(graph_name); if (open_ok) - graph = load_commit_graph_one_fd_st(fd, &st, odb); + graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb); else graph = read_commit_graph_one(the_repository, odb); @@ -119,13 +121,15 @@ static int graph_verify(int argc, const char **argv) } extern int read_replace_refs; -static struct split_commit_graph_opts split_opts; +static struct commit_graph_opts write_opts; static int write_option_parse_split(const struct option *opt, const char *arg, int unset) { enum commit_graph_split_flags *flags = opt->value; + BUG_ON_OPT_NEG(unset); + opts.split = 1; if (!arg) return 0; @@ -162,6 +166,35 @@ static int read_one_commit(struct oidset *commits, struct progress *progress, return 0; } +static int write_option_max_new_filters(const struct option *opt, + const char *arg, + int unset) +{ + int *to = opt->value; + if (unset) + *to = -1; + else { + const char *s; + *to = strtol(arg, (char **)&s, 10); + if (*s) + return error(_("%s expects a numerical value"), + optname(opt, opt->flags)); + } + return 0; +} + +static int git_commit_graph_write_config(const char *var, const char *value, + void *cb) +{ + if (!strcmp(var, "commitgraph.maxnewfilters")) + write_opts.max_new_filters = git_config_int(var, value); + /* + * No need to fall-back to 'git_default_config', since this was already + * called in 'cmd_commit_graph()'. + */ + return 0; +} + static int graph_write(int argc, const char **argv) { struct string_list pack_indexes = STRING_LIST_INIT_NODUP; @@ -187,27 +220,33 @@ static int graph_write(int argc, const char **argv) OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths, N_("enable computation for changed paths")), OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")), - OPT_CALLBACK_F(0, "split", &split_opts.flags, NULL, + OPT_CALLBACK_F(0, "split", &write_opts.split_flags, NULL, N_("allow writing an incremental commit-graph file"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, write_option_parse_split), - OPT_INTEGER(0, "max-commits", &split_opts.max_commits, + OPT_INTEGER(0, "max-commits", &write_opts.max_commits, N_("maximum number of commits in a non-base split commit-graph")), - OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple, + OPT_INTEGER(0, "size-multiple", &write_opts.size_multiple, N_("maximum ratio between two levels of a split commit-graph")), - OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time, + OPT_EXPIRY_DATE(0, "expire-time", &write_opts.expire_time, N_("only expire files older than a given date-time")), + OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters, + NULL, N_("maximum number of changed-path Bloom filters to compute"), + 0, write_option_max_new_filters), OPT_END(), }; opts.progress = isatty(2); opts.enable_changed_paths = -1; - split_opts.size_multiple = 2; - split_opts.max_commits = 0; - split_opts.expire_time = 0; + write_opts.size_multiple = 2; + write_opts.max_commits = 0; + write_opts.expire_time = 0; + write_opts.max_new_filters = -1; trace2_cmd_mode("write"); + git_config(git_commit_graph_write_config, &opts); + argc = parse_options(argc, argv, NULL, builtin_commit_graph_write_options, builtin_commit_graph_write_usage, 0); @@ -232,7 +271,7 @@ static int graph_write(int argc, const char **argv) odb = find_odb(the_repository, opts.obj_dir); if (opts.reachable) { - if (write_commit_graph_reachable(odb, flags, &split_opts)) + if (write_commit_graph_reachable(odb, flags, &write_opts)) return 1; return 0; } @@ -261,7 +300,7 @@ static int graph_write(int argc, const char **argv) opts.stdin_packs ? &pack_indexes : NULL, opts.stdin_commits ? &commits : NULL, flags, - &split_opts)) + &write_opts)) result = 1; cleanup: diff --git a/builtin/commit.c b/builtin/commit.c index 42b964e0ca..1dfd799ec5 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -326,7 +326,7 @@ static void refresh_cache_or_die(int refresh_flags) die_resolve_conflict("commit"); } -static const char *prepare_index(int argc, const char **argv, const char *prefix, +static const char *prepare_index(const char **argv, const char *prefix, const struct commit *current_head, int is_status) { struct string_list partial = STRING_LIST_INIT_DUP; @@ -378,7 +378,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(argc, argv, prefix, patch_interactive) != 0) + if (interactive_add(argv, prefix, patch_interactive) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -1241,13 +1241,13 @@ static int parse_and_validate_options(int argc, const char *argv[], return argc; } -static int dry_run_commit(int argc, const char **argv, const char *prefix, +static int dry_run_commit(const char **argv, const char *prefix, const struct commit *current_head, struct wt_status *s) { int committable; const char *index_file; - index_file = prepare_index(argc, argv, prefix, current_head, 1); + index_file = prepare_index(argv, prefix, current_head, 1); committable = run_status(stdout, index_file, prefix, 0, s); rollback_index_files(); @@ -1584,8 +1584,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose; if (dry_run) - return dry_run_commit(argc, argv, prefix, current_head, &s); - index_file = prepare_index(argc, argv, prefix, current_head, 0); + return dry_run_commit(argv, prefix, current_head, &s); + index_file = prepare_index(argv, prefix, current_head, 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ diff --git a/builtin/diff.c b/builtin/diff.c index cb98811c21..cd4083fed9 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -203,8 +203,7 @@ static int builtin_diff_combined(struct rev_info *revs, revs->dense_combined_merges = revs->combine_merges = 1; for (i = 1; i < ents; i++) oid_array_append(&parents, &ent[i].item->oid); - diff_tree_combined(&ent[0].item->oid, &parents, - revs->dense_combined_merges, revs); + diff_tree_combined(&ent[0].item->oid, &parents, revs); oid_array_clear(&parents); return 0; } diff --git a/builtin/env--helper.c b/builtin/env--helper.c index 23c214fff6..27349098b0 100644 --- a/builtin/env--helper.c +++ b/builtin/env--helper.c @@ -7,18 +7,22 @@ static char const * const env__helper_usage[] = { NULL }; -static enum { +enum cmdmode { ENV_HELPER_TYPE_BOOL = 1, ENV_HELPER_TYPE_ULONG -} cmdmode = 0; +}; static int option_parse_type(const struct option *opt, const char *arg, int unset) { + enum cmdmode *cmdmode = opt->value; + + BUG_ON_OPT_NEG(unset); + if (!strcmp(arg, "bool")) - cmdmode = ENV_HELPER_TYPE_BOOL; + *cmdmode = ENV_HELPER_TYPE_BOOL; else if (!strcmp(arg, "ulong")) - cmdmode = ENV_HELPER_TYPE_ULONG; + *cmdmode = ENV_HELPER_TYPE_ULONG; else die(_("unrecognized --type argument, %s"), arg); @@ -33,6 +37,7 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix) int ret; int ret_int, default_int; unsigned long ret_ulong, default_ulong; + enum cmdmode cmdmode = 0; struct option opts[] = { OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"), N_("value is given this type"), PARSE_OPT_NONEG, diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 1b8fca3ee0..d2e33f5005 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -405,12 +405,12 @@ static char *generate_fake_oid(void *data) { static uint32_t counter = 1; /* avoid null oid */ const unsigned hashsz = the_hash_algo->rawsz; - unsigned char out[GIT_MAX_RAWSZ]; + struct object_id oid; char *hex = xmallocz(GIT_MAX_HEXSZ); - hashclr(out); - put_be32(out + hashsz - 4, counter++); - return hash_to_hex_algop_r(hex, out, the_hash_algo); + oidclr(&oid); + put_be32(oid.hash + hashsz - 4, counter++); + return oid_to_hex_r(hex, &oid); } static const char *anonymize_oid(const char *oid_hex) @@ -1026,7 +1026,7 @@ static void handle_tags_and_duplicates(struct string_list *extras) /* * Getting here means we have a commit which * was excluded by a negative refspec (e.g. - * fast-export ^master master). If we are + * fast-export ^HEAD HEAD). If we are * referencing excluded commits, set the ref * to the exact commit. Otherwise, the user * wants the branch exported but every commit diff --git a/builtin/fetch.c b/builtin/fetch.c index d5bf526277..f9c3c49f14 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -539,6 +539,16 @@ static struct ref *get_ref_map(struct remote *remote, tail = &rm->next; } + /* + * apply negative refspecs first, before we remove duplicates. This is + * necessary as negative refspecs might remove an otherwise conflicting + * duplicate. + */ + if (rs->nr) + ref_map = apply_negative_refspecs(ref_map, rs); + else + ref_map = apply_negative_refspecs(ref_map, &remote->fetch); + ref_map = ref_remove_duplicates(ref_map); for (rm = ref_map; rm; rm = rm->next) { @@ -1684,7 +1694,7 @@ static inline void fetch_one_setup_partial(struct remote *remote) * If this is a partial-fetch request, we enable partial on * this repo if not already enabled and remember the given * filter-spec as the default for subsequent fetches to this - * remote. + * remote if there is currently no default filter-spec. */ if (filter_options.choice) { partial_clone_register(remote->name, &filter_options); diff --git a/builtin/init-db.c b/builtin/init-db.c index cd3e760541..01bc648d41 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -179,7 +179,7 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree) return 1; } -void initialize_repository_version(int hash_algo) +void initialize_repository_version(int hash_algo, int reinit) { char repo_version_string[10]; int repo_version = GIT_REPO_VERSION; @@ -195,6 +195,8 @@ void initialize_repository_version(int hash_algo) if (hash_algo != GIT_HASH_SHA1) git_config_set("extensions.objectformat", hash_algos[hash_algo].name); + else if (reinit) + git_config_set_gently("extensions.objectformat", NULL); } static int create_default_files(const char *template_path, @@ -277,7 +279,7 @@ static int create_default_files(const char *template_path, free(ref); } - initialize_repository_version(fmt->hash_algo); + initialize_repository_version(fmt->hash_algo, 0); /* Check filemode trustability */ path = git_path_buf(&buf, "config"); diff --git a/builtin/log.c b/builtin/log.c index 55a4fd9eaf..0a7ed4bef9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1205,6 +1205,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, log.in1 = 2; log.in2 = 4; log.file = rev->diffopt.file; + log.groups = SHORTLOG_GROUP_AUTHOR; for (i = 0; i < nr; i++) shortlog_add_commit(&log, list[i]); diff --git a/builtin/pull.c b/builtin/pull.c index 015f6ded0b..425950f469 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -344,8 +344,7 @@ static enum rebase_type config_get_rebase(void) if (!git_config_get_value("pull.rebase", &value)) return parse_config_rebase("pull.rebase", value, 1); - if (opt_verbosity >= 0 && - (!opt_ff || strcmp(opt_ff, "--ff-only"))) { + if (opt_verbosity >= 0 && !opt_ff) { warning(_("Pulling without specifying how to reconcile divergent branches is\n" "discouraged. You can squelch this message by running one of the following\n" "commands sometime before your next pull:\n" diff --git a/builtin/push.c b/builtin/push.c index 0eeb2c8dd5..6da3a8e5d3 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -379,7 +379,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs, return 1; } -static int do_push(const char *repo, int flags, +static int do_push(int flags, const struct string_list *push_options, struct remote *remote) { @@ -629,7 +629,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) if (strchr(item->string, '\n')) die(_("push options must not have new line characters")); - rc = do_push(repo, flags, push_options, remote); + rc = do_push(flags, push_options, remote); string_list_clear(&push_options_cmdline, 0); string_list_clear(&push_options_config, 0); if (rc == -1) diff --git a/builtin/shortlog.c b/builtin/shortlog.c index c856c58bb5..0a5c4968f6 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -9,6 +9,7 @@ #include "mailmap.h" #include "shortlog.h" #include "parse-options.h" +#include "trailer.h" static char const * const shortlog_usage[] = { N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"), @@ -49,12 +50,12 @@ static int compare_by_list(const void *a1, const void *a2) } static void insert_one_record(struct shortlog *log, - const char *author, + const char *ident, const char *oneline) { struct string_list_item *item; - item = string_list_insert(&log->list, author); + item = string_list_insert(&log->list, ident); if (log->summary) item->util = (void *)(UTIL_TO_INT(item) + 1); @@ -97,8 +98,8 @@ static void insert_one_record(struct shortlog *log, } } -static int parse_stdin_author(struct shortlog *log, - struct strbuf *out, const char *in) +static int parse_ident(struct shortlog *log, + struct strbuf *out, const char *in) { const char *mailbuf, *namebuf; size_t namelen, maillen; @@ -122,18 +123,33 @@ static int parse_stdin_author(struct shortlog *log, static void read_from_stdin(struct shortlog *log) { - struct strbuf author = STRBUF_INIT; - struct strbuf mapped_author = STRBUF_INIT; + struct strbuf ident = STRBUF_INIT; + struct strbuf mapped_ident = STRBUF_INIT; struct strbuf oneline = STRBUF_INIT; static const char *author_match[2] = { "Author: ", "author " }; static const char *committer_match[2] = { "Commit: ", "committer " }; const char **match; - match = log->committer ? committer_match : author_match; - while (strbuf_getline_lf(&author, stdin) != EOF) { + if (HAS_MULTI_BITS(log->groups)) + die(_("using multiple --group options with stdin is not supported")); + + switch (log->groups) { + case SHORTLOG_GROUP_AUTHOR: + match = author_match; + break; + case SHORTLOG_GROUP_COMMITTER: + match = committer_match; + break; + case SHORTLOG_GROUP_TRAILER: + die(_("using --group=trailer with stdin is not supported")); + default: + BUG("unhandled shortlog group"); + } + + while (strbuf_getline_lf(&ident, stdin) != EOF) { const char *v; - if (!skip_prefix(author.buf, match[0], &v) && - !skip_prefix(author.buf, match[1], &v)) + if (!skip_prefix(ident.buf, match[0], &v) && + !skip_prefix(ident.buf, match[1], &v)) continue; while (strbuf_getline_lf(&oneline, stdin) != EOF && oneline.len) @@ -142,23 +158,118 @@ static void read_from_stdin(struct shortlog *log) !oneline.len) ; /* discard blanks */ - strbuf_reset(&mapped_author); - if (parse_stdin_author(log, &mapped_author, v) < 0) + strbuf_reset(&mapped_ident); + if (parse_ident(log, &mapped_ident, v) < 0) continue; - insert_one_record(log, mapped_author.buf, oneline.buf); + insert_one_record(log, mapped_ident.buf, oneline.buf); } - strbuf_release(&author); - strbuf_release(&mapped_author); + strbuf_release(&ident); + strbuf_release(&mapped_ident); strbuf_release(&oneline); } +struct strset_item { + struct hashmap_entry ent; + char value[FLEX_ARRAY]; +}; + +struct strset { + struct hashmap map; +}; + +#define STRSET_INIT { { NULL } } + +static int strset_item_hashcmp(const void *hash_data, + const struct hashmap_entry *entry, + const struct hashmap_entry *entry_or_key, + const void *keydata) +{ + const struct strset_item *a, *b; + + a = container_of(entry, const struct strset_item, ent); + if (keydata) + return strcmp(a->value, keydata); + + b = container_of(entry_or_key, const struct strset_item, ent); + return strcmp(a->value, b->value); +} + +/* + * Adds "str" to the set if it was not already present; returns true if it was + * already there. + */ +static int strset_check_and_add(struct strset *ss, const char *str) +{ + unsigned int hash = strhash(str); + struct strset_item *item; + + if (!ss->map.table) + hashmap_init(&ss->map, strset_item_hashcmp, NULL, 0); + + if (hashmap_get_from_hash(&ss->map, hash, str)) + return 1; + + FLEX_ALLOC_STR(item, value, str); + hashmap_entry_init(&item->ent, hash); + hashmap_add(&ss->map, &item->ent); + return 0; +} + +static void strset_clear(struct strset *ss) +{ + if (!ss->map.table) + return; + hashmap_free_entries(&ss->map, struct strset_item, ent); +} + +static void insert_records_from_trailers(struct shortlog *log, + struct strset *dups, + struct commit *commit, + struct pretty_print_context *ctx, + const char *oneline) +{ + struct trailer_iterator iter; + const char *commit_buffer, *body; + struct strbuf ident = STRBUF_INIT; + + /* + * Using format_commit_message("%B") would be simpler here, but + * this saves us copying the message. + */ + commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding); + body = strstr(commit_buffer, "\n\n"); + if (!body) + return; + + trailer_iterator_init(&iter, body); + while (trailer_iterator_advance(&iter)) { + const char *value = iter.val.buf; + + if (!string_list_has_string(&log->trailers, iter.key.buf)) + continue; + + strbuf_reset(&ident); + if (!parse_ident(log, &ident, value)) + value = ident.buf; + + if (strset_check_and_add(dups, value)) + continue; + insert_one_record(log, value, oneline); + } + trailer_iterator_release(&iter); + + strbuf_release(&ident); + unuse_commit_buffer(commit, commit_buffer); +} + void shortlog_add_commit(struct shortlog *log, struct commit *commit) { - struct strbuf author = STRBUF_INIT; + struct strbuf ident = STRBUF_INIT; struct strbuf oneline = STRBUF_INIT; + struct strset dups = STRSET_INIT; struct pretty_print_context ctx = {0}; - const char *fmt; + const char *oneline_str; ctx.fmt = CMIT_FMT_USERFORMAT; ctx.abbrev = log->abbrev; @@ -166,21 +277,38 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) ctx.date_mode.type = DATE_NORMAL; ctx.output_encoding = get_log_output_encoding(); - fmt = log->committer ? - (log->email ? "%cN <%cE>" : "%cN") : - (log->email ? "%aN <%aE>" : "%aN"); - - format_commit_message(commit, fmt, &author, &ctx); if (!log->summary) { if (log->user_format) pretty_print_commit(&ctx, commit, &oneline); else format_commit_message(commit, "%s", &oneline, &ctx); } + oneline_str = oneline.len ? oneline.buf : "<none>"; + + if (log->groups & SHORTLOG_GROUP_AUTHOR) { + strbuf_reset(&ident); + format_commit_message(commit, + log->email ? "%aN <%aE>" : "%aN", + &ident, &ctx); + if (!HAS_MULTI_BITS(log->groups) || + !strset_check_and_add(&dups, ident.buf)) + insert_one_record(log, ident.buf, oneline_str); + } + if (log->groups & SHORTLOG_GROUP_COMMITTER) { + strbuf_reset(&ident); + format_commit_message(commit, + log->email ? "%cN <%cE>" : "%cN", + &ident, &ctx); + if (!HAS_MULTI_BITS(log->groups) || + !strset_check_and_add(&dups, ident.buf)) + insert_one_record(log, ident.buf, oneline_str); + } + if (log->groups & SHORTLOG_GROUP_TRAILER) { + insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); + } - insert_one_record(log, author.buf, oneline.len ? oneline.buf : "<none>"); - - strbuf_release(&author); + strset_clear(&dups); + strbuf_release(&ident); strbuf_release(&oneline); } @@ -241,6 +369,28 @@ static int parse_wrap_args(const struct option *opt, const char *arg, int unset) return 0; } +static int parse_group_option(const struct option *opt, const char *arg, int unset) +{ + struct shortlog *log = opt->value; + const char *field; + + if (unset) { + log->groups = 0; + string_list_clear(&log->trailers, 0); + } else if (!strcasecmp(arg, "author")) + log->groups |= SHORTLOG_GROUP_AUTHOR; + else if (!strcasecmp(arg, "committer")) + log->groups |= SHORTLOG_GROUP_COMMITTER; + else if (skip_prefix(arg, "trailer:", &field)) { + log->groups |= SHORTLOG_GROUP_TRAILER; + string_list_append(&log->trailers, field); + } else + return error(_("unknown group type: %s"), arg); + + return 0; +} + + void shortlog_init(struct shortlog *log) { memset(log, 0, sizeof(*log)); @@ -251,6 +401,8 @@ void shortlog_init(struct shortlog *log) log->wrap = DEFAULT_WRAPLEN; log->in1 = DEFAULT_INDENT1; log->in2 = DEFAULT_INDENT2; + log->trailers.strdup_strings = 1; + log->trailers.cmp = strcasecmp; } int cmd_shortlog(int argc, const char **argv, const char *prefix) @@ -260,8 +412,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) int nongit = !startup_info->have_repository; const struct option options[] = { - OPT_BOOL('c', "committer", &log.committer, - N_("Group by committer rather than author")), + OPT_BIT('c', "committer", &log.groups, + N_("Group by committer rather than author"), + SHORTLOG_GROUP_COMMITTER), OPT_BOOL('n', "numbered", &log.sort_by_number, N_("sort output according to the number of commits per author")), OPT_BOOL('s', "summary", &log.summary, @@ -271,6 +424,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) OPT_CALLBACK_F('w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"), N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args), + OPT_CALLBACK(0, "group", &log, N_("field"), + N_("Group by field"), parse_group_option), OPT_END(), }; @@ -311,6 +466,10 @@ parse_done: log.abbrev = rev.abbrev; log.file = rev.diffopt.file; + if (!log.groups) + log.groups = SHORTLOG_GROUP_AUTHOR; + string_list_sort(&log.trailers); + /* assume HEAD if from a tty */ if (!nongit && !rev.pending.nr && isatty(0)) add_head_to_pending(&rev); diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 4003f4d13a..e3140db2a0 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -46,12 +46,24 @@ static void write_patterns_to_file(FILE *fp, struct pattern_list *pl) } } +static char const * const builtin_sparse_checkout_list_usage[] = { + N_("git sparse-checkout list"), + NULL +}; + static int sparse_checkout_list(int argc, const char **argv) { + static struct option builtin_sparse_checkout_list_options[] = { + OPT_END(), + }; struct pattern_list pl; char *sparse_filename; int res; + argc = parse_options(argc, argv, NULL, + builtin_sparse_checkout_list_options, + builtin_sparse_checkout_list_usage, 0); + memset(&pl, 0, sizeof(pl)); pl.use_cone_patterns = core_sparse_checkout_cone; @@ -560,17 +572,42 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix, return modify_pattern_list(argc, argv, m); } +static char const * const builtin_sparse_checkout_reapply_usage[] = { + N_("git sparse-checkout reapply"), + NULL +}; + static int sparse_checkout_reapply(int argc, const char **argv) { + static struct option builtin_sparse_checkout_reapply_options[] = { + OPT_END(), + }; + + argc = parse_options(argc, argv, NULL, + builtin_sparse_checkout_reapply_options, + builtin_sparse_checkout_reapply_usage, 0); + repo_read_index(the_repository); return update_working_directory(NULL); } +static char const * const builtin_sparse_checkout_disable_usage[] = { + N_("git sparse-checkout disable"), + NULL +}; + static int sparse_checkout_disable(int argc, const char **argv) { + static struct option builtin_sparse_checkout_disable_options[] = { + OPT_END(), + }; struct pattern_list pl; struct strbuf match_all = STRBUF_INIT; + argc = parse_options(argc, argv, NULL, + builtin_sparse_checkout_disable_options, + builtin_sparse_checkout_disable_usage, 0); + repo_read_index(the_repository); memset(&pl, 0, sizeof(pl)); |
