*/
#include "cache.h"
#include "cache-tree.h"
+#include "refs.h"
/* Index extensions.
*
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)
{
switch (st->st_mode & S_IFMT) {
if (ce_compare_link(ce, xsize_t(st->st_size)))
return DATA_CHANGED;
break;
+ case S_IFDIR:
+ if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+ return 0;
default:
return TYPE_CHANGED;
}
(has_symlinks || !S_ISREG(st->st_mode)))
changed |= TYPE_CHANGED;
break;
+ case S_IFDIRLNK:
+ if (!S_ISDIR(st->st_mode))
+ changed |= TYPE_CHANGED;
+ else if (ce_compare_gitlink(ce))
+ changed |= DATA_CHANGED;
+ return changed;
default:
die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
}
if (lstat(path, &st))
die("%s: unable to stat (%s)", path, strerror(errno));
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
- die("%s: can only add regular files or symbolic links", path);
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+ die("%s: can only add regular files, symbolic links or git-directories", path);
namelen = strlen(path);
+ if (S_ISDIR(st.st_mode)) {
+ while (namelen && path[namelen-1] == '/')
+ namelen--;
+ }
size = cache_entry_size(namelen);
ce = xcalloc(1, size);
memcpy(ce->name, path, namelen);
continue;
if (p->name[len] != '/')
continue;
+ if (!ce_stage(p) && !p->ce_mode)
+ continue;
retval = -1;
if (!ok_to_replace)
break;
pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage)));
if (pos >= 0) {
- retval = -1;
- if (!ok_to_replace)
- break;
- remove_cache_entry_at(pos);
- continue;
+ /*
+ * Found one, but not so fast. This could
+ * be a marker that says "I was here, but
+ * I am being removed". Such an entry is
+ * not a part of the resulting tree, and
+ * it is Ok to have a directory at the same
+ * path.
+ */
+ if (stage || active_cache[pos]->ce_mode) {
+ retval = -1;
+ if (!ok_to_replace)
+ break;
+ remove_cache_entry_at(pos);
+ continue;
+ }
}
+ else
+ pos = -pos-1;
/*
* Trivial optimization: if we find an entry that
* already matches the sub-directory, then we know
* we're ok, and we can exit.
*/
- pos = -pos-1;
while (pos < active_nr) {
struct cache_entry *p = active_cache[pos];
if ((ce_namelen(p) <= len) ||
(p->name[len] != '/') ||
memcmp(p->name, name, len))
break; /* not our subdirectory */
- if (ce_stage(p) == stage)
+ if (ce_stage(p) == stage && (stage || p->ce_mode))
/* p is at the same stage as our entry, and
* is a subdirectory of what we are looking
* at, so we cannot have conflicts at our
*/
static int check_file_directory_conflict(const struct cache_entry *ce, int pos, int ok_to_replace)
{
+ int retval;
+
+ /*
+ * When ce is an "I am going away" entry, we allow it to be added
+ */
+ if (!ce_stage(ce) && !ce->ce_mode)
+ return 0;
+
/*
* We check if the path is a sub-path of a subsequent pathname
* first, since removing those will not change the position
- * in the array
+ * in the array.
*/
- int retval = has_file_name(ce, pos, ok_to_replace);
+ retval = has_file_name(ce, pos, ok_to_replace);
+
/*
* Then check if the path might have a clashing sub-directory
* before it.