Sync with 2.3.10
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Sep 2015 22:28:26 +0000 (15:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Sep 2015 22:28:31 +0000 (15:28 -0700)
12 files changed:
1  2 
Documentation/git.txt
builtin/blame.c
builtin/merge-file.c
builtin/rerere.c
combine-diff.c
connect.c
diff.c
http.c
line-log.c
transport-helper.c
transport.c
transport.h

diff --combined Documentation/git.txt
@@@ -43,23 -43,10 +43,24 @@@ unreleased) version of Git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
- * link:v2.3.9/git.html[documentation for release 2.3.9]
 +* link:v2.4.9/git.html[documentation for release 2.4.9]
 +
 +* release notes for
 +  link:RelNotes/2.4.9.txt[2.4.9],
 +  link:RelNotes/2.4.8.txt[2.4.8],
 +  link:RelNotes/2.4.7.txt[2.4.7],
 +  link:RelNotes/2.4.6.txt[2.4.6],
 +  link:RelNotes/2.4.5.txt[2.4.5],
 +  link:RelNotes/2.4.4.txt[2.4.4],
 +  link:RelNotes/2.4.3.txt[2.4.3],
 +  link:RelNotes/2.4.2.txt[2.4.2],
 +  link:RelNotes/2.4.1.txt[2.4.1],
 +  link:RelNotes/2.4.0.txt[2.4].
 +
+ * link:v2.3.10/git.html[documentation for release 2.3.10]
  
  * release notes for
+   link:RelNotes/2.3.10.txt[2.3.10],
    link:RelNotes/2.3.9.txt[2.3.9],
    link:RelNotes/2.3.8.txt[2.3.8],
    link:RelNotes/2.3.7.txt[2.3.7],
@@@ -941,7 -928,7 +942,7 @@@ for further details
        If this environment variable is set, then Git commands which need to
        acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
        will call this program with a suitable prompt as command-line argument
 -      and read the password from its STDOUT. See also the 'core.askpass'
 +      and read the password from its STDOUT. See also the 'core.askPass'
        option in linkgit:git-config[1].
  
  'GIT_TERMINAL_PROMPT'::
@@@ -1059,6 -1046,33 +1060,33 @@@ GIT_ICASE_PATHSPECS:
        an operation has touched every ref (e.g., because you are
        cloning a repository to make a backup).
  
+ `GIT_ALLOW_PROTOCOL`::
+       If set, provide a colon-separated list of protocols which are
+       allowed to be used with fetch/push/clone. This is useful to
+       restrict recursive submodule initialization from an untrusted
+       repository. Any protocol not mentioned will be disallowed (i.e.,
+       this is a whitelist, not a blacklist). If the variable is not
+       set at all, all protocols are enabled.  The protocol names
+       currently used by git are:
+         - `file`: any local file-based path (including `file://` URLs,
+           or local paths)
+         - `git`: the anonymous git protocol over a direct TCP
+           connection (or proxy, if configured)
+         - `ssh`: git over ssh (including `host:path` syntax,
+           `git+ssh://`, etc).
+         - `rsync`: git over rsync
+         - `http`: git over http, both "smart http" and "dumb http".
+           Note that this does _not_ include `https`; if you want both,
+           you should specify both as `http:https`.
+         - any external helpers are named by their protocol (e.g., use
+           `hg` to allow the `git-remote-hg` helper)
  
  Discussion[[Discussion]]
  ------------------------
diff --combined builtin/blame.c
  #include "userdiff.h"
  #include "line-range.h"
  #include "line-log.h"
 +#include "dir.h"
  
 -static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file");
 +static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
  
  static const char *blame_opt_usage[] = {
        blame_usage,
        "",
 -      N_("[rev-opts] are documented in git-rev-list(1)"),
 +      N_("<rev-opts> are documented in git-rev-list(1)"),
        NULL
  };
  
@@@ -973,7 -972,10 +973,10 @@@ static void pass_blame_to_parent(struc
        fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
  
-       diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
+       if (diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d))
+               die("unable to generate diff (%s -> %s)",
+                   sha1_to_hex(parent->commit->object.sha1),
+                   sha1_to_hex(target->commit->object.sha1));
        /* The rest are the same as the parent */
        blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
        *d.dstq = NULL;
@@@ -1119,7 -1121,9 +1122,9 @@@ static void find_copy_in_blob(struct sc
         * file_p partially may match that image.
         */
        memset(split, 0, sizeof(struct blame_entry [3]));
-       diff_hunks(file_p, &file_o, 1, handle_split_cb, &d);
+       if (diff_hunks(file_p, &file_o, 1, handle_split_cb, &d))
+               die("unable to generate diff (%s)",
+                   sha1_to_hex(parent->commit->object.sha1));
        /* remainder, if any, all match the preimage */
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
  }
