Merge branch 'cc/sha1-file-name'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:10 +0000 (13:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:10 +0000 (13:39 -0800)
Code clean-up.

* cc/sha1-file-name:
sha1_file: improve sha1_file_name() perfs
sha1_file: remove static strbuf from sha1_file_name()

1  2 
cache.h
http.c
sha1_file.c
diff --combined cache.h
index 7414eb47b7e0489849ba92086d9f8f7311defc7d,269dfc01a0774aa74230e91c1ce4d3f649a31670..8cdcee544681829a409e6cb9fdf54c57a8b33b4c
+++ b/cache.h
@@@ -4,7 -4,7 +4,7 @@@
  #include "git-compat-util.h"
  #include "strbuf.h"
  #include "hashmap.h"
 -#include "mru.h"
 +#include "list.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -14,7 -14,6 +14,7 @@@
  #include "hash.h"
  #include "path.h"
  #include "sha1-array.h"
 +#include "repository.h"
  
  #ifndef platform_SHA_CTX
  /*
@@@ -78,8 -77,6 +78,8 @@@ struct object_id 
        unsigned char hash[GIT_MAX_RAWSZ];
  };
  
 +#define the_hash_algo the_repository->hash_algo
 +
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
@@@ -207,7 -204,6 +207,7 @@@ struct cache_entry 
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 +#define CE_FSMONITOR_VALID   (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -331,7 -327,6 +331,7 @@@ static inline unsigned int canon_mode(u
  #define CACHE_TREE_CHANGED    (1 << 5)
  #define SPLIT_INDEX_ORDERED   (1 << 6)
  #define UNTRACKED_CHANGED     (1 << 7)
 +#define FSMONITOR_CHANGED     (1 << 8)
  
  struct split_index;
  struct untracked_cache;
@@@ -350,8 -345,6 +350,8 @@@ struct index_state 
        struct hashmap dir_hash;
        unsigned char sha1[20];
        struct untracked_cache *untracked;
 +      uint64_t fsmonitor_last_update;
 +      struct ewah_bitmap *fsmonitor_dirty;
  };
  
  extern struct index_state the_index;
@@@ -453,16 -446,6 +453,16 @@@ static inline enum object_type object_t
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
 +/*
 + * Environment variable used in handshaking the wire protocol.
 + * Contains a colon ':' separated list of keys with optional values
 + * 'key[=value]'.  Presence of unknown keys and values must be
 + * ignored.
 + */
 +#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
 +/* HTTP header used to handshake the wire protocol */
 +#define GIT_PROTOCOL_HEADER "Git-Protocol"
 +
  /*
   * This environment variable is expected to contain a boolean indicating
   * whether we should or should not treat:
@@@ -619,40 -602,12 +619,40 @@@ extern int do_read_index(struct index_s
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 +
 +/* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 -#define CLOSE_LOCK            (1 << 1)
 +
 +/*
 + * Write the index while holding an already-taken lock. Close the lock,
 + * and if `COMMIT_LOCK` is given, commit it.
 + *
 + * Unless a split index is in use, write the index into the lockfile.
 + *
 + * With a split index, write the shared index to a temporary file,
 + * adjust its permissions and rename it into place, then write the
 + * split index to the lockfile. If the temporary file for the shared
 + * index cannot be created, fall back to the behavior described in
 + * the previous paragraph.
 + *
 + * With `COMMIT_LOCK`, the lock is always committed or rolled back.
 + * Without it, the lock is closed, but neither committed nor rolled
 + * back.
 + */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 +
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
 +
 +/**
 + * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
 + * branch, returns 1 if there are entries in the index, 0 otherwise. If an
 + * strbuf is provided, the space-separated list of files that differ will be
 + * appended to it.
 + */
 +extern int index_has_changes(struct strbuf *sb);
 +
  extern int verify_path(const char *path);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
@@@ -726,14 -681,11 +726,14 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_IGNORE_MISSING               0x08
  /* enable stat refresh */
  #define CE_MATCH_REFRESH              0x10
 -extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 -extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 +#define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
 +#define HASH_RENORMALIZE  4
  extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
  extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
@@@ -764,17 -716,12 +764,17 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
 +/*
 + * Opportunistically update the index but do not complain if we can't.
 + * The lockfile is always committed or rolled back.
 + */
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
 +extern int verify_ce_order;
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -828,7 -775,6 +828,7 @@@ extern int core_apply_sparse_checkout
  extern int precomposed_unicode;
  extern int protect_hfs;
  extern int protect_ntfs;
 +extern const char *core_fsmonitor;
  
  /*
   * Include broken refs in all ref iterations, which will
@@@ -914,15 -860,11 +914,15 @@@ extern int grafts_replace_parents
  #define GIT_REPO_VERSION 0
  #define GIT_REPO_VERSION_READ 1
  extern int repository_format_precious_objects;
 +extern char *repository_format_partial_clone;
 +extern const char *core_partial_clone_filter_default;
  
  struct repository_format {
        int version;
        int precious_objects;
 +      char *partial_clone; /* value of extensions.partialclone */
        int is_bare;
 +      int hash_algo;
        char *work_tree;
        struct string_list unknown_extensions;
  };
