Merge branch 'ew/force-ipv4'
authorJunio C Hamano <gitster@pobox.com>
Wed, 24 Feb 2016 21:25:54 +0000 (13:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 24 Feb 2016 21:25:54 +0000 (13:25 -0800)
"git fetch" and friends that make network connections can now be
told to only use ipv4 (or ipv6).

* ew/force-ipv4:
connect & http: support -4 and -6 switches for remote operations

1  2 
Documentation/fetch-options.txt
Documentation/git-push.txt
builtin/clone.c
builtin/fetch.c
builtin/push.c
http.c
http.h
remote-curl.c
transport-helper.c
transport.c
index 952dfdfef098ca00b7976ef9d549401f171514f6,b94d2c50371818ae0e774b70019d84d2ef6ee3fc..036edfb099c367262f34d2c1275ac5079a1e0914
@@@ -8,11 -8,10 +8,11 @@@
        option old data in `.git/FETCH_HEAD` will be overwritten.
  
  --depth=<depth>::
 -      Deepen or shorten the history of a 'shallow' repository created by
 -      `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
 -      to the specified number of commits from the tip of each remote
 -      branch history. Tags for the deepened commits are not fetched.
 +      Limit fetching to the specified number of commits from the tip of
 +      each remote branch history. If fetching to a 'shallow' repository
 +      created by `git clone` with `--depth=<depth>` option (see
 +      linkgit:git-clone[1]), deepen or shorten the history to the specified
 +      number of commits. Tags for the deepened commits are not fetched.
  
  --unshallow::
        If the source repository is complete, convert a shallow
@@@ -101,13 -100,6 +101,13 @@@ ifndef::git-pull[
        reference to a commit that isn't already in the local submodule
        clone.
  
 +-j::
 +--jobs=<n>::
 +      Number of parallel children to be used for fetching submodules.
 +      Each will fetch from different submodules, such that fetching many
 +      submodules will be faster. By default submodules will be fetched
 +      one at a time.
 +
  --no-recurse-submodules::
        Disable recursive fetching of submodules (this has the same effect as
        using the '--recurse-submodules=no' option).
@@@ -158,3 -150,11 +158,11 @@@ endif::git-pull[
        by default when it is attached to a terminal, unless -q
        is specified. This flag forces progress status even if the
        standard error stream is not directed to a terminal.
+ -4::
+ --ipv4::
+       Use IPv4 addresses only, ignoring IPv6 addresses.
+ -6::
+ --ipv6::
+       Use IPv6 addresses only, ignoring IPv4 addresses.
index 32482cec42d3ea5661813097919ff38657c2e01c,a284d7240167ee692cd4961b0a20a2ef54f4e9d0..669a357c81fbba17c245adc3660cdd07479e564e
@@@ -10,7 -10,7 +10,7 @@@ SYNOPSI
  --------
  [verse]
  'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
 -         [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
 +         [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
           [-u | --set-upstream]
           [--[no-]signed|--sign=(true|false|if-asked)]
           [--force-with-lease[=<refname>[:<expect>]]]
@@@ -277,6 -277,13 +277,13 @@@ origin +master` to force a push to the 
        default is --verify, giving the hook a chance to prevent the
        push.  With --no-verify, the hook is bypassed completely.
  
+ -4::
+ --ipv4::
+       Use IPv4 addresses only, ignoring IPv6 addresses.
+ -6::
+ --ipv6::
+       Use IPv6 addresses only, ignoring IPv4 addresses.
  
  include::urls-remotes.txt[]
  
diff --combined builtin/clone.c
index bcba0805e1c9c5691c2e876f9748a2cf492b1c70,f3dee7158b6e46f53a8016e51fb10452b6b12d67..b8c39aa701d6fd53f5bbb121ef9b9f0e77ca9d6d
@@@ -47,6 -47,7 +47,7 @@@ static const char *real_git_dir
  static char *option_upload_pack = "git-upload-pack";
  static int option_verbosity;
  static int option_progress = -1;
+ static enum transport_family family;
  static struct string_list option_config;
  static struct string_list option_reference;
  static int option_dissociate;
@@@ -92,6 -93,10 +93,10 @@@ static struct option builtin_clone_opti
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
+       OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+                       TRANSPORT_FAMILY_IPV4),
+       OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+                       TRANSPORT_FAMILY_IPV6),
        OPT_END()
  };
  