@@@ -2152,6 -2156,16 +2157,6 @@@ static void sanity_check_refcnt(struct 
        }
  }
  
 -/*
 - * Used for the command line parsing; check if the path exists
 - * in the working tree.
 - */
 -static int has_string_in_work_tree(const char *path)
 -{
 -      struct stat st;
 -      return !lstat(path, &st);
 -}
 -
  static unsigned parse_score(const char *arg)
  {
        char *end;
@@@ -2339,7 -2353,6 +2344,7 @@@ static struct commit *fake_working_tree
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
        }
 +      convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
@@@ -2647,14 -2660,14 +2652,14 @@@ parse_done
                if (argc < 2)
                        usage_with_options(blame_opt_usage, options);
                path = add_prefix(prefix, argv[argc - 1]);
 -              if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
 +              if (argc == 3 && !file_exists(path)) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
                }
                argv[argc - 1] = "--";
  
                setup_work_tree();
 -              if (!has_string_in_work_tree(path))
 +              if (!file_exists(path))
                        die_errno("cannot stat path '%s'", path);
        }
  
diff --combined builtin/merge-file.c
@@@ -5,7 -5,7 +5,7 @@@
  #include "parse-options.h"
  
  static const char *const merge_file_usage[] = {
 -      N_("git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"),
 +      N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
        NULL
  };
  
@@@ -42,7 -42,7 +42,7 @@@ int cmd_merge_file(int argc, const cha
                            N_("for conflicts, use this marker size")),
                OPT__QUIET(&quiet, N_("do not warn about conflicts")),
                OPT_CALLBACK('L', NULL, names, N_("name"),
 -                           N_("set labels for file1/orig_file/file2"), &label_cb),
 +                           N_("set labels for file1/orig-file/file2"), &label_cb),
                OPT_END(),
        };
  
@@@ -75,7 -75,8 +75,8 @@@
                        names[i] = argv[i];
                if (read_mmfile(mmfs + i, fname))
                        return -1;
-               if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
+               if (mmfs[i].size > MAX_XDIFF_SIZE ||
+                   buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
                        return error("Cannot merge binary files: %s",
                                        argv[i]);
        }
diff --combined builtin/rerere.c
@@@ -9,7 -9,7 +9,7 @@@
  #include "pathspec.h"
  
  static const char * const rerere_usage[] = {
 -      N_("git rerere [clear | forget path... | status | remaining | diff | gc]"),
 +      N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"),
        NULL,
  };
  
@@@ -29,9 -29,10 +29,10 @@@ static int diff_two(const char *file1, 
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
        mmfile_t minus, plus;
+       int ret;
  
        if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
-               return 1;
+               return -1;
  
        printf("--- a/%s\n+++ b/%s\n", label1, label2);
        fflush(stdout);
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        ecb.outf = outf;
-       xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+       ret = xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
  
        free(minus.ptr);
        free(plus.ptr);
-       return 0;
+       return ret;
  }
  
  int cmd_rerere(int argc, const char **argv, const char *prefix)
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *path = merge_rr.items[i].string;
                        const char *name = (const char *)merge_rr.items[i].util;
-                       diff_two(rerere_path(name, "preimage"), path, path, path);
+                       if (diff_two(rerere_path(name, "preimage"), path, path, path))
+                               die("unable to generate diff for %s", name);
                }
        else
                usage_with_options(rerere_usage, options);
diff --combined combine-diff.c
@@@ -419,8 -419,10 +419,10 @@@ static void combine_diff(const unsigne
        state.num_parent = num_parent;
        state.n = n;
  
-       xdi_diff_outf(&parent_file, result_file, consume_line, &state,
-                     &xpp, &xecfg);
+       if (xdi_diff_outf(&parent_file, result_file, consume_line, &state,
+                         &xpp, &xecfg))
+               die("unable to generate combined diff for %s",
+                   sha1_to_hex(parent));
        free(parent_file.ptr);
  
        /* Assign line numbers for this parent.
@@@ -730,7 -732,7 +732,7 @@@ static void dump_sline(struct sline *sl
        const char *c_func = diff_get_color(use_color, DIFF_FUNCINFO);
        const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW);
        const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD);
 -      const char *c_plain = diff_get_color(use_color, DIFF_PLAIN);
 +      const char *c_context = diff_get_color(use_color, DIFF_CONTEXT);
        const char *c_reset = diff_get_color(use_color, DIFF_RESET);
  
        if (result_deleted)
                        }
                        if (comment_end)
                                printf("%s%s %s%s", c_reset,
 -                                                  c_plain, c_reset,
 +                                                  c_context, c_reset,
                                                    c_func);
                        for (i = 0; i < comment_end; i++)
                                putchar(hunk_comment[i]);
                                 */
                                if (!context)
                                        continue;
 -                              fputs(c_plain, stdout);
 +                              fputs(c_context, stdout);
                        }
                        else
                                fputs(c_new, stdout);
