Merge branch 'sj/t9117-path-is-file' GSoC practice to replace "test -f" with "test_path_is_file". * sj/t9117-path-is-file: t9117: prefer test_path_* helper functions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index a6a9656..32e69f7 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines
@@ -446,12 +446,41 @@ detail. - The first #include in C files, except in platform specific compat/ - implementations and sha1dc/, must be either "git-compat-util.h" or - one of the approved headers that includes it first for you. (The - approved headers currently include "builtin.h", - "t/helper/test-tool.h", "xdiff/xinclude.h", or - "reftable/system.h".) You do not have to include more than one of - these. + implementations and sha1dc/, must be <git-compat-util.h>. This + header file insulates other header files and source files from + platform differences, like which system header files must be + included in what order, and what C preprocessor feature macros must + be defined to trigger certain features we expect out of the system. + A collorary to this is that C files should not directly include + system header files themselves. + + There are some exceptions, because certain group of files that + implement an API all have to include the same header file that + defines the API and it is convenient to include <git-compat-util.h> + there. Namely: + + - the implementation of the built-in commands in the "builtin/" + directory that include "builtin.h" for the cmd_foo() prototype + definition, + + - the test helper programs in the "t/helper/" directory that include + "t/helper/test-tool.h" for the cmd__foo() prototype definition, + + - the xdiff implementation in the "xdiff/" directory that includes + "xdiff/xinclude.h" for the xdiff machinery internals, + + - the unit test programs in "t/unit-tests/" directory that include + "t/unit-tests/test-lib.h" that gives them the unit-tests + framework, and + + - the source files that implement reftable in the "reftable/" + directory that include "reftable/system.h" for the reftable + internals, + + are allowed to assume that they do not have to include + <git-compat-util.h> themselves, as it is included as the first + '#include' in these header files. These headers must be the first + header file to be "#include"d in them, though. - A C file must directly include the header files that declare the functions and the types it uses, except for the functions and types
diff --git a/Documentation/RelNotes/2.45.0.txt b/Documentation/RelNotes/2.45.0.txt index 321da04..c0f9b9e 100644 --- a/Documentation/RelNotes/2.45.0.txt +++ b/Documentation/RelNotes/2.45.0.txt
@@ -17,6 +17,23 @@ * "git reflog" learned a "list" subcommand that enumerates known reflogs. + * When a merge conflicted at a submodule, merge-ort backend used to + unconditionally give a lengthy message to suggest how to resolve + it. Now the message can be squelched as an advice message. + + * "git for-each-ref" learned "--include-root-refs" option to show + even the stuff outside the 'refs/' hierarchy. + + * "git rev-list --missing=print" has learned to optionally take + "--allow-missing-tips", which allows the objects at the starting + points to be missing. + + * "git merge-tree" has learned that the three trees involved in the + 3-way merge only need to be trees, not necessarily commits. + + * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and + other kinds of *_HEAD pseudorefs. + Performance, Internal Implementation, Development Support etc. @@ -33,6 +50,10 @@ specified; use "_<placeholder>_" to typeset the word inside a pair of <angle-brakets> emphasized. + * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy + fetching of objects from the promisor remote, which may be handy + for debugging. + Fixes since v2.44 ----------------- @@ -65,6 +86,47 @@ option; it used to always exit with 0 and signalled success. (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint). + * The code incorrectly attempted to use textconv cache when asked, + even when we are not running in a repository, which has been + corrected. + (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint). + + * Remove an empty file that shouldn't have been added in the first + place. + (merge 4f66942215 js/remove-cruft-files later to maint). + + * The logic to access reflog entries by date and number had ugly + corner cases at the boundaries, which have been cleaned up. + (merge 5edd126720 jk/reflog-special-cases-fix later to maint). + + * An error message from "git upload-pack", which responds to "git + fetch" requests, had a trialing NUL in it, which has been + corrected. + (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint). + + * Clarify wording in the CodingGuidelines that requires <git-compat-util.h> + to be the first header file. + (merge 4e89f0e07c jc/doc-compat-util later to maint). + + * "git commit -v --cleanup=scissors" used to add the scissors line + twice in the log message buffer, which has been corrected. + (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint). + + * A custom remote helper no longer cannot access the newly created + repository during "git clone", which is a regression in Git 2.44. + This has been corrected. + (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint). + + * Various parts of upload-pack has been updated to bound the resource + consumption relative to the size of the repository to protect from + abusive clients. + (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint). + + * The upload-pack program, when talking over v2, accepted the + packfile-uris protocol extension from the client, even if it did + not advertise the capability, which has been corrected. + (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint). + * Other code cleanup, docfix, build fix, etc. (merge f0e578c69c rs/use-xstrncmpz later to maint). (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint). @@ -74,3 +136,9 @@ (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint). (merge 6835f0efe9 jw/remote-doc-typofix later to maint). (merge 244001aa20 hs/rebase-not-in-progress later to maint). + (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint). + (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint). + (merge f39addd0d9 rs/name-rev-with-mempool later to maint). + (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint). + (merge 40b8076462 ak/rebase-autosquash later to maint). + (merge 3223204456 eg/add-uflags later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt index e3a74dd..782c2ba 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt
@@ -369,20 +369,18 @@ names do not conflict with those that are used by Git itself and other popular tools, and describe them in your documentation. -include::config/advice.txt[] - -include::config/attr.txt[] - -include::config/core.txt[] - include::config/add.txt[] +include::config/advice.txt[] + include::config/alias.txt[] include::config/am.txt[] include::config/apply.txt[] +include::config/attr.txt[] + include::config/blame.txt[] include::config/branch.txt[] @@ -405,10 +403,12 @@ include::config/commitgraph.txt[] -include::config/credential.txt[] - include::config/completion.txt[] +include::config/core.txt[] + +include::config/credential.txt[] + include::config/diff.txt[] include::config/difftool.txt[] @@ -421,10 +421,10 @@ include::config/fetch.txt[] -include::config/format.txt[] - include::config/filter.txt[] +include::config/format.txt[] + include::config/fsck.txt[] include::config/fsmonitor--daemon.txt[] @@ -435,10 +435,10 @@ include::config/gitweb.txt[] -include::config/grep.txt[] - include::config/gpg.txt[] +include::config/grep.txt[] + include::config/gui.txt[] include::config/guitool.txt[] @@ -519,10 +519,10 @@ include::config/ssh.txt[] -include::config/status.txt[] - include::config/stash.txt[] +include::config/status.txt[] + include::config/submodule.txt[] include::config/tag.txt[]
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt index c7ea70f..dde8e78 100644 --- a/Documentation/config/advice.txt +++ b/Documentation/config/advice.txt
@@ -129,6 +129,9 @@ submoduleAlternateErrorStrategyDie:: Advice shown when a submodule.alternateErrorStrategy option configured to "die" causes a fatal error. + submoduleMergeConflict:: + Advice shown when a non-trivial submodule merge conflict is + encountered. submodulesNotUpdated:: Advice shown when a user runs a submodule command that fails because `git submodule update --init` was not run.
diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt index a9cbdb8..f1ce50f 100644 --- a/Documentation/config/transfer.txt +++ b/Documentation/config/transfer.txt
@@ -121,3 +121,7 @@ information from the remote server (if advertised) and download bundles before continuing the clone through the Git protocol. Defaults to `false`. + +transfer.advertiseObjectInfo:: + When `true`, the `object-info` capability is advertised by + servers. Defaults to false.
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 3a9ad91..c1dd12b 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt
@@ -10,7 +10,7 @@ [verse] 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl] [(--sort=<key>)...] [--format=<format>] - [ --stdin | <pattern>... ] + [--include-root-refs] [ --stdin | <pattern>... ] [--points-at=<object>] [--merged[=<object>]] [--no-merged[=<object>]] [--contains[=<object>]] [--no-contains[=<object>]] @@ -105,6 +105,9 @@ any excluded pattern(s) are shown. Matching is done using the same rules as `<pattern>` above. +--include-root-refs:: + List root refs (HEAD and pseudorefs) apart from regular refs. + FIELD NAMES -----------
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt index b50acac..dd388fa 100644 --- a/Documentation/git-merge-tree.txt +++ b/Documentation/git-merge-tree.txt
@@ -64,10 +64,13 @@ share no common history. This flag can be given to override that check and make the merge proceed anyway. ---merge-base=<commit>:: +--merge-base=<tree-ish>:: Instead of finding the merge-bases for <branch1> and <branch2>, specify a merge-base for the merge, and specifying multiple bases is currently not supported. This option is incompatible with `--stdin`. ++ +As the merge-base is provided directly, <branch1> and <branch2> do not need +to specify commits; trees are enough. [[OUTPUT]] OUTPUT
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 0620652..e7e7250 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt
@@ -607,7 +607,7 @@ linkgit:git-commit[1], which take the target commit as an argument and automatically fill in the subject line of the new commit from that. + -Settting configuration variable `rebase.autoSquash` to true enables +Setting configuration variable `rebase.autoSquash` to true enables auto-squashing by default for interactive rebase. The `--no-autosquash` option can be used to override that setting. +
diff --git a/Documentation/git.txt b/Documentation/git.txt index 0d25224..e6b766d 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt
@@ -174,8 +174,17 @@ directory. --no-replace-objects:: - Do not use replacement refs to replace Git objects. See - linkgit:git-replace[1] for more information. + Do not use replacement refs to replace Git objects. + This is equivalent to exporting the `GIT_NO_REPLACE_OBJECTS` + environment variable with any value. + See linkgit:git-replace[1] for more information. + +--no-lazy-fetch:: + Do not fetch missing objects from the promisor remote on + demand. Useful together with `git cat-file -e <object>` to + see if the object is locally available. + This is equivalent to setting the `GIT_NO_LAZY_FETCH` + environment variable to `1`. --literal-pathspecs:: Treat pathspecs literally (i.e. no globbing, no pathspec magic). @@ -872,6 +881,10 @@ header and packfile URIs. Set this Boolean environment variable to false to prevent this redaction. +`GIT_NO_REPLACE_OBJECTS`:: + Setting and exporting this environment variable tells Git to + ignore replacement refs and do not replace Git objects. + `GIT_LITERAL_PATHSPECS`:: Setting this Boolean environment variable to true will cause Git to treat all pathspecs literally, rather than as glob patterns. For example, @@ -893,6 +906,11 @@ Setting this Boolean environment variable to true will cause Git to treat all pathspecs as case-insensitive. +`GIT_NO_LAZY_FETCH`:: + Setting this Boolean environment variable to true tells Git + not to lazily fetch missing objects from the promisor remote + on demand. + `GIT_REFLOG_ACTION`:: When a ref is updated, reflog entries are created to keep track of the reason why the ref was updated (which is
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index e5fac94..7c70932 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt
@@ -81,9 +81,6 @@ Here are the rules regarding the "flags" that you should follow when you are scripting Git: - * It's preferred to use the non-dashed form of Git commands, which means that - you should prefer `git foo` to `git-foo`. - * Splitting short options to separate words (prefer `git foo -a -b` to `git foo -ab`, the latter may not even work).
diff --git a/Documentation/gitprotocol-v2.txt b/Documentation/gitprotocol-v2.txt index 0b800ab..414bc62 100644 --- a/Documentation/gitprotocol-v2.txt +++ b/Documentation/gitprotocol-v2.txt
@@ -346,7 +346,8 @@ want-ref <ref> Indicates to the server that the client wants to retrieve a particular ref, where <ref> is the full name of a ref on the - server. + server. It is a protocol error to send want-ref for the + same ref more than once. If the 'sideband-all' feature is advertised, the following argument can be included in the client's request: @@ -361,7 +362,8 @@ If the 'packfile-uris' feature is advertised, the following argument can be included in the client's request as well as the potential addition of the 'packfile-uris' section in the server's response as -explained below. +explained below. Note that at most one `packfile-uris` line can be sent +to the server. packfile-uris <comma-separated-list-of-protocols> Indicates to the server that the client is willing to receive
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index a583b52..408d931 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt
@@ -341,8 +341,11 @@ Under `--pretty=reference`, this information will not be shown at all. --merge:: - After a failed merge, show refs that touch files having a - conflict and don't exist on all heads to merge. + Show commits touching conflicted paths in the range `HEAD...<other>`, + where `<other>` is the first existing pseudoref in `MERGE_HEAD`, + `CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works + when the index has unmerged entries. This option can be used to show + relevant commits when resolving conflicts from a 3-way merge. --boundary:: Output excluded boundary commits. Boundary commits are @@ -1019,6 +1022,10 @@ + 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. ++ +If some tips passed to the traversal are missing, they will be +considered as missing too, and the traversal will ignore them. In case +we cannot get their Object ID though, an error will be raised. --exclude-promisor-objects:: (For internal use only.) Prefilter object traversal at
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index c9d1d29..df788c7 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.44.0 +DEF_VER=v2.44.GIT LF=' '
diff --git a/advice.c b/advice.c index 6e9098f..4ba64ee 100644 --- a/advice.c +++ b/advice.c
@@ -79,6 +79,7 @@ static struct { [ADVICE_STATUS_U_OPTION] = { "statusUoption" }, [ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated" }, [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie" }, + [ADVICE_SUBMODULE_MERGE_CONFLICT] = { "submoduleMergeConflict" }, [ADVICE_SUGGEST_DETACHING_HEAD] = { "suggestDetachingHead" }, [ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath" }, [ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor" },
diff --git a/advice.h b/advice.h index 9d4f49a..7d0a821 100644 --- a/advice.h +++ b/advice.h
@@ -47,6 +47,7 @@ enum advice_type { ADVICE_STATUS_U_OPTION, ADVICE_SUBMODULES_NOT_UPDATED, ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE, + ADVICE_SUBMODULE_MERGE_CONFLICT, ADVICE_SUGGEST_DETACHING_HEAD, ADVICE_UPDATE_SPARSE_PATH, ADVICE_WAITING_FOR_EDITOR,
diff --git a/bisect.c b/bisect.c index f75e50c..60aae2f 100644 --- a/bisect.c +++ b/bisect.c
@@ -836,10 +836,11 @@ static void handle_skipped_merge_base(const struct object_id *mb) static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout) { enum bisect_error res = BISECT_OK; - struct commit_list *result; + struct commit_list *result = NULL; - result = repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1, - rev + 1); + if (repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1, + rev + 1, &result) < 0) + exit(128); for (; result; result = result->next) { const struct object_id *mb = &result->item->object.oid;
diff --git a/builtin/add.c b/builtin/add.c index ada7719..393c10c 100644 --- a/builtin/add.c +++ b/builtin/add.c
@@ -115,7 +115,7 @@ static int refresh(int verbose, const struct pathspec *pathspec) int i, ret = 0; char *skip_worktree_seen = NULL; struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP; - int flags = REFRESH_IGNORE_SKIP_WORKTREE | + unsigned int flags = REFRESH_IGNORE_SKIP_WORKTREE | (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET); seen = xcalloc(pathspec->nr, 1);
diff --git a/builtin/branch.c b/builtin/branch.c index cfb63cc..b3cbb7f 100644 --- a/builtin/branch.c +++ b/builtin/branch.c
@@ -158,6 +158,8 @@ static int branch_merged(int kind, const char *name, merged = reference_rev ? repo_in_merge_bases(the_repository, rev, reference_rev) : 0; + if (merged < 0) + exit(128); /* * After the safety valve is fully redefined to "check with @@ -166,9 +168,13 @@ static int branch_merged(int kind, const char *name, * any of the following code, but during the transition period, * a gentle reminder is in order. */ - if ((head_rev != reference_rev) && - (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) { - if (merged) + if (head_rev != reference_rev) { + int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0; + if (expect < 0) + exit(128); + if (expect == merged) + ; /* okay */ + else if (merged) warning(_("deleting branch '%s' that has been merged to\n" " '%s', but not yet merged to HEAD"), name, reference_name);
diff --git a/builtin/checkout.c b/builtin/checkout.c index 067c251..15293a3 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c
@@ -704,7 +704,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o, init_checkout_metadata(&opts.meta, info->refname, info->commit ? &info->commit->object.oid : null_oid(), NULL); - parse_tree(tree); + if (parse_tree(tree) < 0) + return 128; init_tree_desc(&tree_desc, tree->buffer, tree->size); switch (unpack_trees(1, &tree_desc, &opts)) { case -2: @@ -783,9 +784,15 @@ static int merge_working_tree(const struct checkout_opts *opts, if (new_branch_info->commit) BUG("'switch --orphan' should never accept a commit as starting point"); new_tree = parse_tree_indirect(the_hash_algo->empty_tree); - } else + if (!new_tree) + BUG("unable to read empty tree"); + } else { new_tree = repo_get_commit_tree(the_repository, new_branch_info->commit); + if (!new_tree) + return error(_("unable to read tree (%s)"), + oid_to_hex(&new_branch_info->commit->object.oid)); + } if (opts->discard_changes) { ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info); if (ret) @@ -820,7 +827,8 @@ static int merge_working_tree(const struct checkout_opts *opts, oid_to_hex(old_commit_oid)); init_tree_desc(&trees[0], tree->buffer, tree->size); - parse_tree(new_tree); + if (parse_tree(new_tree) < 0) + exit(128); tree = new_tree; init_tree_desc(&trees[1], tree->buffer, tree->size); @@ -1240,10 +1248,15 @@ static void setup_new_branch_info_and_source_tree( if (!new_branch_info->commit) { /* not a commit */ *source_tree = parse_tree_indirect(rev); + if (!*source_tree) + die(_("unable to read tree (%s)"), oid_to_hex(rev)); } else { parse_commit_or_die(new_branch_info->commit); *source_tree = repo_get_commit_tree(the_repository, new_branch_info->commit); + if (!*source_tree) + die(_("unable to read tree (%s)"), + oid_to_hex(&new_branch_info->commit->object.oid)); } }
diff --git a/builtin/clone.c b/builtin/clone.c index bad1b70..93892ab 100644 --- a/builtin/clone.c +++ b/builtin/clone.c
@@ -738,7 +738,8 @@ static int checkout(int submodule_progress, int filter_submodules) tree = parse_tree_indirect(&oid); if (!tree) die(_("unable to parse commit %s"), oid_to_hex(&oid)); - parse_tree(tree); + if (parse_tree(tree) < 0) + exit(128); init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) die(_("unable to checkout working tree")); @@ -926,6 +927,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct ref *mapped_refs = NULL; const struct ref *ref; struct strbuf key = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; const char *src_ref_prefix = "refs/heads/"; @@ -1126,6 +1128,50 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } /* + * We have a chicken-and-egg situation between initializing the refdb + * and spawning transport helpers: + * + * - Initializing the refdb requires us to know about the object + * format. We thus have to spawn the transport helper to learn + * about it. + * + * - The transport helper may want to access the Git repository. But + * because the refdb has not been initialized, we don't have "HEAD" + * or "refs/". Thus, the helper cannot find the Git repository. + * + * Ideally, we would have structured the helper protocol such that it's + * mandatory for the helper to first announce its capabilities without + * yet assuming a fully initialized repository. Like that, we could + * have added a "lazy-refdb-init" capability that announces whether the + * helper is ready to handle not-yet-initialized refdbs. If any helper + * didn't support them, we would have fully initialized the refdb with + * the SHA1 object format, but later on bailed out if we found out that + * the remote repository used a different object format. + * + * But we didn't, and thus we use the following workaround to partially + * initialize the repository's refdb such that it can be discovered by + * Git commands. To do so, we: + * + * - Create an invalid HEAD ref pointing at "refs/heads/.invalid". + * + * - Create the "refs/" directory. + * + * - Set up the ref storage format and repository version as + * required. + * + * This is sufficient for Git commands to discover the Git directory. + */ + initialize_repository_version(GIT_HASH_UNKNOWN, + the_repository->ref_storage_format, 1); + + strbuf_addf(&buf, "%s/HEAD", git_dir); + write_file(buf.buf, "ref: refs/heads/.invalid"); + + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/refs", git_dir); + safe_create_dir(buf.buf, 1); + + /* * additional config can be injected with -c, make sure it's included * after init_db, which clears the entire config environment. */ @@ -1453,6 +1499,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) free(remote_name); strbuf_release(&reflog_msg); strbuf_release(&branch_top); + strbuf_release(&buf); strbuf_release(&key); free_refs(mapped_refs); free_refs(remote_head_points_at);
diff --git a/builtin/commit.c b/builtin/commit.c index 6d1fa71..a911972 100644 --- a/builtin/commit.c +++ b/builtin/commit.c
@@ -331,7 +331,8 @@ static void create_base_index(const struct commit *current_head) tree = parse_tree_indirect(¤t_head->object.oid); if (!tree) die(_("failed to unpack HEAD tree object")); - parse_tree(tree); + if (parse_tree(tree) < 0) + exit(128); init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) exit(128); /* We've already reported the error, finish dying */ @@ -737,7 +738,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix, const char *hook_arg2 = NULL; int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE); int old_display_comment_prefix; - int merge_contains_scissors = 0; int invoked_hook; /* This checks and barfs if author is badly specified */ @@ -841,7 +841,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start) - merge_contains_scissors = 1; + s->added_cut_line = 1; } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) { if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0) die_errno(_("could not read SQUASH_MSG")); @@ -924,9 +924,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, " yourself if you want to.\n" "An empty message aborts the commit.\n"); if (whence != FROM_COMMIT) { - if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS && - !merge_contains_scissors) - wt_status_add_cut_line(s->fp); + if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) + wt_status_add_cut_line(s); status_printf_ln( s, GIT_COLOR_NORMAL, whence == FROM_MERGE ? @@ -946,8 +945,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL) status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char); else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { - if (whence == FROM_COMMIT && !merge_contains_scissors) - wt_status_add_cut_line(s->fp); + if (whence == FROM_COMMIT) + wt_status_add_cut_line(s); } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 92eda20..71a195c 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c
@@ -1625,6 +1625,7 @@ static int update_branch(struct branch *b) oidclr(&old_oid); if (!force_update && !is_null_oid(&old_oid)) { struct commit *old_cmit, *new_cmit; + int ret; old_cmit = lookup_commit_reference_gently(the_repository, &old_oid, 0); @@ -1633,7 +1634,10 @@ static int update_branch(struct branch *b) if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); - if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) { + ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit); + if (ret < 0) + exit(128); + if (!ret) { warning("Not updating %s" " (new tip %s does not contain %s)", b->name, oid_to_hex(&b->oid),
diff --git a/builtin/fetch.c b/builtin/fetch.c index 3aedfd1..46a7934 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c
@@ -448,9 +448,8 @@ static void filter_prefetch_refspec(struct refspec *rs) continue; if (!rs->items[i].dst || (rs->items[i].src && - !strncmp(rs->items[i].src, - ref_namespace[NAMESPACE_TAGS].ref, - strlen(ref_namespace[NAMESPACE_TAGS].ref)))) { + starts_with(rs->items[i].src, + ref_namespace[NAMESPACE_TAGS].ref))) { int j; free(rs->items[i].src); @@ -982,6 +981,8 @@ static int update_local_ref(struct ref *ref, uint64_t t_before = getnanotime(); fast_forward = repo_in_merge_bases(the_repository, current, updated); + if (fast_forward < 0) + exit(128); forced_updates_ms += (getnanotime() - t_before) / 1000000; } else { fast_forward = 1;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 3885a9c..919282e 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c
@@ -20,10 +20,10 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; - int icase = 0; + int icase = 0, include_root_refs = 0, from_stdin = 0; struct ref_filter filter = REF_FILTER_INIT; struct ref_format format = REF_FORMAT_INIT; - int from_stdin = 0; + unsigned int flags = FILTER_REFS_REGULAR; struct strvec vec = STRVEC_INIT; struct option opts[] = { @@ -53,6 +53,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")), OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")), OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")), + OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")), OPT_END(), }; @@ -96,8 +97,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) filter.name_patterns = argv; } + if (include_root_refs) + flags |= FILTER_REFS_ROOT_REFS; + filter.match_as_path = 1; - filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format); + filter_and_format_refs(&filter, flags, sorting, &format); ref_filter_clear(&filter); ref_sorting_release(sorting);
diff --git a/builtin/log.c b/builtin/log.c index db1808d..e5da0d1 100644 --- a/builtin/log.c +++ b/builtin/log.c
@@ -1625,7 +1625,7 @@ static struct commit *get_base_commit(const char *base_commit, { struct commit *base = NULL; struct commit **rev; - int i = 0, rev_nr = 0, auto_select, die_on_failure; + int i = 0, rev_nr = 0, auto_select, die_on_failure, ret; switch (auto_base) { case AUTO_BASE_NEVER: @@ -1658,7 +1658,7 @@ static struct commit *get_base_commit(const char *base_commit, struct branch *curr_branch = branch_get(NULL); const char *upstream = branch_get_upstream(curr_branch, NULL); if (upstream) { - struct commit_list *base_list; + struct commit_list *base_list = NULL; struct commit *commit; struct object_id oid; @@ -1669,11 +1669,12 @@ static struct commit *get_base_commit(const char *base_commit, return NULL; } commit = lookup_commit_or_die(&oid, "upstream base"); - base_list = repo_get_merge_bases_many(the_repository, - commit, total, - list); - /* There should be one and only one merge base. */ - if (!base_list || base_list->next) { + if (repo_get_merge_bases_many(the_repository, + commit, total, + list, + &base_list) < 0 || + /* There should be one and only one merge base. */ + !base_list || base_list->next) { if (die_on_failure) { die(_("could not find exact merge base")); } else { @@ -1704,11 +1705,11 @@ static struct commit *get_base_commit(const char *base_commit, */ while (rev_nr > 1) { for (i = 0; i < rev_nr / 2; i++) { - struct commit_list *merge_base; - merge_base = repo_get_merge_bases(the_repository, - rev[2 * i], - rev[2 * i + 1]); - if (!merge_base || merge_base->next) { + struct commit_list *merge_base = NULL; + if (repo_get_merge_bases(the_repository, + rev[2 * i], + rev[2 * i + 1], &merge_base) < 0 || + !merge_base || merge_base->next) { if (die_on_failure) { die(_("failed to find exact merge base")); } else { @@ -1725,7 +1726,10 @@ static struct commit *get_base_commit(const char *base_commit, rev_nr = DIV_ROUND_UP(rev_nr, 2); } - if (!repo_in_merge_bases(the_repository, base, rev[0])) { + ret = repo_in_merge_bases(the_repository, base, rev[0]); + if (ret < 0) + exit(128); + if (!ret) { if (die_on_failure) { die(_("base commit should be the ancestor of revision list")); } else {
diff --git a/builtin/merge-base.c b/builtin/merge-base.c index d26e8fb..5a8e729 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c
@@ -10,10 +10,13 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { - struct commit_list *result, *r; + struct commit_list *result = NULL, *r; - result = repo_get_merge_bases_many_dirty(the_repository, rev[0], - rev_nr - 1, rev + 1); + if (repo_get_merge_bases_many_dirty(the_repository, rev[0], + rev_nr - 1, rev + 1, &result) < 0) { + free_commit_list(result); + return -1; + } if (!result) return 1; @@ -74,13 +77,17 @@ static int handle_independent(int count, const char **args) static int handle_octopus(int count, const char **args, int show_all) { struct commit_list *revs = NULL; - struct commit_list *result, *rev; + struct commit_list *result = NULL, *rev; int i; for (i = count - 1; i >= 0; i--) commit_list_insert(get_commit_reference(args[i]), &revs); - result = get_octopus_merge_bases(revs); + if (get_octopus_merge_bases(revs, &result) < 0) { + free_commit_list(revs); + free_commit_list(result); + return 128; + } free_commit_list(revs); reduce_heads_replace(&result); @@ -100,12 +107,16 @@ static int handle_octopus(int count, const char **args, int show_all) static int handle_is_ancestor(int argc, const char **argv) { struct commit *one, *two; + int ret; if (argc != 2) die("--is-ancestor takes exactly two commits"); one = get_commit_reference(argv[0]); two = get_commit_reference(argv[1]); - if (repo_in_merge_bases(the_repository, one, two)) + ret = repo_in_merge_bases(the_repository, one, two); + if (ret < 0) + exit(128); + if (ret) return 0; else return 1;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 3bdec53..05d0cad 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c
@@ -429,41 +429,56 @@ static int real_merge(struct merge_tree_options *o, struct merge_options opt; copy_merge_options(&opt, &o->merge_options); - parent1 = get_merge_parent(branch1); - if (!parent1) - help_unknown_ref(branch1, "merge-tree", - _("not something we can merge")); - - parent2 = get_merge_parent(branch2); - if (!parent2) - help_unknown_ref(branch2, "merge-tree", - _("not something we can merge")); - opt.show_rename_progress = 0; opt.branch1 = branch1; opt.branch2 = branch2; if (merge_base) { - struct commit *base_commit; struct tree *base_tree, *parent1_tree, *parent2_tree; - base_commit = lookup_commit_reference_by_name(merge_base); - if (!base_commit) - die(_("could not lookup commit '%s'"), merge_base); + /* + * We actually only need the trees because we already + * have a merge base. + */ + struct object_id base_oid, head_oid, merge_oid; + + if (repo_get_oid_treeish(the_repository, merge_base, &base_oid)) + die(_("could not parse as tree '%s'"), merge_base); + base_tree = parse_tree_indirect(&base_oid); + if (!base_tree) + die(_("unable to read tree (%s)"), oid_to_hex(&base_oid)); + if (repo_get_oid_treeish(the_repository, branch1, &head_oid)) + die(_("could not parse as tree '%s'"), branch1); + parent1_tree = parse_tree_indirect(&head_oid); + if (!parent1_tree) + die(_("unable to read tree (%s)"), oid_to_hex(&head_oid)); + if (repo_get_oid_treeish(the_repository, branch2, &merge_oid)) + die(_("could not parse as tree '%s'"), branch2); + parent2_tree = parse_tree_indirect(&merge_oid); + if (!parent2_tree) + die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid)); opt.ancestor = merge_base; - base_tree = repo_get_commit_tree(the_repository, base_commit); - parent1_tree = repo_get_commit_tree(the_repository, parent1); - parent2_tree = repo_get_commit_tree(the_repository, parent2); merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result); } else { + parent1 = get_merge_parent(branch1); + if (!parent1) + help_unknown_ref(branch1, "merge-tree", + _("not something we can merge")); + + parent2 = get_merge_parent(branch2); + if (!parent2) + help_unknown_ref(branch2, "merge-tree", + _("not something we can merge")); + /* * Get the merge bases, in reverse order; see comment above * merge_incore_recursive in merge-ort.h */ - merge_bases = repo_get_merge_bases(the_repository, parent1, - parent2); + if (repo_get_merge_bases(the_repository, parent1, + parent2, &merge_bases) < 0) + exit(128); if (!merge_bases && !o->allow_unrelated_histories) die(_("refusing to merge unrelated histories")); merge_bases = reverse_commit_list(merge_bases);
diff --git a/builtin/merge.c b/builtin/merge.c index 935c8a5..a0ba1f9 100644 --- a/builtin/merge.c +++ b/builtin/merge.c
@@ -1513,13 +1513,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!remoteheads) ; /* already up-to-date */ - else if (!remoteheads->next) - common = repo_get_merge_bases(the_repository, head_commit, - remoteheads->item); - else { + else if (!remoteheads->next) { + if (repo_get_merge_bases(the_repository, head_commit, + remoteheads->item, &common) < 0) { + ret = 2; + goto done; + } + } else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); - common = get_octopus_merge_bases(list); + if (get_octopus_merge_bases(list, &common) < 0) { + free(list); + ret = 2; + goto done; + } free(list); } @@ -1626,7 +1633,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) struct commit_list *j; for (j = remoteheads; j; j = j->next) { - struct commit_list *common_one; + struct commit_list *common_one = NULL; struct commit *common_item; /* @@ -1634,9 +1641,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = repo_get_merge_bases(the_repository, - head_commit, - j->item); + if (repo_get_merge_bases(the_repository, head_commit, + j->item, &common_one) < 0) + exit(128); + common_item = common_one->item; free_commit_list(common_one); if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 2dd1807..ad9930c 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c
@@ -15,6 +15,7 @@ #include "commit-slab.h" #include "commit-graph.h" #include "wildmatch.h" +#include "mem-pool.h" /* * One day. See the 'name a rev shortly after epoch' test in t6120 when @@ -155,30 +156,25 @@ static struct rev_name *create_or_update_name(struct commit *commit, return name; } -static char *get_parent_name(const struct rev_name *name, int parent_number) +static char *get_parent_name(const struct rev_name *name, int parent_number, + struct mem_pool *string_pool) { - struct strbuf sb = STRBUF_INIT; size_t len; strip_suffix(name->tip_name, "^0", &len); if (name->generation > 0) { - strbuf_grow(&sb, len + - 1 + decimal_width(name->generation) + - 1 + decimal_width(parent_number)); - strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name, - name->generation, parent_number); + return mem_pool_strfmt(string_pool, "%.*s~%d^%d", + (int)len, name->tip_name, + name->generation, parent_number); } else { - strbuf_grow(&sb, len + - 1 + decimal_width(parent_number)); - strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name, - parent_number); + return mem_pool_strfmt(string_pool, "%.*s^%d", + (int)len, name->tip_name, parent_number); } - return strbuf_detach(&sb, NULL); } static void name_rev(struct commit *start_commit, const char *tip_name, timestamp_t taggerdate, - int from_tag, int deref) + int from_tag, int deref, struct mem_pool *string_pool) { struct prio_queue queue; struct commit *commit; @@ -195,9 +191,10 @@ static void name_rev(struct commit *start_commit, if (!start_name) return; if (deref) - start_name->tip_name = xstrfmt("%s^0", tip_name); + start_name->tip_name = mem_pool_strfmt(string_pool, "%s^0", + tip_name); else - start_name->tip_name = xstrdup(tip_name); + start_name->tip_name = mem_pool_strdup(string_pool, tip_name); memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */ prio_queue_put(&queue, start_commit); @@ -235,7 +232,8 @@ static void name_rev(struct commit *start_commit, if (parent_number > 1) parent_name->tip_name = get_parent_name(name, - parent_number); + parent_number, + string_pool); else parent_name->tip_name = name->tip_name; ALLOC_GROW(parents_to_queue, @@ -415,7 +413,7 @@ static int name_ref(const char *path, const struct object_id *oid, return 0; } -static void name_tips(void) +static void name_tips(struct mem_pool *string_pool) { int i; @@ -428,7 +426,7 @@ static void name_tips(void) struct tip_table_entry *e = &tip_table.table[i]; if (e->commit) { name_rev(e->commit, e->refname, e->taggerdate, - e->from_tag, e->deref); + e->from_tag, e->deref, string_pool); } } } @@ -561,6 +559,7 @@ static void name_rev_line(char *p, struct name_ref_data *data) int cmd_name_rev(int argc, const char **argv, const char *prefix) { + struct mem_pool string_pool; struct object_array revs = OBJECT_ARRAY_INIT; int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP }; @@ -587,6 +586,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_END(), }; + mem_pool_init(&string_pool, 0); init_commit_rev_name(&rev_names); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); @@ -648,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) adjust_cutoff_timestamp_for_slop(); for_each_ref(name_ref, &data); - name_tips(); + name_tips(&string_pool); if (annotate_stdin) { struct strbuf sb = STRBUF_INIT; @@ -676,6 +676,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) always, allow_undefined, data.name_only); } + UNLEAK(string_pool); UNLEAK(revs); return 0; }
diff --git a/builtin/pull.c b/builtin/pull.c index 73a68b7..72cbb76 100644 --- a/builtin/pull.c +++ b/builtin/pull.c
@@ -815,7 +815,7 @@ static int get_octopus_merge_base(struct object_id *merge_base, const struct object_id *merge_head, const struct object_id *fork_point) { - struct commit_list *revs = NULL, *result; + struct commit_list *revs = NULL, *result = NULL; commit_list_insert(lookup_commit_reference(the_repository, curr_head), &revs); @@ -825,7 +825,8 @@ static int get_octopus_merge_base(struct object_id *merge_base, commit_list_insert(lookup_commit_reference(the_repository, fork_point), &revs); - result = get_octopus_merge_bases(revs); + if (get_octopus_merge_bases(revs, &result) < 0) + exit(128); free_commit_list(revs); reduce_heads_replace(&result); @@ -926,6 +927,8 @@ static int get_can_ff(struct object_id *orig_head, merge_head = lookup_commit_reference(the_repository, orig_merge_head); ret = repo_is_descendant_of(the_repository, merge_head, list); free_commit_list(list); + if (ret < 0) + exit(128); return ret; } @@ -950,6 +953,8 @@ static int already_up_to_date(struct object_id *orig_head, commit_list_insert(theirs, &list); ok = repo_is_descendant_of(the_repository, ours, list); free_commit_list(list); + if (ok < 0) + exit(128); if (!ok) return 0; }
diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 20e7db1..1ffd863 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c
@@ -261,7 +261,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) cache_tree_free(&the_index.cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; - parse_tree(tree); + if (parse_tree(tree) < 0) + return 128; init_tree_desc(t+i, tree->buffer, tree->size); } if (unpack_trees(nr_trees, t, &opts))
diff --git a/builtin/rebase.c b/builtin/rebase.c index 6ead946..be78769 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c
@@ -867,7 +867,8 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream, if (!upstream) goto done; - merge_bases = repo_get_merge_bases(the_repository, upstream, head); + if (repo_get_merge_bases(the_repository, upstream, head, &merge_bases) < 0) + exit(128); if (!merge_bases || merge_bases->next) goto done; @@ -886,8 +887,9 @@ static void fill_branch_base(struct rebase_options *options, { struct commit_list *merge_bases = NULL; - merge_bases = repo_get_merge_bases(the_repository, options->onto, - options->orig_head); + if (repo_get_merge_bases(the_repository, options->onto, + options->orig_head, &merge_bases) < 0) + exit(128); if (!merge_bases || merge_bases->next) oidcpy(branch_base, null_oid()); else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index db65607..56d8a77 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c
@@ -1526,6 +1526,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) starts_with(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; + int ret2; old_object = parse_object(the_repository, old_oid); new_object = parse_object(the_repository, new_oid); @@ -1539,7 +1540,10 @@ static const char *update(struct command *cmd, struct shallow_info *si) } old_commit = (struct commit *)old_object; new_commit = (struct commit *)new_object; - if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) { + ret2 = repo_in_merge_bases(the_repository, old_commit, new_commit); + if (ret2 < 0) + exit(128); + if (!ret2) { rp_error("denying non-fast-forward %s" " (you should pull first)", name); ret = "non-fast-forward";
diff --git a/builtin/reset.c b/builtin/reset.c index f0bf29a..1d62ff6 100644 --- a/builtin/reset.c +++ b/builtin/reset.c
@@ -116,6 +116,10 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t if (reset_type == MIXED || reset_type == HARD) { tree = parse_tree_indirect(oid); + if (!tree) { + error(_("unable to read tree (%s)"), oid_to_hex(oid)); + goto out; + } prime_cache_tree(the_repository, the_repository->index, tree); }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c index b3f4783..ec455aa 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c
@@ -545,6 +545,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) * * Let "--missing" to conditionally set fetch_if_missing. */ + /* + * NEEDSWORK: These loops that attempt to find presence of + * options without understanding that the options they are + * skipping are broken (e.g., it would not know "--grep + * --exclude-promisor-objects" is not triggering + * "--exclude-promisor-objects" option). We really need + * setup_revisions() to have a mechanism to allow and disallow + * some sets of options for different commands (like rev-list, + * replay, etc). Such a mechanism should do an early parsing + * of options and be able to manage the `--missing=...` and + * `--exclude-promisor-objects` options below. + */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--exclude-promisor-objects")) { @@ -753,8 +765,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (arg_print_omitted) oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE); - if (arg_missing_action == MA_PRINT) + if (arg_missing_action == MA_PRINT) { oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE); + /* Add missing tips */ + oidset_insert_from_set(&missing_objects, &revs.missing_commits); + oidset_clear(&revs.missing_commits); + } traverse_commit_list_filtered( &revs, show_commit, show_object, &info,
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index d089876..181c703 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c
@@ -297,7 +297,7 @@ static int try_difference(const char *arg) show_rev(NORMAL, &end_oid, end); show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); if (symmetric) { - struct commit_list *exclude; + struct commit_list *exclude = NULL; struct commit *a, *b; a = lookup_commit_reference(the_repository, &start_oid); b = lookup_commit_reference(the_repository, &end_oid); @@ -305,7 +305,8 @@ static int try_difference(const char *arg) *dotdot = '.'; return 0; } - exclude = repo_get_merge_bases(the_repository, a, b); + if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) + exit(128); while (exclude) { struct commit *commit = pop_commit(&exclude); show_rev(REVERSED, &commit->object.oid, NULL);
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index 9b021ef..15afb97 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c
@@ -8,6 +8,7 @@ #include "replace-object.h" #include "upload-pack.h" #include "serve.h" +#include "commit.h" static const char * const upload_pack_usage[] = { N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n" @@ -37,6 +38,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("upload-pack"); disable_replace_refs(); + save_commit_buffer = 0; argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
diff --git a/cache-tree.c b/cache-tree.c index 64678fe..80ca832 100644 --- a/cache-tree.c +++ b/cache-tree.c
@@ -778,8 +778,8 @@ static void prime_cache_tree_rec(struct repository *r, struct cache_tree_sub *sub; struct tree *subtree = lookup_tree(r, &entry.oid); - if (!subtree->object.parsed) - parse_tree(subtree); + if (parse_tree(subtree) < 0) + exit(128); sub = cache_tree_sub(it, entry.path); sub->cache_tree = cache_tree();
diff --git a/commit-reach.c b/commit-reach.c index ecc913f..8f9b008 100644 --- a/commit-reach.c +++ b/commit-reach.c
@@ -49,13 +49,14 @@ static int queue_has_nonstale(struct prio_queue *queue) } /* all input commits in one and twos[] must have been parsed! */ -static struct commit_list *paint_down_to_common(struct repository *r, - struct commit *one, int n, - struct commit **twos, - timestamp_t min_generation) +static int paint_down_to_common(struct repository *r, + struct commit *one, int n, + struct commit **twos, + timestamp_t min_generation, + int ignore_missing_commits, + struct commit_list **result) { struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; - struct commit_list *result = NULL; int i; timestamp_t last_gen = GENERATION_NUMBER_INFINITY; @@ -64,8 +65,8 @@ static struct commit_list *paint_down_to_common(struct repository *r, one->object.flags |= PARENT1; if (!n) { - commit_list_append(one, &result); - return result; + commit_list_append(one, result); + return 0; } prio_queue_put(&queue, one); @@ -93,7 +94,7 @@ static struct commit_list *paint_down_to_common(struct repository *r, if (flags == (PARENT1 | PARENT2)) { if (!(commit->object.flags & RESULT)) { commit->object.flags |= RESULT; - commit_list_insert_by_date(commit, &result); + commit_list_insert_by_date(commit, result); } /* Mark parents of a found merge stale */ flags |= STALE; @@ -104,67 +105,97 @@ static struct commit_list *paint_down_to_common(struct repository *r, parents = parents->next; if ((p->object.flags & flags) == flags) continue; - if (repo_parse_commit(r, p)) - return NULL; + if (repo_parse_commit(r, p)) { + clear_prio_queue(&queue); + free_commit_list(*result); + *result = NULL; + /* + * At this stage, we know that the commit is + * missing: `repo_parse_commit()` uses + * `OBJECT_INFO_DIE_IF_CORRUPT` and therefore + * corrupt commits would already have been + * dispatched with a `die()`. + */ + if (ignore_missing_commits) + return 0; + return error(_("could not parse commit %s"), + oid_to_hex(&p->object.oid)); + } p->object.flags |= flags; prio_queue_put(&queue, p); } } clear_prio_queue(&queue); - return result; + return 0; } -static struct commit_list *merge_bases_many(struct repository *r, - struct commit *one, int n, - struct commit **twos) +static int merge_bases_many(struct repository *r, + struct commit *one, int n, + struct commit **twos, + struct commit_list **result) { struct commit_list *list = NULL; - struct commit_list *result = NULL; int i; for (i = 0; i < n; i++) { - if (one == twos[i]) + if (one == twos[i]) { /* * We do not mark this even with RESULT so we do not * have to clean it up. */ - return commit_list_insert(one, &result); + *result = commit_list_insert(one, result); + return 0; + } } + if (!one) + return 0; if (repo_parse_commit(r, one)) - return NULL; + return error(_("could not parse commit %s"), + oid_to_hex(&one->object.oid)); for (i = 0; i < n; i++) { + if (!twos[i]) + return 0; if (repo_parse_commit(r, twos[i])) - return NULL; + return error(_("could not parse commit %s"), + oid_to_hex(&twos[i]->object.oid)); } - list = paint_down_to_common(r, one, n, twos, 0); + if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) { + free_commit_list(list); + return -1; + } while (list) { struct commit *commit = pop_commit(&list); if (!(commit->object.flags & STALE)) - commit_list_insert_by_date(commit, &result); + commit_list_insert_by_date(commit, result); } - return result; + return 0; } -struct commit_list *get_octopus_merge_bases(struct commit_list *in) +int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result) { - struct commit_list *i, *j, *k, *ret = NULL; + struct commit_list *i, *j, *k; if (!in) - return ret; + return 0; - commit_list_insert(in->item, &ret); + commit_list_insert(in->item, result); for (i = in->next; i; i = i->next) { struct commit_list *new_commits = NULL, *end = NULL; - for (j = ret; j; j = j->next) { - struct commit_list *bases; - bases = repo_get_merge_bases(the_repository, i->item, - j->item); + for (j = *result; j; j = j->next) { + struct commit_list *bases = NULL; + if (repo_get_merge_bases(the_repository, i->item, + j->item, &bases) < 0) { + free_commit_list(bases); + free_commit_list(*result); + *result = NULL; + return -1; + } if (!new_commits) new_commits = bases; else @@ -172,10 +203,10 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in) for (k = bases; k; k = k->next) end = k; } - free_commit_list(ret); - ret = new_commits; + free_commit_list(*result); + *result = new_commits; } - return ret; + return 0; } static int remove_redundant_no_gen(struct repository *r, @@ -193,7 +224,7 @@ static int remove_redundant_no_gen(struct repository *r, for (i = 0; i < cnt; i++) repo_parse_commit(r, array[i]); for (i = 0; i < cnt; i++) { - struct commit_list *common; + struct commit_list *common = NULL; timestamp_t min_generation = commit_graph_generation(array[i]); if (redundant[i]) @@ -209,8 +240,16 @@ static int remove_redundant_no_gen(struct repository *r, if (curr_generation < min_generation) min_generation = curr_generation; } - common = paint_down_to_common(r, array[i], filled, - work, min_generation); + if (paint_down_to_common(r, array[i], filled, + work, min_generation, 0, &common)) { + clear_commit_marks(array[i], all_flags); + clear_commit_marks_many(filled, work, all_flags); + free_commit_list(common); + free(work); + free(redundant); + free(filled_index); + return -1; + } if (array[i]->object.flags & PARENT2) redundant[i] = 1; for (j = 0; j < filled; j++) @@ -375,69 +414,77 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt return remove_redundant_no_gen(r, array, cnt); } -static struct commit_list *get_merge_bases_many_0(struct repository *r, - struct commit *one, - int n, - struct commit **twos, - int cleanup) +static int get_merge_bases_many_0(struct repository *r, + struct commit *one, + int n, + struct commit **twos, + int cleanup, + struct commit_list **result) { struct commit_list *list; struct commit **rslt; - struct commit_list *result; int cnt, i; - result = merge_bases_many(r, one, n, twos); + if (merge_bases_many(r, one, n, twos, result) < 0) + return -1; for (i = 0; i < n; i++) { if (one == twos[i]) - return result; + return 0; } - if (!result || !result->next) { + if (!*result || !(*result)->next) { if (cleanup) { clear_commit_marks(one, all_flags); clear_commit_marks_many(n, twos, all_flags); } - return result; + return 0; } /* There are more than one */ - cnt = commit_list_count(result); + cnt = commit_list_count(*result); CALLOC_ARRAY(rslt, cnt); - for (list = result, i = 0; list; list = list->next) + for (list = *result, i = 0; list; list = list->next) rslt[i++] = list->item; - free_commit_list(result); + free_commit_list(*result); + *result = NULL; clear_commit_marks(one, all_flags); clear_commit_marks_many(n, twos, all_flags); cnt = remove_redundant(r, rslt, cnt); - result = NULL; + if (cnt < 0) { + free(rslt); + return -1; + } for (i = 0; i < cnt; i++) - commit_list_insert_by_date(rslt[i], &result); + commit_list_insert_by_date(rslt[i], result); free(rslt); - return result; + return 0; } -struct commit_list *repo_get_merge_bases_many(struct repository *r, - struct commit *one, - int n, - struct commit **twos) +int repo_get_merge_bases_many(struct repository *r, + struct commit *one, + int n, + struct commit **twos, + struct commit_list **result) { - return get_merge_bases_many_0(r, one, n, twos, 1); + return get_merge_bases_many_0(r, one, n, twos, 1, result); } -struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r, - struct commit *one, - int n, - struct commit **twos) +int repo_get_merge_bases_many_dirty(struct repository *r, + struct commit *one, + int n, + struct commit **twos, + struct commit_list **result) { - return get_merge_bases_many_0(r, one, n, twos, 0); + return get_merge_bases_many_0(r, one, n, twos, 0, result); } -struct commit_list *repo_get_merge_bases(struct repository *r, - struct commit *one, - struct commit *two) +int repo_get_merge_bases(struct repository *r, + struct commit *one, + struct commit *two, + struct commit_list **result) { - return get_merge_bases_many_0(r, one, 1, &two, 1); + return get_merge_bases_many_0(r, one, 1, &two, 1, result); } /* @@ -460,11 +507,13 @@ int repo_is_descendant_of(struct repository *r, } else { while (with_commit) { struct commit *other; + int ret; other = with_commit->item; with_commit = with_commit->next; - if (repo_in_merge_bases_many(r, other, 1, &commit)) - return 1; + ret = repo_in_merge_bases_many(r, other, 1, &commit, 0); + if (ret) + return ret; } return 0; } @@ -474,17 +523,18 @@ int repo_is_descendant_of(struct repository *r, * Is "commit" an ancestor of one of the "references"? */ int repo_in_merge_bases_many(struct repository *r, struct commit *commit, - int nr_reference, struct commit **reference) + int nr_reference, struct commit **reference, + int ignore_missing_commits) { - struct commit_list *bases; + struct commit_list *bases = NULL; int ret = 0, i; timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO; if (repo_parse_commit(r, commit)) - return ret; + return ignore_missing_commits ? 0 : -1; for (i = 0; i < nr_reference; i++) { if (repo_parse_commit(r, reference[i])) - return ret; + return ignore_missing_commits ? 0 : -1; generation = commit_graph_generation(reference[i]); if (generation > max_generation) @@ -495,10 +545,11 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit, if (generation > max_generation) return ret; - bases = paint_down_to_common(r, commit, - nr_reference, reference, - generation); - if (commit->object.flags & PARENT2) + if (paint_down_to_common(r, commit, + nr_reference, reference, + generation, ignore_missing_commits, &bases)) + ret = -1; + else if (commit->object.flags & PARENT2) ret = 1; clear_commit_marks(commit, all_flags); clear_commit_marks_many(nr_reference, reference, all_flags); @@ -551,6 +602,10 @@ struct commit_list *reduce_heads(struct commit_list *heads) } } num_head = remove_redundant(the_repository, array, num_head); + if (num_head < 0) { + free(array); + return NULL; + } for (i = 0; i < num_head; i++) tail = &commit_list_insert(array[i], tail)->next; free(array); @@ -593,6 +648,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid) commit_list_insert(old_commit, &old_commit_list); ret = repo_is_descendant_of(the_repository, new_commit, old_commit_list); + if (ret < 0) + exit(128); free_commit_list(old_commit_list); return ret; }
diff --git a/commit-reach.h b/commit-reach.h index 35c4da4..bf63cc4 100644 --- a/commit-reach.h +++ b/commit-reach.h
@@ -9,18 +9,21 @@ struct ref_filter; struct object_id; struct object_array; -struct commit_list *repo_get_merge_bases(struct repository *r, - struct commit *rev1, - struct commit *rev2); -struct commit_list *repo_get_merge_bases_many(struct repository *r, - struct commit *one, int n, - struct commit **twos); +int repo_get_merge_bases(struct repository *r, + struct commit *rev1, + struct commit *rev2, + struct commit_list **result); +int repo_get_merge_bases_many(struct repository *r, + struct commit *one, int n, + struct commit **twos, + struct commit_list **result); /* To be used only when object flags after this call no longer matter */ -struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r, - struct commit *one, int n, - struct commit **twos); +int repo_get_merge_bases_many_dirty(struct repository *r, + struct commit *one, int n, + struct commit **twos, + struct commit_list **result); -struct commit_list *get_octopus_merge_bases(struct commit_list *in); +int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result); int repo_is_descendant_of(struct repository *r, struct commit *commit, @@ -30,7 +33,8 @@ int repo_in_merge_bases(struct repository *r, struct commit *reference); int repo_in_merge_bases_many(struct repository *r, struct commit *commit, - int nr_reference, struct commit **reference); + int nr_reference, struct commit **reference, + int ignore_missing_commits); /* * Takes a list of commits and returns a new list where those
diff --git a/commit.c b/commit.c index ef679a0..467be9f 100644 --- a/commit.c +++ b/commit.c
@@ -1052,7 +1052,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit) { struct object_id oid; struct rev_collect revs; - struct commit_list *bases; + struct commit_list *bases = NULL; int i; struct commit *ret = NULL; char *full_refname; @@ -1077,8 +1077,9 @@ struct commit *get_fork_point(const char *refname, struct commit *commit) for (i = 0; i < revs.nr; i++) revs.commit[i]->object.flags &= ~TMP_MARK; - bases = repo_get_merge_bases_many(the_repository, commit, revs.nr, - revs.commit); + if (repo_get_merge_bases_many(the_repository, commit, revs.nr, + revs.commit, &bases) < 0) + exit(128); /* * There should be one and only one merge base, when we found
diff --git a/compat/compiler.h b/compat/compiler.h index 10dbb65..e9ad9db 100644 --- a/compat/compiler.h +++ b/compat/compiler.h
@@ -1,7 +1,6 @@ #ifndef COMPILER_H #define COMPILER_H -#include "git-compat-util.h" #include "strbuf.h" #ifdef __GLIBC__
diff --git a/compat/disk.h b/compat/disk.h index 6c979c2..23bc1be 100644 --- a/compat/disk.h +++ b/compat/disk.h
@@ -1,7 +1,6 @@ #ifndef COMPAT_DISK_H #define COMPAT_DISK_H -#include "git-compat-util.h" #include "abspath.h" #include "gettext.h"
diff --git a/diff-lib.c b/diff-lib.c index 6c8df04..5e8717c 100644 --- a/diff-lib.c +++ b/diff-lib.c
@@ -570,7 +570,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb) { int i; struct commit *mb_child[2] = {0}; - struct commit_list *merge_bases; + struct commit_list *merge_bases = NULL; for (i = 0; i < revs->pending.nr; i++) { struct object *obj = revs->pending.objects[i].item; @@ -597,7 +597,8 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb) mb_child[1] = lookup_commit_reference(the_repository, &oid); } - merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]); + if (repo_get_merge_bases(the_repository, mb_child[0], mb_child[1], &merge_bases) < 0) + exit(128); if (!merge_bases) die(_("no merge base found")); if (merge_bases->next)
diff --git a/environment.c b/environment.c index 90632a3..60706ea 100644 --- a/environment.c +++ b/environment.c
@@ -207,6 +207,9 @@ void setup_git_env(const char *git_dir) shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); if (shallow_file) set_alternate_shallow_file(the_repository, shallow_file, 0); + + if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0)) + fetch_if_missing = 0; } int is_bare_repository(void)
diff --git a/environment.h b/environment.h index e5351c9..5cec19c 100644 --- a/environment.h +++ b/environment.h
@@ -36,6 +36,7 @@ const char *getenv_safe(struct strvec *argv, const char *name); #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES" #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS" #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE" +#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]"
diff --git a/git.c b/git.c index 7068a18..5265f92 100644 --- a/git.c +++ b/git.c
@@ -4,6 +4,7 @@ #include "exec-cmd.h" #include "gettext.h" #include "help.h" +#include "object-file.h" #include "pager.h" #include "read-cache-ll.h" #include "run-command.h" @@ -186,6 +187,11 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) use_pager = 0; if (envchanged) *envchanged = 1; + } else if (!strcmp(cmd, "--no-lazy-fetch")) { + fetch_if_missing = 0; + setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; } else if (!strcmp(cmd, "--no-replace-objects")) { disable_replace_refs(); setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
diff --git a/http-push.c b/http-push.c index 12d1113..33db41b 100644 --- a/http-push.c +++ b/http-push.c
@@ -1575,8 +1575,11 @@ static int verify_merge_base(struct object_id *head_oid, struct ref *remote) struct commit *head = lookup_commit_or_die(head_oid, "HEAD"); struct commit *branch = lookup_commit_or_die(&remote->old_oid, remote->name); + int ret = repo_in_merge_bases(the_repository, branch, head); - return repo_in_merge_bases(the_repository, branch, head); + if (ret < 0) + exit(128); + return ret; } static int delete_remote_branch(const char *pattern, int force)
diff --git a/list-objects-filter.c b/list-objects-filter.c index da287cc..4346f8d 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c
@@ -711,15 +711,6 @@ static void filter_combine__free(void *filter_data) free(d); } -static void add_all(struct oidset *dest, struct oidset *src) { - struct oidset_iter iter; - struct object_id *src_oid; - - oidset_iter_init(src, &iter); - while ((src_oid = oidset_iter_next(&iter)) != NULL) - oidset_insert(dest, src_oid); -} - static void filter_combine__finalize_omits( struct oidset *omits, void *filter_data) @@ -728,7 +719,7 @@ static void filter_combine__finalize_omits( size_t sub; for (sub = 0; sub < d->nr; sub++) { - add_all(omits, &d->sub[sub].omits); + oidset_insert_from_set(omits, &d->sub[sub].omits); oidset_clear(&d->sub[sub].omits); } }
diff --git a/log-tree.c b/log-tree.c index 337b933..e5438b0 100644 --- a/log-tree.c +++ b/log-tree.c
@@ -1011,7 +1011,7 @@ static int do_remerge_diff(struct rev_info *opt, struct object_id *oid) { struct merge_options o; - struct commit_list *bases; + struct commit_list *bases = NULL; struct merge_result res = {0}; struct pretty_print_context ctx = {0}; struct commit *parent1 = parents->item; @@ -1036,7 +1036,8 @@ static int do_remerge_diff(struct rev_info *opt, /* Parse the relevant commits and get the merge bases */ parse_commit_or_die(parent1); parse_commit_or_die(parent2); - bases = repo_get_merge_bases(the_repository, parent1, parent2); + if (repo_get_merge_bases(the_repository, parent1, parent2, &bases) < 0) + exit(128); /* Re-merge the parents */ merge_incore_recursive(&o, bases, parent1, parent2, &res);
diff --git a/mem-pool.c b/mem-pool.c index c7d6256..2078c22 100644 --- a/mem-pool.c +++ b/mem-pool.c
@@ -107,6 +107,45 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len) return r; } +static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt, + va_list ap) +{ + struct mp_block *block = pool->mp_block; + char *next_free = block ? block->next_free : NULL; + size_t available = block ? block->end - block->next_free : 0; + va_list cp; + int len, len2; + char *ret; + + va_copy(cp, ap); + len = vsnprintf(next_free, available, fmt, cp); + va_end(cp); + if (len < 0) + BUG("your vsnprintf is broken (returned %d)", len); + + ret = mem_pool_alloc(pool, len + 1); /* 1 for NUL */ + + /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */ + if (ret == next_free) + return ret; + + len2 = vsnprintf(ret, len + 1, fmt, ap); + if (len2 != len) + BUG("your vsnprintf is broken (returns inconsistent lengths)"); + return ret; +} + +char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = mem_pool_strvfmt(pool, fmt, ap); + va_end(ap); + return ret; +} + void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size) { size_t len = st_mult(count, size);
diff --git a/mem-pool.h b/mem-pool.h index fe7507f..d1c6641 100644 --- a/mem-pool.h +++ b/mem-pool.h
@@ -48,6 +48,11 @@ char *mem_pool_strdup(struct mem_pool *pool, const char *str); char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len); /* + * Allocate memory from the memory pool and format a string into it. + */ +char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...); + +/* * Move the memory associated with the 'src' pool to the 'dst' pool. The 'src' * pool will be empty and not contain any memory. It still needs to be free'd * with a call to `mem_pool_discard`.
diff --git a/merge-ort.c b/merge-ort.c index 8617bab..201f8f7 100644 --- a/merge-ort.c +++ b/merge-ort.c
@@ -18,6 +18,7 @@ #include "merge-ort.h" #include "alloc.h" +#include "advice.h" #include "attr.h" #include "cache-tree.h" #include "commit.h" @@ -542,6 +543,7 @@ enum conflict_and_info_types { CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, CONFLICT_SUBMODULE_NULL_MERGE_BASE, + CONFLICT_SUBMODULE_CORRUPT, /* Keep this entry _last_ in the list */ NB_CONFLICT_TYPES, @@ -594,7 +596,9 @@ static const char *type_short_descriptions[] = { [CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] = "CONFLICT (submodule may have rewinds)", [CONFLICT_SUBMODULE_NULL_MERGE_BASE] = - "CONFLICT (submodule lacks merge base)" + "CONFLICT (submodule lacks merge base)", + [CONFLICT_SUBMODULE_CORRUPT] = + "CONFLICT (submodule corrupt)" }; struct logical_conflict_info { @@ -1657,9 +1661,10 @@ static int collect_merge_info(struct merge_options *opt, info.data = opt; info.show_all_errors = 1; - parse_tree(merge_base); - parse_tree(side1); - parse_tree(side2); + if (parse_tree(merge_base) < 0 || + parse_tree(side1) < 0 || + parse_tree(side2) < 0) + return -1; init_tree_desc(t + 0, merge_base->buffer, merge_base->size); init_tree_desc(t + 1, side1->buffer, side1->size); init_tree_desc(t + 2, side2->buffer, side2->size); @@ -1708,7 +1713,14 @@ static int find_first_merges(struct repository *repo, die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { struct object *o = &(commit->object); - if (repo_in_merge_bases(repo, b, commit)) + int ret = repo_in_merge_bases(repo, b, commit); + + if (ret < 0) { + object_array_clear(&merges); + release_revisions(&revs); + return ret; + } + if (ret > 0) add_object_array(o, NULL, &merges); } reset_revision_walk(); @@ -1723,9 +1735,17 @@ static int find_first_merges(struct repository *repo, contains_another = 0; for (j = 0; j < merges.nr; j++) { struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && repo_in_merge_bases(repo, m2, m1)) { - contains_another = 1; - break; + if (i != j) { + int ret = repo_in_merge_bases(repo, m2, m1); + if (ret < 0) { + object_array_clear(&merges); + release_revisions(&revs); + return ret; + } + if (ret > 0) { + contains_another = 1; + break; + } } } @@ -1747,7 +1767,7 @@ static int merge_submodule(struct merge_options *opt, { struct repository subrepo; struct strbuf sb = STRBUF_INIT; - int ret = 0; + int ret = 0, ret2; struct commit *commit_o, *commit_a, *commit_b; int parent_count; struct object_array merges; @@ -1794,8 +1814,28 @@ static int merge_submodule(struct merge_options *opt, } /* check whether both changes are forward */ - if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) || - !repo_in_merge_bases(&subrepo, commit_o, commit_b)) { + ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a); + if (ret2 < 0) { + path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0, + path, NULL, NULL, NULL, + _("Failed to merge submodule %s " + "(repository corrupt)"), + path); + ret = -1; + goto cleanup; + } + if (ret2 > 0) + ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b); + if (ret2 < 0) { + path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0, + path, NULL, NULL, NULL, + _("Failed to merge submodule %s " + "(repository corrupt)"), + path); + ret = -1; + goto cleanup; + } + if (!ret2) { path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0, path, NULL, NULL, NULL, _("Failed to merge submodule %s " @@ -1805,7 +1845,17 @@ static int merge_submodule(struct merge_options *opt, } /* Case #1: a is contained in b or vice versa */ - if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) { + ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b); + if (ret2 < 0) { + path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0, + path, NULL, NULL, NULL, + _("Failed to merge submodule %s " + "(repository corrupt)"), + path); + ret = -1; + goto cleanup; + } + if (ret2 > 0) { oidcpy(result, b); path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1, path, NULL, NULL, NULL, @@ -1814,7 +1864,17 @@ static int merge_submodule(struct merge_options *opt, ret = 1; goto cleanup; } - if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) { + ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a); + if (ret2 < 0) { + path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0, + path, NULL, NULL, NULL, + _("Failed to merge submodule %s " + "(repository corrupt)"), + path); + ret = -1; + goto cleanup; + } + if (ret2 > 0) { oidcpy(result, a); path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1, path, NULL, NULL, NULL, @@ -1839,6 +1899,14 @@ static int merge_submodule(struct merge_options *opt, parent_count = find_first_merges(&subrepo, path, commit_a, commit_b, &merges); switch (parent_count) { + case -1: + path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0, + path, NULL, NULL, NULL, + _("Failed to merge submodule %s " + "(repository corrupt)"), + path); + ret = -1; + break; case 0: path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0, path, NULL, NULL, NULL, @@ -4376,9 +4444,11 @@ static int checkout(struct merge_options *opt, unpack_opts.verbose_update = (opt->verbosity > 2); unpack_opts.fn = twoway_merge; unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */ - parse_tree(prev); + if (parse_tree(prev) < 0) + return -1; init_tree_desc(&trees[0], prev->buffer, prev->size); - parse_tree(next); + if (parse_tree(next) < 0) + return -1; init_tree_desc(&trees[1], next->buffer, next->size); ret = unpack_trees(2, trees, &unpack_opts); @@ -4556,7 +4626,7 @@ static void print_submodule_conflict_suggestion(struct string_list *csub) { " - commit the resulting index in the superproject\n"), tmp.buf, subs.buf); - printf("%s", msg.buf); + advise_if_enabled(ADVICE_SUBMODULE_MERGE_CONFLICT, "%s", msg.buf); strbuf_release(&subs); strbuf_release(&tmp); @@ -4982,6 +5052,9 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt, if (result->clean >= 0) { result->tree = parse_tree_indirect(&working_tree_oid); + if (!result->tree) + die(_("unable to read tree (%s)"), + oid_to_hex(&working_tree_oid)); /* existence of conflicted entries implies unclean */ result->clean &= strmap_empty(&opt->priv->conflicted); } @@ -5007,7 +5080,11 @@ static void merge_ort_internal(struct merge_options *opt, struct strbuf merge_base_abbrev = STRBUF_INIT; if (!merge_bases) { - merge_bases = repo_get_merge_bases(the_repository, h1, h2); + if (repo_get_merge_bases(the_repository, h1, h2, + &merge_bases) < 0) { + result->clean = -1; + return; + } /* See merge-ort.h:merge_incore_recursive() declaration NOTE */ merge_bases = reverse_commit_list(merge_bases); }
diff --git a/merge-recursive.c b/merge-recursive.c index a0c3e7a..103ee32 100644 --- a/merge-recursive.c +++ b/merge-recursive.c
@@ -405,7 +405,8 @@ static inline int merge_detect_rename(struct merge_options *opt) static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) { - parse_tree(tree); + if (parse_tree(tree) < 0) + exit(128); init_tree_desc(desc, tree->buffer, tree->size); } @@ -1139,7 +1140,13 @@ static int find_first_merges(struct repository *repo, die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { struct object *o = &(commit->object); - if (repo_in_merge_bases(repo, b, commit)) + int ret = repo_in_merge_bases(repo, b, commit); + if (ret < 0) { + object_array_clear(&merges); + release_revisions(&revs); + return ret; + } + if (ret) add_object_array(o, NULL, &merges); } reset_revision_walk(); @@ -1154,9 +1161,17 @@ static int find_first_merges(struct repository *repo, contains_another = 0; for (j = 0; j < merges.nr; j++) { struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && repo_in_merge_bases(repo, m2, m1)) { - contains_another = 1; - break; + if (i != j) { + int ret = repo_in_merge_bases(repo, m2, m1); + if (ret < 0) { + object_array_clear(&merges); + release_revisions(&revs); + return ret; + } + if (ret > 0) { + contains_another = 1; + break; + } } } @@ -1192,7 +1207,7 @@ static int merge_submodule(struct merge_options *opt, const struct object_id *b) { struct repository subrepo; - int ret = 0; + int ret = 0, ret2; struct commit *commit_base, *commit_a, *commit_b; int parent_count; struct object_array merges; @@ -1229,14 +1244,32 @@ static int merge_submodule(struct merge_options *opt, } /* check whether both changes are forward */ - if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) || - !repo_in_merge_bases(&subrepo, commit_base, commit_b)) { + ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (ret2 > 0) + ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (!ret2) { output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); goto cleanup; } /* Case #1: a is contained in b or vice versa */ - if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) { + ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (ret2) { oidcpy(result, b); if (show(opt, 3)) { output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); @@ -1249,7 +1282,13 @@ static int merge_submodule(struct merge_options *opt, ret = 1; goto cleanup; } - if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) { + ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a); + if (ret2 < 0) { + output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + goto cleanup; + } + if (ret2) { oidcpy(result, a); if (show(opt, 3)) { output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); @@ -1278,6 +1317,10 @@ static int merge_submodule(struct merge_options *opt, parent_count = find_first_merges(&subrepo, &merges, path, commit_a, commit_b); switch (parent_count) { + case -1: + output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path); + ret = -1; + break; case 0: output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); break; @@ -1392,11 +1435,14 @@ static int merge_mode_and_contents(struct merge_options *opt, /* FIXME: bug, what if modes didn't match? */ result->clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result->clean = merge_submodule(opt, &result->blob.oid, - o->path, - &o->oid, - &a->oid, - &b->oid); + int clean = merge_submodule(opt, &result->blob.oid, + o->path, + &o->oid, + &a->oid, + &b->oid); + if (clean < 0) + return -1; + result->clean = clean; } else if (S_ISLNK(a->mode)) { switch (opt->recursive_variant) { case MERGE_VARIANT_NORMAL: @@ -3597,7 +3643,9 @@ static int merge_recursive_internal(struct merge_options *opt, } if (!merge_bases) { - merge_bases = repo_get_merge_bases(the_repository, h1, h2); + if (repo_get_merge_bases(the_repository, h1, h2, + &merge_bases) < 0) + return -1; merge_bases = reverse_commit_list(merge_bases); }
diff --git a/merge.c b/merge.c index ca89b31..563281b 100644 --- a/merge.c +++ b/merge.c
@@ -77,7 +77,10 @@ int checkout_fast_forward(struct repository *r, return -1; } for (i = 0; i < nr_trees; i++) { - parse_tree(trees[i]); + if (parse_tree(trees[i]) < 0) { + rollback_lock_file(&lock_file); + return -1; + } init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); }
diff --git a/neue b/neue deleted file mode 100644 index e69de29..0000000 --- a/neue +++ /dev/null
diff --git a/notes-merge.c b/notes-merge.c index 8799b52..5128293 100644 --- a/notes-merge.c +++ b/notes-merge.c
@@ -607,7 +607,8 @@ int notes_merge(struct notes_merge_options *o, assert(local && remote); /* Find merge bases */ - bases = repo_get_merge_bases(the_repository, local, remote); + if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0) + exit(128); if (!bases) { base_oid = null_oid(); base_tree_oid = the_hash_algo->empty_tree;
diff --git a/object-name.c b/object-name.c index 3a2ef5d..bd77695 100644 --- a/object-name.c +++ b/object-name.c
@@ -1034,6 +1034,15 @@ static int get_oid_basic(struct repository *r, const char *str, int len, len, str, show_date(co_time, co_tz, DATE_MODE(RFC2822))); } + } else if (nth == co_cnt && !is_null_oid(oid)) { + /* + * We were asked for the Nth reflog (counting + * from 0), but there were only N entries. + * read_ref_at() will have returned "1" to tell + * us it did not find an entry, but it did + * still fill in the oid with the "old" value, + * which we can use. + */ } else { if (flags & GET_OID_QUIETLY) { exit(128); @@ -1479,7 +1488,7 @@ int repo_get_oid_mb(struct repository *r, struct object_id *oid) { struct commit *one, *two; - struct commit_list *mbs; + struct commit_list *mbs = NULL; struct object_id oid_tmp; const char *dots; int st; @@ -1507,7 +1516,10 @@ int repo_get_oid_mb(struct repository *r, two = lookup_commit_reference_gently(r, &oid_tmp, 0); if (!two) return -1; - mbs = repo_get_merge_bases(r, one, two); + if (repo_get_merge_bases(r, one, two, &mbs) < 0) { + free_commit_list(mbs); + return -1; + } if (!mbs || mbs->next) st = -1; else {
diff --git a/object.c b/object.c index e6a1c4d..f11c59a 100644 --- a/object.c +++ b/object.c
@@ -271,6 +271,7 @@ struct object *parse_object_with_flags(struct repository *r, enum parse_object_flags flags) { int skip_hash = !!(flags & PARSE_OBJECT_SKIP_HASH_CHECK); + int discard_tree = !!(flags & PARSE_OBJECT_DISCARD_TREE); unsigned long size; enum object_type type; int eaten; @@ -298,6 +299,17 @@ struct object *parse_object_with_flags(struct repository *r, return lookup_object(r, oid); } + /* + * If the caller does not care about the tree buffer and does not + * care about checking the hash, we can simply verify that we + * have the on-disk object with the correct type. + */ + if (skip_hash && discard_tree && + (!obj || obj->type == OBJ_TREE) && + oid_object_info(r, oid, NULL) == OBJ_TREE) { + return &lookup_tree(r, oid)->object; + } + buffer = repo_read_object_file(r, oid, &type, &size); if (buffer) { if (!skip_hash && @@ -311,6 +323,8 @@ struct object *parse_object_with_flags(struct repository *r, buffer, &eaten); if (!eaten) free(buffer); + if (discard_tree && type == OBJ_TREE) + free_tree_buffer((struct tree *)obj); return obj; } return NULL;
diff --git a/object.h b/object.h index 114d459..c7123ca 100644 --- a/object.h +++ b/object.h
@@ -197,6 +197,7 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet); */ enum parse_object_flags { PARSE_OBJECT_SKIP_HASH_CHECK = 1 << 0, + PARSE_OBJECT_DISCARD_TREE = 1 << 1, }; struct object *parse_object(struct repository *r, const struct object_id *oid); struct object *parse_object_with_flags(struct repository *r,
diff --git a/oidset.c b/oidset.c index d1e5376..91d1385 100644 --- a/oidset.c +++ b/oidset.c
@@ -23,6 +23,16 @@ int oidset_insert(struct oidset *set, const struct object_id *oid) return !added; } +void oidset_insert_from_set(struct oidset *dest, struct oidset *src) +{ + struct oidset_iter iter; + struct object_id *src_oid; + + oidset_iter_init(src, &iter); + while ((src_oid = oidset_iter_next(&iter))) + oidset_insert(dest, src_oid); +} + int oidset_remove(struct oidset *set, const struct object_id *oid) { khiter_t pos = kh_get_oid_set(&set->set, *oid);
diff --git a/oidset.h b/oidset.h index ba4a5a2..262f425 100644 --- a/oidset.h +++ b/oidset.h
@@ -48,6 +48,12 @@ int oidset_contains(const struct oidset *set, const struct object_id *oid); int oidset_insert(struct oidset *set, const struct object_id *oid); /** + * Insert all the oids that are in set 'src' into set 'dest'; a copy + * is made of each oid inserted into set 'dest'. + */ +void oidset_insert_from_set(struct oidset *dest, struct oidset *src); + +/** * Remove the oid from the set. * * Returns 1 if the oid was present in the set, 0 otherwise.
diff --git a/ref-filter.c b/ref-filter.c index be14b56..0ec29f7 100644 --- a/ref-filter.c +++ b/ref-filter.c
@@ -2628,6 +2628,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, each_ref_fn cb, void *cb_data) { + if (filter->kind == FILTER_REFS_KIND_MASK) { + /* In this case, we want to print all refs including root refs. */ + return refs_for_each_include_root_refs(get_main_ref_store(the_repository), + cb, cb_data); + } + if (!filter->match_as_path) { /* * in this case, the patterns are applied after @@ -2750,6 +2756,9 @@ static int ref_kind_from_refname(const char *refname) return ref_kind[i].kind; } + if (is_pseudoref(get_main_ref_store(the_repository), refname)) + return FILTER_REFS_PSEUDOREFS; + return FILTER_REFS_OTHERS; } @@ -2781,7 +2790,16 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */ kind = filter_ref_kind(filter, refname); - if (!(kind & filter->kind)) + + /* + * Generally HEAD refs are printed with special description denoting a rebase, + * detached state and so forth. This is useful when only printing the HEAD ref + * But when it is being printed along with other pseudorefs, it makes sense to + * keep the formatting consistent. So we mask the type to act like a pseudoref. + */ + if (filter->kind == FILTER_REFS_KIND_MASK && kind == FILTER_REFS_DETACHED_HEAD) + kind = FILTER_REFS_PSEUDOREFS; + else if (!(kind & filter->kind)) return NULL; if (!filter_pattern_match(filter, refname)) @@ -3047,9 +3065,15 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref ret = for_each_fullref_in("refs/remotes/", fn, cb_data); else if (filter->kind == FILTER_REFS_TAGS) ret = for_each_fullref_in("refs/tags/", fn, cb_data); - else if (filter->kind & FILTER_REFS_ALL) + else if (filter->kind & FILTER_REFS_REGULAR) ret = for_each_fullref_in_pattern(filter, fn, cb_data); - if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) + + /* + * When printing all ref types, HEAD is already included, + * so we don't want to print HEAD again. + */ + if (!ret && (filter->kind != FILTER_REFS_KIND_MASK) && + (filter->kind & FILTER_REFS_DETACHED_HEAD)) head_ref(fn, cb_data); }
diff --git a/ref-filter.h b/ref-filter.h index 07cd6f6..0ca28d2 100644 --- a/ref-filter.h +++ b/ref-filter.h
@@ -19,10 +19,13 @@ #define FILTER_REFS_BRANCHES 0x0004 #define FILTER_REFS_REMOTES 0x0008 #define FILTER_REFS_OTHERS 0x0010 -#define FILTER_REFS_ALL (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \ +#define FILTER_REFS_REGULAR (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \ FILTER_REFS_REMOTES | FILTER_REFS_OTHERS) #define FILTER_REFS_DETACHED_HEAD 0x0020 -#define FILTER_REFS_KIND_MASK (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD) +#define FILTER_REFS_PSEUDOREFS 0x0040 +#define FILTER_REFS_ROOT_REFS (FILTER_REFS_DETACHED_HEAD | FILTER_REFS_PSEUDOREFS) +#define FILTER_REFS_KIND_MASK (FILTER_REFS_REGULAR | FILTER_REFS_DETACHED_HEAD | \ + FILTER_REFS_PSEUDOREFS) struct atom_value; struct ref_sorting;
diff --git a/refs.c b/refs.c index f926126..55d2e0b 100644 --- a/refs.c +++ b/refs.c
@@ -860,6 +860,47 @@ static int is_pseudoref_syntax(const char *refname) return 1; } +int is_pseudoref(struct ref_store *refs, const char *refname) +{ + static const char *const irregular_pseudorefs[] = { + "AUTO_MERGE", + "BISECT_EXPECTED_REV", + "NOTES_MERGE_PARTIAL", + "NOTES_MERGE_REF", + "MERGE_AUTOSTASH", + }; + struct object_id oid; + size_t i; + + if (!is_pseudoref_syntax(refname)) + return 0; + + if (ends_with(refname, "_HEAD")) { + refs_resolve_ref_unsafe(refs, refname, + RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + &oid, NULL); + return !is_null_oid(&oid); + } + + for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++) + if (!strcmp(refname, irregular_pseudorefs[i])) { + refs_resolve_ref_unsafe(refs, refname, + RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + &oid, NULL); + return !is_null_oid(&oid); + } + + return 0; +} + +int is_headref(struct ref_store *refs, const char *refname) +{ + if (!strcmp(refname, "HEAD")) + return refs_ref_exists(refs, refname); + + return 0; +} + static int is_current_worktree_ref(const char *ref) { return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref); } @@ -1039,55 +1080,40 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; - int reached_count; cb->tz = tz; cb->date = timestamp; - /* - * It is not possible for cb->cnt == 0 on the first iteration because - * that special case is handled in read_ref_at(). - */ - if (cb->cnt > 0) - cb->cnt--; - reached_count = cb->cnt == 0 && !is_null_oid(ooid); - if (timestamp <= cb->at_time || reached_count) { + if (timestamp <= cb->at_time || cb->cnt == 0) { set_read_ref_cutoffs(cb, timestamp, tz, message); /* * we have not yet updated cb->[n|o]oid so they still * hold the values for the previous record. */ - if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid)) - warning(_("log for ref %s has gap after %s"), + if (!is_null_oid(&cb->ooid)) { + oidcpy(cb->oid, noid); + if (!oideq(&cb->ooid, noid)) + warning(_("log for ref %s has gap after %s"), cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); - if (reached_count) - oidcpy(cb->oid, ooid); - else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time) + } + else if (cb->date == cb->at_time) oidcpy(cb->oid, noid); else if (!oideq(noid, cb->oid)) warning(_("log for ref %s unexpectedly ended on %s"), cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); + cb->reccnt++; + oidcpy(&cb->ooid, ooid); + oidcpy(&cb->noid, noid); cb->found_it = 1; + return 1; } cb->reccnt++; oidcpy(&cb->ooid, ooid); oidcpy(&cb->noid, noid); - return cb->found_it; -} - -static int read_ref_at_ent_newest(struct object_id *ooid UNUSED, - struct object_id *noid, - const char *email UNUSED, - timestamp_t timestamp, int tz, - const char *message, void *cb_data) -{ - struct read_ref_at_cb *cb = cb_data; - - set_read_ref_cutoffs(cb, timestamp, tz, message); - oidcpy(cb->oid, noid); - /* We just want the first entry */ - return 1; + if (cb->cnt > 0) + cb->cnt--; + return 0; } static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, @@ -1099,7 +1125,7 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid set_read_ref_cutoffs(cb, timestamp, tz, message); oidcpy(cb->oid, ooid); - if (is_null_oid(cb->oid)) + if (cb->at_time && is_null_oid(cb->oid)) oidcpy(cb->oid, noid); /* We just want the first entry */ return 1; @@ -1122,14 +1148,24 @@ int read_ref_at(struct ref_store *refs, const char *refname, cb.cutoff_cnt = cutoff_cnt; cb.oid = oid; - if (cb.cnt == 0) { - refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb); - return 0; - } - refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb); if (!cb.reccnt) { + if (cnt == 0) { + /* + * The caller asked for ref@{0}, and we had no entries. + * It's a bit subtle, but in practice all callers have + * prepped the "oid" field with the current value of + * the ref, which is the most reasonable fallback. + * + * We'll put dummy values into the out-parameters (so + * they're not just uninitialized garbage), and the + * caller can take our return value as a hint that + * we did not find any such reflog. + */ + set_read_ref_cutoffs(&cb, 0, 0, "empty reflog"); + return 1; + } if (flags & GET_OID_QUIETLY) exit(128); else @@ -1720,6 +1756,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data); } +int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn, + void *cb_data) +{ + return do_for_each_ref(refs, "", NULL, fn, 0, + DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data); +} + static int qsort_strcmp(const void *va, const void *vb) { const char *a = *(const char **)va;
diff --git a/refs.h b/refs.h index 895579a..298caf6 100644 --- a/refs.h +++ b/refs.h
@@ -399,6 +399,12 @@ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data); int for_each_rawref(each_ref_fn fn, void *cb_data); /* + * Iterates over all refs including root refs, i.e. pseudorefs and HEAD. + */ +int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn, + void *cb_data); + +/* * Normalizes partial refs to their fully qualified form. * Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'. * <prefix> will default to 'refs/' if NULL. @@ -440,7 +446,20 @@ int refs_create_reflog(struct ref_store *refs, const char *refname, struct strbuf *err); int safe_create_reflog(const char *refname, struct strbuf *err); -/** Reads log for the value of ref during at_time. **/ +/** + * Reads log for the value of ref during at_time (in which case "cnt" should be + * negative) or the reflog "cnt" entries from the top (in which case "at_time" + * should be 0). + * + * If we found the reflog entry in question, returns 0 (and details of the + * entry can be found in the out-parameters). + * + * If we ran out of reflog entries, the out-parameters are filled with the + * details of the oldest entry we did find, and the function returns 1. Note + * that there is one important special case here! If the reflog was empty + * and the caller asked for the 0-th cnt, we will return "1" but leave the + * "oid" field untouched. + **/ int read_ref_at(struct ref_store *refs, const char *refname, unsigned int flags, timestamp_t at_time, int cnt, @@ -1030,4 +1049,7 @@ extern struct ref_namespace_info ref_namespace[NAMESPACE__COUNT]; */ void update_ref_namespace(enum ref_namespace namespace, char *ref); +int is_pseudoref(struct ref_store *refs, const char *refname); +int is_headref(struct ref_store *refs, const char *refname); + #endif /* REFS_H */
diff --git a/refs/files-backend.c b/refs/files-backend.c index 6f98168..a098d14 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c
@@ -229,6 +229,38 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir } } +static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs, + const char *refname, + struct ref_dir *dir) +{ + struct object_id oid; + int flag; + + if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING, + &oid, &flag)) { + oidclr(&oid); + flag |= REF_ISBROKEN; + } else if (is_null_oid(&oid)) { + /* + * It is so astronomically unlikely + * that null_oid is the OID of an + * actual object that we consider its + * appearance in a loose reference + * file to be repo corruption + * (probably due to a software bug). + */ + flag |= REF_ISBROKEN; + } + + if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(refname)) + die("loose refname is dangerous: %s", refname); + oidclr(&oid); + flag |= REF_BAD_NAME | REF_ISBROKEN; + } + add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag)); +} + /* * Read the loose references from the namespace dirname into dir * (without recursing). dirname must end with '/'. dir must be the @@ -257,8 +289,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_add(&refname, dirname, dirnamelen); while ((de = readdir(d)) != NULL) { - struct object_id oid; - int flag; unsigned char dtype; if (de->d_name[0] == '.') @@ -274,33 +304,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, create_dir_entry(dir->cache, refname.buf, refname.len)); } else if (dtype == DT_REG) { - if (!refs_resolve_ref_unsafe(&refs->base, - refname.buf, - RESOLVE_REF_READING, - &oid, &flag)) { - oidclr(&oid); - flag |= REF_ISBROKEN; - } else if (is_null_oid(&oid)) { - /* - * It is so astronomically unlikely - * that null_oid is the OID of an - * actual object that we consider its - * appearance in a loose reference - * file to be repo corruption - * (probably due to a software bug). - */ - flag |= REF_ISBROKEN; - } - - if (check_refname_format(refname.buf, - REFNAME_ALLOW_ONELEVEL)) { - if (!refname_is_safe(refname.buf)) - die("loose refname is dangerous: %s", refname.buf); - oidclr(&oid); - flag |= REF_BAD_NAME | REF_ISBROKEN; - } - add_entry_to_dir(dir, - create_ref_entry(refname.buf, &oid, flag)); + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); } strbuf_setlen(&refname, dirnamelen); } @@ -311,9 +315,59 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, add_per_worktree_entries_to_dir(dir, dirname); } -static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) +/* + * Add pseudorefs to the ref dir by parsing the directory for any files + * which follow the pseudoref syntax. + */ +static void add_pseudoref_and_head_entries(struct ref_store *ref_store, + struct ref_dir *dir, + const char *dirname) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir"); + struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT; + struct dirent *de; + size_t dirnamelen; + DIR *d; + + files_ref_path(refs, &path, dirname); + + d = opendir(path.buf); + if (!d) { + strbuf_release(&path); + return; + } + + strbuf_addstr(&refname, dirname); + dirnamelen = refname.len; + + while ((de = readdir(d)) != NULL) { + unsigned char dtype; + + if (de->d_name[0] == '.') + continue; + if (ends_with(de->d_name, ".lock")) + continue; + strbuf_addstr(&refname, de->d_name); + + dtype = get_dtype(de, &path, 1); + if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) || + is_headref(ref_store, de->d_name))) + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); + + strbuf_setlen(&refname, dirnamelen); + } + strbuf_release(&refname); + strbuf_release(&path); + closedir(d); +} + +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs, + unsigned int flags) { if (!refs->loose) { + struct ref_dir *dir; + /* * Mark the top-level directory complete because we * are about to read the only subdirectory that can @@ -324,12 +378,17 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) /* We're going to fill the top level ourselves: */ refs->loose->root->flag &= ~REF_INCOMPLETE; + dir = get_ref_dir(refs->loose->root); + + if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS) + add_pseudoref_and_head_entries(dir->cache->ref_store, dir, + refs->loose->root->name); + /* * Add an incomplete entry for "refs/" (to be filled * lazily): */ - add_entry_to_dir(get_ref_dir(refs->loose->root), - create_dir_entry(refs->loose, "refs/", 5)); + add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5)); } return refs->loose; } @@ -857,7 +916,7 @@ static struct ref_iterator *files_ref_iterator_begin( * disk, and re-reads it if not. */ - loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags), prefix, ref_store->repo, 1); /* @@ -1217,7 +1276,7 @@ static int files_pack_refs(struct ref_store *ref_store, packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err); - iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, + iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL, the_repository, 0); while ((ok = ref_iterator_advance(iter)) == ITER_OK) { /*
diff --git a/refs/refs-internal.h b/refs/refs-internal.h index a9b6e88..56641aa 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h
@@ -260,6 +260,12 @@ enum do_for_each_ref_flags { * INCLUDE_BROKEN, since they are otherwise not included at all. */ DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), + + /* + * Include root refs i.e. HEAD and pseudorefs along with the regular + * refs. + */ + DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3), }; /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 6c11c4a..2c88bbd 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c
@@ -364,12 +364,15 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) break; /* - * The files backend only lists references contained in - * "refs/". We emulate the same behaviour here and thus skip - * all references that don't start with this prefix. + * The files backend only lists references contained in "refs/" unless + * the root refs are to be included. We emulate the same behaviour here. */ - if (!starts_with(iter->ref.refname, "refs/")) + if (!starts_with(iter->ref.refname, "refs/") && + !(iter->flags & DO_FOR_EACH_INCLUDE_ROOT_REFS && + (is_pseudoref(&iter->refs->base, iter->ref.refname) || + is_headref(&iter->refs->base, iter->ref.refname)))) { continue; + } if (iter->prefix && strncmp(iter->prefix, iter->ref.refname, strlen(iter->prefix))) { @@ -778,6 +781,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, &head_referent, &head_type); if (ret < 0) goto done; + ret = 0; for (i = 0; i < transaction->nr; i++) { struct ref_update *u = transaction->updates[i];
diff --git a/remote.c b/remote.c index 9090632..2b650b8 100644 --- a/remote.c +++ b/remote.c
@@ -2679,7 +2679,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote) if (MERGE_BASES_BATCH_SIZE < size) size = MERGE_BASES_BATCH_SIZE; - if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk))) + if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk, 0))) break; }
diff --git a/reset.c b/reset.c index 0f2ff0f..d619cb7 100644 --- a/reset.c +++ b/reset.c
@@ -157,6 +157,11 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts) } tree = parse_tree_indirect(oid); + if (!tree) { + ret = error(_("unable to read tree (%s)"), oid_to_hex(oid)); + goto leave_reset_head; + } + prime_cache_tree(r, r->index, tree); if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
diff --git a/revision.c b/revision.c index ac45c6d..4589365 100644 --- a/revision.c +++ b/revision.c
@@ -381,13 +381,18 @@ static struct object *get_reference(struct rev_info *revs, const char *name, object = parse_object_with_flags(revs->repo, oid, revs->verify_objects ? 0 : - PARSE_OBJECT_SKIP_HASH_CHECK); + PARSE_OBJECT_SKIP_HASH_CHECK | + PARSE_OBJECT_DISCARD_TREE); if (!object) { if (revs->ignore_missing) - return object; + return NULL; if (revs->exclude_promisor_objects && is_promisor_object(oid)) return NULL; + if (revs->do_not_die_on_missing_objects) { + oidset_insert(&revs->missing_commits, oid); + return NULL; + } die("bad object %s", name); } object->flags |= flags; @@ -415,15 +420,21 @@ static struct commit *handle_commit(struct rev_info *revs, */ while (object->type == OBJ_TAG) { struct tag *tag = (struct tag *) object; + struct object_id *oid; if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); - object = parse_object(revs->repo, get_tagged_oid(tag)); + oid = get_tagged_oid(tag); + object = parse_object(revs->repo, oid); if (!object) { if (revs->ignore_missing_links || (flags & UNINTERESTING)) return NULL; if (revs->exclude_promisor_objects && is_promisor_object(&tag->tagged->oid)) return NULL; + if (revs->do_not_die_on_missing_objects && oid) { + oidset_insert(&revs->missing_commits, oid); + return NULL; + } die("bad object %s", oid_to_hex(&tag->tagged->oid)); } object->flags |= flags; @@ -1945,6 +1956,7 @@ void repo_init_revisions(struct repository *r, init_display_notes(&revs->notes_opt); list_objects_filter_init(&revs->filter); init_ref_exclusions(&revs->ref_excludes); + oidset_init(&revs->missing_commits, 0); } static void add_pending_commit_list(struct rev_info *revs, @@ -1959,11 +1971,31 @@ static void add_pending_commit_list(struct rev_info *revs, } } +static const char *lookup_other_head(struct object_id *oid) +{ + int i; + static const char *const other_head[] = { + "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD" + }; + + for (i = 0; i < ARRAY_SIZE(other_head); i++) + if (!read_ref_full(other_head[i], + RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + oid, NULL)) { + if (is_null_oid(oid)) + die(_("%s exists but is a symbolic ref"), other_head[i]); + return other_head[i]; + } + + die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD")); +} + static void prepare_show_merge(struct rev_info *revs) { - struct commit_list *bases; + struct commit_list *bases = NULL; struct commit *head, *other; struct object_id oid; + const char *other_name; const char **prune = NULL; int i, prune_num = 1; /* counting terminating NULL */ struct index_state *istate = revs->repo->index; @@ -1971,12 +2003,12 @@ static void prepare_show_merge(struct rev_info *revs) if (repo_get_oid(the_repository, "HEAD", &oid)) die("--merge without HEAD?"); head = lookup_commit_or_die(&oid, "HEAD"); - if (repo_get_oid(the_repository, "MERGE_HEAD", &oid)) - die("--merge without MERGE_HEAD?"); - other = lookup_commit_or_die(&oid, "MERGE_HEAD"); + other_name = lookup_other_head(&oid); + other = lookup_commit_or_die(&oid, other_name); add_pending_object(revs, &head->object, "HEAD"); - add_pending_object(revs, &other->object, "MERGE_HEAD"); - bases = repo_get_merge_bases(the_repository, head, other); + add_pending_object(revs, &other->object, other_name); + if (repo_get_merge_bases(the_repository, head, other, &bases) < 0) + exit(128); add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM); add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM); free_commit_list(bases); @@ -2064,14 +2096,17 @@ static int handle_dotdot_1(const char *arg, char *dotdot, } else { /* A...B -- find merge bases between the two */ struct commit *a, *b; - struct commit_list *exclude; + struct commit_list *exclude = NULL; a = lookup_commit_reference(revs->repo, &a_obj->oid); b = lookup_commit_reference(revs->repo, &b_obj->oid); if (!a || !b) return dotdot_missing(arg, dotdot, revs, symmetric); - exclude = repo_get_merge_bases(the_repository, a, b); + if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) { + free_commit_list(exclude); + return -1; + } add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, flags_exclude); add_pending_commit_list(revs, exclude, flags_exclude); @@ -2176,13 +2211,18 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl if (revarg_opt & REVARG_COMMITTISH) get_sha1_flags |= GET_OID_COMMITTISH; + /* + * Even if revs->do_not_die_on_missing_objects is set, we + * should error out if we can't even get an oid, as + * `--missing=print` should be able to report missing oids. + */ if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc)) return revs->ignore_missing ? 0 : -1; if (!cant_be_filename) verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, &oid, flags ^ local_flags); if (!object) - return revs->ignore_missing ? 0 : -1; + return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1; add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags); add_pending_object_with_path(revs, object, arg, oc.mode, oc.path); free(oc.path); @@ -3828,8 +3868,6 @@ int prepare_revision_walk(struct rev_info *revs) FOR_EACH_OBJECT_PROMISOR_ONLY); } - oidset_init(&revs->missing_commits, 0); - if (!revs->reflog_info) prepare_to_use_bloom_filter(revs); if (!revs->unsorted_input)
diff --git a/sequencer.c b/sequencer.c index f49a871..5c6f541 100644 --- a/sequencer.c +++ b/sequencer.c
@@ -707,6 +707,8 @@ static int do_recursive_merge(struct repository *r, o.show_rename_progress = 1; head_tree = parse_tree_indirect(head); + if (!head_tree) + return error(_("unable to read tree (%s)"), oid_to_hex(head)); next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r); base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r); @@ -3882,6 +3884,8 @@ static int do_reset(struct repository *r, } tree = parse_tree_indirect(&oid); + if (!tree) + return error(_("unable to read tree (%s)"), oid_to_hex(&oid)); prime_cache_tree(r, r->index, tree); if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) @@ -3908,7 +3912,7 @@ static int do_merge(struct repository *r, int run_commit_flags = 0; struct strbuf ref_name = STRBUF_INIT; struct commit *head_commit, *merge_commit, *i; - struct commit_list *bases, *j; + struct commit_list *bases = NULL, *j; struct commit_list *to_merge = NULL, **tail = &to_merge; const char *strategy = !opts->xopts.nr && (!opts->strategy || @@ -4134,7 +4138,11 @@ static int do_merge(struct repository *r, } merge_commit = to_merge->item; - bases = repo_get_merge_bases(r, head_commit, merge_commit); + if (repo_get_merge_bases(r, head_commit, merge_commit, &bases) < 0) { + ret = -1; + goto leave_merge; + } + if (bases && oideq(&merge_commit->object.oid, &bases->item->object.oid)) { ret = 0;
diff --git a/serve.c b/serve.c index a1d7113..aa651b7 100644 --- a/serve.c +++ b/serve.c
@@ -12,6 +12,7 @@ #include "trace2.h" static int advertise_sid = -1; +static int advertise_object_info = -1; static int client_hash_algo = GIT_HASH_SHA1; static int always_advertise(struct repository *r UNUSED, @@ -67,6 +68,17 @@ static void session_id_receive(struct repository *r UNUSED, trace2_data_string("transfer", NULL, "client-sid", client_sid); } +static int object_info_advertise(struct repository *r, struct strbuf *value UNUSED) +{ + if (advertise_object_info == -1 && + repo_config_get_bool(r, "transfer.advertiseobjectinfo", + &advertise_object_info)) { + /* disabled by default */ + advertise_object_info = 0; + } + return advertise_object_info; +} + struct protocol_capability { /* * The name of the capability. The server uses this name when @@ -135,7 +147,7 @@ static struct protocol_capability capabilities[] = { }, { .name = "object-info", - .advertise = always_advertise, + .advertise = object_info_advertise, .command = cap_object_info, }, {
diff --git a/setup.c b/setup.c index b69b1cb..e3b76e8 100644 --- a/setup.c +++ b/setup.c
@@ -1889,6 +1889,13 @@ void initialize_repository_version(int hash_algo, char repo_version_string[10]; int repo_version = GIT_REPO_VERSION; + /* + * Note that we initialize the repository version to 1 when the ref + * storage format is unknown. This is on purpose so that we can add the + * correct object format to the config during git-clone(1). The format + * version will get adjusted by git-clone(1) once it has learned about + * the remote repository's format. + */ if (hash_algo != GIT_HASH_SHA1 || ref_storage_format != REF_STORAGE_FORMAT_FILES) repo_version = GIT_REPO_VERSION_READ; @@ -1898,7 +1905,7 @@ void initialize_repository_version(int hash_algo, "%d", repo_version); git_config_set("core.repositoryformatversion", repo_version_string); - if (hash_algo != GIT_HASH_SHA1) + if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN) git_config_set("extensions.objectformat", hash_algos[hash_algo].name); else if (reinit)
diff --git a/shallow.c b/shallow.c index 7711798..7ff50dd 100644 --- a/shallow.c +++ b/shallow.c
@@ -794,12 +794,16 @@ static void post_assign_shallow(struct shallow_info *info, if (!*bitmap) continue; for (j = 0; j < bitmap_nr; j++) - if (bitmap[0][j] && - /* Step 7, reachability test at commit level */ - !repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits)) { - update_refstatus(ref_status, info->ref->nr, *bitmap); - dst++; - break; + if (bitmap[0][j]) { + /* Step 7, reachability test at commit level */ + int ret = repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1); + if (ret < 0) + exit(128); + if (!ret) { + update_refstatus(ref_status, info->ref->nr, *bitmap); + dst++; + break; + } } } info->nr_ours = dst; @@ -827,7 +831,10 @@ int delayed_reachability_test(struct shallow_info *si, int c) si->reachable[c] = repo_in_merge_bases_many(the_repository, commit, si->nr_commits, - si->commits); + si->commits, + 1); + if (si->reachable[c] < 0) + exit(128); si->need_reachability_test[c] = 0; } return si->reachable[c];
diff --git a/submodule.c b/submodule.c index 213da79..f0ddb31 100644 --- a/submodule.c +++ b/submodule.c
@@ -592,7 +592,12 @@ static void show_submodule_header(struct diff_options *o, (!is_null_oid(two) && !*right)) message = "(commits not present)"; - *merge_bases = repo_get_merge_bases(sub, *left, *right); + *merge_bases = NULL; + if (repo_get_merge_bases(sub, *left, *right, merge_bases) < 0) { + message = "(corrupt repository)"; + goto output_header; + } + if (*merge_bases) { if ((*merge_bases)->item == *left) fast_forward = 1; @@ -1687,8 +1692,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, task = get_fetch_task_from_changed(spf, err); if (task) { - struct strbuf submodule_prefix = STRBUF_INIT; - child_process_init(cp); cp->dir = task->repo->gitdir; prepare_submodule_repo_env_in_gitdir(&cp->env); @@ -1698,15 +1701,11 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, strvec_pushv(&cp->args, task->git_args.v); strvec_pushv(&cp->args, spf->args.v); strvec_push(&cp->args, task->default_argv); - strvec_push(&cp->args, "--submodule-prefix"); + strvec_pushf(&cp->args, "--submodule-prefix=%s%s/", + spf->prefix, task->sub->path); - strbuf_addf(&submodule_prefix, "%s%s/", - spf->prefix, - task->sub->path); - strvec_push(&cp->args, submodule_prefix.buf); *task_cb = task; - strbuf_release(&submodule_prefix); string_list_insert(&spf->seen_submodule_names, task->sub->name); return 1; } @@ -1714,12 +1713,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, if (spf->oid_fetch_tasks_nr) { struct fetch_task *task = spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1]; - struct strbuf submodule_prefix = STRBUF_INIT; spf->oid_fetch_tasks_nr--; - strbuf_addf(&submodule_prefix, "%s%s/", - spf->prefix, task->sub->path); - child_process_init(cp); prepare_submodule_repo_env_in_gitdir(&cp->env); cp->git_cmd = 1; @@ -1728,8 +1723,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, strvec_init(&cp->args); strvec_pushv(&cp->args, spf->args.v); strvec_push(&cp->args, "on-demand"); - strvec_push(&cp->args, "--submodule-prefix"); - strvec_push(&cp->args, submodule_prefix.buf); + strvec_pushf(&cp->args, "--submodule-prefix=%s%s/", + spf->prefix, task->sub->path); /* NEEDSWORK: have get_default_remote from submodule--helper */ strvec_push(&cp->args, "origin"); @@ -1737,7 +1732,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, append_oid_to_argv, &cp->args); *task_cb = task; - strbuf_release(&submodule_prefix); return 1; }
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c index 1e159a7..1e3b431 100644 --- a/t/helper/test-reach.c +++ b/t/helper/test-reach.c
@@ -111,13 +111,16 @@ int cmd__reach(int ac, const char **av) repo_in_merge_bases(the_repository, A, B)); else if (!strcmp(av[1], "in_merge_bases_many")) printf("%s(A,X):%d\n", av[1], - repo_in_merge_bases_many(the_repository, A, X_nr, X_array)); + repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0)); else if (!strcmp(av[1], "is_descendant_of")) printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X)); else if (!strcmp(av[1], "get_merge_bases_many")) { - struct commit_list *list = repo_get_merge_bases_many(the_repository, - A, X_nr, - X_array); + struct commit_list *list = NULL; + if (repo_get_merge_bases_many(the_repository, + A, X_nr, + X_array, + &list) < 0) + exit(128); printf("%s(A,X):\n", av[1]); print_sorted_commit_ids(list); } else if (!strcmp(av[1], "reduce_heads")) {
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index 0f98b21..88a66f0 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh
@@ -665,6 +665,21 @@ git -C partial.git rev-list --objects --missing=print HEAD >out && grep "[?]$FILE_HASH" out && + # The no-lazy-fetch mechanism prevents Git from fetching + test_must_fail env GIT_NO_LAZY_FETCH=1 \ + git -C partial.git cat-file -e "$FILE_HASH" && + + # The same with command line option to "git" + test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" && + + # The same, forcing a subprocess via an alias + test_must_fail git --no-lazy-fetch -C partial.git \ + -c alias.foo="!git cat-file" foo -e "$FILE_HASH" && + + # Sanity check that the file is still missing + git -C partial.git rev-list --objects --missing=print HEAD >out && + grep "[?]$FILE_HASH" out && + git -C full cat-file -s "$FILE_HASH" >expect && test-tool partial-clone object-info partial.git "$FILE_HASH" >actual && test_cmp expect actual &&
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh index 09e71b2..6421434 100755 --- a/t/t0600-reffiles-backend.sh +++ b/t/t0600-reffiles-backend.sh
@@ -279,18 +279,18 @@ # direct FS access for creating the reflogs. 3) PSEUDO-WT and refs/bisect/random # do not create reflogs by default, so it is not testing a realistic scenario. test_expect_success 'for_each_reflog()' ' - echo $ZERO_OID > .git/logs/PSEUDO-MAIN && + echo $ZERO_OID >.git/logs/PSEUDO_MAIN_HEAD && mkdir -p .git/logs/refs/bisect && - echo $ZERO_OID > .git/logs/refs/bisect/random && + echo $ZERO_OID >.git/logs/refs/bisect/random && - echo $ZERO_OID > .git/worktrees/wt/logs/PSEUDO-WT && + echo $ZERO_OID >.git/worktrees/wt/logs/PSEUDO_WT_HEAD && mkdir -p .git/worktrees/wt/logs/refs/bisect && - echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random && + echo $ZERO_OID >.git/worktrees/wt/logs/refs/bisect/wt-random && $RWT for-each-reflog >actual && cat >expected <<-\EOF && HEAD - PSEUDO-WT + PSEUDO_WT_HEAD refs/bisect/wt-random refs/heads/main refs/heads/wt-main @@ -300,7 +300,7 @@ $RMAIN for-each-reflog >actual && cat >expected <<-\EOF && HEAD - PSEUDO-MAIN + PSEUDO_MAIN_HEAD refs/bisect/random refs/heads/main refs/heads/wt-main
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh index 6a131e4..6867811 100755 --- a/t/t0610-reftable-basics.sh +++ b/t/t0610-reftable-basics.sh
@@ -328,6 +328,18 @@ EOF ' +test_expect_success 'ref transaction: empty transaction in empty repo' ' + test_when_finished "rm -rf repo" && + git init repo && + test_commit -C repo --no-tag A && + git -C repo update-ref -d refs/heads/main && + test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD && + git -C repo update-ref --stdin <<-EOF + prepare + commit + EOF +' + test_expect_success 'pack-refs: compacts tables' ' test_when_finished "rm -rf repo" && git init repo &&
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh index 6a98b2d..a1139f7 100755 --- a/t/t3202-show-branch.sh +++ b/t/t3202-show-branch.sh
@@ -4,9 +4,6 @@ . ./test-lib.sh -# arbitrary reference time: 2009-08-30 19:20:00 -GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW - test_expect_success 'error descriptions on empty repository' ' current=$(git branch --show-current) && cat >expect <<-EOF && @@ -187,18 +184,6 @@ test_cmp expect actual ' -test_expect_success 'show branch --reflog=2' ' - sed "s/^> //" >expect <<-\EOF && - > ! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10 - > ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10 - > -- - > + [refs/heads/branch10@{0}] branch10 - > ++ [refs/heads/branch10@{1}] initial - EOF - git show-branch --reflog=2 >actual && - test_cmp actual expect -' - # incompatible options while read combo do @@ -264,4 +249,38 @@ test_branch_op_in_wt -c new-branch ' +test_expect_success 'setup reflogs' ' + test_commit base && + git checkout -b branch && + test_commit one && + git reset --hard HEAD^ && + test_commit two && + test_commit three +' + +test_expect_success '--reflog shows reflog entries' ' + cat >expect <<-\EOF && + ! [branch@{0}] (0 seconds ago) commit: three + ! [branch@{1}] (60 seconds ago) commit: two + ! [branch@{2}] (2 minutes ago) reset: moving to HEAD^ + ! [branch@{3}] (2 minutes ago) commit: one + ---- + + [branch@{0}] three + ++ [branch@{1}] two + + [branch@{3}] one + ++++ [branch@{2}] base + EOF + # the output always contains relative timestamps; use + # a known time to get deterministic results + GIT_TEST_DATE_NOW=$test_tick \ + git show-branch --reflog branch >actual && + test_cmp expect actual +' + +test_expect_success '--reflog handles missing reflog' ' + git reflog expire --expire=now branch && + git show-branch --reflog branch >actual && + test_must_be_empty actual +' + test_done
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh index bf33aed..8ebfa3c 100755 --- a/t/t4042-diff-textconv-caching.sh +++ b/t/t4042-diff-textconv-caching.sh
@@ -118,4 +118,26 @@ git log --no-walk -p refs/notes/textconv/magic HEAD ' +test_expect_success 'caching is silently ignored outside repo' ' + mkdir -p non-repo && + echo one >non-repo/one && + echo two >non-repo/two && + echo "* diff=test" >attr && + test_expect_code 1 \ + nongit git -c core.attributesFile="$PWD/attr" \ + -c diff.test.textconv="tr a-z A-Z <" \ + -c diff.test.cachetextconv=true \ + diff --no-index one two >actual && + cat >expect <<-\EOF && + diff --git a/one b/two + index 5626abf..f719efd 100644 + --- a/one + +++ b/two + @@ -1 +1 @@ + -ONE + +TWO + EOF + test_cmp expect actual +' + test_done
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh index 12ac436..29e9974 100755 --- a/t/t4301-merge-tree-write-tree.sh +++ b/t/t4301-merge-tree-write-tree.sh
@@ -945,4 +945,49 @@ test_cmp expect actual ' +test_expect_success '--merge-base with tree OIDs' ' + git merge-tree --merge-base=side1^ side1 side3 >with-commits && + git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees && + test_cmp with-commits with-trees +' + +test_expect_success 'error out on missing tree objects' ' + git init --bare missing-tree.git && + git rev-list side3 >list && + git rev-parse side3^: >>list && + git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list && + side3=$(git rev-parse side3) && + test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err && + test_grep "Could not read $(git rev-parse $side3:)" err && + test_must_be_empty actual +' + +test_expect_success 'error out on missing blob objects' ' + echo 1 | git hash-object -w --stdin >blob1 && + echo 2 | git hash-object -w --stdin >blob2 && + echo 3 | git hash-object -w --stdin >blob3 && + printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 && + printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 && + printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 && + git init --bare missing-blob.git && + cat blob1 blob3 tree1 tree2 tree3 | + git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing && + test_must_fail git --git-dir=missing-blob.git >actual 2>err \ + merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) && + test_grep "unable to read blob object $(cat blob2)" err && + test_must_be_empty actual +' + +test_expect_success 'error out on missing commits as well' ' + git init --bare missing-commit.git && + git rev-list --objects side1 side3 >list-including-initial && + grep -v ^$(git rev-parse side1^) <list-including-initial >list && + git pack-objects missing-commit.git/objects/pack/missing-initial <list && + side1=$(git rev-parse side1) && + side3=$(git rev-parse side3) && + test_must_fail git --git-dir=missing-commit.git \ + merge-tree --allow-unrelated-histories $side1 $side3 >actual && + test_must_be_empty actual +' + test_done
diff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh index b1cfe8b..3dcb334 100755 --- a/t/t5555-http-smart-common.sh +++ b/t/t5555-http-smart-common.sh
@@ -131,7 +131,6 @@ fetch=shallow wait-for-done server-option object-format=$(test_oid algo) - object-info 0000 EOF
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh index 3591bc2..c48830d 100755 --- a/t/t5701-git-serve.sh +++ b/t/t5701-git-serve.sh
@@ -20,7 +20,6 @@ fetch=shallow wait-for-done server-option object-format=$(test_oid algo) - object-info EOF cat >expect.trailer <<-EOF && 0000 @@ -323,6 +322,8 @@ # Test the basics of object-info # test_expect_success 'basics of object-info' ' + test_config transfer.advertiseObjectInfo true && + test-tool pkt-line pack >in <<-EOF && command=object-info object-format=$(test_oid algo) @@ -380,4 +381,25 @@ test_must_be_empty out ' +test_expect_success 'object-info missing from capabilities when disabled' ' + test_config transfer.advertiseObjectInfo false && + + GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \ + --advertise-capabilities >out && + test-tool pkt-line unpack <out >actual && + + ! grep object.info actual +' + +test_expect_success 'object-info commands rejected when disabled' ' + test_config transfer.advertiseObjectInfo false && + + test-tool pkt-line pack >in <<-EOF && + command=object-info + EOF + + test_must_fail test-tool serve-v2 --stateless-rpc <in 2>err && + grep invalid.command err +' + test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 6ef4971..1ef540f 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh
@@ -778,6 +778,25 @@ ! grep ^GIT_PROTOCOL env.trace ' +test_expect_success 'reject client packfile-uris if not advertised' ' + { + packetize command=fetch && + packetize object-format=$(test_oid algo) && + printf 0001 && + packetize packfile-uris https && + packetize done && + printf 0000 + } >input && + test_must_fail env GIT_PROTOCOL=version=2 \ + git upload-pack client <input && + test_must_fail env GIT_PROTOCOL=version=2 \ + git -c uploadpack.blobpackfileuri \ + upload-pack client <input && + GIT_PROTOCOL=version=2 \ + git -c uploadpack.blobpackfileuri=anything \ + upload-pack client <input +' + # Test protocol v2 with 'http://' transport # . "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit index 1544d6d..bcfb358 100755 --- a/t/t5801/git-remote-testgit +++ b/t/t5801/git-remote-testgit
@@ -12,6 +12,11 @@ dir="$GIT_DIR/testgit/$alias" +if ! git rev-parse --is-inside-git-dir +then + exit 1 +fi + h_refspec="refs/heads/*:refs/testgit/$alias/heads/*" t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh index 2116727..127180e 100755 --- a/t/t6022-rev-list-missing.sh +++ b/t/t6022-rev-list-missing.sh
@@ -10,7 +10,10 @@ test_expect_success 'create repository and alternate directory' ' test_commit 1 && test_commit 2 && - test_commit 3 + test_commit 3 && + git tag -m "tag message" annot_tag HEAD~1 && + git tag regul_tag HEAD~1 && + git branch a_branch HEAD~1 ' # We manually corrupt the repository, which means that the commit-graph may @@ -46,9 +49,10 @@ git rev-list --objects --no-object-names \ HEAD ^$obj >expect.raw && - # Blobs are shared by all commits, so evethough a commit/tree + # Blobs are shared by all commits, so even though a commit/tree # might be skipped, its blob must be accounted for. - if [ $obj != "HEAD:1.t" ]; then + if test $obj != "HEAD:1.t" + then echo $(git rev-parse HEAD:1.t) >>expect.raw && echo $(git rev-parse HEAD:2.t) >>expect.raw fi && @@ -77,4 +81,69 @@ done done +for missing_tip in "annot_tag" "regul_tag" "a_branch" "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t" +do + # We want to check that things work when both + # - all the tips passed are missing (case existing_tip = ""), and + # - there is one missing tip and one existing tip (case existing_tip = "HEAD") + for existing_tip in "" "HEAD" + do + for action in "allow-any" "print" + do + test_expect_success "--missing=$action with tip '$missing_tip' missing and tip '$existing_tip'" ' + # Before the object is made missing, we use rev-list to + # get the expected oids. + if test "$existing_tip" = "HEAD" + then + git rev-list --objects --no-object-names \ + HEAD ^$missing_tip >expect.raw + else + >expect.raw + fi && + + # Blobs are shared by all commits, so even though a commit/tree + # might be skipped, its blob must be accounted for. + if test "$existing_tip" = "HEAD" && test $missing_tip != "HEAD:1.t" + then + echo $(git rev-parse HEAD:1.t) >>expect.raw && + echo $(git rev-parse HEAD:2.t) >>expect.raw + fi && + + missing_oid="$(git rev-parse $missing_tip)" && + + if test "$missing_tip" = "annot_tag" + then + oid="$(git rev-parse $missing_tip^{commit})" && + echo "$missing_oid" >>expect.raw + else + oid="$missing_oid" + fi && + + path=".git/objects/$(test_oid_to_path $oid)" && + + mv "$path" "$path.hidden" && + test_when_finished "mv $path.hidden $path" && + + git rev-list --missing=$action --objects --no-object-names \ + $missing_oid $existing_tip >actual.raw && + + # When the action is to print, we should also add the missing + # oid to the expect list. + case $action in + allow-any) + ;; + print) + grep ?$oid actual.raw && + echo ?$oid >>expect.raw + ;; + esac && + + sort actual.raw >actual && + sort expect.raw >expect && + test_cmp expect actual + ' + done + done +done + test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 561080b..cdc0270 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh
@@ -878,7 +878,7 @@ echo "" > expected.ok cat > expected.missing-tree.default <<EOF -fatal: unable to read tree $deleted +fatal: unable to read tree ($deleted) EOF test_expect_success 'bisect fails if tree is broken on start commit' '
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index 82f3d1e..948f1bb 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh
@@ -31,6 +31,37 @@ git update-ref refs/odd/spot main ' +test_expect_success '--include-root-refs pattern prints pseudorefs' ' + cat >expect <<-\EOF && + HEAD + ORIG_HEAD + refs/heads/main + refs/heads/side + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git update-ref ORIG_HEAD main && + git for-each-ref --format="%(refname)" --include-root-refs >actual && + test_cmp expect actual +' + +test_expect_success '--include-root-refs with other patterns' ' + cat >expect <<-\EOF && + HEAD + ORIG_HEAD + EOF + git update-ref ORIG_HEAD main && + git for-each-ref --format="%(refname)" --include-root-refs "*HEAD" >actual && + test_cmp expect actual +' + test_expect_success 'filtering with --points-at' ' cat >expect <<-\EOF && refs/heads/main
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh index 7065052..7a3f1cb 100755 --- a/t/t6437-submodule-merge.sh +++ b/t/t6437-submodule-merge.sh
@@ -113,7 +113,7 @@ git checkout -b test-nonforward-a b && if test "$GIT_TEST_MERGE_ALGORITHM" = ort then - test_must_fail git merge c >actual && + test_must_fail git merge c 2>actual && sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" && grep "$sub_expect" actual else @@ -154,9 +154,9 @@ git rev-parse --short sub-d > ../expect) && if test "$GIT_TEST_MERGE_ALGORITHM" = ort then - test_must_fail git merge c >actual && + test_must_fail git merge c >actual 2>sub-actual && sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" && - grep "$sub_expect" actual + grep "$sub_expect" sub-actual else test_must_fail git merge c 2> actual fi && @@ -181,9 +181,9 @@ ) && if test "$GIT_TEST_MERGE_ALGORITHM" = ort then - test_must_fail git merge c >actual && + test_must_fail git merge c >actual 2>sub-actual && sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" && - grep "$sub_expect" actual + grep "$sub_expect" sub-actual else test_must_fail git merge c 2> actual fi && @@ -227,7 +227,7 @@ git commit -a -m "f" && git checkout -b test-backward e && - test_must_fail git merge f >actual && + test_must_fail git merge f 2>actual && if test "$GIT_TEST_MERGE_ALGORITHM" = ort then sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" && @@ -535,7 +535,7 @@ git checkout -b b init && git add sub && git commit -m "b" && - test_must_fail git merge a >actual && + test_must_fail git merge a 2>actual && if test "$GIT_TEST_MERGE_ALGORITHM" = ort then sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh index 2b3c363..aa2fdc3 100755 --- a/t/t7402-submodule-rebase.sh +++ b/t/t7402-submodule-rebase.sh
@@ -116,7 +116,7 @@ test_tick && git commit -m fourth && - test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 >actual_output && + test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 2>actual_output && git ls-files -s submodule >actual && ( cd submodule &&
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh index a87c211..b37e201 100755 --- a/t/t7502-commit-porcelain.sh +++ b/t/t7502-commit-porcelain.sh
@@ -736,6 +736,11 @@ .git/COMMIT_EDITMSG ' +test_expect_success 'message does not have multiple scissors lines' ' + git commit --cleanup=scissors -v --allow-empty -e -m foo && + test $(grep -c -e "--- >8 ---" .git/COMMIT_EDITMSG) -eq 1 +' + test_expect_success AUTOIDENT 'message shows committer when it is automatic' ' echo >>negative &&
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c index f315489..d6ac1fe 100644 --- a/t/unit-tests/t-ctype.c +++ b/t/unit-tests/t-ctype.c
@@ -1,30 +1,19 @@ #include "test-lib.h" -static int is_in(const char *s, int ch) -{ - /* - * We can't find NUL using strchr. Accept it as the first - * character in the spec -- there are no empty classes. - */ - if (ch == '\0') - return ch == *s; - if (*s == '\0') - s++; - return !!strchr(s, ch); -} - -/* Macro to test a character type */ -#define TEST_CTYPE_FUNC(func, string) \ -static void test_ctype_##func(void) { \ - for (int i = 0; i < 256; i++) { \ - if (!check_int(func(i), ==, is_in(string, i))) \ - test_msg(" i: 0x%02x", i); \ +#define TEST_CHAR_CLASS(class, string) do { \ + size_t len = ARRAY_SIZE(string) - 1 + \ + BUILD_ASSERT_OR_ZERO(ARRAY_SIZE(string) > 0) + \ + BUILD_ASSERT_OR_ZERO(sizeof(string[0]) == sizeof(char)); \ + int skip = test__run_begin(); \ + if (!skip) { \ + for (int i = 0; i < 256; i++) { \ + if (!check_int(class(i), ==, !!memchr(string, i, len)))\ + test_msg(" i: 0x%02x", i); \ + } \ + check(!class(EOF)); \ } \ - if (!check(!func(EOF))) \ - test_msg(" i: 0x%02x (EOF)", EOF); \ -} - -#define TEST_CHAR_CLASS(class) TEST(test_ctype_##class(), #class " works") + test__run_end(!skip, TEST_LOCATION(), #class " works"); \ +} while (0) #define DIGIT "0123456789" #define LOWER "abcdefghijklmnopqrstuvwxyz" @@ -44,37 +33,21 @@ static void test_ctype_##func(void) { \ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \ "\x7f" -TEST_CTYPE_FUNC(isdigit, DIGIT) -TEST_CTYPE_FUNC(isspace, " \n\r\t") -TEST_CTYPE_FUNC(isalpha, LOWER UPPER) -TEST_CTYPE_FUNC(isalnum, LOWER UPPER DIGIT) -TEST_CTYPE_FUNC(is_glob_special, "*?[\\") -TEST_CTYPE_FUNC(is_regex_special, "$()*+.?[\\^{|") -TEST_CTYPE_FUNC(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~") -TEST_CTYPE_FUNC(isascii, ASCII) -TEST_CTYPE_FUNC(islower, LOWER) -TEST_CTYPE_FUNC(isupper, UPPER) -TEST_CTYPE_FUNC(iscntrl, CNTRL) -TEST_CTYPE_FUNC(ispunct, PUNCT) -TEST_CTYPE_FUNC(isxdigit, DIGIT "abcdefABCDEF") -TEST_CTYPE_FUNC(isprint, LOWER UPPER DIGIT PUNCT " ") - int cmd_main(int argc, const char **argv) { - /* Run all character type tests */ - TEST_CHAR_CLASS(isspace); - TEST_CHAR_CLASS(isdigit); - TEST_CHAR_CLASS(isalpha); - TEST_CHAR_CLASS(isalnum); - TEST_CHAR_CLASS(is_glob_special); - TEST_CHAR_CLASS(is_regex_special); - TEST_CHAR_CLASS(is_pathspec_magic); - TEST_CHAR_CLASS(isascii); - TEST_CHAR_CLASS(islower); - TEST_CHAR_CLASS(isupper); - TEST_CHAR_CLASS(iscntrl); - TEST_CHAR_CLASS(ispunct); - TEST_CHAR_CLASS(isxdigit); - TEST_CHAR_CLASS(isprint); + TEST_CHAR_CLASS(isspace, " \n\r\t"); + TEST_CHAR_CLASS(isdigit, DIGIT); + TEST_CHAR_CLASS(isalpha, LOWER UPPER); + TEST_CHAR_CLASS(isalnum, LOWER UPPER DIGIT); + TEST_CHAR_CLASS(is_glob_special, "*?[\\"); + TEST_CHAR_CLASS(is_regex_special, "$()*+.?[\\^{|"); + TEST_CHAR_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~"); + TEST_CHAR_CLASS(isascii, ASCII); + TEST_CHAR_CLASS(islower, LOWER); + TEST_CHAR_CLASS(isupper, UPPER); + TEST_CHAR_CLASS(iscntrl, CNTRL); + TEST_CHAR_CLASS(ispunct, PUNCT); + TEST_CHAR_CLASS(isxdigit, DIGIT "abcdefABCDEF"); + TEST_CHAR_CLASS(isprint, LOWER UPPER DIGIT PUNCT " "); return test_done(); }
diff --git a/tree-walk.c b/tree-walk.c index b517792..690fa65 100644 --- a/tree-walk.c +++ b/tree-walk.c
@@ -100,7 +100,7 @@ void *fill_tree_descriptor(struct repository *r, if (oid) { buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL); if (!buf) - die("unable to read tree %s", oid_to_hex(oid)); + die(_("unable to read tree (%s)"), oid_to_hex(oid)); } init_tree_desc(desc, buf, size); return buf;
diff --git a/upload-pack.c b/upload-pack.c index 2537aff..902144b 100644 --- a/upload-pack.c +++ b/upload-pack.c
@@ -28,6 +28,7 @@ #include "shallow.h" #include "write-or-die.h" #include "json-writer.h" +#include "strmap.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11) @@ -61,12 +62,11 @@ struct upload_pack_data { struct string_list symref; /* v0 only */ struct object_array want_obj; struct object_array have_obj; - struct oid_array haves; /* v2 only */ - struct string_list wanted_refs; /* v2 only */ + struct strmap wanted_refs; /* v2 only */ struct strvec hidden_refs; struct object_array shallows; - struct string_list deepen_not; + struct oidset deepen_not; struct object_array extra_edge_obj; int depth; timestamp_t deepen_since; @@ -113,6 +113,8 @@ struct upload_pack_data { unsigned done : 1; /* v2 only */ unsigned allow_ref_in_want : 1; /* v2 only */ unsigned allow_sideband_all : 1; /* v2 only */ + unsigned seen_haves : 1; /* v2 only */ + unsigned allow_packfile_uris : 1; /* v2 only */ unsigned advertise_sid : 1; unsigned sent_capabilities : 1; }; @@ -120,13 +122,12 @@ struct upload_pack_data { static void upload_pack_data_init(struct upload_pack_data *data) { struct string_list symref = STRING_LIST_INIT_DUP; - struct string_list wanted_refs = STRING_LIST_INIT_DUP; + struct strmap wanted_refs = STRMAP_INIT; struct strvec hidden_refs = STRVEC_INIT; struct object_array want_obj = OBJECT_ARRAY_INIT; struct object_array have_obj = OBJECT_ARRAY_INIT; - struct oid_array haves = OID_ARRAY_INIT; struct object_array shallows = OBJECT_ARRAY_INIT; - struct string_list deepen_not = STRING_LIST_INIT_DUP; + struct oidset deepen_not = OID_ARRAY_INIT; struct string_list uri_protocols = STRING_LIST_INIT_DUP; struct object_array extra_edge_obj = OBJECT_ARRAY_INIT; struct string_list allowed_filters = STRING_LIST_INIT_DUP; @@ -137,7 +138,6 @@ static void upload_pack_data_init(struct upload_pack_data *data) data->hidden_refs = hidden_refs; data->want_obj = want_obj; data->have_obj = have_obj; - data->haves = haves; data->shallows = shallows; data->deepen_not = deepen_not; data->uri_protocols = uri_protocols; @@ -155,13 +155,12 @@ static void upload_pack_data_init(struct upload_pack_data *data) static void upload_pack_data_clear(struct upload_pack_data *data) { string_list_clear(&data->symref, 1); - string_list_clear(&data->wanted_refs, 1); + strmap_clear(&data->wanted_refs, 1); strvec_clear(&data->hidden_refs); object_array_clear(&data->want_obj); object_array_clear(&data->have_obj); - oid_array_clear(&data->haves); object_array_clear(&data->shallows); - string_list_clear(&data->deepen_not, 0); + oidset_clear(&data->deepen_not); object_array_clear(&data->extra_edge_obj); list_objects_filter_release(&data->filter_options); string_list_clear(&data->allowed_filters, 0); @@ -463,7 +462,7 @@ static void create_pack_file(struct upload_pack_data *pack_data, fail: free(output_state); - send_client_data(3, abort_msg, sizeof(abort_msg), + send_client_data(3, abort_msg, strlen(abort_msg), pack_data->use_sideband); die("git upload-pack: %s", abort_msg); } @@ -471,7 +470,9 @@ static void create_pack_file(struct upload_pack_data *pack_data, static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid) { int we_knew_they_have = 0; - struct object *o = parse_object(the_repository, oid); + struct object *o = parse_object_with_flags(the_repository, oid, + PARSE_OBJECT_SKIP_HASH_CHECK | + PARSE_OBJECT_DISCARD_TREE); if (!o) die("oops (%s)", oid_to_hex(oid)); @@ -528,8 +529,6 @@ static int get_common_commits(struct upload_pack_data *data, int got_other = 0; int sent_ready = 0; - save_commit_buffer = 0; - for (;;) { const char *arg; @@ -926,12 +925,13 @@ static int send_shallow_list(struct upload_pack_data *data) strvec_push(&av, "rev-list"); if (data->deepen_since) strvec_pushf(&av, "--max-age=%"PRItime, data->deepen_since); - if (data->deepen_not.nr) { + if (oidset_size(&data->deepen_not)) { + const struct object_id *oid; + struct oidset_iter iter; strvec_push(&av, "--not"); - for (i = 0; i < data->deepen_not.nr; i++) { - struct string_list_item *s = data->deepen_not.items + i; - strvec_push(&av, s->string); - } + oidset_iter_init(&data->deepen_not, &iter); + while ((oid = oidset_iter_next(&iter))) + strvec_push(&av, oid_to_hex(oid)); strvec_push(&av, "--not"); } for (i = 0; i < data->want_obj.nr; i++) { @@ -1007,7 +1007,7 @@ static int process_deepen_since(const char *line, timestamp_t *deepen_since, int return 0; } -static int process_deepen_not(const char *line, struct string_list *deepen_not, int *deepen_rev_list) +static int process_deepen_not(const char *line, struct oidset *deepen_not, int *deepen_rev_list) { const char *arg; if (skip_prefix(line, "deepen-not ", &arg)) { @@ -1015,7 +1015,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not, struct object_id oid; if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1) die("git upload-pack: ambiguous deepen-not: %s", line); - string_list_append(deepen_not, ref); + oidset_insert(deepen_not, &oid); free(ref); *deepen_rev_list = 1; return 1; @@ -1151,7 +1151,9 @@ static void receive_needs(struct upload_pack_data *data, free(client_sid); } - o = parse_object(the_repository, &oid_buf); + o = parse_object_with_flags(the_repository, &oid_buf, + PARSE_OBJECT_SKIP_HASH_CHECK | + PARSE_OBJECT_DISCARD_TREE); if (!o) { packet_writer_error(&data->writer, "upload-pack: not our ref %s", @@ -1362,6 +1364,9 @@ static int upload_pack_config(const char *var, const char *value, data->allow_ref_in_want = git_config_bool(var, value); } else if (!strcmp("uploadpack.allowsidebandall", var)) { data->allow_sideband_all = git_config_bool(var, value); + } else if (!strcmp("uploadpack.blobpackfileuri", var)) { + if (value) + data->allow_packfile_uris = 1; } else if (!strcmp("core.precomposeunicode", var)) { precomposed_unicode = git_config_bool(var, value); } else if (!strcmp("transfer.advertisesid", var)) { @@ -1385,10 +1390,13 @@ static int upload_pack_protected_config(const char *var, const char *value, return 0; } -static void get_upload_pack_config(struct upload_pack_data *data) +static void get_upload_pack_config(struct repository *r, + struct upload_pack_data *data) { - git_config(upload_pack_config, data); + repo_config(r, upload_pack_config, data); git_protected_config(upload_pack_protected_config, data); + + data->allow_sideband_all |= git_env_bool("GIT_TEST_SIDEBAND_ALL", 0); } void upload_pack(const int advertise_refs, const int stateless_rpc, @@ -1398,7 +1406,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc, struct upload_pack_data data; upload_pack_data_init(&data); - get_upload_pack_config(&data); + get_upload_pack_config(the_repository, &data); data.stateless_rpc = stateless_rpc; data.timeout = timeout; @@ -1468,7 +1476,8 @@ static int parse_want(struct packet_writer *writer, const char *line, "expected to get oid, not '%s'", line); o = parse_object_with_flags(the_repository, &oid, - PARSE_OBJECT_SKIP_HASH_CHECK); + PARSE_OBJECT_SKIP_HASH_CHECK | + PARSE_OBJECT_DISCARD_TREE); if (!o) { packet_writer_error(writer, @@ -1490,14 +1499,13 @@ static int parse_want(struct packet_writer *writer, const char *line, } static int parse_want_ref(struct packet_writer *writer, const char *line, - struct string_list *wanted_refs, + struct strmap *wanted_refs, struct strvec *hidden_refs, struct object_array *want_obj) { const char *refname_nons; if (skip_prefix(line, "want-ref ", &refname_nons)) { struct object_id oid; - struct string_list_item *item; struct object *o = NULL; struct strbuf refname = STRBUF_INIT; @@ -1509,8 +1517,11 @@ static int parse_want_ref(struct packet_writer *writer, const char *line, } strbuf_release(&refname); - item = string_list_append(wanted_refs, refname_nons); - item->util = oiddup(&oid); + if (strmap_put(wanted_refs, refname_nons, oiddup(&oid))) { + packet_writer_error(writer, "duplicate want-ref %s", + refname_nons); + die("duplicate want-ref %s", refname_nons); + } if (!starts_with(refname_nons, "refs/tags/")) { struct commit *commit = lookup_commit_in_graph(the_repository, &oid); @@ -1532,15 +1543,14 @@ static int parse_want_ref(struct packet_writer *writer, const char *line, return 0; } -static int parse_have(const char *line, struct oid_array *haves) +static int parse_have(const char *line, struct upload_pack_data *data) { const char *arg; if (skip_prefix(line, "have ", &arg)) { struct object_id oid; - if (get_oid_hex(arg, &oid)) - die("git upload-pack: expected SHA1 object, got '%s'", arg); - oid_array_append(haves, &oid); + got_oid(data, arg, &oid); + data->seen_haves = 1; return 1; } @@ -1552,13 +1562,13 @@ static void trace2_fetch_info(struct upload_pack_data *data) struct json_writer jw = JSON_WRITER_INIT; jw_object_begin(&jw, 0); - jw_object_intmax(&jw, "haves", data->haves.nr); + jw_object_intmax(&jw, "haves", data->have_obj.nr); jw_object_intmax(&jw, "wants", data->want_obj.nr); - jw_object_intmax(&jw, "want-refs", data->wanted_refs.nr); + jw_object_intmax(&jw, "want-refs", strmap_get_size(&data->wanted_refs)); jw_object_intmax(&jw, "depth", data->depth); jw_object_intmax(&jw, "shallows", data->shallows.nr); jw_object_bool(&jw, "deepen-since", data->deepen_since); - jw_object_intmax(&jw, "deepen-not", data->deepen_not.nr); + jw_object_intmax(&jw, "deepen-not", oidset_size(&data->deepen_not)); jw_object_bool(&jw, "deepen-relative", data->deepen_relative); if (data->filter_options.choice) jw_object_string(&jw, "filter", list_object_filter_config_name(data->filter_options.choice)); @@ -1586,7 +1596,7 @@ static void process_args(struct packet_reader *request, &data->hidden_refs, &data->want_obj)) continue; /* process have line */ - if (parse_have(arg, &data->haves)) + if (parse_have(arg, data)) continue; /* process args like thin-pack */ @@ -1638,14 +1648,17 @@ static void process_args(struct packet_reader *request, continue; } - if ((git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) || - data->allow_sideband_all) && + if (data->allow_sideband_all && !strcmp(arg, "sideband-all")) { data->writer.use_sideband = 1; continue; } - if (skip_prefix(arg, "packfile-uris ", &p)) { + if (data->allow_packfile_uris && + skip_prefix(arg, "packfile-uris ", &p)) { + if (data->uri_protocols.nr) + send_err_and_die(data, + "multiple packfile-uris lines forbidden"); string_list_split(&data->uri_protocols, p, ',', -1); continue; } @@ -1664,27 +1677,7 @@ static void process_args(struct packet_reader *request, trace2_fetch_info(data); } -static int process_haves(struct upload_pack_data *data, struct oid_array *common) -{ - int i; - - /* Process haves */ - for (i = 0; i < data->haves.nr; i++) { - const struct object_id *oid = &data->haves.oid[i]; - - if (!repo_has_object_file_with_flags(the_repository, oid, - OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT)) - continue; - - oid_array_append(common, oid); - - do_got_oid(data, oid); - } - - return 0; -} - -static int send_acks(struct upload_pack_data *data, struct oid_array *acks) +static int send_acks(struct upload_pack_data *data, struct object_array *acks) { int i; @@ -1696,7 +1689,7 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks) for (i = 0; i < acks->nr; i++) { packet_writer_write(&data->writer, "ACK %s\n", - oid_to_hex(&acks->oid[i])); + oid_to_hex(&acks->objects[i].item->oid)); } if (!data->wait_for_done && ok_to_give_up(data)) { @@ -1710,13 +1703,11 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks) static int process_haves_and_send_acks(struct upload_pack_data *data) { - struct oid_array common = OID_ARRAY_INIT; int ret = 0; - process_haves(data, &common); if (data->done) { ret = 1; - } else if (send_acks(data, &common)) { + } else if (send_acks(data, &data->have_obj)) { packet_writer_delim(&data->writer); ret = 1; } else { @@ -1725,24 +1716,23 @@ static int process_haves_and_send_acks(struct upload_pack_data *data) ret = 0; } - oid_array_clear(&data->haves); - oid_array_clear(&common); return ret; } static void send_wanted_ref_info(struct upload_pack_data *data) { - const struct string_list_item *item; + struct hashmap_iter iter; + const struct strmap_entry *e; - if (!data->wanted_refs.nr) + if (strmap_empty(&data->wanted_refs)) return; packet_writer_write(&data->writer, "wanted-refs\n"); - for_each_string_list_item(item, &data->wanted_refs) { + strmap_for_each_entry(&data->wanted_refs, &iter, e) { packet_writer_write(&data->writer, "%s %s\n", - oid_to_hex(item->util), - item->string); + oid_to_hex(e->value), + e->key); } packet_writer_delim(&data->writer); @@ -1771,7 +1761,7 @@ enum fetch_state { FETCH_DONE, }; -int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request) +int upload_pack_v2(struct repository *r, struct packet_reader *request) { enum fetch_state state = FETCH_PROCESS_ARGS; struct upload_pack_data data; @@ -1780,7 +1770,7 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request) upload_pack_data_init(&data); data.use_sideband = LARGE_PACKET_MAX; - get_upload_pack_config(&data); + get_upload_pack_config(r, &data); while (state != FETCH_DONE) { switch (state) { @@ -1796,7 +1786,7 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request) * they didn't want anything. */ state = FETCH_DONE; - } else if (data.haves.nr) { + } else if (data.seen_haves) { /* * Request had 'have' lines, so lets ACK them. */ @@ -1839,41 +1829,28 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request) int upload_pack_advertise(struct repository *r, struct strbuf *value) { - if (value) { - int allow_filter_value; - int allow_ref_in_want; - int allow_sideband_all_value; - char *str = NULL; + struct upload_pack_data data; + upload_pack_data_init(&data); + get_upload_pack_config(r, &data); + + if (value) { strbuf_addstr(value, "shallow wait-for-done"); - if (!repo_config_get_bool(r, - "uploadpack.allowfilter", - &allow_filter_value) && - allow_filter_value) + if (data.allow_filter) strbuf_addstr(value, " filter"); - if (!repo_config_get_bool(r, - "uploadpack.allowrefinwant", - &allow_ref_in_want) && - allow_ref_in_want) + if (data.allow_ref_in_want) strbuf_addstr(value, " ref-in-want"); - if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) || - (!repo_config_get_bool(r, - "uploadpack.allowsidebandall", - &allow_sideband_all_value) && - allow_sideband_all_value)) + if (data.allow_sideband_all) strbuf_addstr(value, " sideband-all"); - if (!repo_config_get_string(r, - "uploadpack.blobpackfileuri", - &str) && - str) { + if (data.allow_packfile_uris) strbuf_addstr(value, " packfile-uris"); - free(str); - } } + upload_pack_data_clear(&data); + return 1; }
diff --git a/userdiff.c b/userdiff.c index 2b1dab2..92ef649 100644 --- a/userdiff.c +++ b/userdiff.c
@@ -3,6 +3,7 @@ #include "userdiff.h" #include "attr.h" #include "strbuf.h" +#include "environment.h" static struct userdiff_driver *drivers; static int ndrivers; @@ -459,7 +460,8 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r, if (!driver->textconv) return NULL; - if (driver->textconv_want_cache && !driver->textconv_cache) { + if (driver->textconv_want_cache && !driver->textconv_cache && + have_git_dir()) { struct notes_cache *c = xmalloc(sizeof(*c)); struct strbuf name = STRBUF_INIT;
diff --git a/wt-status.c b/wt-status.c index b5a2908..7108a92 100644 --- a/wt-status.c +++ b/wt-status.c
@@ -1107,12 +1107,15 @@ void wt_status_append_cut_line(struct strbuf *buf) strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char); } -void wt_status_add_cut_line(FILE *fp) +void wt_status_add_cut_line(struct wt_status *s) { struct strbuf buf = STRBUF_INIT; + if (s->added_cut_line) + return; + s->added_cut_line = 1; wt_status_append_cut_line(&buf); - fputs(buf.buf, fp); + fputs(buf.buf, s->fp); strbuf_release(&buf); } @@ -1143,11 +1146,12 @@ static void wt_longstatus_print_verbose(struct wt_status *s) * file (and even the "auto" setting won't work, since it * will have checked isatty on stdout). But we then do want * to insert the scissor line here to reliably remove the - * diff before committing. + * diff before committing, if we didn't already include one + * before. */ if (s->fp != stdout) { rev.diffopt.use_color = 0; - wt_status_add_cut_line(s->fp); + wt_status_add_cut_line(s); } if (s->verbose > 1 && s->committable) { /* print_updated() printed a header, so do we */
diff --git a/wt-status.h b/wt-status.h index 819dcad..5e99ba4 100644 --- a/wt-status.h +++ b/wt-status.h
@@ -130,6 +130,7 @@ struct wt_status { int rename_score; int rename_limit; enum wt_status_format status_format; + unsigned char added_cut_line; /* boolean */ struct wt_status_state state; struct object_id oid_commit; /* when not Initial */ @@ -147,7 +148,7 @@ struct wt_status { size_t wt_status_locate_end(const char *s, size_t len); void wt_status_append_cut_line(struct strbuf *buf); -void wt_status_add_cut_line(FILE *fp); +void wt_status_add_cut_line(struct wt_status *s); void wt_status_prepare(struct repository *r, struct wt_status *s); void wt_status_print(struct wt_status *s); void wt_status_collect(struct wt_status *s);