Merge branch 'jk/git-connection-deadlock-fix' into maint-1.7.4
authorJunio C Hamano <gitster@pobox.com>
Thu, 26 May 2011 17:28:10 +0000 (10:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 26 May 2011 17:28:10 +0000 (10:28 -0700)
* jk/git-connection-deadlock-fix:
test core.gitproxy configuration
send-pack: avoid deadlock on git:// push with failed pack-objects
connect: let callers know if connection is a socket
connect: treat generic proxy processes like ssh processes

Conflicts:
connect.c

1  2 
builtin/send-pack.c
cache.h
connect.c
diff --combined builtin/send-pack.c
index eb38ab4e01fac3b8ba2d564b31d17da21206f06b,0000000000000000000000000000000000000000..fe07bb7ba3fa53bfa49684068781dd693b6ffe1b
mode 100644,000000..100644
--- /dev/null
@@@ -1,539 -1,0 +1,541 @@@
 +#include "cache.h"
 +#include "commit.h"
 +#include "refs.h"
 +#include "pkt-line.h"
 +#include "sideband.h"
 +#include "run-command.h"
 +#include "remote.h"
 +#include "send-pack.h"
 +#include "quote.h"
 +#include "transport.h"
 +
 +static const char send_pack_usage[] =
 +"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
 +"  --all and explicit <ref> specification are mutually exclusive.";
 +
 +static struct send_pack_args args;
 +
 +static int feed_object(const unsigned char *sha1, int fd, int negative)
 +{
 +      char buf[42];
 +
 +      if (negative && !has_sha1_file(sha1))
 +              return 1;
 +
 +      memcpy(buf + negative, sha1_to_hex(sha1), 40);
 +      if (negative)
 +              buf[0] = '^';
 +      buf[40 + negative] = '\n';
 +      return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
 +}
 +
 +/*
 + * Make a pack stream and spit it out into file descriptor fd
 + */
 +static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
 +{
 +      /*
 +       * The child becomes pack-objects --revs; we feed
 +       * the revision parameters to it via its stdin and
 +       * let its stdout go back to the other end.
 +       */
 +      const char *argv[] = {
 +              "pack-objects",
 +              "--all-progress-implied",
 +              "--revs",
 +              "--stdout",
 +              NULL,
 +              NULL,
 +              NULL,
 +              NULL,
 +              NULL,
 +      };
 +      struct child_process po;
 +      int i;
 +
 +      i = 4;
 +      if (args->use_thin_pack)
 +              argv[i++] = "--thin";
 +      if (args->use_ofs_delta)
 +              argv[i++] = "--delta-base-offset";
 +      if (args->quiet)
 +              argv[i++] = "-q";
 +      if (args->progress)
 +              argv[i++] = "--progress";
 +      memset(&po, 0, sizeof(po));
 +      po.argv = argv;
 +      po.in = -1;
 +      po.out = args->stateless_rpc ? -1 : fd;
 +      po.git_cmd = 1;
 +      if (start_command(&po))
 +              die_errno("git pack-objects failed");
 +
 +      /*
 +       * We feed the pack-objects we just spawned with revision
 +       * parameters by writing to the pipe.
 +       */
 +      for (i = 0; i < extra->nr; i++)
 +              if (!feed_object(extra->array[i], po.in, 1))
 +                      break;
 +
 +      while (refs) {
 +              if (!is_null_sha1(refs->old_sha1) &&
 +                  !feed_object(refs->old_sha1, po.in, 1))
 +                      break;
 +              if (!is_null_sha1(refs->new_sha1) &&
 +                  !feed_object(refs->new_sha1, po.in, 0))
 +                      break;
 +              refs = refs->next;
 +      }
 +
 +      close(po.in);
 +
 +      if (args->stateless_rpc) {
 +              char *buf = xmalloc(LARGE_PACKET_MAX);
 +              while (1) {
 +                      ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
 +                      if (n <= 0)
 +                              break;
 +                      send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
 +              }
 +              free(buf);
 +              close(po.out);
 +              po.out = -1;
 +      }
 +
 +      if (finish_command(&po))
 +              return -1;
 +      return 0;
 +}
 +
 +static int receive_status(int in, struct ref *refs)
 +{
 +      struct ref *hint;
 +      char line[1000];
 +      int ret = 0;
 +      int len = packet_read_line(in, line, sizeof(line));
 +      if (len < 10 || memcmp(line, "unpack ", 7))
 +              return error("did not receive remote status");
 +      if (memcmp(line, "unpack ok\n", 10)) {
 +              char *p = line + strlen(line) - 1;
 +              if (*p == '\n')
 +                      *p = '\0';
 +              error("unpack failed: %s", line + 7);
 +              ret = -1;
 +      }
 +      hint = NULL;
 +      while (1) {
 +              char *refname;
 +              char *msg;
 +              len = packet_read_line(in, line, sizeof(line));
 +              if (!len)
 +                      break;
 +              if (len < 3 ||
 +                  (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
 +                      fprintf(stderr, "protocol error: %s\n", line);
 +                      ret = -1;
 +                      break;
 +              }
 +
 +              line[strlen(line)-1] = '\0';
 +              refname = line + 3;
 +              msg = strchr(refname, ' ');
 +              if (msg)
 +                      *msg++ = '\0';
 +
 +              /* first try searching at our hint, falling back to all refs */
 +              if (hint)
 +                      hint = find_ref_by_name(hint, refname);
 +              if (!hint)
 +                      hint = find_ref_by_name(refs, refname);
 +              if (!hint) {
 +                      warning("remote reported status on unknown ref: %s",
 +                                      refname);
 +                      continue;
 +              }
 +              if (hint->status != REF_STATUS_EXPECTING_REPORT) {
 +                      warning("remote reported status on unexpected ref: %s",
 +                                      refname);
 +                      continue;
 +              }
 +
 +              if (line[0] == 'o' && line[1] == 'k')
 +                      hint->status = REF_STATUS_OK;
 +              else {
 +                      hint->status = REF_STATUS_REMOTE_REJECT;
 +                      ret = -1;
 +              }
 +              if (msg)
 +                      hint->remote_status = xstrdup(msg);
 +              /* start our next search from the next ref */
 +              hint = hint->next;
 +      }
 +      return ret;
 +}
 +
 +static void print_helper_status(struct ref *ref)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +
 +      for (; ref; ref = ref->next) {
 +              const char *msg = NULL;
 +              const char *res;
 +
 +              switch(ref->status) {
 +              case REF_STATUS_NONE:
 +                      res = "error";
 +                      msg = "no match";
 +                      break;
 +
 +              case REF_STATUS_OK:
 +                      res = "ok";
 +                      break;
 +
 +              case REF_STATUS_UPTODATE:
 +                      res = "ok";
 +                      msg = "up to date";
 +                      break;
 +
 +              case REF_STATUS_REJECT_NONFASTFORWARD:
 +                      res = "error";
 +                      msg = "non-fast forward";
 +                      break;
 +
 +              case REF_STATUS_REJECT_NODELETE:
 +              case REF_STATUS_REMOTE_REJECT:
 +                      res = "error";
 +                      break;
 +
 +              case REF_STATUS_EXPECTING_REPORT:
 +              default:
 +                      continue;
 +              }
 +
 +              strbuf_reset(&buf);
 +              strbuf_addf(&buf, "%s %s", res, ref->name);
 +              if (ref->remote_status)
 +                      msg = ref->remote_status;
 +              if (msg) {
 +                      strbuf_addch(&buf, ' ');
 +                      quote_two_c_style(&buf, "", msg, 0);
 +              }
 +              strbuf_addch(&buf, '\n');
 +
 +              safe_write(1, buf.buf, buf.len);
 +      }
 +      strbuf_release(&buf);
 +}
 +
 +static int sideband_demux(int in, int out, void *data)
 +{
 +      int *fd = data, ret;
 +#ifdef NO_PTHREADS
 +      close(fd[1]);
 +#endif
 +      ret = recv_sideband("send-pack", fd[0], out);
 +      close(out);
 +      return ret;
 +}
 +
 +int send_pack(struct send_pack_args *args,
 +            int fd[], struct child_process *conn,
 +            struct ref *remote_refs,
 +            struct extra_have_objects *extra_have)
 +{
 +      int in = fd[0];
 +      int out = fd[1];
 +      struct strbuf req_buf = STRBUF_INIT;
 +      struct ref *ref;
 +      int new_refs;
 +      int allow_deleting_refs = 0;
 +      int status_report = 0;
 +      int use_sideband = 0;
 +      unsigned cmds_sent = 0;
 +      int ret;
 +      struct async demux;
 +
 +      /* Does the other end support the reporting? */
 +      if (server_supports("report-status"))
 +              status_report = 1;
 +      if (server_supports("delete-refs"))
 +              allow_deleting_refs = 1;
 +      if (server_supports("ofs-delta"))
 +              args->use_ofs_delta = 1;
 +      if (server_supports("side-band-64k"))
 +              use_sideband = 1;
 +
 +      if (!remote_refs) {
 +              fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
 +                      "Perhaps you should specify a branch such as 'master'.\n");
 +              return 0;
 +      }
 +
 +      /*
 +       * Finally, tell the other end!
 +       */
 +      new_refs = 0;
 +      for (ref = remote_refs; ref; ref = ref->next) {
 +              if (!ref->peer_ref && !args->send_mirror)
 +                      continue;
 +
 +              /* Check for statuses set by set_ref_status_for_push() */
 +              switch (ref->status) {
 +              case REF_STATUS_REJECT_NONFASTFORWARD:
 +              case REF_STATUS_UPTODATE:
 +                      continue;
 +              default:
 +                      ; /* do nothing */
 +              }
 +
 +              if (ref->deletion && !allow_deleting_refs) {
 +                      ref->status = REF_STATUS_REJECT_NODELETE;
 +                      continue;
 +              }
 +
 +              if (!ref->deletion)
 +                      new_refs++;
 +
 +              if (args->dry_run) {
 +                      ref->status = REF_STATUS_OK;
 +              } else {
 +                      char *old_hex = sha1_to_hex(ref->old_sha1);
 +                      char *new_hex = sha1_to_hex(ref->new_sha1);
 +
 +                      if (!cmds_sent && (status_report || use_sideband)) {
 +                              packet_buf_write(&req_buf, "%s %s %s%c%s%s",
 +                                      old_hex, new_hex, ref->name, 0,
 +                                      status_report ? " report-status" : "",
 +                                      use_sideband ? " side-band-64k" : "");
 +                      }
 +                      else
 +                              packet_buf_write(&req_buf, "%s %s %s",
 +                                      old_hex, new_hex, ref->name);
 +                      ref->status = status_report ?
 +                              REF_STATUS_EXPECTING_REPORT :
 +                              REF_STATUS_OK;
 +                      cmds_sent++;
 +              }
 +      }
 +
 +      if (args->stateless_rpc) {
 +              if (!args->dry_run && cmds_sent) {
 +                      packet_buf_flush(&req_buf);
 +                      send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
 +              }
 +      } else {
 +              safe_write(out, req_buf.buf, req_buf.len);
 +              packet_flush(out);
 +      }
 +      strbuf_release(&req_buf);
 +
 +      if (use_sideband && cmds_sent) {
 +              memset(&demux, 0, sizeof(demux));
 +              demux.proc = sideband_demux;
 +              demux.data = fd;
 +              demux.out = -1;
 +              if (start_async(&demux))
 +                      die("receive-pack: unable to fork off sideband demultiplexer");
 +              in = demux.out;
 +      }
 +
 +      if (new_refs && cmds_sent) {
 +              if (pack_objects(out, remote_refs, extra_have, args) < 0) {
 +                      for (ref = remote_refs; ref; ref = ref->next)
 +                              ref->status = REF_STATUS_NONE;
 +                      if (args->stateless_rpc)
 +                              close(out);
++                      if (git_connection_is_socket(conn))
++                              shutdown(fd[0], SHUT_WR);
 +                      if (use_sideband)
 +                              finish_async(&demux);
 +                      return -1;
 +              }
 +      }
 +      if (args->stateless_rpc && cmds_sent)
 +              packet_flush(out);
 +
 +      if (status_report && cmds_sent)
 +              ret = receive_status(in, remote_refs);
 +      else
 +              ret = 0;
 +      if (args->stateless_rpc)
 +              packet_flush(out);
 +
 +      if (use_sideband && cmds_sent) {
 +              if (finish_async(&demux)) {
 +                      error("error in sideband demultiplexer");
 +                      ret = -1;
 +              }
 +              close(demux.out);
 +      }
 +
 +      if (ret < 0)
 +              return ret;
 +
 +      if (args->porcelain)
 +              return 0;
 +
 +      for (ref = remote_refs; ref; ref = ref->next) {
 +              switch (ref->status) {
 +              case REF_STATUS_NONE:
 +              case REF_STATUS_UPTODATE:
 +              case REF_STATUS_OK:
 +                      break;
 +              default:
 +                      return -1;
 +              }
 +      }
 +      return 0;
 +}
 +
 +int cmd_send_pack(int argc, const char **argv, const char *prefix)
 +{
 +      int i, nr_refspecs = 0;
 +      const char **refspecs = NULL;
 +      const char *remote_name = NULL;
 +      struct remote *remote = NULL;
 +      const char *dest = NULL;
 +      int fd[2];
 +      struct child_process *conn;
 +      struct extra_have_objects extra_have;
 +      struct ref *remote_refs, *local_refs;
 +      int ret;
 +      int helper_status = 0;
 +      int send_all = 0;
 +      const char *receivepack = "git-receive-pack";
 +      int flags;
 +      int nonfastforward = 0;
 +
 +      argv++;
 +      for (i = 1; i < argc; i++, argv++) {
 +              const char *arg = *argv;
 +
 +              if (*arg == '-') {
 +                      if (!prefixcmp(arg, "--receive-pack=")) {
 +                              receivepack = arg + 15;
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--exec=")) {
 +                              receivepack = arg + 7;
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--remote=")) {
 +                              remote_name = arg + 9;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--all")) {
 +                              send_all = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--dry-run")) {
 +                              args.dry_run = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--mirror")) {
 +                              args.send_mirror = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--force")) {
 +                              args.force_update = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--verbose")) {
 +                              args.verbose = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--thin")) {
 +                              args.use_thin_pack = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--stateless-rpc")) {
 +                              args.stateless_rpc = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--helper-status")) {
 +                              helper_status = 1;
 +                              continue;
 +                      }
 +                      usage(send_pack_usage);
 +              }
 +              if (!dest) {
 +                      dest = arg;
 +                      continue;
 +              }
 +              refspecs = (const char **) argv;
 +              nr_refspecs = argc - i;
 +              break;
 +      }
 +      if (!dest)
 +              usage(send_pack_usage);
 +      /*
 +       * --all and --mirror are incompatible; neither makes sense
 +       * with any refspecs.
 +       */
 +      if ((refspecs && (send_all || args.send_mirror)) ||
 +          (send_all && args.send_mirror))
 +              usage(send_pack_usage);
 +
 +      if (remote_name) {
 +              remote = remote_get(remote_name);
 +              if (!remote_has_url(remote, dest)) {
 +                      die("Destination %s is not a uri for %s",
 +                          dest, remote_name);
 +              }
 +      }
 +
 +      if (args.stateless_rpc) {
 +              conn = NULL;
 +              fd[0] = 0;
 +              fd[1] = 1;
 +      } else {
 +              conn = git_connect(fd, dest, receivepack,
 +                      args.verbose ? CONNECT_VERBOSE : 0);
 +      }
 +
 +      memset(&extra_have, 0, sizeof(extra_have));
 +
 +      get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
 +                       &extra_have);
 +
 +      transport_verify_remote_names(nr_refspecs, refspecs);
 +
 +      local_refs = get_local_heads();
 +
 +      flags = MATCH_REFS_NONE;
 +
 +      if (send_all)
 +              flags |= MATCH_REFS_ALL;
 +      if (args.send_mirror)
 +              flags |= MATCH_REFS_MIRROR;
 +
 +      /* match them up */
 +      if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
 +              return -1;
 +
 +      set_ref_status_for_push(remote_refs, args.send_mirror,
 +              args.force_update);
 +
 +      ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
 +
 +      if (helper_status)
 +              print_helper_status(remote_refs);
 +
 +      close(fd[1]);
 +      close(fd[0]);
 +
 +      ret |= finish_connect(conn);
 +
 +      if (!helper_status)
 +              transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward);
 +
 +      if (!args.dry_run && remote) {
 +              struct ref *ref;
 +              for (ref = remote_refs; ref; ref = ref->next)
 +                      transport_update_tracking_ref(remote, ref, args.verbose);
 +      }
 +
 +      if (!ret && !transport_refs_pushed(remote_refs))
 +              fprintf(stderr, "Everything up-to-date\n");
 +
 +      return ret;
 +}
