Code

Merge branch 'nd/root-git'
authorJunio C Hamano <gitster@pobox.com>
Sun, 7 Mar 2010 20:47:15 +0000 (12:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 7 Mar 2010 20:47:15 +0000 (12:47 -0800)
* nd/root-git:
  Add test for using Git at root of file system
  Support working directory located at root
  Move offset_1st_component() to path.c
  init-db, rev-parse --git-dir: do not append redundant slash
  make_absolute_path(): Do not append redundant slash

Conflicts:
setup.c
sha1_file.c

1  2 
builtin-rev-parse.c
cache.h
path.c
setup.c
sha1_file.c

diff --combined builtin-rev-parse.c
index a8c5043dedd785b8fc43c0921edd01e2adac65e1,d0ccb4c96891749f1203d9bdb2ddd897cd2fb7f8..88bad9af3caae741653e7836a0d53df9563e557c
@@@ -41,7 -41,6 +41,7 @@@ static int is_rev_argument(const char *
                "--all",
                "--bisect",
                "--dense",
 +              "--branches=",
                "--branches",
                "--header",
                "--max-age=",
                "--objects-edge",
                "--parents",
                "--pretty",
 +              "--remotes=",
                "--remotes",
 +              "--glob=",
                "--sparse",
 +              "--tags=",
                "--tags",
                "--topo-order",
                "--date-order",
@@@ -573,43 -569,18 +573,43 @@@ int cmd_rev_parse(int argc, const char 
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
 +                      if (!prefixcmp(arg, "--branches=")) {
 +                              for_each_glob_ref_in(show_reference, arg + 11,
 +                                      "refs/heads/", NULL);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--branches")) {
                                for_each_branch_ref(show_reference, NULL);
                                continue;
                        }
 +                      if (!prefixcmp(arg, "--tags=")) {
 +                              for_each_glob_ref_in(show_reference, arg + 7,
 +                                      "refs/tags/", NULL);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--tags")) {
                                for_each_tag_ref(show_reference, NULL);
                                continue;
                        }
 +                      if (!prefixcmp(arg, "--glob=")) {
 +                              for_each_glob_ref(show_reference, arg + 7, NULL);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--remotes=")) {
 +                              for_each_glob_ref_in(show_reference, arg + 10,
 +                                      "refs/remotes/", NULL);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--remotes")) {
                                for_each_remote_ref(show_reference, NULL);
                                continue;
                        }
 +                      if (!strcmp(arg, "--show-toplevel")) {
 +                              const char *work_tree = get_git_work_tree();
 +                              if (work_tree)
 +                                      puts(work_tree);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
                        if (!strcmp(arg, "--git-dir")) {
                                const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
                                static char cwd[PATH_MAX];
+                               int len;
                                if (gitdir) {
                                        puts(gitdir);
                                        continue;
                                }
                                if (!getcwd(cwd, PATH_MAX))
                                        die_errno("unable to get current working directory");
-                               printf("%s/.git\n", cwd);
+                               len = strlen(cwd);
+                               printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
                                continue;
                        }
                        if (!strcmp(arg, "--is-inside-git-dir")) {
diff --combined cache.h
index 4d89aa3da4d32653289c742f37abe8ef604ab11b,caf4192b886af079df36677bb0205e6db3fe642e..c286310ded0628fba4706f81b77708559d759c29
+++ b/cache.h
@@@ -177,22 -177,15 +177,22 @@@ struct cache_entry 
  
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
 +#define CE_CONFLICTED (0x800000)
 +
 +/* Only remove in work directory, not index */
 +#define CE_WT_REMOVE (0x400000)
 +
 +#define CE_UNPACKED  (0x1000000)
  
  /*
   * Extended on-disk flags
   */
  #define CE_INTENT_TO_ADD 0x20000000
 +#define CE_SKIP_WORKTREE 0x40000000
  /* CE_EXTENDED2 is for future extension */
  #define CE_EXTENDED2 0x80000000
  
 -#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
 +#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  /*
   * Safeguard to avoid saving wrong flags:
@@@ -241,7 -234,6 +241,7 @@@ static inline size_t ce_namelen(const s
                            ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 +#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@@ -290,7 -282,6 +290,7 @@@ static inline int ce_to_dtype(const str
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
 +      struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
        struct cache_time timestamp;
        void *alloc;
@@@ -345,9 -336,6 +345,9 @@@ static inline void remove_name_hash(str
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
 +#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
 +#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
 +#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #endif
  
  enum object_type {
@@@ -457,6 -445,7 +457,6 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 -extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  extern int remove_index_entry_at(struct index_state *, int pos);
  extern void remove_marked_cache_entries(struct index_state *istate);
@@@ -475,9 -464,7 +475,9 @@@ extern int index_name_is_other(const st
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
  /* do not check the contents but report dirty on racily-clean entries */
 -#define CE_MATCH_RACY_IS_DIRTY        02
 +#define CE_MATCH_RACY_IS_DIRTY                02
 +/* do stat comparison even if CE_SKIP_WORKTREE is true */
 +#define CE_MATCH_IGNORE_SKIP_WORKTREE 04
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
@@@ -486,6 -473,9 +486,6 @@@ extern int index_fd(unsigned char *sha1
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
 -/* "careful lstat()" */
 -extern int check_path(const char *path, int len, struct stat *st, int skiplen);
 -
  #define REFRESH_REALLY                0x0001  /* ignore_valid */
  #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
  #define REFRESH_QUIET         0x0004  /* be quiet about it */
@@@ -539,7 -529,6 +539,7 @@@ extern int auto_crlf
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
 +extern int core_apply_sparse_checkout;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -555,7 -544,6 +555,7 @@@ enum branch_track 
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
 +      BRANCH_TRACK_OVERRIDE,
  };
  
  enum rebase_setup_type {
@@@ -630,6 -618,7 +630,6 @@@ static inline void hashclr(unsigned cha
  {
        memset(hash, 0, 20);
  }
 -extern int is_empty_blob_sha1(const unsigned char *sha1);
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@@@ -641,10 -630,6 +641,10 @@@ int git_mkstemp(char *path, size_t n, c
  
  int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
  
 +/* set default permissions by passing mode arguments to open(2) */
 +int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
 +int git_mkstemp_mode(char *pattern, int mode);
 +
  /*
   * NOTE NOTE NOTE!!
   *
@@@ -679,6 -664,7 +679,7 @@@ int normalize_path_copy(char *dst, cons
  int longest_ancestor_length(const char *path, const char *prefix_list);
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
+ int offset_1st_component(const char *path);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
@@@ -703,6 -689,7 +704,6 @@@ extern int has_sha1_pack(const unsigne
  extern int has_sha1_file(const unsigned char *sha1);
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern int has_pack_file(const unsigned char *sha1);
  extern int has_pack_index(const unsigned char *sha1);
  
  extern const signed char hexval_table[256];
@@@ -716,11 -703,7 +717,11 @@@ static inline unsigned int hexval(unsig
  #define DEFAULT_ABBREV 7
  
  extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
 +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
 +static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 +{
 +      return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
 +}
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
@@@ -728,7 -711,6 +729,7 @@@ extern const char *resolve_ref(const ch
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  extern int interpret_branch_name(const char *str, struct strbuf *);
 +extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
  extern const char *ref_rev_parse_rules[];
@@@ -779,7 -761,7 +780,7 @@@ extern const char *git_committer_info(i
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
  extern const char *git_editor(void);
 -extern const char *git_pager(void);
 +extern const char *git_pager(int stdout_is_tty);
  
  struct checkout {
        const char *base_dir;
@@@ -804,6 -786,8 +805,6 @@@ extern int has_symlink_leading_path(con
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
  extern int has_symlink_or_noent_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 -extern void invalidate_lstat_cache(const char *name, int len);
 -extern void clear_lstat_cache(void);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -943,11 -927,7 +944,11 @@@ extern const char *config_exclusive_fil
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
  extern char git_default_name[MAX_GITNAME];
 +#define IDENT_NAME_GIVEN 01
 +#define IDENT_MAIL_GIVEN 02
 +#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
  extern int user_ident_explicitly_given;
 +extern int user_ident_sufficiently_given(void);
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
@@@ -1015,7 -995,6 +1016,7 @@@ extern int diff_auto_refresh_index
  
  /* match-trees.c */
  void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 +void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
  
  /*
   * whitespace rules.
diff --combined path.c
index c290e744865af774f8dfb575d66f2755603744fa,06fd9e0577f48b2587f92ea5fa3abcae53dba337..b4c8d917229a4187f36a76f43603fc036e65632e
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -157,85 -157,6 +157,85 @@@ int git_mkstemps(char *path, size_t len
        return mkstemps(path, suffix_len);
  }
  
 +/* Adapted from libiberty's mkstemp.c. */
 +
 +#undef TMP_MAX
 +#define TMP_MAX 16384
 +
 +int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
 +{
 +      static const char letters[] =
 +              "abcdefghijklmnopqrstuvwxyz"
 +              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 +              "0123456789";
 +      static const int num_letters = 62;
 +      uint64_t value;
 +      struct timeval tv;
 +      char *template;
 +      size_t len;
 +      int fd, count;
 +
 +      len = strlen(pattern);
 +
 +      if (len < 6 + suffix_len) {
 +              errno = EINVAL;
 +              return -1;
 +      }
 +
 +      if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
 +              errno = EINVAL;
 +              return -1;
 +      }
 +
 +      /*
 +       * Replace pattern's XXXXXX characters with randomness.
 +       * Try TMP_MAX different filenames.
 +       */
 +      gettimeofday(&tv, NULL);
 +      value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
 +      template = &pattern[len - 6 - suffix_len];
 +      for (count = 0; count < TMP_MAX; ++count) {
 +              uint64_t v = value;
 +              /* Fill in the random bits. */
 +              template[0] = letters[v % num_letters]; v /= num_letters;
 +              template[1] = letters[v % num_letters]; v /= num_letters;
 +              template[2] = letters[v % num_letters]; v /= num_letters;
 +              template[3] = letters[v % num_letters]; v /= num_letters;
 +              template[4] = letters[v % num_letters]; v /= num_letters;
 +              template[5] = letters[v % num_letters]; v /= num_letters;
 +
 +              fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
 +              if (fd > 0)
 +                      return fd;
 +              /*
 +               * Fatal error (EPERM, ENOSPC etc).
 +               * It doesn't make sense to loop.
 +               */
 +              if (errno != EEXIST)
 +                      break;
 +              /*
 +               * This is a random value.  It is only necessary that
 +               * the next TMP_MAX values generated by adding 7777 to
 +               * VALUE are different with (module 2^32).
 +               */
 +              value += 7777;
 +      }
 +      /* We return the null string if we can't find a unique file name.  */
 +      pattern[0] = '\0';
 +      return -1;
 +}
 +
 +int git_mkstemp_mode(char *pattern, int mode)
 +{
 +      /* mkstemp is just mkstemps with no suffix */
 +      return git_mkstemps_mode(pattern, 0, mode);
 +}
 +
 +int gitmkstemps(char *pattern, int suffix_len)
 +{
 +      return git_mkstemps_mode(pattern, suffix_len, 0600);
 +}
 +
  int validate_headref(const char *path)
  {
        struct stat st;
@@@ -415,7 -336,7 +415,7 @@@ char *enter_repo(char *path, int strict
  
        if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
            validate_headref("HEAD") == 0) {
 -              setenv(GIT_DIR_ENVIRONMENT, ".", 1);
 +              set_git_dir(".");
                check_repository_format();
                return path;
        }
@@@ -689,7 -610,7 +689,7 @@@ int daemon_avoid_alias(const char *p
        /*
         * This resurrects the belts and suspenders paranoia check by HPA
         * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
 -       * does not do getcwd() based path canonicalizations.
 +       * does not do getcwd() based path canonicalization.
         *
         * sl becomes true immediately after seeing '/' and continues to
         * be true as long as dots continue after that without intervening
                }
        }
  }
+ int offset_1st_component(const char *path)
+ {
+       if (has_dos_drive_prefix(path))
+               return 2 + is_dir_sep(path[2]);
+       return is_dir_sep(path[0]);
+ }
diff --combined setup.c
index 0717a98d16b8be3cb70b33b6dea7040694130a0b,3b9efdbaea837b9b91a51f950affbc8bf6b5344f..5716d90b57574d045114f4aaad1bdf36fd79ed89
+++ b/setup.c
@@@ -18,14 -18,15 +18,15 @@@ const char *prefix_path(const char *pre
        if (normalize_path_copy(sanitized, sanitized))
                goto error_out;
        if (is_absolute_path(orig)) {
-               size_t len, total;
+               size_t root_len, len, total;
                const char *work_tree = get_git_work_tree();
                if (!work_tree)
                        goto error_out;
                len = strlen(work_tree);
+               root_len = offset_1st_component(work_tree);
                total = strlen(sanitized) + 1;
                if (strncmp(sanitized, work_tree, len) ||
-                   (sanitized[len] != '\0' && sanitized[len] != '/')) {
+                   (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
                error_out:
                        die("'%s' is outside repository", orig);
                }
@@@ -77,18 -78,6 +78,18 @@@ int check_filename(const char *prefix, 
        die_errno("failed to stat '%s'", arg);
  }
  
 +static void NORETURN die_verify_filename(const char *prefix, const char *arg)
 +{
 +      unsigned char sha1[20];
 +      unsigned mode;
 +      /* try a detailed diagnostic ... */
 +      get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
 +      /* ... or fall back the most general message. */
 +      die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
 +          "Use '--' to separate paths from revisions", arg);
 +
 +}
 +
  /*
   * Verify a filename that we got as an argument for a pathspec
   * entry. Note that a filename that begins with "-" never verifies
@@@ -102,7 -91,8 +103,7 @@@ void verify_filename(const char *prefix
                die("bad flag '%s' used after filename", arg);
        if (check_filename(prefix, arg))
                return;
 -      die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
 -          "Use '--' to separate paths from revisions", arg);
 +      die_verify_filename(prefix, arg);
  }
  
  /*
@@@ -206,7 -196,7 +207,7 @@@ int is_inside_work_tree(void
  }
  
  /*
 - * set_work_tree() is only ever called if you set GIT_DIR explicitely.
 + * set_work_tree() is only ever called if you set GIT_DIR explicitly.
   * The old behaviour (which we retain here) is to set the work tree root
   * to the cwd, unless overridden by the config, the command line, or
   * GIT_WORK_TREE.
@@@ -263,8 -253,6 +264,8 @@@ static int check_repository_format_gent
  const char *read_gitfile_gently(const char *path)
  {
        char *buf;
 +      char *dir;
 +      const char *slash;
        struct stat st;
        int fd;
        size_t 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);
 +      dir = buf + 8;
 +
 +      if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
 +              size_t pathlen = slash+1 - path;
 +              size_t dirlen = pathlen + len - 8;
 +              dir = xmalloc(dirlen + 1);
 +              strncpy(dir, path, pathlen);
 +              strncpy(dir + pathlen, buf + 8, len - 8);
 +              dir[dirlen] = '\0';
 +              free(buf);
 +              buf = dir;
 +      }
 +
 +      if (!is_git_directory(dir))
 +              die("Not a git repository: %s", dir);
 +      path = make_absolute_path(dir);
 +
        free(buf);
        return path;
  }
@@@ -321,7 -295,7 +322,7 @@@ const char *setup_git_directory_gently(
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
        const char *gitfile_dir;
-       int len, offset, ceil_offset;
+       int len, offset, ceil_offset, root_len;
  
        /*
         * Let's assume that we are in a git repository.
                        if (!work_tree_env)
                                inside_work_tree = 0;
                        if (offset != len) {
-                               cwd[offset] = '\0';
+                               root_len = offset_1st_component(cwd);
+                               cwd[offset > root_len ? offset : root_len] = '\0';
                                set_git_dir(cwd);
                        } else
                                set_git_dir(".");
        inside_git_dir = 0;
        if (!work_tree_env)
                inside_work_tree = 1;
-       git_work_tree_cfg = xstrndup(cwd, offset);
+       root_len = offset_1st_component(cwd);
+       git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
        if (check_repository_format_gently(nongit_ok))
                return NULL;
        if (offset == len)
diff --combined sha1_file.c
index c23cc5e6e19a2d8c9a92161b0a5d62a5ef8e920b,923d9d1cd10e65f72b226fff0b215bc8ac31ccf1..a08a9d08808bdb2f4a138d7e6f602b61fc093c1b
@@@ -35,13 -35,54 +35,6 @@@ static size_t sz_fmt(size_t s) { retur
  
  const unsigned char null_sha1[20];
  
- static inline int offset_1st_component(const char *path)
 -const signed char hexval_table[256] = {
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
 -        0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
 -        8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
 -       -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
 -       -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
 -       -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
 -};
 -
 -int get_sha1_hex(const char *hex, unsigned char *sha1)
--{
-       if (has_dos_drive_prefix(path))
-               return 2 + (path[2] == '/');
-       return *path == '/';
 -      int i;
 -      for (i = 0; i < 20; i++) {
 -              unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
 -              if (val & ~0xff)
 -                      return -1;
 -              *sha1++ = val;
 -              hex += 2;
 -      }
 -      return 0;
--}
--
  int safe_create_leading_directories(char *path)
  {
        char *pos = path + offset_1st_component(path);
@@@ -85,6 -126,24 +78,6 @@@ int safe_create_leading_directories_con
        return result;
  }
  
 -char *sha1_to_hex(const unsigned char *sha1)
 -{
 -      static int bufno;
 -      static char hexbuffer[4][50];
 -      static const char hex[] = "0123456789abcdef";
 -      char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
 -      int i;
 -
 -      for (i = 0; i < 20; i++) {
 -              unsigned int val = *sha1++;
 -              *buf++ = hex[val >> 4];
 -              *buf++ = hex[val & 0xf];
 -      }
 -      *buf = '\0';
 -
 -      return buffer;
 -}
 -
  static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
  {
        int i;
@@@ -2206,7 -2265,7 +2199,7 @@@ int move_temp_to_file(const char *tmpfi
        }
  
  out:
 -      if (set_shared_perm(filename, (S_IFREG|0444)))
 +      if (adjust_shared_perm(filename))
                return error("unable to set permission to '%s'", filename);
        return 0;
  }
@@@ -2262,7 -2321,7 +2255,7 @@@ static int create_tmpfile(char *buffer
        }
        memcpy(buffer, filename, dirlen);
        strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
 -      fd = mkstemp(buffer);
 +      fd = git_mkstemp_mode(buffer, 0444);
        if (fd < 0 && dirlen && errno == ENOENT) {
                /* Make sure the directory exists */
                memcpy(buffer, filename, dirlen);
  
                /* Try again */
                strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
 -              fd = mkstemp(buffer);
 +              fd = git_mkstemp_mode(buffer, 0444);
        }
        return fd;
  }
@@@ -2281,10 -2340,9 +2274,10 @@@ static int write_loose_object(const uns
                              void *buf, unsigned long len, time_t mtime)
  {
        int fd, ret;
 -      size_t size;
 -      unsigned char *compressed;
 +      unsigned char compressed[4096];
        z_stream stream;
 +      git_SHA_CTX c;
 +      unsigned char parano_sha1[20];
        char *filename;
        static char tmpfile[PATH_MAX];
  
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
        deflateInit(&stream, zlib_compression_level);
 -      size = 8 + deflateBound(&stream, len+hdrlen);
 -      compressed = xmalloc(size);
 -
 -      /* Compress it */
        stream.next_out = compressed;
 -      stream.avail_out = size;
 +      stream.avail_out = sizeof(compressed);
 +      git_SHA1_Init(&c);
  
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
        while (deflate(&stream, 0) == Z_OK)
                /* nothing */;
 +      git_SHA1_Update(&c, hdr, hdrlen);
  
        /* Then the data itself.. */
        stream.next_in = buf;
        stream.avail_in = len;
 -      ret = deflate(&stream, Z_FINISH);
 +      do {
 +              unsigned char *in0 = stream.next_in;
 +              ret = deflate(&stream, Z_FINISH);
 +              git_SHA1_Update(&c, in0, stream.next_in - in0);
 +              if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
 +                      die("unable to write sha1 file");
 +              stream.next_out = compressed;
 +              stream.avail_out = sizeof(compressed);
 +      } while (ret == Z_OK);
 +
        if (ret != Z_STREAM_END)
                die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
 -
        ret = deflateEnd(&stream);
        if (ret != Z_OK)
                die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
 +      git_SHA1_Final(parano_sha1, &c);
 +      if (hashcmp(sha1, parano_sha1) != 0)
 +              die("confused by unstable object source data for %s", sha1_to_hex(sha1));
  
 -      size = stream.total_out;
 -
 -      if (write_buffer(fd, compressed, size) < 0)
 -              die("unable to write sha1 file");
        close_sha1_file(fd);
 -      free(compressed);
  
        if (mtime) {
                struct utimbuf utb;
@@@ -2395,6 -2449,14 +2388,6 @@@ int has_pack_index(const unsigned char 
        return 1;
  }
  
 -int has_pack_file(const unsigned char *sha1)
 -{
 -      struct stat st;
 -      if (stat(sha1_pack_name(sha1), &st))
 -              return 0;
 -      return 1;
 -}
 -
  int has_sha1_pack(const unsigned char *sha1)
  {
        struct pack_entry e;
@@@ -2439,8 -2501,6 +2432,8 @@@ static int index_mem(unsigned char *sha
        return ret;
  }
  
 +#define SMALL_FILE_SIZE (32*1024)
 +
  int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
             enum object_type type, const char *path)
  {
                else
                        ret = -1;
                strbuf_release(&sbuf);
 +      } else if (size <= SMALL_FILE_SIZE) {
 +              char *buf = xmalloc(size);
 +              if (size == read_in_full(fd, buf, size))
 +                      ret = index_mem(sha1, buf, size, write_object, type,
 +                                      path);
 +              else
 +                      ret = error("short read %s", strerror(errno));
 +              free(buf);
        } else if (size) {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
                ret = index_mem(sha1, buf, size, write_object, type, path);