diff options
Diffstat (limited to 'builtin')
49 files changed, 1272 insertions, 536 deletions
diff --git a/builtin/add.c b/builtin/add.c index a648cf4c56..bf01d89e28 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); @@ -276,6 +296,7 @@ static struct option builtin_add_options[] = { OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")), 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); diff --git a/builtin/am.c b/builtin/am.c index d7513f5375..acfe9d3c8c 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; } /** @@ -1068,8 +1072,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) @@ -1134,52 +1138,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 +1376,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 +1400,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); @@ -1488,11 +1455,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"); @@ -1686,8 +1652,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"); @@ -1946,15 +1912,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 +1935,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 +1951,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 +1969,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; @@ -2135,7 +2099,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,11 +2111,11 @@ 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); 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/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..005f55aaa2 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -708,8 +708,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; @@ -734,9 +734,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 +803,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); diff --git a/builtin/branch.c b/builtin/branch.c index 79dc9181fd..8dcc2ed058 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'"), @@ -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")); @@ -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/checkout-index.c b/builtin/checkout-index.c index 39c8be05dc..b0e78b819d 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; @@ -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..c54c78df54 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" @@ -247,7 +248,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 +276,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 +375,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 +401,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.hash, DEFAULT_ABBREV), sb.buf); + } else { + fprintf(stderr, "%s %s %s\n", msg, + find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf); + } strbuf_release(&sb); } @@ -472,9 +477,9 @@ static int merge_working_tree(const struct checkout_opts *opts, 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")); @@ -516,7 +521,7 @@ static int merge_working_tree(const struct checkout_opts *opts, } tree = parse_tree_indirect(old->commit ? &old->commit->object.oid : - &empty_tree_oid); + the_hash_algo->empty_tree); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(&new->commit->object.oid); init_tree_desc(&trees[1], tree->buffer, tree->size); @@ -591,7 +596,7 @@ 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) @@ -642,8 +647,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts, else create_branch(opts->new_branch, new->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; @@ -664,8 +669,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (!strcmp(new->name, "HEAD") && !new->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); + update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL, + REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); if (!opts->quiet) { if (old->path && advice_detached_head && !opts->force_detach) @@ -786,7 +791,6 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) { struct rev_info revs; struct object *object = &old->object; - struct object_array refs; init_revisions(&revs, NULL); setup_revisions(0, NULL, &revs, NULL); @@ -797,14 +801,6 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) 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; - - /* - * 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)) @@ -813,9 +809,7 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) describe_detached_head(_("Previous HEAD position was"), old); /* 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, @@ -827,7 +821,7 @@ static int switch_branches(const struct checkout_opts *opts, 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); + old.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag); if (old.path) old.commit = lookup_commit_reference_gently(&rev, 1); if (!(flag & REF_ISSYMREF)) @@ -874,46 +868,6 @@ 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, @@ -1038,7 +992,7 @@ static int parse_branchname_arg(int argc, const char **argv, setup_branch_path(new); if (!check_refname_format(new->path, 0) && - !read_ref(new->path, branch_rev.hash)) + !read_ref(new->path, &branch_rev)) oidcpy(rev, &branch_rev); else new->path = NULL; /* not an existing branch */ @@ -1136,7 +1090,7 @@ static int checkout_branch(struct checkout_opts *opts, 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); } @@ -1289,11 +1243,11 @@ 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); } diff --git a/builtin/clone.c b/builtin/clone.c index dbddd98f80..284651797e 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -452,7 +452,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 +473,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 +504,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 +591,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 +613,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 +633,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 +685,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 +709,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 +718,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 +736,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 +752,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 +865,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; @@ -938,7 +945,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 +956,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 +974,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); @@ -1083,9 +1100,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) diff --git a/builtin/commit.c b/builtin/commit.c index be370f64a9..4610e3d8e3 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -118,7 +118,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; /* @@ -139,7 +139,7 @@ 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 +355,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 +374,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 +401,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); @@ -474,7 +474,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 +486,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(); @@ -701,7 +701,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 +731,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)) { /* @@ -912,11 +914,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); @@ -1075,6 +1078,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) { @@ -1183,8 +1199,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) @@ -1363,8 +1379,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 +1401,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); @@ -1487,7 +1509,7 @@ static void print_summary(const char *prefix, const struct object_id *oid, rev.show_root_diff = 1; get_commit_format(format.buf, &rev); rev.always_show_header = 0; - rev.diffopt.detect_rename = 1; + rev.diffopt.detect_rename = DIFF_DETECT_RENAME; rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); @@ -1790,9 +1812,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix) transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, "HEAD", oid.hash, + ref_transaction_update(transaction, "HEAD", &oid, current_head - ? current_head->object.oid.hash : null_sha1, + ? ¤t_head->object.oid : &null_oid, 0, sb.buf, &err) || ref_transaction_commit(transaction, &err)) { rollback_index_files(); diff --git a/builtin/config.c b/builtin/config.c index d13daeeb55..ab5f95476e 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -52,6 +52,7 @@ static int show_origin; #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 +81,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 +161,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 +280,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) diff --git a/builtin/describe.c b/builtin/describe.c index 29075dbd0f..6fe1c51281 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->hash, 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_addstr(dst, find_unique_abbrev(cmit_oid->hash, 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 (lookup_blob(&oid)) + 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..b775a75647 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -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..bcc79d1888 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -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..796d0cd66c 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -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) { @@ -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; @@ -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.c b/builtin/fetch.c index 225c734924..7bbcd26faf 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" @@ -457,8 +458,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; @@ -727,7 +728,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 +738,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; } @@ -1094,9 +1095,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(); @@ -1397,7 +1395,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) 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, diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 27a2361e91..8e8a15ea4a 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) @@ -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..04846d46f9 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -555,7 +555,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"); @@ -759,6 +759,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/grep.c b/builtin/grep.c index 5a6cfe6b45..3ca4ac80d8 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1015,6 +1015,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/index-pack.c b/builtin/index-pack.c index 8ec459f522..4c51aec81f 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1660,10 +1660,7 @@ 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); @@ -1679,10 +1676,8 @@ 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 (starts_with(arg, "--threads=")) { char *end; nr_threads = strtoul(arg+10, &end, 0); diff --git a/builtin/log.c b/builtin/log.c index d81a09051e..46b4ca13e5 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -121,20 +121,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 +142,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 +188,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 +213,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 +399,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 +491,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, @@ -667,9 +674,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 +982,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); @@ -1341,7 +1348,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 +1519,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"; @@ -1613,8 +1620,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) 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); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 8c713c47ac..2fc836e330 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])) { @@ -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/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.c b/builtin/merge.c index fbbf2a9e5e..30264cfd7c 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -405,9 +405,8 @@ 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. @@ -455,7 +454,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); @@ -568,6 +567,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")) @@ -1144,7 +1145,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 +1263,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; } @@ -1358,8 +1359,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. */ diff --git a/builtin/mv.c b/builtin/mv.c index ffdd5f01a1..cf3684d907 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -291,7 +291,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } if (gitmodules_modified) - stage_updated_gitmodules(); + stage_updated_gitmodules(&the_index); if (active_cache_changed && write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) diff --git a/builtin/notes.c b/builtin/notes.c index e435286dd8..7c81761645 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 }; @@ -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..6b9cfc289d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -15,6 +15,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" @@ -79,6 +81,15 @@ 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 */ +}; +static enum missing_action arg_missing_action; +static show_object_fn fn_show_object; + /* * stats */ @@ -151,7 +162,7 @@ static unsigned long do_compress(void **pptr, unsigned long size) } static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f, - const unsigned char *sha1) + const struct object_id *oid) { git_zstream stream; unsigned char ibuf[1024 * 16]; @@ -165,7 +176,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; @@ -339,7 +350,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent sha1write(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); @@ -557,13 +568,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; } @@ -792,7 +803,7 @@ 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) @@ -823,13 +834,13 @@ static void write_pack_file(void) * If so, rewrite it like in fast-import */ if (pack_to_stdout) { - sha1close(f, sha1, CSUM_CLOSE); + sha1close(f, oid.hash, CSUM_CLOSE); } else if (nr_written == nr_remaining) { - sha1close(f, sha1, CSUM_FSYNC); + sha1close(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 = sha1close(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 +874,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 +898,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 +939,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,7 +1001,7 @@ 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) @@ -998,7 +1009,7 @@ static int want_object_in_pack(const unsigned char *sha1, struct mru_entry *entry; int want; - if (!exclude && local && has_loose_object_nonlocal(sha1)) + if (!exclude && local && has_loose_object_nonlocal(oid->hash)) return 0; /* @@ -1019,7 +1030,7 @@ static int want_object_in_pack(const unsigned char *sha1, 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) { @@ -1039,7 +1050,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 +1061,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 +1081,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 +1100,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 +1108,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 +1136,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 +1155,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 +1171,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 +1187,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_sha1_file(oid->hash, &type, &size); if (!data) return NULL; if (type != OBJ_TREE) { @@ -1202,7 +1213,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 +1258,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 +1269,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 +1328,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 +1338,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->hash, tree_type, &size, tree_oid.hash); 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 +1363,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; } @@ -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,17 +2532,17 @@ 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); } } @@ -2538,7 +2550,7 @@ static void read_object_list_from_stdin(void) 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 +2560,49 @@ 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 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; + fn_show_object = show_object__ma_allow_any; + 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 { @@ -2601,7 +2649,7 @@ 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; + struct object_id oid; struct object *o; if (!p->pack_local || p->pack_keep) @@ -2614,8 +2662,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 +2674,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); @@ -2642,7 +2690,7 @@ static int add_loose_object(const struct object_id *oid, const char *path, return 0; } - add_object_entry(oid->hash, type, "", 0); + add_object_entry(oid, type, "", 0); return 0; } @@ -2658,7 +2706,7 @@ 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; @@ -2667,7 +2715,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1) 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; } @@ -2718,7 +2766,7 @@ 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)) die("unable to force loose object"); @@ -2816,7 +2864,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 +3005,10 @@ 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_END(), }; @@ -3028,6 +3085,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 * diff --git a/builtin/prune.c b/builtin/prune.c index cddabf26a9..d2fdae680a 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; diff --git a/builtin/pull.c b/builtin/pull.c index 1b4d9cd974..511dbbe0f6 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), @@ -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; @@ -606,6 +610,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..1c28427d82 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[] = { @@ -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..7daee544b7 100644 --- a/builtin/rebase--helper.c +++ b/builtin/rebase--helper.c @@ -12,10 +12,12 @@ 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")), @@ -27,19 +29,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); + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); opts.action = REPLAY_INTERACTIVE_REBASE; opts.allow_ff = 1; @@ -48,21 +53,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..b7ce7c7f52 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -24,6 +24,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>"), @@ -870,7 +871,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 +1140,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 +1157,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); @@ -1270,7 +1271,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 +1279,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 +1310,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 +1321,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; } @@ -1961,6 +1962,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(); } diff --git a/builtin/reflog.c b/builtin/reflog.c index 2067cca5b1..2233725315 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]; }; @@ -385,7 +385,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 +416,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 +433,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 +579,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 +591,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 +649,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 +658,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 +673,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..d95bf904c3 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -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++) { @@ -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) { @@ -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/replace.c b/builtin/replace.c index 3e71a77152..10078ae371 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -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); @@ -175,7 +175,7 @@ static int replace_object_oid(const char *object_ref, 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); diff --git a/builtin/reset.c b/builtin/reset.c index 9cd89b2305..e15f595799 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" @@ -166,7 +166,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 +266,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..d5345b6a2e 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,7 @@ #include "bisect.h" #include "progress.h" #include "reflog-walk.h" +#include "oidset.h" static const char rev_list_usage[] = "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n" @@ -55,6 +58,20 @@ 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 */ +}; +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) { @@ -178,11 +195,31 @@ static void finish_commit(struct commit *commit, void *data) free_commit_buffer(commit); } +static inline void finish_object__ma(struct object *obj) +{ + 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; + + default: + BUG("unhandled missing_action"); + return; + } +} + static void 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)) - die("missing blob object '%s'", oid_to_hex(&obj->oid)); + finish_object__ma(obj); if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT) parse_object(&obj->oid); } @@ -258,17 +295,37 @@ 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; + return 1; + } + + if (!strcmp(value, "print")) { + arg_missing_action = MA_PRINT; + return 1; + } + + return 0; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -294,7 +351,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 +392,30 @@ 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_release(&filter_options); + continue; + } + if (!strcmp(arg, "--filter-print-omitted")) { + arg_print_omitted = 1; + continue; + } + + if (skip_prefix(arg, "--missing=", &arg) && + parse_missing_action_value(arg)) + continue; + usage(rev_list_usage); } @@ -360,6 +441,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); @@ -403,7 +487,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..74aa644cbb 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 diff --git a/builtin/rm.c b/builtin/rm.c index d91451fea1..4a2fcca27b 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -382,7 +382,7 @@ 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) { diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 6fa1f62a88..e8a4aa40cb 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" @@ -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..41e5e71cad 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -38,7 +38,7 @@ static void show_one(const char *refname, const struct object_id *oid) if (!deref_tags) return; - if (!peel_ref(refname, peeled.hash)) { + if (!peel_ref(refname, &peeled)) { hex = find_unique_abbrev(peeled.hash, 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..a5c4a8a694 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -13,6 +13,16 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" + +#define OPT_QUIET (1 << 0) +#define OPT_CACHED (1 << 1) +#define OPT_RECURSIVE (1 << 2) + +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); static char *get_default_remote(void) { @@ -219,6 +229,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; @@ -328,21 +396,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 +433,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 +443,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 +474,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 +501,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 +537,165 @@ 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; + + if (refs_head_ref(get_submodule_ref_store(path), + 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; } @@ -1144,7 +1382,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 +1497,7 @@ 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}, {"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..a7e6a5b0f2 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,7 +97,7 @@ 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)); return 0; @@ -518,7 +518,7 @@ 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); @@ -544,7 +544,7 @@ 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)) diff --git a/builtin/update-index.c b/builtin/update-index.c index bf7420b808..58d1c2d282 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)) @@ -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() }; @@ -1146,7 +1166,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/worktree.c b/builtin/worktree.c index 7b9307aa58..7cef5b120b 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" @@ -32,8 +33,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; @@ -218,20 +230,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 +309,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 +340,15 @@ 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) + ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid), + oid_to_hex(&commit->object.oid), "1", NULL); + argv_array_clear(&child_env); strbuf_release(&sb); strbuf_release(&symref); @@ -341,6 +363,7 @@ 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_STRING('b', NULL, &opts.new_branch, N_("branch"), @@ -350,6 +373,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 +412,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 +444,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 +464,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 +484,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.hash, DEFAULT_ABBREV)); if (wt->is_detached) strbuf_addstr(&sb, "(detached HEAD)"); else if (wt->head_ref) { @@ -455,7 +509,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.hash, *abbrev)); if (sha1_len > *abbrev) *abbrev = sha1_len; } @@ -557,7 +611,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix) OPT_END() }; - git_config(git_default_config, NULL); + git_config(git_worktree_config, NULL); if (ac < 2) usage_with_options(worktree_usage, options); |