OSDN Git Service

Merge branch 'mk/submodule-gitdir-path'
authorJunio C Hamano <gitster@pobox.com>
Thu, 15 Oct 2015 22:43:31 +0000 (15:43 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 15 Oct 2015 22:43:32 +0000 (15:43 -0700)
The submodule code has been taught to work better with separate
work trees created via "git worktree add".

* mk/submodule-gitdir-path:
  path: implement common_dir handling in git_pathdup_submodule()
  submodule refactor: use strbuf_git_path_submodule() in add_submodule_odb()

1  2 
cache.h
path.c

diff --cc cache.h
Simple merge
diff --cc path.c
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -91,256 -91,43 +91,259 @@@ static void replace_dir(struct strbuf *
                buf->buf[newlen] = '/';
  }
  
 -static const char *common_list[] = {
 -      "/branches", "/hooks", "/info", "!/logs", "/lost-found",
 -      "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn",
 -      "config", "!gc.pid", "packed-refs", "shallow",
 -      NULL
 +struct common_dir {
 +      /* Not considered garbage for report_linked_checkout_garbage */
 +      unsigned ignore_garbage:1;
 +      unsigned is_dir:1;
 +      /* Not common even though its parent is */
 +      unsigned exclude:1;
 +      const char *dirname;
  };
  
 -static void update_common_dir(struct strbuf *buf, int git_dir_len, const char *common_dir)
 +static struct common_dir common_list[] = {
 +      { 0, 1, 0, "branches" },
 +      { 0, 1, 0, "hooks" },
 +      { 0, 1, 0, "info" },
 +      { 0, 0, 1, "info/sparse-checkout" },
 +      { 1, 1, 0, "logs" },
 +      { 1, 1, 1, "logs/HEAD" },
 +      { 0, 1, 1, "logs/refs/bisect" },
 +      { 0, 1, 0, "lost-found" },
 +      { 0, 1, 0, "objects" },
 +      { 0, 1, 0, "refs" },
 +      { 0, 1, 1, "refs/bisect" },
 +      { 0, 1, 0, "remotes" },
 +      { 0, 1, 0, "worktrees" },
 +      { 0, 1, 0, "rr-cache" },
 +      { 0, 1, 0, "svn" },
 +      { 0, 0, 0, "config" },
 +      { 1, 0, 0, "gc.pid" },
 +      { 0, 0, 0, "packed-refs" },
 +      { 0, 0, 0, "shallow" },
 +      { 0, 0, 0, NULL }
 +};
 +
 +/*
 + * A compressed trie.  A trie node consists of zero or more characters that
 + * are common to all elements with this prefix, optionally followed by some
 + * children.  If value is not NULL, the trie node is a terminal node.
 + *
 + * For example, consider the following set of strings:
 + * abc
 + * def
 + * definite
 + * definition
 + *
 + * The trie would look look like:
 + * root: len = 0, children a and d non-NULL, value = NULL.
 + *    a: len = 2, contents = bc, value = (data for "abc")
 + *    d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
 + *       i: len = 3, contents = nit, children e and i non-NULL, value = NULL
 + *           e: len = 0, children all NULL, value = (data for "definite")
 + *           i: len = 2, contents = on, children all NULL,
 + *              value = (data for "definition")
 + */
 +struct trie {
 +      struct trie *children[256];
 +      int len;
 +      char *contents;
 +      void *value;
 +};
 +
 +static struct trie *make_trie_node(const char *key, void *value)
  {
 -      char *base = buf->buf + git_dir_len;
 -      const char **p;
 -
 -      if (is_dir_file(base, "logs", "HEAD") ||
 -          is_dir_file(base, "info", "sparse-checkout"))
 -              return; /* keep this in $GIT_DIR */
 -      for (p = common_list; *p; p++) {
 -              const char *path = *p;
 -              int is_dir = 0;
 -              if (*path == '!')
 -                      path++;
 -              if (*path == '/') {
 -                      path++;
 -                      is_dir = 1;
 +      struct trie *new_node = xcalloc(1, sizeof(*new_node));
 +      new_node->len = strlen(key);
 +      if (new_node->len) {
 +              new_node->contents = xmalloc(new_node->len);
 +              memcpy(new_node->contents, key, new_node->len);
 +      }
 +      new_node->value = value;
 +      return new_node;
 +}
 +
 +/*
 + * Add a key/value pair to a trie.  The key is assumed to be \0-terminated.
 + * If there was an existing value for this key, return it.
 + */
 +static void *add_to_trie(struct trie *root, const char *key, void *value)
 +{
 +      struct trie *child;
 +      void *old;
 +      int i;
 +
 +      if (!*key) {
 +              /* we have reached the end of the key */
 +              old = root->value;
 +              root->value = value;
 +              return old;
 +      }
 +
 +      for (i = 0; i < root->len; i++) {
 +              if (root->contents[i] == key[i])
 +                      continue;
 +
 +              /*
 +               * Split this node: child will contain this node's
 +               * existing children.
 +               */
 +              child = malloc(sizeof(*child));
 +              memcpy(child->children, root->children, sizeof(root->children));
 +
 +              child->len = root->len - i - 1;
 +              if (child->len) {
 +                      child->contents = xstrndup(root->contents + i + 1,
 +                                                 child->len);
                }
 +              child->value = root->value;
 +              root->value = NULL;
 +              root->len = i;
  
 -              if (!common_dir)
 -                      common_dir = get_git_common_dir();
 +              memset(root->children, 0, sizeof(root->children));
 +              root->children[(unsigned char)root->contents[i]] = child;
  
 -              if (is_dir && dir_prefix(base, path)) {
 -                      replace_dir(buf, git_dir_len, common_dir);
 -                      return;
 +              /* This is the newly-added child. */
 +              root->children[(unsigned char)key[i]] =
 +                      make_trie_node(key + i + 1, value);
 +              return NULL;
 +      }
 +
 +      /* We have matched the entire compressed section */
 +      if (key[i]) {
 +              child = root->children[(unsigned char)key[root->len]];
 +              if (child) {
 +                      return add_to_trie(child, key + root->len + 1, value);
 +              } else {
 +                      child = make_trie_node(key + root->len + 1, value);
 +                      root->children[(unsigned char)key[root->len]] = child;
 +                      return NULL;
                }
 -              if (!is_dir && !strcmp(base, path)) {
 -                      replace_dir(buf, git_dir_len, common_dir);
 -                      return;
 +      }
 +
 +      old = root->value;
 +      root->value = value;
 +      return old;
 +}
 +
 +typedef int (*match_fn)(const char *unmatched, void *data, void *baton);
 +
 +/*
 + * Search a trie for some key.  Find the longest /-or-\0-terminated
 + * prefix of the key for which the trie contains a value.  Call fn
 + * with the unmatched portion of the key and the found value, and
 + * return its return value.  If there is no such prefix, return -1.
 + *
 + * The key is partially normalized: consecutive slashes are skipped.
 + *
 + * For example, consider the trie containing only [refs,
 + * refs/worktree] (both with values).
 + *
 + * | key             | unmatched  | val from node | return value |
 + * |-----------------|------------|---------------|--------------|
 + * | a               | not called | n/a           | -1           |
 + * | refs            | \0         | refs          | as per fn    |
 + * | refs/           | /          | refs          | as per fn    |
 + * | refs/w          | /w         | refs          | as per fn    |
 + * | refs/worktree   | \0         | refs/worktree | as per fn    |
 + * | refs/worktree/  | /          | refs/worktree | as per fn    |
 + * | refs/worktree/a | /a         | refs/worktree | as per fn    |
 + * |-----------------|------------|---------------|--------------|
 + *
 + */
 +static int trie_find(struct trie *root, const char *key, match_fn fn,
 +                   void *baton)
 +{
 +      int i;
 +      int result;
 +      struct trie *child;
 +
 +      if (!*key) {
 +              /* we have reached the end of the key */
 +              if (root->value && !root->len)
 +                      return fn(key, root->value, baton);
 +              else
 +                      return -1;
 +      }
 +
 +      for (i = 0; i < root->len; i++) {
 +              /* Partial path normalization: skip consecutive slashes. */
 +              if (key[i] == '/' && key[i+1] == '/') {
 +                      key++;
 +                      continue;
                }
 +              if (root->contents[i] != key[i])
 +                      return -1;
        }
- static void update_common_dir(struct strbuf *buf, int git_dir_len)
 +
 +      /* Matched the entire compressed section */
 +      key += i;
 +      if (!*key)
 +              /* End of key */
 +              return fn(key, root->value, baton);
 +
 +      /* Partial path normalization: skip consecutive slashes */
 +      while (key[0] == '/' && key[1] == '/')
 +              key++;
 +
 +      child = root->children[(unsigned char)*key];
 +      if (child)
 +              result = trie_find(child, key + 1, fn, baton);
 +      else
 +              result = -1;
 +
 +      if (result >= 0 || (*key != '/' && *key != 0))
 +              return result;
 +      if (root->value)
 +              return fn(key, root->value, baton);
 +      else
 +              return -1;
 +}
 +
 +static struct trie common_trie;
 +static int common_trie_done_setup;
 +
 +static void init_common_trie(void)
 +{
 +      struct common_dir *p;
 +
 +      if (common_trie_done_setup)
 +              return;
 +
 +      for (p = common_list; p->dirname; p++)
 +              add_to_trie(&common_trie, p->dirname, p);
 +
 +      common_trie_done_setup = 1;
 +}
 +
 +/*
 + * Helper function for update_common_dir: returns 1 if the dir
 + * prefix is common.
 + */
 +static int check_common(const char *unmatched, void *value, void *baton)
 +{
 +      struct common_dir *dir = value;
 +
 +      if (!dir)
 +              return 0;
 +
 +      if (dir->is_dir && (unmatched[0] == 0 || unmatched[0] == '/'))
 +              return !dir->exclude;
 +
 +      if (!dir->is_dir && unmatched[0] == 0)
 +              return !dir->exclude;
 +
 +      return 0;
 +}
 +
-               replace_dir(buf, git_dir_len, get_git_common_dir());
++static void update_common_dir(struct strbuf *buf, int git_dir_len,
++                            const char *common_dir)
 +{
 +      char *base = buf->buf + git_dir_len;
 +      init_common_trie();
++      if (!common_dir)
++              common_dir = get_git_common_dir();
 +      if (trie_find(&common_trie, base, check_common, NULL) > 0)
++              replace_dir(buf, git_dir_len, common_dir);
  }
  
  void report_linked_checkout_garbage(void)
@@@ -648,7 -445,7 +661,7 @@@ const char *enter_repo(const char *path
                }
                if (!suffix[i])
                        return NULL;
--              gitfile = read_gitfile(used_path) ;
++              gitfile = read_gitfile(used_path);
                if (gitfile)
                        strcpy(used_path, gitfile);
                if (chdir(used_path))