diff --combined connect.c
+++ b/connect.c
@@@ -9,6 -9,7 +9,7 @@@
  #include "url.h"
  #include "string-list.h"
  #include "sha1-array.h"
+ #include "transport.h"
  
  static char *server_capabilities;
  static const char *parse_feature_value(const char *, const char *, int *);
@@@ -694,6 -695,8 +695,8 @@@ struct child_process *git_connect(int f
                else
                        target_host = xstrdup(hostandport);
  
+               transport_check_allowed("git");
                /* These underlying connection commands die() if they
                 * cannot connect.
                 */
                conn->in = conn->out = -1;
                if (protocol == PROTO_SSH) {
                        const char *ssh;
 -                      int putty;
 +                      int putty, tortoiseplink = 0;
                        char *ssh_host = hostandport;
                        const char *port = NULL;
+                       transport_check_allowed("ssh");
                        get_host_and_port(&ssh_host, &port);
  
                        if (!port)
  
                                free(hostandport);
                                free(path);
 +                              free(conn);
                                return NULL;
 +                      }
 +
 +                      ssh = getenv("GIT_SSH_COMMAND");
 +                      if (ssh) {
 +                              conn->use_shell = 1;
 +                              putty = 0;
                        } else {
 -                              ssh = getenv("GIT_SSH_COMMAND");
 -                              if (ssh) {
 -                                      conn->use_shell = 1;
 -                                      putty = 0;
 -                              } else {
 -                                      ssh = getenv("GIT_SSH");
 -                                      if (!ssh)
 -                                              ssh = "ssh";
 -                                      putty = !!strcasestr(ssh, "plink");
 -                              }
 -
 -                              argv_array_push(&conn->args, ssh);
 -                              if (putty && !strcasestr(ssh, "tortoiseplink"))
 -                                      argv_array_push(&conn->args, "-batch");
 -                              if (port) {
 -                                      /* P is for PuTTY, p is for OpenSSH */
 -                                      argv_array_push(&conn->args, putty ? "-P" : "-p");
 -                                      argv_array_push(&conn->args, port);
 -                              }
 -                              argv_array_push(&conn->args, ssh_host);
 +                              const char *base;
 +                              char *ssh_dup;
 +
 +                              ssh = getenv("GIT_SSH");
 +                              if (!ssh)
 +                                      ssh = "ssh";
 +
 +                              ssh_dup = xstrdup(ssh);
 +                              base = basename(ssh_dup);
 +
 +                              tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
 +                                      !strcasecmp(base, "tortoiseplink.exe");
 +                              putty = !strcasecmp(base, "plink") ||
 +                                      !strcasecmp(base, "plink.exe") || tortoiseplink;
 +
 +                              free(ssh_dup);
 +                      }
 +
 +                      argv_array_push(&conn->args, ssh);
 +                      if (tortoiseplink)
 +                              argv_array_push(&conn->args, "-batch");
 +                      if (port) {
 +                              /* P is for PuTTY, p is for OpenSSH */
 +                              argv_array_push(&conn->args, putty ? "-P" : "-p");
 +                              argv_array_push(&conn->args, port);
                        }
 +                      argv_array_push(&conn->args, ssh_host);
                } else {
                        /* remove repo-local variables from the environment */
                        conn->env = local_repo_env;
                        conn->use_shell = 1;
+                       transport_check_allowed("file");
                }
                argv_array_push(&conn->args, cmd.buf);
  
diff --combined diff.c
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -42,7 -42,7 +42,7 @@@ static long diff_algorithm
  
  static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
 -      GIT_COLOR_NORMAL,       /* PLAIN */
 +      GIT_COLOR_NORMAL,       /* CONTEXT */
        GIT_COLOR_BOLD,         /* METAINFO */
        GIT_COLOR_CYAN,         /* FRAGINFO */
        GIT_COLOR_RED,          /* OLD */
