Merge branch 'jk/pkt-line-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Mon, 1 Apr 2013 15:59:37 +0000 (08:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Apr 2013 15:59:37 +0000 (08:59 -0700)
Clean up pkt-line API, implementation and its callers to make them
more robust.

* jk/pkt-line-cleanup:
do not use GIT_TRACE_PACKET=3 in tests
remote-curl: always parse incoming refs
remote-curl: move ref-parsing code up in file
remote-curl: pass buffer straight to get_remote_heads
teach get_remote_heads to read from a memory buffer
pkt-line: share buffer/descriptor reading implementation
pkt-line: provide a LARGE_PACKET_MAX static buffer
pkt-line: move LARGE_PACKET_MAX definition from sideband
pkt-line: teach packet_read_line to chomp newlines
pkt-line: provide a generic reading function with options
pkt-line: drop safe_write function
pkt-line: move a misplaced comment
write_or_die: raise SIGPIPE when we get EPIPE
upload-archive: use argv_array to store client arguments
upload-archive: do not copy repo name
send-pack: prefer prefixcmp over memcmp in receive_status
fetch-pack: fix out-of-bounds buffer offset in get_ack
upload-pack: remove packet debugging harness
upload-pack: do not add duplicate objects to shallow list
upload-pack: use get_sha1_hex to parse "shallow" lines

1  2 
builtin/fetch-pack.c
cache.h
daemon.c
fetch-pack.c
transport.c
upload-pack.c
diff --combined builtin/fetch-pack.c
index 670e81fd995953bff9c29cc6fab99d03f1fee8b7,03ed2caae33886078b8182ef46c33f07cf3d56a0..aba44655524ff722d90de09760945f5f30088752
@@@ -7,31 -7,12 +7,31 @@@ static const char fetch_pack_usage[] 
  "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
  "[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
  
 +static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
 +                               const char *name, int namelen)
 +{
 +      struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
 +
 +      memcpy(ref->name, name, namelen);
 +      ref->name[namelen] = '\0';
 +      (*nr)++;
 +      ALLOC_GROW(*sought, *nr, *alloc);
 +      (*sought)[*nr - 1] = ref;
 +}
 +
 +static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
 +                           const char *string)
 +{
 +      add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
 +}
 +
  int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
  {
        int i, ret;
        struct ref *ref = NULL;
        const char *dest = NULL;
 -      struct string_list sought = STRING_LIST_INIT_DUP;
 +      struct ref **sought = NULL;
 +      int nr_sought = 0, alloc_sought = 0;
        int fd[2];
        char *pack_lockfile = NULL;
        char **pack_lockfile_ptr = NULL;
         * refs from the standard input:
         */
        for (; i < argc; i++)
 -              string_list_append(&sought, xstrdup(argv[i]));
 +              add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]);
        if (args.stdin_refs) {
                if (args.stateless_rpc) {
                        /* in stateless RPC mode we use pkt-line to read
                         * from stdin, until we get a flush packet
                         */
-                       static char line[1000];
                        for (;;) {
-                               int n = packet_read_line(0, line, sizeof(line));
-                               if (!n)
+                               char *line = packet_read_line(0, NULL);
+                               if (!line)
                                        break;
-                               if (line[n-1] == '\n')
-                                       n--;
-                               add_sought_entry_mem(&sought, &nr_sought,  &alloc_sought, line, n);
 -                              string_list_append(&sought, xstrdup(line));
++                              add_sought_entry(&sought, &nr_sought,  &alloc_sought, line);
                        }
                }
                else {
                        /* read from stdin one ref per line, until EOF */
                        struct strbuf line = STRBUF_INIT;
                        while (strbuf_getline(&line, stdin, '\n') != EOF)
 -                              string_list_append(&sought, strbuf_detach(&line, NULL));
 +                              add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
                        strbuf_release(&line);
                }
        }
                                   args.verbose ? CONNECT_VERBOSE : 0);
        }
  
