aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes/2.46.0.txt26
-rw-r--r--Documentation/config/alias.txt25
-rw-r--r--Documentation/config/safe.txt3
-rw-r--r--Documentation/git-refs.txt61
-rw-r--r--Makefile6
-rw-r--r--builtin.h1
-rw-r--r--builtin/clone.c2
-rw-r--r--builtin/init-db.c2
-rw-r--r--builtin/mv.c50
-rw-r--r--builtin/refs.c75
-rw-r--r--builtin/sparse-checkout.c49
-rw-r--r--builtin/submodule--helper.c4
-rw-r--r--command-list.txt1
-rw-r--r--decorate.h2
-rw-r--r--dir.c62
-rw-r--r--dir.h6
-rw-r--r--git.c1
-rw-r--r--imap-send.c1
-rw-r--r--json-writer.c5
-rw-r--r--refs.c345
-rw-r--r--refs.h41
-rw-r--r--refs/files-backend.c124
-rw-r--r--refs/packed-backend.c15
-rw-r--r--refs/ref-cache.c2
-rw-r--r--refs/refs-internal.h7
-rw-r--r--refs/reftable-backend.c55
-rw-r--r--reftable/basics_test.c105
-rw-r--r--reftable/merged.c12
-rw-r--r--reftable/merged.h2
-rw-r--r--reftable/record_test.c37
-rw-r--r--reftable/stack.c8
-rw-r--r--reftable/stack_test.c25
-rw-r--r--repository.c3
-rw-r--r--repository.h10
-rw-r--r--run-command.c3
-rw-r--r--setup.c31
-rw-r--r--setup.h9
-rw-r--r--strbuf.c9
-rw-r--r--strbuf.h5
-rw-r--r--t/helper/test-example-decorate.c78
-rw-r--r--t/helper/test-ref-store.c1
-rw-r--r--t/helper/test-reftable.c1
-rw-r--r--t/helper/test-tool.c1
-rw-r--r--t/helper/test-tool.h1
-rwxr-xr-xt/t0008-ignores.sh8
-rwxr-xr-xt/t0014-alias.sh11
-rwxr-xr-xt/t0015-hash.sh56
-rwxr-xr-xt/t0033-safe-directory.sh15
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh1
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh1
-rwxr-xr-xt/t1460-refs-migrate.sh243
-rwxr-xr-xt/t1517-outside-repo.sh52
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh1
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh12
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh1
-rwxr-xr-xt/t9004-example.sh12
-rw-r--r--t/unit-tests/t-example-decorate.c80
-rw-r--r--t/unit-tests/t-hash.c84
-rw-r--r--t/unit-tests/t-reftable-basics.c160
-rw-r--r--t/unit-tests/t-strvec.c3
-rw-r--r--worktree.c29
62 files changed, 1601 insertions, 481 deletions
diff --git a/.gitignore b/.gitignore
index 612c0f6a0f..8caf3700c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@
/git-rebase
/git-receive-pack
/git-reflog
+/git-refs
/git-remote
/git-remote-http
/git-remote-https
diff --git a/Documentation/RelNotes/2.46.0.txt b/Documentation/RelNotes/2.46.0.txt
index 0a6ad3bd1e..6321ddc319 100644
--- a/Documentation/RelNotes/2.46.0.txt
+++ b/Documentation/RelNotes/2.46.0.txt
@@ -117,6 +117,23 @@ Performance, Internal Implementation, Development Support etc.
* "git add -p" learned to complain when an answer with more than one
letter is given to a prompt that expects a single letter answer.
+ * The alias-expanded command lines are logged to the trace output.
+
+ * A new test was added to ensure git commands that are designed to
+ run outside repositories do work.
+
+ * Basic unit tests for reftable have been reimplemented under the
+ unit test framework.
+
+ * A pair of test helpers that essentially are unit tests on hash
+ algorithms have been rewritten using the unit-tests framework.
+
+ * A test helper that essentially is unit tests on the "decorate"
+ logic has been rewritten using the unit-tests framework.
+
+ * Many memory leaks in the sparse-checkout code paths have been
+ plugged.
+
Fixes since v2.45
-----------------
@@ -210,6 +227,15 @@ Fixes since v2.45
which has been corrected.
(merge 407997c1dd ps/fix-reinit-includeif-onbranch later to maint).
+ * Memory leaks in "git mv" has been plugged.
+
+ * The safe.directory configuration knob has been updated to
+ optionally allow leading path matches.
+ (merge 313eec177a jc/safe-directory-leading-path later to maint).
+
+ * An overly large ".gitignore" files are now rejected silently.
+ (merge e7c3d1ddba jk/cap-exclude-file-size later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge a5a4cb7b27 rs/diff-parseopts-cleanup later to maint).
(merge 55702c543e fa/p4-error later to maint).
diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt
index 01df96fab3..2c5db0ad84 100644
--- a/Documentation/config/alias.txt
+++ b/Documentation/config/alias.txt
@@ -21,8 +21,23 @@ If the alias expansion is prefixed with an exclamation point,
it will be treated as a shell command. For example, defining
`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
`git new` is equivalent to running the shell command
-`gitk --all --not ORIG_HEAD`. Note that shell commands will be
-executed from the top-level directory of a repository, which may
-not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
-from the original current directory. See linkgit:git-rev-parse[1].
+`gitk --all --not ORIG_HEAD`. Note:
++
+* Shell commands will be executed from the top-level directory of a
+ repository, which may not necessarily be the current directory.
+* `GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
+ from the original current directory. See linkgit:git-rev-parse[1].
+* Shell command aliases always receive any extra arguments provided to
+ the Git command-line as positional arguments.
+** Care should be taken if your shell alias is a "one-liner" script
+ with multiple commands (e.g. in a pipeline), references multiple
+ arguments, or is otherwise not able to handle positional arguments
+ added at the end. For example: `alias.cmd = "!echo $1 | grep $2"`
+ called as `git cmd 1 2` will be executed as 'echo $1 | grep $2
+ 1 2', which is not what you want.
+** A convenient way to deal with this is to write your script
+ operations in an inline function that is then called with any
+ arguments from the command-line. For example `alias.cmd = "!c() {
+ echo $1 | grep $2 ; }; c" will correctly execute the prior example.
+** Setting `GIT_TRACE=1` can help you debug the command being run for
+ your alias.
diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 577df40223..2d45c98b12 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -44,7 +44,8 @@ string `*`. This will allow all repositories to be treated as if their
directory was listed in the `safe.directory` list. If `safe.directory=*`
is set in system config and you want to re-enable this protection, then
initialize your list with an empty value before listing the repositories
-that you deem safe.
+that you deem safe. Giving a directory with `/*` appended to it will
+allow access to all repositories under the named directory.
+
As explained, Git only allows you to access repositories owned by
yourself, i.e. the user who is running Git, by default. When Git
diff --git a/Documentation/git-refs.txt b/Documentation/git-refs.txt
new file mode 100644
index 0000000000..5b99e04385
--- /dev/null
+++ b/Documentation/git-refs.txt
@@ -0,0 +1,61 @@
+git-refs(1)
+===========
+
+NAME
+----
+git-refs - Low-level access to refs
+
+
+SYNOPSIS
+--------
+[verse]
+'git refs migrate' --ref-format=<format> [--dry-run]
+
+DESCRIPTION
+-----------
+
+This command provides low-level access to refs.
+
+COMMANDS
+--------
+
+migrate::
+ Migrate ref store between different formats.
+
+OPTIONS
+-------
+
+The following options are specific to 'git refs migrate':
+
+--ref-format=<format>::
+ The ref format to migrate the ref store to. Can be one of:
++
+include::ref-storage-format.txt[]
+
+--dry-run::
+ Perform the migration, but do not modify the repository. The migrated
+ refs will be written into a separate directory that can be inspected
+ separately. The name of the directory will be reported on stdout. This
+ can be used to double check that the migration works as expected before
+ performing the actual migration.
+
+KNOWN LIMITATIONS
+-----------------
+
+The ref format migration has several known limitations in its current form:
+
+* It is not possible to migrate repositories that have reflogs.
+
+* It is not possible to migrate repositories that have worktrees.
+
+* There is no way to block concurrent writes to the repository during an
+ ongoing migration. Concurrent writes can lead to an inconsistent migrated
+ state. Users are expected to block writes on a higher level. If your
+ repository is registered for scheduled maintenance, it is recommended to
+ unregister it first with git-maintenance(1).
+
+These limitations may eventually be lifted.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index 03751e0fc0..fe8c58c4b2 100644
--- a/Makefile
+++ b/Makefile
@@ -793,7 +793,6 @@ TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
TEST_BUILTINS_OBJS += test-dump-split-index.o
TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
TEST_BUILTINS_OBJS += test-env-helper.o
-TEST_BUILTINS_OBJS += test-example-decorate.o
TEST_BUILTINS_OBJS += test-example-tap.o
TEST_BUILTINS_OBJS += test-find-pack.o
TEST_BUILTINS_OBJS += test-fsmonitor-client.o
@@ -1281,6 +1280,7 @@ BUILTIN_OBJS += builtin/read-tree.o
BUILTIN_OBJS += builtin/rebase.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
+BUILTIN_OBJS += builtin/refs.o
BUILTIN_OBJS += builtin/remote-ext.o
BUILTIN_OBJS += builtin/remote-fd.o
BUILTIN_OBJS += builtin/remote.o
@@ -1333,9 +1333,12 @@ THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGRAMS += t-example-decorate
+UNIT_TEST_PROGRAMS += t-hash
UNIT_TEST_PROGRAMS += t-mem-pool
UNIT_TEST_PROGRAMS += t-oidtree
UNIT_TEST_PROGRAMS += t-prio-queue
+UNIT_TEST_PROGRAMS += t-reftable-basics
UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-strcmp-offset
UNIT_TEST_PROGRAMS += t-strvec
@@ -2673,7 +2676,6 @@ REFTABLE_OBJS += reftable/stack.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
-REFTABLE_TEST_OBJS += reftable/basics_test.o
REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/dump.o
REFTABLE_TEST_OBJS += reftable/merged_test.o
diff --git a/builtin.h b/builtin.h
index 28280636da..7eda9b2486 100644
--- a/builtin.h
+++ b/builtin.h
@@ -207,6 +207,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix);
int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
int cmd_receive_pack(int argc, const char **argv, const char *prefix);
int cmd_reflog(int argc, const char **argv, const char *prefix);
+int cmd_refs(int argc, const char **argv, const char *prefix);
int cmd_remote(int argc, const char **argv, const char *prefix);
int cmd_remote_ext(int argc, const char **argv, const char *prefix);
int cmd_remote_fd(int argc, const char **argv, const char *prefix);
diff --git a/builtin/clone.c b/builtin/clone.c
index 730b3efae6..b89ca925b1 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -970,7 +970,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int submodule_progress;
int filter_submodules = 0;
int hash_algo;
- unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+ enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 0170469b84..582dcf20f8 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -81,7 +81,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *ref_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
- unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+ enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
diff --git a/builtin/mv.c b/builtin/mv.c
index 81ca910de6..6c69033c5f 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -156,6 +156,28 @@ free_return:
return ret;
}
+static void remove_empty_src_dirs(const char **src_dir, size_t src_dir_nr)
+{
+ size_t i;
+ struct strbuf a_src_dir = STRBUF_INIT;
+
+ for (i = 0; i < src_dir_nr; i++) {
+ int dummy;
+ strbuf_addstr(&a_src_dir, src_dir[i]);
+ /*
+ * if entries under a_src_dir are all moved away,
+ * recursively remove a_src_dir to cleanup
+ */
+ if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
+ &dummy, &dummy) < 1) {
+ remove_dir_recursively(&a_src_dir, 0);
+ }
+ strbuf_reset(&a_src_dir);
+ }
+
+ strbuf_release(&a_src_dir);
+}
+
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
@@ -175,9 +197,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct strvec submodule_gitfiles_to_free = STRVEC_INIT;
const char **submodule_gitfiles;
char *dst_w_slash = NULL;
- const char **src_dir = NULL;
- int src_dir_nr = 0, src_dir_alloc = 0;
- struct strbuf a_src_dir = STRBUF_INIT;
+ struct strvec src_dir = STRVEC_INIT;
enum update_mode *modes, dst_mode = 0;
struct stat st, dest_st;
struct string_list src_for_dst = STRING_LIST_INIT_DUP;
@@ -323,8 +343,7 @@ dir_check:
/* last - first >= 1 */
modes[i] |= WORKING_DIRECTORY;
- ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc);
- src_dir[src_dir_nr++] = src;
+ strvec_push(&src_dir, src);
n = argc + last - first;
REALLOC_ARRAY(modes, n);
@@ -538,25 +557,7 @@ remove_entry:
}
}
- /*
- * cleanup the empty src_dirs
- */
- for (i = 0; i < src_dir_nr; i++) {
- int dummy;
- strbuf_addstr(&a_src_dir, src_dir[i]);
- /*
- * if entries under a_src_dir are all moved away,
- * recursively remove a_src_dir to cleanup
- */
- if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
- &dummy, &dummy) < 1) {
- remove_dir_recursively(&a_src_dir, 0);
- }
- strbuf_reset(&a_src_dir);
- }
-
- strbuf_release(&a_src_dir);
- free(src_dir);
+ remove_empty_src_dirs(src_dir.v, src_dir.nr);
if (dirty_paths.nr)
advise_on_moving_dirty_path(&dirty_paths);
@@ -571,6 +572,7 @@ remove_entry:
ret = 0;
out:
+ strvec_clear(&src_dir);
free(dst_w_slash);
string_list_clear(&src_for_dst, 0);
string_list_clear(&dirty_paths, 0);
diff --git a/builtin/refs.c b/builtin/refs.c
new file mode 100644
index 0000000000..46dcd150d4
--- /dev/null
+++ b/builtin/refs.c
@@ -0,0 +1,75 @@
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "repository.h"
+#include "strbuf.h"
+
+#define REFS_MIGRATE_USAGE \
+ N_("git refs migrate --ref-format=<format> [--dry-run]")
+
+static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
+{
+ const char * const migrate_usage[] = {
+ REFS_MIGRATE_USAGE,
+ NULL,
+ };
+ const char *format_str = NULL;
+ enum ref_storage_format format;
+ unsigned int flags = 0;
+ struct option options[] = {
+ OPT_STRING_F(0, "ref-format", &format_str, N_("format"),
+ N_("specify the reference format to convert to"),
+ PARSE_OPT_NONEG),
+ OPT_BIT(0, "dry-run", &flags,
+ N_("perform a non-destructive dry-run"),
+ REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
+ OPT_END(),
+ };
+ struct strbuf errbuf = STRBUF_INIT;
+ int err;
+
+ argc = parse_options(argc, argv, prefix, options, migrate_usage, 0);
+ if (argc)
+ usage(_("too many arguments"));
+ if (!format_str)
+ usage(_("missing --ref-format=<format>"));
+
+ format = ref_storage_format_by_name(format_str);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN) {
+ err = error(_("unknown ref storage format '%s'"), format_str);
+ goto out;
+ }
+
+ if (the_repository->ref_storage_format == format) {
+ err = error(_("repository already uses '%s' format"),
+ ref_storage_format_to_name(format));
+ goto out;
+ }
+
+ if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) {
+ err = error("%s", errbuf.buf);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ strbuf_release(&errbuf);
+ return err;
+}
+
+int cmd_refs(int argc, const char **argv, const char *prefix)
+{
+ const char * const refs_usage[] = {
+ REFS_MIGRATE_USAGE,
+ NULL,
+ };
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option opts[] = {
+ OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, opts, refs_usage, 0);
+ return fn(argc, argv, prefix);
+}
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 0f52e25249..3f2bfce8fa 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -96,10 +96,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
printf("\n");
}
- return 0;
+ string_list_clear(&sl, 0);
+ } else {
+ write_patterns_to_file(stdout, &pl);
}
- write_patterns_to_file(stdout, &pl);
clear_pattern_list(&pl);
return 0;
@@ -205,11 +206,13 @@ static int update_working_directory(struct pattern_list *pl)
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
struct repository *r = the_repository;
+ struct pattern_list *old_pl;
/* If no branch has been checked out, there are no updates to make. */
if (is_index_unborn(r->index))
return UPDATE_SPARSITY_SUCCESS;
+ old_pl = r->index->sparse_checkout_patterns;
r->index->sparse_checkout_patterns = pl;
memset(&o, 0, sizeof(o));
@@ -241,7 +244,12 @@ static int update_working_directory(struct pattern_list *pl)
clean_tracked_sparse_directories(r);
- r->index->sparse_checkout_patterns = NULL;
+ if (r->index->sparse_checkout_patterns != pl) {
+ clear_pattern_list(r->index->sparse_checkout_patterns);
+ FREE_AND_NULL(r->index->sparse_checkout_patterns);
+ }
+ r->index->sparse_checkout_patterns = old_pl;
+
return result;
}
@@ -311,6 +319,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
fprintf(fp, "%s/\n", pattern);
free(pattern);
}
+
+ string_list_clear(&sl, 0);
}
static int write_patterns_and_update(struct pattern_list *pl)
@@ -440,7 +450,6 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
char *sparse_filename;
int res;
struct object_id oid;
- struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
OPT_BOOL(0, "cone", &init_opts.cone_mode,
@@ -471,6 +480,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
+ clear_pattern_list(&pl);
return update_working_directory(NULL);
}
@@ -491,10 +501,10 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
return 0;
}
- strbuf_addstr(&pattern, "/*");
- add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
- strbuf_addstr(&pattern, "!/*/");
- add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
+ free(sparse_filename);
+
+ add_pattern("/*", empty_base, 0, &pl, 0);
+ add_pattern("!/*/", empty_base, 0, &pl, 0);
pl.use_cone_patterns = init_opts.cone_mode;
return write_patterns_and_update(&pl);
@@ -513,6 +523,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
char *slash = strrchr(e->pattern, '/');
char *oldpattern = e->pattern;
size_t newlen;
+ struct pattern_entry *dup;
if (!slash || slash == e->pattern)
break;
@@ -523,8 +534,14 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
e->pattern = xstrndup(oldpattern, newlen);
hashmap_entry_init(&e->ent, fspathhash(e->pattern));
- if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
+ dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL);
+ if (!dup) {
hashmap_add(&pl->parent_hashmap, &e->ent);
+ } else {
+ free(e->pattern);
+ free(e);
+ e = dup;
+ }
}
}
@@ -581,15 +598,15 @@ static void add_patterns_from_input(struct pattern_list *pl,
strbuf_to_cone_pattern(&line, pl);
}
}
+ strbuf_release(&line);
} else {
if (file) {
struct strbuf line = STRBUF_INIT;
- while (!strbuf_getline(&line, file)) {
- size_t len;
- char *buf = strbuf_detach(&line, &len);
- add_pattern(buf, empty_base, 0, pl, 0);
- }
+ while (!strbuf_getline(&line, file))
+ add_pattern(line.buf, empty_base, 0, pl, 0);
+
+ strbuf_release(&line);
} else {
for (i = 0; i < argc; i++)
add_pattern(argv[i], empty_base, 0, pl, 0);
@@ -891,7 +908,6 @@ static int sparse_checkout_disable(int argc, const char **argv,
OPT_END(),
};
struct pattern_list pl;
- struct strbuf match_all = STRBUF_INIT;
/*
* We do not exit early if !core_apply_sparse_checkout; due to the
@@ -917,8 +933,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
pl.use_cone_patterns = 0;
core_apply_sparse_checkout = 1;
- strbuf_addstr(&match_all, "/*");
- add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
+ add_pattern("/*", empty_base, 0, &pl, 0);
prepare_repo_settings(the_repository);
the_repository->settings.sparse_index = 0;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 897f19868e..880ab4456e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -256,11 +256,9 @@ static void module_list_active(struct module_list *list)
static char *get_up_path(const char *path)
{
- int i;
struct strbuf sb = STRBUF_INIT;
- for (i = count_slashes(path); i; i--)
- strbuf_addstr(&sb, "../");
+ strbuf_addstrings(&sb, "../", count_slashes(path));
/*
* Check if 'path' ends with slash or not
diff --git a/command-list.txt b/command-list.txt
index c4cd0f352b..e0bb87b3b5 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -157,6 +157,7 @@ git-read-tree plumbingmanipulators
git-rebase mainporcelain history
git-receive-pack synchelpers
git-reflog ancillarymanipulators complete
+git-refs ancillarymanipulators complete
git-remote ancillarymanipulators complete
git-repack ancillarymanipulators complete
git-replace ancillarymanipulators complete
diff --git a/decorate.h b/decorate.h
index cdeb17c9df..08af658d34 100644
--- a/decorate.h
+++ b/decorate.h
@@ -3,7 +3,7 @@
/*
* A data structure that associates Git objects to void pointers. See
- * t/helper/test-example-decorate.c for a demonstration of how to use these
+ * t/unit-tests/t-example-decorate.c for a demonstration of how to use these
* functions.
*/
diff --git a/dir.c b/dir.c
index f6066cc01d..45be4ad261 100644
--- a/dir.c
+++ b/dir.c
@@ -30,6 +30,13 @@
#include "symlinks.h"
#include "trace2.h"
#include "tree.h"
+#include "hex.h"
+
+ /*
+ * The maximum size of a pattern/exclude file. If the file exceeds this size
+ * we will ignore it.
+ */
+#define PATTERN_MAX_FILE_SIZE (100 * 1024 * 1024)
/*
* Tells read_directory_recursive how a file or directory should be treated.
@@ -726,6 +733,17 @@ static char *dup_and_filter_pattern(const char *pattern)
return result;
}
+static void clear_pattern_entry_hashmap(struct hashmap *map)
+{
+ struct hashmap_iter iter;
+ struct pattern_entry *entry;
+
+ hashmap_for_each_entry(map, &iter, entry, ent) {
+ free(entry->pattern);
+ }
+ hashmap_clear_and_free(map, struct pattern_entry, ent);
+}
+
static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
{
struct pattern_entry *translated;
@@ -799,6 +817,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
if (given->patternlen > 2 &&
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
+ struct pattern_entry *old;
+
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
@@ -824,7 +844,11 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
}
hashmap_add(&pl->parent_hashmap, &translated->ent);
- hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data);
+ old = hashmap_remove_entry(&pl->recursive_hashmap, translated, ent, &data);
+ if (old) {
+ free(old->pattern);
+ free(old);
+ }
free(data);
return;
}
@@ -855,8 +879,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
clear_hashmaps:
warning(_("disabling cone pattern matching"));
- hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
- hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
+ clear_pattern_entry_hashmap(&pl->recursive_hashmap);
+ clear_pattern_entry_hashmap(&pl->parent_hashmap);
pl->use_cone_patterns = 0;
}
@@ -908,12 +932,7 @@ void add_pattern(const char *string, const char *base,
int nowildcardlen;
parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
- if (flags & PATTERN_FLAG_MUSTBEDIR) {
- FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
- } else {
- pattern = xmalloc(sizeof(*pattern));
- pattern->pattern = string;
- }
+ FLEX_ALLOC_MEM(pattern, pattern, string, patternlen);
pattern->patternlen = patternlen;
pattern->nowildcardlen = nowildcardlen;
pattern->base = base;
@@ -955,9 +974,8 @@ void clear_pattern_list(struct pattern_list *pl)
for (i = 0; i < pl->nr; i++)
free(pl->patterns[i]);
free(pl->patterns);
- free(pl->filebuf);
- hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
- hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
+ clear_pattern_entry_hashmap(&pl->recursive_hashmap);
+ clear_pattern_entry_hashmap(&pl->parent_hashmap);
memset(pl, 0, sizeof(*pl));
}
@@ -1148,7 +1166,14 @@ static int add_patterns(const char *fname, const char *base, int baselen,
}
}
+ if (size > PATTERN_MAX_FILE_SIZE) {
+ warning("ignoring excessively large pattern file: %s", fname);
+ free(buf);
+ return -1;
+ }
+
add_patterns_from_buffer(buf, size, base, baselen, pl);
+ free(buf);
return 0;
}
@@ -1156,16 +1181,15 @@ static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl)
{
+ char *orig = buf;
int i, lineno = 1;
char *entry;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
- pl->filebuf = buf;
-
if (skip_utf8_bom(&buf, size))
- size -= buf - pl->filebuf;
+ size -= buf - orig;
entry = buf;
@@ -1204,7 +1228,15 @@ int add_patterns_from_blob_to_list(
if (r != 1)
return r;
+ if (size > PATTERN_MAX_FILE_SIZE) {
+ warning("ignoring excessively large pattern blob: %s",
+ oid_to_hex(oid));
+ free(buf);
+ return -1;
+ }
+
add_patterns_from_buffer(buf, size, base, baselen, pl);
+ free(buf);
return 0;
}
diff --git a/dir.h b/dir.h
index b9e8e96128..1398a53fb4 100644
--- a/dir.h
+++ b/dir.h
@@ -62,7 +62,6 @@ struct path_pattern {
*/
struct pattern_list *pl;
- const char *pattern;
int patternlen;
int nowildcardlen;
const char *base;
@@ -74,6 +73,8 @@ struct path_pattern {
* and from -1 decrementing for patterns from CLI args.
*/
int srcpos;
+
+ char pattern[FLEX_ARRAY];
};
/* used for hashmaps for cone patterns */
@@ -94,9 +95,6 @@ struct pattern_list {
int nr;
int alloc;
- /* remember pointer to exclude file contents so we can free() */
- char *filebuf;
-
/* origin of list, e.g. path to filename, or descriptive string */
const char *src;
diff --git a/git.c b/git.c
index 637c61ca9c..683bb69194 100644
--- a/git.c
+++ b/git.c
@@ -594,6 +594,7 @@ static struct cmd_struct commands[] = {
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
+ { "refs", cmd_refs, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..185104d019 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1543,6 +1543,7 @@ int cmd_main(int argc, const char **argv)
}
if (all_msgs.len == 0) {
+ strbuf_release(&all_msgs);
fprintf(stderr, "nothing to send\n");
return 1;
}
diff --git a/json-writer.c b/json-writer.c
index 005c820aa4..25b9201f9c 100644
--- a/json-writer.c
+++ b/json-writer.c
@@ -46,10 +46,7 @@ static void append_quoted_string(struct strbuf *out, const char *in)
static void indent_pretty(struct json_writer *jw)
{
- int k;
-
- for (k = 0; k < jw->open_stack.len; k++)
- strbuf_addstr(&jw->json, " ");
+ strbuf_addstrings(&jw->json, " ", jw->open_stack.len);
}
/*
diff --git a/refs.c b/refs.c
index 31032588e0..1304d3dd87 100644
--- a/refs.c
+++ b/refs.c
@@ -37,14 +37,15 @@ static const struct ref_storage_be *refs_backends[] = {
[REF_STORAGE_FORMAT_REFTABLE] = &refs_be_reftable,
};
-static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
+static const struct ref_storage_be *find_ref_storage_backend(
+ enum ref_storage_format ref_storage_format)
{
if (ref_storage_format < ARRAY_SIZE(refs_backends))
return refs_backends[ref_storage_format];
return NULL;
}
-unsigned int ref_storage_format_by_name(const char *name)
+enum ref_storage_format ref_storage_format_by_name(const char *name)
{
for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
@@ -52,7 +53,7 @@ unsigned int ref_storage_format_by_name(const char *name)
return REF_STORAGE_FORMAT_UNKNOWN;
}
-const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format)
{
const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
if (!be)
@@ -1193,6 +1194,12 @@ int ref_transaction_update(struct ref_transaction *transaction,
{
assert(err);
+ if ((flags & REF_FORCE_CREATE_REFLOG) &&
+ (flags & REF_SKIP_CREATE_REFLOG)) {
+ strbuf_addstr(err, _("refusing to force and skip creation of reflog"));
+ return -1;
+ }
+
if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
((new_oid && !is_null_oid(new_oid)) ?
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
@@ -1854,6 +1861,11 @@ int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *e
return refs->be->create_on_disk(refs, flags, err);
}
+int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err)
+{
+ return refs->be->remove_on_disk(refs, err);
+}
+
int repo_resolve_gitlink_ref(struct repository *r,
const char *submodule, const char *refname,
struct object_id *oid)
@@ -1890,16 +1902,17 @@ static struct ref_store *lookup_ref_store_map(struct strmap *map,
/*
* Create, record, and return a ref_store instance for the specified
- * gitdir.
+ * gitdir using the given ref storage format.
*/
static struct ref_store *ref_store_init(struct repository *repo,
+ enum ref_storage_format format,
const char *gitdir,
unsigned int flags)
{
const struct ref_storage_be *be;
struct ref_store *refs;
- be = find_ref_storage_backend(repo->ref_storage_format);
+ be = find_ref_storage_backend(format);
if (!be)
BUG("reference backend is unknown");
@@ -1921,7 +1934,8 @@ struct ref_store *get_main_ref_store(struct repository *r)
if (!r->gitdir)
BUG("attempting to get main_ref_store outside of repository");
- r->refs_private = ref_store_init(r, r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = ref_store_init(r, r->ref_storage_format,
+ r->gitdir, REF_STORE_ALL_CAPS);
r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
return r->refs_private;
}
@@ -1981,7 +1995,8 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
free(subrepo);
goto done;
}
- refs = ref_store_init(subrepo, submodule_sb.buf,
+ refs = ref_store_init(subrepo, the_repository->ref_storage_format,
+ submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
register_ref_store_map(&repo->submodule_ref_stores, "submodule",
refs, submodule);
@@ -2010,12 +2025,12 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
struct strbuf common_path = STRBUF_INIT;
strbuf_git_common_path(&common_path, wt->repo,
"worktrees/%s", wt->id);
- refs = ref_store_init(wt->repo, common_path.buf,
- REF_STORE_ALL_CAPS);
+ refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
+ common_path.buf, REF_STORE_ALL_CAPS);
strbuf_release(&common_path);
} else {
- refs = ref_store_init(wt->repo, wt->repo->commondir,
- REF_STORE_ALL_CAPS);
+ refs = ref_store_init(wt->repo, the_repository->ref_storage_format,
+ wt->repo->commondir, REF_STORE_ALL_CAPS);
}
if (refs)
@@ -2555,3 +2570,311 @@ int ref_update_check_old_target(const char *referent, struct ref_update *update,
referent, update->old_target);
return -1;
}
+
+struct migration_data {
+ struct ref_store *old_refs;
+ struct ref_transaction *transaction;
+ struct strbuf *errbuf;
+};
+
+static int migrate_one_ref(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
+{
+ struct migration_data *data = cb_data;
+ struct strbuf symref_target = STRBUF_INIT;
+ int ret;
+
+ if (flags & REF_ISSYMREF) {
+ ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
+ if (ret < 0)
+ goto done;
+
+ ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(),
+ symref_target.buf, NULL,
+ REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
+ if (ret < 0)
+ goto done;
+ } else {
+ ret = ref_transaction_create(data->transaction, refname, oid,
+ REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
+ NULL, data->errbuf);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ strbuf_release(&symref_target);
+ return ret;
+}
+
+static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
+{
+ struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
+ size_t from_len, to_len;
+ DIR *from_dir;
+ int ret;
+
+ from_dir = opendir(from_path);
+ if (!from_dir) {
+ strbuf_addf(errbuf, "could not open source directory '%s': %s",
+ from_path, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ strbuf_addstr(&from_buf, from_path);
+ strbuf_complete(&from_buf, '/');
+ from_len = from_buf.len;
+
+ strbuf_addstr(&to_buf, to_path);
+ strbuf_complete(&to_buf, '/');
+ to_len = to_buf.len;
+
+ while (1) {
+ struct dirent *ent;
+
+ errno = 0;
+ ent = readdir(from_dir);
+ if (!ent)
+ break;
+
+ if (!strcmp(ent->d_name, ".") ||
+ !strcmp(ent->d_name, ".."))
+ continue;
+
+ strbuf_setlen(&from_buf, from_len);
+ strbuf_addstr(&from_buf, ent->d_name);
+
+ strbuf_setlen(&to_buf, to_len);
+ strbuf_addstr(&to_buf, ent->d_name);
+
+ ret = rename(from_buf.buf, to_buf.buf);
+ if (ret < 0) {
+ strbuf_addf(errbuf, "could not link file '%s' to '%s': %s",
+ from_buf.buf, to_buf.buf, strerror(errno));
+ goto done;
+ }
+ }
+
+ if (errno) {
+ strbuf_addf(errbuf, "could not read entry from directory '%s': %s",
+ from_path, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ strbuf_release(&from_buf);
+ strbuf_release(&to_buf);
+ if (from_dir)
+ closedir(from_dir);
+ return ret;
+}
+
+static int count_reflogs(const char *reflog UNUSED, void *payload)
+{
+ size_t *reflog_count = payload;
+ (*reflog_count)++;
+ return 0;
+}
+
+static int has_worktrees(void)
+{
+ struct worktree **worktrees = get_worktrees();
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; worktrees[i]; i++) {
+ if (is_main_worktree(worktrees[i]))
+ continue;
+ ret = 1;
+ }
+
+ free_worktrees(worktrees);
+ return ret;
+}
+
+int repo_migrate_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format,
+ unsigned int flags,
+ struct strbuf *errbuf)
+{
+ struct ref_store *old_refs = NULL, *new_refs = NULL;
+ struct ref_transaction *transaction = NULL;
+ struct strbuf new_gitdir = STRBUF_INIT;
+ struct migration_data data;
+ size_t reflog_count = 0;
+ int did_migrate_refs = 0;
+ int ret;
+
+ if (repo->ref_storage_format == format) {
+ strbuf_addstr(errbuf, "current and new ref storage format are equal");
+ ret = -1;
+ goto done;
+ }
+
+ old_refs = get_main_ref_store(repo);
+
+ /*
+ * We do not have any interfaces that would allow us to write many
+ * reflog entries. Once we have them we can remove this restriction.
+ */
+ if (refs_for_each_reflog(old_refs, count_reflogs, &reflog_count) < 0) {
+ strbuf_addstr(errbuf, "cannot count reflogs");
+ ret = -1;
+ goto done;
+ }
+ if (reflog_count) {
+ strbuf_addstr(errbuf, "migrating reflogs is not supported yet");
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * Worktrees complicate the migration because every worktree has a
+ * separate ref storage. While it should be feasible to implement, this
+ * is pushed out to a future iteration.
+ *
+ * TODO: we should really be passing the caller-provided repository to
+ * `has_worktrees()`, but our worktree subsystem doesn't yet support
+ * that.
+ */
+ if (has_worktrees()) {
+ strbuf_addstr(errbuf, "migrating repositories with worktrees is not supported yet");
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * The overall logic looks like this:
+ *
+ * 1. Set up a new temporary directory and initialize it with the new
+ * format. This is where all refs will be migrated into.
+ *
+ * 2. Enumerate all refs and write them into the new ref storage.
+ * This operation is safe as we do not yet modify the main
+ * repository.
+ *
+ * 3. If we're in dry-run mode then we are done and can hand over the
+ * directory to the caller for inspection. If not, we now start
+ * with the destructive part.
+ *
+ * 4. Delete the old ref storage from disk. As we have a copy of refs
+ * in the new ref storage it's okay(ish) if we now get interrupted
+ * as there is an equivalent copy of all refs available.
+ *
+ * 5. Move the new ref storage files into place.
+ *
+ * 6. Change the repository format to the new ref format.
+ */
+ strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
+ if (!mkdtemp(new_gitdir.buf)) {
+ strbuf_addf(errbuf, "cannot create migration directory: %s",
+ strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ new_refs = ref_store_init(repo, format, new_gitdir.buf,
+ REF_STORE_ALL_CAPS);
+ ret = ref_store_create_on_disk(new_refs, 0, errbuf);
+ if (ret < 0)
+ goto done;
+
+ transaction = ref_store_transaction_begin(new_refs, errbuf);
+ if (!transaction)
+ goto done;
+
+ data.old_refs = old_refs;
+ data.transaction = transaction;
+ data.errbuf = errbuf;
+
+ /*
+ * We need to use the internal `do_for_each_ref()` here so that we can
+ * also include broken refs and symrefs. These would otherwise be
+ * skipped silently.
+ *
+ * Ideally, we would do this call while locking the old ref storage
+ * such that there cannot be any concurrent modifications. We do not
+ * have the infra for that though, and the "files" backend does not
+ * allow for a central lock due to its design. It's thus on the user to
+ * ensure that there are no concurrent writes.
+ */
+ ret = do_for_each_ref(old_refs, "", NULL, migrate_one_ref, 0,
+ DO_FOR_EACH_INCLUDE_ROOT_REFS | DO_FOR_EACH_INCLUDE_BROKEN,
+ &data);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * TODO: we might want to migrate to `initial_ref_transaction_commit()`
+ * here, which is more efficient for the files backend because it would
+ * write new refs into the packed-refs file directly. At this point,
+ * the files backend doesn't handle pseudo-refs and symrefs correctly
+ * though, so this requires some more work.
+ */
+ ret = ref_transaction_commit(transaction, errbuf);
+ if (ret < 0)
+ goto done;
+ did_migrate_refs = 1;
+
+ if (flags & REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN) {
+ printf(_("Finished dry-run migration of refs, "
+ "the result can be found at '%s'\n"), new_gitdir.buf);
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * Until now we were in the non-destructive phase, where we only
+ * populated the new ref store. From hereon though we are about
+ * to get hands by deleting the old ref store and then moving
+ * the new one into place.
+ *
+ * Assuming that there were no concurrent writes, the new ref
+ * store should have all information. So if we fail from hereon
+ * we may be in an in-between state, but it would still be able
+ * to recover by manually moving remaining files from the
+ * temporary migration directory into place.
+ */
+ ret = ref_store_remove_on_disk(old_refs, errbuf);
+ if (ret < 0)
+ goto done;
+
+ ret = move_files(new_gitdir.buf, old_refs->gitdir, errbuf);
+ if (ret < 0)
+ goto done;
+
+ if (rmdir(new_gitdir.buf) < 0)
+ warning_errno(_("could not remove temporary migration directory '%s'"),
+ new_gitdir.buf);
+
+ /*
+ * We have migrated the repository, so we now need to adjust the
+ * repository format so that clients will use the new ref store.
+ * We also need to swap out the repository's main ref store.
+ */
+ initialize_repository_version(hash_algo_by_ptr(repo->hash_algo), format, 1);
+
+ free(new_refs->gitdir);
+ new_refs->gitdir = xstrdup(old_refs->gitdir);
+ repo->refs_private = new_refs;
+ ref_store_release(old_refs);
+
+ ret = 0;
+
+done:
+ if (ret && did_migrate_refs) {
+ strbuf_complete(errbuf, '\n');
+ strbuf_addf(errbuf, _("migrated refs can be found at '%s'"),
+ new_gitdir.buf);
+ }
+
+ if (ret && new_refs)
+ ref_store_release(new_refs);
+ ref_transaction_free(transaction);
+ strbuf_release(&new_gitdir);
+ return ret;
+}
diff --git a/refs.h b/refs.h
index fe7f0db35e..76d25df4de 100644
--- a/refs.h
+++ b/refs.h
@@ -11,8 +11,14 @@ struct string_list;
struct string_list_item;
struct worktree;
-unsigned int ref_storage_format_by_name(const char *name);
-const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+enum ref_storage_format {
+ REF_STORAGE_FORMAT_UNKNOWN,
+ REF_STORAGE_FORMAT_FILES,
+ REF_STORAGE_FORMAT_REFTABLE,
+};
+
+enum ref_storage_format ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format);
/*
* Resolve a reference, recursively following symbolic refererences.
@@ -124,6 +130,11 @@ int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *e
void ref_store_release(struct ref_store *ref_store);
/*
+ * Remove the ref store from disk. This deletes all associated data.
+ */
+int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err);
+
+/*
* Return the peeled value of the oid currently being iterated via
* for_each_ref(), etc. This is equivalent to calling:
*
@@ -654,12 +665,18 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
#define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
/*
+ * Skip creation of a reflog entry, even if it would have otherwise been
+ * created.
+ */
+#define REF_SKIP_CREATE_REFLOG (1 << 12)
+
+/*
* Bitmask of all of the flags that are allowed to be passed in to
* ref_transaction_update() and friends:
*/
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
- REF_SKIP_REFNAME_VERIFICATION)
+ REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG)
/*
* Add a reference update to transaction. `new_oid` is the value that
@@ -1054,6 +1071,24 @@ int is_root_ref(const char *refname);
int is_pseudo_ref(const char *refname);
/*
+ * The following flags can be passed to `repo_migrate_ref_storage_format()`:
+ *
+ * - REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN: perform a dry-run migration
+ * without touching the main repository. The result will be written into a
+ * temporary ref storage directory.
+ */
+#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
+
+/*
+ * Migrate the ref storage format used by the repository to the
+ * specified one.
+ */
+int repo_migrate_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format,
+ unsigned int flags,
+ struct strbuf *err);
+
+/*
* The following functions have been removed in Git v2.45 in favor of functions
* that receive a `ref_store` as parameter. The intent of this section is
* merely to help patch authors of in-flight series to have a reference what
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 324c59b096..4519b46171 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -323,19 +323,15 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
add_per_worktree_entries_to_dir(dir, dirname);
}
-/*
- * Add pseudorefs to the ref dir by parsing the directory for any files
- * which follow the pseudoref syntax.
- */
-static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
- struct ref_dir *dir,
- const char *dirname)
+static int for_each_root_ref(struct files_ref_store *refs,
+ int (*cb)(const char *refname, void *cb_data),
+ void *cb_data)
{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
+ const char *dirname = refs->loose->root->name;
struct dirent *de;
size_t dirnamelen;
+ int ret;
DIR *d;
files_ref_path(refs, &path, dirname);
@@ -343,7 +339,7 @@ static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
d = opendir(path.buf);
if (!d) {
strbuf_release(&path);
- return;
+ return -1;
}
strbuf_addstr(&refname, dirname);
@@ -359,14 +355,49 @@ static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
strbuf_addstr(&refname, de->d_name);
dtype = get_dtype(de, &path, 1);
- if (dtype == DT_REG && is_root_ref(de->d_name))
- loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
+ if (dtype == DT_REG && is_root_ref(de->d_name)) {
+ ret = cb(refname.buf, cb_data);
+ if (ret)
+ goto done;
+ }
strbuf_setlen(&refname, dirnamelen);
}
+
+ ret = 0;
+
+done:
strbuf_release(&refname);
strbuf_release(&path);
closedir(d);
+ return ret;
+}
+
+struct fill_root_ref_data {
+ struct files_ref_store *refs;
+ struct ref_dir *dir;
+};
+
+static int fill_root_ref(const char *refname, void *cb_data)
+{
+ struct fill_root_ref_data *data = cb_data;
+ loose_fill_ref_dir_regular_file(data->refs, refname, data->dir);
+ return 0;
+}
+
+/*
+ * Add root refs to the ref dir by parsing the directory for any files which
+ * follow the root ref syntax.
+ */
+static void add_root_refs(struct files_ref_store *refs,
+ struct ref_dir *dir)
+{
+ struct fill_root_ref_data data = {
+ .refs = refs,
+ .dir = dir,
+ };
+
+ for_each_root_ref(refs, fill_root_ref, &data);
}
static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
@@ -388,8 +419,7 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
dir = get_ref_dir(refs->loose->root);
if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
- add_pseudoref_and_head_entries(dir->cache->ref_store, dir,
- refs->loose->root->name);
+ add_root_refs(refs, dir);
/*
* Add an incomplete entry for "refs/" (to be filled
@@ -1752,6 +1782,9 @@ static int files_log_ref_write(struct files_ref_store *refs,
{
int logfd, result;
+ if (flags & REF_SKIP_CREATE_REFLOG)
+ return 0;
+
if (log_all_ref_updates == LOG_REFS_UNSET)
log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
@@ -2253,6 +2286,7 @@ static int split_head_update(struct ref_update *update,
struct ref_update *new_update;
if ((update->flags & REF_LOG_ONLY) ||
+ (update->flags & REF_SKIP_CREATE_REFLOG) ||
(update->flags & REF_IS_PRUNING) ||
(update->flags & REF_UPDATE_VIA_HEAD))
return 0;
@@ -3310,11 +3344,73 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
return 0;
}
+struct remove_one_root_ref_data {
+ const char *gitdir;
+ struct strbuf *err;
+};
+
+static int remove_one_root_ref(const char *refname,
+ void *cb_data)
+{
+ struct remove_one_root_ref_data *data = cb_data;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addf(&buf, "%s/%s", data->gitdir, refname);
+
+ ret = unlink(buf.buf);
+ if (ret < 0)
+ strbuf_addf(data->err, "could not delete %s: %s\n",
+ refname, strerror(errno));
+
+ strbuf_release(&buf);
+ return ret;
+}
+
+static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, REF_STORE_WRITE, "remove");
+ struct remove_one_root_ref_data data = {
+ .gitdir = refs->base.gitdir,
+ .err = err,
+ };
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete refs: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/logs", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete logs: %s",
+ strerror(errno));
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+
+ 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)
+ ret = -1;
+
+ strbuf_release(&sb);
+ return ret;
+}
+
struct ref_storage_be refs_be_files = {
.name = "files",
.init = files_ref_store_init,
.release = files_ref_store_release,
.create_on_disk = files_ref_store_create_on_disk,
+ .remove_on_disk = files_ref_store_remove_on_disk,
.transaction_prepare = files_transaction_prepare,
.transaction_finish = files_transaction_finish,
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 2789fd92f5..c4c1e36aa2 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,5 +1,6 @@
#include "../git-compat-util.h"
#include "../config.h"
+#include "../dir.h"
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
@@ -1266,6 +1267,19 @@ static int packed_ref_store_create_on_disk(struct ref_store *ref_store UNUSED,
return 0;
}
+static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct packed_ref_store *refs = packed_downcast(ref_store, 0, "remove");
+
+ if (remove_path(refs->path) < 0) {
+ strbuf_addstr(err, "could not delete packed-refs");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Write the packed refs from the current snapshot to the packed-refs
* tempfile, incorporating any changes from `updates`. `updates` must
@@ -1724,6 +1738,7 @@ struct ref_storage_be refs_be_packed = {
.init = packed_ref_store_init,
.release = packed_ref_store_release,
.create_on_disk = packed_ref_store_create_on_disk,
+ .remove_on_disk = packed_ref_store_remove_on_disk,
.transaction_prepare = packed_transaction_prepare,
.transaction_finish = packed_transaction_finish,
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index b6c53fc8ed..4ce519bbc8 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -71,6 +71,8 @@ static void free_ref_entry(struct ref_entry *entry)
void free_ref_cache(struct ref_cache *cache)
{
+ if (!cache)
+ return;
free_ref_entry(cache->root);
free(cache);
}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 33749fbd83..cbcb6f9c36 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -517,6 +517,12 @@ typedef int ref_store_create_on_disk_fn(struct ref_store *refs,
int flags,
struct strbuf *err);
+/*
+ * Remove the reference store from disk.
+ */
+typedef int ref_store_remove_on_disk_fn(struct ref_store *refs,
+ struct strbuf *err);
+
typedef int ref_transaction_prepare_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
@@ -649,6 +655,7 @@ struct ref_storage_be {
ref_store_init_fn *init;
ref_store_release_fn *release;
ref_store_create_on_disk_fn *create_on_disk;
+ ref_store_remove_on_disk_fn *remove_on_disk;
ref_transaction_prepare_fn *transaction_prepare;
ref_transaction_finish_fn *transaction_finish;
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 438b5c478b..9886fc67a4 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -2,6 +2,7 @@
#include "../abspath.h"
#include "../chdir-notify.h"
#include "../config.h"
+#include "../dir.h"
#include "../environment.h"
#include "../gettext.h"
#include "../hash.h"
@@ -383,6 +384,56 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store,
return 0;
}
+static int reftable_be_remove_on_disk(struct ref_store *ref_store,
+ struct strbuf *err)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "remove");
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+
+ /*
+ * Release the ref store such that all stacks are closed. This is
+ * required so that the "tables.list" file is not open anymore, which
+ * would otherwise make it impossible to remove the file on Windows.
+ */
+ reftable_be_release(ref_store);
+
+ strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
+ if (remove_dir_recursively(&sb, 0) < 0) {
+ strbuf_addf(err, "could not delete reftables: %s",
+ 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;
+}
+
struct reftable_ref_iterator {
struct ref_iterator base;
struct reftable_ref_store *refs;
@@ -1141,7 +1192,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
if (ret)
goto done;
- } else if (u->flags & REF_HAVE_NEW &&
+ } else if (!(u->flags & REF_SKIP_CREATE_REFLOG) &&
+ (u->flags & REF_HAVE_NEW) &&
(u->flags & REF_FORCE_CREATE_REFLOG ||
should_write_log(&arg->refs->base, u->refname))) {
struct reftable_log_record *log;
@@ -2230,6 +2282,7 @@ struct ref_storage_be refs_be_reftable = {
.init = reftable_be_init,
.release = reftable_be_release,
.create_on_disk = reftable_be_create_on_disk,
+ .remove_on_disk = reftable_be_remove_on_disk,
.transaction_prepare = reftable_be_transaction_prepare,
.transaction_finish = reftable_be_transaction_finish,
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
deleted file mode 100644
index 997c4d9e01..0000000000
--- a/reftable/basics_test.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "system.h"
-
-#include "basics.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-struct integer_needle_lesseq_args {
- int needle;
- int *haystack;
-};
-
-static int integer_needle_lesseq(size_t i, void *_args)
-{
- struct integer_needle_lesseq_args *args = _args;
- return args->needle <= args->haystack[i];
-}
-
-static void test_binsearch(void)
-{
- int haystack[] = { 2, 4, 6, 8, 10 };
- struct {
- int needle;
- size_t expected_idx;
- } testcases[] = {
- {-9000, 0},
- {-1, 0},
- {0, 0},
- {2, 0},
- {3, 1},
- {4, 1},
- {7, 3},
- {9, 4},
- {10, 4},
- {11, 5},
- {9000, 5},
- };
- size_t i = 0;
-
- for (i = 0; i < ARRAY_SIZE(testcases); i++) {
- struct integer_needle_lesseq_args args = {
- .haystack = haystack,
- .needle = testcases[i].needle,
- };
- size_t idx;
-
- idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
- EXPECT(idx == testcases[i].expected_idx);
- }
-}
-
-static void test_names_length(void)
-{
- char *a[] = { "a", "b", NULL };
- EXPECT(names_length(a) == 2);
-}
-
-static void test_parse_names_normal(void)
-{
- char in[] = "a\nb\n";
- char **out = NULL;
- parse_names(in, strlen(in), &out);
- EXPECT(!strcmp(out[0], "a"));
- EXPECT(!strcmp(out[1], "b"));
- EXPECT(!out[2]);
- free_names(out);
-}
-
-static void test_parse_names_drop_empty(void)
-{
- char in[] = "a\n\n";
- char **out = NULL;
- parse_names(in, strlen(in), &out);
- EXPECT(!strcmp(out[0], "a"));
- EXPECT(!out[1]);
- free_names(out);
-}
-
-static void test_common_prefix(void)
-{
- struct strbuf s1 = STRBUF_INIT;
- struct strbuf s2 = STRBUF_INIT;
- strbuf_addstr(&s1, "abcdef");
- strbuf_addstr(&s2, "abc");
- EXPECT(common_prefix_size(&s1, &s2) == 3);
- strbuf_release(&s1);
- strbuf_release(&s2);
-}
-
-int basics_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_common_prefix);
- RUN_TEST(test_parse_names_normal);
- RUN_TEST(test_parse_names_drop_empty);
- RUN_TEST(test_binsearch);
- RUN_TEST(test_names_length);
- return 0;
-}
diff --git a/reftable/merged.c b/reftable/merged.c
index 0da9dba265..6adce44f4b 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -225,19 +225,11 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
return 0;
}
-/* clears the list of subtable, without affecting the readers themselves. */
-void merged_table_release(struct reftable_merged_table *mt)
-{
- FREE_AND_NULL(mt->stack);
- mt->stack_len = 0;
-}
-
void reftable_merged_table_free(struct reftable_merged_table *mt)
{
- if (!mt) {
+ if (!mt)
return;
- }
- merged_table_release(mt);
+ FREE_AND_NULL(mt->stack);
reftable_free(mt);
}
diff --git a/reftable/merged.h b/reftable/merged.h
index a10469f58e..2efe571da6 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -24,8 +24,6 @@ struct reftable_merged_table {
uint64_t max;
};
-void merged_table_release(struct reftable_merged_table *mt);
-
struct reftable_iterator;
void merged_table_init_iter(struct reftable_merged_table *mt,
diff --git a/reftable/record_test.c b/reftable/record_test.c
index c158ee79ff..58290bdba3 100644
--- a/reftable/record_test.c
+++ b/reftable/record_test.c
@@ -64,31 +64,6 @@ static void test_varint_roundtrip(void)
}
}
-static void test_common_prefix(void)
-{
- struct {
- const char *a, *b;
- int want;
- } cases[] = {
- { "abc", "ab", 2 },
- { "", "abc", 0 },
- { "abc", "abd", 2 },
- { "abc", "pqr", 0 },
- };
-
- int i = 0;
- for (i = 0; i < ARRAY_SIZE(cases); i++) {
- struct strbuf a = STRBUF_INIT;
- struct strbuf b = STRBUF_INIT;
- strbuf_addstr(&a, cases[i].a);
- strbuf_addstr(&b, cases[i].b);
- EXPECT(common_prefix_size(&a, &b) == cases[i].want);
-
- strbuf_release(&a);
- strbuf_release(&b);
- }
-}
-
static void set_hash(uint8_t *h, int j)
{
int i = 0;
@@ -258,16 +233,6 @@ static void test_reftable_log_record_roundtrip(void)
strbuf_release(&scratch);
}
-static void test_u24_roundtrip(void)
-{
- uint32_t in = 0x112233;
- uint8_t dest[3];
- uint32_t out;
- put_be24(dest, in);
- out = get_be24(dest);
- EXPECT(in == out);
-}
-
static void test_key_roundtrip(void)
{
uint8_t buffer[1024] = { 0 };
@@ -411,9 +376,7 @@ int record_test_main(int argc, const char *argv[])
RUN_TEST(test_reftable_ref_record_roundtrip);
RUN_TEST(test_varint_roundtrip);
RUN_TEST(test_key_roundtrip);
- RUN_TEST(test_common_prefix);
RUN_TEST(test_reftable_obj_record_roundtrip);
RUN_TEST(test_reftable_index_record_roundtrip);
- RUN_TEST(test_u24_roundtrip);
return 0;
}
diff --git a/reftable/stack.c b/reftable/stack.c
index 98ac9cf9f9..324cb27ce9 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -278,10 +278,8 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
new_tables = NULL;
st->readers_len = new_readers_len;
- if (st->merged) {
- merged_table_release(st->merged);
+ if (st->merged)
reftable_merged_table_free(st->merged);
- }
if (st->readers) {
reftable_free(st->readers);
}
@@ -987,10 +985,8 @@ static int stack_write_compact(struct reftable_stack *st,
done:
reftable_iterator_destroy(&it);
- if (mt) {
- merged_table_release(mt);
+ if (mt)
reftable_merged_table_free(mt);
- }
reftable_ref_record_release(&ref);
reftable_log_record_release(&log);
st->stats.entries_written += entries;
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 0f7b1453e6..6b8136e8e2 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -102,29 +102,6 @@ static void test_read_file(void)
(void) remove(fn);
}
-static void test_parse_names(void)
-{
- char buf[] = "line\n";
- char **names = NULL;
- parse_names(buf, strlen(buf), &names);
-
- EXPECT(NULL != names[0]);
- EXPECT(0 == strcmp(names[0], "line"));
- EXPECT(NULL == names[1]);
- free_names(names);
-}
-
-static void test_names_equal(void)
-{
- char *a[] = { "a", "b", "c", NULL };
- char *b[] = { "a", "b", "d", NULL };
- char *c[] = { "a", "b", NULL };
-
- EXPECT(names_equal(a, a));
- EXPECT(!names_equal(a, b));
- EXPECT(!names_equal(a, c));
-}
-
static int write_test_ref(struct reftable_writer *wr, void *arg)
{
struct reftable_ref_record *ref = arg;
@@ -1034,8 +1011,6 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
int stack_test_main(int argc, const char *argv[])
{
RUN_TEST(test_empty_add);
- RUN_TEST(test_names_equal);
- RUN_TEST(test_parse_names);
RUN_TEST(test_read_file);
RUN_TEST(test_reflog_expire);
RUN_TEST(test_reftable_stack_add);
diff --git a/repository.c b/repository.c
index deb68625e0..95d10cc4a0 100644
--- a/repository.c
+++ b/repository.c
@@ -148,7 +148,8 @@ void repo_set_compat_hash_algo(struct repository *repo, int algo)
repo_read_loose_object_map(repo);
}
-void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+void repo_set_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format)
{
repo->ref_storage_format = format;
}
diff --git a/repository.h b/repository.h
index 4bd8969005..a35cd77c35 100644
--- a/repository.h
+++ b/repository.h
@@ -1,6 +1,7 @@
#ifndef REPOSITORY_H
#define REPOSITORY_H
+#include "refs.h"
#include "strmap.h"
struct config_set;
@@ -26,10 +27,6 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP,
};
-#define REF_STORAGE_FORMAT_UNKNOWN 0
-#define REF_STORAGE_FORMAT_FILES 1
-#define REF_STORAGE_FORMAT_REFTABLE 2
-
struct repo_settings {
int initialized;
@@ -181,7 +178,7 @@ struct repository {
const struct git_hash_algo *compat_hash_algo;
/* Repository's reference storage format, as serialized on disk. */
- unsigned int ref_storage_format;
+ enum ref_storage_format ref_storage_format;
/* A unique-id for tracing purposes. */
int trace2_repo_id;
@@ -220,7 +217,8 @@ void repo_set_gitdir(struct repository *repo, const char *root,
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, unsigned int format);
+void repo_set_ref_storage_format(struct repository *repo,
+ enum ref_storage_format format);
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/run-command.c b/run-command.c
index 1b821042b4..31b20123d8 100644
--- a/run-command.c
+++ b/run-command.c
@@ -746,6 +746,8 @@ fail_pipe:
goto end_of_spawn;
}
+ trace_argv_printf(&argv.v[1], "trace: start_command:");
+
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
@@ -913,6 +915,7 @@ end_of_spawn:
else if (cmd->use_shell)
cmd->args.v = prepare_shell_cmd(&nargv, sargv);
+ trace_argv_printf(cmd->args.v, "trace: start_command:");
cmd->pid = mingw_spawnvpe(cmd->args.v[0], cmd->args.v,
(char**) cmd->env.v,
cmd->dir, fhin, fhout, fherr);
diff --git a/setup.c b/setup.c
index e47946d0e7..20f380825b 100644
--- a/setup.c
+++ b/setup.c
@@ -1230,13 +1230,20 @@ static int safe_directory_cb(const char *key, const char *value,
} else if (!strcmp(value, "*")) {
data->is_safe = 1;
} else {
- char *interpolated = NULL;
-
- if (!git_config_pathname(&interpolated, key, value) &&
- !fspathcmp(data->path, interpolated ? interpolated : value))
- data->is_safe = 1;
-
- free(interpolated);
+ char *allowed = NULL;
+
+ if (!git_config_pathname(&allowed, key, value)) {
+ const char *check = allowed ? allowed : value;
+ if (ends_with(check, "/*")) {
+ size_t len = strlen(check);
+ if (!fspathncmp(check, data->path, len - 1))
+ data->is_safe = 1;
+ } else if (!fspathcmp(data->path, check)) {
+ data->is_safe = 1;
+ }
+ }
+ if (allowed != value)
+ free(allowed);
}
return 0;
@@ -2050,7 +2057,7 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
}
void initialize_repository_version(int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
int reinit)
{
char repo_version_string[10];
@@ -2081,6 +2088,8 @@ void initialize_repository_version(int hash_algo,
if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
git_config_set("extensions.refstorage",
ref_storage_format_to_name(ref_storage_format));
+ else if (reinit)
+ git_config_set_gently("extensions.refstorage", NULL);
}
static int is_reinit(void)
@@ -2095,7 +2104,7 @@ static int is_reinit(void)
return ret;
}
-void create_reference_database(unsigned int ref_storage_format,
+void create_reference_database(enum ref_storage_format ref_storage_format,
const char *initial_branch, int quiet)
{
struct strbuf err = STRBUF_INIT;
@@ -2294,7 +2303,7 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
}
static void validate_ref_storage_format(struct repository_format *repo_fmt,
- unsigned int format)
+ enum ref_storage_format format)
{
const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
@@ -2314,7 +2323,7 @@ static void validate_ref_storage_format(struct repository_format *repo_fmt,
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
const char *initial_branch,
int init_shared_repository, unsigned int flags)
{
diff --git a/setup.h b/setup.h
index b3fd3bf45a..cd8dbc2497 100644
--- a/setup.h
+++ b/setup.h
@@ -1,6 +1,7 @@
#ifndef SETUP_H
#define SETUP_H
+#include "refs.h"
#include "string-list.h"
int is_inside_git_dir(void);
@@ -128,7 +129,7 @@ struct repository_format {
int is_bare;
int hash_algo;
int compat_hash_algo;
- unsigned int ref_storage_format;
+ enum ref_storage_format ref_storage_format;
int sparse_index;
char *work_tree;
struct string_list unknown_extensions;
@@ -192,13 +193,13 @@ const char *get_template_dir(const char *option_template);
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
const char *initial_branch, int init_shared_repository,
unsigned int flags);
void initialize_repository_version(int hash_algo,
- unsigned int ref_storage_format,
+ enum ref_storage_format ref_storage_format,
int reinit);
-void create_reference_database(unsigned int ref_storage_format,
+void create_reference_database(enum ref_storage_format ref_storage_format,
const char *initial_branch, int quiet);
/*
diff --git a/strbuf.c b/strbuf.c
index e1076c9891..3d2189a7f6 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -313,6 +313,15 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
strbuf_setlen(sb, sb->len + len);
}
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n)
+{
+ size_t len = strlen(s);
+
+ strbuf_grow(sb, st_mult(len, n));
+ for (size_t i = 0; i < n; i++)
+ strbuf_add(sb, s, len);
+}
+
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
{
strbuf_grow(sb, sb2->len);
diff --git a/strbuf.h b/strbuf.h
index 97fa4a3d01..003f880ff7 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -311,6 +311,11 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
}
/**
+ * Add a NUL-terminated string the specified number of times to the buffer.
+ */
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n);
+
+/**
* Copy the contents of another buffer at the end of the current one.
*/
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
deleted file mode 100644
index 8f59f6be4c..0000000000
--- a/t/helper/test-example-decorate.c
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "test-tool.h"
-#include "git-compat-util.h"
-#include "object.h"
-#include "decorate.h"
-#include "repository.h"
-
-int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
-{
- struct decoration n;
- struct object_id one_oid = { {1} };
- struct object_id two_oid = { {2} };
- struct object_id three_oid = { {3} };
- struct object *one, *two, *three;
-
- int decoration_a, decoration_b;
-
- void *ret;
-
- int i, objects_noticed = 0;
-
- /*
- * The struct must be zero-initialized.
- */
- memset(&n, 0, sizeof(n));
-
- /*
- * Add 2 objects, one with a non-NULL decoration and one with a NULL
- * decoration.
- */
- one = lookup_unknown_object(the_repository, &one_oid);
- two = lookup_unknown_object(the_repository, &two_oid);
- ret = add_decoration(&n, one, &decoration_a);
- if (ret)
- BUG("when adding a brand-new object, NULL should be returned");
- ret = add_decoration(&n, two, NULL);
- if (ret)
- BUG("when adding a brand-new object, NULL should be returned");
-
- /*
- * When re-adding an already existing object, the old decoration is
- * returned.
- */
- ret = add_decoration(&n, one, NULL);
- if (ret != &decoration_a)
- BUG("when readding an already existing object, existing decoration should be returned");
- ret = add_decoration(&n, two, &decoration_b);
- if (ret)
- BUG("when readding an already existing object, existing decoration should be returned");
-
- /*
- * Lookup returns the added declarations, or NULL if the object was
- * never added.
- */
- ret = lookup_decoration(&n, one);
- if (ret)
- BUG("lookup should return added declaration");
- ret = lookup_decoration(&n, two);
- if (ret != &decoration_b)
- BUG("lookup should return added declaration");
- three = lookup_unknown_object(the_repository, &three_oid);
- ret = lookup_decoration(&n, three);
- if (ret)
- BUG("lookup for unknown object should return NULL");
-
- /*
- * The user can also loop through all entries.
- */
- for (i = 0; i < n.size; i++) {
- if (n.entries[i].base)
- objects_noticed++;
- }
- if (objects_noticed != 2)
- BUG("should have 2 objects");
-
- clear_decoration(&n, NULL);
-
- return 0;
-}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index c9efd74c2b..ad24300170 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -126,6 +126,7 @@ static struct flag_definition transaction_flags[] = {
FLAG_DEF(REF_FORCE_CREATE_REFLOG),
FLAG_DEF(REF_SKIP_OID_VERIFICATION),
FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION),
+ FLAG_DEF(REF_SKIP_CREATE_REFLOG),
{ NULL, 0 }
};
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index bae731669c..9160bc5da6 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -5,7 +5,6 @@
int cmd__reftable(int argc, const char **argv)
{
/* test from simple to complex. */
- basics_test_main(argc, argv);
record_test_main(argc, argv);
block_test_main(argc, argv);
tree_test_main(argc, argv);
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 253324a06b..93436a82ae 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -29,7 +29,6 @@ static struct test_cmd cmds[] = {
{ "dump-split-index", cmd__dump_split_index },
{ "dump-untracked-cache", cmd__dump_untracked_cache },
{ "env-helper", cmd__env_helper },
- { "example-decorate", cmd__example_decorate },
{ "example-tap", cmd__example_tap },
{ "find-pack", cmd__find_pack },
{ "fsmonitor-client", cmd__fsmonitor_client },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 460dd7d260..d9033d14e1 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -23,7 +23,6 @@ int cmd__dump_split_index(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
int cmd__dump_reftable(int argc, const char **argv);
int cmd__env_helper(int argc, const char **argv);
-int cmd__example_decorate(int argc, const char **argv);
int cmd__example_tap(int argc, const char **argv);
int cmd__find_pack(int argc, const char **argv);
int cmd__fsmonitor_client(int argc, const char **argv);
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 361446b2f4..02a18d4fdb 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -945,4 +945,12 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
test_grep "unable to access.*gitignore" err
'
+test_expect_success EXPENSIVE 'large exclude file ignored in tree' '
+ test_when_finished "rm .gitignore" &&
+ dd if=/dev/zero of=.gitignore bs=101M count=1 &&
+ git ls-files -o --exclude-standard 2>err &&
+ echo "warning: ignoring excessively large pattern file: .gitignore" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 95568342be..854d59ec58 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -44,4 +44,15 @@ test_expect_success 'run-command formats empty args properly' '
test_cmp expect actual
'
+test_expect_success 'tracing a shell alias with arguments shows trace of prepared command' '
+ cat >expect <<-EOF &&
+ trace: start_command: SHELL -c ${SQ}echo \$* "\$@"${SQ} ${SQ}echo \$*${SQ} arg
+ EOF
+ git config alias.echo "!echo \$*" &&
+ env GIT_TRACE=1 git echo arg 2>output &&
+ # redact platform differences
+ sed -n -e "s/^\(trace: start_command:\) .* -c /\1 SHELL -c /p" output >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
deleted file mode 100755
index 0a087a1983..0000000000
--- a/t/t0015-hash.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-test_description='test basic hash implementation'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'test basic SHA-1 hash values' '
- test-tool sha1 </dev/null >actual &&
- grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual &&
- printf "a" | test-tool sha1 >actual &&
- grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual &&
- printf "abc" | test-tool sha1 >actual &&
- grep a9993e364706816aba3e25717850c26c9cd0d89d actual &&
- printf "message digest" | test-tool sha1 >actual &&
- grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
- printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
- grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
- test-tool sha1 >actual &&
- grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
- printf "blob 0\0" | test-tool sha1 >actual &&
- grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual &&
- printf "blob 3\0abc" | test-tool sha1 >actual &&
- grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual &&
- printf "tree 0\0" | test-tool sha1 >actual &&
- grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual
-'
-
-test_expect_success 'test basic SHA-256 hash values' '
- test-tool sha256 </dev/null >actual &&
- grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual &&
- printf "a" | test-tool sha256 >actual &&
- grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual &&
- printf "abc" | test-tool sha256 >actual &&
- grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual &&
- printf "message digest" | test-tool sha256 >actual &&
- grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual &&
- printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
- grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
- # Try to exercise the chunking code by turning autoflush on.
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
- test-tool sha256 >actual &&
- grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
- perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" |
- test-tool sha256 >actual &&
- grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
- printf "blob 0\0" | test-tool sha256 >actual &&
- grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual &&
- printf "blob 3\0abc" | test-tool sha256 >actual &&
- grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual &&
- printf "tree 0\0" | test-tool sha256 >actual &&
- grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual
-'
-
-test_done
diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
index 11c3e8f28e..5fe61f1291 100755
--- a/t/t0033-safe-directory.sh
+++ b/t/t0033-safe-directory.sh
@@ -71,7 +71,22 @@ test_expect_success 'safe.directory=*, but is reset' '
expect_rejected_dir
'
+test_expect_success 'safe.directory with matching glob' '
+ git config --global --unset-all safe.directory &&
+ p=$(pwd) &&
+ git config --global safe.directory "${p%/*}/*" &&
+ git status
+'
+
+test_expect_success 'safe.directory with unmatching glob' '
+ git config --global --unset-all safe.directory &&
+ p=$(pwd) &&
+ git config --global safe.directory "${p%/*}no/*" &&
+ expect_rejected_dir
+'
+
test_expect_success 'safe.directory in included file' '
+ git config --global --unset-all safe.directory &&
cat >gitconfig-include <<-EOF &&
[safe]
directory = "$(pwd)"
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 3a14218b24..da0e7714d5 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index ab3a105fff..8c5cd651b4 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -8,6 +8,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
GIT_TEST_SPLIT_INDEX=false
export GIT_TEST_SPLIT_INDEX
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
list_files() {
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
new file mode 100755
index 0000000000..f7c0783d30
--- /dev/null
+++ b/t/t1460-refs-migrate.sh
@@ -0,0 +1,243 @@
+#!/bin/sh
+
+test_description='migration of ref storage backends'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_migration () {
+ git -C "$1" for-each-ref --include-root-refs \
+ --format='%(refname) %(objectname) %(symref)' >expect &&
+ git -C "$1" refs migrate --ref-format="$2" &&
+ git -C "$1" for-each-ref --include-root-refs \
+ --format='%(refname) %(objectname) %(symref)' >actual &&
+ test_cmp expect actual &&
+
+ git -C "$1" rev-parse --show-ref-format >actual &&
+ echo "$2" >expect &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ rm -rf .git &&
+ # The migration does not yet support reflogs.
+ git config --global core.logAllRefUpdates false
+'
+
+test_expect_success "superfluous arguments" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate foo 2>err &&
+ cat >expect <<-EOF &&
+ usage: too many arguments
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success "missing ref storage format" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate 2>err &&
+ cat >expect <<-EOF &&
+ usage: missing --ref-format=<format>
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success "unknown ref storage format" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=unknown 2>err &&
+ cat >expect <<-EOF &&
+ error: unknown ref storage format ${SQ}unknown${SQ}
+ EOF
+ test_cmp expect err
+'
+
+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
+
+ test_expect_success "$from_format: migration to same format fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$from_format 2>err &&
+ cat >expect <<-EOF &&
+ error: repository already uses ${SQ}$from_format${SQ} format
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: migration with reflog fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_config -C repo core.logAllRefUpdates true &&
+ test_commit -C repo logged &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$to_format 2>err &&
+ cat >expect <<-EOF &&
+ error: migrating reflogs is not supported yet
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: migration with worktree fails" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ git -C repo worktree add wt &&
+ test_must_fail git -C repo refs migrate \
+ --ref-format=$to_format 2>err &&
+ cat >expect <<-EOF &&
+ error: migrating repositories with worktrees is not supported yet
+ EOF
+ test_cmp expect err
+ '
+
+ test_expect_success "$from_format -> $to_format: unborn HEAD" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: single ref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: bare repository" '
+ test_when_finished "rm -rf repo repo.git" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git clone --ref-format=$from_format --mirror repo repo.git &&
+ test_migration repo.git "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: dangling symref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo symbolic-ref BROKEN_HEAD refs/heads/nonexistent &&
+ test_migration repo "$to_format" &&
+ echo refs/heads/nonexistent >expect &&
+ git -C repo symbolic-ref BROKEN_HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$from_format -> $to_format: broken ref" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ test-tool -C repo ref-store main update-ref "" refs/heads/broken \
+ "$(test_oid 001)" "$ZERO_OID" REF_SKIP_CREATE_REFLOG,REF_SKIP_OID_VERIFICATION &&
+ test_migration repo "$to_format" &&
+ test_oid 001 >expect &&
+ git -C repo rev-parse refs/heads/broken >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "$from_format -> $to_format: pseudo-refs" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo update-ref FOO_HEAD HEAD &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: special refs are left alone" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo rev-parse HEAD >repo/.git/MERGE_HEAD &&
+ git -C repo rev-parse MERGE_HEAD &&
+ test_migration repo "$to_format" &&
+ test_path_is_file repo/.git/MERGE_HEAD
+ '
+
+ test_expect_success "$from_format -> $to_format: a bunch of refs" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+
+ test_commit -C repo initial &&
+ cat >input <<-EOF &&
+ create FOO_HEAD HEAD
+ create refs/heads/branch-1 HEAD
+ create refs/heads/branch-2 HEAD
+ create refs/heads/branch-3 HEAD
+ create refs/heads/branch-4 HEAD
+ create refs/tags/tag-1 HEAD
+ create refs/tags/tag-2 HEAD
+ EOF
+ git -C repo update-ref --stdin <input &&
+ test_migration repo "$to_format"
+ '
+
+ test_expect_success "$from_format -> $to_format: dry-run migration does not modify repository" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ git -C repo refs migrate --dry-run \
+ --ref-format=$to_format >output &&
+ grep "Finished dry-run migration of refs" output &&
+ test_path_is_dir repo/.git/ref_migration.* &&
+ echo $from_format >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+ '
+ done
+done
+
+test_expect_success 'migrating from files format deletes backend files' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=files repo &&
+ test_commit -C repo first &&
+ git -C repo pack-refs --all &&
+ test_commit -C repo second &&
+ git -C repo update-ref ORIG_HEAD HEAD &&
+ git -C repo rev-parse HEAD >repo/.git/FETCH_HEAD &&
+
+ test_path_is_file repo/.git/HEAD &&
+ test_path_is_file repo/.git/ORIG_HEAD &&
+ test_path_is_file repo/.git/refs/heads/main &&
+ test_path_is_file repo/.git/packed-refs &&
+
+ test_migration repo reftable &&
+
+ echo "ref: refs/heads/.invalid" >expect &&
+ test_cmp expect repo/.git/HEAD &&
+ echo "this repository uses the reftable format" >expect &&
+ test_cmp expect repo/.git/refs/heads &&
+ test_path_is_file repo/.git/FETCH_HEAD &&
+ test_path_is_missing repo/.git/ORIG_HEAD &&
+ test_path_is_missing repo/.git/refs/heads/main &&
+ test_path_is_missing repo/.git/logs &&
+ test_path_is_missing repo/.git/packed-refs
+'
+
+test_expect_success 'migrating from reftable format deletes backend files' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=reftable repo &&
+ test_commit -C repo first &&
+
+ test_path_is_dir repo/.git/reftable &&
+ test_migration repo files &&
+
+ test_path_is_missing repo/.git/reftable &&
+ echo "ref: refs/heads/main" >expect &&
+ test_cmp expect repo/.git/HEAD &&
+ test_path_is_file repo/.git/refs/heads/main
+'
+
+test_done
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
index 557808ffa7..990a036582 100755
--- a/t/t1517-outside-repo.sh
+++ b/t/t1517-outside-repo.sh
@@ -56,4 +56,56 @@ test_expect_success 'grep outside repository' '
test_cmp expect actual
'
+test_expect_success 'imap-send outside repository' '
+ test_config_global imap.host imaps://localhost &&
+ test_config_global imap.folder Drafts &&
+
+ echo nothing to send >expect &&
+ test_must_fail git imap-send -v </dev/null 2>actual &&
+ test_cmp expect actual &&
+
+ (
+ cd non-repo &&
+ test_must_fail git imap-send -v </dev/null 2>../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-ref-format outside repository' '
+ git check-ref-format --branch refs/heads/xyzzy >expect &&
+ nongit git check-ref-format --branch refs/heads/xyzzy >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff outside repository' '
+ echo one >one &&
+ echo two >two &&
+ test_must_fail git diff --no-index one two >expect.raw &&
+ (
+ cd non-repo &&
+ cp ../one . &&
+ cp ../two . &&
+ test_must_fail git diff one two >../actual.raw
+ ) &&
+ # outside repository diff falls back to SHA-1 but
+ # GIT_DEFAULT_HASH may be set to sha256 on the in-repo side.
+ sed -e "/^index /d" expect.raw >expect &&
+ sed -e "/^index /d" actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stripspace outside repository' '
+ nongit git stripspace -s </dev/null
+'
+
+test_expect_success 'remote-http outside repository' '
+ test_must_fail git remote-http 2>actual &&
+ test_grep "^error: remote-curl" actual &&
+ (
+ cd non-repo &&
+ test_must_fail git remote-http 2>../actual
+ ) &&
+ test_grep "^error: remote-curl" actual
+'
+
test_done
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
index 08580fd3dc..fcdefba48c 100755
--- a/t/t3602-rm-sparse-checkout.sh
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -2,6 +2,7 @@
test_description='git rm in sparse checked out working trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' "
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 43e1afd44c..0387f35a32 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -701,4 +701,16 @@ test_expect_success 'expand blob limit in protocol' '
grep "blob:limit=1024" trace
'
+test_expect_success EXPENSIVE 'large sparse filter file ignored' '
+ blob=$(dd if=/dev/zero bs=101M count=1 |
+ git hash-object -w --stdin) &&
+ test_must_fail \
+ git rev-list --all --objects --filter=sparse:oid=$blob 2>err &&
+ cat >expect <<-EOF &&
+ warning: ignoring excessively large pattern blob: $blob
+ fatal: unable to parse sparse filter data in $blob
+ EOF
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
index 26582ae4e5..57969ce805 100755
--- a/t/t7002-mv-sparse-checkout.sh
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -2,6 +2,7 @@
test_description='git mv in sparse working trees'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
setup_sparse_checkout () {
diff --git a/t/t9004-example.sh b/t/t9004-example.sh
deleted file mode 100755
index 590aab0304..0000000000
--- a/t/t9004-example.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-test_description='check that example code compiles and runs'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'decorate' '
- test-tool example-decorate
-'
-
-test_done
diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c
new file mode 100644
index 0000000000..3c856a8cf2
--- /dev/null
+++ b/t/unit-tests/t-example-decorate.c
@@ -0,0 +1,80 @@
+#include "test-lib.h"
+#include "object.h"
+#include "decorate.h"
+#include "repository.h"
+
+struct test_vars {
+ struct object *one, *two, *three;
+ struct decoration n;
+ int decoration_a, decoration_b;
+};
+
+static void t_add(struct test_vars *vars)
+{
+ void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a);
+
+ if (!check(ret == NULL))
+ test_msg("when adding a brand-new object, NULL should be returned");
+ ret = add_decoration(&vars->n, vars->two, NULL);
+ if (!check(ret == NULL))
+ test_msg("when adding a brand-new object, NULL should be returned");
+}
+
+static void t_readd(struct test_vars *vars)
+{
+ void *ret = add_decoration(&vars->n, vars->one, NULL);
+
+ if (!check(ret == &vars->decoration_a))
+ test_msg("when readding an already existing object, existing decoration should be returned");
+ ret = add_decoration(&vars->n, vars->two, &vars->decoration_b);
+ if (!check(ret == NULL))
+ test_msg("when readding an already existing object, existing decoration should be returned");
+}
+
+static void t_lookup(struct test_vars *vars)
+{
+ void *ret = lookup_decoration(&vars->n, vars->one);
+
+ if (!check(ret == NULL))
+ test_msg("lookup should return added declaration");
+ ret = lookup_decoration(&vars->n, vars->two);
+ if (!check(ret == &vars->decoration_b))
+ test_msg("lookup should return added declaration");
+ ret = lookup_decoration(&vars->n, vars->three);
+ if (!check(ret == NULL))
+ test_msg("lookup for unknown object should return NULL");
+}
+
+static void t_loop(struct test_vars *vars)
+{
+ int i, objects_noticed = 0;
+
+ for (i = 0; i < vars->n.size; i++) {
+ if (vars->n.entries[i].base)
+ objects_noticed++;
+ }
+ if (!check_int(objects_noticed, ==, 2))
+ test_msg("should have 2 objects");
+}
+
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
+{
+ struct object_id one_oid = { { 1 } }, two_oid = { { 2 } }, three_oid = { { 3 } };
+ struct test_vars vars = { 0 };
+
+ vars.one = lookup_unknown_object(the_repository, &one_oid);
+ vars.two = lookup_unknown_object(the_repository, &two_oid);
+ vars.three = lookup_unknown_object(the_repository, &three_oid);
+
+ TEST(t_add(&vars),
+ "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration.");
+ TEST(t_readd(&vars),
+ "When re-adding an already existing object, the old decoration is returned.");
+ TEST(t_lookup(&vars),
+ "Lookup returns the added declarations, or NULL if the object was never added.");
+ TEST(t_loop(&vars), "The user can also loop through all entries.");
+
+ clear_decoration(&vars.n, NULL);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-hash.c b/t/unit-tests/t-hash.c
new file mode 100644
index 0000000000..e9a78bf2c0
--- /dev/null
+++ b/t/unit-tests/t-hash.c
@@ -0,0 +1,84 @@
+#include "test-lib.h"
+#include "hex.h"
+#include "strbuf.h"
+
+static void check_hash_data(const void *data, size_t data_length,
+ const char *expected_hashes[])
+{
+ if (!check(data != NULL)) {
+ test_msg("BUG: NULL data pointer provided");
+ return;
+ }
+
+ for (size_t i = 1; i < ARRAY_SIZE(hash_algos); i++) {
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_MAX_HEXSZ];
+ const struct git_hash_algo *algop = &hash_algos[i];
+
+ algop->init_fn(&ctx);
+ algop->update_fn(&ctx, data, data_length);
+ algop->final_fn(hash, &ctx);
+
+ if (!check_str(hash_to_hex_algop(hash, algop), expected_hashes[i - 1]))
+ test_msg("result does not match with the expected for %s\n", hash_algos[i].name);
+ }
+}
+
+/* Works with a NUL terminated string. Doesn't work if it should contain a NUL character. */
+#define TEST_HASH_STR(data, expected_sha1, expected_sha256) do { \
+ const char *expected_hashes[] = { expected_sha1, expected_sha256 }; \
+ TEST(check_hash_data(data, strlen(data), expected_hashes), \
+ "SHA1 and SHA256 (%s) works", #data); \
+ } while (0)
+
+/* Only works with a literal string, useful when it contains a NUL character. */
+#define TEST_HASH_LITERAL(literal, expected_sha1, expected_sha256) do { \
+ const char *expected_hashes[] = { expected_sha1, expected_sha256 }; \
+ TEST(check_hash_data(literal, (sizeof(literal) - 1), expected_hashes), \
+ "SHA1 and SHA256 (%s) works", #literal); \
+ } while (0)
+
+int cmd_main(int argc, const char **argv)
+{
+ struct strbuf aaaaaaaaaa_100000 = STRBUF_INIT;
+ struct strbuf alphabet_100000 = STRBUF_INIT;
+
+ strbuf_addstrings(&aaaaaaaaaa_100000, "aaaaaaaaaa", 100000);
+ strbuf_addstrings(&alphabet_100000, "abcdefghijklmnopqrstuvwxyz", 100000);
+
+ TEST_HASH_STR("",
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ TEST_HASH_STR("a",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
+ TEST_HASH_STR("abc",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+ TEST_HASH_STR("message digest",
+ "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650");
+ TEST_HASH_STR("abcdefghijklmnopqrstuvwxyz",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73");
+ TEST_HASH_STR(aaaaaaaaaa_100000.buf,
+ "34aa973cd4c4daa4f61eeb2bdbad27316534016f",
+ "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+ TEST_HASH_STR(alphabet_100000.buf,
+ "e7da7c55b3484fdf52aebec9cbe7b85a98f02fd4",
+ "e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35");
+ TEST_HASH_LITERAL("blob 0\0",
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813");
+ TEST_HASH_LITERAL("blob 3\0abc",
+ "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f",
+ "c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6");
+ TEST_HASH_LITERAL("tree 0\0",
+ "4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ "6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321");
+
+ strbuf_release(&aaaaaaaaaa_100000);
+ strbuf_release(&alphabet_100000);
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
new file mode 100644
index 0000000000..529049af12
--- /dev/null
+++ b/t/unit-tests/t-reftable-basics.c
@@ -0,0 +1,160 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "test-lib.h"
+#include "reftable/basics.h"
+
+struct integer_needle_lesseq_args {
+ int needle;
+ int *haystack;
+};
+
+static int integer_needle_lesseq(size_t i, void *_args)
+{
+ struct integer_needle_lesseq_args *args = _args;
+ return args->needle <= args->haystack[i];
+}
+
+static void test_binsearch(void)
+{
+ int haystack[] = { 2, 4, 6, 8, 10 };
+ struct {
+ int needle;
+ size_t expected_idx;
+ } testcases[] = {
+ {-9000, 0},
+ {-1, 0},
+ {0, 0},
+ {2, 0},
+ {3, 1},
+ {4, 1},
+ {7, 3},
+ {9, 4},
+ {10, 4},
+ {11, 5},
+ {9000, 5},
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct integer_needle_lesseq_args args = {
+ .haystack = haystack,
+ .needle = testcases[i].needle,
+ };
+ size_t idx;
+
+ idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
+ check_int(idx, ==, testcases[i].expected_idx);
+ }
+}
+
+static void test_names_length(void)
+{
+ char *a[] = { "a", "b", NULL };
+ check_int(names_length(a), ==, 2);
+}
+
+static void test_names_equal(void)
+{
+ char *a[] = { "a", "b", "c", NULL };
+ char *b[] = { "a", "b", "d", NULL };
+ char *c[] = { "a", "b", NULL };
+
+ check(names_equal(a, a));
+ check(!names_equal(a, b));
+ check(!names_equal(a, c));
+}
+
+static void test_parse_names_normal(void)
+{
+ char in1[] = "line\n";
+ char in2[] = "a\nb\nc";
+ char **out = NULL;
+ parse_names(in1, strlen(in1), &out);
+ check_str(out[0], "line");
+ check(!out[1]);
+ free_names(out);
+
+ parse_names(in2, strlen(in2), &out);
+ check_str(out[0], "a");
+ check_str(out[1], "b");
+ check_str(out[2], "c");
+ check(!out[3]);
+ free_names(out);
+}
+
+static void test_parse_names_drop_empty(void)
+{
+ char in[] = "a\n\nb\n";
+ char **out = NULL;
+ parse_names(in, strlen(in), &out);
+ check_str(out[0], "a");
+ /* simply '\n' should be dropped as empty string */
+ check_str(out[1], "b");
+ check(!out[2]);
+ free_names(out);
+}
+
+static void test_common_prefix(void)
+{
+ struct strbuf a = STRBUF_INIT;
+ struct strbuf b = STRBUF_INIT;
+ struct {
+ const char *a, *b;
+ int want;
+ } cases[] = {
+ {"abcdef", "abc", 3},
+ { "abc", "ab", 2 },
+ { "", "abc", 0 },
+ { "abc", "abd", 2 },
+ { "abc", "pqr", 0 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
+ strbuf_addstr(&a, cases[i].a);
+ strbuf_addstr(&b, cases[i].b);
+ check_int(common_prefix_size(&a, &b), ==, cases[i].want);
+ strbuf_reset(&a);
+ strbuf_reset(&b);
+ }
+ strbuf_release(&a);
+ strbuf_release(&b);
+}
+
+static void test_u24_roundtrip(void)
+{
+ uint32_t in = 0x112233;
+ uint8_t dest[3];
+ uint32_t out;
+ put_be24(dest, in);
+ out = get_be24(dest);
+ check_int(in, ==, out);
+}
+
+static void test_u16_roundtrip(void)
+{
+ uint32_t in = 0xfef1;
+ uint8_t dest[3];
+ uint32_t out;
+ put_be16(dest, in);
+ out = get_be16(dest);
+ check_int(in, ==, out);
+}
+
+int cmd_main(int argc, const char *argv[])
+{
+ TEST(test_common_prefix(), "common_prefix_size works");
+ TEST(test_parse_names_normal(), "parse_names works for basic input");
+ TEST(test_parse_names_drop_empty(), "parse_names drops empty string");
+ TEST(test_binsearch(), "binary search with binsearch works");
+ TEST(test_names_length(), "names_length retuns size of a NULL-terminated string array");
+ TEST(test_names_equal(), "names_equal compares NULL-terminated string arrays");
+ TEST(test_u24_roundtrip(), "put_be24 and get_be24 work");
+ TEST(test_u16_roundtrip(), "put_be16 and get_be16 work");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c
index f17fb10d9e..d4615ab06d 100644
--- a/t/unit-tests/t-strvec.c
+++ b/t/unit-tests/t-strvec.c
@@ -4,6 +4,7 @@
#define check_strvec(vec, ...) \
check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__)
+LAST_ARG_MUST_BE_NULL
static void check_strvec_loc(const char *loc, struct strvec *vec, ...)
{
va_list ap;
@@ -22,11 +23,13 @@ static void check_strvec_loc(const char *loc, struct strvec *vec, ...)
strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr);
test_assert(loc, msg.buf, 0);
strbuf_release(&msg);
+ va_end(ap);
return;
}
nr++;
}
+ va_end(ap);
check_uint(vec->nr, ==, nr);
check_uint(vec->alloc, >=, nr);
diff --git a/worktree.c b/worktree.c
index 12eadacc61..70844d023a 100644
--- a/worktree.c
+++ b/worktree.c
@@ -53,6 +53,15 @@ static void add_head_info(struct worktree *wt)
wt->is_detached = 1;
}
+static int is_current_worktree(struct worktree *wt)
+{
+ char *git_dir = absolute_pathdup(get_git_dir());
+ const char *wt_git_dir = get_worktree_git_dir(wt);
+ int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+ free(git_dir);
+ return is_current;
+}
+
/**
* get the main worktree
*/
@@ -76,6 +85,7 @@ static struct worktree *get_main_worktree(int skip_reading_head)
*/
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
+ worktree->is_current = is_current_worktree(worktree);
if (!skip_reading_head)
add_head_info(worktree);
return worktree;
@@ -102,6 +112,7 @@ struct worktree *get_linked_worktree(const char *id,
worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
+ worktree->is_current = is_current_worktree(worktree);
if (!skip_reading_head)
add_head_info(worktree);
@@ -111,23 +122,6 @@ done:
return worktree;
}
-static void mark_current_worktree(struct worktree **worktrees)
-{
- char *git_dir = absolute_pathdup(get_git_dir());
- int i;
-
- for (i = 0; worktrees[i]; i++) {
- struct worktree *wt = worktrees[i];
- const char *wt_git_dir = get_worktree_git_dir(wt);
-
- if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
- wt->is_current = 1;
- break;
- }
- }
- free(git_dir);
-}
-
/*
* NEEDSWORK: This function exists so that we can look up metadata of a
* worktree without trying to access any of its internals like the refdb. It
@@ -164,7 +158,6 @@ static struct worktree **get_worktrees_internal(int skip_reading_head)
ALLOC_GROW(list, counter + 1, alloc);
list[counter] = NULL;
- mark_current_worktree(list);
return list;
}