Merge branch 'lh/git-file'
authorJunio C Hamano <gitster@pobox.com>
Tue, 6 May 2008 02:16:16 +0000 (19:16 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 6 May 2008 02:16:16 +0000 (19:16 -0700)
* lh/git-file:
Teach GIT-VERSION-GEN about the .git file
Teach git-submodule.sh about the .git file
Teach resolve_gitlink_ref() about the .git file
Add platform-independent .git "symlink"

1  2 
cache.h
git-submodule.sh
refs.c
setup.c
diff --combined cache.h
index 5a28dddec931f983a4aeff2ee7129444d07e07aa,2bda40c6166f9b7d33e1ee1ef1e8e1ca931bc996..d5d5dad1463e2251dc84cf0a85095a7e3e0eaeae
+++ b/cache.h
@@@ -311,6 -311,7 +311,7 @@@ extern char *get_index_file(void)
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
  extern const char *get_git_work_tree(void);
+ extern const char *read_gitfile_gently(const char *path);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
@@@ -474,20 -475,10 +475,20 @@@ static inline void hashclr(unsigned cha
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
 +/*
 + * NOTE NOTE NOTE!!
 + *
 + * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
 + * not be changed. Old repositories have core.sharedrepository written in
 + * numeric format, and therefore these values are preserved for compatibility
 + * reasons.
 + */
  enum sharedrepo {
 -      PERM_UMASK = 0,
 -      PERM_GROUP,
 -      PERM_EVERYBODY
 +      PERM_UMASK          = 0,
 +      OLD_PERM_GROUP      = 1,
 +      OLD_PERM_EVERYBODY  = 2,
 +      PERM_GROUP          = 0660,
 +      PERM_EVERYBODY      = 0664,
  };
  int git_config_perm(const char *var, const char *value);
  int adjust_shared_perm(const char *path);
@@@ -702,7 -693,6 +703,7 @@@ extern int git_parse_long(const char *
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
 +extern int git_config_bool_or_int(const char *, const char *, int *);
  extern int git_config_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
@@@ -726,8 -716,8 +727,8 @@@ extern const char *git_log_output_encod
  extern void maybe_flush_or_die(FILE *, const char *);
  extern int copy_fd(int ifd, int ofd);
  extern int copy_file(const char *dst, const char *src, int mode);
 -extern int read_in_full(int fd, void *buf, size_t count);
 -extern int write_in_full(int fd, const void *buf, size_t count);
 +extern ssize_t read_in_full(int fd, void *buf, size_t count);
 +extern ssize_t write_in_full(int fd, const void *buf, size_t count);
  extern void write_or_die(int fd, const void *buf, size_t count);
  extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
  extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
diff --combined git-submodule.sh
index ce0f00c8a4f6d5f9a2c1c3ebdbadff52106b60d0,5bff86022ca80e06ad170e35377bfe94890f41d4..67f7a28cb30ae5cb16e5b5f7d947e00621267ff5
@@@ -300,7 -300,7 +300,7 @@@ cmd_update(
                        continue
                fi
  
-               if ! test -d "$path"/.git
+               if ! test -d "$path"/.git -o -f "$path"/.git
                then
                        module_clone "$path" "$url" || exit
                        subsha1=
@@@ -327,8 -327,7 +327,8 @@@ set_name_rev () 
                cd "$1" && {
                        git describe "$2" 2>/dev/null ||
                        git describe --tags "$2" 2>/dev/null ||
 -                      git describe --contains --tags "$2"
 +                      git describe --contains "$2" 2>/dev/null ||
 +                      git describe --all --always "$2"
                }
        ) )
        test -z "$revname" || revname=" ($revname)"
  #
  cmd_summary() {
        summary_limit=-1
 +      for_status=
  
        # parse $args after "submodule ... summary".
        while test $# -ne 0
                --cached)
                        cached="$1"
                        ;;
 +              --for-status)
 +                      for_status="$1"
 +                      ;;
                -n|--summary-limit)
                        if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
                        then
                done
        )
  
 -      test -n "$modules" &&
 +      test -z "$modules" && return
 +
        git diff-index $cached --raw $head -- $modules |
        grep -e '^:160000' -e '^:[0-7]* 160000' |
        cut -c2- |
                        echo
                fi
                echo
 -      done
 +      done |
 +      if test -n "$for_status"; then
 +              echo "# Modified submodules:"
 +              echo "#"
 +              sed -e 's|^|# |' -e 's|^# $|#|'
 +      else
 +              cat
 +      fi
  }
  #
  # List all submodules, prefixed with:
