summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.14.5.txt16
-rw-r--r--Documentation/RelNotes/2.15.3.txt6
-rw-r--r--Documentation/RelNotes/2.16.5.txt6
-rw-r--r--Documentation/RelNotes/2.17.2.txt12
-rw-r--r--Documentation/RelNotes/2.18.1.txt6
-rw-r--r--Documentation/RelNotes/2.19.1.txt6
-rw-r--r--Documentation/git-interpret-trailers.txt10
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile1
l---------RelNotes2
-rw-r--r--builtin/add.c2
-rw-r--r--builtin/interpret-trailers.c1
-rw-r--r--builtin/remote.c6
-rw-r--r--builtin/submodule--helper.c1
-rw-r--r--commit.c6
-rw-r--r--commit.h2
-rw-r--r--compat/mingw.c36
-rw-r--r--fetch-object.c17
-rw-r--r--fetch-object.h8
-rw-r--r--fsck.c14
-rw-r--r--lockfile.h4
-rw-r--r--pretty.c3
-rw-r--r--ref-filter.c2
-rw-r--r--rerere.c2
-rw-r--r--sequencer.c32
-rw-r--r--sequencer.h9
-rw-r--r--sha1-file.c2
-rw-r--r--submodule-config.c10
-rw-r--r--submodule.c3
-rw-r--r--t/helper/test-tool.c3
-rw-r--r--t/helper/test-tool.h3
-rw-r--r--t/helper/test-windows-named-pipe.c72
-rwxr-xr-xt/t0051-windows-named-pipe.sh17
-rwxr-xr-xt/t0090-cache-tree.sh18
-rwxr-xr-xt/t0410-partial-clone.sh12
-rwxr-xr-xt/t3404-rebase-interactive.sh7
-rwxr-xr-xt/t3405-rebase-malformed.sh2
-rwxr-xr-xt/t3415-rebase-autosquash.sh19
-rwxr-xr-xt/t3505-cherry-pick-empty.sh18
-rwxr-xr-xt/t4200-rerere.sh29
-rwxr-xr-xt/t4205-log-pretty-formats.sh23
-rwxr-xr-xt/t5505-remote.sh4
-rwxr-xr-xt/t6135-pathspec-with-attrs.sh2
-rwxr-xr-xt/t6300-for-each-ref.sh23
-rwxr-xr-xt/t7416-submodule-dash-url.sh49
-rwxr-xr-xt/t7417-submodule-path-url.sh28
-rwxr-xr-xt/t7501-commit.sh16
-rwxr-xr-xt/t7513-interpret-trailers.sh42
-rw-r--r--tempfile.c2
-rw-r--r--tempfile.h4
-rw-r--r--trailer.c62
-rw-r--r--trailer.h4
-rw-r--r--unpack-trees.c2
53 files changed, 590 insertions, 98 deletions
diff --git a/Documentation/RelNotes/2.14.5.txt b/Documentation/RelNotes/2.14.5.txt
new file mode 100644
index 0000000000..130645fb29
--- /dev/null
+++ b/Documentation/RelNotes/2.14.5.txt
@@ -0,0 +1,16 @@
+Git v2.14.5 Release Notes
+=========================
+
+This release is to address the recently reported CVE-2018-17456.
+
+Fixes since v2.14.4
+-------------------
+
+ * Submodules' "URL"s come from the untrusted .gitmodules file, but
+ we blindly gave it to "git clone" to clone submodules when "git
+ clone --recurse-submodules" was used to clone a project that has
+ such a submodule. The code has been hardened to reject such
+ malformed URLs (e.g. one that begins with a dash).
+
+Credit for finding and fixing this vulnerability goes to joernchen
+and Jeff King, respectively.
diff --git a/Documentation/RelNotes/2.15.3.txt b/Documentation/RelNotes/2.15.3.txt
new file mode 100644
index 0000000000..fd2e6f8df7
--- /dev/null
+++ b/Documentation/RelNotes/2.15.3.txt
@@ -0,0 +1,6 @@
+Git v2.15.3 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 to address
+the recently reported CVE-2018-17456; see the release notes for that
+version for details.
diff --git a/Documentation/RelNotes/2.16.5.txt b/Documentation/RelNotes/2.16.5.txt
new file mode 100644
index 0000000000..cb8ee02a9a
--- /dev/null
+++ b/Documentation/RelNotes/2.16.5.txt
@@ -0,0 +1,6 @@
+Git v2.16.5 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 to address
+the recently reported CVE-2018-17456; see the release notes for that
+version for details.
diff --git a/Documentation/RelNotes/2.17.2.txt b/Documentation/RelNotes/2.17.2.txt
new file mode 100644
index 0000000000..ef021be870
--- /dev/null
+++ b/Documentation/RelNotes/2.17.2.txt
@@ -0,0 +1,12 @@
+Git v2.17.2 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 to address
+the recently reported CVE-2018-17456; see the release notes for that
+version for details.
+
+In addition, this release also teaches "fsck" and the server side
+logic to reject pushes to repositories that attempt to create such a
+problematic ".gitmodules" file as tracked contents, to help hosting
+sites protect their customers by preventing malicious contents from
+spreading.
diff --git a/Documentation/RelNotes/2.18.1.txt b/Documentation/RelNotes/2.18.1.txt
new file mode 100644
index 0000000000..2098cdd776
--- /dev/null
+++ b/Documentation/RelNotes/2.18.1.txt
@@ -0,0 +1,6 @@
+Git v2.18.1 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 and in
+v2.17.2 to address the recently reported CVE-2018-17456; see the
+release notes for those versions for details.
diff --git a/Documentation/RelNotes/2.19.1.txt b/Documentation/RelNotes/2.19.1.txt
new file mode 100644
index 0000000000..da7672674e
--- /dev/null
+++ b/Documentation/RelNotes/2.19.1.txt
@@ -0,0 +1,6 @@
+Git v2.19.1 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 and in
+v2.17.2 to address the recently reported CVE-2018-17456; see the
+release notes for those versions for details.
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index b8fafb1e8b..a5e8b36f62 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -56,8 +56,9 @@ least one Git-generated or user-configured trailer and consists of at
least 25% trailers.
The group must be preceded by one or more empty (or whitespace-only) lines.
The group must either be at the end of the message or be the last
-non-whitespace lines before a line that starts with '---'. Such three
-minus signs start the patch part of the message.
+non-whitespace lines before a line that starts with '---' (followed by a
+space or the end of the line). Such three minus signs start the patch
+part of the message. See also `--no-divider` below.
When reading trailers, there can be whitespaces after the
token, the separator and the value. There can also be whitespaces
@@ -125,6 +126,11 @@ OPTIONS
A convenience alias for `--only-trailers --only-input
--unfold`.
+--no-divider::
+ Do not treat `---` as the end of the commit message. Use this
+ when you know your input contains just the commit message itself
+ (and not an email or the output of `git format-patch`).
+
CONFIGURATION VARIABLES
-----------------------
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index e9dc8f7a01..164fa4f499 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.19.0
+DEF_VER=v2.19.1
LF='
'
diff --git a/Makefile b/Makefile
index 5a969f5830..02a452b1f8 100644
--- a/Makefile
+++ b/Makefile
@@ -738,6 +738,7 @@ TEST_BUILTINS_OBJS += test-submodule-config.o
TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
TEST_BUILTINS_OBJS += test-wildmatch.o
+TEST_BUILTINS_OBJS += test-windows-named-pipe.o
TEST_BUILTINS_OBJS += test-write-cache.o
TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
diff --git a/RelNotes b/RelNotes
index 5d139ba7f1..5c08e40d2b 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.19.0.txt \ No newline at end of file
+Documentation/RelNotes/2.19.1.txt \ No newline at end of file
diff --git a/builtin/add.c b/builtin/add.c
index 9916498a29..0b64bcdebe 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -454,7 +454,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
*/
- parse_pathspec(&pathspec, 0,
+ parse_pathspec(&pathspec, PATHSPEC_ATTR,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index b742539d4d..4b87e0dd2e 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -104,6 +104,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
{ OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+ OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
N_("trailer(s) to add"), option_parse_trailer),
OPT_END()
diff --git a/builtin/remote.c b/builtin/remote.c
index 7876db1c20..5fd1012faa 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -625,7 +625,7 @@ static int mv(int argc, const char **argv)
oldremote = remote_get(rename.old_name);
if (!remote_is_configured(oldremote, 1))
- die(_("No such remote: %s"), rename.old_name);
+ die(_("No such remote: '%s'"), rename.old_name);
if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
return migrate_file(oldremote);
@@ -761,7 +761,7 @@ static int rm(int argc, const char **argv)
remote = remote_get(argv[1]);
if (!remote_is_configured(remote, 1))
- die(_("No such remote: %s"), argv[1]);
+ die(_("No such remote: '%s'"), argv[1]);
known_remotes.to_delete = remote;
for_each_remote(add_known_remote, &known_remotes);
@@ -860,7 +860,7 @@ static int get_remote_ref_states(const char *name,
states->remote = remote_get(name);
if (!states->remote)
- return error(_("No such remote: %s"), name);
+ return error(_("No such remote: '%s'"), name);
read_branches();
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f6fb8991f3..5e6f2db4cd 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1233,6 +1233,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
if (gitdir && *gitdir)
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
+ argv_array_push(&cp.args, "--");
argv_array_push(&cp.args, url);
argv_array_push(&cp.args, path);
diff --git a/commit.c b/commit.c
index 449c1f4920..db679c2adf 100644
--- a/commit.c
+++ b/commit.c
@@ -1787,10 +1787,10 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
* Returns the number of bytes from the tail to ignore, to be fed as
* the second parameter to append_signoff().
*/
-int ignore_non_trailer(const char *buf, size_t len)
+size_t ignore_non_trailer(const char *buf, size_t len)
{
- int boc = 0;
- int bol = 0;
+ size_t boc = 0;
+ size_t bol = 0;
int in_old_conflicts_block = 0;
size_t cutoff = wt_status_locate_end(buf, len);
diff --git a/commit.h b/commit.h
index da0db36eba..43f93b973d 100644
--- a/commit.h
+++ b/commit.h
@@ -322,7 +322,7 @@ extern const char *find_commit_header(const char *msg, const char *key,
size_t *out_len);
/* Find the end of the log message, the right place for a new trailer. */
-extern int ignore_non_trailer(const char *buf, size_t len);
+extern size_t ignore_non_trailer(const char *buf, size_t len);
typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
void *cb_data);
diff --git a/compat/mingw.c b/compat/mingw.c
index 858ca14a57..18caf21969 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -341,6 +341,19 @@ int mingw_mkdir(const char *path, int mode)
return ret;
}
+/*
+ * Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA
+ * is documented in [1] as opening a writable file handle in append mode.
+ * (It is believed that) this is atomic since it is maintained by the
+ * kernel unlike the O_APPEND flag which is racily maintained by the CRT.
+ *
+ * [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants
+ *
+ * This trick does not appear to work for named pipes. Instead it creates
+ * a named pipe client handle that cannot be written to. Callers should
+ * just use the regular _wopen() for them. (And since client handle gets
+ * bound to a unique server handle, it isn't really an issue.)
+ */
static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
{
HANDLE handle;
@@ -360,10 +373,12 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
return errno = err_win_to_posix(GetLastError()), -1;
+
/*
* No O_APPEND here, because the CRT uses it only to reset the
- * file pointer to EOF on write(); but that is not necessary
- * for a file created with FILE_APPEND_DATA.
+ * file pointer to EOF before each write(); but that is not
+ * necessary (and may lead to races) for a file created with
+ * FILE_APPEND_DATA.
*/
fd = _open_osfhandle((intptr_t)handle, O_BINARY);
if (fd < 0)
@@ -371,6 +386,21 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
return fd;
}
+/*
+ * Does the pathname map to the local named pipe filesystem?
+ * That is, does it have a "//./pipe/" prefix?
+ */
+static int is_local_named_pipe_path(const char *filename)
+{
+ return (is_dir_sep(filename[0]) &&
+ is_dir_sep(filename[1]) &&
+ filename[2] == '.' &&
+ is_dir_sep(filename[3]) &&
+ !strncasecmp(filename+4, "pipe", 4) &&
+ is_dir_sep(filename[8]) &&
+ filename[9]);
+}
+
int mingw_open (const char *filename, int oflags, ...)
{
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
@@ -387,7 +417,7 @@ int mingw_open (const char *filename, int oflags, ...)
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
- if (oflags & O_APPEND)
+ if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
open_fn = mingw_open_append;
else
open_fn = _wopen;
diff --git a/fetch-object.c b/fetch-object.c
index 853624f811..4266548800 100644
--- a/fetch-object.c
+++ b/fetch-object.c
@@ -23,21 +23,16 @@ static void fetch_refs(const char *remote_name, struct ref *ref)
fetch_if_missing = original_fetch_if_missing;
}
-void fetch_object(const char *remote_name, const unsigned char *sha1)
-{
- struct ref *ref = alloc_ref(sha1_to_hex(sha1));
- hashcpy(ref->old_oid.hash, sha1);
- fetch_refs(remote_name, ref);
-}
-
-void fetch_objects(const char *remote_name, const struct oid_array *to_fetch)
+void fetch_objects(const char *remote_name, const struct object_id *oids,
+ int oid_nr)
{
struct ref *ref = NULL;
int i;
- for (i = 0; i < to_fetch->nr; i++) {
- struct ref *new_ref = alloc_ref(oid_to_hex(&to_fetch->oid[i]));
- oidcpy(&new_ref->old_oid, &to_fetch->oid[i]);
+ for (i = 0; i < oid_nr; i++) {
+ struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i]));
+ oidcpy(&new_ref->old_oid, &oids[i]);
+ new_ref->exact_oid = 1;
new_ref->next = ref;
ref = new_ref;
}
diff --git a/fetch-object.h b/fetch-object.h
index 4b269d07ed..d2f996d4e8 100644
--- a/fetch-object.h
+++ b/fetch-object.h
@@ -1,11 +1,7 @@
#ifndef FETCH_OBJECT_H
#define FETCH_OBJECT_H
-#include "sha1-array.h"
-
-extern void fetch_object(const char *remote_name, const unsigned char *sha1);
-
-extern void fetch_objects(const char *remote_name,
- const struct oid_array *to_fetch);
+void fetch_objects(const char *remote_name, const struct object_id *oids,
+ int oid_nr);
#endif
diff --git a/fsck.c b/fsck.c
index a0cee0be59..0f56977d6a 100644
--- a/fsck.c
+++ b/fsck.c
@@ -67,6 +67,8 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(GITMODULES_LARGE, ERROR) \
FUNC(GITMODULES_NAME, ERROR) \
FUNC(GITMODULES_SYMLINK, ERROR) \
+ FUNC(GITMODULES_URL, ERROR) \
+ FUNC(GITMODULES_PATH, ERROR) \
/* warnings */ \
FUNC(BAD_FILEMODE, WARN) \
FUNC(EMPTY_NAME, WARN) \
@@ -992,6 +994,18 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
FSCK_MSG_GITMODULES_NAME,
"disallowed submodule name: %s",
name);
+ if (!strcmp(key, "url") && value &&
+ looks_like_command_line_option(value))
+ data->ret |= report(data->options, data->obj,
+ FSCK_MSG_GITMODULES_URL,
+ "disallowed submodule url: %s",
+ value);
+ if (!strcmp(key, "path") && value &&
+ looks_like_command_line_option(value))
+ data->ret |= report(data->options, data->obj,
+ FSCK_MSG_GITMODULES_PATH,
+ "disallowed submodule path: %s",
+ value);
free(name);
return 0;
diff --git a/lockfile.h b/lockfile.h
index f401c979f0..35403ccc0d 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -263,8 +263,8 @@ static inline int close_lock_file_gently(struct lock_file *lk)
* nobody else) to inspect the contents you wrote, while still
* holding the lock yourself.
*
- * * `reopen_lock_file()` to reopen the lockfile. Make further updates
- * to the contents.
+ * * `reopen_lock_file()` to reopen the lockfile, truncating the existing
+ * contents. Write out the new contents.
*
* * `commit_lock_file()` to make the final version permanent.
*/
diff --git a/pretty.c b/pretty.c
index 98cf5228f9..8ca29e9281 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1304,6 +1304,9 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (skip_prefix(placeholder, "(trailers", &arg)) {
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+
+ opts.no_divider = 1;
+
if (*arg == ':') {
arg++;
for (;;) {
diff --git a/ref-filter.c b/ref-filter.c
index 0bccfceff2..3e8ee04d09 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -263,6 +263,8 @@ static int trailers_atom_parser(const struct ref_format *format, struct used_ato
struct string_list params = STRING_LIST_INIT_DUP;
int i;
+ atom->u.contents.trailer_opts.no_divider = 1;
+
if (arg) {
string_list_split(&params, arg, ',', -1);
for (i = 0; i < params.nr; i++) {
diff --git a/rerere.c b/rerere.c
index c7787aa07f..783d4dae2a 100644
--- a/rerere.c
+++ b/rerere.c
@@ -533,7 +533,7 @@ static int check_one_conflict(int i, int *type)
}
*type = PUNTED;
- while (ce_stage(active_cache[i]) == 1)
+ while (i < active_nr && ce_stage(active_cache[i]) == 1)
i++;
/* Only handle regular files with both stages #2 and #3 */
diff --git a/sequencer.c b/sequencer.c
index dc2c58d464..f388405b4e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -225,13 +225,16 @@ static const char *get_todo_path(const struct replay_opts *opts)
* Returns 3 when sob exists within conforming footer as last entry
*/
static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
- int ignore_footer)
+ size_t ignore_footer)
{
+ struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
struct trailer_info info;
- int i;
+ size_t i;
int found_sob = 0, found_sob_last = 0;
- trailer_info_get(&info, sb->buf);
+ opts.no_divider = 1;
+
+ trailer_info_get(&info, sb->buf, &opts);
if (info.trailer_start == info.trailer_end)
return 0;
@@ -899,7 +902,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
if ((flags & ALLOW_EMPTY))
argv_array_push(&cmd.args, "--allow-empty");
- if (opts->allow_empty_message)
+ if (!(flags & EDIT_MSG))
argv_array_push(&cmd.args, "--allow-empty-message");
if (cmd.err == -1) {
@@ -1313,7 +1316,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
- if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+ if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
res = 1; /* run 'git commit' to display error message */
goto out;
}
@@ -3607,9 +3610,20 @@ static int commit_staged_changes(struct replay_opts *opts,
* the commit message and if there was a squash, let the user
* edit it.
*/
- if (is_clean && !oidcmp(&head, &to_amend) &&
- opts->current_fixup_count > 0 &&
- file_exists(rebase_path_stopped_sha())) {
+ if (!is_clean || !opts->current_fixup_count)
+ ; /* this is not the final fixup */
+ else if (oidcmp(&head, &to_amend) ||
+ !file_exists(rebase_path_stopped_sha())) {
+ /* was a final fixup or squash done manually? */
+ if (!is_fixup(peek_command(todo_list, 0))) {
+ unlink(rebase_path_fixup_msg());
+ unlink(rebase_path_squash_msg());
+ unlink(rebase_path_current_fixups());
+ strbuf_reset(&opts->current_fixups);
+ opts->current_fixup_count = 0;
+ }
+ } else {
+ /* we are in a fixup/squash chain */
const char *p = opts->current_fixups.buf;
int len = opts->current_fixups.len;
@@ -3828,7 +3842,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
return res;
}
-void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
+void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
{
unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
struct strbuf sob = STRBUF_INIT;
diff --git a/sequencer.h b/sequencer.h
index c751c9d6e4..c986bc8251 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -90,7 +90,14 @@ int rearrange_squash(void);
extern const char sign_off_header[];
-void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
+/*
+ * Append a signoff to the commit message in "msgbuf". The ignore_footer
+ * parameter specifies the number of bytes at the end of msgbuf that should
+ * not be considered at all. I.e., they are not checked for existing trailers,
+ * and the new signoff will be spliced into the buffer before those bytes.
+ */
+void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
+
void append_conflicts_hint(struct strbuf *msgbuf);
int message_is_empty(const struct strbuf *sb,
enum commit_msg_cleanup_mode cleanup_mode);
diff --git a/sha1-file.c b/sha1-file.c
index 97b7423848..2edf4564f6 100644
--- a/sha1-file.c
+++ b/sha1-file.c
@@ -1317,7 +1317,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid,
* TODO Pass a repository struct through fetch_object,
* such that arbitrary repositories work.
*/
- fetch_object(repository_format_partial_clone, real->hash);
+ fetch_objects(repository_format_partial_clone, real, 1);
already_retried = 1;
continue;
}
diff --git a/submodule-config.c b/submodule-config.c
index fc2c41b947..0b2f1647ab 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -384,6 +384,12 @@ static void warn_multiple_config(const struct object_id *treeish_name,
commit_string, name, option);
}
+static void warn_command_line_option(const char *var, const char *value)
+{
+ warning(_("ignoring '%s' which may be interpreted as"
+ " a command-line option: %s"), var, value);
+}
+
struct parse_config_parameter {
struct submodule_cache *cache;
const struct object_id *treeish_name;
@@ -409,6 +415,8 @@ static int parse_config(const char *var, const char *value, void *data)
if (!strcmp(item.buf, "path")) {
if (!value)
ret = config_error_nonbool(var);
+ else if (looks_like_command_line_option(value))
+ warn_command_line_option(var, value);
else if (!me->overwrite && submodule->path)
warn_multiple_config(me->treeish_name, submodule->name,
"path");
@@ -449,6 +457,8 @@ static int parse_config(const char *var, const char *value, void *data)
} else if (!strcmp(item.buf, "url")) {
if (!value) {
ret = config_error_nonbool(var);
+ } else if (looks_like_command_line_option(value)) {
+ warn_command_line_option(var, value);
} else if (!me->overwrite && submodule->url) {
warn_multiple_config(me->treeish_name, submodule->name,
"url");
diff --git a/submodule.c b/submodule.c
index a2b266fbfa..4a5212bdfb 100644
--- a/submodule.c
+++ b/submodule.c
@@ -65,8 +65,7 @@ int is_staging_gitmodules_ok(struct index_state *istate)
if ((pos >= 0) && (pos < istate->cache_nr)) {
struct stat st;
if (lstat(GITMODULES_FILE, &st) == 0 &&
- ie_match_stat(istate, istate->cache[pos], &st,
- CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED)
+ ie_match_stat(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
return 0;
}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 0edafcfd65..db19131f71 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -43,6 +43,9 @@ static struct test_cmd cmds[] = {
{ "subprocess", cmd__subprocess },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
{ "wildmatch", cmd__wildmatch },
+#ifdef GIT_WINDOWS_NATIVE
+ { "windows-named-pipe", cmd__windows_named_pipe },
+#endif
{ "write-cache", cmd__write_cache },
};
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index e954e8c522..4914001086 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -39,6 +39,9 @@ int cmd__submodule_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
int cmd__urlmatch_normalization(int argc, const char **argv);
int cmd__wildmatch(int argc, const char **argv);
+#ifdef GIT_WINDOWS_NATIVE
+int cmd__windows_named_pipe(int argc, const char **argv);
+#endif
int cmd__write_cache(int argc, const char **argv);
#endif
diff --git a/t/helper/test-windows-named-pipe.c b/t/helper/test-windows-named-pipe.c
new file mode 100644
index 0000000000..b4b752b01a
--- /dev/null
+++ b/t/helper/test-windows-named-pipe.c
@@ -0,0 +1,72 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
+
+#ifdef GIT_WINDOWS_NATIVE
+static const char *usage_string = "<pipe-filename>";
+
+#define TEST_BUFSIZE (4096)
+
+int cmd__windows_named_pipe(int argc, const char **argv)
+{
+ const char *filename;
+ struct strbuf pathname = STRBUF_INIT;
+ int err;
+ HANDLE h;
+ BOOL connected;
+ char buf[TEST_BUFSIZE + 1];
+
+ if (argc < 2)
+ goto print_usage;
+ filename = argv[1];
+ if (strchr(filename, '/') || strchr(filename, '\\'))
+ goto print_usage;
+ strbuf_addf(&pathname, "//./pipe/%s", filename);
+
+ /*
+ * Create a single instance of the server side of the named pipe.
+ * This will allow exactly one client instance to connect to it.
+ */
+ h = CreateNamedPipeA(
+ pathname.buf,
+ PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ TEST_BUFSIZE, TEST_BUFSIZE, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ err = err_win_to_posix(GetLastError());
+ fprintf(stderr, "CreateNamedPipe failed: %s\n",
+ strerror(err));
+ return err;
+ }
+
+ connected = ConnectNamedPipe(h, NULL)
+ ? TRUE
+ : (GetLastError() == ERROR_PIPE_CONNECTED);
+ if (!connected) {
+ err = err_win_to_posix(GetLastError());
+ fprintf(stderr, "ConnectNamedPipe failed: %s\n",
+ strerror(err));
+ CloseHandle(h);
+ return err;
+ }
+
+ while (1) {
+ DWORD nbr;
+ BOOL success = ReadFile(h, buf, TEST_BUFSIZE, &nbr, NULL);
+ if (!success || nbr == 0)
+ break;
+ buf[nbr] = 0;
+
+ write(1, buf, nbr);
+ }
+
+ DisconnectNamedPipe(h);
+ CloseHandle(h);
+ return 0;
+
+print_usage:
+ fprintf(stderr, "usage: %s %s\n", argv[0], usage_string);
+ return 1;
+}
+#endif
diff --git a/t/t0051-windows-named-pipe.sh b/t/t0051-windows-named-pipe.sh
new file mode 100755
index 0000000000..10ac92d225
--- /dev/null
+++ b/t/t0051-windows-named-pipe.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description='Windows named pipes'
+
+. ./test-lib.sh
+
+test_expect_success MINGW 'o_append write to named pipe' '
+ GIT_TRACE="$(pwd)/expect" git status >/dev/null 2>&1 &&
+ { test-tool windows-named-pipe t0051 >actual 2>&1 & } &&
+ pid=$! &&
+ sleep 1 &&
+ GIT_TRACE=//./pipe/t0051 git status >/dev/null 2>warning &&
+ wait $pid &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 7de40141ca..94fcb4a78e 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -161,6 +161,24 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
test_cache_tree
'
+test_expect_success PERL 'commit -p with shrinking cache-tree' '
+ mkdir -p deep/subdir &&
+ echo content >deep/subdir/file &&
+ git add deep &&
+ git commit -m add &&
+ git rm -r deep &&
+
+ before=$(wc -c <.git/index) &&
+ git commit -m delete -p &&
+ after=$(wc -c <.git/index) &&
+
+ # double check that the index shrank
+ test $before -gt $after &&
+
+ # and that our index was not corrupted
+ git fsck
+'
+
test_expect_success 'commit in child dir has cache-tree' '
mkdir dir &&
>dir/child.t &&
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 1281300664..0ab02c337d 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -170,6 +170,18 @@ test_expect_success 'fetching of missing objects' '
git verify-pack --verbose "$IDX" | grep "$HASH"
'
+test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
+ # ref-in-want requires protocol version 2
+ git -C server config protocol.version 2 &&
+ git -C server config uploadpack.allowrefinwant 1 &&
+ git -C repo config protocol.version 2 &&
+
+ rm -rf repo/.git/objects/* &&
+ rm -f trace &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C repo cat-file -p "$HASH" &&
+ grep "git< fetch=.*ref-in-want" trace
+'
+
test_expect_success 'rev-list stops traversal at missing and promised commit' '
rm -rf repo &&
test_create_repo repo &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 86bba5ed7c..ff89b6341a 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -569,16 +569,15 @@ test_expect_success '--continue tries to commit, even for "edit"' '
'
test_expect_success 'aborted --continue does not squash commits after "edit"' '
- test_when_finished "git rebase --abort" &&
old=$(git rev-parse HEAD) &&
test_tick &&
set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
echo "edited again" > file7 &&
git add file7 &&
- echo all the things >>conflict &&
- test_must_fail git rebase --continue &&
- test $old = $(git rev-parse HEAD)
+ test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
+ test $old = $(git rev-parse HEAD) &&
+ git rebase --abort
'
test_expect_success 'auto-amend only edited commits after "edit"' '
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index da94dddc86..860e63e444 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -83,7 +83,7 @@ test_expect_success 'rebase -m commit with empty message' '
test_expect_success 'rebase -i commit with empty message' '
git checkout diff-in-message &&
set_fake_editor &&
- env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
+ test_must_fail env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
git rebase -i HEAD^
'
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index e364c12622..13f5688135 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -330,4 +330,23 @@ test_expect_success 'wrapped original subject' '
test $base = $parent
'
+test_expect_success 'abort last squash' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ test_when_finished "git checkout master" &&
+
+ git checkout -b some-squashes &&
+ git commit --allow-empty -m first &&
+ git commit --allow-empty --squash HEAD &&
+ git commit --allow-empty -m second &&
+ git commit --allow-empty --squash HEAD &&
+
+ test_must_fail git -c core.editor="grep -q ^pick" \
+ rebase -ki --autosquash HEAD~4 &&
+ : do not finish the squash, but resolve it manually &&
+ git commit --allow-empty --amend -m edited-first &&
+ git rebase --skip &&
+ git show >actual &&
+ ! grep first actual
+'
+
test_done
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index fbdc47cfbd..5f911bb529 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -11,17 +11,14 @@ test_expect_success setup '
test_tick &&
git commit -m "first" &&
- git checkout -b empty-branch &&
- test_tick &&
- git commit --allow-empty -m "empty" &&
-
+ git checkout -b empty-message-branch &&
echo third >> file1 &&
git add file1 &&
test_tick &&
git commit --allow-empty-message -m "" &&
git checkout master &&
- git checkout -b empty-branch2 &&
+ git checkout -b empty-change-branch &&
test_tick &&
git commit --allow-empty -m "empty"
@@ -29,7 +26,7 @@ test_expect_success setup '
test_expect_success 'cherry-pick an empty commit' '
git checkout master &&
- test_expect_code 1 git cherry-pick empty-branch^
+ test_expect_code 1 git cherry-pick empty-change-branch
'
test_expect_success 'index lockfile was removed' '
@@ -37,8 +34,9 @@ test_expect_success 'index lockfile was removed' '
'
test_expect_success 'cherry-pick a commit with an empty message' '
+ test_when_finished "git reset --hard empty-message-branch~1" &&
git checkout master &&
- test_expect_code 1 git cherry-pick empty-branch
+ git cherry-pick empty-message-branch
'
test_expect_success 'index lockfile was removed' '
@@ -47,7 +45,7 @@ test_expect_success 'index lockfile was removed' '
test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
git checkout -f master &&
- git cherry-pick --allow-empty-message empty-branch
+ git cherry-pick --allow-empty-message empty-message-branch
'
test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
@@ -55,12 +53,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
echo fourth >>file2 &&
git add file2 &&
git commit -m "fourth" &&
- test_must_fail git cherry-pick empty-branch2
+ test_must_fail git cherry-pick empty-change-branch
'
test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
git checkout master &&
- git cherry-pick --allow-empty empty-branch2
+ git cherry-pick --allow-empty empty-change-branch
'
test_expect_success 'cherry pick with --keep-redundant-commits' '
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 65da74c766..313222d0d6 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -577,4 +577,33 @@ test_expect_success 'multiple identical conflicts' '
count_pre_post 0 0
'
+test_expect_success 'setup simple stage 1 handling' '
+ test_create_repo stage_1_handling &&
+ (
+ cd stage_1_handling &&
+
+ test_seq 1 10 >original &&
+ git add original &&
+ git commit -m original &&
+
+ git checkout -b A master &&
+ git mv original A &&
+ git commit -m "rename to A" &&
+
+ git checkout -b B master &&
+ git mv original B &&
+ git commit -m "rename to B"
+ )
+'
+
+test_expect_success 'test simple stage 1 handling' '
+ (
+ cd stage_1_handling &&
+
+ git config rerere.enabled true &&
+ git checkout A^0 &&
+ test_must_fail git merge B^0
+ )
+'
+
test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 2052cadb11..978a8a66ff 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -598,4 +598,27 @@ test_expect_success ':only and :unfold work together' '
test_cmp expect actual
'
+test_expect_success 'trailer parsing not fooled by --- line' '
+ git commit --allow-empty -F - <<-\EOF &&
+ this is the subject
+
+ This is the body. The message has a "---" line which would confuse a
+ message+patch parser. But here we know we have only a commit message,
+ so we get it right.
+
+ trailer: wrong
+ ---
+ This is more body.
+
+ trailer: right
+ EOF
+
+ {
+ echo "trailer: right" &&
+ echo
+ } >expect &&
+ git log --no-walk --format="%(trailers)" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 241e6a319d..d2a2cdd453 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -145,7 +145,7 @@ test_expect_success 'remove remote protects local branches' '
test_expect_success 'remove errors out early when deleting non-existent branch' '
(
cd test &&
- echo "fatal: No such remote: foo" >expect &&
+ echo "fatal: No such remote: '\''foo'\''" >expect &&
test_must_fail git remote rm foo 2>actual &&
test_i18ncmp expect actual
)
@@ -173,7 +173,7 @@ test_expect_success 'remove remote with a branch without configured merge' '
test_expect_success 'rename errors out early when deleting non-existent branch' '
(
cd test &&
- echo "fatal: No such remote: foo" >expect &&
+ echo "fatal: No such remote: '\''foo'\''" >expect &&
test_must_fail git remote rename foo bar 2>actual &&
test_i18ncmp expect actual
)
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
index 77b8cef661..e436a73962 100755
--- a/t/t6135-pathspec-with-attrs.sh
+++ b/t/t6135-pathspec-with-attrs.sh
@@ -166,7 +166,7 @@ test_expect_success 'fail if attr magic is used places not implemented' '
# though, but git-add is convenient as it has its own internal pathspec
# parsing.
test_must_fail git add ":(attr:labelB)" 2>actual &&
- test_i18ngrep "unsupported magic" actual
+ test_i18ngrep "magic not supported" actual
'
test_expect_success 'abort on giving invalid label on the command line' '
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 024f8c06f7..97bfbee6e8 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -715,6 +715,29 @@ test_expect_success 'basic atom: head contents:trailers' '
test_cmp expect actual.clean
'
+test_expect_success 'trailer parsing not fooled by --- line' '
+ git commit --allow-empty -F - <<-\EOF &&
+ this is the subject
+
+ This is the body. The message has a "---" line which would confuse a
+ message+patch parser. But here we know we have only a commit message,
+ so we get it right.
+
+ trailer: wrong
+ ---
+ This is more body.
+
+ trailer: right
+ EOF
+
+ {
+ echo "trailer: right" &&
+ echo
+ } >expect &&
+ git for-each-ref --format="%(trailers)" refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'Add symbolic ref for the following tests' '
git symbolic-ref refs/heads/sym refs/heads/master
'
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
new file mode 100755
index 0000000000..1cd2c1c1ea
--- /dev/null
+++ b/t/t7416-submodule-dash-url.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='check handling of .gitmodule url with dash'
+. ./test-lib.sh
+
+test_expect_success 'create submodule with protected dash in url' '
+ git init upstream &&
+ git -C upstream commit --allow-empty -m base &&
+ mv upstream ./-upstream &&
+ git submodule add ./-upstream sub &&
+ git add sub .gitmodules &&
+ git commit -m submodule
+'
+
+test_expect_success 'clone can recurse submodule' '
+ test_when_finished "rm -rf dst" &&
+ git clone --recurse-submodules . dst &&
+ echo base >expect &&
+ git -C dst/sub log -1 --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fsck accepts protected dash' '
+ test_when_finished "rm -rf dst" &&
+ git init --bare dst &&
+ git -C dst config transfer.fsckObjects true &&
+ git push dst HEAD
+'
+
+test_expect_success 'remove ./ protection from .gitmodules url' '
+ perl -i -pe "s{\./}{}" .gitmodules &&
+ git commit -am "drop protection"
+'
+
+test_expect_success 'clone rejects unprotected dash' '
+ test_when_finished "rm -rf dst" &&
+ test_must_fail git clone --recurse-submodules . dst 2>err &&
+ test_i18ngrep ignoring err
+'
+
+test_expect_success 'fsck rejects unprotected dash' '
+ test_when_finished "rm -rf dst" &&
+ git init --bare dst &&
+ git -C dst config transfer.fsckObjects true &&
+ test_must_fail git push dst HEAD 2>err &&
+ grep gitmodulesUrl err
+'
+
+test_done
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
new file mode 100755
index 0000000000..756af8c4d6
--- /dev/null
+++ b/t/t7417-submodule-path-url.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='check handling of .gitmodule path with dash'
+. ./test-lib.sh
+
+test_expect_success 'create submodule with dash in path' '
+ git init upstream &&
+ git -C upstream commit --allow-empty -m base &&
+ git submodule add ./upstream sub &&
+ git mv sub ./-sub &&
+ git commit -m submodule
+'
+
+test_expect_success 'clone rejects unprotected dash' '
+ test_when_finished "rm -rf dst" &&
+ git clone --recurse-submodules . dst 2>err &&
+ test_i18ngrep ignoring err
+'
+
+test_expect_success 'fsck rejects unprotected dash' '
+ test_when_finished "rm -rf dst" &&
+ git init --bare dst &&
+ git -C dst config transfer.fsckObjects true &&
+ test_must_fail git push dst HEAD 2>err &&
+ grep gitmodulesPath err
+'
+
+test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 4cae92804d..1a6773ee68 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -517,6 +517,22 @@ Myfooter: x" &&
test_cmp expected actual
'
+test_expect_success 'signoff not confused by ---' '
+ cat >expected <<-EOF &&
+ subject
+
+ body
+ ---
+ these dashes confuse the parser!
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ # should be a noop, since we already signed
+ git commit --allow-empty --signoff -F expected &&
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'multiple -m' '
>negative &&
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 164719d1c9..c441861331 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1417,4 +1417,46 @@ test_expect_success 'unfold' '
test_cmp expected actual
'
+test_expect_success 'handling of --- lines in input' '
+ echo "real-trailer: just right" >expected &&
+
+ git interpret-trailers --parse >actual <<-\EOF &&
+ subject
+
+ body
+
+ not-a-trailer: too soon
+ ------ this is just a line in the commit message with a bunch of
+ ------ dashes; it does not have any syntactic meaning.
+
+ real-trailer: just right
+ ---
+ below the dashed line may be a patch, etc.
+
+ not-a-trailer: too late
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'suppress --- handling' '
+ echo "real-trailer: just right" >expected &&
+
+ git interpret-trailers --parse --no-divider >actual <<-\EOF &&
+ subject
+
+ This commit message has a "---" in it, but because we tell
+ interpret-trailers not to respect that, it has no effect.
+
+ not-a-trailer: too soon
+ ---
+
+ This is still the commit message body.
+
+ real-trailer: just right
+ EOF
+
+ test_cmp expected actual
+'
+
test_done
diff --git a/tempfile.c b/tempfile.c
index 139ecd97f8..d43ad8c191 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -279,7 +279,7 @@ int reopen_tempfile(struct tempfile *tempfile)
BUG("reopen_tempfile called for an inactive object");
if (0 <= tempfile->fd)
BUG("reopen_tempfile called for an open object");
- tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
+ tempfile->fd = open(tempfile->filename.buf, O_WRONLY|O_TRUNC);
return tempfile->fd;
}
diff --git a/tempfile.h b/tempfile.h
index 36434eb6fa..61d8dc4d1b 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -236,8 +236,8 @@ extern int close_tempfile_gently(struct tempfile *tempfile);
* it (and nobody else) to inspect or even modify the file's
* contents.
*
- * * `reopen_tempfile()` to reopen the temporary file. Make further
- * updates to the contents.
+ * * `reopen_tempfile()` to reopen the temporary file, truncating the existing
+ * contents. Write out the new contents.
*
* * `rename_tempfile()` to move the file to its permanent location.
*/
diff --git a/trailer.c b/trailer.c
index 4e309460d1..0796f326b3 100644
--- a/trailer.c
+++ b/trailer.c
@@ -585,7 +585,7 @@ static const char *token_from_item(struct arg_item *item, char *tok)
return item->conf.name;
}
-static int token_matches_item(const char *tok, struct arg_item *item, int tok_len)
+static int token_matches_item(const char *tok, struct arg_item *item, size_t tok_len)
{
if (!strncasecmp(tok, item->conf.name, tok_len))
return 1;
@@ -603,7 +603,7 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_le
* distinguished from the non-well-formed-line case (in which this function
* returns -1) because some callers of this function need such a distinction.
*/
-static int find_separator(const char *line, const char *separators)
+static ssize_t find_separator(const char *line, const char *separators)
{
int whitespace_found = 0;
const char *c;
@@ -630,10 +630,10 @@ static int find_separator(const char *line, const char *separators)
*/
static void parse_trailer(struct strbuf *tok, struct strbuf *val,
const struct conf_info **conf, const char *trailer,
- int separator_pos)
+ ssize_t separator_pos)
{
struct arg_item *item;
- int tok_len;
+ size_t tok_len;
struct list_head *pos;
if (separator_pos != -1) {
@@ -721,7 +721,7 @@ static void process_command_line_args(struct list_head *arg_head,
list_for_each(pos, new_trailer_head) {
struct new_trailer_item *tr =
list_entry(pos, struct new_trailer_item, list);
- int separator_pos = find_separator(tr->text, cl_separators);
+ ssize_t separator_pos = find_separator(tr->text, cl_separators);
if (separator_pos == 0) {
struct strbuf sb = STRBUF_INIT;
@@ -763,9 +763,9 @@ static const char *next_line(const char *str)
/*
* Return the position of the start of the last line. If len is 0, return -1.
*/
-static int last_line(const char *buf, size_t len)
+static ssize_t last_line(const char *buf, size_t len)
{
- int i;
+ ssize_t i;
if (len == 0)
return -1;
if (len == 1)
@@ -788,12 +788,14 @@ static int last_line(const char *buf, size_t len)
* Return the position of the start of the patch or the length of str if there
* is no patch in the message.
*/
-static int find_patch_start(const char *str)
+static size_t find_patch_start(const char *str)
{
const char *s;
for (s = str; *s; s = next_line(s)) {
- if (starts_with(s, "---"))
+ const char *v;
+
+ if (skip_prefix(s, "---", &v) && isspace(*v))
return s - str;
}
@@ -804,10 +806,11 @@ static int find_patch_start(const char *str)
* Return the position of the first trailer line or len if there are no
* trailers.
*/
-static int find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_start(const char *buf, size_t len)
{
const char *s;
- int end_of_title, l, only_spaces = 1;
+ ssize_t end_of_title, l;
+ int only_spaces = 1;
int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
/*
* Number of possible continuation lines encountered. This will be
@@ -838,7 +841,7 @@ static int find_trailer_start(const char *buf, size_t len)
l = last_line(buf, l)) {
const char *bol = buf + l;
const char **p;
- int separator_pos;
+ ssize_t separator_pos;
if (bol[0] == comment_line_char) {
non_trailer_lines += possible_continuation_lines;
@@ -899,14 +902,14 @@ continue_outer_loop:
}
/* Return the position of the end of the trailers. */
-static int find_trailer_end(const char *buf, size_t len)
+static size_t find_trailer_end(const char *buf, size_t len)
{
return len - ignore_non_trailer(buf, len);
}
static int ends_with_blank_line(const char *buf, size_t len)
{
- int ll = last_line(buf, len);
+ ssize_t ll = last_line(buf, len);
if (ll < 0)
return 0;
return is_blank_line(buf + ll);
@@ -939,17 +942,17 @@ static void unfold_value(struct strbuf *val)
strbuf_release(&out);
}
-static int process_input_file(FILE *outfile,
- const char *str,
- struct list_head *head,
- const struct process_trailer_options *opts)
+static size_t process_input_file(FILE *outfile,
+ const char *str,
+ struct list_head *head,
+ const struct process_trailer_options *opts)
{
struct trailer_info info;
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
- int i;
+ size_t i;
- trailer_info_get(&info, str);
+ trailer_info_get(&info, str, opts);
/* Print lines before the trailers as is */
if (!opts->only_trailers)
@@ -1032,7 +1035,7 @@ void process_trailers(const char *file,
{
LIST_HEAD(head);
struct strbuf sb = STRBUF_INIT;
- int trailer_end;
+ size_t trailer_end;
FILE *outfile = stdout;
ensure_configured();
@@ -1066,7 +1069,8 @@ void process_trailers(const char *file,
strbuf_release(&sb);
}
-void trailer_info_get(struct trailer_info *info, const char *str)
+void trailer_info_get(struct trailer_info *info, const char *str,
+ const struct process_trailer_options *opts)
{
int patch_start, trailer_end, trailer_start;
struct strbuf **trailer_lines, **ptr;
@@ -1076,7 +1080,11 @@ void trailer_info_get(struct trailer_info *info, const char *str)
ensure_configured();
- patch_start = find_patch_start(str);
+ if (opts->no_divider)
+ patch_start = strlen(str);
+ else
+ patch_start = find_patch_start(str);
+
trailer_end = find_trailer_end(str, patch_start);
trailer_start = find_trailer_start(str, trailer_end);
@@ -1111,7 +1119,7 @@ void trailer_info_get(struct trailer_info *info, const char *str)
void trailer_info_release(struct trailer_info *info)
{
- int i;
+ size_t i;
for (i = 0; i < info->trailer_nr; i++)
free(info->trailers[i]);
free(info->trailers);
@@ -1121,7 +1129,7 @@ static void format_trailer_info(struct strbuf *out,
const struct trailer_info *info,
const struct process_trailer_options *opts)
{
- int i;
+ size_t i;
/* If we want the whole block untouched, we can take the fast path. */
if (!opts->only_trailers && !opts->unfold) {
@@ -1132,7 +1140,7 @@ static void format_trailer_info(struct strbuf *out,
for (i = 0; i < info->trailer_nr; i++) {
char *trailer = info->trailers[i];
- int separator_pos = find_separator(trailer, separators);
+ ssize_t separator_pos = find_separator(trailer, separators);
if (separator_pos >= 1) {
struct strbuf tok = STRBUF_INIT;
@@ -1158,7 +1166,7 @@ void format_trailers_from_commit(struct strbuf *out, const char *msg,
{
struct trailer_info info;
- trailer_info_get(&info, msg);
+ trailer_info_get(&info, msg, opts);
format_trailer_info(out, &info, opts);
trailer_info_release(&info);
}
diff --git a/trailer.h b/trailer.h
index 9c10026c35..b997739649 100644
--- a/trailer.h
+++ b/trailer.h
@@ -71,6 +71,7 @@ struct process_trailer_options {
int only_trailers;
int only_input;
int unfold;
+ int no_divider;
};
#define PROCESS_TRAILER_OPTIONS_INIT {0}
@@ -79,7 +80,8 @@ void process_trailers(const char *file,
const struct process_trailer_options *opts,
struct list_head *new_trailer_head);
-void trailer_info_get(struct trailer_info *info, const char *str);
+void trailer_info_get(struct trailer_info *info, const char *str,
+ const struct process_trailer_options *opts);
void trailer_info_release(struct trailer_info *info);
diff --git a/unpack-trees.c b/unpack-trees.c
index f25089b878..82a83dbc67 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -392,7 +392,7 @@ static int check_updates(struct unpack_trees_options *o)
}
if (to_fetch.nr)
fetch_objects(repository_format_partial_clone,
- &to_fetch);
+ to_fetch.oid, to_fetch.nr);
fetch_if_missing = fetch_if_missing_store;
oid_array_clear(&to_fetch);
}