Merge branch 'nd/maint-setup'
authorJunio C Hamano <gitster@pobox.com>
Mon, 2 May 2011 22:58:30 +0000 (15:58 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 2 May 2011 22:58:30 +0000 (15:58 -0700)
* nd/maint-setup:
Kill off get_relative_cwd()
setup: return correct prefix if worktree is '/'

Conflicts:
dir.c
setup.c

1  2 
dir.c
dir.h
setup.c
diff --combined dir.c
index 532bcb65b523223b66efd3f4e458f62fcbe0d6a5,522a6fe9da5e95b44f19b5ffa804ce44d9c52cc7..9789bd204ef33c0623e6576c5afd533786fcd406
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -87,21 -87,6 +87,21 @@@ int fill_directory(struct dir_struct *d
        return len;
  }
  
 +int within_depth(const char *name, int namelen,
 +                      int depth, int max_depth)
 +{
 +      const char *cp = name, *cpe = name + namelen;
 +
 +      while (cp < cpe) {
 +              if (*cp++ != '/')
 +                      continue;
 +              depth++;
 +              if (depth > max_depth)
 +                      return 0;
 +      }
 +      return 1;
 +}
 +
  /*
   * Does 'match' match the given name?
   * A match is found if
@@@ -199,95 -184,6 +199,95 @@@ int match_pathspec(const char **pathspe
        return retval;
  }
  
 +/*
 + * Does 'match' match the given name?
 + * A match is found if
 + *
 + * (1) the 'match' string is leading directory of 'name', or
 + * (2) the 'match' string is a wildcard and matches 'name', or
 + * (3) the 'match' string is exactly the same as 'name'.
 + *
 + * and the return value tells which case it was.
 + *
 + * It returns 0 when there is no match.
 + */
 +static int match_pathspec_item(const struct pathspec_item *item, int prefix,
 +                             const char *name, int namelen)
 +{
 +      /* name/namelen has prefix cut off by caller */
 +      const char *match = item->match + prefix;
 +      int matchlen = item->len - prefix;
 +
 +      /* If the match was just the prefix, we matched */
 +      if (!*match)
 +              return MATCHED_RECURSIVELY;
 +
 +      if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
 +              if (matchlen == namelen)
 +                      return MATCHED_EXACTLY;
 +
 +              if (match[matchlen-1] == '/' || name[matchlen] == '/')
 +                      return MATCHED_RECURSIVELY;
 +      }
 +
 +      if (item->has_wildcard && !fnmatch(match, name, 0))
 +              return MATCHED_FNMATCH;
 +
 +      return 0;
 +}
 +
 +/*
 + * Given a name and a list of pathspecs, see if the name matches
 + * any of the pathspecs.  The caller is also interested in seeing
 + * all pathspec matches some names it calls this function with
 + * (otherwise the user could have mistyped the unmatched pathspec),
 + * and a mark is left in seen[] array for pathspec element that
 + * actually matched anything.
 + */
 +int match_pathspec_depth(const struct pathspec *ps,
 +                       const char *name, int namelen,
 +                       int prefix, char *seen)
 +{
 +      int i, retval = 0;
 +
 +      if (!ps->nr) {
 +              if (!ps->recursive || ps->max_depth == -1)
 +                      return MATCHED_RECURSIVELY;
 +
 +              if (within_depth(name, namelen, 0, ps->max_depth))
 +                      return MATCHED_EXACTLY;
 +              else
 +                      return 0;
 +      }
 +
 +      name += prefix;
 +      namelen -= prefix;
 +
 +      for (i = ps->nr - 1; i >= 0; i--) {
 +              int how;
 +              if (seen && seen[i] == MATCHED_EXACTLY)
 +                      continue;
 +              how = match_pathspec_item(ps->items+i, prefix, name, namelen);
 +              if (ps->recursive && ps->max_depth != -1 &&
 +                  how && how != MATCHED_FNMATCH) {
 +                      int len = ps->items[i].len;
 +                      if (name[len] == '/')
 +                              len++;
 +                      if (within_depth(name+len, namelen-len, 0, ps->max_depth))
 +                              how = MATCHED_EXACTLY;
 +                      else
 +                              how = 0;
 +              }
 +              if (how) {
 +                      if (retval < how)
 +                              retval = how;
 +                      if (seen && seen[i] < how)
 +                              seen[i] = how;
 +              }
 +      }
 +      return retval;
 +}
 +
  static int no_wildcard(const char *string)
  {
        return string[strcspn(string, "*?[{\\")] == '\0';
@@@ -1105,57 -1001,45 +1105,45 @@@ int file_exists(const char *f
  }
  
  /*
-  * 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".
+  * Given two normalized paths (a trailing slash is ok), if subdir is
+  * outside dir, return -1.  Otherwise return the offset in subdir that
+  * can be used as relative path to dir.
   */
char *get_relative_cwd(char *buffer, int size, const char *dir)
int dir_inside_of(const char *subdir, const char *dir)
  {
-       char *cwd = buffer;
-       if (!dir)
-               return NULL;
-       if (!getcwd(buffer, size))
-               die_errno("can't find the current directory");
+       int offset = 0;
  
-       if (!is_absolute_path(dir))
-               dir = real_path(dir);
+       assert(dir && subdir && *dir && *subdir);
  
-       while (*dir && *dir == *cwd) {
+       while (*dir && *subdir && *dir == *subdir) {
                dir++;
-               cwd++;
-       }
-       if (*dir)
-               return NULL;
-       switch (*cwd) {
-       case '\0':
-               return cwd;
-       case '/':
-               return cwd + 1;
-       default:
-               /*
-                * dir can end with a path separator when it's root
-                * directory. Return proper prefix in that case.
-                */
-               if (dir[-1] == '/')
-                       return cwd;
-               return NULL;
+               subdir++;
+               offset++;
        }
+       /* hel[p]/me vs hel[l]/yeah */
+       if (*dir && *subdir)
+               return -1;
+       if (!*subdir)
+               return !*dir ? offset : -1; /* same dir */
+       /* foo/[b]ar vs foo/[] */
+       if (is_dir_sep(dir[-1]))
+               return is_dir_sep(subdir[-1]) ? offset : -1;
+       /* foo[/]bar vs foo[] */
+       return is_dir_sep(*subdir) ? offset + 1 : -1;
  }
  
  int is_inside_dir(const char *dir)
  {
-       char buffer[PATH_MAX];
-       return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
+       char cwd[PATH_MAX];
+       if (!dir)
+               return 0;
+       if (!getcwd(cwd, sizeof(cwd)))
+               die_errno("can't find the current directory");
+       return dir_inside_of(cwd, dir) >= 0;
  }
  
  int is_empty_dir(const char *path)
@@@ -1192,7 -1076,7 +1180,7 @@@ int remove_dir_recursively(struct strbu
  
        dir = opendir(path->buf);
        if (!dir)
 -              return -1;
 +              return rmdir(path->buf);
        if (path->buf[original_len - 1] != '/')
                strbuf_addch(path, '/');
  
@@@ -1255,50 -1139,3 +1243,50 @@@ int remove_path(const char *name
        return 0;
  }
  
 +static int pathspec_item_cmp(const void *a_, const void *b_)
 +{
 +      struct pathspec_item *a, *b;
 +
 +      a = (struct pathspec_item *)a_;
 +      b = (struct pathspec_item *)b_;
 +      return strcmp(a->match, b->match);
 +}
 +
 +int init_pathspec(struct pathspec *pathspec, const char **paths)
 +{
 +      const char **p = paths;
 +      int i;
 +
 +      memset(pathspec, 0, sizeof(*pathspec));
 +      if (!p)
 +              return 0;
 +      while (*p)
 +              p++;
 +      pathspec->raw = paths;
 +      pathspec->nr = p - paths;
 +      if (!pathspec->nr)
 +              return 0;
 +
 +      pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
 +      for (i = 0; i < pathspec->nr; i++) {
 +              struct pathspec_item *item = pathspec->items+i;
 +              const char *path = paths[i];
 +
 +              item->match = path;
 +              item->len = strlen(path);
 +              item->has_wildcard = !no_wildcard(path);
 +              if (item->has_wildcard)
 +                      pathspec->has_wildcard = 1;
 +      }
 +
 +      qsort(pathspec->items, pathspec->nr,
 +            sizeof(struct pathspec_item), pathspec_item_cmp);
 +
 +      return 0;
 +}
 +
 +void free_pathspec(struct pathspec *pathspec)
 +{
 +      free(pathspec->items);
 +      pathspec->items = NULL;
 +}
diff --combined dir.h
index aa511da77b0ec54cbdbd3786faad0c1285df7182,1cf271098a9c346b273f742da09143a6d3d91c50..433b5b4cd4c51e9b7557d3056570ed46b7ceba92
--- 1/dir.h
--- 2/dir.h
+++ b/dir.h
@@@ -65,10 -65,6 +65,10 @@@ struct dir_struct 
  #define MATCHED_FNMATCH 2
  #define MATCHED_EXACTLY 3
  extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 +extern int match_pathspec_depth(const struct pathspec *pathspec,
 +                              const char *name, int namelen,
 +                              int prefix, char *seen);
 +extern int within_depth(const char *name, int namelen, int depth, int max_depth);
  
  extern int fill_directory(struct dir_struct *dir, const char **pathspec);
  extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
@@@ -85,8 -81,8 +85,8 @@@ extern void add_exclude(const char *str
  extern void free_excludes(struct exclude_list *el);
  extern int file_exists(const char *);
  
- extern char *get_relative_cwd(char *buffer, int size, const char *dir);
  extern int is_inside_dir(const char *dir);
+ extern int dir_inside_of(const char *subdir, const char *dir);
  
  static inline int is_dot_or_dotdot(const char *name)
  {
diff --combined setup.c
index 03cd84f2fcbf9cdbc25116308a9c2c9407af8e71,f0468c6864195e0ff9532f9ec5251d5288b38397..b6e6b5ae272b3ecae7dfa2cb446aef97e826884a
+++ b/setup.c
@@@ -7,13 -7,10 +7,13 @@@ static int inside_work_tree = -1
  char *prefix_path(const char *prefix, int len, const char *path)
  {
        const char *orig = path;
 -      char *sanitized = xmalloc(len + strlen(path) + 1);
 -      if (is_absolute_path(orig))
 -              strcpy(sanitized, path);
 -      else {
 +      char *sanitized;
 +      if (is_absolute_path(orig)) {
 +              const char *temp = real_path(path);
 +              sanitized = xmalloc(len + strlen(temp) + 1);
 +              strcpy(sanitized, temp);
 +      } else {
 +              sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
@@@ -221,7 -218,7 +221,7 @@@ void setup_work_tree(void
        work_tree = get_git_work_tree();
        git_dir = get_git_dir();
        if (!is_absolute_path(git_dir))
 -              git_dir = make_absolute_path(git_dir);
 +              git_dir = real_path(get_git_dir());
        if (!work_tree || chdir(work_tree))
                die("This operation must be run in a work tree");
  
        if (getenv(GIT_WORK_TREE_ENVIRONMENT))
                setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
  
 -      set_git_dir(make_relative_path(git_dir, work_tree));
 +      set_git_dir(relative_path(git_dir, work_tree));
        initialized = 1;
  }
  
@@@ -312,7 -309,7 +312,7 @@@ const char *read_gitfile_gently(const c
  
        if (!is_git_directory(dir))
                die("Not a git repository: %s", dir);
 -      path = make_absolute_path(dir);
 +      path = real_path(dir);
  
        free(buf);
        return path;
@@@ -325,6 -322,7 +325,7 @@@ static const char *setup_explicit_git_d
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        const char *worktree;
        char *gitfile;
+       int offset;
  
        if (PATH_MAX - 40 < strlen(gitdirenv))
                die("'$%s' too big", GIT_DIR_ENVIRONMENT);
                return NULL;
        }
  
-       if (!prefixcmp(cwd, worktree) &&
-           cwd[strlen(worktree)] == '/') { /* cwd inside worktree */
+       offset = dir_inside_of(cwd, worktree);
+       if (offset >= 0) {      /* cwd inside worktree? */
 -              set_git_dir(make_absolute_path(gitdirenv));
 +              set_git_dir(real_path(gitdirenv));
                if (chdir(worktree))
                        die_errno("Could not chdir to '%s'", worktree);
                cwd[len++] = '/';
                cwd[len] = '\0';
                free(gitfile);
-               return cwd + strlen(worktree) + 1;
+               return cwd + offset;
        }
  
        /* cwd outside worktree */
@@@ -417,7 -415,7 +418,7 @@@ static const char *setup_discovered_git
        /* --work-tree is set without --git-dir; use discovered one */
        if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
                if (offset != len && !is_absolute_path(gitdir))
 -                      gitdir = xstrdup(make_absolute_path(gitdir));
 +                      gitdir = xstrdup(real_path(gitdir));
                if (chdir(cwd))
                        die_errno("Could not come back to cwd");
                return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
  
        /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
        if (is_bare_repository_cfg > 0) {
 -              set_git_dir(offset == len ? gitdir : make_absolute_path(gitdir));
 +              set_git_dir(offset == len ? gitdir : real_path(gitdir));
                if (chdir(cwd))
                        die_errno("Could not come back to cwd");
                return NULL;