]> asedeno.scripts.mit.edu Git - git.git/blobdiff - dir.c
send-email: add transfer encoding header with content-type
[git.git] / dir.c
diff --git a/dir.c b/dir.c
index f543f50f42d5bca1a6a6f981e8bb1b3cafd352ea..e9e5f1c277c6133913a8a636814ebe268c6eed3e 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -271,27 +271,34 @@ int excluded(struct dir_struct *dir, const char *pathname)
        return 0;
 }
 
-struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
-{
+static struct dir_entry *dir_entry_new(const char *pathname, int len) {
        struct dir_entry *ent;
 
-       if (cache_name_pos(pathname, len) >= 0)
-               return NULL;
-
-       if (dir->nr == dir->alloc) {
-               int alloc = alloc_nr(dir->alloc);
-               dir->alloc = alloc;
-               dir->entries = xrealloc(dir->entries, alloc*sizeof(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;
-       dir->entries[dir->nr++] = ent;
        return ent;
 }
 
+struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
+{
+       if (cache_name_pos(pathname, len) >= 0)
+               return NULL;
+
+       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,
@@ -424,6 +431,36 @@ 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;
+}
+
+static int get_dtype(struct dirent *de, const char *path)
+{
+       int dtype = DTYPE(de);
+       struct stat st;
+
+       if (dtype != DT_UNKNOWN)
+               return dtype;
+       if (lstat(path, &st))
+               return dtype;
+       if (S_ISREG(st.st_mode))
+               return DT_REG;
+       if (S_ISDIR(st.st_mode))
+               return DT_DIR;
+       if (S_ISLNK(st.st_mode))
+               return DT_LNK;
+       return dtype;
+}
+
 /*
  * Read a directory tree. We currently ignore anything but
  * directories, regular files and symlinks. That's because git
@@ -447,7 +484,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                exclude_stk = push_exclude_per_directory(dir, base, baselen);
 
                while ((de = readdir(fdir)) != NULL) {
-                       int len;
+                       int len, dtype;
                        int exclude;
 
                        if ((de->d_name[0] == '.') &&
@@ -464,24 +501,33 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                continue;
 
                        exclude = excluded(dir, fullname);
-                       if (exclude != dir->show_ignored) {
-                               if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
+                       if (exclude && dir->collect_ignored
+                           && in_pathspec(fullname, baselen + len, simplify))
+                               dir_add_ignored(dir, fullname, baselen + len);
+
+                       /*
+                        * Excluded? If we don't explicitly want to show
+                        * ignored files, ignore it
+                        */
+                       if (exclude && !dir->show_ignored)
+                               continue;
+
+                       dtype = get_dtype(de, fullname);
+
+                       /*
+                        * Do we want to see just the ignored files?
+                        * We still need to recurse into directories,
+                        * even if we don't ignore them, since the
+                        * directory may contain files that we do..
+                        */
+                       if (!exclude && dir->show_ignored) {
+                               if (dtype != DT_DIR)
                                        continue;
-                               }
                        }
 
-                       switch (DTYPE(de)) {
-                       struct stat st;
+                       switch (dtype) {
                        default:
                                continue;
-                       case DT_UNKNOWN:
-                               if (lstat(fullname, &st))
-                                       continue;
-                               if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
-                                       break;
-                               if (!S_ISDIR(st.st_mode))
-                                       continue;
-                               /* fallthrough */
                        case DT_DIR:
                                memcpy(fullname + baselen + len, "/", 2);
                                len++;
@@ -580,6 +626,7 @@ static void free_simplify(struct path_simplify *simplify)
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
        struct path_simplify *simplify = create_simplify(pathspec);
+       char *pp = NULL;
 
        /*
         * Make sure to do the per-directory exclude for all the
@@ -587,7 +634,8 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
         */
        if (baselen) {
                if (dir->exclude_per_dir) {
-                       char *p, *pp = xmalloc(baselen+1);
+                       char *p;
+                       pp = xmalloc(baselen+1);
                        memcpy(pp, base, baselen+1);
                        p = pp;
                        while (1) {
@@ -603,13 +651,14 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
                                else
                                        p = pp + baselen;
                        }
-                       free(pp);
                }
        }
 
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
+       free(pp);
        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;
 }
 
@@ -619,3 +668,58 @@ 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;
+}
+
+void setup_standard_excludes(struct dir_struct *dir)
+{
+       const char *path;
+
+       dir->exclude_per_dir = ".gitignore";
+       path = git_path("info/exclude");
+       if (!access(path, R_OK))
+               add_excludes_from_file(dir, path);
+       if (excludes_file && !access(excludes_file, R_OK))
+               add_excludes_from_file(dir, excludes_file);
+}