Merge branch 'ps/worktree-prune-help-fix'
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index bfa8c8a9a51297c3a969c8706467504cb11ce51b..65c3e681b8e04aa47b6cf41faca0822ead59fb6a 100644 (file)
--- a/dir.c
+++ b/dir.c
 #include "varint.h"
 #include "ewah/ewok.h"
 
-struct path_simplify {
-       int len;
-       const char *path;
-};
-
 /*
  * Tells read_directory_recursive how a file or directory should be treated.
  * Values are ordered by significance, e.g. if a directory contains both
@@ -50,7 +45,7 @@ struct cached_dir {
 
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        const char *path, int len, struct untracked_cache_dir *untracked,
-       int check_only, const struct path_simplify *simplify);
+       int check_only, const struct pathspec *pathspec);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
 int fspathcmp(const char *a, const char *b)
@@ -179,17 +174,21 @@ char *common_prefix(const struct pathspec *pathspec)
 
 int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
 {
-       size_t len;
+       char *prefix;
+       size_t prefix_len;
 
        /*
         * Calculate common prefix for the pathspec, and
         * use that to optimize the directory walk
         */
-       len = common_prefix_len(pathspec);
+       prefix = common_prefix(pathspec);
+       prefix_len = prefix ? strlen(prefix) : 0;
 
        /* Read the directory and prune it */
-       read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec);
-       return len;
+       read_directory(dir, prefix, prefix_len, pathspec);
+
+       free(prefix);
+       return prefix_len;
 }
 
 int within_depth(const char *name, int namelen,
@@ -1312,7 +1311,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
 static enum path_treatment treat_directory(struct dir_struct *dir,
        struct untracked_cache_dir *untracked,
        const char *dirname, int len, int baselen, int exclude,
-       const struct path_simplify *simplify)
+       const struct pathspec *pathspec)
 {
        /* The "len-1" is to strip the final '/' */
        switch (directory_exists_in_index(dirname, len-1)) {
@@ -1341,7 +1340,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
        untracked = lookup_untracked(dir->untracked, untracked,
                                     dirname + baselen, len - baselen);
        return read_directory_recursive(dir, dirname, len,
-                                       untracked, 1, simplify);
+                                       untracked, 1, pathspec);
 }
 
 /*
@@ -1349,24 +1348,33 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
  * reading - if the path cannot possibly be in the pathspec,
  * return true, and we'll skip it early.
  */
