git clone -c core.symlinks=true does not work
Asked Answered
L

2

8

I try to clone a repository with submodules and symlinks. I'm on Windows 10 and clone onto an NTFS SSD.

I'm cloning with

$git clone -c core.symlinks=true --recurse-submodules -b develop [email protected]/my-project.git

When cloned, the config contains the line symlinks = false and the symlinks are not created.

I have to use

$git config core.symlinks true

$git reset --hard

after cloning to create the symlinks.

Why does it not work when cloning directly?

Locule answered 18/4, 2018 at 7:16 Comment(2)
Try git -c core.symlinks=true clone --recurse-submodules -b develop [email protected]/my-project.git. -c name=value should be between git and the sub-command.Stpierre
The issue should not happen again with Git 2.21+ (Q1 2019). See my answer belowCharmain
S
4

When the git clone code creates the new (empty) repository, it uses the init_db function, which ends with a block including this code:

/* Check if symlink is supported in the work tree */
path = git_path_buf(&buf, "tXXXXXX");
if (!close(xmkstemp(path)) &&
    !unlink(path) &&
    !symlink("testing", path) &&
    !lstat(path, &st1) &&
    S_ISLNK(st1.st_mode))
        unlink(path); /* good */
else
        git_config_set("core.symlinks", "false");

Since you are getting an explicit core.symlinks = false, this line must be overriding your request to set core.symlinks = true. Note that the setting depends on the actual OS support, rather than any command line option. However, if you move the work-tree elsewhere later, the explicit setting remains in the .git/config file.

Meanwhile, there are two other issues. One is what ElpieKay mentioned in a comment: For most commands, you can temporarily override the configuration using git -c name=value subcommand, which would be git -c core.symlinks=true clone arguments. This is worth trying, in case your particular Git version is out of date and does not match the code I am quoting and linking-to here.

However, git clone is quite special: it's essentially an optional mkdir to create the repository and work-tree directories, followed by a git init, followed by some configuration setting, followed by a git fetch and a git checkout. So this means you can use git -c name=value clone -c name=value .... The first name=value setting is in use while the repository gets created, i.e., during git init. The second name=value is the one that git clone drops into the new clone, using this bit of code:

init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);

if (real_git_dir)
        git_dir = real_git_dir;

write_config(&option_config);

git_config(git_default_config, NULL);

The write_config function is where Git writes your core.symlinks=true from your git clone -c core.symlinks=true ... command. So this in theory should override the core.symlinks=false setting that init_db computed. This is the part I find curious: that it doesn't.

The subsequent git_config call reads the actual config written into the new repository's .git/config. This will set the internal has_symlinks variable, which will control the internal git checkout that git clone runs. So if this did override the init_db setting, everything would work just the way you want. What's not obvious is why it doesn't override. The code here is a bit twisty, but it seems like your command-line option should override the init_db setting.

(Of course, if symlinks actually work in the work-tree, the init_db code should not be setting core.symlinks=false in the first place!)

Sonnnie answered 18/4, 2018 at 15:30 Comment(0)
C
1

That should be fixed with Git 2.21 (Q1 2019)

Refspecs configured with "git -c var=val clone" did not propagate to the resulting repository, which has been corrected.

See commit 7eae4a3, commit 515be83, commit 3e42cb3 (14 Nov 2018) by SZEDER Gábor (szeder).
(Merged by Junio C Hamano -- gitster -- in commit 84d1783, 04 Jan 2019)

clone: respect additional configured fetch refspecs during initial fetch

The initial fetch during a clone doesn't transfer refs matching additional fetch refspecs given on the command line as configuration variables, e.g. '-c remote.origin.fetch=<refspec>'.
This contradicts the documentation stating that configuration variables specified via 'git clone -c <key>=<value> ...' "take effect immediately after the repository is initialized, but before the remote history is fetched" and the given example specifically mentions "adding additional fetch refspecs to the origin remote".

Furthermore, one-shot configuration variables specified via 'git -c <key>=<value> clone ...', though not written to the newly created repository's config file, live during the lifetime of the 'clone' command, including the initial fetch.

All this implies that any fetch refspecs specified this way should already be taken into account during the initial fetch.

The reason for this is that the initial fetch is not a fully fledged 'git fetch' but a bunch of direct calls into the fetch/transport machinery with clone's own refs-to-refspec matching logic, which bypasses parts of 'git fetch' processing configured fetch refspecs.
This logic only considers a single default refspec, potentially influenced by options like '--single-branch' and '--mirror'.

The configured refspecs are, however, already read and parsed properly when clone calls remote.c:remote_get(), but it never looks at the parsed refspecs in the resulting 'struct remote'.

Modify clone to take the remote's configured fetch refspecs into account to retrieve all matching refs during the initial fetch.

Note that we have to explicitly add the default fetch refspec to the remote's refspecs, because at that point the remote only includes the fetch refspecs specified on the command line.

Add tests to check that refspecs given both via 'git clone -c ...' and 'git -c ... clone' retrieve all refs matching either the default or the additional refspecs, and that it works even when the user specifies an alternative remote name via '--origin=<name>'.

Caveat:

Due to limitations in the current implementation, some configuration variables specified via 'git clone -c var=val' (or 'git -c var=val clone') are ignored during the initial fetch and checkout.

Let the users know which configuration variables are known to be ignored ('remote.origin.mirror' and 'remote.origin.tagOpt') under the documentation of 'git clone -c', along with hints to use the options '--mirror' and '--no-tags' instead.

The man page for git clone now includes:

Due to limitations of the current implementation, some configuration variables do not take effect until after the initial fetch and checkout.

Configuration variables known to not take effect are:

  • remote.<name>.mirror
  • remote.<name>.tagOpt.

Use the corresponding --mirror and --no-tags options instead.

Charmain answered 6/1, 2019 at 3:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.