diff options
Diffstat (limited to 'builtin')
-rw-r--r-- | builtin/blame.c | 10 | ||||
-rw-r--r-- | builtin/clean.c | 6 | ||||
-rw-r--r-- | builtin/clone.c | 2 | ||||
-rw-r--r-- | builtin/commit-graph.c | 60 | ||||
-rw-r--r-- | builtin/commit.c | 6 | ||||
-rw-r--r-- | builtin/diff-tree.c | 9 | ||||
-rw-r--r-- | builtin/fmt-merge-msg.c | 658 | ||||
-rw-r--r-- | builtin/grep.c | 48 | ||||
-rw-r--r-- | builtin/help.c | 2 | ||||
-rw-r--r-- | builtin/index-pack.c | 5 | ||||
-rw-r--r-- | builtin/log.c | 17 | ||||
-rw-r--r-- | builtin/ls-files.c | 5 | ||||
-rw-r--r-- | builtin/merge.c | 32 | ||||
-rw-r--r-- | builtin/prune-packed.c | 44 | ||||
-rw-r--r-- | builtin/prune.c | 1 | ||||
-rw-r--r-- | builtin/pull.c | 9 | ||||
-rw-r--r-- | builtin/rebase.c | 309 | ||||
-rw-r--r-- | builtin/receive-pack.c | 27 | ||||
-rw-r--r-- | builtin/reflog.c | 2 | ||||
-rw-r--r-- | builtin/repack.c | 1 | ||||
-rw-r--r-- | builtin/sparse-checkout.c | 55 | ||||
-rw-r--r-- | builtin/stash.c | 19 | ||||
-rw-r--r-- | builtin/update-ref.c | 245 |
23 files changed, 447 insertions, 1125 deletions
diff --git a/builtin/blame.c b/builtin/blame.c index bf1cecdf3f..3c13634f27 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1061,6 +1061,14 @@ parse_done: string_list_clear(&ignore_revs_file_list, 0); string_list_clear(&ignore_rev_list, 0); setup_scoreboard(&sb, path, &o); + + /* + * Changed-path Bloom filters are disabled when looking + * for copies. + */ + if (!(opt & PICKAXE_BLAME_COPY)) + setup_blame_bloom_data(&sb, path); + lno = sb.num_lines; if (lno && !range_list.nr) @@ -1164,5 +1172,7 @@ parse_done: printf("num get patch: %d\n", sb.num_get_patch); printf("num commits: %d\n", sb.num_commits); } + + cleanup_scoreboard(&sb); return 0; } diff --git a/builtin/clean.c b/builtin/clean.c index c8c011d2dd..f14c21b863 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -983,12 +983,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (!cache_name_is_other(ent->name, ent->len)) continue; - if (pathspec.nr) - matches = dir_path_match(&the_index, ent, &pathspec, 0, NULL); - - if (pathspec.nr && !matches) - continue; - if (lstat(ent->name, &st)) die_errno("Cannot lstat '%s'", ent->name); diff --git a/builtin/clone.c b/builtin/clone.c index a4f836d1ba..cb48a291ca 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -102,10 +102,10 @@ static struct option builtin_clone_options[] = { N_("don't use local hardlinks, always copy")), OPT_BOOL('s', "shared", &option_shared, N_("setup as shared repository")), - OPT_ALIAS(0, "recursive", "recurse-submodules"), { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules, N_("pathspec"), N_("initialize submodules in the clone"), PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." }, + OPT_ALIAS(0, "recursive", "recurse-submodules"), OPT_INTEGER('j', "jobs", &max_jobs, N_("number of submodules cloned in parallel")), OPT_STRING(0, "template", &option_template, N_("template-directory"), diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index d1ab6625f6..15fe60317c 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -9,7 +9,9 @@ static char const * const builtin_commit_graph_usage[] = { N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"), - N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"), + N_("git commit-graph write [--object-dir <objdir>] [--append] " + "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " + "[--changed-paths] [--[no-]progress] <split options>"), NULL }; @@ -19,7 +21,9 @@ static const char * const builtin_commit_graph_verify_usage[] = { }; static const char * const builtin_commit_graph_write_usage[] = { - N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"), + N_("git commit-graph write [--object-dir <objdir>] [--append] " + "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " + "[--changed-paths] [--[no-]progress] <split options>"), NULL }; @@ -32,6 +36,7 @@ static struct opts_commit_graph { int split; int shallow; int progress; + int enable_changed_paths; } opts; static struct object_directory *find_odb(struct repository *r, @@ -114,10 +119,29 @@ static int graph_verify(int argc, const char **argv) extern int read_replace_refs; static struct split_commit_graph_opts split_opts; +static int write_option_parse_split(const struct option *opt, const char *arg, + int unset) +{ + enum commit_graph_split_flags *flags = opt->value; + + opts.split = 1; + if (!arg) + return 0; + + if (!strcmp(arg, "no-merge")) + *flags = COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED; + else if (!strcmp(arg, "replace")) + *flags = COMMIT_GRAPH_SPLIT_REPLACE; + else + die(_("unrecognized --split argument, %s"), arg); + + return 0; +} + static int graph_write(int argc, const char **argv) { struct string_list *pack_indexes = NULL; - struct string_list *commit_hex = NULL; + struct oidset commits = OIDSET_INIT; struct object_directory *odb = NULL; struct string_list lines; int result = 0; @@ -135,15 +159,19 @@ static int graph_write(int argc, const char **argv) N_("start walk at commits listed by stdin")), OPT_BOOL(0, "append", &opts.append, N_("include all commits already in the commit-graph file")), + OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths, + N_("enable computation for changed paths")), OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")), - OPT_BOOL(0, "split", &opts.split, - N_("allow writing an incremental commit-graph file")), + OPT_CALLBACK_F(0, "split", &split_opts.flags, NULL, + N_("allow writing an incremental commit-graph file"), + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, + write_option_parse_split), OPT_INTEGER(0, "max-commits", &split_opts.max_commits, N_("maximum number of commits in a non-base split commit-graph")), OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple, N_("maximum ratio between two levels of a split commit-graph")), OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time, - N_("maximum number of commits in a non-base split commit-graph")), + N_("only expire files older than a given date-time")), OPT_END(), }; @@ -168,6 +196,9 @@ static int graph_write(int argc, const char **argv) flags |= COMMIT_GRAPH_WRITE_SPLIT; if (opts.progress) flags |= COMMIT_GRAPH_WRITE_PROGRESS; + if (opts.enable_changed_paths || + git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0)) + flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS; read_replace_refs = 0; odb = find_odb(the_repository, opts.obj_dir); @@ -188,7 +219,20 @@ static int graph_write(int argc, const char **argv) if (opts.stdin_packs) pack_indexes = &lines; if (opts.stdin_commits) { - commit_hex = &lines; + struct string_list_item *item; + oidset_init(&commits, lines.nr); + for_each_string_list_item(item, &lines) { + struct object_id oid; + const char *end; + + if (parse_oid_hex(item->string, &oid, &end)) { + error(_("unexpected non-hex object ID: " + "%s"), item->string); + return 1; + } + + oidset_insert(&commits, &oid); + } flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS; } @@ -197,7 +241,7 @@ static int graph_write(int argc, const char **argv) if (write_commit_graph(odb, pack_indexes, - commit_hex, + opts.stdin_commits ? &commits : NULL, flags, &split_opts)) result = 1; diff --git a/builtin/commit.c b/builtin/commit.c index d3e7781e65..4743ea5a4c 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1700,9 +1700,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) "new_index file. Check that disk is not full and quota is\n" "not exceeded, and then \"git restore --staged :/\" to recover.")); - if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) && - write_commit_graph_reachable(the_repository->objects->odb, 0, NULL)) - return 1; + git_test_write_commit_graph_or_die(); repo_rerere(the_repository, 0); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); @@ -1721,6 +1719,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) &oid, flags); } + apply_autostash(git_path_merge_autostash(the_repository)); + UNLEAK(err); UNLEAK(sb); return 0; diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index cb9ea79367..802363d0a2 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -109,6 +109,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) struct object *tree1, *tree2; static struct rev_info *opt = &log_tree_opt; struct setup_revision_opt s_r_opt; + struct userformat_want w; int read_stdin = 0; if (argc == 2 && !strcmp(argv[1], "-h")) @@ -127,6 +128,14 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) precompose_argv(argc, argv); argc = setup_revisions(argc, argv, opt, &s_r_opt); + memset(&w, 0, sizeof(w)); + userformat_find_requirements(NULL, &w); + + if (!opt->show_notes_given && w.notes) + opt->show_notes = 1; + if (opt->show_notes) + load_display_notes(&opt->notes_opt); + while (--argc > 0) { const char *arg = *++argv; diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 172dfbd852..48a8699de7 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -1,669 +1,13 @@ #include "builtin.h" -#include "cache.h" #include "config.h" -#include "refs.h" -#include "object-store.h" -#include "commit.h" -#include "diff.h" -#include "revision.h" -#include "tag.h" -#include "string-list.h" -#include "branch.h" #include "fmt-merge-msg.h" -#include "gpg-interface.h" -#include "repository.h" -#include "commit-reach.h" +#include "parse-options.h" static const char * const fmt_merge_msg_usage[] = { N_("git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"), NULL }; -static int use_branch_desc; - -int fmt_merge_msg_config(const char *key, const char *value, void *cb) -{ - if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) { - int is_bool; - merge_log_config = git_config_bool_or_int(key, value, &is_bool); - if (!is_bool && merge_log_config < 0) - return error("%s: negative length %s", key, value); - if (is_bool && merge_log_config) - merge_log_config = DEFAULT_MERGE_LOG_LEN; - } else if (!strcmp(key, "merge.branchdesc")) { - use_branch_desc = git_config_bool(key, value); - } else { - return git_default_config(key, value, cb); - } - return 0; -} - -/* merge data per repository where the merged tips came from */ -struct src_data { - struct string_list branch, tag, r_branch, generic; - int head_status; -}; - -struct origin_data { - struct object_id oid; - unsigned is_local_branch:1; -}; - -static void init_src_data(struct src_data *data) -{ - data->branch.strdup_strings = 1; - data->tag.strdup_strings = 1; - data->r_branch.strdup_strings = 1; - data->generic.strdup_strings = 1; -} - -static struct string_list srcs = STRING_LIST_INIT_DUP; -static struct string_list origins = STRING_LIST_INIT_DUP; - -struct merge_parents { - int alloc, nr; - struct merge_parent { - struct object_id given; - struct object_id commit; - unsigned char used; - } *item; -}; - -/* - * I know, I know, this is inefficient, but you won't be pulling and merging - * hundreds of heads at a time anyway. - */ -static struct merge_parent *find_merge_parent(struct merge_parents *table, - struct object_id *given, - struct object_id *commit) -{ - int i; - for (i = 0; i < table->nr; i++) { - if (given && !oideq(&table->item[i].given, given)) - continue; - if (commit && !oideq(&table->item[i].commit, commit)) - continue; - return &table->item[i]; - } - return NULL; -} - -static void add_merge_parent(struct merge_parents *table, - struct object_id *given, - struct object_id *commit) -{ - if (table->nr && find_merge_parent(table, given, commit)) - return; - ALLOC_GROW(table->item, table->nr + 1, table->alloc); - oidcpy(&table->item[table->nr].given, given); - oidcpy(&table->item[table->nr].commit, commit); - table->item[table->nr].used = 0; - table->nr++; -} - -static int handle_line(char *line, struct merge_parents *merge_parents) -{ - int i, len = strlen(line); - struct origin_data *origin_data; - char *src; - const char *origin, *tag_name; - struct src_data *src_data; - struct string_list_item *item; - int pulling_head = 0; - struct object_id oid; - const unsigned hexsz = the_hash_algo->hexsz; - - if (len < hexsz + 3 || line[hexsz] != '\t') - return 1; - - if (starts_with(line + hexsz + 1, "not-for-merge")) - return 0; - - if (line[hexsz + 1] != '\t') - return 2; - - i = get_oid_hex(line, &oid); - if (i) - return 3; - - if (!find_merge_parent(merge_parents, &oid, NULL)) - return 0; /* subsumed by other parents */ - - origin_data = xcalloc(1, sizeof(struct origin_data)); - oidcpy(&origin_data->oid, &oid); - - if (line[len - 1] == '\n') - line[len - 1] = 0; - line += hexsz + 2; - - /* - * At this point, line points at the beginning of comment e.g. - * "branch 'frotz' of git://that/repository.git". - * Find the repository name and point it with src. - */ - src = strstr(line, " of "); - if (src) { - *src = 0; - src += 4; - pulling_head = 0; - } else { - src = line; - pulling_head = 1; - } - - item = unsorted_string_list_lookup(&srcs, src); - if (!item) { - item = string_list_append(&srcs, src); - item->util = xcalloc(1, sizeof(struct src_data)); - init_src_data(item->util); - } - src_data = item->util; - - if (pulling_head) { - origin = src; - src_data->head_status |= 1; - } else if (skip_prefix(line, "branch ", &origin)) { - origin_data->is_local_branch = 1; - string_list_append(&src_data->branch, origin); - src_data->head_status |= 2; - } else if (skip_prefix(line, "tag ", &tag_name)) { - origin = line; - string_list_append(&src_data->tag, tag_name); - src_data->head_status |= 2; - } else if (skip_prefix(line, "remote-tracking branch ", &origin)) { - string_list_append(&src_data->r_branch, origin); - src_data->head_status |= 2; - } else { - origin = src; - string_list_append(&src_data->generic, line); - src_data->head_status |= 2; - } - - if (!strcmp(".", src) || !strcmp(src, origin)) { - int len = strlen(origin); - if (origin[0] == '\'' && origin[len - 1] == '\'') - origin = xmemdupz(origin + 1, len - 2); - } else - origin = xstrfmt("%s of %s", origin, src); - if (strcmp(".", src)) - origin_data->is_local_branch = 0; - string_list_append(&origins, origin)->util = origin_data; - return 0; -} - -static void print_joined(const char *singular, const char *plural, - struct string_list *list, struct strbuf *out) -{ - if (list->nr == 0) - return; - if (list->nr == 1) { - strbuf_addf(out, "%s%s", singular, list->items[0].string); - } else { - int i; - strbuf_addstr(out, plural); - for (i = 0; i < list->nr - 1; i++) - strbuf_addf(out, "%s%s", i > 0 ? ", " : "", - list->items[i].string); - strbuf_addf(out, " and %s", list->items[list->nr - 1].string); - } -} - -static void add_branch_desc(struct strbuf *out, const char *name) -{ - struct strbuf desc = STRBUF_INIT; - - if (!read_branch_desc(&desc, name)) { - const char *bp = desc.buf; - while (*bp) { - const char *ep = strchrnul(bp, '\n'); - if (*ep) - ep++; - strbuf_addf(out, " : %.*s", (int)(ep - bp), bp); - bp = ep; - } - strbuf_complete_line(out); - } - strbuf_release(&desc); -} - -#define util_as_integral(elem) ((intptr_t)((elem)->util)) - -static void record_person_from_buf(int which, struct string_list *people, - const char *buffer) -{ - char *name_buf, *name, *name_end; - struct string_list_item *elem; - const char *field; - - field = (which == 'a') ? "\nauthor " : "\ncommitter "; - name = strstr(buffer, field); - if (!name) - return; - name += strlen(field); - name_end = strchrnul(name, '<'); - if (*name_end) - name_end--; - while (isspace(*name_end) && name <= name_end) - name_end--; - if (name_end < name) - return; - name_buf = xmemdupz(name, name_end - name + 1); - - elem = string_list_lookup(people, name_buf); - if (!elem) { - elem = string_list_insert(people, name_buf); - elem->util = (void *)0; - } - elem->util = (void*)(util_as_integral(elem) + 1); - free(name_buf); -} - - -static void record_person(int which, struct string_list *people, - struct commit *commit) -{ - const char *buffer = get_commit_buffer(commit, NULL); - record_person_from_buf(which, people, buffer); - unuse_commit_buffer(commit, buffer); -} - -static int cmp_string_list_util_as_integral(const void *a_, const void *b_) -{ - const struct string_list_item *a = a_, *b = b_; - return util_as_integral(b) - util_as_integral(a); -} - -static void add_people_count(struct strbuf *out, struct string_list *people) -{ - if (people->nr == 1) - strbuf_addstr(out, people->items[0].string); - else if (people->nr == 2) - strbuf_addf(out, "%s (%d) and %s (%d)", - people->items[0].string, - (int)util_as_integral(&people->items[0]), - people->items[1].string, - (int)util_as_integral(&people->items[1])); - else if (people->nr) - strbuf_addf(out, "%s (%d) and others", - people->items[0].string, - (int)util_as_integral(&people->items[0])); -} - -static void credit_people(struct strbuf *out, - struct string_list *them, - int kind) -{ - const char *label; - const char *me; - - if (kind == 'a') { - label = "By"; - me = git_author_info(IDENT_NO_DATE); - } else { - label = "Via"; - me = git_committer_info(IDENT_NO_DATE); - } - - if (!them->nr || - (them->nr == 1 && - me && - skip_prefix(me, them->items->string, &me) && - starts_with(me, " <"))) - return; - strbuf_addf(out, "\n%c %s ", comment_line_char, label); - add_people_count(out, them); -} - -static void add_people_info(struct strbuf *out, - struct string_list *authors, - struct string_list *committers) -{ - QSORT(authors->items, authors->nr, - cmp_string_list_util_as_integral); - QSORT(committers->items, committers->nr, - cmp_string_list_util_as_integral); - - credit_people(out, authors, 'a'); - credit_people(out, committers, 'c'); -} - -static void shortlog(const char *name, - struct origin_data *origin_data, - struct commit *head, - struct rev_info *rev, - struct fmt_merge_msg_opts *opts, - struct strbuf *out) -{ - int i, count = 0; - struct commit *commit; - struct object *branch; - struct string_list subjects = STRING_LIST_INIT_DUP; - struct string_list authors = STRING_LIST_INIT_DUP; - struct string_list committers = STRING_LIST_INIT_DUP; - int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; - struct strbuf sb = STRBUF_INIT; - const struct object_id *oid = &origin_data->oid; - int limit = opts->shortlog_len; - - branch = deref_tag(the_repository, parse_object(the_repository, oid), - oid_to_hex(oid), - the_hash_algo->hexsz); - if (!branch || branch->type != OBJ_COMMIT) - return; - - setup_revisions(0, NULL, rev, NULL); - add_pending_object(rev, branch, name); - add_pending_object(rev, &head->object, "^HEAD"); - head->object.flags |= UNINTERESTING; - if (prepare_revision_walk(rev)) - die("revision walk setup failed"); - while ((commit = get_revision(rev)) != NULL) { - struct pretty_print_context ctx = {0}; - - if (commit->parents && commit->parents->next) { - /* do not list a merge but count committer */ - if (opts->credit_people) - record_person('c', &committers, commit); - continue; - } - if (!count && opts->credit_people) - /* the 'tip' committer */ - record_person('c', &committers, commit); - if (opts->credit_people) - record_person('a', &authors, commit); - count++; - if (subjects.nr > limit) - continue; - - format_commit_message(commit, "%s", &sb, &ctx); - strbuf_ltrim(&sb); - - if (!sb.len) - string_list_append(&subjects, - oid_to_hex(&commit->object.oid)); - else - string_list_append_nodup(&subjects, - strbuf_detach(&sb, NULL)); - } - - if (opts->credit_people) - add_people_info(out, &authors, &committers); - if (count > limit) - strbuf_addf(out, "\n* %s: (%d commits)\n", name, count); - else - strbuf_addf(out, "\n* %s:\n", name); - - if (origin_data->is_local_branch && use_branch_desc) - add_branch_desc(out, name); - - for (i = 0; i < subjects.nr; i++) - if (i >= limit) - strbuf_addstr(out, " ...\n"); - else - strbuf_addf(out, " %s\n", subjects.items[i].string); - - clear_commit_marks((struct commit *)branch, flags); - clear_commit_marks(head, flags); - free_commit_list(rev->commits); - rev->commits = NULL; - rev->pending.nr = 0; - - string_list_clear(&authors, 0); - string_list_clear(&committers, 0); - string_list_clear(&subjects, 0); -} - -static void fmt_merge_msg_title(struct strbuf *out, - const char *current_branch) -{ - int i = 0; - char *sep = ""; - - strbuf_addstr(out, "Merge "); - for (i = 0; i < srcs.nr; i++) { - struct src_data *src_data = srcs.items[i].util; - const char *subsep = ""; - - strbuf_addstr(out, sep); - sep = "; "; - - if (src_data->head_status == 1) { - strbuf_addstr(out, srcs.items[i].string); - continue; - } - if (src_data->head_status == 3) { - subsep = ", "; - strbuf_addstr(out, "HEAD"); - } - if (src_data->branch.nr) { - strbuf_addstr(out, subsep); - subsep = ", "; - print_joined("branch ", "branches ", &src_data->branch, - out); - } - if (src_data->r_branch.nr) { - strbuf_addstr(out, subsep); - subsep = ", "; - print_joined("remote-tracking branch ", "remote-tracking branches ", - &src_data->r_branch, out); - } - if (src_data->tag.nr) { - strbuf_addstr(out, subsep); - subsep = ", "; - print_joined("tag ", "tags ", &src_data->tag, out); - } - if (src_data->generic.nr) { - strbuf_addstr(out, subsep); - print_joined("commit ", "commits ", &src_data->generic, - out); - } - if (strcmp(".", srcs.items[i].string)) - strbuf_addf(out, " of %s", srcs.items[i].string); - } - - if (!strcmp("master", current_branch)) - strbuf_addch(out, '\n'); - else - strbuf_addf(out, " into %s\n", current_branch); -} - -static void fmt_tag_signature(struct strbuf *tagbuf, - struct strbuf *sig, - const char *buf, - unsigned long len) -{ - const char *tag_body = strstr(buf, "\n\n"); - if (tag_body) { - tag_body += 2; - strbuf_add(tagbuf, tag_body, buf + len - tag_body); - } - strbuf_complete_line(tagbuf); - if (sig->len) { - strbuf_addch(tagbuf, '\n'); - strbuf_add_commented_lines(tagbuf, sig->buf, sig->len); - } -} - -static void fmt_merge_msg_sigs(struct strbuf *out) -{ - int i, tag_number = 0, first_tag = 0; - struct strbuf tagbuf = STRBUF_INIT; - - for (i = 0; i < origins.nr; i++) { - struct object_id *oid = origins.items[i].util; - enum object_type type; - unsigned long size, len; - char *buf = read_object_file(oid, &type, &size); - struct signature_check sigc = { 0 }; - struct strbuf sig = STRBUF_INIT; - - if (!buf || type != OBJ_TAG) - goto next; - len = parse_signature(buf, size); - - if (size == len) - ; /* merely annotated */ - else if (check_signature(buf, len, buf + len, size - len, &sigc) && - !sigc.gpg_output) - strbuf_addstr(&sig, "gpg verification failed.\n"); - else - strbuf_addstr(&sig, sigc.gpg_output); - signature_check_clear(&sigc); - - if (!tag_number++) { - fmt_tag_signature(&tagbuf, &sig, buf, len); - first_tag = i; - } else { - if (tag_number == 2) { - struct strbuf tagline = STRBUF_INIT; - strbuf_addch(&tagline, '\n'); - strbuf_add_commented_lines(&tagline, - origins.items[first_tag].string, - strlen(origins.items[first_tag].string)); - strbuf_insert(&tagbuf, 0, tagline.buf, - tagline.len); - strbuf_release(&tagline); - } - strbuf_addch(&tagbuf, '\n'); - strbuf_add_commented_lines(&tagbuf, - origins.items[i].string, - strlen(origins.items[i].string)); - fmt_tag_signature(&tagbuf, &sig, buf, len); - } - strbuf_release(&sig); - next: - free(buf); - } - if (tagbuf.len) { - strbuf_addch(out, '\n'); - strbuf_addbuf(out, &tagbuf); - } - strbuf_release(&tagbuf); -} - -static void find_merge_parents(struct merge_parents *result, - struct strbuf *in, struct object_id *head) -{ - struct commit_list *parents; - struct commit *head_commit; - int pos = 0, i, j; - - parents = NULL; - while (pos < in->len) { - int len; - char *p = in->buf + pos; - char *newline = strchr(p, '\n'); - const char *q; - struct object_id oid; - struct commit *parent; - struct object *obj; - - len = newline ? newline - p : strlen(p); - pos += len + !!newline; - - if (parse_oid_hex(p, &oid, &q) || - q[0] != '\t' || - q[1] != '\t') - continue; /* skip not-for-merge */ - /* - * Do not use get_merge_parent() here; we do not have - * "name" here and we do not want to contaminate its - * util field yet. - */ - obj = parse_object(the_repository, &oid); - parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT); - if (!parent) - continue; - commit_list_insert(parent, &parents); - add_merge_parent(result, &obj->oid, &parent->object.oid); - } - head_commit = lookup_commit(the_repository, head); - if (head_commit) - commit_list_insert(head_commit, &parents); - reduce_heads_replace(&parents); - - while (parents) { - struct commit *cmit = pop_commit(&parents); - for (i = 0; i < result->nr; i++) - if (oideq(&result->item[i].commit, &cmit->object.oid)) - result->item[i].used = 1; - } - - for (i = j = 0; i < result->nr; i++) { - if (result->item[i].used) { - if (i != j) - result->item[j] = result->item[i]; - j++; - } - } - result->nr = j; -} - -int fmt_merge_msg(struct strbuf *in, struct strbuf *out, - struct fmt_merge_msg_opts *opts) -{ - int i = 0, pos = 0; - struct object_id head_oid; - const char *current_branch; - void *current_branch_to_free; - struct merge_parents merge_parents; - - memset(&merge_parents, 0, sizeof(merge_parents)); - - /* get current branch */ - current_branch = current_branch_to_free = - resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL); - if (!current_branch) - die("No current branch"); - if (starts_with(current_branch, "refs/heads/")) - current_branch += 11; - - find_merge_parents(&merge_parents, in, &head_oid); - - /* get a line */ - while (pos < in->len) { - int len; - char *newline, *p = in->buf + pos; - - newline = strchr(p, '\n'); - len = newline ? newline - p : strlen(p); - pos += len + !!newline; - i++; - p[len] = 0; - if (handle_line(p, &merge_parents)) - die("error in line %d: %.*s", i, len, p); - } - - if (opts->add_title && srcs.nr) - fmt_merge_msg_title(out, current_branch); - - if (origins.nr) - fmt_merge_msg_sigs(out); - - if (opts->shortlog_len) { - struct commit *head; - struct rev_info rev; - - head = lookup_commit_or_die(&head_oid, "HEAD"); - repo_init_revisions(the_repository, &rev, NULL); - rev.commit_format = CMIT_FMT_ONELINE; - rev.ignore_merges = 1; - rev.limited = 1; - - strbuf_complete_line(out); - - for (i = 0; i < origins.nr; i++) - shortlog(origins.items[i].string, - origins.items[i].util, - head, &rev, opts, out); - } - - strbuf_complete_line(out); - free(current_branch_to_free); - free(merge_parents.item); - return 0; -} - int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) { const char *inpath = NULL; diff --git a/builtin/grep.c b/builtin/grep.c index 99e2685090..5e150f5825 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -295,6 +295,38 @@ static int grep_cmd_config(const char *var, const char *value, void *cb) return st; } +static void grep_source_name(struct grep_opt *opt, const char *filename, + int tree_name_len, struct strbuf *out) +{ + strbuf_reset(out); + + if (opt->null_following_name) { + if (opt->relative && opt->prefix_length) { + struct strbuf rel_buf = STRBUF_INIT; + const char *rel_name = + relative_path(filename + tree_name_len, + opt->prefix, &rel_buf); + + if (tree_name_len) + strbuf_add(out, filename, tree_name_len); + + strbuf_addstr(out, rel_name); + strbuf_release(&rel_buf); + } else { + strbuf_addstr(out, filename); + } + return; + } + + if (opt->relative && opt->prefix_length) + quote_path_relative(filename + tree_name_len, opt->prefix, out); + else + quote_c_style(filename + tree_name_len, out, NULL, 0); + + if (tree_name_len) + strbuf_insert(out, 0, filename, tree_name_len); +} + static int grep_oid(struct grep_opt *opt, const struct object_id *oid, const char *filename, int tree_name_len, const char *path) @@ -302,13 +334,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, struct strbuf pathbuf = STRBUF_INIT; struct grep_source gs; - if (opt->relative && opt->prefix_length) { - quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf); - strbuf_insert(&pathbuf, 0, filename, tree_name_len); - } else { - strbuf_addstr(&pathbuf, filename); - } - + grep_source_name(opt, filename, tree_name_len, &pathbuf); grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); strbuf_release(&pathbuf); @@ -334,11 +360,7 @@ static int grep_file(struct grep_opt *opt, const char *filename) struct strbuf buf = STRBUF_INIT; struct grep_source gs; - if (opt->relative && opt->prefix_length) - quote_path_relative(filename, opt->prefix, &buf); - else - strbuf_addstr(&buf, filename); - + grep_source_name(opt, filename, 0, &buf); grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); strbuf_release(&buf); @@ -679,8 +701,6 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, fill_directory(&dir, opt->repo->index, pathspec); for (i = 0; i < dir.nr; i++) { - if (!dir_path_match(opt->repo->index, dir.entries[i], pathspec, 0, NULL)) - continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) break; diff --git a/builtin/help.c b/builtin/help.c index e5590d7787..c024110531 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -242,7 +242,7 @@ static int add_man_viewer_cmd(const char *name, static int add_man_viewer_info(const char *var, const char *value) { const char *name, *subkey; - int namelen; + size_t namelen; if (parse_config_key(var, "man", &name, &namelen, &subkey) < 0 || !name) return 0; diff --git a/builtin/index-pack.c b/builtin/index-pack.c index d967d188a3..f176dd28c8 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1368,9 +1368,8 @@ static void fix_unresolved_deltas(struct hashfile *f) continue; oid_array_append(&to_fetch, &d->oid); } - if (to_fetch.nr) - promisor_remote_get_direct(the_repository, - to_fetch.oid, to_fetch.nr); + promisor_remote_get_direct(the_repository, + to_fetch.oid, to_fetch.nr); oid_array_clear(&to_fetch); } diff --git a/builtin/log.c b/builtin/log.c index a5c3ace9a0..bef7403d5e 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -166,15 +166,18 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, int quiet = 0, source = 0, mailmap; 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_exclude_config = 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}; + &decorate_refs_exclude, + &decorate_refs_exclude_config}; static struct revision_sources revision_sources; 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_ALIAS(0, "mailmap", "use-mailmap"), 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, @@ -238,7 +241,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, } if (decoration_style) { + const struct string_list *config_exclude = + repo_config_get_value_multi(the_repository, + "log.excludeDecoration"); + + if (config_exclude) { + struct string_list_item *item; + for_each_string_list_item(item, config_exclude) + string_list_append(&decorate_refs_exclude_config, + item->string); + } + rev->show_decorations = 1; + load_ref_decorations(&decoration_filter, decoration_style); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index f069a028ce..b87c22ac24 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -128,8 +128,9 @@ static void show_dir_entry(const struct index_state *istate, if (len > ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); - if (!dir_path_match(istate, ent, &pathspec, len, ps_matched)) - return; + /* If ps_matches is non-NULL, figure out which pathspec(s) match. */ + if (ps_matched) + dir_path_match(istate, ent, &pathspec, len, ps_matched); fputs(tag, stdout); write_eolinfo(istate, NULL, ent->name); diff --git a/builtin/merge.c b/builtin/merge.c index df83ba2a80..97066a5632 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -40,6 +40,7 @@ #include "branch.h" #include "commit-reach.h" #include "wt-status.h" +#include "commit-graph.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -82,6 +83,7 @@ static int show_progress = -1; static int default_to_upstream = 1; static int signoff; static const char *sign_commit; +static int autostash; static int no_verify; static struct strategy all_strategy[] = { @@ -286,6 +288,7 @@ static struct option builtin_merge_options[] = { OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + OPT_AUTOSTASH(&autostash), OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")), @@ -475,6 +478,7 @@ static void finish(struct commit *head_commit, /* Run a post-merge hook */ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL); + apply_autostash(git_path_merge_autostash(the_repository)); strbuf_release(&reflog_message); } @@ -636,6 +640,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) return 0; } else if (!strcmp(k, "gpg.mintrustlevel")) { check_trust_level = 0; + } else if (!strcmp(k, "merge.autostash")) { + autostash = git_config_bool(k, v); + return 0; } status = fmt_merge_msg_config(k, v, cb); @@ -1283,6 +1290,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (abort_current_merge) { int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; + struct strbuf stash_oid = STRBUF_INIT; if (orig_argc != 2) usage_msg_opt(_("--abort expects no arguments"), @@ -1291,8 +1299,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!file_exists(git_path_merge_head(the_repository))) die(_("There is no merge to abort (MERGE_HEAD missing).")); + if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository), + READ_ONELINER_SKIP_IF_EMPTY)) + unlink(git_path_merge_autostash(the_repository)); + /* Invoke 'git reset --merge' */ ret = cmd_reset(nargc, nargv, prefix); + + if (stash_oid.len) + apply_autostash_oid(stash_oid.buf); + + strbuf_release(&stash_oid); goto done; } @@ -1515,6 +1532,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } + if (autostash) + create_autostash(the_repository, + git_path_merge_autostash(the_repository), + "merge"); if (checkout_fast_forward(the_repository, &head_commit->object.oid, &commit->object.oid, @@ -1581,6 +1602,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (fast_forward == FF_ONLY) die(_("Not possible to fast-forward, aborting.")); + if (autostash) + create_autostash(the_repository, + git_path_merge_autostash(the_repository), + "merge"); + /* We are going to make a new commit. */ git_committer_info(IDENT_STRICT); @@ -1675,9 +1701,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix) head_commit); } - if (squash) + if (squash) { finish(head_commit, remoteheads, NULL, NULL); - else + + git_test_write_commit_graph_or_die(); + } else write_merge_state(remoteheads); if (merge_was_ok) diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index 48c5e78e33..b7b9281a8c 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -1,54 +1,12 @@ #include "builtin.h" -#include "cache.h" -#include "progress.h" #include "parse-options.h" -#include "packfile.h" -#include "object-store.h" +#include "prune-packed.h" static const char * const prune_packed_usage[] = { N_("git prune-packed [-n | --dry-run] [-q | --quiet]"), NULL }; -static struct progress *progress; - -static int prune_subdir(unsigned int nr, const char *path, void *data) -{ - int *opts = data; - display_progress(progress, nr + 1); - if (!(*opts & PRUNE_PACKED_DRY_RUN)) - rmdir(path); - return 0; -} - -static int prune_object(const struct object_id *oid, const char *path, - void *data) -{ - int *opts = data; - - if (!has_object_pack(oid)) - return 0; - - if (*opts & PRUNE_PACKED_DRY_RUN) - printf("rm -f %s\n", path); - else - unlink_or_warn(path); - return 0; -} - -void prune_packed_objects(int opts) -{ - if (opts & PRUNE_PACKED_VERBOSE) - progress = start_delayed_progress(_("Removing duplicate objects"), 256); - - for_each_loose_file_in_objdir(get_object_directory(), - prune_object, NULL, prune_subdir, &opts); - - /* Ensure we show 100% before finishing progress */ - display_progress(progress, 256); - stop_progress(&progress); -} - int cmd_prune_packed(int argc, const char **argv, const char *prefix) { int opts = isatty(2) ? PRUNE_PACKED_VERBOSE : 0; diff --git a/builtin/prune.c b/builtin/prune.c index 2b76872ad2..fd9acc7222 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -6,6 +6,7 @@ #include "reachable.h" #include "parse-options.h" #include "progress.h" +#include "prune-packed.h" #include "object-store.h" static const char * const prune_usage[] = { diff --git a/builtin/pull.c b/builtin/pull.c index b5d51ea74f..f1fa6db74e 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -164,7 +164,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG), OPT_BOOL(0, "autostash", &opt_autostash, - N_("automatically stash/stash pop before and after rebase")), + N_("automatically stash/stash pop before and after")), OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"), N_("merge strategy to use"), 0), @@ -695,6 +695,10 @@ static int run_merge(void) argv_array_pushv(&args, opt_strategy_opts.argv); if (opt_gpg_sign) argv_array_push(&args, opt_gpg_sign); + if (opt_autostash == 0) + argv_array_push(&args, "--no-autostash"); + else if (opt_autostash == 1) + argv_array_push(&args, "--autostash"); if (opt_allow_unrelated_histories > 0) argv_array_push(&args, "--allow-unrelated-histories"); @@ -942,9 +946,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (get_oid("HEAD", &orig_head)) oidclr(&orig_head); - if (!opt_rebase && opt_autostash != -1) - die(_("--[no-]autostash option is only valid with --rebase.")); - autostash = config_autostash; if (opt_rebase) { if (opt_autostash != -1) diff --git a/builtin/rebase.c b/builtin/rebase.c index c466923869..1c0a49c642 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -27,6 +27,9 @@ #include "branch.h" #include "sequencer.h" #include "rebase-interactive.h" +#include "reset.h" + +#define DEFAULT_REFLOG_ACTION "rebase" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] " @@ -590,15 +593,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o return path.buf; } -/* Read one file, then strip line endings */ -static int read_one(const char *path, struct strbuf *buf) -{ - if (strbuf_read_file(buf, path, 0) < 0) - return error_errno(_("could not read '%s'"), path); - strbuf_trim_trailing_newline(buf); - return 0; -} - /* Initialize the rebase options from the state directory. */ static int read_basic_state(struct rebase_options *opts) { @@ -606,8 +600,10 @@ static int read_basic_state(struct rebase_options *opts) struct strbuf buf = STRBUF_INIT; struct object_id oid; - if (read_one(state_dir_path("head-name", opts), &head_name) || - read_one(state_dir_path("onto", opts), &buf)) + if (!read_oneliner(&head_name, state_dir_path("head-name", opts), + READ_ONELINER_WARN_MISSING) || + !read_oneliner(&buf, state_dir_path("onto", opts), + READ_ONELINER_WARN_MISSING)) return -1; opts->head_name = starts_with(head_name.buf, "refs/") ? xstrdup(head_name.buf) : NULL; @@ -623,9 +619,11 @@ static int read_basic_state(struct rebase_options *opts) */ strbuf_reset(&buf); if (file_exists(state_dir_path("orig-head", opts))) { - if (read_one(state_dir_path("orig-head", opts), &buf)) + if (!read_oneliner(&buf, state_dir_path("orig-head", opts), + READ_ONELINER_WARN_MISSING)) return -1; - } else if (read_one(state_dir_path("head", opts), &buf)) + } else if (!read_oneliner(&buf, state_dir_path("head", opts), + READ_ONELINER_WARN_MISSING)) return -1; if (get_oid(buf.buf, &opts->orig_head)) return error(_("invalid orig-head: '%s'"), buf.buf); @@ -645,8 +643,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("allow_rerere_autoupdate", opts), - &buf)) + if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), + READ_ONELINER_WARN_MISSING)) return -1; if (!strcmp(buf.buf, "--rerere-autoupdate")) opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE; @@ -659,8 +657,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("gpg_sign_opt", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("gpg_sign_opt", opts), - &buf)) + if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), + READ_ONELINER_WARN_MISSING)) return -1; free(opts->gpg_sign_opt); opts->gpg_sign_opt = xstrdup(buf.buf); @@ -668,7 +666,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("strategy", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("strategy", opts), &buf)) + if (!read_oneliner(&buf, state_dir_path("strategy", opts), + READ_ONELINER_WARN_MISSING)) return -1; free(opts->strategy); opts->strategy = xstrdup(buf.buf); @@ -676,7 +675,8 @@ static int read_basic_state(struct rebase_options *opts) if (file_exists(state_dir_path("strategy_opts", opts))) { strbuf_reset(&buf); - if (read_one(state_dir_path("strategy_opts", opts), &buf)) + if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), + READ_ONELINER_WARN_MISSING)) return -1; free(opts->strategy_opts); opts->strategy_opts = xstrdup(buf.buf); @@ -719,51 +719,6 @@ static int rebase_write_basic_state(struct rebase_options *opts) return 0; } -static int apply_autostash(struct rebase_options *opts) -{ - const char *path = state_dir_path("autostash", opts); - struct strbuf autostash = STRBUF_INIT; - struct child_process stash_apply = CHILD_PROCESS_INIT; - - if (!file_exists(path)) - return 0; - - if (read_one(path, &autostash)) - return error(_("Could not read '%s'"), path); - /* Ensure that the hash is not mistaken for a number */ - strbuf_addstr(&autostash, "^0"); - argv_array_pushl(&stash_apply.args, - "stash", "apply", autostash.buf, NULL); - stash_apply.git_cmd = 1; - stash_apply.no_stderr = stash_apply.no_stdout = - stash_apply.no_stdin = 1; - if (!run_command(&stash_apply)) - printf(_("Applied autostash.\n")); - else { - struct argv_array args = ARGV_ARRAY_INIT; - int res = 0; - - argv_array_pushl(&args, - "stash", "store", "-m", "autostash", "-q", - autostash.buf, NULL); - if (run_command_v_opt(args.argv, RUN_GIT_CMD)) - res = error(_("Cannot store %s"), autostash.buf); - argv_array_clear(&args); - strbuf_release(&autostash); - if (res) - return res; - - fprintf(stderr, - _("Applying autostash resulted in conflicts.\n" - "Your changes are safe in the stash.\n" - "You can run \"git stash pop\" or \"git stash drop\" " - "at any time.\n")); - } - - strbuf_release(&autostash); - return 0; -} - static int finish_rebase(struct rebase_options *opts) { struct strbuf dir = STRBUF_INIT; @@ -771,7 +726,7 @@ static int finish_rebase(struct rebase_options *opts) int ret = 0; delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - apply_autostash(opts); + apply_autostash(state_dir_path("autostash", opts)); close_object_store(the_repository->objects); /* * We ignore errors in 'gc --auto', since the @@ -816,144 +771,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value) } } -#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" - -#define RESET_HEAD_DETACH (1<<0) -#define RESET_HEAD_HARD (1<<1) -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) -#define RESET_HEAD_REFS_ONLY (1<<3) -#define RESET_ORIG_HEAD (1<<4) - -static int reset_head(struct object_id *oid, const char *action, - const char *switch_to_branch, unsigned flags, - const char *reflog_orig_head, const char *reflog_head) -{ - unsigned detach_head = flags & RESET_HEAD_DETACH; - unsigned reset_hard = flags & RESET_HEAD_HARD; - unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; - unsigned refs_only = flags & RESET_HEAD_REFS_ONLY; - unsigned update_orig_head = flags & RESET_ORIG_HEAD; - struct object_id head_oid; - struct tree_desc desc[2] = { { NULL }, { NULL } }; - struct lock_file lock = LOCK_INIT; - struct unpack_trees_options unpack_tree_opts; - struct tree *tree; - const char *reflog_action; - struct strbuf msg = STRBUF_INIT; - size_t prefix_len; - struct object_id *orig = NULL, oid_orig, - *old_orig = NULL, oid_old_orig; - int ret = 0, nr = 0; - - if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) - BUG("Not a fully qualified branch: '%s'", switch_to_branch); - - if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) { - ret = -1; - goto leave_reset_head; - } - - if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) { - ret = error(_("could not determine HEAD revision")); - goto leave_reset_head; - } - - if (!oid) - oid = &head_oid; - - if (refs_only) - goto reset_head_refs; - - memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); - setup_unpack_trees_porcelain(&unpack_tree_opts, action); - unpack_tree_opts.head_idx = 1; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; - unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update = 1; - unpack_tree_opts.merge = 1; - init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL); - if (!detach_head) - unpack_tree_opts.reset = 1; - - if (repo_read_index_unmerged(the_repository) < 0) { - ret = error(_("could not read index")); - goto leave_reset_head; - } - - if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) { - ret = error(_("failed to find tree of %s"), - oid_to_hex(&head_oid)); - goto leave_reset_head; - } - - if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) { - ret = error(_("failed to find tree of %s"), oid_to_hex(oid)); - goto leave_reset_head; - } - - if (unpack_trees(nr, desc, &unpack_tree_opts)) { - ret = -1; - goto leave_reset_head; - } - - tree = parse_tree_indirect(oid); - prime_cache_tree(the_repository, the_repository->index, tree); - - if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) { - ret = error(_("could not write index")); - goto leave_reset_head; - } - -reset_head_refs: - reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); - strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); - prefix_len = msg.len; - - if (update_orig_head) { - if (!get_oid("ORIG_HEAD", &oid_old_orig)) - old_orig = &oid_old_orig; - if (!get_oid("HEAD", &oid_orig)) { - orig = &oid_orig; - if (!reflog_orig_head) { - strbuf_addstr(&msg, "updating ORIG_HEAD"); - reflog_orig_head = msg.buf; - } - update_ref(reflog_orig_head, "ORIG_HEAD", orig, - old_orig, 0, UPDATE_REFS_MSG_ON_ERR); - } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig, 0); - } - - if (!reflog_head) { - strbuf_setlen(&msg, prefix_len); - strbuf_addstr(&msg, "updating HEAD"); - reflog_head = msg.buf; - } - if (!switch_to_branch) - ret = update_ref(reflog_head, "HEAD", oid, orig, - detach_head ? REF_NO_DEREF : 0, - UPDATE_REFS_MSG_ON_ERR); - else { - ret = update_ref(reflog_head, switch_to_branch, oid, - NULL, 0, UPDATE_REFS_MSG_ON_ERR); - if (!ret) - ret = create_symref("HEAD", switch_to_branch, - reflog_head); - } - if (run_hook) - run_hook_le(NULL, "post-checkout", - oid_to_hex(orig ? orig : &null_oid), - oid_to_hex(oid), "1", NULL); - -leave_reset_head: - strbuf_release(&msg); - rollback_lock_file(&lock); - while (nr) - free((void *)desc[--nr].buffer); - return ret; -} - static int move_to_original_branch(struct rebase_options *opts) { struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; @@ -969,8 +786,10 @@ static int move_to_original_branch(struct rebase_options *opts) opts->head_name, oid_to_hex(&opts->onto->object.oid)); strbuf_addf(&head_reflog, "rebase finished: returning to %s", opts->head_name); - ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY, - orig_head_reflog.buf, head_reflog.buf); + ret = reset_head(the_repository, NULL, "", opts->head_name, + RESET_HEAD_REFS_ONLY, + orig_head_reflog.buf, head_reflog.buf, + DEFAULT_REFLOG_ACTION); strbuf_release(&orig_head_reflog); strbuf_release(&head_reflog); @@ -1058,8 +877,9 @@ static int run_am(struct rebase_options *opts) free(rebased_patches); argv_array_clear(&am.args); - reset_head(&opts->orig_head, "checkout", opts->head_name, 0, - "HEAD", NULL); + reset_head(the_repository, &opts->orig_head, "checkout", + opts->head_name, 0, + "HEAD", NULL, DEFAULT_REFLOG_ACTION); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n" @@ -1218,7 +1038,7 @@ finished_rebase: } else if (status == 2) { struct strbuf dir = STRBUF_INIT; - apply_autostash(opts); + apply_autostash(state_dir_path("autostash", opts)); strbuf_addstr(&dir, opts->state_dir); remove_dir_recursively(&dir, 0); strbuf_release(&dir); @@ -1459,7 +1279,6 @@ static int check_exec_cmd(const char *cmd) return 0; } - int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = REBASE_OPTIONS_INIT; @@ -1562,8 +1381,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_BOOL(0, "autostash", &options.autostash, - N_("automatically stash/stash pop before and after")), + OPT_AUTOSTASH(&options.autostash), OPT_STRING_LIST('x', "exec", &exec, N_("exec"), N_("add exec lines after each commit of the " "editable list")), @@ -1720,8 +1538,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) rerere_clear(the_repository, &merge_rr); string_list_clear(&merge_rr, 1); - if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD, - NULL, NULL) < 0) + if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD, + NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) die(_("could not discard worktree changes")); remove_branch_state(the_repository, 0); if (read_basic_state(&options)) @@ -1738,9 +1556,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (read_basic_state(&options)) exit(1); - if (reset_head(&options.orig_head, "reset", + if (reset_head(the_repository, &options.orig_head, "reset", options.head_name, RESET_HEAD_HARD, - NULL, NULL) < 0) + NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) die(_("could not move back to %s"), oid_to_hex(&options.orig_head)); remove_branch_state(the_repository, 0); @@ -1748,6 +1566,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) goto cleanup; } case ACTION_QUIT: { + save_autostash(state_dir_path("autostash", &options)); if (options.type == REBASE_MERGE) { struct replay_opts replay = REPLAY_OPTS_INIT; @@ -2098,49 +1917,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("could not read index")); if (options.autostash) { - struct lock_file lock_file = LOCK_INIT; - int fd; - - fd = hold_locked_index(&lock_file, 0); - refresh_cache(REFRESH_QUIET); - if (0 <= fd) - repo_update_index_if_able(the_repository, &lock_file); - rollback_lock_file(&lock_file); - - if (has_unstaged_changes(the_repository, 1) || - has_uncommitted_changes(the_repository, 1)) { - const char *autostash = - state_dir_path("autostash", &options); - struct child_process stash = CHILD_PROCESS_INIT; - struct object_id oid; - - argv_array_pushl(&stash.args, - "stash", "create", "autostash", NULL); - stash.git_cmd = 1; - stash.no_stdin = 1; - strbuf_reset(&buf); - if (capture_command(&stash, &buf, GIT_MAX_HEXSZ)) - die(_("Cannot autostash")); - strbuf_trim_trailing_newline(&buf); - if (get_oid(buf.buf, &oid)) - die(_("Unexpected stash response: '%s'"), - buf.buf); - strbuf_reset(&buf); - strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV); - - if (safe_create_leading_directories_const(autostash)) - die(_("Could not create directory for '%s'"), - options.state_dir); - write_file(autostash, "%s", oid_to_hex(&oid)); - printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(NULL, "reset --hard", - NULL, RESET_HEAD_HARD, NULL, NULL) < 0) - die(_("could not reset --hard")); - - if (discard_index(the_repository->index) < 0 || - repo_read_index(the_repository) < 0) - die(_("could not read index")); - } + create_autostash(the_repository, state_dir_path("autostash", &options), + DEFAULT_REFLOG_ACTION); } if (require_clean_work_tree(the_repository, "rebase", @@ -2174,10 +1952,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&buf, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.switch_to); - if (reset_head(&options.orig_head, "checkout", + if (reset_head(the_repository, + &options.orig_head, "checkout", options.head_name, RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, buf.buf) < 0) { + NULL, buf.buf, + DEFAULT_REFLOG_ACTION) < 0) { ret = !!error(_("could not switch to " "%s"), options.switch_to); @@ -2249,10 +2029,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "%s: checkout %s", getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name); - if (reset_head(&options.onto->object.oid, "checkout", NULL, + if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL, RESET_HEAD_DETACH | RESET_ORIG_HEAD | RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, msg.buf)) + NULL, msg.buf, DEFAULT_REFLOG_ACTION)) die(_("Could not detach HEAD")); strbuf_release(&msg); @@ -2267,8 +2047,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "rebase finished: %s onto %s", options.head_name ? options.head_name : "detached HEAD", oid_to_hex(&options.onto->object.oid)); - reset_head(NULL, "Fast-forwarded", options.head_name, - RESET_HEAD_REFS_ONLY, "HEAD", msg.buf); + reset_head(the_repository, NULL, "Fast-forwarded", options.head_name, + RESET_HEAD_REFS_ONLY, "HEAD", msg.buf, + DEFAULT_REFLOG_ACTION); strbuf_release(&msg); ret = !!finish_rebase(&options); goto cleanup; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index d46147f709..a00f91c1a0 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -499,12 +499,27 @@ static char *find_header(const char *msg, size_t len, const char *key, return NULL; } +/* + * Return zero if a and b are equal up to n bytes and nonzero if they are not. + * This operation is guaranteed to run in constant time to avoid leaking data. + */ +static int constant_memequal(const char *a, const char *b, size_t n) +{ + int res = 0; + size_t i; + + for (i = 0; i < n; i++) + res |= a[i] ^ b[i]; + return res; +} + static const char *check_nonce(const char *buf, size_t len) { char *nonce = find_header(buf, len, "nonce", NULL); timestamp_t stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; + size_t noncelen; if (!nonce) { retval = NONCE_MISSING; @@ -546,8 +561,14 @@ static const char *check_nonce(const char *buf, size_t len) goto leave; } + noncelen = strlen(nonce); expect = prepare_push_cert_nonce(service_dir, stamp); - if (strcmp(expect, nonce)) { + if (noncelen != strlen(expect)) { + /* This is not even the right size. */ + retval = NONCE_BAD; + goto leave; + } + if (constant_memequal(expect, nonce, noncelen)) { /* Not what we would have signed earlier */ retval = NONCE_BAD; goto leave; @@ -872,12 +893,12 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si) opt.env = tmp_objdir_env(tmp_objdir); setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra); if (check_connected(command_singleton_iterator, cmd, &opt)) { - rollback_lock_file(&shallow_lock); + rollback_shallow_file(the_repository, &shallow_lock); oid_array_clear(&extra); return -1; } - commit_lock_file(&shallow_lock); + commit_shallow_file(the_repository, &shallow_lock); /* * Make sure setup_alternate_shallow() for the next ref does diff --git a/builtin/reflog.c b/builtin/reflog.c index 81dfd563c0..52ecf6d43c 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -459,7 +459,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) static int reflog_expire_config(const char *var, const char *value, void *cb) { const char *pattern, *key; - int pattern_len; + size_t pattern_len; timestamp_t expire; int slot; struct reflog_expire_cfg *ent; diff --git a/builtin/repack.c b/builtin/repack.c index 0781763b06..1b686ee9ce 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -10,6 +10,7 @@ #include "argv-array.h" #include "midx.h" #include "packfile.h" +#include "prune-packed.h" #include "object-store.h" #include "promisor-remote.h" diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 740da4b6d5..95d0882417 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -18,7 +18,7 @@ static const char *empty_base = ""; static char const * const builtin_sparse_checkout_usage[] = { - N_("git sparse-checkout (init|list|set|add|disable) <options>"), + N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"), NULL }; @@ -94,50 +94,37 @@ static int sparse_checkout_list(int argc, const char **argv) static int update_working_directory(struct pattern_list *pl) { - int result = 0; + enum update_sparsity_result result; struct unpack_trees_options o; struct lock_file lock_file = LOCK_INIT; - struct object_id oid; - struct tree *tree; - struct tree_desc t; struct repository *r = the_repository; - if (repo_read_index_unmerged(r)) - die(_("you need to resolve your current index first")); - - if (get_oid("HEAD", &oid)) - return 0; - - tree = parse_tree_indirect(&oid); - parse_tree(tree); - init_tree_desc(&t, tree->buffer, tree->size); - memset(&o, 0, sizeof(o)); o.verbose_update = isatty(2); - o.merge = 1; o.update = 1; - o.fn = oneway_merge; o.head_idx = -1; o.src_index = r->index; o.dst_index = r->index; o.skip_sparse_checkout = 0; o.pl = pl; - o.keep_pattern_list = !!pl; - resolve_undo_clear_index(r->index); setup_work_tree(); - cache_tree_free(&r->index->cache_tree); - repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); - core_apply_sparse_checkout = 1; - result = unpack_trees(1, &t, &o); - - if (!result) { - prime_cache_tree(r, r->index, tree); + setup_unpack_trees_porcelain(&o, "sparse-checkout"); + result = update_sparsity(&o); + clear_unpack_trees_porcelain(&o); + + if (result == UPDATE_SPARSITY_WARNINGS) + /* + * We don't do any special handling of warnings from untracked + * files in the way or dirty entries that can't be removed. + */ + result = UPDATE_SPARSITY_SUCCESS; + if (result == UPDATE_SPARSITY_SUCCESS) write_locked_index(r->index, &lock_file, COMMIT_LOCK); - } else + else rollback_lock_file(&lock_file); return result; @@ -304,8 +291,6 @@ static int sparse_checkout_init(int argc, const char **argv) }; repo_read_index(the_repository); - require_clean_work_tree(the_repository, - N_("initialize sparse-checkout"), NULL, 1, 0); argc = parse_options(argc, argv, NULL, builtin_sparse_checkout_init_options, @@ -560,8 +545,6 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix, }; repo_read_index(the_repository); - require_clean_work_tree(the_repository, - N_("set sparse-checkout patterns"), NULL, 1, 0); argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_set_options, @@ -571,14 +554,18 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix, return modify_pattern_list(argc, argv, m); } +static int sparse_checkout_reapply(int argc, const char **argv) +{ + repo_read_index(the_repository); + return update_working_directory(NULL); +} + static int sparse_checkout_disable(int argc, const char **argv) { struct pattern_list pl; struct strbuf match_all = STRBUF_INIT; repo_read_index(the_repository); - require_clean_work_tree(the_repository, - N_("disable sparse-checkout"), NULL, 1, 0); memset(&pl, 0, sizeof(pl)); hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0); @@ -622,6 +609,8 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) return sparse_checkout_set(argc, argv, prefix, REPLACE); if (!strcmp(argv[0], "add")) return sparse_checkout_set(argc, argv, prefix, ADD); + if (!strcmp(argv[0], "reapply")) + return sparse_checkout_reapply(argc, argv); if (!strcmp(argv[0], "disable")) return sparse_checkout_disable(argc, argv); } diff --git a/builtin/stash.c b/builtin/stash.c index 6d586ef06d..0c52a3b849 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -861,30 +861,23 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked, struct strbuf *untracked_files) { int i; - int max_len; int found = 0; - char *seen; struct dir_struct dir; memset(&dir, 0, sizeof(dir)); if (include_untracked != INCLUDE_ALL_FILES) setup_standard_excludes(&dir); - seen = xcalloc(ps->nr, 1); - - max_len = fill_directory(&dir, the_repository->index, ps); + fill_directory(&dir, the_repository->index, ps); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; - if (dir_path_match(&the_index, ent, ps, max_len, seen)) { - found++; - strbuf_addstr(untracked_files, ent->name); - /* NUL-terminate: will be fed to update-index -z */ - strbuf_addch(untracked_files, '\0'); - } + found++; + strbuf_addstr(untracked_files, ent->name); + /* NUL-terminate: will be fed to update-index -z */ + strbuf_addch(untracked_files, '\0'); free(ent); } - free(seen); free(dir.entries); free(dir.ignored); clear_directory(&dir); @@ -1041,7 +1034,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, } cp_diff_tree.git_cmd = 1; - argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD", + argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL); if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { ret = -1; diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 2d8f7f0578..b74dd9a69d 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -50,7 +50,7 @@ static const char *parse_arg(const char *next, struct strbuf *arg) * the argument. Die if C-quoting is malformed or the reference name * is invalid. */ -static char *parse_refname(struct strbuf *input, const char **next) +static char *parse_refname(const char **next) { struct strbuf ref = STRBUF_INIT; @@ -95,7 +95,7 @@ static char *parse_refname(struct strbuf *input, const char **next) * provided but cannot be converted to a SHA-1, die. flags can * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY. */ -static int parse_next_oid(struct strbuf *input, const char **next, +static int parse_next_oid(const char **next, const char *end, struct object_id *oid, const char *command, const char *refname, int flags) @@ -103,7 +103,7 @@ static int parse_next_oid(struct strbuf *input, const char **next, struct strbuf arg = STRBUF_INIT; int ret = 0; - if (*next == input->buf + input->len) + if (*next == end) goto eof; if (line_termination) { @@ -128,7 +128,7 @@ static int parse_next_oid(struct strbuf *input, const char **next, die("%s %s: expected NUL but got: %s", command, refname, *next); (*next)++; - if (*next == input->buf + input->len) + if (*next == end) goto eof; strbuf_addstr(&arg, *next); *next += arg.len; @@ -178,23 +178,23 @@ static int parse_next_oid(struct strbuf *input, const char **next, * depending on how line_termination is set. */ -static const char *parse_cmd_update(struct ref_transaction *transaction, - struct strbuf *input, const char *next) +static void parse_cmd_update(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; struct object_id new_oid, old_oid; int have_old; - refname = parse_refname(input, &next); + refname = parse_refname(&next); if (!refname) die("update: missing <ref>"); - if (parse_next_oid(input, &next, &new_oid, "update", refname, + if (parse_next_oid(&next, end, &new_oid, "update", refname, PARSE_SHA1_ALLOW_EMPTY)) die("update %s: missing <newvalue>", refname); - have_old = !parse_next_oid(input, &next, &old_oid, "update", refname, + have_old = !parse_next_oid(&next, end, &old_oid, "update", refname, PARSE_SHA1_OLD); if (*next != line_termination) @@ -209,22 +209,20 @@ static const char *parse_cmd_update(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_create(struct ref_transaction *transaction, - struct strbuf *input, const char *next) +static void parse_cmd_create(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; struct object_id new_oid; - refname = parse_refname(input, &next); + refname = parse_refname(&next); if (!refname) die("create: missing <ref>"); - if (parse_next_oid(input, &next, &new_oid, "create", refname, 0)) + if (parse_next_oid(&next, end, &new_oid, "create", refname, 0)) die("create %s: missing <newvalue>", refname); if (is_null_oid(&new_oid)) @@ -241,23 +239,21 @@ static const char *parse_cmd_create(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_delete(struct ref_transaction *transaction, - struct strbuf *input, const char *next) +static void parse_cmd_delete(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; struct object_id old_oid; int have_old; - refname = parse_refname(input, &next); + refname = parse_refname(&next); if (!refname) die("delete: missing <ref>"); - if (parse_next_oid(input, &next, &old_oid, "delete", refname, + if (parse_next_oid(&next, end, &old_oid, "delete", refname, PARSE_SHA1_OLD)) { have_old = 0; } else { @@ -277,22 +273,20 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_verify(struct ref_transaction *transaction, - struct strbuf *input, const char *next) +static void parse_cmd_verify(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; struct object_id old_oid; - refname = parse_refname(input, &next); + refname = parse_refname(&next); if (!refname) die("verify: missing <ref>"); - if (parse_next_oid(input, &next, &old_oid, "verify", refname, + if (parse_next_oid(&next, end, &old_oid, "verify", refname, PARSE_SHA1_OLD)) oidclr(&old_oid); @@ -306,50 +300,179 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_option(struct strbuf *input, const char *next) +static void parse_cmd_option(struct ref_transaction *transaction, + const char *next, const char *end) { const char *rest; if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) update_flags |= REF_NO_DEREF; else die("option unknown: %s", next); - return rest; } -static void update_refs_stdin(struct ref_transaction *transaction) +static void parse_cmd_start(struct ref_transaction *transaction, + const char *next, const char *end) +{ + if (*next != line_termination) + die("start: extra input: %s", next); + puts("start: ok"); +} + +static void parse_cmd_prepare(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf error = STRBUF_INIT; + if (*next != line_termination) + die("prepare: extra input: %s", next); + if (ref_transaction_prepare(transaction, &error)) + die("prepare: %s", error.buf); + puts("prepare: ok"); +} + +static void parse_cmd_abort(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf error = STRBUF_INIT; + if (*next != line_termination) + die("abort: extra input: %s", next); + if (ref_transaction_abort(transaction, &error)) + die("abort: %s", error.buf); + puts("abort: ok"); +} + +static void parse_cmd_commit(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf error = STRBUF_INIT; + if (*next != line_termination) + die("commit: extra input: %s", next); + if (ref_transaction_commit(transaction, &error)) + die("commit: %s", error.buf); + puts("commit: ok"); + ref_transaction_free(transaction); +} + +enum update_refs_state { + /* Non-transactional state open for updates. */ + UPDATE_REFS_OPEN, + /* A transaction has been started. */ + UPDATE_REFS_STARTED, + /* References are locked and ready for commit */ + UPDATE_REFS_PREPARED, + /* Transaction has been committed or closed. */ + UPDATE_REFS_CLOSED, +}; + +static const struct parse_cmd { + const char *prefix; + void (*fn)(struct ref_transaction *, const char *, const char *); + unsigned args; + enum update_refs_state state; +} command[] = { + { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, + { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, + { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, + { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, + { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, + { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, + { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, + { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, +}; + +static void update_refs_stdin(void) { - struct strbuf input = STRBUF_INIT; - const char *next; + struct strbuf input = STRBUF_INIT, err = STRBUF_INIT; + enum update_refs_state state = UPDATE_REFS_OPEN; + struct ref_transaction *transaction; + int i, j; + + transaction = ref_transaction_begin(&err); + if (!transaction) + die("%s", err.buf); - if (strbuf_read(&input, 0, 1000) < 0) - die_errno("could not read from stdin"); - next = input.buf; /* Read each line dispatch its command */ - while (next < input.buf + input.len) { - if (*next == line_termination) + while (!strbuf_getwholeline(&input, stdin, line_termination)) { + const struct parse_cmd *cmd = NULL; + + if (*input.buf == line_termination) die("empty command in input"); - else if (isspace(*next)) - die("whitespace before command: %s", next); - else if (skip_prefix(next, "update ", &next)) - next = parse_cmd_update(transaction, &input, next); - else if (skip_prefix(next, "create ", &next)) - next = parse_cmd_create(transaction, &input, next); - else if (skip_prefix(next, "delete ", &next)) - next = parse_cmd_delete(transaction, &input, next); - else if (skip_prefix(next, "verify ", &next)) - next = parse_cmd_verify(transaction, &input, next); - else if (skip_prefix(next, "option ", &next)) - next = parse_cmd_option(&input, next); - else - die("unknown command: %s", next); - - next++; + else if (isspace(*input.buf)) + die("whitespace before command: %s", input.buf); + + for (i = 0; i < ARRAY_SIZE(command); i++) { + const char *prefix = command[i].prefix; + char c; + + if (!starts_with(input.buf, prefix)) + continue; + + /* + * If the command has arguments, verify that it's + * followed by a space. Otherwise, it shall be followed + * by a line terminator. + */ + c = command[i].args ? ' ' : line_termination; + if (input.buf[strlen(prefix)] != c) + continue; + + cmd = &command[i]; + break; + } + if (!cmd) + die("unknown command: %s", input.buf); + + /* + * Read additional arguments if NUL-terminated. Do not raise an + * error in case there is an early EOF to let the command + * handle missing arguments with a proper error message. + */ + for (j = 1; line_termination == '\0' && j < cmd->args; j++) + if (strbuf_appendwholeline(&input, stdin, line_termination)) + break; + + switch (state) { + case UPDATE_REFS_OPEN: + case UPDATE_REFS_STARTED: + /* Do not downgrade a transaction to a non-transaction. */ + if (cmd->state >= state) + state = cmd->state; + break; + case UPDATE_REFS_PREPARED: + if (cmd->state != UPDATE_REFS_CLOSED) + die("prepared transactions can only be closed"); + state = cmd->state; + break; + case UPDATE_REFS_CLOSED: + die("transaction is closed"); + break; + } + + cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args, + input.buf + input.len); + } + + switch (state) { + case UPDATE_REFS_OPEN: + /* Commit by default if no transaction was requested. */ + if (ref_transaction_commit(transaction, &err)) + die("%s", err.buf); + ref_transaction_free(transaction); + break; + case UPDATE_REFS_STARTED: + case UPDATE_REFS_PREPARED: + /* If using a transaction, we want to abort it. */ + if (ref_transaction_abort(transaction, &err)) + die("%s", err.buf); + break; + case UPDATE_REFS_CLOSED: + /* Otherwise no need to do anything, the transaction was closed already. */ + break; } + strbuf_release(&err); strbuf_release(&input); } @@ -384,21 +507,11 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) } if (read_stdin) { - struct strbuf err = STRBUF_INIT; - struct ref_transaction *transaction; - - transaction = ref_transaction_begin(&err); - if (!transaction) - die("%s", err.buf); if (delete || argc > 0) usage_with_options(git_update_ref_usage, options); if (end_null) line_termination = '\0'; - update_refs_stdin(transaction); - if (ref_transaction_commit(transaction, &err)) - die("%s", err.buf); - ref_transaction_free(transaction); - strbuf_release(&err); + update_refs_stdin(); return 0; } |