Sync with 2.4.10
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Sep 2015 22:33:56 +0000 (15:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Sep 2015 22:33:56 +0000 (15:33 -0700)
1  2 
Documentation/git.txt
builtin/blame.c
combine-diff.c
diff.c
git-submodule.sh
http.c
ll-merge.c
transport-helper.c
transport.c
diff --combined Documentation/git.txt
index 2b39aa9f5153846884e4a280102e01f4aad357d1,a405e4f2e7094298873bcbc0578d41c407a5ff9c..b2d88688aa0fe02d08130aa0f4aac802c69787f0
@@@ -43,17 -43,10 +43,18 @@@ unreleased) version of Git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
- * link:v2.4.9/git.html[documentation for release 2.4.9]
 +* link:v2.5.3/git.html[documentation for release 2.5.3]
 +
 +* release notes for
 +  link:RelNotes/2.5.3.txt[2.5.3],
 +  link:RelNotes/2.5.2.txt[2.5.2],
 +  link:RelNotes/2.5.1.txt[2.5.1],
 +  link:RelNotes/2.5.0.txt[2.5].
 +
+ * link:v2.4.10/git.html[documentation for release 2.4.10]
  
  * release notes for
+   link:RelNotes/2.4.10.txt[2.4.10],
    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.1.txt[2.4.1],
    link:RelNotes/2.4.0.txt[2.4].
  
- * link:v2.3.9/git.html[documentation for release 2.3.9]
+ * 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],
@@@ -787,7 -781,7 +789,7 @@@ The Git Repositor
  ~~~~~~~~~~~~~~~~~~
  These environment variables apply to 'all' core Git commands. Nb: it
  is worth noting that they may be used/overridden by SCMS sitting above
 -Git so take care if using Cogito etc.
 +Git so take care if using a foreign front-end.
  
  'GIT_INDEX_FILE'::
        This environment allows the specification of an alternate
        an explicit repository directory set via 'GIT_DIR' or on the
        command line.
  
 +'GIT_COMMON_DIR'::
 +      If this variable is set to a path, non-worktree files that are
 +      normally in $GIT_DIR will be taken from this path
 +      instead. Worktree-specific files such as HEAD or index are
 +      taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and
 +      linkgit:git-worktree[1] for
 +      details. This variable has lower precedence than other path
 +      variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
 +
  Git Commits
  ~~~~~~~~~~~
  'GIT_AUTHOR_NAME'::
@@@ -1076,6 -1061,33 +1078,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
index a22ac174078742cc8991c0ee563980d239731e9b,048ed53c2f70961898d3d2a213e05315ae1ac92d..191368bfab7508862cf28c37420a353f1ad8dee3
@@@ -973,7 -973,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 -1122,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);
  }
@@@ -2176,14 -2181,6 +2181,14 @@@ static int git_blame_config(const char 
                blank_boundary = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "blame.showemail")) {
 +              int *output_option = cb;
 +              if (git_config_bool(var, value))
 +                      *output_option |= OUTPUT_SHOW_EMAIL;
 +              else
 +                      *output_option &= ~OUTPUT_SHOW_EMAIL;
 +              return 0;
 +      }
        if (!strcmp(var, "blame.date")) {
                if (!value)
                        return config_error_nonbool(var);
@@@ -2528,7 -2525,7 +2533,7 @@@ int cmd_blame(int argc, const char **ar
        unsigned int range_i;
        long anchor;
  
 -      git_config(git_blame_config, NULL);
 +      git_config(git_blame_config, &output_option);
        init_revisions(&revs, NULL);
        revs.date_mode = blame_date_mode;
        DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
diff --combined combine-diff.c
index 30c7eb6d3cddbdf17d6ca8ff3bc85267041d1669,5cae5fbd62180e08709d404cbf357adceb1d4d06..0f62f54b5ef4bb4ec81cff5aead2d6cd66da194e
@@@ -44,9 -44,9 +44,9 @@@ static struct combine_diff_path *inters
                        memset(p->parent, 0,
                               sizeof(p->parent[0]) * num_parent);
  
 -                      hashcpy(p->sha1, q->queue[i]->two->sha1);
 +                      hashcpy(p->oid.hash, q->queue[i]->two->sha1);
                        p->mode = q->queue[i]->two->mode;
 -                      hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
 +                      hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1);
                        p->parent[n].mode = q->queue[i]->one->mode;
                        p->parent[n].status = q->queue[i]->status;
                        *tail = p;
@@@ -77,7 -77,7 +77,7 @@@
                        continue;
                }
  
 -              hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
 +              hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1);
                p->parent[n].mode = q->queue[i]->one->mode;
                p->parent[n].status = q->queue[i]->status;
  