@@@ -960,12 -902,10 +960,10 @@@ extern void check_repository_format(voi
  #define TYPE_CHANGED    0x0040
  
  /*
-  * Return the name of the file in the local object database that would
-  * be used to store a loose object with the specified sha1.  The
-  * return value is a pointer to a statically allocated buffer that is
-  * overwritten each time the function is called.
+  * Put in `buf` the name of the file in the local object database that
+  * would be used to store a loose object with the specified sha1.
   */
- extern const char *sha1_file_name(const unsigned char *sha1);
+ extern void sha1_file_name(struct strbuf *buf, const unsigned char *sha1);
  
  /*
   * Return an abbreviated sha1 unique within this repository's object database.
@@@ -1055,22 -995,22 +1053,22 @@@ extern const struct object_id empty_blo
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_tree);
  }
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1375,13 -1315,6 +1373,13 @@@ extern int set_disambiguate_hint_config
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 +
  /*
   * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
@@@ -1503,7 -1436,6 +1501,7 @@@ extern const char *ident_default_name(v
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
  extern void reset_ident_date(void);
  
@@@ -1638,7 -1570,6 +1636,7 @@@ struct pack_window 
  
  extern struct packed_git {
        struct packed_git *next;
 +      struct list_head mru;
        struct pack_window *windows;
        off_t pack_size;
        const void *index_data;
        unsigned pack_local:1,
                 pack_keep:1,
                 freshened:1,
 -               do_not_close:1;
 +               do_not_close:1,
 +               pack_promisor:1;
        unsigned char sha1[20];
        struct revindex_entry *revindex;
        /* something like ".git/objects/pack/xxxxx.pack" */
  } *packed_git;
  
  /*
 - * A most-recently-used ordered version of the packed_git list, which can
 - * be iterated instead of packed_git (and marked via mru_mark).
 + * A most-recently-used ordered version of the packed_git list.
   */
 -extern struct mru packed_git_mru;
 +extern struct list_head packed_git_mru;
  
  struct pack_entry {
        off_t offset;
@@@ -1791,14 -1722,6 +1789,14 @@@ struct object_info 
  #define OBJECT_INFO_QUICK 8
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
  
 +/*
 + * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
 + * blobs. This has a difference only if extensions.partialClone is set.
 + *
 + * Its default value is 1.
 + */
 +extern int fetch_if_missing;
 +
  /* Dumb servers support */
  extern int update_server_info(int);
  
@@@ -1994,10 -1917,4 +1992,10 @@@ void sleep_millisec(int millisec)
   */
  void safe_create_dir(const char *dir, int share);
  
 +/*
 + * Should we print an ellipsis after an abbreviated SHA-1 value
 + * when doing diff-raw output or indicating a detached HEAD?
 + */
 +extern int print_sha1_ellipsis(void);
 +
  #endif /* CACHE_H */
diff --combined http.c
index 5977712712b22d9076d8cd5ccda0fe12b3057d27,7f1e187f72d781d83214029724686785fc8afbcd..5979305bc9569c4915af5dbf9889d1f2c6d1bbe9
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -12,7 -12,6 +12,7 @@@
  #include "gettext.h"
  #include "transport.h"
  #include "packfile.h"
 +#include "protocol.h"
  
  static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
  #if LIBCURL_VERSION_NUM >= 0x070a08
@@@ -865,11 -864,6 +865,11 @@@ static CURL *get_curl_handle(void
                else if (starts_with(curl_http_proxy, "socks"))
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
 +#endif
 +#if LIBCURL_VERSION_NUM >= 0x073400
 +              else if (starts_with(curl_http_proxy, "https"))
 +                      curl_easy_setopt(result,
 +                              CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
  #endif
                if (strstr(curl_http_proxy, "://"))
                        credential_from_url(&proxy_auth, curl_http_proxy);
@@@ -904,21 -898,6 +904,21 @@@ static void set_from_env(const char **v
                *var = val;
  }
  
 +static void protocol_http_header(void)
 +{
 +      if (get_protocol_version_config() > 0) {
 +              struct strbuf protocol_header = STRBUF_INIT;
 +
 +              strbuf_addf(&protocol_header, GIT_PROTOCOL_HEADER ": version=%d",
 +                          get_protocol_version_config());
 +
 +
 +              extra_http_headers = curl_slist_append(extra_http_headers,
 +                                                     protocol_header.buf);
 +              strbuf_release(&protocol_header);
 +      }
 +}
 +
  void http_init(struct remote *remote, const char *url, int proactive_auth)
  {
        char *low_speed_limit;
        if (remote)
                var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
  
 +      protocol_http_header();
 +
        pragma_header = curl_slist_append(http_copy_default_headers(),
                "Pragma: no-cache");
        no_pragma_header = curl_slist_append(http_copy_default_headers(),
@@@ -2030,6 -2007,7 +2030,6 @@@ int finish_http_pack_request(struct htt
        char *tmp_idx;
        size_t len;
        struct child_process ip = CHILD_PROCESS_INIT;
 -      const char *ip_argv[8];
  
        close_pack_index(p);
  
                die("BUG: pack tmpfile does not end in .pack.temp?");
        tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile);
  
 -      ip_argv[0] = "index-pack";
 -      ip_argv[1] = "-o";
 -      ip_argv[2] = tmp_idx;
 -      ip_argv[3] = preq->tmpfile;
 -      ip_argv[4] = NULL;
 -
 -      ip.argv = ip_argv;
 +      argv_array_push(&ip.args, "index-pack");
 +      argv_array_pushl(&ip.args, "-o", tmp_idx, NULL);
 +      argv_array_push(&ip.args, preq->tmpfile);
        ip.git_cmd = 1;
        ip.no_stdin = 1;
        ip.no_stdout = 1;
@@@ -2168,7 -2150,7 +2168,7 @@@ struct http_object_request *new_http_ob
        unsigned char *sha1)
  {
        char *hex = sha1_to_hex(sha1);
-       const char *filename;
+       struct strbuf filename = STRBUF_INIT;
        char prevfile[PATH_MAX];
        int prevlocal;
        char prev_buf[PREV_BUF_SIZE];
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
  
-       filename = sha1_file_name(sha1);
+       sha1_file_name(&filename, sha1);
        snprintf(freq->tmpfile, sizeof(freq->tmpfile),
-                "%s.temp", filename);
+                "%s.temp", filename.buf);
  
-       snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
+       snprintf(prevfile, sizeof(prevfile), "%s.prev", filename.buf);
        unlink_or_warn(prevfile);
        rename(freq->tmpfile, prevfile);
        unlink_or_warn(freq->tmpfile);
+       strbuf_release(&filename);
  
        if (freq->localfile != -1)
                error("fd leakage in start: %d", freq->localfile);
@@@ -2302,6 -2285,7 +2303,7 @@@ void process_http_object_request(struc
  int finish_http_object_request(struct http_object_request *freq)
  {
        struct stat st;
+       struct strbuf filename = STRBUF_INIT;
  
        close(freq->localfile);
        freq->localfile = -1;
                unlink_or_warn(freq->tmpfile);
                return -1;
        }
-       freq->rename =
-               finalize_object_file(freq->tmpfile, sha1_file_name(freq->sha1));
+       sha1_file_name(&filename, freq->sha1);
+       freq->rename = finalize_object_file(freq->tmpfile, filename.buf);
+       strbuf_release(&filename);
  
        return freq->rename;
  }
diff --combined sha1_file.c
index 4d3f282c80374f769de548787e346642b599db7d,d82cf1b4a4d4b2c4a344057c3441e93dcd0d199b..831d9e73438487d5d4918d3c21ae1725d915501f
  #include "bulk-checkin.h"
  #include "streaming.h"
  #include "dir.h"
 -#include "mru.h"
  #include "list.h"
  #include "mergesort.h"
  #include "quote.h"
  #include "packfile.h"
 +#include "fetch-object.h"
  
  const unsigned char null_sha1[GIT_MAX_RAWSZ];
  const struct object_id null_oid;
@@@ -39,64 -39,6 +39,64 @@@ const struct object_id empty_blob_oid 
        EMPTY_BLOB_SHA1_BIN_LITERAL
  };
  
 +static void git_hash_sha1_init(void *ctx)
 +{
 +      git_SHA1_Init((git_SHA_CTX *)ctx);
 +}
 +
 +static void git_hash_sha1_update(void *ctx, const void *data, size_t len)
 +{
 +      git_SHA1_Update((git_SHA_CTX *)ctx, data, len);
 +}
 +
 +static void git_hash_sha1_final(unsigned char *hash, void *ctx)
 +{
 +      git_SHA1_Final(hash, (git_SHA_CTX *)ctx);
 +}
 +
 +static void git_hash_unknown_init(void *ctx)
 +{
 +      die("trying to init unknown hash");
 +}
 +
 +static void git_hash_unknown_update(void *ctx, const void *data, size_t len)
 +{
 +      die("trying to update unknown hash");
 +}
 +
 +static void git_hash_unknown_final(unsigned char *hash, void *ctx)
 +{
 +      die("trying to finalize unknown hash");
 +}
 +
 +const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
 +      {
 +              NULL,
 +              0x00000000,
 +              0,
 +              0,
 +              0,
 +              git_hash_unknown_init,
 +              git_hash_unknown_update,
 +              git_hash_unknown_final,
 +              NULL,
 +              NULL,
 +      },
 +      {
 +              "sha-1",
 +              /* "sha1", big-endian */
 +              0x73686131,
 +              sizeof(git_SHA_CTX),
 +              GIT_SHA1_RAWSZ,
 +              GIT_SHA1_HEXSZ,
 +              git_hash_sha1_init,
 +              git_hash_sha1_update,
 +              git_hash_sha1_final,
 +              &empty_tree_oid,
 +              &empty_blob_oid,
 +      },
 +};
 +
  /*
   * This is meant to hold a *small* number of objects that you would
   * want read_sha1_file() to be able to return, but yet you do not want
@@@ -132,18 -74,6 +132,18 @@@ static struct cached_object *find_cache
        return NULL;
  }
  
 +
 +static int get_conv_flags(unsigned flags)
 +{
 +      if (flags & HASH_RENORMALIZE)
 +              return CONV_EOL_RENORMALIZE;
 +      else if (flags & HASH_WRITE_OBJECT)
 +        return global_conv_flags_eol;
 +      else
 +              return 0;
 +}
 +
 +
  int mkdir_in_gitdir(const char *path)
  {
        if (mkdir(path, 0777)) {
@@@ -321,15 -251,11 +321,11 @@@ static void fill_sha1_path(struct strbu
        }
  }
  
const char *sha1_file_name(const unsigned char *sha1)
void sha1_file_name(struct strbuf *buf, const unsigned char *sha1)
  {
-       static struct strbuf buf = STRBUF_INIT;
-       strbuf_reset(&buf);
-       strbuf_addf(&buf, "%s/", get_object_directory());
-       fill_sha1_path(&buf, sha1);
-       return buf.buf;
+       strbuf_addstr(buf, get_object_directory());
+       strbuf_addch(buf, '/');
+       fill_sha1_path(buf, sha1);
  }
  
  struct strbuf *alt_scratch_buf(struct alternate_object_database *alt)
@@@ -529,19 -455,19 +525,19 @@@ struct alternate_object_database *alloc
  
  void add_to_alternates_file(const char *reference)
  {
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock = LOCK_INIT;
        char *alts = git_pathdup("objects/info/alternates");
        FILE *in, *out;
 +      int found = 0;
  
 -      hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
 -      out = fdopen_lock_file(lock, "w");
 +      hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
 +      out = fdopen_lock_file(&lock, "w");
        if (!out)
                die_errno("unable to fdopen alternates lockfile");
  
        in = fopen(alts, "r");
        if (in) {
                struct strbuf line = STRBUF_INIT;
 -              int found = 0;
  
                while (strbuf_getline(&line, in) != EOF) {
                        if (!strcmp(reference, line.buf)) {
  
                strbuf_release(&line);
                fclose(in);
 -
 -              if (found) {
 -                      rollback_lock_file(lock);
 -                      lock = NULL;
 -              }
        }
        else if (errno != ENOENT)
                die_errno("unable to read alternates file");
  
 -      if (lock) {
 +      if (found) {
 +              rollback_lock_file(&lock);
 +      } else {
                fprintf_or_die(out, "%s\n", reference);
 -              if (commit_lock_file(lock))
 +              if (commit_lock_file(&lock))
                        die_errno("unable to move new alternates file into place");
                if (alt_odb_tail)
                        link_alt_odb_entries(reference, '\n', NULL, 0);
@@@ -710,7 -639,12 +706,12 @@@ int check_and_freshen_file(const char *
  
  static int check_and_freshen_local(const unsigned char *sha1, int freshen)
  {
-       return check_and_freshen_file(sha1_file_name(sha1), freshen);
+       static struct strbuf buf = STRBUF_INIT;
+       strbuf_reset(&buf);
+       sha1_file_name(&buf, sha1);
+       return check_and_freshen_file(buf.buf, freshen);
  }
  
  static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
@@@ -866,8 -800,12 +867,12 @@@ static int stat_sha1_file(const unsigne
                          const char **path)
  {
        struct alternate_object_database *alt;
+       static struct strbuf buf = STRBUF_INIT;
+       strbuf_reset(&buf);
+       sha1_file_name(&buf, sha1);
+       *path = buf.buf;
  
-       *path = sha1_file_name(sha1);
        if (!lstat(*path, st))
                return 0;
  
@@@ -891,8 -829,12 +896,12 @@@ static int open_sha1_file(const unsigne
        int fd;
        struct alternate_object_database *alt;
        int most_interesting_errno;
+       static struct strbuf buf = STRBUF_INIT;
+       strbuf_reset(&buf);
+       sha1_file_name(&buf, sha1);
+       *path = buf.buf;
  
-       *path = sha1_file_name(sha1);
        fd = git_open(*path);
        if (fd >= 0)
                return fd;
@@@ -1213,8 -1155,6 +1222,8 @@@ static int sha1_loose_object_info(cons
        return (status < 0) ? status : 0;
  }
  
 +int fetch_if_missing = 1;
 +
  int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
  {
        static struct object_info blank_oi = OBJECT_INFO_INIT;
        const unsigned char *real = (flags & OBJECT_INFO_LOOKUP_REPLACE) ?
                                    lookup_replace_object(sha1) :
                                    sha1;
 +      int already_retried = 0;
 +
 +      if (is_null_sha1(real))
 +              return -1;
  
        if (!oi)
                oi = &blank_oi;
                }
        }
  
 -      if (!find_pack_entry(real, &e)) {
 +      while (1) {
 +              if (find_pack_entry(real, &e))
 +                      break;
 +
                /* Most likely it's a loose object. */
                if (!sha1_loose_object_info(real, oi, flags))
                        return 0;
  
                /* Not a loose object; someone else may have just packed it. */
 -              if (flags & OBJECT_INFO_QUICK) {
 -                      return -1;
 -              } else {
 -                      reprepare_packed_git();
 -                      if (!find_pack_entry(real, &e))
 -                              return -1;
 +              reprepare_packed_git();
 +              if (find_pack_entry(real, &e))
 +                      break;
 +
 +              /* Check if it is a missing object */
 +              if (fetch_if_missing && repository_format_partial_clone &&
 +                  !already_retried) {
 +                      /*
 +                       * TODO Investigate haveing fetch_object() return
 +                       * TODO error/success and stopping the music here.
 +                       */
 +                      fetch_object(repository_format_partial_clone, real);
 +                      already_retried = 1;
 +                      continue;
                }
 +
 +              return -1;
        }
  
        if (oi == &blank_oi)
                 * information below, so return early.
                 */
                return 0;
 -
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
@@@ -1572,9 -1496,12 +1581,12 @@@ static int write_loose_object(const uns
        git_SHA_CTX c;
        unsigned char parano_sha1[20];
        static struct strbuf tmp_file = STRBUF_INIT;
-       const char *filename = sha1_file_name(sha1);
+       static struct strbuf filename = STRBUF_INIT;
+       strbuf_reset(&filename);
+       sha1_file_name(&filename, sha1);
  
-       fd = create_tmpfile(&tmp_file, filename);
+       fd = create_tmpfile(&tmp_file, filename.buf);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s", get_object_directory());
                        warning_errno("failed utime() on %s", tmp_file.buf);
        }
  
-       return finalize_object_file(tmp_file.buf, filename);
+       return finalize_object_file(tmp_file.buf, filename.buf);
  }
  
  static int freshen_loose_object(const unsigned char *sha1)
@@@ -1751,7 -1678,7 +1763,7 @@@ static void check_tag(const void *buf, 
                die("corrupt tag");
  }
  
 -static int index_mem(unsigned char *sha1, void *buf, size_t size,
 +static int index_mem(struct object_id *oid, void *buf, size_t size,
                     enum object_type type,
                     const char *path, unsigned flags)
  {
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
                if (convert_to_git(&the_index, path, buf, size, &nbuf,
 -                                 write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
 +                                 get_conv_flags(flags))) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
        }
  
        if (write_object)
 -              ret = write_sha1_file(buf, size, typename(type), sha1);
 +              ret = write_sha1_file(buf, size, typename(type), oid->hash);
        else
 -              ret = hash_sha1_file(buf, size, typename(type), sha1);
 +              ret = hash_sha1_file(buf, size, typename(type), oid->hash);
        if (re_allocated)
                free(buf);
        return ret;
  }
  
 -static int index_stream_convert_blob(unsigned char *sha1, int fd,
 +static int index_stream_convert_blob(struct object_id *oid, int fd,
                                     const char *path, unsigned flags)
  {
        int ret;
        assert(would_convert_to_git_filter_fd(path));
  
        convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
 -                               write_object ? safe_crlf : SAFE_CRLF_FALSE);
 +                               get_conv_flags(flags));
  
        if (write_object)
                ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 -                                    sha1);
 +                                    oid->hash);
        else
                ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 -                                   sha1);
 +                                   oid->hash);
        strbuf_release(&sbuf);
        return ret;
  }
  
 -static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
 +static int index_pipe(struct object_id *oid, int fd, enum object_type type,
                      const char *path, unsigned flags)
  {
        struct strbuf sbuf = STRBUF_INIT;
        int ret;
  
        if (strbuf_read(&sbuf, fd, 4096) >= 0)
 -              ret = index_mem(sha1, sbuf.buf, sbuf.len, type, path, flags);
 +              ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
        else
                ret = -1;
        strbuf_release(&sbuf);
  
  #define SMALL_FILE_SIZE (32*1024)
  
 -static int index_core(unsigned char *sha1, int fd, size_t size,
 +static int index_core(struct object_id *oid, int fd, size_t size,
                      enum object_type type, const char *path,
                      unsigned flags)
  {
        int ret;
  
        if (!size) {
 -              ret = index_mem(sha1, "", size, type, path, flags);
 +              ret = index_mem(oid, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                ssize_t read_result = read_in_full(fd, buf, size);
                        ret = error("short read while indexing %s",
                                    path ? path : "<unknown>");
                else
 -                      ret = index_mem(sha1, buf, size, type, path, flags);
 +                      ret = index_mem(oid, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -              ret = index_mem(sha1, buf, size, type, path, flags);
 +              ret = index_mem(oid, buf, size, type, path, flags);
                munmap(buf, size);
        }
        return ret;
@@@ -1889,12 -1816,12 +1901,12 @@@ int index_fd(struct object_id *oid, in
         * die() for large files.
         */
        if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
 -              ret = index_stream_convert_blob(oid->hash, fd, path, flags);
 +              ret = index_stream_convert_blob(oid, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
 -              ret = index_pipe(oid->hash, fd, type, path, flags);
 +              ret = index_pipe(oid, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
                 (path && would_convert_to_git(&the_index, path)))
 -              ret = index_core(oid->hash, fd, xsize_t(st->st_size), type, path,
 +              ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
                                 flags);
        else
                ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
@@@ -1928,7 -1855,7 +1940,7 @@@ int index_path(struct object_id *oid, c
                strbuf_release(&sb);
                break;
        case S_IFDIR:
 -              return resolve_gitlink_ref(path, "HEAD", oid->hash);
 +              return resolve_gitlink_ref(path, "HEAD", oid);
        default:
                return error("%s: unsupported file type", path);
        }
@@@ -1971,7 -1898,6 +1983,7 @@@ int for_each_file_in_obj_subdir(unsigne
        DIR *dir;
        struct dirent *de;
        int r = 0;
 +      struct object_id oid;
  
        if (subdir_nr > 0xff)
                BUG("invalid loose object subdirectory: %x", subdir_nr);
        origlen = path->len;
        strbuf_complete(path, '/');
        strbuf_addf(path, "%02x", subdir_nr);
 -      baselen = path->len;
  
        dir = opendir(path->buf);
        if (!dir) {
                return r;
        }
  
 +      oid.hash[0] = subdir_nr;
 +      strbuf_addch(path, '/');
 +      baselen = path->len;
 +
        while ((de = readdir(dir))) {
 +              size_t namelen;
                if (is_dot_or_dotdot(de->d_name))
                        continue;
  
 +              namelen = strlen(de->d_name);
                strbuf_setlen(path, baselen);
 -              strbuf_addf(path, "/%s", de->d_name);
 -
 -              if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
 -                      char hex[GIT_MAX_HEXSZ+1];
 -                      struct object_id oid;
 -
 -                      xsnprintf(hex, sizeof(hex), "%02x%s",
 -                                subdir_nr, de->d_name);
 -                      if (!get_oid_hex(hex, &oid)) {
 -                              if (obj_cb) {
 -                                      r = obj_cb(&oid, path->buf, data);
 -                                      if (r)
 -                                              break;
 -                              }
 -                              continue;
 +              strbuf_add(path, de->d_name, namelen);
 +              if (namelen == GIT_SHA1_HEXSZ - 2 &&
 +                  !hex_to_bytes(oid.hash + 1, de->d_name,
 +                                GIT_SHA1_RAWSZ - 1)) {
 +                      if (obj_cb) {
 +                              r = obj_cb(&oid, path->buf, data);
 +                              if (r)
 +                                      break;
                        }
 +                      continue;
                }
  
                if (cruft_cb) {
        }
        closedir(dir);
  
 -      strbuf_setlen(path, baselen);
 +      strbuf_setlen(path, baselen - 1);
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);