-static int simplify_away(const char *path, int pathlen, const struct path_simplify *simplify)
+static int simplify_away(const char *path, int pathlen,
+                        const struct pathspec *pathspec)
 {
-       if (simplify) {
-               for (;;) {
-                       const char *match = simplify->path;
-                       int len = simplify->len;
+       int i;
 
-                       if (!match)
-                               break;
-                       if (len > pathlen)
-                               len = pathlen;
-                       if (!memcmp(path, match, len))
-                               return 0;
-                       simplify++;
-               }
-               return 1;
+       if (!pathspec || !pathspec->nr)
+               return 0;
+
+       GUARD_PATHSPEC(pathspec,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
+
+       for (i = 0; i < pathspec->nr; i++) {
+               const struct pathspec_item *item = &pathspec->items[i];
+               int len = item->nowildcard_len;
+
+               if (len > pathlen)
+                       len = pathlen;
+               if (!ps_strncmp(item, item->match, path, len))
+                       return 0;
        }
-       return 0;
+
+       return 1;
 }
 
 /*
@@ -1380,19 +1388,33 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
  *   2. the path is a directory prefix of some element in the
  *      pathspec
  */
-static int exclude_matches_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;
-                       if (len < simplify->len
-                           && simplify->path[len] == '/'
-                           && !memcmp(path, simplify->path, len))
-                               return 1;
-               }
+static int exclude_matches_pathspec(const char *path, int pathlen,
+                                   const struct pathspec *pathspec)
+{
+       int i;
+
+       if (!pathspec || !pathspec->nr)
+               return 0;
+
+       GUARD_PATHSPEC(pathspec,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
+
+       for (i = 0; i < pathspec->nr; i++) {
+               const struct pathspec_item *item = &pathspec->items[i];
+               int len = item->nowildcard_len;
+
+               if (len == pathlen &&
+                   !ps_strncmp(item, item->match, path, pathlen))
+                       return 1;
+               if (len > pathlen &&
+                   item->match[pathlen] == '/' &&
+                   !ps_strncmp(item, item->match, path, pathlen))
+                       return 1;
        }
        return 0;
 }
@@ -1460,7 +1482,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                                          struct untracked_cache_dir *untracked,
                                          struct strbuf *path,
                                          int baselen,
-                                         const struct path_simplify *simplify,
+                                         const struct pathspec *pathspec,
                                          int dtype, struct dirent *de)
 {
        int exclude;
@@ -1512,7 +1534,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
        case DT_DIR:
                strbuf_addch(path, '/');
                return treat_directory(dir, untracked, path->buf, path->len,
-                                      baselen, exclude, simplify);
+                                      baselen, exclude, pathspec);
        case DT_REG:
        case DT_LNK:
                return exclude ? path_excluded : path_untracked;
@@ -1524,7 +1546,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
                                           struct cached_dir *cdir,
                                           struct strbuf *path,
                                           int baselen,
-                                          const struct path_simplify *simplify)
+                                          const struct pathspec *pathspec)
 {
        strbuf_setlen(path, baselen);
        if (!cdir->ucd) {
@@ -1541,7 +1563,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
                 * with check_only set.
                 */
                return read_directory_recursive(dir, path->buf, path->len,
-                                               cdir->ucd, 1, simplify);
+                                               cdir->ucd, 1, pathspec);
        /*
         * We get path_recurse in the first run when
         * directory_exists_in_index() returns index_nonexistent. We
@@ -1556,23 +1578,23 @@ static enum path_treatment treat_path(struct dir_struct *dir,
                                      struct cached_dir *cdir,
                                      struct strbuf *path,
                                      int baselen,
-                                     const struct path_simplify *simplify)
+                                     const struct pathspec *pathspec)
 {
        int dtype;
        struct dirent *de = cdir->de;
 
        if (!de)
                return treat_path_fast(dir, untracked, cdir, path,
-                                      baselen, simplify);
+                                      baselen, pathspec);
        if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
-       if (simplify_away(path->buf, path->len, simplify))
+       if (simplify_away(path->buf, path->len, pathspec))
                return path_none;
 
        dtype = DTYPE(de);
-       return treat_one_path(dir, untracked, path, baselen, simplify, dtype, de);
+       return treat_one_path(dir, untracked, path, baselen, pathspec, dtype, de);
 }
 
 static void add_untracked(struct untracked_cache_dir *dir, const char *name)
@@ -1703,7 +1725,7 @@ static void close_cached_dir(struct cached_dir *cdir)
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                                    const char *base, int baselen,
                                    struct untracked_cache_dir *untracked, int check_only,
-                                   const struct path_simplify *simplify)
+                                   const struct pathspec *pathspec)
 {
        struct cached_dir cdir;
        enum path_treatment state, subdir_state, dir_state = path_none;
@@ -1719,7 +1741,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 
        while (!read_cached_dir(&cdir)) {
                /* check how the file or directory should be treated */
-               state = treat_path(dir, untracked, &cdir, &path, baselen, simplify);
+               state = treat_path(dir, untracked, &cdir, &path,
+                                  baselen, pathspec);
 
                if (state > dir_state)
                        dir_state = state;
@@ -1731,8 +1754,9 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                                              path.buf + baselen,
                                              path.len - baselen);
                        subdir_state =
-                               read_directory_recursive(dir, path.buf, path.len,
-                                                        ud, check_only, simplify);
+                               read_directory_recursive(dir, path.buf,
+                                                        path.len, ud,
+                                                        check_only, pathspec);
                        if (subdir_state > dir_state)
                                dir_state = subdir_state;
                }
@@ -1756,7 +1780,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                        else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
                                ((dir->flags & DIR_COLLECT_IGNORED) &&
                                exclude_matches_pathspec(path.buf, path.len,
-                                       simplify)))
+                                                        pathspec)))
                                dir_add_ignored(dir, path.buf, path.len);
                        break;
 
@@ -1787,36 +1811,9 @@ static int cmp_name(const void *p1, const void *p2)
        return name_compare(e1->name, e1->len, e2->name, e2->len);
 }
 