diff --combined cache.h
index 342b4100f12d5a56262a13290c27298431e82325,724aad41bac20be6de9d375bac8d5d12c1c6a1b2..5e9675d76d83632ee23ed8bad6340929edf7560f
+++ b/cache.h
@@@ -170,28 -170,22 +170,28 @@@ struct cache_entry 
   *
   * In-memory only flags
   */
 -#define CE_UPDATE    (0x10000)
 -#define CE_REMOVE    (0x20000)
 -#define CE_UPTODATE  (0x40000)
 -#define CE_ADDED     (0x80000)
 +#define CE_UPDATE            (1 << 16)
 +#define CE_REMOVE            (1 << 17)
 +#define CE_UPTODATE          (1 << 18)
 +#define CE_ADDED             (1 << 19)
  
 -#define CE_HASHED    (0x100000)
 -#define CE_UNHASHED  (0x200000)
 +#define CE_HASHED            (1 << 20)
 +#define CE_UNHASHED          (1 << 21)
 +#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 +#define CE_CONFLICTED        (1 << 23)
 +
 +#define CE_UNPACKED          (1 << 24)
 +#define CE_NEW_SKIP_WORKTREE (1 << 25)
  
  /*
   * Extended on-disk flags
   */
 -#define CE_INTENT_TO_ADD 0x20000000
 +#define CE_INTENT_TO_ADD     (1 << 29)
 +#define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2 0x80000000
 +#define CE_EXTENDED2         (1 << 31)
  
 -#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:
