Merge branch 'sg/split-index-racefix' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 21 Nov 2018 13:57:48 +0000 (22:57 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 21 Nov 2018 13:57:48 +0000 (22:57 +0900)
The codepath to support the experimental split-index mode had
remaining "racily clean" issues fixed.

* sg/split-index-racefix:
split-index: BUG() when cache entry refers to non-existing shared entry
split-index: smudge and add racily clean cache entries to split index
split-index: don't compare cached data of entries already marked for split index
split-index: count the number of deleted entries
t1700-split-index: date back files to avoid racy situations
split-index: add tests to demonstrate the racy split index problem
t1700-split-index: document why FSMONITOR is disabled in this test script

78 files changed:
.mailmap
Documentation/RelNotes/2.14.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.15.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.16.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.17.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.18.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.19.1.txt [new file with mode: 0644]
Documentation/git-interpret-trailers.txt
Documentation/git-update-ref.txt
Documentation/technical/commit-graph.txt
GIT-VERSION-GEN
Makefile
RelNotes
builtin/add.c
builtin/commit-graph.c
builtin/commit.c
builtin/interpret-trailers.c
builtin/remote.c
builtin/replace.c
builtin/submodule--helper.c
builtin/update-ref.c
commit-graph.c
commit-graph.h
commit.c
commit.h
compat/mingw.c
fetch-object.c
fetch-object.h
fetch-pack.c
fetch-pack.h
fsck.c
linear-assignment.c
lockfile.h
pretty.c
ref-filter.c
refs.c
refs.h
refs/iterator.c
refs/refs-internal.h
replace-object.c
replace-object.h
rerere.c
sequencer.c
sequencer.h
sha1-file.c
submodule-config.c
submodule.c
t/helper/test-repository.c
t/helper/test-tool.c
t/helper/test-tool.h
t/helper/test-windows-named-pipe.c [new file with mode: 0644]
t/t0051-windows-named-pipe.sh [new file with mode: 0755]
t/t0090-cache-tree.sh
t/t0410-partial-clone.sh
t/t1400-update-ref.sh
t/t1500-rev-parse.sh
t/t3206-range-diff.sh
t/t3404-rebase-interactive.sh
t/t3405-rebase-malformed.sh
t/t3415-rebase-autosquash.sh
t/t3505-cherry-pick-empty.sh
t/t4200-rerere.sh
t/t4205-log-pretty-formats.sh
t/t5318-commit-graph.sh
t/t5505-remote.sh
t/t6135-pathspec-with-attrs.sh
t/t6300-for-each-ref.sh
t/t7416-submodule-dash-url.sh [new file with mode: 0755]
t/t7417-submodule-path-url.sh [new file with mode: 0755]
t/t7500-commit.sh
t/t7501-commit.sh
t/t7513-interpret-trailers.sh
tempfile.c
tempfile.h
trailer.c
trailer.h
unpack-trees.c
upload-pack.c
index f165222a7821e982174adc8e1c3678275ae80753..bef3352b0d3c6e84a33304b2fe44f6a68f6044a3 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -25,7 +25,7 @@ Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca>
 Benoit Sigoure <tsunanet@gmail.com> <tsuna@lrde.epita.fr>
 Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca>
 Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
-brian m. carlson <sandals@crustytoothpaste.net> Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
+brian m. carlson <sandals@crustytoothpaste.net>
 brian m. carlson <sandals@crustytoothpaste.net> <sandals@crustytoothpaste.ath.cx>
 Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
 Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com>
diff --git a/Documentation/RelNotes/2.14.5.txt b/Documentation/RelNotes/2.14.5.txt
new file mode 100644 (file)
index 0000000..130645f
--- /dev/null
@@ -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 (file)
index 0000000..fd2e6f8
--- /dev/null
@@ -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 (file)
index 0000000..cb8ee02
--- /dev/null
@@ -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 (file)
index 0000000..ef021be
--- /dev/null
@@ -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 (file)
index 0000000..2098cdd
--- /dev/null
@@ -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 (file)
index 0000000..da76726
--- /dev/null
@@ -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.
index b8fafb1e8bdd6c3e061b88b227e1ef264bbfbd07..a5e8b36f62bcf5eeedfb5a04ac852c476d9f43f3 100644 (file)
@@ -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
 -----------------------
 
index bc8fdfd4691326b44e2cbdea3dc4925cf7440515..fda8516677237e9c0a8343f9ca96a1eb969b3492 100644 (file)
@@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
 SYNOPSIS
 --------
 [verse]
-'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
+'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<oldvalue>] | [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
 
 DESCRIPTION
 -----------
index c664acbd765d06f000b2feeef1d7e98f1616ff62..001395e95071ec50d2fef65ead09cf5c49558a6a 100644 (file)
@@ -112,12 +112,24 @@ Design Details
 - The file format includes parameters for the object ID hash function,
   so a future change of hash algorithm does not require a change in format.
 
+- Commit grafts and replace objects can change the shape of the commit
+  history. The latter can also be enabled/disabled on the fly using
+  `--no-replace-objects`. This leads to difficultly storing both possible
+  interpretations of a commit id, especially when computing generation
+  numbers. The commit-graph will not be read or written when
+  replace-objects or grafts are present.
+
+- Shallow clones create grafts of commits by dropping their parents. This
+  leads the commit-graph to think those commits have generation number 1.
+  If and when those commits are made unshallow, those generation numbers
+  become invalid. Since shallow clones are intended to restrict the commit
+  history to a very small set of commits, the commit-graph feature is less
+  helpful for these clones, anyway. The commit-graph will not be read or
+  written when shallow commits are present.
+
 Future Work
 -----------
 
-- The commit graph feature currently does not honor commit grafts. This can
-  be remedied by duplicating or refactoring the current graft logic.
-
 - After computing and storing generation numbers, we must make graph
   walks aware of generation numbers to gain the performance benefits they
   enable. This will mostly be accomplished by swapping a commit-date-ordered
index e9dc8f7a01d4a9a96e55757b0c5b1f369049aefc..164fa4f49967817434d55ef2ff5f8ec851a42965 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.19.0
+DEF_VER=v2.19.1
 
 LF='
 '
index 5a969f5830a4105d3e3e6236eaa51e19880cc873..02a452b1f88314c957b9808996052043b3750953 100644 (file)
--- 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
index 5d139ba7f1e20412d24b1397b33e2d4cde016d19..5c08e40d2b4b4eb5c2b5122dbce75f7b941271fe 120000 (symlink)
--- 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
index 9916498a29bbd8fa7c5c5d8e7bd32e1dc184909b..0b64bcdebe0f4581953adccf8addf1c3fd1eb61e 100644 (file)
@@ -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);
index 0bf0c486575a8557b729658ce05be02d28a1a8b8..da737df321528088d8aa20e3e87e803336b1f550 100644 (file)
@@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)
        return 0;
 }
 