-       get_remote_heads(fd[0], &ref, 0, NULL);
+       get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
  
        ref = fetch_pack(&args, fd, conn, ref, dest,
 -                       &sought, pack_lockfile_ptr);
 +                       sought, nr_sought, pack_lockfile_ptr);
        if (pack_lockfile) {
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
        if (finish_connect(conn))
                return 1;
  
 -      ret = !ref || sought.nr;
 +      ret = !ref;
  
        /*
         * If the heads to pull were given, we should have consumed
         * remote no-such-ref' would silently succeed without issuing
         * an error.
         */
 -      for (i = 0; i < sought.nr; i++)
 -              error("no such remote ref %s", sought.items[i].string);
 +      for (i = 0; i < nr_sought; i++) {
 +              if (!sought[i] || sought[i]->matched)
 +                      continue;
 +              error("no such remote ref %s", sought[i]->name);
 +              ret = 1;
 +      }
 +
        while (ref) {
                printf("%s %s\n",
                       sha1_to_hex(ref->old_sha1), ref->name);
diff --combined cache.h
index 59e5b5317910c2cd41a00d694f0de50b3d6f40f7,db646a2ff87ee64d42bccd506456f983ac31062f..bcdb3edbc4c10a8d87c075f831a9177e60b13251
+++ b/cache.h
@@@ -34,7 -34,6 +34,7 @@@ int git_inflate(git_zstream *, int flus
  
  void git_deflate_init(git_zstream *, int level);
  void git_deflate_init_gzip(git_zstream *, int level);
 +void git_deflate_init_raw(git_zstream *, int level);
  void git_deflate_end(git_zstream *);
  int git_deflate_abort(git_zstream *);
  int git_deflate_end_gently(git_zstream *);
@@@ -342,11 -341,9 +342,11 @@@ static inline enum object_type object_t
                OBJ_BLOB;
  }
  
 +/* Double-check local_repo_env below if you add to this list. */
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
  #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 +#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
  
  /*
 - * Repository-local GIT_* environment variables
 - * The array is NULL-terminated to simplify its usage in contexts such
 - * environment creation or simple walk of the list.
 - * The number of non-NULL entries is available as a macro.
 + * This environment variable is expected to contain a boolean indicating
 + * whether we should or should not treat:
 + *
 + *   GIT_DIR=foo.git git ...
 + *
 + * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
 + * of this, but we use it internally to communicate to sub-processes that we
 + * are in a bare repo. If not set, defaults to true.
 + */
 +#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
 +
 +/*
 + * Repository-local GIT_* environment variables; these will be cleared
 + * when git spawns a sub-process that runs inside another repository.
 + * The array is NULL-terminated, which makes it easy to pass in the "env"
 + * parameter of a run-command invocation, or to do a simple walk.
   */
 -#define LOCAL_REPO_ENV_SIZE 9
 -extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
 +extern const char * const local_repo_env[];
  
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
@@@ -1031,8 -1017,7 +1031,8 @@@ struct ref 
                force:1,
                forced_update:1,
                merge:1,
 -              deletion:1;
 +              deletion:1,
 +              matched:1;
        enum {
                REF_STATUS_NONE = 0,
                REF_STATUS_OK,
@@@ -1064,7 -1049,9 +1064,9 @@@ struct extra_have_objects 
        int nr, alloc;
        unsigned char (*array)[20];
  };
- extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
+ extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
+                                    struct ref **list, unsigned int flags,
+                                    struct extra_have_objects *);
  extern int server_supports(const char *feature);
  extern int parse_feature_request(const char *features, const char *feature);
  extern const char *server_feature_value(const char *feature, int *len_ret);
@@@ -1072,9 -1059,6 +1074,9 @@@ extern const char *parse_feature_value(
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
 +/* A hook for count-objects to report invalid files in pack directory */
 +extern void (*report_garbage)(const char *desc, const char *path);
 +
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
  extern void install_packed_git(struct packed_git *pack);
diff --combined daemon.c
index df8c0ab0588e70ad6e6f56195535030055782d85,82d5bf50e90cd28997e8610f5f8584d4250e9459..6aeddcb98d2694a705d34e80293fc81a20bc39a8
+++ b/daemon.c
@@@ -9,6 -9,10 +9,6 @@@
  #define HOST_NAME_MAX 256
  #endif
  
 -#ifndef NI_MAXSERV
 -#define NI_MAXSERV 32
 -#endif
 -
  #ifdef NO_INITGROUPS
  #define initgroups(x, y) (0) /* nothing */
  #endif
@@@ -600,7 -604,7 +600,7 @@@ static void parse_host_arg(char *extra_
  
  static int execute(void)
  {
-       static char line[1000];
+       char *line = packet_buffer;
        int pktlen, len, i;
        char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
  
                loginfo("Connection from %s:%s", addr, port);
  
        alarm(init_timeout ? init_timeout : timeout);
-       pktlen = packet_read_line(0, line, sizeof(line));
+       pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
        alarm(0);
  
        len = strlen(line);
diff --combined fetch-pack.c
index cef8fde61bf4516f95ae13b05f73cb26a268f90c,66ff9add89a43e7d22dc6761371fb4532b8b7123..f156dd4fac30cda4e09c508b7091cdb8d96917e2
@@@ -36,7 -36,7 +36,7 @@@ static int marked
  #define MAX_IN_VAIN 256
  
  static struct commit_list *rev_list;
 -static int non_common_revs, multi_ack, use_sideband;
 +static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
  
  static void rev_list_push(struct commit *commit, int mark)
  {
@@@ -172,8 -172,8 +172,8 @@@ static void consume_shallow_list(struc
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
                 */
-               char line[1000];
-               while (packet_read_line(fd, line, sizeof(line))) {
+               char *line;
+               while ((line = packet_read_line(fd, NULL))) {
                        if (!prefixcmp(line, "shallow "))
                                continue;
                        if (!prefixcmp(line, "unshallow "))
@@@ -215,17 -215,17 +215,17 @@@ static int write_shallow_commits(struc
  
  static enum ack_type get_ack(int fd, unsigned char *result_sha1)
  {
-       static char line[1000];
-       int len = packet_read_line(fd, line, sizeof(line));
+       int len;
+       char *line = packet_read_line(fd, &len);
  
        if (!len)
                die("git fetch-pack: expected ACK/NAK, got EOF");
-       if (line[len-1] == '\n')
-               line[--len] = 0;
        if (!strcmp(line, "NAK"))
                return NAK;
        if (!prefixcmp(line, "ACK ")) {
                if (!get_sha1_hex(line+4, result_sha1)) {
+                       if (len < 45)
+                               return ACK;
                        if (strstr(line+45, "continue"))
                                return ACK_continue;
                        if (strstr(line+45, "common"))
@@@ -245,7 -245,7 +245,7 @@@ static void send_request(struct fetch_p
                send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
                packet_flush(fd);
        } else
-               safe_write(fd, buf->buf, buf->len);
+               write_or_die(fd, buf->buf, buf->len);
  }
  
  static void insert_one_alternate_ref(const struct ref *ref, void *unused)
@@@ -346,11 -346,11 +346,11 @@@ static int find_common(struct fetch_pac
        state_len = req_buf.len;
  
        if (args->depth > 0) {
-               char line[1024];
+               char *line;
                unsigned char sha1[20];
  
                send_request(args, fd[1], &req_buf);
-               while (packet_read_line(fd[0], line, sizeof(line))) {
+               while ((line = packet_read_line(fd[0], NULL))) {
                        if (!prefixcmp(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
                                        die("invalid shallow line: %s", line);
@@@ -520,37 -520,47 +520,37 @@@ static void mark_recent_complete_commit
        }
  }
  
 -static int non_matching_ref(struct string_list_item *item, void *unused)
 -{
 -      if (item->util) {
 -              item->util = NULL;
 -              return 0;
 -      }
 -      else
 -              return 1;
 -}
 -
  static void filter_refs(struct fetch_pack_args *args,
 -                      struct ref **refs, struct string_list *sought)
 +                      struct ref **refs,
 +                      struct ref **sought, int nr_sought)
  {
        struct ref *newlist = NULL;
        struct ref **newtail = &newlist;
        struct ref *ref, *next;
 -      int sought_pos;
 +      int i;
  
 -      sought_pos = 0;
 +      i = 0;
        for (ref = *refs; ref; ref = next) {
                int keep = 0;
                next = ref->next;
 +
                if (!memcmp(ref->name, "refs/", 5) &&
                    check_refname_format(ref->name + 5, 0))
                        ; /* trash */
                else {
 -                      while (sought_pos < sought->nr) {
 -                              int cmp = strcmp(ref->name, sought->items[sought_pos].string);
 +                      while (i < nr_sought) {
 +                              int cmp = strcmp(ref->name, sought[i]->name);
                                if (cmp < 0)
                                        break; /* definitely do not have it */
                                else if (cmp == 0) {
                                        keep = 1; /* definitely have it */
 -                                      sought->items[sought_pos++].util = "matched";
 -                                      break;
 +                                      sought[i]->matched = 1;
                                }
 -                              else
 -                                      sought_pos++; /* might have it; keep looking */
 +                              i++;
                        }
                }
  
 -              if (! keep && args->fetch_all &&
 +              if (!keep && args->fetch_all &&
                    (!args->depth || prefixcmp(ref->name, "refs/tags/")))
                        keep = 1;
  
                }
        }
  
 -      filter_string_list(sought, 0, non_matching_ref, NULL);
 +      /* Append unmatched requests to the list */
 +      if (allow_tip_sha1_in_want) {
 +              for (i = 0; i < nr_sought; i++) {
 +                      ref = sought[i];
 +                      if (ref->matched)
 +                              continue;
 +                      if (get_sha1_hex(ref->name, ref->old_sha1))
 +                              continue;
 +
 +                      ref->matched = 1;
 +                      *newtail = ref;
 +                      ref->next = NULL;
 +                      newtail = &ref->next;
 +              }
 +      }
        *refs = newlist;
  }
  
@@@ -587,8 -583,7 +587,8 @@@ static void mark_alternate_complete(con
  }
  
  static int everything_local(struct fetch_pack_args *args,
 -                          struct ref **refs, struct string_list *sought)
 +                          struct ref **refs,
 +                          struct ref **sought, int nr_sought)
  {
        struct ref *ref;
        int retval;
                }
        }
  
 -      filter_refs(args, refs, sought);
 +      filter_refs(args, refs, sought, nr_sought);
  
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
                const unsigned char *remote = ref->old_sha1;
@@@ -772,17 -767,10 +772,17 @@@ static int get_pack(struct fetch_pack_a
        return 0;
  }
  
 +static int cmp_ref_by_name(const void *a_, const void *b_)
 +{
 +      const struct ref *a = *((const struct ref **)a_);
 +      const struct ref *b = *((const struct ref **)b_);
 +      return strcmp(a->name, b->name);
 +}
 +
  static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                                 int fd[2],
                                 const struct ref *orig_ref,
 -                               struct string_list *sought,
 +                               struct ref **sought, int nr_sought,
                                 char **pack_lockfile)
  {
        struct ref *ref = copy_ref_list(orig_ref);
        int agent_len;
  
        sort_ref_list(&ref, ref_compare_name);
 +      qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
  
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
 +      if (server_supports("allow-tip-sha1-in-want")) {
 +              if (args->verbose)
 +                      fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
 +              allow_tip_sha1_in_want = 1;
 +      }
        if (!server_supports("thin-pack"))
                args->use_thin_pack = 0;
        if (!server_supports("no-progress"))
                                agent_len, agent_feature);
        }
  
 -      if (everything_local(args, &ref, sought)) {
 +      if (everything_local(args, &ref, sought, nr_sought)) {
                packet_flush(fd[1]);
                goto all_done;
        }
@@@ -908,32 -890,11 +908,32 @@@ static void fetch_pack_setup(void
        did_setup = 1;
  }
  
 +static int remove_duplicates_in_refs(struct ref **ref, int nr)
 +{
 +      struct string_list names = STRING_LIST_INIT_NODUP;
 +      int src, dst;
 +
 +      for (src = dst = 0; src < nr; src++) {
 +              struct string_list_item *item;
 +              item = string_list_insert(&names, ref[src]->name);
 +              if (item->util)
 +                      continue; /* already have it */
 +              item->util = ref[src];
 +              if (src != dst)
 +                      ref[dst] = ref[src];
 +              dst++;
 +      }
 +      for (src = dst; src < nr; src++)
 +              ref[src] = NULL;
 +      string_list_clear(&names, 0);
 +      return dst;
 +}
 +
  struct ref *fetch_pack(struct fetch_pack_args *args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
                       const char *dest,
 -                     struct string_list *sought,
 +                     struct ref **sought, int nr_sought,
                       char **pack_lockfile)
  {
        struct stat st;
                        st.st_mtime = 0;
        }
  
 -      if (sought->nr) {
 -              sort_string_list(sought);
 -              string_list_remove_duplicates(sought, 0);
 -      }
 +      if (nr_sought)
 +              nr_sought = remove_duplicates_in_refs(sought, nr_sought);
  
        if (!ref) {
                packet_flush(fd[1]);
                die("no matching remote head");
        }
 -      ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
 +      ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
  
        if (args->depth > 0) {
                static struct lock_file lock;
diff --combined transport.c
index eb9eb33e0f0f151156ca646bb998c538f2471d7f,62df466c1a82705c3e1d0cb21aa6f316710f0cfe..ba5d8afb1b04ba9d331c721fd9f730184fff2f23
@@@ -106,8 -106,7 +106,8 @@@ static void insert_packed_refs(const ch
                return;
  
        for (;;) {
 -              int cmp = cmp, len;
 +              int cmp = 0; /* assigned before used */
 +              int len;
  
                if (!fgets(buffer, sizeof(buffer), f)) {
                        fclose(f);
@@@ -508,7 -507,7 +508,7 @@@ static struct ref *get_refs_via_connect
        struct ref *refs;
  
        connect_setup(transport, for_push, 0);
-       get_remote_heads(data->fd[0], &refs,
+       get_remote_heads(data->fd[0], NULL, 0, &refs,
                         for_push ? REF_NORMAL : 0, &data->extra_have);
        data->got_remote_heads = 1;
  
@@@ -519,9 -518,11 +519,9 @@@ static int fetch_refs_via_pack(struct t
                               int nr_heads, struct ref **to_fetch)
  {
        struct git_transport_data *data = transport->data;
 -      struct string_list sought = STRING_LIST_INIT_DUP;
        const struct ref *refs;
        char *dest = xstrdup(transport->url);
        struct fetch_pack_args args;
 -      int i;
        struct ref *refs_tmp = NULL;
  
        memset(&args, 0, sizeof(args));
        args.no_progress = !transport->progress;
        args.depth = data->options.depth;
  
 -      for (i = 0; i < nr_heads; i++)
 -              string_list_append(&sought, to_fetch[i]->name);
 -
        if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
-               get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
+               get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL);
                data->got_remote_heads = 1;
        }
  
        refs = fetch_pack(&args, data->fd, data->conn,
                          refs_tmp ? refs_tmp : transport->remote_refs,
 -                        dest, &sought, &transport->pack_lockfile);
 +                        dest, to_fetch, nr_heads,
 +                        &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
        if (finish_connect(data->conn))
  
        free_refs(refs_tmp);
  
 -      string_list_clear(&sought, 0);
        free(dest);
        return (refs ? 0 : -1);
  }
@@@ -795,7 -799,7 +795,7 @@@ static int git_transport_push(struct tr
                struct ref *tmp_refs;
                connect_setup(transport, 1, 0);
  
-               get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
+               get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL);
                data->got_remote_heads = 1;
        }
  
@@@ -1128,8 -1132,6 +1128,8 @@@ int transport_push(struct transport *tr
                        match_flags |= MATCH_REFS_MIRROR;
                if (flags & TRANSPORT_PUSH_PRUNE)
                        match_flags |= MATCH_REFS_PRUNE;
 +              if (flags & TRANSPORT_PUSH_FOLLOW_TAGS)
 +                      match_flags |= MATCH_REFS_FOLLOW_TAGS;
  
                if (match_push_refs(local_refs, &remote_refs,
                                    refspec_nr, refspec, match_flags)) {
diff --combined upload-pack.c
index f5673ee4c2d65787040658c5e3ee06ecd129636d,98ddb6958180033c0936884d5dd040148b681950..bfa6279cc418278079bd95854adfe8d8301b6788
@@@ -26,7 -26,6 +26,7 @@@ static const char upload_pack_usage[] 
  #define SHALLOW               (1u << 16)
  #define NOT_SHALLOW   (1u << 17)
  #define CLIENT_SHALLOW        (1u << 18)
 +#define HIDDEN_REF    (1u << 19)
  
  static unsigned long oldest_have;
  
@@@ -34,7 -33,6 +34,7 @@@ static int multi_ack
  static int no_done;
  static int use_thin_pack, use_ofs_delta, use_include_tag;
  static int no_progress, daemon_mode;
 +static int allow_tip_sha1_in_want;
  static int shallow_nr;
  static struct object_array have_obj;
  static struct object_array want_obj;
@@@ -44,7 -42,6 +44,6 @@@ static unsigned int timeout
   * otherwise maximum packet size (up to 65520 bytes).
   */
  static int use_sideband;
- static int debug_fd;
  static int advertise_refs;
  static int stateless_rpc;
  
@@@ -53,13 -50,6 +52,6 @@@ static void reset_timeout(void
        alarm(timeout);
  }
  
- static int strip(char *line, int len)
- {
-       if (len && line[len-1] == '\n')
-               line[--len] = 0;
-       return len;
- }
  static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
  {
        if (use_sideband)
@@@ -72,7 -62,8 +64,8 @@@
                xwrite(fd, data, sz);
                return sz;
        }
-       return safe_write(fd, data, sz);
+       write_or_die(fd, data, sz);
+       return sz;
  }
  
  static FILE *pack_pipe = NULL;
@@@ -327,7 -318,9 +320,7 @@@ static int got_sha1(char *hex, unsigne
        if (!has_sha1_file(sha1))
                return -1;
  
 -      o = lookup_object(sha1);
 -      if (!(o && o->parsed))
 -              o = parse_object(sha1);
 +      o = parse_object(sha1);
        if (!o)
                die("oops (%s)", sha1_to_hex(sha1));
        if (o->type == OBJ_COMMIT) {
@@@ -415,7 -408,6 +408,6 @@@ static int ok_to_give_up(void
  
  static int get_common_commits(void)
  {
-       static char line[1000];
        unsigned char sha1[20];
        char last_hex[41];
        int got_common = 0;
        save_commit_buffer = 0;
  
        for (;;) {
-               int len = packet_read_line(0, line, sizeof(line));
+               char *line = packet_read_line(0, NULL);
                reset_timeout();
  
-               if (!len) {
+               if (!line) {
                        if (multi_ack == 2 && got_common
                            && !got_other && ok_to_give_up()) {
                                sent_ready = 1;
                        got_other = 0;
                        continue;
                }
-               strip(line, len);
                if (!prefixcmp(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
        }
  }
  
 +static int is_our_ref(struct object *o)
 +{
 +      return o->flags &
 +              ((allow_tip_sha1_in_want ? HIDDEN_REF : 0) | OUR_REF);
 +}
 +
  static void check_non_tip(void)
  {
        static const char *argv[] = {
                o = get_indexed_object(--i);
                if (!o)
                        continue;
 -              if (!(o->flags & OUR_REF))
 +              if (!is_our_ref(o))
                        continue;
                memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
                if (write_in_full(cmd.in, namebuf, 42) < 0)
        namebuf[40] = '\n';
        for (i = 0; i < want_obj.nr; i++) {
                o = want_obj.objects[i].item;
 -              if (o->flags & OUR_REF)
 +              if (is_our_ref(o))
                        continue;
                memcpy(namebuf, sha1_to_hex(o->sha1), 40);
                if (write_in_full(cmd.in, namebuf, 41) < 0)
@@@ -572,7 -557,7 +563,7 @@@ error
        /* Pick one of them (we know there at least is one) */
        for (i = 0; i < want_obj.nr; i++) {
                o = want_obj.objects[i].item;
 -              if (!(o->flags & OUR_REF))
 +              if (!is_our_ref(o))
                        die("git upload-pack: not our ref %s",
                            sha1_to_hex(o->sha1));
        }
  static void receive_needs(void)
  {
        struct object_array shallows = OBJECT_ARRAY_INIT;
-       static char line[1000];
-       int len, depth = 0;
+       int depth = 0;
        int has_non_tip = 0;
  
        shallow_nr = 0;
-       if (debug_fd)
-               write_str_in_full(debug_fd, "#S\n");
        for (;;) {
                struct object *o;
                const char *features;
                unsigned char sha1_buf[20];
-               len = packet_read_line(0, line, sizeof(line));
+               char *line = packet_read_line(0, NULL);
                reset_timeout();
-               if (!len)
+               if (!line)
                        break;
-               if (debug_fd)
-                       write_in_full(debug_fd, line, len);
  
                if (!prefixcmp(line, "shallow ")) {
                        unsigned char sha1[20];
                        struct object *object;
-                       if (get_sha1(line + 8, sha1))
+                       if (get_sha1_hex(line + 8, sha1))
                                die("invalid shallow line: %s", line);
                        object = parse_object(sha1);
                        if (!object)
                                die("did not find object for %s", line);
                        if (object->type != OBJ_COMMIT)
                                die("invalid shallow object %s", sha1_to_hex(sha1));
-                       object->flags |= CLIENT_SHALLOW;
-                       add_object_array(object, NULL, &shallows);
+                       if (!(object->flags & CLIENT_SHALLOW)) {
+                               object->flags |= CLIENT_SHALLOW;
+                               add_object_array(object, NULL, &shallows);
+                       }
                        continue;
                }
                if (!prefixcmp(line, "deepen ")) {
                if (parse_feature_request(features, "include-tag"))
                        use_include_tag = 1;
  
 -              o = lookup_object(sha1_buf);
 +              o = parse_object(sha1_buf);
                if (!o)
                        die("git upload-pack: not our ref %s",
                            sha1_to_hex(sha1_buf));
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
 -                      if (!(o->flags & OUR_REF))
 +                      if (!is_our_ref(o))
                                has_non_tip = 1;
                        add_object_array(o, NULL, &want_obj);
                }
        }
-       if (debug_fd)
-               write_str_in_full(debug_fd, "#E\n");
  
        /*
         * We have sent all our refs already, and the other end
@@@ -738,10 -718,8 +724,10 @@@ static int mark_our_ref(const char *ref
  {
        struct object *o = lookup_unknown_object(sha1);
  
 -      if (ref_is_hidden(refname))
 +      if (ref_is_hidden(refname)) {
 +              o->flags |= HIDDEN_REF;
                return 1;
 +      }
        if (!o)
                die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
        o->flags |= OUR_REF;
@@@ -760,10 -738,9 +746,10 @@@ static int send_ref(const char *refname
                return 0;
  
        if (capabilities)
 -              packet_write(1, "%s %s%c%s%s agent=%s\n",
 +              packet_write(1, "%s %s%c%s%s%s agent=%s\n",
                             sha1_to_hex(sha1), refname_nons,
                             0, capabilities,
 +                           allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
                             stateless_rpc ? " no-done" : "",
                             git_user_agent_sanitized());
        else
@@@ -797,8 -774,6 +783,8 @@@ static void upload_pack(void
  
  static int upload_pack_config(const char *var, const char *value, void *unused)
  {
 +      if (!strcmp("uploadpack.allowtipsha1inwant", var))
 +              allow_tip_sha1_in_want = git_config_bool(var, value);
        return parse_hide_refs_config(var, value, "uploadpack");
  }
  
@@@ -854,8 -829,6 +840,6 @@@ int main(int argc, char **argv
        if (is_repository_shallow())
                die("attempt to fetch/clone from a shallow repository");
        git_config(upload_pack_config, NULL);
-       if (getenv("GIT_DEBUG_SEND_PACK"))
-               debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
        upload_pack();
        return 0;
  }