@@@ -240,7 -234,6 +240,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)
@@@ -277,16 -270,9 +277,16 @@@ static inline int ce_to_dtype(const str
        else
                return DT_UNKNOWN;
  }
 -#define canon_mode(mode) \
 -      (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 -      S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 +static inline unsigned int canon_mode(unsigned int mode)
 +{
 +      if (S_ISREG(mode))
 +              return S_IFREG | ce_permissions(mode);
 +      if (S_ISLNK(mode))
 +              return S_IFLNK;
 +      if (S_ISDIR(mode))
 +              return S_IFDIR;
 +      return S_IFGITLINK;
 +}
  
  #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) flexible_size(cache_entry,len)
  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;
@@@ -351,9 -336,6 +351,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 {
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
        OBJ_ANY,
 -      OBJ_MAX,
 +      OBJ_MAX
  };
  
  static inline enum object_type object_type(unsigned int mode)
  #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
  #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
  #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 +#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
  #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
  #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
 + * 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.
 + */
 +#define LOCAL_REPO_ENV_SIZE 9
 +extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
  
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
@@@ -428,7 -397,7 +428,7 @@@ extern const char **get_pathspec(const 
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
 -extern const char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
@@@ -445,7 -414,7 +445,7 @@@ extern int init_db(const char *template
   * at least 'nr' entries; the number of entries currently allocated
   * is 'alloc', using the standard growing factor alloc_nr() macro.
   *
 - * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
 + * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
   */
  #define ALLOC_GROW(x, nr, alloc) \
        do { \
                                alloc = alloc_nr(alloc); \
                        x = xrealloc((x), alloc * sizeof(*(x))); \
                } \
 -      } while(0)
 +      } while (0)
  
  /* Initialize and use the cache information */
  extern int read_index(struct index_state *);
@@@ -476,6 -445,7 +476,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);
@@@ -494,9 -464,7 +494,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);
  