@@@ -54,8 -54,8 +54,8 @@@
  
  static int parse_diff_color_slot(const char *var)
  {
 -      if (!strcasecmp(var, "plain"))
 -              return DIFF_PLAIN;
 +      if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
 +              return DIFF_CONTEXT;
        if (!strcasecmp(var, "meta"))
                return DIFF_METAINFO;
        if (!strcasecmp(var, "frag"))
@@@ -501,7 -501,7 +501,7 @@@ static void emit_add_line(const char *r
  static void emit_hunk_header(struct emit_callback *ecbdata,
                             const char *line, int len)
  {
 -      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        if (len < 10 ||
            memcmp(line, atat, 2) ||
            !(ep = memmem(line + 2, len - 2, atat, 2))) {
 -              emit_line(ecbdata->opt, plain, reset, line, len);
 +              emit_line(ecbdata->opt, context, reset, line, len);
                return;
        }
        ep += 2; /* skip over @@ */
                if (*ep != ' ' && *ep != '\t')
                        break;
        if (ep != cp) {
 -              strbuf_addstr(&msgbuf, plain);
 +              strbuf_addstr(&msgbuf, context);
                strbuf_add(&msgbuf, cp, ep - cp);
                strbuf_addstr(&msgbuf, reset);
        }
@@@ -623,10 -623,10 +623,10 @@@ static void emit_rewrite_lines(struct e
                data += len;
        }
        if (!endp) {
 -              const char *plain = diff_get_color(ecb->color_diff,
 -                                                 DIFF_PLAIN);
 +              const char *context = diff_get_color(ecb->color_diff,
 +                                                   DIFF_CONTEXT);
                putc('\n', ecb->opt->file);
 -              emit_line_0(ecb->opt, plain, reset, '\\',
 +              emit_line_0(ecb->opt, context, reset, '\\',
                            nneof, strlen(nneof));
        }
  }
@@@ -1002,8 -1002,9 +1002,9 @@@ static void diff_words_show(struct diff
        xpp.flags = 0;
        /* as only the hunk header will be parsed, we need a 0-context */
        xecfg.ctxlen = 0;
-       xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
-                     &xpp, &xecfg);
+       if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
+                         &xpp, &xecfg))
+               die("unable to generate word diff");
        free(minus.ptr);
        free(plus.ptr);
        if (diff_words->current_plus != diff_words->plus.text.ptr +
@@@ -1086,7 -1087,7 +1087,7 @@@ static void init_diff_words_data(struc
                struct diff_words_style *st = ecbdata->diff_words->style;
                st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
                st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
 -              st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
 +              st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT);
        }
  }
  