-static struct path_simplify *create_simplify(const char **pathspec)
-{
-       int nr, alloc = 0;
-       struct path_simplify *simplify = NULL;
-
-       if (!pathspec)
-               return NULL;
-
-       for (nr = 0 ; ; nr++) {
-               const char *match;
-               ALLOC_GROW(simplify, nr + 1, alloc);
-               match = *pathspec++;
-               if (!match)
-                       break;
-               simplify[nr].path = match;
-               simplify[nr].len = simple_length(match);
-       }
-       simplify[nr].path = NULL;
-       simplify[nr].len = 0;
-       return simplify;
-}
-
-static void free_simplify(struct path_simplify *simplify)
-{
-       free(simplify);
-}
-
 static int treat_leading_path(struct dir_struct *dir,
                              const char *path, int len,
-                             const struct path_simplify *simplify)
+                             const struct pathspec *pathspec)
 {
        struct strbuf sb = STRBUF_INIT;
        int baselen, rc = 0;
@@ -1840,9 +1837,9 @@ static int treat_leading_path(struct dir_struct *dir,
                strbuf_add(&sb, path, baselen);
                if (!is_directory(sb.buf))
                        break;
-               if (simplify_away(sb.buf, sb.len, simplify))
+               if (simplify_away(sb.buf, sb.len, pathspec))
                        break;
-               if (treat_one_path(dir, NULL, &sb, baselen, simplify,
+               if (treat_one_path(dir, NULL, &sb, baselen, pathspec,
                                   DT_DIR, NULL) == path_none)
                        break; /* do not recurse into it */
                if (len <= baselen) {
@@ -2010,33 +2007,14 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
        return root;
 }
 
-int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
+int read_directory(struct dir_struct *dir, const char *path,
+                  int len, const struct pathspec *pathspec)
 {
-       struct path_simplify *simplify;
        struct untracked_cache_dir *untracked;
 
-       /*
-        * Check out create_simplify()
-        */
-       if (pathspec)
-               GUARD_PATHSPEC(pathspec,
-                              PATHSPEC_FROMTOP |
-                              PATHSPEC_MAXDEPTH |
-                              PATHSPEC_LITERAL |
-                              PATHSPEC_GLOB |
-                              PATHSPEC_ICASE |
-                              PATHSPEC_EXCLUDE);
-
        if (has_symlink_leading_path(path, len))
                return dir->nr;
 
-       /*
-        * exclude patterns are treated like positive ones in
-        * create_simplify. Usually exclude patterns should be a
-        * subset of positive ones, which has no impacts on
-        * create_simplify().
-        */
-       simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
        untracked = validate_untracked_cache(dir, len, pathspec);
        if (!untracked)
                /*
@@ -2044,9 +2022,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
                 * e.g. prep_exclude()
                 */
                dir->untracked = NULL;
-       if (!len || treat_leading_path(dir, path, len, simplify))
-               read_directory_recursive(dir, path, len, untracked, 0, simplify);
-       free_simplify(simplify);
+       if (!len || treat_leading_path(dir, path, len, pathspec))
+               read_directory_recursive(dir, path, len, untracked, 0, pathspec);
        QSORT(dir->entries, dir->nr, cmp_name);
        QSORT(dir->ignored, dir->ignored_nr, cmp_name);
        if (dir->untracked) {
@@ -2748,3 +2725,40 @@ void untracked_cache_add_to_index(struct index_state *istate,
 {
        untracked_cache_invalidate_path(istate, path);
 }
+
+/* Update gitfile and core.worktree setting to connect work tree and git dir */
+void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
+{
+       struct strbuf file_name = STRBUF_INIT;
+       struct strbuf rel_path = STRBUF_INIT;
+       char *git_dir = real_pathdup(git_dir_);
+       char *work_tree = real_pathdup(work_tree_);
+
+       /* Update gitfile */
+       strbuf_addf(&file_name, "%s/.git", work_tree);
+       write_file(file_name.buf, "gitdir: %s",
+                  relative_path(git_dir, work_tree, &rel_path));
+
+       /* Update core.worktree setting */
+       strbuf_reset(&file_name);
+       strbuf_addf(&file_name, "%s/config", git_dir);
+       git_config_set_in_file(file_name.buf, "core.worktree",
+                              relative_path(work_tree, git_dir, &rel_path));
+
+       strbuf_release(&file_name);
+       strbuf_release(&rel_path);
+       free(work_tree);
+       free(git_dir);
+}
+
+/*
+ * Migrate the git directory of the given path from old_git_dir to new_git_dir.
+ */
+void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_git_dir)
+{
+       if (rename(old_git_dir, new_git_dir) < 0)
+               die_errno(_("could not migrate git directory from '%s' to '%s'"),
+                       old_git_dir, new_git_dir);
+
+       connect_work_tree_and_git_dir(path, new_git_dir);
+}