@@@ -505,6 -473,9 +505,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 */
@@@ -527,7 -498,6 +527,7 @@@ extern NORETURN void unable_to_lock_ind
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
 +extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern int commit_locked_index(struct lock_file *);
@@@ -541,7 -511,6 +541,7 @@@ extern int trust_executable_bit
  extern int trust_ctime;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int minimum_abbrev, default_abbrev;
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -556,61 -525,39 +556,61 @@@ extern int core_compression_seen
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
 -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,
        SAFE_CRLF_FAIL = 1,
 -      SAFE_CRLF_WARN = 2,
 +      SAFE_CRLF_WARN = 2
  };
  
  extern enum safe_crlf safe_crlf;
  
 +enum auto_crlf {
 +      AUTO_CRLF_FALSE = 0,
 +      AUTO_CRLF_TRUE = 1,
 +      AUTO_CRLF_INPUT = -1
 +};
 +
 +extern enum auto_crlf auto_crlf;
 +
 +enum eol {
 +      EOL_UNSET,
 +      EOL_CRLF,
 +      EOL_LF,
 +#ifdef NATIVE_CRLF
 +      EOL_NATIVE = EOL_CRLF
 +#else
 +      EOL_NATIVE = EOL_LF
 +#endif
 +};
 +
 +extern enum eol eol;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
 +      BRANCH_TRACK_OVERRIDE
  };
  
  enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
        AUTOREBASE_REMOTE,
 -      AUTOREBASE_ALWAYS,
 +      AUTOREBASE_ALWAYS
  };
  
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 -      PUSH_DEFAULT_TRACKING,
 -      PUSH_DEFAULT_CURRENT,
 +      PUSH_DEFAULT_UPSTREAM,
 +      PUSH_DEFAULT_CURRENT
  };
  
  extern enum branch_track git_branch_track;
