Merge branch 'sp/maint-dumb-http-pack-reidx'
authorJunio C Hamano <gitster@pobox.com>
Fri, 21 May 2010 11:02:19 +0000 (04:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 21 May 2010 11:02:19 +0000 (04:02 -0700)
* sp/maint-dumb-http-pack-reidx:
http.c::new_http_pack_request: do away with the temp variable filename
http-fetch: Use temporary files for pack-*.idx until verified
http-fetch: Use index-pack rather than verify-pack to check packs
Allow parse_pack_index on temporary files
Extract verify_pack_index for reuse from verify_pack
Introduce close_pack_index to permit replacement
http.c: Remove unnecessary strdup of sha1_to_hex result
http.c: Don't store destination name in request structures
http.c: Drop useless != NULL test in finish_http_pack_request
http.c: Tiny refactoring of finish_http_pack_request
t5550-http-fetch: Use subshell for repository operations
http.c: Remove bad free of static block

1  2 
cache.h
http-walker.c
http.c
http.h
pack.h
sha1_file.c
diff --combined cache.h
index d00d2be320152cfdd2580468db27d6735dc59748,7db23eff26a7f42d2a92f8f7023e22bad3f4e661..c96602305f94b50e3f39be124c5e8d5af5a36f51
+++ b/cache.h
@@@ -387,9 -387,6 +387,9 @@@ static inline enum object_type object_t
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
  #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
  #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
 +#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
 +#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
 +#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
  
  /*
   * Repository-local GIT_* environment variables
@@@ -691,7 -688,6 +691,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 *);
@@@ -701,7 -697,7 +701,7 @@@ static inline void *read_sha1_file(cons
        return read_sha1_file_repl(sha1, type, size, NULL);
  }
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 -extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 +extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
  
@@@ -718,8 -714,6 +718,8 @@@ extern int has_loose_object_nonlocal(co
  
  extern int has_pack_index(const unsigned char *sha1);
  
 +extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
 +
  extern const signed char hexval_table[256];
  static inline unsigned int hexval(unsigned char c)
  {
@@@ -896,7 -890,6 +896,7 @@@ struct ref 
  extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
  
  #define CONNECT_VERBOSE       (1u << 0)
 +extern char *git_getpass(const char *prompt);
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
@@@ -907,7 -900,7 +907,7 @@@ struct extra_have_objects 
  extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
  
- extern struct packed_git *parse_pack_index(unsigned char *sha1);
+ extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
@@@ -918,6 -911,7 +918,7 @@@ extern struct packed_git *find_sha1_pac
  
  extern void pack_report(void);
  extern int open_pack_index(struct packed_git *);
+ extern void close_pack_index(struct packed_git *);
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
@@@ -938,15 -932,12 +939,15 @@@ extern int update_server_info(int)
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
 +extern int git_config_parse_parameter(const char *text);
 +extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
  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_maybe_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
@@@ -954,7 -945,6 +955,7 @@@ extern int git_config_set_multivar(cons
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
 +extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
@@@ -1046,7 -1036,6 +1047,7 @@@ void shift_tree_by(const unsigned char 
  #define WS_INDENT_WITH_NON_TAB        04
  #define WS_CR_AT_EOL           010
  #define WS_BLANK_AT_EOF        020
 +#define WS_TAB_IN_INDENT       040
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
  extern unsigned whitespace_rule_cfg;
@@@ -1055,7 -1044,7 +1056,7 @@@ extern unsigned parse_whitespace_rule(c
  extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
  extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
  extern char *whitespace_error_string(unsigned ws);
 -extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
 +extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
  
  /* ls-files */
@@@ -1065,7 -1054,4 +1066,7 @@@ void overlay_tree_on_cache(const char *
  char *alias_lookup(const char *alias);
  int split_cmdline(char *cmdline, const char ***argv);
  
 +/* builtin/merge.c */
 +int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
 +
  #endif /* CACHE_H */
diff --combined http-walker.c
index ef99ae647ae02995495c71455eef785bdeca1789,3a726dea0864878644d34fdc5f0afa8e6e633c95..8ca76d0507bdc1d95283e1f5fee3f88180cdce26
@@@ -510,7 -510,7 +510,7 @@@ static int fetch_object(struct walker *
                ret = error("File %s has bad hash", hex);
        } else if (req->rename < 0) {
                ret = error("unable to write sha1 filename %s",
-                           req->filename);
+                           sha1_file_name(req->sha1));
        }
  
        release_http_object_request(req);
@@@ -543,30 -543,17 +543,30 @@@ static int fetch_ref(struct walker *wal
  
  static void cleanup(struct walker *walker)
  {
 -      http_cleanup();
 +      struct walker_data *data = walker->data;
 +      struct alt_base *alt, *alt_next;
 +
 +      if (data) {
 +              alt = data->alt;
 +              while (alt) {
 +                      alt_next = alt->next;
 +
 +                      free(alt->base);
 +                      free(alt);
 +
 +                      alt = alt_next;
 +              }
 +              free(data);
 +              walker->data = NULL;
 +      }
  }
  
 -struct walker *get_http_walker(const char *url, struct remote *remote)
 +struct walker *get_http_walker(const char *url)
  {
        char *s;
        struct walker_data *data = xmalloc(sizeof(struct walker_data));
        struct walker *walker = xmalloc(sizeof(struct walker));
  
 -      http_init(remote);
 -
        data->alt = xmalloc(sizeof(*data->alt));
        data->alt->base = xmalloc(strlen(url) + 1);
        strcpy(data->alt->base, url);
diff --combined http.c
index 07a03fd79b5475f13d21f2b3a6825df7246188bf,e41fcc380034943d1f7bd80661febd42af6f4104..1320c50e32eb7b8715b263bc2af089c3dbce39fa
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -1,6 -1,7 +1,7 @@@
  #include "http.h"
  #include "pack.h"
  #include "sideband.h"
+ #include "run-command.h"
  
  int data_received;
  int active_requests;
@@@ -204,7 -205,7 +205,7 @@@ static void init_curl_http_auth(CURL *r
        if (user_name) {
                struct strbuf up = STRBUF_INIT;
                if (!user_pass)
 -                      user_pass = xstrdup(getpass("Password: "));
 +                      user_pass = xstrdup(git_getpass("Password: "));
                strbuf_addf(&up, "%s:%s", user_name, user_pass);
                curl_easy_setopt(result, CURLOPT_USERPWD,
                                 strbuf_detach(&up, NULL));
@@@ -219,7 -220,7 +220,7 @@@ static int has_cert_password(void
                return 0;
        /* Only prompt the user once. */
        ssl_cert_password_required = -1;
 -      ssl_cert_password = getpass("Certificate Password: ");
 +      ssl_cert_password = git_getpass("Certificate Password: ");
        if (ssl_cert_password != NULL) {
                ssl_cert_password = xstrdup(ssl_cert_password);
                return 1;
@@@ -720,7 -721,7 +721,7 @@@ static inline int hex(int v
                return 'A' + v - 10;
  }
  
 -static void end_url_with_slash(struct strbuf *buf, const char *url)
 +void end_url_with_slash(struct strbuf *buf, const char *url)
  {
        strbuf_addstr(buf, url);
        if (buf->len && buf->buf[buf->len - 1] != '/')
@@@ -815,21 -816,7 +816,21 @@@ static int http_request(const char *url
                        ret = HTTP_OK;
                else if (missing_target(&results))
                        ret = HTTP_MISSING_TARGET;
 -              else
 +              else if (results.http_code == 401) {
 +                      if (user_name) {
 +                              ret = HTTP_NOAUTH;
 +                      } else {
 +                              /*
 +                               * git_getpass is needed here because its very likely stdin/stdout are
 +                               * pipes to our parent process.  So we instead need to use /dev/tty,
 +                               * but that is non-portable.  Using git_getpass() can at least be stubbed
 +                               * on other platforms with a different implementation if/when necessary.
 +                               */
 +                              user_name = xstrdup(git_getpass("Username: "));
 +                              init_curl_http_auth(slot->curl);
 +                              ret = HTTP_REAUTH;
 +                      }
 +              } else
                        ret = HTTP_ERROR;
        } else {
                error("Unable to start HTTP request for %s", url);
  
  int http_get_strbuf(const char *url, struct strbuf *result, int options)
  {
 -      return http_request(url, result, HTTP_REQUEST_STRBUF, options);
 +      int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
 +      if (http_ret == HTTP_REAUTH) {
 +              http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
 +      }
 +      return http_ret;
  }
  
  /*
@@@ -914,47 -897,67 +915,67 @@@ int http_fetch_ref(const char *base, st
  }
  
  /* Helpers for fetching packs */
- static int fetch_pack_index(unsigned char *sha1, const char *base_url)
+ static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
  {
-       int ret = 0;
-       char *hex = xstrdup(sha1_to_hex(sha1));
-       char *filename;
-       char *url = NULL;
+       char *url, *tmp;
        struct strbuf buf = STRBUF_INIT;
  
-       if (has_pack_index(sha1)) {
-               ret = 0;
-               goto cleanup;
-       }
        if (http_is_verbose)
-               fprintf(stderr, "Getting index for pack %s\n", hex);
+               fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
  
        end_url_with_slash(&buf, base_url);
-       strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
+       strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
        url = strbuf_detach(&buf, NULL);
  
-       filename = sha1_pack_index_name(sha1);
-       if (http_get_file(url, filename, 0) != HTTP_OK)
-               ret = error("Unable to get pack index %s\n", url);
+       strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+       tmp = strbuf_detach(&buf, NULL);
+       if (http_get_file(url, tmp, 0) != HTTP_OK) {
+               error("Unable to get pack index %s\n", url);
+               free(tmp);
+               tmp = NULL;
+       }
  
- cleanup:
-       free(hex);
        free(url);
-       return ret;
+       return tmp;
  }
  
  static int fetch_and_setup_pack_index(struct packed_git **packs_head,
        unsigned char *sha1, const char *base_url)
  {
        struct packed_git *new_pack;
+       char *tmp_idx = NULL;
+       int ret;
  
-       if (fetch_pack_index(sha1, base_url))
+       if (has_pack_index(sha1)) {
+               new_pack = parse_pack_index(sha1, NULL);
+               if (!new_pack)
+                       return -1; /* parse_pack_index() already issued error message */
+               goto add_pack;
+       }
+       tmp_idx = fetch_pack_index(sha1, base_url);
+       if (!tmp_idx)
                return -1;
  
-       new_pack = parse_pack_index(sha1);
-       if (!new_pack)
+       new_pack = parse_pack_index(sha1, tmp_idx);
+       if (!new_pack) {
+               unlink(tmp_idx);
+               free(tmp_idx);
                return -1; /* parse_pack_index() already issued error message */
+       }
+       ret = verify_pack_index(new_pack);
+       if (!ret) {
+               close_pack_index(new_pack);
+               ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
+       }
+       free(tmp_idx);
+       if (ret)
+               return -1;
+ add_pack:
        new_pack->next = *packs_head;
        *packs_head = new_pack;
        return 0;
@@@ -1018,37 -1021,62 +1039,62 @@@ void release_http_pack_request(struct h
  
  int finish_http_pack_request(struct http_pack_request *preq)
  {
-       int ret;
        struct packed_git **lst;
+       struct packed_git *p = preq->target;
+       char *tmp_idx;
+       struct child_process ip;
+       const char *ip_argv[8];
  
-       preq->target->pack_size = ftell(preq->packfile);
+       close_pack_index(p);
  
-       if (preq->packfile != NULL) {
-               fclose(preq->packfile);
-               preq->packfile = NULL;
-               preq->slot->local = NULL;
-       }
-       ret = move_temp_to_file(preq->tmpfile, preq->filename);
-       if (ret)
-               return ret;
+       fclose(preq->packfile);
+       preq->packfile = NULL;
+       preq->slot->local = NULL;
  
        lst = preq->lst;
-       while (*lst != preq->target)
+       while (*lst != p)
                lst = &((*lst)->next);
        *lst = (*lst)->next;
  
-       if (verify_pack(preq->target))
+       tmp_idx = xstrdup(preq->tmpfile);
+       strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
+              ".idx.temp");
+       ip_argv[0] = "index-pack";
+       ip_argv[1] = "-o";
+       ip_argv[2] = tmp_idx;
+       ip_argv[3] = preq->tmpfile;
+       ip_argv[4] = NULL;
+       memset(&ip, 0, sizeof(ip));
+       ip.argv = ip_argv;
+       ip.git_cmd = 1;
+       ip.no_stdin = 1;
+       ip.no_stdout = 1;
+       if (run_command(&ip)) {
+               unlink(preq->tmpfile);
+               unlink(tmp_idx);
+               free(tmp_idx);
+               return -1;
+       }
+       unlink(sha1_pack_index_name(p->sha1));
+       if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
+        || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+               free(tmp_idx);
                return -1;
-       install_packed_git(preq->target);
+       }
  
+       install_packed_git(p);
+       free(tmp_idx);
        return 0;
  }
  
  struct http_pack_request *new_http_pack_request(
        struct packed_git *target, const char *base_url)
  {
-       char *filename;
        long prev_posn = 0;
        char range[RANGE_HEADER_SIZE];
        struct strbuf buf = STRBUF_INIT;
                sha1_to_hex(target->sha1));
        preq->url = strbuf_detach(&buf, NULL);
  
-       filename = sha1_pack_name(target->sha1);
-       snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
-       snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
+       snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
+               sha1_pack_name(target->sha1));
        preq->packfile = fopen(preq->tmpfile, "a");
        if (!preq->packfile) {
                error("Unable to open local file %s for pack",
        return preq;
  
  abort:
-       free(filename);
        free(preq->url);
        free(preq);
        return NULL;
@@@ -1155,7 -1181,6 +1199,6 @@@ struct http_object_request *new_http_ob
        freq->localfile = -1;
  
        filename = sha1_file_name(sha1);
-       snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
        snprintf(freq->tmpfile, sizeof(freq->tmpfile),
                 "%s.temp", filename);
  
        }
  
        if (freq->localfile < 0) {
-               error("Couldn't create temporary file %s for %s: %s",
-                     freq->tmpfile, freq->filename, strerror(errno));
+               error("Couldn't create temporary file %s: %s",
+                     freq->tmpfile, strerror(errno));
                goto abort;
        }
  
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
                        if (ftruncate(freq->localfile, 0) < 0) {
-                               error("Couldn't truncate temporary file %s for %s: %s",
-                                         freq->tmpfile, freq->filename, strerror(errno));
+                               error("Couldn't truncate temporary file %s: %s",
+                                         freq->tmpfile, strerror(errno));
                                goto abort;
                        }
                }
@@@ -1309,7 -1334,7 +1352,7 @@@ int finish_http_object_request(struct h
                return -1;
        }
        freq->rename =
-               move_temp_to_file(freq->tmpfile, freq->filename);
+               move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
  
        return freq->rename;
  }
diff --combined http.h
index 37a6a6a3b0cd0ddbbf395230f04b94fc54191dbe,84bdbd0f76787090a1ec511a006b1c9885521d71..a0b59015948c3b9736dd6c09fd9137f7ecd4f59a
--- 1/http.h
--- 2/http.h
+++ b/http.h
@@@ -117,7 -117,6 +117,7 @@@ extern void append_remote_object_url(st
                                     int only_two_digit_prefix);
  extern char *get_remote_object_url(const char *url, const char *hex,
                                   int only_two_digit_prefix);
 +extern void end_url_with_slash(struct strbuf *buf, const char *url);
  
  /* Options for http_request_*() */
  #define HTTP_NO_CACHE         1
  #define HTTP_MISSING_TARGET   1
  #define HTTP_ERROR            2
  #define HTTP_START_FAILED     3
 +#define HTTP_REAUTH   4
 +#define HTTP_NOAUTH   5
  
  /*
   * Requests an url and stores the result in a strbuf.
@@@ -155,7 -152,6 +155,6 @@@ struct http_pack_reques
        struct packed_git *target;
        struct packed_git **lst;
        FILE *packfile;
-       char filename[PATH_MAX];
        char tmpfile[PATH_MAX];
        struct curl_slist *range_header;
        struct active_request_slot *slot;
@@@ -170,7 -166,6 +169,6 @@@ extern void release_http_pack_request(s
  struct http_object_request
  {
        char *url;
-       char filename[PATH_MAX];
        char tmpfile[PATH_MAX];
        int localfile;
        CURLcode curl_result;
diff --combined pack.h
index d268c014c9eb7040bd65125b13d68edce670274b,880f9c2930c98ee1cd28f8143a2dc94f10d4f530..bb275762b7eb6f473f333ae40780821e383db20b
--- 1/pack.h
--- 2/pack.h
+++ b/pack.h
@@@ -57,10 -57,10 +57,11 @@@ struct pack_idx_entry 
  
  extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
  extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
+ extern int verify_pack_index(struct packed_git *);
  extern int verify_pack(struct packed_git *);
  extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
  extern char *index_pack_lockfile(int fd);
 +extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned char *);
  
  #define PH_ERROR_EOF          (-1)
  #define PH_ERROR_PACK_SIGNATURE       (-2)
diff --combined sha1_file.c
index b2074e7a8ee6d55fdb1094b93a2954bd164d5445,9f3f514e713cf7d1e2efdbe5de093671cc6b8fdf..d8e61a65d10ed6fa792d207e36fb6b245ef86970
@@@ -35,6 -35,13 +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)
 -{
 -      if (has_dos_drive_prefix(path))
 -              return 2 + (path[2] == '/');
 -      return *path == '/';
 -}
 -
  int safe_create_leading_directories(char *path)
  {
        char *pos = path + offset_1st_component(path);
@@@ -599,6 -606,14 +599,14 @@@ void unuse_pack(struct pack_window **w_
        }
  }
  
+ void close_pack_index(struct packed_git *p)
+ {
+       if (p->index_data) {
+               munmap((void *)p->index_data, p->index_size);
+               p->index_data = NULL;
+       }
+ }
  /*
   * This is used by git-repack in case a newly created pack happens to
   * contain the same set of objects as an existing one.  In that case
@@@ -620,8 -635,7 +628,7 @@@ void free_pack_by_name(const char *pack
                        close_pack_windows(p);
                        if (p->pack_fd != -1)
                                close(p->pack_fd);
-                       if (p->index_data)
-                               munmap((void *)p->index_data, p->index_size);
+                       close_pack_index(p);
                        free(p->bad_object_sha1);
                        *pp = p->next;
                        free(p);
@@@ -831,9 -845,8 +838,8 @@@ struct packed_git *add_packed_git(cons
        return p;
  }
  
- struct packed_git *parse_pack_index(unsigned char *sha1)
+ struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
  {
-       const char *idx_path = sha1_pack_index_name(sha1);
        const char *path = sha1_pack_name(sha1);
        struct packed_git *p = alloc_packed_git(strlen(path) + 1);
  
@@@ -2271,7 -2284,7 +2277,7 @@@ static int create_tmpfile(char *buffer
  }
  
  static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 -                            void *buf, unsigned long len, time_t mtime)
 +                            const void *buf, unsigned long len, time_t mtime)
  {
        int fd, ret;
        unsigned char compressed[4096];
        git_SHA1_Update(&c, hdr, hdrlen);
  
        /* Then the data itself.. */
 -      stream.next_in = buf;
 +      stream.next_in = (void *)buf;
        stream.avail_in = len;
        do {
                unsigned char *in0 = stream.next_in;
        return move_temp_to_file(tmpfile, filename);
  }
  
 -int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 +int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
  {
        unsigned char sha1[20];
        char hdr[32];
@@@ -2448,8 -2461,6 +2454,8 @@@ int index_fd(unsigned char *sha1, int f
                else
                        ret = -1;
                strbuf_release(&sbuf);
 +      } else if (!size) {
 +              ret = index_mem(sha1, NULL, size, write_object, type, path);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
                else
                        ret = error("short read %s", strerror(errno));
                free(buf);
 -      } else if (size) {
 +      } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
                ret = index_mem(sha1, buf, size, write_object, type, path);
                munmap(buf, size);
 -      } else
 -              ret = index_mem(sha1, NULL, size, write_object, type, path);
 +      }
        close(fd);
        return ret;
  }
@@@ -2517,13 -2529,3 +2523,13 @@@ int read_pack_header(int fd, struct pac
                return PH_ERROR_PROTOCOL;
        return 0;
  }
 +
 +void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
 +{
 +      enum object_type type = sha1_object_info(sha1, NULL);
 +      if (type < 0)
 +              die("%s is not a valid object", sha1_to_hex(sha1));
 +      if (type != expect)
 +              die("%s is not a valid '%s' object", sha1_to_hex(sha1),
 +                  typename(expect));
 +}