diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index bec200428f9f61..b8670751f5c705 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -668,6 +668,19 @@ For C programs: unsigned other_field:1; unsigned field_with_longer_name:1; + - Array names should be named in the singular form if the individual items are + subject of use. E.g.: + + char *dog[] = ...; + walk_dog(dog[0]); + walk_dog(dog[1]); + + Cases where the array is employed as a whole rather than as its unit parts, + the plural forms is preferable. E.g: + + char *dogs[] = ...; + walk_all_dogs(dogs); + For Perl programs: - Most of the C guidelines above apply. diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc index 6425d47e00c165..f37c0be602db33 100644 --- a/Documentation/RelNotes/2.54.0.adoc +++ b/Documentation/RelNotes/2.54.0.adoc @@ -47,6 +47,19 @@ UI, Workflows & Features * "git add -p" learned a new mode that allows the user to revisit a file that was already dealt with. + * Allow the directory in which reference backends store their data to + be specified. + + * "gitweb" has been taught to be mobile friendly. + + * "git apply --directory=./un/../normalized/path" now normalizes the + given path before using it. + + * "git maintenance" starts using the "geometric" strategy by default. + + * "git config list" is taught to show the values interpreted for + specific type with "--type=" option. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -110,6 +123,22 @@ Performance, Internal Implementation, Development Support etc. * The code to accept shallow "git push" has been optimized. + * Simplify build procedure for oxskeychain (in contrib/). + + * Fix dependency screw-up in meson-based builds. + + * Wean the mailmap code off of the_repository dependency. + + * API clean-up for the worktree subsystem. + + * The last uses of the_repository in "tree-diff.c" have been + eradicated. + + * Clean-up the code around "git repo info" command. + + * Mark the marge-ort codebase to prevent more uses of the_repository + from getting added. + Fixes since v2.53 ----------------- @@ -185,6 +214,17 @@ Fixes since v2.53 * An earlier attempt to optimize "git subtree" discarded too much relevant histories, which has been corrected. + * A prefetch call can be triggered to access a stale diff_queue entry + after diffcore-break breaks a filepair into two and freed the + original entry that is no longer used, leading to a segfault, which + has been corrected. + (merge 2d88ab078d hy/diff-lazy-fetch-with-break-fix later to maint). + + * "git fetch --deepen" that tries to go beyond merged branch used to + get confused where the updated shallow points are, which has been + corrected. + (merge 3ef68ff40e sp/shallow-deepen-relative-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint). (merge 7a747f972d dd/t5403-modernise later to maint). @@ -214,9 +254,4 @@ Fixes since v2.53 (merge b10e0cb1f3 kh/doc-am-xref later to maint). (merge ed84bc1c0d kh/doc-patch-id-4 later to maint). (merge 7451864bfa sc/pack-redundant-leakfix later to maint). - - * A prefetch call can be triggered to access a stale diff_queue entry - after diffcore-break breaks a filepair into two and freed the - original entry that is no longer used, leading to a segfault, which - has been corrected. - (merge 2d88ab078d hy/diff-lazy-fetch-with-break-fix later to maint). + (merge f87593ab1a cx/fetch-display-ubfix later to maint). diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc index 2aef3315b1d275..be6678bb5b5585 100644 --- a/Documentation/config/extensions.adoc +++ b/Documentation/config/extensions.adoc @@ -57,10 +57,24 @@ For historical reasons, this extension is respected regardless of the `core.repositoryFormatVersion` setting. refStorage::: - Specify the ref storage format to use. The acceptable values are: + Specify the ref storage format and a corresponding payload. The value + can be either a format name or a URI: + -- +* A format name alone (e.g., `reftable` or `files`). + +* A URI format `://` explicitly specifies both the + format and payload (e.g., `reftable:///foo/bar`). + +Supported format names are: + include::../ref-storage-format.adoc[] + +The payload is passed directly to the reference backend. For the files and +reftable backends, this must be a filesystem path where the references will +be stored. Defaulting to the commondir when no payload is provided. Relative +paths are resolved relative to the `$GIT_DIR`. Future backends may support +other payload schemes, e.g., postgres://127.0.0.1:5432?database=myrepo. -- + Note that this setting should only be set by linkgit:git-init[1] or diff --git a/Documentation/config/maintenance.adoc b/Documentation/config/maintenance.adoc index d0c38f03fabd60..b578856dde1dd4 100644 --- a/Documentation/config/maintenance.adoc +++ b/Documentation/config/maintenance.adoc @@ -30,8 +30,7 @@ The possible strategies are: + * `none`: This strategy implies no tasks are run at all. This is the default strategy for scheduled maintenance. -* `gc`: This strategy runs the `gc` task. This is the default strategy for - manual maintenance. +* `gc`: This strategy runs the `gc` task. * `geometric`: This strategy performs geometric repacking of packfiles and keeps auxiliary data structures up-to-date. The strategy expires data in the reflog and removes worktrees that cannot be located anymore. When the @@ -40,7 +39,8 @@ The possible strategies are: are already part of a cruft pack will be expired. + This repacking strategy is a full replacement for the `gc` strategy and is -recommended for large repositories. +recommended for large repositories. This is the default strategy for manual +maintenance. * `incremental`: This setting optimizes for performing small maintenance activities that do not delete any data. This does not schedule the `gc` task, but runs the `prefetch` and `commit-graph` tasks hourly, the diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc index ac3b536a155c6e..5300dd4c51257a 100644 --- a/Documentation/git-config.adoc +++ b/Documentation/git-config.adoc @@ -240,6 +240,9 @@ Valid ``'s include: that the given value is canonicalize-able as an ANSI color, but it is written as-is. + +If the command is in `list` mode, then the `--type ` argument will apply +to each listed config value. If the value does not successfully parse in that +format, then it will be omitted from the list. --bool:: --int:: diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc index 319d30bd86e7f4..5e2968b707eacd 100644 --- a/Documentation/git-repo.adoc +++ b/Documentation/git-repo.adoc @@ -33,14 +33,14 @@ supported: + `lines`::: - output key-value pairs one per line using the `=` character as + Output key-value pairs one per line using the `=` character as the delimiter between the key and the value. Values containing "unusual" characters are quoted as explained for the configuration variable `core.quotePath` (see linkgit:git-config[1]). This is the default. `nul`::: - similar to `lines`, but using a newline character as the delimiter - between the key and the value and using a NUL character after each value. + Similar to `lines`, but using a newline character as the delimiter + between the key and the value and using a _NUL_ character after each value. This format is better suited for being parsed by another applications than `lines`. Unlike in the `lines` format, the values are never quoted. + @@ -80,7 +80,7 @@ supported: configuration variable `core.quotePath` (see linkgit:git-config[1]). `nul`::: - Similar to `lines`, but uses a NUL character to delimit between + Similar to `lines`, but uses a _NUL_ character to delimit between key-value pairs instead of a newline. Also uses a newline character as the delimiter between the key and value instead of '='. Unlike the `lines` format, values containing "unusual" characters are never diff --git a/Documentation/git.adoc b/Documentation/git.adoc index ce099e78b8023e..66442735ea61e0 100644 --- a/Documentation/git.adoc +++ b/Documentation/git.adoc @@ -584,6 +584,11 @@ double-quotes and respecting backslash escapes. E.g., the value repositories will be set to this value. The default is "files". See `--ref-format` in linkgit:git-init[1]. +`GIT_REFERENCE_BACKEND`:: + Specify which reference backend to be used along with its URI. + See `extensions.refStorage` option in linkgit:git-config[1] for more + details. Overrides the config variable when used. + Git Commits ~~~~~~~~~~~ `GIT_AUTHOR_NAME`:: diff --git a/Makefile b/Makefile index 7f37ad8f58435e..f3264d0a37cc50 100644 --- a/Makefile +++ b/Makefile @@ -2688,9 +2688,10 @@ $(BUILT_INS): git$X cp $< $@ config-list.h: generate-configlist.sh + @mkdir -p .depend + $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh . $@ .depend/config-list.h.d -config-list.h: Documentation/*config.adoc Documentation/config/*.adoc - $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh . $@ +-include .depend/config-list.h.d command-list.h: generate-cmdlist.sh command-list.txt @@ -2880,6 +2881,10 @@ objects: $(OBJECTS) dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d) dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS)))) +ifeq ($(uname_S),Darwin) + dep_dirs += $(addsuffix .depend,$(sort $(dir contrib/credential/osxkeychain/git-credential-osxkeychain.o))) +endif + ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes) $(dep_dirs): @mkdir -p $@ @@ -4067,3 +4072,20 @@ $(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT) contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT) $(AR) $(ARFLAGS) $@ $^ + +contrib/credential/osxkeychain/git-credential-osxkeychain: contrib/credential/osxkeychain/git-credential-osxkeychain.o $(LIB_FILE) GIT-LDFLAGS + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ + $(filter %.o,$^) $(LIB_FILE) $(EXTLIBS) -framework Security -framework CoreFoundation + +contrib/credential/osxkeychain/git-credential-osxkeychain.o: contrib/credential/osxkeychain/git-credential-osxkeychain.c GIT-CFLAGS + $(QUIET_LINK)$(CC) -o $@ -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< + +install-git-credential-osxkeychain: contrib/credential/osxkeychain/git-credential-osxkeychain + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) $(INSTALL_STRIP) $< '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + +.PHONY: clean-git-credential-osxkeychain +clean-git-credential-osxkeychain: + $(RM) \ + contrib/credential/osxkeychain/git-credential-osxkeychain \ + contrib/credential/osxkeychain/git-credential-osxkeychain.o diff --git a/apply.c b/apply.c index d044c95d50b8a4..b6dd1066a0c3e1 100644 --- a/apply.c +++ b/apply.c @@ -5022,6 +5022,10 @@ static int apply_option_parse_directory(const struct option *opt, strbuf_reset(&state->root); strbuf_addstr(&state->root, arg); + + if (strbuf_normalize_path(&state->root) < 0) + return error(_("unable to normalize directory: '%s'"), arg); + strbuf_complete(&state->root, '/'); return 0; } diff --git a/builtin/blame.c b/builtin/blame.c index eac2fe73201ca2..f3a11eff44ffc7 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1252,7 +1252,7 @@ int cmd_blame(int argc, sb.xdl_opts = xdl_opts; sb.no_whole_file_rename = no_whole_file_rename; - read_mailmap(&mailmap); + read_mailmap(the_repository, &mailmap); sb.found_guilty_entry = &found_guilty_entry; sb.found_guilty_entry_data = π diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 53ffe80c79eeac..da059d0e2654b4 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -1127,7 +1127,7 @@ int cmd_cat_file(int argc, opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's'); if (use_mailmap) - read_mailmap(&mailmap); + read_mailmap(the_repository, &mailmap); switch (batch.objects_filter.choice) { case LOFC_DISABLED: diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index 9cc5c598302657..3f2a39cae0c1d0 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -63,9 +63,9 @@ int cmd_check_mailmap(int argc, if (argc == 0 && !use_stdin) die(_("no contacts specified")); - read_mailmap(&mailmap); + read_mailmap(the_repository, &mailmap); if (mailmap_blob) - read_mailmap_blob(&mailmap, mailmap_blob); + read_mailmap_blob(the_repository, &mailmap, mailmap_blob); if (mailmap_file) read_mailmap_file(&mailmap, mailmap_file, 0); diff --git a/builtin/clone.c b/builtin/clone.c index 45d8fa0eed78c4..70c8a68e6f30df 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -1228,12 +1228,7 @@ int cmd_clone(int argc, initialize_repository_version(GIT_HASH_UNKNOWN, the_repository->ref_storage_format, 1); - strbuf_addf(&buf, "%s/HEAD", git_dir); - write_file(buf.buf, "ref: refs/heads/.invalid"); - - strbuf_reset(&buf); - strbuf_addf(&buf, "%s/refs", git_dir); - safe_create_dir(the_repository, buf.buf, 1); + refs_create_refdir_stubs(the_repository, git_dir, NULL); /* * additional config can be injected with -c, make sure it's included @@ -1445,7 +1440,7 @@ int cmd_clone(int argc, hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1); repo_set_hash_algo(the_repository, hash_algo); - create_reference_database(the_repository->ref_storage_format, NULL, 1); + create_reference_database(NULL, 1); /* * Before fetching from the remote, download and install bundle diff --git a/builtin/commit.c b/builtin/commit.c index 9e3a09d532bfce..3700f66ba95efd 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1155,7 +1155,7 @@ static const char *find_author_by_nickname(const char *name) setup_revisions(ac, av, &revs, NULL); revs.mailmap = xmalloc(sizeof(struct string_list)); string_list_init_nodup(revs.mailmap); - read_mailmap(revs.mailmap); + read_mailmap(the_repository, revs.mailmap); if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); diff --git a/builtin/config.c b/builtin/config.c index 288ebdfdaaab1c..7c4857be622904 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -3,6 +3,7 @@ #include "abspath.h" #include "config.h" #include "color.h" +#include "date.h" #include "editor.h" #include "environment.h" #include "gettext.h" @@ -85,6 +86,17 @@ struct config_location_options { .respect_includes_opt = -1, \ } +enum config_type { + TYPE_NONE = 0, + TYPE_BOOL, + TYPE_INT, + TYPE_BOOL_OR_INT, + TYPE_PATH, + TYPE_EXPIRY_DATE, + TYPE_COLOR, + TYPE_BOOL_OR_STR, +}; + #define CONFIG_TYPE_OPTIONS(type) \ OPT_GROUP(N_("Type")), \ OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \ @@ -110,7 +122,7 @@ struct config_display_options { int show_origin; int show_scope; int show_keys; - int type; + enum config_type type; char *default_value; /* Populated via `display_options_init()`. */ int term; @@ -121,16 +133,9 @@ struct config_display_options { .term = '\n', \ .delim = '=', \ .key_delim = ' ', \ + .type = TYPE_NONE, \ } -#define TYPE_BOOL 1 -#define TYPE_INT 2 -#define TYPE_BOOL_OR_INT 3 -#define TYPE_PATH 4 -#define TYPE_EXPIRY_DATE 5 -#define TYPE_COLOR 6 -#define TYPE_BOOL_OR_STR 7 - #define OPT_CALLBACK_VALUE(s, l, v, h, i) { \ .type = OPTION_CALLBACK, \ .short_name = (s), \ @@ -231,104 +236,231 @@ static void show_config_scope(const struct config_display_options *opts, strbuf_addch(buf, term); } -static int show_all_config(const char *key_, const char *value_, - const struct config_context *ctx, - void *cb) +struct strbuf_list { + struct strbuf *items; + int nr; + int alloc; +}; + +static int format_config_int64(struct strbuf *buf, + const char *key_, + const char *value_, + const struct key_value_info *kvi, + int gently) { - const struct config_display_options *opts = cb; - const struct key_value_info *kvi = ctx->kvi; + int64_t v = 0; + if (gently) { + if (!git_parse_int64(value_, &v)) + return -1; + } else { + /* may die() */ + v = git_config_int64(key_, value_ ? value_ : "", kvi); + } - if (opts->show_origin || opts->show_scope) { - struct strbuf buf = STRBUF_INIT; - if (opts->show_scope) - show_config_scope(opts, kvi, &buf); - if (opts->show_origin) - show_config_origin(opts, kvi, &buf); - /* Use fwrite as "buf" can contain \0's if "end_null" is set. */ - fwrite(buf.buf, 1, buf.len, stdout); - strbuf_release(&buf); + strbuf_addf(buf, "%"PRId64, v); + return 0; +} + +static int format_config_bool(struct strbuf *buf, + const char *key_, + const char *value_, + int gently) +{ + int v = 0; + if (gently) { + if ((v = git_parse_maybe_bool(value_)) < 0) + return -1; + } else { + /* may die() */ + v = git_config_bool(key_, value_); } - if (!opts->omit_values && value_) - printf("%s%c%s%c", key_, opts->delim, value_, opts->term); + + strbuf_addstr(buf, v ? "true" : "false"); + return 0; +} + +static int format_config_bool_or_int(struct strbuf *buf, + const char *key_, + const char *value_, + const struct key_value_info *kvi, + int gently) +{ + int v, is_bool = 0; + + if (gently) { + v = git_parse_maybe_bool_text(value_); + + if (v >= 0) + is_bool = 1; + else if (!git_parse_int(value_, &v)) + return -1; + } else { + v = git_config_bool_or_int(key_, value_, kvi, + &is_bool); + } + + if (is_bool) + strbuf_addstr(buf, v ? "true" : "false"); else - printf("%s%c", key_, opts->term); + strbuf_addf(buf, "%d", v); + return 0; } -struct strbuf_list { - struct strbuf *items; - int nr; - int alloc; -}; +/* This mode is always gentle. */ +static int format_config_bool_or_str(struct strbuf *buf, + const char *value_) +{ + int v = git_parse_maybe_bool(value_); + if (v < 0) + strbuf_addstr(buf, value_); + else + strbuf_addstr(buf, v ? "true" : "false"); + return 0; +} + +static int format_config_path(struct strbuf *buf, + const char *key_, + const char *value_, + int gently) +{ + char *v; + + if (git_config_pathname(&v, key_, value_) < 0) + return -1; + + if (v) + strbuf_addstr(buf, v); + else + return gently ? -1 : 1; /* :(optional)no-such-file */ + + free(v); + return 0; +} + +static int format_config_expiry_date(struct strbuf *buf, + const char *key_, + const char *value_, + int quietly) +{ + timestamp_t t; + if (quietly) { + if (parse_expiry_date(value_, &t)) + return -1; + } else if (git_config_expiry_date(&t, key_, value_) < 0) { + return -1; + } + + strbuf_addf(buf, "%"PRItime, t); + return 0; +} + +static int format_config_color(struct strbuf *buf, + const char *key_, + const char *value_, + int gently) +{ + char v[COLOR_MAXLEN]; + + if (gently) { + if (color_parse_quietly(value_, v) < 0) + return -1; + } else if (git_config_color(v, key_, value_) < 0) { + return -1; + } + + strbuf_addstr(buf, v); + return 0; +} /* * Format the configuration key-value pair (`key_`, `value_`) and * append it into strbuf `buf`. Returns a negative value on failure, * 0 on success, 1 on a missing optional value (i.e., telling the * caller to pretend that did not exist). + * + * Note: 'gently' is currently ignored, but will be implemented in + * a future change. */ static int format_config(const struct config_display_options *opts, struct strbuf *buf, const char *key_, - const char *value_, const struct key_value_info *kvi) + const char *value_, const struct key_value_info *kvi, + int gently) { + int res = 0; if (opts->show_scope) show_config_scope(opts, kvi, buf); if (opts->show_origin) show_config_origin(opts, kvi, buf); if (opts->show_keys) strbuf_addstr(buf, key_); - if (!opts->omit_values) { - if (opts->show_keys) - strbuf_addch(buf, opts->key_delim); - - if (opts->type == TYPE_INT) - strbuf_addf(buf, "%"PRId64, - git_config_int64(key_, value_ ? value_ : "", kvi)); - else if (opts->type == TYPE_BOOL) - strbuf_addstr(buf, git_config_bool(key_, value_) ? - "true" : "false"); - else if (opts->type == TYPE_BOOL_OR_INT) { - int is_bool, v; - v = git_config_bool_or_int(key_, value_, kvi, - &is_bool); - if (is_bool) - strbuf_addstr(buf, v ? "true" : "false"); - else - strbuf_addf(buf, "%d", v); - } else if (opts->type == TYPE_BOOL_OR_STR) { - int v = git_parse_maybe_bool(value_); - if (v < 0) - strbuf_addstr(buf, value_); - else - strbuf_addstr(buf, v ? "true" : "false"); - } else if (opts->type == TYPE_PATH) { - char *v; - if (git_config_pathname(&v, key_, value_) < 0) - return -1; - if (v) - strbuf_addstr(buf, v); - else - return 1; /* :(optional)no-such-file */ - free((char *)v); - } else if (opts->type == TYPE_EXPIRY_DATE) { - timestamp_t t; - if (git_config_expiry_date(&t, key_, value_) < 0) - return -1; - strbuf_addf(buf, "%"PRItime, t); - } else if (opts->type == TYPE_COLOR) { - char v[COLOR_MAXLEN]; - if (git_config_color(v, key_, value_) < 0) - return -1; - strbuf_addstr(buf, v); - } else if (value_) { + + if (opts->omit_values) + goto terminator; + + if (opts->show_keys) + strbuf_addch(buf, opts->key_delim); + + switch (opts->type) { + case TYPE_INT: + res = format_config_int64(buf, key_, value_, kvi, gently); + break; + + case TYPE_BOOL: + res = format_config_bool(buf, key_, value_, gently); + break; + + case TYPE_BOOL_OR_INT: + res = format_config_bool_or_int(buf, key_, value_, kvi, gently); + break; + + case TYPE_BOOL_OR_STR: + res = format_config_bool_or_str(buf, value_); + break; + + case TYPE_PATH: + res = format_config_path(buf, key_, value_, gently); + break; + + case TYPE_EXPIRY_DATE: + res = format_config_expiry_date(buf, key_, value_, gently); + break; + + case TYPE_COLOR: + res = format_config_color(buf, key_, value_, gently); + break; + + case TYPE_NONE: + if (value_) { strbuf_addstr(buf, value_); } else { /* Just show the key name; back out delimiter */ if (opts->show_keys) strbuf_setlen(buf, buf->len - 1); } + break; + + default: + BUG("undefined type %d", opts->type); } + +terminator: strbuf_addch(buf, opts->term); + return res; +} + +static int show_all_config(const char *key_, const char *value_, + const struct config_context *ctx, + void *cb) +{ + const struct config_display_options *opts = cb; + const struct key_value_info *kvi = ctx->kvi; + struct strbuf formatted = STRBUF_INIT; + + if (format_config(opts, &formatted, key_, value_, kvi, 1) >= 0) + fwrite(formatted.buf, 1, formatted.len, stdout); + + strbuf_release(&formatted); return 0; } @@ -372,7 +504,7 @@ static int collect_config(const char *key_, const char *value_, strbuf_init(&values->items[values->nr], 0); status = format_config(data->display_opts, &values->items[values->nr++], - key_, value_, kvi); + key_, value_, kvi, 0); if (status < 0) return status; if (status) { @@ -463,7 +595,7 @@ static int get_value(const struct config_location_options *opts, strbuf_init(item, 0); status = format_config(display_opts, item, key_, - display_opts->default_value, &kvi); + display_opts->default_value, &kvi, 0); if (status < 0) die(_("failed to format default config value: %s"), display_opts->default_value); @@ -743,7 +875,7 @@ static int get_urlmatch(const struct config_location_options *opts, status = format_config(&display_opts, &buf, item->string, matched->value_is_null ? NULL : matched->value.buf, - &matched->kvi); + &matched->kvi, 0); if (!status) fwrite(buf.buf, 1, buf.len, stdout); strbuf_release(&buf); @@ -868,6 +1000,19 @@ static void display_options_init(struct config_display_options *opts) } } +static void display_options_init_list(struct config_display_options *opts) +{ + opts->show_keys = 1; + + if (opts->end_nul) { + display_options_init(opts); + } else { + opts->term = '\n'; + opts->delim = ' '; + opts->key_delim = '='; + } +} + static int cmd_config_list(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { @@ -886,7 +1031,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix, check_argc(argc, 0, 0); location_options_init(&location_opts, prefix); - display_options_init(&display_opts); + display_options_init_list(&display_opts); setup_auto_pager("config", 1); @@ -1317,6 +1462,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) if (actions == ACTION_LIST) { check_argc(argc, 0, 0); + display_options_init_list(&display_opts); if (config_with_options(show_all_config, &display_opts, &location_opts.source, the_repository, &location_opts.options) < 0) { diff --git a/builtin/fetch.c b/builtin/fetch.c index 573c2952415bc2..ef071e1e096196 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -721,7 +721,7 @@ static void display_state_init(struct display_state *display_state, struct ref * display_state->url = xstrdup("foreign"); display_state->url_len = strlen(display_state->url); - for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--) + for (i = display_state->url_len - 1; 0 <= i && display_state->url[i] == '/'; i--) ; display_state->url_len = i + 1; if (4 < i && !strncmp(".git", display_state->url + i - 3, 4)) diff --git a/builtin/fsck.c b/builtin/fsck.c index 384d47ee7731d2..51de6a6395ee74 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1105,7 +1105,7 @@ int cmd_fsck(int argc, * and may get overwritten by other calls * while we're examining the index. */ - path = xstrdup(worktree_git_path(the_repository, wt, "index")); + path = xstrdup(worktree_git_path(wt, "index")); wt_gitdir = get_worktree_git_dir(wt); read_index_from(&istate, path, wt_gitdir); diff --git a/builtin/gc.c b/builtin/gc.c index 4390eee6eca382..fb329c2cffab80 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1980,7 +1980,7 @@ static void initialize_task_config(struct maintenance_run_opts *opts, strategy = none_strategy; type = MAINTENANCE_TYPE_SCHEDULED; } else { - strategy = gc_strategy; + strategy = geometric_strategy; type = MAINTENANCE_TYPE_MANUAL; } diff --git a/builtin/log.c b/builtin/log.c index 3c76ee516901c0..7cb919bca9cc0c 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -336,7 +336,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (mailmap) { rev->mailmap = xmalloc(sizeof(struct string_list)); string_list_init_nodup(rev->mailmap); - read_mailmap(rev->mailmap); + read_mailmap(the_repository, rev->mailmap); } if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) { diff --git a/builtin/repo.c b/builtin/repo.c index 6a62a6020a5115..f943be74510158 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -31,7 +31,7 @@ enum output_format { FORMAT_NUL_TERMINATED, }; -struct field { +struct repo_info_field { const char *key; get_value_fn *get_value; }; @@ -62,30 +62,32 @@ static int get_references_format(struct repository *repo, struct strbuf *buf) return 0; } -/* repo_info_fields keys must be in lexicographical order */ -static const struct field repo_info_fields[] = { +/* repo_info_field keys must be in lexicographical order */ +static const struct repo_info_field repo_info_field[] = { { "layout.bare", get_layout_bare }, { "layout.shallow", get_layout_shallow }, { "object.format", get_object_format }, { "references.format", get_references_format }, }; -static int repo_info_fields_cmp(const void *va, const void *vb) +static int repo_info_field_cmp(const void *va, const void *vb) { - const struct field *a = va; - const struct field *b = vb; + const struct repo_info_field *a = va; + const struct repo_info_field *b = vb; return strcmp(a->key, b->key); } -static get_value_fn *get_value_fn_for_key(const char *key) +static const struct repo_info_field *get_repo_info_field(const char *key) { - const struct field search_key = { key, NULL }; - const struct field *found = bsearch(&search_key, repo_info_fields, - ARRAY_SIZE(repo_info_fields), - sizeof(*found), - repo_info_fields_cmp); - return found ? found->get_value : NULL; + const struct repo_info_field search_key = { key, NULL }; + const struct repo_info_field *found = bsearch(&search_key, + repo_info_field, + ARRAY_SIZE(repo_info_field), + sizeof(*found), + repo_info_field_cmp); + + return found; } static void print_field(enum output_format format, const char *key, @@ -113,18 +115,16 @@ static int print_fields(int argc, const char **argv, struct strbuf valbuf = STRBUF_INIT; for (int i = 0; i < argc; i++) { - get_value_fn *get_value; const char *key = argv[i]; + const struct repo_info_field *field = get_repo_info_field(key); - get_value = get_value_fn_for_key(key); - - if (!get_value) { + if (!field) { ret = error(_("key '%s' not found"), key); continue; } strbuf_reset(&valbuf); - get_value(repo, &valbuf); + field->get_value(repo, &valbuf); print_field(format, key, valbuf.buf); } @@ -137,8 +137,8 @@ static int print_all_fields(struct repository *repo, { struct strbuf valbuf = STRBUF_INIT; - for (size_t i = 0; i < ARRAY_SIZE(repo_info_fields); i++) { - const struct field *field = &repo_info_fields[i]; + for (size_t i = 0; i < ARRAY_SIZE(repo_info_field); i++) { + const struct repo_info_field *field = &repo_info_field[i]; strbuf_reset(&valbuf); field->get_value(repo, &valbuf); @@ -164,8 +164,8 @@ static int print_keys(enum output_format format) die(_("--keys can only be used with --format=lines or --format=nul")); } - for (size_t i = 0; i < ARRAY_SIZE(repo_info_fields); i++) { - const struct field *field = &repo_info_fields[i]; + for (size_t i = 0; i < ARRAY_SIZE(repo_info_field); i++) { + const struct repo_info_field *field = &repo_info_field[i]; printf("%s%c", field->key, sep); } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index d80bf1a7d055fc..6b2a0b93b5992e 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -357,7 +357,7 @@ void shortlog_init(struct shortlog *log) { memset(log, 0, sizeof(*log)); - read_mailmap(&log->mailmap); + read_mailmap(the_repository, &log->mailmap); log->list.strdup_strings = 1; log->wrap = DEFAULT_WRAPLEN; diff --git a/builtin/worktree.c b/builtin/worktree.c index 3d6547c23b434f..892800f70915f1 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -425,6 +425,39 @@ static int make_worktree_orphan(const char * ref, const struct add_opts *opts, return run_command(&cp); } +/* + * References for worktrees are generally stored in '$GIT_DIR/worktrees/'. + * But when using alternate reference directories, we want to store the worktree + * references in '$ALTERNATE_REFERENCE_DIR/worktrees/'. + * + * Create the necessary folder structure to facilitate the same. But to ensure + * that the former path is still considered a Git directory, add stubs. + */ +static void setup_alternate_ref_dir(struct worktree *wt, const char *wt_git_path) +{ + struct strbuf sb = STRBUF_INIT; + char *path; + + path = wt->repo->ref_storage_payload; + if (!path) + return; + + if (!is_absolute_path(path)) + strbuf_addf(&sb, "%s/", wt->repo->commondir); + + strbuf_addf(&sb, "%s/worktrees", path); + safe_create_dir(wt->repo, sb.buf, 1); + strbuf_addf(&sb, "/%s", wt->id); + safe_create_dir(wt->repo, sb.buf, 1); + strbuf_reset(&sb); + + strbuf_addf(&sb, "this worktree stores references in %s/worktrees/%s", + path, wt->id); + refs_create_refdir_stubs(wt->repo, wt_git_path, sb.buf); + + strbuf_release(&sb); +} + static int add_worktree(const char *path, const char *refname, const struct add_opts *opts) { @@ -518,6 +551,7 @@ static int add_worktree(const char *path, const char *refname, ret = error(_("could not find created worktree '%s'"), name); goto done; } + setup_alternate_ref_dir(wt, sb_repo.buf); wt_refs = get_worktree_ref_store(wt); ret = ref_store_create_on_disk(wt_refs, REF_STORE_CREATE_ON_DISK_IS_WORKTREE, &sb); @@ -1191,14 +1225,14 @@ static void validate_no_submodules(const struct worktree *wt) wt_gitdir = get_worktree_git_dir(wt); - if (is_directory(worktree_git_path(the_repository, wt, "modules"))) { + if (is_directory(worktree_git_path(wt, "modules"))) { /* * There could be false positives, e.g. the "modules" * directory exists but is empty. But it's a rare case and * this simpler check is probably good enough for now. */ found_submodules = 1; - } else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"), + } else if (read_index_from(&istate, worktree_git_path(wt, "index"), wt_gitdir) > 0) { for (i = 0; i < istate.cache_nr; i++) { struct cache_entry *ce = istate.cache[i]; diff --git a/color.c b/color.c index 07ac8c9d400906..00b53f97acbcc7 100644 --- a/color.c +++ b/color.c @@ -223,11 +223,6 @@ static int parse_attr(const char *name, size_t len) return -1; } -int color_parse(const char *value, char *dst) -{ - return color_parse_mem(value, strlen(value), dst); -} - /* * Write the ANSI color codes for "c" to "out"; the string should * already have the ANSI escape code in it. "out" should have enough @@ -264,7 +259,8 @@ static int color_empty(const struct color *c) return c->type <= COLOR_NORMAL; } -int color_parse_mem(const char *value, int value_len, char *dst) +static int color_parse_mem_1(const char *value, int value_len, + char *dst, int quiet) { const char *ptr = value; int len = value_len; @@ -365,10 +361,25 @@ int color_parse_mem(const char *value, int value_len, char *dst) OUT(0); return 0; bad: - return error(_("invalid color value: %.*s"), value_len, value); + return quiet ? -1 : error(_("invalid color value: %.*s"), value_len, value); #undef OUT } +int color_parse_mem(const char *value, int value_len, char *dst) +{ + return color_parse_mem_1(value, value_len, dst, 0); +} + +int color_parse(const char *value, char *dst) +{ + return color_parse_mem(value, strlen(value), dst); +} + +int color_parse_quietly(const char *value, char *dst) +{ + return color_parse_mem_1(value, strlen(value), dst, 1); +} + enum git_colorbool git_config_colorbool(const char *var, const char *value) { if (value) { diff --git a/color.h b/color.h index 43e6c9ad0972b3..0d7254030003f0 100644 --- a/color.h +++ b/color.h @@ -118,6 +118,7 @@ bool want_color_fd(int fd, enum git_colorbool var); * terminal. */ int color_parse(const char *value, char *dst); +int color_parse_quietly(const char *value, char *dst); int color_parse_mem(const char *value, int len, char *dst); /* diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile index c68445b82dc3e5..219b0d7f49e016 100644 --- a/contrib/credential/osxkeychain/Makefile +++ b/contrib/credential/osxkeychain/Makefile @@ -1,66 +1,13 @@ # The default target of this Makefile is... all:: git-credential-osxkeychain -include ../../../config.mak.uname --include ../../../config.mak.autogen --include ../../../config.mak +git-credential-osxkeychain: + $(MAKE) -C ../../.. contrib/credential/osxkeychain/git-credential-osxkeychain -ifdef ZLIB_NG - BASIC_CFLAGS += -DHAVE_ZLIB_NG - ifdef ZLIB_NG_PATH - BASIC_CFLAGS += -I$(ZLIB_NG_PATH)/include - EXTLIBS += $(call libpath_template,$(ZLIB_NG_PATH)/$(lib)) - endif - EXTLIBS += -lz-ng -else - ifdef ZLIB_PATH - BASIC_CFLAGS += -I$(ZLIB_PATH)/include - EXTLIBS += $(call libpath_template,$(ZLIB_PATH)/$(lib)) - endif - EXTLIBS += -lz -endif -ifndef NO_ICONV - ifdef NEEDS_LIBICONV - ifdef ICONVDIR - BASIC_CFLAGS += -I$(ICONVDIR)/include - ICONV_LINK = $(call libpath_template,$(ICONVDIR)/$(lib)) - else - ICONV_LINK = - endif - ifdef NEEDS_LIBINTL_BEFORE_LIBICONV - ICONV_LINK += -lintl - endif - EXTLIBS += $(ICONV_LINK) -liconv - endif -endif -ifndef LIBC_CONTAINS_LIBINTL - EXTLIBS += -lintl -endif - -prefix ?= /usr/local -gitexecdir ?= $(prefix)/libexec/git-core - -CC ?= gcc -CFLAGS ?= -g -O2 -Wall -I../../.. $(BASIC_CFLAGS) -LDFLAGS ?= $(BASIC_LDFLAGS) $(EXTLIBS) -INSTALL ?= install -RM ?= rm -f - -%.o: %.c - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< - -git-credential-osxkeychain: git-credential-osxkeychain.o ../../../libgit.a - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) \ - -framework Security -framework CoreFoundation - -install: git-credential-osxkeychain - $(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir) - $(INSTALL) -m 755 $< $(DESTDIR)$(gitexecdir) - -../../../libgit.a: - cd ../../..; make libgit.a +install: + $(MAKE) -C ../../.. install-git-credential-osxkeychain clean: - $(RM) git-credential-osxkeychain git-credential-osxkeychain.o + $(MAKE) -C ../../.. clean-git-credential-osxkeychain -.PHONY: all install clean +.PHONY: all git-credential-osxkeychain install clean diff --git a/diff.c b/diff.c index 7fcaa1a0359273..501648a5c49abc 100644 --- a/diff.c +++ b/diff.c @@ -7194,7 +7194,7 @@ void diffcore_std(struct diff_options *options) * If no prefetching occurs, diffcore_rename() will prefetch if it * decides that it needs inexact rename detection. */ - if (options->repo == the_repository && repo_has_promisor_remote(the_repository) && + if (repo_has_promisor_remote(options->repo) && (options->output_format & output_formats_to_prefetch || options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)) diff_queued_diff_prefetch(options->repo); diff --git a/diffcore-break.c b/diffcore-break.c index 9b11fe2fa0e622..17b5ad1fedeb3b 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -69,7 +69,7 @@ static int should_break(struct repository *r, oideq(&src->oid, &dst->oid)) return 0; /* they are the same */ - if (r == the_repository && repo_has_promisor_remote(the_repository)) { + if (repo_has_promisor_remote(r)) { options.missing_object_cb = diff_queued_diff_prefetch; options.missing_object_data = r; } diff --git a/diffcore-rename.c b/diffcore-rename.c index d9476db35acbf7..c797d8ed2f54dd 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -987,7 +987,7 @@ static int find_basename_matches(struct diff_options *options, strintmap_set(&dests, base, i); } - if (options->repo == the_repository && repo_has_promisor_remote(the_repository)) { + if (repo_has_promisor_remote(options->repo)) { dpf_options.missing_object_cb = basename_prefetch; dpf_options.missing_object_data = &prefetch_options; } @@ -1574,7 +1574,7 @@ void diffcore_rename_extended(struct diff_options *options, /* Finish setting up dpf_options */ prefetch_options.skip_unmodified = skip_unmodified; - if (options->repo == the_repository && repo_has_promisor_remote(the_repository)) { + if (repo_has_promisor_remote(options->repo)) { dpf_options.missing_object_cb = inexact_prefetch; dpf_options.missing_object_data = &prefetch_options; } diff --git a/environment.c b/environment.c index 0026eb227487dd..2764d8f4817c2a 100644 --- a/environment.c +++ b/environment.c @@ -647,22 +647,6 @@ static int git_default_push_config(const char *var, const char *value) return 0; } -static int git_default_mailmap_config(const char *var, const char *value) -{ - if (!strcmp(var, "mailmap.file")) { - FREE_AND_NULL(git_mailmap_file); - return git_config_pathname(&git_mailmap_file, var, value); - } - - if (!strcmp(var, "mailmap.blob")) { - FREE_AND_NULL(git_mailmap_blob); - return git_config_string(&git_mailmap_blob, var, value); - } - - /* Add other config variables here and to Documentation/config.adoc. */ - return 0; -} - static int git_default_attr_config(const char *var, const char *value) { if (!strcmp(var, "attr.tree")) { @@ -697,9 +681,6 @@ int git_default_config(const char *var, const char *value, if (starts_with(var, "push.")) return git_default_push_config(var, value); - if (starts_with(var, "mailmap.")) - return git_default_mailmap_config(var, value); - if (starts_with(var, "attr.")) return git_default_attr_config(var, value); diff --git a/environment.h b/environment.h index 27f657af046a51..540e0a7f6dd51f 100644 --- a/environment.h +++ b/environment.h @@ -42,6 +42,7 @@ #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS" #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR" #define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE" +#define GIT_REFERENCE_BACKEND_ENVIRONMENT "GIT_REFERENCE_BACKEND" /* * Environment variable used to propagate the --no-advice global option to the diff --git a/generate-configlist.sh b/generate-configlist.sh index 75c39ade20939d..e28054f9e0e9ba 100755 --- a/generate-configlist.sh +++ b/generate-configlist.sh @@ -2,10 +2,11 @@ SOURCE_DIR="$1" OUTPUT="$2" +DEPFILE="$3" if test -z "$SOURCE_DIR" || ! test -d "$SOURCE_DIR" || test -z "$OUTPUT" then - echo >&2 "USAGE: $0 " + echo >&2 "USAGE: $0 []" exit 1 fi @@ -36,3 +37,16 @@ EOF echo print_config_list } >"$OUTPUT" + +if test -n "$DEPFILE" +then + QUOTED_OUTPUT="$(printf '%s\n' "$OUTPUT" | sed 's,[&/\],\\&,g')" + { + printf '%s\n' "$SOURCE_DIR"/Documentation/*config.adoc \ + "$SOURCE_DIR"/Documentation/config/*.adoc | + sed -e 's/[# ]/\\&/g' -e "s/^/$QUOTED_OUTPUT: /" + printf '%s:\n' "$SOURCE_DIR"/Documentation/*config.adoc \ + "$SOURCE_DIR"/Documentation/config/*.adoc | + sed -e 's/[# ]/\\&/g' + } >"$DEPFILE" +fi diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b5490dfecf2da7..fde804593b6ff3 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4214,6 +4214,7 @@ sub git_header_html { + $title EOF # the stylesheet, favicon etc urls won't work correctly with path_info diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css index 48d2e5101542ad..e2e6dd96a2c915 100644 --- a/gitweb/static/gitweb.css +++ b/gitweb/static/gitweb.css @@ -42,7 +42,7 @@ a.list img.avatar { } div.page_header { - height: 25px; + min-height: 25px; padding: 8px; font-size: 150%; font-weight: bold; @@ -73,11 +73,17 @@ div.page_path { } div.page_footer { - height: 22px; + min-height: 22px; padding: 4px 8px; background-color: #d9d8d1; } +div.page_footer::after { + content: ""; + display: table; + clear: both; +} + div.page_footer_text { line-height: 22px; float: left; @@ -123,6 +129,7 @@ div.title_text { div.log_body { padding: 8px 8px 8px 150px; + overflow-wrap: anywhere; } span.age { @@ -684,3 +691,66 @@ div.remote { .kwb { color:#830000; } .kwc { color:#000000; font-weight:bold; } .kwd { color:#010181; } + +@media (max-width: 768px) { + div.page_body { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + + div.page_body div.pre { + min-width: max-content; + } + + div.projsearch { + padding: 0 8px; + box-sizing: border-box; + } + + div.projsearch input[type="text"] { + max-width: 100%; + box-sizing: border-box; + } + + div.title_text { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + padding-left: 4px; + padding-right: 4px; + box-sizing: border-box; + } + + div.title_text table.object_header { + width: max-content; + } + + div.log_body { + padding: 8px; + clear: left; + } + + div.patchset div.patch { + width: max-content; + min-width: 100%; + } + + div.diff.header { + padding: 4px 8px 2px 8px; + white-space: nowrap; + overflow-wrap: normal; + } + + div.diff.extended_header { + padding: 2px 8px; + white-space: nowrap; + overflow-wrap: normal; + } + + div.diff.ctx, + div.diff.add, + div.diff.rem, + div.diff.chunk_header { + padding: 0 8px; + white-space: pre; + } +} diff --git a/mailmap.c b/mailmap.c index 37fd158a516d25..3b2691781d8ff1 100644 --- a/mailmap.c +++ b/mailmap.c @@ -7,9 +7,7 @@ #include "object-name.h" #include "odb.h" #include "setup.h" - -char *git_mailmap_file; -char *git_mailmap_blob; +#include "config.h" struct mailmap_info { char *name; @@ -183,7 +181,8 @@ static void read_mailmap_string(struct string_list *map, char *buf) } } -int read_mailmap_blob(struct string_list *map, const char *name) +int read_mailmap_blob(struct repository *repo, struct string_list *map, + const char *name) { struct object_id oid; char *buf; @@ -192,10 +191,10 @@ int read_mailmap_blob(struct string_list *map, const char *name) if (!name) return 0; - if (repo_get_oid(the_repository, name, &oid) < 0) + if (repo_get_oid(repo, name, &oid) < 0) return 0; - buf = odb_read_object(the_repository->objects, &oid, &type, &size); + buf = odb_read_object(repo->objects, &oid, &type, &size); if (!buf) return error("unable to read mailmap object at %s", name); if (type != OBJ_BLOB) { @@ -209,23 +208,32 @@ int read_mailmap_blob(struct string_list *map, const char *name) return 0; } -int read_mailmap(struct string_list *map) +int read_mailmap(struct repository *repo, struct string_list *map) { int err = 0; + char *mailmap_file = NULL, *mailmap_blob = NULL; + + repo_config_get_pathname(repo, "mailmap.file", &mailmap_file); + repo_config_get_string(repo, "mailmap.blob", &mailmap_blob); map->strdup_strings = 1; map->cmp = namemap_cmp; - if (!git_mailmap_blob && is_bare_repository()) - git_mailmap_blob = xstrdup("HEAD:.mailmap"); + if (!mailmap_blob && is_bare_repository()) + mailmap_blob = xstrdup("HEAD:.mailmap"); if (!startup_info->have_repository || !is_bare_repository()) err |= read_mailmap_file(map, ".mailmap", startup_info->have_repository ? MAILMAP_NOFOLLOW : 0); if (startup_info->have_repository) - err |= read_mailmap_blob(map, git_mailmap_blob); - err |= read_mailmap_file(map, git_mailmap_file, 0); + err |= read_mailmap_blob(repo, map, mailmap_blob); + + err |= read_mailmap_file(map, mailmap_file, 0); + + free(mailmap_file); + free(mailmap_blob); + return err; } diff --git a/mailmap.h b/mailmap.h index 908365e1bffafc..6866cb6f1d6c6f 100644 --- a/mailmap.h +++ b/mailmap.h @@ -1,19 +1,18 @@ #ifndef MAILMAP_H #define MAILMAP_H +struct repository; struct string_list; -extern char *git_mailmap_file; -extern char *git_mailmap_blob; - /* Flags for read_mailmap_file() */ #define MAILMAP_NOFOLLOW (1<<0) int read_mailmap_file(struct string_list *map, const char *filename, unsigned flags); -int read_mailmap_blob(struct string_list *map, const char *name); +int read_mailmap_blob(struct repository *repo, struct string_list *map, + const char *name); -int read_mailmap(struct string_list *map); +int read_mailmap(struct repository *repo, struct string_list *map); void clear_mailmap(struct string_list *map); int map_user(struct string_list *map, diff --git a/merge-ort.c b/merge-ort.c index 0a59d1e596200e..00923ce3cd749b 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -53,6 +53,14 @@ #include "unpack-trees.h" #include "xdiff-interface.h" +/* + * We technically need USE_THE_REPOSITORY_VARIABLE above for DEFAULT_ABBREV, + * but do not want more uses of the_repository. Prevent them. + * + * opt->repo is available; use it instead. + */ +#define the_repository DO_NOT_USE_THE_REPOSITORY + /* * We have many arrays of size 3. Whenever we have such an array, the * indices refer to one of the sides of the three-way merge. This is so @@ -1732,9 +1740,9 @@ static int collect_merge_info(struct merge_options *opt, info.data = opt; info.show_all_errors = 1; - if (repo_parse_tree(the_repository, merge_base) < 0 || - repo_parse_tree(the_repository, side1) < 0 || - repo_parse_tree(the_repository, side2) < 0) + if (repo_parse_tree(opt->repo, merge_base) < 0 || + repo_parse_tree(opt->repo, side1) < 0 || + repo_parse_tree(opt->repo, side2) < 0) return -1; init_tree_desc(t + 0, &merge_base->object.oid, merge_base->buffer, merge_base->size); @@ -1857,7 +1865,7 @@ static int merge_submodule(struct merge_options *opt, BUG("submodule deleted on one side; this should be handled outside of merge_submodule()"); if ((sub_not_initialized = repo_submodule_init(&subrepo, - opt->repo, path, null_oid(the_hash_algo)))) { + opt->repo, path, null_oid(opt->repo->hash_algo)))) { path_msg(opt, CONFLICT_SUBMODULE_NOT_INITIALIZED, 0, path, NULL, NULL, NULL, _("Failed to merge submodule %s (not checked out)"), @@ -2136,9 +2144,9 @@ static int merge_3way(struct merge_options *opt, name2 = mkpathdup("%s:%s", opt->branch2, pathnames[2]); } - read_mmblob(&orig, the_repository->objects, o); - read_mmblob(&src1, the_repository->objects, a); - read_mmblob(&src2, the_repository->objects, b); + read_mmblob(&orig, opt->repo->objects, o); + read_mmblob(&src1, opt->repo->objects, a); + read_mmblob(&src2, opt->repo->objects, b); merge_status = ll_merge(result_buf, path, &orig, base, &src1, name1, &src2, name2, @@ -2240,7 +2248,7 @@ static int handle_content_merge(struct merge_options *opt, two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode)); merge_status = merge_3way(opt, path, - two_way ? null_oid(the_hash_algo) : &o->oid, + two_way ? null_oid(opt->repo->hash_algo) : &o->oid, &a->oid, &b->oid, pathnames, extra_marker_size, &result_buf); @@ -2254,7 +2262,7 @@ static int handle_content_merge(struct merge_options *opt, } if (!ret && record_object && - odb_write_object(the_repository->objects, result_buf.ptr, result_buf.size, + odb_write_object(opt->repo->objects, result_buf.ptr, result_buf.size, OBJ_BLOB, &result->oid)) { path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0, pathnames[0], pathnames[1], pathnames[2], NULL, @@ -2272,7 +2280,7 @@ static int handle_content_merge(struct merge_options *opt, } else if (S_ISGITLINK(a->mode)) { int two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode)); clean = merge_submodule(opt, pathnames[0], - two_way ? null_oid(the_hash_algo) : &o->oid, + two_way ? null_oid(opt->repo->hash_algo) : &o->oid, &a->oid, &b->oid, &result->oid); if (clean < 0) return -1; @@ -2786,7 +2794,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt, assert(!new_ci->match_mask); new_ci->dirmask = 0; new_ci->stages[1].mode = 0; - oidcpy(&new_ci->stages[1].oid, null_oid(the_hash_algo)); + oidcpy(&new_ci->stages[1].oid, null_oid(opt->repo->hash_algo)); /* * Now that we have the file information in new_ci, make sure @@ -2799,7 +2807,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt, continue; /* zero out any entries related to files */ ci->stages[i].mode = 0; - oidcpy(&ci->stages[i].oid, null_oid(the_hash_algo)); + oidcpy(&ci->stages[i].oid, null_oid(opt->repo->hash_algo)); } /* Now we want to focus on new_ci, so reassign ci to it. */ @@ -3214,7 +3222,7 @@ static int process_renames(struct merge_options *opt, if (type_changed) { /* rename vs. typechange */ /* Mark the original as resolved by removal */ - memcpy(&oldinfo->stages[0].oid, null_oid(the_hash_algo), + memcpy(&oldinfo->stages[0].oid, null_oid(opt->repo->hash_algo), sizeof(oldinfo->stages[0].oid)); oldinfo->stages[0].mode = 0; oldinfo->filemask &= 0x06; @@ -3713,7 +3721,7 @@ static int read_oid_strbuf(struct merge_options *opt, void *buf; enum object_type type; unsigned long size; - buf = odb_read_object(the_repository->objects, oid, &type, &size); + buf = odb_read_object(opt->repo->objects, oid, &type, &size); if (!buf) { path_msg(opt, ERROR_OBJECT_READ_FAILED, 0, path, NULL, NULL, NULL, @@ -3822,15 +3830,16 @@ static int tree_entry_order(const void *a_, const void *b_) b->string, strlen(b->string), bmi->result.mode); } -static int write_tree(struct object_id *result_oid, +static int write_tree(struct repository *repo, + struct object_id *result_oid, struct string_list *versions, - unsigned int offset, - size_t hash_size) + unsigned int offset) { size_t maxlen = 0, extra; unsigned int nr; struct strbuf buf = STRBUF_INIT; int i, ret = 0; + size_t hash_size = repo->hash_algo->rawsz; assert(offset <= versions->nr); nr = versions->nr - offset; @@ -3856,7 +3865,7 @@ static int write_tree(struct object_id *result_oid, } /* Write this object file out, and record in result_oid */ - if (odb_write_object(the_repository->objects, buf.buf, + if (odb_write_object(repo->objects, buf.buf, buf.len, OBJ_TREE, result_oid)) ret = -1; strbuf_release(&buf); @@ -4026,8 +4035,8 @@ static int write_completed_directory(struct merge_options *opt, dir_info->is_null = 0; dir_info->result.mode = S_IFDIR; if (record_tree && - write_tree(&dir_info->result.oid, &info->versions, offset, - opt->repo->hash_algo->rawsz) < 0) + write_tree(opt->repo, &dir_info->result.oid, &info->versions, + offset) < 0) ret = -1; } @@ -4101,7 +4110,7 @@ static int process_entry(struct merge_options *opt, if (ci->filemask & (1 << i)) continue; ci->stages[i].mode = 0; - oidcpy(&ci->stages[i].oid, null_oid(the_hash_algo)); + oidcpy(&ci->stages[i].oid, null_oid(opt->repo->hash_algo)); } } else if (ci->df_conflict && ci->merged.result.mode != 0) { /* @@ -4148,7 +4157,7 @@ static int process_entry(struct merge_options *opt, continue; /* zero out any entries related to directories */ new_ci->stages[i].mode = 0; - oidcpy(&new_ci->stages[i].oid, null_oid(the_hash_algo)); + oidcpy(&new_ci->stages[i].oid, null_oid(opt->repo->hash_algo)); } /* @@ -4270,11 +4279,11 @@ static int process_entry(struct merge_options *opt, new_ci->merged.result.mode = ci->stages[2].mode; oidcpy(&new_ci->merged.result.oid, &ci->stages[2].oid); new_ci->stages[1].mode = 0; - oidcpy(&new_ci->stages[1].oid, null_oid(the_hash_algo)); + oidcpy(&new_ci->stages[1].oid, null_oid(opt->repo->hash_algo)); new_ci->filemask = 5; if ((S_IFMT & b_mode) != (S_IFMT & o_mode)) { new_ci->stages[0].mode = 0; - oidcpy(&new_ci->stages[0].oid, null_oid(the_hash_algo)); + oidcpy(&new_ci->stages[0].oid, null_oid(opt->repo->hash_algo)); new_ci->filemask = 4; } @@ -4282,11 +4291,11 @@ static int process_entry(struct merge_options *opt, ci->merged.result.mode = ci->stages[1].mode; oidcpy(&ci->merged.result.oid, &ci->stages[1].oid); ci->stages[2].mode = 0; - oidcpy(&ci->stages[2].oid, null_oid(the_hash_algo)); + oidcpy(&ci->stages[2].oid, null_oid(opt->repo->hash_algo)); ci->filemask = 3; if ((S_IFMT & a_mode) != (S_IFMT & o_mode)) { ci->stages[0].mode = 0; - oidcpy(&ci->stages[0].oid, null_oid(the_hash_algo)); + oidcpy(&ci->stages[0].oid, null_oid(opt->repo->hash_algo)); ci->filemask = 2; } @@ -4414,7 +4423,7 @@ static int process_entry(struct merge_options *opt, /* Deleted on both sides */ ci->merged.is_null = 1; ci->merged.result.mode = 0; - oidcpy(&ci->merged.result.oid, null_oid(the_hash_algo)); + oidcpy(&ci->merged.result.oid, null_oid(opt->repo->hash_algo)); assert(!ci->df_conflict); ci->merged.clean = !ci->path_conflict; } @@ -4438,7 +4447,7 @@ static void prefetch_for_content_merges(struct merge_options *opt, struct string_list_item *e; struct oid_array to_fetch = OID_ARRAY_INIT; - if (opt->repo != the_repository || !repo_has_promisor_remote(the_repository)) + if (!repo_has_promisor_remote(opt->repo)) return; for (e = &plist->items[plist->nr-1]; e >= plist->items; --e) { @@ -4573,8 +4582,7 @@ static int process_entries(struct merge_options *opt, BUG("dir_metadata accounting completely off; shouldn't happen"); } if (record_tree && - write_tree(result_oid, &dir_metadata.versions, 0, - opt->repo->hash_algo->rawsz) < 0) + write_tree(opt->repo, result_oid, &dir_metadata.versions, 0) < 0) ret = -1; cleanup: string_list_clear(&plist, 0); @@ -4619,10 +4627,10 @@ static int checkout(struct merge_options *opt, unpack_opts.verbose_update = (opt->verbosity > 2); unpack_opts.fn = twoway_merge; unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */ - if (repo_parse_tree(the_repository, prev) < 0) + if (repo_parse_tree(opt->repo, prev) < 0) return -1; init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size); - if (repo_parse_tree(the_repository, next) < 0) + if (repo_parse_tree(opt->repo, next) < 0) return -1; init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size); @@ -5280,7 +5288,7 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt, if (result->clean >= 0) { if (!opt->mergeability_only) { - result->tree = repo_parse_tree_indirect(the_repository, + result->tree = repo_parse_tree_indirect(opt->repo, &working_tree_oid); if (!result->tree) die(_("unable to read tree (%s)"), @@ -5309,7 +5317,7 @@ static void merge_ort_internal(struct merge_options *opt, struct strbuf merge_base_abbrev = STRBUF_INIT; if (!merge_bases) { - if (repo_get_merge_bases(the_repository, h1, h2, + if (repo_get_merge_bases(opt->repo, h1, h2, &merge_bases) < 0) { result->clean = -1; goto out; @@ -5440,20 +5448,20 @@ static void merge_recursive_config(struct merge_options *opt, int ui) { char *value = NULL; int renormalize = 0; - repo_config_get_int(the_repository, "merge.verbosity", &opt->verbosity); - repo_config_get_int(the_repository, "diff.renamelimit", &opt->rename_limit); - repo_config_get_int(the_repository, "merge.renamelimit", &opt->rename_limit); - repo_config_get_bool(the_repository, "merge.renormalize", &renormalize); + repo_config_get_int(opt->repo, "merge.verbosity", &opt->verbosity); + repo_config_get_int(opt->repo, "diff.renamelimit", &opt->rename_limit); + repo_config_get_int(opt->repo, "merge.renamelimit", &opt->rename_limit); + repo_config_get_bool(opt->repo, "merge.renormalize", &renormalize); opt->renormalize = renormalize; - if (!repo_config_get_string(the_repository, "diff.renames", &value)) { + if (!repo_config_get_string(opt->repo, "diff.renames", &value)) { opt->detect_renames = git_config_rename("diff.renames", value); free(value); } - if (!repo_config_get_string(the_repository, "merge.renames", &value)) { + if (!repo_config_get_string(opt->repo, "merge.renames", &value)) { opt->detect_renames = git_config_rename("merge.renames", value); free(value); } - if (!repo_config_get_string(the_repository, "merge.directoryrenames", &value)) { + if (!repo_config_get_string(opt->repo, "merge.directoryrenames", &value)) { int boolval = git_parse_maybe_bool(value); if (0 <= boolval) { opt->detect_directory_renames = boolval ? @@ -5466,7 +5474,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui) free(value); } if (ui) { - if (!repo_config_get_string(the_repository, "diff.algorithm", &value)) { + if (!repo_config_get_string(opt->repo, "diff.algorithm", &value)) { long diff_algorithm = parse_algorithm_value(value); if (diff_algorithm < 0) die(_("unknown value for config '%s': %s"), "diff.algorithm", value); @@ -5474,7 +5482,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui) free(value); } } - repo_config(the_repository, git_xmerge_config, NULL); + repo_config(opt->repo, git_xmerge_config, NULL); } static void init_merge_options(struct merge_options *opt, diff --git a/meson.build b/meson.build index 6f155beafa4922..4b536e012481ca 100644 --- a/meson.build +++ b/meson.build @@ -722,11 +722,14 @@ endif builtin_sources += custom_target( output: 'config-list.h', + depfile: 'config-list.h.d', + depend_files: [ 'generate-configlist.sh' ], command: [ shell, - meson.current_source_dir() + '/generate-configlist.sh', + meson.current_source_dir() / 'generate-configlist.sh', meson.current_source_dir(), '@OUTPUT@', + '@DEPFILE@', ], env: script_environment, ) diff --git a/path.c b/path.c index 1772fcb21c15cd..119e5f23b67d51 100644 --- a/path.c +++ b/path.c @@ -486,17 +486,16 @@ const char *mkpath(const char *fmt, ...) return cleanup_path(pathname->buf); } -const char *worktree_git_path(struct repository *r, - const struct worktree *wt, const char *fmt, ...) +const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...) { struct strbuf *pathname = get_pathname(); va_list args; - if (wt && wt->repo != r) - BUG("worktree not connected to expected repository"); + if (!wt) + BUG("%s() called with NULL worktree", __func__); va_start(args, fmt); - repo_git_pathv(r, wt, pathname, fmt, args); + repo_git_pathv(wt->repo, wt, pathname, fmt, args); va_end(args); return pathname->buf; } diff --git a/path.h b/path.h index 0ec95a0b079c90..cbcad254a0a0b5 100644 --- a/path.h +++ b/path.h @@ -66,13 +66,11 @@ const char *repo_git_path_replace(struct repository *repo, /* * Similar to repo_git_path() but can produce paths for a specified - * worktree instead of current one. When no worktree is given, then the path is - * computed relative to main worktree of the given repository. + * worktree instead of current one. */ -const char *worktree_git_path(struct repository *r, - const struct worktree *wt, +const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...) - __attribute__((format (printf, 3, 4))); + __attribute__((format (printf, 2, 3))); /* * The `repo_worktree_path` family of functions will construct a path into a diff --git a/pretty.c b/pretty.c index e0646bbc5d49cc..ebf4da4834075f 100644 --- a/pretty.c +++ b/pretty.c @@ -781,7 +781,7 @@ static int mailmap_name(const char **email, size_t *email_len, static struct string_list *mail_map; if (!mail_map) { CALLOC_ARRAY(mail_map, 1); - read_mailmap(mail_map); + read_mailmap(the_repository, mail_map); } return mail_map->nr && map_user(mail_map, email, email_len, name, name_len); } diff --git a/ref-filter.c b/ref-filter.c index 6bbb6fac18a333..291d94328c6160 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1753,7 +1753,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void (starts_with(name + wholen, "email") && (atom->u.email_option.option & EO_MAILMAP))) { if (!mailmap.items) - read_mailmap(&mailmap); + read_mailmap(the_repository, &mailmap); strbuf_addstr(&mailmap_buf, buf); apply_mailmap_to_header(&mailmap_buf, headers, &mailmap); wholine = find_wholine(who, wholen, mailmap_buf.buf); diff --git a/refs.c b/refs.c index 600913b99f3f88..0ad1da990dc0fb 100644 --- a/refs.c +++ b/refs.c @@ -5,6 +5,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "git-compat-util.h" +#include "abspath.h" #include "advice.h" #include "config.h" #include "environment.h" @@ -2166,15 +2167,93 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs, return NULL; } +void refs_create_refdir_stubs(struct repository *repo, const char *refdir, + const char *refs_heads_content) +{ + struct strbuf path = STRBUF_INIT; + + strbuf_addf(&path, "%s/HEAD", refdir); + write_file(path.buf, "ref: refs/heads/.invalid"); + adjust_shared_perm(repo, path.buf); + + strbuf_reset(&path); + strbuf_addf(&path, "%s/refs", refdir); + safe_create_dir(repo, path.buf, 1); + + if (refs_heads_content) { + strbuf_reset(&path); + strbuf_addf(&path, "%s/refs/heads", refdir); + write_file(path.buf, "%s", refs_heads_content); + adjust_shared_perm(repo, path.buf); + } + + strbuf_release(&path); +} + /* backend functions */ int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err) { - return refs->be->create_on_disk(refs, flags, err); + int ret = refs->be->create_on_disk(refs, flags, err); + + if (!ret) { + /* Creation of stubs for linked worktrees are handled in the worktree code. */ + if (!(flags & REF_STORE_CREATE_ON_DISK_IS_WORKTREE) && refs->repo->ref_storage_payload) { + refs_create_refdir_stubs(refs->repo, refs->repo->gitdir, + "repository uses alternate refs storage"); + } else if (ref_storage_format_by_name(refs->be->name) != REF_STORAGE_FORMAT_FILES) { + struct strbuf msg = STRBUF_INIT; + strbuf_addf(&msg, "this repository uses the %s format", refs->be->name); + refs_create_refdir_stubs(refs->repo, refs->gitdir, msg.buf); + strbuf_release(&msg); + } + } + + return ret; } int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err) { - return refs->be->remove_on_disk(refs, err); + int ret = refs->be->remove_on_disk(refs, err); + + if (!ret) { + enum ref_storage_format format = ref_storage_format_by_name(refs->be->name); + struct strbuf sb = STRBUF_INIT; + + /* Backends apart from the files backend create stubs. */ + if (format == REF_STORAGE_FORMAT_FILES) + return ret; + + /* Alternate refs backend require stubs in the gitdir. */ + if (refs->repo->ref_storage_payload) + return ret; + + strbuf_addf(&sb, "%s/HEAD", refs->gitdir); + if (unlink(sb.buf) < 0) { + strbuf_addf(err, "could not delete stub HEAD: %s", + strerror(errno)); + ret = -1; + } + strbuf_reset(&sb); + + strbuf_addf(&sb, "%s/refs/heads", refs->gitdir); + if (unlink(sb.buf) < 0) { + strbuf_addf(err, "could not delete stub heads: %s", + strerror(errno)); + ret = -1; + } + strbuf_reset(&sb); + + strbuf_addf(&sb, "%s/refs", refs->gitdir); + if (rmdir(sb.buf) < 0) { + strbuf_addf(err, "could not delete refs directory: %s", + strerror(errno)); + ret = -1; + } + + strbuf_release(&sb); + } + + return ret; } int repo_resolve_gitlink_ref(struct repository *r, @@ -2227,7 +2306,11 @@ static struct ref_store *ref_store_init(struct repository *repo, if (!be) BUG("reference backend is unknown"); - refs = be->init(repo, gitdir, flags); + /* + * TODO Send in a 'struct worktree' instead of a 'gitdir', and + * allow the backend to handle how it wants to deal with worktrees. + */ + refs = be->init(repo, repo->ref_storage_payload, gitdir, flags); return refs; } @@ -3410,3 +3493,40 @@ const char *ref_transaction_error_msg(enum ref_transaction_error err) return "unknown failure"; } } + +void refs_compute_filesystem_location(const char *gitdir, const char *payload, + bool *is_worktree, struct strbuf *refdir, + struct strbuf *ref_common_dir) +{ + struct strbuf sb = STRBUF_INIT; + + *is_worktree = get_common_dir_noenv(ref_common_dir, gitdir); + + if (!payload) { + /* + * We can use the 'gitdir' as the 'refdir' without appending the + * worktree path, as the 'gitdir' here is already the worktree + * path and is different from 'commondir' denoted by 'ref_common_dir'. + */ + strbuf_addstr(refdir, gitdir); + return; + } + + if (!is_absolute_path(payload)) { + strbuf_addf(&sb, "%s/%s", ref_common_dir->buf, payload); + strbuf_realpath(ref_common_dir, sb.buf, 1); + } else { + strbuf_realpath(ref_common_dir, payload, 1); + } + + strbuf_addbuf(refdir, ref_common_dir); + + if (*is_worktree) { + const char *wt_id = strrchr(gitdir, '/'); + if (!wt_id) + BUG("worktree path does not contain slash"); + strbuf_addf(refdir, "/worktrees/%s", wt_id + 1); + } + + strbuf_release(&sb); +} diff --git a/refs.h b/refs.h index f16b1b697b2bf8..f3a1d604ad89a1 100644 --- a/refs.h +++ b/refs.h @@ -1428,4 +1428,17 @@ void ref_iterator_free(struct ref_iterator *ref_iterator); int do_for_each_ref_iterator(struct ref_iterator *iter, each_ref_fn fn, void *cb_data); +/* + * Git only recognizes a directory as a repository if it contains: + * - HEAD file + * - refs/ folder + * While it is necessary within the files backend, newer backends may not + * follow the same structure. To go around this, we create stubs as necessary. + * + * If provided with a 'refs_heads_content', we create the 'refs/heads/head' file + * with the provided message. + */ +void refs_create_refdir_stubs(struct repository *repo, const char *refdir, + const char *refs_heads_content); + #endif /* REFS_H */ diff --git a/refs/files-backend.c b/refs/files-backend.c index b1b13b41f6c7d1..d2c4be1653d255 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -106,19 +106,24 @@ static void clear_loose_ref_cache(struct files_ref_store *refs) * set of caches. */ static struct ref_store *files_ref_store_init(struct repository *repo, + const char *payload, const char *gitdir, unsigned int flags) { struct files_ref_store *refs = xcalloc(1, sizeof(*refs)); struct ref_store *ref_store = (struct ref_store *)refs; - struct strbuf sb = STRBUF_INIT; + struct strbuf ref_common_dir = STRBUF_INIT; + struct strbuf refdir = STRBUF_INIT; + bool is_worktree; + + refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir, + &ref_common_dir); - base_ref_store_init(ref_store, repo, gitdir, &refs_be_files); + base_ref_store_init(ref_store, repo, refdir.buf, &refs_be_files); refs->store_flags = flags; - get_common_dir_noenv(&sb, gitdir); - refs->gitcommondir = strbuf_detach(&sb, NULL); + refs->gitcommondir = strbuf_detach(&ref_common_dir, NULL); refs->packed_ref_store = - packed_ref_store_init(repo, refs->gitcommondir, flags); + packed_ref_store_init(repo, NULL, refs->gitcommondir, flags); refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo); repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs); @@ -126,6 +131,8 @@ static struct ref_store *files_ref_store_init(struct repository *repo, chdir_notify_reparent("files-backend $GIT_COMMONDIR", &refs->gitcommondir); + strbuf_release(&refdir); + return ref_store; } @@ -3699,7 +3706,11 @@ static int files_ref_store_remove_on_disk(struct ref_store *ref_store, if (for_each_root_ref(refs, remove_one_root_ref, &data) < 0) ret = -1; - if (ref_store_remove_on_disk(refs->packed_ref_store, err) < 0) + /* + * Directly access the cleanup functions for packed-refs as the generic function + * would try to clear stubs which isn't required for the files backend. + */ + if (refs->packed_ref_store->be->remove_on_disk(refs->packed_ref_store, err) < 0) ret = -1; strbuf_release(&sb); diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 59b3ecb9d64716..8872a7fe18841b 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -211,7 +211,12 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot) return snapshot->refs->base.repo->hash_algo->hexsz; } +/* + * Since packed-refs is only stored in the common dir, don't parse the + * payload and rely on the files-backend to set 'gitdir' correctly. + */ struct ref_store *packed_ref_store_init(struct repository *repo, + const char *payload UNUSED, const char *gitdir, unsigned int store_flags) { diff --git a/refs/packed-backend.h b/refs/packed-backend.h index 9481d5e7c2e2f5..2c2377a35653ec 100644 --- a/refs/packed-backend.h +++ b/refs/packed-backend.h @@ -14,6 +14,7 @@ struct ref_transaction; */ struct ref_store *packed_ref_store_init(struct repository *repo, + const char *payload, const char *gitdir, unsigned int store_flags); diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 191a25683fdf31..d79e35fd269a6c 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -391,6 +391,7 @@ struct ref_store; * the ref_store and to record the ref_store for later lookup. */ typedef struct ref_store *ref_store_init_fn(struct repository *repo, + const char *payload, const char *gitdir, unsigned int flags); /* @@ -668,4 +669,17 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs unsigned int initial_transaction, struct strbuf *err); +/* + * Given a gitdir and the reference storage payload provided, retrieve the + * 'refdir' and 'ref_common_dir'. The former is where references should be + * stored for the current worktree, the latter is the common reference + * directory if working with a linked worktree. If working with the main + * worktree, both values will be the same. + * + * This is used by backends that store references in the repository directly. + */ +void refs_compute_filesystem_location(const char *gitdir, const char *payload, + bool *is_worktree, struct strbuf *refdir, + struct strbuf *ref_common_dir); + #endif /* REFS_REFS_INTERNAL_H */ diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 5611808ad75a8b..57b5d489054fe3 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -372,18 +372,24 @@ static int reftable_be_fsync(int fd) } static struct ref_store *reftable_be_init(struct repository *repo, + const char *payload, const char *gitdir, unsigned int store_flags) { struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs)); + struct strbuf ref_common_dir = STRBUF_INIT; + struct strbuf refdir = STRBUF_INIT; struct strbuf path = STRBUF_INIT; - int is_worktree; + bool is_worktree; mode_t mask; mask = umask(0); umask(mask); - base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable); + refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir, + &ref_common_dir); + + base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable); strmap_init(&refs->worktree_backends); refs->store_flags = store_flags; refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo); @@ -419,14 +425,11 @@ static struct ref_store *reftable_be_init(struct repository *repo, /* * Set up the main reftable stack that is hosted in GIT_COMMON_DIR. * This stack contains both the shared and the main worktree refs. - * - * Note that we don't try to resolve the path in case we have a - * worktree because `get_common_dir_noenv()` already does it for us. */ - is_worktree = get_common_dir_noenv(&path, gitdir); + strbuf_addbuf(&path, &ref_common_dir); if (!is_worktree) { strbuf_reset(&path); - strbuf_realpath(&path, gitdir, 0); + strbuf_realpath(&path, ref_common_dir.buf, 0); } strbuf_addstr(&path, "/reftable"); refs->err = reftable_backend_init(&refs->main_backend, path.buf, @@ -443,10 +446,9 @@ static struct ref_store *reftable_be_init(struct repository *repo, * do it efficiently. */ if (is_worktree) { - strbuf_reset(&path); - strbuf_addf(&path, "%s/reftable", gitdir); + strbuf_addstr(&refdir, "/reftable"); - refs->err = reftable_backend_init(&refs->worktree_backend, path.buf, + refs->err = reftable_backend_init(&refs->worktree_backend, refdir.buf, &refs->write_options); if (refs->err) goto done; @@ -456,6 +458,8 @@ static struct ref_store *reftable_be_init(struct repository *repo, done: assert(refs->err != REFTABLE_API_ERROR); + strbuf_release(&ref_common_dir); + strbuf_release(&refdir); strbuf_release(&path); return &refs->base; } @@ -491,19 +495,6 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store, safe_create_dir(the_repository, sb.buf, 1); strbuf_reset(&sb); - strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir); - write_file(sb.buf, "ref: refs/heads/.invalid"); - adjust_shared_perm(the_repository, sb.buf); - strbuf_reset(&sb); - - strbuf_addf(&sb, "%s/refs", refs->base.gitdir); - safe_create_dir(the_repository, sb.buf, 1); - strbuf_reset(&sb); - - strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir); - write_file(sb.buf, "this repository uses the reftable format"); - adjust_shared_perm(the_repository, sb.buf); - strbuf_release(&sb); return 0; } @@ -529,30 +520,6 @@ static int reftable_be_remove_on_disk(struct ref_store *ref_store, strerror(errno)); ret = -1; } - strbuf_reset(&sb); - - strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir); - if (unlink(sb.buf) < 0) { - strbuf_addf(err, "could not delete stub HEAD: %s", - strerror(errno)); - ret = -1; - } - strbuf_reset(&sb); - - strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir); - if (unlink(sb.buf) < 0) { - strbuf_addf(err, "could not delete stub heads: %s", - strerror(errno)); - ret = -1; - } - strbuf_reset(&sb); - - strbuf_addf(&sb, "%s/refs", refs->base.gitdir); - if (rmdir(sb.buf) < 0) { - strbuf_addf(err, "could not delete refs directory: %s", - strerror(errno)); - ret = -1; - } strbuf_release(&sb); return ret; diff --git a/replay.c b/replay.c index f97d652f338f1d..a63f6714c4c2ec 100644 --- a/replay.c +++ b/replay.c @@ -11,6 +11,12 @@ #include "strmap.h" #include "tree.h" +/* + * We technically need USE_THE_REPOSITORY_VARIABLE for DEFAULT_ABBREV, but + * do not want to use the_repository. + */ +#define the_repository DO_NOT_USE_THE_REPOSITORY + static const char *short_commit_name(struct repository *repo, struct commit *commit) { diff --git a/repository.c b/repository.c index 46a7c999302a4c..44e77cd05a3287 100644 --- a/repository.c +++ b/repository.c @@ -193,9 +193,12 @@ void repo_set_compat_hash_algo(struct repository *repo, int algo) } void repo_set_ref_storage_format(struct repository *repo, - enum ref_storage_format format) + enum ref_storage_format format, + const char *payload) { repo->ref_storage_format = format; + free(repo->ref_storage_payload); + repo->ref_storage_payload = xstrdup_or_null(payload); } /* @@ -277,7 +280,8 @@ int repo_init(struct repository *repo, repo_set_hash_algo(repo, format.hash_algo); repo_set_compat_hash_algo(repo, format.compat_hash_algo); - repo_set_ref_storage_format(repo, format.ref_storage_format); + repo_set_ref_storage_format(repo, format.ref_storage_format, + format.ref_storage_payload); repo->repository_format_worktree_config = format.worktree_config; repo->repository_format_relative_worktrees = format.relative_worktrees; repo->repository_format_precious_objects = format.precious_objects; @@ -370,6 +374,7 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->index_file); FREE_AND_NULL(repo->worktree); FREE_AND_NULL(repo->submodule_prefix); + FREE_AND_NULL(repo->ref_storage_payload); odb_free(repo->objects); repo->objects = NULL; diff --git a/repository.h b/repository.h index 7141237f97d87d..72a5e9d410c264 100644 --- a/repository.h +++ b/repository.h @@ -150,6 +150,11 @@ struct repository { /* Repository's reference storage format, as serialized on disk. */ enum ref_storage_format ref_storage_format; + /* + * Reference storage information as needed for the backend. This contains + * only the payload from the reference URI without the schema. + */ + char *ref_storage_payload; /* A unique-id for tracing purposes. */ int trace2_repo_id; @@ -205,7 +210,8 @@ void repo_set_worktree(struct repository *repo, const char *path); void repo_set_hash_algo(struct repository *repo, int algo); void repo_set_compat_hash_algo(struct repository *repo, int compat_algo); void repo_set_ref_storage_format(struct repository *repo, - enum ref_storage_format format); + enum ref_storage_format format, + const char *payload); void initialize_repository(struct repository *repo); RESULT_MUST_BE_USED int repo_init(struct repository *r, const char *gitdir, const char *worktree); diff --git a/revision.c b/revision.c index ddddbe4993d7d4..402eb1b0298214 100644 --- a/revision.c +++ b/revision.c @@ -1848,7 +1848,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags) wt_gitdir = get_worktree_git_dir(wt); if (read_index_from(&istate, - worktree_git_path(the_repository, wt, "index"), + worktree_git_path(wt, "index"), wt_gitdir) > 0) do_add_index_objects_to_pending(revs, &istate, flags); diff --git a/run-command.c b/run-command.c index e3e02475ccec50..438a290d30744d 100644 --- a/run-command.c +++ b/run-command.c @@ -1828,7 +1828,7 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint) */ if (repo_config_get_bool(the_repository, "maintenance.autodetach", &auto_detach) && repo_config_get_bool(the_repository, "gc.autodetach", &auto_detach)) - auto_detach = 1; + auto_detach = git_env_bool("GIT_TEST_MAINT_AUTO_DETACH", true); maint->git_cmd = 1; maint->close_object_store = 1; diff --git a/setup.c b/setup.c index c8336eb20e26fd..393b970ae4f96d 100644 --- a/setup.c +++ b/setup.c @@ -631,6 +631,21 @@ static enum extension_result handle_extension_v0(const char *var, return EXTENSION_UNKNOWN; } +static void parse_reference_uri(const char *value, char **format, + char **payload) +{ + const char *schema_end; + + schema_end = strstr(value, "://"); + if (!schema_end) { + *format = xstrdup(value); + *payload = NULL; + } else { + *format = xstrndup(value, schema_end - value); + *payload = xstrdup_or_null(schema_end + 3); + } +} + /* * Record any new extensions in this function. */ @@ -673,10 +688,17 @@ static enum extension_result handle_extension(const char *var, return EXTENSION_OK; } else if (!strcmp(ext, "refstorage")) { unsigned int format; + char *format_str; if (!value) return config_error_nonbool(var); - format = ref_storage_format_by_name(value); + + parse_reference_uri(value, &format_str, + &data->ref_storage_payload); + + format = ref_storage_format_by_name(format_str); + free(format_str); + if (format == REF_STORAGE_FORMAT_UNKNOWN) return error(_("invalid value for '%s': '%s'"), "extensions.refstorage", value); @@ -852,6 +874,7 @@ void clear_repository_format(struct repository_format *format) string_list_clear(&format->v1_only_extensions, 0); free(format->work_tree); free(format->partial_clone); + free(format->ref_storage_payload); init_repository_format(format); } @@ -1817,6 +1840,7 @@ const char *setup_git_directory_gently(int *nongit_ok) static struct strbuf cwd = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT; const char *prefix = NULL; + const char *ref_backend_uri; struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; /* @@ -1944,7 +1968,8 @@ const char *setup_git_directory_gently(int *nongit_ok) repo_set_compat_hash_algo(the_repository, repo_fmt.compat_hash_algo); repo_set_ref_storage_format(the_repository, - repo_fmt.ref_storage_format); + repo_fmt.ref_storage_format, + repo_fmt.ref_storage_payload); the_repository->repository_format_worktree_config = repo_fmt.worktree_config; the_repository->repository_format_relative_worktrees = @@ -1975,6 +2000,25 @@ const char *setup_git_directory_gently(int *nongit_ok) setenv(GIT_PREFIX_ENVIRONMENT, "", 1); } + /* + * The env variable should override the repository config + * for 'extensions.refStorage'. + */ + ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); + if (ref_backend_uri) { + char *backend, *payload; + enum ref_storage_format format; + + parse_reference_uri(ref_backend_uri, &backend, &payload); + format = ref_storage_format_by_name(backend); + if (format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format: '%s'"), backend); + repo_set_ref_storage_format(the_repository, format, payload); + + free(backend); + free(payload); + } + setup_original_cwd(); strbuf_release(&dir); @@ -2046,7 +2090,8 @@ void check_repository_format(struct repository_format *fmt) repo_set_hash_algo(the_repository, fmt->hash_algo); repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo); repo_set_ref_storage_format(the_repository, - fmt->ref_storage_format); + fmt->ref_storage_format, + fmt->ref_storage_payload); the_repository->repository_format_worktree_config = fmt->worktree_config; the_repository->repository_format_submodule_path_cfg = @@ -2319,7 +2364,8 @@ void initialize_repository_version(int hash_algo, * the remote repository's format. */ if (hash_algo != GIT_HASH_SHA1_LEGACY || - ref_storage_format != REF_STORAGE_FORMAT_FILES) + ref_storage_format != REF_STORAGE_FORMAT_FILES || + the_repository->ref_storage_payload) target_version = GIT_REPO_VERSION_READ; if (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN) @@ -2328,11 +2374,20 @@ void initialize_repository_version(int hash_algo, else if (reinit) repo_config_set_gently(the_repository, "extensions.objectformat", NULL); - if (ref_storage_format != REF_STORAGE_FORMAT_FILES) + if (the_repository->ref_storage_payload) { + struct strbuf ref_uri = STRBUF_INIT; + + strbuf_addf(&ref_uri, "%s://%s", + ref_storage_format_to_name(ref_storage_format), + the_repository->ref_storage_payload); + repo_config_set(the_repository, "extensions.refstorage", ref_uri.buf); + strbuf_release(&ref_uri); + } else if (ref_storage_format != REF_STORAGE_FORMAT_FILES) { repo_config_set(the_repository, "extensions.refstorage", ref_storage_format_to_name(ref_storage_format)); - else if (reinit) + } else if (reinit) { repo_config_set_gently(the_repository, "extensions.refstorage", NULL); + } if (reinit) { struct strbuf config = STRBUF_INIT; @@ -2375,14 +2430,12 @@ static int is_reinit(void) return ret; } -void create_reference_database(enum ref_storage_format ref_storage_format, - const char *initial_branch, int quiet) +void create_reference_database(const char *initial_branch, int quiet) { struct strbuf err = STRBUF_INIT; char *to_free = NULL; int reinit = is_reinit(); - repo_set_ref_storage_format(the_repository, ref_storage_format); if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err)) die("failed to set up refs db: %s", err.buf); @@ -2616,6 +2669,7 @@ static void repository_format_configure(struct repository_format *repo_fmt, .ignore_repo = 1, .ignore_worktree = 1, }; + const char *ref_backend_uri; const char *env; config_with_options(read_default_format_config, &cfg, NULL, NULL, &opts); @@ -2661,7 +2715,26 @@ static void repository_format_configure(struct repository_format *repo_fmt, } else { repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT; } - repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format); + + + ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); + if (ref_backend_uri) { + char *backend, *payload; + enum ref_storage_format format; + + parse_reference_uri(ref_backend_uri, &backend, &payload); + format = ref_storage_format_by_name(backend); + if (format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format: '%s'"), backend); + + repo_fmt->ref_storage_format = format; + repo_fmt->ref_storage_payload = payload; + + free(backend); + } + + repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format, + repo_fmt->ref_storage_payload); } int init_db(const char *git_dir, const char *real_git_dir, @@ -2717,8 +2790,7 @@ int init_db(const char *git_dir, const char *real_git_dir, &repo_fmt, init_shared_repository); if (!(flags & INIT_DB_SKIP_REFDB)) - create_reference_database(repo_fmt.ref_storage_format, - initial_branch, flags & INIT_DB_QUIET); + create_reference_database(initial_branch, flags & INIT_DB_QUIET); create_object_directory(); if (repo_settings_get_shared_repository(the_repository)) { diff --git a/setup.h b/setup.h index 0738dec244adec..bcb16b0b4a6662 100644 --- a/setup.h +++ b/setup.h @@ -172,6 +172,7 @@ struct repository_format { int hash_algo; int compat_hash_algo; enum ref_storage_format ref_storage_format; + char *ref_storage_payload; int sparse_index; char *work_tree; struct string_list unknown_extensions; @@ -241,8 +242,7 @@ int init_db(const char *git_dir, const char *real_git_dir, void initialize_repository_version(int hash_algo, enum ref_storage_format ref_storage_format, int reinit); -void create_reference_database(enum ref_storage_format ref_storage_format, - const char *initial_branch, int quiet); +void create_reference_database(const char *initial_branch, int quiet); /* * NOTE NOTE NOTE!! diff --git a/shallow.c b/shallow.c index 0409b1354cb5f8..7a3dd567950dea 100644 --- a/shallow.c +++ b/shallow.c @@ -130,11 +130,24 @@ static void free_depth_in_slab(int **ptr) { FREE_AND_NULL(*ptr); } -struct commit_list *get_shallow_commits(struct object_array *heads, int depth, - int shallow_flag, int not_shallow_flag) +/* + * This is a common internal function that can either return a list of + * shallow commits or calculate the current maximum depth of a shallow + * repository, depending on the input parameters. + * + * Depth calculation is triggered by passing the `shallows` parameter. + * In this case, the computed depth is stored in `max_cur_depth` (if it is + * provided), and the function returns NULL. + * + * Otherwise, `max_cur_depth` remains unchanged and the function returns + * a list of shallow commits. + */ +static struct commit_list *get_shallows_or_depth(struct object_array *heads, + struct object_array *shallows, int *max_cur_depth, + int depth, int shallow_flag, int not_shallow_flag) { size_t i = 0; - int cur_depth = 0; + int cur_depth = 0, cur_depth_shallow = 0; struct commit_list *result = NULL; struct object_array stack = OBJECT_ARRAY_INIT; struct commit *commit = NULL; @@ -168,16 +181,30 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, } parse_commit_or_die(commit); cur_depth++; - if ((depth != INFINITE_DEPTH && cur_depth >= depth) || - (is_repository_shallow(the_repository) && !commit->parents && - (graft = lookup_commit_graft(the_repository, &commit->object.oid)) != NULL && - graft->nr_parent < 0)) { - commit_list_insert(commit, &result); - commit->object.flags |= shallow_flag; - commit = NULL; - continue; + if (shallows) { + for (size_t j = 0; j < shallows->nr; j++) + if (oideq(&commit->object.oid, &shallows->objects[j].item->oid)) + if (!cur_depth_shallow || cur_depth < cur_depth_shallow) + cur_depth_shallow = cur_depth; + + if ((is_repository_shallow(the_repository) && !commit->parents && + (graft = lookup_commit_graft(the_repository, &commit->object.oid)) != NULL && + graft->nr_parent < 0)) { + commit = NULL; + continue; + } + } else { + if ((depth != INFINITE_DEPTH && cur_depth >= depth) || + (is_repository_shallow(the_repository) && !commit->parents && + (graft = lookup_commit_graft(the_repository, &commit->object.oid)) != NULL && + graft->nr_parent < 0)) { + commit_list_insert(commit, &result); + commit->object.flags |= shallow_flag; + commit = NULL; + continue; + } + commit->object.flags |= not_shallow_flag; } - commit->object.flags |= not_shallow_flag; for (p = commit->parents, commit = NULL; p; p = p->next) { int **depth_slot = commit_depth_at(&depths, p->item); if (!*depth_slot) { @@ -198,10 +225,32 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, } } deep_clear_commit_depth(&depths, free_depth_in_slab); + object_array_clear(&stack); + if (shallows && max_cur_depth) + *max_cur_depth = cur_depth_shallow; return result; } +int get_shallows_depth(struct object_array *heads, struct object_array *shallows) +{ + int max_cur_depth = 0; + get_shallows_or_depth(heads, shallows, &max_cur_depth, 0, 0, 0); + return max_cur_depth; + +} + +struct commit_list *get_shallow_commits(struct object_array *heads, + struct object_array *shallows, int deepen_relative, + int depth, int shallow_flag, int not_shallow_flag) +{ + if (shallows && deepen_relative) { + depth += get_shallows_depth(heads, shallows); + } + return get_shallows_or_depth(heads, NULL, NULL, + depth, shallow_flag, not_shallow_flag); +} + static void show_commit(struct commit *commit, void *data) { commit_list_insert(commit, data); diff --git a/shallow.h b/shallow.h index 1c0787de1d66b9..e20ca4c21ba6d7 100644 --- a/shallow.h +++ b/shallow.h @@ -36,7 +36,9 @@ int commit_shallow_file(struct repository *r, struct shallow_lock *lk); /* rollback $GIT_DIR/shallow and reset stat-validity checks */ void rollback_shallow_file(struct repository *r, struct shallow_lock *lk); +int get_shallows_depth(struct object_array *heads, struct object_array *shallows); struct commit_list *get_shallow_commits(struct object_array *heads, + struct object_array *shallows, int deepen_relative, int depth, int shallow_flag, int not_shallow_flag); struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv, int shallow_flag, int not_shallow_flag); diff --git a/t/meson.build b/t/meson.build index 6d91470ebc1cf7..106c68df3d014d 100644 --- a/t/meson.build +++ b/t/meson.build @@ -213,6 +213,7 @@ integration_tests = [ 't1420-lost-found.sh', 't1421-reflog-write.sh', 't1422-show-ref-exists.sh', + 't1423-ref-backend.sh', 't1430-bad-ref-name.sh', 't1450-fsck.sh', 't1451-fsck-buffer.sh', @@ -242,7 +243,7 @@ integration_tests = [ 't1700-split-index.sh', 't1701-racy-split-index.sh', 't1800-hook.sh', - 't1900-repo.sh', + 't1900-repo-info.sh', 't1901-repo-structure.sh', 't2000-conflict-when-checking-files-out.sh', 't2002-checkout-cache-u.sh', diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh index 5a628bf7356445..26f017422d7253 100755 --- a/t/t0081-find-pack.sh +++ b/t/t0081-find-pack.sh @@ -68,6 +68,7 @@ test_expect_success 'add more packfiles' ' ' test_expect_success 'add more commits (as loose objects)' ' + test_config maintenance.auto false && test_commit six && test_commit seven && diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 9850fcd5b567a5..128971ee12fa6c 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2459,9 +2459,15 @@ done cat >.git/config <<-\EOF && [section] -foo = true +foo = True number = 10 big = 1M +path = ~/dir +red = red +blue = Blue +date = Fri Jun 4 15:46:55 2010 +missing=:(optional)no-such-path +exists=:(optional)expect EOF test_expect_success 'identical modern --type specifiers are allowed' ' @@ -2503,6 +2509,82 @@ test_expect_success 'unset type specifiers may be reset to conflicting ones' ' test_cmp_config 1048576 --type=bool --no-type --type=int section.big ' +test_expect_success 'list --type=int shows only canonicalizable int values' ' + cat >expect <<-EOF && + section.number=10 + section.big=1048576 + EOF + + git config ${mode_prefix}list --type=int >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success 'list --type=bool shows only canonicalizable bool values' ' + cat >expect <<-EOF && + section.foo=true + section.number=true + section.big=true + EOF + + git config ${mode_prefix}list --type=bool >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success 'list --type=bool-or-int shows only canonicalizable values' ' + cat >expect <<-EOF && + section.foo=true + section.number=10 + section.big=1048576 + EOF + + git config ${mode_prefix}list --type=bool-or-int >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success 'list --type=path shows only canonicalizable path values' ' + cat >expect <<-EOF && + section.foo=True + section.number=10 + section.big=1M + section.path=$HOME/dir + section.red=red + section.blue=Blue + section.date=Fri Jun 4 15:46:55 2010 + section.exists=expect + EOF + + git config ${mode_prefix}list --type=path >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success 'list --type=expiry-date shows only canonicalizable dates' ' + git config ${mode_prefix}list --type=expiry-date >actual 2>err && + + # section.number and section.big parse as relative dates that could + # have clock skew in their results. + test_grep section.big actual && + test_grep section.number actual && + test_grep "section.date=$(git config --type=expiry-date section.$key)" actual && + test_must_be_empty err +' + +test_expect_success 'list --type=color shows only canonicalizable color values' ' + cat >expect <<-EOF && + section.number=<> + section.red= + section.blue= + EOF + + git config ${mode_prefix}list --type=color >actual.raw 2>err && + test_decode_color actual && + test_cmp expect actual && + test_must_be_empty err +' + test_expect_success '--type rejects unknown specifiers' ' test_must_fail git config --type=nonsense section.foo 2>error && test_grep "unrecognized --type argument" error diff --git a/t/t1423-ref-backend.sh b/t/t1423-ref-backend.sh new file mode 100755 index 00000000000000..fd47d77e8e2904 --- /dev/null +++ b/t/t1423-ref-backend.sh @@ -0,0 +1,280 @@ +#!/bin/sh + +test_description='Test reference backend URIs' + +. ./test-lib.sh + +# Run a git command with the provided reference storage. Reset the backend +# post running the command. +# Usage: run_with_uri +# is the relative path to the repo to run the command in. +# is the original ref storage of the repo. +# is the new URI to be set for the ref storage. +# is the git subcommand to be run in the repository. +# if 'config', set the backend via the 'extensions.refStorage' config. +# if 'env', set the backend via the 'GIT_REFERENCE_BACKEND' env. +run_with_uri () { + repo=$1 && + backend=$2 && + uri=$3 && + cmd=$4 && + via=$5 && + + git -C "$repo" config set core.repositoryformatversion 1 && + if test "$via" = "env" + then + test_env GIT_REFERENCE_BACKEND="$uri" git -C "$repo" $cmd + elif test "$via" = "config" + then + git -C "$repo" config set extensions.refStorage "$uri" && + git -C "$repo" $cmd && + git -C "$repo" config set extensions.refStorage "$backend" + fi +} + +# Test a repository with a given reference storage by running and comparing +# 'git refs list' before and after setting the new reference backend. If +# err_msg is set, expect the command to fail and grep for the provided err_msg. +# Usage: run_with_uri +# is the relative path to the repo to run the command in. +# is the original ref storage of the repo. +# is the new URI to be set for the ref storage. +# if 'config', set the backend via the 'extensions.refStorage' config. +# if 'env', set the backend via the 'GIT_REFERENCE_BACKEND' env. +# (optional) if set, check if 'git-refs(1)' failed with the provided msg. +test_refs_backend () { + repo=$1 && + backend=$2 && + uri=$3 && + via=$4 && + err_msg=$5 && + + + if test -n "$err_msg"; + then + if test "$via" = "env" + then + test_env GIT_REFERENCE_BACKEND="$uri" test_must_fail git -C "$repo" refs list 2>err + elif test "$via" = "config" + then + git -C "$repo" config set extensions.refStorage "$uri" && + test_must_fail git -C "$repo" refs list 2>err && + test_grep "$err_msg" err + fi + else + git -C "$repo" refs list >expect && + run_with_uri "$repo" "$backend" "$uri" "refs list" "$via">actual && + test_cmp expect actual + fi +} + +# Verify that the expected files are present in the gitdir and the refsdir. +# Usage: verify_files_exist +# is the path for the gitdir. +# is the path for the refdir. +verify_files_exist () { + gitdir=$1 && + refdir=$2 && + + # verify that the stubs were added to the $GITDIR. + echo "repository uses alternate refs storage" >expect && + test_cmp expect $gitdir/refs/heads && + echo "ref: refs/heads/.invalid" >expect && + test_cmp expect $gitdir/HEAD + + # verify that backend specific files exist. + case "$GIT_DEFAULT_REF_FORMAT" in + files) + test_path_is_dir $refdir/refs/heads && + test_path_is_file $refdir/HEAD;; + reftable) + test_path_is_dir $refdir/reftable && + test_path_is_file $refdir/reftable/tables.list;; + *) + BUG "unhandled ref format $GIT_DEFAULT_REF_FORMAT";; + esac +} + +methods="config env" +for method in $methods +do + +test_expect_success "$method: URI is invalid" ' + test_when_finished "rm -rf repo" && + git init repo && + test_refs_backend repo files "reftable@/home/reftable" "$method" \ + "invalid value for ${SQ}extensions.refstorage${SQ}" +' + +test_expect_success "$method: URI ends with colon" ' + test_when_finished "rm -rf repo" && + git init repo && + test_refs_backend repo files "reftable:" "$method" \ + "invalid value for ${SQ}extensions.refstorage${SQ}" +' + +test_expect_success "$method: unknown reference backend" ' + test_when_finished "rm -rf repo" && + git init repo && + test_refs_backend repo files "db://.git" "$method" \ + "invalid value for ${SQ}extensions.refstorage${SQ}" +' + +ref_formats="files reftable" +for from_format in $ref_formats +do + +for to_format in $ref_formats +do + if test "$from_format" = "$to_format" + then + continue + fi + + + for dir in "$(pwd)/repo/.git" "." + do + + test_expect_success "$method: read from $to_format backend, $dir dir" ' + test_when_finished "rm -rf repo" && + git init --ref-format=$from_format repo && + ( + cd repo && + test_commit 1 && + test_commit 2 && + test_commit 3 && + + git refs migrate --dry-run --ref-format=$to_format >out && + BACKEND_PATH="$dir/$(sed "s/.* ${SQ}.git\/\(.*\)${SQ}/\1/" out)" && + test_refs_backend . $from_format "$to_format://$BACKEND_PATH" "$method" + ) + ' + + test_expect_success "$method: write to $to_format backend, $dir dir" ' + test_when_finished "rm -rf repo" && + git init --ref-format=$from_format repo && + ( + cd repo && + test_commit 1 && + test_commit 2 && + test_commit 3 && + + git refs migrate --dry-run --ref-format=$to_format >out && + BACKEND_PATH="$dir/$(sed "s/.* ${SQ}.git\/\(.*\)${SQ}/\1/" out)" && + + test_refs_backend . $from_format "$to_format://$BACKEND_PATH" "$method" && + + git refs list >expect && + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "tag -d 1" "$method" && + git refs list >actual && + test_cmp expect actual && + + git refs list | grep -v "refs/tags/1" >expect && + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "refs list" "$method" >actual && + test_cmp expect actual + ) + ' + + test_expect_success "$method: with worktree and $to_format backend, $dir dir" ' + test_when_finished "rm -rf repo wt" && + git init --ref-format=$from_format repo && + ( + cd repo && + test_commit 1 && + test_commit 2 && + test_commit 3 && + + git refs migrate --dry-run --ref-format=$to_format >out && + BACKEND_PATH="$dir/$(sed "s/.* ${SQ}.git\/\(.*\)${SQ}/\1/" out)" && + + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "worktree add ../wt 2" "$method" && + + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "for-each-ref --include-root-refs" "$method" >actual && + run_with_uri ../wt "$from_format" "$to_format://$BACKEND_PATH" \ + "for-each-ref --include-root-refs" "$method" >expect && + ! test_cmp expect actual && + + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "rev-parse 2" "$method" >actual && + run_with_uri ../wt "$from_format" "$to_format://$BACKEND_PATH" \ + "rev-parse HEAD" "$method" >expect && + test_cmp expect actual + ) + ' + done # closes dir + + test_expect_success "migrating repository to $to_format with alternate refs directory" ' + test_when_finished "rm -rf repo refdir" && + mkdir refdir && + GIT_REFERENCE_BACKEND="${from_format}://$(pwd)/refdir" git init repo && + ( + cd repo && + + test_commit 1 && + test_commit 2 && + test_commit 3 && + + git refs migrate --ref-format=$to_format && + git refs list >out && + test_grep "refs/tags/1" out && + test_grep "refs/tags/2" out && + test_grep "refs/tags/3" out + ) + ' + +done # closes to_format +done # closes from_format + +done # closes method + +test_expect_success 'initializing repository with alt ref directory' ' + test_when_finished "rm -rf repo refdir" && + mkdir refdir && + BACKEND="$(test_detect_ref_format)://$(pwd)/refdir" && + GIT_REFERENCE_BACKEND=$BACKEND git init repo && + verify_files_exist repo/.git refdir && + ( + cd repo && + + git config get extensions.refstorage >actual && + echo $BACKEND >expect && + test_cmp expect actual && + + test_commit 1 && + test_commit 2 && + test_commit 3 && + git refs list >out && + test_grep "refs/tags/1" out && + test_grep "refs/tags/2" out && + test_grep "refs/tags/3" out + ) +' + +test_expect_success 'cloning repository with alt ref directory' ' + test_when_finished "rm -rf source repo refdir" && + mkdir refdir && + + git init source && + test_commit -C source 1 && + test_commit -C source 2 && + test_commit -C source 3 && + + BACKEND="$(test_detect_ref_format)://$(pwd)/refdir" && + GIT_REFERENCE_BACKEND=$BACKEND git clone source repo && + + git -C repo config get extensions.refstorage >actual && + echo $BACKEND >expect && + test_cmp expect actual && + + verify_files_exist repo/.git refdir && + + git -C source for-each-ref refs/tags/ >expect && + git -C repo for-each-ref refs/tags/ >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t1900-repo.sh b/t/t1900-repo-info.sh similarity index 100% rename from t/t1900-repo.sh rename to t/t1900-repo-info.sh diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh index a6f2591d9a61ff..a67b38ab178125 100755 --- a/t/t1901-repo-structure.sh +++ b/t/t1901-repo-structure.sh @@ -145,18 +145,18 @@ test_expect_success SHA1 'lines and nul format' ' test_cmp expect out && test_line_count = 0 err && - # Replace key and value delimiters for nul format. - tr "\n=" "\0\n" expect_nul && git repo structure --format=nul >out 2>err && + tr "\012\000" "=\012" actual && - test_cmp expect_nul out && + test_cmp expect actual && test_line_count = 0 err && # "-z", as a synonym to "--format=nul", participates in the # usual "last one wins" rule. git repo structure --format=table -z >out 2>err && + tr "\012\000" "=\012" actual && - test_cmp expect_nul out && + test_cmp expect actual && test_line_count = 0 err ) ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index e778dd8ae4a6dc..3e44562afaab40 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -31,6 +31,12 @@ Initial setup: . "$TEST_DIRECTORY"/lib-rebase.sh test_expect_success 'setup' ' + # Commit dates are hardcoded to 2005, and the reflog entries will have + # a matching timestamp. Maintenance may thus immediately expire + # reflogs if it was running. + git config set gc.reflogExpire never && + git config set gc.reflogExpireUnreachable never && + git switch -C primary && test_commit A file1 && test_commit B file1 && diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh index a1d7fa7f7c6965..bc51a9d3a70d55 100755 --- a/t/t3406-rebase-message.sh +++ b/t/t3406-rebase-message.sh @@ -8,6 +8,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh test_expect_success 'setup' ' + # Commit dates are hardcoded to 2005, and the reflog entries will have + # a matching timestamp. Maintenance may thus immediately expire + # reflogs if it was running. + git config set gc.reflogExpire never && + git config set gc.reflogExpireUnreachable never && + test_commit O fileO && test_commit X fileX && git branch fast-forward && diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh index be09fc78c16aab..4336f417c27a7c 100755 --- a/t/t3431-rebase-fork-point.sh +++ b/t/t3431-rebase-fork-point.sh @@ -17,6 +17,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME # C was formerly part of main but main was rewound to remove C # test_expect_success setup ' + # Commit dates are hardcoded to 2005, and the reflog entries will have + # a matching timestamp. Maintenance may thus immediately expire + # reflogs if it was running. + git config set gc.reflogExpire never && + git config set gc.reflogExpireUnreachable never && + test_commit A && test_commit B && test_commit C && diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh index 5086e14c022071..181d19dcc14a5e 100755 --- a/t/t3432-rebase-fast-forward.sh +++ b/t/t3432-rebase-fast-forward.sh @@ -11,6 +11,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh test_expect_success setup ' + # Commit dates are hardcoded to 2005, and the reflog entries will have + # a matching timestamp. Maintenance may thus immediately expire + # reflogs if it was running. + git config set gc.reflogExpire never && + git config set gc.reflogExpireUnreachable never && + test_commit A && test_commit B && test_commit C && diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh index f6db5a79dd9d35..5eba15fa66b956 100755 --- a/t/t4128-apply-root.sh +++ b/t/t4128-apply-root.sh @@ -43,6 +43,47 @@ test_expect_success 'apply --directory -p (2) ' ' ' +test_expect_success 'apply --directory (./ prefix)' ' + git reset --hard initial && + git apply --directory=./some/sub -p3 --index patch && + echo Bello >expect && + git show :some/sub/dir/file >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/file +' + +test_expect_success 'apply --directory (double slash)' ' + git reset --hard initial && + git apply --directory=some//sub -p3 --index patch && + echo Bello >expect && + git show :some/sub/dir/file >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/file +' + +test_expect_success 'apply --directory (./ in the middle)' ' + git reset --hard initial && + git apply --directory=some/./sub -p3 --index patch && + echo Bello >expect && + git show :some/sub/dir/file >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/file +' + +test_expect_success 'apply --directory (../ in the middle)' ' + git reset --hard initial && + git apply --directory=some/../some/sub -p3 --index patch && + echo Bello >expect && + git show :some/sub/dir/file >actual && + test_cmp expect actual && + test_cmp expect some/sub/dir/file +' + +test_expect_success 'apply --directory rejects leading ../' ' + test_must_fail git apply --directory=../foo -p3 patch 2>err && + test_grep "unable to normalize directory" err +' + cat > patch << EOF diff --git a/newfile b/newfile new file mode 100644 diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh index 03dfb7a61ea978..8a067a45cb4bae 100755 --- a/t/t5316-pack-delta-depth.sh +++ b/t/t5316-pack-delta-depth.sh @@ -48,6 +48,7 @@ test_description='pack-objects breaks long cross-pack delta chains' # repeatedly-modified file to generate the delta chain). test_expect_success 'create series of packs' ' + test_config maintenance.auto false && test-tool genrandom foo 4096 >content && prev= && for i in $(test_seq 1 10) diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index 449353416ffce4..a7c79225f67526 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -1315,6 +1315,7 @@ test_expect_success 'bitmapped packs are stored via the BTMP chunk' ' git init repo && ( cd repo && + git config set maintenance.auto false && for i in 1 2 3 4 5 do diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh index 892aeb09e4b9d7..62bd973d92afc9 100755 --- a/t/t5326-multi-pack-bitmaps.sh +++ b/t/t5326-multi-pack-bitmaps.sh @@ -93,7 +93,8 @@ test_midx_bitmap_cases () { test_expect_success 'setup test_repository' ' rm -rf * .git && git init && - git config pack.writeBitmapLookupTable '"$writeLookupTable"' + git config pack.writeBitmapLookupTable '"$writeLookupTable"' && + git config maintenance.auto false ' midx_bitmap_core diff --git a/t/t5327-multi-pack-bitmaps-rev.sh b/t/t5327-multi-pack-bitmaps-rev.sh index 9cac03a94bf4b4..cfa12de2a8a7ba 100755 --- a/t/t5327-multi-pack-bitmaps-rev.sh +++ b/t/t5327-multi-pack-bitmaps-rev.sh @@ -30,7 +30,8 @@ test_midx_bitmap_rev () { test_expect_success 'setup bitmap config' ' rm -rf * .git && git init && - git config pack.writeBitmapLookupTable '"$writeLookupTable"' + git config pack.writeBitmapLookupTable '"$writeLookupTable"' && + git config maintenance.auto false ' midx_bitmap_core rev diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh index c3bbc76b0de7a0..7eb79bc2cdb5b5 100755 --- a/t/t5331-pack-objects-stdin.sh +++ b/t/t5331-pack-objects-stdin.sh @@ -14,6 +14,7 @@ packed_objects () { test_expect_success 'setup for --stdin-packs tests' ' git init stdin-packs && + git -C stdin-packs config set maintenance.auto false && ( cd stdin-packs && @@ -255,6 +256,7 @@ test_expect_success '--stdin-packs=follow walks into unknown packs' ' git init repo && ( cd repo && + git config set maintenance.auto false && for c in A B C D do diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh index 395d09444ced72..881ce668e1d14e 100755 --- a/t/t5332-multi-pack-reuse.sh +++ b/t/t5332-multi-pack-reuse.sh @@ -59,6 +59,7 @@ test_pack_objects_reused () { test_expect_success 'preferred pack is reused for single-pack reuse' ' test_config pack.allowPackReuse single && + git config set maintenance.auto false && for i in A B do diff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh index d30d7253d6f6cc..99c7d44d8e9d34 100755 --- a/t/t5334-incremental-multi-pack-index.sh +++ b/t/t5334-incremental-multi-pack-index.sh @@ -15,6 +15,7 @@ midx_chain=$midxdir/multi-pack-index-chain test_expect_success 'convert non-incremental MIDX to incremental' ' test_commit base && + git config set maintenance.auto false && git repack -ad && git multi-pack-index write && diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 83b42ff0733971..b32a0a6aa77e12 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -187,6 +187,7 @@ test_expect_success 'receive-pack runs auto-gc in remote repo' ' cd child && git config gc.autopacklimit 1 && git config gc.autodetach false && + git config maintenance.strategy gc && git branch test_auto_gc && # And create a file that follows the temporary object naming # convention for the auto-gc to remove diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 4bb56c167a52ec..649a615ec9af15 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -154,7 +154,8 @@ test_expect_success 'clone shallow depth 1 with fsck' ' ' test_expect_success 'clone shallow' ' - git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow + git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow && + git -C shallow config set maintenance.auto false ' test_expect_success 'clone shallow depth count' ' @@ -960,6 +961,29 @@ test_expect_success 'fetching deepen' ' ) ' +test_expect_success 'fetching deepen beyond merged branch' ' + test_create_repo shallow-deepen-merged && + ( + cd shallow-deepen-merged && + git commit --allow-empty -m one && + git commit --allow-empty -m two && + git commit --allow-empty -m three && + git switch -c branch && + git commit --allow-empty -m four && + git commit --allow-empty -m five && + git switch main && + git merge --no-ff branch && + cd - && + git clone --bare --depth 3 "file://$(pwd)/shallow-deepen-merged" deepen.git && + git -C deepen.git fetch origin --deepen=1 && + git -C deepen.git rev-list --all >actual && + for commit in $(sed "/^$/d" deepen.git/shallow) + do + test_grep "$commit" actual || exit 1 + done + ) +' + test_negotiation_algorithm_default () { test_when_finished rm -rf clientv0 clientv2 && rm -rf server client && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index c69afb5a609343..5dcb4b51a47d88 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -1321,6 +1321,7 @@ test_expect_success 'fetching with auto-gc does not lock up' ' git config fetch.unpackLimit 1 && git config gc.autoPackLimit 1 && git config gc.autoDetach false && + git config maintenance.strategy gc && GIT_ASK_YESNO="$TRASH_DIRECTORY/askyesno" git fetch --verbose >fetch.out 2>&1 && test_grep "Auto packing the repository" fetch.out && ! grep "Should I try again" fetch.out diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index 1e354e057fa12c..1c2805accac636 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh @@ -229,7 +229,7 @@ test_expect_success 'fetch --refetch triggers repacking' ' GIT_TRACE2_EVENT="$PWD/trace1.event" \ git -C pc1 fetch --refetch origin && - test_subcommand git maintenance run --auto --no-quiet --detach pack-list && test_line_count = 2 pack-list && + test_config -C pack-test maintenance.auto false && for i in A B C do test_commit -C src $i && diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index bef472cb8dc37b..ea9aaad47091a4 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -11,6 +11,7 @@ test_expect_success 'setup' ' # behavior, make sure we always pack everything to one pack by # default git config gc.bigPackThreshold 2g && + git config set --global maintenance.strategy gc && test_oid_init ' diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh index 25e8e9711f8fef..08e82f79140841 100755 --- a/t/t7512-status-help.sh +++ b/t/t7512-status-help.sh @@ -594,6 +594,15 @@ EOF test_cmp expected actual ' +test_expect_success 'rebase in a linked worktree' ' + test_might_fail git rebase --abort && + git worktree add wt && + test_when_finished "test_might_fail git -C wt rebase --abort; + git worktree remove wt" && + GIT_SEQUENCE_EDITOR="echo break >" git -C wt rebase -i HEAD && + git -C wt status >actual && + test_grep "interactive rebase in progress" actual +' test_expect_success 'prepare am_session' ' git reset --hard main && diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index 73b78bdd887d80..acc2589f212727 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -217,6 +217,7 @@ test_expect_success 'repack --keep-pack' ' cd keep-pack && # avoid producing different packs due to delta/base choices git config pack.window 0 && + git config maintenance.auto false && P1=$(commit_and_pack 1) && P2=$(commit_and_pack 2) && P3=$(commit_and_pack 3) && @@ -260,6 +261,7 @@ test_expect_success 'repacking fails when missing .pack actually means missing o # Avoid producing different packs due to delta/base choices git config pack.window 0 && + git config maintenance.auto false && P1=$(commit_and_pack 1) && P2=$(commit_and_pack 2) && P3=$(commit_and_pack 3) && @@ -534,6 +536,7 @@ test_expect_success 'setup for --write-midx tests' ' ( cd midx && git config core.multiPackIndex true && + git config maintenance.auto false && test_commit base ) diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 7cc0ce57f8f320..4700beacc18281 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -7,6 +7,9 @@ test_description='git maintenance builtin' GIT_TEST_COMMIT_GRAPH=0 GIT_TEST_MULTI_PACK_INDEX=0 +# Ensure that auto-maintenance detaches as usual. +sane_unset GIT_TEST_MAINT_AUTO_DETACH + test_lazy_prereq XMLLINT ' xmllint --version ' @@ -42,7 +45,8 @@ test_expect_success 'help text' ' test_grep "usage: git maintenance" err ' -test_expect_success 'run [--auto|--quiet]' ' +test_expect_success 'run [--auto|--quiet] with gc strategy' ' + test_config maintenance.strategy gc && GIT_TRACE2_EVENT="$(pwd)/run-no-auto.txt" \ git maintenance run 2>/dev/null && GIT_TRACE2_EVENT="$(pwd)/run-auto.txt" \ @@ -496,6 +500,7 @@ test_expect_success 'maintenance.incremental-repack.auto' ' ( cd incremental-repack-true && git config core.multiPackIndex true && + git config maintenance.auto false && run_incremental_repack_and_verify ) ' @@ -506,6 +511,7 @@ test_expect_success 'maintenance.incremental-repack.auto (when config is unset)' ( cd incremental-repack-unset && test_unconfig core.multiPackIndex && + git config maintenance.auto false && run_incremental_repack_and_verify ) ' @@ -616,6 +622,7 @@ test_expect_success 'geometric repacking with --auto' ' git init repo && ( cd repo && + git config set maintenance.auto false && # An empty repository does not need repacking, except when # explicitly told to do it. diff --git a/t/test-lib.sh b/t/test-lib.sh index 67d15ae07905c1..dd12b0bdb69cac 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1957,6 +1957,10 @@ test_lazy_prereq COMPAT_HASH ' GIT_TEST_MAINT_SCHEDULER="none:exit 1" export GIT_TEST_MAINT_SCHEDULER +# Ensure that tests cannot race with background maintenance by default. +GIT_TEST_MAINT_AUTO_DETACH="false" +export GIT_TEST_MAINT_AUTO_DETACH + # Does this platform support `git fsmonitor--daemon` # test_lazy_prereq FSMONITOR_DAEMON ' diff --git a/tree-diff.c b/tree-diff.c index 631ea868124256..2f5c956d0259c7 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -2,7 +2,6 @@ * Helper functions for tree diff generation */ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" @@ -11,7 +10,6 @@ #include "hash.h" #include "tree.h" #include "tree-walk.h" -#include "environment.h" #include "repository.h" #include "dir.h" @@ -253,7 +251,7 @@ static void emit_path(struct combine_diff_path ***tail, strbuf_add(base, path, pathlen); p = combine_diff_path_new(base->buf, base->len, mode, - oid ? oid : null_oid(the_hash_algo), + oid ? oid : null_oid(opt->repo->hash_algo), nparent); strbuf_setlen(base, old_baselen); @@ -278,7 +276,7 @@ static void emit_path(struct combine_diff_path ***tail, mode_i = tp[i].entry.mode; } else { - oid_i = null_oid(the_hash_algo); + oid_i = null_oid(opt->repo->hash_algo); mode_i = 0; } diff --git a/upload-pack.c b/upload-pack.c index 2d2b70cbf2dd0b..88dac1b65c81af 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -704,56 +704,6 @@ static int do_reachable_revlist(struct child_process *cmd, return -1; } -static int get_reachable_list(struct upload_pack_data *data, - struct object_array *reachable) -{ - struct child_process cmd = CHILD_PROCESS_INIT; - int i; - struct object *o; - char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */ - const unsigned hexsz = the_hash_algo->hexsz; - int ret; - - if (do_reachable_revlist(&cmd, &data->shallows, reachable, - data->allow_uor) < 0) { - ret = -1; - goto out; - } - - while ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) { - struct object_id oid; - const char *p; - - if (parse_oid_hex(namebuf, &oid, &p) || *p != '\n') - break; - - o = lookup_object(the_repository, &oid); - if (o && o->type == OBJ_COMMIT) { - o->flags &= ~TMP_MARK; - } - } - for (i = get_max_object_index(the_repository); 0 < i; i--) { - o = get_indexed_object(the_repository, i - 1); - if (o && o->type == OBJ_COMMIT && - (o->flags & TMP_MARK)) { - add_object_array(o, NULL, reachable); - o->flags &= ~TMP_MARK; - } - } - close(cmd.out); - - if (finish_command(&cmd)) { - ret = -1; - goto out; - } - - ret = 0; - -out: - child_process_clear(&cmd); - return ret; -} - static int has_unreachable(struct object_array *src, enum allow_uor allow_uor) { struct child_process cmd = CHILD_PROCESS_INIT; @@ -881,29 +831,11 @@ static void deepen(struct upload_pack_data *data, int depth) struct object *object = data->shallows.objects[i].item; object->flags |= NOT_SHALLOW; } - } else if (data->deepen_relative) { - struct object_array reachable_shallows = OBJECT_ARRAY_INIT; - struct commit_list *result; - - /* - * Checking for reachable shallows requires that our refs be - * marked with OUR_REF. - */ - refs_head_ref_namespaced(get_main_ref_store(the_repository), - check_ref, data); - for_each_namespaced_ref_1(check_ref, data); - - get_reachable_list(data, &reachable_shallows); - result = get_shallow_commits(&reachable_shallows, - depth + 1, - SHALLOW, NOT_SHALLOW); - send_shallow(data, result); - free_commit_list(result); - object_array_clear(&reachable_shallows); } else { struct commit_list *result; - result = get_shallow_commits(&data->want_obj, depth, + result = get_shallow_commits(&data->want_obj, &data->shallows, + data->deepen_relative, depth, SHALLOW, NOT_SHALLOW); send_shallow(data, result); free_commit_list(result); diff --git a/worktree.c b/worktree.c index 9308389cb6f029..6e2f0f78283dbf 100644 --- a/worktree.c +++ b/worktree.c @@ -66,6 +66,26 @@ static int is_current_worktree(struct worktree *wt) return is_current; } +struct worktree *get_worktree_from_repository(struct repository *repo) +{ + struct worktree *wt = xcalloc(1, sizeof(*wt)); + char *gitdir = absolute_pathdup(repo->gitdir); + char *commondir = absolute_pathdup(repo->commondir); + + wt->repo = repo; + wt->path = absolute_pathdup(repo->worktree ? repo->worktree + : repo->gitdir); + wt->is_bare = !repo->worktree; + if (fspathcmp(gitdir, commondir)) + wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); + wt->is_current = is_current_worktree(wt); + add_head_info(wt); + + free(gitdir); + free(commondir); + return wt; +} + /* * When in a secondary worktree, and when extensions.worktreeConfig * is true, only $commondir/config and $commondir/worktrees// @@ -288,7 +308,7 @@ const char *worktree_lock_reason(struct worktree *wt) if (!wt->lock_reason_valid) { struct strbuf path = STRBUF_INIT; - strbuf_addstr(&path, worktree_git_path(the_repository, wt, "locked")); + strbuf_addstr(&path, worktree_git_path(wt, "locked")); if (file_exists(path.buf)) { struct strbuf lock_reason = STRBUF_INIT; if (strbuf_read_file(&lock_reason, path.buf, 0) < 0) diff --git a/worktree.h b/worktree.h index e4bcccdc0aef5a..06efe26b835a81 100644 --- a/worktree.h +++ b/worktree.h @@ -38,6 +38,12 @@ struct worktree **get_worktrees(void); */ struct worktree **get_worktrees_without_reading_head(void); +/* + * Construct a struct worktree corresponding to repo->gitdir and + * repo->worktree. + */ +struct worktree *get_worktree_from_repository(struct repository *repo); + /* * Returns 1 if linked worktrees exist, 0 otherwise. */ diff --git a/wt-status.c b/wt-status.c index 95942399f8cec6..68257d6dfd2957 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1648,7 +1648,7 @@ static char *get_branch(const struct worktree *wt, const char *path) struct object_id oid; const char *branch_name; - if (strbuf_read_file(&sb, worktree_git_path(the_repository, wt, "%s", path), 0) <= 0) + if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0) goto got_nothing; while (sb.len && sb.buf[sb.len - 1] == '\n') @@ -1747,18 +1747,21 @@ int wt_status_check_rebase(const struct worktree *wt, { struct stat st; - if (!stat(worktree_git_path(the_repository, wt, "rebase-apply"), &st)) { - if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/applying"), &st)) { + if (!wt) + BUG("wt_status_check_rebase() called with NULL worktree"); + + if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) { + if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) { state->am_in_progress = 1; - if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/patch"), &st) && !st.st_size) + if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size) state->am_empty_patch = 1; } else { state->rebase_in_progress = 1; state->branch = get_branch(wt, "rebase-apply/head-name"); state->onto = get_branch(wt, "rebase-apply/onto"); } - } else if (!stat(worktree_git_path(the_repository, wt, "rebase-merge"), &st)) { - if (!stat(worktree_git_path(the_repository, wt, "rebase-merge/interactive"), &st)) + } else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) { + if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st)) state->rebase_interactive_in_progress = 1; else state->rebase_in_progress = 1; @@ -1774,7 +1777,10 @@ int wt_status_check_bisect(const struct worktree *wt, { struct stat st; - if (!stat(worktree_git_path(the_repository, wt, "BISECT_LOG"), &st)) { + if (!wt) + BUG("wt_status_check_bisect() called with NULL worktree"); + + if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) { state->bisect_in_progress = 1; state->bisecting_from = get_branch(wt, "BISECT_START"); return 1; @@ -1819,18 +1825,19 @@ void wt_status_get_state(struct repository *r, struct stat st; struct object_id oid; enum replay_action action; + struct worktree *wt = get_worktree_from_repository(r); if (!stat(git_path_merge_head(r), &st)) { - wt_status_check_rebase(NULL, state); + wt_status_check_rebase(wt, state); state->merge_in_progress = 1; - } else if (wt_status_check_rebase(NULL, state)) { + } else if (wt_status_check_rebase(wt, state)) { ; /* all set */ } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) { state->cherry_pick_in_progress = 1; oidcpy(&state->cherry_pick_head_oid, &oid); } - wt_status_check_bisect(NULL, state); + wt_status_check_bisect(wt, state); if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") && !repo_get_oid(r, "REVERT_HEAD", &oid)) { state->revert_in_progress = 1; @@ -1848,6 +1855,8 @@ void wt_status_get_state(struct repository *r, if (get_detached_from) wt_status_get_detached_from(r, state); wt_status_check_sparse_checkout(r, state); + + free_worktree(wt); } static void wt_longstatus_print_state(struct wt_status *s)