@@@ -619,7 -566,7 +619,7 @@@ extern enum push_default_type push_defa
  
  enum object_creation_mode {
        OBJECT_CREATION_USES_HARDLINKS = 0,
 -      OBJECT_CREATION_USES_RENAMES = 1,
 +      OBJECT_CREATION_USES_RENAMES = 1
  };
  
  extern enum object_creation_mode object_creation_mode;
@@@ -650,9 -597,6 +650,9 @@@ extern char *git_pathdup(const char *fm
  /* Return a statically allocated filename matching the sha1 signature */
  extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern char *git_path_submodule(const char *path, const char *fmt, ...)
 +      __attribute__((format (printf, 2, 3)));
 +
  extern char *sha1_file_name(const unsigned char *sha1);
  extern char *sha1_pack_name(const unsigned char *sha1);
  extern char *sha1_pack_index_name(const unsigned char *sha1);
@@@ -674,23 -618,18 +674,23 @@@ 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"
 -#define EMPTY_TREE_SHA1_BIN \
 +#define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 +#define EMPTY_TREE_SHA1_BIN \
 +       ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
  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!!
   *
@@@ -704,7 -643,7 +704,7 @@@ enum sharedrepo 
        OLD_PERM_GROUP      = 1,
        OLD_PERM_EVERYBODY  = 2,
        PERM_GROUP          = 0660,
 -      PERM_EVERYBODY      = 0664,
 +      PERM_EVERYBODY      = 0664
  };
  int git_config_perm(const char *var, const char *value);
  int set_shared_perm(const char *path, int mode);
