Skip to content

Commit 36596fd

Browse files
matheustavaresavar
authored andcommitted
clone: better handle symlinked files at .git/objects/
There is currently an odd behaviour when locally cloning a repository with symlinks at .git/objects: using --no-hardlinks all symlinks are dereferenced but without it, Git will try to hardlink the files with the link() function, which has an OS-specific behaviour on symlinks. On OSX and NetBSD, it creates a hardlink to the file pointed by the symlink whilst on GNU/Linux, it creates a hardlink to the symlink itself. On Manjaro GNU/Linux: $ touch a $ ln -s a b $ link b c $ ls -li a b c 155 [...] a 156 [...] b -> a 156 [...] c -> a But on NetBSD: $ ls -li a b c 2609160 [...] a 2609164 [...] b -> a 2609160 [...] c It's not good to have the result of a local clone to be OS-dependent and besides that, the current behaviour on GNU/Linux may result in broken symlinks. So let's standardize this by making the hardlinks always point to dereferenced paths, instead of the symlinks themselves. Also, add tests for symlinked files at .git/objects/. Note: Git won't create symlinks at .git/objects itself, but it's better to handle this case and be friendly with users who manually create them. Signed-off-by: Matheus Tavares <[email protected]> Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Co-authored-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0315616 commit 36596fd

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

builtin/clone.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
442442
if (unlink(dest->buf) && errno != ENOENT)
443443
die_errno(_("failed to unlink '%s'"), dest->buf);
444444
if (!option_no_hardlinks) {
445-
if (!link(src->buf, dest->buf))
445+
if (!link(real_path(src->buf), dest->buf))
446446
continue;
447447
if (option_local > 0)
448448
die_errno(_("failed to create link '%s'"), dest->buf);

t/t5604-clone-reference.sh

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ test_expect_success 'clone a repo with garbage in objects/*' '
266266
test_cmp expected actual
267267
'
268268

269-
test_expect_success SYMLINKS 'setup repo with manually symlinked dirs and unknown files at objects/' '
269+
test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown files at objects/' '
270270
git init T &&
271271
(
272272
cd T &&
@@ -280,18 +280,27 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked dirs and unknow
280280
ln -s packs pack &&
281281
find ?? -type d >loose-dirs &&
282282
last_loose=$(tail -n 1 loose-dirs) &&
283-
rm -f loose-dirs &&
284283
mv $last_loose a-loose-dir &&
285284
ln -s a-loose-dir $last_loose &&
285+
first_loose=$(head -n 1 loose-dirs) &&
286+
rm -f loose-dirs &&
287+
288+
cd $first_loose &&
289+
obj=$(ls *) &&
290+
mv $obj ../an-object &&
291+
ln -s ../an-object $obj &&
292+
293+
cd ../ &&
286294
find . -type f | sort >../../../T.objects-files.raw &&
295+
find . -type l | sort >../../../T.objects-symlinks.raw &&
287296
echo unknown_content >unknown_file
288297
) &&
289298
git -C T fsck &&
290299
git -C T rev-list --all --objects >T.objects
291300
'
292301

293302

294-
test_expect_success SYMLINKS 'clone repo with symlinked dirs and unknown files at objects/' '
303+
test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at objects/' '
295304
for option in --local --no-hardlinks --shared --dissociate
296305
do
297306
git clone $option T T$option || return 1 &&
@@ -300,7 +309,8 @@ test_expect_success SYMLINKS 'clone repo with symlinked dirs and unknown files a
300309
test_cmp T.objects T$option.objects &&
301310
(
302311
cd T$option/.git/objects &&
303-
find . -type f | sort >../../../T$option.objects-files.raw
312+
find . -type f | sort >../../../T$option.objects-files.raw &&
313+
find . -type l | sort >../../../T$option.objects-symlinks.raw
304314
)
305315
done &&
306316
@@ -314,6 +324,7 @@ test_expect_success SYMLINKS 'clone repo with symlinked dirs and unknown files a
314324
./Y/Z
315325
./Y/Z
316326
./a-loose-dir/Z
327+
./an-object
317328
./Y/Z
318329
./info/packs
319330
./pack/pack-Z.idx
@@ -323,13 +334,15 @@ test_expect_success SYMLINKS 'clone repo with symlinked dirs and unknown files a
323334
./unknown_file
324335
EOF
325336
326-
for option in --local --dissociate --no-hardlinks
337+
for option in --local --no-hardlinks --dissociate
327338
do
328-
test_cmp expected-files T$option.objects-files.raw.de-sha || return 1
339+
test_cmp expected-files T$option.objects-files.raw.de-sha || return 1 &&
340+
test_must_be_empty T$option.objects-symlinks.raw.de-sha || return 1
329341
done &&
330342
331343
echo ./info/alternates >expected-files &&
332-
test_cmp expected-files T--shared.objects-files.raw
344+
test_cmp expected-files T--shared.objects-files.raw &&
345+
test_must_be_empty T--shared.objects-symlinks.raw
333346
'
334347

335348
test_done

0 commit comments

Comments
 (0)