diff options
132 files changed, 1869 insertions, 418 deletions
diff --git a/.gitattributes b/.gitattributes index 1bdc91e282..49b3051641 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,7 @@ /command-list.txt eol=lf /GIT-VERSION-GEN eol=lf /mergetools/* eol=lf +/Documentation/git-merge.txt conflict-marker-size=32 +/Documentation/gitk.txt conflict-marker-size=32 +/Documentation/user-manual.txt conflict-marker-size=32 +/t/t????-*.sh conflict-marker-size=32 @@ -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/.travis.yml b/.travis.yml index 4d4e26c9df..8d2499739e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: c -sudo: false - cache: directories: - $HOME/travis-cache diff --git a/Documentation/Makefile b/Documentation/Makefile index a42dcfc745..95f6a321f2 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -344,7 +344,7 @@ $(OBSOLETE_HTML): %.html : %.txto asciidoc.conf mv $@+ $@ manpage-base-url.xsl: manpage-base-url.xsl.in - sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@ + $(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@ %.1 %.5 %.7 : %.xml manpage-base-url.xsl $(QUIET_XMLTO)$(RM) $@ && \ diff --git a/Documentation/RelNotes/2.14.5.txt b/Documentation/RelNotes/2.14.5.txt new file mode 100644 index 0000000000..130645fb29 --- /dev/null +++ b/Documentation/RelNotes/2.14.5.txt @@ -0,0 +1,16 @@ +Git v2.14.5 Release Notes +========================= + +This release is to address the recently reported CVE-2018-17456. + +Fixes since v2.14.4 +------------------- + + * Submodules' "URL"s come from the untrusted .gitmodules file, but + we blindly gave it to "git clone" to clone submodules when "git + clone --recurse-submodules" was used to clone a project that has + such a submodule. The code has been hardened to reject such + malformed URLs (e.g. one that begins with a dash). + +Credit for finding and fixing this vulnerability goes to joernchen +and Jeff King, respectively. diff --git a/Documentation/RelNotes/2.15.3.txt b/Documentation/RelNotes/2.15.3.txt new file mode 100644 index 0000000000..fd2e6f8df7 --- /dev/null +++ b/Documentation/RelNotes/2.15.3.txt @@ -0,0 +1,6 @@ +Git v2.15.3 Release Notes +========================= + +This release merges up the fixes that appear in v2.14.5 to address +the recently reported CVE-2018-17456; see the release notes for that +version for details. diff --git a/Documentation/RelNotes/2.16.5.txt b/Documentation/RelNotes/2.16.5.txt new file mode 100644 index 0000000000..cb8ee02a9a --- /dev/null +++ b/Documentation/RelNotes/2.16.5.txt @@ -0,0 +1,6 @@ +Git v2.16.5 Release Notes +========================= + +This release merges up the fixes that appear in v2.14.5 to address +the recently reported CVE-2018-17456; see the release notes for that +version for details. diff --git a/Documentation/RelNotes/2.17.2.txt b/Documentation/RelNotes/2.17.2.txt new file mode 100644 index 0000000000..ef021be870 --- /dev/null +++ b/Documentation/RelNotes/2.17.2.txt @@ -0,0 +1,12 @@ +Git v2.17.2 Release Notes +========================= + +This release merges up the fixes that appear in v2.14.5 to address +the recently reported CVE-2018-17456; see the release notes for that +version for details. + +In addition, this release also teaches "fsck" and the server side +logic to reject pushes to repositories that attempt to create such a +problematic ".gitmodules" file as tracked contents, to help hosting +sites protect their customers by preventing malicious contents from +spreading. diff --git a/Documentation/RelNotes/2.18.1.txt b/Documentation/RelNotes/2.18.1.txt new file mode 100644 index 0000000000..2098cdd776 --- /dev/null +++ b/Documentation/RelNotes/2.18.1.txt @@ -0,0 +1,6 @@ +Git v2.18.1 Release Notes +========================= + +This release merges up the fixes that appear in v2.14.5 and in +v2.17.2 to address the recently reported CVE-2018-17456; see the +release notes for those versions for details. diff --git a/Documentation/RelNotes/2.19.1.txt b/Documentation/RelNotes/2.19.1.txt new file mode 100644 index 0000000000..da7672674e --- /dev/null +++ b/Documentation/RelNotes/2.19.1.txt @@ -0,0 +1,6 @@ +Git v2.19.1 Release Notes +========================= + +This release merges up the fixes that appear in v2.14.5 and in +v2.17.2 to address the recently reported CVE-2018-17456; see the +release notes for those versions for details. diff --git a/Documentation/config.txt b/Documentation/config.txt index eb66a11975..ee3b5dd8ef 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2828,6 +2828,8 @@ protocol.version:: * `1` - the original wire protocol with the addition of a version string in the initial response from the server. +* `2` - link:technical/protocol-v2.html[wire protocol version 2]. + -- pull.ff:: diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt index ea70653369..a595a0ffee 100644 --- a/Documentation/git-archimport.txt +++ b/Documentation/git-archimport.txt @@ -3,7 +3,7 @@ git-archimport(1) NAME ---- -git-archimport - Import an Arch repository into Git +git-archimport - Import a GNU Arch repository into Git SYNOPSIS @@ -14,7 +14,8 @@ SYNOPSIS DESCRIPTION ----------- -Imports a project from one or more Arch repositories. It will follow branches +Imports a project from one or more GNU Arch repositories. +It will follow branches and repositories within the namespaces defined by the <archive/branch> parameters supplied. If it cannot find the remote branch a merge comes from it will just import it as a regular commit. If it can find it, it will mark it diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt index 03d18465d4..763afabb6d 100644 --- a/Documentation/git-column.txt +++ b/Documentation/git-column.txt @@ -13,7 +13,10 @@ SYNOPSIS DESCRIPTION ----------- -This command formats its input into multiple columns. +This command formats the lines of its standard input into a table with +multiple columns. Each input line occupies one cell of the table. It +is used internally by other git commands to format output into +columns. OPTIONS ------- @@ -23,7 +26,7 @@ OPTIONS --mode=<mode>:: Specify layout mode. See configuration variable column.ui for option - syntax. + syntax in linkgit:git-config[1]. --raw-mode=<n>:: Same as --mode but take mode encoded as a number. This is mainly used @@ -43,6 +46,34 @@ OPTIONS --padding=<N>:: The number of spaces between columns. One space by default. +EXAMPLES +------ + +Format data by columns: +------------ +$ seq 1 24 | git column --mode=column --padding=5 +1 4 7 10 13 16 19 22 +2 5 8 11 14 17 20 23 +3 6 9 12 15 18 21 24 +------------ + +Format data by rows: +------------ +$ seq 1 21 | git column --mode=row --padding=5 +1 2 3 4 5 6 7 +8 9 10 11 12 13 14 +15 16 17 18 19 20 21 +------------ + +List some tags in a table with unequal column widths: +------------ +$ git tag --list 'v2.4.*' --column=row,dense +v2.4.0 v2.4.0-rc0 v2.4.0-rc1 v2.4.0-rc2 v2.4.0-rc3 +v2.4.1 v2.4.10 v2.4.11 v2.4.12 v2.4.2 +v2.4.3 v2.4.4 v2.4.5 v2.4.6 v2.4.7 +v2.4.8 v2.4.9 +------------ + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index e027fb8c4b..ccdc5f83d6 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -18,7 +18,9 @@ The command finds the most recent tag that is reachable from a commit. If the tag points to the commit, then only the tag is shown. Otherwise, it suffixes the tag name with the number of additional commits on top of the tagged object and the -abbreviated object name of the most recent commit. +abbreviated object name of the most recent commit. The result +is a "human-readable" object name which can also be used to +identify the commit to other git commands. By default (without --all or --tags) `git describe` only shows annotated tags. For more information about creating annotated tags diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt index b8fafb1e8b..a5e8b36f62 100644 --- a/Documentation/git-interpret-trailers.txt +++ b/Documentation/git-interpret-trailers.txt @@ -56,8 +56,9 @@ least one Git-generated or user-configured trailer and consists of at least 25% trailers. The group must be preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the message or be the last -non-whitespace lines before a line that starts with '---'. Such three -minus signs start the patch part of the message. +non-whitespace lines before a line that starts with '---' (followed by a +space or the end of the line). Such three minus signs start the patch +part of the message. See also `--no-divider` below. When reading trailers, there can be whitespaces after the token, the separator and the value. There can also be whitespaces @@ -125,6 +126,11 @@ OPTIONS A convenience alias for `--only-trailers --only-input --unfold`. +--no-divider:: + Do not treat `---` as the end of the commit message. Use this + when you know your input contains just the commit message itself + (and not an email or the output of `git format-patch`). + CONFIGURATION VARIABLES ----------------------- diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index bc8fdfd469..fda8516677 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -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 ----------- diff --git a/Documentation/git.txt b/Documentation/git.txt index dba7f0c18e..74a9d7edb4 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -599,8 +599,8 @@ trace messages into this file descriptor. + Alternatively, if the variable is set to an absolute path (starting with a '/' character), Git will interpret this -as a file path and will try to write the trace messages -into it. +as a file path and will try to append the trace messages +to it. + Unsetting the variable, or setting it to empty, "0" or "false" (case insensitive) disables trace messages. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 7b273635de..21978ebbac 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -756,7 +756,6 @@ Unexpected missing objects will raise an error. + The form '--missing=print' is like 'allow-any', but will also print a list of the missing objects. Object IDs are prefixed with a ``?'' character. -endif::git-rev-list[] --exclude-promisor-objects:: (For internal use only.) Prefilter object traversal at @@ -764,6 +763,7 @@ endif::git-rev-list[] stronger than `--missing=allow-promisor` because it limits the traversal, rather than just silencing errors about missing objects. +endif::git-rev-list[] --no-walk[=(sorted|unsorted)]:: Only show the given commits, but do not traverse their ancestors. diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt index c664acbd76..001395e950 100644 --- a/Documentation/technical/commit-graph.txt +++ b/Documentation/technical/commit-graph.txt @@ -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 diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index e9dc8f7a01..164fa4f499 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.19.0 +DEF_VER=v2.19.1 LF=' ' @@ -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 @@ -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 @@ -110,7 +110,8 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate, static struct attr_check *check; if (!check) check = attr_check_initl("export-ignore", "export-subst", NULL); - return git_check_attr(istate, path, check) ? NULL : check; + git_check_attr(istate, path, check); + return check; } static int check_attr_export_ignore(const struct attr_check *check) @@ -1143,9 +1143,9 @@ static void collect_some_attrs(const struct index_state *istate, fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem); } -int git_check_attr(const struct index_state *istate, - const char *path, - struct attr_check *check) +void git_check_attr(const struct index_state *istate, + const char *path, + struct attr_check *check) { int i; @@ -1158,8 +1158,6 @@ int git_check_attr(const struct index_state *istate, value = ATTR__UNSET; check->items[i].value = value; } - - return 0; } void git_all_attrs(const struct index_state *istate, @@ -63,8 +63,8 @@ void attr_check_free(struct attr_check *check); */ const char *git_attr_name(const struct git_attr *); -int git_check_attr(const struct index_state *istate, - const char *path, struct attr_check *check); +void git_check_attr(const struct index_state *istate, + const char *path, struct attr_check *check); /* * Retrieve all attributes that apply to the specified path. diff --git a/builtin/add.c b/builtin/add.c index 9916498a29..0b64bcdebe 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -454,7 +454,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ - parse_pathspec(&pathspec, 0, + parse_pathspec(&pathspec, PATHSPEC_ATTR, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 64ec1745ab..0520cecc9a 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -50,6 +50,13 @@ static int filter_object(const char *path, unsigned mode, return 0; } +static int stream_blob(const struct object_id *oid) +{ + if (stream_blob_to_fd(1, oid, NULL, 0)) + die("unable to stream %s to stdout", oid_to_hex(oid)); + return 0; +} + static int cat_one_file(int opt, const char *exp_type, const char *obj_name, int unknown_type) { @@ -131,7 +138,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, } if (type == OBJ_BLOB) - return stream_blob_to_fd(1, &oid, NULL, 0); + return stream_blob(&oid); buf = read_object_file(&oid, &type, &size); if (!buf) die("Cannot read object %s", obj_name); @@ -154,7 +161,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, oidcpy(&blob_oid, &oid); if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) - return stream_blob_to_fd(1, &blob_oid, NULL, 0); + return stream_blob(&blob_oid); /* * we attempted to dereference a tag to a blob * and failed; there may be new dereference @@ -317,8 +324,9 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d BUG("invalid cmdmode: %c", opt->cmdmode); batch_write(opt, contents, size); free(contents); - } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0) - die("unable to stream %s to stdout", oid_to_hex(oid)); + } else { + stream_blob(oid); + } } else { enum object_type type; diff --git a/builtin/check-attr.c b/builtin/check-attr.c index c05573ff9c..30a2f84274 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -65,8 +65,7 @@ static void check_attr(const char *prefix, if (collect_all) { git_all_attrs(&the_index, full_path, check); } else { - if (git_check_attr(&the_index, full_path, check)) - die("git_check_attr died"); + git_check_attr(&the_index, full_path, check); } output_attr(check, file); diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 0bf0c48657..da737df321 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -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; diff --git a/builtin/commit.c b/builtin/commit.c index 0d9828e29e..83233ca1a5 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -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"; diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index b742539d4d..4b87e0dd2e 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -104,6 +104,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")), { OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse }, + OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")), OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"), N_("trailer(s) to add"), option_parse_trailer), OPT_END() diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index d1144a8f7e..2eba0a695b 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -951,8 +951,7 @@ static int no_try_delta(const char *path) if (!check) check = attr_check_initl("delta", NULL); - if (git_check_attr(&the_index, path, check)) - return 0; + git_check_attr(&the_index, path, check); if (ATTR_FALSE(check->items[0].value)) return 1; return 0; @@ -2299,7 +2298,6 @@ static void init_threaded_search(void) pthread_mutex_init(&cache_mutex, NULL); pthread_mutex_init(&progress_mutex, NULL); pthread_cond_init(&progress_cond, NULL); - pthread_mutex_init(&to_pack.lock, NULL); old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads); } @@ -2991,6 +2989,7 @@ static void get_object_list(int ac, const char **av) init_revisions(&revs, NULL); save_commit_buffer = 0; + revs.allow_exclude_promisor_objects_opt = 1; setup_revisions(ac, av, &revs, NULL); /* make sure shallows are read */ diff --git a/builtin/prune.c b/builtin/prune.c index 4916a4daa2..d356ad70f6 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -120,6 +120,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; read_replace_refs = 0; ref_paranoia = 1; + revs.allow_exclude_promisor_objects_opt = 1; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); @@ -161,7 +162,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) free(s); if (is_repository_shallow(the_repository)) - prune_shallow(show_only); + prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0); return 0; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c17ce94e12..95eb7002ee 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -465,7 +465,7 @@ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) unsigned char sha1[GIT_SHA1_RAWSZ]; strbuf_addf(&buf, "%s:%"PRItime, path, stamp); - hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));; + hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed)); strbuf_release(&buf); /* RFC 2104 5. HMAC-SHA1-80 */ @@ -1025,6 +1025,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) const char *ret; struct object_id *old_oid = &cmd->old_oid; struct object_id *new_oid = &cmd->new_oid; + int do_update_worktree = 0; /* only refs/... are allowed */ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { @@ -1050,9 +1051,8 @@ static const char *update(struct command *cmd, struct shallow_info *si) refuse_unconfigured_deny(); return "branch is currently checked out"; case DENY_UPDATE_INSTEAD: - ret = update_worktree(new_oid->hash); - if (ret) - return ret; + /* pass -- let other checks intervene first */ + do_update_worktree = 1; break; } } @@ -1117,6 +1117,12 @@ static const char *update(struct command *cmd, struct shallow_info *si) return "hook declined"; } + if (do_update_worktree) { + ret = update_worktree(new_oid->hash); + if (ret) + return ret; + } + if (is_null_oid(new_oid)) { struct strbuf err = STRBUF_INIT; if (!parse_object(the_repository, old_oid)) { @@ -1833,7 +1839,7 @@ static void prepare_shallow_update(struct command *commands, /* * keep hooks happy by forcing a temporary shallow file via * env variable because we can't add --shallow-file to every - * command. check_everything_connected() will be done with + * command. check_connected() will be done with * true .git/shallow though. */ setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1); diff --git a/builtin/remote.c b/builtin/remote.c index 7876db1c20..5fd1012faa 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -625,7 +625,7 @@ static int mv(int argc, const char **argv) oldremote = remote_get(rename.old_name); if (!remote_is_configured(oldremote, 1)) - die(_("No such remote: %s"), rename.old_name); + die(_("No such remote: '%s'"), rename.old_name); if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG) return migrate_file(oldremote); @@ -761,7 +761,7 @@ static int rm(int argc, const char **argv) remote = remote_get(argv[1]); if (!remote_is_configured(remote, 1)) - die(_("No such remote: %s"), argv[1]); + die(_("No such remote: '%s'"), argv[1]); known_remotes.to_delete = remote; for_each_remote(add_known_remote, &known_remotes); @@ -860,7 +860,7 @@ static int get_remote_ref_states(const char *name, states->remote = remote_get(name); if (!states->remote) - return error(_("No such remote: %s"), name); + return error(_("No such remote: '%s'"), name); read_branches(); diff --git a/builtin/repack.c b/builtin/repack.c index d5886039cc..3c30e16ec5 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -531,6 +531,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (!po_args.quiet && isatty(2)) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); + + if (!keep_unreachable && + (!(pack_everything & LOOSEN_UNREACHABLE) || + unpack_unreachable) && + is_repository_shallow(the_repository)) + prune_shallow(PRUNE_QUICK); } if (!no_update_server_info) diff --git a/builtin/replace.c b/builtin/replace.c index 4f05791f3e..17868a92dc 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -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)); diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 5b07f3f4a2..41dd33c6a7 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -372,6 +372,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = DEFAULT_ABBREV; + revs.allow_exclude_promisor_objects_opt = 1; revs.commit_format = CMIT_FMT_UNSPECIFIED; /* diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f6fb8991f3..5e6f2db4cd 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1233,6 +1233,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url if (gitdir && *gitdir) argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL); + argv_array_push(&cp.args, "--"); argv_array_push(&cp.args, url); argv_array_push(&cp.args, path); diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 4fa3c0a86f..2d8f7f0578 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -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); } @@ -781,6 +781,8 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *, #define CE_MATCH_REFRESH 0x10 /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */ #define CE_MATCH_IGNORE_FSMONITOR 0X20 +extern int is_racy_timestamp(const struct index_state *istate, + const struct cache_entry *ce); extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int); diff --git a/commit-graph.c b/commit-graph.c index 8a1bec7b8a..7cfa779dcb 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -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); diff --git a/commit-graph.h b/commit-graph.h index eea62f8c0e..b62d1b0837 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -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 @@ -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); @@ -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); @@ -255,7 +256,9 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info, uint32_t **used, int *ref_status); extern int delayed_reachability_test(struct shallow_info *si, int c); -extern void prune_shallow(int show_only); +#define PRUNE_SHOW_ONLY 1 +#define PRUNE_QUICK 2 +extern void prune_shallow(unsigned options); extern struct trace_key trace_shallow; int is_descendant_of(struct commit *, struct commit_list *); @@ -322,7 +325,7 @@ extern const char *find_commit_header(const char *msg, const char *key, size_t *out_len); /* Find the end of the log message, the right place for a new trailer. */ -extern int ignore_non_trailer(const char *buf, size_t len); +extern size_t ignore_non_trailer(const char *buf, size_t len); typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra, void *cb_data); diff --git a/compat/mingw.c b/compat/mingw.c index 858ca14a57..18caf21969 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -341,6 +341,19 @@ int mingw_mkdir(const char *path, int mode) return ret; } +/* + * Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA + * is documented in [1] as opening a writable file handle in append mode. + * (It is believed that) this is atomic since it is maintained by the + * kernel unlike the O_APPEND flag which is racily maintained by the CRT. + * + * [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants + * + * This trick does not appear to work for named pipes. Instead it creates + * a named pipe client handle that cannot be written to. Callers should + * just use the regular _wopen() for them. (And since client handle gets + * bound to a unique server handle, it isn't really an issue.) + */ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...) { HANDLE handle; @@ -360,10 +373,12 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...) NULL, create, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) return errno = err_win_to_posix(GetLastError()), -1; + /* * No O_APPEND here, because the CRT uses it only to reset the - * file pointer to EOF on write(); but that is not necessary - * for a file created with FILE_APPEND_DATA. + * file pointer to EOF before each write(); but that is not + * necessary (and may lead to races) for a file created with + * FILE_APPEND_DATA. */ fd = _open_osfhandle((intptr_t)handle, O_BINARY); if (fd < 0) @@ -371,6 +386,21 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...) return fd; } +/* + * Does the pathname map to the local named pipe filesystem? + * That is, does it have a "//./pipe/" prefix? + */ +static int is_local_named_pipe_path(const char *filename) +{ + return (is_dir_sep(filename[0]) && + is_dir_sep(filename[1]) && + filename[2] == '.' && + is_dir_sep(filename[3]) && + !strncasecmp(filename+4, "pipe", 4) && + is_dir_sep(filename[8]) && + filename[9]); +} + int mingw_open (const char *filename, int oflags, ...) { typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...); @@ -387,7 +417,7 @@ int mingw_open (const char *filename, int oflags, ...) if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; - if (oflags & O_APPEND) + if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename)) open_fn = mingw_open_append; else open_fn = _wopen; diff --git a/config.mak.dev b/config.mak.dev index 9a998149d9..92d268137f 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -7,6 +7,7 @@ CFLAGS += -pedantic CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0 endif CFLAGS += -Wdeclaration-after-statement +CFLAGS += -Wformat-security CFLAGS += -Wno-format-zero-length CFLAGS += -Wold-style-definition CFLAGS += -Woverflow @@ -1297,6 +1297,7 @@ static void convert_attrs(const struct index_state *istate, struct conv_attrs *ca, const char *path) { static struct attr_check *check; + struct attr_check_item *ccheck = NULL; if (!check) { check = attr_check_initl("crlf", "ident", "filter", @@ -1306,30 +1307,25 @@ static void convert_attrs(const struct index_state *istate, git_config(read_convert_config, NULL); } - if (!git_check_attr(istate, path, check)) { - struct attr_check_item *ccheck = check->items; - ca->crlf_action = git_path_check_crlf(ccheck + 4); - if (ca->crlf_action == CRLF_UNDEFINED) - ca->crlf_action = git_path_check_crlf(ccheck + 0); - ca->ident = git_path_check_ident(ccheck + 1); - ca->drv = git_path_check_convert(ccheck + 2); - if (ca->crlf_action != CRLF_BINARY) { - enum eol eol_attr = git_path_check_eol(ccheck + 3); - if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_LF) - ca->crlf_action = CRLF_AUTO_INPUT; - else if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_CRLF) - ca->crlf_action = CRLF_AUTO_CRLF; - else if (eol_attr == EOL_LF) - ca->crlf_action = CRLF_TEXT_INPUT; - else if (eol_attr == EOL_CRLF) - ca->crlf_action = CRLF_TEXT_CRLF; - } - ca->working_tree_encoding = git_path_check_encoding(ccheck + 5); - } else { - ca->drv = NULL; - ca->crlf_action = CRLF_UNDEFINED; - ca->ident = 0; + git_check_attr(istate, path, check); + ccheck = check->items; + ca->crlf_action = git_path_check_crlf(ccheck + 4); + if (ca->crlf_action == CRLF_UNDEFINED) + ca->crlf_action = git_path_check_crlf(ccheck + 0); + ca->ident = git_path_check_ident(ccheck + 1); + ca->drv = git_path_check_convert(ccheck + 2); + if (ca->crlf_action != CRLF_BINARY) { + enum eol eol_attr = git_path_check_eol(ccheck + 3); + if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_LF) + ca->crlf_action = CRLF_AUTO_INPUT; + else if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_CRLF) + ca->crlf_action = CRLF_AUTO_CRLF; + else if (eol_attr == EOL_LF) + ca->crlf_action = CRLF_TEXT_INPUT; + else if (eol_attr == EOL_CRLF) + ca->crlf_action = CRLF_TEXT_CRLF; } + ca->working_tree_encoding = git_path_check_encoding(ccheck + 5); /* Save attr and make a decision for action */ ca->attr_action = ca->crlf_action; @@ -4256,12 +4256,12 @@ static void diff_fill_oid_info(struct diff_filespec *one) static void strip_prefix(int prefix_length, const char **namep, const char **otherp) { /* Strip the prefix but do not molest /dev/null and absolute paths */ - if (*namep && **namep != '/') { + if (*namep && !is_absolute_path(*namep)) { *namep += prefix_length; if (**namep == '/') ++*namep; } - if (*otherp && **otherp != '/') { + if (*otherp && !is_absolute_path(*otherp)) { *otherp += prefix_length; if (**otherp == '/') ++*otherp; diff --git a/fetch-object.c b/fetch-object.c index 853624f811..4266548800 100644 --- a/fetch-object.c +++ b/fetch-object.c @@ -23,21 +23,16 @@ static void fetch_refs(const char *remote_name, struct ref *ref) fetch_if_missing = original_fetch_if_missing; } -void fetch_object(const char *remote_name, const unsigned char *sha1) -{ - struct ref *ref = alloc_ref(sha1_to_hex(sha1)); - hashcpy(ref->old_oid.hash, sha1); - fetch_refs(remote_name, ref); -} - -void fetch_objects(const char *remote_name, const struct oid_array *to_fetch) +void fetch_objects(const char *remote_name, const struct object_id *oids, + int oid_nr) { struct ref *ref = NULL; int i; - for (i = 0; i < to_fetch->nr; i++) { - struct ref *new_ref = alloc_ref(oid_to_hex(&to_fetch->oid[i])); - oidcpy(&new_ref->old_oid, &to_fetch->oid[i]); + for (i = 0; i < oid_nr; i++) { + struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i])); + oidcpy(&new_ref->old_oid, &oids[i]); + new_ref->exact_oid = 1; new_ref->next = ref; ref = new_ref; } diff --git a/fetch-object.h b/fetch-object.h index 4b269d07ed..d2f996d4e8 100644 --- a/fetch-object.h +++ b/fetch-object.h @@ -1,11 +1,7 @@ #ifndef FETCH_OBJECT_H #define FETCH_OBJECT_H -#include "sha1-array.h" - -extern void fetch_object(const char *remote_name, const unsigned char *sha1); - -extern void fetch_objects(const char *remote_name, - const struct oid_array *to_fetch); +void fetch_objects(const char *remote_name, const struct object_id *oids, + int oid_nr); #endif diff --git a/fetch-pack.c b/fetch-pack.c index 88a078e9be..79007f996c 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -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")); diff --git a/fetch-pack.h b/fetch-pack.h index 5b6e868802..43ec344d95 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -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). @@ -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; @@ -842,27 +842,55 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) } /* - * Draw an octopus merge and return the number of characters written. + * Draw the horizontal dashes of an octopus merge and return the number of + * characters written. */ static int graph_draw_octopus_merge(struct git_graph *graph, struct strbuf *sb) { /* - * Here dashless_commits represents the number of parents - * which don't need to have dashes (because their edges fit - * neatly under the commit). - */ - const int dashless_commits = 2; - int col_num, i; - int num_dashes = - ((graph->num_parents - dashless_commits) * 2) - 1; - for (i = 0; i < num_dashes; i++) { - col_num = (i / 2) + dashless_commits + graph->commit_index; - strbuf_write_column(sb, &graph->new_columns[col_num], '-'); + * Here dashless_parents represents the number of parents which don't + * need to have dashes (the edges labeled "0" and "1"). And + * dashful_parents are the remaining ones. + * + * | *---. + * | |\ \ \ + * | | | | | + * x 0 1 2 3 + * + */ + const int dashless_parents = 2; + int dashful_parents = graph->num_parents - dashless_parents; + + /* + * Usually, we add one new column for each parent (like the diagram + * above) but sometimes the first parent goes into an existing column, + * like this: + * + * | *---. + * | |\ \ \ + * |/ / / / + * x 0 1 2 + * + * In which case the number of parents will be one greater than the + * number of added columns. + */ + int added_cols = (graph->num_new_columns - graph->num_columns); + int parent_in_old_cols = graph->num_parents - added_cols; + + /* + * In both cases, commit_index corresponds to the edge labeled "0". + */ + int first_col = graph->commit_index + dashless_parents + - parent_in_old_cols; + + int i; + for (i = 0; i < dashful_parents; i++) { + strbuf_write_column(sb, &graph->new_columns[i+first_col], '-'); + strbuf_write_column(sb, &graph->new_columns[i+first_col], + i == dashful_parents-1 ? '.' : '-'); } - col_num = (i / 2) + dashless_commits + graph->commit_index; - strbuf_write_column(sb, &graph->new_columns[col_num], '.'); - return num_dashes + 1; + return 2 * dashful_parents; } static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) diff --git a/linear-assignment.c b/linear-assignment.c index 9b3e56e283..ecffc09be6 100644 --- a/linear-assignment.c +++ b/linear-assignment.c @@ -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); diff --git a/ll-merge.c b/ll-merge.c index 0e2800f7bb..1936fee9e1 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -371,13 +371,12 @@ int ll_merge(mmbuffer_t *result_buf, if (!check) check = attr_check_initl("merge", "conflict-marker-size", NULL); - if (!git_check_attr(&the_index, path, check)) { - ll_driver_name = check->items[0].value; - if (check->items[1].value) { - marker_size = atoi(check->items[1].value); - if (marker_size <= 0) - marker_size = DEFAULT_CONFLICT_MARKER_SIZE; - } + git_check_attr(&the_index, path, check); + ll_driver_name = check->items[0].value; + if (check->items[1].value) { + marker_size = atoi(check->items[1].value); + if (marker_size <= 0) + marker_size = DEFAULT_CONFLICT_MARKER_SIZE; } driver = find_ll_merge_driver(ll_driver_name); @@ -398,7 +397,8 @@ int ll_merge_marker_size(const char *path) if (!check) check = attr_check_initl("conflict-marker-size", NULL); - if (!git_check_attr(&the_index, path, check) && check->items[0].value) { + git_check_attr(&the_index, path, check); + if (check->items[0].value) { marker_size = atoi(check->items[0].value); if (marker_size <= 0) marker_size = DEFAULT_CONFLICT_MARKER_SIZE; diff --git a/lockfile.h b/lockfile.h index f401c979f0..35403ccc0d 100644 --- a/lockfile.h +++ b/lockfile.h @@ -263,8 +263,8 @@ static inline int close_lock_file_gently(struct lock_file *lk) * nobody else) to inspect the contents you wrote, while still * holding the lock yourself. * - * * `reopen_lock_file()` to reopen the lockfile. Make further updates - * to the contents. + * * `reopen_lock_file()` to reopen the lockfile, truncating the existing + * contents. Write out the new contents. * * * `commit_lock_file()` to make the final version permanent. */ diff --git a/merge-recursive.c b/merge-recursive.c index e5243dbc54..1b5c255918 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -2239,7 +2239,7 @@ static struct dir_rename_entry *check_dir_renamed(const char *path, { char *temp = xstrdup(path); char *end; - struct dir_rename_entry *entry = NULL;; + struct dir_rename_entry *entry = NULL; while ((end = strrchr(temp, '/'))) { *end = '\0'; diff --git a/pack-objects.c b/pack-objects.c index 6ef87e5683..f73f609884 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -148,6 +148,9 @@ void prepare_packing_data(struct packing_data *pdata) 1U << OE_SIZE_BITS); pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE", 1UL << OE_DELTA_SIZE_BITS); +#ifndef NO_PTHREADS + pthread_mutex_init(&pdata->lock, NULL); +#endif } struct object_entry *packlist_alloc(struct packing_data *pdata, diff --git a/pack-objects.h b/pack-objects.h index 62806ccc39..c34036166a 100644 --- a/pack-objects.h +++ b/pack-objects.h @@ -356,7 +356,7 @@ static inline unsigned long oe_delta_size(struct packing_data *pack, return e->delta_size_; /* - * pack->detla_size[] can't be NULL because oe_set_delta_size() + * pack->delta_size[] can't be NULL because oe_set_delta_size() * must have been called when a new delta is saved with * oe_set_delta(). * If oe_delta() returns NULL (i.e. default state, which means @@ -1304,6 +1304,9 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ if (skip_prefix(placeholder, "(trailers", &arg)) { struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; + + opts.no_divider = 1; + if (*arg == ':') { arg++; for (;;) { diff --git a/read-cache.c b/read-cache.c index 7b1354d759..8f644f68b4 100644 --- a/read-cache.c +++ b/read-cache.c @@ -337,7 +337,7 @@ static int is_racy_stat(const struct index_state *istate, ); } -static int is_racy_timestamp(const struct index_state *istate, +int is_racy_timestamp(const struct index_state *istate, const struct cache_entry *ce) { return (!S_ISGITLINK(ce->ce_mode) && diff --git a/ref-filter.c b/ref-filter.c index 0bccfceff2..3e8ee04d09 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -263,6 +263,8 @@ static int trailers_atom_parser(const struct ref_format *format, struct used_ato struct string_list params = STRING_LIST_INIT_DUP; int i; + atom->u.contents.trailer_opts.no_divider = 1; + if (arg) { string_list_split(¶ms, arg, ',', -1); for (i = 0; i < params.nr; i++) { @@ -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 @@ cleanup: 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) @@ -277,6 +277,16 @@ 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 * value, stop the iteration and return that value. Please note that @@ -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); diff --git a/refs/iterator.c b/refs/iterator.c index 2ac91ac340..629e00a122 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -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, diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 04425d6d1e..b425ae40b1 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -282,7 +282,7 @@ int refs_rename_ref_available(struct ref_store *refs, * * // Access information about the current reference: * if (!(iter->flags & REF_ISSYMREF)) - * printf("%s is %s\n", iter->refname, oid_to_hex(&iter->oid)); + * printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid)); * * // If you need to peel the reference: * ref_iterator_peel(iter, &oid); @@ -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. diff --git a/remote-curl.c b/remote-curl.c index fb28309e85..762a55a75f 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -178,7 +178,7 @@ static int set_option(const char *name, const char *value) options.no_dependents = 1; return 0; } else if (!strcmp(name, "filter")) { - options.filter = xstrdup(value);; + options.filter = xstrdup(value); return 0; } else { return 1 /* unsupported */; diff --git a/replace-object.c b/replace-object.c index 4ec77ce418..e295e87943 100644 --- a/replace-object.c +++ b/replace-object.c @@ -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; diff --git a/replace-object.h b/replace-object.h index 9345e105dd..16528df942 100644 --- a/replace-object.h +++ b/replace-object.h @@ -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. @@ -533,7 +533,7 @@ static int check_one_conflict(int i, int *type) } *type = PUNTED; - while (ce_stage(active_cache[i]) == 1) + while (i < active_nr && ce_stage(active_cache[i]) == 1) i++; /* Only handle regular files with both stages #2 and #3 */ diff --git a/revision.c b/revision.c index de4dce600d..e411802ebe 100644 --- a/revision.c +++ b/revision.c @@ -2133,7 +2133,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->limited = 1; } else if (!strcmp(arg, "--ignore-missing")) { revs->ignore_missing = 1; - } else if (!strcmp(arg, "--exclude-promisor-objects")) { + } else if (revs->allow_exclude_promisor_objects_opt && + !strcmp(arg, "--exclude-promisor-objects")) { if (fetch_if_missing) BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0"); revs->exclude_promisor_objects = 1; diff --git a/revision.h b/revision.h index 007278cc11..701a4973dd 100644 --- a/revision.h +++ b/revision.h @@ -127,6 +127,7 @@ struct rev_info { tree_blobs_in_commit_order:1, /* for internal use only */ + allow_exclude_promisor_objects_opt:1, exclude_promisor_objects:1; /* Diff flags */ diff --git a/run-command.c b/run-command.c index 84b883c213..d679cc267c 100644 --- a/run-command.c +++ b/run-command.c @@ -380,7 +380,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr) set_error_routine(old_errfn); } -static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) +static int prepare_cmd(struct argv_array *out, const struct child_process *cmd) { if (!cmd->argv[0]) BUG("command is empty"); @@ -403,16 +403,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) /* * If there are no '/' characters in the command then perform a path * lookup and use the resolved path as the command to exec. If there - * are no '/' characters or if the command wasn't found in the path, - * have exec attempt to invoke the command directly. + * are '/' characters, we have exec attempt to invoke the command + * directly. */ if (!strchr(out->argv[1], '/')) { char *program = locate_in_PATH(out->argv[1]); if (program) { free((char *)out->argv[1]); out->argv[1] = program; + } else { + argv_array_clear(out); + errno = ENOENT; + return -1; } } + + return 0; } static char **prep_childenv(const char *const *deltaenv) @@ -719,6 +725,12 @@ fail_pipe: struct child_err cerr; struct atfork_state as; + if (prepare_cmd(&argv, cmd) < 0) { + failed_errno = errno; + cmd->pid = -1; + goto end_of_spawn; + } + if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; @@ -729,7 +741,6 @@ fail_pipe: set_cloexec(null_fd); } - prepare_cmd(&argv, cmd); childenv = prep_childenv(cmd->env); atfork_prepare(&as); @@ -857,6 +868,8 @@ fail_pipe: argv_array_clear(&argv); free(childenv); } +end_of_spawn: + #else { int fhin = 0, fhout = 1, fherr = 2; diff --git a/sequencer.c b/sequencer.c index dc2c58d464..f388405b4e 100644 --- a/sequencer.c +++ b/sequencer.c @@ -225,13 +225,16 @@ static const char *get_todo_path(const struct replay_opts *opts) * Returns 3 when sob exists within conforming footer as last entry */ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob, - int ignore_footer) + size_t ignore_footer) { + struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; struct trailer_info info; - int i; + size_t i; int found_sob = 0, found_sob_last = 0; - trailer_info_get(&info, sb->buf); + opts.no_divider = 1; + + trailer_info_get(&info, sb->buf, &opts); if (info.trailer_start == info.trailer_end) return 0; @@ -899,7 +902,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, if ((flags & ALLOW_EMPTY)) argv_array_push(&cmd.args, "--allow-empty"); - if (opts->allow_empty_message) + if (!(flags & EDIT_MSG)) argv_array_push(&cmd.args, "--allow-empty-message"); if (cmd.err == -1) { @@ -1313,7 +1316,7 @@ static int try_to_commit(struct strbuf *msg, const char *author, if (cleanup != COMMIT_MSG_CLEANUP_NONE) strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL); - if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) { + if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) { res = 1; /* run 'git commit' to display error message */ goto out; } @@ -3607,9 +3610,20 @@ static int commit_staged_changes(struct replay_opts *opts, * the commit message and if there was a squash, let the user * edit it. */ - if (is_clean && !oidcmp(&head, &to_amend) && - opts->current_fixup_count > 0 && - file_exists(rebase_path_stopped_sha())) { + if (!is_clean || !opts->current_fixup_count) + ; /* this is not the final fixup */ + else if (oidcmp(&head, &to_amend) || + !file_exists(rebase_path_stopped_sha())) { + /* was a final fixup or squash done manually? */ + if (!is_fixup(peek_command(todo_list, 0))) { + unlink(rebase_path_fixup_msg()); + unlink(rebase_path_squash_msg()); + unlink(rebase_path_current_fixups()); + strbuf_reset(&opts->current_fixups); + opts->current_fixup_count = 0; + } + } else { + /* we are in a fixup/squash chain */ const char *p = opts->current_fixups.buf; int len = opts->current_fixups.len; @@ -3828,7 +3842,7 @@ int sequencer_pick_revisions(struct replay_opts *opts) return res; } -void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag) +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag) { unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP; struct strbuf sob = STRBUF_INIT; diff --git a/sequencer.h b/sequencer.h index c751c9d6e4..c986bc8251 100644 --- a/sequencer.h +++ b/sequencer.h @@ -90,7 +90,14 @@ int rearrange_squash(void); extern const char sign_off_header[]; -void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag); +/* + * Append a signoff to the commit message in "msgbuf". The ignore_footer + * parameter specifies the number of bytes at the end of msgbuf that should + * not be considered at all. I.e., they are not checked for existing trailers, + * and the new signoff will be spliced into the buffer before those bytes. + */ +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag); + void append_conflicts_hint(struct strbuf *msgbuf); int message_is_empty(const struct strbuf *sb, enum commit_msg_cleanup_mode cleanup_mode); diff --git a/sha1-file.c b/sha1-file.c index 97b7423848..03b86aec71 100644 --- a/sha1-file.c +++ b/sha1-file.c @@ -1317,7 +1317,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid, * TODO Pass a repository struct through fetch_object, * such that arbitrary repositories work. */ - fetch_object(repository_format_partial_clone, real->hash); + fetch_objects(repository_format_partial_clone, real, 1); already_retried = 1; continue; } @@ -2191,7 +2191,8 @@ static int check_stream_sha1(git_zstream *stream, * see the comment in unpack_sha1_rest for details. */ while (total_read <= size && - (status == Z_OK || status == Z_BUF_ERROR)) { + (status == Z_OK || + (status == Z_BUF_ERROR && !stream->avail_out))) { stream->next_out = buf; stream->avail_out = sizeof(buf); if (size - total_read < stream->avail_out) @@ -246,6 +246,7 @@ static void check_shallow_file_for_update(struct repository *r) #define SEEN_ONLY 1 #define VERBOSE 2 +#define QUICK 4 struct write_shallow_data { struct strbuf *out; @@ -260,7 +261,10 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data) const char *hex = oid_to_hex(&graft->oid); if (graft->nr_parent != -1) return 0; - if (data->flags & SEEN_ONLY) { + if (data->flags & QUICK) { + if (!has_object_file(&graft->oid)) + return 0; + } else if (data->flags & SEEN_ONLY) { struct commit *c = lookup_commit(the_repository, &graft->oid); if (!c || !(c->object.flags & SEEN)) { if (data->flags & VERBOSE) @@ -370,16 +374,23 @@ void advertise_shallow_grafts(int fd) /* * mark_reachable_objects() should have been run prior to this and all - * reachable commits marked as "SEEN". + * reachable commits marked as "SEEN", except when quick_prune is non-zero, + * in which case lines are excised from the shallow file if they refer to + * commits that do not exist (any longer). */ -void prune_shallow(int show_only) +void prune_shallow(unsigned options) { struct lock_file shallow_lock = LOCK_INIT; struct strbuf sb = STRBUF_INIT; + unsigned flags = SEEN_ONLY; int fd; - if (show_only) { - write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY | VERBOSE); + if (options & PRUNE_QUICK) + flags |= QUICK; + + if (options & PRUNE_SHOW_ONLY) { + flags |= VERBOSE; + write_shallow_commits_1(&sb, 0, NULL, flags); strbuf_release(&sb); return; } @@ -387,7 +398,7 @@ void prune_shallow(int show_only) git_path_shallow(the_repository), LOCK_DIE_ON_ERROR); check_shallow_file_for_update(the_repository); - if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) { + if (write_shallow_commits_1(&sb, 0, NULL, flags)) { if (write_in_full(fd, sb.buf, sb.len) < 0) die_errno("failed to write to %s", get_lock_file_path(&shallow_lock)); diff --git a/split-index.c b/split-index.c index 84f067e10d..5820412dc5 100644 --- a/split-index.c +++ b/split-index.c @@ -111,7 +111,7 @@ static void mark_entry_for_delete(size_t pos, void *data) die("position for delete %d exceeds base index size %d", (int)pos, istate->cache_nr); istate->cache[pos]->ce_flags |= CE_REMOVE; - istate->split_index->nr_deletions = 1; + istate->split_index->nr_deletions++; } static void replace_entry(size_t pos, void *data) @@ -188,6 +188,30 @@ void merge_base_index(struct index_state *istate) si->saved_cache_nr = 0; } +/* + * Compare most of the fields in two cache entries, i.e. all except the + * hashmap_entry and the name. + */ +static int compare_ce_content(struct cache_entry *a, struct cache_entry *b) +{ + const unsigned int ondisk_flags = CE_STAGEMASK | CE_VALID | + CE_EXTENDED_FLAGS; + unsigned int ce_flags = a->ce_flags; + unsigned int base_flags = b->ce_flags; + int ret; + + /* only on-disk flags matter */ + a->ce_flags &= ondisk_flags; + b->ce_flags &= ondisk_flags; + ret = memcmp(&a->ce_stat_data, &b->ce_stat_data, + offsetof(struct cache_entry, name) - + offsetof(struct cache_entry, ce_stat_data)); + a->ce_flags = ce_flags; + b->ce_flags = base_flags; + + return ret; +} + void prepare_to_write_split_index(struct index_state *istate) { struct split_index *si = init_split_index(istate); @@ -207,38 +231,109 @@ void prepare_to_write_split_index(struct index_state *istate) */ for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *base; - /* namelen is checked separately */ - const unsigned int ondisk_flags = - CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS; - unsigned int ce_flags, base_flags, ret; ce = istate->cache[i]; - if (!ce->index) + if (!ce->index) { + /* + * During simple update index operations this + * is a cache entry that is not present in + * the shared index. It will be added to the + * split index. + * + * However, it might also represent a file + * that already has a cache entry in the + * shared index, but a new index has just + * been constructed by unpack_trees(), and + * this entry now refers to different content + * than what was recorded in the original + * index, e.g. during 'read-tree -m HEAD^' or + * 'checkout HEAD^'. In this case the + * original entry in the shared index will be + * marked as deleted, and this entry will be + * added to the split index. + */ continue; + } if (ce->index > si->base->cache_nr) { - ce->index = 0; - continue; + BUG("ce refers to a shared ce at %d, which is beyond the shared index size %d", + ce->index, si->base->cache_nr); } ce->ce_flags |= CE_MATCHED; /* or "shared" */ base = si->base->cache[ce->index - 1]; - if (ce == base) + if (ce == base) { + /* The entry is present in the shared index. */ + if (ce->ce_flags & CE_UPDATE_IN_BASE) { + /* + * Already marked for inclusion in + * the split index, either because + * the corresponding file was + * modified and the cached stat data + * was refreshed, or because there + * is already a replacement entry in + * the split index. + * Nothing more to do here. + */ + } else if (!ce_uptodate(ce) && + is_racy_timestamp(istate, ce)) { + /* + * A racily clean cache entry stored + * only in the shared index: it must + * be added to the split index, so + * the subsequent do_write_index() + * can smudge its stat data. + */ + ce->ce_flags |= CE_UPDATE_IN_BASE; + } else { + /* + * The entry is only present in the + * shared index and it was not + * refreshed. + * Just leave it there. + */ + } continue; + } if (ce->ce_namelen != base->ce_namelen || strcmp(ce->name, base->name)) { ce->index = 0; continue; } - ce_flags = ce->ce_flags; - base_flags = base->ce_flags; - /* only on-disk flags matter */ - ce->ce_flags &= ondisk_flags; - base->ce_flags &= ondisk_flags; - ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data, - offsetof(struct cache_entry, name) - - offsetof(struct cache_entry, ce_stat_data)); - ce->ce_flags = ce_flags; - base->ce_flags = base_flags; - if (ret) + /* + * This is the copy of a cache entry that is present + * in the shared index, created by unpack_trees() + * while it constructed a new index. + */ + if (ce->ce_flags & CE_UPDATE_IN_BASE) { + /* + * Already marked for inclusion in the split + * index, either because the corresponding + * file was modified and the cached stat data + * was refreshed, or because the original + * entry already had a replacement entry in + * the split index. + * Nothing to do. + */ + } else if (!ce_uptodate(ce) && + is_racy_timestamp(istate, ce)) { + /* + * A copy of a racily clean cache entry from + * the shared index. It must be added to + * the split index, so the subsequent + * do_write_index() can smudge its stat data. + */ ce->ce_flags |= CE_UPDATE_IN_BASE; + } else { + /* + * Thoroughly compare the cached data to see + * whether it should be marked for inclusion + * in the split index. + * + * This comparison might be unnecessary, as + * code paths modifying the cached data do + * set CE_UPDATE_IN_BASE as well. + */ + if (compare_ce_content(ce, base)) + ce->ce_flags |= CE_UPDATE_IN_BASE; + } discard_cache_entry(base); si->base->cache[ce->index - 1] = ce; } diff --git a/string-list.c b/string-list.c index 771c455098..1f6063f2a2 100644 --- a/string-list.c +++ b/string-list.c @@ -195,16 +195,6 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c list->nr = list->alloc = 0; } - -void print_string_list(const struct string_list *p, const char *text) -{ - int i; - if ( text ) - printf("%s\n", text); - for (i = 0; i < p->nr; i++) - printf("%s:%p\n", p->items[i].string, p->items[i].util); -} - struct string_list_item *string_list_append_nodup(struct string_list *list, char *string) { diff --git a/string-list.h b/string-list.h index ff8f6094a3..18c718c12c 100644 --- a/string-list.h +++ b/string-list.h @@ -114,14 +114,6 @@ void filter_string_list(struct string_list *list, int free_util, string_list_each_func_t want, void *cb_data); /** - * Dump a string_list to stdout, useful mainly for debugging - * purposes. It can take an optional header argument and it writes out - * the string-pointer pairs of the string_list, each one in its own - * line. - */ -void print_string_list(const struct string_list *p, const char *text); - -/** * Free a string_list. The `string` pointer of the items will be freed * in case the `strdup_strings` member of the string_list is set. The * second parameter controls if the `util` pointer of the items should diff --git a/submodule-config.c b/submodule-config.c index fc2c41b947..0b2f1647ab 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -384,6 +384,12 @@ static void warn_multiple_config(const struct object_id *treeish_name, commit_string, name, option); } +static void warn_command_line_option(const char *var, const char *value) +{ + warning(_("ignoring '%s' which may be interpreted as" + " a command-line option: %s"), var, value); +} + struct parse_config_parameter { struct submodule_cache *cache; const struct object_id *treeish_name; @@ -409,6 +415,8 @@ static int parse_config(const char *var, const char *value, void *data) if (!strcmp(item.buf, "path")) { if (!value) ret = config_error_nonbool(var); + else if (looks_like_command_line_option(value)) + warn_command_line_option(var, value); else if (!me->overwrite && submodule->path) warn_multiple_config(me->treeish_name, submodule->name, "path"); @@ -449,6 +457,8 @@ static int parse_config(const char *var, const char *value, void *data) } else if (!strcmp(item.buf, "url")) { if (!value) { ret = config_error_nonbool(var); + } else if (looks_like_command_line_option(value)) { + warn_command_line_option(var, value); } else if (!me->overwrite && submodule->url) { warn_multiple_config(me->treeish_name, submodule->name, "url"); diff --git a/submodule.c b/submodule.c index a2b266fbfa..f27f14e868 100644 --- a/submodule.c +++ b/submodule.c @@ -65,8 +65,7 @@ int is_staging_gitmodules_ok(struct index_state *istate) if ((pos >= 0) && (pos < istate->cache_nr)) { struct stat st; if (lstat(GITMODULES_FILE, &st) == 0 && - ie_match_stat(istate, istate->cache[pos], &st, - CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED) + ie_match_stat(istate, istate->cache[pos], &st, 0) & DATA_CHANGED) return 0; } @@ -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)) @@ -154,6 +154,7 @@ appropriately before running "make". As the names depend on the tests' file names, it is safe to run the tests with this option in parallel. +-V:: --verbose-log:: Write verbose output to the same logfile as `--tee`, but do _not_ write it to stdout. Unlike `--tee --verbose`, this option diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index 2762ca6562..6a84a53efb 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -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); diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 0edafcfd65..db19131f71 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -43,6 +43,9 @@ static struct test_cmd cmds[] = { { "subprocess", cmd__subprocess }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "wildmatch", cmd__wildmatch }, +#ifdef GIT_WINDOWS_NATIVE + { "windows-named-pipe", cmd__windows_named_pipe }, +#endif { "write-cache", cmd__write_cache }, }; diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index e954e8c522..4914001086 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -39,6 +39,9 @@ int cmd__submodule_config(int argc, const char **argv); int cmd__subprocess(int argc, const char **argv); int cmd__urlmatch_normalization(int argc, const char **argv); int cmd__wildmatch(int argc, const char **argv); +#ifdef GIT_WINDOWS_NATIVE +int cmd__windows_named_pipe(int argc, const char **argv); +#endif int cmd__write_cache(int argc, const char **argv); #endif diff --git a/t/helper/test-windows-named-pipe.c b/t/helper/test-windows-named-pipe.c new file mode 100644 index 0000000000..b4b752b01a --- /dev/null +++ b/t/helper/test-windows-named-pipe.c @@ -0,0 +1,72 @@ +#include "test-tool.h" +#include "git-compat-util.h" +#include "strbuf.h" + +#ifdef GIT_WINDOWS_NATIVE +static const char *usage_string = "<pipe-filename>"; + +#define TEST_BUFSIZE (4096) + +int cmd__windows_named_pipe(int argc, const char **argv) +{ + const char *filename; + struct strbuf pathname = STRBUF_INIT; + int err; + HANDLE h; + BOOL connected; + char buf[TEST_BUFSIZE + 1]; + + if (argc < 2) + goto print_usage; + filename = argv[1]; + if (strchr(filename, '/') || strchr(filename, '\\')) + goto print_usage; + strbuf_addf(&pathname, "//./pipe/%s", filename); + + /* + * Create a single instance of the server side of the named pipe. + * This will allow exactly one client instance to connect to it. + */ + h = CreateNamedPipeA( + pathname.buf, + PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + TEST_BUFSIZE, TEST_BUFSIZE, 0, NULL); + if (h == INVALID_HANDLE_VALUE) { + err = err_win_to_posix(GetLastError()); + fprintf(stderr, "CreateNamedPipe failed: %s\n", + strerror(err)); + return err; + } + + connected = ConnectNamedPipe(h, NULL) + ? TRUE + : (GetLastError() == ERROR_PIPE_CONNECTED); + if (!connected) { + err = err_win_to_posix(GetLastError()); + fprintf(stderr, "ConnectNamedPipe failed: %s\n", + strerror(err)); + CloseHandle(h); + return err; + } + + while (1) { + DWORD nbr; + BOOL success = ReadFile(h, buf, TEST_BUFSIZE, &nbr, NULL); + if (!success || nbr == 0) + break; + buf[nbr] = 0; + + write(1, buf, nbr); + } + + DisconnectNamedPipe(h); + CloseHandle(h); + return 0; + +print_usage: + fprintf(stderr, "usage: %s %s\n", argv[0], usage_string); + return 1; +} +#endif diff --git a/t/t0051-windows-named-pipe.sh b/t/t0051-windows-named-pipe.sh new file mode 100755 index 0000000000..10ac92d225 --- /dev/null +++ b/t/t0051-windows-named-pipe.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +test_description='Windows named pipes' + +. ./test-lib.sh + +test_expect_success MINGW 'o_append write to named pipe' ' + GIT_TRACE="$(pwd)/expect" git status >/dev/null 2>&1 && + { test-tool windows-named-pipe t0051 >actual 2>&1 & } && + pid=$! && + sleep 1 && + GIT_TRACE=//./pipe/t0051 git status >/dev/null 2>warning && + wait $pid && + test_cmp expect actual +' + +test_done diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 3e131c5325..cf932c8514 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -12,10 +12,14 @@ cat >hello-script <<-EOF cat hello-script EOF -test_expect_success 'start_command reports ENOENT' ' +test_expect_success 'start_command reports ENOENT (slash)' ' test-tool run-command start-command-ENOENT ./does-not-exist ' +test_expect_success 'start_command reports ENOENT (no slash)' ' + test-tool run-command start-command-ENOENT does-not-exist +' + test_expect_success 'run_command can run a command' ' cat hello-script >hello.sh && chmod +x hello.sh && @@ -25,6 +29,13 @@ test_expect_success 'run_command can run a command' ' test_must_be_empty err ' +test_expect_success 'run_command is restricted to PATH' ' + write_script should-not-run <<-\EOF && + echo yikes + EOF + test_must_fail test-tool run-command run-command should-not-run +' + test_expect_success !MINGW 'run_command can run a script without a #! line' ' cat >hello <<-\EOF && cat hello-script diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh index 7de40141ca..504334e552 100755 --- a/t/t0090-cache-tree.sh +++ b/t/t0090-cache-tree.sh @@ -161,6 +161,24 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi test_cache_tree ' +test_expect_success PERL 'commit -p with shrinking cache-tree' ' + mkdir -p deep/subdir && + echo content >deep/subdir/file && + git add deep && + git commit -m add && + git rm -r deep && + + before=$(wc -c <.git/index) && + git commit -m delete -p && + after=$(wc -c <.git/index) && + + # double check that the index shrank + test $before -gt $after && + + # and that our index was not corrupted + git fsck +' + test_expect_success 'commit in child dir has cache-tree' ' mkdir dir && >dir/child.t && @@ -243,13 +261,16 @@ test_expect_success 'no phantom error when switching trees' ' ' test_expect_success 'switching trees does not invalidate shared index' ' - git update-index --split-index && - >split && - git add split && - test-tool dump-split-index .git/index | grep -v ^own >before && - git commit -m "as-is" && - test-tool dump-split-index .git/index | grep -v ^own >after && - test_cmp before after + ( + sane_unset GIT_TEST_SPLIT_INDEX && + git update-index --split-index && + >split && + git add split && + test-tool dump-split-index .git/index | grep -v ^own >before && + git commit -m "as-is" && + test-tool dump-split-index .git/index | grep -v ^own >after && + test_cmp before after + ) ' test_done diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index 1281300664..7762f89b3d 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh @@ -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 && diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 7c8df20955..02493f14ba 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -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 && diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 0f2dd26f74..b5677d26a4 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -673,16 +673,35 @@ test_expect_success 'fsck detects trailing loose garbage (commit)' ' test_i18ngrep "garbage.*$commit" out ' -test_expect_success 'fsck detects trailing loose garbage (blob)' ' +test_expect_success 'fsck detects trailing loose garbage (large blob)' ' blob=$(echo trailing | git hash-object -w --stdin) && file=$(sha1_file $blob) && test_when_finished "remove_object $blob" && chmod +w "$file" && echo garbage >>"$file" && - test_must_fail git fsck 2>out && + test_must_fail git -c core.bigfilethreshold=5 fsck 2>out && test_i18ngrep "garbage.*$blob" out ' +test_expect_success 'fsck detects truncated loose object' ' + # make it big enough that we know we will truncate in the data + # portion, not the header + test-tool genrandom truncate 4096 >file && + blob=$(git hash-object -w file) && + file=$(sha1_file $blob) && + test_when_finished "remove_object $blob" && + test_copy_bytes 1024 <"$file" >tmp && + rm "$file" && + mv -f tmp "$file" && + + # check both regular and streaming code paths + test_must_fail git fsck 2>out && + test_i18ngrep corrupt.*$blob out && + + test_must_fail git -c core.bigfilethreshold=128 fsck 2>out && + test_i18ngrep corrupt.*$blob out +' + # for each of type, we have one version which is referenced by another object # (and so while unreachable, not dangling), and another variant which really is # dangling. diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 5c715fe2cf..01abee533d 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -142,6 +142,22 @@ test_expect_success 'showing the superproject correctly' ' 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 ' diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index b3b4d83eaf..f053bf83eb 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -6,8 +6,18 @@ test_description='split index mode tests' # We need total control of index splitting here sane_unset GIT_TEST_SPLIT_INDEX +# A couple of tests expect the index to have a specific checksum, +# but the presence of the optional FSMN extension would interfere +# with those checks, so disable it in this test script. sane_unset GIT_FSMONITOR_TEST +# Create a file named as $1 with content read from stdin. +# Set the file's mtime to a few seconds in the past to avoid racy situations. +create_non_racy_file () { + cat >"$1" && + test-tool chmtime =-5 "$1" +} + test_expect_success 'enable split index' ' git config splitIndex.maxPercentChange 100 && git update-index --split-index && @@ -31,7 +41,7 @@ test_expect_success 'enable split index' ' ' test_expect_success 'add one file' ' - : >one && + create_non_racy_file one && git update-index --add one && git ls-files --stage >ls-files.actual && cat >ls-files.expect <<-EOF && @@ -57,7 +67,7 @@ test_expect_success 'disable split index' ' EOF test_cmp ls-files.expect ls-files.actual && - BASE=$(test-tool dump-split-index .git/index | grep "^own" | sed "s/own/base/") && + BASE=$(test-tool dump-split-index .git/index | sed -n "s/^own/base/p") && test-tool dump-split-index .git/index | sed "/^own/d" >actual && cat >expect <<-EOF && not a split index @@ -83,7 +93,7 @@ test_expect_success 'enable split index again, "one" now belongs to base index"' ' test_expect_success 'modify original file, base index untouched' ' - echo modified >one && + echo modified | create_non_racy_file one && git update-index one && git ls-files --stage >ls-files.actual && cat >ls-files.expect <<-EOF && @@ -102,7 +112,7 @@ test_expect_success 'modify original file, base index untouched' ' ' test_expect_success 'add another file, which stays index' ' - : >two && + create_non_racy_file two && git update-index --add two && git ls-files --stage >ls-files.actual && cat >ls-files.expect <<-EOF && @@ -155,7 +165,7 @@ test_expect_success 'remove file in base index' ' ' test_expect_success 'add original file back' ' - : >one && + create_non_racy_file one && git update-index --add one && git ls-files --stage >ls-files.actual && cat >ls-files.expect <<-EOF && @@ -174,7 +184,7 @@ test_expect_success 'add original file back' ' ' test_expect_success 'add new file' ' - : >two && + create_non_racy_file two && git update-index --add two && git ls-files --stage >actual && cat >expect <<-EOF && @@ -218,7 +228,7 @@ test_expect_success 'rev-parse --shared-index-path' ' test_expect_success 'set core.splitIndex config variable to true' ' git config core.splitIndex true && - : >three && + create_non_racy_file three && git update-index --add three && git ls-files --stage >ls-files.actual && cat >ls-files.expect <<-EOF && @@ -253,9 +263,9 @@ test_expect_success 'set core.splitIndex config variable to false' ' test_cmp expect actual ' -test_expect_success 'set core.splitIndex config variable to true' ' +test_expect_success 'set core.splitIndex config variable back to true' ' git config core.splitIndex true && - : >three && + create_non_racy_file three && git update-index --add three && BASE=$(test-tool dump-split-index .git/index | grep "^base") && test-tool dump-split-index .git/index | sed "/^own/d" >actual && @@ -265,7 +275,7 @@ test_expect_success 'set core.splitIndex config variable to true' ' deletions: EOF test_cmp expect actual && - : >four && + create_non_racy_file four && git update-index --add four && test-tool dump-split-index .git/index | sed "/^own/d" >actual && cat >expect <<-EOF && @@ -279,7 +289,7 @@ test_expect_success 'set core.splitIndex config variable to true' ' test_expect_success 'check behavior with splitIndex.maxPercentChange unset' ' git config --unset splitIndex.maxPercentChange && - : >five && + create_non_racy_file five && git update-index --add five && BASE=$(test-tool dump-split-index .git/index | grep "^base") && test-tool dump-split-index .git/index | sed "/^own/d" >actual && @@ -289,7 +299,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' ' deletions: EOF test_cmp expect actual && - : >six && + create_non_racy_file six && git update-index --add six && test-tool dump-split-index .git/index | sed "/^own/d" >actual && cat >expect <<-EOF && @@ -303,7 +313,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' ' test_expect_success 'check splitIndex.maxPercentChange set to 0' ' git config splitIndex.maxPercentChange 0 && - : >seven && + create_non_racy_file seven && git update-index --add seven && BASE=$(test-tool dump-split-index .git/index | grep "^base") && test-tool dump-split-index .git/index | sed "/^own/d" >actual && @@ -313,7 +323,7 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' ' deletions: EOF test_cmp expect actual && - : >eight && + create_non_racy_file eight && git update-index --add eight && BASE=$(test-tool dump-split-index .git/index | grep "^base") && test-tool dump-split-index .git/index | sed "/^own/d" >actual && @@ -326,17 +336,17 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' ' ' test_expect_success 'shared index files expire after 2 weeks by default' ' - : >ten && + create_non_racy_file ten && git update-index --add ten && test $(ls .git/sharedindex.* | wc -l) -gt 2 && just_under_2_weeks_ago=$((5-14*86400)) && test-tool chmtime =$just_under_2_weeks_ago .git/sharedindex.* && - : >eleven && + create_non_racy_file eleven && git update-index --add eleven && test $(ls .git/sharedindex.* | wc -l) -gt 2 && just_over_2_weeks_ago=$((-1-14*86400)) && test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* && - : >twelve && + create_non_racy_file twelve && git update-index --add twelve && test $(ls .git/sharedindex.* | wc -l) -le 2 ' @@ -344,12 +354,12 @@ test_expect_success 'shared index files expire after 2 weeks by default' ' test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' ' git config splitIndex.sharedIndexExpire "16.days.ago" && test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* && - : >thirteen && + create_non_racy_file thirteen && git update-index --add thirteen && test $(ls .git/sharedindex.* | wc -l) -gt 2 && just_over_16_days_ago=$((-1-16*86400)) && test-tool chmtime =$just_over_16_days_ago .git/sharedindex.* && - : >fourteen && + create_non_racy_file fourteen && git update-index --add fourteen && test $(ls .git/sharedindex.* | wc -l) -le 2 ' @@ -358,13 +368,13 @@ test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now" git config splitIndex.sharedIndexExpire never && just_10_years_ago=$((-365*10*86400)) && test-tool chmtime =$just_10_years_ago .git/sharedindex.* && - : >fifteen && + create_non_racy_file fifteen && git update-index --add fifteen && test $(ls .git/sharedindex.* | wc -l) -gt 2 && git config splitIndex.sharedIndexExpire now && just_1_second_ago=-1 && test-tool chmtime =$just_1_second_ago .git/sharedindex.* && - : >sixteen && + create_non_racy_file sixteen && git update-index --add sixteen && test $(ls .git/sharedindex.* | wc -l) -le 2 ' @@ -379,7 +389,7 @@ do # Create one new shared index file git config core.sharedrepository "$mode" && git config core.splitIndex true && - : >one && + create_non_racy_file one && git update-index --add one && echo "$modebits" >expect && test_modebits .git/index >actual && diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh new file mode 100755 index 0000000000..5dc221ef38 --- /dev/null +++ b/t/t1701-racy-split-index.sh @@ -0,0 +1,214 @@ +#!/bin/sh + +# This test can give false success if your machine is sufficiently +# slow or all trials happened to happen on second boundaries. + +test_description='racy split index' + +. ./test-lib.sh + +test_expect_success 'setup' ' + # Only split the index when the test explicitly says so. + sane_unset GIT_TEST_SPLIT_INDEX && + git config splitIndex.maxPercentChange 100 && + + echo "cached content" >racy-file && + git add racy-file && + git commit -m initial && + + echo something >other-file && + # No raciness with this file. + test-tool chmtime =-20 other-file && + + echo "+cached content" >expect +' + +check_cached_diff () { + git diff-index --patch --cached $EMPTY_TREE racy-file >diff && + tail -1 diff >actual && + test_cmp expect actual +} + +trials="0 1 2 3 4" +for trial in $trials +do + test_expect_success "split the index while adding a racily clean file #$trial" ' + rm -f .git/index .git/sharedindex.* && + + # The next three commands must be run within the same + # second (so both writes to racy-file result in the same + # mtime) to create the interesting racy situation. + echo "cached content" >racy-file && + + # Update and split the index. The cache entry of + # racy-file will be stored only in the shared index. + git update-index --split-index --add racy-file && + + # File size must stay the same. + echo "dirty worktree" >racy-file && + + # Subsequent git commands should notice that racy-file + # and the split index have the same mtime, and check + # the content of the file to see if it is actually + # clean. + check_cached_diff + ' +done + +for trial in $trials +do + test_expect_success "add a racily clean file to an already split index #$trial" ' + rm -f .git/index .git/sharedindex.* && + + git update-index --split-index && + + # The next three commands must be run within the same + # second. + echo "cached content" >racy-file && + + # Update the split index. The cache entry of racy-file + # will be stored only in the split index. + git update-index --add racy-file && + + # File size must stay the same. + echo "dirty worktree" >racy-file && + + # Subsequent git commands should notice that racy-file + # and the split index have the same mtime, and check + # the content of the file to see if it is actually + # clean. + check_cached_diff + ' +done + +for trial in $trials +do + test_expect_success "split the index when the index contains a racily clean cache entry #$trial" ' + rm -f .git/index .git/sharedindex.* && + + # The next three commands must be run within the same + # second. + echo "cached content" >racy-file && + + git update-index --add racy-file && + + # File size must stay the same. + echo "dirty worktree" >racy-file && + + # Now wait a bit to ensure that the split index written + # below will get a more recent mtime than racy-file. + sleep 1 && + + # Update and split the index when the index contains + # the racily clean cache entry of racy-file. + # A corresponding replacement cache entry with smudged + # stat data should be added to the new split index. + git update-index --split-index --add other-file && + + # Subsequent git commands should notice the smudged + # stat data in the replacement cache entry and that it + # doesnt match with the file the worktree. + check_cached_diff + ' +done + +for trial in $trials +do + test_expect_success "update the split index when it contains a new racily clean cache entry #$trial" ' + rm -f .git/index .git/sharedindex.* && + + git update-index --split-index && + + # The next three commands must be run within the same + # second. + echo "cached content" >racy-file && + + # Update the split index. The cache entry of racy-file + # will be stored only in the split index. + git update-index --add racy-file && + + # File size must stay the same. + echo "dirty worktree" >racy-file && + + # Now wait a bit to ensure that the split index written + # below will get a more recent mtime than racy-file. + sleep 1 && + + # Update the split index when the racily clean cache + # entry of racy-file is only stored in the split index. + # An updated cache entry with smudged stat data should + # be added to the new split index. + git update-index --add other-file && + + # Subsequent git commands should notice the smudged + # stat data. + check_cached_diff + ' +done + +for trial in $trials +do + test_expect_success "update the split index when a racily clean cache entry is stored only in the shared index #$trial" ' + rm -f .git/index .git/sharedindex.* && + + # The next three commands must be run within the same + # second. + echo "cached content" >racy-file && + + # Update and split the index. The cache entry of + # racy-file will be stored only in the shared index. + git update-index --split-index --add racy-file && + + # File size must stay the same. + echo "dirty worktree" >racy-file && + + # Now wait a bit to ensure that the split index written + # below will get a more recent mtime than racy-file. + sleep 1 && + + # Update the split index when the racily clean cache + # entry of racy-file is only stored in the shared index. + # A corresponding replacement cache entry with smudged + # stat data should be added to the new split index. + git update-index --add other-file && + + # Subsequent git commands should notice the smudged + # stat data. + check_cached_diff + ' +done + +for trial in $trials +do + test_expect_success "update the split index after unpack trees() copied a racily clean cache entry from the shared index #$trial" ' + rm -f .git/index .git/sharedindex.* && + + # The next three commands must be run within the same + # second. + echo "cached content" >racy-file && + + # Update and split the index. The cache entry of + # racy-file will be stored only in the shared index. + git update-index --split-index --add racy-file && + + # File size must stay the same. + echo "dirty worktree" >racy-file && + + # Now wait a bit to ensure that the split index written + # below will get a more recent mtime than racy-file. + sleep 1 && + + # Update the split index after unpack_trees() copied the + # racily clean cache entry of racy-file from the shared + # index. A corresponding replacement cache entry + # with smudged stat data should be added to the new + # split index. + git read-tree -m HEAD && + + # Subsequent git commands should notice the smudged + # stat data. + check_cached_diff + ' +done + +test_done diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index 2237c7f4af..fb4c13a84a 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -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 diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 86bba5ed7c..ff89b6341a 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -569,16 +569,15 @@ test_expect_success '--continue tries to commit, even for "edit"' ' ' test_expect_success 'aborted --continue does not squash commits after "edit"' ' - test_when_finished "git rebase --abort" && old=$(git rev-parse HEAD) && test_tick && set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo "edited again" > file7 && git add file7 && - echo all the things >>conflict && - test_must_fail git rebase --continue && - test $old = $(git rev-parse HEAD) + test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue && + test $old = $(git rev-parse HEAD) && + git rebase --abort ' test_expect_success 'auto-amend only edited commits after "edit"' ' diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh index da94dddc86..860e63e444 100755 --- a/t/t3405-rebase-malformed.sh +++ b/t/t3405-rebase-malformed.sh @@ -83,7 +83,7 @@ test_expect_success 'rebase -m commit with empty message' ' test_expect_success 'rebase -i commit with empty message' ' git checkout diff-in-message && set_fake_editor && - env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \ + test_must_fail env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \ git rebase -i HEAD^ ' diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index e364c12622..13f5688135 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh @@ -330,4 +330,23 @@ test_expect_success 'wrapped original subject' ' test $base = $parent ' +test_expect_success 'abort last squash' ' + test_when_finished "test_might_fail git rebase --abort" && + test_when_finished "git checkout master" && + + git checkout -b some-squashes && + git commit --allow-empty -m first && + git commit --allow-empty --squash HEAD && + git commit --allow-empty -m second && + git commit --allow-empty --squash HEAD && + + test_must_fail git -c core.editor="grep -q ^pick" \ + rebase -ki --autosquash HEAD~4 && + : do not finish the squash, but resolve it manually && + git commit --allow-empty --amend -m edited-first && + git rebase --skip && + git show >actual && + ! grep first actual +' + test_done diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh index fbdc47cfbd..5f911bb529 100755 --- a/t/t3505-cherry-pick-empty.sh +++ b/t/t3505-cherry-pick-empty.sh @@ -11,17 +11,14 @@ test_expect_success setup ' test_tick && git commit -m "first" && - git checkout -b empty-branch && - test_tick && - git commit --allow-empty -m "empty" && - + git checkout -b empty-message-branch && echo third >> file1 && git add file1 && test_tick && git commit --allow-empty-message -m "" && git checkout master && - git checkout -b empty-branch2 && + git checkout -b empty-change-branch && test_tick && git commit --allow-empty -m "empty" @@ -29,7 +26,7 @@ test_expect_success setup ' test_expect_success 'cherry-pick an empty commit' ' git checkout master && - test_expect_code 1 git cherry-pick empty-branch^ + test_expect_code 1 git cherry-pick empty-change-branch ' test_expect_success 'index lockfile was removed' ' @@ -37,8 +34,9 @@ test_expect_success 'index lockfile was removed' ' ' test_expect_success 'cherry-pick a commit with an empty message' ' + test_when_finished "git reset --hard empty-message-branch~1" && git checkout master && - test_expect_code 1 git cherry-pick empty-branch + git cherry-pick empty-message-branch ' test_expect_success 'index lockfile was removed' ' @@ -47,7 +45,7 @@ test_expect_success 'index lockfile was removed' ' test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' ' git checkout -f master && - git cherry-pick --allow-empty-message empty-branch + git cherry-pick --allow-empty-message empty-message-branch ' test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' ' @@ -55,12 +53,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' ' echo fourth >>file2 && git add file2 && git commit -m "fourth" && - test_must_fail git cherry-pick empty-branch2 + test_must_fail git cherry-pick empty-change-branch ' test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' ' git checkout master && - git cherry-pick --allow-empty empty-branch2 + git cherry-pick --allow-empty empty-change-branch ' test_expect_success 'cherry pick with --keep-redundant-commits' ' diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 609fbfdc31..65dfbc033a 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -540,7 +540,7 @@ test_expect_success 'add -p does not expand argument lists' ' # update it, but we want to be sure that our "." pathspec # was not expanded into the argument list of any command. # So look only for "not-changed". - ! grep not-changed trace.out + ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out ' test_expect_success 'hunk-editing handles custom comment char' ' diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 453e6c35eb..6e0dd6f9e5 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -127,4 +127,14 @@ test_expect_success 'diff --no-index from repo subdir respects config (implicit) test_cmp expect actual.head ' +test_expect_success 'diff --no-index from repo subdir with absolute paths' ' + cat <<-EOF >expect && + 1 1 $(pwd)/non/git/{a => b} + EOF + test_expect_code 1 \ + git -C repo/sub diff --numstat \ + "$(pwd)/non/git/a" "$(pwd)/non/git/b" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 65da74c766..313222d0d6 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -577,4 +577,33 @@ test_expect_success 'multiple identical conflicts' ' count_pre_post 0 0 ' +test_expect_success 'setup simple stage 1 handling' ' + test_create_repo stage_1_handling && + ( + cd stage_1_handling && + + test_seq 1 10 >original && + git add original && + git commit -m original && + + git checkout -b A master && + git mv original A && + git commit -m "rename to A" && + + git checkout -b B master && + git mv original B && + git commit -m "rename to B" + ) +' + +test_expect_success 'test simple stage 1 handling' ' + ( + cd stage_1_handling && + + git config rerere.enabled true && + git checkout A^0 && + test_must_fail git merge B^0 + ) +' + test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 153a506151..819c24d10e 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -1703,4 +1703,8 @@ test_expect_success 'log --source paints symmetric ranges' ' test_cmp expect actual ' +test_expect_success '--exclude-promisor-objects does not BUG-crash' ' + test_must_fail git log --exclude-promisor-objects source-a +' + test_done diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 2052cadb11..978a8a66ff 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -598,4 +598,27 @@ test_expect_success ':only and :unfold work together' ' test_cmp expect actual ' +test_expect_success 'trailer parsing not fooled by --- line' ' + git commit --allow-empty -F - <<-\EOF && + this is the subject + + This is the body. The message has a "---" line which would confuse a + message+patch parser. But here we know we have only a commit message, + so we get it right. + + trailer: wrong + --- + This is more body. + + trailer: right + EOF + + { + echo "trailer: right" && + echo + } >expect && + git log --no-walk --format="%(trailers)" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh new file mode 100755 index 0000000000..dab96c89aa --- /dev/null +++ b/t/t4214-log-graph-octopus.sh @@ -0,0 +1,102 @@ +#!/bin/sh + +test_description='git log --graph of skewed left octopus merge.' + +. ./test-lib.sh + +test_expect_success 'set up merge history' ' + cat >expect.uncolored <<-\EOF && + * left + | *---. octopus-merge + | |\ \ \ + |/ / / / + | | | * 4 + | | * | 3 + | | |/ + | * | 2 + | |/ + * | 1 + |/ + * initial + EOF + cat >expect.colors <<-\EOF && + * left + <RED>|<RESET> *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET> octopus-merge + <RED>|<RESET> <RED>|<RESET><YELLOW>\<RESET> <BLUE>\<RESET> <MAGENTA>\<RESET> + <RED>|<RESET><RED>/<RESET> <YELLOW>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET> + <RED>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4 + <RED>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3 + <RED>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET> + <RED>|<RESET> * <MAGENTA>|<RESET> 2 + <RED>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET> + * <MAGENTA>|<RESET> 1 + <MAGENTA>|<RESET><MAGENTA>/<RESET> + * initial + EOF + test_commit initial && + for i in 1 2 3 4 ; do + git checkout master -b $i || return $? + # Make tag name different from branch name, to avoid + # ambiguity error when calling checkout. + test_commit $i $i $i tag$i || return $? + done && + git checkout 1 -b merge && + test_tick && + git merge -m octopus-merge 1 2 3 4 && + git checkout 1 -b L && + test_commit left +' + +test_expect_success 'log --graph with tricky octopus merge with colors' ' + test_config log.graphColors red,green,yellow,blue,magenta,cyan && + git log --color=always --graph --date-order --pretty=tformat:%s --all >actual.colors.raw && + test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors && + test_cmp expect.colors actual.colors +' + +test_expect_success 'log --graph with tricky octopus merge, no color' ' + git log --color=never --graph --date-order --pretty=tformat:%s --all >actual.raw && + sed "s/ *\$//" actual.raw >actual && + test_cmp expect.uncolored actual +' + +# Repeat the previous two tests with "normal" octopus merge (i.e., +# without the first parent skewing to the "left" branch column). + +test_expect_success 'log --graph with normal octopus merge, no color' ' + cat >expect.uncolored <<-\EOF && + *---. octopus-merge + |\ \ \ + | | | * 4 + | | * | 3 + | | |/ + | * | 2 + | |/ + * | 1 + |/ + * initial + EOF + git log --color=never --graph --date-order --pretty=tformat:%s merge >actual.raw && + sed "s/ *\$//" actual.raw >actual && + test_cmp expect.uncolored actual +' + +test_expect_success 'log --graph with normal octopus merge with colors' ' + cat >expect.colors <<-\EOF && + *<YELLOW>-<RESET><YELLOW>-<RESET><BLUE>-<RESET><BLUE>.<RESET> octopus-merge + <RED>|<RESET><GREEN>\<RESET> <YELLOW>\<RESET> <BLUE>\<RESET> + <RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 4 + <RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 3 + <RED>|<RESET> <GREEN>|<RESET> <BLUE>|<RESET><BLUE>/<RESET> + <RED>|<RESET> * <BLUE>|<RESET> 2 + <RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET> + * <BLUE>|<RESET> 1 + <BLUE>|<RESET><BLUE>/<RESET> + * initial + EOF + test_config log.graphColors red,green,yellow,blue,magenta,cyan && + git log --color=always --graph --date-order --pretty=tformat:%s merge >actual.colors.raw && + test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors && + test_cmp expect.colors actual.colors +' +test_done diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 0c500f7ca2..2799969f3e 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -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 diff --git a/t/t5321-pack-large-objects.sh b/t/t5321-pack-large-objects.sh new file mode 100755 index 0000000000..a75eab87d3 --- /dev/null +++ b/t/t5321-pack-large-objects.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright (c) 2018 Johannes Schindelin +# + +test_description='git pack-object with "large" deltas + +' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-pack.sh + +# Two similar-ish objects that we have computed deltas between. +A=01d7713666f4de822776c7622c10f1b07de280dc +B=e68fe8129b546b101aee9510c5328e7f21ca1d18 + +test_expect_success 'setup' ' + clear_packs && + { + pack_header 2 && + pack_obj $A $B && + pack_obj $B + } >ab.pack && + pack_trailer ab.pack && + git index-pack --stdin <ab.pack +' + +test_expect_success 'repack large deltas' ' + printf "%s\\n" $A $B | + GIT_TEST_OE_DELTA_SIZE=2 git pack-objects tmp-pack +' + +test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 241e6a319d..d2a2cdd453 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -145,7 +145,7 @@ test_expect_success 'remove remote protects local branches' ' test_expect_success 'remove errors out early when deleting non-existent branch' ' ( cd test && - echo "fatal: No such remote: foo" >expect && + echo "fatal: No such remote: '\''foo'\''" >expect && test_must_fail git remote rm foo 2>actual && test_i18ncmp expect actual ) @@ -173,7 +173,7 @@ test_expect_success 'remove remote with a branch without configured merge' ' test_expect_success 'rename errors out early when deleting non-existent branch' ' ( cd test && - echo "fatal: No such remote: foo" >expect && + echo "fatal: No such remote: '\''foo'\''" >expect && test_must_fail git remote rename foo bar 2>actual && test_i18ncmp expect actual ) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 539c25aada..bb0a36535c 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1552,7 +1552,13 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' ' test $(git -C .. rev-parse master) = $(git rev-parse HEAD) && git diff --quiet && git diff --cached --quiet - ) + ) && + + # (6) updateInstead intervened by fast-forward check + test_must_fail git push void master^:master && + test $(git -C void rev-parse HEAD) = $(git rev-parse master) && + git -C void diff --quiet && + git -C void diff --cached --quiet ' test_expect_success 'updateInstead with push-to-checkout hook' ' diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh index 7045685e2d..6faf17e17a 100755 --- a/t/t5537-fetch-shallow.sh +++ b/t/t5537-fetch-shallow.sh @@ -186,6 +186,33 @@ EOF test_cmp expect actual ' +test_expect_success '.git/shallow is edited by repack' ' + git init shallow-server && + test_commit -C shallow-server A && + test_commit -C shallow-server B && + git -C shallow-server checkout -b branch && + test_commit -C shallow-server C && + test_commit -C shallow-server E && + test_commit -C shallow-server D && + d="$(git -C shallow-server rev-parse --verify D^0)" && + git -C shallow-server checkout master && + + git clone --depth=1 --no-tags --no-single-branch \ + "file://$PWD/shallow-server" shallow-client && + + : now remove the branch and fetch with prune && + git -C shallow-server branch -D branch && + git -C shallow-client fetch --prune --depth=1 \ + origin "+refs/heads/*:refs/remotes/origin/*" && + git -C shallow-client repack -adfl && + test_must_fail git -C shallow-client rev-parse --verify $d^0 && + ! grep $d shallow-client/.git/shallow && + + git -C shallow-server branch branch-orig $d && + git -C shallow-client fetch --prune --depth=2 \ + origin "+refs/heads/*:refs/remotes/origin/*" +' + . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 771f36f9ff..3dc8f8ecec 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -23,26 +23,26 @@ test_expect_success 'create http-accessible bare repository' ' setup_askpass_helper -cat >exp <<EOF -> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 -> Accept: */* -> Accept-Encoding: ENCODINGS -> Pragma: no-cache -< HTTP/1.1 200 OK -< Pragma: no-cache -< Cache-Control: no-cache, max-age=0, must-revalidate -< Content-Type: application/x-git-upload-pack-advertisement -> POST /smart/repo.git/git-upload-pack HTTP/1.1 -> Accept-Encoding: ENCODINGS -> Content-Type: application/x-git-upload-pack-request -> Accept: application/x-git-upload-pack-result -> Content-Length: xxx -< HTTP/1.1 200 OK -< Pragma: no-cache -< Cache-Control: no-cache, max-age=0, must-revalidate -< Content-Type: application/x-git-upload-pack-result -EOF test_expect_success 'clone http repository' ' + cat >exp <<-\EOF && + > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 + > Accept: */* + > Accept-Encoding: ENCODINGS + > Pragma: no-cache + < HTTP/1.1 200 OK + < Pragma: no-cache + < Cache-Control: no-cache, max-age=0, must-revalidate + < Content-Type: application/x-git-upload-pack-advertisement + > POST /smart/repo.git/git-upload-pack HTTP/1.1 + > Accept-Encoding: ENCODINGS + > Content-Type: application/x-git-upload-pack-request + > Accept: application/x-git-upload-pack-result + > Content-Length: xxx + < HTTP/1.1 200 OK + < Pragma: no-cache + < Cache-Control: no-cache, max-age=0, must-revalidate + < Content-Type: application/x-git-upload-pack-result + EOF GIT_TRACE_CURL=true git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err && test_cmp file clone/file && tr '\''\015'\'' Q <err | @@ -96,13 +96,13 @@ test_expect_success 'fetch changes via http' ' test_cmp file clone/file ' -cat >exp <<EOF -GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 -POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 -POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -EOF test_expect_success 'used upload-pack service' ' + cat >exp <<-\EOF && + GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 + POST /smart/repo.git/git-upload-pack HTTP/1.1 200 + GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 + POST /smart/repo.git/git-upload-pack HTTP/1.1 200 + EOF check_access_log exp ' @@ -203,19 +203,19 @@ test_expect_success 'dumb clone via http-backend respects namespace' ' test_cmp expect actual ' -cat >cookies.txt <<EOF -127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue -EOF -cat >expect_cookies.txt <<EOF - -127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue -127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value -EOF test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' ' + cat >cookies.txt <<-\EOF && + 127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue + EOF + sort >expect_cookies.txt <<-\EOF && + + 127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue + 127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value + EOF git config http.cookiefile cookies.txt && git config http.savecookies true && git ls-remote $HTTPD_URL/smart_cookies/repo.git master && - tail -3 cookies.txt >cookies_tail.txt && + tail -3 cookies.txt | sort >cookies_tail.txt && test_cmp expect_cookies.txt cookies_tail.txt ' diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh index 77b8cef661..e436a73962 100755 --- a/t/t6135-pathspec-with-attrs.sh +++ b/t/t6135-pathspec-with-attrs.sh @@ -166,7 +166,7 @@ test_expect_success 'fail if attr magic is used places not implemented' ' # though, but git-add is convenient as it has its own internal pathspec # parsing. test_must_fail git add ":(attr:labelB)" 2>actual && - test_i18ngrep "unsupported magic" actual + test_i18ngrep "magic not supported" actual ' test_expect_success 'abort on giving invalid label on the command line' ' diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 024f8c06f7..97bfbee6e8 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -715,6 +715,29 @@ test_expect_success 'basic atom: head contents:trailers' ' test_cmp expect actual.clean ' +test_expect_success 'trailer parsing not fooled by --- line' ' + git commit --allow-empty -F - <<-\EOF && + this is the subject + + This is the body. The message has a "---" line which would confuse a + message+patch parser. But here we know we have only a commit message, + so we get it right. + + trailer: wrong + --- + This is more body. + + trailer: right + EOF + + { + echo "trailer: right" && + echo + } >expect && + git for-each-ref --format="%(trailers)" refs/heads/master >actual && + test_cmp expect actual +' + test_expect_success 'Add symbolic ref for the following tests' ' git symbolic-ref refs/heads/sym refs/heads/master ' diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh new file mode 100755 index 0000000000..1cd2c1c1ea --- /dev/null +++ b/t/t7416-submodule-dash-url.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='check handling of .gitmodule url with dash' +. ./test-lib.sh + +test_expect_success 'create submodule with protected dash in url' ' + git init upstream && + git -C upstream commit --allow-empty -m base && + mv upstream ./-upstream && + git submodule add ./-upstream sub && + git add sub .gitmodules && + git commit -m submodule +' + +test_expect_success 'clone can recurse submodule' ' + test_when_finished "rm -rf dst" && + git clone --recurse-submodules . dst && + echo base >expect && + git -C dst/sub log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success 'fsck accepts protected dash' ' + test_when_finished "rm -rf dst" && + git init --bare dst && + git -C dst config transfer.fsckObjects true && + git push dst HEAD +' + +test_expect_success 'remove ./ protection from .gitmodules url' ' + perl -i -pe "s{\./}{}" .gitmodules && + git commit -am "drop protection" +' + +test_expect_success 'clone rejects unprotected dash' ' + test_when_finished "rm -rf dst" && + test_must_fail git clone --recurse-submodules . dst 2>err && + test_i18ngrep ignoring err +' + +test_expect_success 'fsck rejects unprotected dash' ' + test_when_finished "rm -rf dst" && + git init --bare dst && + git -C dst config transfer.fsckObjects true && + test_must_fail git push dst HEAD 2>err && + grep gitmodulesUrl err +' + +test_done diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh new file mode 100755 index 0000000000..756af8c4d6 --- /dev/null +++ b/t/t7417-submodule-path-url.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +test_description='check handling of .gitmodule path with dash' +. ./test-lib.sh + +test_expect_success 'create submodule with dash in path' ' + git init upstream && + git -C upstream commit --allow-empty -m base && + git submodule add ./upstream sub && + git mv sub ./-sub && + git commit -m submodule +' + +test_expect_success 'clone rejects unprotected dash' ' + test_when_finished "rm -rf dst" && + git clone --recurse-submodules . dst 2>err && + test_i18ngrep ignoring err +' + +test_expect_success 'fsck rejects unprotected dash' ' + test_when_finished "rm -rf dst" && + git init --bare dst && + git -C dst config transfer.fsckObjects true && + test_must_fail git push dst HEAD 2>err && + grep gitmodulesPath err +' + +test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 170b4810e0..31ab608b67 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -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 diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 4cae92804d..1a6773ee68 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -517,6 +517,22 @@ Myfooter: x" && test_cmp expected actual ' +test_expect_success 'signoff not confused by ---' ' + cat >expected <<-EOF && + subject + + body + --- + these dashes confuse the parser! + + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + # should be a noop, since we already signed + git commit --allow-empty --signoff -F expected && + git log -1 --pretty=format:%B >actual && + test_cmp expected actual +' + test_expect_success 'multiple -m' ' >negative && diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh index 164719d1c9..c441861331 100755 --- a/t/t7513-interpret-trailers.sh +++ b/t/t7513-interpret-trailers.sh @@ -1417,4 +1417,46 @@ test_expect_success 'unfold' ' test_cmp expected actual ' +test_expect_success 'handling of --- lines in input' ' + echo "real-trailer: just right" >expected && + + git interpret-trailers --parse >actual <<-\EOF && + subject + + body + + not-a-trailer: too soon + ------ this is just a line in the commit message with a bunch of + ------ dashes; it does not have any syntactic meaning. + + real-trailer: just right + --- + below the dashed line may be a patch, etc. + + not-a-trailer: too late + EOF + + test_cmp expected actual +' + +test_expect_success 'suppress --- handling' ' + echo "real-trailer: just right" >expected && + + git interpret-trailers --parse --no-divider >actual <<-\EOF && + subject + + This commit message has a "---" in it, but because we tell + interpret-trailers not to respect that, it has no effect. + + not-a-trailer: too soon + --- + + This is still the commit message body. + + real-trailer: just right + EOF + + test_cmp expected actual +' + test_done diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index 380e1c1054..eea048e52c 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh @@ -118,4 +118,8 @@ test_expect_success '--no-abbrev works like --abbrev=40' ' check_abbrev 40 --no-abbrev ' +test_expect_success '--exclude-promisor-objects does not BUG-crash' ' + test_must_fail git blame --exclude-promisor-objects one +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 44288cbb59..1373fd4592 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -67,7 +67,7 @@ case "$GIT_TEST_TEE_STARTED, $* " in done,*) # do not redirect again ;; -*' --tee '*|*' --va'*|*' --verbose-log '*) +*' --tee '*|*' --va'*|*' -V '*|*' --verbose-log '*) mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results" BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)" @@ -285,7 +285,7 @@ do echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD" fi shift ;; - --verbose-log) + -V|--verbose-log) verbose_log=t shift ;; *) diff --git a/tempfile.c b/tempfile.c index 139ecd97f8..d43ad8c191 100644 --- a/tempfile.c +++ b/tempfile.c @@ -279,7 +279,7 @@ int reopen_tempfile(struct tempfile *tempfile) BUG("reopen_tempfile called for an inactive object"); if (0 <= tempfile->fd) BUG("reopen_tempfile called for an open object"); - tempfile->fd = open(tempfile->filename.buf, O_WRONLY); + tempfile->fd = open(tempfile->filename.buf, O_WRONLY|O_TRUNC); return tempfile->fd; } diff --git a/tempfile.h b/tempfile.h index 36434eb6fa..61d8dc4d1b 100644 --- a/tempfile.h +++ b/tempfile.h @@ -236,8 +236,8 @@ extern int close_tempfile_gently(struct tempfile *tempfile); * it (and nobody else) to inspect or even modify the file's * contents. * - * * `reopen_tempfile()` to reopen the temporary file. Make further - * updates to the contents. + * * `reopen_tempfile()` to reopen the temporary file, truncating the existing + * contents. Write out the new contents. * * * `rename_tempfile()` to move the file to its permanent location. */ @@ -585,7 +585,7 @@ static const char *token_from_item(struct arg_item *item, char *tok) return item->conf.name; } -static int token_matches_item(const char *tok, struct arg_item *item, int tok_len) +static int token_matches_item(const char *tok, struct arg_item *item, size_t tok_len) { if (!strncasecmp(tok, item->conf.name, tok_len)) return 1; @@ -603,7 +603,7 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_le * distinguished from the non-well-formed-line case (in which this function * returns -1) because some callers of this function need such a distinction. */ -static int find_separator(const char *line, const char *separators) +static ssize_t find_separator(const char *line, const char *separators) { int whitespace_found = 0; const char *c; @@ -630,10 +630,10 @@ static int find_separator(const char *line, const char *separators) */ static void parse_trailer(struct strbuf *tok, struct strbuf *val, const struct conf_info **conf, const char *trailer, - int separator_pos) + ssize_t separator_pos) { struct arg_item *item; - int tok_len; + size_t tok_len; struct list_head *pos; if (separator_pos != -1) { @@ -721,7 +721,7 @@ static void process_command_line_args(struct list_head *arg_head, list_for_each(pos, new_trailer_head) { struct new_trailer_item *tr = list_entry(pos, struct new_trailer_item, list); - int separator_pos = find_separator(tr->text, cl_separators); + ssize_t separator_pos = find_separator(tr->text, cl_separators); if (separator_pos == 0) { struct strbuf sb = STRBUF_INIT; @@ -763,9 +763,9 @@ static const char *next_line(const char *str) /* * Return the position of the start of the last line. If len is 0, return -1. */ -static int last_line(const char *buf, size_t len) +static ssize_t last_line(const char *buf, size_t len) { - int i; + ssize_t i; if (len == 0) return -1; if (len == 1) @@ -788,12 +788,14 @@ static int last_line(const char *buf, size_t len) * Return the position of the start of the patch or the length of str if there * is no patch in the message. */ -static int find_patch_start(const char *str) +static size_t find_patch_start(const char *str) { const char *s; for (s = str; *s; s = next_line(s)) { - if (starts_with(s, "---")) + const char *v; + + if (skip_prefix(s, "---", &v) && isspace(*v)) return s - str; } @@ -804,10 +806,11 @@ static int find_patch_start(const char *str) * Return the position of the first trailer line or len if there are no * trailers. */ -static int find_trailer_start(const char *buf, size_t len) +static size_t find_trailer_start(const char *buf, size_t len) { const char *s; - int end_of_title, l, only_spaces = 1; + ssize_t end_of_title, l; + int only_spaces = 1; int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0; /* * Number of possible continuation lines encountered. This will be @@ -838,7 +841,7 @@ static int find_trailer_start(const char *buf, size_t len) l = last_line(buf, l)) { const char *bol = buf + l; const char **p; - int separator_pos; + ssize_t separator_pos; if (bol[0] == comment_line_char) { non_trailer_lines += possible_continuation_lines; @@ -899,14 +902,14 @@ continue_outer_loop: } /* Return the position of the end of the trailers. */ -static int find_trailer_end(const char *buf, size_t len) +static size_t find_trailer_end(const char *buf, size_t len) { return len - ignore_non_trailer(buf, len); } static int ends_with_blank_line(const char *buf, size_t len) { - int ll = last_line(buf, len); + ssize_t ll = last_line(buf, len); if (ll < 0) return 0; return is_blank_line(buf + ll); @@ -939,17 +942,17 @@ static void unfold_value(struct strbuf *val) strbuf_release(&out); } -static int process_input_file(FILE *outfile, - const char *str, - struct list_head *head, - const struct process_trailer_options *opts) +static size_t process_input_file(FILE *outfile, + const char *str, + struct list_head *head, + const struct process_trailer_options *opts) { struct trailer_info info; struct strbuf tok = STRBUF_INIT; struct strbuf val = STRBUF_INIT; - int i; + size_t i; - trailer_info_get(&info, str); + trailer_info_get(&info, str, opts); /* Print lines before the trailers as is */ if (!opts->only_trailers) @@ -1032,7 +1035,7 @@ void process_trailers(const char *file, { LIST_HEAD(head); struct strbuf sb = STRBUF_INIT; - int trailer_end; + size_t trailer_end; FILE *outfile = stdout; ensure_configured(); @@ -1066,7 +1069,8 @@ void process_trailers(const char *file, strbuf_release(&sb); } -void trailer_info_get(struct trailer_info *info, const char *str) +void trailer_info_get(struct trailer_info *info, const char *str, + const struct process_trailer_options *opts) { int patch_start, trailer_end, trailer_start; struct strbuf **trailer_lines, **ptr; @@ -1076,7 +1080,11 @@ void trailer_info_get(struct trailer_info *info, const char *str) ensure_configured(); - patch_start = find_patch_start(str); + if (opts->no_divider) + patch_start = strlen(str); + else + patch_start = find_patch_start(str); + trailer_end = find_trailer_end(str, patch_start); trailer_start = find_trailer_start(str, trailer_end); @@ -1111,7 +1119,7 @@ void trailer_info_get(struct trailer_info *info, const char *str) void trailer_info_release(struct trailer_info *info) { - int i; + size_t i; for (i = 0; i < info->trailer_nr; i++) free(info->trailers[i]); free(info->trailers); @@ -1121,7 +1129,7 @@ static void format_trailer_info(struct strbuf *out, const struct trailer_info *info, const struct process_trailer_options *opts) { - int i; + size_t i; /* If we want the whole block untouched, we can take the fast path. */ if (!opts->only_trailers && !opts->unfold) { @@ -1132,7 +1140,7 @@ static void format_trailer_info(struct strbuf *out, for (i = 0; i < info->trailer_nr; i++) { char *trailer = info->trailers[i]; - int separator_pos = find_separator(trailer, separators); + ssize_t separator_pos = find_separator(trailer, separators); if (separator_pos >= 1) { struct strbuf tok = STRBUF_INIT; @@ -1158,7 +1166,7 @@ void format_trailers_from_commit(struct strbuf *out, const char *msg, { struct trailer_info info; - trailer_info_get(&info, msg); + trailer_info_get(&info, msg, opts); format_trailer_info(out, &info, opts); trailer_info_release(&info); } @@ -71,6 +71,7 @@ struct process_trailer_options { int only_trailers; int only_input; int unfold; + int no_divider; }; #define PROCESS_TRAILER_OPTIONS_INIT {0} @@ -79,7 +80,8 @@ void process_trailers(const char *file, const struct process_trailer_options *opts, struct list_head *new_trailer_head); -void trailer_info_get(struct trailer_info *info, const char *str); +void trailer_info_get(struct trailer_info *info, const char *str, + const struct process_trailer_options *opts); void trailer_info_release(struct trailer_info *info); diff --git a/unpack-trees.c b/unpack-trees.c index f25089b878..82a83dbc67 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -392,7 +392,7 @@ static int check_updates(struct unpack_trees_options *o) } if (to_fetch.nr) fetch_objects(repository_format_partial_clone, - &to_fetch); + to_fetch.oid, to_fetch.nr); fetch_if_missing = fetch_if_missing_store; oid_array_clear(&to_fetch); } diff --git a/upload-pack.c b/upload-pack.c index 82b393ec31..2ae9d9bb47 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -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); diff --git a/userdiff.c b/userdiff.c index f3f4be579c..f565f6731d 100644 --- a/userdiff.c +++ b/userdiff.c @@ -278,8 +278,7 @@ struct userdiff_driver *userdiff_find_by_path(const char *path) check = attr_check_initl("diff", NULL); if (!path) return NULL; - if (git_check_attr(&the_index, path, check)) - return NULL; + git_check_attr(&the_index, path, check); if (ATTR_TRUE(check->items[0].value)) return &driver_true; @@ -74,35 +74,31 @@ unsigned parse_whitespace_rule(const char *string) unsigned whitespace_rule(const char *pathname) { static struct attr_check *attr_whitespace_rule; + const char *value; if (!attr_whitespace_rule) attr_whitespace_rule = attr_check_initl("whitespace", NULL); - if (!git_check_attr(&the_index, pathname, attr_whitespace_rule)) { - const char *value; - - value = attr_whitespace_rule->items[0].value; - if (ATTR_TRUE(value)) { - /* true (whitespace) */ - unsigned all_rule = ws_tab_width(whitespace_rule_cfg); - int i; - for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) - if (!whitespace_rule_names[i].loosens_error && - !whitespace_rule_names[i].exclude_default) - all_rule |= whitespace_rule_names[i].rule_bits; - return all_rule; - } else if (ATTR_FALSE(value)) { - /* false (-whitespace) */ - return ws_tab_width(whitespace_rule_cfg); - } else if (ATTR_UNSET(value)) { - /* reset to default (!whitespace) */ - return whitespace_rule_cfg; - } else { - /* string */ - return parse_whitespace_rule(value); - } - } else { + git_check_attr(&the_index, pathname, attr_whitespace_rule); + value = attr_whitespace_rule->items[0].value; + if (ATTR_TRUE(value)) { + /* true (whitespace) */ + unsigned all_rule = ws_tab_width(whitespace_rule_cfg); + int i; + for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) + if (!whitespace_rule_names[i].loosens_error && + !whitespace_rule_names[i].exclude_default) + all_rule |= whitespace_rule_names[i].rule_bits; + return all_rule; + } else if (ATTR_FALSE(value)) { + /* false (-whitespace) */ + return ws_tab_width(whitespace_rule_cfg); + } else if (ATTR_UNSET(value)) { + /* reset to default (!whitespace) */ return whitespace_rule_cfg; + } else { + /* string */ + return parse_whitespace_rule(value); } } |