+extern int read_replace_refs;
+
 static int graph_write(int argc, const char **argv)
 {
        struct string_list *pack_indexes = NULL;
@@ -150,6 +152,8 @@ static int graph_write(int argc, const char **argv)
        if (!opts.obj_dir)
                opts.obj_dir = get_object_directory();
 
+       read_replace_refs = 0;
+
        if (opts.reachable) {
                write_commit_graph_reachable(opts.obj_dir, opts.append);
                return 0;
index 0d9828e29ebe89f037e68761d5cd5b407339cd48..83233ca1a54aaf1d25c7084df90d5f1fd4f49a24 100644 (file)
@@ -872,6 +872,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                s->use_color = 0;
                commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
+               string_list_clear(&s->change, 1);
        } else {
                struct object_id oid;
                const char *parent = "HEAD";
index b742539d4de20361bb43bcb4dc33cfc9165c42fb..4b87e0dd2e8854bf940e92e3020c5e4f66e8a6bd 100644 (file)
@@ -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()
index 7876db1c20d317e28e4a84880197c6a690bf9ef6..5fd1012faaeceaa0d8db18c3a15310b0d8570043 100644 (file)
@@ -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();
 
index 4f05791f3e895f78ba511dd6571bd09abab9269c..17868a92dcfccce335d69854f54f0ef5d3a9ca1b 100644 (file)
@@ -39,7 +39,8 @@ struct show_data {
        enum replace_format format;
 };
 
-static int show_reference(const char *refname, const struct object_id *oid,
+static int show_reference(struct repository *r, const char *refname,
+                         const struct object_id *oid,
                          int flag, void *cb_data)
 {
        struct show_data *data = cb_data;
@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,
                        if (get_oid(refname, &object))
                                return error(_("failed to resolve '%s' as a valid ref"), refname);
 
-                       obj_type = oid_object_info(the_repository, &object,
-                                                  NULL);
-                       repl_type = oid_object_info(the_repository, oid, NULL);
+                       obj_type = oid_object_info(r, &object, NULL);
+                       repl_type = oid_object_info(r, oid, NULL);
 
                        printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
                               oid_to_hex(oid), type_name(repl_type));
index f6fb8991f3a81b0b2dd895c1e8550b5bda5ea99a..5e6f2db4cdbcf9e88ddfc1890359f7f2f86b2cd2 100644 (file)
@@ -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);
 
index 4fa3c0a86fd7cbeb5214223e06aa10a2ecfa2d85..2d8f7f05785dca5a083ee6d003d16b4d008dc4d2 100644 (file)
@@ -14,7 +14,8 @@ static const char * const git_update_ref_usage[] = {
 };
 
 static char line_termination = '\n';
-static int update_flags;
+static unsigned int update_flags;
+static unsigned int default_flags;
 static unsigned create_reflog_flag;
 static const char *msg;
 
@@ -205,7 +206,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
                                   msg, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -237,7 +238,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
                                   msg, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -273,7 +274,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
                                   update_flags, msg, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -302,7 +303,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
                                   update_flags, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -357,7 +358,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        const char *refname, *oldval;
        struct object_id oid, oldoid;
        int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
-       unsigned int flags = 0;
        int create_reflog = 0;
        struct option options[] = {
                OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
@@ -378,6 +378,11 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 
        create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0;
 
+       if (no_deref) {
+               default_flags = REF_NO_DEREF;
+               update_flags = default_flags;
+       }
+
        if (read_stdin) {
                struct strbuf err = STRBUF_INIT;
                struct ref_transaction *transaction;
@@ -385,7 +390,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                transaction = ref_transaction_begin(&err);
                if (!transaction)
                        die("%s", err.buf);
-               if (delete || no_deref || argc > 0)
+               if (delete || argc > 0)
                        usage_with_options(git_update_ref_usage, options);
                if (end_null)
                        line_termination = '\0';
@@ -427,8 +432,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                        die("%s: not a valid old SHA1", oldval);
        }
 
-       if (no_deref)
-               flags = REF_NO_DEREF;
        if (delete)
                /*
                 * For purposes of backwards compatibility, we treat
@@ -436,9 +439,9 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                 */
                return delete_ref(msg, refname,
                                  (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
-                                 flags);
+                                 default_flags);
        else
                return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
-                                 flags | create_reflog_flag,
+                                 default_flags | create_reflog_flag,
                                  UPDATE_REFS_DIE_ON_ERR);
 }
index 8a1bec7b8aa420dd3d4ecadc95dee31029533c07..7cfa779dcbdcf9a3e2137cc54c1c9ef62c5f6895 100644 (file)
@@ -13,6 +13,8 @@
 #include "commit-graph.h"
 #include "object-store.h"
 #include "alloc.h"
+#include "hashmap.h"
+#include "replace-object.h"
 
 #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
@@ -56,6 +58,28 @@ static struct commit_graph *alloc_commit_graph(void)
        return g;
 }
 
