diff options
Diffstat (limited to 'builtin')
51 files changed, 1096 insertions, 278 deletions
diff --git a/builtin/add.c b/builtin/add.c index d461ba08b9..db2dfa4350 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -3,6 +3,7 @@ * * Copyright (C) 2006 Linus Torvalds */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "builtin.h" @@ -137,7 +138,7 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) continue; /* do not touch non blobs */ if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) continue; - retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE); + retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE); } return retval; diff --git a/builtin/am.c b/builtin/am.c index 95370313b6..58a2aef28b 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -3,6 +3,7 @@ * * Based on git-am.sh by Junio C Hamano. */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "builtin.h" @@ -35,22 +36,6 @@ #include "repository.h" /** - * Returns 1 if the file is empty or does not exist, 0 otherwise. - */ -static int is_empty_file(const char *filename) -{ - struct stat st; - - if (stat(filename, &st) < 0) { - if (errno == ENOENT) - return 1; - die_errno(_("could not stat %s"), filename); - } - - return !st.st_size; -} - -/** * Returns the length of the first line of msg. */ static int linelen(const char *msg) @@ -527,7 +512,7 @@ static int copy_notes_for_rebase(const struct am_state *state) } finish: - finish_copy_notes_for_rewrite(c, msg); + finish_copy_notes_for_rewrite(the_repository, c, msg); fclose(fp); strbuf_release(&sb); return ret; @@ -1220,7 +1205,7 @@ static int parse_mail(struct am_state *state, const char *mail) goto finish; } - if (is_empty_file(am_path(state, "patch"))) { + if (is_empty_or_missing_file(am_path(state, "patch"))) { printf_ln(_("Patch is empty.")); die_user_resolve(state); } @@ -1545,7 +1530,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa * changes. */ - init_merge_options(&o); + init_merge_options(&o, the_repository); o.branch1 = "HEAD"; their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg); @@ -1719,7 +1704,7 @@ static void am_run(struct am_state *state, int resume) refresh_and_write_cache(); - if (index_has_changes(&the_index, NULL, &sb)) { + if (repo_index_has_changes(the_repository, NULL, &sb)) { write_state_bool(state, "dirtyindex", 1); die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf); } @@ -1777,7 +1762,7 @@ static void am_run(struct am_state *state, int resume) * the result may have produced the same tree as ours. */ if (!apply_status && - !index_has_changes(&the_index, NULL, NULL)) { + !repo_index_has_changes(the_repository, NULL, NULL)) { say(state, stdout, _("No changes -- Patch already applied.")); goto next; } @@ -1803,7 +1788,7 @@ next: resume = 0; } - if (!is_empty_file(am_path(state, "rewritten"))) { + if (!is_empty_or_missing_file(am_path(state, "rewritten"))) { assert(state->rebasing); copy_notes_for_rebase(state); run_post_rewrite_hook(state); @@ -1831,7 +1816,7 @@ static void am_resolve(struct am_state *state) say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg); - if (!index_has_changes(&the_index, NULL, NULL)) { + if (!repo_index_has_changes(the_repository, NULL, NULL)) { printf_ln(_("No changes - did you forget to use 'git add'?\n" "If there is nothing left to stage, chances are that something else\n" "already introduced the same changes; you might want to skip this patch.")); @@ -2000,6 +1985,15 @@ static void am_skip(struct am_state *state) if (clean_index(&head, &head)) die(_("failed to clean index")); + if (state->rebasing) { + FILE *fp = xfopen(am_path(state, "rewritten"), "a"); + + assert(!is_null_oid(&state->orig_commit)); + fprintf(fp, "%s ", oid_to_hex(&state->orig_commit)); + fprintf(fp, "%s\n", oid_to_hex(&head)); + fclose(fp); + } + am_next(state); am_load(state); am_run(state, 0); @@ -2278,7 +2272,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) /* Ensure a valid committer ident can be constructed */ git_committer_info(IDENT_STRICT); - if (read_index_preload(&the_index, NULL, 0) < 0) + if (repo_read_index_preload(the_repository, NULL, 0) < 0) die(_("failed to read the index")); if (in_progress) { diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 417d141c09..c1cff32661 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -3,18 +3,58 @@ #include "parse-options.h" #include "bisect.h" #include "refs.h" +#include "dir.h" +#include "argv-array.h" +#include "run-command.h" +#include "prompt.h" +#include "quote.h" static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") +static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") +static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD") +static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") +static GIT_PATH_FUNC(git_path_head_name, "head-name") +static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --next-all [--no-checkout]"), N_("git bisect--helper --write-terms <bad_term> <good_term>"), N_("git bisect--helper --bisect-clean-state"), + N_("git bisect--helper --bisect-reset [<commit>]"), + N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"), + N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"), + N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"), + N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"), + N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]" + "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"), NULL }; +struct bisect_terms { + char *term_good; + char *term_bad; +}; + +static void free_terms(struct bisect_terms *terms) +{ + FREE_AND_NULL(terms->term_good); + FREE_AND_NULL(terms->term_bad); +} + +static void set_terms(struct bisect_terms *terms, const char *bad, + const char *good) +{ + free((void *)terms->term_good); + terms->term_good = xstrdup(good); + free((void *)terms->term_bad); + terms->term_bad = xstrdup(bad); +} + +static const char *vocab_bad = "bad|new"; +static const char *vocab_good = "good|old"; + /* * Check whether the string `term` belongs to the set of strings * included in the variable arguments. @@ -106,15 +146,482 @@ static void check_expected_revs(const char **revs, int rev_nr) } } +static int bisect_reset(const char *commit) +{ + struct strbuf branch = STRBUF_INIT; + + if (!commit) { + if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) { + printf(_("We are not bisecting.\n")); + return 0; + } + strbuf_rtrim(&branch); + } else { + struct object_id oid; + + if (get_oid_commit(commit, &oid)) + return error(_("'%s' is not a valid commit"), commit); + strbuf_addstr(&branch, commit); + } + + if (!file_exists(git_path_bisect_head())) { + struct argv_array argv = ARGV_ARRAY_INIT; + + argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL); + if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) { + strbuf_release(&branch); + argv_array_clear(&argv); + return error(_("could not check out original" + " HEAD '%s'. Try 'git bisect" + "reset <commit>'."), branch.buf); + } + argv_array_clear(&argv); + } + + strbuf_release(&branch); + return bisect_clean_state(); +} + +static void log_commit(FILE *fp, char *fmt, const char *state, + struct commit *commit) +{ + struct pretty_print_context pp = {0}; + struct strbuf commit_msg = STRBUF_INIT; + char *label = xstrfmt(fmt, state); + + format_commit_message(commit, "%s", &commit_msg, &pp); + + fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid), + commit_msg.buf); + + strbuf_release(&commit_msg); + free(label); +} + +static int bisect_write(const char *state, const char *rev, + const struct bisect_terms *terms, int nolog) +{ + struct strbuf tag = STRBUF_INIT; + struct object_id oid; + struct commit *commit; + FILE *fp = NULL; + int retval = 0; + + if (!strcmp(state, terms->term_bad)) { + strbuf_addf(&tag, "refs/bisect/%s", state); + } else if (one_of(state, terms->term_good, "skip", NULL)) { + strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev); + } else { + retval = error(_("Bad bisect_write argument: %s"), state); + goto finish; + } + + if (get_oid(rev, &oid)) { + retval = error(_("couldn't get the oid of the rev '%s'"), rev); + goto finish; + } + + if (update_ref(NULL, tag.buf, &oid, NULL, 0, + UPDATE_REFS_MSG_ON_ERR)) { + retval = -1; + goto finish; + } + + fp = fopen(git_path_bisect_log(), "a"); + if (!fp) { + retval = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log()); + goto finish; + } + + commit = lookup_commit_reference(the_repository, &oid); + log_commit(fp, "%s", state, commit); + + if (!nolog) + fprintf(fp, "git bisect %s %s\n", state, rev); + +finish: + if (fp) + fclose(fp); + strbuf_release(&tag); + return retval; +} + +static int check_and_set_terms(struct bisect_terms *terms, const char *cmd) +{ + int has_term_file = !is_empty_or_missing_file(git_path_bisect_terms()); + + if (one_of(cmd, "skip", "start", "terms", NULL)) + return 0; + + if (has_term_file && strcmp(cmd, terms->term_bad) && + strcmp(cmd, terms->term_good)) + return error(_("Invalid command: you're currently in a " + "%s/%s bisect"), terms->term_bad, + terms->term_good); + + if (!has_term_file) { + if (one_of(cmd, "bad", "good", NULL)) { + set_terms(terms, "bad", "good"); + return write_terms(terms->term_bad, terms->term_good); + } + if (one_of(cmd, "new", "old", NULL)) { + set_terms(terms, "new", "old"); + return write_terms(terms->term_bad, terms->term_good); + } + } + + return 0; +} + +static int mark_good(const char *refname, const struct object_id *oid, + int flag, void *cb_data) +{ + int *m_good = (int *)cb_data; + *m_good = 0; + return 1; +} + +static const char *need_bad_and_good_revision_warning = + N_("You need to give me at least one %s and %s revision.\n" + "You can use \"git bisect %s\" and \"git bisect %s\" for that."); + +static const char *need_bisect_start_warning = + N_("You need to start by \"git bisect start\".\n" + "You then need to give me at least one %s and %s revision.\n" + "You can use \"git bisect %s\" and \"git bisect %s\" for that."); + +static int bisect_next_check(const struct bisect_terms *terms, + const char *current_term) +{ + int missing_good = 1, missing_bad = 1, retval = 0; + const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad); + const char *good_glob = xstrfmt("%s-*", terms->term_good); + + if (ref_exists(bad_ref)) + missing_bad = 0; + + for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/", + (void *) &missing_good); + + if (!missing_good && !missing_bad) + goto finish; + + if (!current_term) { + retval = -1; + goto finish; + } + + if (missing_good && !missing_bad && + !strcmp(current_term, terms->term_good)) { + char *yesno; + /* + * have bad (or new) but not good (or old). We could bisect + * although this is less optimum. + */ + warning(_("bisecting only with a %s commit"), terms->term_bad); + if (!isatty(0)) + goto finish; + /* + * TRANSLATORS: Make sure to include [Y] and [n] in your + * translation. The program will only accept English input + * at this point. + */ + yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO); + if (starts_with(yesno, "N") || starts_with(yesno, "n")) + retval = -1; + goto finish; + } + if (!is_empty_or_missing_file(git_path_bisect_start())) { + retval = error(_(need_bad_and_good_revision_warning), + vocab_bad, vocab_good, vocab_bad, vocab_good); + } else { + retval = error(_(need_bisect_start_warning), + vocab_good, vocab_bad, vocab_good, vocab_bad); + } + +finish: + free((void *) good_glob); + free((void *) bad_ref); + return retval; +} + +static int get_terms(struct bisect_terms *terms) +{ + struct strbuf str = STRBUF_INIT; + FILE *fp = NULL; + int res = 0; + + fp = fopen(git_path_bisect_terms(), "r"); + if (!fp) { + res = -1; + goto finish; + } + + free_terms(terms); + strbuf_getline_lf(&str, fp); + terms->term_bad = strbuf_detach(&str, NULL); + strbuf_getline_lf(&str, fp); + terms->term_good = strbuf_detach(&str, NULL); + +finish: + if (fp) + fclose(fp); + strbuf_release(&str); + return res; +} + +static int bisect_terms(struct bisect_terms *terms, const char *option) +{ + if (get_terms(terms)) + return error(_("no terms defined")); + + if (option == NULL) { + printf(_("Your current terms are %s for the old state\n" + "and %s for the new state.\n"), + terms->term_good, terms->term_bad); + return 0; + } + if (one_of(option, "--term-good", "--term-old", NULL)) + printf("%s\n", terms->term_good); + else if (one_of(option, "--term-bad", "--term-new", NULL)) + printf("%s\n", terms->term_bad); + else + return error(_("invalid argument %s for 'git bisect terms'.\n" + "Supported options are: " + "--term-good|--term-old and " + "--term-bad|--term-new."), option); + + return 0; +} + +static int bisect_append_log_quoted(const char **argv) +{ + int retval = 0; + FILE *fp = fopen(git_path_bisect_log(), "a"); + struct strbuf orig_args = STRBUF_INIT; + + if (!fp) + return -1; + + if (fprintf(fp, "git bisect start") < 1) { + retval = -1; + goto finish; + } + + sq_quote_argv(&orig_args, argv); + if (fprintf(fp, "%s\n", orig_args.buf) < 1) + retval = -1; + +finish: + fclose(fp); + strbuf_release(&orig_args); + return retval; +} + +static int bisect_start(struct bisect_terms *terms, int no_checkout, + const char **argv, int argc) +{ + int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0; + int flags, pathspec_pos, retval = 0; + struct string_list revs = STRING_LIST_INIT_DUP; + struct string_list states = STRING_LIST_INIT_DUP; + struct strbuf start_head = STRBUF_INIT; + struct strbuf bisect_names = STRBUF_INIT; + struct object_id head_oid; + struct object_id oid; + const char *head; + + if (is_bare_repository()) + no_checkout = 1; + + /* + * Check for one bad and then some good revisions + */ + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + has_double_dash = 1; + break; + } + } + + for (i = 0; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(argv[i], "--")) { + break; + } else if (!strcmp(arg, "--no-checkout")) { + no_checkout = 1; + } else if (!strcmp(arg, "--term-good") || + !strcmp(arg, "--term-old")) { + must_write_terms = 1; + free((void *) terms->term_good); + terms->term_good = xstrdup(argv[++i]); + } else if (skip_prefix(arg, "--term-good=", &arg) || + skip_prefix(arg, "--term-old=", &arg)) { + must_write_terms = 1; + free((void *) terms->term_good); + terms->term_good = xstrdup(arg); + } else if (!strcmp(arg, "--term-bad") || + !strcmp(arg, "--term-new")) { + must_write_terms = 1; + free((void *) terms->term_bad); + terms->term_bad = xstrdup(argv[++i]); + } else if (skip_prefix(arg, "--term-bad=", &arg) || + skip_prefix(arg, "--term-new=", &arg)) { + must_write_terms = 1; + free((void *) terms->term_bad); + terms->term_bad = xstrdup(arg); + } else if (starts_with(arg, "--") && + !one_of(arg, "--term-good", "--term-bad", NULL)) { + 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); + + string_list_append(&revs, oid_to_hex(&oid)); + free(commit_id); + } + } + pathspec_pos = i; + + /* + * The user ran "git bisect start <sha1> <sha1>", hence did not + * explicitly specify the terms, but we are already starting to + * set references named with the default terms, and won't be able + * to change afterwards. + */ + if (revs.nr) + must_write_terms = 1; + for (i = 0; i < revs.nr; i++) { + if (bad_seen) { + string_list_append(&states, terms->term_good); + } else { + bad_seen = 1; + string_list_append(&states, terms->term_bad); + } + } + + /* + * Verify HEAD + */ + head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags); + if (!head) + if (get_oid("HEAD", &head_oid)) + return error(_("bad HEAD - I need a HEAD")); + + /* + * Check if we are bisecting + */ + if (!is_empty_or_missing_file(git_path_bisect_start())) { + /* Reset to the rev from where we started */ + strbuf_read_file(&start_head, git_path_bisect_start(), 0); + strbuf_trim(&start_head); + if (!no_checkout) { + struct argv_array argv = ARGV_ARRAY_INIT; + + argv_array_pushl(&argv, "checkout", start_head.buf, + "--", NULL); + if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) { + retval = error(_("checking out '%s' failed." + " Try 'git bisect start " + "<valid-branch>'."), + start_head.buf); + goto finish; + } + } + } else { + /* Get the rev from where we start. */ + if (!get_oid(head, &head_oid) && + !starts_with(head, "refs/heads/")) { + strbuf_reset(&start_head); + strbuf_addstr(&start_head, oid_to_hex(&head_oid)); + } else if (!get_oid(head, &head_oid) && + skip_prefix(head, "refs/heads/", &head)) { + /* + * This error message should only be triggered by + * cogito usage, and cogito users should understand + * it relates to cg-seek. + */ + if (!is_empty_or_missing_file(git_path_head_name())) + return error(_("won't bisect on cg-seek'ed tree")); + strbuf_addstr(&start_head, head); + } else { + return error(_("bad HEAD - strange symbolic ref")); + } + } + + /* + * Get rid of any old bisect state. + */ + if (bisect_clean_state()) + return -1; + + /* + * In case of mistaken revs or checkout error, or signals received, + * "bisect_auto_next" below may exit or misbehave. + * We have to trap this to be able to clean up using + * "bisect_clean_state". + */ + + /* + * Write new start state + */ + write_file(git_path_bisect_start(), "%s\n", start_head.buf); + + if (no_checkout) { + get_oid(start_head.buf, &oid); + if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0, + UPDATE_REFS_MSG_ON_ERR)) { + retval = -1; + goto finish; + } + } + + if (pathspec_pos < argc - 1) + sq_quote_argv(&bisect_names, argv + pathspec_pos); + write_file(git_path_bisect_names(), "%s\n", bisect_names.buf); + + for (i = 0; i < states.nr; i++) + if (bisect_write(states.items[i].string, + revs.items[i].string, terms, 1)) { + retval = -1; + goto finish; + } + + if (must_write_terms && write_terms(terms->term_bad, + terms->term_good)) { + retval = -1; + goto finish; + } + + retval = bisect_append_log_quoted(argv); + if (retval) + retval = -1; + +finish: + string_list_clear(&revs, 0); + string_list_clear(&states, 0); + strbuf_release(&start_head); + strbuf_release(&bisect_names); + return retval; +} + int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { enum { NEXT_ALL = 1, WRITE_TERMS, BISECT_CLEAN_STATE, - CHECK_EXPECTED_REVS + CHECK_EXPECTED_REVS, + BISECT_RESET, + BISECT_WRITE, + CHECK_AND_SET_TERMS, + BISECT_NEXT_CHECK, + BISECT_TERMS, + BISECT_START } cmdmode = 0; - int no_checkout = 0; + int no_checkout = 0, res = 0, nolog = 0; struct option options[] = { OPT_CMDMODE(0, "next-all", &cmdmode, N_("perform 'git bisect next'"), NEXT_ALL), @@ -124,13 +631,29 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) N_("cleanup the bisection state"), BISECT_CLEAN_STATE), OPT_CMDMODE(0, "check-expected-revs", &cmdmode, N_("check for expected revs"), CHECK_EXPECTED_REVS), + OPT_CMDMODE(0, "bisect-reset", &cmdmode, + N_("reset the bisection state"), BISECT_RESET), + OPT_CMDMODE(0, "bisect-write", &cmdmode, + N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE), + OPT_CMDMODE(0, "check-and-set-terms", &cmdmode, + N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS), + OPT_CMDMODE(0, "bisect-next-check", &cmdmode, + N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK), + OPT_CMDMODE(0, "bisect-terms", &cmdmode, + N_("print out the bisect terms"), BISECT_TERMS), + OPT_CMDMODE(0, "bisect-start", &cmdmode, + N_("start the bisect session"), BISECT_START), OPT_BOOL(0, "no-checkout", &no_checkout, N_("update BISECT_HEAD instead of checking out the current commit")), + OPT_BOOL(0, "no-log", &nolog, + N_("no log for BISECT_WRITE ")), OPT_END() }; + struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL }; argc = parse_options(argc, argv, prefix, options, - git_bisect_helper_usage, 0); + git_bisect_helper_usage, + PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN); if (!cmdmode) usage_with_options(git_bisect_helper_usage, options); @@ -149,8 +672,40 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) case CHECK_EXPECTED_REVS: check_expected_revs(argv, argc); return 0; + case BISECT_RESET: + if (argc > 1) + return error(_("--bisect-reset requires either no argument or a commit")); + return !!bisect_reset(argc ? argv[0] : NULL); + case BISECT_WRITE: + if (argc != 4 && argc != 5) + return error(_("--bisect-write requires either 4 or 5 arguments")); + set_terms(&terms, argv[3], argv[2]); + res = bisect_write(argv[0], argv[1], &terms, nolog); + break; + case CHECK_AND_SET_TERMS: + if (argc != 3) + return error(_("--check-and-set-terms requires 3 arguments")); + set_terms(&terms, argv[2], argv[1]); + res = check_and_set_terms(&terms, argv[0]); + break; + case BISECT_NEXT_CHECK: + if (argc != 2 && argc != 3) + return error(_("--bisect-next-check requires 2 or 3 arguments")); + set_terms(&terms, argv[1], argv[0]); + res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL); + break; + case BISECT_TERMS: + if (argc > 1) + return error(_("--bisect-terms requires 0 or 1 argument")); + res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL); + break; + case BISECT_START: + set_terms(&terms, "bad", "good"); + res = bisect_start(&terms, no_checkout, argv, argc); + break; default: return error("BUG: unknown subcommand '%d'", cmdmode); } - return 0; + free_terms(&terms); + return !!res; } diff --git a/builtin/blame.c b/builtin/blame.c index 6d798f9939..581de0d832 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -925,6 +925,10 @@ parse_done: */ blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */ break; + case DATE_HUMAN: + /* If the year is shown, no time is shown */ + blame_date_width = sizeof("Thu Oct 19 16:00"); + break; case DATE_NORMAL: blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); break; @@ -1007,7 +1011,8 @@ parse_done: long bottom, top; if (parse_range_arg(range_list.items[range_i].string, nth_line_cb, &sb, lno, anchor, - &bottom, &top, sb.path, &the_index)) + &bottom, &top, sb.path, + the_repository->index)) usage(blame_usage); if ((!lno && (top || bottom)) || lno < bottom) die(Q_("file %s has only %lu line", diff --git a/builtin/bundle.c b/builtin/bundle.c index 9e9c65d9c6..1ea4bfdfc1 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -56,8 +56,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix) } if (!startup_info->have_repository) die(_("Need a repository to create a bundle.")); - return !!create_bundle(the_repository, &header, - bundle_file, argc, argv); + return !!create_bundle(the_repository, bundle_file, argc, argv); } else if (!strcmp(cmd, "unbundle")) { if (!startup_info->have_repository) die(_("Need a repository to unbundle.")); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 2ca56fd086..0f092382e1 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "builtin.h" @@ -73,7 +74,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (unknown_type) flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE; - if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH, + if (get_oid_with_context(the_repository, obj_name, + GET_OID_RECORD_PATH, &oid, &obj_context)) die("Not a valid object name %s", obj_name); @@ -209,14 +211,14 @@ struct expand_data { /* * After a mark_query run, this object_info is set up to be - * passed to sha1_object_info_extended. It will point to the data + * passed to oid_object_info_extended. It will point to the data * elements above, so you can retrieve the response from there. */ struct object_info info; /* * This flag will be true if the requested batch format and options - * don't require us to call sha1_object_info, which can then be + * don't require us to call oid_object_info, which can then be * optimized out. */ unsigned skip_object_info : 1; @@ -380,14 +382,18 @@ static void batch_one_object(const char *obj_name, { struct object_context ctx; int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0; - enum follow_symlinks_result result; + enum get_oid_result result; - result = get_oid_with_context(obj_name, flags, &data->oid, &ctx); + result = get_oid_with_context(the_repository, obj_name, + flags, &data->oid, &ctx); if (result != FOUND) { switch (result) { case MISSING_OBJECT: printf("%s missing\n", obj_name); break; + case SHORT_NAME_AMBIGUOUS: + printf("%s ambiguous\n", obj_name); + break; case DANGLING_SYMLINK: printf("dangling %"PRIuMAX"\n%s\n", (uintmax_t)strlen(obj_name), obj_name); @@ -490,7 +496,7 @@ static int batch_objects(struct batch_options *opt) /* * Expand once with our special mark_query flag, which will prime the - * object_info to be handed to sha1_object_info_extended for each + * object_info to be handed to oid_object_info_extended for each * object. */ memset(&data, 0, sizeof(data)); diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 30a2f84274..dd83397786 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "config.h" diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index ec9a959e08..599097304b 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "config.h" diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index a2a726ad8d..1ac1cc290e 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -4,6 +4,7 @@ * Copyright (C) 2005 Linus Torvalds * */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "lockfile.h" diff --git a/builtin/checkout.c b/builtin/checkout.c index 6fadf412e8..ece4eb14bf 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "checkout.h" @@ -288,7 +289,7 @@ static int checkout_paths(const struct checkout_opts *opts, return run_add_interactive(revision, "--patch=checkout", &opts->pathspec); - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); if (read_cache_preload(&opts->pathspec) < 0) return error(_("index file corrupt")); @@ -592,6 +593,14 @@ static int skip_merge_working_tree(const struct checkout_opts *opts, * Remaining variables are not checkout options but used to track state */ + /* + * Do the merge if this is the initial checkout. We cannot use + * is_cache_unborn() here because the index hasn't been loaded yet + * so cache_nr and timestamp.sec are always zero. + */ + if (!file_exists(get_index_file())) + return 0; + return 1; } @@ -693,7 +702,7 @@ static int merge_working_tree(const struct checkout_opts *opts, * a pain; plumb in an option to set * o.renormalize? */ - init_merge_options(&o); + init_merge_options(&o, the_repository); o.verbosity = 0; work = write_tree_from_memory(&o); diff --git a/builtin/clean.c b/builtin/clean.c index bbcdeb2d9e..aaba4af3c2 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -6,6 +6,7 @@ * Based on git-clean.sh by Pavel Roskin */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "config.h" diff --git a/builtin/clone.c b/builtin/clone.c index 739de68820..50bde99618 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -8,6 +8,7 @@ * Clone a repository into a different directory that does not yet exist. */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "lockfile.h" diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index c02a3f1221..4ae502754c 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -110,8 +110,8 @@ static int graph_read(int argc, const char **argv) printf(" oid_lookup"); if (graph->chunk_commit_data) printf(" commit_metadata"); - if (graph->chunk_large_edges) - printf(" large_edges"); + if (graph->chunk_extra_edges) + printf(" extra_edges"); printf("\n"); UNLEAK(graph); diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 9ec36a82b6..12cc403bd7 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -66,7 +66,13 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) continue; } - if (skip_prefix(arg, "-S", &sign_commit)) + if (!strcmp(arg, "--gpg-sign")) { + sign_commit = ""; + continue; + } + + if (skip_prefix(arg, "-S", &sign_commit) || + skip_prefix(arg, "--gpg-sign=", &sign_commit)) continue; if (!strcmp(arg, "--no-gpg-sign")) { diff --git a/builtin/commit.c b/builtin/commit.c index 7d2e0b61e5..2986553d5f 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -5,6 +5,7 @@ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "lockfile.h" @@ -1368,7 +1369,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) if (status_format != STATUS_FORMAT_PORCELAIN && status_format != STATUS_FORMAT_PORCELAIN_V2) progress_flag = REFRESH_PROGRESS; - read_index(&the_index); + repo_read_index(the_repository); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED|progress_flag, &s.pathspec, NULL, NULL); @@ -1397,7 +1398,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) wt_status_collect(&s); if (0 <= fd) - update_index_if_able(&the_index, &index_lock); + repo_update_index_if_able(the_repository, &index_lock); if (s.relative_paths) s.prefix = prefix; @@ -1675,7 +1676,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { - commit_post_rewrite(current_head, &oid); + commit_post_rewrite(the_repository, current_head, &oid); } if (!quiet) { unsigned int flags = 0; diff --git a/builtin/describe.c b/builtin/describe.c index cc118448ee..1409cedce2 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "lockfile.h" @@ -629,12 +630,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix) struct argv_array args = ARGV_ARRAY_INIT; int fd, result; + setup_work_tree(); read_cache(); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) - update_index_if_able(&the_index, &index_lock); + repo_update_index_if_able(the_repository, &index_lock); repo_init_revisions(the_repository, &revs, prefix); argv_array_pushv(&args, diff_index_args); diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 48cfcb935d..86ae474fbf 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "diff.h" diff --git a/builtin/diff-index.c b/builtin/diff-index.c index fcccd1f10d..93ec642423 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "diff.h" diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index ef996126d7..a90681bcba 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "diff.h" @@ -165,7 +166,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) if (opt->diffopt.detect_rename) { if (!the_index.cache) - read_index(&the_index); + repo_read_index(the_repository); opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE; } while (fgets(line, sizeof(line), stdin)) { diff --git a/builtin/diff.c b/builtin/diff.c index f0393bba23..9f6109224b 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -3,6 +3,7 @@ * * Copyright (c) 2006 Junio C Hamano */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "lockfile.h" @@ -102,7 +103,7 @@ static int builtin_diff_blobs(struct rev_info *revs, int argc, const char **argv, struct object_array_entry **blob) { - unsigned mode = canon_mode(S_IFREG | 0644); + const unsigned mode = canon_mode(S_IFREG | 0644); if (argc > 1) usage(builtin_diff_usage); @@ -212,7 +213,7 @@ static void refresh_index_quietly(void) discard_cache(); read_cache(); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); - update_index_if_able(&the_index, &lock_file); + repo_update_index_if_able(the_repository, &lock_file); } static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) diff --git a/builtin/difftool.c b/builtin/difftool.c index 71318c26e1..a3ea60ea71 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -11,6 +11,7 @@ * * Copyright (C) 2016 Johannes Schindelin */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "builtin.h" diff --git a/builtin/fetch.c b/builtin/fetch.c index 61a443031d..5a09fe24cd 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -317,8 +317,7 @@ static void find_non_local_tags(const struct ref *refs, !has_object_file_with_flags(&ref->old_oid, OBJECT_INFO_QUICK) && !will_fetch(head, ref->old_oid.hash) && - !has_sha1_file_with_flags(item->oid.hash, - OBJECT_INFO_QUICK) && + !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) && !will_fetch(head, item->oid.hash)) oidclr(&item->oid); item = NULL; @@ -332,7 +331,7 @@ static void find_non_local_tags(const struct ref *refs, * fetch. */ if (item && - !has_sha1_file_with_flags(item->oid.hash, OBJECT_INFO_QUICK) && + !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) && !will_fetch(head, item->oid.hash)) oidclr(&item->oid); @@ -353,7 +352,7 @@ static void find_non_local_tags(const struct ref *refs, * checked to see if it needs fetching. */ if (item && - !has_sha1_file_with_flags(item->oid.hash, OBJECT_INFO_QUICK) && + !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) && !will_fetch(head, item->oid.hash)) oidclr(&item->oid); @@ -629,9 +628,14 @@ static int find_and_replace(struct strbuf *haystack, const char *needle, const char *placeholder) { - const char *p = strstr(haystack->buf, needle); + const char *p = NULL; int plen, nlen; + nlen = strlen(needle); + if (ends_with(haystack->buf, needle)) + p = haystack->buf + haystack->len - nlen; + else + p = strstr(haystack->buf, needle); if (!p) return 0; @@ -639,7 +643,6 @@ static int find_and_replace(struct strbuf *haystack, return 0; plen = strlen(p); - nlen = strlen(needle); if (plen > nlen && p[nlen] != '/') return 0; diff --git a/builtin/fsck.c b/builtin/fsck.c index 9c4c11d0ad..bb4227bebc 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "repository.h" diff --git a/builtin/grep.c b/builtin/grep.c index dd52ea968b..580fd38f41 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -3,6 +3,7 @@ * * Copyright (c) 2006 Junio C Hamano */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "repository.h" #include "config.h" @@ -393,21 +394,22 @@ static void run_pager(struct grep_opt *opt, const char *prefix) exit(status); } -static int grep_cache(struct grep_opt *opt, struct repository *repo, +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached); static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, struct strbuf *base, int tn_len, - int check_attr, struct repository *repo); + int check_attr); -static int grep_submodule(struct grep_opt *opt, struct repository *superproject, +static int grep_submodule(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_id *oid, const char *filename, const char *path) { struct repository subrepo; + struct repository *superproject = opt->repo; const struct submodule *sub = submodule_from_path(superproject, &null_oid, path); - + struct grep_opt subopt; int hit; /* @@ -443,6 +445,9 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject, add_to_alternates_memory(subrepo.objects->odb->path); grep_read_unlock(); + memcpy(&subopt, opt, sizeof(subopt)); + subopt.repo = &subrepo; + if (oid) { struct object *object; struct tree_desc tree; @@ -464,21 +469,22 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject, strbuf_addch(&base, '/'); init_tree_desc(&tree, data, size); - hit = grep_tree(opt, pathspec, &tree, &base, base.len, - object->type == OBJ_COMMIT, &subrepo); + hit = grep_tree(&subopt, pathspec, &tree, &base, base.len, + object->type == OBJ_COMMIT); strbuf_release(&base); free(data); } else { - hit = grep_cache(opt, &subrepo, pathspec, 1); + hit = grep_cache(&subopt, pathspec, 1); } repo_clear(&subrepo); return hit; } -static int grep_cache(struct grep_opt *opt, struct repository *repo, +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached) { + struct repository *repo = opt->repo; int hit = 0; int nr; struct strbuf name = STRBUF_INIT; @@ -516,7 +522,7 @@ static int grep_cache(struct grep_opt *opt, struct repository *repo, } } else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && submodule_path_match(repo->index, pathspec, name.buf, NULL)) { - hit |= grep_submodule(opt, repo, pathspec, NULL, ce->name, ce->name); + hit |= grep_submodule(opt, pathspec, NULL, ce->name, ce->name); } else { continue; } @@ -538,8 +544,9 @@ static int grep_cache(struct grep_opt *opt, struct repository *repo, static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, struct strbuf *base, int tn_len, - int check_attr, struct repository *repo) + int check_attr) { + struct repository *repo = opt->repo; int hit = 0; enum interesting match = entry_not_interesting; struct name_entry entry; @@ -586,10 +593,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); hit |= grep_tree(opt, pathspec, &sub, base, tn_len, - check_attr, repo); + check_attr); free(data); } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { - hit |= grep_submodule(opt, repo, pathspec, &entry.oid, + hit |= grep_submodule(opt, pathspec, &entry.oid, base->buf, base->buf + tn_len); } @@ -631,7 +638,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, } init_tree_desc(&tree, data, size); hit = grep_tree(opt, pathspec, &tree, &base, base.len, - obj->type == OBJ_COMMIT, the_repository); + obj->type == OBJ_COMMIT); strbuf_release(&base); free(data); return hit; @@ -648,12 +655,12 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, for (i = 0; i < nr; i++) { struct object *real_obj; - real_obj = deref_tag(the_repository, list->objects[i].item, + real_obj = deref_tag(opt->repo, list->objects[i].item, NULL, 0); /* load the gitmodules file for this rev */ if (recurse_submodules) { - submodule_free(the_repository); + submodule_free(opt->repo); gitmodules_config_oid(&real_obj->oid); } if (grep_object(opt, pathspec, real_obj, list->objects[i].name, @@ -678,9 +685,9 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, if (exc_std) setup_standard_excludes(&dir); - fill_directory(&dir, &the_index, pathspec); + fill_directory(&dir, opt->repo->index, pathspec); for (i = 0; i < dir.nr; i++) { - if (!dir_path_match(&the_index, dir.entries[i], pathspec, 0, NULL)) + if (!dir_path_match(opt->repo->index, dir.entries[i], pathspec, 0, NULL)) continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) @@ -1018,7 +1025,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) break; } - if (get_oid_with_context(arg, GET_OID_RECORD_PATH, + if (get_oid_with_context(the_repository, arg, + GET_OID_RECORD_PATH, &oid, &oc)) { if (seen_dashdash) die(_("unable to resolve revision: %s"), arg); @@ -1121,7 +1129,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!cached) setup_work_tree(); - hit = grep_cache(&opt, the_repository, &pathspec, cached); + hit = grep_cache(&opt, &pathspec, cached); } else { if (cached) die(_("both --cached and trees are given")); diff --git a/builtin/hash-object.c b/builtin/hash-object.c index d6f06ea32f..e055c11103 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -40,7 +40,8 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags, if (fstat(fd, &st) < 0 || (literally ? hash_literally(&oid, fd, type, flags) - : index_fd(&the_index, &oid, fd, &st, type_from_string(type), path, flags))) + : index_fd(the_repository->index, &oid, fd, &st, + type_from_string(type), path, flags))) die((flags & HASH_WRITE_OBJECT) ? "Unable to add %s to database" : "Unable to hash %s", path); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index ac1f4ea9a7..31046c7a0a 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -772,7 +772,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (startup_info->have_repository) { read_lock(); collision_test_needed = - has_sha1_file_with_flags(oid->hash, OBJECT_INFO_QUICK); + has_object_file_with_flags(oid, OBJECT_INFO_QUICK); read_unlock(); } diff --git a/builtin/log.c b/builtin/log.c index c91a59dec1..57869267d8 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -4,6 +4,7 @@ * (C) Copyright 2006 Linus Torvalds * 2006 Junio Hamano */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "refs.h" @@ -509,7 +510,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c !rev->diffopt.flags.allow_textconv) return stream_blob_to_fd(1, oid, NULL, 0); - if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH, + if (get_oid_with_context(the_repository, obj_name, + GET_OID_RECORD_PATH, &oidc, &obj_context)) die(_("Not a valid object name %s"), obj_name); if (!obj_context.path || diff --git a/builtin/ls-files.c b/builtin/ls-files.c index cde87cbeeb..29a8762d46 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -5,7 +5,6 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "repository.h" #include "config.h" diff --git a/builtin/merge-index.c b/builtin/merge-index.c index c99443b095..38ea6ad6ca 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "run-command.h" diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c index 0b07263415..4594507420 100644 --- a/builtin/merge-ours.c +++ b/builtin/merge-ours.c @@ -7,6 +7,7 @@ * * Pretend we resolved the heads, but declare our tree trumps everybody else. */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "git-compat-util.h" #include "builtin.h" #include "diff.h" diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 7545136c2a..5b910e351e 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -29,7 +29,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) char *better1, *better2; struct commit *result; - init_merge_options(&o); + init_merge_options(&o, the_repository); if (argv[0] && ends_with(argv[0], "-subtree")) o.subtree_shift = ""; diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 5541ad091e..34ca0258b1 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "tree-walk.h" #include "xdiff-interface.h" @@ -76,7 +77,8 @@ static void *result(struct merge_list *entry, unsigned long *size) their = NULL; if (entry) their = entry->blob; - return merge_blobs(&the_index, path, base, our, their, size); + return merge_blobs(the_repository->index, path, + base, our, their, size); } static void *origin(struct merge_list *entry, unsigned long *size) diff --git a/builtin/merge.c b/builtin/merge.c index dc0b7cc521..e47d77baee 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -6,6 +6,7 @@ * Based on git-merge.sh by Junio C Hamano. */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "parse-options.h" @@ -702,7 +703,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, return 2; } - init_merge_options(&o); + init_merge_options(&o, the_repository); if (!strcmp(strategy, "subtree")) o.subtree_shift = ""; diff --git a/builtin/mv.c b/builtin/mv.c index 80bb967a63..be15ba7044 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -3,6 +3,7 @@ * * Copyright (C) 2006 Johannes Schindelin */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "pathspec.h" diff --git a/builtin/notes.c b/builtin/notes.c index 4996a670f7..02e97f55c5 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -330,10 +330,10 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) } if (!rewrite_cmd) { - commit_notes(t, msg); + commit_notes(the_repository, t, msg); free_notes(t); } else { - finish_copy_notes_for_rewrite(c, msg); + finish_copy_notes_for_rewrite(the_repository, c, msg); } strbuf_release(&buf); return ret; @@ -469,12 +469,14 @@ static int add(int argc, const char **argv, const char *prefix) write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) BUG("combine_notes_overwrite failed"); - commit_notes(t, "Notes added by 'git notes add'"); + commit_notes(the_repository, t, + "Notes added by 'git notes add'"); } else { fprintf(stderr, _("Removing note for object %s\n"), oid_to_hex(&object)); remove_note(t, object.hash); - commit_notes(t, "Notes removed by 'git notes add'"); + commit_notes(the_repository, t, + "Notes removed by 'git notes add'"); } free_note_data(&d); @@ -552,7 +554,8 @@ static int copy(int argc, const char **argv, const char *prefix) if (add_note(t, &object, from_note, combine_notes_overwrite)) BUG("combine_notes_overwrite failed"); - commit_notes(t, "Notes added by 'git notes copy'"); + commit_notes(the_repository, t, + "Notes added by 'git notes copy'"); out: free_notes(t); return retval; @@ -636,7 +639,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) remove_note(t, object.hash); logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]); } - commit_notes(t, logmsg); + commit_notes(the_repository, t, logmsg); free(logmsg); free_note_data(&d); @@ -937,7 +940,8 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) strbuf_release(&sb); } if (!retval) - commit_notes(t, "Notes removed by 'git notes remove'"); + commit_notes(the_repository, t, + "Notes removed by 'git notes remove'"); free_notes(t); return retval; } @@ -965,7 +969,8 @@ static int prune(int argc, const char **argv, const char *prefix) prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) | (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) ); if (!show_only) - commit_notes(t, "Notes removed by 'git notes prune'"); + commit_notes(the_repository, t, + "Notes removed by 'git notes prune'"); free_notes(t); return 0; } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0a70d04604..a9fac7c128 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -84,6 +84,7 @@ static unsigned long pack_size_limit; static int depth = 50; static int delta_search_threads; static int pack_to_stdout; +static int sparse; static int thin; static int num_preferred_base; static struct progress *progress_state; @@ -970,7 +971,7 @@ static int no_try_delta(const char *path) if (!check) check = attr_check_initl("delta", NULL); - git_check_attr(&the_index, path, check); + git_check_attr(the_repository->index, path, check); if (ATTR_FALSE(check->items[0].value)) return 1; return 0; @@ -1642,7 +1643,7 @@ static void check_object(struct object_entry *entry) /* * No choice but to fall back to the recursive delta walk - * with sha1_object_info() to find about the object type + * with oid_object_info() to find about the object type * at this point... */ give_up: @@ -1718,7 +1719,7 @@ static void drop_reused_delta(struct object_entry *entry) if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { /* * We failed to get the info from this pack for some reason; - * fall back to sha1_object_info, which may find another copy. + * fall back to oid_object_info, which may find another copy. * And if that fails, the error will be recorded in oe_type(entry) * and dealt with in prepare_pack(). */ @@ -1901,10 +1902,10 @@ static int type_size_sort(const void *_a, const void *_b) { const struct object_entry *a = *(struct object_entry **)_a; const struct object_entry *b = *(struct object_entry **)_b; - enum object_type a_type = oe_type(a); - enum object_type b_type = oe_type(b); - unsigned long a_size = SIZE(a); - unsigned long b_size = SIZE(b); + const enum object_type a_type = oe_type(a); + const enum object_type b_type = oe_type(b); + const unsigned long a_size = SIZE(a); + const unsigned long b_size = SIZE(b); if (a_type > b_type) return -1; @@ -1919,7 +1920,7 @@ static int type_size_sort(const void *_a, const void *_b) if (a->preferred_base < b->preferred_base) return 1; if (use_delta_islands) { - int island_cmp = island_delta_cmp(&a->idx.oid, &b->idx.oid); + const int island_cmp = island_delta_cmp(&a->idx.oid, &b->idx.oid); if (island_cmp) return island_cmp; } @@ -1953,11 +1954,6 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size, return 0; } -/* Protect access to object database */ -static pthread_mutex_t read_mutex; -#define read_lock() pthread_mutex_lock(&read_mutex) -#define read_unlock() pthread_mutex_unlock(&read_mutex) - /* Protect delta_cache_size */ static pthread_mutex_t cache_mutex; #define cache_lock() pthread_mutex_lock(&cache_mutex) @@ -1993,11 +1989,11 @@ unsigned long oe_get_size_slow(struct packing_data *pack, unsigned long used, avail, size; if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) { - read_lock(); + packing_data_lock(&to_pack); if (oid_object_info(the_repository, &e->idx.oid, &size) < 0) die(_("unable to get size of %s"), oid_to_hex(&e->idx.oid)); - read_unlock(); + packing_data_unlock(&to_pack); return size; } @@ -2005,7 +2001,7 @@ unsigned long oe_get_size_slow(struct packing_data *pack, if (!p) BUG("when e->type is a delta, it must belong to a pack"); - read_lock(); + packing_data_lock(&to_pack); w_curs = NULL; buf = use_pack(p, &w_curs, e->in_pack_offset, &avail); used = unpack_object_header_buffer(buf, avail, &type, &size); @@ -2014,7 +2010,7 @@ unsigned long oe_get_size_slow(struct packing_data *pack, oid_to_hex(&e->idx.oid)); unuse_pack(&w_curs); - read_unlock(); + packing_data_unlock(&to_pack); return size; } @@ -2076,9 +2072,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Load data if not already done */ if (!trg->data) { - read_lock(); + packing_data_lock(&to_pack); trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz); - read_unlock(); + packing_data_unlock(&to_pack); if (!trg->data) die(_("object %s cannot be read"), oid_to_hex(&trg_entry->idx.oid)); @@ -2089,9 +2085,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, *mem_usage += sz; } if (!src->data) { - read_lock(); + packing_data_lock(&to_pack); src->data = read_object_file(&src_entry->idx.oid, &type, &sz); - read_unlock(); + packing_data_unlock(&to_pack); if (!src->data) { if (src_entry->preferred_base) { static int warned = 0; @@ -2171,7 +2167,7 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n) struct object_entry *child = DELTA_CHILD(me); unsigned int m = n; while (child) { - unsigned int c = check_delta_limit(child, n + 1); + const unsigned int c = check_delta_limit(child, n + 1); if (m < c) m = c; child = DELTA_SIBLING(child); @@ -2226,7 +2222,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, while (window_memory_limit && mem_usage > window_memory_limit && count > 1) { - uint32_t tail = (idx + window - count) % window; + const uint32_t tail = (idx + window - count) % window; mem_usage -= free_unpacked(array + tail); count--; } @@ -2337,9 +2333,9 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, static void try_to_free_from_threads(size_t size) { - read_lock(); + packing_data_lock(&to_pack); release_pack_memory(size); - read_unlock(); + packing_data_unlock(&to_pack); } static try_to_free_t old_try_to_free_routine; @@ -2381,7 +2377,6 @@ static pthread_cond_t progress_cond; */ static void init_threaded_search(void) { - init_recursive_mutex(&read_mutex); pthread_mutex_init(&cache_mutex, NULL); pthread_mutex_init(&progress_mutex, NULL); pthread_cond_init(&progress_cond, NULL); @@ -2392,7 +2387,6 @@ static void cleanup_threaded_search(void) { set_try_to_free_routine(old_try_to_free_routine); pthread_cond_destroy(&progress_cond); - pthread_mutex_destroy(&read_mutex); pthread_mutex_destroy(&cache_mutex); pthread_mutex_destroy(&progress_mutex); } @@ -2710,6 +2704,10 @@ static int git_pack_config(const char *k, const char *v, void *cb) use_bitmap_index_default = git_config_bool(k, v); return 0; } + if (!strcmp(k, "pack.usesparse")) { + sparse = 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) @@ -3137,7 +3135,7 @@ static void get_object_list(int ac, const char **av) if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); - mark_edges_uninteresting(&revs, show_edge); + mark_edges_uninteresting(&revs, show_edge, sparse); if (!fn_show_object) fn_show_object = show_object; @@ -3294,6 +3292,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"), N_("unpack unreachable objects newer than <time>"), PARSE_OPT_OPTARG, option_parse_unpack_unreachable }, + OPT_BOOL(0, "sparse", &sparse, + N_("use the sparse reachability algorithm")), OPT_BOOL(0, "thin", &thin, N_("create thin packs")), OPT_BOOL(0, "shallow", &shallow, @@ -3326,6 +3326,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs = 0; + sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0); reset_pack_idx_option(&pack_idx_opts); git_config(git_pack_config, NULL); diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index cf9a9aabd4..11bc514566 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -166,7 +166,7 @@ redo_from_start: l = (hint == NULL) ? list->front : hint; prev = NULL; while (l) { - int cmp = oidcmp(l->oid, oid); + const int cmp = oidcmp(l->oid, oid); if (cmp > 0) /* not in list, since sorted */ return prev; if (!cmp) { /* found */ @@ -264,7 +264,7 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) while (p1_off < p1->pack->num_objects * p1_step && p2_off < p2->pack->num_objects * p2_step) { - int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); + const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); /* cmp ~ p1 - p2 */ if (cmp == 0) { p1_hint = llist_sorted_remove(p1->unique_objects, diff --git a/builtin/pull.c b/builtin/pull.c index 74808b9455..701d1473dc 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -5,6 +5,7 @@ * * Fetch one or more remote refs and merge it/them into the current HEAD. */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "builtin.h" diff --git a/builtin/read-tree.c b/builtin/read-tree.c index ac255ad2c2..9083dcfa28 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "lockfile.h" diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c index c2c2d51a3b..888390f911 100644 --- a/builtin/rebase--interactive.c +++ b/builtin/rebase--interactive.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "config.h" diff --git a/builtin/rebase.c b/builtin/rebase.c index 774264bae8..b9d61771ab 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -4,6 +4,7 @@ * Copyright (c) 2018 Pratik Karki */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "run-command.h" #include "exec-cmd.h" @@ -123,7 +124,7 @@ static void imply_interactive(struct rebase_options *opts, const char *option) case REBASE_PRESERVE_MERGES: break; case REBASE_MERGE: - /* we silently *upgrade* --merge to --interactive if needed */ + /* we now implement --merge via --interactive */ default: opts->type = REBASE_INTERACTIVE; /* implied */ break; @@ -186,10 +187,7 @@ static int read_basic_state(struct rebase_options *opts) if (get_oid(buf.buf, &opts->orig_head)) return error(_("invalid orig-head: '%s'"), buf.buf); - strbuf_reset(&buf); - if (read_one(state_dir_path("quiet", opts), &buf)) - return -1; - if (buf.len) + if (file_exists(state_dir_path("quiet", opts))) opts->flags &= ~REBASE_NO_QUIET; else opts->flags |= REBASE_NO_QUIET; @@ -247,6 +245,37 @@ static int read_basic_state(struct rebase_options *opts) return 0; } +static int write_basic_state(struct rebase_options *opts) +{ + write_file(state_dir_path("head-name", opts), "%s", + opts->head_name ? opts->head_name : "detached HEAD"); + write_file(state_dir_path("onto", opts), "%s", + opts->onto ? oid_to_hex(&opts->onto->object.oid) : ""); + write_file(state_dir_path("orig-head", opts), "%s", + oid_to_hex(&opts->orig_head)); + write_file(state_dir_path("quiet", opts), "%s", + opts->flags & REBASE_NO_QUIET ? "" : "t"); + if (opts->flags & REBASE_VERBOSE) + write_file(state_dir_path("verbose", opts), "%s", ""); + if (opts->strategy) + write_file(state_dir_path("strategy", opts), "%s", + opts->strategy); + if (opts->strategy_opts) + write_file(state_dir_path("strategy_opts", opts), "%s", + opts->strategy_opts); + if (opts->allow_rerere_autoupdate >= 0) + write_file(state_dir_path("allow_rerere_autoupdate", opts), + "-%s-rerere-autoupdate", + opts->allow_rerere_autoupdate ? "" : "-no"); + if (opts->gpg_sign_opt) + write_file(state_dir_path("gpg_sign_opt", opts), "%s", + opts->gpg_sign_opt); + if (opts->signoff) + write_file(state_dir_path("strategy", opts), "--signoff"); + + return 0; +} + static int apply_autostash(struct rebase_options *opts) { const char *path = state_dir_path("autostash", opts); @@ -334,6 +363,161 @@ static void add_var(struct strbuf *buf, const char *name, const char *value) } } +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) + +static int reset_head(struct object_id *oid, const char *action, + const char *switch_to_branch, unsigned flags, + const char *reflog_orig_head, const char *reflog_head) +{ + unsigned detach_head = flags & RESET_HEAD_DETACH; + unsigned reset_hard = flags & RESET_HEAD_HARD; + unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + unsigned refs_only = flags & RESET_HEAD_REFS_ONLY; + struct object_id head_oid; + struct tree_desc desc[2] = { { NULL }, { NULL } }; + struct lock_file lock = LOCK_INIT; + struct unpack_trees_options unpack_tree_opts; + struct tree *tree; + const char *reflog_action; + struct strbuf msg = STRBUF_INIT; + size_t prefix_len; + struct object_id *orig = NULL, oid_orig, + *old_orig = NULL, oid_old_orig; + int ret = 0, nr = 0; + + if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) + BUG("Not a fully qualified branch: '%s'", switch_to_branch); + + if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) { + ret = -1; + goto leave_reset_head; + } + + if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) { + ret = error(_("could not determine HEAD revision")); + goto leave_reset_head; + } + + if (!oid) + oid = &head_oid; + + if (refs_only) + goto reset_head_refs; + + memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); + setup_unpack_trees_porcelain(&unpack_tree_opts, action); + unpack_tree_opts.head_idx = 1; + unpack_tree_opts.src_index = the_repository->index; + unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; + unpack_tree_opts.update = 1; + unpack_tree_opts.merge = 1; + if (!detach_head) + unpack_tree_opts.reset = 1; + + if (repo_read_index_unmerged(the_repository) < 0) { + ret = error(_("could not read index")); + goto leave_reset_head; + } + + if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) { + ret = error(_("failed to find tree of %s"), + oid_to_hex(&head_oid)); + goto leave_reset_head; + } + + if (!fill_tree_descriptor(&desc[nr++], oid)) { + ret = error(_("failed to find tree of %s"), oid_to_hex(oid)); + goto leave_reset_head; + } + + if (unpack_trees(nr, desc, &unpack_tree_opts)) { + ret = -1; + goto leave_reset_head; + } + + tree = parse_tree_indirect(oid); + prime_cache_tree(the_repository, the_repository->index, tree); + + if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) { + ret = error(_("could not write index")); + goto leave_reset_head; + } + +reset_head_refs: + reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); + strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); + prefix_len = msg.len; + + if (!get_oid("ORIG_HEAD", &oid_old_orig)) + old_orig = &oid_old_orig; + if (!get_oid("HEAD", &oid_orig)) { + orig = &oid_orig; + if (!reflog_orig_head) { + strbuf_addstr(&msg, "updating ORIG_HEAD"); + reflog_orig_head = msg.buf; + } + update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0, + UPDATE_REFS_MSG_ON_ERR); + } else if (old_orig) + delete_ref(NULL, "ORIG_HEAD", old_orig, 0); + if (!reflog_head) { + strbuf_setlen(&msg, prefix_len); + strbuf_addstr(&msg, "updating HEAD"); + reflog_head = msg.buf; + } + if (!switch_to_branch) + ret = update_ref(reflog_head, "HEAD", oid, orig, + detach_head ? REF_NO_DEREF : 0, + UPDATE_REFS_MSG_ON_ERR); + else { + ret = update_ref(reflog_orig_head, switch_to_branch, oid, + NULL, 0, UPDATE_REFS_MSG_ON_ERR); + if (!ret) + ret = create_symref("HEAD", switch_to_branch, + reflog_head); + } + if (run_hook) + run_hook_le(NULL, "post-checkout", + oid_to_hex(orig ? orig : &null_oid), + oid_to_hex(oid), "1", NULL); + +leave_reset_head: + strbuf_release(&msg); + rollback_lock_file(&lock); + while (nr) + free((void *)desc[--nr].buffer); + return ret; +} + +static int move_to_original_branch(struct rebase_options *opts) +{ + struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; + int ret; + + if (!opts->head_name) + return 0; /* nothing to move back to */ + + if (!opts->onto) + BUG("move_to_original_branch without onto"); + + strbuf_addf(&orig_head_reflog, "rebase finished: %s onto %s", + opts->head_name, oid_to_hex(&opts->onto->object.oid)); + strbuf_addf(&head_reflog, "rebase finished: returning to %s", + opts->head_name); + ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY, + orig_head_reflog.buf, head_reflog.buf); + + strbuf_release(&orig_head_reflog); + strbuf_release(&head_reflog); + return ret; +} + static const char *resolvemsg = N_("Resolve all conflicts manually, mark them as resolved with\n" "\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n" @@ -341,6 +525,126 @@ N_("Resolve all conflicts manually, mark them as resolved with\n" "To abort and get back to the state before \"git rebase\", run " "\"git rebase --abort\"."); +static int run_am(struct rebase_options *opts) +{ + struct child_process am = CHILD_PROCESS_INIT; + struct child_process format_patch = CHILD_PROCESS_INIT; + struct strbuf revisions = STRBUF_INIT; + int status; + char *rebased_patches; + + am.git_cmd = 1; + argv_array_push(&am.args, "am"); + + if (opts->action && !strcmp("continue", opts->action)) { + argv_array_push(&am.args, "--resolved"); + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + if (opts->gpg_sign_opt) + argv_array_push(&am.args, opts->gpg_sign_opt); + status = run_command(&am); + if (status) + return status; + + return move_to_original_branch(opts); + } + if (opts->action && !strcmp("skip", opts->action)) { + argv_array_push(&am.args, "--skip"); + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + status = run_command(&am); + if (status) + return status; + + return move_to_original_branch(opts); + } + if (opts->action && !strcmp("show-current-patch", opts->action)) { + argv_array_push(&am.args, "--show-current-patch"); + return run_command(&am); + } + + strbuf_addf(&revisions, "%s...%s", + oid_to_hex(opts->root ? + /* this is now equivalent to !opts->upstream */ + &opts->onto->object.oid : + &opts->upstream->object.oid), + oid_to_hex(&opts->orig_head)); + + rebased_patches = xstrdup(git_path("rebased-patches")); + format_patch.out = open(rebased_patches, + O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (format_patch.out < 0) { + status = error_errno(_("could not open '%s' for writing"), + rebased_patches); + free(rebased_patches); + argv_array_clear(&am.args); + return status; + } + + format_patch.git_cmd = 1; + argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout", + "--full-index", "--cherry-pick", "--right-only", + "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", + "--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL); + if (opts->git_format_patch_opt.len) + argv_array_split(&format_patch.args, + opts->git_format_patch_opt.buf); + argv_array_push(&format_patch.args, revisions.buf); + if (opts->restrict_revision) + argv_array_pushf(&format_patch.args, "^%s", + oid_to_hex(&opts->restrict_revision->object.oid)); + + status = run_command(&format_patch); + if (status) { + unlink(rebased_patches); + free(rebased_patches); + argv_array_clear(&am.args); + + reset_head(&opts->orig_head, "checkout", opts->head_name, 0, + "HEAD", NULL); + error(_("\ngit encountered an error while preparing the " + "patches to replay\n" + "these revisions:\n" + "\n %s\n\n" + "As a result, git cannot rebase them."), + opts->revisions); + + strbuf_release(&revisions); + return status; + } + strbuf_release(&revisions); + + am.in = open(rebased_patches, O_RDONLY); + if (am.in < 0) { + status = error_errno(_("could not open '%s' for reading"), + rebased_patches); + free(rebased_patches); + argv_array_clear(&am.args); + return status; + } + + argv_array_pushv(&am.args, opts->git_am_opts.argv); + argv_array_push(&am.args, "--rebasing"); + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + argv_array_push(&am.args, "--patch-format=mboxrd"); + if (opts->allow_rerere_autoupdate > 0) + argv_array_push(&am.args, "--rerere-autoupdate"); + else if (opts->allow_rerere_autoupdate == 0) + argv_array_push(&am.args, "--no-rerere-autoupdate"); + if (opts->gpg_sign_opt) + argv_array_push(&am.args, opts->gpg_sign_opt); + status = run_command(&am); + unlink(rebased_patches); + free(rebased_patches); + + if (!status) { + return move_to_original_branch(opts); + } + + if (is_directory(opts->state_dir)) + write_basic_state(opts); + + return status; +} + static int run_specific_rebase(struct rebase_options *opts) { const char *argv[] = { NULL, NULL }; @@ -355,7 +659,8 @@ static int run_specific_rebase(struct rebase_options *opts) argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s", resolvemsg); if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { - argv_array_push(&child.env_array, "GIT_EDITOR=:"); + argv_array_push(&child.env_array, + "GIT_SEQUENCE_EDITOR=:"); opts->autosquash = 0; } @@ -423,6 +728,11 @@ static int run_specific_rebase(struct rebase_options *opts) goto finished_rebase; } + if (opts->type == REBASE_AM) { + status = run_am(opts); + goto finished_rebase; + } + add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir())); add_var(&script_snippet, "state_dir", opts->state_dir); @@ -478,7 +788,7 @@ static int run_specific_rebase(struct rebase_options *opts) if (is_interactive(opts) && !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { strbuf_addstr(&script_snippet, - "GIT_EDITOR=:; export GIT_EDITOR; "); + "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; "); opts->autosquash = 0; } @@ -487,10 +797,6 @@ static int run_specific_rebase(struct rebase_options *opts) backend = "git-rebase--am"; backend_func = "git_rebase__am"; break; - case REBASE_MERGE: - backend = "git-rebase--merge"; - backend_func = "git_rebase__merge"; - break; case REBASE_PRESERVE_MERGES: backend = "git-rebase--preserve-merges"; backend_func = "git_rebase__preserve_merges"; @@ -529,131 +835,6 @@ finished_rebase: return status ? -1 : 0; } -#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" - -#define RESET_HEAD_DETACH (1<<0) -#define RESET_HEAD_HARD (1<<1) -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) - -static int reset_head(struct object_id *oid, const char *action, - const char *switch_to_branch, unsigned flags, - const char *reflog_orig_head, const char *reflog_head) -{ - unsigned detach_head = flags & RESET_HEAD_DETACH; - unsigned reset_hard = flags & RESET_HEAD_HARD; - unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; - struct object_id head_oid; - struct tree_desc desc[2] = { { NULL }, { NULL } }; - struct lock_file lock = LOCK_INIT; - struct unpack_trees_options unpack_tree_opts; - struct tree *tree; - const char *reflog_action; - struct strbuf msg = STRBUF_INIT; - size_t prefix_len; - struct object_id *orig = NULL, oid_orig, - *old_orig = NULL, oid_old_orig; - int ret = 0, nr = 0; - - if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) - BUG("Not a fully qualified branch: '%s'", switch_to_branch); - - if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) { - ret = -1; - goto leave_reset_head; - } - - if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) { - ret = error(_("could not determine HEAD revision")); - goto leave_reset_head; - } - - if (!oid) - oid = &head_oid; - - memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); - setup_unpack_trees_porcelain(&unpack_tree_opts, action); - unpack_tree_opts.head_idx = 1; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; - unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update = 1; - unpack_tree_opts.merge = 1; - if (!detach_head) - unpack_tree_opts.reset = 1; - - if (read_index_unmerged(the_repository->index) < 0) { - ret = error(_("could not read index")); - goto leave_reset_head; - } - - if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) { - ret = error(_("failed to find tree of %s"), - oid_to_hex(&head_oid)); - goto leave_reset_head; - } - - if (!fill_tree_descriptor(&desc[nr++], oid)) { - ret = error(_("failed to find tree of %s"), oid_to_hex(oid)); - goto leave_reset_head; - } - - if (unpack_trees(nr, desc, &unpack_tree_opts)) { - ret = -1; - goto leave_reset_head; - } - - tree = parse_tree_indirect(oid); - prime_cache_tree(the_repository, the_repository->index, tree); - - if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) { - ret = error(_("could not write index")); - goto leave_reset_head; - } - - reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); - strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); - prefix_len = msg.len; - - if (!get_oid("ORIG_HEAD", &oid_old_orig)) - old_orig = &oid_old_orig; - if (!get_oid("HEAD", &oid_orig)) { - orig = &oid_orig; - if (!reflog_orig_head) { - strbuf_addstr(&msg, "updating ORIG_HEAD"); - reflog_orig_head = msg.buf; - } - update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0, - UPDATE_REFS_MSG_ON_ERR); - } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig, 0); - if (!reflog_head) { - strbuf_setlen(&msg, prefix_len); - strbuf_addstr(&msg, "updating HEAD"); - reflog_head = msg.buf; - } - if (!switch_to_branch) - ret = update_ref(reflog_head, "HEAD", oid, orig, - detach_head ? REF_NO_DEREF : 0, - UPDATE_REFS_MSG_ON_ERR); - else { - ret = create_symref("HEAD", switch_to_branch, msg.buf); - if (!ret) - ret = update_ref(reflog_head, "HEAD", oid, NULL, 0, - UPDATE_REFS_MSG_ON_ERR); - } - if (run_hook) - run_hook_le(NULL, "post-checkout", - oid_to_hex(orig ? orig : &null_oid), - oid_to_hex(oid), "1", NULL); - -leave_reset_head: - strbuf_release(&msg); - rollback_lock_file(&lock); - while (nr) - free((void *)desc[--nr].buffer); - return ret; -} - static int rebase_config(const char *var, const char *value, void *data) { struct rebase_options *opts = data; @@ -824,6 +1005,19 @@ static void set_reflog_action(struct rebase_options *options) strbuf_release(&buf); } +static int check_exec_cmd(const char *cmd) +{ + if (strchr(cmd, '\n')) + return error(_("exec commands cannot contain newlines")); + + /* Does the command consist purely of whitespace? */ + if (!cmd[strspn(cmd, " \t\r\f\v")]) + return error(_("empty exec command")); + + return 0; +} + + int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { @@ -1053,13 +1247,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("Cannot read HEAD")); fd = hold_locked_index(&lock_file, 0); - if (read_index(the_repository->index) < 0) + if (repo_read_index(the_repository) < 0) die(_("could not read index")); refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); if (0 <= fd) - update_index_if_able(the_repository->index, - &lock_file); + repo_update_index_if_able(the_repository, &lock_file); rollback_lock_file(&lock_file); if (has_unstaged_changes(the_repository, 1)) { @@ -1168,6 +1361,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } } + for (i = 0; i < exec.nr; i++) + if (check_exec_cmd(exec.items[i].string)) + exit(1); + if (!(options.flags & REBASE_NO_QUIET)) argv_array_push(&options.git_am_opts, "-q"); @@ -1232,6 +1429,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } } + if (options.type == REBASE_MERGE) + imply_interactive(&options, "--merge"); + if (options.root && !options.onto_name) imply_interactive(&options, "--root without --onto"); @@ -1264,14 +1464,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) break; if (is_interactive(&options) && i >= 0) - die(_("error: cannot combine interactive options " - "(--interactive, --exec, --rebase-merges, " - "--preserve-merges, --keep-empty, --root + " - "--onto) with am options (%s)"), buf.buf); - if (options.type == REBASE_MERGE && i >= 0) - die(_("error: cannot combine merge options (--merge, " - "--strategy, --strategy-option) with am options " - "(%s)"), buf.buf); + die(_("cannot combine am options with either " + "interactive or merge options")); } if (options.signoff) { @@ -1289,7 +1483,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * git-rebase.txt caveats with "unless you know what you are doing" */ if (options.rebase_merges) - die(_("error: cannot combine '--preserve-merges' with " + die(_("cannot combine '--preserve-merges' with " "'--rebase-merges'")); if (options.reschedule_failed_exec) @@ -1299,10 +1493,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.rebase_merges) { if (strategy_options.nr) - die(_("error: cannot combine '--rebase-merges' with " + die(_("cannot combine '--rebase-merges' with " "'--strategy-option'")); if (options.strategy) - die(_("error: cannot combine '--rebase-merges' with " + die(_("cannot combine '--rebase-merges' with " "'--strategy'")); } @@ -1414,7 +1608,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) get_fork_point(options.upstream_name, head); } - if (read_index(the_repository->index) < 0) + if (repo_read_index(the_repository) < 0) die(_("could not read index")); if (options.autostash) { @@ -1424,7 +1618,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) fd = hold_locked_index(&lock_file, 0); refresh_cache(REFRESH_QUIET); if (0 <= fd) - update_index_if_able(&the_index, &lock_file); + repo_update_index_if_able(the_repository, &lock_file); rollback_lock_file(&lock_file); if (has_unstaged_changes(the_repository, 1) || @@ -1469,7 +1663,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) putchar('\n'); if (discard_index(the_repository->index) < 0 || - read_index(the_repository->index) < 0) + repo_read_index(the_repository) < 0) die(_("could not read index")); } } diff --git a/builtin/reflog.c b/builtin/reflog.c index 1f1010e2d9..4d3430900d 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -94,7 +94,7 @@ static int tree_is_complete(const struct object_id *oid) init_tree_desc(&desc, tree->buffer, tree->size); complete = 1; while (tree_entry(&desc, &entry)) { - if (!has_sha1_file(entry.oid.hash) || + if (!has_object_file(&entry.oid) || (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) { tree->object.flags |= INCOMPLETE; complete = 0; diff --git a/builtin/replace.c b/builtin/replace.c index affcdfb416..5b80b7f211 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -295,7 +295,7 @@ static int import_object(struct object_id *oid, enum object_type type, close(fd); return -1; } - if (index_fd(&the_index, oid, fd, &st, type, NULL, flags) < 0) + if (index_fd(the_repository->index, oid, fd, &st, type, NULL, flags) < 0) return error(_("unable to write object to database")); /* index_fd close()s fd for us */ } diff --git a/builtin/reset.c b/builtin/reset.c index 59898c972e..4d18a461fa 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -7,6 +7,7 @@ * * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "lockfile.h" diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 14ef659c12..5b5b6dbb1c 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -546,7 +546,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (prepare_revision_walk(&revs)) die("revision walk setup failed"); if (revs.tree_objects) - mark_edges_uninteresting(&revs, show_edge); + mark_edges_uninteresting(&revs, show_edge, 0); if (bisect_list) { int reaches, all; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 10d4dab894..f8bbe6d47e 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "commit.h" @@ -933,7 +934,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) name++; type = REVERSED; } - if (!get_oid_with_context(name, flags, &oid, &unused)) { + if (!get_oid_with_context(the_repository, name, + flags, &oid, &unused)) { if (verify) revs_count++; else diff --git a/builtin/rm.c b/builtin/rm.c index 17086d3d97..db85b33982 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds 2006 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "lockfile.h" diff --git a/builtin/show-ref.c b/builtin/show-ref.c index ed888ffa48..6a706c02a6 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -23,7 +23,7 @@ static void show_one(const char *refname, const struct object_id *oid) const char *hex; struct object_id peeled; - if (!has_sha1_file(oid->hash)) + if (!has_object_file(oid)) die("git show-ref: bad ref %s (%s)", refname, oid_to_hex(oid)); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 0e140f176c..b80fc4ba3d 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "repository.h" #include "cache.h" diff --git a/builtin/update-index.c b/builtin/update-index.c index e19da77edc..02ace602b9 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "lockfile.h" diff --git a/builtin/write-tree.c b/builtin/write-tree.c index cdcbf8264e..3d46d22ee5 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" #include "config.h" |