diff options
Diffstat (limited to 'builtin')
| -rw-r--r-- | builtin/blame.c | 2 | ||||
| -rw-r--r-- | builtin/branch.c | 53 | ||||
| -rw-r--r-- | builtin/check-mailmap.c | 2 | ||||
| -rw-r--r-- | builtin/checkout.c | 3 | ||||
| -rw-r--r-- | builtin/commit.c | 2 | ||||
| -rw-r--r-- | builtin/describe.c | 2 | ||||
| -rw-r--r-- | builtin/diff-files.c | 5 | ||||
| -rw-r--r-- | builtin/diff.c | 9 | ||||
| -rw-r--r-- | builtin/fetch.c | 224 | ||||
| -rw-r--r-- | builtin/for-each-ref.c | 2 | ||||
| -rw-r--r-- | builtin/fsck.c | 20 | ||||
| -rw-r--r-- | builtin/gc.c | 18 | ||||
| -rw-r--r-- | builtin/grep.c | 5 | ||||
| -rw-r--r-- | builtin/log.c | 24 | ||||
| -rw-r--r-- | builtin/ls-files.c | 81 | ||||
| -rw-r--r-- | builtin/merge.c | 3 | ||||
| -rw-r--r-- | builtin/mktag.c | 235 | ||||
| -rw-r--r-- | builtin/name-rev.c | 10 | ||||
| -rw-r--r-- | builtin/pack-objects.c | 47 | ||||
| -rw-r--r-- | builtin/pack-redundant.c | 13 | ||||
| -rw-r--r-- | builtin/repack.c | 8 | ||||
| -rw-r--r-- | builtin/shortlog.c | 16 | ||||
| -rw-r--r-- | builtin/show-ref.c | 2 | ||||
| -rw-r--r-- | builtin/sparse-checkout.c | 5 | ||||
| -rw-r--r-- | builtin/tag.c | 46 | ||||
| -rw-r--r-- | builtin/worktree.c | 110 |
26 files changed, 490 insertions, 457 deletions
diff --git a/builtin/blame.c b/builtin/blame.c index 2c1c02c455..b66e938022 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1151,7 +1151,7 @@ parse_done: sb.xdl_opts = xdl_opts; sb.no_whole_file_rename = no_whole_file_rename; - read_mailmap(&mailmap, NULL); + read_mailmap(&mailmap); sb.found_guilty_entry = &found_guilty_entry; sb.found_guilty_entry_data = π diff --git a/builtin/branch.c b/builtin/branch.c index 9b68591add..bcc00bcf18 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -202,6 +202,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, int remote_branch = 0; struct strbuf bname = STRBUF_INIT; unsigned allowed_interpret; + struct string_list refs_to_delete = STRING_LIST_INIT_DUP; + struct string_list_item *item; + int branch_name_pos; switch (kinds) { case FILTER_REFS_REMOTES: @@ -219,6 +222,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, default: die(_("cannot use -a with -d")); } + branch_name_pos = strcspn(fmt, "%"); if (!force) { head_rev = lookup_commit_reference(the_repository, &head_oid); @@ -265,30 +269,35 @@ 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, - REF_NO_DEREF)) { - error(remote_branch - ? _("Error deleting remote-tracking branch '%s'") - : _("Error deleting branch '%s'"), - bname.buf); - ret = 1; - goto next; - } - if (!quiet) { - printf(remote_branch - ? _("Deleted remote-tracking branch %s (was %s).\n") - : _("Deleted branch %s (was %s).\n"), - bname.buf, - (flags & REF_ISBROKEN) ? "broken" - : (flags & REF_ISSYMREF) ? target - : find_unique_abbrev(&oid, DEFAULT_ABBREV)); - } - delete_branch_config(bname.buf); + item = string_list_append(&refs_to_delete, name); + item->util = xstrdup((flags & REF_ISBROKEN) ? "broken" + : (flags & REF_ISSYMREF) ? target + : find_unique_abbrev(&oid, DEFAULT_ABBREV)); next: free(target); } + if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF)) + ret = 1; + + for_each_string_list_item(item, &refs_to_delete) { + char *describe_ref = item->util; + char *name = item->string; + if (!ref_exists(name)) { + char *refname = name + branch_name_pos; + if (!quiet) + printf(remote_branch + ? _("Deleted remote-tracking branch %s (was %s).\n") + : _("Deleted branch %s (was %s).\n"), + name + branch_name_pos, describe_ref); + + delete_branch_config(refname); + } + free(describe_ref); + } + string_list_clear(&refs_to_delete, 0); + free(name); strbuf_release(&bname); @@ -726,7 +735,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) print_current_branch_name(); return 0; } else if (list) { - /* git branch --local also shows HEAD when it is detached */ + /* git branch --list also shows HEAD when it is detached */ if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached) filter.kind |= FILTER_REFS_DETACHED_HEAD; filter.name_patterns = argv; @@ -739,7 +748,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) */ if (!sorting) sorting = ref_default_sorting(); - ref_sorting_icase_all(sorting, icase); + ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase); + ref_sorting_set_sort_flags_all( + sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1); print_ref_list(&filter, sorting, &format); print_columns(&output, colopts, NULL); string_list_clear(&output, 0); diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index cdce144f3b..7dc47e4793 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -47,7 +47,7 @@ int cmd_check_mailmap(int argc, const char **argv, const char *prefix) if (argc == 0 && !use_stdin) die(_("no contacts specified")); - read_mailmap(&mailmap, NULL); + read_mailmap(&mailmap); for (i = 0; i < argc; ++i) check_mailmap(&mailmap, argv[i]); diff --git a/builtin/checkout.c b/builtin/checkout.c index c9ba23c279..2d6550bc3c 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -821,9 +821,6 @@ static int merge_working_tree(const struct checkout_opts *opts, } } - if (!active_cache_tree) - active_cache_tree = cache_tree(); - if (!cache_tree_fully_valid(active_cache_tree)) cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); diff --git a/builtin/commit.c b/builtin/commit.c index 505fe60956..739110c5a7 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1039,7 +1039,7 @@ static const char *find_author_by_nickname(const char *name) av[++ac] = NULL; setup_revisions(ac, av, &revs, NULL); revs.mailmap = &mailmap; - read_mailmap(revs.mailmap, NULL); + read_mailmap(revs.mailmap); if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); diff --git a/builtin/describe.c b/builtin/describe.c index 7668591d57..40482d8e9f 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -194,7 +194,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi } /* Is it annotated? */ - if (!peel_ref(path, &peeled)) { + if (!peel_iterated_oid(oid, &peeled)) { is_annotated = !oideq(oid, &peeled); } else { oidcpy(&peeled, oid); diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 1e352dd8f7..4742a4559b 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -7,6 +7,7 @@ #include "cache.h" #include "config.h" #include "diff.h" +#include "diff-merges.h" #include "commit.h" #include "revision.h" #include "builtin.h" @@ -69,9 +70,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) * was not asked to. "diff-files -c -p" should not densify * (the user should ask with "diff-files --cc" explicitly). */ - if (rev.max_count == -1 && !rev.combine_merges && + if (rev.max_count == -1 && (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) - rev.combine_merges = rev.dense_combined_merges = 1; + diff_merges_set_dense_combined_if_unset(&rev); if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); diff --git a/builtin/diff.c b/builtin/diff.c index 780c33877f..5cfe1717e8 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -13,6 +13,7 @@ #include "blob.h" #include "tag.h" #include "diff.h" +#include "diff-merges.h" #include "diffcore.h" #include "revision.h" #include "log-tree.h" @@ -216,8 +217,8 @@ static int builtin_diff_combined(struct rev_info *revs, if (argc > 1) usage(builtin_diff_usage); - if (!revs->dense_combined_merges && !revs->combine_merges) - revs->dense_combined_merges = revs->combine_merges = 1; + diff_merges_set_dense_combined_if_unset(revs); + for (i = 1; i < ents; i++) oid_array_append(&parents, &ent[i].item->oid); diff_tree_combined(&ent[0].item->oid, &parents, revs); @@ -265,9 +266,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv * dense one, --cc can be explicitly asked for, or just rely * on the default). */ - if (revs->max_count == -1 && !revs->combine_merges && + if (revs->max_count == -1 && (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) - revs->combine_merges = revs->dense_combined_merges = 1; + diff_merges_set_dense_combined_if_unset(revs); setup_work_tree(); if (read_cache_preload(&revs->diffopt.pathspec) < 0) { diff --git a/builtin/fetch.c b/builtin/fetch.c index ecf8537605..91f3d20696 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -63,6 +63,7 @@ static int enable_auto_gc = 1; static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; static int max_jobs = -1, submodule_fetch_jobs_config = -1; static int fetch_parallel_config = 1; +static int atomic_fetch; static enum transport_family family; static const char *depth; static const char *deepen_since; @@ -144,6 +145,8 @@ static struct option builtin_fetch_options[] = { N_("set upstream for git pull/fetch")), OPT_BOOL('a', "append", &append, N_("append to .git/FETCH_HEAD instead of overwriting")), + OPT_BOOL(0, "atomic", &atomic_fetch, + N_("use atomic transaction to update references")), OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), N_("path to upload pack on remote end")), OPT__FORCE(&force, N_("force overwrite of local reference"), 0), @@ -583,13 +586,14 @@ static struct ref *get_ref_map(struct remote *remote, static int s_update_ref(const char *action, struct ref *ref, + struct ref_transaction *transaction, int check_old) { char *msg; char *rla = getenv("GIT_REFLOG_ACTION"); - struct ref_transaction *transaction; + struct ref_transaction *our_transaction = NULL; struct strbuf err = STRBUF_INIT; - int ret, df_conflict = 0; + int ret; if (dry_run) return 0; @@ -597,31 +601,47 @@ static int s_update_ref(const char *action, rla = default_rla.buf; msg = xstrfmt("%s: %s", rla, action); - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, ref->name, - &ref->new_oid, - check_old ? &ref->old_oid : NULL, - 0, msg, &err)) - goto fail; + /* + * If no transaction was passed to us, we manage the transaction + * ourselves. Otherwise, we trust the caller to handle the transaction + * lifecycle. + */ + if (!transaction) { + transaction = our_transaction = ref_transaction_begin(&err); + if (!transaction) { + ret = STORE_REF_ERROR_OTHER; + goto out; + } + } - ret = ref_transaction_commit(transaction, &err); + ret = ref_transaction_update(transaction, ref->name, &ref->new_oid, + check_old ? &ref->old_oid : NULL, + 0, msg, &err); if (ret) { - df_conflict = (ret == TRANSACTION_NAME_CONFLICT); - goto fail; + ret = STORE_REF_ERROR_OTHER; + goto out; } - ref_transaction_free(transaction); - strbuf_release(&err); - free(msg); - return 0; -fail: - ref_transaction_free(transaction); - error("%s", err.buf); + if (our_transaction) { + switch (ref_transaction_commit(our_transaction, &err)) { + case 0: + break; + case TRANSACTION_NAME_CONFLICT: + ret = STORE_REF_ERROR_DF_CONFLICT; + goto out; + default: + ret = STORE_REF_ERROR_OTHER; + goto out; + } + } + +out: + ref_transaction_free(our_transaction); + if (ret) + error("%s", err.buf); strbuf_release(&err); free(msg); - return df_conflict ? STORE_REF_ERROR_DF_CONFLICT - : STORE_REF_ERROR_OTHER; + return ret; } static int refcol_width = 10; @@ -759,6 +779,7 @@ static void format_display(struct strbuf *display, char code, } static int update_local_ref(struct ref *ref, + struct ref_transaction *transaction, const char *remote, const struct ref *remote_ref, struct strbuf *display, @@ -799,7 +820,7 @@ static int update_local_ref(struct ref *ref, starts_with(ref->name, "refs/tags/")) { if (force || ref->force) { int r; - r = s_update_ref("updating tag", ref, 0); + r = s_update_ref("updating tag", ref, transaction, 0); format_display(display, r ? '!' : 't', _("[tag update]"), r ? _("unable to update local ref") : NULL, remote, pretty_ref, summary_width); @@ -836,7 +857,7 @@ static int update_local_ref(struct ref *ref, what = _("[new ref]"); } - r = s_update_ref(msg, ref, 0); + r = s_update_ref(msg, ref, transaction, 0); format_display(display, r ? '!' : '*', what, r ? _("unable to update local ref") : NULL, remote, pretty_ref, summary_width); @@ -858,7 +879,7 @@ static int update_local_ref(struct ref *ref, strbuf_add_unique_abbrev(&quickref, ¤t->object.oid, DEFAULT_ABBREV); strbuf_addstr(&quickref, ".."); strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); - r = s_update_ref("fast-forward", ref, 1); + r = s_update_ref("fast-forward", ref, transaction, 1); format_display(display, r ? '!' : ' ', quickref.buf, r ? _("unable to update local ref") : NULL, remote, pretty_ref, summary_width); @@ -870,7 +891,7 @@ static int update_local_ref(struct ref *ref, strbuf_add_unique_abbrev(&quickref, ¤t->object.oid, DEFAULT_ABBREV); strbuf_addstr(&quickref, "..."); strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); - r = s_update_ref("forced-update", ref, 1); + r = s_update_ref("forced-update", ref, transaction, 1); format_display(display, r ? '!' : '+', quickref.buf, r ? _("unable to update local ref") : _("forced update"), remote, pretty_ref, summary_width); @@ -897,6 +918,89 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid) return 0; } +struct fetch_head { + FILE *fp; + struct strbuf buf; +}; + +static int open_fetch_head(struct fetch_head *fetch_head) +{ + const char *filename = git_path_fetch_head(the_repository); + + if (write_fetch_head) { + fetch_head->fp = fopen(filename, "a"); + if (!fetch_head->fp) + return error_errno(_("cannot open %s"), filename); + strbuf_init(&fetch_head->buf, 0); + } else { + fetch_head->fp = NULL; + } + + return 0; +} + +static void append_fetch_head(struct fetch_head *fetch_head, + const struct object_id *old_oid, + enum fetch_head_status fetch_head_status, + const char *note, + const char *url, size_t url_len) +{ + char old_oid_hex[GIT_MAX_HEXSZ + 1]; + const char *merge_status_marker; + size_t i; + + if (!fetch_head->fp) + return; + + switch (fetch_head_status) { + case FETCH_HEAD_NOT_FOR_MERGE: + merge_status_marker = "not-for-merge"; + break; + case FETCH_HEAD_MERGE: + merge_status_marker = ""; + break; + default: + /* do not write anything to FETCH_HEAD */ + return; + } + + strbuf_addf(&fetch_head->buf, "%s\t%s\t%s", + oid_to_hex_r(old_oid_hex, old_oid), merge_status_marker, note); + for (i = 0; i < url_len; ++i) + if ('\n' == url[i]) + strbuf_addstr(&fetch_head->buf, "\\n"); + else + strbuf_addch(&fetch_head->buf, url[i]); + strbuf_addch(&fetch_head->buf, '\n'); + + /* + * When using an atomic fetch, we do not want to update FETCH_HEAD if + * any of the reference updates fails. We thus have to write all + * updates to a buffer first and only commit it as soon as all + * references have been successfully updated. + */ + if (!atomic_fetch) { + strbuf_write(&fetch_head->buf, fetch_head->fp); + strbuf_reset(&fetch_head->buf); + } +} + +static void commit_fetch_head(struct fetch_head *fetch_head) +{ + if (!fetch_head->fp || !atomic_fetch) + return; + strbuf_write(&fetch_head->buf, fetch_head->fp); +} + +static void close_fetch_head(struct fetch_head *fetch_head) +{ + if (!fetch_head->fp) + return; + + fclose(fetch_head->fp); + strbuf_release(&fetch_head->buf); +} + static const char warn_show_forced_updates[] = N_("Fetch normally indicates which branches had a forced update,\n" "but that check has been disabled. To re-enable, use '--show-forced-updates'\n" @@ -909,22 +1013,20 @@ N_("It took %.2f seconds to check forced updates. You can use\n" static int store_updated_refs(const char *raw_url, const char *remote_name, int connectivity_checked, struct ref *ref_map) { - FILE *fp; + struct fetch_head fetch_head; struct commit *commit; int url_len, i, rc = 0; - struct strbuf note = STRBUF_INIT; + struct strbuf note = STRBUF_INIT, err = STRBUF_INIT; + struct ref_transaction *transaction = NULL; const char *what, *kind; struct ref *rm; char *url; - const char *filename = (!write_fetch_head - ? "/dev/null" - : git_path_fetch_head(the_repository)); int want_status; int summary_width = transport_summary_width(ref_map); - fp = fopen(filename, "a"); - if (!fp) - return error_errno(_("cannot open %s"), filename); + rc = open_fetch_head(&fetch_head); + if (rc) + return -1; if (raw_url) url = transport_anonymize_url(raw_url); @@ -941,6 +1043,14 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, } } + if (atomic_fetch) { + transaction = ref_transaction_begin(&err); + if (!transaction) { + error("%s", err.buf); + goto abort; + } + } + prepare_format_display(ref_map); /* @@ -953,7 +1063,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, want_status++) { for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; - const char *merge_status_marker = ""; if (rm->status == REF_STATUS_REJECT_SHALLOW) { if (want_status == FETCH_HEAD_MERGE) @@ -1011,31 +1120,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, strbuf_addf(¬e, "%s ", kind); strbuf_addf(¬e, "'%s' of ", what); } - switch (rm->fetch_head_status) { - case FETCH_HEAD_NOT_FOR_MERGE: - merge_status_marker = "not-for-merge"; - /* fall-through */ - case FETCH_HEAD_MERGE: - fprintf(fp, "%s\t%s\t%s", - oid_to_hex(&rm->old_oid), - merge_status_marker, - note.buf); - for (i = 0; i < url_len; ++i) - if ('\n' == url[i]) - fputs("\\n", fp); - else - fputc(url[i], fp); - fputc('\n', fp); - break; - default: - /* do not write anything to FETCH_HEAD */ - break; - } + + append_fetch_head(&fetch_head, &rm->old_oid, + rm->fetch_head_status, + note.buf, url, url_len); strbuf_reset(¬e); if (ref) { - rc |= update_local_ref(ref, what, rm, ¬e, - summary_width); + rc |= update_local_ref(ref, transaction, what, + rm, ¬e, summary_width); free(ref); } else if (write_fetch_head || dry_run) { /* @@ -1060,6 +1153,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, } } + if (!rc && transaction) { + rc = ref_transaction_commit(transaction, &err); + if (rc) { + error("%s", err.buf); + goto abort; + } + } + + if (!rc) + commit_fetch_head(&fetch_head); + if (rc & STORE_REF_ERROR_DF_CONFLICT) error(_("some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting " @@ -1076,8 +1180,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, abort: strbuf_release(¬e); + strbuf_release(&err); + ref_transaction_free(transaction); free(url); - fclose(fp); + close_fetch_head(&fetch_head); return rc; } @@ -1887,6 +1993,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) die(_("--filter can only be used with the remote " "configured in extensions.partialclone")); + if (atomic_fetch) + die(_("--atomic can only be used when fetching " + "from one remote")); + if (stdin_refspecs) die(_("--stdin can only be used when fetching " "from one remote")); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 9d1ecda2b8..cb9c81a046 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -70,7 +70,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) if (!sorting) sorting = ref_default_sorting(); - ref_sorting_icase_all(sorting, icase); + ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase); filter.ignore_case = icase; filter.name_patterns = argv; diff --git a/builtin/fsck.c b/builtin/fsck.c index fbf26cafcf..821e7798c7 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -73,25 +73,7 @@ static const char *printable_type(const struct object_id *oid, static int fsck_config(const char *var, const char *value, void *cb) { - if (strcmp(var, "fsck.skiplist") == 0) { - const char *path; - struct strbuf sb = STRBUF_INIT; - - if (git_config_pathname(&path, var, value)) - return 1; - strbuf_addf(&sb, "skiplist=%s", path); - free((char *)path); - fsck_set_msg_types(&fsck_obj_options, sb.buf); - strbuf_release(&sb); - return 0; - } - - if (skip_prefix(var, "fsck.", &var)) { - fsck_set_msg_type(&fsck_obj_options, var, value); - return 0; - } - - return git_default_config(var, value, cb); + return fsck_config_internal(var, value, cb, &fsck_obj_options); } static int objerror(struct object *obj, const char *err) diff --git a/builtin/gc.c b/builtin/gc.c index b315b2ad58..4c40594d66 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -92,7 +92,7 @@ static void process_log_file(void) */ int saved_errno = errno; fprintf(stderr, _("Failed to fstat %s: %s"), - get_tempfile_path(log_lock.tempfile), + get_lock_file_path(&log_lock), strerror(saved_errno)); fflush(stderr); commit_lock_file(&log_lock); @@ -301,7 +301,7 @@ static uint64_t estimate_repack_memory(struct packed_git *pack) /* and then obj_hash[], underestimated in fact */ heap += sizeof(struct object *) * nr_objects; /* revindex is used also */ - heap += sizeof(struct revindex_entry) * nr_objects; + heap += (sizeof(off_t) + sizeof(uint32_t)) * nr_objects; /* * read_sha1_file() (either at delta calculation phase, or * writing phase) also fills up the delta base cache @@ -769,7 +769,7 @@ static int dfs_on_ref(const char *refname, struct commit_list *stack = NULL; struct commit *commit; - if (!peel_ref(refname, &peeled)) + if (!peel_iterated_oid(oid, &peeled)) oid = &peeled; if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT) return 0; @@ -897,6 +897,12 @@ static int maintenance_task_prefetch(struct maintenance_run_opts *opts) struct string_list_item *item; struct string_list remotes = STRING_LIST_INIT_DUP; + git_config_set_multivar_gently("log.excludedecoration", + "refs/prefetch/", + "refs/prefetch/", + CONFIG_FLAGS_FIXED_VALUE | + CONFIG_FLAGS_MULTI_REPLACE); + if (for_each_remote(append_remote, &remotes)) { error(_("failed to fill remotes")); result = 1; @@ -1953,11 +1959,11 @@ static int update_background_schedule(int enable) return error(_("another process is scheduling background maintenance")); if (!strcmp(scheduler, "launchctl")) - result = launchctl_update_schedule(enable, lk.tempfile->fd, cmd); + result = launchctl_update_schedule(enable, get_lock_file_fd(&lk), cmd); else if (!strcmp(scheduler, "schtasks")) - result = schtasks_update_schedule(enable, lk.tempfile->fd, cmd); + result = schtasks_update_schedule(enable, get_lock_file_fd(&lk), cmd); else if (!strcmp(scheduler, "crontab")) - result = crontab_update_schedule(enable, lk.tempfile->fd, cmd); + result = crontab_update_schedule(enable, get_lock_file_fd(&lk), cmd); else die("unknown background scheduler: %s", scheduler); diff --git a/builtin/grep.c b/builtin/grep.c index ca259af441..55d06c9513 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -216,8 +216,6 @@ static void start_threads(struct grep_opt *opt) int err; struct grep_opt *o = grep_opt_dup(opt); o->output = strbuf_out; - if (i) - o->debug = 0; compile_grep_patterns(o); err = pthread_create(&threads[i], NULL, run, o); @@ -936,9 +934,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("indicate hit with exit status without output")), OPT_BOOL(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), - OPT_SET_INT_F(0, "debug", &opt.debug, - N_("show parse tree for grep expression"), - 1, PARSE_OPT_HIDDEN), OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), diff --git a/builtin/log.c b/builtin/log.c index f23ccdbec3..d0cbaaf68a 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -12,6 +12,7 @@ #include "color.h" #include "commit.h" #include "diff.h" +#include "diff-merges.h" #include "revision.h" #include "log-tree.h" #include "builtin.h" @@ -230,7 +231,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (mailmap) { rev->mailmap = xcalloc(1, sizeof(struct string_list)); - read_mailmap(rev->mailmap, NULL); + read_mailmap(rev->mailmap); } if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) { @@ -607,15 +608,10 @@ static int show_tree_object(const struct object_id *oid, static void show_setup_revisions_tweak(struct rev_info *rev, struct setup_revision_opt *opt) { - if (rev->ignore_merges < 0) { - /* There was no "-m" variant on the command line */ - rev->ignore_merges = 0; - if (!rev->first_parent_only && !rev->combine_merges) { - /* No "--first-parent", "-c", or "--cc" */ - rev->combine_merges = 1; - rev->dense_combined_merges = 1; - } - } + if (rev->first_parent_only) + diff_merges_default_to_first_parent(rev); + else + diff_merges_default_to_dense_combined(rev); if (!rev->diffopt.output_format) rev->diffopt.output_format = DIFF_FORMAT_PATCH; } @@ -736,12 +732,8 @@ static void log_setup_revisions_tweak(struct rev_info *rev, rev->prune_data.nr == 1) 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) - rev->diffopt.output_format = DIFF_FORMAT_PATCH; - - if (rev->first_parent_only && rev->ignore_merges < 0) - rev->ignore_merges = 0; + if (rev->first_parent_only) + diff_merges_default_to_first_parent(rev); } int cmd_log(int argc, const char **argv, const char *prefix) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index c8eae899b8..f6f9e483b2 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -35,6 +35,7 @@ static int line_terminator = '\n'; static int debug_mode; static int show_eol; static int recurse_submodules; +static int skipping_duplicates; static const char *prefix; static int max_prefix_len; @@ -312,45 +313,59 @@ static void show_files(struct repository *repo, struct dir_struct *dir) if (show_killed) show_killed_files(repo->index, dir); } - if (show_cached || show_stage) { - for (i = 0; i < repo->index->cache_nr; i++) { - const struct cache_entry *ce = repo->index->cache[i]; - construct_fullname(&fullname, repo, ce); + if (!(show_cached || show_stage || show_deleted || show_modified)) + return; + for (i = 0; i < repo->index->cache_nr; i++) { + const struct cache_entry *ce = repo->index->cache[i]; + struct stat st; + int stat_err; - if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, repo->index, fullname.buf, ce)) - continue; - if (show_unmerged && !ce_stage(ce)) - continue; - if (ce->ce_flags & CE_UPDATE) - continue; + construct_fullname(&fullname, repo, ce); + + if ((dir->flags & DIR_SHOW_IGNORED) && + !ce_excluded(dir, repo->index, fullname.buf, ce)) + continue; + if (ce->ce_flags & CE_UPDATE) + continue; + if ((show_cached || show_stage) && + (!show_unmerged || ce_stage(ce))) { show_ce(repo, dir, ce, fullname.buf, ce_stage(ce) ? tag_unmerged : (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached)); + if (skipping_duplicates) + goto skip_to_next_name; } - } - if (show_deleted || show_modified) { - for (i = 0; i < repo->index->cache_nr; i++) { - const struct cache_entry *ce = repo->index->cache[i]; - struct stat st; - int err; - construct_fullname(&fullname, repo, ce); - - if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, repo->index, fullname.buf, ce)) - continue; - if (ce->ce_flags & CE_UPDATE) - continue; - if (ce_skip_worktree(ce)) - continue; - err = lstat(fullname.buf, &st); - if (show_deleted && err) - show_ce(repo, dir, ce, fullname.buf, tag_removed); - if (show_modified && ie_modified(repo->index, ce, &st, 0)) - show_ce(repo, dir, ce, fullname.buf, tag_modified); + if (!(show_deleted || show_modified)) + continue; + if (ce_skip_worktree(ce)) + continue; + stat_err = lstat(fullname.buf, &st); + if (stat_err && (errno != ENOENT && errno != ENOTDIR)) + error_errno("cannot lstat '%s'", fullname.buf); + if (stat_err && show_deleted) { + show_ce(repo, dir, ce, fullname.buf, tag_removed); + if (skipping_duplicates) + goto skip_to_next_name; + } + if (show_modified && + (stat_err || ie_modified(repo->index, ce, &st, 0))) { + show_ce(repo, dir, ce, fullname.buf, tag_modified); + if (skipping_duplicates) + goto skip_to_next_name; + } + continue; + +skip_to_next_name: + { + int j; + struct cache_entry **cache = repo->index->cache; + for (j = i + 1; j < repo->index->cache_nr; j++) + if (strcmp(ce->name, cache[j]->name)) + break; + i = j - 1; /* compensate for the for loop */ } } @@ -578,6 +593,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("pretend that paths removed since <tree-ish> are still present")), OPT__ABBREV(&abbrev), OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), + OPT_BOOL(0, "deduplicate", &skipping_duplicates, + N_("suppress duplicate entries")), OPT_END() }; @@ -617,6 +634,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) * you also show the stage information. */ show_stage = 1; + if (show_tag || show_stage) + skipping_duplicates = 0; if (dir.exclude_per_dir) exc_given = 1; diff --git a/builtin/merge.c b/builtin/merge.c index 1cff730715..eb00b273e6 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -14,6 +14,7 @@ #include "lockfile.h" #include "run-command.h" #include "diff.h" +#include "diff-merges.h" #include "refs.h" #include "refspec.h" #include "commit.h" @@ -409,7 +410,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead printf(_("Squash commit -- not updating HEAD\n")); repo_init_revisions(the_repository, &rev, NULL); - rev.ignore_merges = 1; + diff_merges_suppress(&rev); rev.commit_format = CMIT_FMT_MEDIUM; commit->object.flags |= UNINTERESTING; diff --git a/builtin/mktag.c b/builtin/mktag.c index 4982d3a93e..41a399a69e 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -1,179 +1,110 @@ #include "builtin.h" +#include "parse-options.h" #include "tag.h" #include "replace-object.h" #include "object-store.h" +#include "fsck.h" +#include "config.h" -/* - * A signature file has a very simple fixed format: four lines - * of "object <sha1>" + "type <typename>" + "tag <tagname>" + - * "tagger <committer>", followed by a blank line, a free-form tag - * message and a signature block that git itself doesn't care about, - * but that can be verified with gpg or similar. - * - * The first four lines are guaranteed to be at least 83 bytes: - * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the - * shortest possible type-line, "tag .\n" at 6 bytes is the shortest - * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is - * the shortest possible tagger-line. - */ - -/* - * We refuse to tag something we can't verify. Just because. - */ -static int verify_object(const struct object_id *oid, const char *expected_type) +static char const * const builtin_mktag_usage[] = { + N_("git mktag"), + NULL +}; +static int option_strict = 1; + +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; + +static int mktag_config(const char *var, const char *value, void *cb) { - int ret = -1; - enum object_type type; - unsigned long size; - void *buffer = read_object_file(oid, &type, &size); - const struct object_id *repl = lookup_replace_object(the_repository, oid); - - if (buffer) { - if (type == type_from_string(expected_type)) { - ret = check_object_signature(the_repository, repl, - buffer, size, - expected_type); + return fsck_config_internal(var, value, cb, &fsck_options); +} + +static int mktag_fsck_error_func(struct fsck_options *o, + const struct object_id *oid, + enum object_type object_type, + int msg_type, const char *message) +{ + switch (msg_type) { + case FSCK_WARN: + if (!option_strict) { + fprintf_ln(stderr, _("warning: tag input does not pass fsck: %s"), message); + return 0; + } - free(buffer); + /* fallthrough */ + case FSCK_ERROR: + /* + * We treat both warnings and errors as errors, things + * like missing "tagger" lines are "only" warnings + * under fsck, we've always considered them an error. + */ + fprintf_ln(stderr, _("error: tag input does not pass fsck: %s"), message); + return 1; + default: + BUG(_("%d (FSCK_IGNORE?) should never trigger this callback"), + msg_type); } - return ret; } -static int verify_tag(char *buffer, unsigned long size) +static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type) { - int typelen; - char type[20]; - struct object_id oid; - const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p; - size_t len; - - if (size < 84) - return error("wanna fool me ? you obviously got the size wrong !"); - - buffer[size] = 0; - - /* Verify object line */ - object = buffer; - if (memcmp(object, "object ", 7)) - return error("char%d: does not start with \"object \"", 0); - - if (parse_oid_hex(object + 7, &oid, &p)) - return error("char%d: could not get SHA1 hash", 7); - - /* Verify type line */ - type_line = p + 1; - if (memcmp(type_line - 1, "\ntype ", 6)) - return error("char%d: could not find \"\\ntype \"", 47); - - /* Verify tag-line */ - tag_line = strchr(type_line, '\n'); - if (!tag_line) - return error("char%"PRIuMAX": could not find next \"\\n\"", - (uintmax_t) (type_line - buffer)); - tag_line++; - if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n') - return error("char%"PRIuMAX": no \"tag \" found", - (uintmax_t) (tag_line - buffer)); - - /* Get the actual type */ - typelen = tag_line - type_line - strlen("type \n"); - if (typelen >= sizeof(type)) - return error("char%"PRIuMAX": type too long", - (uintmax_t) (type_line+5 - buffer)); - - memcpy(type, type_line+5, typelen); - type[typelen] = 0; - - /* Verify that the object matches */ - if (verify_object(&oid, type)) - return error("char%d: could not verify object %s", 7, oid_to_hex(&oid)); - - /* Verify the tag-name: we don't allow control characters or spaces in it */ - tag_line += 4; - for (;;) { - unsigned char c = *tag_line++; - if (c == '\n') - break; - if (c > ' ') - continue; - return error("char%"PRIuMAX": could not verify tag name", - (uintmax_t) (tag_line - buffer)); - } + int ret; + enum object_type type; + unsigned long size; + void *buffer; + const struct object_id *repl; + + buffer = read_object_file(tagged_oid, &type, &size); + if (!buffer) + die(_("could not read tagged object '%s'"), + oid_to_hex(tagged_oid)); + if (type != *tagged_type) + die(_("object '%s' tagged as '%s', but is a '%s' type"), + oid_to_hex(tagged_oid), + type_name(*tagged_type), type_name(type)); + + repl = lookup_replace_object(the_repository, tagged_oid); + ret = check_object_signature(the_repository, repl, + buffer, size, type_name(*tagged_type)); + free(buffer); - /* Verify the tagger line */ - tagger_line = tag_line; - - if (memcmp(tagger_line, "tagger ", 7)) - return error("char%"PRIuMAX": could not find \"tagger \"", - (uintmax_t) (tagger_line - buffer)); - - /* - * Check for correct form for name and email - * i.e. " <" followed by "> " on _this_ line - * No angle brackets within the name or email address fields. - * No spaces within the email address field. - */ - tagger_line += 7; - if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) || - strpbrk(tagger_line, "<>\n") != lb+1 || - strpbrk(lb+2, "><\n ") != rb) - return error("char%"PRIuMAX": malformed tagger field", - (uintmax_t) (tagger_line - buffer)); - - /* Check for author name, at least one character, space is acceptable */ - if (lb == tagger_line) - return error("char%"PRIuMAX": missing tagger name", - (uintmax_t) (tagger_line - buffer)); - - /* timestamp, 1 or more digits followed by space */ - tagger_line = rb + 2; - if (!(len = strspn(tagger_line, "0123456789"))) - return error("char%"PRIuMAX": missing tag timestamp", - (uintmax_t) (tagger_line - buffer)); - tagger_line += len; - if (*tagger_line != ' ') - return error("char%"PRIuMAX": malformed tag timestamp", - (uintmax_t) (tagger_line - buffer)); - tagger_line++; - - /* timezone, 5 digits [+-]hhmm, max. 1400 */ - if (!((tagger_line[0] == '+' || tagger_line[0] == '-') && - strspn(tagger_line+1, "0123456789") == 4 && - tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400)) - return error("char%"PRIuMAX": malformed tag timezone", - (uintmax_t) (tagger_line - buffer)); - tagger_line += 6; - - /* Verify the blank line separating the header from the body */ - if (*tagger_line != '\n') - return error("char%"PRIuMAX": trailing garbage in tag header", - (uintmax_t) (tagger_line - buffer)); - - /* The actual stuff afterwards we don't care about.. */ - return 0; + return ret; } int cmd_mktag(int argc, const char **argv, const char *prefix) { + static struct option builtin_mktag_options[] = { + OPT_BOOL(0, "strict", &option_strict, + N_("enable more strict checking")), + OPT_END(), + }; struct strbuf buf = STRBUF_INIT; + struct object_id tagged_oid; + int tagged_type; struct object_id result; - if (argc != 1) - usage("git mktag"); + argc = parse_options(argc, argv, NULL, + builtin_mktag_options, + builtin_mktag_usage, 0); - if (strbuf_read(&buf, 0, 4096) < 0) { - die_errno("could not read from stdin"); - } + if (strbuf_read(&buf, 0, 0) < 0) + die_errno(_("could not read from stdin")); + + fsck_options.error_func = mktag_fsck_error_func; + fsck_set_msg_type(&fsck_options, "extraheaderentry", "warn"); + /* config might set fsck.extraHeaderEntry=* again */ + git_config(mktag_config, NULL); + if (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options, + &tagged_oid, &tagged_type)) + die(_("tag on stdin did not pass our strict fsck check")); - /* Verify it for some basic sanity: it needs to start with - "object <sha1>\ntype\ntagger " */ - if (verify_tag(buf.buf, buf.len) < 0) - die("invalid tag signature file"); + if (verify_object_in_tag(&tagged_oid, &tagged_type)) + die(_("tag on stdin did not refer to a valid object")); if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0) - die("unable to write tag file"); + die(_("unable to write tag file")); strbuf_release(&buf); - printf("%s\n", oid_to_hex(&result)); + puts(oid_to_hex(&result)); return 0; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 3fe71a8c01..b221d30014 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -390,10 +390,10 @@ static void name_tips(void) } } -static const unsigned char *nth_tip_table_ent(size_t ix, void *table_) +static const struct object_id *nth_tip_table_ent(size_t ix, const void *table_) { - struct tip_table_entry *table = table_; - return table[ix].oid.hash; + const struct tip_table_entry *table = table_; + return &table[ix].oid; } static const char *get_exact_ref_match(const struct object *o) @@ -408,8 +408,8 @@ static const char *get_exact_ref_match(const struct object *o) tip_table.sorted = 1; } - found = hash_pos(o->oid.hash, tip_table.table, tip_table.nr, - nth_tip_table_ent); + found = oid_pos(&o->oid, tip_table.table, tip_table.nr, + nth_tip_table_ent); if (0 <= found) return tip_table.table[found].refname; return NULL; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 2a00358f34..13cde5896a 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -419,7 +419,7 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, { struct packed_git *p = IN_PACK(entry); struct pack_window *w_curs = NULL; - struct revindex_entry *revidx; + uint32_t pos; off_t offset; enum object_type type = oe_type(entry); off_t datalen; @@ -436,10 +436,15 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, type, entry_size); offset = entry->in_pack_offset; - revidx = find_pack_revindex(p, offset); - datalen = revidx[1].offset - offset; + if (offset_to_pack_pos(p, offset, &pos) < 0) + die(_("write_reuse_object: could not locate %s, expected at " + "offset %"PRIuMAX" in pack %s"), + oid_to_hex(&entry->idx.oid), (uintmax_t)offset, + p->pack_name); + datalen = pack_pos_to_offset(p, pos + 1) - offset; if (!pack_to_stdout && p->index_version > 1 && - check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) { + check_pack_crc(p, &w_curs, offset, datalen, + pack_pos_to_index(p, pos))) { error(_("bad packed object CRC for %s"), oid_to_hex(&entry->idx.oid)); unuse_pack(&w_curs); @@ -629,7 +634,7 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag, if (entry) entry->tagged = 1; - if (!peel_ref(path, &peeled)) { + if (!peel_iterated_oid(oid, &peeled)) { entry = packlist_find(&to_pack, &peeled); if (entry) entry->tagged = 1; @@ -863,8 +868,8 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out, enum object_type type; unsigned long size; - offset = reuse_packfile->revindex[pos].offset; - next = reuse_packfile->revindex[pos + 1].offset; + offset = pack_pos_to_offset(reuse_packfile, pos); + next = pack_pos_to_offset(reuse_packfile, pos + 1); record_reused_object(offset, offset - hashfile_total(out)); @@ -884,11 +889,17 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out, /* Convert to REF_DELTA if we must... */ if (!allow_ofs_delta) { - int base_pos = find_revindex_position(reuse_packfile, base_offset); + uint32_t base_pos; struct object_id base_oid; + if (offset_to_pack_pos(reuse_packfile, base_offset, &base_pos) < 0) + die(_("expected object at offset %"PRIuMAX" " + "in pack %s"), + (uintmax_t)base_offset, + reuse_packfile->pack_name); + nth_packed_object_id(&base_oid, reuse_packfile, - reuse_packfile->revindex[base_pos].nr); + pack_pos_to_index(reuse_packfile, base_pos)); len = encode_in_pack_object_header(header, sizeof(header), OBJ_REF_DELTA, size); @@ -941,7 +952,7 @@ static size_t write_reused_pack_verbatim(struct hashfile *out, off_t to_write; written = (pos * BITS_IN_EWORD); - to_write = reuse_packfile->revindex[written].offset + to_write = pack_pos_to_offset(reuse_packfile, written) - sizeof(struct pack_header); /* We're recording one chunk, not one object. */ @@ -1806,11 +1817,11 @@ static void check_object(struct object_entry *entry, uint32_t object_index) goto give_up; } if (reuse_delta && !entry->preferred_base) { - struct revindex_entry *revidx; - revidx = find_pack_revindex(p, ofs); - if (!revidx) + uint32_t pos; + if (offset_to_pack_pos(p, ofs, &pos) < 0) goto give_up; - if (!nth_packed_object_id(&base_ref, p, revidx->nr)) + if (!nth_packed_object_id(&base_ref, p, + pack_pos_to_index(p, pos))) have_base = 1; } entry->in_pack_header_size = used + used_0; @@ -2803,13 +2814,11 @@ static void add_tag_chain(const struct object_id *oid) } } -static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data) +static int add_ref_tag(const char *tag, const struct object_id *oid, int flag, void *cb_data) { struct object_id peeled; - if (starts_with(path, "refs/tags/") && /* is a tag? */ - !peel_ref(path, &peeled) && /* peelable? */ - obj_is_packed(&peeled)) /* object packed? */ + if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled)) add_tag_chain(oid); return 0; } @@ -3740,7 +3749,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } cleanup_preferred_base(); if (include_tag && nr_result) - for_each_ref(add_ref_tag, NULL); + for_each_tag_ref(add_ref_tag, NULL); stop_progress(&progress_state); trace2_region_leave("pack-objects", "enumerate-objects", the_repository); diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 9fcea3e253..6e115a811a 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -560,6 +560,7 @@ static void load_all(void) int cmd_pack_redundant(int argc, const char **argv, const char *prefix) { int i; + int i_still_use_this = 0; struct pack_list *min = NULL, *red, *pl; struct llist *ignore; struct object_id *oid; @@ -586,12 +587,24 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) alt_odb = 1; continue; } + if (!strcmp(arg, "--i-still-use-this")) { + i_still_use_this = 1; + continue; + } if (*arg == '-') usage(pack_redundant_usage); else break; } + if (!i_still_use_this) { + fputs(_("'git pack-redundant' is nominated for removal.\n" + "If you still use this command, please add an extra\n" + "option, '--i-still-use-this', on the command line\n" + "and let us know you still use it by sending an e-mail\n" + "to <git@vger.kernel.org>. Thanks.\n"), stderr); + } + if (load_all_packs) load_all(); else diff --git a/builtin/repack.c b/builtin/repack.c index 279be11a16..2158b48f4c 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -14,6 +14,7 @@ #include "object-store.h" #include "promisor-remote.h" #include "shallow.h" +#include "pack.h" static int delta_base_offset = 1; static int pack_kept_objects = -1; @@ -263,7 +264,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args, while (strbuf_getline_lf(&line, out) != EOF) { struct string_list_item *item; char *promisor_name; - int fd; + if (line.len != the_hash_algo->hexsz) die(_("repack: Expecting full hex object ID lines only from pack-objects.")); item = string_list_append(names, line.buf); @@ -281,10 +282,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args, */ promisor_name = mkpathdup("%s-%s.promisor", packtmp, line.buf); - fd = open(promisor_name, O_CREAT|O_EXCL|O_WRONLY, 0600); - if (fd < 0) - die_errno(_("unable to create '%s'"), promisor_name); - close(fd); + write_promisor_file(promisor_name, NULL, 0); item->util = (void *)(uintptr_t)populate_pack_exts(item->string); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 1c0b3a9b05..3e7ab1ca82 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -61,8 +61,7 @@ static void insert_one_record(struct shortlog *log, if (log->summary) item->util = (void *)(UTIL_TO_INT(item) + 1); else { - const char *dot3 = log->common_repo_prefix; - char *buffer, *p; + char *buffer; struct strbuf subject = STRBUF_INIT; const char *eol; @@ -82,17 +81,6 @@ static void insert_one_record(struct shortlog *log, format_subject(&subject, oneline, " "); buffer = strbuf_detach(&subject, NULL); - if (dot3) { - int dot3len = strlen(dot3); - if (dot3len > 5) { - while ((p = strstr(buffer, dot3)) != NULL) { - int taillen = strlen(p) - dot3len; - memcpy(p, "/.../", 5); - memmove(p + 5, p + dot3len, taillen + 1); - } - } - } - if (item->util == NULL) item->util = xcalloc(1, sizeof(struct string_list)); string_list_append(item->util, buffer); @@ -342,7 +330,7 @@ void shortlog_init(struct shortlog *log) { memset(log, 0, sizeof(*log)); - read_mailmap(&log->mailmap, &log->common_repo_prefix); + read_mailmap(&log->mailmap); log->list.strdup_strings = 1; log->wrap = DEFAULT_WRAPLEN; diff --git a/builtin/show-ref.c b/builtin/show-ref.c index ae60b4acf2..7f8a5332f8 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -40,7 +40,7 @@ static void show_one(const char *refname, const struct object_id *oid) if (!deref_tags) return; - if (!peel_ref(refname, &peeled)) { + if (!peel_iterated_oid(oid, &peeled)) { hex = find_unique_abbrev(&peeled, abbrev); printf("%s %s^{}\n", hex, refname); } diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index e3140db2a0..2306a9ad98 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -22,11 +22,6 @@ static char const * const builtin_sparse_checkout_usage[] = { NULL }; -static char *get_sparse_checkout_filename(void) -{ - return git_pathdup("info/sparse-checkout"); -} - static void write_patterns_to_file(FILE *fp, struct pattern_list *pl) { int i; diff --git a/builtin/tag.c b/builtin/tag.c index ecf011776d..e8b85eefd8 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -72,10 +72,10 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, } typedef int (*each_tag_name_fn)(const char *name, const char *ref, - const struct object_id *oid, const void *cb_data); + const struct object_id *oid, void *cb_data); static int for_each_tag_name(const char **argv, each_tag_name_fn fn, - const void *cb_data) + void *cb_data) { const char **p; struct strbuf ref = STRBUF_INIT; @@ -97,18 +97,42 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, return had_error; } -static int delete_tag(const char *name, const char *ref, - const struct object_id *oid, const void *cb_data) +static int collect_tags(const char *name, const char *ref, + const struct object_id *oid, void *cb_data) { - if (delete_ref(NULL, ref, oid, 0)) - return 1; - printf(_("Deleted tag '%s' (was %s)\n"), name, - find_unique_abbrev(oid, DEFAULT_ABBREV)); + struct string_list *ref_list = cb_data; + + string_list_append(ref_list, ref); + ref_list->items[ref_list->nr - 1].util = oiddup(oid); return 0; } +static int delete_tags(const char **argv) +{ + int result; + struct string_list refs_to_delete = STRING_LIST_INIT_DUP; + struct string_list_item *item; + + result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete); + if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF)) + result = 1; + + for_each_string_list_item(item, &refs_to_delete) { + const char *name = item->string; + struct object_id *oid = item->util; + if (!ref_exists(name)) + printf(_("Deleted tag '%s' (was %s)\n"), + item->string + 10, + find_unique_abbrev(oid, DEFAULT_ABBREV)); + + free(oid); + } + string_list_clear(&refs_to_delete, 0); + return result; +} + static int verify_tag(const char *name, const char *ref, - const struct object_id *oid, const void *cb_data) + const struct object_id *oid, void *cb_data) { int flags; const struct ref_format *format = cb_data; @@ -485,7 +509,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) } if (!sorting) sorting = ref_default_sorting(); - ref_sorting_icase_all(sorting, icase); + ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase); filter.ignore_case = icase; if (cmdmode == 'l') { int ret; @@ -512,7 +536,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (filter.reachable_from || filter.unreachable_from) die(_("--merged and --no-merged options are only allowed in list mode")); if (cmdmode == 'd') - return for_each_tag_name(argv, delete_tag, NULL); + return delete_tags(argv); if (cmdmode == 'v') { if (format.format && verify_ref_format(&format)) usage_with_options(git_tag_usage, options); diff --git a/builtin/worktree.c b/builtin/worktree.c index 71287b2da6..1cd5c2016e 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -12,6 +12,7 @@ #include "submodule.h" #include "utf8.h" #include "worktree.h" +#include "quote.h" static const char * const worktree_usage[] = { N_("git worktree add [<options>] <path> [<commit-ish>]"), @@ -67,79 +68,6 @@ static void delete_worktrees_dir_if_empty(void) rmdir(git_path("worktrees")); /* ignore failed removal */ } -/* - * Return true if worktree entry should be pruned, along with the reason for - * pruning. Otherwise, return false and the worktree's path, or NULL if it - * cannot be determined. Caller is responsible for freeing returned path. - */ -static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath) -{ - struct stat st; - char *path; - int fd; - size_t len; - ssize_t read_result; - - *wtpath = NULL; - if (!is_directory(git_path("worktrees/%s", id))) { - strbuf_addstr(reason, _("not a valid directory")); - return 1; - } - if (file_exists(git_path("worktrees/%s/locked", id))) - return 0; - if (stat(git_path("worktrees/%s/gitdir", id), &st)) { - strbuf_addstr(reason, _("gitdir file does not exist")); - return 1; - } - fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY); - if (fd < 0) { - strbuf_addf(reason, _("unable to read gitdir file (%s)"), - strerror(errno)); - return 1; - } - len = xsize_t(st.st_size); - path = xmallocz(len); - - read_result = read_in_full(fd, path, len); - if (read_result < 0) { - strbuf_addf(reason, _("unable to read gitdir file (%s)"), - strerror(errno)); - close(fd); - free(path); - return 1; - } - close(fd); - - if (read_result != len) { - strbuf_addf(reason, - _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"), - (uintmax_t)len, (uintmax_t)read_result); - free(path); - return 1; - } - while (len && (path[len - 1] == '\n' || path[len - 1] == '\r')) - len--; - if (!len) { - strbuf_addstr(reason, _("invalid gitdir file")); - free(path); - return 1; - } - path[len] = '\0'; - if (!file_exists(path)) { - if (stat(git_path("worktrees/%s/index", id), &st) || - st.st_mtime <= expire) { - strbuf_addstr(reason, _("gitdir file points to non-existent location")); - free(path); - return 1; - } else { - *wtpath = path; - return 0; - } - } - *wtpath = path; - return 0; -} - static void prune_worktree(const char *id, const char *reason) { if (show_only || verbose) @@ -195,7 +123,7 @@ static void prune_worktrees(void) if (is_dot_or_dotdot(d->d_name)) continue; strbuf_reset(&reason); - if (should_prune_worktree(d->d_name, &reason, &path)) + if (should_prune_worktree(d->d_name, &reason, &path, expire)) prune_worktree(d->d_name, reason.buf); else if (path) string_list_append(&kept, path)->util = xstrdup(d->d_name); @@ -642,6 +570,8 @@ static int add(int ac, const char **av, const char *prefix) static void show_worktree_porcelain(struct worktree *wt) { + const char *reason; + printf("worktree %s\n", wt->path); if (wt->is_bare) printf("bare\n"); @@ -652,6 +582,20 @@ static void show_worktree_porcelain(struct worktree *wt) else if (wt->head_ref) printf("branch %s\n", wt->head_ref); } + + reason = worktree_lock_reason(wt); + if (reason && *reason) { + struct strbuf sb = STRBUF_INIT; + quote_c_style(reason, &sb, NULL, 0); + printf("locked %s\n", sb.buf); + strbuf_release(&sb); + } else if (reason) + printf("locked\n"); + + reason = worktree_prune_reason(wt, expire); + if (reason) + printf("prunable %s\n", reason); + printf("\n"); } @@ -660,6 +604,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) struct strbuf sb = STRBUF_INIT; int cur_path_len = strlen(wt->path); int path_adj = cur_path_len - utf8_strwidth(wt->path); + const char *reason; strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path); if (wt->is_bare) @@ -677,9 +622,18 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) strbuf_addstr(&sb, "(error)"); } - if (!is_main_worktree(wt) && worktree_lock_reason(wt)) + reason = worktree_lock_reason(wt); + if (verbose && reason && *reason) + strbuf_addf(&sb, "\n\tlocked: %s", reason); + else if (reason) strbuf_addstr(&sb, " locked"); + reason = worktree_prune_reason(wt, expire); + if (verbose && reason) + strbuf_addf(&sb, "\n\tprunable: %s", reason); + else if (reason) + strbuf_addstr(&sb, " prunable"); + printf("%s\n", sb.buf); strbuf_release(&sb); } @@ -723,12 +677,18 @@ static int list(int ac, const char **av, const char *prefix) struct option options[] = { OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")), + OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")), + OPT_EXPIRY_DATE(0, "expire", &expire, + N_("add 'prunable' annotation to worktrees older than <time>")), OPT_END() }; + expire = TIME_MAX; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac) usage_with_options(worktree_usage, options); + else if (verbose && porcelain) + die(_("--verbose and --porcelain are mutually exclusive")); else { struct worktree **worktrees = get_worktrees(); int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i; |
