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
@@@ -7,7 -7,6 +7,7 @@@
  #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"
@@@ -48,7 -47,6 +48,7 @@@ static int pack_objects(int fd, struct 
                NULL,
                NULL,
                NULL,
 +              NULL,
        };
        struct child_process po;
        int i;
@@@ -60,8 -58,6 +60,8 @@@
                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;
        }
  
        if (finish_command(&po))
 -              return error("pack-objects died with strange error");
 +              return -1;
        return 0;
  }
  
@@@ -173,6 -169,156 +173,6 @@@ static int receive_status(int in, struc
        return ret;
  }
  
 -static void update_tracking_ref(struct remote *remote, struct ref *ref)
 -{
 -      struct refspec rs;
 -
 -      if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
 -              return;
 -
 -      rs.src = ref->name;
 -      rs.dst = NULL;
 -
 -      if (!remote_find_tracking(remote, &rs)) {
 -              if (args.verbose)
 -                      fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
 -              if (ref->deletion) {
 -                      delete_ref(rs.dst, NULL, 0);
 -              } else
 -                      update_ref("update by push", rs.dst,
 -                                      ref->new_sha1, NULL, 0, 0);
 -              free(rs.dst);
 -      }
 -}
 -
 -#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 -
 -static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
 -{
 -      fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
 -      if (from)
 -              fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
 -      else
 -              fputs(prettify_refname(to->name), stderr);
 -      if (msg) {
 -              fputs(" (", stderr);
 -              fputs(msg, stderr);
 -              fputc(')', stderr);
 -      }
 -      fputc('\n', stderr);
 -}
 -
 -static const char *status_abbrev(unsigned char sha1[20])
 -{
 -      return find_unique_abbrev(sha1, DEFAULT_ABBREV);
 -}
 -
 -static void print_ok_ref_status(struct ref *ref)
 -{
 -      if (ref->deletion)
 -              print_ref_status('-', "[deleted]", ref, NULL, NULL);
 -      else if (is_null_sha1(ref->old_sha1))
 -              print_ref_status('*',
 -                      (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
 -                        "[new branch]"),
 -                      ref, ref->peer_ref, NULL);
 -      else {
 -              char quickref[84];
 -              char type;
 -              const char *msg;
 -
 -              strcpy(quickref, status_abbrev(ref->old_sha1));
 -              if (ref->nonfastforward) {
 -                      strcat(quickref, "...");
 -                      type = '+';
 -                      msg = "forced update";
 -              } else {
 -                      strcat(quickref, "..");
 -                      type = ' ';
 -                      msg = NULL;
 -              }
 -              strcat(quickref, status_abbrev(ref->new_sha1));
 -
 -              print_ref_status(type, quickref, ref, ref->peer_ref, msg);
 -      }
 -}
 -
 -static int print_one_push_status(struct ref *ref, const char *dest, int count)
 -{
 -      if (!count)
 -              fprintf(stderr, "To %s\n", dest);
 -
 -      switch(ref->status) {
 -      case REF_STATUS_NONE:
 -              print_ref_status('X', "[no match]", ref, NULL, NULL);
 -              break;
 -      case REF_STATUS_REJECT_NODELETE:
 -              print_ref_status('!', "[rejected]", ref, NULL,
 -                              "remote does not support deleting refs");
 -              break;
 -      case REF_STATUS_UPTODATE:
 -              print_ref_status('=', "[up to date]", ref,
 -                              ref->peer_ref, NULL);
 -              break;
 -      case REF_STATUS_REJECT_NONFASTFORWARD:
 -              print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 -                              "non-fast-forward");
 -              break;
 -      case REF_STATUS_REMOTE_REJECT:
 -              print_ref_status('!', "[remote rejected]", ref,
 -                              ref->deletion ? NULL : ref->peer_ref,
 -                              ref->remote_status);
 -              break;
 -      case REF_STATUS_EXPECTING_REPORT:
 -              print_ref_status('!', "[remote failure]", ref,
 -                              ref->deletion ? NULL : ref->peer_ref,
 -                              "remote failed to report status");
 -              break;
 -      case REF_STATUS_OK:
 -              print_ok_ref_status(ref);
 -              break;
 -      }
 -
 -      return 1;
 -}
 -
 -static void print_push_status(const char *dest, struct ref *refs)
 -{
 -      struct ref *ref;
 -      int n = 0;
 -
 -      if (args.verbose) {
 -              for (ref = refs; ref; ref = ref->next)
 -                      if (ref->status == REF_STATUS_UPTODATE)
 -                              n += print_one_push_status(ref, dest, n);
 -      }
 -
 -      for (ref = refs; ref; ref = ref->next)
 -              if (ref->status == REF_STATUS_OK)
 -                      n += print_one_push_status(ref, dest, n);
 -
 -      for (ref = refs; ref; ref = ref->next) {
 -              if (ref->status != REF_STATUS_NONE &&
 -                  ref->status != REF_STATUS_UPTODATE &&
 -                  ref->status != REF_STATUS_OK)
 -                      n += print_one_push_status(ref, dest, n);
 -      }
 -}
 -
 -static int refs_pushed(struct ref *ref)
 -{
 -      for (; ref; ref = ref->next) {
 -              switch(ref->status) {
 -              case REF_STATUS_NONE:
 -              case REF_STATUS_UPTODATE:
 -                      break;
 -              default:
 -                      return 1;
 -              }
 -      }
 -      return 0;
 -}
 -
  static void print_helper_status(struct ref *ref)
  {
        struct strbuf buf = STRBUF_INIT;
  static int sideband_demux(int in, int out, void *data)
  {
        int *fd = data, ret;
 -#ifndef WIN32
 +#ifdef NO_PTHREADS
        close(fd[1]);
  #endif
        ret = recv_sideband("send-pack", fd[0], out);
@@@ -275,20 -421,50 +275,20 @@@ int send_pack(struct send_pack_args *ar
         */
        new_refs = 0;
        for (ref = remote_refs; ref; ref = ref->next) {
 -
 -              if (ref->peer_ref)
 -                      hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 -              else if (!args->send_mirror)
 +              if (!ref->peer_ref && !args->send_mirror)
                        continue;
  
 -              ref->deletion = is_null_sha1(ref->new_sha1);
 -              if (ref->deletion && !allow_deleting_refs) {
 -                      ref->status = REF_STATUS_REJECT_NODELETE;
 -                      continue;
 -              }
 -              if (!ref->deletion &&
 -                  !hashcmp(ref->old_sha1, ref->new_sha1)) {
 -                      ref->status = REF_STATUS_UPTODATE;
 +              /* 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 */
                }
  
 -              /* This part determines what can overwrite what.
 -               * The rules are:
 -               *
 -               * (0) you can always use --force or +A:B notation to
 -               *     selectively force individual ref pairs.
 -               *
 -               * (1) if the old thing does not exist, it is OK.
 -               *
 -               * (2) if you do not have the old thing, you are not allowed
 -               *     to overwrite it; you would not know what you are losing
 -               *     otherwise.
 -               *
 -               * (3) if both new and old are commit-ish, and new is a
 -               *     descendant of old, it is OK.
 -               *
 -               * (4) regardless of all of the above, removing :B is
 -               *     always allowed.
 -               */
 -
 -              ref->nonfastforward =
 -                  !ref->deletion &&
 -                  !is_null_sha1(ref->old_sha1) &&
 -                  (!has_sha1_file(ref->old_sha1)
 -                    || !ref_newer(ref->new_sha1, ref->old_sha1));
 -
 -              if (ref->nonfastforward && !ref->force && !args->force_update) {
 -                      ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
 +              if (ref->deletion && !allow_deleting_refs) {
 +                      ref->status = REF_STATUS_REJECT_NODELETE;
                        continue;
                }
  
                                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 (ret < 0)
                return ret;
 +
 +      if (args->porcelain)
 +              return 0;
 +
        for (ref = remote_refs; ref; ref = ref->next) {
                switch (ref->status) {
                case REF_STATUS_NONE:
        return 0;
  }
  
 -static void verify_remote_names(int nr_heads, const char **heads)
 -{
 -      int i;
 -
 -      for (i = 0; i < nr_heads; i++) {
 -              const char *local = heads[i];
 -              const char *remote = strrchr(heads[i], ':');
 -
 -              if (*local == '+')
 -                      local++;
 -
 -              /* A matching refspec is okay.  */
 -              if (remote == local && remote[1] == '\0')
 -                      continue;
 -
 -              remote = remote ? (remote + 1) : local;
 -              switch (check_ref_format(remote)) {
 -              case 0: /* ok */
 -              case CHECK_REF_FORMAT_ONELEVEL:
 -                      /* ok but a single level -- that is fine for
 -                       * a match pattern.
 -                       */
 -              case CHECK_REF_FORMAT_WILDCARD:
 -                      /* ok but ends with a pattern-match character */
 -                      continue;
 -              }
 -              die("remote part of refspec is not a valid name in %s",
 -                  heads[i]);
 -      }
 -}
 -
  int cmd_send_pack(int argc, const char **argv, const char *prefix)
  {
        int i, nr_refspecs = 0;
        int send_all = 0;
        const char *receivepack = "git-receive-pack";
        int flags;
 +      int nonfastforward = 0;
  
        argv++;
        for (i = 1; i < argc; i++, argv++) {
        get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
                         &extra_have);
  
 -      verify_remote_names(nr_refspecs, refspecs);
 +      transport_verify_remote_names(nr_refspecs, refspecs);
  
        local_refs = get_local_heads();
  
        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)
        ret |= finish_connect(conn);
  
        if (!helper_status)
 -              print_push_status(dest, remote_refs);
 +              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)
 -                      update_tracking_ref(remote, ref);
 +                      transport_update_tracking_ref(remote, ref, args.verbose);
        }
  
 -      if (!ret && !refs_pushed(remote_refs))
 +      if (!ret && !transport_refs_pushed(remote_refs))
                fprintf(stderr, "Everything up-to-date\n");
  
        return ret;
diff --combined cache.h
+++ 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
+++ 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;
 +}