+extern int read_replace_refs;
+
+static int commit_graph_compatible(struct repository *r)
+{
+       if (!r->gitdir)
+               return 0;
+
+       if (read_replace_refs) {
+               prepare_replace_object(r);
+               if (hashmap_get_size(&r->objects->replace_map->map))
+                       return 0;
+       }
+
+       prepare_commit_graft(r);
+       if (r->parsed_objects && r->parsed_objects->grafts_nr)
+               return 0;
+       if (is_repository_shallow(r))
+               return 0;
+
+       return 1;
+}
+
 struct commit_graph *load_commit_graph_one(const char *graph_file)
 {
        void *graph_map;
@@ -223,6 +247,9 @@ static int prepare_commit_graph(struct repository *r)
                 */
                return 0;
 
+       if (!commit_graph_compatible(r))
+               return 0;
+
        obj_dir = r->objects->objectdir;
        prepare_commit_graph_one(r, obj_dir);
        prepare_alt_odb(r);
@@ -233,10 +260,10 @@ static int prepare_commit_graph(struct repository *r)
        return !!r->objects->commit_graph;
 }
 
-static void close_commit_graph(void)
+void close_commit_graph(struct repository *r)
 {
-       free_commit_graph(the_repository->objects->commit_graph);
-       the_repository->objects->commit_graph = NULL;
+       free_commit_graph(r->objects->commit_graph);
+       r->objects->commit_graph = NULL;
 }
 
 static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -693,6 +720,9 @@ void write_commit_graph(const char *obj_dir,
        int num_extra_edges;
        struct commit_list *parent;
 
+       if (!commit_graph_compatible(the_repository))
+               return;
+
        oids.nr = 0;
        oids.alloc = approximate_object_count() / 4;
 
@@ -845,7 +875,7 @@ void write_commit_graph(const char *obj_dir,
        write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
        write_graph_chunk_large_edges(f, commits.list, commits.nr);
 
-       close_commit_graph();
+       close_commit_graph(the_repository);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
 
index eea62f8c0ee53b56630a1a2b0c2c716b4cd63670..b62d1b08373633afd545f7d8734cbd0d61963bb6 100644 (file)
@@ -60,6 +60,7 @@ void write_commit_graph(const char *obj_dir,
 
 int verify_commit_graph(struct repository *r, struct commit_graph *g);
 
+void close_commit_graph(struct repository *);
 void free_commit_graph(struct commit_graph *);
 
 #endif
index 449c1f4920cff631f5cf2479772c350c9b8b7325..fb4c99cb8908b53c2bbba43ef08de0e2e217533e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)
        return 0;
 }
 
-static void prepare_commit_graft(struct repository *r)
+void prepare_commit_graft(struct repository *r)
 {
        char *graft_file;
 
@@ -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);
 
index da0db36eba2bf16277dbb7aadcb3384e29191ecc..12b8b2d6543c758a6e96c0ba2ee287ef0d048e77 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
 struct commit_graft *read_graft_line(struct strbuf *line);
 int register_commit_graft(struct repository *r, struct commit_graft *, int);
+void prepare_commit_graft(struct repository *r);
 struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
@@ -322,7 +323,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);
index 858ca14a57351062d2bba147c72ed460fb0aa533..18caf21969a5917e8e6c6d754beffbba2741e6c2 100644 (file)
@@ -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;
index 853624f811c59c17af88814ebeecf4154095a19c..42665488008bc0fffafa5d4e0455bc22b510b92a 100644 (file)
@@ -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;
        }
