diff --git a/Documentation/RelNotes/2.51.0.adoc b/Documentation/RelNotes/2.51.0.adoc index ca0e39e1e82964..8ff921809a9107 100644 --- a/Documentation/RelNotes/2.51.0.adoc +++ b/Documentation/RelNotes/2.51.0.adoc @@ -84,6 +84,10 @@ Performance, Internal Implementation, Development Support etc. * Code clean-up around object access API. + * Define .precision to more canned parse-options type to avoid bugs + coming from using a variable with a wrong type to capture the + parsed values. + Fixes since v2.50 ----------------- @@ -166,6 +170,14 @@ including security updates, are included in this release. * Leakfix with a new and a bit invasive test on pack-bitmap files. (merge bfd5522e98 ly/load-bitmap-leakfix later to maint). + * "git fetch --prune" used to be O(n^2) expensive when there are many + refs, which has been corrected. + (merge 87d8d8c5d0 ph/fetch-prune-optim later to maint). + + * When a ref creation at refs/heads/foo/bar fails, the files backend + now removes refs/heads/foo/ if the directory is otherwise not used. + (merge a3a7f20516 ps/refs-files-remove-empty-parent later to maint). + * Other code cleanup, docfix, build fix, etc. (merge b257adb571 lo/my-first-ow-doc-update later to maint). (merge 8b34b6a220 ly/sequencer-update-squash-is-fixup-only later to maint). @@ -188,3 +200,4 @@ including security updates, are included in this release. (merge de404249ab ps/perlless-test-fixes later to maint). (merge 953049eed8 ts/merge-orig-head-doc-fix later to maint). (merge 0c83bbc704 rj/freebsd-sysinfo-build-fix later to maint). + (merge ad7780b38f ps/doc-pack-refs-auto-with-files-backend-fix later to maint). diff --git a/Documentation/config/sendemail.adoc b/Documentation/config/sendemail.adoc index 54f1248e647273..47223346579727 100644 --- a/Documentation/config/sendemail.adoc +++ b/Documentation/config/sendemail.adoc @@ -31,8 +31,8 @@ sendemail.confirm:: values. sendemail.mailmap:: - If true, makes linkgit:git-send-email[1] assume `--mailmap`, - otherwise assume `--no-mailmap`. False by default. + If `true`, makes linkgit:git-send-email[1] assume `--mailmap`, + otherwise assume `--no-mailmap`. `False` by default. sendemail.mailmap.file:: The location of a linkgit:git-send-email[1] specific augmenting @@ -96,6 +96,11 @@ sendemail.xmailer:: linkgit:git-send-email[1] command-line options. See its documentation for details. +sendemail.outlookidfix:: + If `true`, makes linkgit:git-send-email[1] assume `--outlook-id-fix`, + and if `false` assume `--no-outlook-id-fix`. If not specified, it will + behave the same way as if `--outlook-id-fix` is not specified. + sendemail.signedOffCc (deprecated):: Deprecated alias for `sendemail.signedOffByCc`. diff --git a/Documentation/git-pack-refs.adoc b/Documentation/git-pack-refs.adoc index 652c5497715a0f..42b90051e695a5 100644 --- a/Documentation/git-pack-refs.adoc +++ b/Documentation/git-pack-refs.adoc @@ -66,7 +66,10 @@ Pack refs as needed depending on the current state of the ref database. The behavior depends on the ref format used by the repository and may change in the future. + - - "files": No special handling for `--auto` has been implemented. + - "files": Loose references are packed into the `packed-refs` file + based on the ratio of loose references to the size of the + `packed-refs` file. The bigger the `packed-refs` file, the more loose + references need to exist before we repack. + - "reftable": Tables are compacted such that they form a geometric sequence. For two tables N and N+1, where N+1 is newer, this diff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc index 7bd09c254b271e..5335502d68fc7b 100644 --- a/Documentation/git-send-email.adoc +++ b/Documentation/git-send-email.adoc @@ -280,12 +280,14 @@ must be used for each option. Path to a store of trusted CA certificates for SMTP SSL/TLS certificate validation (either a directory that has been processed by `c_rehash`, or a single file containing one or more PEM format - certificates concatenated together: see verify(1) -CAfile and - -CApath for more information on these). Set it to an empty string - to disable certificate verification. Defaults to the value of the - `sendemail.smtpSSLCertPath` configuration variable, if set, or the - backing SSL library's compiled-in default otherwise (which should - be the best choice on most platforms). + certificates concatenated together: see the description of the + `-CAfile` __ and the `-CApath` __ options of + https://docs.openssl.org/master/man1/openssl-verify/ + [OpenSSL's verify(1) manual page] for more information on these). + Set it to an empty string to disable certificate verification. + Defaults to the value of the `sendemail.smtpSSLCertPath` configuration + variable, if set, or the backing SSL library's compiled-in default + otherwise (which should be the best choice on most platforms). --smtp-user=:: Username for SMTP-AUTH. Default is the value of `sendemail.smtpUser`; @@ -598,9 +600,20 @@ available online. Community maintained credential helpers are also available: - https://github.com/AdityaGarg8/git-credential-email[git-credential-yahoo] (cross platform, dedicated helper for authenticating Yahoo accounts) + - https://github.com/AdityaGarg8/git-credential-email[git-credential-aol] + (cross platform, dedicated helper for authenticating AOL accounts) + You can also see linkgit:gitcredentials[7] for more OAuth based authentication helpers. +Proton Mail does not provide an SMTP server to send emails. If you are a paid +customer of Proton Mail, you can use +https://proton.me/mail/bridge[Proton Mail Bridge] +officially provided by Proton Mail to create a local SMTP server for sending +emails. For both free and paid users, community maintained projects like +https://github.com/AdityaGarg8/git-credential-email[git-protonmail] can be +used. + Note: the following core Perl modules that may be installed with your distribution of Perl are required: @@ -614,6 +627,35 @@ These additional Perl modules are also required: https://metacpan.org/pod/Authen::SASL[Authen::SASL] and https://metacpan.org/pod/Mail::Address[Mail::Address]. +Exploiting the `sendmailCmd` option of `git send-email` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Apart from sending emails via an SMTP server, `git send-email` can also send +emails through any application that supports sendmail-like commands. You can +read documentation of `--sendmail-cmd=` above for more information. +This ability can be very useful if you want to use another application as an +SMTP client for `git send-email`, or if your email provider uses proprietary +APIs instead of SMTP to send emails. + +As an example, lets see how to configure https://marlam.de/msmtp/[msmtp], a +popular SMTP client found in many Linux distributions. Edit `~/.gitconfig` +to instruct `git-send-email` to use it for sending emails. + +---- +[sendemail] + sendmailCmd = /usr/bin/msmtp # Change this to the path where msmtp is installed +---- + +Links of a few such community maintained helpers are: + + - https://marlam.de/msmtp/[msmtp] + (popular SMTP client with many features, available for Linux and macOS) + + - https://github.com/AdityaGarg8/git-credential-email[git-protonmail] + (cross platform client that can send emails using the ProtonMail API) + + - https://github.com/AdityaGarg8/git-credential-email[git-msgraph] + (cross platform client that can send emails using the Microsoft Graph API) SEE ALSO -------- diff --git a/builtin/am.c b/builtin/am.c index a800003340f6ec..c9d925f7b96071 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2406,6 +2406,7 @@ int cmd_am(int argc, .type = OPTION_CALLBACK, .long_name = "show-current-patch", .value = &resume_mode, + .precision = sizeof(resume_mode), .argh = "(diff|raw)", .help = N_("show the patch being applied"), .flags = PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, diff --git a/builtin/fetch.c b/builtin/fetch.c index d231a669d70945..87a0cca7994ff3 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1340,9 +1340,10 @@ static int prune_refs(struct display_state *display_state, int result = 0; struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map); struct strbuf err = STRBUF_INIT; - const char *dangling_msg = dry_run - ? _(" (%s will become dangling)") - : _(" (%s has become dangling)"); + struct string_list refnames = STRING_LIST_INIT_NODUP; + + for (ref = stale_refs; ref; ref = ref->next) + string_list_append(&refnames, ref->name); if (!dry_run) { if (transaction) { @@ -1353,15 +1354,9 @@ static int prune_refs(struct display_state *display_state, goto cleanup; } } else { - struct string_list refnames = STRING_LIST_INIT_NODUP; - - for (ref = stale_refs; ref; ref = ref->next) - string_list_append(&refnames, ref->name); - result = refs_delete_refs(get_main_ref_store(the_repository), "fetch: prune", &refnames, 0); - string_list_clear(&refnames, 0); } } @@ -1373,12 +1368,14 @@ static int prune_refs(struct display_state *display_state, _("(none)"), ref->name, &ref->new_oid, &ref->old_oid, summary_width); - refs_warn_dangling_symref(get_main_ref_store(the_repository), - stderr, dangling_msg, ref->name); } + string_list_sort(&refnames); + refs_warn_dangling_symrefs(get_main_ref_store(the_repository), + stderr, " ", dry_run, &refnames); } cleanup: + string_list_clear(&refnames, 0); strbuf_release(&err); free_refs(stale_refs); return result; diff --git a/builtin/rebase.c b/builtin/rebase.c index 2e8c4ee6784d84..e90562a3b8a84e 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1128,6 +1128,7 @@ int cmd_rebase(int argc, .short_name = 'n', .long_name = "no-stat", .value = &options.flags, + .precision = sizeof(options.flags), .help = N_("do not show diffstat of what changed upstream"), .flags = PARSE_OPT_NOARG, .defval = REBASE_DIFFSTAT, diff --git a/builtin/remote.c b/builtin/remote.c index 7cbda285ebed33..18843b6bf6ccb5 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1521,9 +1521,6 @@ static int prune_remote(const char *remote, int dry_run) struct ref_states states = REF_STATES_INIT; struct string_list refs_to_prune = STRING_LIST_INIT_NODUP; struct string_list_item *item; - const char *dangling_msg = dry_run - ? _(" %s will become dangling!") - : _(" %s has become dangling!"); get_remote_ref_states(remote, &states, GET_REF_STATES); @@ -1555,7 +1552,7 @@ static int prune_remote(const char *remote, int dry_run) } refs_warn_dangling_symrefs(get_main_ref_store(the_repository), - stdout, dangling_msg, &refs_to_prune); + stdout, " ", dry_run, &refs_to_prune); string_list_clear(&refs_to_prune, 0); free_remote_ref_states(&states); diff --git a/builtin/update-index.c b/builtin/update-index.c index 538b619ba4f3bb..0c1d4ed55beb08 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -981,6 +981,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "assume-unchanged", .value = &mark_valid_only, + .precision = sizeof(mark_valid_only), .help = N_("mark files as \"not changing\""), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = MARK_FLAG, @@ -989,6 +990,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "no-assume-unchanged", .value = &mark_valid_only, + .precision = sizeof(mark_valid_only), .help = N_("clear assumed-unchanged bit"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = UNMARK_FLAG, @@ -997,6 +999,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "skip-worktree", .value = &mark_skip_worktree_only, + .precision = sizeof(mark_skip_worktree_only), .help = N_("mark files as \"index-only\""), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = MARK_FLAG, @@ -1005,6 +1008,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "no-skip-worktree", .value = &mark_skip_worktree_only, + .precision = sizeof(mark_skip_worktree_only), .help = N_("clear skip-worktree bit"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = UNMARK_FLAG, @@ -1079,6 +1083,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "fsmonitor-valid", .value = &mark_fsmonitor_only, + .precision = sizeof(mark_fsmonitor_only), .help = N_("mark files as fsmonitor valid"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = MARK_FLAG, @@ -1087,6 +1092,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "no-fsmonitor-valid", .value = &mark_fsmonitor_only, + .precision = sizeof(mark_fsmonitor_only), .help = N_("clear fsmonitor valid bit"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = UNMARK_FLAG, diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 5a8dc377ec07b0..cfec044710a56a 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -35,6 +35,7 @@ int cmd_write_tree(int argc, .type = OPTION_BIT, .long_name = "ignore-cache-tree", .value = &flags, + .precision = sizeof(flags), .help = N_("only useful for debugging"), .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, .defval = WRITE_TREE_IGNORE_CACHE_TREE, diff --git a/parse-options.c b/parse-options.c index a9a39ecaef6c36..5224203ffe7bf8 100644 --- a/parse-options.c +++ b/parse-options.c @@ -68,6 +68,64 @@ static char *fix_filename(const char *prefix, const char *file) return prefix_filename_except_for_dash(prefix, file); } +static int do_get_int_value(const void *value, size_t precision, intmax_t *ret) +{ + switch (precision) { + case sizeof(int8_t): + *ret = *(int8_t *)value; + return 0; + case sizeof(int16_t): + *ret = *(int16_t *)value; + return 0; + case sizeof(int32_t): + *ret = *(int32_t *)value; + return 0; + case sizeof(int64_t): + *ret = *(int64_t *)value; + return 0; + default: + return -1; + } +} + +static intmax_t get_int_value(const struct option *opt, enum opt_parsed flags) +{ + intmax_t ret; + if (do_get_int_value(opt->value, opt->precision, &ret)) + BUG("invalid precision for option %s", optname(opt, flags)); + return ret; +} + +static enum parse_opt_result set_int_value(const struct option *opt, + enum opt_parsed flags, + intmax_t value) +{ + switch (opt->precision) { + case sizeof(int8_t): + *(int8_t *)opt->value = value; + return 0; + case sizeof(int16_t): + *(int16_t *)opt->value = value; + return 0; + case sizeof(int32_t): + *(int32_t *)opt->value = value; + return 0; + case sizeof(int64_t): + *(int64_t *)opt->value = value; + return 0; + default: + BUG("invalid precision for option %s", optname(opt, flags)); + } +} + +static int signed_int_fits(intmax_t value, size_t precision) +{ + size_t bits = precision * CHAR_BIT; + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); + intmax_t lower_bound = -upper_bound - 1; + return lower_bound <= value && value <= upper_bound; +} + static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, const struct option *opt, enum opt_parsed flags, @@ -89,35 +147,55 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return opt->ll_callback(p, opt, NULL, unset); case OPTION_BIT: + { + intmax_t value = get_int_value(opt, flags); if (unset) - *(int *)opt->value &= ~opt->defval; + value &= ~opt->defval; else - *(int *)opt->value |= opt->defval; - return 0; + value |= opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_NEGBIT: + { + intmax_t value = get_int_value(opt, flags); if (unset) - *(int *)opt->value |= opt->defval; + value |= opt->defval; else - *(int *)opt->value &= ~opt->defval; - return 0; + value &= ~opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_BITOP: + { + intmax_t value = get_int_value(opt, flags); if (unset) BUG("BITOP can't have unset form"); - *(int *)opt->value &= ~opt->extra; - *(int *)opt->value |= opt->defval; - return 0; + value &= ~opt->extra; + value |= opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_COUNTUP: - if (*(int *)opt->value < 0) - *(int *)opt->value = 0; - *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; - return 0; + { + size_t bits = CHAR_BIT * opt->precision; + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); + intmax_t value = get_int_value(opt, flags); + + if (value < 0) + value = 0; + if (unset) + value = 0; + else if (value < upper_bound) + value++; + else + return error(_("value for %s exceeds %"PRIdMAX), + optname(opt, flags), upper_bound); + return set_int_value(opt, flags, value); + } case OPTION_SET_INT: - *(int *)opt->value = unset ? 0 : opt->defval; - return 0; + return set_int_value(opt, flags, unset ? 0 : opt->defval); case OPTION_STRING: if (unset) @@ -199,23 +277,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound); - switch (opt->precision) { - case 1: - *(int8_t *)opt->value = value; - return 0; - case 2: - *(int16_t *)opt->value = value; - return 0; - case 4: - *(int32_t *)opt->value = value; - return 0; - case 8: - *(int64_t *)opt->value = value; - return 0; - default: - BUG("invalid precision for option %s", - optname(opt, flags)); - } + return set_int_value(opt, flags, value); } case OPTION_UNSIGNED: { @@ -266,7 +328,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, } struct parse_opt_cmdmode_list { - int value, *value_ptr; + intmax_t value; + void *value_ptr; + size_t precision; const struct option *opt; const char *arg; enum opt_parsed flags; @@ -280,7 +344,7 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, for (; opts->type != OPTION_END; opts++) { struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list; - int *value_ptr = opts->value; + void *value_ptr = opts->value; if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr) continue; @@ -292,10 +356,13 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, CALLOC_ARRAY(elem, 1); elem->value_ptr = value_ptr; - elem->value = *value_ptr; + elem->precision = opts->precision; + if (do_get_int_value(value_ptr, opts->precision, &elem->value)) + optbug(opts, "has invalid precision"); elem->next = ctx->cmdmode_list; ctx->cmdmode_list = elem; } + BUG_if_bug("invalid 'struct option'"); } static char *optnamearg(const struct option *opt, const char *arg, @@ -317,7 +384,13 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, char *opt_name, *other_opt_name; for (; elem; elem = elem->next) { - if (*elem->value_ptr == elem->value) + intmax_t new_value; + + if (do_get_int_value(elem->value_ptr, elem->precision, + &new_value)) + BUG("impossible: invalid precision"); + + if (new_value == elem->value) continue; if (elem->opt && @@ -327,7 +400,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, elem->opt = opt; elem->arg = arg; elem->flags = flags; - elem->value = *elem->value_ptr; + elem->value = new_value; } if (result || !elem) @@ -586,10 +659,14 @@ static void parse_options_check(const struct option *opts) opts->long_name && !(opts->flags & PARSE_OPT_NONEG)) optbug(opts, "OPTION_SET_INT 0 should not be negatable"); switch (opts->type) { - case OPTION_COUNTUP: + case OPTION_SET_INT: case OPTION_BIT: case OPTION_NEGBIT: - case OPTION_SET_INT: + case OPTION_BITOP: + case OPTION_COUNTUP: + if (!signed_int_fits(opts->defval, opts->precision)) + optbug(opts, "has invalid defval"); + /* fallthru */ case OPTION_NUMBER: if ((opts->flags & PARSE_OPT_OPTARG) || !(opts->flags & PARSE_OPT_NOARG)) diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b3dda..312045604d98d1 100644 --- a/parse-options.h +++ b/parse-options.h @@ -172,6 +172,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG|(f), \ .callback = NULL, \ @@ -182,6 +183,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG|(f), \ } @@ -190,6 +192,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG | (f), \ .defval = (i), \ @@ -238,6 +241,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG|PARSE_OPT_NONEG, \ .defval = (set), \ @@ -248,6 +252,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG, \ .defval = (b), \ @@ -260,6 +265,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \ .defval = 1, \ @@ -269,6 +275,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \ .defval = (i), \ diff --git a/refs.c b/refs.c index 0efeb11311a1ab..73913b6627be95 100644 --- a/refs.c +++ b/refs.c @@ -439,9 +439,9 @@ static int for_each_filter_refs(const char *refname, const char *referent, struct warn_if_dangling_data { struct ref_store *refs; FILE *fp; - const char *refname; const struct string_list *refnames; - const char *msg_fmt; + const char *indent; + int dry_run; }; static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED, @@ -449,44 +449,34 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU int flags, void *cb_data) { struct warn_if_dangling_data *d = cb_data; - const char *resolves_to; + const char *resolves_to, *msg; if (!(flags & REF_ISSYMREF)) return 0; resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL); if (!resolves_to - || (d->refname - ? strcmp(resolves_to, d->refname) - : !string_list_has_string(d->refnames, resolves_to))) { + || !string_list_has_string(d->refnames, resolves_to)) { return 0; } - fprintf(d->fp, d->msg_fmt, refname); - fputc('\n', d->fp); + msg = d->dry_run + ? _("%s%s will become dangling after %s is deleted\n") + : _("%s%s has become dangling after %s was deleted\n"); + fprintf(d->fp, msg, d->indent, refname, resolves_to); return 0; } -void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const char *refname) -{ - struct warn_if_dangling_data data = { - .refs = refs, - .fp = fp, - .refname = refname, - .msg_fmt = msg_fmt, - }; - refs_for_each_rawref(refs, warn_if_dangling_symref, &data); -} - void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const struct string_list *refnames) + const char *indent, int dry_run, + const struct string_list *refnames) { struct warn_if_dangling_data data = { .refs = refs, .fp = fp, .refnames = refnames, - .msg_fmt = msg_fmt, + .indent = indent, + .dry_run = dry_run, }; refs_for_each_rawref(refs, warn_if_dangling_symref, &data); } diff --git a/refs.h b/refs.h index 2d58af3d88257a..efa182c6a144ca 100644 --- a/refs.h +++ b/refs.h @@ -452,10 +452,9 @@ static inline const char *has_glob_specials(const char *pattern) return strpbrk(pattern, "?*["); } -void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const char *refname); void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const struct string_list *refnames); + const char *indent, int dry_run, + const struct string_list *refnames); /* * Flags for controlling behaviour of pack_refs() diff --git a/refs/files-backend.c b/refs/files-backend.c index 92c3d2c318a054..89ae4517a97558 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2760,6 +2760,8 @@ static void files_transaction_cleanup(struct files_ref_store *refs, if (lock) { unlock_ref(lock); + try_remove_empty_parents(refs, update->refname, + REMOVE_EMPTY_PARENTS_REF); update->backend_data = NULL; } } diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index f2663dd0c07279..68579d83f3939e 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -131,6 +131,7 @@ int cmd__parse_options(int argc, const char **argv) .short_name = 'B', .long_name = "no-fear", .value = &boolean, + .precision = sizeof(boolean), .help = "be brave", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = 1, @@ -148,9 +149,16 @@ int cmd__parse_options(int argc, const char **argv) OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1), OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2), - OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)", - "set integer to 3 or 4 (cmdmode option)", - PARSE_OPT_CMDMODE, mode34_callback), + { + .type = OPTION_CALLBACK, + .long_name = "mode34", + .value = &integer, + .precision = sizeof(integer), + .argh = "(3|4)", + .help = "set integer to 3 or 4 (cmdmode option)", + .flags = PARSE_OPT_CMDMODE, + .callback = mode34_callback, + }, OPT_CALLBACK('L', "length", &integer, "str", "get length of ", length_callback), OPT_FILENAME('F', "file", &file, "set file to "), @@ -170,6 +178,7 @@ int cmd__parse_options(int argc, const char **argv) .type = OPTION_COUNTUP, .short_name = '+', .value = &boolean, + .precision = sizeof(boolean), .help = "same as -b", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, }, @@ -177,6 +186,7 @@ int cmd__parse_options(int argc, const char **argv) .type = OPTION_COUNTUP, .long_name = "ambiguous", .value = &ambiguous, + .precision = sizeof(ambiguous), .help = "positive ambiguity", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, }, @@ -184,6 +194,7 @@ int cmd__parse_options(int argc, const char **argv) .type = OPTION_COUNTUP, .long_name = "no-ambiguous", .value = &ambiguous, + .precision = sizeof(ambiguous), .help = "negative ambiguity", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, }, diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index f123ef1e360aca..1f61b666a7d382 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -197,7 +197,7 @@ $content" # FIXME: %(rest) is incompatible with object names that include whitespace, # e.g. HEAD:path/to/a/file with spaces. Use the resolved OID as input to # test this instead of the raw object name. - if echo "$object_name" | grep " "; then + if echo "$object_name" | grep -q " "; then test_rest=test_expect_failure else test_rest=test_expect_success diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 7359af02a2aaeb..96648a6e5dfd89 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -2349,4 +2349,23 @@ test_expect_success 'update-ref should also create reflog for HEAD' ' test_cmp expect actual ' +test_expect_success REFFILES 'empty directories are pruned when aborting a transaction' ' + test_path_is_missing .git/refs/heads/nested && + git update-ref --stdin <<-EOF && + create refs/heads/nested/something HEAD + prepare + abort + EOF + test_path_is_missing .git/refs/heads/nested +' + +test_expect_success REFFILES 'empty directories are pruned when not committing' ' + test_path_is_missing .git/refs/heads/nested && + git update-ref --stdin <<-EOF && + create refs/heads/nested/something HEAD + prepare + EOF + test_path_is_missing .git/refs/heads/nested +' + test_done