@@@ -284,7 -284,7 +284,7 @@@ static struct lline *coalesce_lines(str
        return base;
  }
  
 -static char *grab_blob(const unsigned char *sha1, unsigned int mode,
 +static char *grab_blob(const struct object_id *oid, unsigned int mode,
                       unsigned long *size, struct userdiff_driver *textconv,
                       const char *path)
  {
        if (S_ISGITLINK(mode)) {
                blob = xmalloc(100);
                *size = snprintf(blob, 100,
 -                               "Subproject commit %s\n", sha1_to_hex(sha1));
 -      } else if (is_null_sha1(sha1)) {
 +                               "Subproject commit %s\n", oid_to_hex(oid));
 +      } else if (is_null_oid(oid)) {
                /* deleted blob */
                *size = 0;
                return xcalloc(1, 1);
        } else if (textconv) {
                struct diff_filespec *df = alloc_filespec(path);
 -              fill_filespec(df, sha1, 1, mode);
 +              fill_filespec(df, oid->hash, 1, mode);
                *size = fill_textconv(textconv, df, &blob);
                free_filespec(df);
        } else {
 -              blob = read_sha1_file(sha1, &type, size);
 +              blob = read_sha1_file(oid->hash, &type, size);
                if (type != OBJ_BLOB)
 -                      die("object '%s' is not a blob!", sha1_to_hex(sha1));
 +                      die("object '%s' is not a blob!", oid_to_hex(oid));
        }
        return blob;
  }
@@@ -389,7 -389,7 +389,7 @@@ static void consume_line(void *state_, 
        }
  }
  
 -static void combine_diff(const unsigned char *parent, unsigned int mode,
 +static void combine_diff(const struct object_id *parent, unsigned int mode,
                         mmfile_t *result_file,
                         struct sline *sline, unsigned int cnt, int n,
                         int num_parent, int result_deleted,
        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));
