refs.c: kill register_ref_store(), add register_submodule_ref_store()
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 9ae454ddec376461a429752b61ea8c34902d4ad9..837ff965a470e74028fce6019f4584e55dcf445c 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -9,6 +9,7 @@
  */
 #include "cache.h"
 #include "dir.h"
+#include "attr.h"
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
@@ -134,7 +135,8 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (n = 0; n < pathspec->nr; n++) {
                size_t i = 0, len = 0, item_len;
@@ -174,17 +176,20 @@ char *common_prefix(const struct pathspec *pathspec)
 
 int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
 {
-       size_t len;
+       const 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_len = common_prefix_len(pathspec);
+       prefix = prefix_len ? pathspec->items[0].match : "";
 
        /* 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);
+
+       return prefix_len;
 }
 
 int within_depth(const char *name, int namelen,
@@ -206,6 +211,36 @@ int within_depth(const char *name, int namelen,
 #define DO_MATCH_DIRECTORY (1<<1)
 #define DO_MATCH_SUBMODULE (1<<2)
 
+static int match_attrs(const char *name, int namelen,
+                      const struct pathspec_item *item)
+{
+       int i;
+
+       git_check_attr(name, item->attr_check);
+       for (i = 0; i < item->attr_match_nr; i++) {
+               const char *value;
+               int matched;
+               enum attr_match_mode match_mode;
+
+               value = item->attr_check->items[i].value;
+               match_mode = item->attr_match[i].match_mode;
+
+               if (ATTR_TRUE(value))
+                       matched = (match_mode == MATCH_SET);
+               else if (ATTR_FALSE(value))
+                       matched = (match_mode == MATCH_UNSET);
+               else if (ATTR_UNSET(value))
+                       matched = (match_mode == MATCH_UNSPECIFIED);
+               else
+                       matched = (match_mode == MATCH_VALUE &&
+                                  !strcmp(item->attr_match[i].value, value));
+               if (!matched)
+                       return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -258,6 +293,9 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
            strncmp(item->match, name - prefix, item->prefix))
                return 0;
 
+       if (item->attr_match_nr && !match_attrs(name, namelen, item))
+               return 0;
+
        /* If the match was just the prefix, we matched */
        if (!*match)
                return MATCHED_RECURSIVELY;
@@ -336,7 +374,8 @@ static int do_match_pathspec(const struct pathspec *ps,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -1358,7 +1397,8 @@ static int simplify_away(const char *path, int pathlen,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (i = 0; i < pathspec->nr; i++) {
                const struct pathspec_item *item = &pathspec->items[i];
@@ -2721,3 +2761,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_, 1);
+       char *work_tree = real_pathdup(work_tree_, 1);
+
+       /* 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);
+}