diff options
Diffstat (limited to 'builtin')
75 files changed, 3086 insertions, 1543 deletions
diff --git a/builtin/add.c b/builtin/add.c index a648cf4c56..9ef7fb02d5 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -26,6 +26,7 @@ static const char * const builtin_add_usage[] = { }; static int patch_interactive, add_interactive, edit_interactive; static int take_worktree_changes; +static int add_renormalize; struct update_callback_data { int flags; @@ -116,13 +117,32 @@ int add_files_to_cache(const char *prefix, rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = &data; - rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG; + rev.diffopt.flags.override_submodule_config = 1; rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); clear_pathspec(&rev.prune_data); return !!data.add_errors; } +static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) +{ + int i, retval = 0; + + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + + if (ce_stage(ce)) + continue; /* do not touch unmerged paths */ + if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode)) + continue; /* do not touch non blobs */ + if (pathspec && !ce_path_match(ce, pathspec, NULL)) + continue; + retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE); + } + + return retval; +} + static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix) { char *seen; @@ -218,7 +238,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) argc = setup_revisions(argc, argv, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_PATCH; rev.diffopt.use_color = 0; - DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES); + rev.diffopt.flags.ignore_dirty_submodules = 1; out = open(file, O_CREAT | O_WRONLY, 0666); if (out < 0) die(_("Could not open '%s' for writing."), file); @@ -274,8 +294,9 @@ static struct option builtin_add_options[] = { OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), - OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")), + OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), + OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")), OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")), { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit, @@ -406,7 +427,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) chmod_arg[1] != 'x' || chmod_arg[2])) die(_("--chmod param '%s' must be either -x or +x"), chmod_arg); - add_new_files = !take_worktree_changes && !refresh_only; + add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize; require_pathspec = !(take_worktree_changes || (0 < addremove_explicit)); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); @@ -500,7 +521,10 @@ int cmd_add(int argc, const char **argv, const char *prefix) plug_bulk_checkin(); - exit_status |= add_files_to_cache(prefix, &pathspec, flags); + if (add_renormalize) + exit_status |= renormalize_tracked_files(&pathspec, flags); + else + exit_status |= add_files_to_cache(prefix, &pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); @@ -510,10 +534,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) unplug_bulk_checkin(); finish: - if (active_cache_changed) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("Unable to write new index file")); - } + if (write_locked_index(&the_index, &lock_file, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) + die(_("Unable to write new index file")); UNLEAK(pathspec); UNLEAK(dir); diff --git a/builtin/am.c b/builtin/am.c index d7513f5375..9c82603f70 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -708,6 +708,7 @@ static int split_mail_mbox(struct am_state *state, const char **paths, { struct child_process cp = CHILD_PROCESS_INIT; struct strbuf last = STRBUF_INIT; + int ret; cp.git_cmd = 1; argv_array_push(&cp.args, "mailsplit"); @@ -721,13 +722,16 @@ static int split_mail_mbox(struct am_state *state, const char **paths, argv_array_push(&cp.args, "--"); argv_array_pushv(&cp.args, paths); - if (capture_command(&cp, &last, 8)) - return -1; + ret = capture_command(&cp, &last, 8); + if (ret) + goto exit; state->cur = 1; state->last = strtol(last.buf, NULL, 10); - return 0; +exit: + strbuf_release(&last); + return ret ? -1 : 0; } /** @@ -1007,6 +1011,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, if (mkdir(state->dir, 0777) < 0 && errno != EEXIST) die_errno(_("failed to create directory '%s'"), state->dir); + delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); if (split_mail(state, patch_format, paths, keep_cr) < 0) { am_destroy(state); @@ -1057,7 +1062,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, } write_state_text(state, "scissors", str); - sq_quote_argv(&sb, state->git_apply_opts.argv, 0); + sq_quote_argv(&sb, state->git_apply_opts.argv); write_state_text(state, "apply-opt", sb.buf); if (state->rebasing) @@ -1068,8 +1073,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, if (!get_oid("HEAD", &curr_head)) { write_state_text(state, "abort-safety", oid_to_hex(&curr_head)); if (!state->rebasing) - update_ref_oid("am", "ORIG_HEAD", &curr_head, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); } else { write_state_text(state, "abort-safety", ""); if (!state->rebasing) @@ -1106,6 +1111,7 @@ static void am_next(struct am_state *state) oidclr(&state->orig_commit); unlink(am_path(state, "original-commit")); + delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); if (!get_oid("HEAD", &head)) write_state_text(state, "abort-safety", oid_to_hex(&head)); @@ -1134,52 +1140,15 @@ static const char *msgnum(const struct am_state *state) */ static void refresh_and_write_cache(void) { - struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); + struct lock_file lock_file = LOCK_INIT; - hold_locked_index(lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write index file")); } /** - * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn - * branch, returns 1 if there are entries in the index, 0 otherwise. If an - * strbuf is provided, the space-separated list of files that differ will be - * appended to it. - */ -static int index_has_changes(struct strbuf *sb) -{ - struct object_id head; - int i; - - if (!get_oid_tree("HEAD", &head)) { - struct diff_options opt; - - diff_setup(&opt); - DIFF_OPT_SET(&opt, EXIT_WITH_STATUS); - if (!sb) - DIFF_OPT_SET(&opt, QUICK); - do_diff_cache(&head, &opt); - diffcore_std(&opt); - for (i = 0; sb && i < diff_queued_diff.nr; i++) { - if (i) - strbuf_addch(sb, ' '); - strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path); - } - diff_flush(&opt); - return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0; - } else { - for (i = 0; sb && i < active_nr; i++) { - if (i) - strbuf_addch(sb, ' '); - strbuf_addstr(sb, active_cache[i]->name); - } - return !!active_nr; - } -} - -/** * Dies with a user-friendly message on how to proceed after resolving the * problem. This message can be overridden with state->resolvemsg. */ @@ -1409,8 +1378,8 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm rev_info.show_root_diff = 1; rev_info.diffopt.output_format = DIFF_FORMAT_PATCH; rev_info.no_commit_id = 1; - DIFF_OPT_SET(&rev_info.diffopt, BINARY); - DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX); + rev_info.diffopt.flags.binary = 1; + rev_info.diffopt.flags.full_index = 1; rev_info.diffopt.use_color = 0; rev_info.diffopt.file = fp; rev_info.diffopt.close_file = 1; @@ -1433,7 +1402,7 @@ static void write_index_patch(const struct am_state *state) if (!get_oid_tree("HEAD", &head)) tree = lookup_tree(&head); else - tree = lookup_tree(&empty_tree_oid); + tree = lookup_tree(the_hash_algo->empty_tree); fp = xfopen(am_path(state, "patch"), "w"); init_revisions(&rev_info, NULL); @@ -1474,6 +1443,8 @@ static int parse_mail_rebase(struct am_state *state, const char *mail) oidcpy(&state->orig_commit, &commit_oid); write_state_text(state, "original-commit", oid_to_hex(&commit_oid)); + update_ref("am", "REBASE_HEAD", &commit_oid, + NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); return 0; } @@ -1488,11 +1459,10 @@ static int run_apply(const struct am_state *state, const char *index_file) struct argv_array apply_opts = ARGV_ARRAY_INIT; struct apply_state apply_state; int res, opts_left; - static struct lock_file lock_file; int force_apply = 0; int options = 0; - if (init_apply_state(&apply_state, NULL, &lock_file)) + if (init_apply_state(&apply_state, NULL)) die("BUG: init_apply_state() failed"); argv_array_push(&apply_opts, "apply"); @@ -1580,7 +1550,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa discard_cache(); read_cache_from(index_path); - if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL)) + if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL)) return error(_("Repository lacks necessary blobs to fall back on 3-way merge.")); say(state, stdout, _("Using index info to reconstruct a base tree...")); @@ -1605,7 +1575,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa return error(_("Did you hand edit your patch?\n" "It does not apply to blobs recorded in its index.")); - if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL)) + if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL)) return error("could not write tree"); say(state, stdout, _("Falling back to patching base and 3-way merge...")); @@ -1656,7 +1626,7 @@ static void do_commit(const struct am_state *state) if (run_hook_le(NULL, "pre-applypatch", NULL)) exit(1); - if (write_cache_as_tree(tree.hash, 0, NULL)) + if (write_cache_as_tree(&tree, 0, NULL)) die(_("git write-tree failed to write a tree")); if (!get_oid_commit("HEAD", &parent)) { @@ -1675,8 +1645,8 @@ static void do_commit(const struct am_state *state) setenv("GIT_COMMITTER_DATE", state->ignore_date ? "" : state->author_date, 1); - if (commit_tree(state->msg, state->msg_len, tree.hash, parents, commit.hash, - author, state->sign_commit)) + if (commit_tree(state->msg, state->msg_len, &tree, parents, &commit, + author, state->sign_commit)) die(_("failed to write commit object")); reflog_msg = getenv("GIT_REFLOG_ACTION"); @@ -1686,8 +1656,8 @@ static void do_commit(const struct am_state *state) strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg), state->msg); - update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 0, - UPDATE_REFS_DIE_ON_ERR); + update_ref(sb.buf, "HEAD", &commit, old_oid, 0, + UPDATE_REFS_DIE_ON_ERR); if (state->rebasing) { FILE *fp = xfopen(am_path(state, "rewritten"), "a"); @@ -1865,8 +1835,7 @@ static void am_run(struct am_state *state, int resume) git_config_get_bool("advice.amworkdir", &advice_amworkdir); if (advice_amworkdir) - printf_ln(_("The copy of the patch that failed is found in: %s"), - am_path(state, "patch")); + printf_ln(_("Use 'git am --show-current-patch' to see the failed patch")); die_user_resolve(state); } @@ -1893,7 +1862,7 @@ next: */ if (!state->rebasing) { am_destroy(state); - close_all_packs(); + close_all_packs(the_repository->objects); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } @@ -1946,15 +1915,14 @@ next: */ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) { - struct lock_file *lock_file; + struct lock_file lock_file = LOCK_INIT; struct unpack_trees_options opts; struct tree_desc t[2]; if (parse_tree(head) || parse_tree(remote)) return -1; - lock_file = xcalloc(1, sizeof(struct lock_file)); - hold_locked_index(lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); @@ -1970,11 +1938,11 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) init_tree_desc(&t[1], remote->buffer, remote->size); if (unpack_trees(2, t, &opts)) { - rollback_lock_file(lock_file); + rollback_lock_file(&lock_file); return -1; } - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); return 0; @@ -1986,15 +1954,14 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) */ static int merge_tree(struct tree *tree) { - struct lock_file *lock_file; + struct lock_file lock_file = LOCK_INIT; struct unpack_trees_options opts; struct tree_desc t[1]; if (parse_tree(tree)) return -1; - lock_file = xcalloc(1, sizeof(struct lock_file)); - hold_locked_index(lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; @@ -2005,11 +1972,11 @@ static int merge_tree(struct tree *tree) init_tree_desc(&t[0], tree->buffer, tree->size); if (unpack_trees(1, t, &opts)) { - rollback_lock_file(lock_file); + rollback_lock_file(&lock_file); return -1; } - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); return 0; @@ -2037,7 +2004,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem if (fast_forward_to(head_tree, head_tree, 1)) return -1; - if (write_cache_as_tree(index.hash, 0, NULL)) + if (write_cache_as_tree(&index, 0, NULL)) return -1; index_tree = parse_tree_indirect(&index); @@ -2135,7 +2102,7 @@ static void am_abort(struct am_state *state) am_rerere_clear(); - curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL); + curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL); has_curr_head = curr_branch && !is_null_oid(&curr_head); if (!has_curr_head) hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN); @@ -2147,16 +2114,44 @@ static void am_abort(struct am_state *state) clean_index(&curr_head, &orig_head); if (has_orig_head) - update_ref_oid("am --abort", "HEAD", &orig_head, - has_curr_head ? &curr_head : NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + update_ref("am --abort", "HEAD", &orig_head, + has_curr_head ? &curr_head : NULL, 0, + UPDATE_REFS_DIE_ON_ERR); else if (curr_branch) - delete_ref(NULL, curr_branch, NULL, REF_NODEREF); + delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF); free(curr_branch); am_destroy(state); } +static int show_patch(struct am_state *state) +{ + struct strbuf sb = STRBUF_INIT; + const char *patch_path; + int len; + + if (!is_null_oid(&state->orig_commit)) { + const char *av[4] = { "show", NULL, "--", NULL }; + char *new_oid_str; + int ret; + + av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit)); + ret = run_command_v_opt(av, RUN_GIT_CMD); + free(new_oid_str); + return ret; + } + + patch_path = am_path(state, msgnum(state)); + len = strbuf_read_file(&sb, patch_path, 0); + if (len < 0) + die_errno(_("failed to read '%s'"), patch_path); + + setup_pager(); + write_in_full(1, sb.buf, sb.len); + strbuf_release(&sb); + return 0; +} + /** * parse_options() callback that validates and sets opt->value to the * PATCH_FORMAT_* enum value corresponding to `arg`. @@ -2185,7 +2180,9 @@ enum resume_mode { RESUME_APPLY, RESUME_RESOLVED, RESUME_SKIP, - RESUME_ABORT + RESUME_ABORT, + RESUME_QUIT, + RESUME_SHOW_PATCH }; static int git_am_config(const char *k, const char *v, void *cb) @@ -2207,6 +2204,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) int patch_format = PATCH_FORMAT_UNKNOWN; enum resume_mode resume = RESUME_FALSE; int in_progress; + int ret = 0; const char * const usage[] = { N_("git am [<options>] [(<mbox> | <Maildir>)...]"), @@ -2285,6 +2283,12 @@ int cmd_am(int argc, const char **argv, const char *prefix) OPT_CMDMODE(0, "abort", &resume, N_("restore the original branch and abort the patching operation."), RESUME_ABORT), + OPT_CMDMODE(0, "quit", &resume, + N_("abort the patching operation but keep HEAD where it is."), + RESUME_QUIT), + OPT_CMDMODE(0, "show-current-patch", &resume, + N_("show the patch being applied."), + RESUME_SHOW_PATCH), OPT_BOOL(0, "committer-date-is-author-date", &state.committer_date_is_author_date, N_("lie about committer date")), @@ -2353,7 +2357,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) * stray directories. */ if (file_exists(state.dir) && !state.rebasing) { - if (resume == RESUME_ABORT) { + if (resume == RESUME_ABORT || resume == RESUME_QUIT) { am_destroy(&state); am_state_release(&state); return 0; @@ -2395,11 +2399,18 @@ int cmd_am(int argc, const char **argv, const char *prefix) case RESUME_ABORT: am_abort(&state); break; + case RESUME_QUIT: + am_rerere_clear(); + am_destroy(&state); + break; + case RESUME_SHOW_PATCH: + ret = show_patch(&state); + break; default: die("BUG: invalid resume value"); } am_state_release(&state); - return 0; + return ret; } diff --git a/builtin/apply.c b/builtin/apply.c index 81b9a61c37..48d3989331 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -9,8 +9,6 @@ static const char * const apply_usage[] = { NULL }; -static struct lock_file lock_file; - int cmd_apply(int argc, const char **argv, const char *prefix) { int force_apply = 0; @@ -18,7 +16,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) int ret; struct apply_state state; - if (init_apply_state(&state, prefix, &lock_file)) + if (init_apply_state(&state, prefix)) exit(128); argc = apply_parse_options(argc, argv, diff --git a/builtin/archive.c b/builtin/archive.c index f863465a0f..73971d0dd2 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -55,7 +55,7 @@ static int run_remote_archiver(int argc, const char **argv, buf = packet_read_line(fd[0], NULL); if (!buf) - die(_("git archive: expected ACK/NAK, got EOF")); + die(_("git archive: expected ACK/NAK, got a flush packet")); if (strcmp(buf, "ACK")) { if (starts_with(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 3324229025..4b5fadcbe1 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -2,19 +2,128 @@ #include "cache.h" #include "parse-options.h" #include "bisect.h" +#include "refs.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 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"), NULL }; +/* + * Check whether the string `term` belongs to the set of strings + * included in the variable arguments. + */ +LAST_ARG_MUST_BE_NULL +static int one_of(const char *term, ...) +{ + int res = 0; + va_list matches; + const char *match; + + va_start(matches, term); + while (!res && (match = va_arg(matches, const char *))) + res = !strcmp(term, match); + va_end(matches); + + return res; +} + +static int check_term_format(const char *term, const char *orig_term) +{ + int res; + char *new_term = xstrfmt("refs/bisect/%s", term); + + res = check_refname_format(new_term, 0); + free(new_term); + + if (res) + return error(_("'%s' is not a valid term"), term); + + if (one_of(term, "help", "start", "skip", "next", "reset", + "visualize", "view", "replay", "log", "run", "terms", NULL)) + return error(_("can't use the builtin command '%s' as a term"), term); + + /* + * In theory, nothing prevents swapping completely good and bad, + * but this situation could be confusing and hasn't been tested + * enough. Forbid it for now. + */ + + if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) || + (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL))) + return error(_("can't change the meaning of the term '%s'"), term); + + return 0; +} + +static int write_terms(const char *bad, const char *good) +{ + FILE *fp = NULL; + int res; + + if (!strcmp(bad, good)) + return error(_("please use two different terms")); + + 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 = fprintf(fp, "%s\n%s\n", bad, good); + res |= fclose(fp); + return (res < 0) ? -1 : 0; +} + +static int is_expected_rev(const char *expected_hex) +{ + struct strbuf actual_hex = STRBUF_INIT; + int res = 0; + if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) { + strbuf_trim(&actual_hex); + res = !strcmp(actual_hex.buf, expected_hex); + } + strbuf_release(&actual_hex); + return res; +} + +static void check_expected_revs(const char **revs, int rev_nr) +{ + int i; + + for (i = 0; i < rev_nr; i++) { + if (!is_expected_rev(revs[i])) { + unlink_or_warn(git_path_bisect_ancestors_ok()); + unlink_or_warn(git_path_bisect_expected_rev()); + } + } +} + int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { - int next_all = 0; + enum { + NEXT_ALL = 1, + WRITE_TERMS, + BISECT_CLEAN_STATE, + CHECK_EXPECTED_REVS + } cmdmode = 0; int no_checkout = 0; struct option options[] = { - OPT_BOOL(0, "next-all", &next_all, - N_("perform 'git bisect next'")), + OPT_CMDMODE(0, "next-all", &cmdmode, + N_("perform 'git bisect next'"), NEXT_ALL), + OPT_CMDMODE(0, "write-terms", &cmdmode, + N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS), + OPT_CMDMODE(0, "bisect-clean-state", &cmdmode, + N_("cleanup the bisection state"), BISECT_CLEAN_STATE), + OPT_CMDMODE(0, "check-expected-revs", &cmdmode, + N_("check for expected revs"), CHECK_EXPECTED_REVS), OPT_BOOL(0, "no-checkout", &no_checkout, N_("update BISECT_HEAD instead of checking out the current commit")), OPT_END() @@ -23,9 +132,25 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_bisect_helper_usage, 0); - if (!next_all) + if (!cmdmode) usage_with_options(git_bisect_helper_usage, options); - /* next-all */ - return bisect_next_all(prefix, no_checkout); + switch (cmdmode) { + case NEXT_ALL: + return bisect_next_all(prefix, no_checkout); + case WRITE_TERMS: + if (argc != 2) + return error(_("--write-terms requires two arguments")); + return write_terms(argv[0], argv[1]); + case BISECT_CLEAN_STATE: + if (argc != 0) + return error(_("--bisect-clean-state requires no arguments")); + return bisect_clean_state(); + case CHECK_EXPECTED_REVS: + check_expected_revs(argv, argc); + return 0; + default: + return error("BUG: unknown subcommand '%d'", cmdmode); + } + return 0; } diff --git a/builtin/blame.c b/builtin/blame.c index 67adaef4d8..db38c0b307 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -499,7 +499,7 @@ static int read_ancestry(const char *graft_file) static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) { - const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash, + const char *uniq = find_unique_abbrev(&suspect->commit->object.oid, auto_abbrev); int len = strlen(uniq); if (auto_abbrev < len) @@ -649,6 +649,15 @@ static int blame_move_callback(const struct option *option, const char *arg, int return 0; } +static int is_a_rev(const char *name) +{ + struct object_id oid; + + if (get_oid(name, &oid)) + return 0; + return OBJ_NONE < oid_object_info(&oid, NULL); +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -708,8 +717,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) git_config(git_blame_config, &output_option); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; - DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); - DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES); + revs.diffopt.flags.allow_textconv = 1; + revs.diffopt.flags.follow_renames = 1; save_commit_buffer = 0; dashdash_pos = 0; @@ -720,6 +729,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) for (;;) { switch (parse_options_step(&ctx, options, blame_opt_usage)) { case PARSE_OPT_HELP: + case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_DONE: if (ctx.argv[0]) @@ -734,9 +744,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_revision_opt(&revs, &ctx, options, blame_opt_usage); } parse_done: - no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES); + no_whole_file_rename = !revs.diffopt.flags.follow_renames; xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC; - DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES); + revs.diffopt.flags.follow_renames = 0; argc = parse_options_end(&ctx); if (incremental || (output_option & OUTPUT_PORCELAIN)) { @@ -803,7 +813,7 @@ parse_done: } blame_date_width -= 1; /* strip the null */ - if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER)) + if (revs.diffopt.flags.find_copies_harder) opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE | PICKAXE_BLAME_COPY_HARDER); @@ -845,16 +855,15 @@ parse_done: } else { if (argc < 2) usage_with_options(blame_opt_usage, options); - path = add_prefix(prefix, argv[argc - 1]); - if (argc == 3 && !file_exists(path)) { /* (2b) */ + if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */ path = add_prefix(prefix, argv[1]); argv[1] = argv[2]; + } else { /* (2a) */ + if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree()) + die("missing <path> to blame"); + path = add_prefix(prefix, argv[argc - 1]); } argv[argc - 1] = "--"; - - setup_work_tree(); - if (!file_exists(path)) - die_errno("cannot stat path '%s'", path); } revs.disable_stdin = 1; diff --git a/builtin/branch.c b/builtin/branch.c index 79dc9181fd..5bd2a0dd48 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -125,7 +125,7 @@ static int branch_merged(int kind, const char *name, if (upstream && (reference_name = reference_name_to_free = resolve_refdup(upstream, RESOLVE_REF_READING, - oid.hash, NULL)) != NULL) + &oid, NULL)) != NULL) reference_rev = lookup_commit_reference(&oid); } if (!reference_rev) @@ -241,7 +241,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE | RESOLVE_REF_ALLOW_BAD_NAME, - oid.hash, &flags); + &oid, &flags); if (!target) { error(remote_branch ? _("remote-tracking branch '%s' not found.") @@ -257,8 +257,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, goto next; } - if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash, - REF_NODEREF)) { + if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid, + REF_NO_DEREF)) { error(remote_branch ? _("Error deleting remote-tracking branch '%s'") : _("Error deleting branch '%s'"), @@ -273,7 +273,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, bname.buf, (flags & REF_ISBROKEN) ? "broken" : (flags & REF_ISSYMREF) ? target - : find_unique_abbrev(oid.hash, DEFAULT_ABBREV)); + : find_unique_abbrev(&oid, DEFAULT_ABBREV)); } delete_branch_config(bname.buf); @@ -462,8 +462,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int { struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT; struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; + const char *interpreted_oldname = NULL; + const char *interpreted_newname = NULL; int recovery = 0; - int clobber_head_ok; if (!oldname) { if (copy) @@ -487,12 +488,18 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int * A command like "git branch -M currentbranch currentbranch" cannot * cause the worktree to become inconsistent with HEAD, so allow it. */ - clobber_head_ok = !strcmp(oldname, newname); - - validate_new_branchname(newname, &newref, force, clobber_head_ok); + if (!strcmp(oldname, newname)) + validate_branchname(newname, &newref); + else + validate_new_branchname(newname, &newref, force); reject_rebase_or_bisect_branch(oldref.buf); + if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) || + !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) { + die("BUG: expected prefix missing for refs"); + } + if (copy) strbuf_addf(&logmsg, "Branch: copied %s to %s", oldref.buf, newref.buf); @@ -507,11 +514,11 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int if (recovery) { if (copy) - warning(_("Copied a misnamed branch '%s' away"), - oldref.buf + 11); + warning(_("Created a copy of a misnamed branch '%s'"), + interpreted_oldname); else warning(_("Renamed a misnamed branch '%s' away"), - oldref.buf + 11); + interpreted_oldname); } if (!copy && @@ -520,9 +527,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int strbuf_release(&logmsg); - strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11); + strbuf_addf(&oldsection, "branch.%s", interpreted_oldname); strbuf_release(&oldref); - strbuf_addf(&newsection, "branch.%s", newref.buf + 11); + strbuf_addf(&newsection, "branch.%s", interpreted_newname); strbuf_release(&newref); if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0) die(_("Branch is renamed, but update of config-file failed")); @@ -608,7 +615,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")), OPT_BOOL(0, "edit-description", &edit_description, N_("edit the description for the branch")), - OPT__FORCE(&force, N_("force creation, move/rename, deletion")), + OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE), OPT_MERGED(&filter, N_("print only branches that are merged")), OPT_NO_MERGED(&filter, N_("print only branches that are not merged")), OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), @@ -636,7 +643,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) track = git_branch_track; - head = resolve_refdup("HEAD", 0, head_oid.hash, NULL); + head = resolve_refdup("HEAD", 0, &head_oid, NULL); if (!head) die(_("Failed to resolve HEAD as a valid ref.")); if (!strcmp(head, "HEAD")) @@ -675,6 +682,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) copy *= 2; } + if (list) + setup_auto_pager("branch", 1); + if (delete) { if (!argc) die(_("branch name required")); @@ -793,9 +803,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } else if (argc > 0 && argc <= 2) { struct branch *branch = branch_get(argv[0]); - if (!strcmp(argv[0], "HEAD")) - die(_("it does not make sense to create 'HEAD' manually")); - if (!branch) die(_("no such branch '%s'"), argv[0]); @@ -806,7 +813,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead.")); create_branch(argv[0], (argc == 2) ? argv[1] : head, - force, reflog, 0, quiet, track); + force, 0, reflog, quiet, track); } else usage_with_options(builtin_branch_usage, options); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index f5fa4fd75a..2c46d257cd 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -32,7 +32,7 @@ static int filter_object(const char *path, unsigned mode, { enum object_type type; - *buf = read_sha1_file(oid->hash, &type, size); + *buf = read_object_file(oid, &type, size); if (!*buf) return error(_("cannot read object %s '%s'"), oid_to_hex(oid), path); @@ -76,8 +76,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, buf = NULL; switch (opt) { case 't': - oi.typename = &sb; - if (sha1_object_info_extended(oid.hash, &oi, flags) < 0) + oi.type_name = &sb; + if (oid_object_info_extended(&oid, &oi, flags) < 0) die("git cat-file: could not get object info"); if (sb.len) { printf("%s\n", sb.buf); @@ -88,7 +88,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, case 's': oi.sizep = &size; - if (sha1_object_info_extended(oid.hash, &oi, flags) < 0) + if (oid_object_info_extended(&oid, &oi, flags) < 0) die("git cat-file: could not get object info"); printf("%lu\n", size); return 0; @@ -116,7 +116,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, /* else fallthrough */ case 'p': - type = sha1_object_info(oid.hash, NULL); + type = oid_object_info(&oid, NULL); if (type < 0) die("Not a valid object name %s", obj_name); @@ -130,7 +130,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (type == OBJ_BLOB) return stream_blob_to_fd(1, &oid, NULL, 0); - buf = read_sha1_file(oid.hash, &type, &size); + buf = read_object_file(&oid, &type, &size); if (!buf) die("Cannot read object %s", obj_name); @@ -140,8 +140,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, case 0: if (type_from_string(exp_type) == OBJ_BLOB) { struct object_id blob_oid; - if (sha1_object_info(oid.hash, NULL) == OBJ_TAG) { - char *buffer = read_sha1_file(oid.hash, &type, &size); + if (oid_object_info(&oid, NULL) == OBJ_TAG) { + char *buffer = read_object_file(&oid, &type, + &size); const char *target; if (!skip_prefix(buffer, "object ", &target) || get_oid_hex(target, &blob_oid)) @@ -150,7 +151,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, } else oidcpy(&blob_oid, &oid); - if (sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB) + if (oid_object_info(&blob_oid, NULL) == OBJ_BLOB) return stream_blob_to_fd(1, &blob_oid, NULL, 0); /* * we attempted to dereference a tag to a blob @@ -159,7 +160,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, * fall-back to the usual case. */ } - buf = read_object_with_reference(oid.hash, exp_type, &size, NULL); + buf = read_object_with_reference(&oid, exp_type, &size, NULL); break; default: @@ -229,7 +230,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, if (data->mark_query) data->info.typep = &data->type; else - strbuf_addstr(sb, typename(data->type)); + strbuf_addstr(sb, type_name(data->type)); } else if (is_atom("objectsize", atom, len)) { if (data->mark_query) data->info.sizep = &data->size; @@ -304,8 +305,9 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d enum object_type type; if (!textconv_object(data->rest, 0100644, oid, 1, &contents, &size)) - contents = read_sha1_file(oid->hash, &type, - &size); + contents = read_object_file(oid, + &type, + &size); if (!contents) die("could not convert '%s' %s", oid_to_hex(oid), data->rest); @@ -321,7 +323,7 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d unsigned long size; void *contents; - contents = read_sha1_file(oid->hash, &type, &size); + contents = read_object_file(oid, &type, &size); if (!contents) die("object %s disappeared", oid_to_hex(oid)); if (type != data->type) @@ -340,8 +342,8 @@ static void batch_object_write(const char *obj_name, struct batch_options *opt, struct strbuf buf = STRBUF_INIT; if (!data->skip_object_info && - sha1_object_info_extended(data->oid.hash, &data->info, - OBJECT_INFO_LOOKUP_REPLACE) < 0) { + oid_object_info_extended(&data->oid, &data->info, + OBJECT_INFO_LOOKUP_REPLACE) < 0) { printf("%s missing\n", obj_name ? obj_name : oid_to_hex(&data->oid)); fflush(stdout); @@ -475,6 +477,8 @@ static int batch_objects(struct batch_options *opt) for_each_loose_object(batch_loose_object, &sa, 0); for_each_packed_object(batch_packed_object, &sa, 0); + if (repository_format_partial_clone) + warning("This repository has extensions.partialClone set. Some objects may not be loaded."); cb.opt = opt; cb.expand = &data; diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 3e280b9c7a..ec9a959e08 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -72,7 +72,7 @@ static int check_ignore(struct dir_struct *dir, { const char *full_path; char *seen; - int num_ignored = 0, dtype = DT_UNKNOWN, i; + int num_ignored = 0, i; struct exclude *exclude; struct pathspec pathspec; @@ -104,6 +104,7 @@ static int check_ignore(struct dir_struct *dir, full_path = pathspec.items[i].match; exclude = NULL; if (!seen[i]) { + int dtype = DT_UNKNOWN; exclude = last_exclude_matching(dir, &the_index, full_path, &dtype); } diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 39c8be05dc..a730f6a1aa 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -129,8 +129,6 @@ static const char * const builtin_checkout_index_usage[] = { NULL }; -static struct lock_file lock_file; - static int option_parse_stage(const struct option *opt, const char *arg, int unset) { @@ -150,7 +148,7 @@ static int option_parse_stage(const struct option *opt, int cmd_checkout_index(int argc, const char **argv, const char *prefix) { int i; - int newfd = -1; + struct lock_file lock_file = LOCK_INIT; int all = 0; int read_from_stdin = 0; int prefix_length; @@ -159,7 +157,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) struct option builtin_checkout_index_options[] = { OPT_BOOL('a', "all", &all, N_("check out all files in the index")), - OPT__FORCE(&force, N_("force overwrite of existing files")), + OPT__FORCE(&force, N_("force overwrite of existing files"), 0), OPT__QUIET(&quiet, N_("no warning for existing files and files not in index")), OPT_BOOL('n', "no-create", ¬_new, @@ -206,7 +204,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (index_opt && !state.base_dir_len && !to_tempfile) { state.refresh_cache = 1; state.istate = &the_index; - newfd = hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); } /* Check out named files first */ @@ -251,7 +249,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (all) checkout_all(prefix, prefix_length); - if (0 <= newfd && + if (is_lock_file_locked(&lock_file) && write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die("Unable to write new index file"); return 0; diff --git a/builtin/checkout.c b/builtin/checkout.c index fc4f8fd2ea..b49b582071 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "config.h" +#include "checkout.h" #include "lockfile.h" #include "parse-options.h" #include "refs.h" @@ -53,19 +54,19 @@ struct checkout_opts { struct tree *source_tree; }; -static int post_checkout_hook(struct commit *old, struct commit *new, +static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit, int changed) { return run_hook_le(NULL, "post-checkout", - oid_to_hex(old ? &old->object.oid : &null_oid), - oid_to_hex(new ? &new->object.oid : &null_oid), + oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid), + oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid), changed ? "1" : "0", NULL); - /* "new" can be NULL when checking out from the index before + /* "new_commit" can be NULL when checking out from the index before a commit exists. */ } -static int update_some(const unsigned char *sha1, struct strbuf *base, +static int update_some(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { int len; @@ -77,7 +78,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base, len = base->len + strlen(pathname); ce = xcalloc(1, cache_entry_size(len)); - hashcpy(ce->oid.hash, sha1); + oidcpy(&ce->oid, oid); memcpy(ce->name, base->buf, base->len); memcpy(ce->name + base->len, pathname, len - base->len); ce->ce_flags = create_ce_flags(0) | CE_UPDATE; @@ -226,8 +227,7 @@ static int checkout_merged(int pos, const struct checkout *state) * (it also writes the merge result to the object database even * when it may contain conflicts). */ - if (write_sha1_file(result_buf.ptr, result_buf.size, - blob_type, oid.hash)) + if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid)) die(_("Unable to add merge result for '%s'"), path); free(result_buf.ptr); ce = make_cache_entry(mode, oid.hash, path, 2, 0); @@ -247,7 +247,7 @@ static int checkout_paths(const struct checkout_opts *opts, struct object_id rev; struct commit *head; int errs = 0; - struct lock_file *lock_file; + struct lock_file lock_file = LOCK_INIT; if (opts->track != BRANCH_TRACK_UNSPECIFIED) die(_("'%s' cannot be used with updating paths"), "--track"); @@ -275,9 +275,7 @@ static int checkout_paths(const struct checkout_opts *opts, return run_add_interactive(revision, "--patch=checkout", &opts->pathspec); - lock_file = xcalloc(1, sizeof(struct lock_file)); - - hold_locked_index(lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache_preload(&opts->pathspec) < 0) return error(_("index file corrupt")); @@ -376,10 +374,10 @@ static int checkout_paths(const struct checkout_opts *opts, } errs |= finish_delayed_checkout(&state); - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); - read_ref_full("HEAD", 0, rev.hash, NULL); + read_ref_full("HEAD", 0, &rev, NULL); head = lookup_commit_reference_gently(&rev, 1); errs |= post_checkout_hook(head, head, 0); @@ -402,10 +400,16 @@ static void show_local_changes(struct object *head, static void describe_detached_head(const char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; + if (!parse_commit(commit)) pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); - fprintf(stderr, "%s %s... %s\n", msg, - find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf); + if (print_sha1_ellipsis()) { + fprintf(stderr, "%s %s... %s\n", msg, + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); + } else { + fprintf(stderr, "%s %s %s\n", msg, + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); + } strbuf_release(&sb); } @@ -467,20 +471,20 @@ static void setup_branch_path(struct branch_info *branch) } static int merge_working_tree(const struct checkout_opts *opts, - struct branch_info *old, - struct branch_info *new, + struct branch_info *old_branch_info, + struct branch_info *new_branch_info, int *writeout_error) { int ret; - struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); + struct lock_file lock_file = LOCK_INIT; - hold_locked_index(lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache_preload(NULL) < 0) return error(_("index file corrupt")); resolve_undo_clear(); if (opts->force) { - ret = reset_tree(new->commit->tree, opts, 1, writeout_error); + ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error); if (ret) return ret; } else { @@ -506,7 +510,7 @@ static int merge_working_tree(const struct checkout_opts *opts, topts.initial_checkout = is_cache_unborn(); topts.update = 1; topts.merge = 1; - topts.gently = opts->merge && old->commit; + topts.gently = opts->merge && old_branch_info->commit; topts.verbose_update = opts->show_progress; topts.fn = twoway_merge; if (opts->overwrite_ignore) { @@ -514,11 +518,11 @@ static int merge_working_tree(const struct checkout_opts *opts, topts.dir->flags |= DIR_SHOW_IGNORED; setup_standard_excludes(topts.dir); } - tree = parse_tree_indirect(old->commit ? - &old->commit->object.oid : - &empty_tree_oid); + tree = parse_tree_indirect(old_branch_info->commit ? + &old_branch_info->commit->object.oid : + the_hash_algo->empty_tree); init_tree_desc(&trees[0], tree->buffer, tree->size); - tree = parse_tree_indirect(&new->commit->object.oid); + tree = parse_tree_indirect(&new_branch_info->commit->object.oid); init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); @@ -535,10 +539,10 @@ static int merge_working_tree(const struct checkout_opts *opts, return 1; /* - * Without old->commit, the below is the same as + * Without old_branch_info->commit, the below is the same as * the two-tree unpack we already tried and failed. */ - if (!old->commit) + if (!old_branch_info->commit) return 1; /* Do more real merge */ @@ -566,18 +570,18 @@ static int merge_working_tree(const struct checkout_opts *opts, o.verbosity = 0; work = write_tree_from_memory(&o); - ret = reset_tree(new->commit->tree, opts, 1, + ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error); if (ret) return ret; - o.ancestor = old->name; - o.branch1 = new->name; + o.ancestor = old_branch_info->name; + o.branch1 = new_branch_info->name; o.branch2 = "local"; - ret = merge_trees(&o, new->commit->tree, work, - old->commit->tree, &result); + ret = merge_trees(&o, new_branch_info->commit->tree, work, + old_branch_info->commit->tree, &result); if (ret < 0) exit(128); - ret = reset_tree(new->commit->tree, opts, 0, + ret = reset_tree(new_branch_info->commit->tree, opts, 0, writeout_error); strbuf_release(&o.obuf); if (ret) @@ -591,29 +595,29 @@ static int merge_working_tree(const struct checkout_opts *opts, if (!cache_tree_fully_valid(active_cache_tree)) cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); if (!opts->force && !opts->quiet) - show_local_changes(&new->commit->object, &opts->diff_options); + show_local_changes(&new_branch_info->commit->object, &opts->diff_options); return 0; } -static void report_tracking(struct branch_info *new) +static void report_tracking(struct branch_info *new_branch_info) { struct strbuf sb = STRBUF_INIT; - struct branch *branch = branch_get(new->name); + struct branch *branch = branch_get(new_branch_info->name); - if (!format_tracking_info(branch, &sb)) + if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL)) return; fputs(sb.buf, stdout); strbuf_release(&sb); } static void update_refs_for_switch(const struct checkout_opts *opts, - struct branch_info *old, - struct branch_info *new) + struct branch_info *old_branch_info, + struct branch_info *new_branch_info) { struct strbuf msg = STRBUF_INIT; const char *old_desc, *reflog_msg; @@ -640,69 +644,69 @@ static void update_refs_for_switch(const struct checkout_opts *opts, free(refname); } else - create_branch(opts->new_branch, new->name, + create_branch(opts->new_branch, new_branch_info->name, opts->new_branch_force ? 1 : 0, - opts->new_branch_log, opts->new_branch_force ? 1 : 0, + opts->new_branch_log, opts->quiet, opts->track); - new->name = opts->new_branch; - setup_branch_path(new); + new_branch_info->name = opts->new_branch; + setup_branch_path(new_branch_info); } - old_desc = old->name; - if (!old_desc && old->commit) - old_desc = oid_to_hex(&old->commit->object.oid); + old_desc = old_branch_info->name; + if (!old_desc && old_branch_info->commit) + old_desc = oid_to_hex(&old_branch_info->commit->object.oid); reflog_msg = getenv("GIT_REFLOG_ACTION"); if (!reflog_msg) strbuf_addf(&msg, "checkout: moving from %s to %s", - old_desc ? old_desc : "(invalid)", new->name); + old_desc ? old_desc : "(invalid)", new_branch_info->name); else strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg)); - if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) { + if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) { /* Nothing to do. */ - } else if (opts->force_detach || !new->path) { /* No longer on any branch. */ - update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL, - REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); + } else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */ + update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL, + REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); if (!opts->quiet) { - if (old->path && + if (old_branch_info->path && advice_detached_head && !opts->force_detach) - detach_advice(new->name); - describe_detached_head(_("HEAD is now at"), new->commit); + detach_advice(new_branch_info->name); + describe_detached_head(_("HEAD is now at"), new_branch_info->commit); } - } else if (new->path) { /* Switch branches. */ - if (create_symref("HEAD", new->path, msg.buf) < 0) + } else if (new_branch_info->path) { /* Switch branches. */ + if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0) die(_("unable to update HEAD")); if (!opts->quiet) { - if (old->path && !strcmp(new->path, old->path)) { + if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) { if (opts->new_branch_force) fprintf(stderr, _("Reset branch '%s'\n"), - new->name); + new_branch_info->name); else fprintf(stderr, _("Already on '%s'\n"), - new->name); + new_branch_info->name); } else if (opts->new_branch) { if (opts->branch_exists) - fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name); + fprintf(stderr, _("Switched to and reset branch '%s'\n"), new_branch_info->name); else - fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name); + fprintf(stderr, _("Switched to a new branch '%s'\n"), new_branch_info->name); } else { fprintf(stderr, _("Switched to branch '%s'\n"), - new->name); + new_branch_info->name); } } - if (old->path && old->name) { - if (!ref_exists(old->path) && reflog_exists(old->path)) - delete_reflog(old->path); + if (old_branch_info->path && old_branch_info->name) { + if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path)) + delete_reflog(old_branch_info->path); } } remove_branch_state(); strbuf_release(&msg); if (!opts->quiet && - (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD")))) - report_tracking(new); + (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD")))) + report_tracking(new_branch_info); } static int add_pending_uninteresting_ref(const char *refname, @@ -716,7 +720,7 @@ static int add_pending_uninteresting_ref(const char *refname, static void describe_one_orphan(struct strbuf *sb, struct commit *commit) { strbuf_addstr(sb, " "); - strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV); strbuf_addch(sb, ' '); if (!parse_commit(commit)) pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); @@ -774,7 +778,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) " git branch <new-branch-name> %s\n\n", /* Give ngettext() the count */ lost), - find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV)); + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); } /* @@ -782,11 +786,10 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) * HEAD. If it is not reachable from any ref, this is the last chance * for the user to do so without resorting to reflog. */ -static void orphaned_commit_warning(struct commit *old, struct commit *new) +static void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit) { struct rev_info revs; - struct object *object = &old->object; - struct object_array refs; + struct object *object = &old_commit->object; init_revisions(&revs, NULL); setup_revisions(0, NULL, &revs, NULL); @@ -795,67 +798,57 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) add_pending_object(&revs, object, oid_to_hex(&object->oid)); for_each_ref(add_pending_uninteresting_ref, &revs); - add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING); - - /* Save pending objects, so they can be cleaned up later. */ - refs = revs.pending; - revs.leak_pending = 1; + add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING); - /* - * prepare_revision_walk (together with .leak_pending = 1) makes us - * the sole owner of the list of pending objects. - */ if (prepare_revision_walk(&revs)) die(_("internal error in revision walk")); - if (!(old->object.flags & UNINTERESTING)) - suggest_reattach(old, &revs); + if (!(old_commit->object.flags & UNINTERESTING)) + suggest_reattach(old_commit, &revs); else - describe_detached_head(_("Previous HEAD position was"), old); + describe_detached_head(_("Previous HEAD position was"), old_commit); /* Clean up objects used, as they will be reused. */ - clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS); - - object_array_clear(&refs); + clear_commit_marks_all(ALL_REV_FLAGS); } static int switch_branches(const struct checkout_opts *opts, - struct branch_info *new) + struct branch_info *new_branch_info) { int ret = 0; - struct branch_info old; + struct branch_info old_branch_info; void *path_to_free; struct object_id rev; int flag, writeout_error = 0; - memset(&old, 0, sizeof(old)); - old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag); - if (old.path) - old.commit = lookup_commit_reference_gently(&rev, 1); + memset(&old_branch_info, 0, sizeof(old_branch_info)); + old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag); + if (old_branch_info.path) + old_branch_info.commit = lookup_commit_reference_gently(&rev, 1); if (!(flag & REF_ISSYMREF)) - old.path = NULL; + old_branch_info.path = NULL; - if (old.path) - skip_prefix(old.path, "refs/heads/", &old.name); + if (old_branch_info.path) + skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name); - if (!new->name) { - new->name = "HEAD"; - new->commit = old.commit; - if (!new->commit) + if (!new_branch_info->name) { + new_branch_info->name = "HEAD"; + new_branch_info->commit = old_branch_info.commit; + if (!new_branch_info->commit) die(_("You are on a branch yet to be born")); - parse_commit_or_die(new->commit); + parse_commit_or_die(new_branch_info->commit); } - ret = merge_working_tree(opts, &old, new, &writeout_error); + ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error); if (ret) { free(path_to_free); return ret; } - if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) - orphaned_commit_warning(old.commit, new->commit); + if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit) + orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit); - update_refs_for_switch(opts, &old, new); + update_refs_for_switch(opts, &old_branch_info, new_branch_info); - ret = post_checkout_hook(old.commit, new->commit, 1); + ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1); free(path_to_free); return ret || writeout_error; } @@ -874,49 +867,9 @@ static int git_checkout_config(const char *var, const char *value, void *cb) return git_xmerge_config(var, value, NULL); } -struct tracking_name_data { - /* const */ char *src_ref; - char *dst_ref; - struct object_id *dst_oid; - int unique; -}; - -static int check_tracking_name(struct remote *remote, void *cb_data) -{ - struct tracking_name_data *cb = cb_data; - struct refspec query; - memset(&query, 0, sizeof(struct refspec)); - query.src = cb->src_ref; - if (remote_find_tracking(remote, &query) || - get_oid(query.dst, cb->dst_oid)) { - free(query.dst); - return 0; - } - if (cb->dst_ref) { - free(query.dst); - cb->unique = 0; - return 0; - } - cb->dst_ref = query.dst; - return 0; -} - -static const char *unique_tracking_name(const char *name, struct object_id *oid) -{ - struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 }; - cb_data.src_ref = xstrfmt("refs/heads/%s", name); - cb_data.dst_oid = oid; - for_each_remote(check_tracking_name, &cb_data); - free(cb_data.src_ref); - if (cb_data.unique) - return cb_data.dst_ref; - free(cb_data.dst_ref); - return NULL; -} - static int parse_branchname_arg(int argc, const char **argv, int dwim_new_local_branch_ok, - struct branch_info *new, + struct branch_info *new_branch_info, struct checkout_opts *opts, struct object_id *rev) { @@ -1034,22 +987,22 @@ static int parse_branchname_arg(int argc, const char **argv, argv++; argc--; - new->name = arg; - setup_branch_path(new); + new_branch_info->name = arg; + setup_branch_path(new_branch_info); - if (!check_refname_format(new->path, 0) && - !read_ref(new->path, branch_rev.hash)) + if (!check_refname_format(new_branch_info->path, 0) && + !read_ref(new_branch_info->path, &branch_rev)) oidcpy(rev, &branch_rev); else - new->path = NULL; /* not an existing branch */ + new_branch_info->path = NULL; /* not an existing branch */ - new->commit = lookup_commit_reference_gently(rev, 1); - if (!new->commit) { + new_branch_info->commit = lookup_commit_reference_gently(rev, 1); + if (!new_branch_info->commit) { /* not a commit */ *source_tree = parse_tree_indirect(rev); } else { - parse_commit_or_die(new->commit); - *source_tree = new->commit->tree; + parse_commit_or_die(new_branch_info->commit); + *source_tree = new_branch_info->commit->tree; } if (!*source_tree) /* case (1): want a tree */ @@ -1089,7 +1042,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) } static int checkout_branch(struct checkout_opts *opts, - struct branch_info *new) + struct branch_info *new_branch_info) { if (opts->pathspec.nr) die(_("paths cannot be used with switching branches")); @@ -1118,35 +1071,35 @@ static int checkout_branch(struct checkout_opts *opts, } else if (opts->track == BRANCH_TRACK_UNSPECIFIED) opts->track = git_branch_track; - if (new->name && !new->commit) + if (new_branch_info->name && !new_branch_info->commit) die(_("Cannot switch branch to a non-commit '%s'"), - new->name); + new_branch_info->name); - if (new->path && !opts->force_detach && !opts->new_branch && + if (new_branch_info->path && !opts->force_detach && !opts->new_branch && !opts->ignore_other_worktrees) { int flag; char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag); if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) - die_if_checked_out(new->path, 1); + (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path))) + die_if_checked_out(new_branch_info->path, 1); free(head_ref); } - if (!new->commit && opts->new_branch) { + if (!new_branch_info->commit && opts->new_branch) { struct object_id rev; int flag; - if (!read_ref_full("HEAD", 0, rev.hash, &flag) && + if (!read_ref_full("HEAD", 0, &rev, &flag) && (flag & REF_ISSYMREF) && is_null_oid(&rev)) return switch_unborn_to_new_branch(opts); } - return switch_branches(opts, new); + return switch_branches(opts, new_branch_info); } int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; - struct branch_info new; + struct branch_info new_branch_info; char *conflict_style = NULL; int dwim_new_local_branch = 1; struct option options[] = { @@ -1164,9 +1117,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) 2), OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), 3), - OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")), + OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"), + PARSE_OPT_NOCOMPLETE), OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), - OPT_BOOL(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")), + OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore, + N_("update ignored files (default)"), + PARSE_OPT_NOCOMPLETE), OPT_STRING(0, "conflict", &conflict_style, N_("style"), N_("conflict style (merge or diff3)")), OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")), @@ -1184,7 +1140,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) }; memset(&opts, 0, sizeof(opts)); - memset(&new, 0, sizeof(new)); + memset(&new_branch_info, 0, sizeof(new_branch_info)); opts.overwrite_ignore = 1; opts.prefix = prefix; opts.show_progress = -1; @@ -1256,7 +1212,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.track == BRANCH_TRACK_UNSPECIFIED && !opts.new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, - &new, &opts, &rev); + &new_branch_info, &opts, &rev); argv += n; argc -= n; } @@ -1289,17 +1245,17 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.new_branch) { struct strbuf buf = STRBUF_INIT; - opts.branch_exists = - validate_new_branchname(opts.new_branch, &buf, - !!opts.new_branch_force, - !!opts.new_branch_force); - + if (opts.new_branch_force) + opts.branch_exists = validate_branchname(opts.new_branch, &buf); + else + opts.branch_exists = + validate_new_branchname(opts.new_branch, &buf, 0); strbuf_release(&buf); } UNLEAK(opts); if (opts.patch_mode || opts.pathspec.nr) - return checkout_paths(&opts, new.name); + return checkout_paths(&opts, new_branch_info.name); else - return checkout_branch(&opts, &new); + return checkout_branch(&opts, &new_branch_info); } diff --git a/builtin/clean.c b/builtin/clean.c index 189e20628c..fad533a0a7 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -909,7 +909,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct option options[] = { OPT__QUIET(&quiet, N_("do not print names of files removed")), OPT__DRY_RUN(&dry_run, N_("dry run")), - OPT__FORCE(&force, N_("force")), + OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE), OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")), OPT_BOOL('d', NULL, &remove_directories, N_("remove whole directories")), diff --git a/builtin/clone.c b/builtin/clone.c index dbddd98f80..7df5932b85 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -26,6 +26,8 @@ #include "run-command.h" #include "connected.h" #include "packfile.h" +#include "list-objects-filter-options.h" +#include "object-store.h" /* * Overall FIXMEs: @@ -60,6 +62,7 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; static int option_dissociate; static int max_jobs = -1; static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP; +static struct list_objects_filter_options filter_options; static int recurse_submodules_cb(const struct option *opt, const char *arg, int unset) @@ -135,6 +138,7 @@ static struct option builtin_clone_options[] = { TRANSPORT_FAMILY_IPV4), OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), TRANSPORT_FAMILY_IPV6), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; @@ -452,7 +456,8 @@ static void clone_local(const char *src_repo, const char *dest_repo) { if (option_shared) { struct strbuf alt = STRBUF_INIT; - strbuf_addf(&alt, "%s/objects", src_repo); + get_common_dir(&alt, src_repo); + strbuf_addstr(&alt, "/objects"); add_to_alternates_file(alt.buf); strbuf_release(&alt); } else { @@ -472,7 +477,9 @@ static void clone_local(const char *src_repo, const char *dest_repo) } static const char *junk_work_tree; +static int junk_work_tree_flags; static const char *junk_git_dir; +static int junk_git_dir_flags; static enum { JUNK_LEAVE_NONE, JUNK_LEAVE_REPO, @@ -501,12 +508,12 @@ static void remove_junk(void) if (junk_git_dir) { strbuf_addstr(&sb, junk_git_dir); - remove_dir_recursively(&sb, 0); + remove_dir_recursively(&sb, junk_git_dir_flags); strbuf_reset(&sb); } if (junk_work_tree) { strbuf_addstr(&sb, junk_work_tree); - remove_dir_recursively(&sb, 0); + remove_dir_recursively(&sb, junk_work_tree_flags); } strbuf_release(&sb); } @@ -588,7 +595,7 @@ static void write_remote_refs(const struct ref *local_refs) for (r = local_refs; r; r = r->next) { if (!r->peer_ref) continue; - if (ref_transaction_create(t, r->peer_ref->name, r->old_oid.hash, + if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid, 0, NULL, &err)) die("%s", err.buf); } @@ -610,12 +617,12 @@ static void write_followtags(const struct ref *refs, const char *msg) continue; if (!has_object_file(&ref->old_oid)) continue; - update_ref(msg, ref->name, ref->old_oid.hash, - NULL, 0, UPDATE_REFS_DIE_ON_ERR); + update_ref(msg, ref->name, &ref->old_oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); } } -static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) +static int iterate_ref_map(void *cb_data, struct object_id *oid) { struct ref **rm = cb_data; struct ref *ref = *rm; @@ -630,7 +637,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) if (!ref) return -1; - hashcpy(sha1, ref->old_oid.hash); + oidcpy(oid, &ref->old_oid); *rm = ref->next; return 0; } @@ -682,23 +689,23 @@ static void update_head(const struct ref *our, const struct ref *remote, if (create_symref("HEAD", our->name, NULL) < 0) die(_("unable to update HEAD")); if (!option_bare) { - update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0, + update_ref(msg, "HEAD", &our->old_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); install_branch_config(0, head, option_origin, our->name); } } else if (our) { struct commit *c = lookup_commit_reference(&our->old_oid); /* --branch specifies a non-branch (i.e. tags), detach HEAD */ - update_ref(msg, "HEAD", c->object.oid.hash, - NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); + update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF, + UPDATE_REFS_DIE_ON_ERR); } else if (remote) { /* * We know remote HEAD points to a non-branch, or * HEAD points to a branch but we don't know which one. * Detach HEAD in all these cases. */ - update_ref(msg, "HEAD", remote->old_oid.hash, - NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); + update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF, + UPDATE_REFS_DIE_ON_ERR); } } @@ -706,7 +713,7 @@ static int checkout(int submodule_progress) { struct object_id oid; char *head; - struct lock_file *lock_file; + struct lock_file lock_file = LOCK_INIT; struct unpack_trees_options opts; struct tree *tree; struct tree_desc t; @@ -715,7 +722,7 @@ static int checkout(int submodule_progress) if (option_no_checkout) return 0; - head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL); + head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL); if (!head) { warning(_("remote HEAD refers to nonexistent ref, " "unable to checkout.\n")); @@ -733,8 +740,7 @@ static int checkout(int submodule_progress) /* We need to be in the new work tree for the checkout */ setup_work_tree(); - lock_file = xcalloc(1, sizeof(struct lock_file)); - hold_locked_index(lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); memset(&opts, 0, sizeof opts); opts.update = 1; @@ -750,7 +756,7 @@ static int checkout(int submodule_progress) if (unpack_trees(1, &t, &opts) < 0) die(_("unable to checkout working tree")); - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), @@ -863,10 +869,15 @@ static void dissociate_from_references(void) free(alternates); } +static int dir_exists(const char *path) +{ + struct stat sb; + return !stat(path, &sb); +} + int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; - struct stat buf; const char *repo_name, *repo, *work_tree, *git_dir; char *path, *dir; int dest_exists; @@ -886,6 +897,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct refspec *refspec; const char *fetch_pattern; + fetch_if_missing = 0; + packet_trace_identity("clone"); argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); @@ -938,7 +951,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) dir = guess_dir_name(repo_name, is_bundle, option_bare); strip_trailing_slashes(dir); - dest_exists = !stat(dir, &buf); + dest_exists = dir_exists(dir); if (dest_exists && !is_empty_dir(dir)) die(_("destination path '%s' already exists and is not " "an empty directory."), dir); @@ -949,7 +962,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) work_tree = NULL; else { work_tree = getenv("GIT_WORK_TREE"); - if (work_tree && !stat(work_tree, &buf)) + if (work_tree && dir_exists(work_tree)) die(_("working tree '%s' already exists."), work_tree); } @@ -967,14 +980,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); - if (!dest_exists && mkdir(work_tree, 0777)) + if (dest_exists) + junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL; + else if (mkdir(work_tree, 0777)) die_errno(_("could not create work tree dir '%s'"), work_tree); junk_work_tree = work_tree; set_git_work_tree(work_tree); } - junk_git_dir = real_git_dir ? real_git_dir : git_dir; + if (real_git_dir) { + if (dir_exists(real_git_dir)) + junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL; + junk_git_dir = real_git_dir; + } else { + if (dest_exists) + junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL; + junk_git_dir = git_dir; + } if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); @@ -1073,6 +1096,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) warning(_("--shallow-since is ignored in local clones; use file:// instead.")); if (option_not.nr) warning(_("--shallow-exclude is ignored in local clones; use file:// instead.")); + if (filter_options.choice) + warning(_("--filter is ignored in local clones; use file:// instead.")); if (!access(mkpath("%s/shallow", path), F_OK)) { if (option_local > 0) warning(_("source repository is shallow, ignoring --local")); @@ -1083,9 +1108,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) warning(_("--local is ignored")); transport->cloning = 1; - if (!transport->get_refs_list || (!is_local && !transport->fetch)) - die(_("Don't know how to clone %s"), transport->url); - transport_set_option(transport, TRANS_OPT_KEEP, "yes"); if (option_depth) @@ -1104,7 +1126,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_set_option(transport, TRANS_OPT_UPLOADPACK, option_upload_pack); - if (transport->smart_options && !deepen) + if (filter_options.choice) { + transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, + filter_options.filter_spec); + transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); + } + + if (transport->smart_options && !deepen && !filter_options.choice) transport->smart_options->check_self_contained_and_connected = 1; refs = transport_get_remote_refs(transport); @@ -1164,13 +1192,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix) write_refspec_config(src_ref_prefix, our_head_points_at, remote_head_points_at, &branch_top); + if (filter_options.choice) + partial_clone_register("origin", &filter_options); + if (is_local) clone_local(path, git_dir); else if (refs && complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, - branch_top.buf, reflog_msg.buf, transport, !is_local); + branch_top.buf, reflog_msg.buf, transport, + !is_local && !filter_options.choice); update_head(our_head_points_at, remote_head, reflog_msg.buf); @@ -1186,11 +1218,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_disconnect(transport); if (option_dissociate) { - close_all_packs(); + close_all_packs(the_repository->objects); dissociate_from_references(); } junk_mode = JUNK_LEAVE_REPO; + fetch_if_missing = 1; err = checkout(submodule_progress); strbuf_release(&reflog_msg); diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 2177251e24..ecf42191da 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -58,7 +58,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) usage(commit_tree_usage); if (get_oid_commit(argv[i], &oid)) die("Not a valid object name %s", argv[i]); - assert_sha1_type(oid.hash, OBJ_COMMIT); + assert_oid_type(&oid, OBJ_COMMIT); new_parent(lookup_commit(&oid), &parents); continue; } @@ -117,8 +117,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) die_errno("git commit-tree: failed to read"); } - if (commit_tree(buffer.buf, buffer.len, tree_oid.hash, parents, - commit_oid.hash, NULL, sign_commit)) { + if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid, + NULL, sign_commit)) { strbuf_release(&buffer); return 1; } diff --git a/builtin/commit.c b/builtin/commit.c index be370f64a9..37fcb55ab0 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -31,9 +31,7 @@ #include "gpg-interface.h" #include "column.h" #include "sequencer.h" -#include "notes-utils.h" #include "mailmap.h" -#include "sigchain.h" static const char * const builtin_commit_usage[] = { N_("git commit [<options>] [--] <pathspec>..."), @@ -45,31 +43,6 @@ static const char * const builtin_status_usage[] = { NULL }; -static const char implicit_ident_advice_noconfig[] = -N_("Your name and email address were configured automatically based\n" -"on your username and hostname. Please check that they are accurate.\n" -"You can suppress this message by setting them explicitly. Run the\n" -"following command and follow the instructions in your editor to edit\n" -"your configuration file:\n" -"\n" -" git config --global --edit\n" -"\n" -"After doing this, you may fix the identity used for this commit with:\n" -"\n" -" git commit --amend --reset-author\n"); - -static const char implicit_ident_advice_config[] = -N_("Your name and email address were configured automatically based\n" -"on your username and hostname. Please check that they are accurate.\n" -"You can suppress this message by setting them explicitly:\n" -"\n" -" git config --global user.name \"Your Name\"\n" -" git config --global user.email you@example.com\n" -"\n" -"After doing this, you may fix the identity used for this commit with:\n" -"\n" -" git commit --amend --reset-author\n"); - static const char empty_amend_advice[] = N_("You asked to amend the most recent commit, but doing so would make\n" "it empty. You can repeat your command with --allow-empty, or you can\n" @@ -93,8 +66,6 @@ N_("If you wish to skip this commit, use:\n" "Then \"git cherry-pick --continue\" will resume cherry-picking\n" "the remaining commits.\n"); -static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG") - static const char *use_message_buffer; static struct lock_file index_lock; /* real index */ static struct lock_file false_lock; /* used only for partial commits */ @@ -118,7 +89,7 @@ static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ static int no_post_rewrite, allow_empty_message; -static char *untracked_files_arg, *force_date, *ignore_submodule_arg; +static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; static char *sign_commit; /* @@ -128,18 +99,13 @@ static char *sign_commit; * if editor is used, and only the whitespaces if the message * is specified explicitly. */ -static enum { - CLEANUP_SPACE, - CLEANUP_NONE, - CLEANUP_SCISSORS, - CLEANUP_ALL -} cleanup_mode; +static enum commit_msg_cleanup_mode cleanup_mode; static const char *cleanup_arg; static enum commit_whence whence; static int sequencer_in_use; static int use_editor = 1, include_status = 1; -static int show_ignored_in_status, have_option_m; +static int have_option_m; static struct strbuf message = STRBUF_INIT; static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; @@ -355,7 +321,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix refresh_cache_or_die(refresh_flags); - if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) + if (write_locked_index(&the_index, &index_lock, 0)) die(_("unable to create temporary index")); old_index_env = getenv(INDEX_ENVIRONMENT); @@ -374,7 +340,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) { if (reopen_lock_file(&index_lock) < 0) die(_("unable to write index file")); - if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) + if (write_locked_index(&the_index, &index_lock, 0)) die(_("unable to update temporary index")); } else warning(_("Failed to update main cache tree")); @@ -401,7 +367,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) + if (write_locked_index(&the_index, &index_lock, 0)) die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); @@ -423,13 +389,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix if (active_cache_changed || !cache_tree_fully_valid(active_cache_tree)) update_main_cache_tree(WRITE_TREE_SILENT); - if (active_cache_changed) { - if (write_locked_index(&the_index, &index_lock, - COMMIT_LOCK)) - die(_("unable to write new_index file")); - } else { - rollback_lock_file(&index_lock); - } + if (write_locked_index(&the_index, &index_lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) + die(_("unable to write new_index file")); commit_style = COMMIT_AS_IS; ret = get_index_file(); goto out; @@ -474,7 +436,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix add_remove_files(&partial); refresh_cache(REFRESH_QUIET); update_main_cache_tree(WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) + if (write_locked_index(&the_index, &index_lock, 0)) die(_("unable to write new_index file")); hold_lock_file_for_update(&false_lock, @@ -486,7 +448,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix add_remove_files(&partial); refresh_cache(REFRESH_QUIET); - if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK)) + if (write_locked_index(&the_index, &false_lock, 0)) die(_("unable to write temporary index file")); discard_cache(); @@ -673,7 +635,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct strbuf sb = STRBUF_INIT; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; - int clean_message_contents = (cleanup_mode != CLEANUP_NONE); + int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE); int old_display_comment_prefix; /* This checks and barfs if author is badly specified */ @@ -701,7 +663,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } } - if (have_option_m) { + if (have_option_m && !fixup_message) { strbuf_addbuf(&sb, &message); hook_arg1 = "message"; } else if (logfile && !strcmp(logfile, "-")) { @@ -731,6 +693,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, ctx.output_encoding = get_commit_output_encoding(); format_commit_message(commit, "fixup! %s\n\n", &sb, &ctx); + if (have_option_m) + strbuf_addbuf(&sb, &message); hook_arg1 = "message"; } else if (!stat(git_path_merge_msg(), &statbuf)) { /* @@ -812,7 +776,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct ident_split ci, ai; if (whence != FROM_COMMIT) { - if (cleanup_mode == CLEANUP_SCISSORS) + if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) wt_status_add_cut_line(s->fp); status_printf_ln(s, GIT_COLOR_NORMAL, whence == FROM_MERGE @@ -832,14 +796,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } fprintf(s->fp, "\n"); - if (cleanup_mode == CLEANUP_ALL) + if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL) status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." " Lines starting\nwith '%c' will be ignored, and an empty" " message aborts the commit.\n"), comment_line_char); - else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT) + else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS && + whence == FROM_COMMIT) wt_status_add_cut_line(s->fp); - else /* CLEANUP_SPACE, that is. */ + else /* COMMIT_MSG_CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." " Lines starting\n" @@ -912,11 +877,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix, * submodules which were manually staged, which would * be really confusing. */ - int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG; + struct diff_flags flags = DIFF_FLAGS_INIT; + flags.override_submodule_config = 1; if (ignore_submodule_arg && !strcmp(ignore_submodule_arg, "all")) - diff_flags |= DIFF_OPT_IGNORE_SUBMODULES; - commitable = index_differs_from(parent, diff_flags, 1); + flags.ignore_submodules = 1; + commitable = index_differs_from(parent, &flags, 1); } } strbuf_release(&committer_ident); @@ -983,65 +949,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix, return 1; } -static int rest_is_empty(struct strbuf *sb, int start) -{ - int i, eol; - const char *nl; - - /* Check if the rest is just whitespace and Signed-off-by's. */ - for (i = start; i < sb->len; i++) { - nl = memchr(sb->buf + i, '\n', sb->len - i); - if (nl) - eol = nl - sb->buf; - else - eol = sb->len; - - if (strlen(sign_off_header) <= eol - i && - starts_with(sb->buf + i, sign_off_header)) { - i = eol; - continue; - } - while (i < eol) - if (!isspace(sb->buf[i++])) - return 0; - } - - return 1; -} - -/* - * Find out if the message in the strbuf contains only whitespace and - * Signed-off-by lines. - */ -static int message_is_empty(struct strbuf *sb) -{ - if (cleanup_mode == CLEANUP_NONE && sb->len) - return 0; - return rest_is_empty(sb, 0); -} - -/* - * See if the user edited the message in the editor or left what - * was in the template intact - */ -static int template_untouched(struct strbuf *sb) -{ - struct strbuf tmpl = STRBUF_INIT; - const char *start; - - if (cleanup_mode == CLEANUP_NONE && sb->len) - return 0; - - if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0) - return 0; - - strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL); - if (!skip_prefix(sb->buf, tmpl.buf, &start)) - start = sb->buf; - strbuf_release(&tmpl); - return rest_is_empty(sb, start - sb->buf); -} - static const char *find_author_by_nickname(const char *name) { struct rev_info revs; @@ -1075,6 +982,19 @@ static const char *find_author_by_nickname(const char *name) die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name); } +static void handle_ignored_arg(struct wt_status *s) +{ + if (!ignored_arg) + ; /* default already initialized */ + else if (!strcmp(ignored_arg, "traditional")) + s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED; + else if (!strcmp(ignored_arg, "no")) + s->show_ignored_mode = SHOW_NO_IGNORED; + else if (!strcmp(ignored_arg, "matching")) + s->show_ignored_mode = SHOW_MATCHING_IGNORED; + else + die(_("Invalid ignored mode '%s'"), ignored_arg); +} static void handle_untracked_files_arg(struct wt_status *s) { @@ -1137,6 +1057,9 @@ static void finalize_deferred_config(struct wt_status *s) s->show_branch = status_deferred_config.show_branch; if (s->show_branch < 0) s->show_branch = 0; + + if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) + s->ahead_behind_flags = AHEAD_BEHIND_FULL; } static int parse_and_validate_options(int argc, const char *argv[], @@ -1183,8 +1106,8 @@ static int parse_and_validate_options(int argc, const char *argv[], f++; if (f > 1) die(_("Only one of -c/-C/-F/--fixup can be used.")); - if (have_option_m && f > 0) - die((_("Option -m cannot be combined with -c/-C/-F/--fixup."))); + if (have_option_m && (edit_message || use_message || logfile)) + die((_("Option -m cannot be combined with -c/-C/-F."))); if (f || have_option_m) template_file = NULL; if (edit_message) @@ -1213,15 +1136,17 @@ static int parse_and_validate_options(int argc, const char *argv[], if (argc == 0 && (also || (only && !amend && !allow_empty))) die(_("No paths with --include/--only does not make sense.")); if (!cleanup_arg || !strcmp(cleanup_arg, "default")) - cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; + cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL : + COMMIT_MSG_CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) - cleanup_mode = CLEANUP_NONE; + cleanup_mode = COMMIT_MSG_CLEANUP_NONE; else if (!strcmp(cleanup_arg, "whitespace")) - cleanup_mode = CLEANUP_SPACE; + cleanup_mode = COMMIT_MSG_CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "strip")) - cleanup_mode = CLEANUP_ALL; + cleanup_mode = COMMIT_MSG_CLEANUP_ALL; else if (!strcmp(cleanup_arg, "scissors")) - cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE; + cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS : + COMMIT_MSG_CLEANUP_SPACE; else die(_("Invalid cleanup mode %s"), cleanup_arg); @@ -1351,6 +1276,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("show branch information")), OPT_BOOL(0, "show-stash", &s.show_stash, N_("show stash information")), + OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags, + N_("compute full ahead/behind values")), { OPTION_CALLBACK, 0, "porcelain", &status_format, N_("version"), N_("machine-readable output"), PARSE_OPT_OPTARG, opt_parse_porcelain }, @@ -1363,8 +1290,10 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, - OPT_BOOL(0, "ignored", &show_ignored_in_status, - N_("show ignored files")), + { OPTION_STRING, 0, "ignored", &ignored_arg, + N_("mode"), + N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"), + PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" }, { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"), N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, @@ -1383,8 +1312,12 @@ int cmd_status(int argc, const char **argv, const char *prefix) finalize_deferred_config(&s); handle_untracked_files_arg(&s); - if (show_ignored_in_status) - s.show_ignored_files = 1; + handle_ignored_arg(&s); + + if (s.show_ignored_mode == SHOW_MATCHING_IGNORED && + s.show_untracked_files == SHOW_NO_UNTRACKED_FILES) + die(_("Unsupported combination of ignored and untracked-files arguments")); + parse_pathspec(&s.pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); @@ -1417,98 +1350,6 @@ int cmd_status(int argc, const char **argv, const char *prefix) return 0; } -static const char *implicit_ident_advice(void) -{ - char *user_config = expand_user_path("~/.gitconfig", 0); - char *xdg_config = xdg_config_home("config"); - int config_exists = file_exists(user_config) || file_exists(xdg_config); - - free(user_config); - free(xdg_config); - - if (config_exists) - return _(implicit_ident_advice_config); - else - return _(implicit_ident_advice_noconfig); - -} - -static void print_summary(const char *prefix, const struct object_id *oid, - int initial_commit) -{ - struct rev_info rev; - struct commit *commit; - struct strbuf format = STRBUF_INIT; - const char *head; - struct pretty_print_context pctx = {0}; - struct strbuf author_ident = STRBUF_INIT; - struct strbuf committer_ident = STRBUF_INIT; - - commit = lookup_commit(oid); - if (!commit) - die(_("couldn't look up newly created commit")); - if (parse_commit(commit)) - die(_("could not parse newly created commit")); - - strbuf_addstr(&format, "format:%h] %s"); - - format_commit_message(commit, "%an <%ae>", &author_ident, &pctx); - format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx); - if (strbuf_cmp(&author_ident, &committer_ident)) { - strbuf_addstr(&format, "\n Author: "); - strbuf_addbuf_percentquote(&format, &author_ident); - } - if (author_date_is_interesting()) { - struct strbuf date = STRBUF_INIT; - format_commit_message(commit, "%ad", &date, &pctx); - strbuf_addstr(&format, "\n Date: "); - strbuf_addbuf_percentquote(&format, &date); - strbuf_release(&date); - } - if (!committer_ident_sufficiently_given()) { - strbuf_addstr(&format, "\n Committer: "); - strbuf_addbuf_percentquote(&format, &committer_ident); - if (advice_implicit_identity) { - strbuf_addch(&format, '\n'); - strbuf_addstr(&format, implicit_ident_advice()); - } - } - strbuf_release(&author_ident); - strbuf_release(&committer_ident); - - init_revisions(&rev, prefix); - setup_revisions(0, NULL, &rev, NULL); - - rev.diff = 1; - rev.diffopt.output_format = - DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY; - - rev.verbose_header = 1; - rev.show_root_diff = 1; - get_commit_format(format.buf, &rev); - rev.always_show_header = 0; - rev.diffopt.detect_rename = 1; - rev.diffopt.break_opt = 0; - diff_setup_done(&rev.diffopt); - - head = resolve_ref_unsafe("HEAD", 0, NULL, NULL); - if (!head) - die_errno(_("unable to resolve HEAD after creating commit")); - if (!strcmp(head, "HEAD")) - head = _("detached HEAD"); - else - skip_prefix(head, "refs/heads/", &head); - printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : ""); - - if (!log_tree_commit(&rev, commit)) { - rev.always_show_header = 1; - rev.use_terminator = 1; - log_tree_commit(&rev, commit); - } - - strbuf_release(&format); -} - static int git_commit_config(const char *k, const char *v, void *cb) { struct wt_status *s = cb; @@ -1538,37 +1379,6 @@ static int git_commit_config(const char *k, const char *v, void *cb) return git_status_config(k, v, s); } -static int run_rewrite_hook(const struct object_id *oldoid, - const struct object_id *newoid) -{ - struct child_process proc = CHILD_PROCESS_INIT; - const char *argv[3]; - int code; - struct strbuf sb = STRBUF_INIT; - - argv[0] = find_hook("post-rewrite"); - if (!argv[0]) - return 0; - - argv[1] = "amend"; - argv[2] = NULL; - - proc.argv = argv; - proc.in = -1; - proc.stdout_to_stderr = 1; - - code = start_command(&proc); - if (code) - return code; - strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid)); - sigchain_push(SIGPIPE, SIG_IGN); - write_in_full(proc.in, sb.buf, sb.len); - close(proc.in); - strbuf_release(&sb); - sigchain_pop(SIGPIPE); - return finish_command(&proc); -} - int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...) { struct argv_array hook_env = ARGV_ARRAY_INIT; @@ -1593,6 +1403,7 @@ int run_commit_hook(int editor_is_used, const char *index_file, const char *name int cmd_commit(int argc, const char **argv, const char *prefix) { + const char *argv_gc_auto[] = {"gc", "--auto", NULL}; static struct wt_status s; static struct option builtin_commit_options[] = { OPT__QUIET(&quiet, N_("suppress summary after successful commit")), @@ -1628,6 +1439,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")), + OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags, + N_("compute full ahead/behind values")), OPT_SET_INT(0, "porcelain", &status_format, N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_SET_INT(0, "long", &status_format, @@ -1651,13 +1464,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct strbuf sb = STRBUF_INIT; struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; - char *nl; struct object_id oid; struct commit_list *parents = NULL; struct stat statbuf; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; - struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; if (argc == 2 && !strcmp(argv[1], "-h")) @@ -1748,17 +1559,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } if (verbose || /* Truncate the message just before the diff, if any. */ - cleanup_mode == CLEANUP_SCISSORS) + cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len)); - if (cleanup_mode != CLEANUP_NONE) - strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL); + if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE) + strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL); - if (message_is_empty(&sb) && !allow_empty_message) { + if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, _("Aborting commit due to empty commit message.\n")); exit(1); } - if (template_untouched(&sb) && !allow_empty_message) { + if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, _("Aborting commit; you did not edit the message.\n")); exit(1); @@ -1772,33 +1583,20 @@ int cmd_commit(int argc, const char **argv, const char *prefix) append_merge_tag_headers(parents, &tail); } - if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->oid.hash, - parents, oid.hash, author_ident.buf, sign_commit, extra)) { + if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid, + parents, &oid, author_ident.buf, sign_commit, + extra)) { rollback_index_files(); die(_("failed to write commit object")); } strbuf_release(&author_ident); free_commit_extra_headers(extra); - nl = strchr(sb.buf, '\n'); - if (nl) - strbuf_setlen(&sb, nl + 1 - sb.buf); - else - strbuf_addch(&sb, '\n'); - strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); - strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); - - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, "HEAD", oid.hash, - current_head - ? current_head->object.oid.hash : null_sha1, - 0, sb.buf, &err) || - ref_transaction_commit(transaction, &err)) { + if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb, + &err)) { rollback_index_files(); die("%s", err.buf); } - ref_transaction_free(transaction); unlink(git_path_cherry_pick_head()); unlink(git_path_revert_head()); @@ -1813,19 +1611,20 @@ int cmd_commit(int argc, const char **argv, const char *prefix) "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); + 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) { - struct notes_rewrite_cfg *cfg; - cfg = init_copy_notes_for_rewrite("amend"); - if (cfg) { - /* we are amending, so current_head is not NULL */ - copy_note_for_rewrite(cfg, ¤t_head->object.oid, &oid); - finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'"); - } - run_rewrite_hook(¤t_head->object.oid, &oid); + commit_post_rewrite(current_head, &oid); + } + if (!quiet) { + unsigned int flags = 0; + + if (!current_head) + flags |= SUMMARY_INITIAL_COMMIT; + if (author_date_is_interesting()) + flags |= SUMMARY_SHOW_AUTHOR_DATE; + print_commit_summary(prefix, &oid, flags); } - if (!quiet) - print_summary(prefix, &oid, !current_head); UNLEAK(err); UNLEAK(sb); diff --git a/builtin/config.c b/builtin/config.c index d13daeeb55..01169dd628 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -48,10 +48,18 @@ static int show_origin; #define ACTION_GET_COLORBOOL (1<<14) #define ACTION_GET_URLMATCH (1<<15) +/* + * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than + * one line of output and which should therefore be paged. + */ +#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \ + ACTION_GET_REGEXP | ACTION_GET_URLMATCH) + #define TYPE_BOOL (1<<0) #define TYPE_INT (1<<1) #define TYPE_BOOL_OR_INT (1<<2) #define TYPE_PATH (1<<3) +#define TYPE_EXPIRY_DATE (1<<4) static struct option builtin_config_options[] = { OPT_GROUP(N_("Config file location")), @@ -80,6 +88,7 @@ static struct option builtin_config_options[] = { OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT), OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH), + OPT_BIT(0, "expiry-date", &types, N_("value is an expiry date"), TYPE_EXPIRY_DATE), OPT_GROUP(N_("Other")), OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), @@ -159,6 +168,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value return -1; strbuf_addstr(buf, v); free((char *)v); + } else if (types == TYPE_EXPIRY_DATE) { + timestamp_t t; + if (git_config_expiry_date(&t, key_, value_) < 0) + return -1; + strbuf_addf(buf, "%"PRItime, t); } else if (value_) { strbuf_addstr(buf, value_); } else { @@ -273,12 +287,13 @@ static char *normalize_value(const char *key, const char *value) if (!value) return NULL; - if (types == 0 || types == TYPE_PATH) + if (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE) /* * We don't do normalization for TYPE_PATH here: If * the path is like ~/foobar/, we prefer to store * "~/foobar/" in the config file, and to expand the ~ * when retrieving the value. + * Also don't do normalization for expiry dates. */ return xstrdup(value); if (types == TYPE_INT) @@ -586,6 +601,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) usage_with_options(builtin_config_usage, builtin_config_options); } + if (actions & PAGING_ACTIONS) + setup_auto_pager("config", 1); + if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (config_with_options(show_all_config, NULL, diff --git a/builtin/count-objects.c b/builtin/count-objects.c index 33343818c8..b054713e1a 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -7,10 +7,12 @@ #include "cache.h" #include "config.h" #include "dir.h" +#include "repository.h" #include "builtin.h" #include "parse-options.h" #include "quote.h" #include "packfile.h" +#include "object-store.h" static unsigned long garbage; static off_t size_garbage; @@ -120,9 +122,8 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) struct strbuf loose_buf = STRBUF_INIT; struct strbuf pack_buf = STRBUF_INIT; struct strbuf garbage_buf = STRBUF_INIT; - if (!packed_git) - prepare_packed_git(); - for (p = packed_git; p; p = p->next) { + + for (p = get_packed_git(the_repository); p; p = p->next) { if (!p->pack_local) continue; if (open_pack_index(p)) diff --git a/builtin/describe.c b/builtin/describe.c index 29075dbd0f..de840f96a4 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -3,16 +3,19 @@ #include "lockfile.h" #include "commit.h" #include "tag.h" +#include "blob.h" #include "refs.h" #include "builtin.h" #include "exec_cmd.h" #include "parse-options.h" +#include "revision.h" #include "diff.h" #include "hashmap.h" #include "argv-array.h" #include "run-command.h" +#include "revision.h" +#include "list-objects.h" -#define SEEN (1u << 0) #define MAX_TAGS (FLAG_BITS - 1) static const char * const describe_usage[] = { @@ -181,7 +184,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi } /* Is it annotated? */ - if (!peel_ref(path, peeled.hash)) { + if (!peel_ref(path, &peeled)) { is_annotated = !!oidcmp(oid, &peeled); } else { oidcpy(&peeled, oid); @@ -256,7 +259,7 @@ static unsigned long finish_depth_computation( return seen_commits; } -static void display_name(struct commit_name *n) +static void append_name(struct commit_name *n, struct strbuf *dst) { if (n->prio == 2 && !n->tag) { n->tag = lookup_tag(&n->oid); @@ -271,20 +274,22 @@ static void display_name(struct commit_name *n) n->name_checked = 1; } - if (n->tag) - printf("%s", n->tag->tag); - else - printf("%s", n->path); + if (n->tag) { + if (all) + strbuf_addstr(dst, "tags/"); + strbuf_addstr(dst, n->tag->tag); + } else { + strbuf_addstr(dst, n->path); + } } -static void show_suffix(int depth, const struct object_id *oid) +static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst) { - printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev)); + strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev)); } -static void describe(const char *arg, int last_one) +static void describe_commit(struct object_id *oid, struct strbuf *dst) { - struct object_id oid; struct commit *cmit, *gave_up_on = NULL; struct commit_list *list; struct commit_name *n; @@ -293,30 +298,25 @@ static void describe(const char *arg, int last_one) unsigned long seen_commits = 0; unsigned int unannotated_cnt = 0; - if (get_oid(arg, &oid)) - die(_("Not a valid object name %s"), arg); - cmit = lookup_commit_reference(&oid); - if (!cmit) - die(_("%s is not a valid '%s' object"), arg, commit_type); + cmit = lookup_commit_reference(oid); n = find_commit_name(&cmit->object.oid); if (n && (tags || all || n->prio == 2)) { /* * Exact match to an existing ref. */ - display_name(n); + append_name(n, dst); if (longformat) - show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid); + append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst); if (suffix) - printf("%s", suffix); - printf("\n"); + strbuf_addstr(dst, suffix); return; } if (!max_candidates) die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid)); if (debug) - fprintf(stderr, _("searching to describe %s\n"), arg); + fprintf(stderr, _("No exact match on refs or tags, searching to describe\n")); if (!have_util) { struct hashmap_iter iter; @@ -381,22 +381,21 @@ static void describe(const char *arg, int last_one) } if (!match_cnt) { - struct object_id *oid = &cmit->object.oid; + struct object_id *cmit_oid = &cmit->object.oid; if (always) { - printf("%s", find_unique_abbrev(oid->hash, abbrev)); + strbuf_add_unique_abbrev(dst, cmit_oid, abbrev); if (suffix) - printf("%s", suffix); - printf("\n"); + strbuf_addstr(dst, suffix); return; } if (unannotated_cnt) die(_("No annotated tags can describe '%s'.\n" "However, there were unannotated tags: try --tags."), - oid_to_hex(oid)); + oid_to_hex(cmit_oid)); else die(_("No tags can describe '%s'.\n" "Try --always, or create some tags."), - oid_to_hex(oid)); + oid_to_hex(cmit_oid)); } QSORT(all_matches, match_cnt, compare_pt); @@ -434,15 +433,86 @@ static void describe(const char *arg, int last_one) } } - display_name(all_matches[0].name); + append_name(all_matches[0].name, dst); if (abbrev) - show_suffix(all_matches[0].depth, &cmit->object.oid); + append_suffix(all_matches[0].depth, &cmit->object.oid, dst); if (suffix) - printf("%s", suffix); - printf("\n"); + strbuf_addstr(dst, suffix); +} + +struct process_commit_data { + struct object_id current_commit; + struct object_id looking_for; + struct strbuf *dst; + struct rev_info *revs; +}; + +static void process_commit(struct commit *commit, void *data) +{ + struct process_commit_data *pcd = data; + pcd->current_commit = commit->object.oid; +} + +static void process_object(struct object *obj, const char *path, void *data) +{ + struct process_commit_data *pcd = data; + + if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) { + reset_revision_walk(); + describe_commit(&pcd->current_commit, pcd->dst); + strbuf_addf(pcd->dst, ":%s", path); + free_commit_list(pcd->revs->commits); + pcd->revs->commits = NULL; + } +} + +static void describe_blob(struct object_id oid, struct strbuf *dst) +{ + struct rev_info revs; + struct argv_array args = ARGV_ARRAY_INIT; + struct process_commit_data pcd = { null_oid, oid, dst, &revs}; + + argv_array_pushl(&args, "internal: The first arg is not parsed", + "--objects", "--in-commit-order", "--reverse", "HEAD", + NULL); + + init_revisions(&revs, NULL); + if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1) + BUG("setup_revisions could not handle all args?"); + + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + + traverse_commit_list(&revs, process_commit, process_object, &pcd); + reset_revision_walk(); +} + +static void describe(const char *arg, int last_one) +{ + struct object_id oid; + struct commit *cmit; + struct strbuf sb = STRBUF_INIT; + + if (debug) + fprintf(stderr, _("describe %s\n"), arg); + + if (get_oid(arg, &oid)) + die(_("Not a valid object name %s"), arg); + cmit = lookup_commit_reference_gently(&oid, 1); + + if (cmit) + describe_commit(&oid, &sb); + else if (oid_object_info(&oid, NULL) == OBJ_BLOB) + describe_blob(oid, &sb); + else + die(_("%s is neither a commit nor blob"), arg); + + puts(sb.buf); if (!last_one) clear_commit_marks(cmit, -1); + + strbuf_release(&sb); } int cmd_describe(int argc, const char **argv, const char *prefix) @@ -543,7 +613,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) } } else if (dirty) { static struct lock_file index_lock; - int fd; + struct rev_info revs; + struct argv_array args = ARGV_ARRAY_INIT; + int fd, result; read_cache_preload(NULL); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, @@ -552,8 +624,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (0 <= fd) update_index_if_able(&the_index, &index_lock); - if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, - diff_index_args, prefix)) + init_revisions(&revs, prefix); + argv_array_pushv(&args, diff_index_args); + if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1) + BUG("malformed internal diff-index command line"); + result = run_diff_index(&revs, 0); + + if (!diff_result_code(&revs.diffopt, result)) suffix = NULL; else suffix = dirty; diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index d66499909e..473615117e 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -76,7 +76,7 @@ static int diff_tree_stdin(char *line) if (obj->type == OBJ_TREE) return stdin_diff_trees((struct tree *)obj, p); error("Object %s is a %s, not a commit or tree", - oid_to_hex(&oid), typename(obj->type)); + oid_to_hex(&oid), type_name(obj->type)); return -1; } @@ -110,6 +110,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(opt, prefix); + if (read_cache() < 0) + die(_("index file corrupt")); opt->abbrev = 0; opt->diff = 1; opt->disable_stdin = 1; diff --git a/builtin/diff.c b/builtin/diff.c index f5bbd4d757..16bfb22f73 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -44,7 +44,7 @@ static void stuff_change(struct diff_options *opt, !oidcmp(old_oid, new_oid) && (old_mode == new_mode)) return; - if (DIFF_OPT_TST(opt, REVERSE_DIFF)) { + if (opt->flags.reverse_diff) { SWAP(old_mode, new_mode); SWAP(old_oid, new_oid); SWAP(old_path, new_path); @@ -203,17 +203,16 @@ static int builtin_diff_combined(struct rev_info *revs, static void refresh_index_quietly(void) { - struct lock_file *lock_file; + struct lock_file lock_file = LOCK_INIT; int fd; - lock_file = xcalloc(1, sizeof(struct lock_file)); - fd = hold_locked_index(lock_file, 0); + fd = hold_locked_index(&lock_file, 0); if (fd < 0) return; discard_cache(); read_cache(); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); - update_index_if_able(&the_index, lock_file); + update_index_if_able(&the_index, &lock_file); } static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) @@ -350,8 +349,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) rev.diffopt.stat_graph_width = -1; /* Default to let external and textconv be used */ - DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); - DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); + rev.diffopt.flags.allow_external = 1; + rev.diffopt.flags.allow_textconv = 1; if (nongit) die(_("Not a git repository")); @@ -361,7 +360,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) diff_setup_done(&rev.diffopt); } - DIFF_OPT_SET(&rev.diffopt, RECURSIVE); + rev.diffopt.flags.recursive = 1; setup_diff_pager(&rev.diffopt); @@ -380,7 +379,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; - tree = lookup_tree(&empty_tree_oid); + tree = lookup_tree(the_hash_algo->empty_tree); add_pending_object(&rev, &tree->object, "HEAD"); } break; diff --git a/builtin/difftool.c b/builtin/difftool.c index b2d3ba7539..ee8dce019e 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -306,7 +306,7 @@ static char *get_symlink(const struct object_id *oid, const char *path) } else { enum object_type type; unsigned long size; - data = read_sha1_file(oid->hash, &type, &size); + data = read_object_file(oid, &type, &size); if (!data) die(_("could not read object %s for symlink %s"), oid_to_hex(oid), path); @@ -616,7 +616,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || write_locked_index(&wtindex, &lock, COMMIT_LOCK)) { ret = error("could not write %s", buf.buf); - rollback_lock_file(&lock); goto finish; } changed_files(&wt_modified, buf.buf, workdir); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 2fb60d6d48..a15898d641 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -237,10 +237,10 @@ static void export_blob(const struct object_id *oid) object = (struct object *)lookup_blob(oid); eaten = 0; } else { - buf = read_sha1_file(oid->hash, &type, &size); + buf = read_object_file(oid, &type, &size); if (!buf) die ("Could not read blob %s", oid_to_hex(oid)); - if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0) + if (check_object_signature(oid, buf, size, type_name(type)) < 0) die("sha1 mismatch in blob %s", oid_to_hex(oid)); object = parse_object_buffer(oid, type, size, buf, &eaten); } @@ -682,7 +682,7 @@ static void handle_tag(const char *name, struct tag *tag) return; } - buf = read_sha1_file(tag->object.oid.hash, &type, &size); + buf = read_object_file(&tag->object.oid, &type, &size); if (!buf) die ("Could not read tag %s", oid_to_hex(&tag->object.oid)); message = memmem(buf, size, "\n\n", 2); @@ -757,7 +757,7 @@ static void handle_tag(const char *name, struct tag *tag) if (tagged->type != OBJ_COMMIT) { die ("Tag %s tags unexported %s!", oid_to_hex(&tag->object.oid), - typename(tagged->type)); + type_name(tagged->type)); } p = (struct commit *)tagged; for (;;) { @@ -823,7 +823,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) if (e->flags & UNINTERESTING) continue; - if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1) + if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1) continue; if (refspecs) { @@ -839,7 +839,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) if (!commit) { warning("%s: Unexpected object of type %s, skipping.", e->name, - typename(e->item->type)); + type_name(e->item->type)); continue; } @@ -851,7 +851,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) continue; default: /* OBJ_TAG (nested tags) is already handled */ warning("Tag points to object of unexpected type %s, skipping.", - typename(commit->object.type)); + type_name(commit->object.type)); continue; } @@ -895,7 +895,7 @@ static void export_marks(char *file) { unsigned int i; uint32_t mark; - struct object_decoration *deco = idnums.hash; + struct decoration_entry *deco = idnums.entries; FILE *f; int e = 0; @@ -947,7 +947,7 @@ static void import_marks(char *input_file) if (last_idnum < mark) last_idnum = mark; - type = sha1_object_info(oid.hash, NULL); + type = oid_object_info(&oid, NULL); if (type < 0) die("object not found: %s", oid_to_hex(&oid)); @@ -1066,7 +1066,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) die("revision walk setup failed"); revs.diffopt.format_callback = show_filemodify; revs.diffopt.format_callback_data = &paths_of_changed_objects; - DIFF_OPT_SET(&revs.diffopt, RECURSIVE); + revs.diffopt.flags.recursive = 1; while ((commit = get_revision(&revs))) { if (has_unshown_parent(commit)) { add_object_array(&commit->object, NULL, &commits); diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 366b9d13f9..a7bc1366ab 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -53,6 +53,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) struct oid_array shallow = OID_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; + fetch_if_missing = 0; + packet_trace_identity("fetch-pack"); memset(&args, 0, sizeof(args)); @@ -143,6 +145,22 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.update_shallow = 1; continue; } + if (!strcmp("--from-promisor", arg)) { + args.from_promisor = 1; + continue; + } + if (!strcmp("--no-dependents", arg)) { + args.no_dependents = 1; + continue; + } + if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { + parse_list_objects_filter(&args.filter_options, arg); + continue; + } + if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { + list_objects_filter_set_no_filter(&args.filter_options); + continue; + } usage(fetch_pack_usage); } if (deepen_not.nr) diff --git a/builtin/fetch.c b/builtin/fetch.c index 225c734924..dcdfc66f09 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -3,6 +3,7 @@ */ #include "cache.h" #include "config.h" +#include "repository.h" #include "refs.h" #include "commit.h" #include "builtin.h" @@ -18,6 +19,7 @@ #include "argv-array.h" #include "utf8.h" #include "packfile.h" +#include "list-objects-filter-options.h" static const char * const builtin_fetch_usage[] = { N_("git fetch [<options>] [<repository> [<refspec>...]]"), @@ -37,6 +39,10 @@ static int fetch_prune_config = -1; /* unspecified */ static int prune = -1; /* unspecified */ #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ +static int fetch_prune_tags_config = -1; /* unspecified */ +static int prune_tags = -1; /* unspecified */ +#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ + static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative; static int progress = -1; static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; @@ -55,6 +61,7 @@ static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; static int shown_url = 0; static int refmap_alloc, refmap_nr; static const char **refmap_array; +static struct list_objects_filter_options filter_options; static int git_fetch_config(const char *k, const char *v, void *cb) { @@ -63,6 +70,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb) return 0; } + if (!strcmp(k, "fetch.prunetags")) { + fetch_prune_tags_config = git_config_bool(k, v); + return 0; + } + if (!strcmp(k, "submodule.recurse")) { int r = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; @@ -114,7 +126,7 @@ static struct option builtin_fetch_options[] = { N_("append to .git/FETCH_HEAD instead of overwriting")), OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), N_("path to upload pack on remote end")), - OPT__FORCE(&force, N_("force overwrite of local branch")), + OPT__FORCE(&force, N_("force overwrite of local branch"), 0), OPT_BOOL('m', "multiple", &multiple, N_("fetch from multiple remotes")), OPT_SET_INT('t', "tags", &tags, @@ -125,6 +137,8 @@ static struct option builtin_fetch_options[] = { N_("number of submodules fetched in parallel")), OPT_BOOL('p', "prune", &prune, N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('P', "prune-tags", &prune_tags, + N_("prune local tags no longer on remote and clobber changed tags")), { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules }, @@ -160,6 +174,7 @@ static struct option builtin_fetch_options[] = { TRANSPORT_FAMILY_IPV4), OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), TRANSPORT_FAMILY_IPV6), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; @@ -457,8 +472,8 @@ static int s_update_ref(const char *action, transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref->name, - ref->new_oid.hash, - check_old ? ref->old_oid.hash : NULL, + &ref->new_oid, + check_old ? &ref->old_oid : NULL, 0, msg, &err)) goto fail; @@ -622,7 +637,7 @@ static int update_local_ref(struct ref *ref, struct branch *current_branch = branch_get(NULL); const char *pretty_ref = prettify_refname(ref->name); - type = sha1_object_info(ref->new_oid.hash, NULL); + type = oid_object_info(&ref->new_oid, NULL); if (type < 0) die(_("object %s not found"), oid_to_hex(&ref->new_oid)); @@ -693,9 +708,9 @@ static int update_local_ref(struct ref *ref, if (in_merge_bases(current, updated)) { struct strbuf quickref = STRBUF_INIT; int r; - strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(&quickref, ¤t->object.oid, DEFAULT_ABBREV); strbuf_addstr(&quickref, ".."); - strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(&ref->new_oid); @@ -708,9 +723,9 @@ static int update_local_ref(struct ref *ref, } else if (force || ref->force) { struct strbuf quickref = STRBUF_INIT; int r; - strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(&quickref, ¤t->object.oid, DEFAULT_ABBREV); strbuf_addstr(&quickref, "..."); - strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(&ref->new_oid); @@ -727,7 +742,7 @@ static int update_local_ref(struct ref *ref, } } -static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) +static int iterate_ref_map(void *cb_data, struct object_id *oid) { struct ref **rm = cb_data; struct ref *ref = *rm; @@ -737,7 +752,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) if (!ref) return -1; /* end of the list */ *rm = ref->next; - hashcpy(sha1, ref->old_oid.hash); + oidcpy(oid, &ref->old_oid); return 0; } @@ -1044,6 +1059,11 @@ static struct transport *prepare_transport(struct remote *remote, int deepen) set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes"); if (update_shallow) set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); + if (filter_options.choice) { + set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, + filter_options.filter_spec); + set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); + } return transport; } @@ -1094,9 +1114,6 @@ static int do_fetch(struct transport *transport, tags = TAGS_UNSET; } - if (!transport->get_refs_list || !transport->fetch) - die(_("Don't know how to fetch from %s"), transport->url); - /* if not appending, truncate FETCH_HEAD */ if (!append && !dry_run) { retcode = truncate_fetch_head(); @@ -1214,6 +1231,8 @@ static void add_options_to_argv(struct argv_array *argv) argv_array_push(argv, "--dry-run"); if (prune != -1) argv_array_push(argv, prune ? "--prune" : "--no-prune"); + if (prune_tags != -1) + argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); if (force) @@ -1267,12 +1286,65 @@ static int fetch_multiple(struct string_list *list) return result; } -static int fetch_one(struct remote *remote, int argc, const char **argv) +/* + * Fetching from the promisor remote should use the given filter-spec + * or inherit the default filter-spec from the config. + */ +static inline void fetch_one_setup_partial(struct remote *remote) +{ + /* + * Explicit --no-filter argument overrides everything, regardless + * of any prior partial clones and fetches. + */ + if (filter_options.no_filter) + return; + + /* + * If no prior partial clone/fetch and the current fetch DID NOT + * request a partial-fetch, do a normal fetch. + */ + if (!repository_format_partial_clone && !filter_options.choice) + return; + + /* + * If this is the FIRST partial-fetch request, we enable partial + * on this repo and remember the given filter-spec as the default + * for subsequent fetches to this remote. + */ + if (!repository_format_partial_clone && filter_options.choice) { + partial_clone_register(remote->name, &filter_options); + return; + } + + /* + * We are currently limited to only ONE promisor remote and only + * allow partial-fetches from the promisor remote. + */ + if (strcmp(remote->name, repository_format_partial_clone)) { + if (filter_options.choice) + die(_("--filter can only be used with the remote configured in core.partialClone")); + return; + } + + /* + * Do a partial-fetch from the promisor remote using either the + * explicitly given filter-spec or inherit the filter-spec from + * the config. + */ + if (!filter_options.choice) + partial_clone_get_default_filter_spec(&filter_options); + return; +} + +static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok) { static const char **refs = NULL; struct refspec *refspec; int ref_nr = 0; + int j = 0; int exit_code; + int maybe_prune_tags; + int remote_via_config = remote_is_configured(remote, 0); if (!remote) die(_("No remote repository specified. Please, specify either a URL or a\n" @@ -1282,18 +1354,39 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) if (prune < 0) { /* no command line request */ - if (0 <= gtransport->remote->prune) - prune = gtransport->remote->prune; + if (0 <= remote->prune) + prune = remote->prune; else if (0 <= fetch_prune_config) prune = fetch_prune_config; else prune = PRUNE_BY_DEFAULT; } + if (prune_tags < 0) { + /* no command line request */ + if (0 <= remote->prune_tags) + prune_tags = remote->prune_tags; + else if (0 <= fetch_prune_tags_config) + prune_tags = fetch_prune_tags_config; + else + prune_tags = PRUNE_TAGS_BY_DEFAULT; + } + + maybe_prune_tags = prune_tags_ok && prune_tags; + if (maybe_prune_tags && remote_via_config) + add_prune_tags_to_fetch_refspec(remote); + + if (argc > 0 || (maybe_prune_tags && !remote_via_config)) { + size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1); + refs = xcalloc(nr_alloc, sizeof(const char *)); + if (maybe_prune_tags) { + refs[j++] = xstrdup("refs/tags/*:refs/tags/*"); + ref_nr++; + } + } + if (argc > 0) { - int j = 0; int i; - refs = xcalloc(st_add(argc, 1), sizeof(const char *)); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "tag")) { i++; @@ -1303,9 +1396,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) argv[i], argv[i]); } else refs[j++] = argv[i]; + ref_nr++; } - refs[j] = NULL; - ref_nr = j; } sigchain_push_common(unlock_pack_on_signal); @@ -1322,12 +1414,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) { int i; struct string_list list = STRING_LIST_INIT_DUP; - struct remote *remote; + struct remote *remote = NULL; int result = 0; + int prune_tags_ok = 1; struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; packet_trace_identity("fetch"); + fetch_if_missing = 0; + /* Record the command line for the reflog */ strbuf_addstr(&default_rla, "fetch"); for (i = 1; i < argc; i++) @@ -1361,23 +1456,23 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (depth || deepen_since || deepen_not.nr) deepen = 1; + if (filter_options.choice && !repository_format_partial_clone) + die("--filter can only be used when extensions.partialClone is set"); + if (all) { if (argc == 1) die(_("fetch --all does not take a repository argument")); else if (argc > 1) die(_("fetch --all does not make sense with refspecs")); (void) for_each_remote(get_one_remote_for_fetch, &list); - result = fetch_multiple(&list); } else if (argc == 0) { /* No arguments -- use default remote */ remote = remote_get(NULL); - result = fetch_one(remote, argc, argv); } else if (multiple) { /* All arguments are assumed to be remotes or groups */ for (i = 0; i < argc; i++) if (!add_remote_or_group(argv[i], &list)) die(_("No such remote or remote group: %s"), argv[i]); - result = fetch_multiple(&list); } else { /* Single remote or group */ (void) add_remote_or_group(argv[0], &list); @@ -1385,19 +1480,32 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) /* More than one remote */ if (argc > 1) die(_("Fetching a group and specifying refspecs does not make sense")); - result = fetch_multiple(&list); } else { /* Zero or one remotes */ remote = remote_get(argv[0]); - result = fetch_one(remote, argc-1, argv+1); + prune_tags_ok = (argc == 1); + argc--; + argv++; } } + if (remote) { + if (filter_options.choice || repository_format_partial_clone) + fetch_one_setup_partial(remote); + result = fetch_one(remote, argc, argv, prune_tags_ok); + } else { + if (filter_options.choice) + die(_("--filter can only be used with the remote configured in core.partialClone")); + /* TODO should this also die if we have a previous partial-clone? */ + result = fetch_multiple(&list); + } + if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) { struct argv_array options = ARGV_ARRAY_INIT; add_options_to_argv(&options); - result = fetch_populated_submodules(&options, + result = fetch_populated_submodules(the_repository, + &options, submodule_prefix, recurse_submodules, recurse_submodules_default, @@ -1408,7 +1516,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) string_list_clear(&list, 0); - close_all_packs(); + close_all_packs(the_repository->objects); argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL); if (verbosity < 0) diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 27a2361e91..bd680be687 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -377,7 +377,8 @@ static void shortlog(const char *name, string_list_append(&subjects, oid_to_hex(&commit->object.oid)); else - string_list_append(&subjects, strbuf_detach(&sb, NULL)); + string_list_append_nodup(&subjects, + strbuf_detach(&sb, NULL)); } if (opts->credit_people) @@ -484,10 +485,10 @@ static void fmt_merge_msg_sigs(struct strbuf *out) struct strbuf tagbuf = STRBUF_INIT; for (i = 0; i < origins.nr; i++) { - unsigned char *sha1 = origins.items[i].util; + struct object_id *oid = origins.items[i].util; enum object_type type; unsigned long size, len; - char *buf = read_sha1_file(sha1, &type, &size); + char *buf = read_object_file(oid, &type, &size); struct strbuf sig = STRBUF_INIT; if (!buf || type != OBJ_TAG) @@ -603,7 +604,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, /* get current branch */ current_branch = current_branch_to_free = - resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL); + resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL); if (!current_branch) die("No current branch"); if (starts_with(current_branch, "refs/heads/")) diff --git a/builtin/fsck.c b/builtin/fsck.c index 56afe405b8..087360a675 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "repository.h" #include "config.h" #include "commit.h" #include "tree.h" @@ -16,6 +17,7 @@ #include "streaming.h" #include "decorate.h" #include "packfile.h" +#include "object-store.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -65,12 +67,12 @@ static const char *printable_type(struct object *obj) const char *ret; if (obj->type == OBJ_NONE) { - enum object_type type = sha1_object_info(obj->oid.hash, NULL); + enum object_type type = oid_object_info(&obj->oid, NULL); if (type > 0) object_as_type(obj, type, 0); } - ret = typename(obj->type); + ret = type_name(obj->type); if (!ret) ret = "unknown"; @@ -137,7 +139,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt printf("broken link from %7s %s\n", printable_type(parent), describe_object(parent)); printf("broken link from %7s %s\n", - (type == OBJ_ANY ? "unknown" : typename(type)), "unknown"); + (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown"); errors_found |= ERROR_REACHABLE; return 1; } @@ -149,6 +151,15 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt if (obj->flags & REACHABLE) return 0; obj->flags |= REACHABLE; + + if (is_promisor_object(&obj->oid)) + /* + * Further recursion does not need to be performed on this + * object since it is a promisor object (so it does not need to + * be added to "pending"). + */ + return 0; + if (!(obj->flags & HAS_OBJ)) { if (parent && !has_object_file(&obj->oid)) { printf("broken link from %7s %s\n", @@ -171,7 +182,13 @@ static void mark_object_reachable(struct object *obj) static int traverse_one_object(struct object *obj) { - return fsck_walk(obj, obj, &fsck_walk_options); + int result = fsck_walk(obj, obj, &fsck_walk_options); + + if (obj->type == OBJ_TREE) { + struct tree *tree = (struct tree *)obj; + free_tree_buffer(tree); + } + return result; } static int traverse_reachable(void) @@ -208,6 +225,8 @@ static void check_reachable_object(struct object *obj) * do a full fsck */ if (!(obj->flags & HAS_OBJ)) { + if (is_promisor_object(&obj->oid)) + return; if (has_sha1_pack(obj->oid.hash)) return; /* it is in pack - forget about it */ printf("missing %s %s\n", printable_type(obj), @@ -398,7 +417,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, xstrfmt("%s@{%"PRItime"}", refname, timestamp)); obj->flags |= USED; mark_object_reachable(obj); - } else { + } else if (!is_promisor_object(oid)) { error("%s: invalid reflog entry %s", refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; } @@ -434,6 +453,14 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid, obj = parse_object(oid); if (!obj) { + if (is_promisor_object(oid)) { + /* + * Increment default_refs anyway, because this is a + * valid ref. + */ + default_refs++; + return 0; + } error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; /* We'll continue with the rest despite the error.. */ @@ -488,7 +515,7 @@ static struct object *parse_loose_object(const struct object_id *oid, unsigned long size; int eaten; - if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0) + if (read_loose_object(path, oid, &type, &size, &contents) < 0) return NULL; if (!contents && type != OBJ_BLOB) @@ -555,7 +582,7 @@ static int fsck_head_link(void) if (verbose) fprintf(stderr, "Checking HEAD link\n"); - head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL); + head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL); if (!head_points_at) { errors_found |= ERROR_REFS; return error("Invalid HEAD"); @@ -659,6 +686,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) int i; struct alternate_object_database *alt; + /* fsck knows how to handle missing promisor objects */ + fetch_if_missing = 0; + errors_found = 0; check_replace_refs = 0; @@ -691,9 +721,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) for_each_loose_object(mark_loose_for_connectivity, NULL, 0); for_each_packed_object(mark_packed_for_connectivity, NULL, 0); } else { + struct alternate_object_database *alt_odb_list; + fsck_object_dir(get_object_directory()); - prepare_alt_odb(); + prepare_alt_odb(the_repository); + alt_odb_list = the_repository->objects->alt_odb_list; for (alt = alt_odb_list; alt; alt = alt->next) fsck_object_dir(alt->path); @@ -702,10 +735,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) uint32_t total = 0, count = 0; struct progress *progress = NULL; - prepare_packed_git(); - if (show_progress) { - for (p = packed_git; p; p = p->next) { + for (p = get_packed_git(the_repository); p; + p = p->next) { if (open_pack_index(p)) continue; total += p->num_objects; @@ -713,7 +745,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) progress = start_progress(_("Checking objects"), total); } - for (p = packed_git; p; p = p->next) { + for (p = get_packed_git(the_repository); p; + p = p->next) { /* verify gives error messages itself */ if (verify_pack(p, fsck_obj_buffer, progress, count)) @@ -731,6 +764,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) struct object *obj = lookup_object(oid.hash); if (!obj || !(obj->flags & HAS_OBJ)) { + if (is_promisor_object(&oid)) + continue; error("%s: object missing", oid_to_hex(&oid)); errors_found |= ERROR_OBJECT; continue; @@ -759,6 +794,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) if (keep_cache_objects) { verify_index_checksum = 1; + verify_ce_order = 1; read_cache(); for (i = 0; i < active_nr; i++) { unsigned int mode; diff --git a/builtin/gc.c b/builtin/gc.c index 3c5eae0edf..3e67124eaa 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -11,6 +11,7 @@ */ #include "builtin.h" +#include "repository.h" #include "config.h" #include "tempfile.h" #include "lockfile.h" @@ -20,6 +21,7 @@ #include "argv-array.h" #include "commit.h" #include "packfile.h" +#include "object-store.h" #define FAILED_RUN "failed to run %s" @@ -172,8 +174,7 @@ static int too_many_packs(void) if (gc_auto_pack_limit <= 0) return 0; - prepare_packed_git(); - for (cnt = 0, p = packed_git; p; p = p->next) { + for (cnt = 0, p = get_packed_git(the_repository); p; p = p->next) { if (!p->pack_local) continue; if (p->pack_keep) @@ -360,8 +361,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix) N_("prune unreferenced objects"), PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), - OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")), - OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")), + OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"), + PARSE_OPT_NOCOMPLETE), + OPT_BOOL_F(0, "force", &force, + N_("force running gc even if there may be another gc running"), + PARSE_OPT_NOCOMPLETE), OPT_END() }; @@ -458,6 +462,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_push(&prune, prune_expire); if (quiet) argv_array_push(&prune, "--no-progress"); + if (repository_format_partial_clone) + argv_array_push(&prune, + "--exclude-promisor-objects"); if (run_command_v_opt(prune.argv, RUN_GIT_CMD)) return error(FAILED_RUN, prune.argv[0]); } @@ -473,7 +480,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) return error(FAILED_RUN, rerere.argv[0]); report_garbage = report_pack_garbage; - reprepare_packed_git(); + reprepare_packed_git(the_repository); if (pack_garbage.nr > 0) clean_pack_garbage(); diff --git a/builtin/grep.c b/builtin/grep.c index 5a6cfe6b45..5f32d2ce84 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -22,6 +22,7 @@ #include "pathspec.h" #include "submodule.h" #include "submodule-config.h" +#include "object-store.h" static char const * const grep_usage[] = { N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"), @@ -92,8 +93,7 @@ static pthread_cond_t cond_result; static int skip_first_line; -static void add_work(struct grep_opt *opt, enum grep_source_type type, - const char *name, const char *path, const void *id) +static void add_work(struct grep_opt *opt, const struct grep_source *gs) { grep_lock(); @@ -101,7 +101,7 @@ static void add_work(struct grep_opt *opt, enum grep_source_type type, pthread_cond_wait(&cond_write, &grep_mutex); } - grep_source_init(&todo[todo_end].source, type, name, path, id); + todo[todo_end].source = *gs; if (opt->binary != GREP_BINARY_TEXT) grep_source_load_driver(&todo[todo_end].source); todo[todo_end].done = 0; @@ -307,7 +307,7 @@ static void *lock_and_read_oid_file(const struct object_id *oid, enum object_typ void *data; grep_read_lock(); - data = read_sha1_file(oid->hash, type, size); + data = read_object_file(oid, type, size); grep_read_unlock(); return data; } @@ -317,6 +317,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, const char *path) { struct strbuf pathbuf = STRBUF_INIT; + struct grep_source gs; if (opt->relative && opt->prefix_length) { quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf); @@ -325,19 +326,22 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, strbuf_addstr(&pathbuf, filename); } + grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); + strbuf_release(&pathbuf); + #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid); - strbuf_release(&pathbuf); + /* + * add_work() copies gs and thus assumes ownership of + * its fields, so do not call grep_source_clear() + */ + add_work(opt, &gs); return 0; } else #endif { - struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); - strbuf_release(&pathbuf); hit = grep_source(opt, &gs); grep_source_clear(&gs); @@ -348,25 +352,29 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, static int grep_file(struct grep_opt *opt, const char *filename) { struct strbuf buf = STRBUF_INIT; + struct grep_source gs; if (opt->relative && opt->prefix_length) quote_path_relative(filename, opt->prefix, &buf); else strbuf_addstr(&buf, filename); + grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); + strbuf_release(&buf); + #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename); - strbuf_release(&buf); + /* + * add_work() copies gs and thus assumes ownership of + * its fields, so do not call grep_source_clear() + */ + add_work(opt, &gs); return 0; } else #endif { - struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); - strbuf_release(&buf); hit = grep_source(opt, &gs); grep_source_clear(&gs); @@ -432,7 +440,7 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject, * object. */ grep_read_lock(); - add_to_alternates_memory(submodule.objectdir); + add_to_alternates_memory(submodule.objects->objectdir); grep_read_unlock(); if (oid) { @@ -445,7 +453,7 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject, object = parse_object_or_die(oid, oid_to_hex(oid)); grep_read_lock(); - data = read_object_with_reference(object->oid.hash, tree_type, + data = read_object_with_reference(&object->oid, tree_type, &size, NULL); grep_read_unlock(); @@ -607,7 +615,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, int hit, len; grep_read_lock(); - data = read_object_with_reference(obj->oid.hash, tree_type, + data = read_object_with_reference(&obj->oid, tree_type, &size, NULL); grep_read_unlock(); @@ -627,7 +635,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, free(data); return hit; } - die(_("unable to grep from object of type %s"), typename(obj->type)); + die(_("unable to grep from object of type %s"), type_name(obj->type)); } static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, @@ -832,8 +840,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_BOOL('L', "files-without-match", &opt.unmatch_name_only, N_("show only the names of files without match")), - OPT_BOOL('z', "null", &opt.null_following_name, - N_("print NUL after filenames")), + OPT_BOOL_F('z', "null", &opt.null_following_name, + N_("print NUL after filenames"), + PARSE_OPT_NOCOMPLETE), OPT_BOOL('c', "count", &opt.count, N_("show the number of matches instead of matching lines")), OPT__COLOR(&opt.color, N_("highlight matches")), @@ -884,9 +893,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), - PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, - OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored, - N_("allow calling of grep(1) (ignored by this build)")), + PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE, + NULL, (intptr_t)default_pager }, + OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored, + N_("allow calling of grep(1) (ignored by this build)"), + PARSE_OPT_NOCOMPLETE), OPT_END() }; @@ -1015,6 +1026,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) prefix, argv + i); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; + pathspec.recurse_submodules = !!recurse_submodules; #ifndef NO_PTHREADS if (list.nr || cached || show_in_pager) diff --git a/builtin/hash-object.c b/builtin/hash-object.c index c532ff9320..526da5c185 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -24,7 +24,8 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig if (strbuf_read(&buf, fd, 4096) < 0) ret = -1; else - ret = hash_sha1_file_literally(buf.buf, buf.len, type, oid, flags); + ret = hash_object_file_literally(buf.buf, buf.len, type, oid, + flags); strbuf_release(&buf); return ret; } diff --git a/builtin/help.c b/builtin/help.c index d3c8fc4082..598867cfea 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -194,11 +194,11 @@ static void do_add_man_viewer_info(const char *name, size_t len, const char *value) { - struct man_viewer_info_list *new; - FLEX_ALLOC_MEM(new, name, name, len); - new->info = xstrdup(value); - new->next = man_viewer_info_list; - man_viewer_info_list = new; + struct man_viewer_info_list *new_man_viewer; + FLEX_ALLOC_MEM(new_man_viewer, name, name, len); + new_man_viewer->info = xstrdup(value); + new_man_viewer->next = man_viewer_info_list; + man_viewer_info_list = new_man_viewer; } static int add_man_viewer_path(const char *name, diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 8ec459f522..d81473e722 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -13,6 +13,7 @@ #include "streaming.h" #include "thread-utils.h" #include "packfile.h" +#include "object-store.h" static const char index_pack_usage[] = "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; @@ -49,6 +50,7 @@ struct thread_local { int pack_fd; }; +/* Remember to update object flag allocation in object.h */ #define FLAG_LINK (1u<<20) #define FLAG_CHECKED (1u<<21) @@ -58,7 +60,7 @@ struct ofs_delta_entry { }; struct ref_delta_entry { - unsigned char sha1[20]; + struct object_id oid; int obj_no; }; @@ -91,7 +93,7 @@ static unsigned int input_offset, input_len; static off_t consumed_bytes; static off_t max_input_size; static unsigned deepest_delta; -static git_SHA_CTX input_ctx; +static git_hash_ctx input_ctx; static uint32_t input_crc32; static int input_fd, output_fd; static const char *curr_pack; @@ -221,14 +223,14 @@ static unsigned check_object(struct object *obj) if (!(obj->flags & FLAG_CHECKED)) { unsigned long size; - int type = sha1_object_info(obj->oid.hash, &size); + int type = oid_object_info(&obj->oid, &size); if (type <= 0) die(_("did not receive expected object %s"), oid_to_hex(&obj->oid)); if (type != obj->type) die(_("object %s: expected type %s, found %s"), oid_to_hex(&obj->oid), - typename(obj->type), typename(type)); + type_name(obj->type), type_name(type)); obj->flags |= FLAG_CHECKED; return 1; } @@ -253,7 +255,7 @@ static void flush(void) if (input_offset) { if (output_fd >= 0) write_or_die(output_fd, input_buffer, input_offset); - git_SHA1_Update(&input_ctx, input_buffer, input_offset); + the_hash_algo->update_fn(&input_ctx, input_buffer, input_offset); memmove(input_buffer, input_buffer + input_offset, input_len); input_offset = 0; } @@ -326,7 +328,7 @@ static const char *open_pack_file(const char *pack_name) output_fd = -1; nothread_data.pack_fd = input_fd; } - git_SHA1_Init(&input_ctx); + the_hash_algo->init_fn(&input_ctx); return pack_name; } @@ -437,22 +439,22 @@ static int is_delta_type(enum object_type type) } static void *unpack_entry_data(off_t offset, unsigned long size, - enum object_type type, unsigned char *sha1) + enum object_type type, struct object_id *oid) { static char fixed_buf[8192]; int status; git_zstream stream; void *buf; - git_SHA_CTX c; + git_hash_ctx c; char hdr[32]; int hdrlen; if (!is_delta_type(type)) { - hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1; - git_SHA1_Init(&c); - git_SHA1_Update(&c, hdr, hdrlen); + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), size) + 1; + the_hash_algo->init_fn(&c); + the_hash_algo->update_fn(&c, hdr, hdrlen); } else - sha1 = NULL; + oid = NULL; if (type == OBJ_BLOB && size > big_file_threshold) buf = fixed_buf; else @@ -469,8 +471,8 @@ static void *unpack_entry_data(off_t offset, unsigned long size, stream.avail_in = input_len; status = git_inflate(&stream, 0); use(input_len - stream.avail_in); - if (sha1) - git_SHA1_Update(&c, last_out, stream.next_out - last_out); + if (oid) + the_hash_algo->update_fn(&c, last_out, stream.next_out - last_out); if (buf == fixed_buf) { stream.next_out = buf; stream.avail_out = sizeof(fixed_buf); @@ -479,15 +481,15 @@ static void *unpack_entry_data(off_t offset, unsigned long size, if (stream.total_out != size || status != Z_STREAM_END) bad_object(offset, _("inflate returned %d"), status); git_inflate_end(&stream); - if (sha1) - git_SHA1_Final(sha1, &c); + if (oid) + the_hash_algo->final_fn(oid->hash, &c); return buf == fixed_buf ? NULL : buf; } static void *unpack_raw_entry(struct object_entry *obj, off_t *ofs_offset, - unsigned char *ref_sha1, - unsigned char *sha1) + struct object_id *ref_oid, + struct object_id *oid) { unsigned char *p; unsigned long size, c; @@ -515,8 +517,8 @@ static void *unpack_raw_entry(struct object_entry *obj, switch (obj->type) { case OBJ_REF_DELTA: - hashcpy(ref_sha1, fill(20)); - use(20); + hashcpy(ref_oid->hash, fill(the_hash_algo->rawsz)); + use(the_hash_algo->rawsz); break; case OBJ_OFS_DELTA: p = fill(1); @@ -546,7 +548,7 @@ static void *unpack_raw_entry(struct object_entry *obj, } obj->hdr_size = consumed_bytes - obj->idx.offset; - data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, sha1); + data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, oid); obj->idx.crc32 = input_crc32; return data; } @@ -671,18 +673,18 @@ static void find_ofs_delta_children(off_t offset, *last_index = last; } -static int compare_ref_delta_bases(const unsigned char *sha1, - const unsigned char *sha2, +static int compare_ref_delta_bases(const struct object_id *oid1, + const struct object_id *oid2, enum object_type type1, enum object_type type2) { int cmp = type1 - type2; if (cmp) return cmp; - return hashcmp(sha1, sha2); + return oidcmp(oid1, oid2); } -static int find_ref_delta(const unsigned char *sha1, enum object_type type) +static int find_ref_delta(const struct object_id *oid, enum object_type type) { int first = 0, last = nr_ref_deltas; @@ -691,7 +693,7 @@ static int find_ref_delta(const unsigned char *sha1, enum object_type type) struct ref_delta_entry *delta = &ref_deltas[next]; int cmp; - cmp = compare_ref_delta_bases(sha1, delta->sha1, + cmp = compare_ref_delta_bases(oid, &delta->oid, type, objects[delta->obj_no].type); if (!cmp) return next; @@ -704,11 +706,11 @@ static int find_ref_delta(const unsigned char *sha1, enum object_type type) return -first-1; } -static void find_ref_delta_children(const unsigned char *sha1, +static void find_ref_delta_children(const struct object_id *oid, int *first_index, int *last_index, enum object_type type) { - int first = find_ref_delta(sha1, type); + int first = find_ref_delta(oid, type); int last = first; int end = nr_ref_deltas - 1; @@ -717,9 +719,9 @@ static void find_ref_delta_children(const unsigned char *sha1, *last_index = -1; return; } - while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1)) + while (first > 0 && !oidcmp(&ref_deltas[first - 1].oid, oid)) --first; - while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1)) + while (last < end && !oidcmp(&ref_deltas[last + 1].oid, oid)) ++last; *first_index = first; *last_index = last; @@ -771,7 +773,7 @@ static int check_collison(struct object_entry *entry) memset(&data, 0, sizeof(data)); data.entry = entry; - data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL); + data.st = open_istream(&entry->idx.oid, &type, &size, NULL); if (!data.st) return -1; if (size != entry->size || type != entry->type) @@ -810,12 +812,12 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, enum object_type has_type; unsigned long has_size; read_lock(); - has_type = sha1_object_info(oid->hash, &has_size); + has_type = oid_object_info(oid, &has_size); if (has_type < 0) die(_("cannot read existing object info %s"), oid_to_hex(oid)); if (has_type != type || has_size != size) die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); - has_data = read_sha1_file(oid->hash, &has_type, &has_size); + has_data = read_object_file(oid, &has_type, &has_size); read_unlock(); if (!data) data = new_data = get_data_from_pack(obj_entry); @@ -827,7 +829,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, free(has_data); } - if (strict) { + if (strict || do_fsck_object) { read_lock(); if (type == OBJ_BLOB) { struct blob *blob = lookup_blob(oid); @@ -849,11 +851,11 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, obj = parse_object_buffer(oid, type, size, buf, &eaten); if (!obj) - die(_("invalid %s"), typename(type)); + die(_("invalid %s"), type_name(type)); if (do_fsck_object && fsck_object(obj, buf, size, &fsck_options)) die(_("Error in object")); - if (fsck_walk(obj, NULL, &fsck_options)) + if (strict && fsck_walk(obj, NULL, &fsck_options)) die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid)); if (obj->type == OBJ_TREE) { @@ -958,9 +960,8 @@ static void resolve_delta(struct object_entry *delta_obj, free(delta_data); if (!result->data) bad_object(delta_obj->idx.offset, _("failed to apply delta")); - hash_sha1_file(result->data, result->size, - typename(delta_obj->real_type), - delta_obj->idx.oid.hash); + hash_object_file(result->data, result->size, + type_name(delta_obj->real_type), &delta_obj->idx.oid); sha1_object(result->data, NULL, result->size, delta_obj->real_type, &delta_obj->idx.oid); counter_lock(); @@ -992,7 +993,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base, struct base_data *prev_base) { if (base->ref_last == -1 && base->ofs_last == -1) { - find_ref_delta_children(base->obj->idx.oid.hash, + find_ref_delta_children(&base->obj->idx.oid, &base->ref_first, &base->ref_last, OBJ_REF_DELTA); @@ -1076,7 +1077,7 @@ static int compare_ref_delta_entry(const void *a, const void *b) const struct ref_delta_entry *delta_a = a; const struct ref_delta_entry *delta_b = b; - return hashcmp(delta_a->sha1, delta_b->sha1); + return oidcmp(&delta_a->oid, &delta_b->oid); } static void resolve_base(struct object_entry *obj) @@ -1119,11 +1120,11 @@ static void *threaded_second_pass(void *data) * - calculate SHA1 of all non-delta objects; * - remember base (SHA1 or offset) for all deltas. */ -static void parse_pack_objects(unsigned char *sha1) +static void parse_pack_objects(unsigned char *hash) { int i, nr_delays = 0; struct ofs_delta_entry *ofs_delta = ofs_deltas; - unsigned char ref_delta_sha1[20]; + struct object_id ref_delta_oid; struct stat st; if (verbose) @@ -1133,8 +1134,8 @@ static void parse_pack_objects(unsigned char *sha1) for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; void *data = unpack_raw_entry(obj, &ofs_delta->offset, - ref_delta_sha1, - obj->idx.oid.hash); + &ref_delta_oid, + &obj->idx.oid); obj->real_type = obj->type; if (obj->type == OBJ_OFS_DELTA) { nr_ofs_deltas++; @@ -1142,7 +1143,7 @@ static void parse_pack_objects(unsigned char *sha1) ofs_delta++; } else if (obj->type == OBJ_REF_DELTA) { ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc); - hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_sha1); + oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid); ref_deltas[nr_ref_deltas].obj_no = i; nr_ref_deltas++; } else if (!data) { @@ -1160,10 +1161,10 @@ static void parse_pack_objects(unsigned char *sha1) /* Check pack integrity */ flush(); - git_SHA1_Final(sha1, &input_ctx); - if (hashcmp(fill(20), sha1)) + the_hash_algo->final_fn(hash, &input_ctx); + if (hashcmp(fill(the_hash_algo->rawsz), hash)) die(_("pack is corrupted (SHA1 mismatch)")); - use(20); + use(the_hash_algo->rawsz); /* If input_fd is a file, we should have reached its end now. */ if (fstat(input_fd, &st)) @@ -1239,21 +1240,21 @@ static void resolve_deltas(void) /* * Third pass: * - append objects to convert thin pack to full pack if required - * - write the final 20-byte SHA-1 + * - write the final pack hash */ -static void fix_unresolved_deltas(struct sha1file *f); -static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1) +static void fix_unresolved_deltas(struct hashfile *f); +static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_hash) { if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) { stop_progress(&progress); - /* Flush remaining pack final 20-byte SHA1. */ + /* Flush remaining pack final hash. */ flush(); return; } if (fix_thin_pack) { - struct sha1file *f; - unsigned char read_sha1[20], tail_sha1[20]; + struct hashfile *f; + unsigned char read_hash[GIT_MAX_RAWSZ], tail_hash[GIT_MAX_RAWSZ]; struct strbuf msg = STRBUF_INIT; int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas; int nr_objects_initial = nr_objects; @@ -1262,7 +1263,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1); memset(objects + nr_objects + 1, 0, nr_unresolved * sizeof(*objects)); - f = sha1fd(output_fd, curr_pack); + f = hashfd(output_fd, curr_pack); fix_unresolved_deltas(f); strbuf_addf(&msg, Q_("completed with %d local object", "completed with %d local objects", @@ -1270,12 +1271,12 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha nr_objects - nr_objects_initial); stop_progress_msg(&progress, msg.buf); strbuf_release(&msg); - sha1close(f, tail_sha1, 0); - hashcpy(read_sha1, pack_sha1); - fixup_pack_header_footer(output_fd, pack_sha1, + hashclose(f, tail_hash, 0); + hashcpy(read_hash, pack_hash); + fixup_pack_header_footer(output_fd, pack_hash, curr_pack, nr_objects, - read_sha1, consumed_bytes-20); - if (hashcmp(read_sha1, tail_sha1) != 0) + read_hash, consumed_bytes-the_hash_algo->rawsz); + if (hashcmp(read_hash, tail_hash) != 0) die(_("Unexpected tail checksum for %s " "(disk corruption?)"), curr_pack); } @@ -1286,7 +1287,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas); } -static int write_compressed(struct sha1file *f, void *in, unsigned int size) +static int write_compressed(struct hashfile *f, void *in, unsigned int size) { git_zstream stream; int status; @@ -1300,7 +1301,7 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size) stream.next_out = outbuf; stream.avail_out = sizeof(outbuf); status = git_deflate(&stream, Z_FINISH); - sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out); + hashwrite(f, outbuf, sizeof(outbuf) - stream.avail_out); } while (status == Z_OK); if (status != Z_STREAM_END) @@ -1310,7 +1311,7 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size) return size; } -static struct object_entry *append_obj_to_pack(struct sha1file *f, +static struct object_entry *append_obj_to_pack(struct hashfile *f, const unsigned char *sha1, void *buf, unsigned long size, enum object_type type) { @@ -1327,7 +1328,7 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f, } header[n++] = c; crc32_begin(f); - sha1write(f, header, n); + hashwrite(f, header, n); obj[0].size = size; obj[0].hdr_size = n; obj[0].type = type; @@ -1335,7 +1336,7 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f, obj[1].idx.offset = obj[0].idx.offset + n; obj[1].idx.offset += write_compressed(f, buf, size); obj[0].idx.crc32 = crc32_end(f); - sha1flush(f); + hashflush(f); hashcpy(obj->idx.oid.hash, sha1); return obj; } @@ -1347,7 +1348,7 @@ static int delta_pos_compare(const void *_a, const void *_b) return a->obj_no - b->obj_no; } -static void fix_unresolved_deltas(struct sha1file *f) +static void fix_unresolved_deltas(struct hashfile *f) { struct ref_delta_entry **sorted_by_pos; int i; @@ -1374,14 +1375,15 @@ static void fix_unresolved_deltas(struct sha1file *f) if (objects[d->obj_no].real_type != OBJ_REF_DELTA) continue; - base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size); + base_obj->data = read_object_file(&d->oid, &type, + &base_obj->size); if (!base_obj->data) continue; - if (check_sha1_signature(d->sha1, base_obj->data, - base_obj->size, typename(type))) - die(_("local object %s is corrupt"), sha1_to_hex(d->sha1)); - base_obj->obj = append_obj_to_pack(f, d->sha1, + if (check_object_signature(&d->oid, base_obj->data, + base_obj->size, type_name(type))) + die(_("local object %s is corrupt"), oid_to_hex(&d->oid)); + base_obj->obj = append_obj_to_pack(f, d->oid.hash, base_obj->data, base_obj->size, type); find_unresolved_deltas(base_obj); display_progress(progress, nr_resolved_deltas); @@ -1389,15 +1391,60 @@ static void fix_unresolved_deltas(struct sha1file *f) free(sorted_by_pos); } +static const char *derive_filename(const char *pack_name, const char *suffix, + struct strbuf *buf) +{ + size_t len; + if (!strip_suffix(pack_name, ".pack", &len)) + die(_("packfile name '%s' does not end with '.pack'"), + pack_name); + strbuf_add(buf, pack_name, len); + strbuf_addch(buf, '.'); + strbuf_addstr(buf, suffix); + return buf->buf; +} + +static void write_special_file(const char *suffix, const char *msg, + const char *pack_name, const unsigned char *hash, + const char **report) +{ + struct strbuf name_buf = STRBUF_INIT; + const char *filename; + int fd; + int msg_len = strlen(msg); + + if (pack_name) + filename = derive_filename(pack_name, suffix, &name_buf); + else + filename = odb_pack_name(&name_buf, hash, suffix); + + fd = odb_pack_keep(filename); + if (fd < 0) { + if (errno != EEXIST) + die_errno(_("cannot write %s file '%s'"), + suffix, filename); + } else { + if (msg_len > 0) { + write_or_die(fd, msg, msg_len); + write_or_die(fd, "\n", 1); + } + if (close(fd) != 0) + die_errno(_("cannot close written %s file '%s'"), + suffix, filename); + if (report) + *report = suffix; + } + strbuf_release(&name_buf); +} + static void final(const char *final_pack_name, const char *curr_pack_name, const char *final_index_name, const char *curr_index_name, - const char *keep_name, const char *keep_msg, - unsigned char *sha1) + const char *keep_msg, const char *promisor_msg, + unsigned char *hash) { const char *report = "pack"; struct strbuf pack_name = STRBUF_INIT; struct strbuf index_name = STRBUF_INIT; - struct strbuf keep_name_buf = STRBUF_INIT; int err; if (!from_stdin) { @@ -1409,32 +1456,16 @@ static void final(const char *final_pack_name, const char *curr_pack_name, die_errno(_("error while closing pack file")); } - if (keep_msg) { - int keep_fd, keep_msg_len = strlen(keep_msg); - - if (!keep_name) - keep_name = odb_pack_name(&keep_name_buf, sha1, "keep"); - - keep_fd = odb_pack_keep(keep_name); - if (keep_fd < 0) { - if (errno != EEXIST) - die_errno(_("cannot write keep file '%s'"), - keep_name); - } else { - if (keep_msg_len > 0) { - write_or_die(keep_fd, keep_msg, keep_msg_len); - write_or_die(keep_fd, "\n", 1); - } - if (close(keep_fd) != 0) - die_errno(_("cannot close written keep file '%s'"), - keep_name); - report = "keep"; - } - } + if (keep_msg) + write_special_file("keep", keep_msg, final_pack_name, hash, + &report); + if (promisor_msg) + write_special_file("promisor", promisor_msg, final_pack_name, + hash, NULL); if (final_pack_name != curr_pack_name) { if (!final_pack_name) - final_pack_name = odb_pack_name(&pack_name, sha1, "pack"); + final_pack_name = odb_pack_name(&pack_name, hash, "pack"); if (finalize_object_file(curr_pack_name, final_pack_name)) die(_("cannot store pack file")); } else if (from_stdin) @@ -1442,18 +1473,18 @@ static void final(const char *final_pack_name, const char *curr_pack_name, if (final_index_name != curr_index_name) { if (!final_index_name) - final_index_name = odb_pack_name(&index_name, sha1, "idx"); + final_index_name = odb_pack_name(&index_name, hash, "idx"); if (finalize_object_file(curr_index_name, final_index_name)) die(_("cannot store index file")); } else chmod(final_index_name, 0444); if (!from_stdin) { - printf("%s\n", sha1_to_hex(sha1)); + printf("%s\n", sha1_to_hex(hash)); } else { struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(sha1)); + strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(hash)); write_or_die(1, buf.buf, buf.len); strbuf_release(&buf); @@ -1472,7 +1503,6 @@ static void final(const char *final_pack_name, const char *curr_pack_name, strbuf_release(&index_name); strbuf_release(&pack_name); - strbuf_release(&keep_name_buf); } static int git_index_pack_config(const char *k, const char *v, void *cb) @@ -1588,7 +1618,7 @@ static void show_pack_info(int stat_only) continue; printf("%s %-6s %lu %lu %"PRIuMAX, oid_to_hex(&obj->idx.oid), - typename(obj->real_type), obj->size, + type_name(obj->real_type), obj->size, (unsigned long)(obj[1].idx.offset - obj->idx.offset), (uintmax_t)obj->idx.offset); if (is_delta_type(obj->type)) { @@ -1615,32 +1645,26 @@ static void show_pack_info(int stat_only) } } -static const char *derive_filename(const char *pack_name, const char *suffix, - struct strbuf *buf) -{ - size_t len; - if (!strip_suffix(pack_name, ".pack", &len)) - die(_("packfile name '%s' does not end with '.pack'"), - pack_name); - strbuf_add(buf, pack_name, len); - strbuf_addstr(buf, suffix); - return buf->buf; -} - int cmd_index_pack(int argc, const char **argv, const char *prefix) { int i, fix_thin_pack = 0, verify = 0, stat_only = 0; const char *curr_index; const char *index_name = NULL, *pack_name = NULL; - const char *keep_name = NULL, *keep_msg = NULL; - struct strbuf index_name_buf = STRBUF_INIT, - keep_name_buf = STRBUF_INIT; + const char *keep_msg = NULL; + const char *promisor_msg = NULL; + struct strbuf index_name_buf = STRBUF_INIT; struct pack_idx_entry **idx_objects; struct pack_idx_option opts; - unsigned char pack_sha1[20]; + unsigned char pack_hash[GIT_MAX_RAWSZ]; unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */ int report_end_of_input = 0; + /* + * index-pack never needs to fetch missing objects, since it only + * accesses the repo to do hash collision checks + */ + fetch_if_missing = 0; + if (argc == 2 && !strcmp(argv[1], "-h")) usage(index_pack_usage); @@ -1660,16 +1684,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) from_stdin = 1; } else if (!strcmp(arg, "--fix-thin")) { fix_thin_pack = 1; - } else if (!strcmp(arg, "--strict")) { - strict = 1; - do_fsck_object = 1; - } else if (skip_prefix(arg, "--strict=", &arg)) { + } else if (skip_to_optional_arg(arg, "--strict", &arg)) { strict = 1; do_fsck_object = 1; fsck_set_msg_types(&fsck_options, arg); } else if (!strcmp(arg, "--check-self-contained-and-connected")) { strict = 1; check_self_contained_and_connected = 1; + } else if (!strcmp(arg, "--fsck-objects")) { + do_fsck_object = 1; } else if (!strcmp(arg, "--verify")) { verify = 1; } else if (!strcmp(arg, "--verify-stat")) { @@ -1679,10 +1702,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) verify = 1; show_stat = 1; stat_only = 1; - } else if (!strcmp(arg, "--keep")) { - keep_msg = ""; - } else if (starts_with(arg, "--keep=")) { - keep_msg = arg + 7; + } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) { + ; /* nothing to do */ + } else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) { + ; /* already parsed */ } else if (starts_with(arg, "--threads=")) { char *end; nr_threads = strtoul(arg+10, &end, 0); @@ -1745,9 +1768,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (from_stdin && !startup_info->have_repository) die(_("--stdin requires a git repository")); if (!index_name && pack_name) - index_name = derive_filename(pack_name, ".idx", &index_name_buf); - if (keep_msg && !keep_name && pack_name) - keep_name = derive_filename(pack_name, ".keep", &keep_name_buf); + index_name = derive_filename(pack_name, "idx", &index_name_buf); if (verify) { if (!index_name) @@ -1773,11 +1794,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (show_stat) obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat)); ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry)); - parse_pack_objects(pack_sha1); + parse_pack_objects(pack_hash); if (report_end_of_input) write_in_full(2, "\0", 1); resolve_deltas(); - conclude_pack(fix_thin_pack, curr_pack, pack_sha1); + conclude_pack(fix_thin_pack, curr_pack, pack_hash); free(ofs_deltas); free(ref_deltas); if (strict) @@ -1789,19 +1810,18 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) ALLOC_ARRAY(idx_objects, nr_objects); for (i = 0; i < nr_objects; i++) idx_objects[i] = &objects[i].idx; - curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_sha1); + curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash); free(idx_objects); if (!verify) final(pack_name, curr_pack, index_name, curr_index, - keep_name, keep_msg, - pack_sha1); + keep_msg, promisor_msg, + pack_hash); else close(input_fd); free(objects); strbuf_release(&index_name_buf); - strbuf_release(&keep_name_buf); if (pack_name == NULL) free((void *) curr_pack); if (index_name == NULL) diff --git a/builtin/init-db.c b/builtin/init-db.c index c9b7946bad..68ff4ad75a 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -24,11 +24,11 @@ static int init_is_bare_repository = 0; static int init_shared_repository = -1; static const char *init_db_template_dir; -static void copy_templates_1(struct strbuf *path, struct strbuf *template, +static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, DIR *dir) { size_t path_baselen = path->len; - size_t template_baselen = template->len; + size_t template_baselen = template_path->len; struct dirent *de; /* Note: if ".git/hooks" file exists in the repository being @@ -44,12 +44,12 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template, int exists = 0; strbuf_setlen(path, path_baselen); - strbuf_setlen(template, template_baselen); + strbuf_setlen(template_path, template_baselen); if (de->d_name[0] == '.') continue; strbuf_addstr(path, de->d_name); - strbuf_addstr(template, de->d_name); + strbuf_addstr(template_path, de->d_name); if (lstat(path->buf, &st_git)) { if (errno != ENOENT) die_errno(_("cannot stat '%s'"), path->buf); @@ -57,36 +57,36 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template, else exists = 1; - if (lstat(template->buf, &st_template)) - die_errno(_("cannot stat template '%s'"), template->buf); + if (lstat(template_path->buf, &st_template)) + die_errno(_("cannot stat template '%s'"), template_path->buf); if (S_ISDIR(st_template.st_mode)) { - DIR *subdir = opendir(template->buf); + DIR *subdir = opendir(template_path->buf); if (!subdir) - die_errno(_("cannot opendir '%s'"), template->buf); + die_errno(_("cannot opendir '%s'"), template_path->buf); strbuf_addch(path, '/'); - strbuf_addch(template, '/'); - copy_templates_1(path, template, subdir); + strbuf_addch(template_path, '/'); + copy_templates_1(path, template_path, subdir); closedir(subdir); } else if (exists) continue; else if (S_ISLNK(st_template.st_mode)) { struct strbuf lnk = STRBUF_INIT; - if (strbuf_readlink(&lnk, template->buf, 0) < 0) - die_errno(_("cannot readlink '%s'"), template->buf); + if (strbuf_readlink(&lnk, template_path->buf, 0) < 0) + die_errno(_("cannot readlink '%s'"), template_path->buf); if (symlink(lnk.buf, path->buf)) die_errno(_("cannot symlink '%s' '%s'"), lnk.buf, path->buf); strbuf_release(&lnk); } else if (S_ISREG(st_template.st_mode)) { - if (copy_file(path->buf, template->buf, st_template.st_mode)) + if (copy_file(path->buf, template_path->buf, st_template.st_mode)) die_errno(_("cannot copy '%s' to '%s'"), - template->buf, path->buf); + template_path->buf, path->buf); } else - error(_("ignoring template %s"), template->buf); + error(_("ignoring template %s"), template_path->buf); } } diff --git a/builtin/log.c b/builtin/log.c index d81a09051e..71f68a3e4f 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -29,6 +29,8 @@ #include "gpg-interface.h" #include "progress.h" +#define MAIL_DEFAULT_WRAP 72 + /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@ -121,20 +123,19 @@ static void cmd_log_init_defaults(struct rev_info *rev) if (fmt_pretty) get_commit_format(fmt_pretty, rev); if (default_follow) - DIFF_OPT_SET(&rev->diffopt, DEFAULT_FOLLOW_RENAMES); + rev->diffopt.flags.default_follow_renames = 1; rev->verbose_header = 1; - DIFF_OPT_SET(&rev->diffopt, RECURSIVE); + rev->diffopt.flags.recursive = 1; rev->diffopt.stat_width = -1; /* use full terminal width */ rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; rev->show_signature = default_show_signature; - DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV); + rev->diffopt.flags.allow_textconv = 1; if (default_date_mode) parse_date_format(default_date_mode, &rev->date_mode); - rev->diffopt.touched_flags = 0; } static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, @@ -143,11 +144,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, struct userformat_want w; int quiet = 0, source = 0, mailmap = 0; static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP}; + static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; + static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; + struct decoration_filter decoration_filter = {&decorate_refs_include, + &decorate_refs_exclude}; const struct option builtin_log_options[] = { OPT__QUIET(&quiet, N_("suppress diff output")), OPT_BOOL(0, "source", &source, N_("show source")), OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")), + OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include, + N_("pattern"), N_("only decorate refs that match <pattern>")), + OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude, + N_("pattern"), N_("do not decorate refs that match <pattern>")), { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"), PARSE_OPT_OPTARG, decorate_callback}, OPT_CALLBACK('L', NULL, &line_cb, "n,m:file", @@ -181,8 +190,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (rev->show_notes) init_display_notes(&rev->notes_opt); - if (rev->diffopt.pickaxe || rev->diffopt.filter || - DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) + if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) || + rev->diffopt.filter || rev->diffopt.flags.follow_renames) rev->always_show_header = 0; if (source) @@ -206,7 +215,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (decoration_style) { rev->show_decorations = 1; - load_ref_decorations(decoration_style); + load_ref_decorations(&decoration_filter, decoration_style); } if (rev->line_level_traverse) @@ -392,7 +401,7 @@ static int cmd_log_walk(struct rev_info *rev) fclose(rev->diffopt.file); if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && - DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { + rev->diffopt.flags.check_failed) { return 02; } return diff_result_code(&rev->diffopt, 0); @@ -484,8 +493,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c unsigned long size; fflush(rev->diffopt.file); - if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) || - !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV)) + if (!rev->diffopt.flags.textconv_set_via_cmdline || + !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, @@ -509,7 +518,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) { unsigned long size; enum object_type type; - char *buf = read_sha1_file(oid->hash, &type, &size); + char *buf = read_object_file(oid, &type, &size); int offset = 0; if (!buf) @@ -532,7 +541,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) return 0; } -static int show_tree_object(const unsigned char *sha1, +static int show_tree_object(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { @@ -667,9 +676,9 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) static void log_setup_revisions_tweak(struct rev_info *rev, struct setup_revision_opt *opt) { - if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) && + if (rev->diffopt.flags.default_follow_renames && rev->prune_data.nr == 1) - DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES); + rev->diffopt.flags.follow_renames = 1; /* Turn --cc/-c into -p --cc/-c when -p was not given */ if (!rev->diffopt.output_format && rev->combine_merges) @@ -975,7 +984,7 @@ static char *find_branch_name(struct rev_info *rev) return NULL; ref = rev->cmdline.rev[positive].name; tip_oid = &rev->cmdline.rev[positive].item->oid; - if (dwim_ref(ref, strlen(ref), branch_oid.hash, &full_ref) && + if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) && skip_prefix(full_ref, "refs/heads/", &v) && !oidcmp(tip_oid, &branch_oid)) branch = xstrdup(v); @@ -1037,7 +1046,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, shortlog_init(&log); log.wrap_lines = 1; - log.wrap = 72; + log.wrap = MAIL_DEFAULT_WRAP; log.in1 = 2; log.in2 = 4; log.file = rev->diffopt.file; @@ -1054,6 +1063,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, memcpy(&opts, &rev->diffopt, sizeof(opts)); opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; + opts.stat_width = MAIL_DEFAULT_WRAP; diff_setup_done(&opts); @@ -1341,7 +1351,7 @@ static void prepare_bases(struct base_tree_info *bases, return; diff_setup(&diffopt); - DIFF_OPT_SET(&diffopt, RECURSIVE); + diffopt.flags.recursive = 1; diff_setup_done(&diffopt); oidcpy(&bases->base_commit, &base->object.oid); @@ -1512,7 +1522,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.verbose_header = 1; rev.diff = 1; rev.max_parents = 1; - DIFF_OPT_SET(&rev.diffopt, RECURSIVE); + rev.diffopt.flags.recursive = 1; rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; @@ -1607,14 +1617,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH)) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; + if (!rev.diffopt.stat_width) + rev.diffopt.stat_width = MAIL_DEFAULT_WRAP; /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; rev.zero_commit = zero_commit; - if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) - DIFF_OPT_SET(&rev.diffopt, BINARY); + if (!rev.diffopt.flags.text && !no_binary_diff) + rev.diffopt.flags.binary = 1; if (rev.show_notes) init_display_notes(&rev.notes_opt); @@ -1861,12 +1873,12 @@ static void print_commit(char sign, struct commit *commit, int verbose, { if (!verbose) { fprintf(file, "%c %s\n", sign, - find_unique_abbrev(commit->object.oid.hash, abbrev)); + find_unique_abbrev(&commit->object.oid, abbrev)); } else { struct strbuf buf = STRBUF_INIT; pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); fprintf(file, "%c %s %s\n", sign, - find_unique_abbrev(commit->object.oid.hash, abbrev), + find_unique_abbrev(&commit->object.oid, abbrev), buf.buf); strbuf_release(&buf); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 8c713c47ac..a71f6bd088 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -31,6 +31,7 @@ static int show_resolve_undo; static int show_modified; static int show_killed; static int show_valid_bit; +static int show_fsmonitor_bit; static int line_terminator = '\n'; static int debug_mode; static int show_eol; @@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag) { static char alttag[4]; - if (tag && *tag && show_valid_bit && (ce->ce_flags & CE_VALID)) { + if (tag && *tag && ((show_valid_bit && (ce->ce_flags & CE_VALID)) || + (show_fsmonitor_bit && (ce->ce_flags & CE_FSMONITOR_VALID)))) { memcpy(alttag, tag, 3); if (isalpha(tag[0])) { @@ -238,7 +240,7 @@ static void show_ce(struct repository *repo, struct dir_struct *dir, printf("%s%06o %s %d\t", tag, ce->ce_mode, - find_unique_abbrev(ce->oid.hash, abbrev), + find_unique_abbrev(&ce->oid, abbrev), ce_stage(ce)); } write_eolinfo(repo->index, ce, fullname); @@ -269,7 +271,7 @@ static void show_ru_info(const struct index_state *istate) if (!ui->mode[i]) continue; printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i], - find_unique_abbrev(ui->sha1[i], abbrev), + find_unique_abbrev(&ui->oid[i], abbrev), i + 1); write_name(path); } @@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("identify the file status with tags")), OPT_BOOL('v', NULL, &show_valid_bit, N_("use lowercase letters for 'assume unchanged' files")), + OPT_BOOL('f', NULL, &show_fsmonitor_bit, + N_("use lowercase letters for 'fsmonitor clean' files")), OPT_BOOL('c', "cached", &show_cached, N_("show cached files in the output (default)")), OPT_BOOL('d', "deleted", &show_deleted, @@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) for (i = 0; i < exclude_list.nr; i++) { add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args); } - if (show_tag || show_valid_bit) { + if (show_tag || show_valid_bit || show_fsmonitor_bit) { tag_cached = "H "; tag_unmerged = "M "; tag_removed = "R "; diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index c4be98ab9e..540d56429f 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -60,8 +60,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL), OPT_BOOL(0, "get-url", &get_url, N_("take url.<base>.insteadOf into account")), - OPT_SET_INT(0, "exit-code", &status, - N_("exit with exit code 2 if no matching refs are found"), 2), + OPT_SET_INT_F(0, "exit-code", &status, + N_("exit with exit code 2 if no matching refs are found"), + 2, PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "symref", &show_symref_target, N_("show underlying ref in addition to the object pointed by it")), OPT_END() diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index ef965408e8..d44b4f9c27 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -60,7 +60,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname) return 0; } -static int show_tree(const unsigned char *sha1, struct strbuf *base, +static int show_tree(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { int retval = 0; @@ -94,7 +94,7 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base, char size_text[24]; if (!strcmp(type, blob_type)) { unsigned long size; - if (sha1_object_info(sha1, &size) == OBJ_BAD) + if (oid_object_info(oid, &size) == OBJ_BAD) xsnprintf(size_text, sizeof(size_text), "BAD"); else @@ -103,11 +103,11 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base, } else xsnprintf(size_text, sizeof(size_text), "-"); printf("%06o %s %s %7s\t", mode, type, - find_unique_abbrev(sha1, abbrev), + find_unique_abbrev(oid, abbrev), size_text); } else printf("%06o %s %s\t", mode, type, - find_unique_abbrev(sha1, abbrev)); + find_unique_abbrev(oid, abbrev)); } baselen = base->len; strbuf_addstr(base, pathname); diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 24f6c71935..3b7600150b 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -158,7 +158,7 @@ static int handle_fork_point(int argc, const char **argv) struct commit_list *bases; int i, ret = 0; - switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) { + switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) { case 0: die("No such ref: '%s'", argv[0]); case 1: diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c index 684411694f..c84c6e05e9 100644 --- a/builtin/merge-ours.c +++ b/builtin/merge-ours.c @@ -9,26 +9,24 @@ */ #include "git-compat-util.h" #include "builtin.h" +#include "diff.h" static const char builtin_merge_ours_usage[] = "git merge-ours <base>... -- HEAD <remote>..."; -static const char *diff_index_args[] = { - "diff-index", "--quiet", "--cached", "HEAD", "--", NULL -}; -#define NARGS (ARRAY_SIZE(diff_index_args) - 1) - int cmd_merge_ours(int argc, const char **argv, const char *prefix) { if (argc == 2 && !strcmp(argv[1], "-h")) usage(builtin_merge_ours_usage); /* - * We need to exit with 2 if the index does not match our HEAD tree, - * because the current index is what we will be committing as the - * merge result. + * The contents of the current index becomes the tree we + * commit. The index must match HEAD, or this merge cannot go + * through. */ - if (cmd_diff_index(NARGS, diff_index_args, prefix)) + if (read_cache() < 0) + die_errno("read_cache failed"); + if (index_differs_from("HEAD", NULL, 0)) exit(2); exit(0); } diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index d01ddecf66..32736e0b10 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -60,7 +60,7 @@ static void *result(struct merge_list *entry, unsigned long *size) const char *path = entry->path; if (!entry->stage) - return read_sha1_file(entry->blob->object.oid.hash, &type, size); + return read_object_file(&entry->blob->object.oid, &type, size); base = NULL; if (entry->stage == 1) { base = entry->blob; @@ -82,7 +82,8 @@ static void *origin(struct merge_list *entry, unsigned long *size) enum object_type type; while (entry) { if (entry->stage == 2) - return read_sha1_file(entry->blob->object.oid.hash, &type, size); + return read_object_file(&entry->blob->object.oid, + &type, size); entry = entry->link; } return NULL; diff --git a/builtin/merge.c b/builtin/merge.c index fbbf2a9e5e..9db5a2cf16 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -33,6 +33,7 @@ #include "sequencer.h" #include "string-list.h" #include "packfile.h" +#include "tag.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -405,14 +406,13 @@ static void finish(struct commit *head_commit, printf(_("No merge message -- not updating HEAD\n")); else { const char *argv_gc_auto[] = { "gc", "--auto", NULL }; - update_ref(reflog_message.buf, "HEAD", - new_head->hash, head->hash, 0, - UPDATE_REFS_DIE_ON_ERR); + update_ref(reflog_message.buf, "HEAD", new_head, head, + 0, UPDATE_REFS_DIE_ON_ERR); /* * We ignore errors in 'gc --auto', since the * user should see them. */ - close_all_packs(); + close_all_packs(the_repository->objects); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } @@ -455,7 +455,7 @@ static void merge_name(const char *remote, struct strbuf *msg) if (!remote_head) die(_("'%s' does not point to a commit"), remote); - if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) { + if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref) > 0) { if (starts_with(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", oid_to_hex(&branch_head), remote); @@ -521,7 +521,7 @@ static void merge_name(const char *remote, struct strbuf *msg) if (desc && desc->obj && desc->obj->type == OBJ_TAG) { strbuf_addf(msg, "%s\t\t%s '%s'\n", oid_to_hex(&desc->obj->oid), - typename(desc->obj->type), + type_name(desc->obj->type), remote); goto cleanup; } @@ -568,6 +568,8 @@ static int git_merge_config(const char *k, const char *v, void *cb) if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) show_diffstat = git_config_bool(k, v); + else if (!strcmp(k, "merge.verifysignatures")) + verify_signatures = git_config_bool(k, v); else if (!strcmp(k, "pull.twohead")) return git_config_string(&pull_twohead, k, v); else if (!strcmp(k, "pull.octopus")) @@ -637,7 +639,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, static void write_tree_trivial(struct object_id *oid) { - if (write_cache_as_tree(oid->hash, 0, NULL)) + if (write_cache_as_tree(oid, 0, NULL)) die(_("git write-tree failed to write a tree")); } @@ -650,10 +652,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, hold_locked_index(&lock, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); - if (active_cache_changed && - write_locked_index(&the_index, &lock, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) return error(_("Unable to write index.")); - rollback_lock_file(&lock); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { int clean, x; @@ -690,10 +691,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, remoteheads->item, reversed, &result); if (clean < 0) exit(128); - if (active_cache_changed && - write_locked_index(&the_index, &lock, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) die (_("unable to write %s"), get_index_file()); - rollback_lock_file(&lock); return clean ? 0 : 1; } else { return try_merge_command(strategy, xopts_nr, xopts, @@ -809,18 +809,17 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) hold_locked_index(&lock, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); - if (active_cache_changed && - write_locked_index(&the_index, &lock, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) return error(_("Unable to write index.")); - rollback_lock_file(&lock); write_tree_trivial(&result_tree); printf(_("Wonderful.\n")); pptr = commit_list_append(head, pptr); pptr = commit_list_append(remoteheads->item, pptr); prepare_to_commit(remoteheads); - if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents, - result_commit.hash, NULL, sign_commit)) + if (commit_tree(merge_msg.buf, merge_msg.len, &result_tree, parents, + &result_commit, NULL, sign_commit)) die(_("failed to write commit object")); finish(head, remoteheads, &result_commit, "In-index merge"); drop_save(); @@ -844,8 +843,8 @@ static int finish_automerge(struct commit *head, commit_list_insert(head, &parents); strbuf_addch(&merge_msg, '\n'); prepare_to_commit(remoteheads); - if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents, - result_commit.hash, NULL, sign_commit)) + if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents, + &result_commit, NULL, sign_commit)) die(_("failed to write commit object")); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); finish(head, remoteheads, &result_commit, buf.buf); @@ -1124,6 +1123,43 @@ static struct commit_list *collect_parents(struct commit *head_commit, return remoteheads; } +static int merging_a_throwaway_tag(struct commit *commit) +{ + char *tag_ref; + struct object_id oid; + int is_throwaway_tag = 0; + + /* Are we merging a tag? */ + if (!merge_remote_util(commit) || + !merge_remote_util(commit)->obj || + merge_remote_util(commit)->obj->type != OBJ_TAG) + return is_throwaway_tag; + + /* + * Now we know we are merging a tag object. Are we downstream + * and following the tags from upstream? If so, we must have + * the tag object pointed at by "refs/tags/$T" where $T is the + * tagname recorded in the tag object. We want to allow such + * a "just to catch up" merge to fast-forward. + * + * Otherwise, we are playing an integrator's role, making a + * merge with a throw-away tag from a contributor with + * something like "git pull $contributor $signed_tag". + * We want to forbid such a merge from fast-forwarding + * by default; otherwise we would not keep the signature + * anywhere. + */ + tag_ref = xstrfmt("refs/tags/%s", + ((struct tag *)merge_remote_util(commit)->obj)->tag); + if (!read_ref(tag_ref, &oid) && + !oidcmp(&oid, &merge_remote_util(commit)->obj->oid)) + is_throwaway_tag = 0; + else + is_throwaway_tag = 1; + free(tag_ref); + return is_throwaway_tag; +} + int cmd_merge(int argc, const char **argv, const char *prefix) { struct object_id result_tree, stash, head_oid; @@ -1144,7 +1180,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL); + branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL); if (branch) skip_prefix(branch, "refs/heads/", &branch); if (!branch || is_null_oid(&head_oid)) @@ -1262,8 +1298,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) die(_("Can merge only exactly one commit into empty head")); remote_head_oid = &remoteheads->item->object.oid; read_empty(remote_head_oid->hash, 0); - update_ref("initial pull", "HEAD", remote_head_oid->hash, - NULL, 0, UPDATE_REFS_DIE_ON_ERR); + update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); goto done; } @@ -1288,7 +1324,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) check_commit_signature(commit, &signature_check); - find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV); + find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV); switch (signature_check.result) { case 'G': break; @@ -1321,10 +1357,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) oid_to_hex(&commit->object.oid)); setenv(buf.buf, merge_remote_util(commit)->name, 1); strbuf_reset(&buf); - if (fast_forward != FF_ONLY && - merge_remote_util(commit) && - merge_remote_util(commit)->obj && - merge_remote_util(commit)->obj->type == OBJ_TAG) + if (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit)) fast_forward = FF_NO; } @@ -1358,8 +1391,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) free(list); } - update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash, - NULL, 0, UPDATE_REFS_DIE_ON_ERR); + update_ref("updating ORIG_HEAD", "ORIG_HEAD", + &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); if (remoteheads && !common) { /* No common ancestors found. */ @@ -1384,9 +1417,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (verbosity >= 0) { printf(_("Updating %s..%s\n"), - find_unique_abbrev(head_commit->object.oid.hash, + find_unique_abbrev(&head_commit->object.oid, DEFAULT_ABBREV), - find_unique_abbrev(remoteheads->item->object.oid.hash, + find_unique_abbrev(&remoteheads->item->object.oid, DEFAULT_ABBREV)); } strbuf_addstr(&msg, "Fast-forward"); diff --git a/builtin/mktag.c b/builtin/mktag.c index 031b750f06..9f5a50a8fd 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -18,17 +18,17 @@ /* * We refuse to tag something we can't verify. Just because. */ -static int verify_object(const unsigned char *sha1, const char *expected_type) +static int verify_object(const struct object_id *oid, const char *expected_type) { int ret = -1; enum object_type type; unsigned long size; - void *buffer = read_sha1_file(sha1, &type, &size); - const unsigned char *repl = lookup_replace_object(sha1); + void *buffer = read_object_file(oid, &type, &size); + const struct object_id *repl = lookup_replace_object(oid); if (buffer) { if (type == type_from_string(expected_type)) - ret = check_sha1_signature(repl, buffer, size, expected_type); + ret = check_object_signature(repl, buffer, size, expected_type); free(buffer); } return ret; @@ -38,8 +38,8 @@ static int verify_tag(char *buffer, unsigned long size) { int typelen; char type[20]; - unsigned char sha1[20]; - const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb; + struct object_id oid; + const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p; size_t len; if (size < 84) @@ -52,11 +52,11 @@ static int verify_tag(char *buffer, unsigned long size) if (memcmp(object, "object ", 7)) return error("char%d: does not start with \"object \"", 0); - if (get_sha1_hex(object + 7, sha1)) + if (parse_oid_hex(object + 7, &oid, &p)) return error("char%d: could not get SHA1 hash", 7); /* Verify type line */ - type_line = object + 48; + type_line = p + 1; if (memcmp(type_line - 1, "\ntype ", 6)) return error("char%d: could not find \"\\ntype \"", 47); @@ -80,8 +80,8 @@ static int verify_tag(char *buffer, unsigned long size) type[typelen] = 0; /* Verify that the object matches */ - if (verify_object(sha1, type)) - return error("char%d: could not verify object %s", 7, sha1_to_hex(sha1)); + if (verify_object(&oid, type)) + return error("char%d: could not verify object %s", 7, oid_to_hex(&oid)); /* Verify the tag-name: we don't allow control characters or spaces in it */ tag_line += 4; @@ -151,7 +151,7 @@ static int verify_tag(char *buffer, unsigned long size) int cmd_mktag(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; - unsigned char result_sha1[20]; + struct object_id result; if (argc != 1) usage("git mktag"); @@ -165,10 +165,10 @@ int cmd_mktag(int argc, const char **argv, const char *prefix) if (verify_tag(buf.buf, buf.len) < 0) die("invalid tag signature file"); - if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0) + if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0) die("unable to write tag file"); strbuf_release(&buf); - printf("%s\n", sha1_to_hex(result_sha1)); + printf("%s\n", oid_to_hex(&result)); return 0; } diff --git a/builtin/mktree.c b/builtin/mktree.c index da0fd8cd70..263c530315 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -10,13 +10,13 @@ static struct treeent { unsigned mode; - unsigned char sha1[20]; + struct object_id oid; int len; char name[FLEX_ARRAY]; } **entries; static int alloc, used; -static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) +static void append_to_tree(unsigned mode, struct object_id *oid, char *path) { struct treeent *ent; size_t len = strlen(path); @@ -26,7 +26,7 @@ static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) FLEX_ALLOC_MEM(ent, name, path, len); ent->mode = mode; ent->len = len; - hashcpy(ent->sha1, sha1); + oidcpy(&ent->oid, oid); ALLOC_GROW(entries, used + 1, alloc); entries[used++] = ent; @@ -40,7 +40,7 @@ static int ent_compare(const void *a_, const void *b_) b->name, b->len, b->mode); } -static void write_tree(unsigned char *sha1) +static void write_tree(struct object_id *oid) { struct strbuf buf; size_t size; @@ -54,10 +54,10 @@ static void write_tree(unsigned char *sha1) for (i = 0; i < used; i++) { struct treeent *ent = entries[i]; strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); - strbuf_add(&buf, ent->sha1, 20); + strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); } - write_sha1_file(buf.buf, buf.len, tree_type, sha1); + write_object_file(buf.buf, buf.len, tree_type, oid); strbuf_release(&buf); } @@ -69,11 +69,12 @@ static const char *mktree_usage[] = { static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_missing) { char *ptr, *ntr; + const char *p; unsigned mode; enum object_type mode_type; /* object type derived from mode */ enum object_type obj_type; /* object type derived from sha */ char *path, *to_free = NULL; - unsigned char sha1[20]; + struct object_id oid; ptr = buf; /* @@ -85,9 +86,8 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss die("input format error: %s", buf); ptr = ntr + 1; /* type */ ntr = strchr(ptr, ' '); - if (!ntr || buf + len <= ntr + 40 || - ntr[41] != '\t' || - get_sha1_hex(ntr + 1, sha1)) + if (!ntr || parse_oid_hex(ntr + 1, &oid, &p) || + *p != '\t') die("input format error: %s", buf); /* It is perfectly normal if we do not have a commit from a submodule */ @@ -112,16 +112,16 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss mode_type = object_type(mode); if (mode_type != type_from_string(ptr)) { die("entry '%s' object type (%s) doesn't match mode type (%s)", - path, ptr, typename(mode_type)); + path, ptr, type_name(mode_type)); } /* Check the type of object identified by sha1 */ - obj_type = sha1_object_info(sha1, NULL); + obj_type = oid_object_info(&oid, NULL); if (obj_type < 0) { if (allow_missing) { ; /* no problem - missing objects are presumed to be of the right type */ } else { - die("entry '%s' object %s is unavailable", path, sha1_to_hex(sha1)); + die("entry '%s' object %s is unavailable", path, oid_to_hex(&oid)); } } else { if (obj_type != mode_type) { @@ -131,18 +131,18 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss * because the new tree entry will never be correct. */ die("entry '%s' object %s is a %s but specified type was (%s)", - path, sha1_to_hex(sha1), typename(obj_type), typename(mode_type)); + path, oid_to_hex(&oid), type_name(obj_type), type_name(mode_type)); } } - append_to_tree(mode, sha1, path); + append_to_tree(mode, &oid, path); free(to_free); } int cmd_mktree(int ac, const char **av, const char *prefix) { struct strbuf sb = STRBUF_INIT; - unsigned char sha1[20]; + struct object_id oid; int nul_term_line = 0; int allow_missing = 0; int is_batch_mode = 0; @@ -181,8 +181,8 @@ int cmd_mktree(int ac, const char **av, const char *prefix) */ ; /* skip creating an empty tree */ } else { - write_tree(sha1); - puts(sha1_to_hex(sha1)); + write_tree(&oid); + puts(oid_to_hex(&oid)); fflush(stdout); } used=0; /* reset tree entry buffer for re-use in batch mode */ diff --git a/builtin/mv.c b/builtin/mv.c index ffdd5f01a1..6d141f7a53 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -122,7 +122,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), - OPT__FORCE(&force, N_("force move/rename even if target exists")), + OPT__FORCE(&force, N_("force move/rename even if target exists"), + PARSE_OPT_NOCOMPLETE), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; @@ -286,15 +287,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix) pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); - if (!show_only) - rename_cache_entry_at(pos, dst); + rename_cache_entry_at(pos, dst); } if (gitmodules_modified) - stage_updated_gitmodules(); + stage_updated_gitmodules(&the_index); - if (active_cache_changed && - write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, &lock_file, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); return 0; diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 9e088ebd11..387ddf85d2 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -328,7 +328,7 @@ static void show_name(const struct object *obj, else if (allow_undefined) printf("undefined\n"); else if (always) - printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV)); + printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV)); else die("cannot describe '%s'", oid_to_hex(oid)); strbuf_release(&buf); diff --git a/builtin/notes.c b/builtin/notes.c index e435286dd8..921e08d5bf 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -12,7 +12,7 @@ #include "builtin.h" #include "notes.h" #include "blob.h" -#include "commit.h" +#include "pretty.h" #include "refs.h" #include "exec_cmd.h" #include "run-command.h" @@ -33,7 +33,7 @@ static const char * const git_notes_usage[] = { N_("git notes merge --commit [-v | -q]"), N_("git notes merge --abort [-v | -q]"), N_("git notes [--ref <notes-ref>] remove [<object>...]"), - N_("git notes [--ref <notes-ref>] prune [-n | -v]"), + N_("git notes [--ref <notes-ref>] prune [-n] [-v]"), N_("git notes [--ref <notes-ref>] get-ref"), NULL }; @@ -118,11 +118,11 @@ static int list_each_note(const struct object_id *object_oid, return 0; } -static void copy_obj_to_fd(int fd, const unsigned char *sha1) +static void copy_obj_to_fd(int fd, const struct object_id *oid) { unsigned long size; enum object_type type; - char *buf = read_sha1_file(sha1, &type, &size); + char *buf = read_object_file(oid, &type, &size); if (buf) { if (size) write_or_die(fd, buf, size); @@ -162,7 +162,7 @@ static void write_commented_object(int fd, const struct object_id *object) } static void prepare_note_data(const struct object_id *object, struct note_data *d, - const unsigned char *old_note) + const struct object_id *old_note) { if (d->use_editor || !d->given) { int fd; @@ -198,9 +198,9 @@ static void prepare_note_data(const struct object_id *object, struct note_data * } } -static void write_note_data(struct note_data *d, unsigned char *sha1) +static void write_note_data(struct note_data *d, struct object_id *oid) { - if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) { + if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) { error(_("unable to write note object")); if (d->edit_path) error(_("the note contents have been left in %s"), @@ -253,7 +253,7 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) if (get_oid(arg, &object)) die(_("failed to resolve '%s' as a valid ref."), arg); - if (!(buf = read_sha1_file(object.hash, &type, &len))) { + if (!(buf = read_object_file(&object, &type, &len))) { free(buf); die(_("failed to read object '%s'."), arg); } @@ -413,7 +413,7 @@ static int add(int argc, const char **argv, const char *prefix) parse_reuse_arg}, OPT_BOOL(0, "allow-empty", &allow_empty, N_("allow storing empty note")), - OPT__FORCE(&force, N_("replace existing notes")), + OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE), OPT_END() }; @@ -457,9 +457,9 @@ static int add(int argc, const char **argv, const char *prefix) oid_to_hex(&object)); } - prepare_note_data(&object, &d, note ? note->hash : NULL); + prepare_note_data(&object, &d, note); if (d.buf.len || allow_empty) { - write_note_data(&d, new_note.hash); + write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes add'"); @@ -484,7 +484,7 @@ static int copy(int argc, const char **argv, const char *prefix) struct notes_tree *t; const char *rewrite_cmd = NULL; struct option options[] = { - OPT__FORCE(&force, N_("replace existing notes")), + OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")), OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"), N_("load rewriting config for <command> (implies " @@ -602,13 +602,13 @@ static int append_edit(int argc, const char **argv, const char *prefix) t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); note = get_note(t, &object); - prepare_note_data(&object, &d, edit && note ? note->hash : NULL); + prepare_note_data(&object, &d, edit && note ? note : NULL); if (note && !edit) { /* Append buf to previous note contents */ unsigned long size; enum object_type type; - char *prev_buf = read_sha1_file(note->hash, &type, &size); + char *prev_buf = read_object_file(note, &type, &size); strbuf_grow(&d.buf, size + 1); if (d.buf.len && prev_buf && size) @@ -619,7 +619,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) } if (d.buf.len || allow_empty) { - write_note_data(&d, new_note.hash); + write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]); @@ -686,7 +686,7 @@ static int merge_abort(struct notes_merge_options *o) if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0)) ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL")); - if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NODEREF)) + if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF)) ret += error(_("failed to delete ref NOTES_MERGE_REF")); if (notes_merge_abort(o)) ret += error(_("failed to remove 'git notes merge' worktree")); @@ -724,7 +724,7 @@ static int merge_commit(struct notes_merge_options *o) init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); o->local_ref = local_ref_to_free = - resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL); + resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL); if (!o->local_ref) die(_("failed to resolve NOTES_MERGE_REF")); @@ -736,8 +736,8 @@ static int merge_commit(struct notes_merge_options *o) format_commit_message(partial, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); strbuf_insert(&msg, 0, "notes: ", 7); - update_ref(msg.buf, o->local_ref, oid.hash, - is_null_oid(&parent_oid) ? NULL : parent_oid.hash, + update_ref(msg.buf, o->local_ref, &oid, + is_null_oid(&parent_oid) ? NULL : &parent_oid, 0, UPDATE_REFS_DIE_ON_ERR); free_notes(t); @@ -850,12 +850,12 @@ static int merge(int argc, const char **argv, const char *prefix) if (result >= 0) /* Merge resulted (trivially) in result_oid */ /* Update default notes ref with new commit */ - update_ref(msg.buf, default_notes_ref(), result_oid.hash, NULL, - 0, UPDATE_REFS_DIE_ON_ERR); + update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); else { /* Merge has unresolved conflicts */ const struct worktree *wt; /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ - update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_oid.hash, NULL, + update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref()); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 6e77dfd444..4bdae5a1d8 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "repository.h" #include "config.h" #include "attr.h" #include "object.h" @@ -15,6 +16,8 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "list-objects-filter.h" +#include "list-objects-filter-options.h" #include "pack-objects.h" #include "progress.h" #include "refs.h" @@ -24,8 +27,9 @@ #include "reachable.h" #include "sha1-array.h" #include "argv-array.h" -#include "mru.h" +#include "list.h" #include "packfile.h" +#include "object-store.h" static const char *pack_usage[] = { N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"), @@ -73,12 +77,24 @@ static int use_bitmap_index = -1; static int write_bitmap_index; static uint16_t write_bitmap_options; +static int exclude_promisor_objects; + static unsigned long delta_cache_size = 0; static unsigned long max_delta_cache_size = 256 * 1024 * 1024; static unsigned long cache_max_small_delta_size = 1000; static unsigned long window_memory_limit = 0; +static struct list_objects_filter_options filter_options; + +enum missing_action { + MA_ERROR = 0, /* fail if any missing objects are encountered */ + MA_ALLOW_ANY, /* silently allow ALL missing objects */ + MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */ +}; +static enum missing_action arg_missing_action; +static show_object_fn fn_show_object; + /* * stats */ @@ -108,11 +124,10 @@ static void *get_delta(struct object_entry *entry) void *buf, *base_buf, *delta_buf; enum object_type type; - buf = read_sha1_file(entry->idx.oid.hash, &type, &size); + buf = read_object_file(&entry->idx.oid, &type, &size); if (!buf) die("unable to read %s", oid_to_hex(&entry->idx.oid)); - base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type, - &base_size); + base_buf = read_object_file(&entry->delta->idx.oid, &type, &base_size); if (!base_buf) die("unable to read %s", oid_to_hex(&entry->delta->idx.oid)); @@ -150,8 +165,8 @@ static unsigned long do_compress(void **pptr, unsigned long size) return stream.total_out; } -static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f, - const unsigned char *sha1) +static unsigned long write_large_blob_data(struct git_istream *st, struct hashfile *f, + const struct object_id *oid) { git_zstream stream; unsigned char ibuf[1024 * 16]; @@ -165,7 +180,7 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi int zret = Z_OK; readlen = read_istream(st, ibuf, sizeof(ibuf)); if (readlen == -1) - die(_("unable to read %s"), sha1_to_hex(sha1)); + die(_("unable to read %s"), oid_to_hex(oid)); stream.next_in = ibuf; stream.avail_in = readlen; @@ -174,7 +189,7 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi stream.next_out = obuf; stream.avail_out = sizeof(obuf); zret = git_deflate(&stream, readlen ? 0 : Z_FINISH); - sha1write(f, obuf, stream.next_out - obuf); + hashwrite(f, obuf, stream.next_out - obuf); olen += stream.next_out - obuf; } if (stream.avail_in) @@ -219,7 +234,7 @@ static int check_pack_inflate(struct packed_git *p, stream.total_in == len) ? 0 : -1; } -static void copy_pack_data(struct sha1file *f, +static void copy_pack_data(struct hashfile *f, struct packed_git *p, struct pack_window **w_curs, off_t offset, @@ -232,14 +247,14 @@ static void copy_pack_data(struct sha1file *f, in = use_pack(p, w_curs, offset, &avail); if (avail > len) avail = (unsigned long)len; - sha1write(f, in, avail); + hashwrite(f, in, avail); offset += avail; len -= avail; } } /* Return 0 if we will bust the pack-size limit */ -static unsigned long write_no_reuse_object(struct sha1file *f, struct object_entry *entry, +static unsigned long write_no_reuse_object(struct hashfile *f, struct object_entry *entry, unsigned long limit, int usable_delta) { unsigned long size, datalen; @@ -253,11 +268,10 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent if (!usable_delta) { if (entry->type == OBJ_BLOB && entry->size > big_file_threshold && - (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL) + (st = open_istream(&entry->idx.oid, &type, &size, NULL)) != NULL) buf = NULL; else { - buf = read_sha1_file(entry->idx.oid.hash, &type, - &size); + buf = read_object_file(&entry->idx.oid, &type, &size); if (!buf) die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); @@ -312,8 +326,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent free(buf); return 0; } - sha1write(f, header, hdrlen); - sha1write(f, dheader + pos, sizeof(dheader) - pos); + hashwrite(f, header, hdrlen); + hashwrite(f, dheader + pos, sizeof(dheader) - pos); hdrlen += sizeof(dheader) - pos; } else if (type == OBJ_REF_DELTA) { /* @@ -326,8 +340,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent free(buf); return 0; } - sha1write(f, header, hdrlen); - sha1write(f, entry->delta->idx.oid.hash, 20); + hashwrite(f, header, hdrlen); + hashwrite(f, entry->delta->idx.oid.hash, 20); hdrlen += 20; } else { if (limit && hdrlen + datalen + 20 >= limit) { @@ -336,13 +350,13 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent free(buf); return 0; } - sha1write(f, header, hdrlen); + hashwrite(f, header, hdrlen); } if (st) { - datalen = write_large_blob_data(st, f, entry->idx.oid.hash); + datalen = write_large_blob_data(st, f, &entry->idx.oid); close_istream(st); } else { - sha1write(f, buf, datalen); + hashwrite(f, buf, datalen); free(buf); } @@ -350,7 +364,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent } /* Return 0 if we will bust the pack-size limit */ -static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, +static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, unsigned long limit, int usable_delta) { struct packed_git *p = entry->in_pack; @@ -401,8 +415,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, unuse_pack(&w_curs); return 0; } - sha1write(f, header, hdrlen); - sha1write(f, dheader + pos, sizeof(dheader) - pos); + hashwrite(f, header, hdrlen); + hashwrite(f, dheader + pos, sizeof(dheader) - pos); hdrlen += sizeof(dheader) - pos; reused_delta++; } else if (type == OBJ_REF_DELTA) { @@ -410,8 +424,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, unuse_pack(&w_curs); return 0; } - sha1write(f, header, hdrlen); - sha1write(f, entry->delta->idx.oid.hash, 20); + hashwrite(f, header, hdrlen); + hashwrite(f, entry->delta->idx.oid.hash, 20); hdrlen += 20; reused_delta++; } else { @@ -419,7 +433,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, unuse_pack(&w_curs); return 0; } - sha1write(f, header, hdrlen); + hashwrite(f, header, hdrlen); } copy_pack_data(f, p, &w_curs, offset, datalen); unuse_pack(&w_curs); @@ -428,7 +442,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, } /* Return 0 if we will bust the pack-size limit */ -static off_t write_object(struct sha1file *f, +static off_t write_object(struct hashfile *f, struct object_entry *entry, off_t write_offset) { @@ -501,7 +515,7 @@ enum write_one_status { WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */ }; -static enum write_one_status write_one(struct sha1file *f, +static enum write_one_status write_one(struct hashfile *f, struct object_entry *e, off_t *offset) { @@ -557,13 +571,13 @@ static enum write_one_status write_one(struct sha1file *f, static int mark_tagged(const char *path, const struct object_id *oid, int flag, void *cb_data) { - unsigned char peeled[20]; + struct object_id peeled; struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL); if (entry) entry->tagged = 1; - if (!peel_ref(path, peeled)) { - entry = packlist_find(&to_pack, peeled, NULL); + if (!peel_ref(path, &peeled)) { + entry = packlist_find(&to_pack, peeled.hash, NULL); if (entry) entry->tagged = 1; } @@ -720,7 +734,7 @@ static struct object_entry **compute_write_order(void) return wo; } -static off_t write_reused_pack(struct sha1file *f) +static off_t write_reused_pack(struct hashfile *f) { unsigned char buffer[8192]; off_t to_write, total; @@ -751,7 +765,7 @@ static off_t write_reused_pack(struct sha1file *f) if (read_pack > to_write) read_pack = to_write; - sha1write(f, buffer, read_pack); + hashwrite(f, buffer, read_pack); to_write -= read_pack; /* @@ -780,7 +794,7 @@ static const char no_split_warning[] = N_( static void write_pack_file(void) { uint32_t i = 0, j; - struct sha1file *f; + struct hashfile *f; off_t offset; uint32_t nr_remaining = nr_result; time_t last_mtime = 0; @@ -792,11 +806,11 @@ static void write_pack_file(void) write_order = compute_write_order(); do { - unsigned char sha1[20]; + struct object_id oid; char *pack_tmp_name = NULL; if (pack_to_stdout) - f = sha1fd_throughput(1, "<stdout>", progress_state); + f = hashfd_throughput(1, "<stdout>", progress_state); else f = create_tmp_packfile(&pack_tmp_name); @@ -823,13 +837,13 @@ static void write_pack_file(void) * If so, rewrite it like in fast-import */ if (pack_to_stdout) { - sha1close(f, sha1, CSUM_CLOSE); + hashclose(f, oid.hash, CSUM_CLOSE); } else if (nr_written == nr_remaining) { - sha1close(f, sha1, CSUM_FSYNC); + hashclose(f, oid.hash, CSUM_FSYNC); } else { - int fd = sha1close(f, sha1, 0); - fixup_pack_header_footer(fd, sha1, pack_tmp_name, - nr_written, sha1, offset); + int fd = hashclose(f, oid.hash, 0); + fixup_pack_header_footer(fd, oid.hash, pack_tmp_name, + nr_written, oid.hash, offset); close(fd); if (write_bitmap_index) { warning(_(no_split_warning)); @@ -863,16 +877,16 @@ static void write_pack_file(void) strbuf_addf(&tmpname, "%s-", base_name); if (write_bitmap_index) { - bitmap_writer_set_checksum(sha1); + bitmap_writer_set_checksum(oid.hash); bitmap_writer_build_type_index(written_list, nr_written); } finish_tmp_packfile(&tmpname, pack_tmp_name, written_list, nr_written, - &pack_idx_opts, sha1); + &pack_idx_opts, oid.hash); if (write_bitmap_index) { - strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1)); + strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid)); stop_progress(&progress_state); @@ -887,7 +901,7 @@ static void write_pack_file(void) strbuf_release(&tmpname); free(pack_tmp_name); - puts(sha1_to_hex(sha1)); + puts(oid_to_hex(&oid)); } /* mark written objects as written to previous pack */ @@ -928,13 +942,13 @@ static int no_try_delta(const char *path) * found the item, since that saves us from having to look it up again a * few lines later when we want to add the new entry. */ -static int have_duplicate_entry(const unsigned char *sha1, +static int have_duplicate_entry(const struct object_id *oid, int exclude, uint32_t *index_pos) { struct object_entry *entry; - entry = packlist_find(&to_pack, sha1, index_pos); + entry = packlist_find(&to_pack, oid->hash, index_pos); if (!entry) return 0; @@ -990,15 +1004,15 @@ static int want_found_object(int exclude, struct packed_git *p) * function finds if there is any pack that has the object and returns the pack * and its offset in these variables. */ -static int want_object_in_pack(const unsigned char *sha1, +static int want_object_in_pack(const struct object_id *oid, int exclude, struct packed_git **found_pack, off_t *found_offset) { - struct mru_entry *entry; int want; + struct list_head *pos; - if (!exclude && local && has_loose_object_nonlocal(sha1)) + if (!exclude && local && has_loose_object_nonlocal(oid->hash)) return 0; /* @@ -1011,15 +1025,14 @@ static int want_object_in_pack(const unsigned char *sha1, if (want != -1) return want; } - - for (entry = packed_git_mru.head; entry; entry = entry->next) { - struct packed_git *p = entry->item; + list_for_each(pos, get_packed_git_mru(the_repository)) { + struct packed_git *p = list_entry(pos, struct packed_git, mru); off_t offset; if (p == *found_pack) offset = *found_offset; else - offset = find_pack_entry_one(sha1, p); + offset = find_pack_entry_one(oid->hash, p); if (offset) { if (!*found_pack) { @@ -1030,7 +1043,8 @@ static int want_object_in_pack(const unsigned char *sha1, } want = want_found_object(exclude, p); if (!exclude && want > 0) - mru_mark(&packed_git_mru, entry); + list_move(&p->mru, + get_packed_git_mru(the_repository)); if (want != -1) return want; } @@ -1039,7 +1053,7 @@ static int want_object_in_pack(const unsigned char *sha1, return 1; } -static void create_object_entry(const unsigned char *sha1, +static void create_object_entry(const struct object_id *oid, enum object_type type, uint32_t hash, int exclude, @@ -1050,7 +1064,7 @@ static void create_object_entry(const unsigned char *sha1, { struct object_entry *entry; - entry = packlist_alloc(&to_pack, sha1, index_pos); + entry = packlist_alloc(&to_pack, oid->hash, index_pos); entry->hash = hash; if (type) entry->type = type; @@ -1070,17 +1084,17 @@ static const char no_closure_warning[] = N_( "disabling bitmap writing, as some objects are not being packed" ); -static int add_object_entry(const unsigned char *sha1, enum object_type type, +static int add_object_entry(const struct object_id *oid, enum object_type type, const char *name, int exclude) { struct packed_git *found_pack = NULL; off_t found_offset = 0; uint32_t index_pos; - if (have_duplicate_entry(sha1, exclude, &index_pos)) + if (have_duplicate_entry(oid, exclude, &index_pos)) return 0; - if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) { + if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) { /* The pack is missing an object, so it will not have closure */ if (write_bitmap_index) { warning(_(no_closure_warning)); @@ -1089,7 +1103,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, return 0; } - create_object_entry(sha1, type, pack_name_hash(name), + create_object_entry(oid, type, pack_name_hash(name), exclude, name && no_try_delta(name), index_pos, found_pack, found_offset); @@ -1097,27 +1111,27 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, return 1; } -static int add_object_entry_from_bitmap(const unsigned char *sha1, +static int add_object_entry_from_bitmap(const struct object_id *oid, enum object_type type, int flags, uint32_t name_hash, struct packed_git *pack, off_t offset) { uint32_t index_pos; - if (have_duplicate_entry(sha1, 0, &index_pos)) + if (have_duplicate_entry(oid, 0, &index_pos)) return 0; - if (!want_object_in_pack(sha1, 0, &pack, &offset)) + if (!want_object_in_pack(oid, 0, &pack, &offset)) return 0; - create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset); + create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset); display_progress(progress_state, nr_result); return 1; } struct pbase_tree_cache { - unsigned char sha1[20]; + struct object_id oid; int ref; int temporary; void *tree_data; @@ -1125,9 +1139,9 @@ struct pbase_tree_cache { }; static struct pbase_tree_cache *(pbase_tree_cache[256]); -static int pbase_tree_cache_ix(const unsigned char *sha1) +static int pbase_tree_cache_ix(const struct object_id *oid) { - return sha1[0] % ARRAY_SIZE(pbase_tree_cache); + return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache); } static int pbase_tree_cache_ix_incr(int ix) { @@ -1144,14 +1158,14 @@ static struct pbase_tree { struct pbase_tree_cache pcache; } *pbase_tree; -static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1) +static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid) { struct pbase_tree_cache *ent, *nent; void *data; unsigned long size; enum object_type type; int neigh; - int my_ix = pbase_tree_cache_ix(sha1); + int my_ix = pbase_tree_cache_ix(oid); int available_ix = -1; /* pbase-tree-cache acts as a limited hashtable. @@ -1160,7 +1174,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1) */ for (neigh = 0; neigh < 8; neigh++) { ent = pbase_tree_cache[my_ix]; - if (ent && !hashcmp(ent->sha1, sha1)) { + if (ent && !oidcmp(&ent->oid, oid)) { ent->ref++; return ent; } @@ -1176,7 +1190,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1) /* Did not find one. Either we got a bogus request or * we need to read and perhaps cache. */ - data = read_sha1_file(sha1, &type, &size); + data = read_object_file(oid, &type, &size); if (!data) return NULL; if (type != OBJ_TREE) { @@ -1202,7 +1216,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1) free(ent->tree_data); nent = ent; } - hashcpy(nent->sha1, sha1); + oidcpy(&nent->oid, oid); nent->tree_data = data; nent->tree_size = size; nent->ref = 1; @@ -1247,7 +1261,7 @@ static void add_pbase_object(struct tree_desc *tree, if (cmp < 0) return; if (name[cmplen] != '/') { - add_object_entry(entry.oid->hash, + add_object_entry(entry.oid, object_type(entry.mode), fullname, 1); return; @@ -1258,7 +1272,7 @@ static void add_pbase_object(struct tree_desc *tree, const char *down = name+cmplen+1; int downlen = name_cmp_len(down); - tree = pbase_tree_get(entry.oid->hash); + tree = pbase_tree_get(entry.oid); if (!tree) return; init_tree_desc(&sub, tree->tree_data, tree->tree_size); @@ -1317,7 +1331,7 @@ static void add_preferred_base_object(const char *name) cmplen = name_cmp_len(name); for (it = pbase_tree; it; it = it->next) { if (cmplen == 0) { - add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1); + add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1); } else { struct tree_desc tree; @@ -1327,22 +1341,22 @@ static void add_preferred_base_object(const char *name) } } -static void add_preferred_base(unsigned char *sha1) +static void add_preferred_base(struct object_id *oid) { struct pbase_tree *it; void *data; unsigned long size; - unsigned char tree_sha1[20]; + struct object_id tree_oid; if (window <= num_preferred_base++) return; - data = read_object_with_reference(sha1, tree_type, &size, tree_sha1); + data = read_object_with_reference(oid, tree_type, &size, &tree_oid); if (!data) return; for (it = pbase_tree; it; it = it->next) { - if (!hashcmp(it->pcache.sha1, tree_sha1)) { + if (!oidcmp(&it->pcache.oid, &tree_oid)) { free(data); return; } @@ -1352,7 +1366,7 @@ static void add_preferred_base(unsigned char *sha1) it->next = pbase_tree; pbase_tree = it; - hashcpy(it->pcache.sha1, tree_sha1); + oidcpy(&it->pcache.oid, &tree_oid); it->pcache.tree_data = data; it->pcache.tree_size = size; } @@ -1365,10 +1379,10 @@ static void cleanup_preferred_base(void) it = pbase_tree; pbase_tree = NULL; while (it) { - struct pbase_tree *this = it; - it = this->next; - free(this->pcache.tree_data); - free(this); + struct pbase_tree *tmp = it; + it = tmp->next; + free(tmp->pcache.tree_data); + free(tmp); } for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) { @@ -1502,7 +1516,7 @@ static void check_object(struct object_entry *entry) unuse_pack(&w_curs); } - entry->type = sha1_object_info(entry->idx.oid.hash, &entry->size); + entry->type = oid_object_info(&entry->idx.oid, &entry->size); /* * The error condition is checked in prepare_pack(). This is * to permit a missing preferred base object to be ignored @@ -1564,8 +1578,7 @@ static void drop_reused_delta(struct object_entry *entry) * And if that fails, the error will be recorded in entry->type * and dealt with in prepare_pack(). */ - entry->type = sha1_object_info(entry->idx.oid.hash, - &entry->size); + entry->type = oid_object_info(&entry->idx.oid, &entry->size); } } @@ -1857,8 +1870,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Load data if not already done */ if (!trg->data) { read_lock(); - trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type, - &sz); + trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz); read_unlock(); if (!trg->data) die("object %s cannot be read", @@ -1871,8 +1883,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, } if (!src->data) { read_lock(); - src->data = read_sha1_file(src_entry->idx.oid.hash, &type, - &sz); + src->data = read_object_file(&src_entry->idx.oid, &type, &sz); read_unlock(); if (!src->data) { if (src_entry->preferred_base) { @@ -2357,7 +2368,7 @@ static void add_tag_chain(const struct object_id *oid) die("unable to pack objects reachable from tag %s", oid_to_hex(oid)); - add_object_entry(tag->object.oid.hash, OBJ_TAG, NULL, 0); + add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0); if (tag->tagged->type != OBJ_TAG) return; @@ -2371,7 +2382,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag, struct object_id peeled; if (starts_with(path, "refs/tags/") && /* is a tag? */ - !peel_ref(path, peeled.hash) && /* peelable? */ + !peel_ref(path, &peeled) && /* peelable? */ packlist_find(&to_pack, peeled.hash, NULL)) /* object packed? */ add_tag_chain(oid); return 0; @@ -2505,8 +2516,9 @@ static int git_pack_config(const char *k, const char *v, void *cb) static void read_object_list_from_stdin(void) { - char line[40 + 1 + PATH_MAX + 2]; - unsigned char sha1[20]; + char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2]; + struct object_id oid; + const char *p; for (;;) { if (!fgets(line, sizeof(line), stdin)) { @@ -2520,25 +2532,26 @@ static void read_object_list_from_stdin(void) continue; } if (line[0] == '-') { - if (get_sha1_hex(line+1, sha1)) - die("expected edge sha1, got garbage:\n %s", + if (get_oid_hex(line+1, &oid)) + die("expected edge object ID, got garbage:\n %s", line); - add_preferred_base(sha1); + add_preferred_base(&oid); continue; } - if (get_sha1_hex(line, sha1)) - die("expected sha1, got garbage:\n %s", line); + if (parse_oid_hex(line, &oid, &p)) + die("expected object ID, got garbage:\n %s", line); - add_preferred_base_object(line+41); - add_object_entry(sha1, 0, line+41, 0); + add_preferred_base_object(p + 1); + add_object_entry(&oid, 0, p + 1, 0); } } +/* Remember to update object flag allocation in object.h */ #define OBJECT_ADDED (1u<<20) static void show_commit(struct commit *commit, void *data) { - add_object_entry(commit->object.oid.hash, OBJ_COMMIT, NULL, 0); + add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0); commit->object.flags |= OBJECT_ADDED; if (write_bitmap_index) @@ -2548,13 +2561,71 @@ static void show_commit(struct commit *commit, void *data) static void show_object(struct object *obj, const char *name, void *data) { add_preferred_base_object(name); - add_object_entry(obj->oid.hash, obj->type, name, 0); + add_object_entry(&obj->oid, obj->type, name, 0); obj->flags |= OBJECT_ADDED; } +static void show_object__ma_allow_any(struct object *obj, const char *name, void *data) +{ + assert(arg_missing_action == MA_ALLOW_ANY); + + /* + * Quietly ignore ALL missing objects. This avoids problems with + * staging them now and getting an odd error later. + */ + if (!has_object_file(&obj->oid)) + return; + + show_object(obj, name, data); +} + +static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data) +{ + assert(arg_missing_action == MA_ALLOW_PROMISOR); + + /* + * Quietly ignore EXPECTED missing objects. This avoids problems with + * staging them now and getting an odd error later. + */ + if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid)) + return; + + show_object(obj, name, data); +} + +static int option_parse_missing_action(const struct option *opt, + const char *arg, int unset) +{ + assert(arg); + assert(!unset); + + if (!strcmp(arg, "error")) { + arg_missing_action = MA_ERROR; + fn_show_object = show_object; + return 0; + } + + if (!strcmp(arg, "allow-any")) { + arg_missing_action = MA_ALLOW_ANY; + fetch_if_missing = 0; + fn_show_object = show_object__ma_allow_any; + return 0; + } + + if (!strcmp(arg, "allow-promisor")) { + arg_missing_action = MA_ALLOW_PROMISOR; + fetch_if_missing = 0; + fn_show_object = show_object__ma_allow_promisor; + return 0; + } + + die(_("invalid value for --missing")); + return 0; +} + static void show_edge(struct commit *commit) { - add_preferred_base(commit->object.oid.hash); + add_preferred_base(&commit->object.oid); } struct in_pack_object { @@ -2600,8 +2671,8 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) memset(&in_pack, 0, sizeof(in_pack)); - for (p = packed_git; p; p = p->next) { - const unsigned char *sha1; + for (p = get_packed_git(the_repository); p; p = p->next) { + struct object_id oid; struct object *o; if (!p->pack_local || p->pack_keep) @@ -2614,8 +2685,8 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) in_pack.alloc); for (i = 0; i < p->num_objects; i++) { - sha1 = nth_packed_object_sha1(p, i); - o = lookup_unknown_object(sha1); + nth_packed_object_oid(&oid, p, i); + o = lookup_unknown_object(oid.hash); if (!(o->flags & OBJECT_ADDED)) mark_in_pack_object(o, p, &in_pack); o->flags |= OBJECT_ADDED; @@ -2626,7 +2697,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) QSORT(in_pack.array, in_pack.nr, ofscmp); for (i = 0; i < in_pack.nr; i++) { struct object *o = in_pack.array[i].object; - add_object_entry(o->oid.hash, o->type, "", 0); + add_object_entry(&o->oid, o->type, "", 0); } } free(in_pack.array); @@ -2635,14 +2706,14 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) static int add_loose_object(const struct object_id *oid, const char *path, void *data) { - enum object_type type = sha1_object_info(oid->hash, NULL); + enum object_type type = oid_object_info(oid, NULL); if (type < 0) { warning("loose object at %s could not be examined", path); return 0; } - add_object_entry(oid->hash, type, "", 0); + add_object_entry(oid, type, "", 0); return 0; } @@ -2658,21 +2729,22 @@ static void add_unreachable_loose_objects(void) NULL, NULL, NULL); } -static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1) +static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid) { static struct packed_git *last_found = (void *)1; struct packed_git *p; - p = (last_found != (void *)1) ? last_found : packed_git; + p = (last_found != (void *)1) ? last_found : + get_packed_git(the_repository); while (p) { if ((!p->pack_local || p->pack_keep) && - find_pack_entry_one(sha1, p)) { + find_pack_entry_one(oid->hash, p)) { last_found = p; return 1; } if (p == last_found) - p = packed_git; + p = get_packed_git(the_repository); else p = p->next; if (p == last_found) @@ -2708,7 +2780,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) uint32_t i; struct object_id oid; - for (p = packed_git; p; p = p->next) { + for (p = get_packed_git(the_repository); p; p = p->next) { if (!p->pack_local || p->pack_keep) continue; @@ -2718,9 +2790,9 @@ static void loosen_unused_packed_objects(struct rev_info *revs) for (i = 0; i < p->num_objects; i++) { nth_packed_object_oid(&oid, p, i); if (!packlist_find(&to_pack, oid.hash, NULL) && - !has_sha1_pack_kept_or_nonlocal(oid.hash) && + !has_sha1_pack_kept_or_nonlocal(&oid) && !loosened_object_can_be_discarded(&oid, p->mtime)) - if (force_object_loose(oid.hash, p->mtime)) + if (force_object_loose(&oid, p->mtime)) die("unable to force loose object"); } } @@ -2816,7 +2888,12 @@ 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); - traverse_commit_list(&revs, show_commit, show_object, NULL); + + if (!fn_show_object) + fn_show_object = show_object; + traverse_commit_list_filtered(&filter_options, &revs, + show_commit, fn_show_object, NULL, + NULL); if (unpack_unreachable_expiration) { revs.ignore_missing_links = 1; @@ -2952,6 +3029,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("use a bitmap index if available to speed up counting objects")), OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index, N_("write a bitmap index together with the pack index")), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), + { OPTION_CALLBACK, 0, "missing", NULL, N_("action"), + N_("handling for missing objects"), PARSE_OPT_NONEG, + option_parse_missing_action }, + OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, + N_("do not pack objects in promisor packfiles")), OPT_END(), }; @@ -2997,6 +3080,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) argv_array_push(&rp, "--unpacked"); } + if (exclude_promisor_objects) { + use_internal_rev_list = 1; + fetch_if_missing = 0; + argv_array_push(&rp, "--exclude-promisor-objects"); + } + if (!reuse_object) reuse_delta = 0; if (pack_compression_level == -1) @@ -3028,6 +3117,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; + if (filter_options.choice) { + if (!pack_to_stdout) + die("cannot use --filter without --stdout."); + use_bitmap_index = 0; + } + /* * "soft" reasons not to use bitmaps - for on-disk repack by default we want * @@ -3053,10 +3148,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (progress && all_progress_implied) progress = 2; - prepare_packed_git(); if (ignore_packed_keep) { struct packed_git *p; - for (p = packed_git; p; p = p->next) + for (p = get_packed_git(the_repository); p; p = p->next) if (p->pack_local && p->pack_keep) break; if (!p) /* no keep-able packs found */ @@ -3069,7 +3163,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) * also covers non-local objects */ struct packed_git *p; - for (p = packed_git; p; p = p->next) { + for (p = get_packed_git(the_repository); p; p = p->next) { if (!p->pack_local) { have_non_local_packs = 1; break; diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index aaa8136322..354478a127 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -7,7 +7,9 @@ */ #include "builtin.h" +#include "repository.h" #include "packfile.h" +#include "object-store.h" #define BLKSIZE 512 @@ -48,17 +50,17 @@ static inline void llist_item_put(struct llist_item *item) static inline struct llist_item *llist_item_get(void) { - struct llist_item *new; + struct llist_item *new_item; if ( free_nodes ) { - new = free_nodes; + new_item = free_nodes; free_nodes = free_nodes->next; } else { int i = 1; - ALLOC_ARRAY(new, BLKSIZE); + ALLOC_ARRAY(new_item, BLKSIZE); for (; i < BLKSIZE; i++) - llist_item_put(&new[i]); + llist_item_put(&new_item[i]); } - return new; + return new_item; } static void llist_free(struct llist *list) @@ -80,26 +82,26 @@ static inline void llist_init(struct llist **list) static struct llist * llist_copy(struct llist *list) { struct llist *ret; - struct llist_item *new, *old, *prev; + struct llist_item *new_item, *old_item, *prev; llist_init(&ret); if ((ret->size = list->size) == 0) return ret; - new = ret->front = llist_item_get(); - new->sha1 = list->front->sha1; + new_item = ret->front = llist_item_get(); + new_item->sha1 = list->front->sha1; - old = list->front->next; - while (old) { - prev = new; - new = llist_item_get(); - prev->next = new; - new->sha1 = old->sha1; - old = old->next; + old_item = list->front->next; + while (old_item) { + prev = new_item; + new_item = llist_item_get(); + prev->next = new_item; + new_item->sha1 = old_item->sha1; + old_item = old_item->next; } - new->next = NULL; - ret->back = new; + new_item->next = NULL; + ret->back = new_item; return ret; } @@ -108,24 +110,24 @@ static inline struct llist_item *llist_insert(struct llist *list, struct llist_item *after, const unsigned char *sha1) { - struct llist_item *new = llist_item_get(); - new->sha1 = sha1; - new->next = NULL; + struct llist_item *new_item = llist_item_get(); + new_item->sha1 = sha1; + new_item->next = NULL; if (after != NULL) { - new->next = after->next; - after->next = new; + new_item->next = after->next; + after->next = new_item; if (after == list->back) - list->back = new; + list->back = new_item; } else {/* insert in front */ if (list->size == 0) - list->back = new; + list->back = new_item; else - new->next = list->front; - list->front = new; + new_item->next = list->front; + list->front = new_item; } list->size++; - return new; + return new_item; } static inline struct llist_item *llist_insert_back(struct llist *list, @@ -571,7 +573,7 @@ static struct pack_list * add_pack(struct packed_git *p) static struct pack_list * add_pack_file(const char *filename) { - struct packed_git *p = packed_git; + struct packed_git *p = get_packed_git(the_repository); if (strlen(filename) < 40) die("Bad pack filename: %s", filename); @@ -586,7 +588,7 @@ static struct pack_list * add_pack_file(const char *filename) static void load_all(void) { - struct packed_git *p = packed_git; + struct packed_git *p = get_packed_git(the_repository); while (p) { add_pack(p); @@ -629,8 +631,6 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) break; } - prepare_packed_git(); - if (load_all_packs) load_all(); else diff --git a/builtin/prune.c b/builtin/prune.c index cddabf26a9..38ced18dad 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -8,7 +8,7 @@ #include "progress.h" static const char * const prune_usage[] = { - N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"), + N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"), NULL }; static int show_only; @@ -50,9 +50,9 @@ static int prune_object(const struct object_id *oid, const char *fullpath, if (st.st_mtime > expire) return 0; if (show_only || verbose) { - enum object_type type = sha1_object_info(oid->hash, NULL); + enum object_type type = oid_object_info(oid, NULL); printf("%s %s\n", oid_to_hex(oid), - (type > 0) ? typename(type) : "unknown"); + (type > 0) ? type_name(type) : "unknown"); } if (!show_only) unlink_or_warn(fullpath); @@ -101,12 +101,15 @@ int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; + int exclude_promisor_objects = 0; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_EXPIRY_DATE(0, "expire", &expire, N_("expire objects older than <time>")), + OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, + N_("limit traversal to objects outside promisor packfiles")), OPT_END() }; char *s; @@ -139,6 +142,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix) show_progress = isatty(2); if (show_progress) progress = start_delayed_progress(_("Checking connectivity"), 0); + if (exclude_promisor_objects) { + fetch_if_missing = 0; + revs.exclude_promisor_objects = 1; + } mark_reachable_objects(&revs, 1, expire, progress); stop_progress(&progress); diff --git a/builtin/pull.c b/builtin/pull.c index 1b4d9cd974..e32d6cd5b4 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -86,6 +86,7 @@ static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static enum rebase_type opt_rebase = -1; static char *opt_diffstat; static char *opt_log; +static char *opt_signoff; static char *opt_squash; static char *opt_commit; static char *opt_edit; @@ -144,6 +145,9 @@ static struct option pull_options[] = { OPT_PASSTHRU(0, "log", &opt_log, N_("n"), N_("add (at most <n>) entries from shortlog to merge commit message"), PARSE_OPT_OPTARG), + OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL, + N_("add Signed-off-by:"), + PARSE_OPT_OPTARG), OPT_PASSTHRU(0, "squash", &opt_squash, NULL, N_("create a single commit instead of doing a merge"), PARSE_OPT_NOARG), @@ -189,7 +193,7 @@ static struct option pull_options[] = { OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"), N_("path to upload pack on remote end"), 0), - OPT__FORCE(&opt_force, N_("force overwrite of local branch")), + OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0), OPT_PASSTHRU('t', "tags", &opt_tags, NULL, N_("fetch all tags and associated objects"), PARSE_OPT_NOARG), @@ -553,10 +557,10 @@ static int pull_into_void(const struct object_id *merge_head, * index/worktree changes that the user already made on the unborn * branch. */ - if (checkout_fast_forward(&empty_tree_oid, merge_head, 0)) + if (checkout_fast_forward(the_hash_algo->empty_tree, merge_head, 0)) return 1; - if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR)) + if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR)) return 1; return 0; @@ -570,6 +574,7 @@ static int rebase_submodules(void) cp.no_stdin = 1; argv_array_pushl(&cp.args, "submodule", "update", "--recursive", "--rebase", NULL); + argv_push_verbosity(&cp.args); return run_command(&cp); } @@ -582,6 +587,7 @@ static int update_submodules(void) cp.no_stdin = 1; argv_array_pushl(&cp.args, "submodule", "update", "--recursive", "--checkout", NULL); + argv_push_verbosity(&cp.args); return run_command(&cp); } @@ -606,6 +612,8 @@ static int run_merge(void) argv_array_push(&args, opt_diffstat); if (opt_log) argv_array_push(&args, opt_log); + if (opt_signoff) + argv_array_push(&args, opt_signoff); if (opt_squash) argv_array_push(&args, opt_squash); if (opt_commit) diff --git a/builtin/push.c b/builtin/push.c index 2ac8104229..013c20d616 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -32,6 +32,8 @@ static const char **refspec; static int refspec_nr; static int refspec_alloc; +static struct string_list push_options_config = STRING_LIST_INIT_DUP; + static void add_refspec(const char *ref) { refspec_nr++; @@ -503,6 +505,15 @@ static int git_push_config(const char *k, const char *v, void *cb) int val = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF; recurse_submodules = val; + } else if (!strcmp(k, "push.pushoption")) { + if (!v) + return config_error_nonbool(k); + else + if (!*v) + string_list_clear(&push_options_config, 0); + else + string_list_append(&push_options_config, v); + return 0; } return git_default_config(k, v, NULL); @@ -515,7 +526,8 @@ int cmd_push(int argc, const char **argv, const char *prefix) int push_cert = -1; int rc; const char *repo = NULL; /* default repository */ - struct string_list push_options = STRING_LIST_INIT_DUP; + struct string_list push_options_cmdline = STRING_LIST_INIT_DUP; + struct string_list *push_options; const struct string_list_item *item; struct option options[] = { @@ -536,7 +548,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "check|on-demand|no", N_("control recursive pushing of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, - OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")), + OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE), OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")), OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")), OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"), @@ -551,7 +563,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) 0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"), PARSE_OPT_OPTARG, option_parse_push_signed }, OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), - OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")), + OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")), OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), TRANSPORT_FAMILY_IPV4), OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), @@ -562,6 +574,9 @@ int cmd_push(int argc, const char **argv, const char *prefix) packet_trace_identity("push"); git_config(git_push_config, &flags); argc = parse_options(argc, argv, prefix, options, push_usage, 0); + push_options = (push_options_cmdline.nr + ? &push_options_cmdline + : &push_options_config); set_push_cert_flags(&flags, push_cert); if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) @@ -584,12 +599,13 @@ int cmd_push(int argc, const char **argv, const char *prefix) set_refspecs(argv + 1, argc - 1, repo); } - for_each_string_list_item(item, &push_options) + for_each_string_list_item(item, push_options) if (strchr(item->string, '\n')) die(_("push options must not have new line characters")); - rc = do_push(repo, flags, &push_options); - string_list_clear(&push_options, 0); + rc = do_push(repo, flags, push_options); + string_list_clear(&push_options_cmdline, 0); + string_list_clear(&push_options_config, 0); if (rc == -1) usage_with_options(push_usage, options); else diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c index f8519363a3..ad074705bb 100644 --- a/builtin/rebase--helper.c +++ b/builtin/rebase--helper.c @@ -12,14 +12,18 @@ static const char * const builtin_rebase_helper_usage[] = { int cmd_rebase__helper(int argc, const char **argv, const char *prefix) { struct replay_opts opts = REPLAY_OPTS_INIT; - int keep_empty = 0; + unsigned flags = 0, keep_empty = 0; + int abbreviate_commands = 0; enum { - CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_SHA1S, EXPAND_SHA1S, - CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH + CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS, + CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH, + ADD_EXEC } command = 0; struct option options[] = { OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")), OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")), + OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message, + N_("allow commits with empty messages")), OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), CONTINUE), OPT_CMDMODE(0, "abort", &command, N_("abort rebase"), @@ -27,19 +31,22 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix) OPT_CMDMODE(0, "make-script", &command, N_("make rebase script"), MAKE_SCRIPT), OPT_CMDMODE(0, "shorten-ids", &command, - N_("shorten SHA-1s in the todo list"), SHORTEN_SHA1S), + N_("shorten commit ids in the todo list"), SHORTEN_OIDS), OPT_CMDMODE(0, "expand-ids", &command, - N_("expand SHA-1s in the todo list"), EXPAND_SHA1S), + N_("expand commit ids in the todo list"), EXPAND_OIDS), OPT_CMDMODE(0, "check-todo-list", &command, N_("check the todo list"), CHECK_TODO_LIST), OPT_CMDMODE(0, "skip-unnecessary-picks", &command, N_("skip unnecessary picks"), SKIP_UNNECESSARY_PICKS), OPT_CMDMODE(0, "rearrange-squash", &command, N_("rearrange fixup/squash lines"), REARRANGE_SQUASH), + OPT_CMDMODE(0, "add-exec-commands", &command, + N_("insert exec commands in todo list"), ADD_EXEC), OPT_END() }; - git_config(git_default_config, NULL); + sequencer_init_config(&opts); + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); opts.action = REPLAY_INTERACTIVE_REBASE; opts.allow_ff = 1; @@ -48,21 +55,25 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, NULL, options, builtin_rebase_helper_usage, PARSE_OPT_KEEP_ARGV0); + flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; + flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; + flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; + if (command == CONTINUE && argc == 1) return !!sequencer_continue(&opts); if (command == ABORT && argc == 1) return !!sequencer_remove_state(&opts); if (command == MAKE_SCRIPT && argc > 1) - return !!sequencer_make_script(keep_empty, stdout, argc, argv); - if (command == SHORTEN_SHA1S && argc == 1) - return !!transform_todo_ids(1); - if (command == EXPAND_SHA1S && argc == 1) - return !!transform_todo_ids(0); + return !!sequencer_make_script(stdout, argc, argv, flags); + if ((command == SHORTEN_OIDS || command == EXPAND_OIDS) && argc == 1) + return !!transform_todos(flags); if (command == CHECK_TODO_LIST && argc == 1) return !!check_todo_list(); if (command == SKIP_UNNECESSARY_PICKS && argc == 1) return !!skip_unnecessary_picks(); if (command == REARRANGE_SQUASH && argc == 1) return !!rearrange_squash(); + if (command == ADD_EXEC && argc == 2) + return !!sequencer_add_exec_commands(argv[1]); usage_with_options(builtin_rebase_helper_usage, options); } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index cc48767405..c4272fbc96 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "repository.h" #include "config.h" #include "lockfile.h" #include "pack.h" @@ -24,6 +25,7 @@ #include "tmp-objdir.h" #include "oidset.h" #include "packfile.h" +#include "protocol.h" static const char * const receive_pack_usage[] = { N_("git receive-pack <git-dir>"), @@ -68,7 +70,7 @@ static int sent_capabilities; static int shallow_update; static const char *alt_shallow_file; static struct strbuf push_cert = STRBUF_INIT; -static unsigned char push_cert_sha1[20]; +static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; static const char *cert_nonce_seed; @@ -632,8 +634,9 @@ static void prepare_push_cert_sha1(struct child_process *proc) int bogs /* beginning_of_gpg_sig */; already_done = 1; - if (write_sha1_file(push_cert.buf, push_cert.len, "blob", push_cert_sha1)) - hashclr(push_cert_sha1); + if (write_object_file(push_cert.buf, push_cert.len, "blob", + &push_cert_oid)) + oidclr(&push_cert_oid); memset(&sigcheck, '\0', sizeof(sigcheck)); sigcheck.result = 'N'; @@ -654,9 +657,9 @@ static void prepare_push_cert_sha1(struct child_process *proc) strbuf_release(&gpg_status); nonce_status = check_nonce(push_cert.buf, bogs); } - if (!is_null_sha1(push_cert_sha1)) { + if (!is_null_oid(&push_cert_oid)) { argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT=%s", - sha1_to_hex(push_cert_sha1)); + oid_to_hex(&push_cert_oid)); argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s", sigcheck.signer ? sigcheck.signer : ""); argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s", @@ -870,7 +873,7 @@ static void refuse_unconfigured_deny_delete_current(void) rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg)); } -static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]); +static int command_singleton_iterator(void *cb_data, struct object_id *oid); static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { static struct lock_file shallow_lock; @@ -1139,7 +1142,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) } if (ref_transaction_delete(transaction, namespaced_name, - old_oid ? old_oid->hash : NULL, + old_oid, 0, "push", &err)) { rp_error("%s", err.buf); strbuf_release(&err); @@ -1156,7 +1159,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) if (ref_transaction_update(transaction, namespaced_name, - new_oid->hash, old_oid->hash, + new_oid, old_oid, 0, "push", &err)) { rp_error("%s", err.buf); @@ -1240,11 +1243,11 @@ static void check_aliased_update(struct command *cmd, struct string_list *list) rp_error("refusing inconsistent update between symref '%s' (%s..%s) and" " its target '%s' (%s..%s)", cmd->ref_name, - find_unique_abbrev(cmd->old_oid.hash, DEFAULT_ABBREV), - find_unique_abbrev(cmd->new_oid.hash, DEFAULT_ABBREV), + find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV), + find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV), dst_cmd->ref_name, - find_unique_abbrev(dst_cmd->old_oid.hash, DEFAULT_ABBREV), - find_unique_abbrev(dst_cmd->new_oid.hash, DEFAULT_ABBREV)); + find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV), + find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV)); cmd->error_string = dst_cmd->error_string = "inconsistent aliased update"; @@ -1270,7 +1273,7 @@ static void check_aliased_updates(struct command *commands) string_list_clear(&ref_list, 0); } -static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]) +static int command_singleton_iterator(void *cb_data, struct object_id *oid) { struct command **cmd_list = cb_data; struct command *cmd = *cmd_list; @@ -1278,7 +1281,7 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]) if (!cmd || is_null_oid(&cmd->new_oid)) return -1; /* end of list */ *cmd_list = NULL; /* this returns only one */ - hashcpy(sha1, cmd->new_oid.hash); + oidcpy(oid, &cmd->new_oid); return 0; } @@ -1309,7 +1312,7 @@ struct iterate_data { struct shallow_info *si; }; -static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20]) +static int iterate_receive_command_list(void *cb_data, struct object_id *oid) { struct iterate_data *data = cb_data; struct command **cmd_list = &data->cmds; @@ -1320,7 +1323,7 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20]) /* to be checked in update_shallow_ref() */ continue; if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) { - hashcpy(sha1, cmd->new_oid.hash); + oidcpy(oid, &cmd->new_oid); *cmd_list = cmd->next; return 0; } @@ -1776,7 +1779,7 @@ static const char *unpack(int err_fd, struct shallow_info *si) status = finish_command(&child); if (status) return "index-pack abnormal exit"; - reprepare_packed_git(); + reprepare_packed_git(the_repository); } return NULL; } @@ -1961,6 +1964,22 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; + switch (determine_protocol_version_server()) { + case protocol_v1: + /* + * v1 is just the original protocol with a version string, + * so just fall through after writing the version string. + */ + if (advertise_refs || !stateless_rpc) + packet_write_fmt(1, "version 1\n"); + + /* fallthrough */ + case protocol_v0: + break; + case protocol_unknown_version: + BUG("unknown protocol version"); + } + if (advertise_refs || !stateless_rpc) { write_head_info(); } @@ -2009,7 +2028,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) proc.git_cmd = 1; proc.argv = argv_gc_auto; - close_all_packs(); + close_all_packs(the_repository->objects); if (!start_command(&proc)) { if (use_sideband) copy_to_sideband(proc.err, -1, NULL); diff --git a/builtin/reflog.c b/builtin/reflog.c index 2067cca5b1..a89bd1dd25 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -42,7 +42,7 @@ struct expire_reflog_policy_cb { }; struct collected_reflog { - unsigned char sha1[20]; + struct object_id oid; char reflog[FLEX_ARRAY]; }; @@ -52,6 +52,7 @@ struct collect_reflog_cb { int nr; }; +/* Remember to update object flag allocation in object.h */ #define INCOMPLETE (1u<<10) #define STUDYING (1u<<11) #define REACHABLE (1u<<12) @@ -74,7 +75,7 @@ static int tree_is_complete(const struct object_id *oid) if (!tree->buffer) { enum object_type type; unsigned long size; - void *data = read_sha1_file(oid->hash, &type, &size); + void *data = read_object_file(oid, &type, &size); if (!data) { tree->object.flags |= INCOMPLETE; return 0; @@ -289,20 +290,20 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no const char *message, void *cb_data) { struct expire_reflog_policy_cb *cb = cb_data; - struct commit *old, *new; + struct commit *old_commit, *new_commit; if (timestamp < cb->cmd.expire_total) return 1; - old = new = NULL; + old_commit = new_commit = NULL; if (cb->cmd.stalefix && - (!keep_entry(&old, ooid) || !keep_entry(&new, noid))) + (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid))) return 1; if (timestamp < cb->cmd.expire_unreachable) { if (cb->unreachable_expire_kind == UE_ALWAYS) return 1; - if (unreachable(cb, old, ooid) || unreachable(cb, new, noid)) + if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid)) return 1; } @@ -385,7 +386,7 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus struct collect_reflog_cb *cb = cb_data; FLEX_ALLOC_STR(e, reflog, ref); - hashcpy(e->sha1, oid->hash); + oidcpy(&e->oid, oid); ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); cb->e[cb->nr++] = e; return 0; @@ -416,16 +417,6 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) return ent; } -static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire) -{ - if (!value) - return config_error_nonbool(var); - if (parse_expiry_date(value, expire)) - return error(_("'%s' for '%s' is not a valid timestamp"), - value, var); - return 0; -} - /* expiry timer slot */ #define EXPIRE_TOTAL 01 #define EXPIRE_UNREACH 02 @@ -443,11 +434,11 @@ static int reflog_expire_config(const char *var, const char *value, void *cb) if (!strcmp(key, "reflogexpire")) { slot = EXPIRE_TOTAL; - if (parse_expire_cfg_value(var, value, &expire)) + if (git_config_expiry_date(&expire, var, value)) return -1; } else if (!strcmp(key, "reflogexpireunreachable")) { slot = EXPIRE_UNREACH; - if (parse_expire_cfg_value(var, value, &expire)) + if (git_config_expiry_date(&expire, var, value)) return -1; } else return git_default_config(var, value, cb); @@ -589,7 +580,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) for (i = 0; i < collected.nr; i++) { struct collected_reflog *e = collected.e[i]; set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); - status |= reflog_expire(e->reflog, e->sha1, flags, + status |= reflog_expire(e->reflog, &e->oid, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, @@ -601,13 +592,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) for (; i < argc; i++) { char *ref; - unsigned char sha1[20]; - if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) { + struct object_id oid; + if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) { status |= error("%s points nowhere!", argv[i]); continue; } set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref); - status |= reflog_expire(ref, sha1, flags, + status |= reflog_expire(ref, &oid, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, @@ -659,7 +650,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) for ( ; i < argc; i++) { const char *spec = strstr(argv[i], "@{"); - unsigned char sha1[20]; + struct object_id oid; char *ep, *ref; int recno; @@ -668,7 +659,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) continue; } - if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) { + if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) { status |= error("no reflog for '%s'", argv[i]); continue; } @@ -683,7 +674,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) cb.cmd.expire_total = 0; } - status |= reflog_expire(ref, sha1, flags, + status |= reflog_expire(ref, &oid, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, diff --git a/builtin/remote.c b/builtin/remote.c index bc89623695..805ffc05cd 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -168,7 +168,7 @@ static int add(int argc, const char **argv) OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")), { OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"), N_("set up remote as a mirror to push to or fetch from"), - PARSE_OPT_OPTARG, parse_mirror_opt }, + PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt }, OPT_END() }; @@ -322,7 +322,7 @@ static void read_branches(void) struct ref_states { struct remote *remote; - struct string_list new, stale, tracked, heads, push; + struct string_list new_refs, stale, tracked, heads, push; int queried; }; @@ -337,12 +337,12 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat die(_("Could not get fetch map for refspec %s"), states->remote->fetch_refspec[i]); - states->new.strdup_strings = 1; + states->new_refs.strdup_strings = 1; states->tracked.strdup_strings = 1; states->stale.strdup_strings = 1; for (ref = fetch_map; ref; ref = ref->next) { if (!ref->peer_ref || !ref_exists(ref->peer_ref->name)) - string_list_append(&states->new, abbrev_branch(ref->name)); + string_list_append(&states->new_refs, abbrev_branch(ref->name)); else string_list_append(&states->tracked, abbrev_branch(ref->name)); } @@ -356,7 +356,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat free_refs(stale_refs); free_refs(fetch_map); - string_list_sort(&states->new); + string_list_sort(&states->new_refs); string_list_sort(&states->tracked); string_list_sort(&states->stale); @@ -546,8 +546,8 @@ static int add_branch_for_removal(const char *refname, } struct rename_info { - const char *old; - const char *new; + const char *old_name; + const char *new_name; struct string_list *remote_branches; }; @@ -560,7 +560,7 @@ static int read_remote_branches(const char *refname, int flag; const char *symref; - strbuf_addf(&buf, "refs/remotes/%s/", rename->old); + strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name); if (starts_with(refname, buf.buf)) { item = string_list_append(rename->remote_branches, xstrdup(refname)); symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING, @@ -615,36 +615,36 @@ static int mv(int argc, const char **argv) if (argc != 3) usage_with_options(builtin_remote_rename_usage, options); - rename.old = argv[1]; - rename.new = argv[2]; + rename.old_name = argv[1]; + rename.new_name = argv[2]; rename.remote_branches = &remote_branches; - oldremote = remote_get(rename.old); + oldremote = remote_get(rename.old_name); if (!remote_is_configured(oldremote, 1)) - die(_("No such remote: %s"), rename.old); + die(_("No such remote: %s"), rename.old_name); - if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG) + if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG) return migrate_file(oldremote); - newremote = remote_get(rename.new); + newremote = remote_get(rename.new_name); if (remote_is_configured(newremote, 1)) - die(_("remote %s already exists."), rename.new); + die(_("remote %s already exists."), rename.new_name); - strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new); + strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name); if (!valid_fetch_refspec(buf.buf)) - die(_("'%s' is not a valid remote name"), rename.new); + die(_("'%s' is not a valid remote name"), rename.new_name); strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s", rename.old); - strbuf_addf(&buf2, "remote.%s", rename.new); + strbuf_addf(&buf, "remote.%s", rename.old_name); + strbuf_addf(&buf2, "remote.%s", rename.new_name); if (git_config_rename_section(buf.buf, buf2.buf) < 1) return error(_("Could not rename config section '%s' to '%s'"), buf.buf, buf2.buf); strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", rename.new); + strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); git_config_set_multivar(buf.buf, NULL, NULL, 1); - strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old); + strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); for (i = 0; i < oldremote->fetch_refspec_nr; i++) { char *ptr; @@ -655,8 +655,8 @@ static int mv(int argc, const char **argv) refspec_updated = 1; strbuf_splice(&buf2, ptr-buf2.buf + strlen(":refs/remotes/"), - strlen(rename.old), rename.new, - strlen(rename.new)); + strlen(rename.old_name), rename.new_name, + strlen(rename.new_name)); } else warning(_("Not updating non-default fetch refspec\n" "\t%s\n" @@ -670,10 +670,10 @@ static int mv(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote_name && !strcmp(info->remote_name, rename.old)) { + if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); - git_config_set(buf.buf, rename.new); + git_config_set(buf.buf, rename.new_name); } } @@ -690,10 +690,10 @@ static int mv(int argc, const char **argv) int flag = 0; struct object_id oid; - read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag); + read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag); if (!(flag & REF_ISSYMREF)) continue; - if (delete_ref(NULL, item->string, NULL, REF_NODEREF)) + if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF)) die(_("deleting '%s' failed"), item->string); } for (i = 0; i < remote_branches.nr; i++) { @@ -703,8 +703,8 @@ static int mv(int argc, const char **argv) continue; strbuf_reset(&buf); strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old), - rename.new, strlen(rename.new)); + strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), + rename.new_name, strlen(rename.new_name)); strbuf_reset(&buf2); strbuf_addf(&buf2, "remote: renamed %s to %s", item->string, buf.buf); @@ -718,12 +718,12 @@ static int mv(int argc, const char **argv) continue; strbuf_reset(&buf); strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old), - rename.new, strlen(rename.new)); + strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), + rename.new_name, strlen(rename.new_name)); strbuf_reset(&buf2); strbuf_addstr(&buf2, item->util); - strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old), - rename.new, strlen(rename.new)); + strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name), + rename.new_name, strlen(rename.new_name)); strbuf_reset(&buf3); strbuf_addf(&buf3, "remote: renamed %s to %s", item->string, buf.buf); @@ -788,7 +788,7 @@ static int rm(int argc, const char **argv) strbuf_release(&buf); if (!result) - result = delete_refs("remote: remove", &branches, REF_NODEREF); + result = delete_refs("remote: remove", &branches, REF_NO_DEREF); string_list_clear(&branches, 0); if (skipped.nr) { @@ -822,7 +822,7 @@ static void clear_push_info(void *util, const char *string) static void free_remote_ref_states(struct ref_states *states) { - string_list_clear(&states->new, 0); + string_list_clear(&states->new_refs, 0); string_list_clear(&states->stale, 1); string_list_clear(&states->tracked, 0); string_list_clear(&states->heads, 0); @@ -907,7 +907,7 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data) if (states->queried) { const char *fmt = "%s"; const char *arg = ""; - if (string_list_has_string(&states->new, name)) { + if (string_list_has_string(&states->new_refs, name)) { fmt = _(" new (next fetch will store in remotes/%s)"); arg = states->remote->name; } else if (string_list_has_string(&states->tracked, name)) @@ -1176,7 +1176,7 @@ static int show(int argc, const char **argv) /* remote branch info */ info.width = 0; - for_each_string_list(&states.new, add_remote_to_show_info, &info); + for_each_string_list(&states.new_refs, add_remote_to_show_info, &info); for_each_string_list(&states.tracked, add_remote_to_show_info, &info); for_each_string_list(&states.stale, add_remote_to_show_info, &info); if (info.list->nr) @@ -1255,7 +1255,7 @@ static int set_head(int argc, const char **argv) head_name = xstrdup(states.heads.items[0].string); free_remote_ref_states(&states); } else if (opt_d && !opt_a && argc == 1) { - if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF)) + if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF)) result |= error(_("Could not delete %s"), buf.buf); } else usage_with_options(builtin_remote_sethead_usage, options); diff --git a/builtin/repack.c b/builtin/repack.c index f17a68a17d..7bdb40142f 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -83,7 +83,8 @@ static void remove_pack_on_signal(int signo) /* * Adds all packs hex strings to the fname list, which do not - * have a corresponding .keep file. + * have a corresponding .keep or .promisor file. These packs are not to + * be kept if we are going to pack everything into one file. */ static void get_non_kept_pack_filenames(struct string_list *fname_list) { @@ -101,7 +102,8 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list) fname = xmemdupz(e->d_name, len); - if (!file_exists(mkpath("%s/%s.keep", packdir, fname))) + if (!file_exists(mkpath("%s/%s.keep", packdir, fname)) && + !file_exists(mkpath("%s/%s.promisor", packdir, fname))) string_list_append_nodup(fname_list, fname); else free(fname); @@ -232,6 +234,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd.args, "--all"); argv_array_push(&cmd.args, "--reflog"); argv_array_push(&cmd.args, "--indexed-objects"); + if (repository_format_partial_clone) + argv_array_push(&cmd.args, "--exclude-promisor-objects"); if (window) argv_array_pushf(&cmd.args, "--window=%s", window); if (window_memory) diff --git a/builtin/replace.c b/builtin/replace.c index 3e71a77152..935647be6b 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -53,11 +53,11 @@ static int show_reference(const char *refname, const struct object_id *oid, if (get_oid(refname, &object)) return error("Failed to resolve '%s' as a valid ref.", refname); - obj_type = sha1_object_info(object.hash, NULL); - repl_type = sha1_object_info(oid->hash, NULL); + obj_type = oid_object_info(&object, NULL); + repl_type = oid_object_info(oid, NULL); - printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type), - oid_to_hex(oid), typename(repl_type)); + printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type), + oid_to_hex(oid), type_name(repl_type)); } } @@ -113,7 +113,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) strbuf_addstr(&ref, oid_to_hex(&oid)); full_hex = ref.buf + base_len; - if (read_ref(ref.buf, oid.hash)) { + if (read_ref(ref.buf, &oid)) { error("replace ref '%s' not found.", full_hex); had_error = 1; continue; @@ -128,7 +128,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) static int delete_replace_ref(const char *name, const char *ref, const struct object_id *oid) { - if (delete_ref(NULL, ref, oid->hash, 0)) + if (delete_ref(NULL, ref, oid, 0)) return 1; printf("Deleted replace ref '%s'\n", name); return 0; @@ -144,7 +144,7 @@ static void check_ref_valid(struct object_id *object, if (check_refname_format(ref->buf, 0)) die("'%s' is not a valid ref name.", ref->buf); - if (read_ref(ref->buf, prev->hash)) + if (read_ref(ref->buf, prev)) oidclr(prev); else if (!force) die("replace ref '%s' already exists", ref->buf); @@ -162,20 +162,20 @@ static int replace_object_oid(const char *object_ref, struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; - obj_type = sha1_object_info(object->hash, NULL); - repl_type = sha1_object_info(repl->hash, NULL); + obj_type = oid_object_info(object, NULL); + repl_type = oid_object_info(repl, NULL); if (!force && obj_type != repl_type) die("Objects must be of the same type.\n" "'%s' points to a replaced object of type '%s'\n" "while '%s' points to a replacement object of type '%s'.", - object_ref, typename(obj_type), - replace_ref, typename(repl_type)); + object_ref, type_name(obj_type), + replace_ref, type_name(repl_type)); check_ref_valid(object, &prev, &ref, force); transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, ref.buf, repl->hash, prev.hash, + ref_transaction_update(transaction, ref.buf, repl, &prev, 0, NULL, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); @@ -215,7 +215,7 @@ static void export_object(const struct object_id *oid, enum object_type type, argv_array_push(&cmd.args, "--no-replace-objects"); argv_array_push(&cmd.args, "cat-file"); if (raw) - argv_array_push(&cmd.args, typename(type)); + argv_array_push(&cmd.args, type_name(type)); else argv_array_push(&cmd.args, "-p"); argv_array_push(&cmd.args, oid_to_hex(oid)); @@ -284,30 +284,30 @@ static int edit_and_replace(const char *object_ref, int force, int raw) { char *tmpfile = git_pathdup("REPLACE_EDITOBJ"); enum object_type type; - struct object_id old, new, prev; + struct object_id old_oid, new_oid, prev; struct strbuf ref = STRBUF_INIT; - if (get_oid(object_ref, &old) < 0) + if (get_oid(object_ref, &old_oid) < 0) die("Not a valid object name: '%s'", object_ref); - type = sha1_object_info(old.hash, NULL); + type = oid_object_info(&old_oid, NULL); if (type < 0) - die("unable to get object type for %s", oid_to_hex(&old)); + die("unable to get object type for %s", oid_to_hex(&old_oid)); - check_ref_valid(&old, &prev, &ref, force); + check_ref_valid(&old_oid, &prev, &ref, force); strbuf_release(&ref); - export_object(&old, type, raw, tmpfile); + export_object(&old_oid, type, raw, tmpfile); if (launch_editor(tmpfile, NULL, NULL) < 0) die("editing object file failed"); - import_object(&new, type, raw, tmpfile); + import_object(&new_oid, type, raw, tmpfile); free(tmpfile); - if (!oidcmp(&old, &new)) - return error("new object is the same as the old one: '%s'", oid_to_hex(&old)); + if (!oidcmp(&old_oid, &new_oid)) + return error("new object is the same as the old one: '%s'", oid_to_hex(&old_oid)); - return replace_object_oid(object_ref, &old, "replacement", &new, force); + return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force); } static void replace_parents(struct strbuf *buf, int argc, const char **argv) @@ -355,7 +355,7 @@ static void check_one_mergetag(struct commit *commit, struct tag *tag; int i; - hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash); + hash_object_file(extra->value, extra->len, type_name(OBJ_TAG), &tag_oid); tag = lookup_tag(&tag_oid); if (!tag) die(_("bad mergetag in commit '%s'"), ref); @@ -386,16 +386,16 @@ static void check_mergetags(struct commit *commit, int argc, const char **argv) static int create_graft(int argc, const char **argv, int force) { - struct object_id old, new; + struct object_id old_oid, new_oid; const char *old_ref = argv[0]; struct commit *commit; struct strbuf buf = STRBUF_INIT; const char *buffer; unsigned long size; - if (get_oid(old_ref, &old) < 0) + if (get_oid(old_ref, &old_oid) < 0) die(_("Not a valid object name: '%s'"), old_ref); - commit = lookup_commit_or_die(&old, old_ref); + commit = lookup_commit_or_die(&old_oid, old_ref); buffer = get_commit_buffer(commit, &size); strbuf_add(&buf, buffer, size); @@ -410,15 +410,15 @@ static int create_graft(int argc, const char **argv, int force) check_mergetags(commit, argc, argv); - if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash)) + if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) die(_("could not write replacement commit for: '%s'"), old_ref); strbuf_release(&buf); - if (!oidcmp(&old, &new)) - return error("new commit is the same as the old one: '%s'", oid_to_hex(&old)); + if (!oidcmp(&old_oid, &new_oid)) + return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid)); - return replace_object_oid(old_ref, &old, "replacement", &new, force); + return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force); } int cmd_replace(int argc, const char **argv, const char *prefix) @@ -439,7 +439,8 @@ int cmd_replace(int argc, const char **argv, const char *prefix) OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE), OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT), OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT), - OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")), + OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"), + PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")), OPT_STRING(0, "format", &format, N_("format"), N_("use this format")), OPT_END() diff --git a/builtin/reset.c b/builtin/reset.c index 9cd89b2305..7f1c3f02a3 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -12,7 +12,7 @@ #include "lockfile.h" #include "tag.h" #include "object.h" -#include "commit.h" +#include "pretty.h" #include "run-command.h" #include "refs.h" #include "diff.h" @@ -106,24 +106,16 @@ out: static void print_new_head_line(struct commit *commit) { - const char *hex, *body; - const char *msg; - - hex = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV); - printf(_("HEAD is now at %s"), hex); - msg = logmsg_reencode(commit, NULL, get_log_output_encoding()); - body = strstr(msg, "\n\n"); - if (body) { - const char *eol; - size_t len; - body = skip_blank_lines(body + 2); - eol = strchr(body, '\n'); - len = eol ? eol - body : strlen(body); - printf(" %.*s\n", (int) len, body); - } - else - printf("\n"); - unuse_commit_buffer(commit, msg); + struct strbuf buf = STRBUF_INIT; + + printf(_("HEAD is now at %s"), + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + + pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); + if (buf.len > 0) + printf(" %s", buf.buf); + putchar('\n'); + strbuf_release(&buf); } static void update_index_from_diff(struct diff_queue_struct *q, @@ -166,7 +158,7 @@ static int read_from_tree(const struct pathspec *pathspec, opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; opt.format_callback_data = &intent_to_add; - opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG; + opt.flags.override_submodule_config = 1; if (do_diff_cache(tree_oid, &opt)) return 1; @@ -266,12 +258,12 @@ static int reset_refs(const char *rev, const struct object_id *oid) if (!get_oid("HEAD", &oid_orig)) { orig = &oid_orig; set_reflog_message(&msg, "updating ORIG_HEAD", NULL); - update_ref_oid(msg.buf, "ORIG_HEAD", orig, old_orig, 0, + update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, UPDATE_REFS_MSG_ON_ERR); } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig->hash, 0); + delete_ref(NULL, "ORIG_HEAD", old_orig, 0); set_reflog_message(&msg, "updating HEAD", rev); - update_ref_status = update_ref_oid(msg.buf, "HEAD", oid, orig, 0, + update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0, UPDATE_REFS_MSG_ON_ERR); strbuf_release(&msg); return update_ref_status; diff --git a/builtin/rev-list.c b/builtin/rev-list.c index fb1c36af6a..fadd3ec14c 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -4,6 +4,8 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "list-objects-filter.h" +#include "list-objects-filter-options.h" #include "pack.h" #include "pack-bitmap.h" #include "builtin.h" @@ -12,6 +14,8 @@ #include "bisect.h" #include "progress.h" #include "reflog-walk.h" +#include "oidset.h" +#include "packfile.h" static const char rev_list_usage[] = "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n" @@ -55,6 +59,21 @@ static const char rev_list_usage[] = static struct progress *progress; static unsigned progress_counter; +static struct list_objects_filter_options filter_options; +static struct oidset omitted_objects; +static int arg_print_omitted; /* print objects omitted by filter */ + +static struct oidset missing_objects; +enum missing_action { + MA_ERROR = 0, /* fail if any missing objects are encountered */ + MA_ALLOW_ANY, /* silently allow ALL missing objects */ + MA_PRINT, /* print ALL missing objects in special section */ + MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */ +}; +static enum missing_action arg_missing_action; + +#define DEFAULT_OIDSET_SIZE (16*1024) + static void finish_commit(struct commit *commit, void *data); static void show_commit(struct commit *commit, void *data) { @@ -89,7 +108,7 @@ static void show_commit(struct commit *commit, void *data) if (!revs->graph) fputs(get_revision_mark(revs, commit), stdout); if (revs->abbrev_commit && revs->abbrev) - fputs(find_unique_abbrev(commit->object.oid.hash, revs->abbrev), + fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev), stdout); else fputs(oid_to_hex(&commit->object.oid), stdout); @@ -115,7 +134,7 @@ static void show_commit(struct commit *commit, void *data) else putchar('\n'); - if (revs->verbose_header && get_cached_commit_buffer(commit, NULL)) { + if (revs->verbose_header) { struct strbuf buf = STRBUF_INIT; struct pretty_print_context ctx = {0}; ctx.abbrev = revs->abbrev; @@ -178,19 +197,56 @@ static void finish_commit(struct commit *commit, void *data) free_commit_buffer(commit); } -static void finish_object(struct object *obj, const char *name, void *cb_data) +static inline void finish_object__ma(struct object *obj) { - struct rev_list_info *info = cb_data; - if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) + /* + * Whether or not we try to dynamically fetch missing objects + * from the server, we currently DO NOT have the object. We + * can either print, allow (ignore), or conditionally allow + * (ignore) them. + */ + switch (arg_missing_action) { + case MA_ERROR: die("missing blob object '%s'", oid_to_hex(&obj->oid)); + return; + + case MA_ALLOW_ANY: + return; + + case MA_PRINT: + oidset_insert(&missing_objects, &obj->oid); + return; + + case MA_ALLOW_PROMISOR: + if (is_promisor_object(&obj->oid)) + return; + die("unexpected missing blob object '%s'", + oid_to_hex(&obj->oid)); + return; + + default: + BUG("unhandled missing_action"); + return; + } +} + +static int finish_object(struct object *obj, const char *name, void *cb_data) +{ + struct rev_list_info *info = cb_data; + if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) { + finish_object__ma(obj); + return 1; + } if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT) parse_object(&obj->oid); + return 0; } static void show_object(struct object *obj, const char *name, void *cb_data) { struct rev_list_info *info = cb_data; - finish_object(obj, name, cb_data); + if (finish_object(obj, name, cb_data)) + return; display_progress(progress, ++progress_counter); if (info->flags & REV_LIST_QUIET) return; @@ -258,17 +314,45 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all) } static int show_object_fast( - const unsigned char *sha1, + const struct object_id *oid, enum object_type type, int exclude, uint32_t name_hash, struct packed_git *found_pack, off_t found_offset) { - fprintf(stdout, "%s\n", sha1_to_hex(sha1)); + fprintf(stdout, "%s\n", oid_to_hex(oid)); return 1; } +static inline int parse_missing_action_value(const char *value) +{ + if (!strcmp(value, "error")) { + arg_missing_action = MA_ERROR; + return 1; + } + + if (!strcmp(value, "allow-any")) { + arg_missing_action = MA_ALLOW_ANY; + fetch_if_missing = 0; + return 1; + } + + if (!strcmp(value, "print")) { + arg_missing_action = MA_PRINT; + fetch_if_missing = 0; + return 1; + } + + if (!strcmp(value, "allow-promisor")) { + arg_missing_action = MA_ALLOW_PROMISOR; + fetch_if_missing = 0; + return 1; + } + + return 0; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -287,6 +371,35 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) init_revisions(&revs, prefix); revs.abbrev = DEFAULT_ABBREV; revs.commit_format = CMIT_FMT_UNSPECIFIED; + + /* + * Scan the argument list before invoking setup_revisions(), so that we + * know if fetch_if_missing needs to be set to 0. + * + * "--exclude-promisor-objects" acts as a pre-filter on missing objects + * by not crossing the boundary from realized objects to promisor + * objects. + * + * Let "--missing" to conditionally set fetch_if_missing. + */ + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(arg, "--exclude-promisor-objects")) { + fetch_if_missing = 0; + revs.exclude_promisor_objects = 1; + break; + } + } + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (skip_prefix(arg, "--missing=", &arg)) { + if (revs.exclude_promisor_objects) + die(_("cannot combine --exclude-promisor-objects and --missing")); + if (parse_missing_action_value(arg)) + break; + } + } + argc = setup_revisions(argc, argv, &revs, NULL); memset(&info, 0, sizeof(info)); @@ -294,7 +407,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (revs.bisect) bisect_list = 1; - if (DIFF_OPT_TST(&revs.diffopt, QUICK)) + if (revs.diffopt.flags.quick) info.flags |= REV_LIST_QUIET; for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; @@ -335,6 +448,31 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) show_progress = arg; continue; } + + if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { + parse_list_objects_filter(&filter_options, arg); + if (filter_options.choice && !revs.blob_objects) + die(_("object filtering requires --objects")); + if (filter_options.choice == LOFC_SPARSE_OID && + !filter_options.sparse_oid_value) + die(_("invalid sparse value '%s'"), + filter_options.filter_spec); + continue; + } + if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { + list_objects_filter_set_no_filter(&filter_options); + continue; + } + if (!strcmp(arg, "--filter-print-omitted")) { + arg_print_omitted = 1; + continue; + } + + if (!strcmp(arg, "--exclude-promisor-objects")) + continue; /* already handled above */ + if (skip_prefix(arg, "--missing=", &arg)) + continue; /* already handled above */ + usage(rev_list_usage); } @@ -360,6 +498,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (revs.show_notes) die(_("rev-list does not support display of notes")); + if (filter_options.choice && use_bitmap_index) + die(_("cannot combine --use-bitmap-index with object filtering")); + save_commit_buffer = (revs.verbose_header || revs.grep_filter.pattern_list || revs.grep_filter.header_list); @@ -395,7 +536,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) mark_edges_uninteresting(&revs, show_edge); if (bisect_list) { - int reaches = reaches, all = all; + int reaches, all; find_bisection(&revs.commits, &reaches, &all, bisect_find_all); @@ -403,7 +544,31 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) return show_bisect_vars(&info, reaches, all); } - traverse_commit_list(&revs, show_commit, show_object, &info); + if (arg_print_omitted) + oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE); + if (arg_missing_action == MA_PRINT) + oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE); + + traverse_commit_list_filtered( + &filter_options, &revs, show_commit, show_object, &info, + (arg_print_omitted ? &omitted_objects : NULL)); + + if (arg_print_omitted) { + struct oidset_iter iter; + struct object_id *oid; + oidset_iter_init(&omitted_objects, &iter); + while ((oid = oidset_iter_next(&iter))) + printf("~%s\n", oid_to_hex(oid)); + oidset_clear(&omitted_objects); + } + if (arg_missing_action == MA_PRINT) { + struct oidset_iter iter; + struct object_id *oid; + oidset_iter_init(&missing_objects, &iter); + while ((oid = oidset_iter_next(&iter))) + printf("?%s\n", oid_to_hex(oid)); + oidset_clear(&missing_objects); + } stop_progress(&progress); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index a8d7e6f7ae..36b2087782 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -133,7 +133,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name) struct object_id discard; char *full; - switch (dwim_ref(name, strlen(name), discard.hash, &full)) { + switch (dwim_ref(name, strlen(name), &discard, &full)) { case 0: /* * Not found -- not a ref. We could @@ -159,7 +159,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name) } } else if (abbrev) - show_with_type(type, find_unique_abbrev(oid->hash, abbrev)); + show_with_type(type, find_unique_abbrev(oid, abbrev)); else show_with_type(type, oid_to_hex(oid)); } @@ -243,28 +243,28 @@ static int show_file(const char *arg, int output_prefix) static int try_difference(const char *arg) { char *dotdot; - struct object_id oid; - struct object_id end; - const char *next; - const char *this; + struct object_id start_oid; + struct object_id end_oid; + const char *end; + const char *start; int symmetric; static const char head_by_default[] = "HEAD"; if (!(dotdot = strstr(arg, ".."))) return 0; - next = dotdot + 2; - this = arg; - symmetric = (*next == '.'); + end = dotdot + 2; + start = arg; + symmetric = (*end == '.'); *dotdot = 0; - next += symmetric; + end += symmetric; - if (!*next) - next = head_by_default; + if (!*end) + end = head_by_default; if (dotdot == arg) - this = head_by_default; + start = head_by_default; - if (this == head_by_default && next == head_by_default && + if (start == head_by_default && end == head_by_default && !symmetric) { /* * Just ".."? That is not a range but the @@ -274,14 +274,14 @@ static int try_difference(const char *arg) return 0; } - if (!get_oid_committish(this, &oid) && !get_oid_committish(next, &end)) { - show_rev(NORMAL, &end, next); - show_rev(symmetric ? NORMAL : REVERSED, &oid, this); + if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) { + show_rev(NORMAL, &end_oid, end); + show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); if (symmetric) { struct commit_list *exclude; struct commit *a, *b; - a = lookup_commit_reference(&oid); - b = lookup_commit_reference(&end); + a = lookup_commit_reference(&start_oid); + b = lookup_commit_reference(&end_oid); exclude = get_merge_bases(a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); @@ -516,7 +516,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) PARSE_OPT_SHELL_EVAL); strbuf_addstr(&parsed, " --"); - sq_quote_argv(&parsed, argv, 0); + sq_quote_argv(&parsed, argv); puts(parsed.buf); return 0; } @@ -526,7 +526,7 @@ static int cmd_sq_quote(int argc, const char **argv) struct strbuf buf = STRBUF_INIT; if (argc) - sq_quote_argv(&buf, argv, 0); + sq_quote_argv(&buf, argv); printf("%s\n", buf.buf); strbuf_release(&buf); diff --git a/builtin/revert.c b/builtin/revert.c index b9d927eb09..76f0a35b07 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -208,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix) if (isatty(0)) opts.edit = 1; opts.action = REPLAY_REVERT; - git_config(git_default_config, NULL); + sequencer_init_config(&opts); res = run_sequencer(argc, argv, &opts); if (res < 0) die(_("revert failed")); @@ -221,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix) int res; opts.action = REPLAY_PICK; - git_config(git_default_config, NULL); + sequencer_init_config(&opts); res = run_sequencer(argc, argv, &opts); if (res < 0) die(_("cherry-pick failed")); diff --git a/builtin/rm.c b/builtin/rm.c index d91451fea1..5b6fc7ee81 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -178,7 +178,7 @@ static int check_local_mod(struct object_id *head, int index_only) * way as changed from the HEAD. */ if (no_head - || get_tree_entry(head->hash, name, oid.hash, &mode) + || get_tree_entry(head, name, &oid, &mode) || ce->ce_mode != create_ce_mode(mode) || oidcmp(&ce->oid, &oid)) staged_changes = 1; @@ -242,7 +242,7 @@ static struct option builtin_rm_options[] = { OPT__DRY_RUN(&show_only, N_("dry run")), OPT__QUIET(&quiet, N_("do not list removed files")), OPT_BOOL( 0 , "cached", &index_only, N_("only remove from the index")), - OPT__FORCE(&force, N_("override the up-to-date check")), + OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE), OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")), OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch, N_("exit with a zero status even if nothing matched")), @@ -382,13 +382,12 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } strbuf_release(&buf); if (gitmodules_modified) - stage_updated_gitmodules(); + stage_updated_gitmodules(&the_index); } - if (active_cache_changed) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("Unable to write new index file")); - } + if (write_locked_index(&the_index, &lock_file, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) + die(_("Unable to write new index file")); return 0; } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index e29875b843..608d6ba77b 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -11,7 +11,8 @@ #include "parse-options.h" static char const * const shortlog_usage[] = { - N_("git shortlog [<options>] [<revision-range>] [[--] [<path>...]]"), + N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"), + N_("git log --pretty=short | git shortlog [<options>]"), NULL }; @@ -283,6 +284,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) for (;;) { switch (parse_options_step(&ctx, options, shortlog_usage)) { case PARSE_OPT_HELP: + case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_DONE: goto parse_done; @@ -292,6 +294,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) parse_done: argc = parse_options_end(&ctx); + if (nongit && argc > 1) { + error(_("too many arguments given outside repository")); + usage_with_options(shortlog_usage, options); + } + if (setup_revisions(argc, argv, &rev, NULL) != 1) { error(_("unrecognized argument: %s"), argv[1]); usage_with_options(shortlog_usage, options); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 6fa1f62a88..6c2148b71d 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -1,6 +1,6 @@ #include "cache.h" #include "config.h" -#include "commit.h" +#include "pretty.h" #include "refs.h" #include "builtin.h" #include "color.h" @@ -292,7 +292,7 @@ static void show_one_commit(struct commit *commit, int no_name) } else printf("[%s] ", - find_unique_abbrev(commit->object.oid.hash, + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); } puts(pretty_str); @@ -705,8 +705,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) static const char *fake_av[2]; fake_av[0] = resolve_refdup("HEAD", - RESOLVE_REF_READING, - oid.hash, NULL); + RESOLVE_REF_READING, &oid, + NULL); fake_av[1] = NULL; av = fake_av; ac = 1; @@ -720,7 +720,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) die(Q_("only %d entry can be shown at one time.", "only %d entries can be shown at one time.", MAX_REVS), MAX_REVS); - if (!dwim_ref(*av, strlen(*av), oid.hash, &ref)) + if (!dwim_ref(*av, strlen(*av), &oid, &ref)) die(_("no such ref %s"), *av); /* Has the base been specified? */ @@ -731,7 +731,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) /* Ah, that is a date spec... */ timestamp_t at; at = approxidate(reflog_base); - read_ref_at(ref, flags, at, -1, oid.hash, NULL, + read_ref_at(ref, flags, at, -1, &oid, NULL, NULL, NULL, &base); } } @@ -743,7 +743,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) timestamp_t timestamp; int tz; - if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg, + if (read_ref_at(ref, flags, 0, base + i, &oid, &logmsg, ×tamp, &tz, NULL)) { reflog = i; break; @@ -775,7 +775,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) } head = resolve_refdup("HEAD", RESOLVE_REF_READING, - head_oid.hash, NULL); + &head_oid, NULL); if (with_current_branch && head) { int has_head = 0; diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 013d241abc..f2eb1a7724 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -29,7 +29,7 @@ static void show_one(const char *refname, const struct object_id *oid) if (quiet) return; - hex = find_unique_abbrev(oid->hash, abbrev); + hex = find_unique_abbrev(oid, abbrev); if (hash_only) printf("%s\n", hex); else @@ -38,8 +38,8 @@ static void show_one(const char *refname, const struct object_id *oid) if (!deref_tags) return; - if (!peel_ref(refname, peeled.hash)) { - hex = find_unique_abbrev(peeled.hash, abbrev); + if (!peel_ref(refname, &peeled)) { + hex = find_unique_abbrev(&peeled, abbrev); printf("%s %s^{}\n", hex, refname); } } @@ -197,7 +197,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) struct object_id oid; if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) && - !read_ref(*pattern, oid.hash)) { + !read_ref(*pattern, &oid)) { show_one(*pattern, &oid); } else if (!quiet) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 06ed02f994..a404df3ea4 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,6 +13,18 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" +#include "object-store.h" + +#define OPT_QUIET (1 << 0) +#define OPT_CACHED (1 << 1) +#define OPT_RECURSIVE (1 << 2) +#define OPT_FORCE (1 << 3) + +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); static char *get_default_remote(void) { @@ -40,6 +52,20 @@ static char *get_default_remote(void) return ret; } +static int print_default_remote(int argc, const char **argv, const char *prefix) +{ + const char *remote; + + if (argc != 1) + die(_("submodule--helper print-default-remote takes no arguments")); + + remote = get_default_remote(); + if (remote) + printf("%s\n", remote); + + return 0; +} + static int starts_with_dot_slash(const char *str) { return str[0] == '.' && is_dir_sep(str[1]); @@ -219,6 +245,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, &sb)); + strbuf_release(&sb); + return displaypath; + } else if (super_prefix) { + return xstrfmt("%s%s", super_prefix, path); + } else { + return xstrdup(path); + } +} + +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { NULL }; + + static const char *describe_tags[] = { "--tags", NULL }; + + static const char *describe_contains[] = { "--contains", NULL }; + + static const char *describe_all_always[] = { "--all", "--always", NULL }; + + static const char **describe_argv[] = { describe_bare, describe_tags, + describe_contains, + describe_all_always, NULL }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(&cp.env_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(&cp.args, "describe"); + argv_array_pushv(&cp.args, *d); + argv_array_push(&cp.args, object_id); + + if (!capture_command(&cp, &sb, 0)) { + strbuf_strip_suffix(&sb, "\n"); + return strbuf_detach(&sb, NULL); + } + } + + strbuf_release(&sb); + return NULL; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -290,6 +374,25 @@ static void module_list_active(struct module_list *list) *list = active_modules; } +static char *get_up_path(const char *path) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + for (i = count_slashes(path); i; i--) + strbuf_addstr(&sb, "../"); + + /* + * Check if 'path' ends with slash or not + * for having the same output for dir/sub_dir + * and dir/sub_dir/ + */ + if (!is_dir_sep(path[strlen(path) - 1])) + strbuf_addstr(&sb, "../"); + + return strbuf_detach(&sb, NULL); +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -328,21 +431,29 @@ static int module_list(int argc, const char **argv, const char *prefix) return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int flags; +}; + +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const char *path, const char *prefix, + unsigned int flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, &sb)); - else if (get_super_prefix()) { - strbuf_addf(&sb, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(&sb, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(&null_oid, path); @@ -357,9 +468,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(&sb); } /* @@ -367,7 +478,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, &url)) { if (!sub->url) @@ -399,14 +509,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!(flags & OPT_QUIET)) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(&sb); /* Copy "update" setting when it is not set yet */ - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, &upd) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { @@ -426,12 +536,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet) free(upd); } +static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; + init_submodule(list_item->name, info->prefix, info->flags); +} + static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")), @@ -456,8 +572,169 @@ static int module_init(int argc, const char **argv, const char *prefix) if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(&list); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(&list, init_submodule_cb, &info); + + return 0; +} + +struct status_cb { + const char *prefix; + unsigned int flags; +}; + +#define STATUS_CB_INIT { NULL, 0 } + +static void print_status(unsigned int flags, char state, const char *path, + const struct object_id *oid, const char *displaypath) +{ + if (flags & OPT_QUIET) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + + printf("\n"); +} + +static int handle_submodule_head_ref(const char *refname, + const struct object_id *oid, int flags, + void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const char *path, const struct object_id *ce_oid, + unsigned int ce_flags, const char *prefix, + unsigned int flags) +{ + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + struct rev_info rev; + int diff_files_result; + + if (!submodule_from_path(&null_oid, path)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); + + displaypath = get_submodule_displaypath(path, prefix); + + if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) { + print_status(flags, 'U', path, &null_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, path)) { + print_status(flags, '-', path, ce_oid, displaypath); + goto cleanup; + } + + argv_array_pushl(&diff_files_args, "diff-files", + "--ignore-submodules=dirty", "--quiet", "--", + path, NULL); + + git_config(git_diff_basic_config, NULL); + init_revisions(&rev, prefix); + rev.abbrev = 0; + diff_files_args.argc = setup_revisions(diff_files_args.argc, + diff_files_args.argv, + &rev, NULL); + diff_files_result = run_diff_files(&rev, 0); + + if (!diff_result_code(&rev.diffopt, diff_files_result)) { + print_status(flags, ' ', path, ce_oid, + displaypath); + } else if (!(flags & OPT_CACHED)) { + struct object_id oid; + struct ref_store *refs = get_submodule_ref_store(path); + + if (!refs) { + print_status(flags, '-', path, ce_oid, displaypath); + goto cleanup; + } + if (refs_head_ref(refs, handle_submodule_head_ref, &oid)) + die(_("could not resolve HEAD ref inside the " + "submodule '%s'"), path); + + print_status(flags, '+', path, &oid, displaypath); + } else { + print_status(flags, '+', path, ce_oid, displaypath); + } + + if (flags & OPT_RECURSIVE) { + struct child_process cpr = CHILD_PROCESS_INIT; + + cpr.git_cmd = 1; + cpr.dir = path; + prepare_submodule_repo_env(&cpr.env_array); + + argv_array_push(&cpr.args, "--super-prefix"); + argv_array_pushf(&cpr.args, "%s/", displaypath); + argv_array_pushl(&cpr.args, "submodule--helper", "status", + "--recursive", NULL); + + if (flags & OPT_CACHED) + argv_array_push(&cpr.args, "--cached"); + + if (flags & OPT_QUIET) + argv_array_push(&cpr.args, "--quiet"); + + if (run_command(&cpr)) + die(_("failed to recurse into submodule '%s'"), path); + } + +cleanup: + argv_array_clear(&diff_files_args); + free(displaypath); +} + +static void status_submodule_cb(const struct cache_entry *list_item, + void *cb_data) +{ + struct status_cb *info = cb_data; + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, + info->prefix, info->flags); +} + +static int module_status(int argc, const char **argv, const char *prefix) +{ + struct status_cb info = STATUS_CB_INIT; + struct pathspec pathspec; + struct module_list list = MODULE_LIST_INIT; + int quiet = 0; + + struct option module_status_options[] = { + OPT__QUIET(&quiet, N_("Suppress submodule status output")), + OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), + OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"), + NULL + }; + + argc = parse_options(argc, argv, prefix, module_status_options, + git_submodule_helper_usage, 0); + + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) + return 1; + + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(&list, status_submodule_cb, &info); return 0; } @@ -480,6 +757,309 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct sync_cb { + const char *prefix; + unsigned int flags; +}; + +#define SYNC_CB_INIT { NULL, 0 } + +static void sync_submodule(const char *path, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + char *remote_key = NULL; + char *sub_origin_url, *super_config_url, *displaypath; + struct strbuf sb = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + char *sub_config_path = NULL; + + if (!is_submodule_active(the_repository, path)) + return; + + sub = submodule_from_path(&null_oid, path); + + if (sub && sub->url) { + if (starts_with_dot_dot_slash(sub->url) || + starts_with_dot_slash(sub->url)) { + char *remote_url, *up_path; + char *remote = get_default_remote(); + strbuf_addf(&sb, "remote.%s.url", remote); + + if (git_config_get_string(sb.buf, &remote_url)) + remote_url = xgetcwd(); + + up_path = get_up_path(path); + sub_origin_url = relative_url(remote_url, sub->url, up_path); + super_config_url = relative_url(remote_url, sub->url, NULL); + + free(remote); + free(up_path); + free(remote_url); + } else { + sub_origin_url = xstrdup(sub->url); + super_config_url = xstrdup(sub->url); + } + } else { + sub_origin_url = xstrdup(""); + super_config_url = xstrdup(""); + } + + displaypath = get_submodule_displaypath(path, prefix); + + if (!(flags & OPT_QUIET)) + printf(_("Synchronizing submodule url for '%s'\n"), + displaypath); + + strbuf_reset(&sb); + strbuf_addf(&sb, "submodule.%s.url", sub->name); + if (git_config_set_gently(sb.buf, super_config_url)) + die(_("failed to register url for submodule path '%s'"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(&cp.env_array); + cp.git_cmd = 1; + cp.dir = path; + argv_array_pushl(&cp.args, "submodule--helper", + "print-default-remote", NULL); + + strbuf_reset(&sb); + if (capture_command(&cp, &sb, 0)) + die(_("failed to get the default remote for submodule '%s'"), + path); + + strbuf_strip_suffix(&sb, "\n"); + remote_key = xstrfmt("remote.%s.url", sb.buf); + + strbuf_reset(&sb); + submodule_to_gitdir(&sb, path); + strbuf_addstr(&sb, "/config"); + + if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url)) + die(_("failed to update remote for submodule '%s'"), + path); + + if (flags & OPT_RECURSIVE) { + struct child_process cpr = CHILD_PROCESS_INIT; + + cpr.git_cmd = 1; + cpr.dir = path; + prepare_submodule_repo_env(&cpr.env_array); + + argv_array_push(&cpr.args, "--super-prefix"); + argv_array_pushf(&cpr.args, "%s/", displaypath); + argv_array_pushl(&cpr.args, "submodule--helper", "sync", + "--recursive", NULL); + + if (flags & OPT_QUIET) + argv_array_push(&cpr.args, "--quiet"); + + if (run_command(&cpr)) + die(_("failed to recurse into submodule '%s'"), + path); + } + +cleanup: + free(super_config_url); + free(sub_origin_url); + strbuf_release(&sb); + free(remote_key); + free(displaypath); + free(sub_config_path); +} + +static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data) +{ + struct sync_cb *info = cb_data; + sync_submodule(list_item->name, info->prefix, info->flags); + +} + +static int module_sync(int argc, const char **argv, const char *prefix) +{ + struct sync_cb info = SYNC_CB_INIT; + struct pathspec pathspec; + struct module_list list = MODULE_LIST_INIT; + int quiet = 0; + int recursive = 0; + + struct option module_sync_options[] = { + OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")), + OPT_BOOL(0, "recursive", &recursive, + N_("Recurse into nested submodules")), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"), + NULL + }; + + argc = parse_options(argc, argv, prefix, module_sync_options, + git_submodule_helper_usage, 0); + + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) + return 1; + + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + if (recursive) + info.flags |= OPT_RECURSIVE; + + for_each_listed_submodule(&list, sync_submodule_cb, &info); + + return 0; +} + +struct deinit_cb { + const char *prefix; + unsigned int flags; +}; +#define DEINIT_CB_INIT { NULL, 0 } + +static void deinit_submodule(const char *path, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + char *displaypath = NULL; + struct child_process cp_config = CHILD_PROCESS_INIT; + struct strbuf sb_config = STRBUF_INIT; + char *sub_git_dir = xstrfmt("%s/.git", path); + + sub = submodule_from_path(&null_oid, path); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(path, prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(path)) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + /* + * protect submodules containing a .git directory + * NEEDSWORK: instead of dying, automatically call + * absorbgitdirs and (possibly) warn. + */ + if (is_directory(sub_git_dir)) + die(_("Submodule work tree '%s' contains a .git " + "directory (use 'rm -rf' if you really want " + "to remove it including all of its history)"), + displaypath); + + if (!(flags & OPT_FORCE)) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(&cp_rm.args, "rm", "-qn", + path, NULL); + + if (run_command(&cp_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + strbuf_addstr(&sb_rm, path); + + if (!remove_dir_recursively(&sb_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!(flags & OPT_QUIET)) + printf(format, displaypath); + + strbuf_release(&sb_rm); + } + + if (mkdir(path, 0777)) + printf(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) { + char *sub_key = xstrfmt("submodule.%s", sub->name); + /* + * remove the whole section so we have a clean state when + * the user later decides to init this submodule again + */ + git_config_rename_section_in_file(NULL, sub_key, NULL); + if (!(flags & OPT_QUIET)) + printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"), + sub->name, sub->url, displaypath); + free(sub_key); + } + +cleanup: + free(displaypath); + free(sub_git_dir); + strbuf_release(&sb_config); +} + +static void deinit_submodule_cb(const struct cache_entry *list_item, + void *cb_data) +{ + struct deinit_cb *info = cb_data; + deinit_submodule(list_item->name, info->prefix, info->flags); +} + +static int module_deinit(int argc, const char **argv, const char *prefix) +{ + struct deinit_cb info = DEINIT_CB_INIT; + struct pathspec pathspec; + struct module_list list = MODULE_LIST_INIT; + int quiet = 0; + int force = 0; + int all = 0; + + struct option module_deinit_options[] = { + OPT__QUIET(&quiet, N_("Suppress submodule status output")), + OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0), + OPT_BOOL(0, "all", &all, N_("Unregister all submodules")), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"), + NULL + }; + + argc = parse_options(argc, argv, prefix, module_deinit_options, + git_submodule_helper_usage, 0); + + if (all && argc) { + error("pathspec and --all are incompatible"); + usage_with_options(git_submodule_helper_usage, + module_deinit_options); + } + + if (!argc && !all) + die(_("Use '--all' if you really want to deinitialize all submodules")); + + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) + return 1; + + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + if (force) + info.flags |= OPT_FORCE; + + for_each_listed_submodule(&list, deinit_submodule_cb, &info); + + return 0; +} + static int clone_submodule(const char *path, const char *gitdir, const char *url, const char *depth, struct string_list *reference, int quiet, int progress) @@ -1144,7 +1724,7 @@ static int push_check(int argc, const char **argv, const char *prefix) argv++; argc--; /* Get the submodule's head ref and determine if it is detached */ - head = resolve_refdup("HEAD", 0, head_oid.hash, NULL); + head = resolve_refdup("HEAD", 0, &head_oid, NULL); if (!head) die(_("Failed to resolve HEAD as a valid ref.")); if (!strcmp(head, "HEAD")) @@ -1259,6 +1839,10 @@ static struct cmd_struct commands[] = { {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, + {"status", module_status, SUPPORT_SUPER_PREFIX}, + {"print-default-remote", print_default_remote, 0}, + {"sync", module_sync, SUPPORT_SUPER_PREFIX}, + {"deinit", module_deinit, 0}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 17aabaa679..80237f0df1 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -58,7 +58,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) die("Cannot delete %s, not a symbolic ref", argv[0]); if (!strcmp(argv[0], "HEAD")) die("deleting '%s' is not allowed", argv[0]); - return delete_ref(NULL, argv[0], NULL, REF_NODEREF); + return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF); } switch (argc) { diff --git a/builtin/tag.c b/builtin/tag.c index b38329b593..8cff6d0b72 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -82,7 +82,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, for (p = argv; *p; p++) { strbuf_reset(&ref); strbuf_addf(&ref, "refs/tags/%s", *p); - if (read_ref(ref.buf, oid.hash)) { + if (read_ref(ref.buf, &oid)) { error(_("tag '%s' not found."), *p); had_error = 1; continue; @@ -97,9 +97,10 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, static int delete_tag(const char *name, const char *ref, const struct object_id *oid, const void *cb_data) { - if (delete_ref(NULL, ref, oid->hash, 0)) + if (delete_ref(NULL, ref, oid, 0)) return 1; - printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV)); + printf(_("Deleted tag '%s' (was %s)\n"), name, + find_unique_abbrev(oid, DEFAULT_ABBREV)); return 0; } @@ -167,7 +168,7 @@ static void write_tag_body(int fd, const struct object_id *oid) enum object_type type; char *buf, *sp; - buf = read_sha1_file(oid->hash, &type, &size); + buf = read_object_file(oid, &type, &size); if (!buf) return; /* skip header */ @@ -187,13 +188,14 @@ static int build_tag_object(struct strbuf *buf, int sign, struct object_id *resu { if (sign && do_sign(buf) < 0) return error(_("unable to sign the tag")); - if (write_sha1_file(buf->buf, buf->len, tag_type, result->hash) < 0) + if (write_object_file(buf->buf, buf->len, tag_type, result) < 0) return error(_("unable to write tag file")); return 0; } struct create_tag_options { unsigned int message_given:1; + unsigned int use_editor:1; unsigned int sign; enum { CLEANUP_NONE, @@ -210,7 +212,7 @@ static void create_tag(const struct object_id *object, const char *tag, struct strbuf header = STRBUF_INIT; char *path = NULL; - type = sha1_object_info(object->hash, NULL); + type = oid_object_info(object, NULL); if (type <= OBJ_NONE) die(_("bad object type.")); @@ -220,11 +222,11 @@ static void create_tag(const struct object_id *object, const char *tag, "tag %s\n" "tagger %s\n\n", oid_to_hex(object), - typename(type), + type_name(type), tag, git_committer_info(IDENT_STRICT)); - if (!opt->message_given) { + if (!opt->message_given || opt->use_editor) { int fd; /* write the template message before editing: */ @@ -233,7 +235,10 @@ static void create_tag(const struct object_id *object, const char *tag, if (fd < 0) die_errno(_("could not create file '%s'"), path); - if (!is_null_oid(prev)) { + if (opt->message_given) { + write_or_die(fd, buf->buf, buf->len); + strbuf_reset(buf); + } else if (!is_null_oid(prev)) { write_tag_body(fd, prev); } else { struct strbuf buf = STRBUF_INIT; @@ -289,17 +294,17 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) strbuf_addstr(sb, rla); } else { strbuf_addstr(sb, "tag: tagging "); - strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(sb, oid, DEFAULT_ABBREV); } strbuf_addstr(sb, " ("); - type = sha1_object_info(oid->hash, NULL); + type = oid_object_info(oid, NULL); switch (type) { default: strbuf_addstr(sb, "object of unknown type"); break; case OBJ_COMMIT: - if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) { + if ((buf = read_object_file(oid, &type, &size)) != NULL) { subject_len = find_commit_subject(buf, &subject_start); strbuf_insert(sb, sb->len, subject_start, subject_len); } else { @@ -372,6 +377,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; struct ref_format format = REF_FORMAT_INIT; int icase = 0; + int edit_flag = 0; struct option options[] = { OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"), @@ -386,12 +392,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_CALLBACK('m', "message", &msg, N_("message"), N_("tag message"), parse_msg_arg), OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), + OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")), OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"), N_("how to strip spaces and #comments from message")), OPT_STRING('u', "local-user", &keyid, N_("key-id"), N_("use another key to sign the tag")), - OPT__FORCE(&force, N_("replace the tag if exists")), + OPT__FORCE(&force, N_("replace the tag if exists"), 0), OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")), OPT_GROUP(N_("Tag listing options")), @@ -518,12 +525,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (strbuf_check_tag_ref(&ref, tag)) die(_("'%s' is not a valid tag name."), tag); - if (read_ref(ref.buf, prev.hash)) + if (read_ref(ref.buf, &prev)) oidclr(&prev); else if (!force) die(_("tag '%s' already exists"), tag); opt.message_given = msg.given || msgfile; + opt.use_editor = edit_flag; if (!cleanup_arg || !strcmp(cleanup_arg, "strip")) opt.cleanup_mode = CLEANUP_ALL; @@ -544,14 +552,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix) transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, ref.buf, object.hash, prev.hash, + ref_transaction_update(transaction, ref.buf, &object, &prev, create_reflog ? REF_FORCE_CREATE_REFLOG : 0, reflog_msg.buf, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); ref_transaction_free(transaction); if (force && !is_null_oid(&prev) && oidcmp(&prev, &object)) - printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV)); + printf(_("Updated tag '%s' (was %s)\n"), tag, + find_unique_abbrev(&prev, DEFAULT_ABBREV)); UNLEAK(buf); UNLEAK(ref); diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c index 32e0155577..300eb59657 100644 --- a/builtin/unpack-file.c +++ b/builtin/unpack-file.c @@ -9,7 +9,7 @@ static char *create_temp_file(struct object_id *oid) unsigned long size; int fd; - buf = read_sha1_file(oid->hash, &type, &size); + buf = read_object_file(oid, &type, &size); if (!buf || type != OBJ_BLOB) die("unable to read blob object %s", oid_to_hex(oid)); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 62ea264c46..b7755c6cc5 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -21,7 +21,7 @@ static unsigned char buffer[4096]; static unsigned int offset, len; static off_t consumed_bytes; static off_t max_input_size; -static git_SHA_CTX ctx; +static git_hash_ctx ctx; static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; /* @@ -62,7 +62,7 @@ static void *fill(int min) if (min > sizeof(buffer)) die("cannot fill %d bytes", min); if (offset) { - git_SHA1_Update(&ctx, buffer, offset); + the_hash_algo->update_fn(&ctx, buffer, offset); memmove(buffer, buffer + offset, len); offset = 0; } @@ -158,6 +158,7 @@ struct obj_info { struct object *obj; }; +/* Remember to update object flag allocation in object.h */ #define FLAG_OPEN (1u<<20) #define FLAG_WRITTEN (1u<<21) @@ -172,7 +173,8 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) { struct object_id oid; - if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), oid.hash) < 0) + if (write_object_file(obj_buf->buffer, obj_buf->size, + type_name(obj->type), &oid) < 0) die("failed to write object %s", oid_to_hex(&obj->oid)); obj->flags |= FLAG_WRITTEN; } @@ -197,7 +199,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op if (!(obj->flags & FLAG_OPEN)) { unsigned long size; - int type = sha1_object_info(obj->oid.hash, &size); + int type = oid_object_info(&obj->oid, &size); if (type != obj->type || type <= 0) die("object of unexpected type"); obj->flags |= FLAG_WRITTEN; @@ -237,14 +239,16 @@ static void write_object(unsigned nr, enum object_type type, void *buf, unsigned long size) { if (!strict) { - if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0) + if (write_object_file(buf, size, type_name(type), + &obj_list[nr].oid) < 0) die("failed to write object"); added_object(nr, type, buf, size); free(buf); obj_list[nr].obj = NULL; } else if (type == OBJ_BLOB) { struct blob *blob; - if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0) + if (write_object_file(buf, size, type_name(type), + &obj_list[nr].oid) < 0) die("failed to write object"); added_object(nr, type, buf, size); free(buf); @@ -258,12 +262,12 @@ static void write_object(unsigned nr, enum object_type type, } else { struct object *obj; int eaten; - hash_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash); + hash_object_file(buf, size, type_name(type), &obj_list[nr].oid); added_object(nr, type, buf, size); obj = parse_object_buffer(&obj_list[nr].oid, type, size, buf, &eaten); if (!obj) - die("invalid %s", typename(type)); + die("invalid %s", type_name(type)); add_object_buffer(obj, buf, size); obj->flags |= FLAG_OPEN; obj_list[nr].obj = obj; @@ -345,8 +349,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, struct object_id base_oid; if (type == OBJ_REF_DELTA) { - hashcpy(base_oid.hash, fill(GIT_SHA1_RAWSZ)); - use(GIT_SHA1_RAWSZ); + hashcpy(base_oid.hash, fill(the_hash_algo->rawsz)); + use(the_hash_algo->rawsz); delta_data = get_data(delta_size); if (dry_run || !delta_data) { free(delta_data); @@ -419,7 +423,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) return; - base = read_sha1_file(base_oid.hash, &type, &base_size); + base = read_object_file(&base_oid, &type, &base_size); if (!base) { error("failed to read delta-pack base object %s", oid_to_hex(&base_oid)); @@ -564,15 +568,15 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) /* We don't take any non-flag arguments now.. Maybe some day */ usage(unpack_usage); } - git_SHA1_Init(&ctx); + the_hash_algo->init_fn(&ctx); unpack_all(); - git_SHA1_Update(&ctx, buffer, offset); - git_SHA1_Final(oid.hash, &ctx); + the_hash_algo->update_fn(&ctx, buffer, offset); + the_hash_algo->final_fn(oid.hash, &ctx); if (strict) write_rest(); - if (hashcmp(fill(GIT_SHA1_RAWSZ), oid.hash)) + if (hashcmp(fill(the_hash_algo->rawsz), oid.hash)) die("final sha1 did not match"); - use(GIT_SHA1_RAWSZ); + use(the_hash_algo->rawsz); /* Write the last part of the buffer to stdout */ while (len) { diff --git a/builtin/update-index.c b/builtin/update-index.c index bf7420b808..10d070a76f 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -16,6 +16,7 @@ #include "pathspec.h" #include "dir.h" #include "split-index.h" +#include "fsmonitor.h" /* * Default to not allowing changes to the list of files. The @@ -32,6 +33,7 @@ static int force_remove; static int verbose; static int mark_valid_only; static int mark_skip_worktree_only; +static int mark_fsmonitor_only; #define MARK_FLAG 1 #define UNMARK_FLAG 2 static struct strbuf mtime_dir = STRBUF_INIT; @@ -228,6 +230,7 @@ static int mark_ce_flags(const char *path, int flag, int mark) int namelen = strlen(path); int pos = cache_name_pos(path, namelen); if (0 <= pos) { + mark_fsmonitor_invalid(&the_index, active_cache[pos]); if (mark) active_cache[pos]->ce_flags |= flag; else @@ -328,7 +331,7 @@ static int process_directory(const char *path, int len, struct stat *st) if (S_ISGITLINK(ce->ce_mode)) { /* Do nothing to the index if there is no HEAD! */ - if (resolve_gitlink_ref(path, "HEAD", oid.hash) < 0) + if (resolve_gitlink_ref(path, "HEAD", &oid) < 0) return 0; return add_one_path(ce, path, len, st); @@ -354,7 +357,7 @@ static int process_directory(const char *path, int len, struct stat *st) } /* No match - should we add it as a gitlink? */ - if (!resolve_gitlink_ref(path, "HEAD", oid.hash)) + if (!resolve_gitlink_ref(path, "HEAD", &oid)) return add_one_path(NULL, path, len, st); /* Error out. */ @@ -460,6 +463,11 @@ static void update_one(const char *path) die("Unable to mark file %s", path); return; } + if (mark_fsmonitor_only) { + if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG)) + die("Unable to mark file %s", path); + return; + } if (force_remove) { if (remove_file_from_cache(path)) @@ -584,7 +592,7 @@ static struct cache_entry *read_one_ent(const char *which, int size; struct cache_entry *ce; - if (get_tree_entry(ent->hash, path, oid.hash, &mode)) { + if (get_tree_entry(ent, path, &oid, &mode)) { if (which) error("%s: not in %s branch.", path, which); return NULL; @@ -679,9 +687,9 @@ static int unresolve_one(const char *path) static void read_head_pointers(void) { - if (read_ref("HEAD", head_oid.hash)) + if (read_ref("HEAD", &head_oid)) die("No HEAD -- no initial commit yet?"); - if (read_ref("MERGE_HEAD", merge_head_oid.hash)) { + if (read_ref("MERGE_HEAD", &merge_head_oid)) { fprintf(stderr, "Not in the middle of a merge.\n"); exit(0); } @@ -721,7 +729,7 @@ static int do_reupdate(int ac, const char **av, PATHSPEC_PREFER_CWD, prefix, av + 1); - if (read_ref("HEAD", head_oid.hash)) + if (read_ref("HEAD", &head_oid)) /* If there is no HEAD, that means it is an initial * commit. Update everything in the index. */ @@ -917,6 +925,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) struct refresh_params refresh_args = {0, &has_errors}; int lock_error = 0; int split_index = -1; + int force_write = 0; + int fsmonitor = -1; struct lock_file lock_file = LOCK_INIT; struct parse_opt_ctx_t ctx; strbuf_getline_fn getline_fn; @@ -1008,6 +1018,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("test if the filesystem supports untracked cache"), UC_TEST), OPT_SET_INT(0, "force-untracked-cache", &untracked_cache, N_("enable untracked cache without testing the filesystem"), UC_FORCE), + OPT_SET_INT(0, "force-write-index", &force_write, + N_("write out the index even if is not flagged as changed"), 1), + OPT_BOOL(0, "fsmonitor", &fsmonitor, + N_("enable or disable file system monitor")), + {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, + N_("mark files as fsmonitor valid"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, + {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, + N_("clear fsmonitor valid bit"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, OPT_END() }; @@ -1039,6 +1059,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) break; switch (parseopt_state) { case PARSE_OPT_HELP: + case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_NON_OPTION: case PARSE_OPT_DONE: @@ -1146,7 +1167,23 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) die("BUG: bad untracked_cache value: %d", untracked_cache); } - if (active_cache_changed) { + if (fsmonitor > 0) { + if (git_config_get_fsmonitor() == 0) + warning(_("core.fsmonitor is unset; " + "set it if you really want to " + "enable fsmonitor")); + add_fsmonitor(&the_index); + report(_("fsmonitor enabled")); + } else if (!fsmonitor) { + if (git_config_get_fsmonitor() == 1) + warning(_("core.fsmonitor is set; " + "remove it if you really want to " + "disable fsmonitor")); + remove_fsmonitor(&the_index); + report(_("fsmonitor disabled")); + } + + if (active_cache_changed || force_write) { if (newfd < 0) { if (refresh_args.flags & REFRESH_QUIET) exit(128); diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 6b90c5dead..4b4714b3fd 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -200,7 +200,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction, die("update %s: extra input: %s", refname, next); if (ref_transaction_update(transaction, refname, - new_oid.hash, have_old ? old_oid.hash : NULL, + &new_oid, have_old ? &old_oid : NULL, update_flags | create_reflog_flag, msg, &err)) die("%s", err.buf); @@ -232,7 +232,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction, if (*next != line_termination) die("create %s: extra input: %s", refname, next); - if (ref_transaction_create(transaction, refname, new_oid.hash, + if (ref_transaction_create(transaction, refname, &new_oid, update_flags | create_reflog_flag, msg, &err)) die("%s", err.buf); @@ -269,7 +269,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction, die("delete %s: extra input: %s", refname, next); if (ref_transaction_delete(transaction, refname, - have_old ? old_oid.hash : NULL, + have_old ? &old_oid : NULL, update_flags, msg, &err)) die("%s", err.buf); @@ -298,7 +298,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, if (*next != line_termination) die("verify %s: extra input: %s", refname, next); - if (ref_transaction_verify(transaction, refname, old_oid.hash, + if (ref_transaction_verify(transaction, refname, &old_oid, update_flags, &err)) die("%s", err.buf); @@ -312,7 +312,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, static const char *parse_cmd_option(struct strbuf *input, const char *next) { if (!strncmp(next, "no-deref", 8) && next[8] == line_termination) - update_flags |= REF_NODEREF; + update_flags |= REF_NO_DEREF; else die("option unknown: %s", next); return next + 8; @@ -427,17 +427,17 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) } if (no_deref) - flags = REF_NODEREF; + flags = REF_NO_DEREF; if (delete) /* * For purposes of backwards compatibility, we treat * NULL_SHA1 as "don't care" here: */ return delete_ref(msg, refname, - (oldval && !is_null_oid(&oldoid)) ? oldoid.hash : NULL, + (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL, flags); else - return update_ref(msg, refname, oid.hash, oldval ? oldoid.hash : NULL, + return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL, flags | create_reflog_flag, UPDATE_REFS_DIE_ON_ERR); } diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index 873070e517..4321a34456 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -12,7 +12,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix) { int force = 0; struct option options[] = { - OPT__FORCE(&force, N_("update the info files from scratch")), + OPT__FORCE(&force, N_("update the info files from scratch"), 0), OPT_END() }; diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index ba38ac9b15..dcdaada111 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -44,12 +44,12 @@ static int verify_commit(const char *name, unsigned flags) if (get_oid(name, &oid)) return error("commit '%s' not found.", name); - buf = read_sha1_file(oid.hash, &type, &size); + buf = read_object_file(&oid, &type, &size); if (!buf) return error("%s: unable to read file.", name); if (type != OBJ_COMMIT) return error("%s: cannot verify a non-commit object of type %s.", - name, typename(type)); + name, type_name(type)); ret = run_gpg_verify(&oid, buf, size, flags); diff --git a/builtin/worktree.c b/builtin/worktree.c index 7b9307aa58..40a438ed6c 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "checkout.h" #include "config.h" #include "builtin.h" #include "dir.h" @@ -13,10 +14,12 @@ #include "worktree.h" static const char * const worktree_usage[] = { - N_("git worktree add [<options>] <path> [<branch>]"), + N_("git worktree add [<options>] <path> [<commit-ish>]"), N_("git worktree list [<options>]"), N_("git worktree lock [<options>] <path>"), + N_("git worktree move <worktree> <new-path>"), N_("git worktree prune [<options>]"), + N_("git worktree remove [<options>] <worktree>"), N_("git worktree unlock <path>"), NULL }; @@ -32,8 +35,19 @@ struct add_opts { static int show_only; static int verbose; +static int guess_remote; static timestamp_t expire; +static int git_worktree_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "worktree.guessremote")) { + guess_remote = git_config_bool(var, value); + return 0; + } + + return git_default_config(var, value, cb); +} + static int prune_worktree(const char *id, struct strbuf *reason) { struct stat st; @@ -87,16 +101,9 @@ static int prune_worktree(const char *id, struct strbuf *reason) } path[len] = '\0'; if (!file_exists(path)) { - struct stat st_link; free(path); - /* - * the repo is moved manually and has not been - * accessed since? - */ - if (!stat(git_path("worktrees/%s/link", id), &st_link) && - st_link.st_nlink > 1) - return 0; - if (st.st_mtime <= expire) { + if (stat(git_path("worktrees/%s/index", id), &st) || + st.st_mtime <= expire) { strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id); return 1; } else { @@ -218,20 +225,21 @@ static int add_worktree(const char *path, const char *refname, int counter = 0, len, ret; struct strbuf symref = STRBUF_INIT; struct commit *commit = NULL; + int is_branch = 0; if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); /* is 'refname' a branch or commit? */ if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && - ref_exists(symref.buf)) { /* it's a branch */ + ref_exists(symref.buf)) { + is_branch = 1; if (!opts->force) die_if_checked_out(symref.buf, 0); - } else { /* must be a commit */ - commit = lookup_commit_reference_by_name(refname); - if (!commit) - die(_("invalid reference: %s"), refname); } + commit = lookup_commit_reference_by_name(refname); + if (!commit) + die(_("invalid reference: %s"), refname); name = worktree_basename(path, &len); git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name); @@ -296,7 +304,7 @@ static int add_worktree(const char *path, const char *refname, argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); cp.git_cmd = 1; - if (commit) + if (!is_branch) argv_array_pushl(&cp.args, "update-ref", "HEAD", oid_to_hex(&commit->object.oid), NULL); else @@ -327,6 +335,29 @@ done: strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); } + + /* + * Hook failure does not warrant worktree deletion, so run hook after + * is_junk is cleared, but do return appropriate code when hook fails. + */ + if (!ret && opts->checkout) { + const char *hook = find_hook("post-checkout"); + if (hook) { + const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL }; + cp.git_cmd = 0; + cp.no_stdin = 1; + cp.stdout_to_stderr = 1; + cp.dir = path; + cp.env = env; + cp.argv = NULL; + argv_array_pushl(&cp.args, absolute_path(hook), + oid_to_hex(&null_oid), + oid_to_hex(&commit->object.oid), + "1", NULL); + ret = run_command(&cp); + } + } + argv_array_clear(&child_env); strbuf_release(&sb); strbuf_release(&symref); @@ -341,8 +372,11 @@ static int add(int ac, const char **av, const char *prefix) const char *new_branch_force = NULL; char *path; const char *branch; + const char *opt_track = NULL; struct option options[] = { - OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")), + OPT__FORCE(&opts.force, + N_("checkout <branch> even if already checked out in other worktree"), + PARSE_OPT_NOCOMPLETE), OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), N_("create a new branch")), OPT_STRING('B', NULL, &new_branch_force, N_("branch"), @@ -350,6 +384,11 @@ static int add(int ac, const char **av, const char *prefix) OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")), OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")), OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")), + OPT_PASSTHRU(0, "track", &opt_track, NULL, + N_("set up tracking mode (see git-branch(1))"), + PARSE_OPT_NOARG | PARSE_OPT_OPTARG), + OPT_BOOL(0, "guess-remote", &guess_remote, + N_("try to match the new branch name with a remote-tracking branch")), OPT_END() }; @@ -384,6 +423,28 @@ static int add(int ac, const char **av, const char *prefix) int n; const char *s = worktree_basename(path, &n); opts.new_branch = xstrndup(s, n); + if (guess_remote) { + struct object_id oid; + const char *remote = + unique_tracking_name(opts.new_branch, &oid); + if (remote) + branch = remote; + } + } + + if (ac == 2 && !opts.new_branch && !opts.detach) { + struct object_id oid; + struct commit *commit; + const char *remote; + + commit = lookup_commit_reference_by_name(branch); + if (!commit) { + remote = unique_tracking_name(branch, &oid); + if (remote) { + opts.new_branch = branch; + branch = remote; + } + } } if (opts.new_branch) { @@ -394,9 +455,13 @@ static int add(int ac, const char **av, const char *prefix) argv_array_push(&cp.args, "--force"); argv_array_push(&cp.args, opts.new_branch); argv_array_push(&cp.args, branch); + if (opt_track) + argv_array_push(&cp.args, opt_track); if (run_command(&cp)) return -1; branch = opts.new_branch; + } else if (opt_track) { + die(_("--[no-]track can only be used if a new branch is created")); } UNLEAK(path); @@ -410,7 +475,7 @@ static void show_worktree_porcelain(struct worktree *wt) if (wt->is_bare) printf("bare\n"); else { - printf("HEAD %s\n", sha1_to_hex(wt->head_sha1)); + printf("HEAD %s\n", oid_to_hex(&wt->head_oid)); if (wt->is_detached) printf("detached\n"); else if (wt->head_ref) @@ -430,7 +495,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) strbuf_addstr(&sb, "(bare)"); else { strbuf_addf(&sb, "%-*s ", abbrev_len, - find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV)); + find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV)); if (wt->is_detached) strbuf_addstr(&sb, "(detached HEAD)"); else if (wt->head_ref) { @@ -455,7 +520,7 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen) if (path_len > *maxlen) *maxlen = path_len; - sha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev)); + sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev)); if (sha1_len > *abbrev) *abbrev = sha1_len; } @@ -551,13 +616,227 @@ static int unlock_worktree(int ac, const char **av, const char *prefix) return ret; } +static void validate_no_submodules(const struct worktree *wt) +{ + struct index_state istate = { NULL }; + int i, found_submodules = 0; + + if (read_index_from(&istate, worktree_git_path(wt, "index"), + get_worktree_git_dir(wt)) > 0) { + for (i = 0; i < istate.cache_nr; i++) { + struct cache_entry *ce = istate.cache[i]; + + if (S_ISGITLINK(ce->ce_mode)) { + found_submodules = 1; + break; + } + } + } + discard_index(&istate); + + if (found_submodules) + die(_("working trees containing submodules cannot be moved or removed")); +} + +static int move_worktree(int ac, const char **av, const char *prefix) +{ + struct option options[] = { + OPT_END() + }; + struct worktree **worktrees, *wt; + struct strbuf dst = STRBUF_INIT; + struct strbuf errmsg = STRBUF_INIT; + const char *reason; + char *path; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac != 2) + usage_with_options(worktree_usage, options); + + path = prefix_filename(prefix, av[1]); + strbuf_addstr(&dst, path); + free(path); + + worktrees = get_worktrees(0); + wt = find_worktree(worktrees, prefix, av[0]); + if (!wt) + die(_("'%s' is not a working tree"), av[0]); + if (is_main_worktree(wt)) + die(_("'%s' is a main working tree"), av[0]); + if (is_directory(dst.buf)) { + const char *sep = find_last_dir_sep(wt->path); + + if (!sep) + die(_("could not figure out destination name from '%s'"), + wt->path); + strbuf_trim_trailing_dir_sep(&dst); + strbuf_addstr(&dst, sep); + } + if (file_exists(dst.buf)) + die(_("target '%s' already exists"), dst.buf); + + validate_no_submodules(wt); + + reason = is_worktree_locked(wt); + if (reason) { + if (*reason) + die(_("cannot move a locked working tree, lock reason: %s"), + reason); + die(_("cannot move a locked working tree")); + } + if (validate_worktree(wt, &errmsg, 0)) + die(_("validation failed, cannot move working tree: %s"), + errmsg.buf); + strbuf_release(&errmsg); + + if (rename(wt->path, dst.buf) == -1) + die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf); + + update_worktree_location(wt, dst.buf); + + strbuf_release(&dst); + free_worktrees(worktrees); + return 0; +} + +/* + * Note, "git status --porcelain" is used to determine if it's safe to + * delete a whole worktree. "git status" does not ignore user + * configuration, so if a normal "git status" shows "clean" for the + * user, then it's ok to remove it. + * + * This assumption may be a bad one. We may want to ignore + * (potentially bad) user settings and only delete a worktree when + * it's absolutely safe to do so from _our_ point of view because we + * know better. + */ +static void check_clean_worktree(struct worktree *wt, + const char *original_path) +{ + struct argv_array child_env = ARGV_ARRAY_INIT; + struct child_process cp; + char buf[1]; + int ret; + + /* + * Until we sort this out, all submodules are "dirty" and + * will abort this function. + */ + validate_no_submodules(wt); + + argv_array_pushf(&child_env, "%s=%s/.git", + GIT_DIR_ENVIRONMENT, wt->path); + argv_array_pushf(&child_env, "%s=%s", + GIT_WORK_TREE_ENVIRONMENT, wt->path); + memset(&cp, 0, sizeof(cp)); + argv_array_pushl(&cp.args, "status", + "--porcelain", "--ignore-submodules=none", + NULL); + cp.env = child_env.argv; + cp.git_cmd = 1; + cp.dir = wt->path; + cp.out = -1; + ret = start_command(&cp); + if (ret) + die_errno(_("failed to run 'git status' on '%s'"), + original_path); + ret = xread(cp.out, buf, sizeof(buf)); + if (ret) + die(_("'%s' is dirty, use --force to delete it"), + original_path); + close(cp.out); + ret = finish_command(&cp); + if (ret) + die_errno(_("failed to run 'git status' on '%s', code %d"), + original_path, ret); +} + +static int delete_git_work_tree(struct worktree *wt) +{ + struct strbuf sb = STRBUF_INIT; + int ret = 0; + + strbuf_addstr(&sb, wt->path); + if (remove_dir_recursively(&sb, 0)) { + error_errno(_("failed to delete '%s'"), sb.buf); + ret = -1; + } + strbuf_release(&sb); + return ret; +} + +static int delete_git_dir(struct worktree *wt) +{ + struct strbuf sb = STRBUF_INIT; + int ret = 0; + + strbuf_addstr(&sb, git_common_path("worktrees/%s", wt->id)); + if (remove_dir_recursively(&sb, 0)) { + error_errno(_("failed to delete '%s'"), sb.buf); + ret = -1; + } + strbuf_release(&sb); + return ret; +} + +static int remove_worktree(int ac, const char **av, const char *prefix) +{ + int force = 0; + struct option options[] = { + OPT_BOOL(0, "force", &force, + N_("force removing even if the worktree is dirty")), + OPT_END() + }; + struct worktree **worktrees, *wt; + struct strbuf errmsg = STRBUF_INIT; + const char *reason; + int ret = 0; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac != 1) + usage_with_options(worktree_usage, options); + + worktrees = get_worktrees(0); + wt = find_worktree(worktrees, prefix, av[0]); + if (!wt) + die(_("'%s' is not a working tree"), av[0]); + if (is_main_worktree(wt)) + die(_("'%s' is a main working tree"), av[0]); + reason = is_worktree_locked(wt); + if (reason) { + if (*reason) + die(_("cannot remove a locked working tree, lock reason: %s"), + reason); + die(_("cannot remove a locked working tree")); + } + if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK)) + die(_("validation failed, cannot remove working tree: %s"), + errmsg.buf); + strbuf_release(&errmsg); + + if (file_exists(wt->path)) { + if (!force) + check_clean_worktree(wt, av[0]); + + ret |= delete_git_work_tree(wt); + } + /* + * continue on even if ret is non-zero, there's no going back + * from here. + */ + ret |= delete_git_dir(wt); + + free_worktrees(worktrees); + return ret; +} + int cmd_worktree(int ac, const char **av, const char *prefix) { struct option options[] = { OPT_END() }; - git_config(git_default_config, NULL); + git_config(git_worktree_config, NULL); if (ac < 2) usage_with_options(worktree_usage, options); @@ -573,5 +852,9 @@ int cmd_worktree(int ac, const char **av, const char *prefix) return lock_worktree(ac - 1, av + 1, prefix); if (!strcmp(av[1], "unlock")) return unlock_worktree(ac - 1, av + 1, prefix); + if (!strcmp(av[1], "move")) + return move_worktree(ac - 1, av + 1, prefix); + if (!strcmp(av[1], "remove")) + return remove_worktree(ac - 1, av + 1, prefix); usage_with_options(worktree_usage, options); } diff --git a/builtin/write-tree.c b/builtin/write-tree.c index bd0a78aa3c..c9d3c544e7 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -19,7 +19,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) { int flags = 0, ret; const char *prefix = NULL; - unsigned char sha1[20]; + struct object_id oid; const char *me = "git-write-tree"; struct option write_tree_options[] = { OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"), @@ -38,10 +38,10 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) argc = parse_options(argc, argv, unused_prefix, write_tree_options, write_tree_usage, 0); - ret = write_cache_as_tree(sha1, flags, prefix); + ret = write_cache_as_tree(&oid, flags, prefix); switch (ret) { case 0: - printf("%s\n", sha1_to_hex(sha1)); + printf("%s\n", oid_to_hex(&oid)); break; case WRITE_TREE_UNREADABLE_INDEX: die("%s: error reading the index", me); |