Code

Merge branch 'dr/ceiling'
authorJunio C Hamano <gitster@pobox.com>
Mon, 7 Jul 2008 09:17:23 +0000 (02:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 7 Jul 2008 09:17:23 +0000 (02:17 -0700)
* dr/ceiling:
  Eliminate an unnecessary chdir("..")
  Add support for GIT_CEILING_DIRECTORIES
  Fold test-absolute-path into test-path-utils
  Implement normalize_absolute_path

Conflicts:

cache.h
setup.c

1  2 
.gitignore
Documentation/git.txt
Makefile
cache.h
path.c
setup.c
t/test-lib.sh

diff --cc .gitignore
Simple merge
Simple merge
diff --cc Makefile
Simple merge
diff --cc cache.h
index 96c43884cccd1c5703aedd88d48bb145c5d6c07d,833f4cd982e419902c0376b0e5917e272640336e..0d8eddac778ace8e4b5f58459f508ac1192dbcff
+++ b/cache.h
@@@ -298,7 -298,9 +298,8 @@@ static inline enum object_type object_t
  #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
  #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
  #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 -#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+ #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
@@@ -521,11 -512,11 +522,13 @@@ int safe_create_leading_directories_con
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
 -      return path[0] == '/';
 +      return path[0] == '/' || has_dos_drive_prefix(path);
  }
  const char *make_absolute_path(const char *path);
 +const char *make_nonrelative_path(const char *path);
 +const char *make_relative_path(const char *abs, const char *base);
+ int normalize_absolute_path(char *buf, const char *path);
+ int longest_ancestor_length(const char *path, const char *prefix_list);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
diff --cc path.c
index 496123ca552a3aad32b009c668962045ec78d218,0c4330486bb83b38c14a356cebb488f4419b93fb..598325598bba8685d18d24c6331562cdf63fc05f
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -291,55 -291,165 +291,151 @@@ int adjust_shared_perm(const char *path
        return 0;
  }
  
 -/* We allow "recursive" symbolic links. Only within reason, though. */
 -#define MAXDEPTH 5
 -
 -const char *make_absolute_path(const char *path)
 +static const char *get_pwd_cwd(void)
  {
 -      static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
 -      char cwd[1024] = "";
 -      int buf_index = 1, len;
 -
 -      int depth = MAXDEPTH;
 -      char *last_elem = NULL;
 -      struct stat st;
 -
 -      if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 -              die ("Too long path: %.*s", 60, path);
 -
 -      while (depth--) {
 -              if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
 -                      char *last_slash = strrchr(buf, '/');
 -                      if (last_slash) {
 -                              *last_slash = '\0';
 -                              last_elem = xstrdup(last_slash + 1);
 -                      } else {
 -                              last_elem = xstrdup(buf);
 -                              *buf = '\0';
 -                      }
 +      static char cwd[PATH_MAX + 1];
 +      char *pwd;
 +      struct stat cwd_stat, pwd_stat;
 +      if (getcwd(cwd, PATH_MAX) == NULL)
 +              return NULL;
 +      pwd = getenv("PWD");
 +      if (pwd && strcmp(pwd, cwd)) {
 +              stat(cwd, &cwd_stat);
 +              if (!stat(pwd, &pwd_stat) &&
 +                  pwd_stat.st_dev == cwd_stat.st_dev &&
 +                  pwd_stat.st_ino == cwd_stat.st_ino) {
 +                      strlcpy(cwd, pwd, PATH_MAX);
                }
 +      }
 +      return cwd;
 +}
  
 -              if (*buf) {
 -                      if (!*cwd && !getcwd(cwd, sizeof(cwd)))
 -                              die ("Could not get current working directory");
 -
 -                      if (chdir(buf))
 -                              die ("Could not switch to '%s'", buf);
 -              }
 -              if (!getcwd(buf, PATH_MAX))
 -                      die ("Could not get current working directory");
 -
 -              if (last_elem) {
 -                      int len = strlen(buf);
 -                      if (len + strlen(last_elem) + 2 > PATH_MAX)
 -                              die ("Too long path name: '%s/%s'",
 -                                              buf, last_elem);
 -                      buf[len] = '/';
 -                      strcpy(buf + len + 1, last_elem);
 -                      free(last_elem);
 -                      last_elem = NULL;
 -              }
 +const char *make_nonrelative_path(const char *path)
 +{
 +      static char buf[PATH_MAX + 1];
  
 -              if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
 -                      len = readlink(buf, next_buf, PATH_MAX);
 -                      if (len < 0)
 -                              die ("Invalid symlink: %s", buf);
 -                      next_buf[len] = '\0';
 -                      buf = next_buf;
 -                      buf_index = 1 - buf_index;
 -                      next_buf = bufs[buf_index];
 -              } else
 -                      break;
 +      if (is_absolute_path(path)) {
 +              if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 +                      die ("Too long path: %.*s", 60, path);
 +      } else {
 +              const char *cwd = get_pwd_cwd();
 +              if (!cwd)
 +                      die("Cannot determine the current working directory");
 +              if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
 +                      die ("Too long path: %.*s", 60, path);
        }
 +      return buf;
 +}
  
 -      if (*cwd && chdir(cwd))
 -              die ("Could not change back to '%s'", cwd);
 -
 +const char *make_relative_path(const char *abs, const char *base)
 +{
 +      static char buf[PATH_MAX + 1];
 +      int baselen;
 +      if (!base)
 +              return abs;
 +      baselen = strlen(base);
 +      if (prefixcmp(abs, base))
 +              return abs;
 +      if (abs[baselen] == '/')
 +              baselen++;
 +      else if (base[baselen - 1] != '/')
 +              return abs;
 +      strcpy(buf, abs + baselen);
        return buf;
  }