++                  oid_to_hex(parent));
        free(parent_file.ptr);
  
        /* Assign line numbers for this parent.
@@@ -897,7 -899,7 +899,7 @@@ static void show_combined_header(struc
                                 int show_file_header)
  {
        struct diff_options *opt = &rev->diffopt;
 -      int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
 +      int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
        const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
        const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
        const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
                         "", elem->path, line_prefix, c_meta, c_reset);
        printf("%s%sindex ", line_prefix, c_meta);
        for (i = 0; i < num_parent; i++) {
 -              abb = find_unique_abbrev(elem->parent[i].sha1,
 +              abb = find_unique_abbrev(elem->parent[i].oid.hash,
                                         abbrev);
                printf("%s%s", i ? "," : "", abb);
        }
 -      abb = find_unique_abbrev(elem->sha1, abbrev);
 +      abb = find_unique_abbrev(elem->oid.hash, abbrev);
        printf("..%s%s\n", abb, c_reset);
  
        if (mode_differs) {
@@@ -991,7 -993,7 +993,7 @@@ static void show_patch_diff(struct comb
  
        /* Read the result of merge first */
        if (!working_tree_file)
 -              result = grab_blob(elem->sha1, elem->mode, &result_size,
 +              result = grab_blob(&elem->oid, elem->mode, &result_size,
                                   textconv, elem->path);
        else {
                /* Used by diff-tree to read from the working tree */
                        result = strbuf_detach(&buf, NULL);
                        elem->mode = canon_mode(st.st_mode);
                } else if (S_ISDIR(st.st_mode)) {
 -                      unsigned char sha1[20];
 -                      if (resolve_gitlink_ref(elem->path, "HEAD", sha1) < 0)
 -                              result = grab_blob(elem->sha1, elem->mode,
 +                      struct object_id oid;
 +                      if (resolve_gitlink_ref(elem->path, "HEAD", oid.hash) < 0)
 +                              result = grab_blob(&elem->oid, elem->mode,
                                                   &result_size, NULL, NULL);
                        else
 -                              result = grab_blob(sha1, elem->mode,
 +                              result = grab_blob(&oid, elem->mode,
                                                   &result_size, NULL, NULL);
                } else if (textconv) {
                        struct diff_filespec *df = alloc_filespec(elem->path);
                for (i = 0; !is_binary && i < num_parent; i++) {
                        char *buf;
                        unsigned long size;
 -                      buf = grab_blob(elem->parent[i].sha1,
 +                      buf = grab_blob(&elem->parent[i].oid,
                                        elem->parent[i].mode,
                                        &size, NULL, NULL);
                        if (buffer_is_binary(buf, size))
        for (i = 0; i < num_parent; i++) {
                int j;
                for (j = 0; j < i; j++) {
 -                      if (!hashcmp(elem->parent[i].sha1,
 -                                   elem->parent[j].sha1)) {
 +                      if (!oidcmp(&elem->parent[i].oid,
 +                                   &elem->parent[j].oid)) {
                                reuse_combine_diff(sline, cnt, i, j);
                                break;
                        }
                }
                if (i <= j)
 -                      combine_diff(elem->parent[i].sha1,
 +                      combine_diff(&elem->parent[i].oid,
                                     elem->parent[i].mode,
                                     &result_file, sline,
                                     cnt, i, num_parent, result_deleted,
@@@ -1206,9 -1208,9 +1208,9 @@@ static void show_raw_diff(struct combin
  
                /* Show sha1's */
                for (i = 0; i < num_parent; i++)
 -                      printf(" %s", diff_unique_abbrev(p->parent[i].sha1,
 +                      printf(" %s", diff_unique_abbrev(p->parent[i].oid.hash,
                                                         opt->abbrev));
 -              printf(" %s ", diff_unique_abbrev(p->sha1, opt->abbrev));
 +              printf(" %s ", diff_unique_abbrev(p->oid.hash, opt->abbrev));
        }
  
        if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) {
@@@ -1271,16 -1273,16 +1273,16 @@@ static struct diff_filepair *combined_p
        for (i = 0; i < num_parent; i++) {
                pair->one[i].path = p->path;
                pair->one[i].mode = p->parent[i].mode;
 -              hashcpy(pair->one[i].sha1, p->parent[i].sha1);
 -              pair->one[i].sha1_valid = !is_null_sha1(p->parent[i].sha1);
 +              hashcpy(pair->one[i].sha1, p->parent[i].oid.hash);
 +              pair->one[i].sha1_valid = !is_null_oid(&p->parent[i].oid);
                pair->one[i].has_more_entries = 1;
        }
        pair->one[num_parent - 1].has_more_entries = 0;
  
        pair->two->path = p->path;
        pair->two->mode = p->mode;
 -      hashcpy(pair->two->sha1, p->sha1);
 -      pair->two->sha1_valid = !is_null_sha1(p->sha1);
 +      hashcpy(pair->two->sha1, p->oid.hash);
 +      pair->two->sha1_valid = !is_null_oid(&p->oid);
        return pair;
  }
  
diff --combined diff.c
index 0f17ec5506e616b0e1383ad689226c774e0df178,f62b7f73d8ad52205b0add37a21fd69911783b25..71f0274ce0df2e6af8cd144c78c64dbb694c1ab7
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -478,59 -478,26 +478,59 @@@ static int new_blank_line_at_eof(struc
        return ws_blank_line(line, len, ecbdata->ws_rule);
  }
  
 -static void emit_add_line(const char *reset,
 -                        struct emit_callback *ecbdata,
 -                        const char *line, int len)
 +static void emit_line_checked(const char *reset,
 +                            struct emit_callback *ecbdata,
 +                            const char *line, int len,
 +                            enum color_diff color,
 +                            unsigned ws_error_highlight,
 +                            char sign)
  {
 -      const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 -      const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 +      const char *set = diff_get_color(ecbdata->color_diff, color);
 +      const char *ws = NULL;
  
 -      if (!*ws)
 -              emit_line_0(ecbdata->opt, set, reset, '+', line, len);
 -      else if (new_blank_line_at_eof(ecbdata, line, len))
 +      if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
 +              ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 +              if (!*ws)
 +                      ws = NULL;
 +      }
 +
 +      if (!ws)
 +              emit_line_0(ecbdata->opt, set, reset, sign, line, len);
 +      else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
                /* Blank line at EOF - paint '+' as well */
 -              emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
 +              emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
        else {
                /* Emit just the prefix, then the rest. */
 -              emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
 +              emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
                ws_check_emit(line, len, ecbdata->ws_rule,
                              ecbdata->opt->file, set, reset, ws);
        }
  }
  
 +static void emit_add_line(const char *reset,
 +                        struct emit_callback *ecbdata,
 +                        const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_FILE_NEW, WSEH_NEW, '+');
 +}
 +
 +static void emit_del_line(const char *reset,
 +                        struct emit_callback *ecbdata,
 +                        const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_FILE_OLD, WSEH_OLD, '-');
 +}
 +
 +static void emit_context_line(const char *reset,
 +                            struct emit_callback *ecbdata,
 +                            const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_CONTEXT, WSEH_CONTEXT, ' ');
 +}
 +
  static void emit_hunk_header(struct emit_callback *ecbdata,
                             const char *line, int len)
  {
@@@ -636,6 -603,7 +636,6 @@@ static void emit_rewrite_lines(struct e
  {
        const char *endp = NULL;
        static const char *nneof = " No newline at end of file\n";
 -      const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
        const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
  
        while (0 < size) {
                len = endp ? (endp - data + 1) : size;
                if (prefix != '+') {
                        ecb->lno_in_preimage++;
 -                      emit_line_0(ecb->opt, old, reset, '-',
 -                                  data, len);
 +                      emit_del_line(reset, ecb, data, len);
                } else {
                        ecb->lno_in_postimage++;
                        emit_add_line(reset, ecb, data, len);
@@@ -1033,8 -1002,9 +1033,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 +
@@@ -1281,27 -1251,17 +1282,27 @@@ static void fn_out_consume(void *priv, 
                return;
        }
  
 -      if (line[0] != '+') {
 -              const char *color =
 -                      diff_get_color(ecbdata->color_diff,
 -                                     line[0] == '-' ? DIFF_FILE_OLD : DIFF_CONTEXT);
 -              ecbdata->lno_in_preimage++;
 -              if (line[0] == ' ')
 -                      ecbdata->lno_in_postimage++;
 -              emit_line(ecbdata->opt, color, reset, line, len);
 -      } else {
 +      switch (line[0]) {
 +      case '+':
                ecbdata->lno_in_postimage++;
                emit_add_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      case '-':
 +              ecbdata->lno_in_preimage++;
 +              emit_del_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      case ' ':
 +              ecbdata->lno_in_postimage++;
 +              ecbdata->lno_in_preimage++;
 +              emit_context_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      default:
 +              /* incomplete line at the end */
 +              ecbdata->lno_in_preimage++;
 +              emit_line(ecbdata->opt,
 +                        diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
 +                        reset, line, len);
 +              break;
        }
  }
  
@@@ -2441,8 -2401,9 +2442,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)
@@@ -2519,8 -2480,9 +2521,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);
@@@ -2566,8 -2528,9 +2569,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;
@@@ -3264,7 -3227,6 +3268,7 @@@ void diff_setup(struct diff_options *op
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = diff_context_default;
 +      options->ws_error_highlight = WSEH_NEW;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
        /* pathchange left =NULL by default */
@@@ -3651,45 -3613,6 +3655,45 @@@ static void enable_patch_output(int *fm
        *fmt |= DIFF_FORMAT_PATCH;
  }
  
 +static int parse_one_token(const char **arg, const char *token)
 +{
 +      const char *rest;
 +      if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
 +              *arg = rest;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
 +{
 +      const char *orig_arg = arg;
 +      unsigned val = 0;
 +      while (*arg) {
 +              if (parse_one_token(&arg, "none"))
 +                      val = 0;
 +              else if (parse_one_token(&arg, "default"))
 +                      val = WSEH_NEW;
 +              else if (parse_one_token(&arg, "all"))
 +                      val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
 +              else if (parse_one_token(&arg, "new"))
 +                      val |= WSEH_NEW;
 +              else if (parse_one_token(&arg, "old"))
 +                      val |= WSEH_OLD;
 +              else if (parse_one_token(&arg, "context"))
 +                      val |= WSEH_CONTEXT;
 +              else {
 +                      error("unknown value after ws-error-highlight=%.*s",
 +                            (int)(arg - orig_arg), orig_arg);
 +                      return 0;
 +              }
 +              if (*arg)
 +                      arg++;
 +      }
 +      opt->ws_error_highlight = val;
 +      return 1;
 +}
 +
  int diff_opt_parse(struct diff_options *options, const char **av, int ac)
  {
        const char *arg = av[0];
                DIFF_OPT_SET(options, SUBMODULE_LOG);
        else if (skip_prefix(arg, "--submodule=", &arg))
                return parse_submodule_opt(options, arg);
 +      else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
 +              return parse_ws_error_highlight(options, arg);
  
        /* misc options */
        else if (!strcmp(arg, "-z"))
@@@ -4508,8 -4429,10 +4512,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 git-submodule.sh
index 25b1ddf2520800af4f78f1b7b72be2e271dfd8e2,78c2740fdb2beb48fd98c656f8ab13955cfdad56..82e35582cd8e741280229f377b4f61d7d985bc6a
@@@ -22,6 -22,15 +22,15 @@@ require_work_tre
  wt_prefix=$(git rev-parse --show-prefix)
  cd_to_toplevel
  
+ # Restrict ourselves to a vanilla subset of protocols; the URLs
+ # we get are under control of a remote repository, and we do not
+ # want them kicking off arbitrary git-remote-* programs.
+ #
+ # If the user has already specified a set of allowed protocols,
+ # we assume they know what they're doing and use that instead.
+ : ${GIT_ALLOW_PROTOCOL=file:git:http:https:ssh}
+ export GIT_ALLOW_PROTOCOL
  command=
  branch=
  force=
@@@ -904,7 -913,7 +913,7 @@@ Maybe you want to use 'update --init'?"
                                ;;
                        !*)
                                command="${update_module#!}"
 -                              die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule  path '\$prefix\$sm_path'")"
 +                              die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule path '\$prefix\$sm_path'")"
                                say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': '\$command \$sha1'")"
                                must_die_on_failure=yes
                                ;;
diff --combined http.c
index e9c6fdd835ea4bd4fe24ad374fbef69d6e9a8ba2,9448c50f0f9ae159aa73feae6d980335265c168f..45348fb9bd7d2713bed668f6ef8ffb43783601ff
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -9,6 -9,7 +9,7 @@@
  #include "version.h"
  #include "pkt-line.h"
  #include "gettext.h"
+ #include "transport.h"
  
  int active_requests;
  int http_is_verbose;
@@@ -36,7 -37,6 +37,7 @@@ char curl_errorstr[CURL_ERROR_SIZE]
  static int curl_ssl_verify = -1;
  static int curl_ssl_try;
  static const char *ssl_cert;
 +static const char *ssl_cipherlist;
  #if LIBCURL_VERSION_NUM >= 0x070903
  static const char *ssl_key;
  #endif
@@@ -188,8 -188,6 +189,8 @@@ static int http_options(const char *var
                curl_ssl_verify = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp("http.sslcipherlist", var))
 +              return git_config_string(&ssl_cipherlist, var, value);
        if (!strcmp("http.sslcert", var))
                return git_config_string(&ssl_cert, var, value);
  #if LIBCURL_VERSION_NUM >= 0x070903
@@@ -340,6 -338,7 +341,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");
        if (http_proactive_auth)
                init_curl_http_auth(result);
  
 +      if (getenv("GIT_SSL_CIPHER_LIST"))
 +              ssl_cipherlist = getenv("GIT_SSL_CIPHER_LIST");
 +
 +      if (ssl_cipherlist != NULL && *ssl_cipherlist)
 +              curl_easy_setopt(result, CURLOPT_SSL_CIPHER_LIST,
 +                              ssl_cipherlist);
 +
        if (ssl_cert != NULL)
                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
        if (has_cert_password())
        }
  
        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);
diff --combined ll-merge.c
index fc3c0495942e2a8d7c31a087429f894633c603d8,4e789f533043c78916b4281ac8113f1e75d342b4..bf83290793059dc42d89db07a12e3bc3c14a48dc
@@@ -9,7 -9,6 +9,7 @@@
  #include "xdiff-interface.h"
  #include "run-command.h"
  #include "ll-merge.h"
 +#include "quote.h"
  
  struct ll_merge_driver;
  
@@@ -89,7 -88,10 +89,10 @@@ static int ll_xdl_merge(const struct ll
        xmparam_t xmp;
        assert(opts);
  
-       if (buffer_is_binary(orig->ptr, orig->size) ||
+       if (orig->size > MAX_XDIFF_SIZE ||
+           src1->size > MAX_XDIFF_SIZE ||
+           src2->size > MAX_XDIFF_SIZE ||
+           buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
            buffer_is_binary(src2->ptr, src2->size)) {
                return ll_binary_merge(drv_unused, result,
@@@ -167,20 -169,17 +170,20 @@@ static int ll_ext_merge(const struct ll
  {
        char temp[4][50];
        struct strbuf cmd = STRBUF_INIT;
 -      struct strbuf_expand_dict_entry dict[5];
 +      struct strbuf_expand_dict_entry dict[6];
 +      struct strbuf path_sq = STRBUF_INIT;
        const char *args[] = { NULL, NULL };
        int status, fd, i;
        struct stat st;
        assert(opts);
  
 +      sq_quote_buf(&path_sq, path);
        dict[0].placeholder = "O"; dict[0].value = temp[0];
        dict[1].placeholder = "A"; dict[1].value = temp[1];
        dict[2].placeholder = "B"; dict[2].value = temp[2];
        dict[3].placeholder = "L"; dict[3].value = temp[3];
 -      dict[4].placeholder = NULL; dict[4].value = NULL;
 +      dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
 +      dict[5].placeholder = NULL; dict[5].value = NULL;
  
        if (fn->cmdline == NULL)
                die("custom merge driver %s lacks command line.", fn->name);
        for (i = 0; i < 3; i++)
                unlink_or_warn(temp[i]);
        strbuf_release(&cmd);
 +      strbuf_release(&path_sq);
        return status;
  }
  
@@@ -274,7 -272,6 +277,7 @@@ static int read_merge_config(const cha
                 *    %A - temporary file name for our version.
                 *    %B - temporary file name for the other branches' version.
                 *    %L - conflict marker length
 +               *    %P - the original path (safely quoted for the shell)
                 *
                 * The external merge driver should write the results in the
                 * file named by %A, and signal that it has done with zero exit
diff --combined transport-helper.c
index 68e498eebdda08f992e11154039e2e94e58f10d1,b486441c48967997925decad012c73af0a98347d..12f50d5b94802eed31bf36377f297bbd62f7d83c
@@@ -490,8 -490,7 +490,8 @@@ static int fetch_with_import(struct tra
                else
                        private = xstrdup(name);
                if (private) {
 -                      read_ref(private, posn->old_sha1);
 +                      if (read_ref(private, posn->old_sha1) < 0)
 +                              die("Could not read ref %s", private);
                        free(private);
                }
        }
@@@ -1020,10 -1019,7 +1020,10 @@@ static struct ref *get_refs_list(struc
                if (eon) {
                        if (has_attribute(eon + 1, "unchanged")) {
                                (*tail)->status |= REF_STATUS_UPTODATE;
 -                              read_ref((*tail)->name, (*tail)->old_sha1);
 +                              if (read_ref((*tail)->name,
 +                                           (*tail)->old_sha1) < 0)
 +                                      die(N_("Could not read ref %s"),
 +                                          (*tail)->name);
                        }
                }
                tail = &((*tail)->next);
@@@ -1043,6 -1039,8 +1043,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
index 40692f8ae8aba65001bdee1300e525def1e1870d,198502d0ba8404ecf8d27acbd5f46639ac1934ae..164a716053ea6511a57bdcf27c3cd1810edfe36d
@@@ -278,11 -278,12 +278,11 @@@ static int fetch_objs_via_rsync(struct 
        return run_command(&rsync);
  }
  
 -static int write_one_ref(const char *name, const unsigned char *sha1,
 -              int flags, void *data)
 +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;
 -      FILE *f;
  
        /* when called via for_each_ref(), flags is non-zero */
        if (flags && !starts_with(name, "refs/heads/") &&
  
        strbuf_addstr(buf, name);
        if (safe_create_leading_directories(buf->buf) ||
 -                      !(f = fopen(buf->buf, "w")) ||
 -                      fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 ||
 -                      fclose(f))
 -              return error("problems writing temporary file %s", buf->buf);
 +          write_file(buf->buf, 0, "%s\n", 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 refspec_nr, const char **refspec)
  {
        int i;
  
        for (i = 0; i < refspec_nr; i++) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                char *ref;
  
 -              if (dwim_ref(refspec[i], strlen(refspec[i]), sha1, &ref) != 1)
 +              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, sha1, 0, temp_dir)) {
 +              if (write_one_ref(ref, &oid, 0, temp_dir)) {
                        free(ref);
                        return -1;
                }
@@@ -912,6 -914,42 +912,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;