diff mbox series

builtin/init-db: handle bare clones when core.bare set to false

Message ID 20210308131718.546055-1-sandals@crustytoothpaste.net (mailing list archive)
State Superseded
Headers show
Series builtin/init-db: handle bare clones when core.bare set to false | expand

Commit Message

brian m. carlson March 8, 2021, 1:17 p.m. UTC
In 552955ed7f ("clone: use more conventional config/option layering",
2020-10-01), clone learned to read configuration options earlier in its
execution, before creating the new repository.  However, that led to a
problem: if the core.bare setting is set to false in the global config,
cloning a bare repository segfaults.  This happens because the
repository is falsely thought to be non-bare, but clone has set the work
tree to NULL, which is then dereferenced.

The code to initialize the repository already considers the fact that a
user might want to override the --bare option for git init, but it
doesn't take into account clone, which uses a different option.  Let's
just check that the work tree is not NULL, since that's how clone
indicates that the repository is bare.  This is also the case for git
init, so we won't be regressing that case.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
It appears from my reading of the init code that we did intentionally
already honor core.bare in some case, so I have not implemented ignoring
that here and instead went for the limited approach.

 builtin/init-db.c        | 4 ++--
 t/t5606-clone-options.sh | 7 +++++++
 2 files changed, 9 insertions(+), 2 deletions(-)

Comments

Eric Sunshine March 8, 2021, 4:43 p.m. UTC | #1
On Mon, Mar 8, 2021 at 8:18 AM brian m. carlson
<sandals@crustytoothpaste.net> wrote:
> In 552955ed7f ("clone: use more conventional config/option layering",
> 2020-10-01), clone learned to read configuration options earlier in its
> execution, before creating the new repository.  However, that led to a
> problem: if the core.bare setting is set to false in the global config,
> cloning a bare repository segfaults.  This happens because the
> repository is falsely thought to be non-bare, but clone has set the work
> tree to NULL, which is then dereferenced.
> [...]
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> ---

Perhaps this deserves a:

    Reported-by: Joseph Vusich <jvusich@amazon.com>

> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
> @@ -104,6 +104,13 @@ test_expect_success 'redirected clone -v does show progress' '
> +test_expect_success 'clone does not segfault with --bare and core.bare=false' '
> +       test_config_global core.bare false &&
> +       git clone --bare "file://$(pwd)/parent" clone-bare &&

Can this be done more simply as:

    git clone --bare parent clone-bare &&

or even:

    git clone --bare . clone-bare &&

without mucking about with $(pwd)?

> +       git -C clone-bare rev-parse --is-bare-repository >is-bare &&
> +       test "$(cat is-bare)" = true

These days, we'd probably say:

    echo true >expect &&
    git -C clone-bare rev-parse --is-bare-repository >actual &&
    test_cmp expect actual

but it's subjective and minor; not at all worth a re-roll.
brian m. carlson March 8, 2021, 11:22 p.m. UTC | #2
On 2021-03-08 at 16:43:58, Eric Sunshine wrote:
> On Mon, Mar 8, 2021 at 8:18 AM brian m. carlson
> <sandals@crustytoothpaste.net> wrote:
> > In 552955ed7f ("clone: use more conventional config/option layering",
> > 2020-10-01), clone learned to read configuration options earlier in its
> > execution, before creating the new repository.  However, that led to a
> > problem: if the core.bare setting is set to false in the global config,
> > cloning a bare repository segfaults.  This happens because the
> > repository is falsely thought to be non-bare, but clone has set the work
> > tree to NULL, which is then dereferenced.
> > [...]
> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> > ---
> 
> Perhaps this deserves a:
> 
>     Reported-by: Joseph Vusich <jvusich@amazon.com>

Good point.  Will fix.

> > diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
> > @@ -104,6 +104,13 @@ test_expect_success 'redirected clone -v does show progress' '
> > +test_expect_success 'clone does not segfault with --bare and core.bare=false' '
> > +       test_config_global core.bare false &&
> > +       git clone --bare "file://$(pwd)/parent" clone-bare &&
> 
> Can this be done more simply as:
> 
>     git clone --bare parent clone-bare &&
> 
> or even:
> 
>     git clone --bare . clone-bare &&
> 
> without mucking about with $(pwd)?

Sure.  I pulled the line from the test above, but I agree that's nicer.

> > +       git -C clone-bare rev-parse --is-bare-repository >is-bare &&
> > +       test "$(cat is-bare)" = true
> 
> These days, we'd probably say:
> 
>     echo true >expect &&
>     git -C clone-bare rev-parse --is-bare-repository >actual &&
>     test_cmp expect actual
> 
> but it's subjective and minor; not at all worth a re-roll.

There's enough nits to warrant a v2, so I can do one.
diff mbox series

Patch

diff --git a/builtin/init-db.c b/builtin/init-db.c
index dcc45bef51..f82efe4aff 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -212,6 +212,7 @@  static int create_default_files(const char *template_path,
 	int reinit;
 	int filemode;
 	struct strbuf err = STRBUF_INIT;
+	const char *work_tree = get_git_work_tree();
 
 	/* Just look for `init.templatedir` */
 	init_db_template_dir = NULL; /* re-set in case it was set before */
@@ -235,7 +236,7 @@  static int create_default_files(const char *template_path,
 	 * We must make sure command-line options continue to override any
 	 * values we might have just re-read from the config.
 	 */
-	is_bare_repository_cfg = init_is_bare_repository;
+	is_bare_repository_cfg = init_is_bare_repository || !work_tree;
 	if (init_shared_repository != -1)
 		set_shared_repository(init_shared_repository);
 
@@ -299,7 +300,6 @@  static int create_default_files(const char *template_path,
 	if (is_bare_repository())
 		git_config_set("core.bare", "true");
 	else {
-		const char *work_tree = get_git_work_tree();
 		git_config_set("core.bare", "false");
 		/* allow template config file to override the default */
 		if (log_all_ref_updates == LOG_REFS_UNSET)
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index 52e5789fb0..9b62708d76 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -104,6 +104,13 @@  test_expect_success 'redirected clone -v does show progress' '
 
 '
 
+test_expect_success 'clone does not segfault with --bare and core.bare=false' '
+	test_config_global core.bare false &&
+	git clone --bare "file://$(pwd)/parent" clone-bare &&
+	git -C clone-bare rev-parse --is-bare-repository >is-bare &&
+	test "$(cat is-bare)" = true
+'
+
 test_expect_success 'chooses correct default initial branch name' '
 	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
 	git -c init.defaultBranch=foo init --bare empty &&