@@@ -1162,7 -1163,7 +1163,7 @@@ static void fn_out_consume(void *priv, 
  {
        struct emit_callback *ecbdata = priv;
        const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
 -      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        struct diff_options *o = ecbdata->opt;
        const char *line_prefix = diff_line_prefix(o);
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
 -                      emit_line(ecbdata->opt, plain, reset, line, len);
 +                      emit_line(ecbdata->opt, context, reset, line, len);
                        fputs("~\n", ecbdata->opt->file);
                } else {
                        /*
                              line++;
                              len--;
                        }
 -                      emit_line(ecbdata->opt, plain, reset, line, len);
 +                      emit_line(ecbdata->opt, context, reset, line, len);
                }
                return;
        }
        if (line[0] != '+') {
                const char *color =
                        diff_get_color(ecbdata->color_diff,
 -                                     line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
 +                                     line[0] == '-' ? DIFF_FILE_OLD : DIFF_CONTEXT);
                ecbdata->lno_in_preimage++;
                if (line[0] == ' ')
                        ecbdata->lno_in_postimage++;
@@@ -2400,8 -2401,9 +2401,9 @@@ static void builtin_diff(const char *na
                        xecfg.ctxlen = strtoul(v, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
-               xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
-                             &xpp, &xecfg);
+               if (xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
+                                 &xpp, &xecfg))
+                       die("unable to generate diff for %s", one->path);
                if (o->word_diff)
                        free_diff_words_data(&ecbdata);
                if (textconv_one)
@@@ -2478,8 -2480,9 +2480,9 @@@ static void builtin_diffstat(const cha
                xpp.flags = o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
-               xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
-                             &xpp, &xecfg);
+               if (xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
+                                 &xpp, &xecfg))
+                       die("unable to generate diffstat for %s", one->path);
        }
  
        diff_free_filespec_data(one);
@@@ -2525,8 -2528,9 +2528,9 @@@ static void builtin_checkdiff(const cha
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = 0;
-               xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
-                             &xpp, &xecfg);
+               if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
+                                 &xpp, &xecfg))
+                       die("unable to generate checkdiff for %s", one->path);
  
                if (data.ws_rule & WS_BLANK_AT_EOF) {
                        struct emit_callback ecbdata;
@@@ -4425,8 -4429,10 +4429,10 @@@ static int diff_get_patch_id(struct dif
                xpp.flags = 0;
                xecfg.ctxlen = 3;
                xecfg.flags = 0;
-               xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
-                             &xpp, &xecfg);
+               if (xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
+                                 &xpp, &xecfg))
+                       return error("unable to generate patch-id diff for %s",
+                                    p->one->path);
        }
  
        git_SHA1_Final(sha1, &ctx);
diff --combined http.c
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -8,7 -8,7 +8,8 @@@
  #include "credential.h"
  #include "version.h"
  #include "pkt-line.h"
 +#include "gettext.h"
+ #include "transport.h"
  
  int active_requests;
  int http_is_verbose;
@@@ -72,8 -72,6 +73,8 @@@ static struct curl_slist *no_pragma_hea
  
  static struct active_request_slot *active_queue_head;
  
 +static char *cached_accept_language;
 +
  size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
  {
        size_t size = eltsize * nmemb;
@@@ -120,37 -118,6 +121,37 @@@ size_t fwrite_null(char *ptr, size_t el
        return eltsize * nmemb;
  }
  
 +static void closedown_active_slot(struct active_request_slot *slot)
 +{
 +      active_requests--;
 +      slot->in_use = 0;
 +}
 +
 +static void finish_active_slot(struct active_request_slot *slot)
 +{
 +      closedown_active_slot(slot);
 +      curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
 +
 +      if (slot->finished != NULL)
 +              (*slot->finished) = 1;
 +
 +      /* Store slot results so they can be read after the slot is reused */
 +      if (slot->results != NULL) {
 +              slot->results->curl_result = slot->curl_result;
 +              slot->results->http_code = slot->http_code;
 +#if LIBCURL_VERSION_NUM >= 0x070a08
 +              curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,
 +                                &slot->results->auth_avail);
 +#else
 +              slot->results->auth_avail = 0;
 +#endif
 +      }
 +
 +      /* Run callback if appropriate */
 +      if (slot->callback_func != NULL)
 +              slot->callback_func(slot->callback_data);
 +}
 +
  #ifdef USE_CURL_MULTI
  static void process_curl_messages(void)
  {
@@@ -337,6 -304,7 +338,7 @@@ static void set_curl_keepalive(CURL *c
  static CURL *get_curl_handle(void)
  {
        CURL *result = curl_easy_init();
+       long allowed_protocols = 0;
  
        if (!result)
                die("curl_easy_init failed");
        }
  
        curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
+       curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
  #if LIBCURL_VERSION_NUM >= 0x071301
        curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
  #elif LIBCURL_VERSION_NUM >= 0x071101
        curl_easy_setopt(result, CURLOPT_POST301, 1);
  #endif
+ #if LIBCURL_VERSION_NUM >= 0x071304
+       if (is_transport_allowed("http"))
+               allowed_protocols |= CURLPROTO_HTTP;
+       if (is_transport_allowed("https"))
+               allowed_protocols |= CURLPROTO_HTTPS;
+       if (is_transport_allowed("ftp"))
+               allowed_protocols |= CURLPROTO_FTP;
+       if (is_transport_allowed("ftps"))
+               allowed_protocols |= CURLPROTO_FTPS;
+       curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
+ #else
+       if (transport_restrict_protocols())
+               warning("protocol restrictions not applied to curl redirects because\n"
+                       "your curl version is too old (>= 7.19.4)");
+ #endif
  
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
  
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
 -              curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
        }
 +#if LIBCURL_VERSION_NUM >= 0x070a07
 +      curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 +#endif
  
        set_curl_keepalive(result);
  
@@@ -554,9 -536,6 +572,9 @@@ void http_cleanup(void
                cert_auth.password = NULL;
        }
        ssl_cert_password_required = 0;
 +
 +      free(cached_accept_language);
 +      cached_accept_language = NULL;
  }
  
  struct active_request_slot *get_active_slot(void)
