clone: teach --recurse-submodules to optionally take a pathspec
[gitweb.git] / attr.c
diff --git a/attr.c b/attr.c
index bcee0921d4e6ae15f43564ac9c21dae3a034561d..5493bff224a98361811c1e9fa88bb16efbf85d22 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -677,26 +677,30 @@ static struct attr_stack *read_attr_from_array(const char **list)
 }
 
 /*
- * NEEDSWORK: these two are tricky.  The callers assume there is a
- * single, system-wide global state "where we read attributes from?"
- * and when the state is flipped by calling git_attr_set_direction(),
- * attr_stack is discarded so that subsequent attr_check will lazily
- * read from the right place.  And they do not know or care who called
- * by them uses the attribute subsystem, hence have no knowledge of
- * existing git_attr_check instances or future ones that will be
- * created).
- *
- * Probably we need a thread_local that holds these two variables,
- * and a list of git_attr_check instances (which need to be maintained
- * by hooking into git_attr_check_alloc(), git_attr_check_initl(), and
- * git_attr_check_clear().  Then git_attr_set_direction() updates the
- * fields in that thread_local for these two variables, iterate over
- * all the active git_attr_check instances and discard the attr_stack
- * they hold.  Yuck, but it sounds doable.
+ * Callers into the attribute system assume there is a single, system-wide
+ * global state where attributes are read from and when the state is flipped by
+ * calling git_attr_set_direction(), the stack frames that have been
+ * constructed need to be discarded so so that subsequent calls into the
+ * attribute system will lazily read from the right place.  Since changing
+ * direction causes a global paradigm shift, it should not ever be called while
+ * another thread could potentially be calling into the attribute system.
  */
 static enum git_attr_direction direction;
 static struct index_state *use_index;
 
