fetch/push: readd rsync support
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 5ba6030e9a89102a8fcf0e7311d80082841de67b..b18257e65ffaf440eb0944c42624e19052178374 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -275,7 +275,6 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) {
        struct dir_entry *ent;
 
        ent = xmalloc(sizeof(*ent) + len + 1);
-       ent->ignored = ent->ignored_dir = 0;
        ent->len = len;
        memcpy(ent->name, pathname, len);
        ent->name[len] = 0;
@@ -287,10 +286,19 @@ struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int
        if (cache_name_pos(pathname, len) >= 0)
                return NULL;
 
-       ALLOC_GROW(dir->entries, dir->nr, dir->alloc);
+       ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
        return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
 }
 
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+{
+       if (cache_name_pos(pathname, len) >= 0)
+               return NULL;
+
+       ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
+       return dir->ignored[dir->ignored_nr++] = dir_entry_new(pathname, len);
+}
+
 enum exist_status {
        index_nonexistent = 0,
        index_directory,
@@ -423,6 +431,18 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
        return 0;
 }
 
+static int in_pathspec(const char *path, int len, const struct path_simplify *simplify)
+{
+       if (simplify) {
+               for (; simplify->path; simplify++) {
+                       if (len == simplify->len
+                           && !memcmp(path, simplify->path, len))
+                               return 1;
+               }
+       }
+       return 0;
+}
+
 /*
  * Read a directory tree. We currently ignore anything but
  * directories, regular files and symlinks. That's because git
@@ -463,6 +483,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                continue;
 
                        exclude = excluded(dir, fullname);
+                       if (exclude && dir->collect_ignored
+                           && in_pathspec(fullname, baselen + len, simplify))
+                               dir_add_ignored(dir, fullname, baselen + len);
                        if (exclude != dir->show_ignored) {
                                if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
                                        continue;
@@ -609,6 +632,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
+       qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
        return dir->nr;
 }
 
@@ -618,3 +642,87 @@ file_exists(const char *f)
   struct stat sb;
   return stat(f, &sb) == 0;
 }
+
+/*
+ * get_relative_cwd() gets the prefix of the current working directory
+ * relative to 'dir'.  If we are not inside 'dir', it returns NULL.
+ *
+ * As a convenience, it also returns NULL if 'dir' is already NULL.  The
+ * reason for this behaviour is that it is natural for functions returning
+ * directory names to return NULL to say "this directory does not exist"
+ * or "this directory is invalid".  These cases are usually handled the
+ * same as if the cwd is not inside 'dir' at all, so get_relative_cwd()
+ * returns NULL for both of them.
+ *
+ * Most notably, get_relative_cwd(buffer, size, get_git_work_tree())
+ * unifies the handling of "outside work tree" with "no work tree at all".
+ */
+char *get_relative_cwd(char *buffer, int size, const char *dir)
+{
+       char *cwd = buffer;
+
+       if (!dir)
+               return NULL;
+       if (!getcwd(buffer, size))
+               die("can't find the current directory: %s", strerror(errno));
+
+       if (!is_absolute_path(dir))
+               dir = make_absolute_path(dir);
+
+       while (*dir && *dir == *cwd) {
+               dir++;
+               cwd++;
+       }
+       if (*dir)
+               return NULL;
+       if (*cwd == '/')
+               return cwd + 1;
+       return cwd;
+}
+
+int is_inside_dir(const char *dir)
+{
+       char buffer[PATH_MAX];
+       return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
+}
+
+int remove_dir_recursively(struct strbuf *path, int only_empty)
+{
+       DIR *dir = opendir(path->buf);
+       struct dirent *e;
+       int ret = 0, original_len = path->len, len;
+
+       if (!dir)
+               return -1;
+       if (path->buf[original_len - 1] != '/')
+               strbuf_addch(path, '/');
+
+       len = path->len;
+       while ((e = readdir(dir)) != NULL) {
+               struct stat st;
+               if ((e->d_name[0] == '.') &&
+                   ((e->d_name[1] == 0) ||
+                    ((e->d_name[1] == '.') && e->d_name[2] == 0)))
+                       continue; /* "." and ".." */
+
+               strbuf_setlen(path, len);
+               strbuf_addstr(path, e->d_name);
+               if (lstat(path->buf, &st))
+                       ; /* fall thru */
+               else if (S_ISDIR(st.st_mode)) {
+                       if (!remove_dir_recursively(path, only_empty))
+                               continue; /* happy */
+               } else if (!only_empty && !unlink(path->buf))
+                       continue; /* happy, too */
+
+               /* path too long, stat fails, or non-directory still exists */
+               ret = -1;
+               break;
+       }
+       closedir(dir);
+
+       strbuf_setlen(path, original_len);
+       if (!ret)
+               ret = rmdir(path->buf);
+       return ret;
+}