Skip to content

Commit f80218b

Browse files
pcloudsgitster
authored andcommitted
clone: report duplicate entries on case-insensitive filesystems
Paths that only differ in case work fine in a case-sensitive filesystems, but if those repos are cloned in a case-insensitive one, you'll get problems. The first thing to notice is "git status" will never be clean with no indication what exactly is "dirty". This patch helps the situation a bit by pointing out the problem at clone time. Even though this patch talks about case sensitivity, the patch makes no assumption about folding rules by the filesystem. It simply observes that if an entry has been already checked out at clone time when we're about to write a new path, some folding rules are behind this. This patch is tested with vim-colorschemes repository on a JFS partition with case insensitive support on Linux. This repository has two files darkBlue.vim and darkblue.vim. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ffc6fa0 commit f80218b

File tree

6 files changed

+66
-1
lines changed

6 files changed

+66
-1
lines changed

builtin/clone.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ static int checkout(int submodule_progress)
747747
memset(&opts, 0, sizeof opts);
748748
opts.update = 1;
749749
opts.merge = 1;
750+
opts.clone = 1;
750751
opts.fn = oneway_merge;
751752
opts.verbose_update = (option_verbosity >= 0);
752753
opts.src_index = &the_index;

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,7 @@ struct checkout {
14551455
unsigned force:1,
14561456
quiet:1,
14571457
not_new:1,
1458+
clone:1,
14581459
refresh_cache:1;
14591460
};
14601461
#define CHECKOUT_INIT { NULL, "" }

entry.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,31 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen)
399399
return lstat(path, st);
400400
}
401401

402+
static void mark_colliding_entries(const struct checkout *state,
403+
struct cache_entry *ce, struct stat *st)
404+
{
405+
int i;
406+
407+
ce->ce_flags |= CE_MATCHED;
408+
409+
#if !defined(GIT_WINDOWS_NATIVE) /* inode is always zero on Windows */
410+
for (i = 0; i < state->istate->cache_nr; i++) {
411+
struct cache_entry *dup = state->istate->cache[i];
412+
413+
if (dup == ce)
414+
break;
415+
416+
if (dup->ce_flags & (CE_MATCHED | CE_VALID | CE_SKIP_WORKTREE))
417+
continue;
418+
419+
if (dup->ce_stat_data.sd_ino == st->st_ino) {
420+
dup->ce_flags |= CE_MATCHED;
421+
break;
422+
}
423+
}
424+
#endif
425+
}
426+
402427
/*
403428
* Write the contents from ce out to the working tree.
404429
*
@@ -455,6 +480,9 @@ int checkout_entry(struct cache_entry *ce,
455480
return -1;
456481
}
457482

483+
if (state->clone)
484+
mark_colliding_entries(state, ce, &st);
485+
458486
/*
459487
* We unlink the old file, to get the new one with the
460488
* right permissions (including umask, which is nasty

t/t5601-clone.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,16 @@ test_expect_success 'clone on case-insensitive fs' '
624624
git hash-object -w -t tree --stdin) &&
625625
c=$(git commit-tree -m bogus $t) &&
626626
git update-ref refs/heads/bogus $c &&
627-
git clone -b bogus . bogus
627+
git clone -b bogus . bogus 2>warning
628628
)
629629
'
630630

631+
test_expect_success !MINGW,!CYGWIN,CASE_INSENSITIVE_FS 'colliding file detection' '
632+
grep X icasefs/warning &&
633+
grep x icasefs/warning &&
634+
test_i18ngrep "the following paths have collided" icasefs/warning
635+
'
636+
631637
partial_clone () {
632638
SERVER="$1" &&
633639
URL="$2" &&

unpack-trees.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,12 @@ static int check_updates(struct unpack_trees_options *o)
359359
state.refresh_cache = 1;
360360
state.istate = index;
361361

362+
if (o->clone) {
363+
state.clone = 1;
364+
for (i = 0; i < index->cache_nr; i++)
365+
index->cache[i]->ce_flags &= ~CE_MATCHED;
366+
}
367+
362368
progress = get_progress(o);
363369

364370
if (o->update)
@@ -423,6 +429,28 @@ static int check_updates(struct unpack_trees_options *o)
423429
errs |= finish_delayed_checkout(&state);
424430
if (o->update)
425431
git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
432+
433+
if (o->clone) {
434+
int printed_warning = 0;
435+
436+
for (i = 0; i < index->cache_nr; i++) {
437+
struct cache_entry *ce = index->cache[i];
438+
439+
if (!(ce->ce_flags & CE_MATCHED))
440+
continue;
441+
442+
if (!printed_warning) {
443+
warning(_("the following paths have collided (e.g. case-sensitive paths\n"
444+
"on a case-insensitive filesystem) and only one from the same\n"
445+
"colliding group is in the working tree:\n"));
446+
printed_warning = 1;
447+
}
448+
449+
fprintf(stderr, " '%s'\n", ce->name);
450+
ce->ce_flags &= ~CE_MATCHED;
451+
}
452+
}
453+
426454
return errs != 0;
427455
}
428456

unpack-trees.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct unpack_trees_options {
4242
unsigned int reset,
4343
merge,
4444
update,
45+
clone,
4546
index_only,
4647
nontrivial_merge,
4748
trivial_merges_only,

0 commit comments

Comments
 (0)