@@@ -775,6 -754,12 +793,6 @@@ void run_active_slot(struct active_requ
  #endif
  }
  
 -static void closedown_active_slot(struct active_request_slot *slot)
 -{
 -      active_requests--;
 -      slot->in_use = 0;
 -}
 -
  static void release_active_slot(struct active_request_slot *slot)
  {
        closedown_active_slot(slot);
  #endif
  }
  
 -void finish_active_slot(struct active_request_slot *slot)
 -{
 -      closedown_active_slot(slot);
 -      curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
 -
 -      if (slot->finished != NULL)
 -              (*slot->finished) = 1;
 -
 -      /* Store slot results so they can be read after the slot is reused */
 -      if (slot->results != NULL) {
 -              slot->results->curl_result = slot->curl_result;
 -              slot->results->http_code = slot->http_code;
 -#if LIBCURL_VERSION_NUM >= 0x070a08
 -              curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,
 -                                &slot->results->auth_avail);
 -#else
 -              slot->results->auth_avail = 0;
 -#endif
 -      }
 -
 -      /* Run callback if appropriate */
 -      if (slot->callback_func != NULL)
 -              slot->callback_func(slot->callback_data);
 -}
 -
  void finish_all_active_slots(void)
  {
        struct active_request_slot *slot = active_queue_head;
@@@ -853,7 -863,7 +871,7 @@@ char *get_remote_object_url(const char 
        return strbuf_detach(&buf, NULL);
  }
  
 -int handle_curl_result(struct slot_results *results)
 +static int handle_curl_result(struct slot_results *results)
  {
        /*
         * If we see a failing http code with CURLE_OK, we have turned off
@@@ -1003,116 -1013,6 +1021,116 @@@ static void extract_content_type(struc
                strbuf_addstr(charset, "ISO-8859-1");
  }
  
 +static void write_accept_language(struct strbuf *buf)
 +{
 +      /*
 +       * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
 +       * that, q-value will be smaller than 0.001, the minimum q-value the
 +       * HTTP specification allows. See
 +       * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
 +       */
 +      const int MAX_DECIMAL_PLACES = 3;
 +      const int MAX_LANGUAGE_TAGS = 1000;
 +      const int MAX_ACCEPT_LANGUAGE_HEADER_SIZE = 4000;
 +      char **language_tags = NULL;
 +      int num_langs = 0;
 +      const char *s = get_preferred_languages();
 +      int i;
 +      struct strbuf tag = STRBUF_INIT;
 +
 +      /* Don't add Accept-Language header if no language is preferred. */
 +      if (!s)
 +              return;
 +
 +      /*
 +       * Split the colon-separated string of preferred languages into
 +       * language_tags array.
 +       */
 +      do {
 +              /* collect language tag */
 +              for (; *s && (isalnum(*s) || *s == '_'); s++)
 +                      strbuf_addch(&tag, *s == '_' ? '-' : *s);
 +
 +              /* skip .codeset, @modifier and any other unnecessary parts */
 +              while (*s && *s != ':')
 +                      s++;
 +
 +              if (tag.len) {
 +                      num_langs++;
 +                      REALLOC_ARRAY(language_tags, num_langs);
 +                      language_tags[num_langs - 1] = strbuf_detach(&tag, NULL);
 +                      if (num_langs >= MAX_LANGUAGE_TAGS - 1) /* -1 for '*' */
 +                              break;
 +              }
 +      } while (*s++);
 +
 +      /* write Accept-Language header into buf */
 +      if (num_langs) {
 +              int last_buf_len = 0;
 +              int max_q;
 +              int decimal_places;
 +              char q_format[32];
 +
 +              /* add '*' */
 +              REALLOC_ARRAY(language_tags, num_langs + 1);
 +              language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
 +
 +              /* compute decimal_places */
 +              for (max_q = 1, decimal_places = 0;
 +                   max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES;
 +                   decimal_places++, max_q *= 10)
 +                      ;
 +
 +              sprintf(q_format, ";q=0.%%0%dd", decimal_places);
 +
 +              strbuf_addstr(buf, "Accept-Language: ");
 +
 +              for (i = 0; i < num_langs; i++) {
 +                      if (i > 0)
 +                              strbuf_addstr(buf, ", ");
 +
 +                      strbuf_addstr(buf, language_tags[i]);
 +
 +                      if (i > 0)
 +                              strbuf_addf(buf, q_format, max_q - i);
 +
 +                      if (buf->len > MAX_ACCEPT_LANGUAGE_HEADER_SIZE) {
 +                              strbuf_remove(buf, last_buf_len, buf->len - last_buf_len);
 +                              break;
 +                      }
 +
 +                      last_buf_len = buf->len;
 +              }
 +      }
 +
 +      /* free language tags -- last one is a static '*' */
 +      for (i = 0; i < num_langs - 1; i++)
 +              free(language_tags[i]);
 +      free(language_tags);
 +}
 +
 +/*
 + * Get an Accept-Language header which indicates user's preferred languages.
 + *
 + * Examples:
 + *   LANGUAGE= -> ""
 + *   LANGUAGE=ko:en -> "Accept-Language: ko, en; q=0.9, *; q=0.1"
 + *   LANGUAGE=ko_KR.UTF-8:sr@latin -> "Accept-Language: ko-KR, sr; q=0.9, *; q=0.1"
 + *   LANGUAGE=ko LANG=en_US.UTF-8 -> "Accept-Language: ko, *; q=0.1"
 + *   LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
 + *   LANGUAGE= LANG=C -> ""
 + */
 +static const char *get_accept_language(void)
 +{
 +      if (!cached_accept_language) {
 +              struct strbuf buf = STRBUF_INIT;
 +              write_accept_language(&buf);
 +              if (buf.len > 0)
 +                      cached_accept_language = strbuf_detach(&buf, NULL);
 +      }
 +
 +      return cached_accept_language;
 +}
  
  /* http_request() targets */
  #define HTTP_REQUEST_STRBUF   0
@@@ -1126,7 -1026,6 +1144,7 @@@ static int http_request(const char *url
        struct slot_results results;
        struct curl_slist *headers = NULL;
        struct strbuf buf = STRBUF_INIT;
 +      const char *accept_language;
        int ret;
  
        slot = get_active_slot();
                                         fwrite_buffer);
        }
  
 +      accept_language = get_accept_language();
 +
 +      if (accept_language)
 +              headers = curl_slist_append(headers, accept_language);
 +
        strbuf_addstr(&buf, "Pragma:");
        if (options && options->no_cache)
                strbuf_addstr(&buf, " no-cache");