@@@ -725,7 -664,6 +725,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 *);
@@@ -735,7 -673,7 +735,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);
  
@@@ -750,10 -688,9 +750,10 @@@ 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 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)
  {
  }
  
  /* Convert to/from hex/sha1 representation */
 -#define MINIMUM_ABBREV 4
 -#define DEFAULT_ABBREV 7
 +#define MINIMUM_ABBREV minimum_abbrev
 +#define DEFAULT_ABBREV default_abbrev
 +
 +struct object_context {
 +      unsigned char tree[20];
 +      char path[PATH_MAX];
 +      unsigned mode;
 +};
  
  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_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
 +static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
 +{
 +      return get_sha1_with_context_1(str, sha1, orc, 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);
@@@ -788,7 -710,6 +788,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[];
@@@ -825,10 -746,8 +825,10 @@@ const char *show_date_relative(unsigne
                               char *timebuf,
                               size_t timebuf_size);
  int parse_date(const char *date, char *buf, int bufsize);
 +int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
 -unsigned long approxidate(const char *);
 +#define approxidate(s) approxidate_careful((s), NULL)
 +unsigned long approxidate_careful(const char *, int *);
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
  
@@@ -840,7 -759,7 +840,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;
@@@ -863,8 -782,10 +863,8 @@@ struct cache_def 
  
  extern int has_symlink_leading_path(const char *name, int len);
  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 check_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);
  
@@@ -900,8 -821,7 +900,8 @@@ extern struct packed_git 
        time_t mtime;
        int pack_fd;
        unsigned pack_local:1,
 -               pack_keep:1;
 +               pack_keep:1,
 +               do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@@ -929,7 -849,7 +929,7 @@@ struct ref 
                REF_STATUS_REJECT_NODELETE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
 -              REF_STATUS_EXPECTING_REPORT,
 +              REF_STATUS_EXPECTING_REPORT
        } status;
        char *remote_status;
        struct ref *peer_ref; /* when renaming */
  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 git_connection_is_socket(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  struct extra_have_objects {
        int nr, alloc;
  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);
@@@ -965,7 -885,6 +966,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 **);
@@@ -986,43 -905,28 +987,43 @@@ 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 void git_config_push_parameter(const char *text);
 +extern int git_config_parse_parameter(const char *text);
 +extern int git_config_parse_environment(void);
 +extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  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 *);
 +extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  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 *);
 +extern const char *get_log_output_encoding(void);
 +extern const char *get_commit_output_encoding(void);
 +
  extern const char *config_exclusive_filename;
  
  #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;
@@@ -1052,7 -956,6 +1053,7 @@@ extern int pager_in_use(void)
  extern int pager_use_color;
  
  extern const char *editor_program;
 +extern const char *askpass_program;
  extern const char *excludes_file;
  
  /* base85 */
@@@ -1072,14 -975,12 +1073,14 @@@ __attribute__((format (printf, 1, 2))
  extern void trace_printf(const char *format, ...);
  __attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
 +extern void trace_repo_setup(const char *prefix);
  
  /* convert.c */
  /* returns 1 if *dst was used */
  extern int convert_to_git(const char *path, const char *src, size_t len,
                            struct strbuf *dst, enum safe_crlf checksafe);
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 +extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* add */
  /*
@@@ -1093,31 -994,26 +1094,31 @@@ 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.
   * used by both diff and apply
 + * last two digits are tab width
   */
 -#define WS_BLANK_AT_EOL         01
 -#define WS_SPACE_BEFORE_TAB   02
 -#define WS_INDENT_WITH_NON_TAB        04
 -#define WS_CR_AT_EOL           010
 -#define WS_BLANK_AT_EOF        020
 +#define WS_BLANK_AT_EOL         0100
 +#define WS_SPACE_BEFORE_TAB     0200
 +#define WS_INDENT_WITH_NON_TAB  0400
 +#define WS_CR_AT_EOL           01000
 +#define WS_BLANK_AT_EOF        02000
 +#define WS_TAB_IN_INDENT       04000
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 -#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 +#define WS_TAB_WIDTH_MASK        077
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
  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);
 +#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@@ -1125,17 -1021,5 +1126,17 @@@ void overlay_tree_on_cache(const char *
  
  char *alias_lookup(const char *alias);
  int split_cmdline(char *cmdline, const char ***argv);
 +/* Takes a negative value returned by split_cmdline */
 +const char *split_cmdline_strerror(int cmdline_errno);
 +
 +/* git.c */
 +struct startup_info {
 +      int have_repository;
 +      const char *prefix;
 +};
 +extern struct startup_info *startup_info;
 +
 +/* builtin/merge.c */
 +int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
  
  #endif /* CACHE_H */