@@@ -339,7 -344,7 +344,7 @@@ static void copy_alternates(struct strb
        FILE *in = fopen(src->buf, "r");
        struct strbuf line = STRBUF_INIT;
  
 -      while (strbuf_getline(&line, in, '\n') != EOF) {
 +      while (strbuf_getline(&line, in) != EOF) {
                char *abs_path;
                if (!line.len || line.buf[0] == '#')
                        continue;
@@@ -636,11 -641,9 +641,11 @@@ static void update_remote_refs(const st
                struct strbuf head_ref = STRBUF_INIT;
                strbuf_addstr(&head_ref, branch_top);
                strbuf_addstr(&head_ref, "HEAD");
 -              create_symref(head_ref.buf,
 -                            remote_head_points_at->peer_ref->name,
 -                            msg);
 +              if (create_symref(head_ref.buf,
 +                                remote_head_points_at->peer_ref->name,
 +                                msg) < 0)
 +                      die("unable to update %s", head_ref.buf);
 +              strbuf_release(&head_ref);
        }
  }
  
@@@ -650,8 -653,7 +655,8 @@@ static void update_head(const struct re
        const char *head;
        if (our && skip_prefix(our->name, "refs/heads/", &head)) {
                /* Local default branch link */
 -              create_symref("HEAD", our->name, NULL);
 +              if (create_symref("HEAD", our->name, NULL) < 0)
 +                      die("unable to update HEAD");
                if (!option_bare) {
                        update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
@@@ -970,6 -972,7 +975,7 @@@ int cmd_clone(int argc, const char **ar
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
        transport_set_verbosity(transport, option_verbosity, option_progress);
+       transport->family = family;
  
        path = get_repo_path(remote->url[0], &is_bundle);
        is_local = option_local != 0 && path && !is_bundle;
diff --combined builtin/fetch.c
index 8e742135f049c2792bfc5a20299dac88518929cc,89b71a8f852780dddd902a519d1183608040fe47..55919a9e4fb8e2fd538b6c111bfdacf83acf62ed
@@@ -37,7 -37,7 +37,8 @@@ static int prune = -1; /* unspecified *
  static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
  static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static int tags = TAGS_DEFAULT, unshallow, update_shallow;
 +static int max_children = 1;
+ static enum transport_family family;
  static const char *depth;
  static const char *upload_pack;
  static struct strbuf default_rla = STRBUF_INIT;
@@@ -100,8 -100,6 +101,8 @@@ static struct option builtin_fetch_opti
                    N_("fetch all tags and associated objects"), TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
                    N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
 +      OPT_INTEGER('j', "jobs", &max_children,
 +                  N_("number of submodules fetched in parallel")),
        OPT_BOOL('p', "prune", &prune,
                 N_("prune remote-tracking branches no longer on remote")),
        { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
                 N_("accept refs that update .git/shallow")),
        { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
          N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+       OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+                       TRANSPORT_FAMILY_IPV4),
+       OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+                       TRANSPORT_FAMILY_IPV6),
        OPT_END()
  };
  
@@@ -840,7 -842,7 +845,7 @@@ static void check_not_current_branch(st
  static int truncate_fetch_head(void)
  {
        const char *filename = git_path_fetch_head();
 -      FILE *fp = fopen(filename, "w");
 +      FILE *fp = fopen_for_writing(filename);
  
        if (!fp)
                return error(_("cannot open %s: %s\n"), filename, strerror(errno));
@@@ -864,6 -866,7 +869,7 @@@ static struct transport *prepare_transp
        struct transport *transport;
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
+       transport->family = family;
        if (upload_pack)
                set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
        if (keep)
@@@ -1216,8 -1219,7 +1222,8 @@@ int cmd_fetch(int argc, const char **ar
                result = fetch_populated_submodules(&options,
                                                    submodule_prefix,
                                                    recurse_submodules,
 -                                                  verbosity < 0);
 +                                                  verbosity < 0,
 +                                                  max_children);
                argv_array_clear(&options);
        }
  
        list.strdup_strings = 1;
        string_list_clear(&list, 0);
  
 +      close_all_packs();
 +
        argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
        if (verbosity < 0)
                argv_array_push(&argv_gc_auto, "--quiet");
diff --combined builtin/push.c
index 960ffc31e1537441a1120dceb120ce584b7edccf,833c9833d8abc435b8694d7b9aa3772cf874cd99..6e13b3c90a08ade04aa7f84b2f3a3c869c0288bd
@@@ -23,6 -23,7 +23,7 @@@ static const char *receivepack
  static int verbosity;
  static int progress = -1;
  static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+ static enum transport_family family;
  
  static struct push_cas_option cas;
  
@@@ -346,6 -347,7 +347,7 @@@ static int push_with_options(struct tra
        unsigned int reject_reasons;
  
        transport_set_verbosity(transport, verbosity, progress);
+       transport->family = family;
  
        if (receivepack)
                transport_set_option(transport,
@@@ -538,7 -540,7 +540,7 @@@ int cmd_push(int argc, const char **arg
                OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
                OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
 -              OPT_BOOL( 0, "delete", &deleterefs, N_("delete refs")),
 +              OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")),
                OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
                OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
                OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
                  0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
                  PARSE_OPT_OPTARG, option_parse_push_signed },
                OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
+               OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+                               TRANSPORT_FAMILY_IPV4),
+               OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+                               TRANSPORT_FAMILY_IPV6),
                OPT_END()
        };
  
diff --combined http.c
index dfc53c1e2554e76126459d6cb1f098facac28593,67e7bc21b075402326ac8ba9d522476e562a577d..f543b142781644900721cd16492faf276e020645
--- 1/http.c
--- 2/http.c
+++ b/http.c
  #include "gettext.h"
  #include "transport.h"
  
+ #if LIBCURL_VERSION_NUM >= 0x070a08
+ long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
+ #else
+ long int git_curl_ipresolve;
+ #endif
  int active_requests;
  int http_is_verbose;
  size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
@@@ -62,26 -67,6 +67,26 @@@ static long curl_low_speed_limit = -1
  static long curl_low_speed_time = -1;
  static int curl_ftp_no_epsv;
  static const char *curl_http_proxy;
 +static const char *http_proxy_authmethod;
 +static struct {
 +      const char *name;
 +      long curlauth_param;
 +} proxy_authmethods[] = {
 +      { "basic", CURLAUTH_BASIC },
 +      { "digest", CURLAUTH_DIGEST },
 +      { "negotiate", CURLAUTH_GSSNEGOTIATE },
 +      { "ntlm", CURLAUTH_NTLM },
 +#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 +      { "anyauth", CURLAUTH_ANY },
 +#endif
 +      /*
 +       * CURLAUTH_DIGEST_IE has no corresponding command-line option in
 +       * curl(1) and is not included in CURLAUTH_ANY, so we leave it out
 +       * here, too
 +       */
 +};
 +static struct credential proxy_auth = CREDENTIAL_INIT;
 +static const char *curl_proxyuserpwd;
  static const char *curl_cookie_file;
  static int curl_save_cookies;
  struct credential http_auth = CREDENTIAL_INIT;
@@@ -179,9 -164,6 +184,9 @@@ static void finish_active_slot(struct a
  #else
                slot->results->auth_avail = 0;
  #endif
 +
 +              curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CONNECTCODE,
 +                      &slot->results->http_connectcode);
        }
  
        /* Run callback if appropriate */
@@@ -279,9 -261,6 +284,9 @@@ static int http_options(const char *var
        if (!strcmp("http.proxy", var))
                return git_config_string(&curl_http_proxy, var, value);
  
 +      if (!strcmp("http.proxyauthmethod", var))
 +              return git_config_string(&http_proxy_authmethod, var, value);
 +
        if (!strcmp("http.cookiefile", var))
                return git_config_string(&curl_cookie_file, var, value);
        if (!strcmp("http.savecookies", var)) {
@@@ -330,64 -309,6 +335,64 @@@ static void init_curl_http_auth(CURL *r
  #endif
  }
  
 +/* *var must be free-able */
 +static void var_override(const char **var, char *value)
 +{
 +      if (value) {
 +              free((void *)*var);
 +              *var = xstrdup(value);
 +      }
 +}
 +
 +static void set_proxyauth_name_password(CURL *result)
 +{
 +#if LIBCURL_VERSION_NUM >= 0x071301
 +              curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
 +                      proxy_auth.username);
 +              curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
 +                      proxy_auth.password);
 +#else
 +              struct strbuf s = STRBUF_INIT;
 +
 +              strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
 +              strbuf_addch(&s, ':');
 +              strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
 +              curl_proxyuserpwd = strbuf_detach(&s, NULL);
 +              curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
 +#endif
 +}
 +
 +static void init_curl_proxy_auth(CURL *result)
 +{
 +      if (proxy_auth.username) {
 +              if (!proxy_auth.password)
 +                      credential_fill(&proxy_auth);
 +              set_proxyauth_name_password(result);
 +      }
 +
 +      var_override(&http_proxy_authmethod, getenv("GIT_HTTP_PROXY_AUTHMETHOD"));
 +
 +#if LIBCURL_VERSION_NUM >= 0x070a07 /* CURLOPT_PROXYAUTH and CURLAUTH_ANY */
 +      if (http_proxy_authmethod) {
 +              int i;
 +              for (i = 0; i < ARRAY_SIZE(proxy_authmethods); i++) {
 +                      if (!strcmp(http_proxy_authmethod, proxy_authmethods[i].name)) {
 +                              curl_easy_setopt(result, CURLOPT_PROXYAUTH,
 +                                              proxy_authmethods[i].curlauth_param);
 +                              break;
 +                      }
 +              }
 +              if (i == ARRAY_SIZE(proxy_authmethods)) {
 +                      warning("unsupported proxy authentication method %s: using anyauth",
 +                                      http_proxy_authmethod);
 +                      curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 +              }
 +      }
 +      else
 +              curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 +#endif
 +}
 +
  static int has_cert_password(void)
  {
        if (ssl_cert == NULL || ssl_cert_password_required != 1)
@@@ -546,31 -467,6 +551,31 @@@ static CURL *get_curl_handle(void
                curl_easy_setopt(result, CURLOPT_USE_SSL, CURLUSESSL_TRY);
  #endif
  
 +      /*
 +       * CURL also examines these variables as a fallback; but we need to query
 +       * them here in order to decide whether to prompt for missing password (cf.
 +       * init_curl_proxy_auth()).
 +       *
 +       * Unlike many other common environment variables, these are historically
 +       * lowercase only. It appears that CURL did not know this and implemented
 +       * only uppercase variants, which was later corrected to take both - with
 +       * the exception of http_proxy, which is lowercase only also in CURL. As
 +       * the lowercase versions are the historical quasi-standard, they take
 +       * precedence here, as in CURL.
 +       */
 +      if (!curl_http_proxy) {
 +              if (!strcmp(http_auth.protocol, "https")) {
 +                      var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));
 +                      var_override(&curl_http_proxy, getenv("https_proxy"));
 +              } else {
 +                      var_override(&curl_http_proxy, getenv("http_proxy"));
 +              }
 +              if (!curl_http_proxy) {
 +                      var_override(&curl_http_proxy, getenv("ALL_PROXY"));
 +                      var_override(&curl_http_proxy, getenv("all_proxy"));
 +              }
 +      }
 +
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
  #if LIBCURL_VERSION_NUM >= 0x071800
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  #endif
 +              if (strstr(curl_http_proxy, "://"))
 +                      credential_from_url(&proxy_auth, curl_http_proxy);
 +              else {
 +                      struct strbuf url = STRBUF_INIT;
 +                      strbuf_addf(&url, "http://%s", curl_http_proxy);
 +                      credential_from_url(&proxy_auth, url.buf);
 +                      strbuf_release(&url);
 +              }
 +
 +              curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
        }
 -#if LIBCURL_VERSION_NUM >= 0x070a07
 -      curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 -#endif
 +      init_curl_proxy_auth(result);
  
        set_curl_keepalive(result);
  
@@@ -636,9 -524,6 +641,9 @@@ void http_init(struct remote *remote, c
        if (remote && remote->http_proxy)
                curl_http_proxy = xstrdup(remote->http_proxy);
  
 +      if (remote)
 +              var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
 +
        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
  
@@@ -737,18 -622,6 +742,18 @@@ void http_cleanup(void
                curl_http_proxy = NULL;
        }
  
 +      if (proxy_auth.password) {
 +              memset(proxy_auth.password, 0, strlen(proxy_auth.password));
 +              free(proxy_auth.password);
 +              proxy_auth.password = NULL;
 +      }
 +
 +      free((void *)curl_proxyuserpwd);
 +      curl_proxyuserpwd = NULL;
 +
 +      free((void *)http_proxy_authmethod);
 +      http_proxy_authmethod = NULL;
 +
        if (cert_auth.password != NULL) {
                memset(cert_auth.password, 0, strlen(cert_auth.password));
                free(cert_auth.password);
@@@ -824,6 -697,10 +829,10 @@@ struct active_request_slot *get_active_
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
        curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
+ #if LIBCURL_VERSION_NUM >= 0x070a08
+       curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
+ #endif
  #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
  #endif
@@@ -1078,8 -955,6 +1087,8 @@@ static int handle_curl_result(struct sl
  
        if (results->curl_result == CURLE_OK) {
                credential_approve(&http_auth);
 +              if (proxy_auth.password)
 +                      credential_approve(&proxy_auth);
                return HTTP_OK;
        } else if (missing_target(results))
                return HTTP_MISSING_TARGET;
                        return HTTP_REAUTH;
                }
        } else {
 +              if (results->http_connectcode == 407)
 +                      credential_reject(&proxy_auth);
  #if LIBCURL_VERSION_NUM >= 0x070c00
                if (!curl_errorstr[0])
                        strlcpy(curl_errorstr,
diff --combined http.h
index f83cfa686823728587b2a803c3e84a8cd4669220,fa45c2b3646d56d5ad937f4add980294ca03f205..4ef4bbda7d86d8ca5238d45897b277709b30414a
--- 1/http.h
--- 2/http.h
+++ b/http.h
@@@ -54,7 -54,6 +54,7 @@@ struct slot_results 
        CURLcode curl_result;
        long http_code;
        long auth_avail;
 +      long http_connectcode;
  };
  
  struct active_request_slot {
@@@ -107,6 -106,7 +107,7 @@@ extern void http_init(struct remote *re
                      int proactive_auth);
  extern void http_cleanup(void);
  
+ extern long int git_curl_ipresolve;
  extern int active_requests;
  extern int http_is_verbose;
  extern size_t http_post_buffer;
diff --combined remote-curl.c
index c7048575fbdca1bd8dc57c114d0cfdc031e63039,70bcac818637c39067bde2aa1ec75bddeaf864eb..b33a1e4235bf4fa7c3a95c680e289b8b8a3cb7bb
@@@ -119,6 -119,19 +119,19 @@@ static int set_option(const char *name
                else
                        return -1;
                return 0;
+ #if LIBCURL_VERSION_NUM >= 0x070a08
+       } else if (!strcmp(name, "family")) {
+               if (!strcmp(value, "ipv4"))
+                       git_curl_ipresolve = CURL_IPRESOLVE_V4;
+               else if (!strcmp(value, "ipv6"))
+                       git_curl_ipresolve = CURL_IPRESOLVE_V6;
+               else if (!strcmp(value, "all"))
+                       git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
+               else
+                       return -1;
+               return 0;
+ #endif /* LIBCURL_VERSION_NUM >= 0x070a08 */
        } else {
                return 1 /* unsupported */;
        }
@@@ -827,7 -840,7 +840,7 @@@ static void parse_fetch(struct strbuf *
                        die("http transport does not support %s", buf->buf);
  
                strbuf_reset(buf);
 -              if (strbuf_getline(buf, stdin, '\n') == EOF)
 +              if (strbuf_getline_lf(buf, stdin) == EOF)
                        return;
                if (!*buf->buf)
                        break;
@@@ -940,7 -953,7 +953,7 @@@ static void parse_push(struct strbuf *b
                        die("http transport does not support %s", buf->buf);
  
                strbuf_reset(buf);
 -              if (strbuf_getline(buf, stdin, '\n') == EOF)
 +              if (strbuf_getline_lf(buf, stdin) == EOF)
                        goto free_specs;
                if (!*buf->buf)
                        break;
@@@ -990,7 -1003,7 +1003,7 @@@ int main(int argc, const char **argv
        do {
                const char *arg;
  
 -              if (strbuf_getline(&buf, stdin, '\n') == EOF) {
 +              if (strbuf_getline_lf(&buf, stdin) == EOF) {
                        if (ferror(stdin))
                                error("remote-curl: error reading command stream from git");
                        return 1;
diff --combined transport-helper.c
index a6bff8b30811c649c82afcdc9e018af1a34ce13c,d50242634fcbc6ec20538a098772184ed0493170..b934183236ca571093f55818f5018c278150e274
@@@ -54,7 -54,7 +54,7 @@@ static int recvline_fh(FILE *helper, st
        strbuf_reset(buffer);
        if (debug)
                fprintf(stderr, "Debug: Remote helper: Waiting...\n");
 -      if (strbuf_getline(buffer, helper, '\n') == EOF) {
 +      if (strbuf_getline(buffer, helper) == EOF) {
                if (debug)
                        fprintf(stderr, "Debug: Remote helper quit.\n");
                return 1;
@@@ -137,8 -137,7 +137,8 @@@ static struct child_process *get_helper
        data->no_disconnect_req = 0;
  
        /*
 -       * Open the output as FILE* so strbuf_getline() can be used.
 +       * Open the output as FILE* so strbuf_getline_*() family of
 +       * functions can be used.
         * Do this with duped fd because fclose() will close the fd,
         * and stuff like taking over will require the fd to remain.
         */
@@@ -321,6 -320,21 +321,21 @@@ static void standard_options(struct tra
        if (n >= sizeof(buf))
                die("impossibly large verbosity value");
        set_helper_option(t, "verbosity", buf);
+       switch (t->family) {
+       case TRANSPORT_FAMILY_ALL:
+               /*
+                * this is already the default,
+                * do not break old remote helpers by setting "all" here
+                */
+               break;
+       case TRANSPORT_FAMILY_IPV4:
+               set_helper_option(t, "family", "ipv4");
+               break;
+       case TRANSPORT_FAMILY_IPV6:
+               set_helper_option(t, "family", "ipv6");
+               break;
+       }
  }
  
  static int release_helper(struct transport *transport)
diff --combined transport.c
index c92f8ae7054e3340cf1a40c3f74c70b7aab7303a,afcec433f3f5a5fa77c6c8d3adb3b3dc1d972ca5..e20bb771faaa56aa876f14f73123e14a71731a1b
  #include "sha1-array.h"
  #include "sigchain.h"
  
 -/* rsync support */
 -
 -/*
 - * We copy packed-refs and refs/ into a temporary file, then read the
 - * loose refs recursively (sorting whenever possible), and then inserting
 - * those packed refs that are not yet in the list (not validating, but
 - * assuming that the file is sorted).
 - *
 - * Appears refactoring this from refs.c is too cumbersome.
 - */
 -
 -static int str_cmp(const void *a, const void *b)
 -{
 -      const char *s1 = a;
 -      const char *s2 = b;
 -
 -      return strcmp(s1, s2);
 -}
 -
 -/* path->buf + name_offset is expected to point to "refs/" */
 -
 -static int read_loose_refs(struct strbuf *path, int name_offset,
 -              struct ref **tail)
 -{
 -      DIR *dir = opendir(path->buf);
 -      struct dirent *de;
 -      struct {
 -              char **entries;
 -              int nr, alloc;
 -      } list;
 -      int i, pathlen;
 -
 -      if (!dir)
 -              return -1;
 -
 -      memset (&list, 0, sizeof(list));
 -
 -      while ((de = readdir(dir))) {
 -              if (is_dot_or_dotdot(de->d_name))
 -                      continue;
 -              ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
 -              list.entries[list.nr++] = xstrdup(de->d_name);
 -      }
 -      closedir(dir);
 -
 -      /* sort the list */
 -
 -      qsort(list.entries, list.nr, sizeof(char *), str_cmp);
 -
 -      pathlen = path->len;
 -      strbuf_addch(path, '/');
 -
 -      for (i = 0; i < list.nr; i++, strbuf_setlen(path, pathlen + 1)) {
 -              strbuf_addstr(path, list.entries[i]);
 -              if (read_loose_refs(path, name_offset, tail)) {
 -                      int fd = open(path->buf, O_RDONLY);
 -                      char buffer[40];
 -                      struct ref *next;
 -
 -                      if (fd < 0)
 -                              continue;
 -                      next = alloc_ref(path->buf + name_offset);
 -                      if (read_in_full(fd, buffer, 40) != 40 ||
 -                                      get_oid_hex(buffer, &next->old_oid)) {
 -                              close(fd);
 -                              free(next);
 -                              continue;
 -                      }
 -                      close(fd);
 -                      (*tail)->next = next;
 -                      *tail = next;
 -              }
 -      }
 -      strbuf_setlen(path, pathlen);
 -
 -      for (i = 0; i < list.nr; i++)
 -              free(list.entries[i]);
 -      free(list.entries);
 -
 -      return 0;
 -}
 -
 -/* insert the packed refs for which no loose refs were found */
 -
 -static void insert_packed_refs(const char *packed_refs, struct ref **list)
 -{
 -      FILE *f = fopen(packed_refs, "r");
 -      static char buffer[PATH_MAX];
 -
 -      if (!f)
 -              return;
 -
 -      for (;;) {
 -              int cmp = 0; /* assigned before used */
 -              int len;
 -
 -              if (!fgets(buffer, sizeof(buffer), f)) {
 -                      fclose(f);
 -                      return;
 -              }
 -
 -              if (!isxdigit(buffer[0]))
 -                      continue;
 -              len = strlen(buffer);
 -              if (len && buffer[len - 1] == '\n')
 -                      buffer[--len] = '\0';
 -              if (len < 41)
 -                      continue;
 -              while ((*list)->next &&
 -                              (cmp = strcmp(buffer + 41,
 -                                    (*list)->next->name)) > 0)
 -                      list = &(*list)->next;
 -              if (!(*list)->next || cmp < 0) {
 -                      struct ref *next = alloc_ref(buffer + 41);
 -                      buffer[40] = '\0';
 -                      if (get_oid_hex(buffer, &next->old_oid)) {
 -                              warning ("invalid SHA-1: %s", buffer);
 -                              free(next);
 -                              continue;
 -                      }
 -                      next->next = (*list)->next;
 -                      (*list)->next = next;
 -                      list = &(*list)->next;
 -              }
 -      }
 -}
 -
  static void set_upstreams(struct transport *transport, struct ref *refs,
        int pretend)
  {
        }
  }
  
 -static const char *rsync_url(const char *url)
 -{
 -      if (!starts_with(url, "rsync://"))
 -              skip_prefix(url, "rsync:", &url);
 -      return url;
 -}
 -
 -static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 -{
 -      struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
 -      struct ref dummy = {NULL}, *tail = &dummy;
 -      struct child_process rsync = CHILD_PROCESS_INIT;
 -      const char *args[5];
 -      int temp_dir_len;
 -
 -      if (for_push)
 -              return NULL;
 -
 -      /* copy the refs to the temporary directory */
 -
 -      strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
 -      if (!mkdtemp(temp_dir.buf))
 -              die_errno ("Could not make temporary directory");
 -      temp_dir_len = temp_dir.len;
 -
 -      strbuf_addstr(&buf, rsync_url(transport->url));
 -      strbuf_addstr(&buf, "/refs");
 -
 -      rsync.argv = args;
 -      rsync.stdout_to_stderr = 1;
 -      args[0] = "rsync";
 -      args[1] = (transport->verbose > 1) ? "-rv" : "-r";
 -      args[2] = buf.buf;
 -      args[3] = temp_dir.buf;
 -      args[4] = NULL;
 -
 -      if (run_command(&rsync))
 -              die ("Could not run rsync to get refs");
 -
 -      strbuf_reset(&buf);
 -      strbuf_addstr(&buf, rsync_url(transport->url));
 -      strbuf_addstr(&buf, "/packed-refs");
 -
 -      args[2] = buf.buf;
 -
 -      if (run_command(&rsync))
 -              die ("Could not run rsync to get refs");
 -
 -      /* read the copied refs */
 -
 -      strbuf_addstr(&temp_dir, "/refs");
 -      read_loose_refs(&temp_dir, temp_dir_len + 1, &tail);
 -      strbuf_setlen(&temp_dir, temp_dir_len);
 -
 -      tail = &dummy;
 -      strbuf_addstr(&temp_dir, "/packed-refs");
 -      insert_packed_refs(temp_dir.buf, &tail);
 -      strbuf_setlen(&temp_dir, temp_dir_len);
 -
 -      if (remove_dir_recursively(&temp_dir, 0))
 -              warning ("Error removing temporary directory %s.",
 -                              temp_dir.buf);
 -
 -      strbuf_release(&buf);
 -      strbuf_release(&temp_dir);
 -
 -      return dummy.next;
 -}
 -
 -static int fetch_objs_via_rsync(struct transport *transport,
 -                              int nr_objs, struct ref **to_fetch)
 -{
 -      struct child_process rsync = CHILD_PROCESS_INIT;
 -
 -      rsync.stdout_to_stderr = 1;
 -      argv_array_push(&rsync.args, "rsync");
 -      argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
 -      argv_array_push(&rsync.args, "--ignore-existing");
 -      argv_array_push(&rsync.args, "--exclude");
 -      argv_array_push(&rsync.args, "info");
 -      argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url));
 -      argv_array_push(&rsync.args, get_object_directory());
 -
 -      /* NEEDSWORK: handle one level of alternates */
 -      return run_command(&rsync);
 -}
 -
 -static int write_one_ref(const char *name, const struct object_id *oid,
 -                       int flags, void *data)
 -{
 -      struct strbuf *buf = data;
 -      int len = buf->len;
 -
 -      /* when called via for_each_ref(), flags is non-zero */
 -      if (flags && !starts_with(name, "refs/heads/") &&
 -                      !starts_with(name, "refs/tags/"))
 -              return 0;
 -
 -      strbuf_addstr(buf, name);
 -      if (safe_create_leading_directories(buf->buf) ||
 -          write_file_gently(buf->buf, "%s", oid_to_hex(oid)))
 -              return error("problems writing temporary file %s: %s",
 -                           buf->buf, strerror(errno));
 -      strbuf_setlen(buf, len);
 -      return 0;
 -}
 -
 -static int write_refs_to_temp_dir(struct strbuf *temp_dir,
 -                                int refspec_nr, const char **refspec)
 -{
 -      int i;
 -
 -      for (i = 0; i < refspec_nr; i++) {
 -              struct object_id oid;
 -              char *ref;
 -
 -              if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1)
 -                      return error("Could not get ref %s", refspec[i]);
 -
 -              if (write_one_ref(ref, &oid, 0, temp_dir)) {
 -                      free(ref);
 -                      return -1;
 -              }
 -              free(ref);
 -      }
 -      return 0;
 -}
 -
 -static int rsync_transport_push(struct transport *transport,
 -              int refspec_nr, const char **refspec, int flags)
 -{
 -      struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
 -      int result = 0, i;
 -      struct child_process rsync = CHILD_PROCESS_INIT;
 -      const char *args[10];
 -
 -      if (flags & TRANSPORT_PUSH_MIRROR)
 -              return error("rsync transport does not support mirror mode");
 -
 -      /* first push the objects */
 -
 -      strbuf_addstr(&buf, rsync_url(transport->url));
 -      strbuf_addch(&buf, '/');
 -
 -      rsync.argv = args;
 -      rsync.stdout_to_stderr = 1;
 -      i = 0;
 -      args[i++] = "rsync";
 -      args[i++] = "-a";
 -      if (flags & TRANSPORT_PUSH_DRY_RUN)
 -              args[i++] = "--dry-run";
 -      if (transport->verbose > 1)
 -              args[i++] = "-v";
 -      args[i++] = "--ignore-existing";
 -      args[i++] = "--exclude";
 -      args[i++] = "info";
 -      args[i++] = get_object_directory();
 -      args[i++] = buf.buf;
 -      args[i++] = NULL;
 -
 -      if (run_command(&rsync))
 -              return error("Could not push objects to %s",
 -                              rsync_url(transport->url));
 -
 -      /* copy the refs to the temporary directory; they could be packed. */
 -
 -      strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
 -      if (!mkdtemp(temp_dir.buf))
 -              die_errno ("Could not make temporary directory");
 -      strbuf_addch(&temp_dir, '/');
 -
 -      if (flags & TRANSPORT_PUSH_ALL) {
 -              if (for_each_ref(write_one_ref, &temp_dir))
 -                      return -1;
 -      } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec))
 -              return -1;
 -
 -      i = 2;
 -      if (flags & TRANSPORT_PUSH_DRY_RUN)
 -              args[i++] = "--dry-run";
 -      if (!(flags & TRANSPORT_PUSH_FORCE))
 -              args[i++] = "--ignore-existing";
 -      args[i++] = temp_dir.buf;
 -      args[i++] = rsync_url(transport->url);
 -      args[i++] = NULL;
 -      if (run_command(&rsync))
 -              result = error("Could not push to %s",
 -                              rsync_url(transport->url));
 -
 -      if (remove_dir_recursively(&temp_dir, 0))
 -              warning ("Could not remove temporary directory %s.",
 -                              temp_dir.buf);
 -
 -      strbuf_release(&buf);
 -      strbuf_release(&temp_dir);
 -
 -      return result;
 -}
 -
  struct bundle_transport_data {
        int fd;
        struct bundle_header header;
@@@ -163,6 -489,12 +163,12 @@@ static int connect_setup(struct transpo
        if (data->conn)
                return 0;
  
+       switch (transport->family) {
+       case TRANSPORT_FAMILY_ALL: break;
+       case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;
+       case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;
+       }
        data->conn = git_connect(data->fd, transport->url,
                                 for_push ? data->options.receivepack :
                                 data->options.uploadpack,
@@@ -659,7 -991,11 +665,7 @@@ struct transport *transport_get(struct 
        if (helper) {
                transport_helper_init(ret, helper);
        } else if (starts_with(url, "rsync:")) {
 -              transport_check_allowed("rsync");
 -              ret->get_refs_list = get_refs_via_rsync;
 -              ret->fetch = fetch_objs_via_rsync;
 -              ret->push = rsync_transport_push;
 -              ret->smart_options = NULL;
 +              die("git-over-rsync is no longer supported");
        } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                transport_check_allowed("file");