@@@ -1462,7 -1356,6 +1480,7 @@@ void release_http_pack_request(struct h
        }
        preq->slot = NULL;
        free(preq->url);
 +      free(preq);
  }
  
  int finish_http_pack_request(struct http_pack_request *preq)
diff --combined line-log.c
@@@ -237,7 -237,7 +237,7 @@@ static void diff_ranges_release(struct 
        range_set_release(&diff->target);
  }
  
 -void line_log_data_init(struct line_log_data *r)
 +static void line_log_data_init(struct line_log_data *r)
  {
        memset(r, 0, sizeof(struct line_log_data));
        range_set_init(&r->ranges, 0);
@@@ -325,7 -325,7 +325,7 @@@ static int collect_diff_cb(long start_a
        return 0;
  }
  
- static void collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
+ static int collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
  {
        struct collect_diff_cbdata cbdata = {NULL};
        xpparam_t xpp;
        xecfg.hunk_func = collect_diff_cb;
        memset(&ecb, 0, sizeof(ecb));
        ecb.priv = &cbdata;
-       xdi_diff(parent, target, &xpp, &xecfg, &ecb);
+       return xdi_diff(parent, target, &xpp, &xecfg, &ecb);
  }
  
  /*
@@@ -893,7 -893,7 +893,7 @@@ static void dump_diff_hacky_one(struct 
        const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
        const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
        const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
 -      const char *c_plain = diff_get_color(opt->use_color, DIFF_PLAIN);
 +      const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
  
        if (!pair || !diff)
                return;
                        int k;
                        for (; t_cur < diff->target.ranges[j].start; t_cur++)
                                print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
 -                                         c_plain, c_reset);
 +                                         c_context, c_reset);
                        for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
                                print_line(prefix, '-', k, p_ends, pair->one->data,
                                           c_old, c_reset);
                }
                for (; t_cur < t_end; t_cur++)
                        print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
 -                                 c_plain, c_reset);
 +                                 c_context, c_reset);
        }
  
        free(p_ends);
@@@ -1030,7 -1030,8 +1030,8 @@@ static int process_diff_filepair(struc
        }
  
        diff_ranges_init(&diff);
-       collect_diff(&file_parent, &file_target, &diff);
+       if (collect_diff(&file_parent, &file_target, &diff))
+               die("unable to generate diff for %s", pair->one->path);
  
        /* NEEDSWORK should apply some heuristics to prevent mismatches */
        free(rg->path);
@@@ -1099,7 -1100,6 +1100,7 @@@ static int process_all_files(struct lin
                        rg->pair = diff_filepair_dup(queue->queue[i]);
                        memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges));
                }
 +              free(pairdiff);
        }
  
        return changed;
diff --combined transport-helper.c
@@@ -97,8 -97,6 +97,8 @@@ static void do_take_over(struct transpo
        free(data);
  }
  
 +static void standard_options(struct transport *t);
 +
  static struct child_process *get_helper(struct transport *transport)
  {
        struct helper_data *data = transport->data;
        strbuf_release(&buf);
        if (debug)
                fprintf(stderr, "Debug: Capabilities complete.\n");
 +      standard_options(transport);
        return data->helper;
  }
  