+void git_attr_set_direction(enum git_attr_direction new_direction,
+                           struct index_state *istate)
+{
+       if (is_bare_repository() && new_direction != GIT_ATTR_INDEX)
+               die("BUG: non-INDEX attr direction in a bare repo");
+
+       if (new_direction != direction)
+               drop_all_attr_stacks();
+
+       direction = new_direction;
+       use_index = istate;
+}
+
 static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
 {
        FILE *fp = fopen(path, "r");
@@ -747,25 +751,28 @@ static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
 
 static struct attr_stack *read_attr(const char *path, int macro_ok)
 {
-       struct attr_stack *res;
+       struct attr_stack *res = NULL;
 
-       if (direction == GIT_ATTR_CHECKOUT) {
+       if (direction == GIT_ATTR_INDEX) {
                res = read_attr_from_index(path, macro_ok);
-               if (!res)
-                       res = read_attr_from_file(path, macro_ok);
-       }
-       else if (direction == GIT_ATTR_CHECKIN) {
-               res = read_attr_from_file(path, macro_ok);
-               if (!res)
-                       /*
-                        * There is no checked out .gitattributes file there, but
-                        * we might have it in the index.  We allow operation in a
-                        * sparsely checked out work tree, so read from it.
-                        */
+       } else if (!is_bare_repository()) {
+               if (direction == GIT_ATTR_CHECKOUT) {
                        res = read_attr_from_index(path, macro_ok);
+                       if (!res)
+                               res = read_attr_from_file(path, macro_ok);
+               } else if (direction == GIT_ATTR_CHECKIN) {
+                       res = read_attr_from_file(path, macro_ok);
+                       if (!res)
+                               /*
+                                * There is no checked out .gitattributes file
+                                * there, but we might have it in the index.
+                                * We allow operation in a sparsely checked out
+                                * work tree, so read from it.
+                                */
+                               res = read_attr_from_index(path, macro_ok);
+               }
        }
-       else
-               res = read_attr_from_index(path, macro_ok);
+
        if (!res)
                res = xcalloc(1, sizeof(*res));
        return res;
@@ -857,10 +864,7 @@ static void bootstrap_attr_stack(struct attr_stack **stack)
        }
 
        /* root directory */
-       if (!is_bare_repository() || direction == GIT_ATTR_INDEX)
-               e = read_attr(GITATTRIBUTES_FILE, 1);
-       else
-               e = xcalloc(1, sizeof(struct attr_stack));
+       e = read_attr(GITATTRIBUTES_FILE, 1);
        push_stack(stack, e, xstrdup(""), 0);
 
        /* info frame */
@@ -877,6 +881,7 @@ static void prepare_attr_stack(const char *path, int dirlen,
                               struct attr_stack **stack)
 {
        struct attr_stack *info;
+       struct strbuf pathbuf = STRBUF_INIT;
 
        /*
         * At the bottom of the attribute stack is the built-in
@@ -923,54 +928,47 @@ static void prepare_attr_stack(const char *path, int dirlen,
        }
 
        /*
-        * Read from parent directories and push them down
+        * bootstrap_attr_stack() should have added, and the
+        * above loop should have stopped before popping, the
+        * root element whose attr_stack->origin is set to an
+        * empty string.
         */
-       if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
-               /*
-                * bootstrap_attr_stack() should have added, and the
-                * above loop should have stopped before popping, the
-                * root element whose attr_stack->origin is set to an
-                * empty string.
-                */
-               struct strbuf pathbuf = STRBUF_INIT;
-
-               assert((*stack)->origin);
-               strbuf_addstr(&pathbuf, (*stack)->origin);
-               /* Build up to the directory 'path' is in */
-               while (pathbuf.len < dirlen) {
-                       size_t len = pathbuf.len;
-                       struct attr_stack *next;
-                       char *origin;
-
-                       /* Skip path-separator */
-                       if (len < dirlen && is_dir_sep(path[len]))
-                               len++;
-                       /* Find the end of the next component */
-                       while (len < dirlen && !is_dir_sep(path[len]))
-                               len++;
-
-                       if (pathbuf.len > 0)
-                               strbuf_addch(&pathbuf, '/');
-                       strbuf_add(&pathbuf, path + pathbuf.len,
-                                  (len - pathbuf.len));
-                       strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
-
-                       next = read_attr(pathbuf.buf, 0);
-
-                       /* reset the pathbuf to not include "/.gitattributes" */
-                       strbuf_setlen(&pathbuf, len);
-
-                       origin = xstrdup(pathbuf.buf);
-                       push_stack(stack, next, origin, len);
-
-               }
-               strbuf_release(&pathbuf);
+       assert((*stack)->origin);
+
+       strbuf_addstr(&pathbuf, (*stack)->origin);
+       /* Build up to the directory 'path' is in */
+       while (pathbuf.len < dirlen) {
+               size_t len = pathbuf.len;
+               struct attr_stack *next;
+               char *origin;
+
+               /* Skip path-separator */
+               if (len < dirlen && is_dir_sep(path[len]))
+                       len++;
+               /* Find the end of the next component */
+               while (len < dirlen && !is_dir_sep(path[len]))
+                       len++;
+
+               if (pathbuf.len > 0)
+                       strbuf_addch(&pathbuf, '/');
+               strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
+               strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
+
+               next = read_attr(pathbuf.buf, 0);
+
+               /* reset the pathbuf to not include "/.gitattributes" */
+               strbuf_setlen(&pathbuf, len);
+
+               origin = xstrdup(pathbuf.buf);
+               push_stack(stack, next, origin, len);
        }
 
        /*
         * Finally push the "info" one at the top of the stack.
         */
        push_stack(stack, info, NULL, 0);
+
+       strbuf_release(&pathbuf);
 }
 
 static int path_matches(const char *pathname, int pathlen,
@@ -1154,19 +1152,6 @@ void git_all_attrs(const char *path, struct attr_check *check)
        }
 }
 
-void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
-{
-       enum git_attr_direction old = direction;
-
-       if (is_bare_repository() && new != GIT_ATTR_INDEX)
-               die("BUG: non-INDEX attr direction in a bare repo");
-
-       direction = new;
-       if (new != old)
-               drop_all_attr_stacks();
-       use_index = istate;
-}
-
 void attr_start(void)
 {
 #ifndef NO_PTHREADS