@ -5,6 +5,7 @@
*/
*/
#include "cache.h"
#include "cache.h"
#include "cache-tree.h"
#include "cache-tree.h"
#include "refs.h"
/* Index extensions.
/* Index extensions.
*
*
@ -91,6 +92,23 @@ static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
return match;
return match;
}
}
static int ce_compare_gitlink(struct cache_entry *ce)
{
unsigned char sha1[20];
/*
* We don't actually require that the .git directory
* under DIRLNK directory be a valid git directory. It
* might even be missing (in case nobody populated that
* sub-project).
*
* If so, we consider it always to match.
*/
if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
return 0;
return hashcmp(sha1, ce->sha1);
}
static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
{
{
switch (st->st_mode & S_IFMT) {
switch (st->st_mode & S_IFMT) {
@ -102,6 +120,9 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
if (ce_compare_link(ce, xsize_t(st->st_size)))
if (ce_compare_link(ce, xsize_t(st->st_size)))
return DATA_CHANGED;
return DATA_CHANGED;
break;
break;
case S_IFDIRLNK:
/* No need to do anything, we did the exact compare in "match_stat_basic" */
break;
default:
default:
return TYPE_CHANGED;
return TYPE_CHANGED;
}
}
@ -127,6 +148,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
(has_symlinks || !S_ISREG(st->st_mode)))
(has_symlinks || !S_ISREG(st->st_mode)))
changed |= TYPE_CHANGED;
changed |= TYPE_CHANGED;
break;
break;
case S_IFDIRLNK:
if (!S_ISDIR(st->st_mode))
changed |= TYPE_CHANGED;
else if (ce_compare_gitlink(ce))
changed |= DATA_CHANGED;
break;
default:
default:
die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
}
}
@ -250,9 +277,9 @@ int base_name_compare(const char *name1, int len1, int mode1,
return cmp;
return cmp;
c1 = name1[len];
c1 = name1[len];
c2 = name2[len];
c2 = name2[len];
if (!c1 && S_ISDIR(mode1))
if (!c1 && (S_ISDIR(mode1) || S_ISDIRLNK(mode1)))
c1 = '/';
c1 = '/';
if (!c2 && S_ISDIR(mode2))
if (!c2 && (S_ISDIR(mode2) || S_ISDIRLNK(mode2)))
c2 = '/';
c2 = '/';
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
}
@ -334,8 +361,8 @@ int add_file_to_cache(const char *path, int verbose)
if (lstat(path, &st))
if (lstat(path, &st))
die("%s: unable to stat (%s)", path, strerror(errno));
die("%s: unable to stat (%s)", path, strerror(errno));
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
die("%s: can only add regular files or symbolic links", path);
die("%s: can only add regular files, symbolic links or git-directories", path);
namelen = strlen(path);
namelen = strlen(path);
size = cache_entry_size(namelen);
size = cache_entry_size(namelen);