@@@ -341,6 -338,17 +341,6 @@@ static int fetch_with_fetch(struct tran
        int i;
        struct strbuf buf = STRBUF_INIT;
  
 -      standard_options(transport);
 -      if (data->check_connectivity &&
 -          data->transport_options.check_self_contained_and_connected)
 -              set_helper_option(transport, "check-connectivity", "true");
 -
 -      if (transport->cloning)
 -              set_helper_option(transport, "cloning", "true");
 -
 -      if (data->transport_options.update_shallow)
 -              set_helper_option(transport, "update-shallow", "true");
 -
        for (i = 0; i < nr_heads; i++) {
                const struct ref *posn = to_fetch[i];
                if (posn->status & REF_STATUS_UPTODATE)
@@@ -614,16 -622,6 +614,16 @@@ static int fetch(struct transport *tran
        if (!count)
                return 0;
  
 +      if (data->check_connectivity &&
 +          data->transport_options.check_self_contained_and_connected)
 +              set_helper_option(transport, "check-connectivity", "true");
 +
 +      if (transport->cloning)
 +              set_helper_option(transport, "cloning", "true");
 +
 +      if (data->transport_options.update_shallow)
 +              set_helper_option(transport, "update-shallow", "true");
 +
        if (data->fetch)
                return fetch_with_fetch(transport, nr_heads, to_fetch);
  
@@@ -828,6 -826,7 +828,6 @@@ static int push_refs_with_push(struct t
                return 0;
        }
  
 -      standard_options(transport);
        for_each_string_list_item(cas_option, &cas_options)
                set_helper_option(transport, "cas", cas_option->string);
  
@@@ -1039,6 -1038,8 +1039,8 @@@ int transport_helper_init(struct transp
        struct helper_data *data = xcalloc(1, sizeof(*data));
        data->name = name;
  
+       transport_check_allowed(name);
        if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
                debug = 1;
  
diff --combined transport.c
@@@ -730,10 -730,6 +730,10 @@@ static int print_one_push_status(struc
                                                 ref->deletion ? NULL : ref->peer_ref,
                                                 "remote failed to report status", porcelain);
                break;
 +      case REF_STATUS_ATOMIC_PUSH_FAILED:
 +              print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 +                                               "atomic push failed", porcelain);
 +              break;
        case REF_STATUS_OK:
                print_ok_ref_status(ref, porcelain);
                break;
@@@ -832,7 -828,6 +832,7 @@@ static int git_transport_push(struct tr
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
        args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
        args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
 +      args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
        args.url = transport->url;
  
        ret = send_pack(&args, data->fd, data->conn, remote_refs,
@@@ -914,6 -909,42 +914,42 @@@ static int external_specification_len(c
        return strchr(url, ':') - url;
  }
  
+ static const struct string_list *protocol_whitelist(void)
+ {
+       static int enabled = -1;
+       static struct string_list allowed = STRING_LIST_INIT_DUP;
+       if (enabled < 0) {
+               const char *v = getenv("GIT_ALLOW_PROTOCOL");
+               if (v) {
+                       string_list_split(&allowed, v, ':', -1);
+                       string_list_sort(&allowed);
+                       enabled = 1;
+               } else {
+                       enabled = 0;
+               }
+       }
+       return enabled ? &allowed : NULL;
+ }
+ int is_transport_allowed(const char *type)
+ {
+       const struct string_list *allowed = protocol_whitelist();
+       return !allowed || string_list_has_string(allowed, type);
+ }
+ void transport_check_allowed(const char *type)
+ {
+       if (!is_transport_allowed(type))
+               die("transport '%s' not allowed", type);
+ }
+ int transport_restrict_protocols(void)
+ {
+       return !!protocol_whitelist();
+ }
  struct transport *transport_get(struct remote *remote, const char *url)
  {
        const char *helper;
        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;
        } 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");
                ret->data = data;
                ret->get_refs_list = get_refs_from_bundle;
                ret->fetch = fetch_refs_from_bundle;
                || starts_with(url, "ssh://")
                || starts_with(url, "git+ssh://")
                || starts_with(url, "ssh+git://")) {
-               /* These are builtin smart transports. */
+               /*
+                * These are builtin smart transports; "allowed" transports
+                * will be checked individually in git_connect.
+                */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->set_option = NULL;
diff --combined transport.h
@@@ -125,7 -125,6 +125,7 @@@ struct transport 
  #define TRANSPORT_PUSH_NO_HOOK 512
  #define TRANSPORT_PUSH_FOLLOW_TAGS 1024
  #define TRANSPORT_PUSH_CERT 2048
 +#define TRANSPORT_PUSH_ATOMIC 4096
  
  #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
  #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
  /* Returns a transport suitable for the url */
  struct transport *transport_get(struct remote *, const char *);
  
+ /*
+  * Check whether a transport is allowed by the environment. Type should
+  * generally be the URL scheme, as described in Documentation/git.txt
+  */
+ int is_transport_allowed(const char *type);
+ /*
+  * Check whether a transport is allowed by the environment,
+  * and die otherwise.
+  */
+ void transport_check_allowed(const char *type);
+ /*
+  * Returns true if the user has attempted to turn on protocol
+  * restrictions at all.
+  */
+ int transport_restrict_protocols(void);
  /* Transport options which apply to git:// and scp-style URLs */
  
  /* The program to use on the remote side to send a pack */