index 4b269d07ed635b3e6b1f5f7b2118d290d27e7e4e..d2f996d4e8c79c7bde78780dad5ad543910fcc29 100644 (file)
@@ -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
index 88a078e9befd281cf5f03e9e64615b14ca768a35..79007f996c899272b6ae9044a9029776181f3438 100644 (file)
@@ -253,8 +253,10 @@ static int find_common(struct fetch_negotiator *negotiator,
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
 
-       mark_tips(negotiator, args->negotiation_tips);
-       for_each_cached_alternate(negotiator, insert_one_alternate_object);
+       if (!args->no_dependents) {
+               mark_tips(negotiator, args->negotiation_tips);
+               for_each_cached_alternate(negotiator, insert_one_alternate_object);
+       }
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -271,8 +273,12 @@ static int find_common(struct fetch_negotiator *negotiator,
                 * We use lookup_object here because we are only
                 * interested in the case we *know* the object is
                 * reachable and we have already scanned it.
+                *
+                * Do this only if args->no_dependents is false (if it is true,
+                * we cannot trust the object flags).
                 */
-               if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+               if (!args->no_dependents &&
+                   ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
                                (o->flags & COMPLETE)) {
                        continue;
                }
@@ -710,31 +716,29 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
 
        oidset_clear(&loose_oid_set);
 
-       if (!args->no_dependents) {
-               if (!args->deepen) {
-                       for_each_ref(mark_complete_oid, NULL);
-                       for_each_cached_alternate(NULL, mark_alternate_complete);
-                       commit_list_sort_by_date(&complete);
-                       if (cutoff)
-                               mark_recent_complete_commits(args, cutoff);
-               }
+       if (!args->deepen) {
+               for_each_ref(mark_complete_oid, NULL);
+               for_each_cached_alternate(NULL, mark_alternate_complete);
+               commit_list_sort_by_date(&complete);
+               if (cutoff)
+                       mark_recent_complete_commits(args, cutoff);
+       }
 
-               /*
-                * Mark all complete remote refs as common refs.
-                * Don't mark them common yet; the server has to be told so first.
-                */
-               for (ref = *refs; ref; ref = ref->next) {
-                       struct object *o = deref_tag(the_repository,
-                                                    lookup_object(the_repository,
-                                                    ref->old_oid.hash),
-                                                    NULL, 0);
+       /*
+        * Mark all complete remote refs as common refs.
+        * Don't mark them common yet; the server has to be told so first.
+        */
+       for (ref = *refs; ref; ref = ref->next) {
+               struct object *o = deref_tag(the_repository,
+                                            lookup_object(the_repository,
+                                            ref->old_oid.hash),
+                                            NULL, 0);
 
-                       if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
-                               continue;
+               if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+                       continue;
 
-                       negotiator->known_common(negotiator,
-                                                (struct commit *)o);
-               }
+               negotiator->known_common(negotiator,
+                                        (struct commit *)o);
        }
 
        save_commit_buffer = old_save_commit_buffer;
@@ -990,11 +994,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (!server_supports("deepen-relative") && args->deepen_relative)
                die(_("Server does not support --deepen"));
 
-       mark_complete_and_common_ref(&negotiator, args, &ref);
-       filter_refs(args, &ref, sought, nr_sought);
-       if (everything_local(args, &ref)) {
-               packet_flush(fd[1]);
-               goto all_done;
+       if (!args->no_dependents) {
+               mark_complete_and_common_ref(&negotiator, args, &ref);
+               filter_refs(args, &ref, sought, nr_sought);
+               if (everything_local(args, &ref)) {
+                       packet_flush(fd[1]);
+                       goto all_done;
+               }
+       } else {
+               filter_refs(args, &ref, sought, nr_sought);
        }
        if (find_common(&negotiator, args, fd, &oid, ref) < 0)
                if (!args->keep_pack)
@@ -1040,7 +1048,7 @@ static void add_shallow_requests(struct strbuf *req_buf,
        }
 }
 
-static void add_wants(const struct ref *wants, struct strbuf *req_buf)
+static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
 {
        int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0);
 
@@ -1057,8 +1065,12 @@ static void add_wants(const struct ref *wants, struct strbuf *req_buf)
                 * We use lookup_object here because we are only
                 * interested in the case we *know* the object is
                 * reachable and we have already scanned it.
+                *
+                * Do this only if args->no_dependents is false (if it is true,
+                * we cannot trust the object flags).
                 */
-               if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+               if (!no_dependents &&
+                   ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
                    (o->flags & COMPLETE)) {
                        continue;
                }
@@ -1155,7 +1167,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
        }
 
        /* add wants */
-       add_wants(wants, &req_buf);
+       add_wants(args->no_dependents, wants, &req_buf);
 
        if (args->no_dependents) {
                packet_buf_write(&req_buf, "done");
@@ -1346,16 +1358,21 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                args->deepen = 1;
 
                        /* Filter 'ref' by 'sought' and those that aren't local */
-                       mark_complete_and_common_ref(&negotiator, args, &ref);
-                       filter_refs(args, &ref, sought, nr_sought);
-                       if (everything_local(args, &ref))
-                               state = FETCH_DONE;
-                       else
+                       if (!args->no_dependents) {
+                               mark_complete_and_common_ref(&negotiator, args, &ref);
+                               filter_refs(args, &ref, sought, nr_sought);
+                               if (everything_local(args, &ref))
+                                       state = FETCH_DONE;
+                               else
+                                       state = FETCH_SEND_REQUEST;
+
+                               mark_tips(&negotiator, args->negotiation_tips);
+                               for_each_cached_alternate(&negotiator,
+                                                         insert_one_alternate_object);
+                       } else {
+                               filter_refs(args, &ref, sought, nr_sought);
                                state = FETCH_SEND_REQUEST;
-
-                       mark_tips(&negotiator, args->negotiation_tips);
-                       for_each_cached_alternate(&negotiator,
-                                                 insert_one_alternate_object);
+                       }
                        break;
                case FETCH_SEND_REQUEST:
                        if (send_fetch_request(&negotiator, fd[1], args, ref,
@@ -1598,6 +1615,20 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        if (nr_sought)
                nr_sought = remove_duplicates_in_refs(sought, nr_sought);
 
+       if (args->no_dependents && !args->filter_options.choice) {
+               /*
+                * The protocol does not support requesting that only the
+                * wanted objects be sent, so approximate this by setting a
+                * "blob:none" filter if no filter is already set. This works
+                * for all object types: note that wanted blobs will still be
+                * sent because they are directly specified as a "want".
+                *
+                * NEEDSWORK: Add an option in the protocol to request that
+                * only the wanted objects be sent, and implement it.
+                */
+               parse_list_objects_filter(&args->filter_options, "blob:none");
+       }
+
        if (!ref) {
                packet_flush(fd[1]);
                die(_("no matching remote head"));
index 5b6e868802b53ca5fa59eb864199274ac538c242..43ec344d95b40c701b2fc703b4b2b1e3ff72a2d2 100644 (file)
@@ -43,6 +43,13 @@ struct fetch_pack_args {
        unsigned from_promisor:1;
 
        /*
+        * Attempt to fetch only the wanted objects, and not any objects
+        * referred to by them. Due to protocol limitations, extraneous
+        * objects may still be included. (When fetching non-blob
+        * objects, only blobs are excluded; when fetching a blob, the
+        * blob itself will still be sent. The client does not need to
+        * know whether a wanted object is a blob or not.)
+        *
         * If 1, fetch_pack() will also not modify any object flags.
         * This allows fetch_pack() to safely be called by any function,
         * regardless of which object flags it uses (if any).
diff --git a/fsck.c b/fsck.c
index a0cee0be590020e4ff4b42da86c322ba4d0010ae..0f56977d6aa6499c06e31eddb8ed4afe484fcdc4 100644 (file)
--- 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;
index 9b3e56e283ccb98c57ca8a7b85dcf48b05b15a4f..ecffc09be6ec5c1a9646ac152a899f3415f6321d 100644 (file)
@@ -19,6 +19,12 @@ void compute_assignment(int column_count, int row_count, int *cost,
        int *free_row, free_count = 0, saved_free_count, *pred, *col;
        int i, j, phase;
 
+       if (column_count < 2) {
+               memset(column2row, 0, sizeof(int) * column_count);
+               memset(row2column, 0, sizeof(int) * row_count);
+               return;
+       }
+
        memset(column2row, -1, sizeof(int) * column_count);
        memset(row2column, -1, sizeof(int) * row_count);
        ALLOC_ARRAY(v, column_count);
index f401c979f0ed907b40bfb509ab8f06ad2632c19e..35403ccc0d586c4732411a5813cb97761dddc435 100644 (file)
@@ -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.
  */
index 98cf5228f9e30fe5622bdfb4c54d996cb3153808..8ca29e92815608437ccb1fb9909e78d5b4989529 100644 (file)
--- 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 (;;) {
index 0bccfceff2ae31200019838d9f2b67e13e32ef6f..3e8ee04d09b2271ea9b0a9a07de6ec932ad09387 100644 (file)
@@ -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/refs.c b/refs.c
index de81c7be7ca8d3ca033b34a61f33b0bff069932f..9a318c8456c3fe8d157cf6c7c6aca5b635d75230 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1394,17 +1394,50 @@ struct ref_iterator *refs_ref_iterator_begin(
  * non-zero value, stop the iteration and return that value;
  * otherwise, return 0.
  */
+static int do_for_each_repo_ref(struct repository *r, const char *prefix,
+                               each_repo_ref_fn fn, int trim, int flags,
+                               void *cb_data)
+{
+       struct ref_iterator *iter;
+       struct ref_store *refs = get_main_ref_store(r);
+
+       if (!refs)
+               return 0;
+
+       iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+
+       return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
+}
+
+struct do_for_each_ref_help {
+       each_ref_fn *fn;
+       void *cb_data;
+};
+
+static int do_for_each_ref_helper(struct repository *r,
+                                 const char *refname,
+                                 const struct object_id *oid,
+                                 int flags,
+                                 void *cb_data)
+{
+       struct do_for_each_ref_help *hp = cb_data;
+
+       return hp->fn(refname, oid, flags, hp->cb_data);
+}
+
 static int do_for_each_ref(struct ref_store *refs, const char *prefix,
                           each_ref_fn fn, int trim, int flags, void *cb_data)
 {
        struct ref_iterator *iter;
+       struct do_for_each_ref_help hp = { fn, cb_data };
 
        if (!refs)
                return 0;
 
        iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
 
-       return do_for_each_ref_iterator(iter, fn, cb_data);
+       return do_for_each_repo_ref_iterator(the_repository, iter,
+                                       do_for_each_ref_helper, &hp);
 }
 
 int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -1449,12 +1482,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
        return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
 }
 
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(get_main_ref_store(r),
-                              git_replace_ref_base, fn,
-                              strlen(git_replace_ref_base),
-                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+       return do_for_each_repo_ref(r, git_replace_ref_base, fn,
+                                   strlen(git_replace_ref_base),
+                                   DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -2033,10 +2065,12 @@ int refs_verify_refname_available(struct ref_store *refs,
 int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        struct ref_iterator *iter;
+       struct do_for_each_ref_help hp = { fn, cb_data };
 
        iter = refs->be->reflog_iterator_begin(refs);
 
-       return do_for_each_ref_iterator(iter, fn, cb_data);
+       return do_for_each_repo_ref_iterator(the_repository, iter,
+                                            do_for_each_ref_helper, &hp);
 }
 
 int for_each_reflog(each_ref_fn fn, void *cb_data)
diff --git a/refs.h b/refs.h
index bd52c1bbae3a68fe8ca8f9e6cae7cc54bdbf9852..6cc0397679fd55bfdfc72b7a7f4cbf3ee5d028a0 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -276,6 +276,16 @@ struct ref_transaction;
 typedef int each_ref_fn(const char *refname,
                        const struct object_id *oid, int flags, void *cb_data);
 
+/*
+ * The same as each_ref_fn, but also with a repository argument that
+ * contains the repository associated with the callback.
+ */
+typedef int each_repo_ref_fn(struct repository *r,
+                            const char *refname,
+                            const struct object_id *oid,
+                            int flags,
+                            void *cb_data);
+
 /*
  * The following functions invoke the specified callback function for
  * each reference indicated.  If the function ever returns a nonzero
@@ -309,7 +319,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
 int for_each_tag_ref(each_ref_fn fn, void *cb_data);
 int for_each_branch_ref(each_ref_fn fn, void *cb_data);
 int for_each_remote_ref(each_ref_fn fn, void *cb_data);
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data);
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
 int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                         const char *prefix, void *cb_data);
index 2ac91ac3401c87108b9cdb4983acb7c165df82d9..629e00a122a7a867ae3350ce35469c2ceb1259a1 100644 (file)
@@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 
 struct ref_iterator *current_ref_iter = NULL;
 
-int do_for_each_ref_iterator(struct ref_iterator *iter,
-                            each_ref_fn fn, void *cb_data)
+int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
+                                 each_repo_ref_fn fn, void *cb_data)
 {
        int retval = 0, ok;
        struct ref_iterator *old_ref_iter = current_ref_iter;
 
        current_ref_iter = iter;
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
-               retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
+               retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
                if (retval) {
                        /*
                         * If ref_iterator_abort() returns ITER_ERROR,
index 04425d6d1e45e2401454e7b89b0ed4b3959926de..89d7dd49cdce945d1bfe7277082d3d444b9d7e85 100644 (file)
@@ -474,8 +474,9 @@ extern struct ref_iterator *current_ref_iter;
  * adapter between the callback style of reference iteration and the
  * iterator style.
  */
-int do_for_each_ref_iterator(struct ref_iterator *iter,
-                            each_ref_fn fn, void *cb_data);
+int do_for_each_repo_ref_iterator(struct repository *r,
+                                 struct ref_iterator *iter,
+                                 each_repo_ref_fn fn, void *cb_data);
 
 /*
  * Only include per-worktree refs in a do_for_each_ref*() iteration.
index 4ec77ce41848311a912256046bd2bf8dc9ee63c0..e295e87943102c2a1ad903aea1a634d34bf603a0 100644 (file)
@@ -6,7 +6,8 @@
 #include "repository.h"
 #include "commit.h"
 
-static int register_replace_ref(const char *refname,
+static int register_replace_ref(struct repository *r,
+                               const char *refname,
                                const struct object_id *oid,
                                int flag, void *cb_data)
 {
@@ -25,13 +26,13 @@ static int register_replace_ref(const char *refname,
        oidcpy(&repl_obj->replacement, oid);
 
        /* Register new object */
-       if (oidmap_put(the_repository->objects->replace_map, repl_obj))
+       if (oidmap_put(r->objects->replace_map, repl_obj))
                die(_("duplicate replace ref: %s"), refname);
 
        return 0;
 }
 
-static void prepare_replace_object(struct repository *r)
+void prepare_replace_object(struct repository *r)
 {
        if (r->objects->replace_map)
                return;
index 9345e105ddcaa38cf9e856df70d960245a3b696c..16528df942f79e3d7ce65d99e0103395d0c69f51 100644 (file)
@@ -10,6 +10,8 @@ struct replace_object {
        struct object_id replacement;
 };
 
+void prepare_replace_object(struct repository *r);
+
 /*
  * This internal function is only declared here for the benefit of
  * lookup_replace_object().  Please do not call it directly.
index c7787aa07f80f00589e3c4f4e4e3cc825c43044e..783d4dae2aa2d7a17c37dfac892e1acdeb4b1db8 100644 (file)
--- 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 */
index dc2c58d464c14be033f4bcba2ab4332886ead327..f388405b4e0d2c260f74777323a738ac845195e0 100644 (file)
@@ -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;
index c751c9d6e4f78e7d9e2700dcc3fb3157961fb049..c986bc825161f1f4702a0cd435c6d9705e3be2df 100644 (file)
@@ -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);
index 97b74238483e00c3f07bd5ab0879eb84bf5c8dfa..2edf4564f6c12f33c59a101470cd2964774fcc34 100644 (file)
@@ -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;
                }
index fc2c41b947cb471deef42323c83f8b28f42780d6..0b2f1647abb935ba63e9230baf0ba7fcef95d19c 100644 (file)
@@ -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");
index a2b266fbfae2cd89b00a11008fbcd28bf09777e2..f27f14e86817607f7d11e376431d1b2864939aab 100644 (file)
@@ -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;
        }
 
@@ -1880,7 +1879,7 @@ const char *get_superproject_working_tree(void)
                 * We're only interested in the name after the tab.
                 */
                super_sub = strchr(sb.buf, '\t') + 1;
-               super_sub_len = sb.buf + sb.len - super_sub - 1;
+               super_sub_len = strlen(super_sub);
 
                if (super_sub_len > cwd_len ||
                    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
index 2762ca656262baa9494d96a2647248f4042a2e01..6a84a53efbf6919c83d3f1fd73786acd92ee7abf 100644 (file)
@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
        struct commit *c;
        struct commit_list *parent;
 
-       repo_init(&r, gitdir, worktree);
+       setup_git_env(gitdir);
+
+       if (repo_init(&r, gitdir, worktree))
+               die("Couldn't init repo");
 
        c = lookup_commit(&r, commit_oid);
 
@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
        struct commit *c;
        struct tree *tree;
 
-       repo_init(&r, gitdir, worktree);
+       setup_git_env(gitdir);
+
+       if (repo_init(&r, gitdir, worktree))
+               die("Couldn't init repo");
 
        c = lookup_commit(&r, commit_oid);
 
index 0edafcfd65db7586bc1521d2e1afa99fbde50292..db19131f719607e2beb29b1858822e5af7209189 100644 (file)
@@ -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 },
 };
 
index e954e8c5222f77e882f577198091adde95cf2532..491400108608d4623704f30db0e4a8b5f3061dfe 100644 (file)
@@ -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 (file)
index 0000000..b4b752b
--- /dev/null
@@ -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 (executable)
index 0000000..10ac92d
--- /dev/null
@@ -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
index 7de40141ca84dc53657d81c0254870b387339f79..94fcb4a78e661ae793eef54ae98b4821218b1e29 100755 (executable)
@@ -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 &&
index 128130066499feb5bdad705b6fb0ef03bf446fe9..7762f89b3d4ab316539afe73067c8613536b4812 100755 (executable)
@@ -170,6 +170,59 @@ 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 'fetching of missing blobs works' '
+       rm -rf server repo &&
+       test_create_repo server &&
+       test_commit -C server foo &&
+       git -C server repack -a -d --write-bitmap-index &&
+
+       git clone "file://$(pwd)/server" repo &&
+       git hash-object repo/foo.t >blobhash &&
+       rm -rf repo/.git/objects/* &&
+
+       git -C server config uploadpack.allowanysha1inwant 1 &&
+       git -C server config uploadpack.allowfilter 1 &&
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "origin" &&
+
+       git -C repo cat-file -p $(cat blobhash)
+'
+
+test_expect_success 'fetching of missing trees does not fetch blobs' '
+       rm -rf server repo &&
+       test_create_repo server &&
+       test_commit -C server foo &&
+       git -C server repack -a -d --write-bitmap-index &&
+
+       git clone "file://$(pwd)/server" repo &&
+       git -C repo rev-parse foo^{tree} >treehash &&
+       git hash-object repo/foo.t >blobhash &&
+       rm -rf repo/.git/objects/* &&
+
+       git -C server config uploadpack.allowanysha1inwant 1 &&
+       git -C server config uploadpack.allowfilter 1 &&
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "origin" &&
+       git -C repo cat-file -p $(cat treehash) &&
+
+       # Ensure that the tree, but not the blob, is fetched
+       git -C repo rev-list --objects --missing=print $(cat treehash) >objects &&
+       grep "^$(cat treehash)" objects &&
+       grep "^[?]$(cat blobhash)" objects
+'
+
 test_expect_success 'rev-list stops traversal at missing and promised commit' '
        rm -rf repo &&
        test_create_repo repo &&
index 7c8df2095574f66f6d434f7717228f6f76a2a908..02493f14ba2071bb79d520c1d9c262ccf63c5207 100755 (executable)
@@ -807,6 +807,37 @@ test_expect_success 'stdin delete symref works option no-deref' '
        test_cmp expect actual
 '
 
+test_expect_success 'stdin update symref works flag --no-deref' '
+       git symbolic-ref TESTSYMREFONE $b &&
+       git symbolic-ref TESTSYMREFTWO $b &&
+       cat >stdin <<-EOF &&
+       update TESTSYMREFONE $a $b
+       update TESTSYMREFTWO $a $b
+       EOF
+       git update-ref --no-deref --stdin <stdin &&
+       git rev-parse TESTSYMREFONE TESTSYMREFTWO >expect &&
+       git rev-parse $a $a >actual &&
+       test_cmp expect actual &&
+       git rev-parse $m~1 >expect &&
+       git rev-parse $b >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'stdin delete symref works flag --no-deref' '
+       git symbolic-ref TESTSYMREFONE $b &&
+       git symbolic-ref TESTSYMREFTWO $b &&
+       cat >stdin <<-EOF &&
+       delete TESTSYMREFONE $b
+       delete TESTSYMREFTWO $b
+       EOF
+       git update-ref --no-deref --stdin <stdin &&
+       test_must_fail git rev-parse --verify -q TESTSYMREFONE &&
+       test_must_fail git rev-parse --verify -q TESTSYMREFTWO &&
+       git rev-parse $m~1 >expect &&
+       git rev-parse $b >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'stdin delete ref works with right old value' '
        echo "delete $b $m~1" >stdin &&
        git update-ref --stdin <stdin &&
index 5c715fe2cf6c7afe2c39e6a7197cdd3d33329b8b..01abee533dedfd1e2d8bd347d06fc5c0c8b7833a 100755 (executable)
@@ -141,6 +141,22 @@ test_expect_success 'showing the superproject correctly' '
        test_commit -C sub test_commit &&
        git -C super submodule add ../sub dir/sub &&
        echo $(pwd)/super >expect  &&
+       git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
+       test_cmp expect out &&
+
+       test_commit -C super submodule_add &&
+       git -C super checkout -b branch1 &&
+       git -C super/dir/sub checkout -b branch1 &&
+       test_commit -C super/dir/sub branch1_commit &&
+       git -C super add dir/sub &&
+       test_commit -C super branch1_commit &&
+       git -C super checkout -b branch2 master &&
+       git -C super/dir/sub checkout -b branch2 master &&
+       test_commit -C super/dir/sub branch2_commit &&
+       git -C super add dir/sub &&
+       test_commit -C super branch2_commit &&
+       test_must_fail git -C super merge branch1 &&
+
        git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
        test_cmp expect out
 '
index 2237c7f4af9464cbee087696ec3a86aeaa86e033..fb4c13a84ade63470f4dabc976ff5f5369c17e1e 100755 (executable)
@@ -142,4 +142,9 @@ test_expect_success 'changed message' '
        test_cmp expected actual
 '
 
+test_expect_success 'no commits on one side' '
+       git commit --amend -m "new message" &&
+       git range-diff master HEAD@{1} HEAD
+'
+
 test_done
index 86bba5ed7c23f78065521a9042244ea978a3a80f..ff89b6341a6fc19801959058e35039778bc6577b 100755 (executable)
@@ -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"' '
index da94dddc86d78830664d3a4b910ab157c2db2f0f..860e63e444d296c64773bae0d743fbb80fe674f1 100755 (executable)
@@ -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^
 '
 
index e364c12622f8566c3c99370de25ec5659a1e1546..13f5688135d853106702b7679eb302f6ae3d1450 100755 (executable)
@@ -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
index fbdc47cfbdae6e3cec7d6762faa4a8e13554d803..5f911bb5290f33126380c45f3f5874d03f78a677 100755 (executable)
@@ -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' '
index 65da74c76683c0ccd109fdec63766ab6bdfb08f0..313222d0d6251f33734b12758745999a79fe6bf2 100755 (executable)
@@ -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
index 2052cadb1109d3644b0c3f82b707f8e8bb35f945..978a8a66ff05055ad1967b5cc25f36880230a5e4 100755 (executable)
@@ -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
index 0c500f7ca2641a2752f5d4819bb11efcf5f588bf..2799969f3e2a15fc636ed042c96d8a846e01ebe8 100755 (executable)
@@ -259,6 +259,66 @@ test_expect_success 'check that gc computes commit-graph' '
        test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
 '
 
+test_expect_success 'replace-objects invalidates commit-graph' '
+       cd "$TRASH_DIRECTORY" &&
+       test_when_finished rm -rf replace &&
+       git clone full replace &&
+       (
+               cd replace &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph &&
+               git replace HEAD~1 HEAD~2 &&
+               git -c core.commitGraph=false log >expect &&
+               git -c core.commitGraph=true log >actual &&
+               test_cmp expect actual &&
+               git commit-graph write --reachable &&
+               git -c core.commitGraph=false --no-replace-objects log >expect &&
+               git -c core.commitGraph=true --no-replace-objects log >actual &&
+               test_cmp expect actual &&
+               rm -rf .git/objects/info/commit-graph &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph
+       )
+'
+
+test_expect_success 'commit grafts invalidate commit-graph' '
+       cd "$TRASH_DIRECTORY" &&
+       test_when_finished rm -rf graft &&
+       git clone full graft &&
+       (
+               cd graft &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph &&
+               H1=$(git rev-parse --verify HEAD~1) &&
+               H3=$(git rev-parse --verify HEAD~3) &&
+               echo "$H1 $H3" >.git/info/grafts &&
+               git -c core.commitGraph=false log >expect &&
+               git -c core.commitGraph=true log >actual &&
+               test_cmp expect actual &&
+               git commit-graph write --reachable &&
+               git -c core.commitGraph=false --no-replace-objects log >expect &&
+               git -c core.commitGraph=true --no-replace-objects log >actual &&
+               test_cmp expect actual &&
+               rm -rf .git/objects/info/commit-graph &&
+               git commit-graph write --reachable &&
+               test_path_is_missing .git/objects/info/commit-graph
+       )
+'
+
+test_expect_success 'replace-objects invalidates commit-graph' '
+       cd "$TRASH_DIRECTORY" &&
+       test_when_finished rm -rf shallow &&
+       git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
+       (
+               cd shallow &&
+               git commit-graph write --reachable &&
+               test_path_is_missing .git/objects/info/commit-graph &&
+               git fetch origin --unshallow &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph
+       )
+'
+
 # the verify tests below expect the commit-graph to contain
 # exactly the commits reachable from the commits/8 branch.
 # If the file changes the set of commits in the list, then the
index 241e6a319df4cefa612b6744c79e72e5cf929462..d2a2cdd453396b1dba5525f32b6325ac8a10d95d 100755 (executable)
@@ -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
        )
index 77b8cef66198a870f669441857207af7142c0fbb..e436a7396241e6aad71a5f62500ffe0b9d7c0b7e 100755 (executable)
@@ -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' '
index 024f8c06f7c58a424204d6fca69021ace2de3cbf..97bfbee6e8d69e46bd1ef1c94dae32d64be977b2 100755 (executable)
@@ -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 (executable)
index 0000000..1cd2c1c
--- /dev/null
@@ -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 (executable)
index 0000000..756af8c
--- /dev/null
@@ -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
index 170b4810e052cc8092cea4ead4f3c098707f0f38..31ab608b6700829427b5aeafa65222469e494d26 100755 (executable)
@@ -359,4 +359,27 @@ test_expect_success 'new line found before status message in commit template' '
        test_i18ncmp expected-template editor-input
 '
 
+test_expect_success 'setup empty commit with unstaged rename and copy' '
+       test_create_repo unstaged_rename_and_copy &&
+       (
+               cd unstaged_rename_and_copy &&
+
+               echo content >orig &&
+               git add orig &&
+               test_commit orig &&
+
+               cp orig new_copy &&
+               mv orig new_rename &&
+               git add -N new_copy new_rename
+       )
+'
+
+test_expect_success 'check commit with unstaged rename and copy' '
+       (
+               cd unstaged_rename_and_copy &&
+
+               test_must_fail git -c diff.renames=copy commit
+       )
+'
+
 test_done
index 4cae92804d11f75f24bdd6f6517a88c834e7cfc7..1a6773ee6889939a0046664bd8a7cdcc4f21fe01 100755 (executable)
@@ -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 &&
index 164719d1c9d3e76a08bbbeb968aaf535370db7d1..c44186133147838d7f17c4d42f8cb96a5df73b28 100755 (executable)
@@ -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
index 139ecd97f8eb88b597aab50c2eb2b171a11ef3ef..d43ad8c1912d977183270fabe7ac76c5bbb7a1a3 100644 (file)
@@ -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;
 }
 
index 36434eb6fa64721bcafdb40db5f9e7d0400a50b4..61d8dc4d1bb2fc446ac7b452661064e615b4b5c9 100644 (file)
@@ -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.
  */
index 4e309460d1367a7b35b61c2391525a165a3645e3..0796f326b36bac334cbf7ca5162cb22c4b62d1e8 100644 (file)
--- 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 @@ static int find_trailer_start(const char *buf, size_t len)
 }
 
 /* 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);
 }
index 9c10026c358326ce0c0098ca52341ce8160c5bbe..b997739649a37e8791e8448c2e7daefe8023bb71 100644 (file)
--- 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);
 
index f25089b878a8b0842a9d6407cb6b1821867a737c..82a83dbc67ba8410e8745c0a508bf9d0dd7b6027 100644 (file)
@@ -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);
        }
index 82b393ec31917c0c2bcd904668a60b5aaef00633..2ae9d9bb475214070529e22cc3f186afebbc6c7c 100644 (file)
@@ -24,6 +24,7 @@
 #include "quote.h"
 #include "upload-pack.h"
 #include "serve.h"
+#include "commit-graph.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE      (1u << 11)
@@ -740,6 +741,7 @@ static void deepen_by_rev_list(int ac, const char **av,
 {
        struct commit_list *result;
 
+       close_commit_graph(the_repository);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
        send_shallow(result);
        free_commit_list(result);