diff --combined connect.c
index 57dc20c43ca1ba205ec0a18e263f9dde081390f4,9b31af02baa51d4994acb5ee9bb9e88aeb31044a..2119c3f74edcd2a976ff0e375c5d8d60d40da2ec
+++ b/connect.c
@@@ -5,7 -5,6 +5,7 @@@
  #include "refs.h"
  #include "run-command.h"
  #include "remote.h"
 +#include "url.h"
  
  static char *server_capabilities;
  
@@@ -132,7 -131,7 +132,7 @@@ int path_match(const char *path, int nr
  enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
 -      PROTO_GIT,
 +      PROTO_GIT
  };
  
  static enum protocol get_protocol(const char *name)
  #define STR_(s)       # s
  #define STR(s)        STR_(s)
  
 +static void get_host_and_port(char **host, const char **port)
 +{
 +      char *colon, *end;
 +
 +      if (*host[0] == '[') {
 +              end = strchr(*host + 1, ']');
 +              if (end) {
 +                      *end = 0;
 +                      end++;
 +                      (*host)++;
 +              } else
 +                      end = *host;
 +      } else
 +              end = *host;
 +      colon = strchr(end, ':');
 +
 +      if (colon) {
 +              *colon = 0;
 +              *port = colon + 1;
 +      }
 +}
 +
  #ifndef NO_IPV6
  
  static const char *ai_name(const struct addrinfo *ai)
  static int git_tcp_connect_sock(char *host, int flags)
  {
        int sockfd = -1, saved_errno = 0;
 -      char *colon, *end;
        const char *port = STR(DEFAULT_GIT_PORT);
        struct addrinfo hints, *ai0, *ai;
        int gai;
        int cnt = 0;
  
 -      if (host[0] == '[') {
 -              end = strchr(host + 1, ']');
 -              if (end) {
 -                      *end = 0;
 -                      end++;
 -                      host++;
 -              } else
 -                      end = host;
 -      } else
 -              end = host;
 -      colon = strchr(end, ':');
 -
 -      if (colon) {
 -              *colon = 0;
 -              port = colon + 1;
 -              if (!*port)
 -                      port = "<none>";
 -      }
 +      get_host_and_port(&host, &port);
 +      if (!*port)
 +              port = "<none>";
  
        memset(&hints, 0, sizeof(hints));
        hints.ai_socktype = SOCK_STREAM;
  static int git_tcp_connect_sock(char *host, int flags)
  {
        int sockfd = -1, saved_errno = 0;
 -      char *colon, *end;
 -      char *port = STR(DEFAULT_GIT_PORT), *ep;
 +      const char *port = STR(DEFAULT_GIT_PORT);
 +      char *ep;
        struct hostent *he;
        struct sockaddr_in sa;
        char **ap;
        unsigned int nport;
        int cnt;
  
 -      if (host[0] == '[') {
 -              end = strchr(host + 1, ']');
 -              if (end) {
 -                      *end = 0;
 -                      end++;
 -                      host++;
 -              } else
 -                      end = host;
 -      } else
 -              end = host;
 -      colon = strchr(end, ':');
 -
 -      if (colon) {
 -              *colon = 0;
 -              port = colon + 1;
 -      }
 +      get_host_and_port(&host, &port);
  
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "Looking up %s ... ", host);
@@@ -395,26 -403,44 +395,28 @@@ static int git_use_proxy(const char *ho
        return (git_proxy_command && *git_proxy_command);
  }
  
- static void git_proxy_connect(int fd[2], char *host)
+ static struct child_process *git_proxy_connect(int fd[2], char *host)
  {
        const char *port = STR(DEFAULT_GIT_PORT);
-       const char *argv[4];
-       struct child_process proxy;
 -      char *colon, *end;
+       const char **argv;
+       struct child_process *proxy;
  
 -      if (host[0] == '[') {
 -              end = strchr(host + 1, ']');
 -              if (end) {
 -                      *end = 0;
 -                      end++;
 -                      host++;
 -              } else
 -                      end = host;
 -      } else
 -              end = host;
 -      colon = strchr(end, ':');
 -
 -      if (colon) {
 -              *colon = 0;
 -              port = colon + 1;
 -      }
 +      get_host_and_port(&host, &port);
  
+       argv = xmalloc(sizeof(*argv) * 4);
        argv[0] = git_proxy_command;
        argv[1] = host;
        argv[2] = port;
        argv[3] = NULL;
-       memset(&proxy, 0, sizeof(proxy));
-       proxy.argv = argv;
-       proxy.in = -1;
-       proxy.out = -1;
-       if (start_command(&proxy))
+       proxy = xcalloc(1, sizeof(*proxy));
+       proxy->argv = argv;
+       proxy->in = -1;
+       proxy->out = -1;
+       if (start_command(proxy))
                die("cannot start proxy %s", argv[0]);
-       fd[0] = proxy.out; /* read from proxy stdout */
-       fd[1] = proxy.in;  /* write to proxy stdin */
+       fd[0] = proxy->out; /* read from proxy stdout */
+       fd[1] = proxy->in;  /* write to proxy stdin */
+       return proxy;
  }
  
  #define MAX_CMD_LEN 1024
@@@ -451,11 -477,11 +453,11 @@@ static struct child_process no_fork
  struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
  {
 -      char *url = xstrdup(url_orig);
 +      char *url;
        char *host, *path;
        char *end;
        int c;
-       struct child_process *conn;
+       struct child_process *conn = &no_fork;
        enum protocol protocol = PROTO_LOCAL;
        int free_path = 0;
        char *port = NULL;
         */
        signal(SIGCHLD, SIG_DFL);
  
 +      if (is_url(url_orig))
 +              url = url_decode(url_orig);
 +      else
 +              url = xstrdup(url_orig);
 +
        host = strstr(url, "://");
        if (host) {
                *host = '\0';
                c = ':';
        }
  
 +      /*
 +       * Don't do destructive transforms with git:// as that
 +       * protocol code does '[]' unwrapping of its own.
 +       */
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
                if (end) {
 -                      *end = 0;
 +                      if (protocol != PROTO_GIT) {
 +                              *end = 0;
 +                              host++;
 +                      }
                        end++;
 -                      host++;
                } else
                        end = host;
        } else
                 */
                char *target_host = xstrdup(host);
                if (git_use_proxy(host))
-                       git_proxy_connect(fd, host);
+                       conn = git_proxy_connect(fd, host);
                else
                        git_tcp_connect(fd, host, flags);
                /*
                free(url);
                if (free_path)
                        free(path);
-               return &no_fork;
+               return conn;
        }
  
        conn = xcalloc(1, sizeof(*conn));
                *arg++ = host;
        }
        else {
 -              /* remove these from the environment */
 -              const char *env[] = {
 -                      ALTERNATE_DB_ENVIRONMENT,
 -                      DB_ENVIRONMENT,
 -                      GIT_DIR_ENVIRONMENT,
 -                      GIT_WORK_TREE_ENVIRONMENT,
 -                      GRAFT_ENVIRONMENT,
 -                      INDEX_ENVIRONMENT,
 -                      NO_REPLACE_OBJECTS_ENVIRONMENT,
 -                      NULL
 -              };
 -              conn->env = env;
 -              *arg++ = "sh";
 -              *arg++ = "-c";
 +              /* remove repo-local variables from the environment */
 +              conn->env = local_repo_env;
 +              conn->use_shell = 1;
        }
        *arg++ = cmd.buf;
        *arg = NULL;
        return conn;
  }
  