+ /*
+  * path = absolute path
+  * buf = buffer of at least max(2, strlen(path)+1) bytes
+  * It is okay if buf == path, but they should not overlap otherwise.
+  *
+  * Performs the following normalizations on path, storing the result in buf:
+  * - Removes trailing slashes.
+  * - Removes empty components.
+  * - Removes "." components.
+  * - Removes ".." components, and the components the precede them.
+  * "" and paths that contain only slashes are normalized to "/".
+  * Returns the length of the output.
+  *
+  * Note that this function is purely textual.  It does not follow symlinks,
+  * verify the existence of the path, or make any system calls.
+  */
+ int normalize_absolute_path(char *buf, const char *path)
+ {
+       const char *comp_start = path, *comp_end = path;
+       char *dst = buf;
+       int comp_len;
+       assert(buf);
+       assert(path);
+       while (*comp_start) {
+               assert(*comp_start == '/');
+               while (*++comp_end && *comp_end != '/')
+                       ; /* nothing */
+               comp_len = comp_end - comp_start;
+               if (!strncmp("/",  comp_start, comp_len) ||
+                   !strncmp("/.", comp_start, comp_len))
+                       goto next;
+               if (!strncmp("/..", comp_start, comp_len)) {
+                       while (dst > buf && *--dst != '/')
+                               ; /* nothing */
+                       goto next;
+               }
+               memcpy(dst, comp_start, comp_len);
+               dst += comp_len;
+       next:
+               comp_start = comp_end;
+       }
+       if (dst == buf)
+               *dst++ = '/';
+       *dst = '\0';
+       return dst - buf;
+ }
+ /*
+  * path = Canonical absolute path
+  * prefix_list = Colon-separated list of absolute paths
+  *
+  * Determines, for each path in parent_list, whether the "prefix" really
+  * is an ancestor directory of path.  Returns the length of the longest
+  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
+  * is an ancestor.  (Note that this means 0 is returned if prefix_list is
+  * "/".) "/foo" is not considered an ancestor of "/foobar".  Directories
+  * are not considered to be their own ancestors.  path must be in a
+  * canonical form: empty components, or "." or ".." components are not
+  * allowed.  prefix_list may be null, which is like "".
+  */
+ int longest_ancestor_length(const char *path, const char *prefix_list)
+ {
+       char buf[PATH_MAX+1];
+       const char *ceil, *colon;
+       int len, max_len = -1;
+       if (prefix_list == NULL || !strcmp(path, "/"))
+               return -1;
+       for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
+               for (colon = ceil; *colon && *colon != ':'; colon++);
+               len = colon - ceil;
+               if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
+                       continue;
+               strlcpy(buf, ceil, len+1);
+               len = normalize_absolute_path(buf, buf);
+               /* Strip "trailing slashes" from "/". */
+               if (len == 1)
+                       len = 0;
+               if (!strncmp(path, buf, len) &&
+                   path[len] == '/' &&
+                   len > max_len) {
+                       max_len = len;
+               }
+       }
+       return max_len;
+ }
diff --cc setup.c
index cc3fb380c1f3471f0371ba54ca2f338f90a8d4fc,045ca20b32f4b512e175601fb371ac54dfce8cee..6cf909463d4ad3681a2f35269db9dc944f4389c2
+++ b/setup.c
@@@ -431,8 -414,8 +431,10 @@@ const char *setup_git_directory_gently(
  
        if (!getcwd(cwd, sizeof(cwd)-1))
                die("Unable to read current working directory");
-       if (has_dos_drive_prefix(cwd))
-               minoffset = 2;
+       ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
++      if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
++              ceil_offset = 1;
  
        /*
         * Test in the following order (relative to the cwd):
diff --cc t/test-lib.sh
Simple merge