@@@ -555,7 -542,7 +555,7 @@@ cmd_status(
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
-               if test -z "$url" || ! test -d "$path"/.git
+               if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
                then
                        say "-$sha1 $path"
                        continue;
diff --combined refs.c
index 4db73ed8f01200fca4008bafb23bda36a8e23446,a81fb291f849d174ce22a7dc96748c2385afd683..9b495eb16ef03b14791aa44e1098cd918859f0cc
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -352,6 -352,7 +352,7 @@@ int resolve_gitlink_ref(const char *pat
  {
        int len = strlen(path), retval;
        char *gitdir;
+       const char *tmp;
  
        while (len && path[len-1] == '/')
                len--;
                return -1;
        gitdir = xmalloc(len + MAXREFLEN + 8);
        memcpy(gitdir, path, len);
-       memcpy(gitdir + len, "/.git/", 7);
-       retval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);
+       memcpy(gitdir + len, "/.git", 6);
+       len += 5;
+       tmp = read_gitfile_gently(gitdir);
+       if (tmp) {
+               free(gitdir);
+               len = strlen(tmp);
+               gitdir = xmalloc(len + MAXREFLEN + 3);
+               memcpy(gitdir, tmp, len);
+       }
+       gitdir[len] = '/';
+       gitdir[++len] = '\0';
+       retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
        free(gitdir);
        return retval;
  }
  
  const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
  {
 -      int depth = MAXDEPTH, len;
 +      int depth = MAXDEPTH;
 +      ssize_t len;
        char buffer[256];
        static char ref_buffer[256];
  
diff --combined setup.c
index 1b4fa6a8c4289bedf10f57d9aa19db4c7f186b01,53580fde43ce01f12dc326b419d94573ea14787a..b8fd476395db782bf942a548b87242b3a9f990f4
+++ b/setup.c
@@@ -314,6 -314,44 +314,44 @@@ static int check_repository_format_gent
        return 0;
  }
  
+ /*
+  * Try to read the location of the git directory from the .git file,
+  * return path to git directory if found.
+  */
+ const char *read_gitfile_gently(const char *path)
+ {
+       char *buf;
+       struct stat st;
+       int fd;
+       size_t len;
+       if (stat(path, &st))
+               return NULL;
+       if (!S_ISREG(st.st_mode))
+               return NULL;
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               die("Error opening %s: %s", path, strerror(errno));
+       buf = xmalloc(st.st_size + 1);
+       len = read_in_full(fd, buf, st.st_size);
+       close(fd);
+       if (len != st.st_size)
+               die("Error reading %s", path);
+       buf[len] = '\0';
+       if (prefixcmp(buf, "gitdir: "))
+               die("Invalid gitfile format: %s", path);
+       while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
+               len--;
+       if (len < 9)
+               die("No path in gitfile: %s", path);
+       buf[len] = '\0';
+       if (!is_git_directory(buf + 8))
+               die("Not a git repository: %s", buf + 8);
+       path = make_absolute_path(buf + 8);
+       free(buf);
+       return path;
+ }
  /*
   * We cannot decide in this function whether we are in the work tree or
   * not, since the config can only be read _after_ this function was called.
@@@ -323,6 -361,7 +361,7 @@@ const char *setup_git_directory_gently(
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
+       const char *gitfile_dir;
        int len, offset;
  
        /*
  
        /*
         * Test in the following order (relative to the cwd):
+        * - .git (file containing "gitdir: <path>")
         * - .git/
         * - ./ (bare)
+        * - ../.git
         * - ../.git/
         * - ../ (bare)
         * - ../../.git/
         */
        offset = len = strlen(cwd);
        for (;;) {
+               gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               if (gitfile_dir) {
+                       if (set_git_dir(gitfile_dir))
+                               die("Repository setup failed");
+                       break;
+               }
                if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
                        break;
                if (is_git_directory(".")) {
  
  int git_config_perm(const char *var, const char *value)
  {
 -      if (value) {
 -              int i;
 -              if (!strcmp(value, "umask"))
 -                      return PERM_UMASK;
 -              if (!strcmp(value, "group"))
 -                      return PERM_GROUP;
 -              if (!strcmp(value, "all") ||
 -                  !strcmp(value, "world") ||
 -                  !strcmp(value, "everybody"))
 -                      return PERM_EVERYBODY;
 -              i = atoi(value);
 -              if (i > 1)
 -                      return i;
 +      int i;
 +      char *endptr;
 +
 +      if (value == NULL)
 +              return PERM_GROUP;
 +
 +      if (!strcmp(value, "umask"))
 +              return PERM_UMASK;
 +      if (!strcmp(value, "group"))
 +              return PERM_GROUP;
 +      if (!strcmp(value, "all") ||
 +          !strcmp(value, "world") ||
 +          !strcmp(value, "everybody"))
 +              return PERM_EVERYBODY;
 +
 +      /* Parse octal numbers */
 +      i = strtol(value, &endptr, 8);
 +
 +      /* If not an octal number, maybe true/false? */
 +      if (*endptr != 0)
 +              return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;
 +
 +      /*
 +       * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
 +       * a chmod value.
 +       */
 +      switch (i) {
 +      case PERM_UMASK:               /* 0 */
 +              return PERM_UMASK;
 +      case OLD_PERM_GROUP:           /* 1 */
 +              return PERM_GROUP;
 +      case OLD_PERM_EVERYBODY:       /* 2 */
 +              return PERM_EVERYBODY;
        }
 -      return git_config_bool(var, value);
 +
 +      /* A filemode value was given: 0xxx */
 +
 +      if ((i & 0600) != 0600)
 +              die("Problem with core.sharedRepository filemode value "
 +                  "(0%.3o).\nThe owner of files must always have "
 +                  "read and write permissions.", i);
 +
 +      /*
 +       * Mask filemode value. Others can not get write permission.
 +       * x flags for directories are handled separately.
 +       */
 +      return i & 0666;
  }
  
  int check_repository_format_version(const char *var, const char *value)