+ int git_connection_is_socket(struct child_process *conn)
+ {
+       return conn == &no_fork;
+ }
  int finish_connect(struct child_process *conn)
  {
        int code;
-       if (!conn || conn == &no_fork)
+       if (!conn || git_connection_is_socket(conn))
                return 0;
  
        code = finish_command(conn);
        free(conn);
        return code;
  }
 +
 +char *git_getpass(const char *prompt)
 +{
 +      const char *askpass;
 +      struct child_process pass;
 +      const char *args[3];
 +      static struct strbuf buffer = STRBUF_INIT;
 +
 +      askpass = getenv("GIT_ASKPASS");
 +      if (!askpass)
 +              askpass = askpass_program;
 +      if (!askpass)
 +              askpass = getenv("SSH_ASKPASS");
 +      if (!askpass || !(*askpass)) {
 +              char *result = getpass(prompt);
 +              if (!result)
 +                      die_errno("Could not read password");
 +              return result;
 +      }
 +
 +      args[0] = askpass;
 +      args[1] = prompt;
 +      args[2] = NULL;
 +
 +      memset(&pass, 0, sizeof(pass));
 +      pass.argv = args;
 +      pass.out = -1;
 +
 +      if (start_command(&pass))
 +              exit(1);
 +
 +      strbuf_reset(&buffer);
 +      if (strbuf_read(&buffer, pass.out, 20) < 0)
 +              die("failed to read password from %s\n", askpass);
 +
 +      close(pass.out);
 +
 +      if (finish_command(&pass))
 +              exit(1);
 +
 +      strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
 +
 +      return buffer.buf;
 +}