+/*
+ * Handle a path that was a directory. Four cases:
+ *
+ * - it's already a gitlink in the index, and we keep it that
+ * way, and update it if we can (if we cannot find the HEAD,
+ * we're going to keep it unchanged in the index!)
+ *
+ * - it's a *file* in the index, in which case it should be
+ * removed as a file if removal is allowed, since it doesn't
+ * exist as such any more. If removal isn't allowed, it's
+ * an error.
+ *
+ * (NOTE! This is old and arguably fairly strange behaviour.
+ * We might want to make this an error unconditionally, and
+ * use "--force-remove" if you actually want to force removal).
+ *
+ * - it used to exist as a subdirectory (ie multiple files with
+ * this particular prefix) in the index, in which case it's wrong
+ * to try to update it as a directory.
+ *
+ * - it doesn't exist at all in the index, but it is a valid
+ * git directory, and it should be *added* as a gitlink.
+ */
+static int process_directory(const char *path, int len, struct stat *st)
+{
+ unsigned char sha1[20];
+ int pos = cache_name_pos(path, len);
+
+ /* Exact match: file or existing gitlink */
+ if (pos >= 0) {
+ struct cache_entry *ce = active_cache[pos];
+ if (S_ISDIRLNK(ntohl(ce->ce_mode))) {
+
+ /* Do nothing to the index if there is no HEAD! */
+ if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
+ return 0;
+
+ return add_one_path(ce, path, len, st);
+ }
+ /* Should this be an unconditional error? */
+ return remove_one_path(path);
+ }
+
+ /* Inexact match: is there perhaps a subdirectory match? */
+ pos = -pos-1;
+ while (pos < active_nr) {
+ struct cache_entry *ce = active_cache[pos++];
+
+ if (strncmp(ce->name, path, len))
+ break;
+ if (ce->name[len] > '/')
+ break;
+ if (ce->name[len] < '/')
+ continue;
+
+ /* Subdirectory match - error out */
+ return error("%s: is a directory - add individual files instead", path);
+ }
+
+ /* No match - should we add it as a gitlink? */
+ if (!resolve_gitlink_ref(path, "HEAD", sha1))
+ return add_one_path(NULL, path, len, st);
+
+ /* Error out. */
+ return error("%s: is a directory - add files inside instead", path);
+}
+
+/*
+ * Process a regular file
+ */
+static int process_file(const char *path, int len, struct stat *st)
+{
+ int pos = cache_name_pos(path, len);
+ struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
+
+ if (ce && S_ISDIRLNK(ntohl(ce->ce_mode)))
+ return error("%s is already a gitlink, not replacing", path);
+
+ return add_one_path(ce, path, len, st);
+}
+
+static int process_path(const char *path)
+{
+ int len;
+ struct stat st;
+
+ /* We probably want to do this in remove_file_from_cache() and
+ * add_cache_entry() instead...
+ */
+ cache_tree_invalidate_path(active_cache_tree, path);
+
+ /*
+ * First things first: get the stat information, to decide
+ * what to do about the pathname!
+ */
+ if (lstat(path, &st) < 0)
+ return process_lstat_error(path, errno);
+
+ len = strlen(path);
+ if (S_ISDIR(st.st_mode))
+ return process_directory(path, len, &st);
+
+ return process_file(path, len, &st);
+}
+