Merge branch 'dm/submodule-update-i-shorthand' The '-i' shorthand for the '--init' option, which was accepted by the 'git submodule update' command until it was broken in a modernization of the option-parsing code, has been restored. * dm/submodule-update-i-shorthand: submodule--helper: accept '-i' shorthand for update --init
diff --git a/.b4-config b/.b4-config new file mode 100644 index 0000000..fd4fb56 --- /dev/null +++ b/.b4-config
@@ -0,0 +1,6 @@ +# Note that these are default values that you can tweak via the typical +# git-config(1) machinery. You thus shouldn't ever have to change this file. +# See also https://b4.docs.kernel.org/en/latest/config.html. +[b4] +send-same-thread = shallow +prep-cover-template = ./.b4-cover-template
diff --git a/.b4-cover-template b/.b4-cover-template new file mode 100644 index 0000000..8168d8a --- /dev/null +++ b/.b4-cover-template
@@ -0,0 +1,12 @@ +${cover} + +--- +${shortlog} + +${diffstat} + +${range_diff} +--- +base-commit: ${base_commit} +change-id: ${change_id} +${prerequisites}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cf341d7..85cfedf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml
@@ -420,7 +420,9 @@ CI_JOB_IMAGE: ${{matrix.vector.image}} CUSTOM_PATH: /custom runs-on: ubuntu-latest - container: ${{matrix.vector.image}} + container: + image: ${{ matrix.vector.image }} + options: ${{ github.repository_visibility == 'private' && '--pids-limit 16384 --ulimit nproc=16384:16384 --ulimit nofile=32768:32768' || '' }} steps: - name: prepare libc6 for actions if: matrix.vector.jobname == 'linux32'
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a8e909..1c4d04d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml
@@ -88,13 +88,8 @@ tags: - saas-macos-large-m2pro variables: - TEST_OUTPUT_DIRECTORY: "/Volumes/RAMDisk" + TEST_OUTPUT_DIRECTORY: "/tmp/test-output" before_script: - # Create a 4GB RAM disk that we use to store test output on. This small hack - # significantly speeds up tests by more than a factor of 2 because the - # macOS runners use network-attached storage as disks, which is _really_ - # slow with the many small writes that our tests do. - - sudo diskutil apfs create $(hdiutil attach -nomount ram://8192000) RAMDisk - ./ci/install-dependencies.sh script: - ./ci/run-build-and-tests.sh @@ -152,6 +147,9 @@ needs: - job: "build:mingw64" artifacts: true + variables: + # Windows runners don't have enough RAM to run EXPENSIVE tests. + GIT_TEST_LONG: false before_script: - *windows_before_script - git-sdk/usr/bin/bash.exe -l -c 'tar xf artifacts/artifacts.tar.gz' @@ -200,6 +198,9 @@ script: - | & "C:/Program Files/Git/usr/bin/bash.exe" -l -c 'ci/run-test-slice-meson.sh build $CI_NODE_INDEX $CI_NODE_TOTAL' + variables: + # Windows runners don't have enough RAM to run EXPENSIVE tests. + GIT_TEST_LONG: false after_script: - | if ($env:CI_JOB_STATUS -ne "success") {
diff --git a/.mailmap b/.mailmap index c2e3939..f8ede07 100644 --- a/.mailmap +++ b/.mailmap
@@ -277,6 +277,7 @@ SZEDER Gábor <szeder.dev@gmail.com> <szeder@ira.uka.de> Tao Qingyun <taoqy@ls-a.me> <845767657@qq.com> Tay Ray Chuan <rctay89@gmail.com> +Taylor Blau <me@ttaylorr.com> <ttaylorr@openai.com> Ted Percival <ted@midg3t.net> <ted.percival@quest.com> Theodore Ts'o <tytso@mit.edu> Thomas Ackermann <th.acker@arcor.de> <th.acker66@arcor.de>
diff --git a/Documentation/MyFirstContribution.adoc b/Documentation/MyFirstContribution.adoc index b9fdefc..34bfa2e 100644 --- a/Documentation/MyFirstContribution.adoc +++ b/Documentation/MyFirstContribution.adoc
@@ -790,7 +790,7 @@ v3", etc. in place of "PATCH". For example, "[PATCH v2 1/3]" would be the first of three patches in the second iteration. Each iteration is sent with a new cover letter (like "[PATCH v2 0/3]" above), itself a reply to the cover letter of the - previous iteration (more on that below). + first iteration (more on that below). NOTE: A single-patch topic is sent with "[PATCH]", "[PATCH v2]", etc. without _i_/_n_ numbering (in the above thread overview, no single-patch topic appears, @@ -833,7 +833,7 @@ be merged. ---- -At this point the tutorial diverges, in order to demonstrate two +At this point the tutorial diverges, in order to demonstrate three different methods of formatting your patchset and getting it reviewed. The first method to be covered is GitGitGadget, which is useful for those @@ -845,9 +845,14 @@ setup which can change depending on your system and will not be covered in this tutorial. +The third method to be covered is `b4`, which builds on top of `git +format-patch` and `git send-email`. This method is the recommended way to +submit patches via mail as it automates a lot of the bookkeeping required by +`git send-email`. + Regardless of which method you choose, your engagement with reviewers will be -the same; the review process will be covered after the sections on GitGitGadget -and `git send-email`. +the same; the review process will be covered after the sections on GitGitGadget, +`git send-email` and `b4`. [[howto-ggg]] == Sending Patches via GitGitGadget @@ -1214,7 +1219,7 @@ need the exact same body in your second cover letter; focus on explaining to reviewers the changes you've made that may not be as visible. -You will also need to go and find the Message-ID of your previous cover letter. +You will also need to go and find the Message-ID of your first cover letter. You can either note it when you send the first series, from the output of `git send-email`, or you can look it up on the https://lore.kernel.org/git[mailing list]. Find your cover letter in the @@ -1227,8 +1232,8 @@ Your Message-ID is `<foo.12345.author@example.com>`. This example will be used below as well; make sure to replace it with the correct Message-ID for your -**previous cover letter** - that is, if you're sending v2, use the Message-ID -from v1; if you're sending v3, use the Message-ID from v2. +**first cover letter** - that is, for any subsequent version that you send, +always use the Message-ID from v1. While you're looking at the email, you should also note who is CC'd, as it's common practice in the mailing list to keep all CCs on a thread. You can add @@ -1296,6 +1301,87 @@ 2.21.0.392.gf8f6787159e-goog ---- +[[howto-b4]] +== Sending Patches with `b4` + +`b4` is a tool that builds on top of `git format-patch` and `git send-email`. +It automates much of the bookkeeping involved in sending a patch series to a +mailing-list-based project. + +Refer to the https://b4.docs.kernel.org/[b4 documentation] for a full reference. + +[[prep-b4]] +=== Preparing a Patch Series + +`b4` tracks your patch series as a branch. To start tracking the `psuh` branch +you have been working on, run: + +---- +$ b4 prep --enroll master +---- + +This enrolls the current branch, using `master` as the base of the topic. `b4` +manages the cover letter as part of the branch, so you can edit it at any time +with: + +---- +$ b4 prep --edit-cover +---- + +The cover letter not only tracks the content of the top-level mail, but also +the set of recipients. You can add recipients by adding `To:` and `Cc:` +trailer lines. + +[[send-b4]] +=== Sending the Patches + +Before sending the series out for real, you can inspect what `b4` would send by +passing `--dry-run`: + +---- +$ b4 send --dry-run +---- + +Once you are happy with the result, send the series with: + +---- +$ b4 send +---- + +[[v2-b4]] +=== Sending v2 + +When you are ready to send a new iteration of your series, refine your +patches as usual using linkgit:git-rebase[1]. Note that you typically want to +rebase on top of the cover letter. You can configure an alias to enable easy +rebases going forward: + +--- +$ git config set alias.b4-rebase 'rebase "HEAD^{/--- b4-submit-tracking ---}"' +$ git b4-rebase -i +--- + +Before sending out the new version you should also update the cover letter with +`b4 prep --edit-cover` to note the relevant changes compared to the previous +version. You can inspect the changes between the two versions with `b4 prep +--compare-to=v1`. + +Same as with the first version, you can use `b4 send` to send out the second +version. `b4` automatically bumps the version to `v2`, generates the range-diff +against the previous iteration, and threads the new series as a reply to the +cover letter of the first version. + +[[configure-b4]] +=== Configure b4 + +`b4` can be configured via linkgit:git-config[1]. In addition to that, projects +can have their own set of defaults in `.b4-config` in the root tree, which also +uses Git's config format. The user's configuration always takes precedence over +the per-project defaults. + +Refer to the https://b4.docs.kernel.org/en/latest/config.html[b4 config documentation] +for more information on the available options. + [[now-what]] == My Patch Got Emailed - Now What? @@ -1330,6 +1416,28 @@ single polished version came 2 days later instead, and that version with fewer mistakes were the only one they would need to review. +This consideration applies not only when going from the initial patch to v2, +but also to later iterations of the same series. There is no fixed rule for how +long to wait before sending a new version. A useful default is to send at most +one new version of the same patch series per day. This gives multiple reviewers +time to comment, gives reviewers across time zones a fair chance to +participate, lets you batch feedback together, and gives you time to think +through the comments you received. Knowing that you should not immediately send +another version also encourages you to review the patches more carefully before +sending them, catch small mistakes such as typos and off-by-one errors +yourself, and let reviewers spend more of their attention on design, +algorithms, and other substantial issues. + +The right timing depends on the topic and the feedback. Larger series usually +need more review time. If the only comments so far are minor, such as typo +fixes, it often makes sense to wait a little longer in case deeper reviews are +still coming. If the comments call for substantial rework, do not rush out an +updated version before you have reviewed the larger changes carefully. Instead, +reply to the review that prompted the rewrite, say that you are preparing a +substantial rework, and mention which parts of the current series will become +obsolete so reviewers can avoid spending time on them until the updated series +is ready. + [[reviewing]] === Responding to Reviews @@ -1337,11 +1445,13 @@ After a few days, you will hopefully receive a reply to your patchset with some comments. Woohoo! Now you can get back to work. -It's good manners to reply to each comment, notifying the reviewer that you have -made the change suggested, feel the original is better, or that the comment -inspired you to do something a new way which is superior to both the original -and the suggested change. This way reviewers don't need to inspect your v2 to -figure out whether you implemented their comment or not. +It's good manners to reply to each comment in the mailing list discussion +instead of letting the next version of your patch be your only response. Tell +the reviewer whether you plan to make the suggested change, keep the original, +or pursue a different approach. This way reviewers can respond to your reasoning +before you spend time preparing a version they may not agree with, and later do +not need to inspect your v2 to figure out whether you implemented their comment +or not. Reviewers may ask you about what you wrote in the patchset, either in the proposed commit log message or in the changes themselves. You @@ -1367,6 +1477,11 @@ if you aren't sure why a change was suggested, or what the reviewer is asking you to do. +When replying to review comments, quote only the parts of the message that are +relevant to your response. It is usually helpful to trim away unrelated context, +such as large portions of the patch that are not being discussed, while keeping +enough quoted text for readers to understand what you are responding to. + Make sure your email client has a plaintext email mode and it is turned on; the Git list rejects HTML email. Please also follow the mailing list etiquette outlined in the
diff --git a/Documentation/RelNotes/2.55.0.adoc b/Documentation/RelNotes/2.55.0.adoc index f564353..e7e77a8 100644 --- a/Documentation/RelNotes/2.55.0.adoc +++ b/Documentation/RelNotes/2.55.0.adoc
@@ -85,8 +85,8 @@ * Promisor remote handling has been refactored and fixed in preparation for auto-configuration of advertised remotes. - * Rust support is enabled by default (but still allows opting out) in - some future version of Git. + * Rust support is enabled by default (but still allows opting out); + in Git version 3.0, Rust will become mandatory. * Preparation of the xdiff/ codebase to work with Rust.
diff --git a/Documentation/RelNotes/2.56.0.adoc b/Documentation/RelNotes/2.56.0.adoc new file mode 100644 index 0000000..9e3bf38 --- /dev/null +++ b/Documentation/RelNotes/2.56.0.adoc
@@ -0,0 +1,308 @@ +Git v2.56 Release Notes +======================= + +UI, Workflows & Features +------------------------ + + * Advice shown by "git status" when the local branch is behind or has + diverged from its push branch has been updated to suggest "git pull + <remote> <branch>". + + * The handling of promisor-remote protocol capability has been updated + to allow the other side to add to the list of promisor remotes via the + 'promisor.acceptFromServerURL' configuration variable. + + * The 'ort' merge backend has been hardened against corrupt trees by + ensuring it aborts under appropriate error conditions. + + * The `fetch.followRemoteHEAD` configuration variable has been added to + provide a default for the per-remote `remote.<name>.followRemoteHEAD` + setting. + + * "git log --follow" has been updated to better handle non-linear + history, in which the path being tracked gets renamed differently in + multiple history lines. + + * The "git repo info" command has been taught new keys to output both + absolute and relative paths for "gitdir" and "commondir", supported by + a new path-formatting helper extracted from "git rev-parse". + + * When 'git push origin/main' or 'git branch origin main' is run, the + command is now recognized as a potential typo, and advice has been + added to offer a typo fix. + + * The 'git refs' toolbox has been extended with new 'create', 'delete', + 'update', and 'rename' subcommands to create, delete, update, and + rename references, respectively. + + * The experimental 'git history' command has been taught a new 'drop' + subcommand to remove a commit, with its descendants replayed onto its + parent. + + * The alignment of commit object name abbreviations in 'git blame' + output has been optimized to reserve a column for marks (caret, + question mark, or asterisk) only when such marks are actually shown. + + * Option parsing with 'git rev-parse --parseopt' and in most 'git' + subcommands has been updated to exit with 0 (instead of 129) when the + help option ('-h' or '--help') is requested directly by the user, + aligning with standard Unix convention. + + +Performance, Internal Implementation, Development Support etc. +-------------------------------------------------------------- + + * The refactoring of 'setup.c' has been continued to drop remaining + global state (`git_work_tree_cfg`, `is_bare_repository_cfg`), updating + `is_bare_repository()` to no longer implicitly rely on + `the_repository`. + + * Project-specific configuration for b4 has been introduced, and the + documentation has been updated to recommend using it as a + streamlined method for submitting patches. + + * The default format path of git cat-file --batch has been optimized + to use strbuf_add_oid_hex() and strbuf_add_uint() instead of + strbuf_addf(), yielding a noticeable speedup. + + * Commands that list branches and tags (like git branch and git tag) + have been optimized to pass the namespace prefix when initializing + their ref iterator, avoiding a loose-ref scaling regression in + repositories with many unrelated loose references. + + * The packed object source has been refactored into a proper struct + odb_source. + + * The global configuration variables protect_hfs and protect_ntfs have + been migrated into struct repo_config_values to tie them to + per-repository configuration state. + + * The trailer sections in SubmittingPatches have been updated to + encourage use of standard trailers. + + * The documentation in SubmittingPatches has been updated to clarify how + patch contributors should respond to design and viability critiques, + and how the resolution of such critiques should be recorded in the + final commit messages. + + * The pack-objects command has been updated to support reachability + bitmaps and delta-islands concurrently with the `--path-walk` option, + allowing faster packaging by falling back to path-walk when bitmaps + cannot fully satisfy the request. + + * Documentation on community contribution guidelines has been updated to + encourage replying to review comments before rerolling, and to advise + a default limit of at most one reroll per day to give reviewers across + different time zones enough time to participate. + + * The lazy priority queue optimization pattern (deferring actual removal + in 'prio_queue_get()' to allow get+put fusion) has been folded + directly into 'prio_queue' itself, speeding up commit traversal + workflows and simplifying callers. + + * The 'reprepare()' callback for object database sources has been + generalized into a 'prepare()' callback with an optional flush cache + flag, and a new 'odb_prepare()' wrapper has been introduced to allow + pre-opening object database sources. + + * The 'whence' field in 'struct object_info' has been removed. The + backend-specific object information retrieval has been refactored into + an opt-in 'struct object_info_source' structure. + + * A racy build failure under Meson has been corrected by ensuring that + the generated header file 'hook-list.h' is built before compiling + files in 'builtin_sources' that depend on it. + + * The repository discovery and repository configuration phases, which + were previously intertwined in 'setup.c', have been split. Repository + discovery has been updated to populate a 'struct repo_discovery' + without modifying the repository state, which is then taken by + repository configuration to initialize the repository, paving the way + for clean unification of repository configuration. + + * The 'SubmittingPatches' document has been updated to explicitly + describe the expectation for contributors to retract or abandon their + patch series when they are no longer pursuing it. + + * The contributor guide has been updated to advise new contributors to + trim irrelevant quoted text when replying to review comments, matching + the existing advice given to reviewers. + + * The build system has been updated to support building universal macOS + binaries when 'Rust' is enabled, by compiling separate static archives + for each target triple listed in 'RUST_TARGETS' and combining them + using the macOS 'lipo' tool. The 'git-credential-osxkeychain' helper + has been updated to link against '$(RUST_LIB)' when 'Rust' is enabled. + + * The test suite has been updated to use the 'test_grep' helper instead + of bare 'grep' for test assertions, allowing file contents to be + printed on failure for easier debugging. A new 'greplint' linter has + been introduced to detect and prevent new bare 'grep' assertions from + being added to the test suite. + + * The pipelines in 't1410-reflog.sh' have been replaced with the + 'test_stdout_line_count' helper to avoid suppressing the exit code of + 'git' commands, ensuring failures are not hidden from the test suite. + + * The cache-scanning loop in 'next_cache_entry()' has been optimized + to avoid rescanning already-unpacked index entries, preventing a + quadratic performance slow-down when diffing the working tree + against a commit with a pathspec matching early index entries. + + * The global configuration variable 'ignore_case' (representing the + 'core.ignorecase' configuration) has been migrated into 'struct + repo_config_values' to tie it to a specific repository instance. + + * The performance of ref updates and reads using the 'reftable' backend + in the presence of many deletion tombstone records has been optimized + by removing the tombstone suppression flag from the merged iterator + and instead skipping tombstones at higher-level call sites where + iteration bounds are known. + + * Various code paths have been hardened against potential NULL-pointer + dereferences and invalid file descriptor accesses flagged by + Coverity. + + * The in-tree 'b4' cover letter template has been updated to include the + 'change-id' trailer, ensuring that sent tags generated by 'b4' contain + the required tracking information for subsequent runs. + + +Fixes since v2.55 +----------------- + + * A regression in the error diagnosis code for invalid .git files has + been fixed, avoiding a potential NULL-pointer crash when reporting + that a .git file does not point to a valid repository. + (merge 54a441bcea jk/setup-gitfile-diag-fix later to maint). + + * Support for hashing loose or packed objects larger than 4GB on Windows + and other LLP64 platforms has been improved by converting object header + buffers and data-handling functions from 'unsigned long' to 'size_t'. + (merge d99e13d0be po/hash-object-size-t later to maint). + + * The display of the rebase todo list in "git status" has been + improved to correctly abbreviate object IDs for more commands and + avoid misinterpreting refs as object IDs. + (merge 6f34e5f9e3 pw/status-rebase-todo later to maint). + + * Reference backend configuration has been updated to load lazily to + avoid recursive calls during repository initialization when 'onbranch' + configuration conditions are evaluated. This has also fixed a memory + leak and allowed the unused `chdir_notify_reparent()` machinery to be + dropped. + (merge d6522d01df ps/refs-onbranch-fixes later to maint). + + * The connectivity check has been refactored to search for promisor + objects in a generic way using the object database interface, + rather than iterating packfiles directly. This allows connectivity + checks to work properly in repositories that do not use packfiles. + (merge 66ee9cb930 ps/connected-generic-promisor-checks later to maint). + + * A test checking interactions between git rebase --quit and + autostash in t3420-rebase-autostash.sh has been corrected to use + test_path_is_missing instead of ! grep on a file that shouldn't + exist in the conflicted state. + (merge eaad121fef sg/t3420-do-not-grep-in-missing-file later to maint). + + * The GPG and SSH signature parsing code has been corrected to strip + carriage return characters only when they immediately precede line + feeds, instead of unconditionally stripping all carriage returns. + (merge 5dea8b690b ad/gpg-strip-cr-before-lf later to maint). + + * A memory leak in the 'reftable_writer_new()' initialization function + has been fixed by delaying the allocation of 'struct reftable_writer' + until after input options are validated. + (merge c6fb3b9c3e jk/reftable-leakfix later to maint). + + * A memory leak in the '--base' handling of 'git format-patch' has been + plugged, and the leak reporting of the test suite when running under a + TAP harness has been improved. + (merge 973a0373ff jk/format-patch-leakfix later to maint). + + * A write file stream resource leak has been fixed as part of a code + cleanup. + (merge ebb4d2ffa3 jc/history-message-prep-fix later to maint). + + * Various memory leaks in the Bloom-filter code paths that are exposed + when running tests with the 'GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1' + environment variable have been plugged. + (merge 459088ec2e jk/bloom-leak-fixes later to maint). + + * The wincred credential helper has been updated to avoid memory + corruption when erasing credentials and to prevent silent + credential loss when storing OAuth tokens, by correcting buffer + allocations and arguments passed to safe-CRT APIs. + (merge f635ab9ab4 js/wincred-fixes later to maint). + + * Various code paths that initialize a cryptographic hash context but + bail out or finish without calling 'git_hash_final()' have been taught + to call 'git_hash_discard()' to release allocated resources, fixing + memory leaks when Git is built with non-default backends like + 'OpenSSL' or 'libgcrypt'. + (merge 600588d2aa jk/hash-algo-leak-fixes later to maint). + + * Various resource leaks, invalid file descriptor closures, and process + handle ownership issues flagged by Coverity have been fixed. + (merge 9184231173 js/coverity-fixes later to maint). + + * Dockerized CI jobs running in private GitHub repositories have been + adjusted to use explicit process and file limits, preventing resource + exhaustion errors on private runners. + (merge bad766fbac js/ci-dockerized-pid-limit later to maint). + + * Various test scripts have been updated to clean up large temporary + files and repositories, reducing peak disk usage during testing. + Also, expensive tests have been disabled on platforms that lack + sufficient resources (like 32-bit platforms and Windows CI runners), + and the long test suite has been enabled in GitLab CI. + (merge 84248444ad ps/t-fixes-for-git-test-long later to maint). + + * The UTF-8 precomposition wrapper on macOS has been updated to use a + flexible array member to represent the name of a directory entry, + preventing fortified libc checks from failing when the name is + reallocated to be larger than 'NAME_MAX' bytes. + (merge 1eb281159f ih/precompose-flex-array later to maint). + + * The 'git_hash_*()' wrappers have been updated to be used consistently + across the codebase instead of direct calls to members of 'struct + git_hash_algo', and 'git_hash_discard()' has been made idempotent to + simplify cleanups. + (merge 9e396aa553 jk/git-hash-cleanups later to maint). + + * The sideband demultiplexer has been updated to recognize ANSI SGR + escape sequences that use colon-separated subfields (e.g., for + 256-color or true-color codes). + (merge 3792b2aea4 mm/sideband-ansi-sgr-colon-fix later to maint). + + * The 'reftable' code has been hardened against corrupted tables by + fixing out-of-bounds writes, out-of-bounds reads, and abort calls + during parsing. + (merge ca93c27328 ps/reftable-hardening later to maint). + + * A description in the release notes for Git 2.55.0 has been + retroactively updated to clarify that Rust support is enabled by + default, but still optional, and will become mandatory in Git 3.0. + (merge 18b2009d14 jc/relnotes-2.55-rust-fix later to maint). + + * The early-exit optimization in 'paint_down_to_common()' has been + gated on the queue being generation-ordered, fixing a bug where + 'git merge-base' (without '--all') could return incorrect results + on repositories with v1 commit graphs and clock skew. + (merge ae68032a8d kk/commit-reach-find-all-fix later to maint). + + * The client-side parser of the server-advertised bundle-URI list has + been updated to drain the remaining response in order to avoid + protocol desynchronization when the server sends a misconfigured list. + Also, the server-side has been taught to omit empty configuration + values instead of sending invalid key-value lines. + (merge 50de1169e4 tc/bundle-uri-empty-fix later to maint). + + * The 'topo_levels' slab was propagated only to the topmost layer of a + split commit-graph chain, causing topological levels for commits in + base layers to be recomputed during incremental writes. This has been + corrected. + + * The stream-based object signature verification path has been + corrected to avoid double-closing the stream on read errors. + (merge cfd52a74a0 ps/odb-stream-double-close-fix later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index f042bb5..c269e47 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches
@@ -48,13 +48,41 @@ . You get comments and suggestions for improvements. You may even get them in an "on top of your change" patch form. You are expected to - respond to them with "Reply-All" on the mailing list, while taking - them into account while preparing an updated set of patches. + respond to them with "Reply-All" on the mailing list, instead of + letting an updated patch series be your only response. Tell + reviewers which suggestions you plan to use, which ones you disagree + with, and when a comment leads you to consider a different approach. + Use these replies and any follow-up discussion as input when + preparing an updated set of patches. ++ +Be particularly mindful of critiques regarding the high-level design +or viability of your proposal (e.g., questioning if the feature is +worth implementing, or if the chosen approach is appropriate). Defend +your design decisions on the list first and work with reviewers and +other members to improve the design before revising the implementation. +This will avoid wasting effort on an implementation before its design is +solid. ++ +Make sure that any new version explains and justifies those design +decisions more clearly, in the cover letter and in the revised commit +messages. Aim to make the reviewers say "it is now clear why we may +want to do this with the updated version". ++ +Topics with unresolved fundamental design critiques will not be +considered ready for merging. + It is often beneficial to allow some time for reviewers to provide feedback before sending a new version, rather than sending an updated series immediately after receiving a review. This helps collect broader -input and avoids unnecessary churn from many rapid iterations. +input, gives reviewers in different time zones a fair chance to comment, +and avoids unnecessary churn from many rapid iterations. Waiting also +encourages you to polish each version before sending it, so reviewers +can focus on substantial issues rather than typos or other small +mistakes. ++ +As a rough default, avoid sending more than one new version of the same +series per day, while considering the size of the series and the depth +of review. . These early update iterations are expected to be full replacements, not incremental updates on top of what you posted already. If you @@ -95,6 +123,20 @@ top, it gets merged to the 'master' branch and waits to become part of the next major release. +But sometimes things do not work as planned: + +. If a mailing list discussion convinces you that your changes aren't + ideal, please explicitly retract the topic to save the maintainer + time and effort. + +. If you must drop a topic due to shifting priorities, lack of time, + or other commitments, notify the list as a courtesy so others can + take over. Anyone can resurrect the topic later when they have the + capacity to do so. + +. Topics with unaddressed review comments that remain inactive for + four weeks may be discarded by the maintainer. + In the following sections, many techniques and conventions are listed to help your patches get reviewed effectively in such a life cycle. @@ -323,6 +365,10 @@ . alternate solutions considered but discarded, if any. +. records the resolution of design or viability concerns raised by the + community during the review, if any, ensuring the historical record + explains why the chosen approach was accepted over alternatives. + [[present-tense]] The problem statement that describes the status quo is written in the present tense. Write "The code does X when it is given input Y", @@ -375,7 +421,7 @@ .... [[sign-off]] -=== Certify your work by adding your `Signed-off-by` trailer +=== Certify your work by adding your `Signed-off-by:` trailer To improve tracking of who did what, we ask you to certify that you wrote the patch or have the right to pass it on under the same license @@ -412,7 +458,7 @@ this project or the open source license(s) involved. ____ -you add a "Signed-off-by" trailer to your commit, that looks like +you add a `Signed-off-by:` trailer to your commit, that looks like this: .... @@ -422,19 +468,23 @@ This line can be added by Git if you run the git-commit command with the -s option. -Notice that you can place your own `Signed-off-by` trailer when +Notice that you can place your own `Signed-off-by:` trailer when forwarding somebody else's patch with the above rules for D-C-O. Indeed you are encouraged to do so. Do not forget to place an in-body "From: " line at the beginning to properly attribute the change to its true author (see (2) above). +Place this `Signed-off-by:` trailer at the end, after trailers added by +others and after other trailers added by you; see +<<commit-trailers,Commit trailers>> below ("chronological order"). + This procedure originally came from the Linux kernel project, so our rule is quite similar to theirs, but what exactly it means to sign-off your patch differs from project to project, so it may be different from that of the project you are accustomed to. [[real-name]] -Please use a known identity in the `Signed-off-by` trailer, since we cannot +Please use a known identity in the `Signed-off-by:` trailer, since we cannot accept anonymous contributions. It is common, but not required, to use some form of your real name. We realize that some contributors are not comfortable doing so or prefer to contribute under a pseudonym or preferred name and we can accept @@ -445,7 +495,15 @@ you if questions arise about your contribution. [[commit-trailers]] -If you like, you can put extra trailers at the end: +=== Commit trailers +It is polite to credit people who have helped with your work to a +substantial enough degree. This project uses commit trailers for that, +where the credited person is written out like a Git author, i.e. with +both their name and their email address. Note that the threshold to +credit someone is a judgement call, and crediting someone for simple +review work is certainly not necessary. + +These are the common trailers in use: . `Reported-by:` is used to credit someone who found the bug that the patch attempts to fix. @@ -458,6 +516,10 @@ and found it to have the desired effect. . `Co-authored-by:` is used to indicate that people exchanged drafts of a patch before submitting it. +. `Based-on-patch-by:` is used when someone else authored parts of the + patch that you are submitting. This might be relevant if someone sent + a patch to the mailing list with their sign-off. (Be mindful and ask + them to sign off on it if they did not.) . `Helped-by:` is used to credit someone who suggested ideas for changes without providing the precise changes in patch form. . `Mentored-by:` is used to credit someone with helping develop a @@ -469,8 +531,18 @@ encourage you to instead use one of the common trailers in this project highlighted above. +Other projects might regularly refer to other kinds of data, like +`Fixes:` and `Link:` in the Linux Kernel project, but these ones in +particular are not used in this project. + Only capitalize the very first letter of the trailer, i.e. favor -"Signed-off-by" over "Signed-Off-By" and "Acked-by:" over "Acked-By". +`Signed-off-by:` over `Signed-Off-By:` and `Acked-by:` over `Acked-By:`. + +As mentioned under <<dco,DCO>> above, trailers are added in +chronological order; one person might sign-off on a patch and send it to +someone else, who then in turn adds her own sign-off. Further, any +trailers that you add beyond your sign-off should come before that +sign-off. That makes it clear what trailers which person added. [[cover-letter]] === Cover Letter @@ -587,8 +659,8 @@ instead left the review to trusted others. Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and -`Tested-by:` lines as necessary to credit people who helped your -patch, and "cc:" them when sending such a final version for inclusion. +`Tested-by:` (see <<commit-trailers,Commit trailers>>), and "cc:" them +when sending such a final version for inclusion. ==== `format-patch` and `send-email` @@ -598,8 +670,10 @@ type e-mails) might render your patches unusable. NOTE: Here we outline the procedure using `format-patch` and -`send-email`, but you can instead use GitGitGadget to send in your -patches (see link:MyFirstContribution.html[MyFirstContribution]). +`send-email`, but you can instead use GitGitGadget or `b4` to send in +your patches (see link:MyFirstContribution.html[MyFirstContribution]). +Contributors are encouraged to use `b4`, which automates much of the +bookkeeping that is otherwise done by hand. People on the Git mailing list need to be able to read and comment on the changes you are submitting. It is important for @@ -613,10 +687,13 @@ series. To that end, send them as replies to either an additional "cover letter" message (see below), the first patch, or the respective preceding patch. Here is a link:MyFirstContribution.html#v2-git-send-email[step-by-step guide] on -how to submit updated versions of a patch series. +how to submit updated versions of a patch series. Before sending another +version, make sure you have answered meaningful review comments in the existing +discussion. Also give reviewers enough time to comment before sending another +version. If your log message (including your name on the -`Signed-off-by` trailer) is not writable in ASCII, make sure that +`Signed-off-by:` trailer) is not writable in ASCII, make sure that you send off a message in the correct encoding. WARNING: Be wary of your MUAs word-wrap @@ -636,7 +713,7 @@ The `git format-patch` command follows the best current practice to format the body of an e-mail message. At the beginning of the patch should come your commit message, ending with the -`Signed-off-by` trailers, and a line that consists of three dashes, +`Signed-off-by:` trailers, and a line that consists of three dashes, followed by the diffstat information and the patch itself. If you are forwarding a patch from somebody else, optionally, at the beginning of the e-mail message just before the commit
diff --git a/Documentation/config/advice.adoc b/Documentation/config/advice.adoc index 257db58..81f80a9 100644 --- a/Documentation/config/advice.adoc +++ b/Documentation/config/advice.adoc
@@ -48,6 +48,10 @@ to create a local branch after the fact. diverging:: Shown when a fast-forward is not possible. + fetchRemoteHEADWarn:: + Shown when linkgit:git-fetch[1] reveals that a remote `HEAD` + differs from what is set locally and the user has opted into + receiving a warning in this situation. fetchShowForcedUpdates:: Shown when linkgit:git-fetch[1] takes a long time to calculate forced updates after ref updates, or to warn @@ -90,6 +94,11 @@ Shown when linkgit:git-push[1] rejects a forced update of a branch when its remote-tracking ref has updates that we do not have locally. + pushRepoLooksLikeRef:: + Shown when the repository given to linkgit:git-push[1] is not + a configured remote but looks like a `<remote>/<branch>` ref, + suggesting that the remote and branch be given as separate + arguments. pushUnqualifiedRefname:: Shown when linkgit:git-push[1] gives up trying to guess based on the source and destination refs what
diff --git a/Documentation/config/fetch.adoc b/Documentation/config/fetch.adoc index 04ac909..00435e9 100644 --- a/Documentation/config/fetch.adoc +++ b/Documentation/config/fetch.adoc
@@ -126,3 +126,22 @@ The creation token values are chosen by the provider serving the specific bundle URI. If you modify the URI at `fetch.bundleURI`, then be sure to remove the value for the `fetch.bundleCreationToken` value before fetching. + +`fetch.followRemoteHEAD`:: + When fetching using a default refspec, this setting determines how to handle + differences between a fetched remote's `HEAD` and the local + `remotes/<name>/HEAD` symbolic-ref. Its value is one of ++ +-- +`create`;; + Create `remotes/<name>/HEAD` if a ref exists on the remote, but not locally. + An existing symbolic-ref will not be touched. This is the default value. +`warn`;; + Display a warning if the remote advertises a different `HEAD` than what is + set locally. Behaves like "create" if the local symbolic-ref doesn't exist. +`always`;; + Silently update `remotes/<name>/HEAD` whenever the remote advertises a new + value. +`never`;; + Never create or modify the `remotes/<name>/HEAD` symbolic-ref. +--
diff --git a/Documentation/config/log.adoc b/Documentation/config/log.adoc index f20cc25..757a7be 100644 --- a/Documentation/config/log.adoc +++ b/Documentation/config/log.adoc
@@ -53,8 +53,7 @@ `log.follow`:: If `true`, `git log` will act as if the `--follow` option was used when a single <path> is given. This has the same limitations as `--follow`, - i.e. it cannot be used to follow multiple files and does not work well - on non-linear history. + i.e. it cannot be used to follow multiple files. `log.graphColors`:: A list of colors, separated by commas, that can be used to draw
diff --git a/Documentation/config/promisor.adoc b/Documentation/config/promisor.adoc index 39af63d..c1f3d76 100644 --- a/Documentation/config/promisor.adoc +++ b/Documentation/config/promisor.adoc
@@ -32,24 +32,136 @@ advertised regardless of this setting. promisor.acceptFromServer:: - If set to "all", a client will accept all the promisor remotes - a server might advertise using the "promisor-remote" - capability. If set to "knownName" the client will accept - promisor remotes which are already configured on the client - and have the same name as those advertised by the client. This - is not very secure, but could be used in a corporate setup - where servers and clients are trusted to not switch name and - URLs. If set to "knownUrl", the client will accept promisor - remotes which have both the same name and the same URL - configured on the client as the name and URL advertised by the - server. This is more secure than "all" or "knownName", so it - should be used if possible instead of those options. Default - is "none", which means no promisor remote advertised by a - server will be accepted. By accepting a promisor remote, the - client agrees that the server might omit objects that are - lazily fetchable from this promisor remote from its responses - to "fetch" and "clone" requests from the client. Name and URL - comparisons are case sensitive. See linkgit:gitprotocol-v2[5]. + Controls which promisor remotes advertised by a server (using the + "promisor-remote" protocol capability) a client will accept. By + accepting a promisor remote, the client agrees that the server + might omit objects that are lazily fetchable from this promisor + remote from its responses to "fetch" and "clone" requests. ++ +Note that this option does not cause new remotes to be automatically +created in the client's configuration. It only allows remotes which +are somehow already configured to be trusted for the current +operation, or their fields to be updated (if `promisor.storeFields` is +set and the remote already exists locally). To allow Git to +automatically create and persist new remotes from server +advertisements, use `promisor.acceptFromServerUrl`. ++ +The available options are: ++ +* `none` (default): No promisor remote advertised by a server will be + accepted. ++ +* `knownUrl`: The client will accept promisor remotes that are already + configured on the client and have both the same name and the same URL + as advertised by the server. This is more secure than `all` or + `knownName`, and should be used if possible instead of those options. ++ +* `knownName`: The client will accept promisor remotes that are already + configured on the client and have the same name as those advertised + by the server. This is not very secure, but could be used in a corporate + setup where servers and clients are trusted to not switch names and URLs. ++ +* `all`: The client will accept all the promisor remotes a server might + advertise. This is the least secure option and should only be used in + fully trusted environments. ++ +Name and URL comparisons are case-sensitive. See linkgit:gitprotocol-v2[5] +for protocol details. + +promisor.acceptFromServerUrl:: + A glob pattern to specify which server-advertised URLs a + client is allowed to act on. When a URL matches, the client + will accept the advertised remote as a promisor remote, may + automatically create a new remote configuration for it and may + automatically accept field updates (such as authentication + tokens) from the server, even if `promisor.acceptFromServer` + is set to `none` (the default). ++ +This option can appear multiple times in config files. An advertised +URL will be accepted if it matches _ANY_ glob pattern specified by +this option in _ANY_ config file read by Git. ++ +When both `promisor.acceptFromServer` and `promisor.acceptFromServerUrl` +are set, `promisor.acceptFromServerUrl` is consulted first and takes +precedence: if a matching pattern leads to acceptance (either by +auto-configuring an unknown remote or by accepting field updates for +a known remote whose URL matches both the local configuration and the +allowlist), the advertised remote is accepted regardless of the +`promisor.acceptFromServer` setting. If no pattern in +`promisor.acceptFromServerUrl` triggers acceptance, the decision is +left to `promisor.acceptFromServer`. ++ +Note however that, even when an advertised URL matches a pattern in +`promisor.acceptFromServerUrl`, an already-existing remote on the +client whose name matches the advertised name but whose configured URL +differs from the advertised one will _NOT_ be accepted through +`promisor.acceptFromServerUrl`. This prevents a server from silently +re-pointing an existing client-side remote at a different URL. (Such a +remote may still be accepted through `promisor.acceptFromServer=all` +or `=knownName`, which have their own, looser semantics; see the +documentation of that option.) ++ +Be _VERY_ careful with these patterns: `*` matches any sequence of +characters within the 'host' and 'path' parts of a URL (but cannot +cross part boundaries). An overly broad pattern is a major security +risk, as a matching URL allows a server to auto-configure new remotes +and to update fields (such as authentication tokens) on known remotes +without further confirmation. To minimize security risks, follow these +guidelines: ++ +-- +1. Start with a secure protocol scheme, like `https://` or `ssh://`. ++ +2. Only allow domain names or paths where you control and trust _ALL_ + the content. Be especially careful with shared hosting platforms + like `github.com` or `gitlab.com`. A broad pattern like + `https://gitlab.com/*` is dangerous because it trusts every + repository on the entire platform. Always restrict such patterns to + your specific organization or namespace (e.g., + `https://gitlab.com/your-org/*`). ++ +3. Never use globs at the end of domain names. For example, + `https://cdn.your-org.com/*` might be safe, but + `https://cdn.your-org.com*/*` is a major security risk because + the latter matches `https://cdn.your-org.com.hacker.net/repo`. ++ +4. Be careful using globs at the beginning of domain names. While the + code ensures a `*` in the host cannot cross into the path, a + pattern like `https://*.example.com/*` will still match any + subdomain. This is extremely dangerous on shared hosting platforms + (e.g., `https://*.github.io/*` trusts every user's site on the + entire platform). +-- ++ +Before matching, both the advertised URL and the pattern are +normalized: the scheme and host are lowercased, percent-encoded +characters are decoded where possible, and path segments like `..` +are resolved. The port must also match exactly (e.g., +`https://example.com:8080/*` will not match a URL advertised on +port 9999). The username and password components of the URL are +ignored during matching. Note that embedding credentials in URLs is +discouraged. Passing authentication tokens via the `token` field of +the `promisor-remote` capability is strongly preferred. ++ +The glob pattern can optionally be prefixed with a remote name and an +equals sign (e.g., `cdn=https://cdn.example.com/*`). If such a prefix +is provided, accepted remotes will be saved under that name. If no +such prefix is provided, a safe remote name will be automatically +generated by sanitizing the URL and prefixing it with +`promisor-auto-`. ++ +If a remote with the chosen name already exists but points to a +different URL, Git will append a numeric suffix (e.g., `-1`, `-2`) to +the name to prevent overwriting existing configurations. You should +make sure that this doesn't happen often though, as remotes will be +rejected if the numeric suffix increases too much. In all cases, the +original name advertised by the server is recorded in the +`remote.<name>.advertisedAs` configuration variable for tracing and +debugging purposes. ++ +For the security implications of accepting a promisor remote, see the +documentation of `promisor.acceptFromServer`. For details on the +protocol, see linkgit:gitprotocol-v2[5]. promisor.checkFields:: A comma or space separated list of additional remote related
diff --git a/Documentation/config/remote.adoc b/Documentation/config/remote.adoc index eb9c8a3..3a20d0f 100644 --- a/Documentation/config/remote.adoc +++ b/Documentation/config/remote.adoc
@@ -91,6 +91,15 @@ When set to true, this remote will be used to fetch promisor objects. +remote.<name>.advertisedAs:: + When a promisor remote is automatically configured using + information advertised by a server through the + `promisor-remote` protocol capability (see + `promisor.acceptFromServerUrl`), the server's originally + advertised name is saved in this variable. This is for + information, tracing and debugging purposes. Users should not + typically modify or create such configuration entries. + remote.<name>.partialclonefilter:: The filter that will be applied when fetching from this promisor remote. Changing or clearing this value will only affect fetches for new commits. @@ -157,15 +166,12 @@ the list from broader config scenarios. remote.<name>.followRemoteHEAD:: - How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD` - when fetching using the configured refspecs of a remote. - The default value is "create", which will create `remotes/<name>/HEAD` - if it exists on the remote, but not locally; this will not touch an - already existing local reference. Setting it to "warn" will print - a message if the remote has a different value than the local one; - in case there is no local reference, it behaves like "create". - A variant on "warn" is "warn-if-not-$branch", which behaves like - "warn", but if `HEAD` on the remote is `$branch` it will be silent. - Setting it to "always" will silently update `remotes/<name>/HEAD` to - the value on the remote. Finally, setting it to "never" will never - change or create the local reference. + When fetching this remote using its default refspec, this setting determines + how to handle differences between the remote's `HEAD` and the local + `remotes/<name>/HEAD` symbolic-ref. Overrides the value of + `fetch.followRemoteHEAD`. See `fetch.followRemoteHEAD` for a description of + accepted values. ++ +In addition to the values supported by `fetch.followRemoteHEAD`, this setting +may also take on the value "warn-if-not-`$branch`", which behaves like "warn", +but ignores the warning if the remote's `HEAD` is `remotes/<name>/$branch`.
diff --git a/Documentation/git-blame.adoc b/Documentation/git-blame.adoc index 8808009..2b74e45 100644 --- a/Documentation/git-blame.adoc +++ b/Documentation/git-blame.adoc
@@ -88,11 +88,12 @@ include::diff-algorithm-option.adoc[] `--abbrev=<n>`:: - Instead of using the default _7+1_ hexadecimal digits as the - abbreviated object name, use _<m>+1_ digits, where _<m>_ is at - least _<n>_ but ensures the commit object names are unique. - Note that 1 column - is used for a caret to mark the boundary commit. + Instead of using the default _7_ hexadecimal digits as the + abbreviated object name, use at least _<n>_ digits, but ensure + the commit object names are unique. + If commits marked with caret (boundary), question mark (ignored) + or asterisk (unblamable) are shown, extend unmarked object names + to align them. THE DEFAULT FORMAT
diff --git a/Documentation/git-history.adoc b/Documentation/git-history.adoc index 2ba8121..28b477c 100644 --- a/Documentation/git-history.adoc +++ b/Documentation/git-history.adoc
@@ -8,6 +8,7 @@ SYNOPSIS -------- [synopsis] +git history drop <commit> [--dry-run] [--update-refs=(branches|head)] [--empty=(drop|keep|abort)] git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)] git history reword <commit> [--dry-run] [--update-refs=(branches|head)] git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...] @@ -51,13 +52,28 @@ first-class conflicts. When using `fixup` with `--empty=drop`, dropping the root commit is not yet -supported. +supported. Likewise, `drop` cannot remove the root commit or a merge commit. COMMANDS -------- The following commands are available to rewrite history in different ways: +`drop <commit>`:: + Remove the specified commit from the history. All descendants of the + commit are replayed directly onto its parent. ++ +The root commit cannot be dropped as that may lead to edge cases where refs +end up with no commits anymore. Merge commits cannot be dropped either; see +LIMITATIONS. ++ +If `HEAD` points at a commit that is to be rewritten, the index and working +tree are updated to match the new `HEAD`. The command aborts before any +references are updated in case local modifications would be overwritten. ++ +If replaying any descendant would result in a conflict, the command aborts +with an error. + `fixup <commit>`:: Apply the currently staged changes to the specified commit. This is similar in nature to `git commit --fixup=<commit>` followed by `git @@ -170,6 +186,26 @@ commit. All descendant commits have been replayed on top of the rewritten history. +Drop a commit +~~~~~~~~~~~~~ + +---------- +$ git log --oneline +abc1234 (HEAD -> main) third +def5678 second +ghi9012 first + +$ git history drop 'main^{/second}' + +$ git log --oneline +jkl3456 (HEAD -> main) third +ghi9012 first +---------- + +The `second` commit has been removed from the history, and `third` has been +replayed directly on top of `first`. All branches that pointed at the dropped +commit have been moved to its parent. + Split a commit ~~~~~~~~~~~~~~
diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc index 8a27aa1..65cd00c 100644 --- a/Documentation/git-pack-objects.adoc +++ b/Documentation/git-pack-objects.adoc
@@ -402,11 +402,13 @@ of filenames that cause collisions in Git's default name-hash algorithm. + -Incompatible with `--delta-islands`. The `--use-bitmap-index` option is -ignored in the presence of `--path-walk`. The `--path-walk` option -supports the `--filter=<spec>` forms `blob:none`, `blob:limit=<n>`, -`tree:0`, `object:type=<type>`, and `sparse:<oid>`. These supported filter -types can be combined with the `combine:<spec>+<spec>` form. +When `--use-bitmap-index` is specified with `--path-walk`, a successful +bitmap traversal is used for object enumeration, with path-walk +remaining as the fallback traversal when the bitmap cannot satisfy the +request. The `--path-walk` option supports the `--filter=<spec>` forms +`blob:none`, `blob:limit=<n>`, `tree:0`, `object:type=<type>`, and +`sparse:<oid>`. These supported filter types can be combined with the +`combine:<spec>+<spec>` form. DELTA ISLANDS
diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc index fa33680..ce278c5 100644 --- a/Documentation/git-refs.adoc +++ b/Documentation/git-refs.adoc
@@ -20,6 +20,10 @@ [ --stdin | (<pattern>...)] git refs exists <ref> git refs optimize [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>] +git refs create [--message=<reason>] [--no-deref] [--create-reflog] <ref> <new-value> +git refs delete [--message=<reason>] [--no-deref] <ref> [<old-value>] +git refs update [--message=<reason>] [--no-deref] [--create-reflog] <ref> <new-value> [<old-value>] +git refs rename [--message=<reason>] <old-ref> <new-ref> DESCRIPTION ----------- @@ -51,6 +55,28 @@ usage. This subcommand is an alias for linkgit:git-pack-refs[1] and offers identical functionality. +create:: + Create the given reference, which must not already exist, pointing at + `<new-value>`. + +delete:: + Delete the given reference. This subcommand mirrors `git update-ref -d` + (see linkgit:git-update-ref[1]). When `<old-value>` is given, the + reference is only deleted after verifying that it currently contains + `<old-value>`. + +update:: + Update the given reference to point at `<new-value>`. If `<old-value>` + is given, the reference is only updated after verifying that it + currently contains `<old-value>`. As a special case, an all-zeroes + `<new-value>` deletes the branch, whereas an all-zeroes `<old-value>` + ensures that the branch does not yet exist. + +rename:: + Rename the reference `<oldref>` to `<newref>`. The old reference must + exist and the new reference must not yet exist, and both must have a + well-formed name (see linkgit:git-check-ref-format[1]). + OPTIONS ------- @@ -90,6 +116,20 @@ include::pack-refs-options.adoc[] +The following options are specific to commands which write references: + +`--create-reflog`:: + Create a reflog for the reference even if one would not ordinarily be + created. + +`--message=<reason>`:: + Use the given <reason> string for the reflog entry associated with the + update. An empty message is rejected. + +`--no-deref`:: + Operate on <ref> itself rather than the reference it points to via a + symbolic ref. + KNOWN LIMITATIONS -----------------
diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc index 42262c1..ed7d80c 100644 --- a/Documentation/git-repo.adoc +++ b/Documentation/git-repo.adoc
@@ -104,6 +104,21 @@ `object.format`:: The object format (hash algorithm) used in the repository. +`path.commondir.absolute`:: + The canonical absolute path to the Git repository's common + directory (the shared `.git` directory containing objects, + refs, and global configuration). + +`path.commondir.relative`:: + The path to the Git repository's common directory relative to + the current working directory. + +`path.gitdir.absolute`:: + The canonical absolute path to the Git repository directory (the `.git` directory). + +`path.gitdir.relative`:: + The path to the Git repository directory relative to the current working directory. + `references.format`:: The reference storage format. The valid values are: +
diff --git a/Documentation/gitprotocol-v2.adoc b/Documentation/gitprotocol-v2.adoc index befa697..2beb705 100644 --- a/Documentation/gitprotocol-v2.adoc +++ b/Documentation/gitprotocol-v2.adoc
@@ -866,10 +866,11 @@ On the server side, the "promisor.advertise" and "promisor.sendFields" configuration options can be used to control what it advertises. On -the client side, the "promisor.acceptFromServer" configuration option -can be used to control what it accepts, and the "promisor.storeFields" -option, to control what it stores. See the documentation of these -configuration options in linkgit:git-config[1] for more information. +the client side, the "promisor.acceptFromServer" and +"promisor.acceptFromServerUrl" configuration options can be used to +control what it accepts, and the "promisor.storeFields" option, to +control what it stores. See the documentation of these configuration +options in linkgit:git-config[1] for more information. Note that in the future it would be nice if the "promisor-remote" protocol capability could be used by the server, when responding to
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6e4df99..a72f090 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@ #!/bin/sh -DEF_VER=v2.55.0 +DEF_VER=v2.55.GIT LF=' '
diff --git a/Makefile b/Makefile index 1cec251..98e995e 100644 --- a/Makefile +++ b/Makefile
@@ -500,6 +500,14 @@ # # Building Rust code requires Cargo. # +# Define RUST_TARGETS if you want to cross-compile. If left unspecified, it uses +# the default Rust target on the system. +# +# On macOS, this supports specifying multiple targets, separated by a space. +# This will produce a Universal static library using `lipo`. +# +# Example: RUST_TARGETS="aarch64-apple-darwin x86_64-apple-darwin" +# # == SHA-1 and SHA-256 defines == # # === SHA-1 backend === @@ -939,16 +947,19 @@ LIB_FILE = libgit.a +ifndef NO_RUST ifdef DEBUG -RUST_TARGET_DIR = target/debug +RUST_BUILD_CONFIG = debug else -RUST_TARGET_DIR = target/release +RUST_BUILD_CONFIG = release endif ifeq ($(uname_S),Windows) -RUST_LIB = $(RUST_TARGET_DIR)/gitcore.lib +RUST_LIB_NAME = gitcore.lib else -RUST_LIB = $(RUST_TARGET_DIR)/libgitcore.a +RUST_LIB_NAME = libgitcore.a +endif +RUST_LIB = target/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME) endif GITLIBS = common-main.o $(LIB_FILE) @@ -1218,6 +1229,7 @@ LIB_OBJS += odb/source-files.o LIB_OBJS += odb/source-inmemory.o LIB_OBJS += odb/source-loose.o +LIB_OBJS += odb/source-packed.o LIB_OBJS += odb/streaming.o LIB_OBJS += odb/transaction.o LIB_OBJS += oid-array.o @@ -1233,6 +1245,7 @@ LIB_OBJS += pack-revindex.o LIB_OBJS += pack-write.o LIB_OBJS += packfile.o +LIB_OBJS += packfile-list.o LIB_OBJS += pager.o LIB_OBJS += parallel-checkout.o LIB_OBJS += parse.o @@ -2599,6 +2612,7 @@ FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o FUZZ_OBJS += oss-fuzz/fuzz-parse-attr-line.o +FUZZ_OBJS += oss-fuzz/fuzz-reftable.o FUZZ_OBJS += oss-fuzz/fuzz-url-decode-mem.o .PHONY: fuzz-objs fuzz-objs: $(FUZZ_OBJS) @@ -3017,11 +3031,35 @@ $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +ifndef NO_RUST +ifeq ($(RUST_TARGETS),) $(RUST_LIB): Cargo.toml $(RUST_SOURCES) $(LIB_FILE) $(QUIET_CARGO)cargo build $(CARGO_ARGS) +else +ifneq ($(words $(RUST_TARGETS)),1) +ifneq ($(uname_S),Darwin) +$(error Building universal Rust libraries requires macOS (lipo is not available on $(uname_S))) +endif +endif + +RUST_MEMBER_LIBS = $(foreach target,$(RUST_TARGETS),target/$(target)/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME)) +$(RUST_MEMBER_LIBS): target/%/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME): Cargo.toml $(RUST_SOURCES) $(LIB_FILE) + $(QUIET_CARGO)cargo build $(CARGO_ARGS) --target $* + +$(RUST_LIB): $(RUST_MEMBER_LIBS) + $(call mkdir_p_parent_template) + $(QUIET_GEN)\ + if test $(words $(RUST_TARGETS)) -gt 1; \ + then \ + lipo -create $^ -output $@; \ + else \ + cp $< $@; \ + fi +endif .PHONY: rust rust: $(RUST_LIB) +endif export DEFAULT_EDITOR DEFAULT_PAGER @@ -4072,7 +4110,8 @@ contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT) $(AR) $(ARFLAGS) $@ $^ -contrib/credential/osxkeychain/git-credential-osxkeychain: contrib/credential/osxkeychain/git-credential-osxkeychain.o $(LIB_FILE) GIT-LDFLAGS +# When Rust is enabled, git-credential-osxkeychain depends on Rust symbols in $(RUST_LIB) +contrib/credential/osxkeychain/git-credential-osxkeychain: contrib/credential/osxkeychain/git-credential-osxkeychain.o $(LIB_FILE) $(RUST_LIB) GIT-LDFLAGS $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(LIBS) -framework Security -framework CoreFoundation
diff --git a/README.md b/README.md index d87bca1..46489b0 100644 --- a/README.md +++ b/README.md
@@ -1,4 +1,5 @@ -[](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush) +[](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush) +[](https://gitlab.com/git-scm/git/-/pipelines?ref=master) Git - fast, scalable, distributed revision control system =========================================================
diff --git a/RelNotes b/RelNotes index 159e44a..752580e 120000 --- a/RelNotes +++ b/RelNotes
@@ -1 +1 @@ -Documentation/RelNotes/2.55.0.adoc \ No newline at end of file +Documentation/RelNotes/2.56.0.adoc \ No newline at end of file
diff --git a/advice.c b/advice.c index 0018501..63bf8b0 100644 --- a/advice.c +++ b/advice.c
@@ -69,6 +69,7 @@ static struct { [ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent" }, [ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching" }, [ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate" }, + [ADVICE_PUSH_REPO_LOOKS_LIKE_REF] = { "pushRepoLooksLikeRef" }, [ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName" }, [ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected" }, [ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward" }, /* backwards compatibility */
diff --git a/advice.h b/advice.h index 8def280..66f6cd6 100644 --- a/advice.h +++ b/advice.h
@@ -36,6 +36,7 @@ enum advice_type { ADVICE_PUSH_NON_FF_CURRENT, ADVICE_PUSH_NON_FF_MATCHING, ADVICE_PUSH_REF_NEEDS_UPDATE, + ADVICE_PUSH_REPO_LOOKS_LIKE_REF, ADVICE_PUSH_UNQUALIFIED_REF_NAME, ADVICE_PUSH_UPDATE_REJECTED, ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
diff --git a/apply.c b/apply.c index 5e54453..5e87ba7 100644 --- a/apply.c +++ b/apply.c
@@ -4008,7 +4008,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na struct cache_entry *ce; ce = index_file_exists(state->repo->index, name->buf, - name->len, ignore_case); + name->len, repo_ignore_case(the_repository)); if (ce && S_ISLNK(ce->ce_mode)) return 1; } else {
diff --git a/attr.c b/attr.c index c61472a..0e63f1b 100644 --- a/attr.c +++ b/attr.c
@@ -681,7 +681,7 @@ static enum git_attr_direction direction; void git_attr_set_direction(enum git_attr_direction new_direction) { - if (is_bare_repository() && new_direction != GIT_ATTR_INDEX) + if (is_bare_repository(the_repository) && new_direction != GIT_ATTR_INDEX) BUG("non-INDEX attr direction in a bare repo"); if (new_direction != direction) @@ -848,7 +848,7 @@ static struct attr_stack *read_attr(struct index_state *istate, res = read_attr_from_index(istate, path, flags); } else if (tree_oid) { res = read_attr_from_blob(istate, tree_oid, path, flags); - } else if (!is_bare_repository()) { + } else if (!is_bare_repository(the_repository)) { if (direction == GIT_ATTR_CHECKOUT) { res = read_attr_from_index(istate, path, flags); if (!res)
diff --git a/bloom.c b/bloom.c index a805ac0..c98d167 100644 --- a/bloom.c +++ b/bloom.c
@@ -16,6 +16,7 @@ define_commit_slab(bloom_filter_slab, struct bloom_filter); static struct bloom_filter_slab bloom_filters; +static int bloom_filter_slab_initialized; struct pathmap_hash_entry { struct hashmap_entry entry; @@ -263,7 +264,10 @@ void add_key_to_filter(const struct bloom_key *key, void init_bloom_filters(void) { + if (bloom_filter_slab_initialized) + return; init_bloom_filter_slab(&bloom_filters); + bloom_filter_slab_initialized = 1; } static void free_one_bloom_filter(struct bloom_filter *filter) @@ -276,6 +280,7 @@ static void free_one_bloom_filter(struct bloom_filter *filter) void deinit_bloom_filters(void) { deep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter); + bloom_filter_slab_initialized = 0; } struct bloom_keyvec *bloom_keyvec_new(const char *path, size_t len,
diff --git a/builtin/bisect.c b/builtin/bisect.c index e7c2d2f..3264e2d 100644 --- a/builtin/bisect.c +++ b/builtin/bisect.c
@@ -663,6 +663,11 @@ static int bisect_successful(struct bisect_terms *terms) refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid); commit = lookup_commit_reference_by_name(bad_ref); + if (!commit) { + error(_("could not find commit for '%s'"), bad_ref); + free(bad_ref); + return BISECT_FAILED; + } repo_format_commit_message(the_repository, commit, "%s", &commit_name, &pp); @@ -724,7 +729,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, struct object_id oid; const char *head; - if (is_bare_repository()) + if (is_bare_repository(the_repository)) no_checkout = 1; /* @@ -806,9 +811,11 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, */ head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, &head_oid, &flags); - if (!head) + if (!head) { if (repo_get_oid(the_repository, "HEAD", &head_oid)) return error(_("bad HEAD - I need a HEAD")); + head = "HEAD"; + } /* * Check if we are bisecting
diff --git a/builtin/blame.c b/builtin/blame.c index ffbd3ce..48d5251 100644 --- a/builtin/blame.c +++ b/builtin/blame.c
@@ -453,6 +453,36 @@ static void determine_line_heat(struct commit_info *ci, const char **dest_color) *dest_color = colorfield[i].col; } +static inline int maybe_putc(int c, FILE *out) +{ + return out ? putc(c, out) : 0; +} + +static size_t print_marks(FILE *out, const struct blame_entry *ent, int opt) +{ + size_t len = 0; + + if ((ent->suspect->commit->object.flags & UNINTERESTING) && + !blank_boundary && !(opt & OUTPUT_ANNOTATE_COMPAT)) { + maybe_putc('^', out); + len++; + } + if (mark_unblamable_lines && ent->unblamable) { + maybe_putc('*', out); + len++; + } + if (mark_ignored_lines && ent->ignored) { + maybe_putc('?', out); + len++; + } + return len; +} + +static size_t count_marks(const struct blame_entry *ent, int opt) +{ + return print_marks(NULL, ent, opt); +} + static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt, struct blame_entry *prev_ent) { @@ -499,23 +529,10 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, if (color) fputs(color, stdout); - if (suspect->commit->object.flags & UNINTERESTING) { - if (blank_boundary) { - memset(hex, ' ', strlen(hex)); - } else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) { - length--; - putchar('^'); - } - } - - if (mark_unblamable_lines && ent->unblamable) { - length--; - putchar('*'); - } - if (mark_ignored_lines && ent->ignored) { - length--; - putchar('?'); - } + if ((suspect->commit->object.flags & UNINTERESTING) && + blank_boundary) + memset(hex, ' ', strlen(hex)); + length -= print_marks(stdout, ent, opt); printf("%.*s", (int)(length < GIT_MAX_HEXSZ ? length : GIT_MAX_HEXSZ), hex); if (opt & OUTPUT_ANNOTATE_COMPAT) { @@ -647,11 +664,15 @@ static void find_alignment(struct blame_scoreboard *sb, int *option) struct blame_entry *e; int compute_auto_abbrev = (abbrev < 0); int auto_abbrev = DEFAULT_ABBREV; + size_t max_marks_count = 0; for (e = sb->ent; e; e = e->next) { struct blame_origin *suspect = e->suspect; int num; + size_t marks_count = count_marks(e, *option); + if (max_marks_count < marks_count) + max_marks_count = marks_count; if (compute_auto_abbrev) auto_abbrev = update_auto_abbrev(auto_abbrev, suspect); if (strcmp(suspect->path, sb->path)) @@ -685,8 +706,12 @@ static void find_alignment(struct blame_scoreboard *sb, int *option) max_score_digits = decimal_width(largest_score); if (compute_auto_abbrev) - /* one more abbrev length is needed for the boundary commit */ - abbrev = auto_abbrev + 1; + abbrev = auto_abbrev; + if (abbrev < (int)the_hash_algo->hexsz) { + abbrev += max_marks_count; + if (abbrev > (int)the_hash_algo->hexsz) + abbrev = the_hash_algo->hexsz; + } } static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa) @@ -1013,6 +1038,8 @@ int cmd_blame(int argc, case PARSE_OPT_UNKNOWN: break; case PARSE_OPT_HELP: + exit(0); + case PARSE_OPT_HELP_ERROR: case PARSE_OPT_ERROR: case PARSE_OPT_SUBCOMMAND: exit(129); @@ -1047,10 +1074,7 @@ int cmd_blame(int argc, } else if (show_progress < 0) show_progress = isatty(2); - if (0 < abbrev && abbrev < (int)the_hash_algo->hexsz) - /* one more abbrev length is needed for the boundary commit */ - abbrev++; - else if (!abbrev) + if (!abbrev) abbrev = the_hash_algo->hexsz; if (revs_file && read_ancestry(revs_file)) @@ -1163,7 +1187,7 @@ int cmd_blame(int argc, revs.disable_stdin = 1; setup_revisions(argc, argv, &revs, NULL); - if (!revs.pending.nr && is_bare_repository()) { + if (!revs.pending.nr && is_bare_repository(the_repository)) { struct commit *head_commit; struct object_id head_oid;
diff --git a/builtin/branch.c b/builtin/branch.c index 1572a4f..dede60d 100644 --- a/builtin/branch.c +++ b/builtin/branch.c
@@ -706,6 +706,29 @@ static int edit_branch_description(const char *branch_name) return 0; } +static void die_if_upstream_looks_like_remote(const char *new_upstream, const char *branch_name) +{ + struct strbuf remote_ref = STRBUF_INIT; + int code; + + if (strchr(new_upstream, '/') || + !remote_is_configured(remote_get(new_upstream), 0)) + return; + + strbuf_addf(&remote_ref, "refs/remotes/%s/%s", new_upstream, branch_name); + if (!refs_ref_exists(get_main_ref_store(the_repository), remote_ref.buf)) { + strbuf_release(&remote_ref); + return; + } + + code = die_message(_("--set-upstream-to takes a single <remote>/<branch> argument")); + advise_if_enabled(ADVICE_SET_UPSTREAM_FAILURE, + _("Did you mean to use: git branch --set-upstream-to=%s/%s?"), + new_upstream, branch_name); + strbuf_release(&remote_ref); + exit(code); +} + int cmd_branch(int argc, const char **argv, const char *prefix, @@ -957,6 +980,15 @@ int cmd_branch(int argc, if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) { if (!argc || branch_checked_out(branch->refname)) die(_("no commit on branch '%s' yet"), branch->name); + /* + * Check the advice up front to avoid the ref + * lookups when the hint is off. The helper still + * calls advise_if_enabled() so the hint carries the + * standard "disable this message" instructions. + */ + if (argc == 1 && + advice_enabled(ADVICE_SET_UPSTREAM_FAILURE)) + die_if_upstream_looks_like_remote(new_upstream, argv[0]); die(_("branch '%s' does not exist"), branch->name); }
diff --git a/builtin/cat-file.c b/builtin/cat-file.c index d6ef841..b4b99a7 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c
@@ -458,9 +458,12 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d static void print_default_format(struct strbuf *scratch, struct expand_data *data, struct batch_options *opt) { - strbuf_addf(scratch, "%s %s %"PRIuMAX"%c", oid_to_hex(&data->oid), - type_name(data->type), - (uintmax_t)data->size, opt->output_delim); + strbuf_add_oid_hex(scratch, &data->oid); + strbuf_addch(scratch, ' '); + strbuf_addstr(scratch, type_name(data->type)); + strbuf_addch(scratch, ' '); + strbuf_add_uint(scratch, data->size); + strbuf_addch(scratch, opt->output_delim); } static void report_object_status(struct batch_options *opt, @@ -497,7 +500,7 @@ static void batch_object_write(const char *obj_name, data->info.sizep = &data->size; if (pack) - ret = packed_object_info(pack, offset, &data->info); + ret = packed_object_info(NULL, pack, offset, &data->info); else ret = odb_read_object_info_extended(the_repository->objects, &data->oid, &data->info, @@ -834,8 +837,9 @@ static int batch_one_object_oi(const struct object_id *oid, void *_payload) { struct for_each_object_payload *payload = _payload; - if (oi && oi->whence == OI_PACKED) - return payload->callback(oid, oi->u.packed.pack, oi->u.packed.offset, + if (oi && oi->source_infop->source->type == ODB_SOURCE_PACKED) + return payload->callback(oid, oi->source_infop->u.packed.pack, + oi->source_infop->u.packed.offset, payload->payload); return payload->callback(oid, NULL, 0, payload->payload); } @@ -906,12 +910,15 @@ static void batch_each_object(struct batch_options *opt, &payload, flags); } } else { - struct object_info oi = { 0 }; + struct odb_source_info source_info; + struct object_info oi = { + .source_infop = &source_info, + }; for (source = the_repository->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); - int ret = packfile_store_for_each_object(files->packed, &oi, - batch_one_object_oi, &payload, &opts); + int ret = odb_source_for_each_object(&files->packed->base, &oi, + batch_one_object_oi, &payload, &opts); if (ret) break; }
diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 98f64d5..217d83e 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c
@@ -116,7 +116,7 @@ int cmd_check_attr(int argc, struct object_id initialized_oid; int cnt, i, doubledash, filei; - if (!is_bare_repository()) + if (!is_bare_repository(the_repository)) setup_work_tree(the_repository); repo_config(the_repository, git_default_config, NULL);
diff --git a/builtin/checkout.c b/builtin/checkout.c index b78b3a1..aee84ca 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c
@@ -952,10 +952,13 @@ static void update_refs_for_switch(const struct checkout_opts *opts, const char *old_desc, *reflog_msg; if (opts->new_branch) { if (opts->new_orphan_branch) { - enum log_refs_config log_all_ref_updates = - repo_settings_get_log_all_ref_updates(the_repository); + enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET; + const char *value; char *refname; + if (!repo_config_get_string_tmp(the_repository, "core.logallrefupdates", &value)) + log_all_ref_updates = refs_parse_log_all_ref_updates_config(value); + refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch); if (opts->new_branch_log && !should_autocreate_reflog(log_all_ref_updates, refname)) {
diff --git a/builtin/clone.c b/builtin/clone.c index d60d1b6..9d08cd8 100644 --- a/builtin/clone.c +++ b/builtin/clone.c
@@ -1116,7 +1116,6 @@ int cmd_clone(int argc, die_errno(_("could not create work tree dir '%s'"), work_tree); junk_work_tree = work_tree; - set_git_work_tree(the_repository, work_tree); } if (real_git_dir) { @@ -1186,9 +1185,10 @@ int cmd_clone(int argc, * repository, and reference backends may persist that information into * their on-disk data structures. */ - init_db(the_repository, git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, - ref_storage_format, NULL, - do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB); + init_db(the_repository, git_dir, real_git_dir, work_tree, option_template, + GIT_HASH_UNKNOWN, ref_storage_format, NULL, + do_not_override_repo_unix_permissions, + INIT_DB_QUIET | INIT_DB_SKIP_REFDB); if (real_git_dir) { free((char *)git_dir);
diff --git a/builtin/describe.c b/builtin/describe.c index a94dad9..c0abc93 100644 --- a/builtin/describe.c +++ b/builtin/describe.c
@@ -251,56 +251,19 @@ static int compare_pt(const void *a_, const void *b_) return 0; } -struct lazy_queue { - struct prio_queue queue; - bool get_pending; -}; - -#define LAZY_QUEUE_INIT { { compare_commits_by_commit_date }, false } - -static void *lazy_queue_get(struct lazy_queue *queue) -{ - if (queue->get_pending) - prio_queue_get(&queue->queue); - else - queue->get_pending = true; - return prio_queue_peek(&queue->queue); -} - -static void lazy_queue_put(struct lazy_queue *queue, void *thing) -{ - if (queue->get_pending) - prio_queue_replace(&queue->queue, thing); - else - prio_queue_put(&queue->queue, thing); - queue->get_pending = false; -} - -static bool lazy_queue_empty(const struct lazy_queue *queue) -{ - return queue->queue.nr == (queue->get_pending ? 1 : 0); -} - -static void lazy_queue_clear(struct lazy_queue *queue) -{ - clear_prio_queue(&queue->queue); - queue->get_pending = false; -} - -static unsigned long finish_depth_computation(struct lazy_queue *queue, +static unsigned long finish_depth_computation(struct prio_queue *queue, struct possible_tag *best) { unsigned long seen_commits = 0; struct oidset unflagged = OIDSET_INIT; + struct commit *c; - for (size_t i = queue->get_pending ? 1 : 0; i < queue->queue.nr; i++) { - struct commit *commit = queue->queue.array[i].data; - if (!(commit->object.flags & best->flag_within)) - oidset_insert(&unflagged, &commit->object.oid); + prio_queue_for_each(queue, c) { + if (!(c->object.flags & best->flag_within)) + oidset_insert(&unflagged, &c->object.oid); } - while (!lazy_queue_empty(queue)) { - struct commit *c = lazy_queue_get(queue); + while ((c = prio_queue_get(queue))) { struct commit_list *parents = c->parents; seen_commits++; if (c->object.flags & best->flag_within) { @@ -316,7 +279,7 @@ static unsigned long finish_depth_computation(struct lazy_queue *queue, repo_parse_commit(the_repository, p); seen = p->object.flags & SEEN; if (!seen) - lazy_queue_put(queue, p); + prio_queue_put(queue, p); flag_before = p->object.flags & best->flag_within; p->object.flags |= c->object.flags; flag_after = p->object.flags & best->flag_within; @@ -364,8 +327,8 @@ static void append_suffix(int depth, const struct object_id *oid, struct strbuf static void describe_commit(struct commit *cmit, struct strbuf *dst) { - struct commit *gave_up_on = NULL; - struct lazy_queue queue = LAZY_QUEUE_INIT; + struct commit *c, *gave_up_on = NULL; + struct prio_queue queue = { compare_commits_by_commit_date }; struct commit_name *n; struct possible_tag all_matches[MAX_TAGS]; unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; @@ -407,9 +370,8 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst) } cmit->object.flags = SEEN; - lazy_queue_put(&queue, cmit); - while (!lazy_queue_empty(&queue)) { - struct commit *c = lazy_queue_get(&queue); + prio_queue_put(&queue, cmit); + while ((c = prio_queue_get(&queue))) { struct commit_list *parents = c->parents; struct commit_name **slot; @@ -443,7 +405,7 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst) t->depth++; } /* Stop if last remaining path already covered by best candidate(s) */ - if (annotated_cnt && lazy_queue_empty(&queue)) { + if (annotated_cnt && !prio_queue_size(&queue)) { int best_depth = INT_MAX; unsigned best_within = 0; for (cur_match = 0; cur_match < match_cnt; cur_match++) { @@ -466,7 +428,7 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst) struct commit *p = parents->item; repo_parse_commit(the_repository, p); if (!(p->object.flags & SEEN)) - lazy_queue_put(&queue, p); + prio_queue_put(&queue, p); p->object.flags |= c->object.flags; parents = parents->next; @@ -481,7 +443,7 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst) strbuf_add_unique_abbrev(dst, cmit_oid, abbrev); if (suffix) strbuf_addstr(dst, suffix); - lazy_queue_clear(&queue); + clear_prio_queue(&queue); return; } if (unannotated_cnt) @@ -497,11 +459,11 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst) QSORT(all_matches, match_cnt, compare_pt); if (gave_up_on) { - lazy_queue_put(&queue, gave_up_on); + prio_queue_put(&queue, gave_up_on); seen_commits--; } seen_commits += finish_depth_computation(&queue, &all_matches[0]); - lazy_queue_clear(&queue); + clear_prio_queue(&queue); if (debug) { static int label_width = -1;
diff --git a/builtin/diff.c b/builtin/diff.c index 4b46e39..18b1083 100644 --- a/builtin/diff.c +++ b/builtin/diff.c
@@ -579,9 +579,13 @@ int cmd_diff(int argc, obj = deref_tag(the_repository, obj, NULL, 0); if (!obj) die(_("invalid object '%s' given."), name); - if (obj->type == OBJ_COMMIT) - obj = &repo_get_commit_tree(the_repository, - ((struct commit *)obj))->object; + if (obj->type == OBJ_COMMIT) { + struct tree *tree = repo_get_commit_tree( + the_repository, (struct commit *)obj); + if (!tree) + die(_("unable to read tree object for commit '%s'"), name); + obj = &tree->object; + } if (obj->type == OBJ_TREE) { if (sdiff.skip && bitmap_get(sdiff.skip, i))
diff --git a/builtin/fast-import.c b/builtin/fast-import.c index aa656c5..6692f7c 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c
@@ -969,7 +969,7 @@ static int store_object( hdrlen = format_object_header((char *)hdr, sizeof(hdr), type, dat->len); - the_hash_algo->init_fn(&c); + git_hash_init(&c, the_hash_algo); git_hash_update(&c, hdr, hdrlen); git_hash_update(&c, dat->buf, dat->len); git_hash_final_oid(&oid, &c); @@ -1131,7 +1131,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) hdrlen = format_object_header((char *)out_buf, out_sz, OBJ_BLOB, len); - the_hash_algo->init_fn(&c); + git_hash_init(&c, the_hash_algo); git_hash_update(&c, out_buf, hdrlen); crc32_begin(pack_file); @@ -1216,6 +1216,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) out: free(in_buf); free(out_buf); + hashfile_checkpoint_release(&checkpoint); } /* All calls must be guarded by find_object() or find_mark() to
diff --git a/builtin/fetch.c b/builtin/fetch.c index c1d7c67..775a797 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c
@@ -103,6 +103,7 @@ static struct string_list negotiation_include = STRING_LIST_INIT_NODUP; struct fetch_config { enum display_format display_format; + enum follow_remote_head_settings follow_remote_head; int all; int prune; int prune_tags; @@ -171,6 +172,23 @@ static int git_fetch_config(const char *k, const char *v, else die(_("invalid value for '%s': '%s'"), "fetch.output", v); + return 0; + } + + if (!strcmp(k, "fetch.followremotehead")) { + if (!v) + return config_error_nonbool(k); + else if (!strcmp(v, "never")) + fetch_config->follow_remote_head = FOLLOW_REMOTE_NEVER; + else if (!strcmp(v, "create")) + fetch_config->follow_remote_head = FOLLOW_REMOTE_CREATE; + else if (!strcmp(v, "warn")) + fetch_config->follow_remote_head = FOLLOW_REMOTE_WARN; + else if (!strcmp(v, "always")) + fetch_config->follow_remote_head = FOLLOW_REMOTE_ALWAYS; + else + warning(_("unrecognized fetch.followRemoteHEAD value '%s' ignored"), v); + return 0; } return git_default_config(k, v, ctx, cb); @@ -1697,17 +1715,19 @@ static const char *strip_refshead(const char *name){ static void set_head_advice_msg(const char *remote, const char *head_name) { const char message_advice_set_head[] = - N_("Run 'git remote set-head %s %s' to follow the change, or set\n" - "'remote.%s.followRemoteHEAD' configuration option to a different value\n" - "if you do not want to see this message. Specifically running\n" - "'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n" - "will disable the warning until the remote changes HEAD to something else."); + N_("Run 'git remote set-head %s %s' to follow the change, or modify\n" + "either of the 'remote.%s.followRemoteHEAD' or 'fetch.followRemoteHEAD'\n" + "configuration variables to handle the situation differently.\n\n" + + "Using this specific setting\n\n" + " git config set remote.%s.followRemoteHEAD warn-if-not-%s\n\n" + "will suppress the warning until the remote changes HEAD to something else."); advise_if_enabled(ADVICE_FETCH_SET_HEAD_WARN, _(message_advice_set_head), remote, head_name, remote, remote, head_name); } -static void report_set_head(const char *remote, const char *head_name, +static void warn_set_head(const char *remote, const char *head_name, struct strbuf *buf_prev, int updateres) { struct strbuf buf_prefix = STRBUF_INIT; const char *prev_head = NULL; @@ -1729,12 +1749,12 @@ static void report_set_head(const char *remote, const char *head_name, strbuf_release(&buf_prefix); } -static int set_head(const struct ref *remote_refs, struct remote *remote) +static int set_head(const struct ref *remote_refs, struct remote *remote, + int follow_remote_head) { int result = 0, create_only, baremirror, was_detached; struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT, b_local_head = STRBUF_INIT; - int follow_remote_head = remote->follow_remote_head; const char *no_warn_branch = remote->no_warn_branch; char *head_name = NULL; struct ref *ref, *matches; @@ -1764,7 +1784,7 @@ static int set_head(const struct ref *remote_refs, struct remote *remote) if (!head_name) goto cleanup; - baremirror = is_bare_repository() && remote->mirror; + baremirror = is_bare_repository(the_repository) && remote->mirror; create_only = follow_remote_head == FOLLOW_REMOTE_ALWAYS ? 0 : !baremirror; if (baremirror) { strbuf_addstr(&b_head, "HEAD"); @@ -1773,7 +1793,7 @@ static int set_head(const struct ref *remote_refs, struct remote *remote) strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote->name); strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote->name, head_name); } - /* make sure it's valid */ + /* make sure it's valid */ if (!baremirror && !refs_ref_exists(refs, b_remote_head.buf)) { result = 1; goto cleanup; @@ -1787,7 +1807,7 @@ static int set_head(const struct ref *remote_refs, struct remote *remote) if (verbosity >= 0 && follow_remote_head == FOLLOW_REMOTE_WARN && (!no_warn_branch || strcmp(no_warn_branch, head_name))) - report_set_head(remote->name, head_name, &b_local_head, was_detached); + warn_set_head(remote->name, head_name, &b_local_head, was_detached); cleanup: free(head_name); @@ -1819,7 +1839,7 @@ static void ref_transaction_rejection_handler(const char *refname, { struct ref_rejection_data *data = cb_data; - if (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && ignore_case && + if (err == REF_TRANSACTION_ERROR_CASE_CONFLICT && repo_ignore_case(the_repository) && !data->case_sensitive_msg_shown) { error(_("You're on a case-insensitive filesystem, and the remote you are\n" "trying to fetch from has references that only differ in casing. It\n" @@ -1901,6 +1921,7 @@ static int do_fetch(struct transport *transport, struct ref_update_display_info_array display_array = { 0 }; struct strmap rejected_refs = STRMAP_INIT; int summary_width = 0; + int follow_remote_head; if (tags == TAGS_DEFAULT) { if (transport->remote->fetch_tags == 2) @@ -1916,6 +1937,22 @@ static int do_fetch(struct transport *transport, goto cleanup; } + /* + * NEEDSWORK: By the time this function executes, we have already parsed + * all such followRemoteHEAD values from the external configuration, + * potentially emitting warning messages for bogus values. Ideally, if + * this fetch ends up not needing to consult these values, then git would + * not ever output a value warning. (eg: when pulling from a URL directly - + * rather than a configured remote, or when a remote's followRemoteHEAD + * overrides the fallback fetch setting) + */ + if (transport->remote->follow_remote_head) + follow_remote_head = transport->remote->follow_remote_head; + else if (config->follow_remote_head) + follow_remote_head = config->follow_remote_head; + else + follow_remote_head = BUILTIN_FOLLOW_REMOTE_HEAD_DFLT; + if (rs->nr) { refspec_ref_prefixes(rs, &transport_ls_refs_options.ref_prefixes); } else { @@ -1924,7 +1961,7 @@ static int do_fetch(struct transport *transport, if (transport->remote->fetch.nr) { refspec_ref_prefixes(&transport->remote->fetch, &transport_ls_refs_options.ref_prefixes); - if (transport->remote->follow_remote_head != FOLLOW_REMOTE_NEVER) + if (follow_remote_head != FOLLOW_REMOTE_NEVER) do_set_head = 1; } if (branch && branch_has_merge_config(branch) && @@ -2131,7 +2168,7 @@ static int do_fetch(struct transport *transport, * Way too many cases where this can go wrong so let's just * ignore errors and fail silently for now. */ - set_head(remote_refs, transport->remote); + set_head(remote_refs, transport->remote, follow_remote_head); } cleanup: @@ -2471,6 +2508,7 @@ int cmd_fetch(int argc, { struct fetch_config config = { .display_format = DISPLAY_FORMAT_FULL, + .follow_remote_head = FOLLOW_REMOTE_UNCONFIGURED, .prune = -1, .prune_tags = -1, .show_forced_updates = 1,
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index f920cf3..4161dd8 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c
@@ -1418,6 +1418,8 @@ static int fsmonitor_run_daemon(void) err = fsmonitor_run_daemon_1(&state); done: + fsmonitor_free_token_data(state.current_token_data); + state.current_token_data = NULL; pthread_cond_destroy(&state.cookies_cond); pthread_mutex_destroy(&state.main_lock); {
diff --git a/builtin/gc.c b/builtin/gc.c index c26c93e..d32af42 100644 --- a/builtin/gc.c +++ b/builtin/gc.c
@@ -902,7 +902,7 @@ int cmd_gc(int argc, die(_("failed to parse gc.logExpiry value %s"), cfg.gc_log_expire); if (cfg.pack_refs < 0) - cfg.pack_refs = !is_bare_repository(); + cfg.pack_refs = !is_bare_repository(the_repository); argc = parse_options(argc, argv, prefix, builtin_gc_options, builtin_gc_usage, 0);
diff --git a/builtin/grep.c b/builtin/grep.c index 26b8547..d3d86ab 100644 --- a/builtin/grep.c +++ b/builtin/grep.c
@@ -25,12 +25,11 @@ #include "setup.h" #include "submodule.h" #include "submodule-config.h" -#include "object-file.h" #include "object-name.h" #include "odb.h" +#include "odb/source.h" #include "oid-array.h" #include "oidset.h" -#include "packfile.h" #include "pager.h" #include "path.h" #include "promisor-remote.h" @@ -1357,15 +1356,8 @@ int cmd_grep(int argc, if (recurse_submodules) repo_read_gitmodules(the_repository, 1); - if (startup_info->have_repository) { - struct odb_source *source; - - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) { - struct odb_source_files *files = odb_source_files_downcast(source); - packfile_store_prepare(files->packed); - } - } + if (startup_info->have_repository) + odb_prepare(the_repository->objects, 0); start_threads(&opt); } else {
diff --git a/builtin/history.c b/builtin/history.c index 091465a..d28c1f0 100644 --- a/builtin/history.c +++ b/builtin/history.c
@@ -17,13 +17,17 @@ #include "read-cache.h" #include "refs.h" #include "replay.h" +#include "reset.h" #include "revision.h" #include "sequencer.h" #include "strvec.h" #include "tree.h" +#include "tree-walk.h" #include "unpack-trees.h" #include "wt-status.h" +#define GIT_HISTORY_DROP_USAGE \ + N_("git history drop <commit> [--dry-run] [--update-refs=(branches|head)] [--empty=(drop|keep|abort)]") #define GIT_HISTORY_FIXUP_USAGE \ N_("git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)]") #define GIT_HISTORY_REWORD_USAGE \ @@ -52,11 +56,6 @@ static int fill_commit_message(struct repository *repo, " empty message aborts the commit.\n"); struct wt_status s; - strbuf_addstr(out, default_message); - strbuf_addch(out, '\n'); - strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str); - write_file_buf(path, out->buf, out->len); - wt_status_prepare(repo, &s); FREE_AND_NULL(s.branch); s.ahead_behind_flags = AHEAD_BEHIND_QUICK; @@ -68,14 +67,22 @@ static int fill_commit_message(struct repository *repo, s.whence = FROM_COMMIT; s.committable = 1; - s.fp = fopen(git_path_commit_editmsg(), "a"); + s.fp = fopen(path, "w"); if (!s.fp) - return error_errno(_("could not open '%s'"), git_path_commit_editmsg()); + return error_errno(_("could not open '%s'"), path); + + strbuf_addstr(out, default_message); + strbuf_addch(out, '\n'); + strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str); + if (fwrite(out->buf, 1, out->len, s.fp) != out->len) + die_errno(_("could not write to '%s'"), path); wt_status_collect_changes_trees(&s, old_tree, new_tree); wt_status_print(&s); wt_status_collect_free_buffers(&s); string_list_clear_func(&s.change, change_data_free); + if (fclose(s.fp)) + die_errno(_("could not write to '%s'"), path); strbuf_reset(out); if (launch_editor(path, out, NULL)) { @@ -333,21 +340,17 @@ static int handle_ref_update(struct ref_transaction *transaction, NULL, NULL, 0, reflog_msg, err); } -static int handle_reference_updates(struct rev_info *revs, - enum ref_action action, - struct commit *original, - struct commit *rewritten, - const char *reflog_msg, - int dry_run, - enum replay_empty_commit_action empty) +static int compute_pending_ref_updates(struct rev_info *revs, + enum ref_action action, + struct commit *original, + struct commit *rewritten, + enum replay_empty_commit_action empty, + struct replay_result *result) { const struct name_decoration *decoration; struct replay_revisions_options opts = { .empty = empty, }; - struct replay_result result = { 0 }; - struct ref_transaction *transaction = NULL; - struct strbuf err = STRBUF_INIT; char hex[GIT_MAX_HEXSZ + 1]; bool detached_head; int head_flags = 0; @@ -359,34 +362,13 @@ static int handle_reference_updates(struct rev_info *revs, opts.onto = oid_to_hex_r(hex, &rewritten->object.oid); - ret = replay_revisions(revs, &opts, &result); + ret = replay_revisions(revs, &opts, result); if (ret) - goto out; + return ret; if (action != REF_ACTION_BRANCHES && action != REF_ACTION_HEAD) BUG("unsupported ref action %d", action); - if (!dry_run) { - transaction = ref_store_transaction_begin(get_main_ref_store(revs->repo), 0, &err); - if (!transaction) { - ret = error(_("failed to begin ref transaction: %s"), err.buf); - goto out; - } - } - - for (size_t i = 0; i < result.updates_nr; i++) { - ret = handle_ref_update(transaction, - result.updates[i].refname, - &result.updates[i].new_oid, - &result.updates[i].old_oid, - reflog_msg, &err); - if (ret) { - ret = error(_("failed to update ref '%s': %s"), - result.updates[i].refname, err.buf); - goto out; - } - } - /* * `replay_revisions()` only updates references that are * ancestors of `rewritten`, so we need to manually @@ -414,14 +396,41 @@ static int handle_reference_updates(struct rev_info *revs, !detached_head) continue; + replay_result_queue_update(result, decoration->name, + &original->object.oid, + &rewritten->object.oid); + } + + return 0; +} + +static int apply_pending_ref_updates(struct repository *repo, + const struct replay_result *result, + const char *reflog_msg, + int dry_run) +{ + struct ref_transaction *transaction = NULL; + struct strbuf err = STRBUF_INIT; + int ret; + + if (!dry_run) { + transaction = ref_store_transaction_begin(get_main_ref_store(repo), + 0, &err); + if (!transaction) { + ret = error(_("failed to begin ref transaction: %s"), err.buf); + goto out; + } + } + + for (size_t i = 0; i < result->updates_nr; i++) { ret = handle_ref_update(transaction, - decoration->name, - &rewritten->object.oid, - &original->object.oid, + result->updates[i].refname, + &result->updates[i].new_oid, + &result->updates[i].old_oid, reflog_msg, &err); if (ret) { ret = error(_("failed to update ref '%s': %s"), - decoration->name, err.buf); + result->updates[i].refname, err.buf); goto out; } } @@ -435,11 +444,33 @@ static int handle_reference_updates(struct rev_info *revs, out: ref_transaction_free(transaction); - replay_result_release(&result); strbuf_release(&err); return ret; } +static int handle_reference_updates(struct rev_info *revs, + enum ref_action action, + struct commit *original, + struct commit *rewritten, + const char *reflog_msg, + int dry_run, + enum replay_empty_commit_action empty) +{ + struct replay_result result = { 0 }; + int ret; + + ret = compute_pending_ref_updates(revs, action, original, rewritten, + empty, &result); + if (ret) + goto out; + + ret = apply_pending_ref_updates(revs->repo, &result, reflog_msg, dry_run); + +out: + replay_result_release(&result); + return ret; +} + static int commit_became_empty(struct repository *repo, struct commit *original, struct tree *result) @@ -525,7 +556,7 @@ static int cmd_history_fixup(int argc, if (action == REF_ACTION_DEFAULT) action = REF_ACTION_BRANCHES; - if (is_bare_repository()) { + if (is_bare_repository(repo)) { ret = error(_("cannot run fixup in a bare repository")); goto out; } @@ -975,12 +1006,191 @@ static int cmd_history_split(int argc, return ret; } +static int update_worktree(struct repository *repo, + const struct commit *old_head, + const struct commit *new_head, + bool dry_run) +{ + struct reset_working_tree_options opts = { + .oid_from = &old_head->object.oid, + .oid = &new_head->object.oid, + }; + if (dry_run) + opts.flags |= RESET_WORKING_TREE_DRY_RUN; + return reset_working_tree(repo, &opts); +} + +static int find_head_tree_change(struct repository *repo, + const struct replay_result *result, + struct commit **old_head, + struct commit **new_head, + bool *changed) +{ + const struct replay_ref_update *head_update = NULL; + struct commit *old_head_commit, *new_head_commit; + struct tree *old_head_tree, *new_head_tree; + const char *head_target; + int head_flags; + + *changed = false; + + head_target = refs_resolve_ref_unsafe(get_main_ref_store(repo), "HEAD", + RESOLVE_REF_NO_RECURSE | RESOLVE_REF_READING, + NULL, &head_flags); + if (!head_target) + return error(_("cannot look up HEAD")); + + for (size_t i = 0; i < result->updates_nr; i++) { + if (!strcmp(result->updates[i].refname, head_target)) { + head_update = &result->updates[i]; + break; + } + } + + if (!head_update) + return 0; + + old_head_commit = lookup_commit_reference(repo, &head_update->old_oid); + new_head_commit = lookup_commit_reference(repo, &head_update->new_oid); + if (!old_head_commit || !new_head_commit) + return error(_("cannot resolve HEAD commit")); + + old_head_tree = repo_get_commit_tree(repo, old_head_commit); + new_head_tree = repo_get_commit_tree(repo, new_head_commit); + if (!old_head_tree || !new_head_tree) + return error(_("cannot resolve tree for HEAD")); + + if (oideq(&old_head_tree->object.oid, &new_head_tree->object.oid)) + return 0; + + *old_head = old_head_commit; + *new_head = new_head_commit; + *changed = true; + + return 0; +} + +static int cmd_history_drop(int argc, + const char **argv, + const char *prefix, + struct repository *repo) +{ + const char * const usage[] = { + GIT_HISTORY_DROP_USAGE, + NULL, + }; + enum replay_empty_commit_action empty = REPLAY_EMPTY_COMMIT_DROP; + enum ref_action action = REF_ACTION_DEFAULT; + int dry_run = 0; + struct option options[] = { + OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)", + N_("control which refs should be updated"), + PARSE_OPT_NONEG, parse_ref_action), + OPT_BOOL('n', "dry-run", &dry_run, + N_("perform a dry-run without updating any refs")), + OPT_CALLBACK_F(0, "empty", &empty, "(drop|keep|abort)", + N_("how to handle descendants that become empty"), + PARSE_OPT_NONEG, parse_opt_empty), + OPT_END(), + }; + struct strbuf reflog_msg = STRBUF_INIT; + struct commit *original, *rewritten; + struct rev_info revs = { 0 }; + struct replay_result result = { 0 }; + struct commit *old_head, *new_head; + bool head_moves = false; + int ret; + + argc = parse_options(argc, argv, prefix, options, usage, 0); + if (argc != 1) { + ret = error(_("command expects a single revision")); + goto out; + } + repo_config(repo, git_default_config, NULL); + + if (action == REF_ACTION_DEFAULT) + action = REF_ACTION_BRANCHES; + + original = lookup_commit_reference_by_name(argv[0]); + if (!original) { + ret = error(_("commit cannot be found: %s"), argv[0]); + goto out; + } + + if (!original->parents) { + ret = error(_("cannot drop root commit %s: " + "it has no parent to replay onto"), + argv[0]); + goto out; + } else if (original->parents->next) { + ret = error(_("cannot drop merge commit: %s"), argv[0]); + goto out; + } + + ret = setup_revwalk(repo, action, original, &revs); + if (ret) + goto out; + + rewritten = original->parents->item; + + ret = compute_pending_ref_updates(&revs, action, original, rewritten, + empty, &result); + if (ret) { + ret = error(_("failed replaying descendants")); + goto out; + } + + /* + * If HEAD will move as a result of the rewrite then we'll have to + * merge in the changes into the worktree and index. This merge can of + * course conflict, which will cause the whole operation to abort. + * + * If we had already updated the refs at that point then we'd have an + * inconsistent repository state. So we first perform a dry-run merge + * here before updating refs. + */ + if (!is_bare_repository(repo)) { + ret = find_head_tree_change(repo, &result, &old_head, + &new_head, &head_moves); + if (ret < 0) + goto out; + + if (head_moves && update_worktree(repo, old_head, new_head, true) < 0) { + ret = error(_("dropping this commit would " + "overwrite local changes; aborting")); + goto out; + } + } + + strbuf_addf(&reflog_msg, "drop: dropping %s", argv[0]); + ret = apply_pending_ref_updates(repo, &result, reflog_msg.buf, dry_run); + if (ret < 0) { + ret = error(_("failed to update references")); + goto out; + } + + if (!dry_run && head_moves && update_worktree(repo, old_head, new_head, false) < 0) { + ret = error(_("could not update working tree to new commit %s"), + oid_to_hex(&new_head->object.oid)); + goto out; + } + + ret = 0; + +out: + replay_result_release(&result); + strbuf_release(&reflog_msg); + release_revisions(&revs); + return ret; +} + int cmd_history(int argc, const char **argv, const char *prefix, struct repository *repo) { const char * const usage[] = { + GIT_HISTORY_DROP_USAGE, GIT_HISTORY_FIXUP_USAGE, GIT_HISTORY_REWORD_USAGE, GIT_HISTORY_SPLIT_USAGE, @@ -988,6 +1198,7 @@ int cmd_history(int argc, }; parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("drop", &fn, cmd_history_drop), OPT_SUBCOMMAND("fixup", &fn, cmd_history_fixup), OPT_SUBCOMMAND("reword", &fn, cmd_history_reword), OPT_SUBCOMMAND("split", &fn, cmd_history_split),
diff --git a/builtin/index-pack.c b/builtin/index-pack.c index f396658..bc86925 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c
@@ -374,7 +374,7 @@ static const char *open_pack_file(const char *pack_name) output_fd = -1; nothread_data.pack_fd = input_fd; } - the_hash_algo->init_fn(&input_ctx); + git_hash_init(&input_ctx, the_hash_algo); return pack_name; } @@ -481,7 +481,7 @@ static void *unpack_entry_data(off_t offset, size_t size, if (!is_delta_type(type)) { hdrlen = format_object_header(hdr, sizeof(hdr), type, size); - the_hash_algo->init_fn(&c); + git_hash_init(&c, the_hash_algo); git_hash_update(&c, hdr, hdrlen); } else oid = NULL; @@ -1291,7 +1291,7 @@ static void parse_pack_objects(unsigned char *hash) /* Check pack integrity */ flush(); - the_hash_algo->init_fn(&tmp_ctx); + git_hash_init(&tmp_ctx, the_hash_algo); git_hash_clone(&tmp_ctx, &input_ctx); git_hash_final(hash, &tmp_ctx); if (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo)) @@ -1825,11 +1825,16 @@ static void repack_local_links(void) oidset_iter_init(&outgoing_links, &iter); while ((oid = oidset_iter_next(&iter))) { - struct object_info info = OBJECT_INFO_INIT; + struct odb_source_info source_info; + struct object_info info = { + .source_infop = &source_info, + }; + if (odb_read_object_info_extended(the_repository->objects, oid, &info, 0)) /* Missing; assume it is a promisor object */ continue; - if (info.whence == OI_PACKED && info.u.packed.pack->pack_promisor) + if (source_info.source->type == ODB_SOURCE_PACKED && + source_info.u.packed.pack->pack_promisor) continue; if (!cmd.args.nr) {
diff --git a/builtin/init-db.c b/builtin/init-db.c index c55517a..e96b128 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c
@@ -81,6 +81,7 @@ int cmd_init_db(int argc, const char *template_dir = NULL; char *template_dir_to_free = NULL; unsigned int flags = 0; + int bare = startup_info->force_bare_repository ? 1 : -1; const char *object_format = NULL; const char *ref_format = NULL; const char *initial_branch = NULL; @@ -90,7 +91,7 @@ int cmd_init_db(int argc, const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"), N_("directory from which templates will be used")), - OPT_SET_INT(0, "bare", &is_bare_repository_cfg, + OPT_SET_INT(0, "bare", &bare, N_("create a bare repository"), 1), { .type = OPTION_CALLBACK, @@ -116,7 +117,7 @@ int cmd_init_db(int argc, argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); - if (real_git_dir && is_bare_repository_cfg == 1) + if (real_git_dir && bare == 1) die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare"); if (real_git_dir && !is_absolute_path(real_git_dir)) @@ -160,7 +161,7 @@ int cmd_init_db(int argc, } else if (0 < argc) { usage(init_db_usage[0]); } - if (is_bare_repository_cfg == 1) { + if (bare == 1) { char *cwd = xgetcwd(); setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0); free(cwd); @@ -187,7 +188,7 @@ int cmd_init_db(int argc, */ git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT)); work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT)); - if ((!git_dir || is_bare_repository_cfg == 1) && work_tree) + if ((!git_dir || bare == 1) && work_tree) die(_("%s (or --work-tree=<directory>) not allowed without " "specifying %s (or --git-dir=<directory>)"), GIT_WORK_TREE_ENVIRONMENT, @@ -224,36 +225,31 @@ int cmd_init_db(int argc, strbuf_release(&sb); } - if (is_bare_repository_cfg < 0) - is_bare_repository_cfg = guess_repository_type(git_dir); + if (bare < 0) + bare = guess_repository_type(git_dir); - if (!is_bare_repository_cfg) { + if (!bare) { const char *git_dir_parent = strrchr(git_dir, '/'); - if (git_dir_parent) { - char *rel = xstrndup(git_dir, git_dir_parent - git_dir); - git_work_tree_cfg = real_pathdup(rel, 1); - free(rel); + + if (!work_tree) { + if (git_dir_parent) { + char *rel = xstrndup(git_dir, git_dir_parent - git_dir); + work_tree = real_pathdup(rel, 1); + free(rel); + } else { + work_tree = xgetcwd(); + } } - if (!git_work_tree_cfg) - git_work_tree_cfg = xgetcwd(); - if (work_tree) - set_git_work_tree(the_repository, work_tree); - else - set_git_work_tree(the_repository, git_work_tree_cfg); - if (access(repo_get_work_tree(the_repository), X_OK)) - die_errno (_("Cannot access work tree '%s'"), - repo_get_work_tree(the_repository)); - } - else { - if (real_git_dir) - die(_("--separate-git-dir incompatible with bare repository")); - if (work_tree) - set_git_work_tree(the_repository, work_tree); + + if (access(work_tree, X_OK)) + die_errno (_("Cannot access work tree '%s'"), work_tree); + } else if (real_git_dir) { + die(_("--separate-git-dir incompatible with bare repository")); } flags |= INIT_DB_EXIST_OK; - ret = init_db(the_repository, git_dir, real_git_dir, template_dir, hash_algo, - ref_storage_format, initial_branch, + ret = init_db(the_repository, git_dir, real_git_dir, work_tree, + template_dir, hash_algo, ref_storage_format, initial_branch, init_shared_repository, flags); free(template_dir_to_free);
diff --git a/builtin/last-modified.c b/builtin/last-modified.c index 8900cee..5478182 100644 --- a/builtin/last-modified.c +++ b/builtin/last-modified.c
@@ -344,6 +344,7 @@ static void process_parent(struct last_modified *lm, static int last_modified_run(struct last_modified *lm) { int max_count, queue_popped = 0; + struct commit *c, *n; struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; struct prio_queue not_queue = { compare_commits_by_gen_then_commit_date }; struct commit_list *list; @@ -389,10 +390,9 @@ static int last_modified_run(struct last_modified *lm) } } - while (queue.nr) { + while ((c = prio_queue_get(&queue))) { int parent_i; struct commit_list *p; - struct commit *c = prio_queue_get(&queue); struct bitmap *active_c = active_paths_for(lm, c); if ((0 <= max_count && max_count < ++queue_popped) || @@ -416,9 +416,8 @@ static int last_modified_run(struct last_modified *lm) */ repo_parse_commit(lm->rev.repo, c); - while (not_queue.nr) { + while ((n = prio_queue_get(¬_queue))) { struct commit_list *np; - struct commit *n = prio_queue_get(¬_queue); repo_parse_commit(lm->rev.repo, n);
diff --git a/builtin/log.c b/builtin/log.c index d027ce1..350b35c 100644 --- a/builtin/log.c +++ b/builtin/log.c
@@ -1888,6 +1888,7 @@ static void prepare_bases(struct base_tree_info *bases, bases->nr_patch_id++; } clear_commit_base(&commit_base); + release_revisions(&revs); } static void print_bases(struct base_tree_info *bases, FILE *file)
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c index 264df62..0993418 100644 --- a/builtin/mailsplit.c +++ b/builtin/mailsplit.c
@@ -225,14 +225,14 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); int file_done = 0; - if (isatty(fileno(f))) - warning(_("reading patches from stdin/tty...")); - if (!f) { error_errno("cannot open mbox %s", file); goto out; } + if (isatty(fileno(f))) + warning(_("reading patches from stdin/tty...")); + do { peek = fgetc(f); if (peek == EOF) {
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 00ffb36..6e73c85 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c
@@ -10,6 +10,7 @@ #include "trace2.h" #include "odb.h" #include "odb/source.h" +#include "odb/source-files.h" #include "replace-object.h" #include "repository.h" @@ -85,12 +86,12 @@ static int parse_object_dir(const struct option *opt, const char *arg, return 0; } -static struct odb_source *handle_object_dir_option(struct repository *repo) +static struct odb_source_files *handle_object_dir_option(struct repository *repo) { struct odb_source *source = odb_find_source(repo->objects, opts.object_dir); if (!source) source = odb_add_to_alternates_memory(repo->objects, opts.object_dir); - return source; + return odb_source_files_downcast(source); } static struct option common_opts[] = { @@ -167,7 +168,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, N_("refs snapshot for selecting bitmap commits")), OPT_END(), }; - struct odb_source *source; + struct odb_source_files *source; int ret; opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE; @@ -211,7 +212,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, read_packs_from_stdin(&packs); - ret = write_midx_file_only(source, &packs, + ret = write_midx_file_only(source->packed, &packs, opts.preferred_pack, opts.refs_snapshot, opts.incremental_base, opts.flags); @@ -223,7 +224,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, } - ret = write_midx_file(source, opts.preferred_pack, + ret = write_midx_file(source->packed, opts.preferred_pack, opts.refs_snapshot, opts.flags); free(opts.refs_snapshot); @@ -237,7 +238,7 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv, struct multi_pack_index *m, *cur; struct multi_pack_index *from_midx = NULL; struct multi_pack_index *to_midx = NULL; - struct odb_source *source; + struct odb_source_files *source; int ret; struct option *options; @@ -282,7 +283,7 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv, FREE_AND_NULL(options); - m = get_multi_pack_index(source); + m = get_multi_pack_index(source->packed); for (cur = m; cur && !(from_midx && to_midx); cur = cur->base_midx) { const char *midx_csum = midx_get_checksum_hex(cur); @@ -305,7 +306,7 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv, die(_("MIDX %s must be an ancestor of %s"), argv[0], argv[1]); } - ret = write_midx_file_compact(source, from_midx, to_midx, + ret = write_midx_file_compact(source->packed, from_midx, to_midx, opts.incremental_base, opts.flags); return ret; @@ -319,7 +320,7 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, static struct option builtin_multi_pack_index_verify_options[] = { OPT_END(), }; - struct odb_source *source; + struct odb_source_files *source; options = add_common_options(builtin_multi_pack_index_verify_options); @@ -337,7 +338,7 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, FREE_AND_NULL(options); - return verify_midx_file(source, opts.flags); + return verify_midx_file(source->packed, opts.flags); } static int cmd_multi_pack_index_expire(int argc, const char **argv, @@ -348,7 +349,7 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, static struct option builtin_multi_pack_index_expire_options[] = { OPT_END(), }; - struct odb_source *source; + struct odb_source_files *source; options = add_common_options(builtin_multi_pack_index_expire_options); @@ -366,7 +367,7 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, FREE_AND_NULL(options); - return expire_midx_packs(source, opts.flags); + return expire_midx_packs(source->packed, opts.flags); } static int cmd_multi_pack_index_repack(int argc, const char **argv, @@ -379,7 +380,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")), OPT_END(), }; - struct odb_source *source; + struct odb_source_files *source; options = add_common_options(builtin_multi_pack_index_repack_options); @@ -398,7 +399,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, FREE_AND_NULL(options); - return midx_repack(source, (size_t)opts.batch_size, opts.flags); + return midx_repack(source->packed, (size_t)opts.batch_size, opts.flags); } int cmd_multi_pack_index(int argc,
diff --git a/builtin/mv.c b/builtin/mv.c index e038233..a82fc97 100644 --- a/builtin/mv.c +++ b/builtin/mv.c
@@ -419,7 +419,7 @@ int cmd_mv(int argc, goto act_on_entry; } if (lstat(dst, &st) == 0 && - (!ignore_case || strcasecmp(src, dst))) { + (!repo_ignore_case(the_repository) || strcasecmp(src, dst))) { bad = _("destination exists"); if (force) { /*
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 27048bb..ea5eab4 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c
@@ -1781,7 +1781,8 @@ static int want_object_in_pack_mtime(const struct object_id *oid, odb_prepare_alternates(the_repository->objects); for (source = the_repository->objects->sources; source; source = source->next) { - struct multi_pack_index *m = get_multi_pack_index(source); + struct odb_source_files *files = odb_source_files_downcast(source); + struct multi_pack_index *m = get_multi_pack_index(files->packed); struct pack_entry e; if (m && fill_midx_entry(m, oid, &e)) { @@ -2462,7 +2463,7 @@ static void drop_reused_delta(struct object_entry *entry) oi.sizep = &size; oi.typep = &type; - if (packed_object_info(IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { + if (packed_object_info(NULL, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { /* * We failed to get the info from this pack for some reason; * fall back to odb_read_object_info, which may find another copy. @@ -2743,6 +2744,22 @@ static inline void oe_set_tree_depth(struct packing_data *pack, pack->tree_depth[e - pack->objects] = tree_depth; } +static void record_tree_depth(const struct object_id *oid, const char *name) +{ + const char *p; + unsigned depth; + struct object_entry *ent; + + /* the empty string is a root tree, which is depth 0 */ + depth = *name ? 1 : 0; + for (p = strchr(name, '/'); p; p = strchr(p + 1, '/')) + depth++; + + ent = packlist_find(&to_pack, oid); + if (ent && depth > oe_tree_depth(&to_pack, ent)) + oe_set_tree_depth(&to_pack, ent, depth); +} + /* * Return the size of the object without doing any delta * reconstruction (so non-deltas are true object sizes, but deltas @@ -3803,7 +3820,7 @@ static int add_object_entry_from_pack(const struct object_id *oid, ofs = nth_packed_object_offset(p, pos); oi.typep = &type; - if (packed_object_info(p, ofs, &oi) < 0) { + if (packed_object_info(NULL, p, ofs, &oi) < 0) { die(_("could not get type of object %s in pack %s"), oid_to_hex(oid), p->pack_name); } else if (type == OBJ_COMMIT) { @@ -4405,20 +4422,8 @@ static void show_object(struct object *obj, const char *name, add_preferred_base_object(name); add_object_entry(&obj->oid, obj->type, name, 0); - if (use_delta_islands) { - const char *p; - unsigned depth; - struct object_entry *ent; - - /* the empty string is a root tree, which is depth 0 */ - depth = *name ? 1 : 0; - for (p = strchr(name, '/'); p; p = strchr(p + 1, '/')) - depth++; - - ent = packlist_find(&to_pack, &obj->oid); - if (ent && depth > oe_tree_depth(&to_pack, ent)) - oe_set_tree_depth(&to_pack, ent, depth); - } + if (use_delta_islands) + record_tree_depth(&obj->oid, name); } static void show_object__ma_allow_any(struct object *obj, const char *name, void *data) @@ -4490,8 +4495,9 @@ static int add_object_in_unpacked_pack(const struct object_id *oid, void *data UNUSED) { if (cruft) { - add_cruft_object_entry(oid, OBJ_NONE, oi->u.packed.pack, - oi->u.packed.offset, NULL, *oi->mtimep); + add_cruft_object_entry(oid, OBJ_NONE, oi->source_infop->u.packed.pack, + oi->source_infop->u.packed.offset, NULL, + *oi->mtimep); } else { add_object_entry(oid, OBJ_NONE, "", 0); } @@ -4508,8 +4514,10 @@ static void add_objects_in_unpacked_packs(void) ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS | ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS, }; + struct odb_source_info source_info; struct object_info oi = { .mtimep = &mtime, + .source_infop = &source_info, }; odb_prepare_alternates(to_pack.repo->objects); @@ -4519,8 +4527,8 @@ static void add_objects_in_unpacked_packs(void) if (!source->local) continue; - if (packfile_store_for_each_object(files->packed, &oi, - add_object_in_unpacked_pack, NULL, &opts)) + if (odb_source_for_each_object(&files->packed->base, &oi, + add_object_in_unpacked_pack, NULL, &opts)) die(_("cannot open pack index")); } } @@ -4762,6 +4770,31 @@ static int add_objects_by_path(const char *path, continue; add_object_entry(oid, type, path, exclude); + + if (type == OBJ_COMMIT) { + struct commit *commit; + + if (!write_bitmap_index && !use_delta_islands) + continue; + + commit = lookup_commit(the_repository, oid); + if (!commit) + die(_("could not find commit %s"), oid_to_hex(oid)); + if (write_bitmap_index) + index_commit_for_bitmap(commit); + /* + * Skip island propagation for boundary commits. + * The regular traversal's show_commit() is only + * called for interesting commits; matching that + * here keeps path-walk from doing extra work that + * would only be a no-op anyway (boundary commits + * are not in island_marks). + */ + if (use_delta_islands && !exclude) + propagate_island_marks(the_repository, commit); + } else if (type == OBJ_TREE && use_delta_islands) { + record_tree_depth(oid, path); + } } oe_end = to_pack.nr_objects; @@ -4795,6 +4828,13 @@ static int get_object_list_path_walk(struct rev_info *revs) info.path_fn_data = &processed; /* + * Path-walk needs boundary commits to discover thin-pack bases, but + * bitmap traversal does not understand the boundary state. Set it + * here so any prior bitmap attempt sees the usual non-boundary walk. + */ + revs->boundary = 1; + + /* * Allow the --[no-]sparse option to be interesting here, if only * for testing purposes. Paths with no interesting objects will not * contribute to the resulting pack, but only create noisy preferred @@ -4999,10 +5039,14 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED, static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED) { - struct object_info info = OBJECT_INFO_INIT; + struct odb_source_info source_info; + struct object_info info = { + .source_infop = &source_info, + }; + if (odb_read_object_info_extended(the_repository->objects, &obj->oid, &info, 0)) BUG("should_include_obj should only be called on existing objects"); - return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor; + return source_info.source->type != ODB_SOURCE_PACKED || !source_info.u.packed.pack->pack_promisor; } static int is_not_in_promisor_pack(struct commit *commit, void *data) { @@ -5217,8 +5261,6 @@ int cmd_pack_objects(int argc, const char *option = NULL; if (!path_walk_filter_compatible(&filter_options)) option = "--filter"; - else if (use_delta_islands) - option = "--delta-islands"; if (option) { warning(_("cannot use %s with %s"), @@ -5227,9 +5269,7 @@ int cmd_pack_objects(int argc, } } if (path_walk) { - strvec_push(&rp, "--boundary"); strvec_push(&rp, "--objects"); - use_bitmap_index = 0; } else if (thin) { use_internal_rev_list = 1; strvec_push(&rp, shallow
diff --git a/builtin/patch-id.c b/builtin/patch-id.c index 2781598..22f36ec 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c
@@ -73,7 +73,7 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1]; struct git_hash_ctx ctx; - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); oidclr(result, the_repository->hash_algo); while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) { @@ -173,6 +173,7 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu oidclr(next_oid, the_repository->hash_algo); flush_one_hunk(result, &ctx); + git_hash_discard(&ctx); return patchlen; }
diff --git a/builtin/push.c b/builtin/push.c index 6021b71..1b2ad3b 100644 --- a/builtin/push.c +++ b/builtin/push.c
@@ -8,6 +8,7 @@ #include "advice.h" #include "branch.h" #include "config.h" +#include "dir.h" #include "environment.h" #include "gettext.h" #include "hex.h" @@ -662,6 +663,29 @@ static int push_multiple(struct string_list *list, return result; } +static void die_if_repo_looks_like_ref(const char *repo) +{ + const char *slash = strchr(repo, '/'); + struct strbuf name = STRBUF_INIT; + int code; + + if (!slash || !slash[1] || file_exists(repo)) + return; + + strbuf_add(&name, repo, slash - repo); + if (!remote_is_configured(remote_get(name.buf), 0)) { + strbuf_release(&name); + return; + } + + code = die_message(_("'%s' is not a valid push target"), repo); + advise_if_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF, + _("Did you mean to use: git push %s %s?"), + name.buf, slash + 1); + strbuf_release(&name); + exit(code); +} + int cmd_push(int argc, const char **argv, const char *prefix, @@ -744,6 +768,17 @@ int cmd_push(int argc, if (repo) { if (!add_remote_or_group(repo, &remote_group)) { + struct remote *r; + + /* + * Check the advice up front to avoid the remote + * lookup when the hint is off. The helper still + * calls advise_if_enabled() so the hint carries the + * standard "disable this message" instructions. + */ + if (advice_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF)) + die_if_repo_looks_like_ref(repo); + /* * Not a configured remote name or group name. * Try treating it as a direct URL or path, e.g. @@ -753,7 +788,7 @@ int cmd_push(int argc, * from the URL so the loop below can handle it * identically to a named remote. */ - struct remote *r = pushremote_get(repo); + r = pushremote_get(repo); if (!r) die(_("bad repository '%s'"), repo); string_list_append(&remote_group, r->name);
diff --git a/builtin/rebase.c b/builtin/rebase.c index fa4f5d9..10a3063 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c
@@ -592,7 +592,7 @@ static int finish_rebase(struct rebase_options *opts) static int move_to_original_branch(struct rebase_options *opts) { struct strbuf branch_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; - struct reset_head_opts ropts = { 0 }; + struct reset_working_tree_options ropts = { 0 }; int ret; if (!opts->head_name) @@ -607,10 +607,11 @@ static int move_to_original_branch(struct rebase_options *opts) strbuf_addf(&head_reflog, "%s (finish): returning to %s", opts->reflog_action, opts->head_name); ropts.branch = opts->head_name; - ropts.flags = RESET_HEAD_REFS_ONLY; + ropts.flags = RESET_WORKING_TREE_REFS_ONLY | + RESET_WORKING_TREE_UPDATE_HEAD; ropts.branch_msg = branch_reflog.buf; ropts.head_msg = head_reflog.buf; - ret = reset_head(the_repository, &ropts); + ret = reset_working_tree(the_repository, &ropts); strbuf_release(&branch_reflog); strbuf_release(&head_reflog); @@ -685,7 +686,7 @@ static int run_am(struct rebase_options *opts) status = run_command(&format_patch); if (status) { - struct reset_head_opts ropts = { 0 }; + struct reset_working_tree_options ropts = { 0 }; unlink(rebased_patches); free(rebased_patches); child_process_clear(&am); @@ -693,7 +694,8 @@ static int run_am(struct rebase_options *opts) ropts.oid = &opts->orig_head->object.oid; ropts.branch = opts->head_name; ropts.default_reflog_action = opts->reflog_action; - reset_head(the_repository, &ropts); + ropts.flags = RESET_WORKING_TREE_UPDATE_HEAD; + reset_working_tree(the_repository, &ropts); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n" @@ -855,18 +857,19 @@ static int rebase_config(const char *var, const char *value, static int checkout_up_to_date(struct rebase_options *options) { struct strbuf buf = STRBUF_INIT; - struct reset_head_opts ropts = { 0 }; + struct reset_working_tree_options ropts = { 0 }; int ret = 0; strbuf_addf(&buf, "%s: checkout %s", options->reflog_action, options->switch_to); ropts.oid = &options->orig_head->object.oid; ropts.branch = options->head_name; - ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + ropts.flags = RESET_WORKING_TREE_RUN_POST_CHECKOUT_HOOK | + RESET_WORKING_TREE_UPDATE_HEAD; if (!ropts.branch) - ropts.flags |= RESET_HEAD_DETACH; + ropts.flags |= RESET_WORKING_TREE_DETACH; ropts.head_msg = buf.buf; - if (reset_head(the_repository, &ropts) < 0) + if (reset_working_tree(the_repository, &ropts) < 0) ret = error(_("could not switch to %s"), options->switch_to); strbuf_release(&buf); @@ -1116,7 +1119,7 @@ int cmd_rebase(int argc, int reschedule_failed_exec = -1; int allow_preemptive_ff = 1; int preserve_merges_selected = 0; - struct reset_head_opts ropts = { 0 }; + struct reset_working_tree_options ropts = { 0 }; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, N_("revision"), @@ -1384,8 +1387,9 @@ int cmd_rebase(int argc, rerere_clear(the_repository, &merge_rr); string_list_clear(&merge_rr, 1); - ropts.flags = RESET_HEAD_HARD; - if (reset_head(the_repository, &ropts) < 0) + ropts.flags = RESET_WORKING_TREE_HARD | + RESET_WORKING_TREE_UPDATE_HEAD; + if (reset_working_tree(the_repository, &ropts) < 0) die(_("could not discard worktree changes")); remove_branch_state(the_repository, 0); if (read_basic_state(&options)) @@ -1409,8 +1413,9 @@ int cmd_rebase(int argc, ropts.oid = &options.orig_head->object.oid; ropts.head_msg = head_msg.buf; ropts.branch = options.head_name; - ropts.flags = RESET_HEAD_HARD; - if (reset_head(the_repository, &ropts) < 0) + ropts.flags = RESET_WORKING_TREE_HARD | + RESET_WORKING_TREE_UPDATE_HEAD; + if (reset_working_tree(the_repository, &ropts) < 0) die(_("could not move back to %s"), oid_to_hex(&options.orig_head->object.oid)); strbuf_release(&head_msg); @@ -1876,11 +1881,13 @@ int cmd_rebase(int argc, options.reflog_action, options.onto_name); ropts.oid = &options.onto->object.oid; ropts.orig_head = &options.orig_head->object.oid; - ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD | - RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + ropts.flags = RESET_WORKING_TREE_DETACH | + RESET_WORKING_TREE_UPDATE_HEAD | + RESET_WORKING_TREE_UPDATE_ORIG_HEAD | + RESET_WORKING_TREE_RUN_POST_CHECKOUT_HOOK; ropts.head_msg = msg.buf; ropts.default_reflog_action = options.reflog_action; - if (reset_head(the_repository, &ropts)) { + if (reset_working_tree(the_repository, &ropts)) { ret = error(_("Could not detach HEAD")); goto cleanup_autostash; }
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 19eb6a1..faf0f12 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c
@@ -615,7 +615,7 @@ static void hmac_hash(unsigned char *out, /* RFC 2104 2. (1) */ memset(key, '\0', GIT_MAX_BLKSZ); if (the_hash_algo->blksz < key_len) { - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); git_hash_update(&ctx, key_in, key_len); git_hash_final(key, &ctx); } else { @@ -629,13 +629,13 @@ static void hmac_hash(unsigned char *out, } /* RFC 2104 2. (3) & (4) */ - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); git_hash_update(&ctx, k_ipad, sizeof(k_ipad)); git_hash_update(&ctx, text, text_len); git_hash_final(out, &ctx); /* RFC 2104 2. (6) & (7) */ - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); git_hash_update(&ctx, k_opad, sizeof(k_opad)); git_hash_update(&ctx, out, the_hash_algo->rawsz); git_hash_final(out, &ctx);
diff --git a/builtin/refs.c b/builtin/refs.c index e3125bc..a9ca205 100644 --- a/builtin/refs.c +++ b/builtin/refs.c
@@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" #include "fsck.h" @@ -22,8 +21,20 @@ #define REFS_OPTIMIZE_USAGE \ N_("git refs optimize " PACK_REFS_OPTS) +#define REFS_CREATE_USAGE \ + N_("git refs create [--message=<reason>] [--no-deref] [--create-reflog] <ref> <new-value>") + +#define REFS_DELETE_USAGE \ + N_("git refs delete [--message=<reason>] [--no-deref] <ref> [<old-value>]") + +#define REFS_UPDATE_USAGE \ + N_("git refs update [--message=<reason>] [--no-deref] [--create-reflog] <ref> <new-value> [<old-value>]") + +#define REFS_RENAME_USAGE \ + N_("git refs rename [--message=<reason>] <old-ref> <new-ref>") + static int cmd_refs_migrate(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { const char * const migrate_usage[] = { REFS_MIGRATE_USAGE, @@ -59,13 +70,13 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix, goto out; } - if (the_repository->ref_storage_format == format) { + if (repo->ref_storage_format == format) { err = error(_("repository already uses '%s' format"), ref_storage_format_to_name(format)); goto out; } - if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) { + if (repo_migrate_ref_storage_format(repo, format, flags, &errbuf) < 0) { err = error("%s", errbuf.buf); goto out; } @@ -99,8 +110,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix, if (argc) usage(_("'git refs verify' takes no arguments")); - repo_config(the_repository, git_fsck_config, &fsck_refs_options); - prepare_repo_settings(the_repository); + repo_config(repo, git_fsck_config, &fsck_refs_options); + prepare_repo_settings(repo); worktrees = get_worktrees_without_reading_head(); for (size_t i = 0; worktrees[i]; i++) @@ -124,7 +135,7 @@ static int cmd_refs_list(int argc, const char **argv, const char *prefix, } static int cmd_refs_exists(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { struct strbuf unused_referent = STRBUF_INIT; struct object_id unused_oid; @@ -145,7 +156,7 @@ static int cmd_refs_exists(int argc, const char **argv, const char *prefix, die(_("'git refs exists' requires a reference")); ref = *argv++; - if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, + if (refs_read_raw_ref(get_main_ref_store(repo), ref, &unused_oid, &unused_referent, &unused_type, &failure_errno)) { if (failure_errno == ENOENT || failure_errno == EISDIR) { @@ -176,6 +187,193 @@ static int cmd_refs_optimize(int argc, const char **argv, const char *prefix, return pack_refs_core(argc, argv, prefix, repo, refs_optimize_usage); } +static int cmd_refs_create(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_create_usage[] = { + REFS_CREATE_USAGE, + NULL + }; + const char *message = NULL; + unsigned flags = 0; + struct option opts[] = { + OPT_STRING(0, "message", &message, N_("reason"), + N_("reason of the update")), + OPT_BIT(0 ,"no-deref", &flags, + N_("update <refname> not the one it points to"), + REF_NO_DEREF), + OPT_BIT(0, "create-reflog", &flags, N_("create a reflog"), + REF_FORCE_CREATE_REFLOG), + OPT_END(), + }; + struct object_id newoid; + const char *refname; + int ret; + + argc = parse_options(argc, argv, prefix, opts, refs_create_usage, 0); + if (argc != 2) + usage(_("create requires reference name and an object ID")); + + if (message && !*message) + die(_("refusing to perform update with empty message")); + + repo_config(repo, git_default_config, NULL); + + refname = argv[0]; + if (repo_get_oid_with_flags(repo, argv[1], &newoid, GET_OID_SKIP_AMBIGUITY_CHECK)) + die(_("invalid object ID: '%s'"), argv[1]); + if (is_null_oid(&newoid)) + die(_("cannot create reference with null new object ID")); + + ret = refs_update_ref(get_main_ref_store(repo), message, refname, + &newoid, null_oid(repo->hash_algo), flags, + UPDATE_REFS_MSG_ON_ERR); + + if (ret < 0) + ret = 1; + return ret; +} + +static int cmd_refs_delete(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_delete_usage[] = { + REFS_DELETE_USAGE, + NULL + }; + const char *message = NULL; + unsigned flags = 0; + struct option opts[] = { + OPT_STRING(0, "message", &message, N_("reason"), + N_("reason of the update")), + OPT_BIT(0 ,"no-deref", &flags, + N_("update <refname> not the one it points to"), + REF_NO_DEREF), + OPT_END(), + }; + struct object_id oldoid; + const char *refname; + int ret; + + argc = parse_options(argc, argv, prefix, opts, refs_delete_usage, 0); + if (argc < 1 || argc > 2) + usage(_("delete requires reference name and an optional old object ID")); + + if (message && !*message) + die(_("refusing to perform update with empty message")); + + repo_config(repo, git_default_config, NULL); + + refname = argv[0]; + if (argc == 2) { + if (repo_get_oid_with_flags(repo, argv[1], &oldoid, GET_OID_SKIP_AMBIGUITY_CHECK)) + die(_("invalid old object ID: '%s'"), argv[1]); + if (is_null_oid(&oldoid)) + die(_("cannot delete reference with null old object ID")); + } + + ret = refs_delete_ref(get_main_ref_store(repo), message, refname, + argc == 2 ? &oldoid : NULL, flags); + + if (ret < 0) + ret = 1; + return ret; +} + +static int cmd_refs_update(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_update_usage[] = { + REFS_UPDATE_USAGE, + NULL + }; + const char *message = NULL; + unsigned flags = 0; + struct option opts[] = { + OPT_STRING(0, "message", &message, N_("reason"), + N_("reason of the update")), + OPT_BIT(0 ,"no-deref", &flags, + N_("update <refname> not the one it points to"), + REF_NO_DEREF), + OPT_BIT(0, "create-reflog", &flags, N_("create a reflog"), + REF_FORCE_CREATE_REFLOG), + OPT_END(), + }; + struct object_id newoid, oldoid; + const char *refname; + int ret; + + argc = parse_options(argc, argv, prefix, opts, refs_update_usage, 0); + if (argc < 2 || argc > 3) + usage(_("update requires reference name, new value and an optional old value")); + + if (message && !*message) + die(_("refusing to perform update with empty message")); + + repo_config(repo, git_default_config, NULL); + + refname = argv[0]; + if (repo_get_oid_with_flags(repo, argv[1], &newoid, + GET_OID_SKIP_AMBIGUITY_CHECK)) + die(_("invalid new object ID: '%s'"), argv[1]); + if (argc == 3 && + repo_get_oid_with_flags(repo, argv[2], &oldoid, + GET_OID_SKIP_AMBIGUITY_CHECK)) + die(_("invalid old object ID: '%s'"), argv[2]); + + ret = refs_update_ref(get_main_ref_store(repo), message, refname, + &newoid, argc == 3 ? &oldoid : NULL, flags, + UPDATE_REFS_MSG_ON_ERR); + + if (ret < 0) + ret = 1; + return ret; +} + +static int cmd_refs_rename(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_rename_usage[] = { + REFS_RENAME_USAGE, + NULL + }; + const char *message = NULL; + struct option opts[] = { + OPT_STRING(0, "message", &message, N_("reason"), + N_("reason of the update")), + OPT_END(), + }; + const char *oldref, *newref; + int ret; + + argc = parse_options(argc, argv, prefix, opts, refs_rename_usage, 0); + if (argc != 2) + usage(_("rename requires old and new reference name")); + if (message && !*message) + die(_("refusing to perform update with empty message")); + + repo_config(repo, git_default_config, NULL); + + oldref = argv[0]; + newref = argv[1]; + + if (check_refname_format(oldref, 0)) + die(_("invalid ref format: '%s'"), oldref); + if (check_refname_format(newref, 0)) + die(_("invalid ref format: '%s'"), newref); + + if (!refs_ref_exists(get_main_ref_store(repo), oldref)) + die(_("reference does not exist: '%s'"), oldref); + if (refs_ref_exists(get_main_ref_store(repo), newref)) + die(_("reference already exists: '%s'"), newref); + + ret = refs_rename_ref(get_main_ref_store(repo), oldref, newref, message); + + if (ret < 0) + ret = 1; + return ret; +} + int cmd_refs(int argc, const char **argv, const char *prefix, @@ -187,6 +385,10 @@ int cmd_refs(int argc, "git refs list " COMMON_USAGE_FOR_EACH_REF, REFS_EXISTS_USAGE, REFS_OPTIMIZE_USAGE, + REFS_CREATE_USAGE, + REFS_DELETE_USAGE, + REFS_UPDATE_USAGE, + REFS_RENAME_USAGE, NULL, }; parse_opt_subcommand_fn *fn = NULL; @@ -196,6 +398,10 @@ int cmd_refs(int argc, OPT_SUBCOMMAND("list", &fn, cmd_refs_list), OPT_SUBCOMMAND("exists", &fn, cmd_refs_exists), OPT_SUBCOMMAND("optimize", &fn, cmd_refs_optimize), + OPT_SUBCOMMAND("create", &fn, cmd_refs_create), + OPT_SUBCOMMAND("delete", &fn, cmd_refs_delete), + OPT_SUBCOMMAND("update", &fn, cmd_refs_update), + OPT_SUBCOMMAND("rename", &fn, cmd_refs_rename), OPT_END(), };
diff --git a/builtin/repack.c b/builtin/repack.c index 1524a9c..db504d6 100644 --- a/builtin/repack.c +++ b/builtin/repack.c
@@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" @@ -265,7 +264,7 @@ int cmd_repack(int argc, if (write_bitmaps < 0) { if (write_midx == REPACK_WRITE_MIDX_NONE && - (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository())) + (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository(repo))) write_bitmaps = 0; } if (po_args.pack_kept_objects < 0) @@ -458,6 +457,8 @@ int cmd_repack(int argc, } if (!names.nr) { + struct odb_source_files *files = odb_source_files_downcast(existing.source); + if (!po_args.quiet) printf_ln(_("Nothing new to pack.")); /* @@ -473,7 +474,7 @@ int cmd_repack(int argc, * midx_has_unknown_packs() will make the decision for * us. */ - if (!get_multi_pack_index(existing.source)) + if (!get_multi_pack_index(files->packed)) midx_must_contain_cruft = 1; } @@ -626,10 +627,12 @@ int cmd_repack(int argc, update_server_info(repo, 0); if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) { + struct odb_source_files *files = odb_source_files_downcast(existing.source); unsigned flags = 0; + if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL, 0)) flags |= MIDX_WRITE_INCREMENTAL; - write_midx_file(existing.source, NULL, NULL, flags); + write_midx_file(files->packed, NULL, NULL, flags); } cleanup:
diff --git a/builtin/repo.c b/builtin/repo.c index 69f3626..84e012f 100644 --- a/builtin/repo.c +++ b/builtin/repo.c
@@ -7,12 +7,14 @@ #include "hex.h" #include "odb.h" #include "parse-options.h" +#include "path.h" #include "path-walk.h" #include "progress.h" #include "quote.h" #include "ref-filter.h" #include "refs.h" #include "revision.h" +#include "setup.h" #include "strbuf.h" #include "string-list.h" #include "shallow.h" @@ -58,7 +60,7 @@ struct repo_info_field { static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf) { - strbuf_addstr(buf, is_bare_repository() ? "true" : "false"); + strbuf_addstr(buf, is_bare_repository(the_repository) ? "true" : "false"); return 0; } @@ -75,6 +77,50 @@ static int get_object_format(struct repository *repo, struct strbuf *buf) return 0; } +static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf) +{ + const char *common_dir = repo_get_common_dir(repo); + + if (!common_dir) + return error(_("unable to get common directory")); + + format_path(buf, common_dir, repo->prefix, PATH_FORMAT_CANONICAL); + return 0; +} + +static int get_path_commondir_relative(struct repository *repo, struct strbuf *buf) +{ + const char *common_dir = repo_get_common_dir(repo); + + if (!common_dir) + return error(_("unable to get common directory")); + + format_path(buf, common_dir, repo->prefix, PATH_FORMAT_RELATIVE); + return 0; +} + +static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf) +{ + const char *git_dir = repo_get_git_dir(repo); + + if (!git_dir) + return error(_("unable to get git directory")); + + format_path(buf, git_dir, repo->prefix, PATH_FORMAT_CANONICAL); + return 0; +} + +static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf) +{ + const char *git_dir = repo_get_git_dir(repo); + + if (!git_dir) + return error(_("unable to get git directory")); + + format_path(buf, git_dir, repo->prefix, PATH_FORMAT_RELATIVE); + return 0; +} + static int get_references_format(struct repository *repo, struct strbuf *buf) { strbuf_addstr(buf, @@ -87,6 +133,10 @@ static const struct repo_info_field repo_info_field[] = { { "layout.bare", get_layout_bare }, { "layout.shallow", get_layout_shallow }, { "object.format", get_object_format }, + { "path.commondir.absolute", get_path_commondir_absolute }, + { "path.commondir.relative", get_path_commondir_relative }, + { "path.gitdir.absolute", get_path_gitdir_absolute }, + { "path.gitdir.relative", get_path_gitdir_relative }, { "references.format", get_references_format }, };
diff --git a/builtin/reset.c b/builtin/reset.c index 3be6bd0..78e69bd 100644 --- a/builtin/reset.c +++ b/builtin/reset.c
@@ -470,7 +470,7 @@ int cmd_reset(int argc, if (reset_type != SOFT && (reset_type != MIXED || repo_get_work_tree(the_repository))) setup_work_tree(the_repository); - if (reset_type == MIXED && is_bare_repository()) + if (reset_type == MIXED && is_bare_repository(the_repository)) die(_("%s reset is not allowed in a bare repository"), _(reset_type_names[reset_type]));
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index bb88267..4369345 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c
@@ -255,7 +255,7 @@ static int show_file(const char *arg, int output_prefix) show_default(); if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) { if (output_prefix) { - const char *prefix = startup_info->prefix; + const char *prefix = the_repository->prefix; char *fname = prefix_filename(prefix, arg); show(fname); free(fname); @@ -653,53 +653,46 @@ enum default_type { DEFAULT_UNMODIFIED, }; -static void print_path(const char *path, const char *prefix, enum format_type format, enum default_type def) +static void print_path(const char *path, const char *prefix, + enum format_type format, enum default_type def) { - char *cwd = NULL; - /* - * We don't ever produce a relative path if prefix is NULL, so set the - * prefix to the current directory so that we can produce a relative - * path whenever possible. If we're using RELATIVE_IF_SHARED mode, then - * we want an absolute path unless the two share a common prefix, so don't - * set it in that case, since doing so causes a relative path to always - * be produced if possible. - */ - if (!prefix && (format != FORMAT_DEFAULT || def != DEFAULT_RELATIVE_IF_SHARED)) - prefix = cwd = xgetcwd(); - if (format == FORMAT_DEFAULT && def == DEFAULT_UNMODIFIED) { - puts(path); - } else if (format == FORMAT_RELATIVE || - (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE)) { - /* - * In order for relative_path to work as expected, we need to - * make sure that both paths are absolute paths. If we don't, - * we can end up with an unexpected absolute path that the user - * didn't want. - */ - struct strbuf buf = STRBUF_INIT, realbuf = STRBUF_INIT, prefixbuf = STRBUF_INIT; - if (!is_absolute_path(path)) { - strbuf_realpath_forgiving(&realbuf, path, 1); - path = realbuf.buf; + struct strbuf sb = STRBUF_INIT; + enum path_format fmt; + + if (format == FORMAT_DEFAULT) { + switch (def) { + case DEFAULT_RELATIVE: + fmt = PATH_FORMAT_RELATIVE; + break; + case DEFAULT_RELATIVE_IF_SHARED: + fmt = PATH_FORMAT_RELATIVE_IF_SHARED; + break; + case DEFAULT_CANONICAL: + fmt = PATH_FORMAT_CANONICAL; + break; + case DEFAULT_UNMODIFIED: + default: + fmt = PATH_FORMAT_UNMODIFIED; + break; } - if (!is_absolute_path(prefix)) { - strbuf_realpath_forgiving(&prefixbuf, prefix, 1); - prefix = prefixbuf.buf; - } - puts(relative_path(path, prefix, &buf)); - strbuf_release(&buf); - strbuf_release(&realbuf); - strbuf_release(&prefixbuf); - } else if (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE_IF_SHARED) { - struct strbuf buf = STRBUF_INIT; - puts(relative_path(path, prefix, &buf)); - strbuf_release(&buf); } else { - struct strbuf buf = STRBUF_INIT; - strbuf_realpath_forgiving(&buf, path, 1); - puts(buf.buf); - strbuf_release(&buf); + switch (format) { + case FORMAT_RELATIVE: + fmt = PATH_FORMAT_RELATIVE; + break; + case FORMAT_CANONICAL: + fmt = PATH_FORMAT_CANONICAL; + break; + default: + fmt = PATH_FORMAT_UNMODIFIED; + break; + } } - free(cwd); + + format_path(&sb, path, prefix, fmt); + puts(sb.buf); + + strbuf_release(&sb); } int cmd_rev_parse(int argc, @@ -839,7 +832,8 @@ int cmd_rev_parse(int argc, prefix = argv[++i]; if (!prefix) die(_("--prefix requires an argument")); - startup_info->prefix = prefix; + FREE_AND_NULL(the_repository->prefix); + the_repository->prefix = xstrdup(prefix); output_prefix = 1; continue; } @@ -1084,7 +1078,7 @@ int cmd_rev_parse(int argc, continue; } if (!strcmp(arg, "--is-bare-repository")) { - printf("%s\n", is_bare_repository() ? "true" + printf("%s\n", is_bare_repository(the_repository) ? "true" : "false"); continue; }
diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 6b2a0b9..4c78d2e 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c
@@ -433,6 +433,8 @@ int cmd_shortlog(int argc, case PARSE_OPT_UNKNOWN: break; case PARSE_OPT_HELP: + exit(0); + case PARSE_OPT_HELP_ERROR: case PARSE_OPT_ERROR: case PARSE_OPT_SUBCOMMAND: exit(129);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c index f02831b..2435e8a 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c
@@ -62,11 +62,10 @@ static const char *get_color_reset_code(void) static struct commit *interesting(struct prio_queue *queue) { - for (size_t i = 0; i < queue->nr; i++) { - struct commit *commit = queue->array[i].data; - if (commit->object.flags & UNINTERESTING) - continue; - return commit; + struct commit *commit; + prio_queue_for_each(queue, commit) { + if (!(commit->object.flags & UNINTERESTING)) + return commit; } return NULL; } @@ -228,17 +227,18 @@ static void join_revs(struct prio_queue *queue, { int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); + struct commit *commit; - while (queue->nr) { + while ((commit = prio_queue_peek(queue))) { struct commit_list *parents; int still_interesting = !!interesting(queue); - struct commit *commit = prio_queue_peek(queue); - bool get_pending = true; int flags = commit->object.flags & all_mask; if (!still_interesting && extra <= 0) break; + prio_queue_get(queue); + mark_seen(commit, seen_p); if ((flags & all_revs) == all_revs) flags |= UNINTERESTING; @@ -254,14 +254,8 @@ static void join_revs(struct prio_queue *queue, if (mark_seen(p, seen_p) && !still_interesting) extra--; p->object.flags |= flags; - if (get_pending) - prio_queue_replace(queue, p); - else - prio_queue_put(queue, p); - get_pending = false; + prio_queue_put(queue, p); } - if (get_pending) - prio_queue_get(queue); } /*
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 3ec8bf5..2b3c976 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c
@@ -550,11 +550,11 @@ static void create_default_gitdir_config(const char *submodule_name) /* Case 2.4: If all the above failed, try a hash of the name as a last resort */ header_len = snprintf(header, sizeof(header), "blob %zu", strlen(submodule_name)); - the_hash_algo->init_fn(&ctx); - the_hash_algo->update_fn(&ctx, header, header_len); - the_hash_algo->update_fn(&ctx, "\0", 1); - the_hash_algo->update_fn(&ctx, submodule_name, strlen(submodule_name)); - the_hash_algo->final_fn(raw_name_hash, &ctx); + git_hash_init(&ctx, the_hash_algo); + git_hash_update(&ctx, header, header_len); + git_hash_update(&ctx, "\0", 1); + git_hash_update(&ctx, submodule_name, strlen(submodule_name)); + git_hash_final(raw_name_hash, &ctx); hash_to_hex_algop_r(hex_name_hash, raw_name_hash, the_hash_algo); strbuf_reset(&gitdir_path); repo_git_path_append(the_repository, &gitdir_path, "modules/%s", hex_name_hash);
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index f3849bb..93a9caa 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c
@@ -670,10 +670,10 @@ int cmd_unpack_objects(int argc, /* We don't take any non-flag arguments now.. Maybe some day */ usage(unpack_usage); } - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); unpack_all(); git_hash_update(&ctx, buffer, offset); - the_hash_algo->init_fn(&tmp_ctx); + git_hash_init(&tmp_ctx, the_hash_algo); git_hash_clone(&tmp_ctx, &ctx); git_hash_final_oid(&oid, &tmp_ctx); if (strict) {
diff --git a/builtin/update-index.c b/builtin/update-index.c index 3d6646c..5d80d48 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c
@@ -875,7 +875,7 @@ static enum parse_opt_result unresolve_callback( const char *arg, int unset) { int *has_errors = opt->value; - const char *prefix = startup_info->prefix; + const char *prefix = the_repository->prefix; BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); @@ -896,7 +896,7 @@ static enum parse_opt_result reupdate_callback( const char *arg, int unset) { int *has_errors = opt->value; - const char *prefix = startup_info->prefix; + const char *prefix = the_repository->prefix; BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); @@ -1133,6 +1133,8 @@ int cmd_update_index(int argc, break; switch (parseopt_state) { case PARSE_OPT_HELP: + exit(0); + case PARSE_OPT_HELP_ERROR: case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_COMPLETE:
diff --git a/builtin/worktree.c b/builtin/worktree.c index d21c43f..4bc7b4f 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c
@@ -945,14 +945,17 @@ static int add(int ac, const char **av, const char *prefix, strvec_push(&cp.args, branch); if (opt_track) strvec_push(&cp.args, opt_track); - if (run_command(&cp)) - return -1; + if (run_command(&cp)) { + ret = -1; + goto cleanup; + } branch = new_branch; } else if (opt_track) { die(_("--[no-]track can only be used if a new branch is created")); } ret = add_worktree(path, branch, &opts); +cleanup: free(path); free(opt_track); free(branch_to_free);
diff --git a/bundle-uri.c b/bundle-uri.c index 3b2e347..9ca2060 100644 --- a/bundle-uri.c +++ b/bundle-uri.c
@@ -378,7 +378,7 @@ static int download_https_uri_to_file(const char *file, const char *uri) if (child_in) fclose(child_in); if (finish_command(&cp)) - return 1; + result = 1; if (child_out) fclose(child_out); return result; @@ -946,8 +946,12 @@ static int config_to_packet_line(const char *key, const char *value, { struct packet_reader *writer = data; - if (starts_with(key, "bundle.")) - packet_write_fmt(writer->fd, "%s=%s", key, value); + if (starts_with(key, "bundle.")) { + if (value && *value) + packet_write_fmt(writer->fd, "%s=%s", key, value); + else + warning(_("config '%s' has no value"), key); + } return 0; }
diff --git a/cache-tree.c b/cache-tree.c index 184f7e2..9204eb3 100644 --- a/cache-tree.c +++ b/cache-tree.c
@@ -162,6 +162,54 @@ void cache_tree_invalidate_path(struct index_state *istate, const char *path) istate->cache_changed |= CACHE_TREE_CHANGED; } +/* + * Check whether this_ce and the next entry in the index form a D/F + * conflict ("path" vs "path/file"). Returns the conflicting "path/..." + * name when one is found, or NULL otherwise. + * + * The cache is sorted, so "path/file" sorts after "path" and the + * conflict is usually visible as adjacent entries. But other entries + * can sort between them -- e.g. "path-internal" sits between "path" + * and "path/file" because '-' (0x2D) precedes '/' (0x2F) -- so when + * the immediately following entry shares our prefix but starts with a + * character that sorts before '/', binary search for "path/" instead. + */ +static const char *find_df_conflict(struct index_state *istate, + const struct cache_entry *this_ce, + const struct cache_entry *next_ce) +{ + const char *this_name = this_ce->name; + const char *next_name = next_ce->name; + int this_len = ce_namelen(this_ce); + const struct cache_entry *other; + struct strbuf probe = STRBUF_INIT; + int pos; + + if (this_len >= ce_namelen(next_ce) || + next_name[this_len] > '/' || + strncmp(this_name, next_name, this_len)) + return NULL; + + if (next_name[this_len] == '/') + return next_name; + + strbuf_add(&probe, this_name, this_len); + strbuf_addch(&probe, '/'); + pos = index_name_pos_sparse(istate, probe.buf, probe.len); + strbuf_release(&probe); + + if (pos < 0) + pos = -pos - 1; + if (pos >= (int)istate->cache_nr) + return NULL; + other = istate->cache[pos]; + if (ce_namelen(other) > this_len && + other->name[this_len] == '/' && + !strncmp(this_name, other->name, this_len)) + return other->name; + return NULL; +} + static int verify_cache(struct index_state *istate, int flags) { unsigned i, funny; @@ -191,24 +239,18 @@ static int verify_cache(struct index_state *istate, int flags) */ funny = 0; for (i = 0; i + 1 < istate->cache_nr; i++) { - /* path/file always comes after path because of the way - * the cache is sorted. Also path can appear only once, - * which means conflicting one would immediately follow. - */ const struct cache_entry *this_ce = istate->cache[i]; const struct cache_entry *next_ce = istate->cache[i + 1]; - const char *this_name = this_ce->name; - const char *next_name = next_ce->name; - int this_len = ce_namelen(this_ce); - if (this_len < ce_namelen(next_ce) && - next_name[this_len] == '/' && - strncmp(this_name, next_name, this_len) == 0) { + const char *conflict_name; + + conflict_name = find_df_conflict(istate, this_ce, next_ce); + if (conflict_name) { if (10 < ++funny) { fprintf(stderr, "...\n"); break; } fprintf(stderr, "You have both %s and %s\n", - this_name, next_name); + this_ce->name, conflict_name); } } if (funny)
diff --git a/chdir-notify.c b/chdir-notify.c index f8bfe3c..1237a45 100644 --- a/chdir-notify.c +++ b/chdir-notify.c
@@ -43,32 +43,6 @@ void chdir_notify_unregister(const char *name, chdir_notify_callback cb, } } -static void reparent_cb(const char *name, - const char *old_cwd, - const char *new_cwd, - void *data) -{ - char **path = data; - char *tmp = *path; - - if (!tmp) - return; - - *path = reparent_relative_path(old_cwd, new_cwd, tmp); - free(tmp); - - if (name) { - trace_printf_key(&trace_setup_key, - "setup: reparent %s to '%s'", - name, *path); - } -} - -void chdir_notify_reparent(const char *name, char **path) -{ - chdir_notify_register(name, reparent_cb, path); -} - int chdir_notify(const char *new_cwd) { struct strbuf old_cwd = STRBUF_INIT;
diff --git a/chdir-notify.h b/chdir-notify.h index 81eb69d..36b4114 100644 --- a/chdir-notify.h +++ b/chdir-notify.h
@@ -19,10 +19,7 @@ * chdir_notify_register("description", foo, data); * * In practice most callers will want to move a relative path to the new root; - * they can use the reparent_relative_path() helper for that. If that's all - * you're doing, you can also use the convenience function: - * - * chdir_notify_reparent("description", &my_path); + * they can use the reparent_relative_path() helper for that. * * Whenever a chdir event occurs, that will update my_path (if it's relative) * to adjust for the new cwd by freeing any existing string and allocating a @@ -43,7 +40,6 @@ typedef void (*chdir_notify_callback)(const char *name, void chdir_notify_register(const char *name, chdir_notify_callback cb, void *data); void chdir_notify_unregister(const char *name, chdir_notify_callback cb, void *data); -void chdir_notify_reparent(const char *name, char **path); /* *
diff --git a/ci/lib.sh b/ci/lib.sh index b939110..6c52154 100755 --- a/ci/lib.sh +++ b/ci/lib.sh
@@ -215,6 +215,7 @@ test macos != "$CI_OS_NAME" || CI_OS_NAME=osx CI_REPO_SLUG="$GITHUB_REPOSITORY" CI_JOB_ID="$GITHUB_RUN_ID" + CI_EVENT="$GITHUB_EVENT_NAME" CC="${CC_PACKAGE:-${CC:-gcc}}" DONT_SKIP_TAGS=t handle_failed_tests () { @@ -239,6 +240,13 @@ CI_BRANCH="$CI_COMMIT_REF_NAME" CI_COMMIT="$CI_COMMIT_SHA" + case "$CI_PIPELINE_SOURCE" in + merge_request_event) + CI_EVENT=pull_request;; + *) + CI_EVENT="$CI_PIPELINE_SOURCE";; + esac + case "$OS,$CI_JOB_IMAGE" in Windows_NT,*) CI_OS_NAME=windows @@ -319,9 +327,9 @@ # enable "expensive" tests for PR events. # In order to catch bugs introduced at integration time by mismerges, # enable the long tests for pushes to the integration branches as well. -case "$GITHUB_EVENT_NAME,$CI_BRANCH" in +case "$CI_EVENT,$CI_BRANCH" in pull_request,*|push,*next*|push,*master*|push,*main*|push,*maint*) - export GIT_TEST_LONG=YesPlease + export GIT_TEST_LONG=${GIT_TEST_LONG:-true} ;; esac
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh index e7b9795..37b24b0 100755 --- a/ci/run-build-and-minimal-fuzzers.sh +++ b/ci/run-build-and-minimal-fuzzers.sh
@@ -21,6 +21,7 @@ pack-headers pack-idx parse-attr-line +reftable url-decode-mem "
diff --git a/commit-graph.c b/commit-graph.c index 801471a..ce2e020 100644 --- a/commit-graph.c +++ b/commit-graph.c
@@ -1538,7 +1538,7 @@ static int add_packed_commits(const struct object_id *oid, struct object_info oi = OBJECT_INFO_INIT; oi.typep = &type; - if (packed_object_info(pack, offset, &oi) < 0) + if (packed_object_info(NULL, pack, offset, &oi) < 0) die(_("unable to get type of object %s"), oid_to_hex(oid)); return add_packed_commits_oi(oid, &oi, data); @@ -1653,6 +1653,7 @@ static void compute_reachable_generation_numbers( { int i; struct commit_list *list = NULL; + intmax_t steps = 0; for (i = 0; i < info->commits->nr; i++) { struct commit *c = info->commits->items[i]; @@ -1671,6 +1672,7 @@ static void compute_reachable_generation_numbers( int all_parents_computed = 1; timestamp_t max_gen = 0; + steps++; for (parent = current->parents; parent; parent = parent->next) { repo_parse_commit(info->r, parent->item); gen = info->get_generation(parent->item, info->data); @@ -1694,6 +1696,9 @@ static void compute_reachable_generation_numbers( } } } + + trace2_data_intmax("commit-graph", info->r, + "generation-dfs-steps", steps); } static timestamp_t get_topo_level(struct commit *c, void *data) @@ -2016,8 +2021,8 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx) odb_prepare_alternates(ctx->r->objects); for (source = ctx->r->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); - packfile_store_for_each_object(files->packed, &oi, add_packed_commits_oi, - ctx, &opts); + odb_source_for_each_object(&files->packed->base, &oi, add_packed_commits_oi, + ctx, &opts); } if (ctx->progress_done < ctx->approx_nr_objects) @@ -2605,7 +2610,7 @@ int write_commit_graph(struct odb_source *source, g = prepare_commit_graph(ctx.r); for (struct commit_graph *chain = g; chain; chain = chain->base_graph) - g->topo_levels = &topo_levels; + chain->topo_levels = &topo_levels; if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS) ctx.changed_paths = 1;
diff --git a/commit-reach.c b/commit-reach.c index 5df471a..d7221fe 100644 --- a/commit-reach.c +++ b/commit-reach.c
@@ -108,11 +108,14 @@ static int paint_down_to_common(struct repository *r, { compare_commits_by_gen_then_commit_date } }; int i; + int gen_ordered = 1; timestamp_t last_gen = GENERATION_NUMBER_INFINITY; struct commit_list **tail = result; - if (!min_generation && !corrected_commit_dates_enabled(r)) + if (!min_generation && !corrected_commit_dates_enabled(r)) { queue.pq.compare = compare_commits_by_commit_date; + gen_ordered = 0; + } one->object.flags |= PARENT1; if (!n) { @@ -147,11 +150,12 @@ static int paint_down_to_common(struct repository *r, commit->object.flags |= RESULT; tail = commit_list_append(commit, tail); /* - * The queue is generation-ordered; no - * remaining common ancestor can be a + * When the queue is generation-ordered, + * no remaining common ancestor can be a * descendant of this one. */ if (!(mb_flags & MERGE_BASE_FIND_ALL) && + gen_ordered && generation < GENERATION_NUMBER_INFINITY) break; } @@ -1121,6 +1125,7 @@ void ahead_behind(struct repository *r, struct nonstale_queue queue = { { .compare = compare_commits_by_gen_then_commit_date } }; + void *entry; size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD); if (!commits_nr || !counts_nr) @@ -1186,8 +1191,8 @@ void ahead_behind(struct repository *r, /* STALE is used here, PARENT2 is used by insert_no_dup(). */ repo_clear_commit_marks(r, PARENT2 | STALE); - for (size_t i = 0; i < queue.pq.nr; i++) - free_bit_array(queue.pq.array[i].data); + prio_queue_for_each(&queue.pq, entry) + free_bit_array(entry); clear_bit_arrays(&bit_arrays); clear_nonstale_queue(&queue); } @@ -1320,7 +1325,7 @@ int get_branch_base_for_tip(struct repository *r, size_t bases_nr) { int best_index = -1; - struct commit *branch_point = NULL; + struct commit *c, *branch_point = NULL; struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; int found_missing_gen = 0; @@ -1373,8 +1378,7 @@ int get_branch_base_for_tip(struct repository *r, prio_queue_put(&queue, c); } - while (queue.nr) { - struct commit *c = prio_queue_get(&queue); + while ((c = prio_queue_get(&queue))) { int best_for_c = get_best(c); int best_for_p, positive; struct commit *parent;
diff --git a/commit.c b/commit.c index 5c4a431..ad26f0b 100644 --- a/commit.c +++ b/commit.c
@@ -782,24 +782,17 @@ void commit_list_sort_by_date(struct commit_list **list) struct commit *pop_most_recent_commit(struct prio_queue *queue, unsigned int mark) { - struct commit *ret = prio_queue_peek(queue); - int get_pending = 1; + struct commit *ret = prio_queue_get(queue); struct commit_list *parents = ret->parents; while (parents) { struct commit *commit = parents->item; if (!repo_parse_commit(the_repository, commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; - if (get_pending) - prio_queue_replace(queue, commit); - else - prio_queue_put(queue, commit); - get_pending = 0; + prio_queue_put(queue, commit); } parents = parents->next; } - if (get_pending) - prio_queue_get(queue); return ret; }
diff --git a/common-init.c b/common-init.c index 5cc73f0..d26c9c1 100644 --- a/common-init.c +++ b/common-init.c
@@ -5,7 +5,10 @@ #include "exec-cmd.h" #include "gettext.h" #include "attr.h" +#include "odb.h" +#include "parse.h" #include "repository.h" +#include "replace-object.h" #include "setup.h" #include "strbuf.h" #include "trace2.h" @@ -31,6 +34,22 @@ static void restore_sigpipe_to_default(void) signal(SIGPIPE, SIG_DFL); } +static void setup_environment(void) +{ + char *git_replace_ref_base; + const char *replace_ref_base; + + if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) + disable_replace_refs(); + replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT); + git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base + : "refs/replace/"); + update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base); + + if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0)) + fetch_if_missing = 0; +} + void init_git(const char **argv) { struct strbuf tmp = STRBUF_INIT; @@ -51,6 +70,7 @@ void init_git(const char **argv) git_setup_gettext(); initialize_repository(the_repository); + setup_environment(); attr_start();
diff --git a/compat/mingw.c b/compat/mingw.c index 41e055f..3eca3a7 100644 --- a/compat/mingw.c +++ b/compat/mingw.c
@@ -2269,10 +2269,8 @@ int mingw_kill(pid_t pid, int sig) } ret = terminate_process_tree(h, 128 + sig); } - if (ret) { + if (ret) errno = err_win_to_posix(GetLastError()); - CloseHandle(h); - } return ret; } else if (pid > 0 && sig == 0) { HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); @@ -3405,7 +3403,7 @@ int is_valid_win32_path(const char *path, int allow_literal_nul) const char *p = path; int preceding_space_or_period = 0, i = 0, periods = 0; - if (!protect_ntfs) + if (!repo_protect_ntfs(the_repository)) return 1; skip_dos_drive_prefix((char **)&path);
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c index 1711794..8077f62 100644 --- a/compat/precompose_utf8.c +++ b/compat/precompose_utf8.c
@@ -19,6 +19,11 @@ typedef char *iconv_ibp; static const char *repo_encoding = "UTF-8"; static const char *path_encoding = "UTF-8-MAC"; +static size_t dirent_prec_psx_size(size_t max_name_len) +{ + return st_add(offsetof(dirent_prec_psx, d_name), max_name_len); +} + static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c) { const uint8_t *ptr = (const uint8_t *)s; @@ -114,8 +119,8 @@ const char *precompose_argv_prefix(int argc, const char **argv, const char *pref PREC_DIR *precompose_utf8_opendir(const char *dirname) { PREC_DIR *prec_dir = xmalloc(sizeof(PREC_DIR)); - prec_dir->dirent_nfc = xmalloc(sizeof(dirent_prec_psx)); - prec_dir->dirent_nfc->max_name_len = sizeof(prec_dir->dirent_nfc->d_name); + prec_dir->dirent_nfc = xmalloc(dirent_prec_psx_size(NAME_MAX + 1)); + prec_dir->dirent_nfc->max_name_len = NAME_MAX + 1; prec_dir->dirp = opendir(dirname); if (!prec_dir->dirp) { @@ -145,8 +150,7 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir) int ret_errno = errno; if (new_maxlen > prec_dir->dirent_nfc->max_name_len) { - size_t new_len = sizeof(dirent_prec_psx) + new_maxlen - - sizeof(prec_dir->dirent_nfc->d_name); + size_t new_len = dirent_prec_psx_size(new_maxlen); prec_dir->dirent_nfc = xrealloc(prec_dir->dirent_nfc, new_len); prec_dir->dirent_nfc->max_name_len = new_maxlen;
diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h index fea06cf..c7c3cc2 100644 --- a/compat/precompose_utf8.h +++ b/compat/precompose_utf8.h
@@ -14,11 +14,12 @@ typedef struct dirent_prec_psx { /* * See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html - * NAME_MAX + 1 should be enough, but some systems have - * NAME_MAX=255 and strlen(d_name) may return 508 or 510 - * Solution: allocate more when needed, see precompose_utf8_readdir() + * Start with room for NAME_MAX + 1 bytes, but keep d_name as a + * flexible array. Some systems have NAME_MAX=255 while strlen(d_name) + * from readdir() may return 508 or 510 bytes. Grow the allocation as + * needed in precompose_utf8_readdir(). */ - char d_name[NAME_MAX+1]; + char d_name[FLEX_ARRAY]; } dirent_prec_psx;
diff --git a/compat/win32/exit-process.h b/compat/win32/exit-process.h index d539898..2600416 100644 --- a/compat/win32/exit-process.h +++ b/compat/win32/exit-process.h
@@ -159,6 +159,7 @@ static int exit_process(HANDLE process, int exit_code) return terminate_process_tree(process, exit_code); } + CloseHandle(process); return 0; }
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c index 966ef77..f779f36 100644 --- a/compat/win32/path-utils.c +++ b/compat/win32/path-utils.c
@@ -2,6 +2,7 @@ #include "../../git-compat-util.h" #include "../../environment.h" +#include "../../repository.h" int win32_has_dos_drive_prefix(const char *path) { @@ -75,7 +76,7 @@ int win32_fspathncmp(const char *a, const char *b, size_t count) } else if (is_dir_sep(*b)) return +1; - diff = ignore_case ? + diff = repo_ignore_case(the_repository) ? (unsigned char)tolower(*a) - (int)(unsigned char)tolower(*b) : (unsigned char)*a - (int)(unsigned char)*b; if (diff)
diff --git a/connect.c b/connect.c index 47e39d2..1d74c1e 100644 --- a/connect.c +++ b/connect.c
@@ -517,7 +517,7 @@ static void send_capabilities(int fd_out, struct packet_reader *reader) int get_remote_bundle_uri(int fd_out, struct packet_reader *reader, struct bundle_list *bundles, int stateless_rpc) { - int line_nr = 1; + int line_nr = 1, err = 0; /* Assert bundle-uri support */ ensure_server_supports_v2("bundle-uri"); @@ -536,10 +536,19 @@ int get_remote_bundle_uri(int fd_out, struct packet_reader *reader, const char *line = reader->line; line_nr++; + /* + * Do not parse if an error was encountered, but + * continue draining the response so no stale data + * is left in the reader for subsequent protocol + * exchanges. + */ + if (err) + continue; + if (!bundle_uri_parse_line(bundles, line)) continue; - return error(_("error on bundle-uri response line %d: %s"), + err = error(_("error on bundle-uri response line %d: %s"), line_nr, line); } @@ -554,7 +563,7 @@ int get_remote_bundle_uri(int fd_out, struct packet_reader *reader, check_stateless_delimiter(stateless_rpc, reader, _("expected response end packet after ref listing")); - return 0; + return err; } struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
diff --git a/connected.c b/connected.c index 7e26976..929b9bd 100644 --- a/connected.c +++ b/connected.c
@@ -11,6 +11,62 @@ #include "packfile.h" #include "promisor-remote.h" +static int promised_object_cb(const struct object_id *oid UNUSED, + struct object_info *oi UNUSED, + void *payload) +{ + bool *found = payload; + *found = true; + return 1; +} + +/* + * For partial clones, we don't want to have to do a regular connectivity check + * because we have to enumerate and exclude all promisor objects (slow), and + * then the connectivity check itself becomes a no-op because in a partial + * clone every object is a promisor object. Instead, just make sure we + * received, in a promisor packfile, the objects pointed to by each wanted ref. + * + * Before checking for promisor packs, be sure we have the latest pack-files + * loaded into memory. + * + * Returns 1 when all object IDs have been found in promisor packs, in which + * case we're fully connected and thus done. Returns 0 when we have found + * objects in non-promisor packs, in which case we'll have to fall back to the + * rev-list-based connectivity checks. Returns a negative error code on error. + */ +static int check_connected_promisor(oid_iterate_fn fn, + void *cb_data, + const struct object_id **oid) +{ + struct odb_for_each_object_options opts = { + .flags = ODB_FOR_EACH_OBJECT_PROMISOR_ONLY, + .prefix_hex_len = the_repository->hash_algo->hexsz, + }; + int err; + + odb_reprepare(the_repository->objects); + do { + bool found = false; + + opts.prefix = *oid; + + err = odb_for_each_object_ext(the_repository->objects, NULL, + promised_object_cb, &found, &opts); + if (err < 0) + return err; + + /* + * We have found an object that is not part of a promisor pack, + * and thus we cannot skip the full connectivity check. + */ + if (!found) + return 0; + } while ((*oid = fn(cb_data)) != NULL); + + return 1; +} + /* * If we feed all the commits we want to verify to this command * @@ -46,42 +102,16 @@ int check_connected(oid_iterate_fn fn, void *cb_data, } if (repo_has_promisor_remote(the_repository)) { - /* - * For partial clones, we don't want to have to do a regular - * connectivity check because we have to enumerate and exclude - * all promisor objects (slow), and then the connectivity check - * itself becomes a no-op because in a partial clone every - * object is a promisor object. Instead, just make sure we - * received, in a promisor packfile, the objects pointed to by - * each wanted ref. - * - * Before checking for promisor packs, be sure we have the - * latest pack-files loaded into memory. - */ - odb_reprepare(the_repository->objects); - do { - struct packed_git *p; - - repo_for_each_pack(the_repository, p) { - if (!p->pack_promisor) - continue; - if (find_pack_entry_one(oid, p)) - goto promisor_pack_found; - } - /* - * Fallback to rev-list with oid and the rest of the - * object IDs provided by fn. - */ - goto no_promisor_pack_found; -promisor_pack_found: - ; - } while ((oid = fn(cb_data)) != NULL); - if (opt->err_fd) - close(opt->err_fd); - return 0; + err = check_connected_promisor(fn, cb_data, &oid); + if (err) { + if (opt->err_fd) + close(opt->err_fd); + if (err > 0) + err = 0; + return err; + } } -no_promisor_pack_found: if (opt->shallow_file) { strvec_push(&rev_list.args, "--shallow-file"); strvec_push(&rev_list.args, opt->shallow_file);
diff --git a/contrib/Makefile b/contrib/Makefile index 787cd07..1203c726 100644 --- a/contrib/Makefile +++ b/contrib/Makefile
@@ -1,10 +1,22 @@ +include ../config.mak.uname +-include ../config.mak.autogen +-include ../config.mak + + +ifeq ($(uname_S),Darwin) +OS_CONTRIB += credential/osxkeychain +endif + all:: + $(foreach dir,$(OS_CONTRIB),$(MAKE) -C $(dir) $@;) test:: $(MAKE) -C diff-highlight $@ $(MAKE) -C subtree $@ + $(foreach dir,$(OS_CONTRIB),$(MAKE) -C $(dir) $@;) clean:: $(MAKE) -C contacts $@ $(MAKE) -C diff-highlight $@ $(MAKE) -C subtree $@ + $(foreach dir,$(OS_CONTRIB),$(MAKE) -C $(dir) $@;)
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile index 219b0d7..d9fba07 100644 --- a/contrib/credential/osxkeychain/Makefile +++ b/contrib/credential/osxkeychain/Makefile
@@ -10,4 +10,6 @@ clean: $(MAKE) -C ../../.. clean-git-credential-osxkeychain -.PHONY: all git-credential-osxkeychain install clean +test: git-credential-osxkeychain + +.PHONY: all git-credential-osxkeychain install clean test
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c index 73c2b9b..22eb27c 100644 --- a/contrib/credential/wincred/git-credential-wincred.c +++ b/contrib/credential/wincred/git-credential-wincred.c
@@ -121,10 +121,10 @@ static int match_part_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim) static int match_cred_password(const CREDENTIALW *cred) { int ret; - WCHAR *cred_password = xmalloc(cred->CredentialBlobSize); - wcsncpy_s(cred_password, cred->CredentialBlobSize, - (LPCWSTR)cred->CredentialBlob, - cred->CredentialBlobSize / sizeof(WCHAR)); + size_t wlen = cred->CredentialBlobSize / sizeof(WCHAR); + WCHAR *cred_password = xmalloc((wlen + 1) * sizeof(WCHAR)); + wcsncpy_s(cred_password, wlen + 1, + (LPCWSTR)cred->CredentialBlob, wlen); ret = !wcscmp(cred_password, password); free(cred_password); return ret; @@ -208,8 +208,8 @@ static void store_credential(void) if (oauth_refresh_token) { wlen = _scwprintf(L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token); - secret = xmalloc(sizeof(WCHAR) * wlen); - _snwprintf_s(secret, sizeof(WCHAR) * wlen, wlen, L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token); + secret = xmalloc((wlen + 1) * sizeof(WCHAR)); + _snwprintf_s(secret, wlen + 1, wlen, L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token); } else { secret = _wcsdup(password); }
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 4194687..c10f283 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh
@@ -99,7 +99,7 @@ } test_expect_success 'shows short help text for -h' ' - test_expect_code 129 git subtree -h >out 2>err && + git subtree -h >out 2>err && test_must_be_empty err && grep -e "^ *or: git subtree pull" out && grep -F -e "--[no-]annotate" out
diff --git a/csum-file.c b/csum-file.c index d7a682c..fe18ee1 100644 --- a/csum-file.c +++ b/csum-file.c
@@ -57,6 +57,7 @@ void hashflush(struct hashfile *f) void free_hashfile(struct hashfile *f) { + git_hash_discard(&f->ctx); free(f->buffer); free(f->check_buffer); free(f); @@ -101,15 +102,6 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result, return fd; } -void discard_hashfile(struct hashfile *f) -{ - if (0 <= f->check_fd) - close(f->check_fd); - if (0 <= f->fd) - close(f->fd); - free_hashfile(f); -} - void hashwrite(struct hashfile *f, const void *buf, uint32_t count) { while (count) { @@ -176,7 +168,7 @@ struct hashfile *hashfd_ext(const struct git_hash_algo *algop, f->skip_hash = 0; f->algop = unsafe_hash_algo(algop); - f->algop->init_fn(&f->ctx); + git_hash_init(&f->ctx, f->algop); f->buffer_len = opts->buffer_len ? opts->buffer_len : DEFAULT_IO_BUFFER_SIZE; f->buffer = xmalloc(f->buffer_len); @@ -201,7 +193,7 @@ void hashfile_checkpoint_init(struct hashfile *f, struct hashfile_checkpoint *checkpoint) { memset(checkpoint, 0, sizeof(*checkpoint)); - f->algop->init_fn(&checkpoint->ctx); + git_hash_init(&checkpoint->ctx, f->algop); } void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpoint) @@ -224,6 +216,11 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint return 0; } +void hashfile_checkpoint_release(struct hashfile_checkpoint *checkpoint) +{ + git_hash_discard(&checkpoint->ctx); +} + void crc32_begin(struct hashfile *f) { f->crc32 = crc32(0, NULL, 0); @@ -248,7 +245,7 @@ int hashfile_checksum_valid(const struct git_hash_algo *algop, if (total_len < algop->rawsz) return 0; /* say "too short"? */ - algop->init_fn(&ctx); + git_hash_init(&ctx, algop); git_hash_update(&ctx, data, data_len); git_hash_final(got, &ctx);
diff --git a/csum-file.h b/csum-file.h index a270738..6ed74d1 100644 --- a/csum-file.h +++ b/csum-file.h
@@ -39,6 +39,7 @@ struct hashfile_checkpoint { void hashfile_checkpoint_init(struct hashfile *, struct hashfile_checkpoint *); void hashfile_checkpoint(struct hashfile *, struct hashfile_checkpoint *); int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *); +void hashfile_checkpoint_release(struct hashfile_checkpoint *); /* finalize_hashfile flags */ #define CSUM_CLOSE 1 @@ -74,7 +75,6 @@ void free_hashfile(struct hashfile *f); * Finalize the hashfile by flushing data to disk and free'ing it. */ int finalize_hashfile(struct hashfile *, unsigned char *, enum fsync_component, unsigned int); -void discard_hashfile(struct hashfile *); void hashwrite(struct hashfile *, const void *, uint32_t); void hashflush(struct hashfile *f); void crc32_begin(struct hashfile *);
diff --git a/diff.c b/diff.c index 2a9d0d8..589c196 100644 --- a/diff.c +++ b/diff.c
@@ -6855,7 +6855,7 @@ void flush_one_hunk(struct object_id *result, struct git_hash_ctx *ctx) int i; git_hash_final(hash, ctx); - the_hash_algo->init_fn(ctx); + git_hash_init(ctx, the_hash_algo); /* 20-byte sum, with carry */ for (i = 0; i < the_hash_algo->rawsz; ++i) { carry += result->hash[i] + hash[i]; @@ -6899,7 +6899,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid struct git_hash_ctx ctx; struct patch_id_t data; - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); memset(&data, 0, sizeof(struct patch_id_t)); data.ctx = &ctx; oidclr(oid, the_repository->hash_algo); @@ -6987,6 +6987,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid flush_one_hunk(oid, &ctx); } + git_hash_discard(&ctx); return 0; }
diff --git a/diffcore-break.c b/diffcore-break.c index 17b5ad1..b5bcc95 100644 --- a/diffcore-break.c +++ b/diffcore-break.c
@@ -289,6 +289,8 @@ void diffcore_merge_broken(void) */ for (j = i + 1; j < q->nr; j++) { struct diff_filepair *pp = q->queue[j]; + if (!pp) + continue; if (pp->broken_pair && !strcmp(pp->one->path, pp->two->path) && !strcmp(p->one->path, pp->two->path)) {
diff --git a/dir.c b/dir.c index 3243009..1094d7b 100644 --- a/dir.c +++ b/dir.c
@@ -126,7 +126,7 @@ int count_slashes(const char *s) int git_fspathcmp(const char *a, const char *b) { - return ignore_case ? strcasecmp(a, b) : strcmp(a, b); + return repo_ignore_case(the_repository) ? strcasecmp(a, b) : strcmp(a, b); } int fspatheq(const char *a, const char *b) @@ -136,7 +136,7 @@ int fspatheq(const char *a, const char *b) int git_fspathncmp(const char *a, const char *b, size_t count) { - return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count); + return repo_ignore_case(the_repository) ? strncasecmp(a, b, count) : strncmp(a, b, count); } int paths_collide(const char *a, const char *b) @@ -153,7 +153,7 @@ int paths_collide(const char *a, const char *b) unsigned int fspathhash(const char *str) { - return ignore_case ? strihash(str) : strhash(str); + return repo_ignore_case(the_repository) ? strihash(str) : strhash(str); } int git_fnmatch(const struct pathspec_item *item, @@ -202,7 +202,7 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen, use_str = str_buf.buf; } - if (ignore_case) + if (repo_ignore_case(the_repository)) flags |= WM_CASEFOLD; match_status = wildmatch(use_pat, use_str, flags); @@ -1851,7 +1851,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir, struct index_state *istate, const char *pathname, int len) { - if (index_file_exists(istate, pathname, len, ignore_case)) + if (index_file_exists(istate, pathname, len, repo_ignore_case(the_repository))) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc); @@ -1888,7 +1888,7 @@ static enum exist_status directory_exists_in_index_icase(struct index_state *ist if (index_dir_exists(istate, dirname, len)) return index_directory; - ce = index_file_exists(istate, dirname, len, ignore_case); + ce = index_file_exists(istate, dirname, len, repo_ignore_case(the_repository)); if (ce && S_ISGITLINK(ce->ce_mode)) return index_gitdir; @@ -1907,7 +1907,7 @@ static enum exist_status directory_exists_in_index(struct index_state *istate, { int pos; - if (ignore_case) + if (repo_ignore_case(the_repository)) return directory_exists_in_index_icase(istate, dirname, len); pos = index_name_pos(istate, dirname, len); @@ -2447,7 +2447,7 @@ static enum path_treatment treat_path(struct dir_struct *dir, /* Always exclude indexed files */ has_path_in_index = !!index_file_exists(istate, path->buf, path->len, - ignore_case); + repo_ignore_case(the_repository)); if (dtype != DT_DIR && has_path_in_index) return path_none; @@ -3201,7 +3201,7 @@ static int cmp_icase(char a, char b) { if (a == b) return 0; - if (ignore_case) + if (repo_ignore_case(the_repository)) return toupper(a) - toupper(b); return a - b; } @@ -3792,13 +3792,18 @@ static int read_one_dir(struct untracked_cache_dir **untracked_, ALLOC_ARRAY(ud.untracked, ud.untracked_nr); ud.dirs_alloc = ud.dirs_nr = decode_varint(&data); - if (data > end) + if (data > end) { + free(ud.untracked); return -1; + } ALLOC_ARRAY(ud.dirs, ud.dirs_nr); eos = memchr(data, '\0', end - data); - if (!eos || eos == end) + if (!eos || eos == end) { + free(ud.untracked); + free(ud.dirs); return -1; + } *untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), eos - data, 1)); memcpy(untracked, &ud, sizeof(ud));
diff --git a/environment.c b/environment.c index ba2c601..c663113 100644 --- a/environment.c +++ b/environment.c
@@ -44,9 +44,7 @@ static int zlib_compression_seen; int trust_executable_bit = 1; int has_symlinks = 1; int minimum_abbrev = 4, default_abbrev = -1; -int ignore_case; int assume_unchanged; -int is_bare_repository_cfg = -1; /* unspecified */ char *git_commit_encoding; char *git_log_output_encoding; char *apply_default_whitespace; @@ -74,12 +72,10 @@ unsigned long pack_size_limit_cfg; #ifndef PROTECT_HFS_DEFAULT #define PROTECT_HFS_DEFAULT 0 #endif -int protect_hfs = PROTECT_HFS_DEFAULT; #ifndef PROTECT_NTFS_DEFAULT #define PROTECT_NTFS_DEFAULT 1 #endif -int protect_ntfs = PROTECT_NTFS_DEFAULT; /* * The character that begins a commented line in user-editable file @@ -92,9 +88,6 @@ int auto_comment_line_char; bool warn_on_auto_comment_char; #endif /* !WITH_BREAKING_CHANGES */ -/* This is set by setup_git_directory_gently() and/or git_default_config() */ -char *git_work_tree_cfg; - /* * Repository-local GIT_* environment variables; see environment.h for details. */ @@ -128,10 +121,31 @@ const char *getenv_safe(struct strvec *argv, const char *name) return argv->v[argv->nr - 1]; } -int is_bare_repository(void) +int is_bare_repository(struct repository *repo) { /* if core.bare is not 'false', let's see if there is a work tree */ - return is_bare_repository_cfg && !repo_get_work_tree(the_repository); + return repo->bare_cfg && !repo_get_work_tree(repo); +} + +int repo_protect_ntfs(struct repository *repo) +{ + return (repo && repo->initialized) ? + repo_config_values(repo)->protect_ntfs : + PROTECT_NTFS_DEFAULT; +} + +int repo_protect_hfs(struct repository *repo) +{ + return (repo && repo->initialized) ? + repo_config_values(repo)->protect_hfs : + PROTECT_HFS_DEFAULT; +} + +int repo_ignore_case(struct repository *repo) +{ + return (repo && repo->initialized) ? + repo_config_values(repo)->ignore_case : + 0; } int have_git_dir(void) @@ -327,7 +341,7 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.ignorecase")) { - ignore_case = git_config_bool(var, value); + cfg->ignore_case = git_config_bool(var, value); return 0; } @@ -337,7 +351,7 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.bare")) { - is_bare_repository_cfg = git_config_bool(var, value); + the_repository->bare_cfg = git_config_bool(var, value); return 0; } @@ -533,12 +547,12 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.protecthfs")) { - protect_hfs = git_config_bool(var, value); + cfg->protect_hfs = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.protectntfs")) { - protect_ntfs = git_config_bool(var, value); + cfg->protect_ntfs = git_config_bool(var, value); return 0; } @@ -716,6 +730,9 @@ void repo_config_values_init(struct repo_config_values *cfg) { cfg->attributes_file = NULL; cfg->apply_sparse_checkout = 0; + cfg->protect_hfs = PROTECT_HFS_DEFAULT; + cfg->protect_ntfs = PROTECT_NTFS_DEFAULT; + cfg->ignore_case = 0; cfg->branch_track = BRANCH_TRACK_REMOTE; cfg->trust_ctime = 1; cfg->check_stat = 1;
diff --git a/environment.h b/environment.h index 6f18286..acfb670 100644 --- a/environment.h +++ b/environment.h
@@ -98,6 +98,9 @@ struct repo_config_values { int precomposed_unicode; int core_sparse_checkout_cone; int warn_on_object_refname_ambiguity; + int protect_hfs; + int protect_ntfs; + int ignore_case; /* section "sparse" config values */ int sparse_expect_files_outside_of_patterns; @@ -133,8 +136,25 @@ int git_default_config(const char *, const char *, int git_default_core_config(const char *var, const char *value, const struct config_context *ctx, void *cb); +/* + * Getters for the `protect_hfs` and `protect_ntfs` fields of `struct repo_config_values`. + * They check `repo->initialized` to prevent calling `repo_config_values()` + * before the repository setup is fully complete or in non-git environments. + */ +int repo_protect_hfs(struct repository *repo); +int repo_protect_ntfs(struct repository *repo); + +/* + * Getter for the `ignore_case` field of `struct repo_config_values`. + * It checks `repo->initialized` to prevent calling repo_config_values()` + * before the repository setup is fully complete or in non-git environments. + */ +int repo_ignore_case(struct repository *repo); + void repo_config_values_init(struct repo_config_values *cfg); +int is_bare_repository(struct repository *repo); + /* * TODO: All the below state either explicitly or implicitly relies on * `the_repository`. We should eventually get rid of these and make the @@ -157,23 +177,15 @@ void repo_config_values_init(struct repo_config_values *cfg); */ int have_git_dir(void); -extern int is_bare_repository_cfg; -int is_bare_repository(void); -extern char *git_work_tree_cfg; - /* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int has_symlinks; extern int minimum_abbrev, default_abbrev; -extern int ignore_case; extern int assume_unchanged; extern char *apply_default_whitespace; extern char *apply_default_ignorewhitespace; extern unsigned long pack_size_limit_cfg; -extern int protect_hfs; -extern int protect_ntfs; - enum rebase_setup_type { AUTOREBASE_NEVER = 0, AUTOREBASE_LOCAL,
diff --git a/fetch-pack.c b/fetch-pack.c index 120e01f..29c4113 100644 --- a/fetch-pack.c +++ b/fetch-pack.c
@@ -662,8 +662,8 @@ static int mark_complete_oid(const struct reference *ref, void *cb_data UNUSED) static void mark_recent_complete_commits(struct fetch_pack_args *args, timestamp_t cutoff) { - while (complete.nr) { - struct commit *item = prio_queue_peek(&complete); + struct commit *item; + while ((item = prio_queue_peek(&complete))) { if (item->date < cutoff) break; print_verbose(args, _("Marking %s as complete"),
diff --git a/fsmonitor.c b/fsmonitor.c index d07dc18..1077675 100644 --- a/fsmonitor.c +++ b/fsmonitor.c
@@ -453,7 +453,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name) * case-insensitive file system, try again using the name-hash * and dir-name-hash. */ - if (!nr_in_cone && ignore_case) { + if (!nr_in_cone && repo_ignore_case(the_repository)) { nr_in_cone = handle_using_name_hash_icase(istate, name); if (!nr_in_cone) nr_in_cone = handle_using_dir_name_hash_icase(
diff --git a/git-gui/Makefile b/git-gui/Makefile index d33204e..2e1711a 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile
@@ -69,8 +69,7 @@ QUIET = @ QUIET_GEN = $(QUIET)echo ' ' GEN '$@' && QUIET_INDEX = $(QUIET)echo ' ' INDEX $(dir $@) && - QUIET_MSGFMT0 = $(QUIET)printf ' MSGFMT %12s ' $@ && v=` - QUIET_MSGFMT1 = 2>&1` && echo "$$v" | sed -e 's/fuzzy translations/fuzzy/' | sed -e 's/ messages*//g' + QUIET_MSGFMT = $(QUIET)echo ' ' MSGFMT '$@' && INSTALL_D0 = dir= INSTALL_D1 = && echo ' ' DEST $$dir && $(INSTALL) -d -m 755 "$$dir" @@ -155,7 +154,7 @@ update-po:: $(PO_TEMPLATE) $(foreach p, $(ALL_POFILES), echo Updating $p ; msgmerge -U $p $(PO_TEMPLATE) ; ) $(ALL_MSGFILES): %.msg : %.po - $(QUIET_MSGFMT0)$(MSGFMT) --statistics --tcl -l $(basename $(notdir $<)) -d $(dir $@) $< $(QUIET_MSGFMT1) + $(QUIET_MSGFMT)$(MSGFMT) --tcl -l $(basename $(notdir $<)) -d $(dir $@) $< lib/tclIndex: $(ALL_LIBFILES) generate-tclindex.sh GIT-GUI-BUILD-OPTIONS $(QUIET_INDEX)$(SHELL_PATH) generate-tclindex.sh . ./GIT-GUI-BUILD-OPTIONS $(ALL_LIBFILES)
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index 487d706..43f79ff 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl
@@ -155,7 +155,7 @@ {i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}} {i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}} {t gui.diffopts {mc "Additional Diff Parameters"}} - {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}} + {i-0..9999 gui.commitmsgwidth {mc "Commit Message Text Width"}} {t gui.newbranchtemplate {mc "New Branch Name Template"}} {c gui.encoding {mc "Default File Contents Encoding"}} {b gui.warndetachedcommit {mc "Warn before committing to a detached head"}}
diff --git a/git-gui/po/bg.po b/git-gui/po/bg.po index ff5adb2..472863d 100644 --- a/git-gui/po/bg.po +++ b/git-gui/po/bg.po
@@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: git-gui master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-22 17:37+0200\n" -"PO-Revision-Date: 2026-01-19 10:12+0100\n" +"POT-Creation-Date: 2026-06-02 19:13+0200\n" +"PO-Revision-Date: 2026-06-14 22:57+0200\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" @@ -43,18 +43,21 @@ msgid "git returned:" msgstr "git върна:" +msgid "Invalid configuration:" +msgstr "Неправилна настройка:" + +msgid "Unusable repo/worktree:" +msgstr "Неизползваемо хранилище/раб. дърво:" + msgid "Git directory not found:" msgstr "Директорията на Git не е открита:" -msgid "Cannot move to top of working directory:" -msgstr "Не може да се премине към родителската директория." +msgid "No working directory" +msgstr "Работната директория липсва" msgid "Cannot use bare repository:" msgstr "Голо хранилище не може да се използва:" -msgid "No working directory" -msgstr "Работната директория липсва" - msgid "Refreshing file status..." msgstr "Обновяване на състоянието на файла…" @@ -305,12 +308,21 @@ msgid "Usage" msgstr "Употреба" -msgid "Error" -msgstr "Грешка" +#, tcl-format +msgid "fatal: no such file '%s' in worktree" +msgstr "фатална грешка: в работното дърво липсва файл „%s“" #, tcl-format -msgid "fatal: cannot stat path %s: No such file or directory" -msgstr "ФАТАЛНА ГРЕШКА: пътят „%s“ липсва: такъв файл или директория няма" +msgid "fatal: '%s' is not a valid rev'" +msgstr "фатална грешка: „%s“ не е версия" + +#, tcl-format +msgid "'%s' is not a directory in rev '%s'" +msgstr "„%s“ не е директория във версия „%s“" + +#, tcl-format +msgid "'%s' is not a filename in rev '%s'" +msgstr "„%s“ не е име на файл във версия „%s“" msgid "Current Branch:" msgstr "Текущ клон:" @@ -461,6 +473,9 @@ msgid "Reading %s..." msgstr "Чете се „%s“…" +msgid "Error" +msgstr "Грешка" + msgid "Loading copy/move tracking annotations..." msgstr "Зареждане на анотациите за проследяване на копирането/преместването…"
diff --git a/git.c b/git.c index 36f0889..e5f1811 100644 --- a/git.c +++ b/git.c
@@ -255,7 +255,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--bare")) { char *cwd = xgetcwd(); - is_bare_repository_cfg = 1; + startup_info->force_bare_repository = true; setenv(GIT_DIR_ENVIRONMENT, cwd, 0); free(cwd); setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1); @@ -306,7 +306,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) } else if (!strcmp(cmd, "--shallow-file")) { (*argv)++; (*argc)--; - set_alternate_shallow_file(the_repository, (*argv)[0], 1); + setenv(GIT_SHALLOW_FILE_ENVIRONMENT, (*argv)[0], 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "-C")) {
diff --git a/gitk-git/Makefile b/gitk-git/Makefile index 41116d8..dd87f50 100644 --- a/gitk-git/Makefile +++ b/gitk-git/Makefile
@@ -43,9 +43,12 @@ ALL_POFILES = $(wildcard po/*.po) ALL_MSGFILES = $(subst .po,.msg,$(ALL_POFILES)) +ifneq ($(findstring s,$(firstword -$(MAKEFLAGS))),s) ifndef V QUIET = @ QUIET_GEN = $(QUIET)echo ' ' GEN $@ && + QUIET_MSGFMT = $(QUIET)echo ' ' MSGFMT $@ && +endif endif all:: gitk-wish $(ALL_MSGFILES) @@ -75,8 +78,7 @@ echo; \ echo " git config filter.gettext-no-location.clean \"msgcat --no-location -\"" $(ALL_MSGFILES): %.msg : %.po - @echo Generating catalog $@ - $(MSGFMT) --statistics --tcl -l $(basename $(notdir $<)) -d $(dir $@) $< + $(QUIET_MSGFMT)$(MSGFMT) --tcl -l $(basename $(notdir $<)) -d $(dir $@) $< .PHONY: all install uninstall clean update-po .PHONY: FORCE
diff --git a/gitk-git/po/bg.po b/gitk-git/po/bg.po index e7e2f87..3521fdb 100644 --- a/gitk-git/po/bg.po +++ b/gitk-git/po/bg.po
@@ -1,15 +1,15 @@ # Bulgarian translation of gitk po-file. -# Copyright (C) 2014, 2015, 2019, 2020, 2024, 2025 Alexander Shopov <ash@kambanaria.org>. +# Copyright (C) 2014, 2015, 2019, 2020, 2024, 2025, 2026 Alexander Shopov <ash@kambanaria.org>. # This file is distributed under the same license as the git package. -# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2019, 2020, 2024, 2025. +# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2019, 2020, 2024, 2025, 2026. # # msgid "" msgstr "" "Project-Id-Version: Gitk master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-22 18:34+0200\n" -"PO-Revision-Date: 2025-07-28 13:38+0200\n" +"POT-Creation-Date: 2026-04-18 17:41+0200\n" +"PO-Revision-Date: 2026-06-14 22:45+0200\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" @@ -183,10 +183,10 @@ msgstr "Нова версия" msgid "Lines of context" -msgstr "Контекст в редове" +msgstr "Редове контекст" msgid "Ignore space change" -msgstr "Празните знаци без значение" +msgstr "Празни знаци без значение" msgid "Line diff" msgstr "Поредови разлики" @@ -954,6 +954,9 @@ msgid "Hide remote refs" msgstr "Скриване на отдалечените указатели" +msgid "Refs to hide (space-separated globs)" +msgstr "Указатели за скриване (шаблони разделени с интервал):" + msgid "Copy commit ID to clipboard" msgstr "Копиране на контролната сума към буфера за обмен" @@ -999,15 +1002,24 @@ msgid "Web browser" msgstr "Уеб браузър" +msgid "Themes - change requires restart" +msgstr "Теми — за прилагане трябва да рестартирате програмата" + +msgid "Theme to use after restart" +msgstr "Тема за прилагане след рестартиране на програмата" + +msgid "Theme definition file" +msgstr "Файл с тема" + +msgid "The theme definition file may affect all themes." +msgstr "Файлът с тема може да повлияе на всички теми." + +msgid "Apply theme" +msgstr "Прилагане на тема" + msgid "Colors: press to choose" msgstr "Цветове: избира се с натискане" -msgid "Interface" -msgstr "Интерфейс" - -msgid "interface" -msgstr "интерфейс" - msgid "Background" msgstr "Фон" @@ -1059,6 +1071,12 @@ msgid "Select bg" msgstr "Избор на фон" +msgid "Link" +msgstr "Връзка" + +msgid "link" +msgstr "връзка" + msgid "Fonts: press to choose" msgstr "Шрифтове: избира се с натискане" @@ -1083,6 +1101,9 @@ msgid "Fonts" msgstr "Шрифтове" +msgid "Gitk: select theme definition" +msgstr "Gitk: избор на тема" + #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: избор на цвят на „%s“"
diff --git a/gitk-git/po/es.po b/gitk-git/po/es.po index 25260c6..881738e 100644 --- a/gitk-git/po/es.po +++ b/gitk-git/po/es.po
@@ -8,30 +8,30 @@ msgstr "" "Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-05-17 14:32+1000\n" -"PO-Revision-Date: 2008-03-25 11:20+0100\n" -"Last-Translator: Santiago Gala <santiago.gala@gmail.com>\n" +"POT-Creation-Date: 2026-04-18 18:45+0200\n" +"PO-Revision-Date: 2026-04-19 20:18+0200\n" +"Last-Translator: Luis B <basuradeluis@gmail.com>\n" "Language-Team: Spanish\n" -"Language: \n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.2\n" msgid "Couldn't get list of unmerged files:" msgstr "Imposible obtener la lista de archivos pendientes de fusión:" msgid "Color words" -msgstr "" +msgstr "Colorear palabras" msgid "Markup words" -msgstr "" +msgstr "Marcar cambios por palabras" -#, fuzzy msgid "Error parsing revisions:" -msgstr "Error al leer las diferencias de fusión:" +msgstr "Error procesando revisiones:" msgid "Error executing --argscmd command:" -msgstr "" +msgstr "Error ejecutando comando --argscmd:" msgid "No files selected: --merge specified but no files are unmerged." msgstr "" @@ -45,9 +45,8 @@ "No hay archivos seleccionados: se seleccionó la opción --merge pero los " "archivos especificados no necesitan fusión." -#, fuzzy msgid "Error executing git log:" -msgstr "Error al crear la etiqueta:" +msgstr "Error ejecutando git log:" msgid "Reading" msgstr "Leyendo" @@ -74,19 +73,19 @@ msgstr "Cancelar" msgid "&Update" -msgstr "Actualizar" +msgstr "Act&ualizar" msgid "&Reload" -msgstr "" +msgstr "&Recargar" msgid "Reread re&ferences" -msgstr "Releer referencias" +msgstr "Releer re&ferencias" msgid "&List references" -msgstr "Lista de referencias" +msgstr "Mostrar &lista de referencias" msgid "Start git &gui" -msgstr "" +msgstr "Ejecutar 'git &gui'" msgid "&Quit" msgstr "Salir" @@ -124,11 +123,11 @@ msgid "&Help" msgstr "Ayuda" -msgid "SHA1 ID:" -msgstr "SHA1 ID:" +msgid "Commit ID:" +msgstr "Commit ID:" msgid "Row" -msgstr "" +msgstr "Fila" msgid "Find" msgstr "Buscar" @@ -146,7 +145,7 @@ msgstr "que añade/elimina cadena:" msgid "changing lines matching:" -msgstr "" +msgstr "que cambia líneas que coinciden con:" msgid "Exact" msgstr "Exacto" @@ -170,7 +169,7 @@ msgstr "Autor" msgid "Committer" -msgstr "" +msgstr "Comiteador" msgid "Search" msgstr "Buscar" @@ -188,10 +187,10 @@ msgstr "Líneas de contexto" msgid "Ignore space change" -msgstr "Ignora cambios de espaciado" +msgstr "Ignorar cambios de espaciado" msgid "Line diff" -msgstr "" +msgstr "Diferencia de líneas" msgid "Patch" msgstr "Parche" @@ -199,6 +198,9 @@ msgid "Tree" msgstr "Árbol" +msgid "Unknown windowing system, cannot bind mouse" +msgstr "Sistema de ventanas desconocido, no se puede asociar el ratón" + msgid "Diff this -> selected" msgstr "Diferencia de esta -> seleccionada" @@ -206,11 +208,14 @@ msgstr "Diferencia de seleccionada -> esta" msgid "Make patch" -msgstr "Crear patch" +msgstr "Crear parche" msgid "Create tag" msgstr "Crear etiqueta" +msgid "Copy commit reference" +msgstr "Copiar referencia de la revisión" + msgid "Write commit to file" msgstr "Escribir revisiones a archivo" @@ -223,39 +228,38 @@ msgid "Reset HEAD branch to here" msgstr "Traer la rama HEAD aquí" -#, fuzzy msgid "Mark this commit" -msgstr "Añadir esta revisión a la rama actual (cherry-pick)" +msgstr "Marcar esta revisión" msgid "Return to mark" -msgstr "" +msgstr "Volver a revisión marcada" msgid "Find descendant of this and mark" -msgstr "" +msgstr "Encontrar primer descendiente de esta y revisión marcada" msgid "Compare with marked commit" -msgstr "" +msgstr "Comparar con revisión marcada" -#, fuzzy msgid "Diff this -> marked commit" -msgstr "Diferencia de esta -> seleccionada" +msgstr "Diferencia de esta -> revisión marcada" -#, fuzzy msgid "Diff marked commit -> this" -msgstr "Diferencia de seleccionada -> esta" +msgstr "Diferencia de revisión marcada -> esta" -#, fuzzy msgid "Revert this commit" -msgstr "Añadir esta revisión a la rama actual (cherry-pick)" +msgstr "Revertir este commit" msgid "Check out this branch" msgstr "Cambiar a esta rama" +msgid "Rename this branch" +msgstr "Renombrar esta rama" + msgid "Remove this branch" msgstr "Eliminar esta rama" msgid "Copy branch name" -msgstr "" +msgstr "Copiar nombre de rama" msgid "Highlight this too" msgstr "Seleccionar también" @@ -264,21 +268,23 @@ msgstr "Seleccionar sólo" msgid "External diff" -msgstr "" +msgstr "Comparador de diferencias externo" msgid "Blame parent commit" -msgstr "" +msgstr "Ejecutar git blame en el commit padre" msgid "Copy path" -msgstr "" +msgstr "Copiar ruta" msgid "Show origin of this line" -msgstr "" +msgstr "Mostrar origen de esta línea" msgid "Run git gui blame on this line" -msgstr "" +msgstr "Ejecutar git gui blame en esta línea" -#, fuzzy +msgid "About gitk" +msgstr "Acerca de gitk" + msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -290,7 +296,7 @@ "\n" "Gitk - un visualizador de revisiones para git\n" "\n" -"Copyright \\u00a9 2005-2016 Paul Mackerras\n" +"Copyright © 2005-2016 Paul Mackerras\n" "\n" "Uso y redistribución permitidos según los términos de la Licencia Pública " "General de GNU (GNU GPL)" @@ -308,9 +314,9 @@ msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tSalir" -#, fuzzy, tcl-format +#, tcl-format msgid "<%s-W>\t\tClose window" -msgstr "<%s-F>\t\tBuscar" +msgstr "<%s-W>\t\tCerrar ventana" msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tIr a la primera revisión" @@ -318,24 +324,21 @@ msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tIr a la última revisión" -#, fuzzy msgid "<Up>, p, k\tMove up one commit" -msgstr "<Up>, p, i\tSubir una revisión" +msgstr "<Up>, p, k\tSubir una revisión" -#, fuzzy msgid "<Down>, n, j\tMove down one commit" -msgstr "<Down>, n, k\tBajar una revisión" +msgstr "<Down>, n, j\tBajar una revisión" -#, fuzzy msgid "<Left>, z, h\tGo back in history list" -msgstr "<Left>, z, j\tRetroceder en la historia" +msgstr "<Left>, z, h\tRetroceder en la historia" msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\tAvanzar en la historia" #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" -msgstr "" +msgstr "<%s-n>\tIr al padre número N del commit actual" msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tSubir una página en la lista de revisiones" @@ -399,12 +402,11 @@ msgid "<Return>\tMove to next find hit" msgstr "<Return>\tBuscar el siguiente" -#, fuzzy msgid "g\t\tGo to commit" -msgstr "<End>\t\tIr a la última revisión" +msgstr "g\t\tIr a la revisión" msgid "/\t\tFocus the search box" -msgstr "" +msgstr "/\t\tFoco en el cuadro de búsqueda" msgid "?\t\tMove to previous find hit" msgstr "?\t\tBuscar el anterior" @@ -439,51 +441,54 @@ msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tActualizar" -#, fuzzy, tcl-format +#, tcl-format msgid "Error creating temporary directory %s:" -msgstr "Error en la creación del parche:" +msgstr "Error creando directorio temporal %s:" -#, fuzzy, tcl-format +#, tcl-format msgid "Error getting \"%s\" from %s:" -msgstr "Error al leer las diferencias de fusión:" +msgstr "Error obteniendo \"%s\" de %s:" -#, fuzzy msgid "command failed:" -msgstr "Línea de comandos" +msgstr "el comando falló:" -#, fuzzy msgid "No such commit" -msgstr "No se han guardado cambios" +msgstr "No existe el commit" msgid "git gui blame: command failed:" -msgstr "" +msgstr "git gui blame: error de ejecución:" #, tcl-format msgid "Couldn't read merge head: %s" -msgstr "" +msgstr "No se pudo leer merge head: %s" -#, fuzzy, tcl-format +#, tcl-format msgid "Error reading index: %s" -msgstr "Error al crear la etiqueta:" +msgstr "Error leyendo índice:%s" #, tcl-format msgid "Couldn't start git blame: %s" -msgstr "" +msgstr "No se pudo iniciar git blame: %s" msgid "Searching" msgstr "Buscando" -#, fuzzy, tcl-format +#, tcl-format msgid "Error running git blame: %s" -msgstr "Error al crear la etiqueta:" +msgstr "Error al ejecutar git blame: %s" #, tcl-format msgid "That line comes from commit %s, which is not in this view" -msgstr "" +msgstr "Esa línea proviene del commit %s, que no está en esta vista" -#, fuzzy msgid "External diff viewer failed:" -msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente" +msgstr "Visor de diferencias externo falló:" + +msgid "All files" +msgstr "Todos los archivos" + +msgid "View" +msgstr "Vista" msgid "Gitk view definition" msgstr "Definición de vistas de Gitk" @@ -492,96 +497,93 @@ msgstr "Recordar esta vista" msgid "References (space separated list):" -msgstr "" +msgstr "Referencias (lista separada por espacios):" msgid "Branches & tags:" -msgstr "" +msgstr "Ramas y etiquetas:" -#, fuzzy msgid "All refs" -msgstr "Todos los archivos" +msgstr "Todas las referencias" msgid "All (local) branches" -msgstr "" +msgstr "Todas las ramas (locales)" msgid "All tags" -msgstr "" +msgstr "Todas las etiquetas" msgid "All remote-tracking branches" -msgstr "" +msgstr "Todas las ramas remotas" msgid "Commit Info (regular expressions):" -msgstr "" +msgstr "Información del commit (expresiones regulares)" -#, fuzzy msgid "Author:" -msgstr "Autor" +msgstr "Autor:" -#, fuzzy msgid "Committer:" -msgstr "revisión" +msgstr "Comiteador:" msgid "Commit Message:" -msgstr "" +msgstr "Mensaje del commit:" msgid "Matches all Commit Info criteria" -msgstr "" +msgstr "Cumple todos los criterios de 'Información del commit'" msgid "Matches no Commit Info criteria" -msgstr "" +msgstr "Cumple ningún criterio de 'Información del commit'" msgid "Changes to Files:" -msgstr "" +msgstr "Cambios en ficheros:" msgid "Fixed String" -msgstr "" +msgstr "Texto exacto" msgid "Regular Expression" -msgstr "" +msgstr "Expresión regular" -#, fuzzy msgid "Search string:" -msgstr "Buscando" +msgstr "Buscar cadena:" +# Notice git log since and until format msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" msgstr "" +"Fechas del commit (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, " +"2009 15:27:38\"):" msgid "Since:" -msgstr "" +msgstr "Desde:" msgid "Until:" -msgstr "" +msgstr "Hasta:" msgid "Limit and/or skip a number of revisions (positive integer):" -msgstr "" +msgstr "Limitar y/o saltar las siguientes revisiones (número positivo)" msgid "Number to show:" -msgstr "" +msgstr "Limitar a n revisiones:" msgid "Number to skip:" -msgstr "" +msgstr "Saltar n revisiones:" msgid "Miscellaneous options:" -msgstr "" +msgstr "Opciones varias:" msgid "Strictly sort by date" -msgstr "" +msgstr "Ordenar estrictamente por fecha" msgid "Mark branch sides" -msgstr "" +msgstr "Marcar lados de ramas" -#, fuzzy msgid "Limit to first parent" -msgstr "Limitar las diferencias a las rutas seleccionadas" +msgstr "Limitar al primer padre" msgid "Simple history" -msgstr "" +msgstr "Historia simple" -#, fuzzy msgid "Additional arguments to git log:" -msgstr "Revisiones a incluir (argumentos a git log):" +msgstr "Argumentos extra a git log:" msgid "Enter files and directories to include, one per line:" msgstr "Introducir archivos y directorios a incluir, uno por línea:" @@ -590,17 +592,16 @@ msgstr "Comando que genera más revisiones a incluir:" msgid "Gitk: edit view" -msgstr "" +msgstr "Gitk: editar vista" msgid "-- criteria for selecting revisions" -msgstr "" +msgstr "-- criterios para filtrar revisiones" -#, fuzzy msgid "View Name" -msgstr "Vista" +msgstr "Nombre de la vista" msgid "Apply (F5)" -msgstr "" +msgstr "Aplicar (F5)" msgid "Error in commit selection arguments:" msgstr "Error en los argumentos de selección de las revisiones:" @@ -626,11 +627,14 @@ msgid "Local uncommitted changes, not checked in to index" msgstr "Cambios locales sin añadir al índice" +msgid "Error starting web browser:" +msgstr "Error al arrancar navegador web:" + msgid "and many more" -msgstr "" +msgstr "y muchos más" msgid "many" -msgstr "" +msgstr "muchos" msgid "Tags:" msgstr "Etiquetas:" @@ -650,28 +654,28 @@ msgid "Precedes" msgstr "Precede-a" -#, fuzzy, tcl-format +#, tcl-format msgid "Error getting diffs: %s" -msgstr "Error al leer las diferencias de fusión:" +msgstr "Error al leer las diferencias: %s" msgid "Goto:" msgstr "Ir a:" #, tcl-format -msgid "Short SHA1 id %s is ambiguous" -msgstr "La id SHA1 abreviada %s es ambigua" - -#, fuzzy, tcl-format -msgid "Revision %s is not known" -msgstr "La id SHA1 %s es desconocida" +msgid "Short commit ID %s is ambiguous" +msgstr "El id SHA1 abreviado %s es ambiguo" #, tcl-format -msgid "SHA1 id %s is not known" -msgstr "La id SHA1 %s es desconocida" +msgid "Revision %s is not known" +msgstr "Revisión %s es desconocida" + +#, tcl-format +msgid "Commit ID %s is not known" +msgstr "El id del commit %s es desconocido" #, tcl-format msgid "Revision %s is not in the current view" -msgstr "" +msgstr "La revisión %s no está en la vista actual" msgid "Date" msgstr "Fecha" @@ -681,51 +685,55 @@ #, tcl-format msgid "Reset %s branch to here" -msgstr "Poner la rama %s en esta revisión" +msgstr "Resetear la rama %s en esta revisión" msgid "Detached head: can't reset" -msgstr "" +msgstr "En estado 'detached head': no se puede resetear la rama" msgid "Skipping merge commit " -msgstr "" +msgstr "Omitiendo el commit de merge " -#, fuzzy msgid "Error getting patch ID for " -msgstr "Error en la creación del parche:" +msgstr "Error obteniendo ID del parche para " msgid " - stopping\n" -msgstr "" +msgstr " - deteniendo\n" -#, fuzzy msgid "Commit " -msgstr "revisión" +msgstr "Revisión " msgid "" " is the same patch as\n" " " msgstr "" +" es el mismo parche que\n" +" " msgid "" " differs from\n" " " msgstr "" +" difiere de\n" +" " msgid "" "Diff of commits:\n" "\n" msgstr "" +"Diferencia de commits:\n" +"\n" #, tcl-format msgid " has %s children - stopping\n" -msgstr "" +msgstr " tiene %s hijos - parando\n" -#, fuzzy, tcl-format +#, tcl-format msgid "Error writing commit to file: %s" -msgstr "Error al escribir revisión:" +msgstr "Error al escribir revisión al fichero: %s" -#, fuzzy, tcl-format +#, tcl-format msgid "Error diffing commits: %s" -msgstr "Error al escribir revisión:" +msgstr "Error comparando revisiones: %s" msgid "Top" msgstr "Origen" @@ -764,11 +772,10 @@ msgstr "Nombre de etiqueta:" msgid "Tag message is optional" -msgstr "" +msgstr "El mensaje de la etiqueta es opcional" -#, fuzzy msgid "Tag message:" -msgstr "Nombre de etiqueta:" +msgstr "Mensaje de la etiqueta:" msgid "Create" msgstr "Crear" @@ -792,15 +799,28 @@ msgid "Error writing commit:" msgstr "Error al escribir revisión:" +msgid "Create branch" +msgstr "Crear nueva rama" + +#, tcl-format +msgid "Rename branch %s" +msgstr "Renombrar rama '%s'" + +msgid "Rename" +msgstr "Renombrar" + msgid "Name:" msgstr "Nombre:" msgid "Please specify a name for the new branch" msgstr "Especifique un nombre para la nueva rama" -#, fuzzy, tcl-format +#, tcl-format msgid "Branch '%s' already exists. Overwrite?" -msgstr "La etiqueta \"%s\" ya existe" +msgstr "La rama '%s' ya existe. ¿Sobreescribir?" + +msgid "Please specify a new name for the branch" +msgstr "Especifique un nuevo nombre para la rama" #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" @@ -814,33 +834,42 @@ "Cherry-pick failed because of local changes to file '%s'.\n" "Please commit, reset or stash your changes and try again." msgstr "" +"El comando cherry-pick falló por los cambios locales al fichero '%s'.\n" +"Por favor, comiteé, reseteé o use stash con sus cambios y pruebe de nuevo" msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" msgstr "" +"El comando cherry-pick falló debido a un conflicto de fusión.\n" +"¿Desea ejecutar git citool para resolverlo?" msgid "No changes committed" msgstr "No se han guardado cambios" -#, fuzzy, tcl-format +#, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" -msgstr "La revisión %s ya está incluida en la rama %s -- ¿Volver a aplicarla?" +msgstr "" +"La revisión %s no está incluida en la rama %s -- ¿seguro que quiere " +"revertirla?" -#, fuzzy msgid "Reverting" -msgstr "Reponiendo" +msgstr "Revirtiendo" #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." msgstr "" +"El comando revert falló por los cambios locales a estos ficheros:%s Por " +"favor, comitéelos, reseteé o use stash y pruebe otra vez" msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" msgstr "" +"Revert falló por un conflicto de fusión.\n" +"¿Quiere ejecutar git citool para resolverlo?" msgid "Confirm reset" msgstr "Confirmar git reset" @@ -868,6 +897,10 @@ msgid "Resetting" msgstr "Reponiendo" +#, tcl-format +msgid "A local branch named %s exists already" +msgstr "Ya existe una rama local llamada '%s'" + msgid "Checking out" msgstr "Creando copia de trabajo" @@ -889,6 +922,9 @@ msgid "Filter" msgstr "Filtro" +msgid "Sort refs by type" +msgstr "Ordenar referencias por tipo" + msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -902,15 +938,6 @@ msgid "Id" msgstr "Id" -msgid "Gitk font chooser" -msgstr "Selector de tipografías gitk" - -msgid "B" -msgstr "B" - -msgid "I" -msgstr "I" - msgid "Commit list display options" msgstr "Opciones de visualización de la lista de revisiones" @@ -924,12 +951,23 @@ msgid "Show local changes" msgstr "Mostrar cambios locales" -#, fuzzy -msgid "Auto-select SHA1 (length)" -msgstr "Seleccionar automáticamente SHA1 hash" - msgid "Hide remote refs" -msgstr "" +msgstr "Esconder referencias remotas" + +msgid "Refs to hide (space-separated globs)" +msgstr "Referencias a esconder (globs separados por espacios)" + +msgid "Copy commit ID to clipboard" +msgstr "Copiar id de la revisión al portapapeles" + +msgid "Copy commit ID to X11 selection" +msgstr "Copiar id de la revisión a selección X11" + +msgid "Length of commit ID to copy" +msgstr "Longitud del ID de la revisión al copiar" + +msgid "Wheel scrolling multiplier" +msgstr "Multiplicador desplazamiento rueda del ratón" msgid "Diff display options" msgstr "Opciones de visualización de diferencias" @@ -937,92 +975,108 @@ msgid "Tab spacing" msgstr "Espaciado de tabulador" -#, fuzzy +msgid "Wrap comment text" +msgstr "Ajustar texto de comentarios" + +msgid "Wrap other text" +msgstr "Ajustar otros textos" + msgid "Display nearby tags/heads" -msgstr "Mostrar etiquetas cercanas" +msgstr "Mostrar etiquetas/referencias cercanas" msgid "Maximum # tags/heads to show" -msgstr "" +msgstr "Máximo número de etiquetas/" msgid "Limit diffs to listed paths" msgstr "Limitar las diferencias a las rutas seleccionadas" msgid "Support per-file encodings" -msgstr "" +msgstr "Soporte para codificaciones por archivo" msgid "External diff tool" -msgstr "" +msgstr "Herramienta de comparación externa" msgid "Choose..." -msgstr "" +msgstr "Escoger..." -#, fuzzy -msgid "General options" -msgstr "Generar parche" +msgid "Web browser" +msgstr "Navegador web" -msgid "Use themed widgets" -msgstr "" +msgid "Themes - change requires restart" +msgstr "Temas - los cambios requieren reinicio" -msgid "(change requires restart)" -msgstr "" +msgid "Theme to use after restart" +msgstr "Tema a usar tras reiniciar" -msgid "(currently unavailable)" -msgstr "" +msgid "Theme definition file" +msgstr "Fichero de definición del tema" + +msgid "The theme definition file may affect all themes." +msgstr "El fichero de definición del tema puede afectar a todos los temas." + +msgid "Apply theme" +msgstr "Aplicar tema" msgid "Colors: press to choose" msgstr "Colores: pulse para seleccionar" -msgid "Interface" -msgstr "" - -#, fuzzy -msgid "interface" -msgstr "Tipografía para interfaz de usuario" - msgid "Background" msgstr "Fondo" -#, fuzzy msgid "background" -msgstr "Fondo" +msgstr "fondo" msgid "Foreground" msgstr "Primer plano" -#, fuzzy msgid "foreground" -msgstr "Primer plano" +msgstr "primer plano" msgid "Diff: old lines" -msgstr "Diff: líneas viejas" +msgstr "Diff: líneas antiguas" -#, fuzzy msgid "diff old lines" -msgstr "Diff: líneas viejas" +msgstr "diff líneas antiguas" + +msgid "Diff: old lines bg" +msgstr "Diff: fondo de líneas antiguas" + +msgid "diff old lines bg" +msgstr "diff fondo de líneas antiguas" msgid "Diff: new lines" msgstr "Diff: líneas nuevas" -#, fuzzy msgid "diff new lines" -msgstr "Diff: líneas nuevas" +msgstr "diff líneas nuevas" + +msgid "Diff: new lines bg" +msgstr "Diff: fondo de líneas nuevas" + +msgid "diff new lines bg" +msgstr "diff fondo de líneas nuevas" msgid "Diff: hunk header" msgstr "Diff: cabecera de fragmento" -#, fuzzy msgid "diff hunk header" -msgstr "Diff: cabecera de fragmento" +msgstr "diff cabecera de fragmento" msgid "Marked line bg" -msgstr "" +msgstr "Fondo de línea marcada" msgid "marked line background" -msgstr "" +msgstr "fondo de linea marcada" msgid "Select bg" msgstr "Color de fondo de la selección" +msgid "Link" +msgstr "Enlace" + +msgid "link" +msgstr "enlace" + msgid "Fonts: press to choose" msgstr "Tipografías: pulse para elegir" @@ -1038,27 +1092,22 @@ msgid "Gitk preferences" msgstr "Preferencias de gitk" -#, fuzzy msgid "General" -msgstr "Generar" +msgstr "General" msgid "Colors" -msgstr "" +msgstr "Colores" msgid "Fonts" -msgstr "" +msgstr "Fuentes" + +msgid "Gitk: select theme definition" +msgstr "Gitk: escoger definición de tema" #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: elegir color para %s" -msgid "" -"Sorry, gitk cannot run with this version of Tcl/Tk.\n" -" Gitk requires at least Tcl/Tk 8.4." -msgstr "" -"Esta versión de Tcl/Tk es demasiado antigua.\n" -" Gitk requiere Tcl/Tk versión 8.4 o superior." - msgid "Cannot find a git repository here." msgstr "No hay un repositorio git aquí." @@ -1070,6 +1119,37 @@ msgid "Bad arguments to gitk:" msgstr "Argumentos incorrectos a Gitk:" +#~ msgid "SHA1 ID:" +#~ msgstr "SHA1 ID:" + +#~ msgid "Gitk font chooser" +#~ msgstr "Selector de tipografías gitk" + +#~ msgid "B" +#~ msgstr "B" + +#~ msgid "I" +#~ msgstr "I" + +#, fuzzy +#~ msgid "Auto-select SHA1 (length)" +#~ msgstr "Seleccionar automáticamente SHA1 hash" + +#, fuzzy +#~ msgid "General options" +#~ msgstr "Generar parche" + +#, fuzzy +#~ msgid "interface" +#~ msgstr "Tipografía para interfaz de usuario" + +#~ msgid "" +#~ "Sorry, gitk cannot run with this version of Tcl/Tk.\n" +#~ " Gitk requires at least Tcl/Tk 8.4." +#~ msgstr "" +#~ "Esta versión de Tcl/Tk es demasiado antigua.\n" +#~ " Gitk requiere Tcl/Tk versión 8.4 o superior." + #~ msgid "SHA1 ID: " #~ msgstr "SHA1 ID: "
diff --git a/gpg-interface.c b/gpg-interface.c index dafd537..95abf1e 100644 --- a/gpg-interface.c +++ b/gpg-interface.c
@@ -990,21 +990,18 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, return ret; } -/* - * Strip CR from the line endings, in case we are on Windows. - * NEEDSWORK: make it trim only CRs before LFs and rename - */ -static void remove_cr_after(struct strbuf *buffer, size_t offset) +/* Strip CR before LF from the line endings, in case we are on Windows. */ +static void strip_cr_before_lf(struct strbuf *buffer, size_t offset) { size_t i, j; for (i = j = offset; i < buffer->len; i++) { - if (buffer->buf[i] != '\r') { - if (i != j) - buffer->buf[j] = buffer->buf[i]; - j++; - } + if (buffer->buf[i] == '\r' && + i + 1 < buffer->len && buffer->buf[i + 1] == '\n') + continue; + buffer->buf[j++] = buffer->buf[i]; } + strbuf_setlen(buffer, j); } @@ -1049,8 +1046,8 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature, } strbuf_release(&gpg_status); - /* Strip CR from the line endings, in case we are on Windows. */ - remove_cr_after(signature, bottom); + /* Strip CR before LF from the line endings, in case we are on Windows. */ + strip_cr_before_lf(signature, bottom); return 0; } @@ -1136,8 +1133,8 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature, ssh_signature_filename.buf); goto out; } - /* Strip CR from the line endings, in case we are on Windows. */ - remove_cr_after(signature, bottom); + /* Strip CR before LF from the line endings, in case we are on Windows. */ + strip_cr_before_lf(signature, bottom); out: if (key_file)
diff --git a/hash.c b/hash.c index e925b97..82f7e24 100644 --- a/hash.c +++ b/hash.c
@@ -72,6 +72,11 @@ static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx * oid->algo = GIT_HASH_SHA1; } +static void git_hash_sha1_discard(struct git_hash_ctx *ctx) +{ + git_SHA1_Discard(&ctx->state.sha1); +} + static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx) { ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]); @@ -102,6 +107,11 @@ static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_has oid->algo = GIT_HASH_SHA1; } +static void git_hash_sha1_discard_unsafe(struct git_hash_ctx *ctx) +{ + git_SHA1_Discard_unsafe(&ctx->state.sha1_unsafe); +} + static void git_hash_sha256_init(struct git_hash_ctx *ctx) { ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]); @@ -135,6 +145,11 @@ static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx oid->algo = GIT_HASH_SHA256; } +static void git_hash_sha256_discard(struct git_hash_ctx *ctx) +{ + git_SHA256_Discard(&ctx->state.sha256); +} + static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED) { BUG("trying to init unknown hash"); @@ -165,6 +180,11 @@ static void git_hash_unknown_final_oid(struct object_id *oid UNUSED, BUG("trying to finalize unknown hash"); } +static void git_hash_unknown_discard(struct git_hash_ctx *ctx UNUSED) +{ + BUG("trying to discard unknown hash"); +} + static const struct git_hash_algo sha1_unsafe_algo = { .name = "sha1", .format_id = GIT_SHA1_FORMAT_ID, @@ -176,6 +196,7 @@ static const struct git_hash_algo sha1_unsafe_algo = { .update_fn = git_hash_sha1_update_unsafe, .final_fn = git_hash_sha1_final_unsafe, .final_oid_fn = git_hash_sha1_final_oid_unsafe, + .discard_fn = git_hash_sha1_discard_unsafe, .empty_tree = &empty_tree_oid, .empty_blob = &empty_blob_oid, .null_oid = &null_oid_sha1, @@ -193,6 +214,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { .update_fn = git_hash_unknown_update, .final_fn = git_hash_unknown_final, .final_oid_fn = git_hash_unknown_final_oid, + .discard_fn = git_hash_unknown_discard, .empty_tree = NULL, .empty_blob = NULL, .null_oid = NULL, @@ -208,6 +230,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { .update_fn = git_hash_sha1_update, .final_fn = git_hash_sha1_final, .final_oid_fn = git_hash_sha1_final_oid, + .discard_fn = git_hash_sha1_discard, .unsafe = &sha1_unsafe_algo, .empty_tree = &empty_tree_oid, .empty_blob = &empty_blob_oid, @@ -224,6 +247,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { .update_fn = git_hash_sha256_update, .final_fn = git_hash_sha256_final, .final_oid_fn = git_hash_sha256_final_oid, + .discard_fn = git_hash_sha256_discard, .empty_tree = &empty_tree_oid_sha256, .empty_blob = &empty_blob_oid_sha256, .null_oid = &null_oid_sha256, @@ -261,26 +285,47 @@ void git_hash_free(struct git_hash_ctx *ctx) void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop) { algop->init_fn(ctx); + ctx->active = true; } void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src) { + if (!src->active) + BUG("attempt to copy from an inactive hash context"); + if (!dst->active) + BUG("attempt to copy to an inactive hash context"); src->algop->clone_fn(dst, src); } void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len) { + if (!ctx->active) + BUG("attempt to update an inactive hash context"); ctx->algop->update_fn(ctx, in, len); } void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx) { + if (!ctx->active) + BUG("attempt to finalize an inactive hash context"); ctx->algop->final_fn(hash, ctx); + ctx->active = false; } void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx) { + if (!ctx->active) + BUG("attempt to finalize an inactive hash context"); ctx->algop->final_oid_fn(oid, ctx); + ctx->active = false; +} + +void git_hash_discard(struct git_hash_ctx *ctx) +{ + if (!ctx->active) + return; + ctx->algop->discard_fn(ctx); + ctx->active = false; } uint32_t hash_algo_by_name(const char *name)
diff --git a/hash.h b/hash.h index c082a53..cf94ad5 100644 --- a/hash.h +++ b/hash.h
@@ -37,6 +37,7 @@ # define platform_SHA1_Clone_unsafe openssl_SHA1_Clone # define platform_SHA1_Update_unsafe openssl_SHA1_Update # define platform_SHA1_Final_unsafe openssl_SHA1_Final +# define platform_SHA1_Discard_unsafe openssl_SHA1_Discard # else # define platform_SHA_CTX_unsafe SHA_CTX # define platform_SHA1_Init_unsafe SHA1_Init @@ -92,6 +93,7 @@ # define platform_SHA1_Final_unsafe platform_SHA1_Final # ifdef platform_SHA1_Clone # define platform_SHA1_Clone_unsafe platform_SHA1_Clone +# define platform_SHA1_Discard_unsafe platform_SHA1_Discard # endif # ifdef SHA1_NEEDS_CLONE_HELPER # define SHA1_NEEDS_CLONE_HELPER_UNSAFE @@ -110,9 +112,11 @@ #ifdef platform_SHA1_Clone #define git_SHA1_Clone platform_SHA1_Clone +#define git_SHA1_Discard platform_SHA1_Discard #endif #ifdef platform_SHA1_Clone_unsafe # define git_SHA1_Clone_unsafe platform_SHA1_Clone_unsafe +# define git_SHA1_Discard_unsafe platform_SHA1_Discard_unsafe #endif #ifndef platform_SHA256_CTX @@ -129,6 +133,7 @@ #ifdef platform_SHA256_Clone #define git_SHA256_Clone platform_SHA256_Clone +#define git_SHA256_Discard platform_SHA256_Discard #endif #ifdef SHA1_MAX_BLOCK_SIZE @@ -142,6 +147,10 @@ static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src) { memcpy(dst, src, sizeof(*dst)); } +static inline void git_SHA1_Discard(git_SHA_CTX *ctx UNUSED) +{ + /* noop */ +} #endif #ifndef SHA1_NEEDS_CLONE_HELPER_UNSAFE static inline void git_SHA1_Clone_unsafe(git_SHA_CTX_unsafe *dst, @@ -149,6 +158,10 @@ static inline void git_SHA1_Clone_unsafe(git_SHA_CTX_unsafe *dst, { memcpy(dst, src, sizeof(*dst)); } +static inline void git_SHA1_Discard_unsafe(git_SHA_CTX_unsafe *ctx UNUSED) +{ + /* noop */ +} #endif #ifndef SHA256_NEEDS_CLONE_HELPER @@ -156,6 +169,10 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s { memcpy(dst, src, sizeof(*dst)); } +static inline void git_SHA256_Discard(git_SHA256_CTX *ctx UNUSED) +{ + /* noop */ +} #endif /* @@ -264,6 +281,7 @@ struct git_hash_ctx { git_SHA_CTX_unsafe sha1_unsafe; git_SHA256_CTX sha256; } state; + bool active; }; typedef void (*git_hash_init_fn)(struct git_hash_ctx *ctx); @@ -271,6 +289,7 @@ typedef void (*git_hash_clone_fn)(struct git_hash_ctx *dst, const struct git_has typedef void (*git_hash_update_fn)(struct git_hash_ctx *ctx, const void *in, size_t len); typedef void (*git_hash_final_fn)(unsigned char *hash, struct git_hash_ctx *ctx); typedef void (*git_hash_final_oid_fn)(struct object_id *oid, struct git_hash_ctx *ctx); +typedef void (*git_hash_discard_fn)(struct git_hash_ctx *ctx); struct git_hash_algo { /* @@ -291,20 +310,16 @@ struct git_hash_algo { /* The block size of the hash. */ size_t blksz; - /* The hash initialization function. */ + /* + * Low-level implementation hooks. Callers should use the git_hash_* + * wrappers below rather than invoking these directly. + */ git_hash_init_fn init_fn; - - /* The hash context cloning function. */ git_hash_clone_fn clone_fn; - - /* The hash update function. */ git_hash_update_fn update_fn; - - /* The hash finalization function. */ git_hash_final_fn final_fn; - - /* The hash finalization function for object IDs. */ git_hash_final_oid_fn final_oid_fn; + git_hash_discard_fn discard_fn; /* The OID of the empty tree. */ const struct object_id *empty_tree; @@ -320,11 +335,40 @@ struct git_hash_algo { }; extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS]; +/* + * Prepare an uninitialized hash context for use. You must eventually release + * the context with git_hash_final() (or final_oid()) or by calling + * git_hash_discard(). + */ void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop); + +/* + * Clone the state of a hash. Both src and dst must have been initialized with + * git_hash_init(). + */ void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src); + +/* + * Add more data to an initialized hash context. + */ void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len); + +/* + * Retrieve the final hash value from a context, releasing any resources. + */ void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx); + +/* + * Like git_hash_final(), but write the result into an object_id. + */ void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx); + +/* + * Discard a hash context without computing the final value, but still + * releasing any resources. + */ +void git_hash_discard(struct git_hash_ctx *ctx); + const struct git_hash_algo *hash_algo_ptr_by_number(uint32_t algo); struct git_hash_ctx *git_hash_alloc(void); void git_hash_free(struct git_hash_ctx *ctx);
diff --git a/http-push.c b/http-push.c index 3c23cbb..60f6f8f 100644 --- a/http-push.c +++ b/http-push.c
@@ -776,7 +776,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { lock->token = xstrdup(ctx->cdata); - the_hash_algo->init_fn(&hash_ctx); + git_hash_init(&hash_ctx, the_hash_algo); git_hash_update(&hash_ctx, lock->token, strlen(lock->token)); git_hash_final(lock_token_hash, &hash_ctx);
diff --git a/http.c b/http.c index b4e7b8d..caccf21 100644 --- a/http.c +++ b/http.c
@@ -2879,7 +2879,7 @@ struct http_object_request *new_http_object_request(const char *base_url, git_inflate_init(&freq->stream); - the_hash_algo->init_fn(&freq->c); + git_hash_init(&freq->c, the_hash_algo); freq->url = get_remote_object_url(base_url, hex, 0); @@ -2915,7 +2915,7 @@ struct http_object_request *new_http_object_request(const char *base_url, git_inflate_end(&freq->stream); memset(&freq->stream, 0, sizeof(freq->stream)); git_inflate_init(&freq->stream); - the_hash_algo->init_fn(&freq->c); + git_hash_init(&freq->c, the_hash_algo); if (prev_posn>0) { prev_posn = 0; lseek(freq->localfile, 0, SEEK_SET); @@ -3028,6 +3028,7 @@ void release_http_object_request(struct http_object_request **freq_p) curl_slist_free_all(freq->headers); strbuf_release(&freq->tmpfile); git_inflate_end(&freq->stream); + git_hash_discard(&freq->c); free(freq); *freq_p = NULL;
diff --git a/imap-send.c b/imap-send.c index cfd6a51..0d16d02 100644 --- a/imap-send.c +++ b/imap-send.c
@@ -1750,6 +1750,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server, curl_easy_cleanup(curl); curl_global_cleanup(); + strbuf_release(&msgbuf.buf); if (cred.username) { if (res == CURLE_OK)
diff --git a/line-log.c b/line-log.c index 5fc75ae..0179f13 100644 --- a/line-log.c +++ b/line-log.c
@@ -1141,8 +1141,7 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit if (range) { if (commit->parents && !bloom_filter_check(rev, commit, range)) { - struct line_log_data *prange = line_log_data_copy(range); - add_line_range(rev, commit->parents->item, prange); + add_line_range(rev, commit->parents->item, range); clear_commit_line_range(rev, commit); } else if (commit->parents && commit->parents->next) changed = process_ranges_merge_commit(rev, commit, range);
diff --git a/log-tree.c b/log-tree.c index 88b3019..83a3c4b 100644 --- a/log-tree.c +++ b/log-tree.c
@@ -3,6 +3,7 @@ #include "git-compat-util.h" #include "commit-reach.h" +#include "commit-slab.h" #include "config.h" #include "diff.h" #include "diffcore.h" @@ -1089,6 +1090,96 @@ static int do_remerge_diff(struct rev_info *opt, return !opt->loginfo; } +/* Per-commit path storage for --follow across merges */ +define_commit_slab(follow_pathspec_slab, char *); + +static const char *pathspec_single_path(const struct pathspec *ps) +{ + if (ps->nr != 1) + return NULL; + return ps->items[0].match; +} + +static void set_pathspec_to_single_path(struct pathspec *ps, const char *path) +{ + const char *paths[2] = { path, NULL }; + + clear_pathspec(ps); + parse_pathspec(ps, + PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, + PATHSPEC_LITERAL_PATH, "", paths); +} + +static void remember_follow_pathspec(struct rev_info *opt, + struct commit *c, const char *path) +{ + char **slot; + + if (!path) + return; + if (!opt->follow_pathspec_slab) { + opt->follow_pathspec_slab = xmalloc(sizeof(*opt->follow_pathspec_slab)); + init_follow_pathspec_slab(opt->follow_pathspec_slab); + } + slot = follow_pathspec_slab_at(opt->follow_pathspec_slab, c); + if (*slot && !strcmp(*slot, path)) + return; + free(*slot); + *slot = xstrdup(path); +} + +static const char *recall_follow_pathspec(struct rev_info *opt, + struct commit *c) +{ + char **slot; + + if (!opt->follow_pathspec_slab) + return NULL; + slot = follow_pathspec_slab_peek(opt->follow_pathspec_slab, c); + return slot ? *slot : NULL; +} + +static void free_follow_pathspec_slot(char **slot) +{ + FREE_AND_NULL(*slot); +} + +void release_follow_pathspec_slab(struct rev_info *opt) +{ + if (!opt->follow_pathspec_slab) + return; + deep_clear_follow_pathspec_slab(opt->follow_pathspec_slab, + free_follow_pathspec_slot); + FREE_AND_NULL(opt->follow_pathspec_slab); +} + +/* Compute a path to follow in parent, if there is one */ +static void propagate_follow_pathspec_to_parent(struct rev_info *opt, + struct commit *commit, + struct commit *parent) +{ + struct diff_options diff_opts; + const char *path; + + parse_commit_or_die(parent); + repo_diff_setup(opt->diffopt.repo, &diff_opts); + copy_pathspec(&diff_opts.pathspec, &opt->diffopt.pathspec); + diff_opts.flags.recursive = 1; + diff_opts.flags.follow_renames = 1; + diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_setup_done(&diff_opts); + diff_tree_oid(get_commit_tree_oid(parent), + get_commit_tree_oid(commit), + "", &diff_opts); + + path = pathspec_single_path(&diff_opts.pathspec); + if (path) + remember_follow_pathspec(opt, parent, path); + + diff_queue_clear(&diff_queued_diff); + diff_free(&diff_opts); +} + /* * Show the diff of a commit. * @@ -1185,6 +1276,16 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit) opt->loginfo = &log; opt->diffopt.no_free = 1; + /* Any recorded path for this commit? If so, restore it */ + if (opt->diffopt.flags.follow_renames) { + const char *stored = recall_follow_pathspec(opt, commit); + if (stored) { + const char *current = pathspec_single_path(&opt->diffopt.pathspec); + if (!current || strcmp(current, stored)) + set_pathspec_to_single_path(&opt->diffopt.pathspec, stored); + } + } + if (opt->track_linear && !opt->linear && !opt->reverse_output_stage) fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar); shown = log_tree_diff(opt, commit, &log); @@ -1197,6 +1298,21 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit) fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar); if (shown) show_diff_of_diff(opt); + + /* Record what path each parent of this commit should use */ + if (opt->diffopt.flags.follow_renames) { + struct commit_list *parents = get_saved_parents(opt, commit); + if (parents && parents->next) { + struct commit_list *p; + for (p = parents; p; p = p->next) + propagate_follow_pathspec_to_parent(opt, commit, + p->item); + } else if (parents) { + remember_follow_pathspec(opt, parents->item, + pathspec_single_path(&opt->diffopt.pathspec)); + } + } + opt->loginfo = NULL; maybe_flush_or_die(opt->diffopt.file, "stdout"); opt->diffopt.no_free = no_free;
diff --git a/log-tree.h b/log-tree.h index 07924be..e8679b6 100644 --- a/log-tree.h +++ b/log-tree.h
@@ -26,6 +26,7 @@ struct decoration_options { int parse_decorate_color_config(const char *var, const char *slot_name, const char *value); int log_tree_diff_flush(struct rev_info *); int log_tree_commit(struct rev_info *, struct commit *); +void release_follow_pathspec_slab(struct rev_info *); void show_log(struct rev_info *opt); void format_decorations(struct strbuf *sb, const struct commit *commit, enum git_colorbool use_color, const struct decoration_options *opts);
diff --git a/loose.c b/loose.c index 0b626c1..bf01d3e 100644 --- a/loose.c +++ b/loose.c
@@ -65,6 +65,7 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_ { struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT; FILE *fp; + int ret = -1; if (!loose->map) loose_object_map_init(&loose->map); @@ -84,7 +85,6 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_ return 0; } - errno = 0; if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header)) goto err; while (!strbuf_getline_lf(&buf, fp)) { @@ -98,13 +98,12 @@ static int load_one_loose_object_map(struct repository *repo, struct odb_source_ insert_loose_map(loose, &oid, &compat_oid); } - strbuf_release(&buf); - strbuf_release(&path); - return errno ? -1 : 0; + ret = ferror(fp) ? -1 : 0; err: + fclose(fp); strbuf_release(&buf); strbuf_release(&path); - return -1; + return ret; } int repo_read_loose_object_map(struct repository *repo) @@ -202,7 +201,8 @@ static int write_one_object(struct odb_source_loose *loose, return 0; errout: error_errno(_("failed to write loose object index %s"), path.buf); - close(fd); + if (fd >= 0) + close(fd); rollback_lock_file(&lock); strbuf_release(&buf); strbuf_release(&path);
diff --git a/mailmap.c b/mailmap.c index 72b639e..1386ab2 100644 --- a/mailmap.c +++ b/mailmap.c
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "environment.h" #include "string-list.h" @@ -219,10 +217,10 @@ int read_mailmap(struct repository *repo, struct string_list *map) map->strdup_strings = 1; map->cmp = namemap_cmp; - if (!mailmap_blob && is_bare_repository()) + if (!mailmap_blob && is_bare_repository(repo)) mailmap_blob = xstrdup("HEAD:.mailmap"); - if (!startup_info->have_repository || !is_bare_repository()) + if (!startup_info->have_repository || !is_bare_repository(repo)) err |= read_mailmap_file(map, ".mailmap", startup_info->have_repository ? MAILMAP_NOFOLLOW : 0);
diff --git a/merge-ort.c b/merge-ort.c index 4f6273b..c410a5d 100644 --- a/merge-ort.c +++ b/merge-ort.c
@@ -728,6 +728,8 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti, strintmap_clear_func(&renames->deferred[i].possible_trivial_merges); strset_clear_func(&renames->deferred[i].target_dirs); renames->deferred[i].trivial_merges_okay = 1; /* 1 == maybe */ + free(renames->pairs[i].queue); + diff_queue_init(&renames->pairs[i]); } renames->cached_pairs_valid_side = 0; renames->dir_rename_mask = 0; @@ -1008,32 +1010,34 @@ static int traverse_trees_wrapper(struct index_state *istate, info->traverse_path = renames->callback_data_traverse_path; info->fn = old_fn; for (i = old_offset; i < renames->callback_data_nr; ++i) { - info->fn(n, - renames->callback_data[i].mask, - renames->callback_data[i].dirmask, - renames->callback_data[i].names, - info); + ret = info->fn(n, + renames->callback_data[i].mask, + renames->callback_data[i].dirmask, + renames->callback_data[i].names, + info); + if (ret < 0) + break; } renames->callback_data_nr = old_offset; free(renames->callback_data_traverse_path); renames->callback_data_traverse_path = old_callback_data_traverse_path; info->traverse_path = NULL; - return 0; + return ret < 0 ? ret : 0; } -static void setup_path_info(struct merge_options *opt, - struct string_list_item *result, - const char *current_dir_name, - int current_dir_name_len, - char *fullpath, /* we'll take over ownership */ - struct name_entry *names, - struct name_entry *merged_version, - unsigned is_null, /* boolean */ - unsigned df_conflict, /* boolean */ - unsigned filemask, - unsigned dirmask, - int resolved /* boolean */) +static int setup_path_info(struct merge_options *opt, + struct string_list_item *result, + const char *current_dir_name, + int current_dir_name_len, + char *fullpath, /* we'll take over ownership */ + struct name_entry *names, + struct name_entry *merged_version, + unsigned is_null, /* boolean */ + unsigned df_conflict, /* boolean */ + unsigned filemask, + unsigned dirmask, + int resolved /* boolean */) { /* result->util is void*, so mi is a convenience typed variable */ struct merged_info *mi; @@ -1077,9 +1081,11 @@ static void setup_path_info(struct merge_options *opt, */ mi->is_null = 1; } - strmap_put(&opt->priv->paths, fullpath, mi); + if (strmap_put(&opt->priv->paths, fullpath, mi)) + return error(_("tree has duplicate entries for '%s'"), fullpath); result->string = fullpath; result->util = mi; + return 0; } static void add_pair(struct merge_options *opt, @@ -1346,9 +1352,10 @@ static int collect_merge_info_callback(int n, */ if (side1_matches_mbase && side2_matches_mbase) { /* mbase, side1, & side2 all match; use mbase as resolution */ - setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, - names, names+0, mbase_null, 0 /* df_conflict */, - filemask, dirmask, 1 /* resolved */); + if (setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, + names, names+0, mbase_null, 0 /* df_conflict */, + filemask, dirmask, 1 /* resolved */)) + return -1; /* Quit traversing */ return mask; } @@ -1360,9 +1367,10 @@ static int collect_merge_info_callback(int n, */ if (sides_match && filemask == 0x07) { /* use side1 (== side2) version as resolution */ - setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, - names, names+1, side1_null, 0, - filemask, dirmask, 1); + if (setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, + names, names+1, side1_null, 0, + filemask, dirmask, 1)) + return -1; /* Quit traversing */ return mask; } @@ -1374,18 +1382,20 @@ static int collect_merge_info_callback(int n, */ if (side1_matches_mbase && filemask == 0x07) { /* use side2 version as resolution */ - setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, - names, names+2, side2_null, 0, - filemask, dirmask, 1); + if (setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, + names, names+2, side2_null, 0, + filemask, dirmask, 1)) + return -1; /* Quit traversing */ return mask; } /* Similar to above but swapping sides 1 and 2 */ if (side2_matches_mbase && filemask == 0x07) { /* use side1 version as resolution */ - setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, - names, names+1, side1_null, 0, - filemask, dirmask, 1); + if (setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, + names, names+1, side1_null, 0, + filemask, dirmask, 1)) + return -1; /* Quit traversing */ return mask; } @@ -1409,8 +1419,9 @@ static int collect_merge_info_callback(int n, * unconflict some more cases, but that comes later so all we can * do now is record the different non-null file hashes.) */ - setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, - names, NULL, 0, df_conflict, filemask, dirmask, 0); + if (setup_path_info(opt, &pi, dirname, info->pathlen, fullpath, + names, NULL, 0, df_conflict, filemask, dirmask, 0)) + return -1; /* Quit traversing */ ci = pi.util; VERIFY_CI(ci); @@ -1738,7 +1749,6 @@ static int collect_merge_info(struct merge_options *opt, setup_traverse_info(&info, opt->priv->toplevel_dir); info.fn = collect_merge_info_callback; info.data = opt; - info.show_all_errors = 1; if (repo_parse_tree(opt->repo, merge_base) < 0 || repo_parse_tree(opt->repo, side1) < 0 ||
diff --git a/meson.build b/meson.build index 3247697..f7c40ea 100644 --- a/meson.build +++ b/meson.build
@@ -161,6 +161,21 @@ # These machine files can be passed to `meson setup` via the `--native-file` # option. # +# Fuzzing +# ======= +# +# Meson supports building the fuzzing targets by setting `-Dfuzzers=true`. By +# default, the targets will be built without libFuzzer and thus won't be usable +# for fuzzing. You have to configure a couple of options to properly wire up +# libFuzzer: +# +# $ meson setup build-fuzzers \ +# -Db_sanitize=address,fuzzer-no-link \ +# -Dfuzzers=true \ +# -Dfuzzers_link_args=-fsanitize=fuzzer +# $ meson compile -C build-fuzzers +# $ ./build-fuzzers/oss-fuzz/fuzz-config <args> +# # Cross compilation # ================= # @@ -278,7 +293,20 @@ 'compat/terminal.c', ] +hook_list = custom_target( + input: 'Documentation/githooks.adoc', + output: 'hook-list.h', + command: [ + shell, + meson.current_source_dir() + '/tools/generate-hooklist.sh', + meson.current_source_dir(), + '@OUTPUT@', + ], + env: script_environment, +) + libgit_sources = [ + hook_list, 'abspath.c', 'add-interactive.c', 'add-patch.c', @@ -406,6 +434,7 @@ 'odb/source-files.c', 'odb/source-inmemory.c', 'odb/source-loose.c', + 'odb/source-packed.c', 'odb/streaming.c', 'odb/transaction.c', 'oid-array.c', @@ -421,6 +450,7 @@ 'pack-revindex.c', 'pack-write.c', 'packfile.c', + 'packfile-list.c', 'pager.c', 'parallel-checkout.c', 'parse.c', @@ -566,19 +596,8 @@ env: script_environment, ) -libgit_sources += custom_target( - input: 'Documentation/githooks.adoc', - output: 'hook-list.h', - command: [ - shell, - meson.current_source_dir() + '/tools/generate-hooklist.sh', - meson.current_source_dir(), - '@OUTPUT@', - ], - env: script_environment, -) - builtin_sources = [ + hook_list, 'builtin/add.c', 'builtin/am.c', 'builtin/annotate.c',
diff --git a/meson_options.txt b/meson_options.txt index d936ada..dc88f13 100644 --- a/meson_options.txt +++ b/meson_options.txt
@@ -131,3 +131,5 @@ description: 'Name of a UTF-8 locale used for testing.') option('fuzzers', type: 'boolean', value: false, description: 'Enable building fuzzers.') +option('fuzzers_link_args', type: 'array', value: [], + description: 'Linker arguments used to link fuzzers. Use -fsanitize=fuzzer for fuzzing.')
diff --git a/midx-write.c b/midx-write.c index 19e1cd1..8c1837f 100644 --- a/midx-write.c +++ b/midx-write.c
@@ -25,9 +25,9 @@ #define NO_PREFERRED_PACK (~((uint32_t)0)) extern int midx_checksum_valid(struct multi_pack_index *m); -extern void clear_midx_files_ext(struct odb_source *source, const char *ext, +extern void clear_midx_files_ext(struct odb_source_packed *source, const char *ext, const char *keep_hash); -extern void clear_incremental_midx_files_ext(struct odb_source *source, +extern void clear_incremental_midx_files_ext(struct odb_source_packed *source, const char *ext, const struct strvec *keep_hashes); extern int cmp_idx_or_pack_name(const char *idx_or_pack_name, @@ -119,7 +119,7 @@ struct write_midx_context { struct string_list *to_include; struct repository *repo; - struct odb_source *source; + struct odb_source_packed *source; }; static uint32_t midx_pack_perm(struct write_midx_context *ctx, @@ -1107,7 +1107,7 @@ static int link_midx_to_chain(struct multi_pack_index *m) return ret; } -static void clear_midx_files(struct odb_source *source, +static void clear_midx_files(struct odb_source_packed *source, const struct strvec *hashes, unsigned incremental) { /* @@ -1237,7 +1237,7 @@ static int midx_hashcmp(const struct multi_pack_index *a, } struct write_midx_opts { - struct odb_source *source; /* non-optional */ + struct odb_source_packed *source; /* non-optional */ struct string_list *packs_to_include; struct string_list *packs_to_drop; @@ -1253,7 +1253,7 @@ struct write_midx_opts { static int write_midx_internal(struct write_midx_opts *opts) { - struct repository *r = opts->source->odb->repo; + struct repository *r = opts->source->base.odb->repo; struct strbuf midx_name = STRBUF_INIT; unsigned char midx_hash[GIT_MAX_RAWSZ]; uint32_t start_pack; @@ -1301,7 +1301,7 @@ static int write_midx_internal(struct write_midx_opts *opts) if (ctx.incremental) strbuf_addf(&midx_name, "%s/pack/multi-pack-index.d/tmp_midx_XXXXXX", - opts->source->path); + opts->source->base.path); else get_midx_filename(opts->source, &midx_name); if (safe_create_leading_directories(r, midx_name.buf)) @@ -1396,7 +1396,7 @@ static int write_midx_internal(struct write_midx_opts *opts) fill_packs_from_midx_range(&ctx, bitmap_order); } else { ctx.to_include = opts->packs_to_include; - for_each_file_in_pack_dir(opts->source->path, add_pack_to_midx, &ctx); + for_each_file_in_pack_dir(opts->source->base.path, add_pack_to_midx, &ctx); } stop_progress(&ctx.progress); @@ -1847,7 +1847,7 @@ static int write_midx_internal(struct write_midx_opts *opts) return result; } -int write_midx_file(struct odb_source *source, +int write_midx_file(struct odb_source_packed *source, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags) @@ -1862,7 +1862,7 @@ int write_midx_file(struct odb_source *source, return write_midx_internal(&opts); } -int write_midx_file_only(struct odb_source *source, +int write_midx_file_only(struct odb_source_packed *source, struct string_list *packs_to_include, const char *preferred_pack_name, const char *refs_snapshot, @@ -1881,7 +1881,7 @@ int write_midx_file_only(struct odb_source *source, return write_midx_internal(&opts); } -int write_midx_file_compact(struct odb_source *source, +int write_midx_file_compact(struct odb_source_packed *source, struct multi_pack_index *from, struct multi_pack_index *to, const char *incremental_base, @@ -1898,7 +1898,7 @@ int write_midx_file_compact(struct odb_source *source, return write_midx_internal(&opts); } -int expire_midx_packs(struct odb_source *source, unsigned flags) +int expire_midx_packs(struct odb_source_packed *source, unsigned flags) { uint32_t i, *count, result = 0; struct string_list packs_to_drop = STRING_LIST_INIT_DUP; @@ -1915,7 +1915,7 @@ int expire_midx_packs(struct odb_source *source, unsigned flags) if (flags & MIDX_PROGRESS) progress = start_delayed_progress( - source->odb->repo, + source->base.odb->repo, _("Counting referenced objects"), m->num_objects); for (i = 0; i < m->num_objects; i++) { @@ -1927,7 +1927,7 @@ int expire_midx_packs(struct odb_source *source, unsigned flags) if (flags & MIDX_PROGRESS) progress = start_delayed_progress( - source->odb->repo, + source->base.odb->repo, _("Finding and deleting unreferenced packfiles"), m->num_packs); for (i = 0; i < m->num_packs; i++) { @@ -2085,9 +2085,9 @@ static void fill_included_packs_batch(struct repository *r, free(pack_info); } -int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags) +int midx_repack(struct odb_source_packed *source, size_t batch_size, unsigned flags) { - struct repository *r = source->odb->repo; + struct repository *r = source->base.odb->repo; int result = 0; uint32_t i, packs_to_repack = 0; unsigned char *include_pack; @@ -2131,7 +2131,7 @@ int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags) strvec_push(&cmd.args, "pack-objects"); - strvec_pushf(&cmd.args, "%s/pack/pack", source->path); + strvec_pushf(&cmd.args, "%s/pack/pack", source->base.path); if (delta_base_offset) strvec_push(&cmd.args, "--delta-base-offset");
diff --git a/midx.c b/midx.c index efbfbb1..76c3f92 100644 --- a/midx.c +++ b/midx.c
@@ -17,9 +17,9 @@ #define MIDX_PACK_ERROR ((void *)(intptr_t)-1) int midx_checksum_valid(struct multi_pack_index *m); -void clear_midx_files_ext(struct odb_source *source, const char *ext, +void clear_midx_files_ext(struct odb_source_packed *source, const char *ext, const char *keep_hash); -void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, +void clear_incremental_midx_files_ext(struct odb_source_packed *source, const char *ext, const struct strvec *keep_hashes); int cmp_idx_or_pack_name(const char *idx_or_pack_name, const char *idx_name); @@ -27,25 +27,25 @@ int cmp_idx_or_pack_name(const char *idx_or_pack_name, const char *midx_get_checksum_hex(const struct multi_pack_index *m) { return hash_to_hex_algop(midx_get_checksum_hash(m), - m->source->odb->repo->hash_algo); + m->source->base.odb->repo->hash_algo); } const unsigned char *midx_get_checksum_hash(const struct multi_pack_index *m) { - return m->data + m->data_len - m->source->odb->repo->hash_algo->rawsz; + return m->data + m->data_len - m->source->base.odb->repo->hash_algo->rawsz; } -void get_midx_filename(struct odb_source *source, struct strbuf *out) +void get_midx_filename(struct odb_source_packed *source, struct strbuf *out) { get_midx_filename_ext(source, out, NULL, NULL); } -void get_midx_filename_ext(struct odb_source *source, struct strbuf *out, +void get_midx_filename_ext(struct odb_source_packed *source, struct strbuf *out, const unsigned char *hash, const char *ext) { - strbuf_addf(out, "%s/pack/multi-pack-index", source->path); + strbuf_addf(out, "%s/pack/multi-pack-index", source->base.path); if (ext) - strbuf_addf(out, "-%s.%s", hash_to_hex_algop(hash, source->odb->repo->hash_algo), ext); + strbuf_addf(out, "-%s.%s", hash_to_hex_algop(hash, source->base.odb->repo->hash_algo), ext); } static int midx_read_oid_fanout(const unsigned char *chunk_start, @@ -99,17 +99,16 @@ static int midx_read_object_offsets(const unsigned char *chunk_start, return 0; } -struct multi_pack_index *get_multi_pack_index(struct odb_source *source) +struct multi_pack_index *get_multi_pack_index(struct odb_source_packed *source) { - struct odb_source_files *files = odb_source_files_downcast(source); - packfile_store_prepare(files->packed); - return files->packed->midx; + odb_source_prepare(&source->base, 0); + return source->midx; } -static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *source, +static struct multi_pack_index *load_multi_pack_index_one(struct odb_source_packed *source, const char *midx_name) { - struct repository *r = source->odb->repo; + struct repository *r = source->base.odb->repo; struct multi_pack_index *m = NULL; int fd; struct stat st; @@ -234,23 +233,23 @@ static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *sou return NULL; } -void get_midx_chain_dirname(struct odb_source *source, struct strbuf *buf) +void get_midx_chain_dirname(struct odb_source_packed *source, struct strbuf *buf) { - strbuf_addf(buf, "%s/pack/multi-pack-index.d", source->path); + strbuf_addf(buf, "%s/pack/multi-pack-index.d", source->base.path); } -void get_midx_chain_filename(struct odb_source *source, struct strbuf *buf) +void get_midx_chain_filename(struct odb_source_packed *source, struct strbuf *buf) { get_midx_chain_dirname(source, buf); strbuf_addstr(buf, "/multi-pack-index-chain"); } -void get_split_midx_filename_ext(struct odb_source *source, struct strbuf *buf, +void get_split_midx_filename_ext(struct odb_source_packed *source, struct strbuf *buf, const unsigned char *hash, const char *ext) { get_midx_chain_dirname(source, buf); strbuf_addf(buf, "/multi-pack-index-%s.%s", - hash_to_hex_algop(hash, source->odb->repo->hash_algo), ext); + hash_to_hex_algop(hash, source->base.odb->repo->hash_algo), ext); } static int open_multi_pack_index_chain(const struct git_hash_algo *hash_algo, @@ -306,11 +305,11 @@ static int add_midx_to_chain(struct multi_pack_index *midx, return 1; } -static struct multi_pack_index *load_midx_chain_fd_st(struct odb_source *source, +static struct multi_pack_index *load_midx_chain_fd_st(struct odb_source_packed *source, int fd, struct stat *st, int *incomplete_chain) { - const struct git_hash_algo *hash_algo = source->odb->repo->hash_algo; + const struct git_hash_algo *hash_algo = source->base.odb->repo->hash_algo; struct multi_pack_index *midx_chain = NULL; struct strbuf buf = STRBUF_INIT; int valid = 1; @@ -362,7 +361,7 @@ static struct multi_pack_index *load_midx_chain_fd_st(struct odb_source *source, return midx_chain; } -static struct multi_pack_index *load_multi_pack_index_chain(struct odb_source *source) +static struct multi_pack_index *load_multi_pack_index_chain(struct odb_source_packed *source) { struct strbuf chain_file = STRBUF_INIT; struct stat st; @@ -370,7 +369,8 @@ static struct multi_pack_index *load_multi_pack_index_chain(struct odb_source *s struct multi_pack_index *m = NULL; get_midx_chain_filename(source, &chain_file); - if (open_multi_pack_index_chain(source->odb->repo->hash_algo, chain_file.buf, &fd, &st)) { + if (open_multi_pack_index_chain(source->base.odb->repo->hash_algo, + chain_file.buf, &fd, &st)) { int incomplete; /* ownership of fd is taken over by load function */ m = load_midx_chain_fd_st(source, fd, &st, &incomplete); @@ -380,7 +380,7 @@ static struct multi_pack_index *load_multi_pack_index_chain(struct odb_source *s return m; } -struct multi_pack_index *load_multi_pack_index(struct odb_source *source) +struct multi_pack_index *load_multi_pack_index(struct odb_source_packed *source) { struct strbuf midx_name = STRBUF_INIT; struct multi_pack_index *m; @@ -456,7 +456,7 @@ static uint32_t midx_for_pack(struct multi_pack_index **_m, int prepare_midx_pack(struct multi_pack_index *m, uint32_t pack_int_id) { - struct odb_source_files *files = odb_source_files_downcast(m->source); + struct odb_source_packed *packed = m->source; struct strbuf pack_name = STRBUF_INIT; struct packed_git *p; @@ -467,10 +467,10 @@ int prepare_midx_pack(struct multi_pack_index *m, if (m->packs[pack_int_id]) return 0; - strbuf_addf(&pack_name, "%s/pack/%s", files->base.path, + strbuf_addf(&pack_name, "%s/pack/%s", packed->base.path, m->pack_names[pack_int_id]); - p = packfile_store_load_pack(files->packed, - pack_name.buf, files->base.local); + p = packfile_store_load_pack(packed, + pack_name.buf, packed->base.local); strbuf_release(&pack_name); if (!p) { @@ -523,7 +523,7 @@ int bsearch_one_midx(const struct object_id *oid, struct multi_pack_index *m, { int ret = bsearch_hash(oid->hash, m->chunk_oid_fanout, m->chunk_oid_lookup, - m->source->odb->repo->hash_algo->rawsz, + m->source->base.odb->repo->hash_algo->rawsz, result); if (result) *result += m->num_objects_in_base; @@ -554,7 +554,7 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid, n = midx_for_object(&m, n); oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n), - m->source->odb->repo->hash_algo); + m->source->base.odb->repo->hash_algo); return oid; } @@ -734,26 +734,25 @@ int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id) return 0; } -int prepare_multi_pack_index_one(struct odb_source *source) +int prepare_multi_pack_index_one(struct odb_source_packed *source) { - struct odb_source_files *files = odb_source_files_downcast(source); - struct repository *r = source->odb->repo; + struct repository *r = source->base.odb->repo; prepare_repo_settings(r); if (!r->settings.core_multi_pack_index) return 0; - if (files->packed->midx) + if (source->midx) return 1; - files->packed->midx = load_multi_pack_index(source); + source->midx = load_multi_pack_index(source); - return !!files->packed->midx; + return !!source->midx; } int midx_checksum_valid(struct multi_pack_index *m) { - return hashfile_checksum_valid(m->source->odb->repo->hash_algo, + return hashfile_checksum_valid(m->source->base.odb->repo->hash_algo, m->data, m->data_len); } @@ -776,7 +775,7 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS die_errno(_("failed to remove %s"), full_path); } -void clear_midx_files_ext(struct odb_source *source, const char *ext, +void clear_midx_files_ext(struct odb_source_packed *source, const char *ext, const char *keep_hash) { struct clear_midx_data data = { @@ -793,12 +792,12 @@ void clear_midx_files_ext(struct odb_source *source, const char *ext, strbuf_release(&buf); } - for_each_file_in_pack_dir(source->path, clear_midx_file_ext, &data); + for_each_file_in_pack_dir(source->base.path, clear_midx_file_ext, &data); strset_clear(&data.keep); } -void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, +void clear_incremental_midx_files_ext(struct odb_source_packed *source, const char *ext, const struct strvec *keep_hashes) { struct clear_midx_data data = { @@ -817,7 +816,7 @@ void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext } } - for_each_file_in_pack_subdir(source->path, "multi-pack-index.d", + for_each_file_in_pack_subdir(source->base.path, "multi-pack-index.d", clear_midx_file_ext, &data); strbuf_release(&buf); @@ -826,26 +825,28 @@ void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext void clear_midx_file(struct repository *r) { + struct odb_source_files *files; struct strbuf midx = STRBUF_INIT; - get_midx_filename(r->objects->sources, &midx); - if (r->objects) { struct odb_source *source; for (source = r->objects->sources; source; source = source->next) { - struct odb_source_files *files = odb_source_files_downcast(source); + files = odb_source_files_downcast(source); if (files->packed->midx) close_midx(files->packed->midx); files->packed->midx = NULL; } } + files = odb_source_files_downcast(r->objects->sources); + get_midx_filename(files->packed, &midx); + if (remove_path(midx.buf)) die(_("failed to clear multi-pack-index at %s"), midx.buf); - clear_midx_files_ext(r->objects->sources, MIDX_EXT_BITMAP, NULL); - clear_midx_files_ext(r->objects->sources, MIDX_EXT_REV, NULL); + clear_midx_files_ext(files->packed, MIDX_EXT_BITMAP, NULL); + clear_midx_files_ext(files->packed, MIDX_EXT_REV, NULL); strbuf_release(&midx); } @@ -853,28 +854,27 @@ void clear_midx_file(struct repository *r) void clear_incremental_midx_files(struct repository *r, const struct strvec *keep_hashes) { - struct odb_source *source = r->objects->sources; + struct odb_source_files *files; + struct odb_source *source; struct strbuf chain = STRBUF_INIT; - get_midx_chain_filename(source, &chain); - - for (; source; source = source->next) { - struct odb_source_files *files = odb_source_files_downcast(source); + for (source = r->objects->sources; source; source = source->next) { + files = odb_source_files_downcast(source); if (files->packed->midx) close_midx(files->packed->midx); files->packed->midx = NULL; } + files = odb_source_files_downcast(r->objects->sources); + get_midx_chain_filename(files->packed, &chain); + if (!keep_hashes && remove_path(chain.buf)) die(_("failed to clear multi-pack-index chain at %s"), chain.buf); - clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_BITMAP, - keep_hashes); - clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_REV, - keep_hashes); - clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_MIDX, - keep_hashes); + clear_incremental_midx_files_ext(files->packed, MIDX_EXT_BITMAP, keep_hashes); + clear_incremental_midx_files_ext(files->packed, MIDX_EXT_REV, keep_hashes); + clear_incremental_midx_files_ext(files->packed, MIDX_EXT_MIDX, keep_hashes); strbuf_release(&chain); } @@ -918,9 +918,9 @@ static int compare_pair_pos_vs_id(const void *_a, const void *_b) display_progress(progress, _n); \ } while (0) -int verify_midx_file(struct odb_source *source, unsigned flags) +int verify_midx_file(struct odb_source_packed *source, unsigned flags) { - struct repository *r = source->odb->repo; + struct repository *r = source->base.odb->repo; struct pair_pos_vs_id *pairs = NULL; uint32_t i; struct progress *progress = NULL;
diff --git a/midx.h b/midx.h index 63853a0..939c18e 100644 --- a/midx.h +++ b/midx.h
@@ -37,7 +37,7 @@ struct strvec; "GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL" struct multi_pack_index { - struct odb_source *source; + struct odb_source_packed *source; const unsigned char *data; size_t data_len; @@ -92,16 +92,16 @@ struct multi_pack_index { const char *midx_get_checksum_hex(const struct multi_pack_index *m) /* static buffer */; const unsigned char *midx_get_checksum_hash(const struct multi_pack_index *m); -void get_midx_filename(struct odb_source *source, struct strbuf *out); -void get_midx_filename_ext(struct odb_source *source, struct strbuf *out, +void get_midx_filename(struct odb_source_packed *source, struct strbuf *out); +void get_midx_filename_ext(struct odb_source_packed *source, struct strbuf *out, const unsigned char *hash, const char *ext); -void get_midx_chain_dirname(struct odb_source *source, struct strbuf *out); -void get_midx_chain_filename(struct odb_source *source, struct strbuf *out); -void get_split_midx_filename_ext(struct odb_source *source, struct strbuf *buf, +void get_midx_chain_dirname(struct odb_source_packed *source, struct strbuf *out); +void get_midx_chain_filename(struct odb_source_packed *source, struct strbuf *out); +void get_split_midx_filename_ext(struct odb_source_packed *source, struct strbuf *buf, const unsigned char *hash, const char *ext); -struct multi_pack_index *get_multi_pack_index(struct odb_source *source); -struct multi_pack_index *load_multi_pack_index(struct odb_source *source); +struct multi_pack_index *get_multi_pack_index(struct odb_source_packed *source); +struct multi_pack_index *load_multi_pack_index(struct odb_source_packed *source); int prepare_midx_pack(struct multi_pack_index *m, uint32_t pack_int_id); struct packed_git *nth_midxed_pack(struct multi_pack_index *m, uint32_t pack_int_id); @@ -123,22 +123,22 @@ int midx_contains_pack(struct multi_pack_index *m, int midx_layer_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name); int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id); -int prepare_multi_pack_index_one(struct odb_source *source); +int prepare_multi_pack_index_one(struct odb_source_packed *source); /* * Variant of write_midx_file which writes a MIDX containing only the packs * specified in packs_to_include. */ -int write_midx_file(struct odb_source *source, +int write_midx_file(struct odb_source_packed *source, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags); -int write_midx_file_only(struct odb_source *source, +int write_midx_file_only(struct odb_source_packed *source, struct string_list *packs_to_include, const char *preferred_pack_name, const char *refs_snapshot, const char *incremental_base, unsigned flags); -int write_midx_file_compact(struct odb_source *source, +int write_midx_file_compact(struct odb_source_packed *source, struct multi_pack_index *from, struct multi_pack_index *to, const char *incremental_base, @@ -146,9 +146,9 @@ int write_midx_file_compact(struct odb_source *source, void clear_midx_file(struct repository *r); void clear_incremental_midx_files(struct repository *r, const struct strvec *keep_hashes); -int verify_midx_file(struct odb_source *source, unsigned flags); -int expire_midx_packs(struct odb_source *source, unsigned flags); -int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags); +int verify_midx_file(struct odb_source_packed *source, unsigned flags); +int expire_midx_packs(struct odb_source_packed *source, unsigned flags); +int midx_repack(struct odb_source_packed *source, size_t batch_size, unsigned flags); void close_midx(struct multi_pack_index *m);
diff --git a/name-hash.c b/name-hash.c index b91e276..83757db 100644 --- a/name-hash.c +++ b/name-hash.c
@@ -126,7 +126,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce) hashmap_add(&istate->name_hash, &ce->ent); } - if (ignore_case) + if (repo_ignore_case(the_repository)) add_dir_entry(istate, ce); } @@ -207,7 +207,7 @@ static int lookup_lazy_params(struct index_state *istate) * code to build the "istate->name_hash". We don't * need the complexity here. */ - if (!ignore_case) + if (!repo_ignore_case(the_repository)) return 0; nr_cpus = online_cpus(); @@ -651,7 +651,7 @@ void remove_name_hash(struct index_state *istate, struct cache_entry *ce) ce->ce_flags &= ~CE_HASHED; hashmap_remove(&istate->name_hash, &ce->ent, ce); - if (ignore_case) + if (repo_ignore_case(the_repository)) remove_dir_entry(istate, ce); }
diff --git a/negotiator/default.c b/negotiator/default.c index 78d58d5..19cdf38 100644 --- a/negotiator/default.c +++ b/negotiator/default.c
@@ -113,10 +113,12 @@ static const struct object_id *get_rev(struct negotiation_state *ns) unsigned int mark; struct commit_list *parents; - if (ns->rev_list.nr == 0 || ns->non_common_revs == 0) + if (ns->non_common_revs == 0) return NULL; commit = prio_queue_get(&ns->rev_list); + if (!commit) + return NULL; repo_parse_commit(the_repository, commit); parents = commit->parents;
diff --git a/negotiator/skipping.c b/negotiator/skipping.c index 68c9b3b..db90fa7 100644 --- a/negotiator/skipping.c +++ b/negotiator/skipping.c
@@ -143,8 +143,7 @@ static int push_parent(struct data *data, struct entry *entry, /* * Find the existing entry and use it. */ - for (size_t i = 0; i < data->rev_list.nr; i++) { - parent_entry = data->rev_list.array[i].data; + prio_queue_for_each(&data->rev_list, parent_entry) { if (parent_entry->commit == to_push) goto parent_found; } @@ -181,10 +180,12 @@ static const struct object_id *get_rev(struct data *data) struct commit_list *p; int parent_pushed = 0; - if (data->rev_list.nr == 0 || data->non_common_revs == 0) + if (data->non_common_revs == 0) return NULL; entry = prio_queue_get(&data->rev_list); + if (!entry) + return NULL; commit = entry->commit; commit->object.flags |= POPPED; if (!(commit->object.flags & COMMON)) @@ -253,8 +254,9 @@ static void have_sent(struct fetch_negotiator *n, struct commit *c) static void release(struct fetch_negotiator *n) { struct data *data = n->data; - for (size_t i = 0; i < data->rev_list.nr; i++) - free(data->rev_list.array[i].data); + void *entry; + prio_queue_for_each(&data->rev_list, entry) + free(entry); clear_prio_queue(&data->rev_list); FREE_AND_NULL(data); }
diff --git a/object-file.c b/object-file.c index e3d92bb..2d84f08 100644 --- a/object-file.c +++ b/object-file.c
@@ -124,16 +124,13 @@ int stream_object_signature(struct repository *r, hdrlen = format_object_header(hdr, sizeof(hdr), st->type, st->size); /* Sha1.. */ - r->hash_algo->init_fn(&c); + git_hash_init(&c, r->hash_algo); git_hash_update(&c, hdr, hdrlen); for (;;) { char buf[1024 * 16]; ssize_t readlen = odb_read_stream_read(st, buf, sizeof(buf)); - - if (readlen < 0) { - odb_read_stream_close(st); + if (readlen < 0) return -1; - } if (!readlen) break; git_hash_update(&c, buf, readlen); @@ -316,27 +313,27 @@ int parse_loose_header(const char *hdr, struct object_info *oi) } static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c, - const void *buf, unsigned long len, + const void *buf, size_t len, struct object_id *oid, - char *hdr, int *hdrlen) + char *hdr, size_t *hdrlen) { - algo->init_fn(c); + git_hash_init(c, algo); git_hash_update(c, hdr, *hdrlen); git_hash_update(c, buf, len); git_hash_final_oid(oid, c); } void write_object_file_prepare(const struct git_hash_algo *algo, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, - char *hdr, int *hdrlen) + char *hdr, size_t *hdrlen) { struct git_hash_ctx c; /* Generate the header */ *hdrlen = format_object_header(hdr, *hdrlen, type, len); - /* Sha1.. */ + /* Hash (function pointers) computation */ hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen); } @@ -472,11 +469,11 @@ int finalize_object_file_flags(struct repository *repo, } void hash_object_file(const struct git_hash_algo *algo, const void *buf, - unsigned long len, enum object_type type, + size_t len, enum object_type type, struct object_id *oid) { char hdr[MAX_HEADER_LEN]; - int hdrlen = sizeof(hdr); + size_t hdrlen = sizeof(hdr); write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen); } @@ -681,9 +678,9 @@ static int start_loose_object_common(struct odb_source_loose *loose, git_deflate_init(stream, cfg->zlib_compression_level); stream->next_out = buf; stream->avail_out = buflen; - algo->init_fn(c); + git_hash_init(c, algo); if (compat && compat_c) - compat->init_fn(compat_c); + git_hash_init(compat_c, compat); /* Start to feed header to zlib stream */ stream->next_in = (unsigned char *)hdr; @@ -1141,7 +1138,7 @@ static int hash_blob_stream(struct odb_write_stream *stream, header_len = format_object_header((char *)buf, sizeof(buf), OBJ_BLOB, size); - hash_algo->init_fn(&ctx); + git_hash_init(&ctx, hash_algo); git_hash_update(&ctx, buf, header_len); while (!stream->is_finished) { @@ -1313,7 +1310,7 @@ static int odb_transaction_files_write_object_stream(struct odb_transaction *bas header_len = format_object_header((char *)obuf, sizeof(obuf), OBJ_BLOB, size); - transaction->base.source->odb->repo->hash_algo->init_fn(&ctx); + git_hash_init(&ctx, transaction->base.source->odb->repo->hash_algo); git_hash_update(&ctx, obuf, header_len); /* @@ -1352,6 +1349,8 @@ static int odb_transaction_files_write_object_stream(struct odb_transaction *bas state->alloc_written); state->written[state->nr_written++] = idx; } + + hashfile_checkpoint_release(&checkpoint); return 0; } @@ -1558,7 +1557,7 @@ static int check_stream_oid(git_zstream *stream, unsigned long total_read; int status = Z_OK; - algop->init_fn(&c); + git_hash_init(&c, algop); git_hash_update(&c, hdr, stream->total_out); /* @@ -1585,11 +1584,13 @@ static int check_stream_oid(git_zstream *stream, if (status != Z_STREAM_END) { error(_("corrupt loose object '%s'"), oid_to_hex(expected_oid)); + git_hash_discard(&c); return -1; } if (stream->avail_in) { error(_("garbage at end of loose object '%s'"), oid_to_hex(expected_oid)); + git_hash_discard(&c); return -1; }
diff --git a/object-file.h b/object-file.h index 528c4e6..4c87cd1 100644 --- a/object-file.h +++ b/object-file.h
@@ -131,12 +131,12 @@ int finalize_object_file_flags(struct repository *repo, enum finalize_object_file_flags flags); void hash_object_file(const struct git_hash_algo *algo, const void *buf, - unsigned long len, enum object_type type, + size_t len, enum object_type type, struct object_id *oid); void write_object_file_prepare(const struct git_hash_algo *algo, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, - char *hdr, int *hdrlen); + char *hdr, size_t *hdrlen); int write_loose_object(struct odb_source_loose *loose, const struct object_id *oid, char *hdr, int hdrlen, const void *buf, unsigned long len,
diff --git a/object-name.c b/object-name.c index 4615946..83efba0 100644 --- a/object-name.c +++ b/object-name.c
@@ -1209,7 +1209,7 @@ static int get_oid_oneline(struct repository *r, l->item->object.flags |= ONELINE_SEEN; prio_queue_put(©, l->item); } - while (copy.nr) { + while (prio_queue_size(©)) { const char *p, *buf; struct commit *commit; int matches; @@ -1708,8 +1708,8 @@ static char *resolve_relative_path(struct repository *r, const char *rel) die(_("relative path syntax can't be used outside working tree")); /* die() inside prefix_path() if resolved path is outside worktree */ - return prefix_path(the_repository, startup_info->prefix, - startup_info->prefix ? strlen(startup_info->prefix) : 0, + return prefix_path(the_repository, the_repository->prefix, + the_repository->prefix ? strlen(the_repository->prefix) : 0, rel); }
diff --git a/odb.c b/odb.c index 7d555be..cf6e793 100644 --- a/odb.c +++ b/odb.c
@@ -691,8 +691,8 @@ static int oid_object_info_convert(struct repository *r, return -1; } } - input_oi->whence = new_oi.whence; - input_oi->u = new_oi.u; + if (input_oi->source_infop) + *input_oi->source_infop = *new_oi.source_infop; return ret; } @@ -1070,7 +1070,7 @@ void odb_free(struct object_database *o) free(o); } -void odb_reprepare(struct object_database *o) +void odb_prepare(struct object_database *o, enum odb_prepare_flags flags) { struct odb_source *source; @@ -1082,13 +1082,19 @@ void odb_reprepare(struct object_database *o) * the linked list, so existing odbs will continue to exist for * the lifetime of the process. */ - o->loaded_alternates = 0; + if (flags & ODB_PREPARE_FLUSH_CACHES) { + o->loaded_alternates = 0; + o->object_count_valid = 0; + } + odb_prepare_alternates(o); - for (source = o->sources; source; source = source->next) - odb_source_reprepare(source); - - o->object_count_valid = 0; + odb_source_prepare(source, flags); obj_read_unlock(); } + +void odb_reprepare(struct object_database *o) +{ + odb_prepare(o, ODB_PREPARE_FLUSH_CACHES); +}
diff --git a/odb.h b/odb.h index 3834a0d..9475464 100644 --- a/odb.h +++ b/odb.h
@@ -124,10 +124,22 @@ void odb_free(struct object_database *o); */ void odb_close(struct object_database *o); +enum odb_prepare_flags { + /* + * Flush caches, reload alternates and then re-prepare each object + * source so that new objects may become accessible. + */ + ODB_PREPARE_FLUSH_CACHES = (1 << 0), +}; + /* - * Clear caches, reload alternates and then reload object sources so that new - * objects may become accessible. + * Prepare the object database for use. Calling this function is generally not + * needed, but can be useful in case the caller wants to pre-open individual + * sources. */ +void odb_prepare(struct object_database *o, enum odb_prepare_flags flags); + +/* Equivalent to `odb_prepare(o, ODB_PREPARE_FLUSH_CACHES)`. */ void odb_reprepare(struct object_database *o); /* @@ -248,33 +260,19 @@ int odb_pretend_object(struct object_database *odb, void *buf, size_t len, enum object_type type, struct object_id *oid); -struct object_info { - /* Request */ - enum object_type *typep; - size_t *sizep; - off_t *disk_sizep; - struct object_id *delta_base_oid; - void **contentp; +/* + * Object database source information that can be used to uniquely identify an + * object and learn more about how exactly it is stored. + */ +struct odb_source_info { + /* The source that this object has been looked up from. */ + struct odb_source *source; /* - * The time the given looked-up object has been last modified. - * - * Note: the mtime may be ambiguous in case the object exists multiple - * times in the object database. It is thus _not_ recommended to use - * this field outside of contexts where you would read every instance - * of the object, like for example with `odb_for_each_object()`. As it - * is impossible to say at the ODB level what the intent of the caller - * is (e.g. whether to find the oldest or newest object), it is the - * responsibility of the caller to disambiguate the mtimes. + * Backend-specific information about the specific object. This can be + * used for example to uniquely identify a given object in case it + * exists multiple times. */ - time_t *mtimep; - - /* Response */ - enum { - OI_CACHED, - OI_LOOSE, - OI_PACKED, - } whence; union { /* * struct { @@ -298,6 +296,52 @@ struct object_info { }; /* + * The object info contains the query and response that is to be used for + * functions that end up reading object information. Callers are expected to + * populate pointers whose information they want to request. + */ +struct object_info { + /* The object type. */ + enum object_type *typep; + + /* The inflated object size in bytes. */ + size_t *sizep; + + /* The object size as stored on disk. */ + off_t *disk_sizep; + + /* + * The base the object is deltified against, in case it is stored as a + * delta. + */ + struct object_id *delta_base_oid; + + /* The object contents. Ownership of memory goes over to the caller. */ + void **contentp; + + /* + * The time the given looked-up object has been last modified. + * + * Note: the mtime may be ambiguous in case the object exists multiple + * times in the object database. It is thus _not_ recommended to use + * this field outside of contexts where you would read every instance + * of the object, like for example with `odb_for_each_object()`. As it + * is impossible to say at the ODB level what the intent of the caller + * is (e.g. whether to find the oldest or newest object), it is the + * responsibility of the caller to disambiguate the mtimes. + */ + time_t *mtimep; + + /* + * Backend-specific information that tells the caller where exactly an + * object was looked up from. This information should help disambiguate + * object lookups in case the same object exists in multiple sources, + * or multiple times in the same source. + */ + struct odb_source_info *source_infop; +}; + +/* * Initializer for a "struct object_info" that wants no items. You may * also memset() the memory to all-zeroes. */
diff --git a/odb/source-files.c b/odb/source-files.c index 5bdd042..6c8e935 100644 --- a/odb/source-files.c +++ b/odb/source-files.c
@@ -29,7 +29,7 @@ static void odb_source_files_free(struct odb_source *source) struct odb_source_files *files = odb_source_files_downcast(source); chdir_notify_unregister(NULL, odb_source_files_reparent, files); odb_source_free(&files->loose->base); - packfile_store_free(files->packed); + odb_source_free(&files->packed->base); odb_source_release(&files->base); free(files); } @@ -38,14 +38,15 @@ static void odb_source_files_close(struct odb_source *source) { struct odb_source_files *files = odb_source_files_downcast(source); odb_source_close(&files->loose->base); - packfile_store_close(files->packed); + odb_source_close(&files->packed->base); } -static void odb_source_files_reprepare(struct odb_source *source) +static void odb_source_files_prepare(struct odb_source *source, + enum odb_prepare_flags flags) { struct odb_source_files *files = odb_source_files_downcast(source); - odb_source_reprepare(&files->loose->base); - packfile_store_reprepare(files->packed); + odb_source_prepare(&files->loose->base, flags); + odb_source_prepare(&files->packed->base, flags); } static int odb_source_files_read_object_info(struct odb_source *source, @@ -55,7 +56,7 @@ static int odb_source_files_read_object_info(struct odb_source *source, { struct odb_source_files *files = odb_source_files_downcast(source); - if (!packfile_store_read_object_info(files->packed, oid, oi, flags) || + if (!odb_source_read_object_info(&files->packed->base, oid, oi, flags) || !odb_source_read_object_info(&files->loose->base, oid, oi, flags)) return 0; @@ -67,7 +68,7 @@ static int odb_source_files_read_object_stream(struct odb_read_stream **out, const struct object_id *oid) { struct odb_source_files *files = odb_source_files_downcast(source); - if (!packfile_store_read_object_stream(out, files->packed, oid) || + if (!odb_source_read_object_stream(out, &files->packed->base, oid) || !odb_source_read_object_stream(out, &files->loose->base, oid)) return 0; return -1; @@ -88,7 +89,7 @@ static int odb_source_files_for_each_object(struct odb_source *source, return ret; } - ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, opts); + ret = odb_source_for_each_object(&files->packed->base, request, cb, cb_data, opts); if (ret) return ret; @@ -103,7 +104,7 @@ static int odb_source_files_count_objects(struct odb_source *source, unsigned long count; int ret; - ret = packfile_store_count_objects(files->packed, flags, &count); + ret = odb_source_count_objects(&files->packed->base, flags, &count); if (ret < 0) goto out; @@ -133,7 +134,7 @@ static int odb_source_files_find_abbrev_len(struct odb_source *source, unsigned len = min_len; int ret; - ret = packfile_store_find_abbrev_len(files->packed, oid, len, &len); + ret = odb_source_find_abbrev_len(&files->packed->base, oid, len, &len); if (ret < 0) goto out; @@ -152,14 +153,14 @@ static int odb_source_files_freshen_object(struct odb_source *source, const struct object_id *oid) { struct odb_source_files *files = odb_source_files_downcast(source); - if (packfile_store_freshen_object(files->packed, oid) || + if (odb_source_freshen_object(&files->packed->base, oid) || odb_source_freshen_object(&files->loose->base, oid)) return 1; return 0; } static int odb_source_files_write_object(struct odb_source *source, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid, @@ -269,11 +270,11 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb, CALLOC_ARRAY(files, 1); odb_source_init(&files->base, odb, ODB_SOURCE_FILES, path, local); files->loose = odb_source_loose_new(odb, path, local); - files->packed = packfile_store_new(&files->base); + files->packed = odb_source_packed_new(odb, path, local); files->base.free = odb_source_files_free; files->base.close = odb_source_files_close; - files->base.reprepare = odb_source_files_reprepare; + files->base.prepare = odb_source_files_prepare; files->base.read_object_info = odb_source_files_read_object_info; files->base.read_object_stream = odb_source_files_read_object_stream; files->base.for_each_object = odb_source_files_for_each_object;
diff --git a/odb/source-files.h b/odb/source-files.h index 23a3b4e..d7ac3c1 100644 --- a/odb/source-files.h +++ b/odb/source-files.h
@@ -4,7 +4,7 @@ #include "odb/source.h" struct odb_source_loose; -struct packfile_store; +struct odb_source_packed; /* * The files object database source uses a combination of loose objects and @@ -13,7 +13,7 @@ struct packfile_store; struct odb_source_files { struct odb_source base; struct odb_source_loose *loose; - struct packfile_store *packed; + struct odb_source_packed *packed; }; /* Allocate and initialize a new object source. */
diff --git a/odb/source-inmemory.c b/odb/source-inmemory.c index e004566..f41a602 100644 --- a/odb/source-inmemory.c +++ b/odb/source-inmemory.c
@@ -52,7 +52,8 @@ static void populate_object_info(struct odb_source_inmemory *source, *oi->contentp = xmemdupz(object->buf, object->size); if (oi->mtimep) *oi->mtimep = 0; - oi->whence = OI_CACHED; + if (oi->source_infop) + oi->source_infop->source = &source->base; } static int odb_source_inmemory_read_object_info(struct odb_source *source, @@ -227,7 +228,7 @@ static int odb_source_inmemory_count_objects(struct odb_source *source, } static int odb_source_inmemory_write_object(struct odb_source *source, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid UNUSED, @@ -325,7 +326,8 @@ static void odb_source_inmemory_close(struct odb_source *source UNUSED) { } -static void odb_source_inmemory_reprepare(struct odb_source *source UNUSED) +static void odb_source_inmemory_prepare(struct odb_source *source UNUSED, + enum odb_prepare_flags flags UNUSED) { } @@ -365,7 +367,7 @@ struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb) source->base.free = odb_source_inmemory_free; source->base.close = odb_source_inmemory_close; - source->base.reprepare = odb_source_inmemory_reprepare; + source->base.prepare = odb_source_inmemory_prepare; source->base.read_object_info = odb_source_inmemory_read_object_info; source->base.read_object_stream = odb_source_inmemory_read_object_stream; source->base.for_each_object = odb_source_inmemory_for_each_object;
diff --git a/odb/source-loose.c b/odb/source-loose.c index 66e6bb8..091e8e0 100644 --- a/odb/source-loose.c +++ b/odb/source-loose.c
@@ -196,8 +196,8 @@ static int read_object_info_from_path(struct odb_source_loose *loose, oi->typep = NULL; if (oi->delta_base_oid) oidclr(oi->delta_base_oid, loose->base.odb->repo->hash_algo); - if (!ret) - oi->whence = OI_LOOSE; + if (oi->source_infop && !ret) + oi->source_infop->source = &loose->base; } return ret; @@ -583,7 +583,7 @@ static int odb_source_loose_freshen_object(struct odb_source *source, } static int odb_source_loose_write_object(struct odb_source *source, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid_in, enum odb_write_object_flags flags) @@ -593,7 +593,7 @@ static int odb_source_loose_write_object(struct odb_source *source, const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; struct object_id compat_oid; char hdr[MAX_HEADER_LEN]; - int hdrlen = sizeof(hdr); + size_t hdrlen = sizeof(hdr); /* Generate compat_oid */ if (compat) { @@ -664,10 +664,12 @@ static void odb_source_loose_clear_cache(struct odb_source_loose *loose) sizeof(loose->subdir_seen)); } -static void odb_source_loose_reprepare(struct odb_source *source) +static void odb_source_loose_prepare(struct odb_source *source, + enum odb_prepare_flags flags) { struct odb_source_loose *loose = odb_source_loose_downcast(source); - odb_source_loose_clear_cache(loose); + if (flags & ODB_PREPARE_FLUSH_CACHES) + odb_source_loose_clear_cache(loose); } static void odb_source_loose_close(struct odb_source *source UNUSED) @@ -708,7 +710,7 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb, loose->base.free = odb_source_loose_free; loose->base.close = odb_source_loose_close; - loose->base.reprepare = odb_source_loose_reprepare; + loose->base.prepare = odb_source_loose_prepare; loose->base.read_object_info = odb_source_loose_read_object_info; loose->base.read_object_stream = odb_source_loose_read_object_stream; loose->base.for_each_object = odb_source_loose_for_each_object;
diff --git a/odb/source-packed.c b/odb/source-packed.c new file mode 100644 index 0000000..647f23c --- /dev/null +++ b/odb/source-packed.c
@@ -0,0 +1,786 @@ +#include "git-compat-util.h" +#include "abspath.h" +#include "chdir-notify.h" +#include "dir.h" +#include "git-zlib.h" +#include "mergesort.h" +#include "midx.h" +#include "odb/source-packed.h" +#include "odb/streaming.h" +#include "packfile.h" + +static int find_pack_entry(struct odb_source_packed *store, + const struct object_id *oid, + struct pack_entry *e) +{ + struct packfile_list_entry *l; + + odb_source_prepare(&store->base, 0); + if (store->midx && fill_midx_entry(store->midx, oid, e)) + return 1; + + for (l = store->packs.head; l; l = l->next) { + struct packed_git *p = l->pack; + + if (!p->multi_pack_index && packfile_fill_entry(p, oid, e)) { + if (!store->skip_mru_updates) + packfile_list_prepend(&store->packs, p); + return 1; + } + } + + return 0; +} + +static int odb_source_packed_read_object_info(struct odb_source *source, + const struct object_id *oid, + struct object_info *oi, + enum object_info_flags flags) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + struct pack_entry e; + int ret; + + /* + * In case the first read didn't surface the object, we have to reload + * packfiles. This may cause us to discover new packfiles that have + * been added since the last time we have prepared the packfile store. + */ + if (flags & OBJECT_INFO_SECOND_READ) + odb_source_prepare(source, ODB_PREPARE_FLUSH_CACHES); + + if (!find_pack_entry(packed, oid, &e)) + return 1; + + /* + * We know that the caller doesn't actually need the + * information below, so return early. + */ + if (!oi) + return 0; + + ret = packed_object_info(packed, e.p, e.offset, oi); + if (ret < 0) { + mark_bad_packed_object(e.p, oid); + return -1; + } + + return 0; +} + +static int odb_source_packed_read_object_stream(struct odb_read_stream **out, + struct odb_source *source, + const struct object_id *oid) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + struct pack_entry e; + + if (!find_pack_entry(packed, oid, &e)) + return -1; + + return packfile_read_object_stream(out, oid, e.p, e.offset); +} + +struct odb_source_packed_for_each_object_wrapper_data { + struct odb_source_packed *store; + const struct object_info *request; + odb_for_each_object_cb cb; + void *cb_data; +}; + +static int odb_source_packed_for_each_object_wrapper(const struct object_id *oid, + struct packed_git *pack, + uint32_t index_pos, + void *cb_data) +{ + struct odb_source_packed_for_each_object_wrapper_data *data = cb_data; + + if (data->request) { + off_t offset = nth_packed_object_offset(pack, index_pos); + struct object_info oi = *data->request; + + if (packed_object_info_with_index_pos(data->store, pack, offset, + &index_pos, &oi) < 0) { + mark_bad_packed_object(pack, oid); + return -1; + } + + return data->cb(oid, &oi, data->cb_data); + } else { + return data->cb(oid, NULL, data->cb_data); + } +} + +static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b) +{ + do { + if (*a != *b) + return 0; + a++; + b++; + len -= 2; + } while (len > 1); + if (len) + if ((*a ^ *b) & 0xf0) + return 0; + return 1; +} + +static bool should_exclude_pack(struct packed_git *p, enum odb_for_each_object_flags flags) +{ + if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local) + return true; + if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) && + !p->pack_promisor) + return true; + if ((flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) && + p->pack_keep_in_core) + return true; + if ((flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) && + p->pack_keep) + return true; + return false; +} + +static int for_each_prefixed_object_in_midx( + struct odb_source_packed *store, + struct multi_pack_index *m, + const struct odb_for_each_object_options *opts, + struct odb_source_packed_for_each_object_wrapper_data *data) +{ + bool pack_errors = false; + int ret; + + for (; m; m = m->base_midx) { + uint32_t num, i, first = 0; + int len = opts->prefix_hex_len > m->source->base.odb->repo->hash_algo->hexsz ? + m->source->base.odb->repo->hash_algo->hexsz : opts->prefix_hex_len; + + if (!m->num_objects) + continue; + + num = m->num_objects + m->num_objects_in_base; + + bsearch_one_midx(opts->prefix, m, &first); + + /* + * At this point, "first" is the location of the lowest + * object with an object name that could match "opts->prefix". + * See if we have 0, 1 or more objects that actually match(es). + */ + for (i = first; i < num; i++) { + const struct object_id *current = NULL; + struct object_id oid; + + current = nth_midxed_object_oid(&oid, m, i); + + if (!match_hash(len, opts->prefix->hash, current->hash)) + break; + + if (opts->flags) { + uint32_t pack_id = nth_midxed_pack_int_id(m, i); + struct packed_git *pack; + + if (prepare_midx_pack(m, pack_id)) { + pack_errors = true; + continue; + } + + pack = nth_midxed_pack(m, pack_id); + if (should_exclude_pack(pack, opts->flags)) + continue; + } + + if (data->request) { + struct object_info oi = *data->request; + + ret = odb_source_read_object_info(&store->base, current, + &oi, 0); + if (ret) + goto out; + + ret = data->cb(&oid, &oi, data->cb_data); + if (ret) + goto out; + } else { + ret = data->cb(&oid, NULL, data->cb_data); + if (ret) + goto out; + } + } + } + + ret = 0; + +out: + if (!ret && pack_errors) + ret = -1; + return ret; +} + +static int for_each_prefixed_object_in_pack( + struct odb_source_packed *store, + struct packed_git *p, + const struct odb_for_each_object_options *opts, + struct odb_source_packed_for_each_object_wrapper_data *data) +{ + uint32_t num, i, first = 0; + int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ? + p->repo->hash_algo->hexsz : opts->prefix_hex_len; + int ret; + + num = p->num_objects; + bsearch_pack(opts->prefix, p, &first); + + /* + * At this point, "first" is the location of the lowest object + * with an object name that could match "bin_pfx". See if we have + * 0, 1 or more objects that actually match(es). + */ + for (i = first; i < num; i++) { + struct object_id oid; + + nth_packed_object_id(&oid, p, i); + if (!match_hash(len, opts->prefix->hash, oid.hash)) + break; + + if (data->request) { + struct object_info oi = *data->request; + + ret = odb_source_read_object_info(&store->base, &oid, &oi, 0); + if (ret) + goto out; + + ret = data->cb(&oid, &oi, data->cb_data); + if (ret) + goto out; + } else { + ret = data->cb(&oid, NULL, data->cb_data); + if (ret) + goto out; + } + } + + ret = 0; + +out: + return ret; +} + +static int odb_source_packed_for_each_prefixed_object( + struct odb_source_packed *store, + const struct odb_for_each_object_options *opts, + struct odb_source_packed_for_each_object_wrapper_data *data) +{ + struct packfile_list_entry *e; + struct multi_pack_index *m; + bool pack_errors = false; + int ret; + + store->skip_mru_updates = true; + + m = get_multi_pack_index(store); + if (m) { + ret = for_each_prefixed_object_in_midx(store, m, opts, data); + if (ret) + goto out; + } + + for (e = packfile_store_get_packs(store); e; e = e->next) { + if (e->pack->multi_pack_index) + continue; + if (should_exclude_pack(e->pack, opts->flags)) + continue; + + if (open_pack_index(e->pack)) { + pack_errors = true; + continue; + } + + if (!e->pack->num_objects) + continue; + + ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data); + if (ret) + goto out; + } + + ret = 0; + +out: + store->skip_mru_updates = false; + if (!ret && pack_errors) + ret = -1; + return ret; +} + +static int odb_source_packed_for_each_object(struct odb_source *source, + const struct object_info *request, + odb_for_each_object_cb cb, + void *cb_data, + const struct odb_for_each_object_options *opts) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + struct odb_source_packed_for_each_object_wrapper_data data = { + .store = packed, + .request = request, + .cb = cb, + .cb_data = cb_data, + }; + struct packfile_list_entry *e; + int pack_errors = 0, ret; + + if (opts->prefix) + return odb_source_packed_for_each_prefixed_object(packed, opts, &data); + + packed->skip_mru_updates = true; + + for (e = packfile_store_get_packs(packed); e; e = e->next) { + struct packed_git *p = e->pack; + + if (should_exclude_pack(p, opts->flags)) + continue; + + if (open_pack_index(p)) { + pack_errors = 1; + continue; + } + + ret = for_each_object_in_pack(p, odb_source_packed_for_each_object_wrapper, + &data, opts->flags); + if (ret) + goto out; + } + + ret = 0; + +out: + packed->skip_mru_updates = false; + + if (!ret && pack_errors) + ret = -1; + return ret; +} + +static int odb_source_packed_count_objects(struct odb_source *source, + enum odb_count_objects_flags flags UNUSED, + unsigned long *out) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + struct packfile_list_entry *e; + struct multi_pack_index *m; + unsigned long count = 0; + int ret; + + m = get_multi_pack_index(packed); + if (m) + count += m->num_objects + m->num_objects_in_base; + + for (e = packfile_store_get_packs(packed); e; e = e->next) { + if (e->pack->multi_pack_index) + continue; + if (open_pack_index(e->pack)) { + ret = -1; + goto out; + } + + count += e->pack->num_objects; + } + + *out = count; + ret = 0; + +out: + return ret; +} + +static int extend_abbrev_len(const struct object_id *a, + const struct object_id *b, + unsigned *out) +{ + unsigned len = oid_common_prefix_hexlen(a, b); + if (len != hash_algos[a->algo].hexsz && len >= *out) + *out = len + 1; + return 0; +} + +static void find_abbrev_len_for_midx(struct multi_pack_index *m, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + unsigned len = min_len; + + for (; m; m = m->base_midx) { + int match = 0; + uint32_t num, first = 0; + struct object_id found_oid; + + if (!m->num_objects) + continue; + + num = m->num_objects + m->num_objects_in_base; + match = bsearch_one_midx(oid, m, &first); + + /* + * first is now the position in the packfile where we + * would insert the object ID if it does not exist (or the + * position of the object ID if it does exist). Hence, we + * consider a maximum of two objects nearby for the + * abbreviation length. + */ + + if (!match) { + if (nth_midxed_object_oid(&found_oid, m, first)) + extend_abbrev_len(&found_oid, oid, &len); + } else if (first < num - 1) { + if (nth_midxed_object_oid(&found_oid, m, first + 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + if (first > 0) { + if (nth_midxed_object_oid(&found_oid, m, first - 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + } + + *out = len; +} + +static void find_abbrev_len_for_pack(struct packed_git *p, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + int match; + uint32_t num, first = 0; + struct object_id found_oid; + unsigned len = min_len; + + num = p->num_objects; + match = bsearch_pack(oid, p, &first); + + /* + * first is now the position in the packfile where we would insert + * the object ID if it does not exist (or the position of mad->hash if + * it does exist). Hence, we consider a maximum of two objects + * nearby for the abbreviation length. + */ + if (!match) { + if (!nth_packed_object_id(&found_oid, p, first)) + extend_abbrev_len(&found_oid, oid, &len); + } else if (first < num - 1) { + if (!nth_packed_object_id(&found_oid, p, first + 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + if (first > 0) { + if (!nth_packed_object_id(&found_oid, p, first - 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + + *out = len; +} + +static int odb_source_packed_find_abbrev_len(struct odb_source *source, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + struct packfile_list_entry *e; + struct multi_pack_index *m; + + m = get_multi_pack_index(packed); + if (m) + find_abbrev_len_for_midx(m, oid, min_len, &min_len); + + for (e = packfile_store_get_packs(packed); e; e = e->next) { + if (e->pack->multi_pack_index) + continue; + if (open_pack_index(e->pack) || !e->pack->num_objects) + continue; + + find_abbrev_len_for_pack(e->pack, oid, min_len, &min_len); + } + + *out = min_len; + return 0; +} + +static int odb_source_packed_freshen_object(struct odb_source *source, + const struct object_id *oid) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + struct pack_entry e; + + if (!find_pack_entry(packed, oid, &e)) + return 0; + if (e.p->is_cruft) + return 0; + if (e.p->freshened) + return 1; + if (utime(e.p->pack_name, NULL)) + return 0; + e.p->freshened = 1; + + return 1; +} + +static int odb_source_packed_write_object(struct odb_source *source UNUSED, + const void *buf UNUSED, + size_t len UNUSED, + enum object_type type UNUSED, + struct object_id *oid UNUSED, + struct object_id *compat_oid UNUSED, + unsigned flags UNUSED) +{ + return error("packed backend cannot write objects"); +} + +static int odb_source_packed_write_object_stream(struct odb_source *source UNUSED, + struct odb_write_stream *stream UNUSED, + size_t len UNUSED, + struct object_id *oid UNUSED) +{ + return error("packed backend cannot write object streams"); +} + +static int odb_source_packed_begin_transaction(struct odb_source *source UNUSED, + struct odb_transaction **out UNUSED) +{ + return error("packed backend cannot begin transactions"); +} + +static int odb_source_packed_read_alternates(struct odb_source *source UNUSED, + struct strvec *out UNUSED) +{ + return 0; +} + +static int odb_source_packed_write_alternate(struct odb_source *source UNUSED, + const char *alternate UNUSED) +{ + return error("packed backend cannot write alternates"); +} + +void (*report_garbage)(unsigned seen_bits, const char *path); + +static void report_helper(const struct string_list *list, + int seen_bits, int first, int last) +{ + if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX)) + return; + + for (; first < last; first++) + report_garbage(seen_bits, list->items[first].string); +} + +static void report_pack_garbage(struct string_list *list) +{ + int baselen = -1, first = 0, seen_bits = 0; + + if (!report_garbage) + return; + + string_list_sort(list); + + for (size_t i = 0; i < list->nr; i++) { + const char *path = list->items[i].string; + if (baselen != -1 && + strncmp(path, list->items[first].string, baselen)) { + report_helper(list, seen_bits, first, i); + baselen = -1; + seen_bits = 0; + } + if (baselen == -1) { + const char *dot = strrchr(path, '.'); + if (!dot) { + report_garbage(PACKDIR_FILE_GARBAGE, path); + continue; + } + baselen = dot - path + 1; + first = i; + } + if (!strcmp(path + baselen, "pack")) + seen_bits |= 1; + else if (!strcmp(path + baselen, "idx")) + seen_bits |= 2; + } + report_helper(list, seen_bits, first, list->nr); +} + +struct prepare_pack_data { + struct odb_source_packed *source; + struct string_list *garbage; +}; + +static void prepare_pack(const char *full_name, size_t full_name_len, + const char *file_name, void *_data) +{ + struct prepare_pack_data *data = (struct prepare_pack_data *)_data; + size_t base_len = full_name_len; + + if (strip_suffix_mem(full_name, &base_len, ".idx") && + !(data->source->midx && + midx_contains_pack(data->source->midx, file_name))) { + char *trimmed_path = xstrndup(full_name, full_name_len); + packfile_store_load_pack(data->source, + trimmed_path, data->source->base.local); + free(trimmed_path); + } + + if (!report_garbage) + return; + + if (!strcmp(file_name, "multi-pack-index") || + !strcmp(file_name, "multi-pack-index.d")) + return; + if (starts_with(file_name, "multi-pack-index") && + (ends_with(file_name, ".bitmap") || ends_with(file_name, ".rev"))) + return; + if (ends_with(file_name, ".idx") || + ends_with(file_name, ".rev") || + ends_with(file_name, ".pack") || + ends_with(file_name, ".bitmap") || + ends_with(file_name, ".keep") || + ends_with(file_name, ".promisor") || + ends_with(file_name, ".mtimes")) + string_list_append(data->garbage, full_name); + else + report_garbage(PACKDIR_FILE_GARBAGE, full_name); +} + +static void prepare_packed_git_one(struct odb_source_packed *source) +{ + struct string_list garbage = STRING_LIST_INIT_DUP; + struct prepare_pack_data data = { + .source = source, + .garbage = &garbage, + }; + + for_each_file_in_pack_dir(source->base.path, prepare_pack, &data); + + report_pack_garbage(data.garbage); + string_list_clear(data.garbage, 0); +} + +DEFINE_LIST_SORT(static, sort_packs, struct packfile_list_entry, next); + +static int sort_pack(const struct packfile_list_entry *a, + const struct packfile_list_entry *b) +{ + int st; + + /* + * Local packs tend to contain objects specific to our + * variant of the project than remote ones. In addition, + * remote ones could be on a network mounted filesystem. + * Favor local ones for these reasons. + */ + st = a->pack->pack_local - b->pack->pack_local; + if (st) + return -st; + + /* + * Younger packs tend to contain more recent objects, + * and more recent objects tend to get accessed more + * often. + */ + if (a->pack->mtime < b->pack->mtime) + return 1; + else if (a->pack->mtime == b->pack->mtime) + return 0; + return -1; +} + +static void odb_source_packed_prepare(struct odb_source *source, + enum odb_prepare_flags flags) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + + if (flags & ODB_PREPARE_FLUSH_CACHES) + packed->initialized = false; + if (packed->initialized) + return; + + prepare_multi_pack_index_one(packed); + prepare_packed_git_one(packed); + + sort_packs(&packed->packs.head, sort_pack); + for (struct packfile_list_entry *e = packed->packs.head; e; e = e->next) + if (!e->next) + packed->packs.tail = e; + + packed->initialized = true; +} + +static void odb_source_packed_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *cb_data) +{ + struct odb_source_packed *packed = cb_data; + char *path = reparent_relative_path(old_cwd, new_cwd, + packed->base.path); + free(packed->base.path); + packed->base.path = path; +} + +static void odb_source_packed_close(struct odb_source *source) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + + for (struct packfile_list_entry *e = packed->packs.head; e; e = e->next) { + if (e->pack->do_not_close) + BUG("want to close pack marked 'do-not-close'"); + close_pack(e->pack); + } + if (packed->midx) + close_midx(packed->midx); + packed->midx = NULL; +} + +static void odb_source_packed_free(struct odb_source *source) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + + chdir_notify_unregister(NULL, odb_source_packed_reparent, packed); + + for (struct packfile_list_entry *e = packed->packs.head; e; e = e->next) + free(e->pack); + packfile_list_clear(&packed->packs); + + strmap_clear(&packed->packs_by_path, 0); + odb_source_release(&packed->base); + free(packed); +} + +struct odb_source_packed *odb_source_packed_new(struct object_database *odb, + const char *path, + bool local) +{ + struct odb_source_packed *packed; + + CALLOC_ARRAY(packed, 1); + odb_source_init(&packed->base, odb, ODB_SOURCE_PACKED, path, local); + strmap_init(&packed->packs_by_path); + + packed->base.free = odb_source_packed_free; + packed->base.close = odb_source_packed_close; + packed->base.prepare = odb_source_packed_prepare; + packed->base.read_object_info = odb_source_packed_read_object_info; + packed->base.read_object_stream = odb_source_packed_read_object_stream; + packed->base.for_each_object = odb_source_packed_for_each_object; + packed->base.count_objects = odb_source_packed_count_objects; + packed->base.find_abbrev_len = odb_source_packed_find_abbrev_len; + packed->base.freshen_object = odb_source_packed_freshen_object; + packed->base.write_object = odb_source_packed_write_object; + packed->base.write_object_stream = odb_source_packed_write_object_stream; + packed->base.begin_transaction = odb_source_packed_begin_transaction; + packed->base.read_alternates = odb_source_packed_read_alternates; + packed->base.write_alternate = odb_source_packed_write_alternate; + + if (!is_absolute_path(path)) + chdir_notify_register(NULL, odb_source_packed_reparent, packed); + + return packed; +}
diff --git a/odb/source-packed.h b/odb/source-packed.h new file mode 100644 index 0000000..77309dd --- /dev/null +++ b/odb/source-packed.h
@@ -0,0 +1,85 @@ +#ifndef ODB_SOURCE_PACKED_H +#define ODB_SOURCE_PACKED_H + +#include "odb/source.h" +#include "packfile-list.h" +#include "strmap.h" + +/* + * A store that manages packfiles for a given object database. + */ +struct odb_source_packed { + struct odb_source base; + + /* + * The list of packfiles in the order in which they have been most + * recently used. + */ + struct packfile_list packs; + + /* + * Cache of packfiles which are marked as "kept", either because there + * is an on-disk ".keep" file or because they are marked as "kept" in + * memory. + * + * Should not be accessed directly, but via + * `packfile_store_get_kept_pack_cache()`. The list of packs gets + * invalidated when the stored flags and the flags passed to + * `packfile_store_get_kept_pack_cache()` mismatch. + */ + struct { + struct packed_git **packs; + unsigned flags; + } kept_cache; + + /* The multi-pack index that belongs to this specific packfile store. */ + struct multi_pack_index *midx; + + /* + * A map of packfile names to packed_git structs for tracking which + * packs have been loaded already. + */ + struct strmap packs_by_path; + + /* + * Whether packfiles have already been populated with this store's + * packs. + */ + bool initialized; + + /* + * Usually, packfiles will be reordered to the front of the `packs` + * list whenever an object is looked up via them. This has the effect + * that packs that contain a lot of accessed objects will be located + * towards the front. + * + * This is usually desirable, but there are exceptions. One exception + * is when the looking up multiple objects in a loop for each packfile. + * In that case, we may easily end up with an infinite loop as the + * packfiles get reordered to the front repeatedly. + * + * Setting this field to `true` thus disables these reorderings. + */ + bool skip_mru_updates; +}; + +/* + * Allocate and initialize a new empty packfile store for the given object + * database. + */ +struct odb_source_packed *odb_source_packed_new(struct object_database *odb, + const char *path, + bool local); + +/* + * Cast the given object database source to the packed backend. This will cause + * a BUG in case the source doesn't use this backend. + */ +static inline struct odb_source_packed *odb_source_packed_downcast(struct odb_source *source) +{ + if (source->type != ODB_SOURCE_PACKED) + BUG("trying to downcast source of type '%d' to packed", source->type); + return container_of(source, struct odb_source_packed, base); +} + +#endif
diff --git a/odb/source.h b/odb/source.h index 2192a10..efeff7e 100644 --- a/odb/source.h +++ b/odb/source.h
@@ -17,6 +17,9 @@ enum odb_source_type { /* The "loose" backend that uses loose objects, only. */ ODB_SOURCE_LOOSE, + /* The "packed" backend that uses packfiles. */ + ODB_SOURCE_PACKED, + /* The "in-memory" backend that stores objects in memory. */ ODB_SOURCE_INMEMORY, }; @@ -80,11 +83,12 @@ struct odb_source { void (*close)(struct odb_source *source); /* - * This callback is expected to clear underlying caches of the object - * database source. The function is called when the repository has for - * example just been repacked so that new objects will become visible. + * This callback is expected to prepare the source so that it becomes + * ready for use. It optionally clears underlying caches of the object + * database source. */ - void (*reprepare)(struct odb_source *source); + void (*prepare)(struct odb_source *source, + enum odb_prepare_flags flags); /* * This callback is expected to read object information from the object @@ -199,7 +203,7 @@ struct odb_source { * return 0 on success, a negative error code otherwise. */ int (*write_object)(struct odb_source *source, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid, @@ -305,13 +309,14 @@ static inline void odb_source_close(struct odb_source *source) } /* - * Reprepare the object database source and clear any caches. Depending on the + * Prepare the object database source and clear any caches. Depending on the * backend used this may have the effect that concurrently-written objects * become visible. */ -static inline void odb_source_reprepare(struct odb_source *source) +static inline void odb_source_prepare(struct odb_source *source, + enum odb_prepare_flags flags) { - source->reprepare(source); + source->prepare(source, flags); } /*
diff --git a/oss-fuzz/.gitignore b/oss-fuzz/.gitignore index f2d74de..dc7a127 100644 --- a/oss-fuzz/.gitignore +++ b/oss-fuzz/.gitignore
@@ -5,4 +5,5 @@ fuzz-pack-headers fuzz-pack-idx fuzz-parse-attr-line +fuzz-reftable fuzz-url-decode-mem
diff --git a/oss-fuzz/fuzz-reftable.c b/oss-fuzz/fuzz-reftable.c new file mode 100644 index 0000000..c46eac2 --- /dev/null +++ b/oss-fuzz/fuzz-reftable.c
@@ -0,0 +1,74 @@ +#include "git-compat-util.h" +#include "reftable/basics.h" +#include "reftable/blocksource.h" +#include "reftable/reftable-blocksource.h" +#include "reftable/reftable-error.h" +#include "reftable/reftable-iterator.h" +#include "reftable/reftable-record.h" +#include "reftable/reftable-table.h" +#include "reftable/reftable-writer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct reftable_block_source source = { 0 }; + struct reftable_buf buf = REFTABLE_BUF_INIT; + struct reftable_table *table = NULL; + int err; + + if (reftable_buf_add(&buf, (const char *)data, size) < 0) + goto out; + block_source_from_buf(&source, &buf); + + err = reftable_table_new(&table, &source, "fuzz-input"); + if (err < 0) + goto out; + + /* + * Exercise the ref, log and raw block iterators so that we cover as + * much of the parsing code as possible. + */ + { + struct reftable_ref_record ref = { 0 }; + struct reftable_iterator it = { 0 }; + + reftable_table_init_ref_iterator(table, &it); + if (!reftable_iterator_seek_ref(&it, "")) + while (!reftable_iterator_next_ref(&it, &ref)) + ; + + reftable_ref_record_release(&ref); + reftable_iterator_destroy(&it); + } + + { + struct reftable_log_record log = { 0 }; + struct reftable_iterator it = { 0 }; + + reftable_table_init_log_iterator(table, &it); + if (!reftable_iterator_seek_log(&it, "")) + while (!reftable_iterator_next_log(&it, &log)) + ; + + reftable_log_record_release(&log); + reftable_iterator_destroy(&it); + } + + { + struct reftable_table_iterator it = { 0 }; + const struct reftable_block *block; + + if (!reftable_table_iterator_init(&it, table)) + while (!reftable_table_iterator_next(&it, &block)) + ; + + reftable_table_iterator_release(&it); + } + +out: + if (table) + reftable_table_decref(table); + reftable_buf_release(&buf); + return 0; +}
diff --git a/oss-fuzz/meson.build b/oss-fuzz/meson.build index 878afd8..5a38542 100644 --- a/oss-fuzz/meson.build +++ b/oss-fuzz/meson.build
@@ -6,6 +6,7 @@ 'fuzz-pack-headers.c', 'fuzz-pack-idx.c', 'fuzz-parse-attr-line.c', + 'fuzz-reftable.c', 'fuzz-url-decode-mem.c', ] @@ -16,5 +17,6 @@ fuzz_program, ], dependencies: [libgit_commonmain], + link_args: get_option('fuzzers_link_args'), ) endforeach
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index 1bcb3f9..acbea89 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c
@@ -636,6 +636,8 @@ static int fill_bitmap_commit(struct bitmap_writer *writer, struct bitmap_index *old_bitmap, const uint32_t *mapping) { + struct commit *c; + struct tree *t; int found; int from_pseudo_merge = commit->object.flags & BITMAP_PSEUDO_MERGE; uint32_t pos; @@ -650,9 +652,8 @@ static int fill_bitmap_commit(struct bitmap_writer *writer, prio_queue_put(queue, commit); - while (queue->nr) { + while ((c = prio_queue_get(queue))) { struct commit_list *p; - struct commit *c = prio_queue_get(queue); if (old_bitmap && mapping) { struct ewah_bitmap *old; @@ -740,8 +741,7 @@ static int fill_bitmap_commit(struct bitmap_writer *writer, } } - while (tree_queue->nr) { - struct tree *t = prio_queue_get(tree_queue); + while ((t = prio_queue_get(tree_queue))) { int found; pos = find_object_pos(writer, &t->object.oid, &found);
diff --git a/pack-bitmap.c b/pack-bitmap.c index e8a8294..1fc3e62 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c
@@ -238,7 +238,7 @@ static uint32_t bitmap_name_hash(struct bitmap_index *index, uint32_t pos) static struct repository *bitmap_repo(struct bitmap_index *bitmap_git) { if (bitmap_is_midx(bitmap_git)) - return bitmap_git->midx->source->odb->repo; + return bitmap_git->midx->source->base.odb->repo; return bitmap_git->pack->repo; } @@ -523,6 +523,10 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git, if (midx->base_midx) { bitmap_git->base = prepare_midx_bitmap_git(midx->base_midx); + if (!bitmap_git->base) { + warning(_("could not open bitmap for base MIDX")); + goto cleanup; + } bitmap_git->base_nr = bitmap_git->base->base_nr + 1; } else { bitmap_git->base_nr = 0; @@ -711,7 +715,8 @@ static int open_midx_bitmap(struct repository *r, odb_prepare_alternates(r->objects); for (source = r->objects->sources; source; source = source->next) { - struct multi_pack_index *midx = get_multi_pack_index(source); + struct odb_source_files *files = odb_source_files_downcast(source); + struct multi_pack_index *midx = get_multi_pack_index(files->packed); if (midx && !open_midx_bitmap_1(bitmap_git, midx)) ret = 0; } @@ -1876,7 +1881,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git, ofs = pack_pos_to_offset(pack, pos); } - if (packed_object_info(pack, ofs, &oi) < 0) { + if (packed_object_info(NULL, pack, ofs, &oi) < 0) { struct object_id oid; nth_bitmap_object_oid(bitmap_git, &oid, pack_pos_to_index(pack, pos)); @@ -3399,7 +3404,8 @@ int verify_bitmap_files(struct repository *r) odb_prepare_alternates(r->objects); for (source = r->objects->sources; source; source = source->next) { - struct multi_pack_index *m = get_multi_pack_index(source); + struct odb_source_files *files = odb_source_files_downcast(source); + struct multi_pack_index *m = get_multi_pack_index(files->packed); char *midx_bitmap_name; if (!m)
diff --git a/pack-check.c b/pack-check.c index 5adfb3f..c3b8db7 100644 --- a/pack-check.c +++ b/pack-check.c
@@ -69,7 +69,7 @@ static int verify_packfile(struct repository *r, if (!is_pack_valid(p)) return error("packfile %s cannot be accessed", p->pack_name); - r->hash_algo->init_fn(&ctx); + git_hash_init(&ctx, r->hash_algo); do { unsigned long remaining; unsigned char *in = use_pack(p, w_curs, offset, &remaining);
diff --git a/pack-revindex.c b/pack-revindex.c index 1b67863..62387ae 100644 --- a/pack-revindex.c +++ b/pack-revindex.c
@@ -383,13 +383,13 @@ int load_midx_revindex(struct multi_pack_index *m) * not want to accidentally call munmap() in the middle of the * MIDX. */ - trace2_data_string("load_midx_revindex", m->source->odb->repo, + trace2_data_string("load_midx_revindex", m->source->base.odb->repo, "source", "midx"); m->revindex_data = (const uint32_t *)m->chunk_revindex; return 0; } - trace2_data_string("load_midx_revindex", m->source->odb->repo, + trace2_data_string("load_midx_revindex", m->source->base.odb->repo, "source", "rev"); if (m->has_chain) @@ -401,7 +401,7 @@ int load_midx_revindex(struct multi_pack_index *m) midx_get_checksum_hash(m), MIDX_EXT_REV); - ret = load_revindex_from_disk(m->source->odb->repo->hash_algo, + ret = load_revindex_from_disk(m->source->base.odb->repo->hash_algo, revindex_name.buf, m->num_objects, &m->revindex_map,
diff --git a/pack-write.c b/pack-write.c index 83eaf88..24033a9 100644 --- a/pack-write.c +++ b/pack-write.c
@@ -402,8 +402,8 @@ void fixup_pack_header_footer(const struct git_hash_algo *hash_algo, char *buf; ssize_t read_result; - hash_algo->init_fn(&old_hash_ctx); - hash_algo->init_fn(&new_hash_ctx); + git_hash_init(&old_hash_ctx, hash_algo); + git_hash_init(&new_hash_ctx, hash_algo); if (lseek(pack_fd, 0, SEEK_SET) != 0) die_errno("Failed seeking to start of '%s'", pack_name); @@ -455,7 +455,7 @@ void fixup_pack_header_footer(const struct git_hash_algo *hash_algo, * pack, which also means making partial_pack_offset * big enough not to matter anymore. */ - hash_algo->init_fn(&old_hash_ctx); + git_hash_init(&old_hash_ctx, hash_algo); partial_pack_offset = ~partial_pack_offset; partial_pack_offset -= MSB(partial_pack_offset, 1); }
diff --git a/packfile-list.c b/packfile-list.c new file mode 100644 index 0000000..01fb913 --- /dev/null +++ b/packfile-list.c
@@ -0,0 +1,86 @@ +#include "git-compat-util.h" +#include "packfile.h" +#include "packfile-list.h" + +void packfile_list_clear(struct packfile_list *list) +{ + struct packfile_list_entry *e, *next; + + for (e = list->head; e; e = next) { + next = e->next; + free(e); + } + + list->head = list->tail = NULL; +} + +static struct packfile_list_entry *packfile_list_remove_internal(struct packfile_list *list, + struct packed_git *pack) +{ + struct packfile_list_entry *e, *prev; + + for (e = list->head, prev = NULL; e; prev = e, e = e->next) { + if (e->pack != pack) + continue; + + if (prev) + prev->next = e->next; + if (list->head == e) + list->head = e->next; + if (list->tail == e) + list->tail = prev; + + return e; + } + + return NULL; +} + +void packfile_list_remove(struct packfile_list *list, struct packed_git *pack) +{ + free(packfile_list_remove_internal(list, pack)); +} + +void packfile_list_prepend(struct packfile_list *list, struct packed_git *pack) +{ + struct packfile_list_entry *entry; + + entry = packfile_list_remove_internal(list, pack); + if (!entry) { + entry = xmalloc(sizeof(*entry)); + entry->pack = pack; + } + entry->next = list->head; + + list->head = entry; + if (!list->tail) + list->tail = entry; +} + +void packfile_list_append(struct packfile_list *list, struct packed_git *pack) +{ + struct packfile_list_entry *entry; + + entry = packfile_list_remove_internal(list, pack); + if (!entry) { + entry = xmalloc(sizeof(*entry)); + entry->pack = pack; + } + entry->next = NULL; + + if (list->tail) { + list->tail->next = entry; + list->tail = entry; + } else { + list->head = list->tail = entry; + } +} + +struct packed_git *packfile_list_find_oid(struct packfile_list_entry *packs, + const struct object_id *oid) +{ + for (; packs; packs = packs->next) + if (find_pack_entry_one(oid, packs->pack)) + return packs->pack; + return NULL; +}
diff --git a/packfile-list.h b/packfile-list.h new file mode 100644 index 0000000..1b05e2a --- /dev/null +++ b/packfile-list.h
@@ -0,0 +1,28 @@ +#ifndef PACKFILE_LIST_H +#define PACKFILE_LIST_H + +struct object_id; + +struct packfile_list { + struct packfile_list_entry *head, *tail; +}; + +struct packfile_list_entry { + struct packfile_list_entry *next; + struct packed_git *pack; +}; + +void packfile_list_clear(struct packfile_list *list); +void packfile_list_remove(struct packfile_list *list, struct packed_git *pack); +void packfile_list_prepend(struct packfile_list *list, struct packed_git *pack); +void packfile_list_append(struct packfile_list *list, struct packed_git *pack); + +/* + * Find the pack within the "packs" list whose index contains the object + * "oid". For general object lookups, you probably don't want this; use + * find_pack_entry() instead. + */ +struct packed_git *packfile_list_find_oid(struct packfile_list_entry *packs, + const struct object_id *oid); + +#endif
diff --git a/packfile.c b/packfile.c index 78c389e..0eee450 100644 --- a/packfile.c +++ b/packfile.c
@@ -8,7 +8,6 @@ #include "pack.h" #include "repository.h" #include "dir.h" -#include "mergesort.h" #include "packfile.h" #include "delta.h" #include "hash-lookup.h" @@ -48,89 +47,6 @@ static size_t pack_mapped; #define SZ_FMT PRIuMAX static inline uintmax_t sz_fmt(size_t s) { return s; } -void packfile_list_clear(struct packfile_list *list) -{ - struct packfile_list_entry *e, *next; - - for (e = list->head; e; e = next) { - next = e->next; - free(e); - } - - list->head = list->tail = NULL; -} - -static struct packfile_list_entry *packfile_list_remove_internal(struct packfile_list *list, - struct packed_git *pack) -{ - struct packfile_list_entry *e, *prev; - - for (e = list->head, prev = NULL; e; prev = e, e = e->next) { - if (e->pack != pack) - continue; - - if (prev) - prev->next = e->next; - if (list->head == e) - list->head = e->next; - if (list->tail == e) - list->tail = prev; - - return e; - } - - return NULL; -} - -void packfile_list_remove(struct packfile_list *list, struct packed_git *pack) -{ - free(packfile_list_remove_internal(list, pack)); -} - -void packfile_list_prepend(struct packfile_list *list, struct packed_git *pack) -{ - struct packfile_list_entry *entry; - - entry = packfile_list_remove_internal(list, pack); - if (!entry) { - entry = xmalloc(sizeof(*entry)); - entry->pack = pack; - } - entry->next = list->head; - - list->head = entry; - if (!list->tail) - list->tail = entry; -} - -void packfile_list_append(struct packfile_list *list, struct packed_git *pack) -{ - struct packfile_list_entry *entry; - - entry = packfile_list_remove_internal(list, pack); - if (!entry) { - entry = xmalloc(sizeof(*entry)); - entry->pack = pack; - } - entry->next = NULL; - - if (list->tail) { - list->tail->next = entry; - list->tail = entry; - } else { - list->head = list->tail = entry; - } -} - -struct packed_git *packfile_list_find_oid(struct packfile_list_entry *packs, - const struct object_id *oid) -{ - for (; packs; packs = packs->next) - if (find_pack_entry_one(oid, packs->pack)) - return packs->pack; - return NULL; -} - void pack_report(struct repository *repo) { fprintf(stderr, @@ -859,7 +775,7 @@ struct packed_git *add_packed_git(struct repository *r, const char *path, return p; } -void packfile_store_add_pack(struct packfile_store *store, +void packfile_store_add_pack(struct odb_source_packed *store, struct packed_git *pack) { if (pack->pack_fd != -1) @@ -869,7 +785,7 @@ void packfile_store_add_pack(struct packfile_store *store, strmap_put(&store->packs_by_path, pack->pack_name, pack); } -struct packed_git *packfile_store_load_pack(struct packfile_store *store, +struct packed_git *packfile_store_load_pack(struct odb_source_packed *store, const char *idx_path, int local) { struct strbuf key = STRBUF_INIT; @@ -885,7 +801,7 @@ struct packed_git *packfile_store_load_pack(struct packfile_store *store, p = strmap_get(&store->packs_by_path, key.buf); if (!p) { - p = add_packed_git(store->source->odb->repo, idx_path, + p = add_packed_git(store->base.odb->repo, idx_path, strlen(idx_path), local); if (p) packfile_store_add_pack(store, p); @@ -895,52 +811,6 @@ struct packed_git *packfile_store_load_pack(struct packfile_store *store, return p; } -void (*report_garbage)(unsigned seen_bits, const char *path); - -static void report_helper(const struct string_list *list, - int seen_bits, int first, int last) -{ - if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX)) - return; - - for (; first < last; first++) - report_garbage(seen_bits, list->items[first].string); -} - -static void report_pack_garbage(struct string_list *list) -{ - int i, baselen = -1, first = 0, seen_bits = 0; - - if (!report_garbage) - return; - - string_list_sort(list); - - for (i = 0; i < list->nr; i++) { - const char *path = list->items[i].string; - if (baselen != -1 && - strncmp(path, list->items[first].string, baselen)) { - report_helper(list, seen_bits, first, i); - baselen = -1; - seen_bits = 0; - } - if (baselen == -1) { - const char *dot = strrchr(path, '.'); - if (!dot) { - report_garbage(PACKDIR_FILE_GARBAGE, path); - continue; - } - baselen = dot - path + 1; - first = i; - } - if (!strcmp(path + baselen, "pack")) - seen_bits |= 1; - else if (!strcmp(path + baselen, "idx")) - seen_bits |= 2; - } - report_helper(list, seen_bits, first, list->nr); -} - void for_each_file_in_pack_subdir(const char *objdir, const char *subdir, each_file_in_pack_dir_fn fn, @@ -983,116 +853,9 @@ void for_each_file_in_pack_dir(const char *objdir, for_each_file_in_pack_subdir(objdir, NULL, fn, data); } -struct prepare_pack_data { - struct odb_source *source; - struct string_list *garbage; -}; - -static void prepare_pack(const char *full_name, size_t full_name_len, - const char *file_name, void *_data) +struct packfile_list_entry *packfile_store_get_packs(struct odb_source_packed *store) { - struct prepare_pack_data *data = (struct prepare_pack_data *)_data; - struct odb_source_files *files = odb_source_files_downcast(data->source); - size_t base_len = full_name_len; - - if (strip_suffix_mem(full_name, &base_len, ".idx") && - !(files->packed->midx && - midx_contains_pack(files->packed->midx, file_name))) { - char *trimmed_path = xstrndup(full_name, full_name_len); - packfile_store_load_pack(files->packed, - trimmed_path, data->source->local); - free(trimmed_path); - } - - if (!report_garbage) - return; - - if (!strcmp(file_name, "multi-pack-index") || - !strcmp(file_name, "multi-pack-index.d")) - return; - if (starts_with(file_name, "multi-pack-index") && - (ends_with(file_name, ".bitmap") || ends_with(file_name, ".rev"))) - return; - if (ends_with(file_name, ".idx") || - ends_with(file_name, ".rev") || - ends_with(file_name, ".pack") || - ends_with(file_name, ".bitmap") || - ends_with(file_name, ".keep") || - ends_with(file_name, ".promisor") || - ends_with(file_name, ".mtimes")) - string_list_append(data->garbage, full_name); - else - report_garbage(PACKDIR_FILE_GARBAGE, full_name); -} - -static void prepare_packed_git_one(struct odb_source *source) -{ - struct string_list garbage = STRING_LIST_INIT_DUP; - struct prepare_pack_data data = { - .source = source, - .garbage = &garbage, - }; - - for_each_file_in_pack_dir(source->path, prepare_pack, &data); - - report_pack_garbage(data.garbage); - string_list_clear(data.garbage, 0); -} - -DEFINE_LIST_SORT(static, sort_packs, struct packfile_list_entry, next); - -static int sort_pack(const struct packfile_list_entry *a, - const struct packfile_list_entry *b) -{ - int st; - - /* - * Local packs tend to contain objects specific to our - * variant of the project than remote ones. In addition, - * remote ones could be on a network mounted filesystem. - * Favor local ones for these reasons. - */ - st = a->pack->pack_local - b->pack->pack_local; - if (st) - return -st; - - /* - * Younger packs tend to contain more recent objects, - * and more recent objects tend to get accessed more - * often. - */ - if (a->pack->mtime < b->pack->mtime) - return 1; - else if (a->pack->mtime == b->pack->mtime) - return 0; - return -1; -} - -void packfile_store_prepare(struct packfile_store *store) -{ - if (store->initialized) - return; - - prepare_multi_pack_index_one(store->source); - prepare_packed_git_one(store->source); - - sort_packs(&store->packs.head, sort_pack); - for (struct packfile_list_entry *e = store->packs.head; e; e = e->next) - if (!e->next) - store->packs.tail = e; - - store->initialized = true; -} - -void packfile_store_reprepare(struct packfile_store *store) -{ - store->initialized = false; - packfile_store_prepare(store); -} - -struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *store) -{ - packfile_store_prepare(store); + odb_source_prepare(&store->base, 0); if (store->midx) { struct multi_pack_index *m = store->midx; @@ -1103,37 +866,6 @@ struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *stor return store->packs.head; } -int packfile_store_count_objects(struct packfile_store *store, - enum odb_count_objects_flags flags UNUSED, - unsigned long *out) -{ - struct packfile_list_entry *e; - struct multi_pack_index *m; - unsigned long count = 0; - int ret; - - m = get_multi_pack_index(store->source); - if (m) - count += m->num_objects + m->num_objects_in_base; - - for (e = packfile_store_get_packs(store); e; e = e->next) { - if (e->pack->multi_pack_index) - continue; - if (open_pack_index(e->pack)) { - ret = -1; - goto out; - } - - count += e->pack->num_objects; - } - - *out = count; - ret = 0; - -out: - return ret; -} - unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, size_t *sizep) { @@ -1592,8 +1324,9 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset, hashmap_add(&delta_base_cache, &ent->ent); } -static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_offset, - uint32_t *maybe_index_pos, struct object_info *oi) +int packed_object_info_with_index_pos(struct odb_source_packed *source, + struct packed_git *p, off_t obj_offset, + uint32_t *maybe_index_pos, struct object_info *oi) { struct pack_window *w_curs = NULL; size_t size; @@ -1688,23 +1421,28 @@ static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_off oidclr(oi->delta_base_oid, p->repo->hash_algo); } - oi->whence = OI_PACKED; - oi->u.packed.offset = obj_offset; - oi->u.packed.pack = p; + if (oi->source_infop) { + if (!source) + BUG("cannot request source without an owning source"); + oi->source_infop->source = &source->base; - switch (type) { - case OBJ_NONE: - oi->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN; - break; - case OBJ_REF_DELTA: - oi->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA; - break; - case OBJ_OFS_DELTA: - oi->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA; - break; - default: - oi->u.packed.type = PACKED_OBJECT_TYPE_FULL; - break; + oi->source_infop->u.packed.offset = obj_offset; + oi->source_infop->u.packed.pack = p; + + switch (type) { + case OBJ_NONE: + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_UNKNOWN; + break; + case OBJ_REF_DELTA: + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_REF_DELTA; + break; + case OBJ_OFS_DELTA: + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_OFS_DELTA; + break; + default: + oi->source_infop->u.packed.type = PACKED_OBJECT_TYPE_FULL; + break; + } } ret = 0; @@ -1714,10 +1452,11 @@ static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_off return ret; } -int packed_object_info(struct packed_git *p, off_t obj_offset, +int packed_object_info(struct odb_source_packed *source, + struct packed_git *p, off_t obj_offset, struct object_info *oi) { - return packed_object_info_with_index_pos(p, obj_offset, NULL, oi); + return packed_object_info_with_index_pos(source, p, obj_offset, NULL, oi); } static void *unpack_compressed_entry(struct packed_git *p, @@ -2118,9 +1857,9 @@ int is_pack_valid(struct packed_git *p) return !open_packed_git(p); } -static int fill_pack_entry(const struct object_id *oid, - struct pack_entry *e, - struct packed_git *p) +int packfile_fill_entry(struct packed_git *p, + const struct object_id *oid, + struct pack_entry *e) { off_t offset; @@ -2146,81 +1885,7 @@ static int fill_pack_entry(const struct object_id *oid, return 1; } -static int find_pack_entry(struct packfile_store *store, - const struct object_id *oid, - struct pack_entry *e) -{ - struct packfile_list_entry *l; - - packfile_store_prepare(store); - if (store->midx && fill_midx_entry(store->midx, oid, e)) - return 1; - - for (l = store->packs.head; l; l = l->next) { - struct packed_git *p = l->pack; - - if (!p->multi_pack_index && fill_pack_entry(oid, e, p)) { - if (!store->skip_mru_updates) - packfile_list_prepend(&store->packs, p); - return 1; - } - } - - return 0; -} - -int packfile_store_freshen_object(struct packfile_store *store, - const struct object_id *oid) -{ - struct pack_entry e; - if (!find_pack_entry(store, oid, &e)) - return 0; - if (e.p->is_cruft) - return 0; - if (e.p->freshened) - return 1; - if (utime(e.p->pack_name, NULL)) - return 0; - e.p->freshened = 1; - return 1; -} - -int packfile_store_read_object_info(struct packfile_store *store, - const struct object_id *oid, - struct object_info *oi, - enum object_info_flags flags) -{ - struct pack_entry e; - int ret; - - /* - * In case the first read didn't surface the object, we have to reload - * packfiles. This may cause us to discover new packfiles that have - * been added since the last time we have prepared the packfile store. - */ - if (flags & OBJECT_INFO_SECOND_READ) - packfile_store_reprepare(store); - - if (!find_pack_entry(store, oid, &e)) - return 1; - - /* - * We know that the caller doesn't actually need the - * information below, so return early. - */ - if (!oi) - return 0; - - ret = packed_object_info(e.p, e.offset, oi); - if (ret < 0) { - mark_bad_packed_object(e.p, oid); - return -1; - } - - return 0; -} - -static void maybe_invalidate_kept_pack_cache(struct packfile_store *store, +static void maybe_invalidate_kept_pack_cache(struct odb_source_packed *store, unsigned flags) { if (!store->kept_cache.packs) @@ -2231,7 +1896,7 @@ static void maybe_invalidate_kept_pack_cache(struct packfile_store *store, store->kept_cache.flags = 0; } -struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *store, +struct packed_git **packfile_store_get_kept_pack_cache(struct odb_source_packed *store, unsigned flags) { maybe_invalidate_kept_pack_cache(store, flags); @@ -2272,14 +1937,12 @@ struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *st int has_object_pack(struct repository *r, const struct object_id *oid) { struct odb_source *source; - struct pack_entry e; odb_prepare_alternates(r->objects); for (source = r->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); - int ret = find_pack_entry(files->packed, oid, &e); - if (ret) - return ret; + if (!odb_source_read_object_info(&files->packed->base, oid, NULL, 0)) + return 1; } return 0; @@ -2299,7 +1962,7 @@ int has_object_kept_pack(struct repository *r, const struct object_id *oid, for (; *cache; cache++) { struct packed_git *p = *cache; - if (fill_pack_entry(oid, &e, p)) + if (packfile_fill_entry(p, oid, &e)) return 1; } } @@ -2351,373 +2014,6 @@ int for_each_object_in_pack(struct packed_git *p, return r; } -struct packfile_store_for_each_object_wrapper_data { - struct packfile_store *store; - const struct object_info *request; - odb_for_each_object_cb cb; - void *cb_data; -}; - -static int packfile_store_for_each_object_wrapper(const struct object_id *oid, - struct packed_git *pack, - uint32_t index_pos, - void *cb_data) -{ - struct packfile_store_for_each_object_wrapper_data *data = cb_data; - - if (data->request) { - off_t offset = nth_packed_object_offset(pack, index_pos); - struct object_info oi = *data->request; - - if (packed_object_info_with_index_pos(pack, offset, - &index_pos, &oi) < 0) { - mark_bad_packed_object(pack, oid); - return -1; - } - - return data->cb(oid, &oi, data->cb_data); - } else { - return data->cb(oid, NULL, data->cb_data); - } -} - -static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b) -{ - do { - if (*a != *b) - return 0; - a++; - b++; - len -= 2; - } while (len > 1); - if (len) - if ((*a ^ *b) & 0xf0) - return 0; - return 1; -} - -static int for_each_prefixed_object_in_midx( - struct packfile_store *store, - struct multi_pack_index *m, - const struct odb_for_each_object_options *opts, - struct packfile_store_for_each_object_wrapper_data *data) -{ - int ret; - - for (; m; m = m->base_midx) { - uint32_t num, i, first = 0; - int len = opts->prefix_hex_len > m->source->odb->repo->hash_algo->hexsz ? - m->source->odb->repo->hash_algo->hexsz : opts->prefix_hex_len; - - if (!m->num_objects) - continue; - - num = m->num_objects + m->num_objects_in_base; - - bsearch_one_midx(opts->prefix, m, &first); - - /* - * At this point, "first" is the location of the lowest - * object with an object name that could match "opts->prefix". - * See if we have 0, 1 or more objects that actually match(es). - */ - for (i = first; i < num; i++) { - const struct object_id *current = NULL; - struct object_id oid; - - current = nth_midxed_object_oid(&oid, m, i); - - if (!match_hash(len, opts->prefix->hash, current->hash)) - break; - - if (data->request) { - struct object_info oi = *data->request; - - ret = packfile_store_read_object_info(store, current, - &oi, 0); - if (ret) - goto out; - - ret = data->cb(&oid, &oi, data->cb_data); - if (ret) - goto out; - } else { - ret = data->cb(&oid, NULL, data->cb_data); - if (ret) - goto out; - } - } - } - - ret = 0; - -out: - return ret; -} - -static int for_each_prefixed_object_in_pack( - struct packfile_store *store, - struct packed_git *p, - const struct odb_for_each_object_options *opts, - struct packfile_store_for_each_object_wrapper_data *data) -{ - uint32_t num, i, first = 0; - int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ? - p->repo->hash_algo->hexsz : opts->prefix_hex_len; - int ret; - - num = p->num_objects; - bsearch_pack(opts->prefix, p, &first); - - /* - * At this point, "first" is the location of the lowest object - * with an object name that could match "bin_pfx". See if we have - * 0, 1 or more objects that actually match(es). - */ - for (i = first; i < num; i++) { - struct object_id oid; - - nth_packed_object_id(&oid, p, i); - if (!match_hash(len, opts->prefix->hash, oid.hash)) - break; - - if (data->request) { - struct object_info oi = *data->request; - - ret = packfile_store_read_object_info(store, &oid, &oi, 0); - if (ret) - goto out; - - ret = data->cb(&oid, &oi, data->cb_data); - if (ret) - goto out; - } else { - ret = data->cb(&oid, NULL, data->cb_data); - if (ret) - goto out; - } - } - - ret = 0; - -out: - return ret; -} - -static int packfile_store_for_each_prefixed_object( - struct packfile_store *store, - const struct odb_for_each_object_options *opts, - struct packfile_store_for_each_object_wrapper_data *data) -{ - struct packfile_list_entry *e; - struct multi_pack_index *m; - bool pack_errors = false; - int ret; - - if (opts->flags) - BUG("flags unsupported"); - - store->skip_mru_updates = true; - - m = get_multi_pack_index(store->source); - if (m) { - ret = for_each_prefixed_object_in_midx(store, m, opts, data); - if (ret) - goto out; - } - - for (e = packfile_store_get_packs(store); e; e = e->next) { - if (e->pack->multi_pack_index) - continue; - - if (open_pack_index(e->pack)) { - pack_errors = true; - continue; - } - - if (!e->pack->num_objects) - continue; - - ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data); - if (ret) - goto out; - } - - ret = 0; - -out: - store->skip_mru_updates = false; - if (!ret && pack_errors) - ret = -1; - return ret; -} - -int packfile_store_for_each_object(struct packfile_store *store, - const struct object_info *request, - odb_for_each_object_cb cb, - void *cb_data, - const struct odb_for_each_object_options *opts) -{ - struct packfile_store_for_each_object_wrapper_data data = { - .store = store, - .request = request, - .cb = cb, - .cb_data = cb_data, - }; - struct packfile_list_entry *e; - int pack_errors = 0, ret; - - if (opts->prefix) - return packfile_store_for_each_prefixed_object(store, opts, &data); - - store->skip_mru_updates = true; - - for (e = packfile_store_get_packs(store); e; e = e->next) { - struct packed_git *p = e->pack; - - if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local) - continue; - if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) && - !p->pack_promisor) - continue; - if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) && - p->pack_keep_in_core) - continue; - if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) && - p->pack_keep) - continue; - if (open_pack_index(p)) { - pack_errors = 1; - continue; - } - - ret = for_each_object_in_pack(p, packfile_store_for_each_object_wrapper, - &data, opts->flags); - if (ret) - goto out; - } - - ret = 0; - -out: - store->skip_mru_updates = false; - - if (!ret && pack_errors) - ret = -1; - return ret; -} - -static int extend_abbrev_len(const struct object_id *a, - const struct object_id *b, - unsigned *out) -{ - unsigned len = oid_common_prefix_hexlen(a, b); - if (len != hash_algos[a->algo].hexsz && len >= *out) - *out = len + 1; - return 0; -} - -static void find_abbrev_len_for_midx(struct multi_pack_index *m, - const struct object_id *oid, - unsigned min_len, - unsigned *out) -{ - unsigned len = min_len; - - for (; m; m = m->base_midx) { - int match = 0; - uint32_t num, first = 0; - struct object_id found_oid; - - if (!m->num_objects) - continue; - - num = m->num_objects + m->num_objects_in_base; - match = bsearch_one_midx(oid, m, &first); - - /* - * first is now the position in the packfile where we - * would insert the object ID if it does not exist (or the - * position of the object ID if it does exist). Hence, we - * consider a maximum of two objects nearby for the - * abbreviation length. - */ - - if (!match) { - if (nth_midxed_object_oid(&found_oid, m, first)) - extend_abbrev_len(&found_oid, oid, &len); - } else if (first < num - 1) { - if (nth_midxed_object_oid(&found_oid, m, first + 1)) - extend_abbrev_len(&found_oid, oid, &len); - } - if (first > 0) { - if (nth_midxed_object_oid(&found_oid, m, first - 1)) - extend_abbrev_len(&found_oid, oid, &len); - } - } - - *out = len; -} - -static void find_abbrev_len_for_pack(struct packed_git *p, - const struct object_id *oid, - unsigned min_len, - unsigned *out) -{ - int match; - uint32_t num, first = 0; - struct object_id found_oid; - unsigned len = min_len; - - num = p->num_objects; - match = bsearch_pack(oid, p, &first); - - /* - * first is now the position in the packfile where we would insert - * the object ID if it does not exist (or the position of mad->hash if - * it does exist). Hence, we consider a maximum of two objects - * nearby for the abbreviation length. - */ - if (!match) { - if (!nth_packed_object_id(&found_oid, p, first)) - extend_abbrev_len(&found_oid, oid, &len); - } else if (first < num - 1) { - if (!nth_packed_object_id(&found_oid, p, first + 1)) - extend_abbrev_len(&found_oid, oid, &len); - } - if (first > 0) { - if (!nth_packed_object_id(&found_oid, p, first - 1)) - extend_abbrev_len(&found_oid, oid, &len); - } - - *out = len; -} - -int packfile_store_find_abbrev_len(struct packfile_store *store, - const struct object_id *oid, - unsigned min_len, - unsigned *out) -{ - struct packfile_list_entry *e; - struct multi_pack_index *m; - - m = get_multi_pack_index(store->source); - if (m) - find_abbrev_len_for_midx(m, oid, min_len, &min_len); - - for (e = packfile_store_get_packs(store); e; e = e->next) { - if (e->pack->multi_pack_index) - continue; - if (open_pack_index(e->pack) || !e->pack->num_objects) - continue; - - find_abbrev_len_for_pack(e->pack, oid, min_len, &min_len); - } - - *out = min_len; - return 0; -} - struct add_promisor_object_data { struct repository *repo; struct oidset *set; @@ -2818,37 +2114,6 @@ int parse_pack_header_option(const char *in, unsigned char *out, unsigned int *l return 0; } -struct packfile_store *packfile_store_new(struct odb_source *source) -{ - struct packfile_store *store; - CALLOC_ARRAY(store, 1); - store->source = source; - strmap_init(&store->packs_by_path); - return store; -} - -void packfile_store_free(struct packfile_store *store) -{ - for (struct packfile_list_entry *e = store->packs.head; e; e = e->next) - free(e->pack); - packfile_list_clear(&store->packs); - - strmap_clear(&store->packs_by_path, 0); - free(store); -} - -void packfile_store_close(struct packfile_store *store) -{ - for (struct packfile_list_entry *e = store->packs.head; e; e = e->next) { - if (e->pack->do_not_close) - BUG("want to close pack marked 'do-not-close'"); - close_pack(e->pack); - } - if (store->midx) - close_midx(store->midx); - store->midx = NULL; -} - struct odb_packed_read_stream { struct odb_read_stream base; struct packed_git *pack; @@ -2972,15 +2237,3 @@ int packfile_read_object_stream(struct odb_read_stream **out, return 0; } - -int packfile_store_read_object_stream(struct odb_read_stream **out, - struct packfile_store *store, - const struct object_id *oid) -{ - struct pack_entry e; - - if (!find_pack_entry(store, oid, &e)) - return -1; - - return packfile_read_object_stream(out, oid, e.p, e.offset); -}
diff --git a/packfile.h b/packfile.h index defb6f4..e1f7715 100644 --- a/packfile.h +++ b/packfile.h
@@ -5,9 +5,10 @@ #include "object.h" #include "odb.h" #include "odb/source-files.h" +#include "odb/source-packed.h" #include "oidset.h" +#include "packfile-list.h" #include "repository.h" -#include "strmap.h" /* in odb.h */ struct object_info; @@ -54,133 +55,18 @@ struct packed_git { char pack_name[FLEX_ARRAY]; /* more */ }; -struct packfile_list { - struct packfile_list_entry *head, *tail; -}; - -struct packfile_list_entry { - struct packfile_list_entry *next; - struct packed_git *pack; -}; - -void packfile_list_clear(struct packfile_list *list); -void packfile_list_remove(struct packfile_list *list, struct packed_git *pack); -void packfile_list_prepend(struct packfile_list *list, struct packed_git *pack); -void packfile_list_append(struct packfile_list *list, struct packed_git *pack); - -/* - * Find the pack within the "packs" list whose index contains the object - * "oid". For general object lookups, you probably don't want this; use - * find_pack_entry() instead. - */ -struct packed_git *packfile_list_find_oid(struct packfile_list_entry *packs, - const struct object_id *oid); - -/* - * A store that manages packfiles for a given object database. - */ -struct packfile_store { - struct odb_source *source; - - /* - * The list of packfiles in the order in which they have been most - * recently used. - */ - struct packfile_list packs; - - /* - * Cache of packfiles which are marked as "kept", either because there - * is an on-disk ".keep" file or because they are marked as "kept" in - * memory. - * - * Should not be accessed directly, but via - * `packfile_store_get_kept_pack_cache()`. The list of packs gets - * invalidated when the stored flags and the flags passed to - * `packfile_store_get_kept_pack_cache()` mismatch. - */ - struct { - struct packed_git **packs; - unsigned flags; - } kept_cache; - - /* The multi-pack index that belongs to this specific packfile store. */ - struct multi_pack_index *midx; - - /* - * A map of packfile names to packed_git structs for tracking which - * packs have been loaded already. - */ - struct strmap packs_by_path; - - /* - * Whether packfiles have already been populated with this store's - * packs. - */ - bool initialized; - - /* - * Usually, packfiles will be reordered to the front of the `packs` - * list whenever an object is looked up via them. This has the effect - * that packs that contain a lot of accessed objects will be located - * towards the front. - * - * This is usually desirable, but there are exceptions. One exception - * is when the looking up multiple objects in a loop for each packfile. - * In that case, we may easily end up with an infinite loop as the - * packfiles get reordered to the front repeatedly. - * - * Setting this field to `true` thus disables these reorderings. - */ - bool skip_mru_updates; -}; - -/* - * Allocate and initialize a new empty packfile store for the given object - * database source. - */ -struct packfile_store *packfile_store_new(struct odb_source *source); - -/* - * Free the packfile store and all its associated state. All packfiles - * tracked by the store will be closed. - */ -void packfile_store_free(struct packfile_store *store); - -/* - * Close all packfiles associated with this store. The packfiles won't be - * free'd, so they can be re-opened at a later point in time. - */ -void packfile_store_close(struct packfile_store *store); - -/* - * Prepare the packfile store by loading packfiles and multi-pack indices for - * all alternates. This becomes a no-op if the store is already prepared. - * - * It shouldn't typically be necessary to call this function directly, as - * functions that access the store know to prepare it. - */ -void packfile_store_prepare(struct packfile_store *store); - -/* - * Clear the packfile caches and try to look up any new packfiles that have - * appeared since last preparing the packfiles store. - * - * This function must be called under the `odb_read_lock()`. - */ -void packfile_store_reprepare(struct packfile_store *store); - /* * Add the pack to the store so that contained objects become accessible via * the store. This moves ownership into the store. */ -void packfile_store_add_pack(struct packfile_store *store, +void packfile_store_add_pack(struct odb_source_packed *store, struct packed_git *pack); /* * Get all packs managed by the given store, including packfiles that are * referenced by multi-pack indices. */ -struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *store); +struct packfile_list_entry *packfile_store_get_packs(struct odb_source_packed *store); struct repo_for_each_pack_data { struct odb_source *source; @@ -238,32 +124,14 @@ static inline void repo_for_each_pack_data_next(struct repo_for_each_pack_data * ((p) = (eack_pack_data.entry ? eack_pack_data.entry->pack : NULL)); \ repo_for_each_pack_data_next(&eack_pack_data)) -int packfile_store_read_object_stream(struct odb_read_stream **out, - struct packfile_store *store, - const struct object_id *oid); - -/* - * Try to read the object identified by its ID from the object store and - * populate the object info with its data. Returns 1 in case the object was - * not found, 0 if it was and read successfully, and a negative error code in - * case the object was corrupted. - */ -int packfile_store_read_object_info(struct packfile_store *store, - const struct object_id *oid, - struct object_info *oi, - enum object_info_flags flags); - /* * Open the packfile and add it to the store if it isn't yet known. Returns * either the newly opened packfile or the preexisting packfile. Returns a * `NULL` pointer in case the packfile could not be opened. */ -struct packed_git *packfile_store_load_pack(struct packfile_store *store, +struct packed_git *packfile_store_load_pack(struct odb_source_packed *store, const char *idx_path, int local); -int packfile_store_freshen_object(struct packfile_store *store, - const struct object_id *oid); - enum kept_pack_type { KEPT_PACK_ON_DISK = (1 << 0), KEPT_PACK_IN_CORE = (1 << 1), @@ -271,21 +139,11 @@ enum kept_pack_type { }; /* - * Count the number objects contained in the given packfile store. If - * successful, the number of objects will be written to the `out` pointer. - * - * Return 0 on success, a negative error code otherwise. - */ -int packfile_store_count_objects(struct packfile_store *store, - enum odb_count_objects_flags flags, - unsigned long *out); - -/* * Retrieve the cache of kept packs from the given packfile store. Accepts a * combination of `kept_pack_type` flags. The cache is computed on demand and * will be recomputed whenever the flags change. */ -struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *store, +struct packed_git **packfile_store_get_kept_pack_cache(struct odb_source_packed *store, unsigned flags); struct pack_window { @@ -356,26 +214,6 @@ int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn, void *data, enum odb_for_each_object_flags flags); -/* - * Iterate through all packed objects in the given packfile store and invoke - * the callback function for each of them. If an object info request is given, - * then the object info will be read for every individual object and passed to - * the callback as if `packfile_store_read_object_info()` was called for the - * object. - * - * The flags parameter is a combination of `odb_for_each_object_flags`. - */ -int packfile_store_for_each_object(struct packfile_store *store, - const struct object_info *request, - odb_for_each_object_cb cb, - void *cb_data, - const struct odb_for_each_object_options *opts); - -int packfile_store_find_abbrev_len(struct packfile_store *store, - const struct object_id *oid, - unsigned min_len, - unsigned *out); - /* A hook to report invalid files in pack directory */ #define PACKDIR_FILE_PACK 1 #define PACKDIR_FILE_IDX 2 @@ -454,6 +292,10 @@ off_t nth_packed_object_offset(const struct packed_git *, uint32_t n); */ off_t find_pack_entry_one(const struct object_id *oid, struct packed_git *); +int packfile_fill_entry(struct packed_git *p, + const struct object_id *oid, + struct pack_entry *e); + int is_pack_valid(struct packed_git *); void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, size_t *); @@ -478,8 +320,12 @@ extern int do_check_packed_object_crc; * Look up the object info for a specific offset in the packfile. * Returns zero on success, a negative error code otherwise. */ -int packed_object_info(struct packed_git *pack, +int packed_object_info(struct odb_source_packed *source, + struct packed_git *pack, off_t offset, struct object_info *); +int packed_object_info_with_index_pos(struct odb_source_packed *source, + struct packed_git *p, off_t obj_offset, + uint32_t *maybe_index_pos, struct object_info *oi); void mark_bad_packed_object(struct packed_git *, const struct object_id *); const struct packed_git *has_packed_and_bad(struct repository *, const struct object_id *);
diff --git a/parse-options.c b/parse-options.c index f4647e0..08c21d9 100644 --- a/parse-options.c +++ b/parse-options.c
@@ -583,7 +583,7 @@ static enum parse_opt_result parse_long_opt( ambiguous.option->long_name, (abbrev.flags & OPT_UNSET) ? "no-" : "", abbrev.option->long_name); - return PARSE_OPT_HELP; + return PARSE_OPT_HELP_ERROR; } if (abbrev.option) { if (*arg_end) @@ -1037,6 +1037,7 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, usage_with_options(usagestr, options); case PARSE_OPT_COMPLETE: case PARSE_OPT_HELP: + case PARSE_OPT_HELP_ERROR: case PARSE_OPT_ERROR: case PARSE_OPT_DONE: case PARSE_OPT_NON_OPTION: @@ -1072,6 +1073,7 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, case PARSE_OPT_NON_OPTION: case PARSE_OPT_SUBCOMMAND: case PARSE_OPT_HELP: + case PARSE_OPT_HELP_ERROR: case PARSE_OPT_COMPLETE: BUG("parse_short_opt() cannot return these"); case PARSE_OPT_DONE: @@ -1099,6 +1101,7 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, case PARSE_OPT_SUBCOMMAND: case PARSE_OPT_COMPLETE: case PARSE_OPT_HELP: + case PARSE_OPT_HELP_ERROR: BUG("parse_short_opt() cannot return these"); case PARSE_OPT_DONE: break; @@ -1133,6 +1136,8 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, goto unknown; case PARSE_OPT_HELP: goto show_usage; + case PARSE_OPT_HELP_ERROR: + goto show_usage_stderr; case PARSE_OPT_NON_OPTION: case PARSE_OPT_SUBCOMMAND: case PARSE_OPT_COMPLETE: @@ -1166,6 +1171,9 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, show_usage: return usage_with_options_internal(ctx, usagestr, options, USAGE_NORMAL, USAGE_TO_STDOUT); + show_usage_stderr: + return usage_with_options_internal(ctx, usagestr, options, + USAGE_NORMAL, USAGE_TO_STDERR); } int parse_options_end(struct parse_opt_ctx_t *ctx) @@ -1197,6 +1205,8 @@ int parse_options(int argc, const char **argv, parse_options_start_1(&ctx, argc, argv, prefix, options, flags); switch (parse_options_step(&ctx, options, usagestr)) { case PARSE_OPT_HELP: + exit(0); + case PARSE_OPT_HELP_ERROR: case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_COMPLETE: @@ -1363,7 +1373,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t parse_options_check_harder(opts); if (!usagestr) - return PARSE_OPT_HELP; + return err ? PARSE_OPT_HELP_ERROR : PARSE_OPT_HELP; if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL) fprintf(outfile, "cat <<\\EOF\n"); @@ -1474,9 +1484,9 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t fputc('\n', outfile); if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL) - fputs("EOF\n", outfile); + fputs("EOF\nexit 0\n", outfile); - return PARSE_OPT_HELP; + return err ? PARSE_OPT_HELP_ERROR : PARSE_OPT_HELP; } void NORETURN usage_with_options(const char * const *usagestr, @@ -1495,11 +1505,11 @@ void show_usage_with_options_if_asked(int ac, const char **av, if (!strcmp(av[1], "-h")) { usage_with_options_internal(NULL, usagestr, opts, USAGE_NORMAL, USAGE_TO_STDOUT); - exit(129); + exit(0); } else if (!strcmp(av[1], "--help-all")) { usage_with_options_internal(NULL, usagestr, opts, USAGE_FULL, USAGE_TO_STDOUT); - exit(129); + exit(0); } } }
diff --git a/parse-options.h b/parse-options.h index 0d1f738..3ec8ba5 100644 --- a/parse-options.h +++ b/parse-options.h
@@ -57,7 +57,8 @@ enum parse_opt_option_flags { }; enum parse_opt_result { - PARSE_OPT_COMPLETE = -3, + PARSE_OPT_COMPLETE = -4, + PARSE_OPT_HELP_ERROR = -3, PARSE_OPT_HELP = -2, PARSE_OPT_ERROR = -1, /* must be the same as error() */ PARSE_OPT_DONE = 0, /* fixed so that "return 0" works */
diff --git a/path-walk.c b/path-walk.c index edc8e73..9ca3248 100644 --- a/path-walk.c +++ b/path-walk.c
@@ -699,6 +699,7 @@ int walk_objects_by_path(struct path_walk_info *info) int ret; size_t commits_nr = 0, paths_nr = 0; struct commit *c; + char *path; struct type_and_oid_list *root_tree_list; struct type_and_oid_list *commit_list; struct path_walk_context ctx = { @@ -808,8 +809,7 @@ int walk_objects_by_path(struct path_walk_info *info) free(commit_list); trace2_region_enter("path-walk", "path-walk", info->revs->repo); - while (!ret && ctx.path_stack.nr) { - char *path = prio_queue_get(&ctx.path_stack); + while (!ret && (path = prio_queue_get(&ctx.path_stack))) { paths_nr++; ret = walk_path(&ctx, path); @@ -821,12 +821,12 @@ int walk_objects_by_path(struct path_walk_info *info) if (!strmap_empty(&ctx.paths_to_lists)) { struct hashmap_iter iter; struct strmap_entry *entry; + char *path; strmap_for_each_entry(&ctx.paths_to_lists, &iter, entry) push_to_stack(&ctx, entry->key); - while (!ret && ctx.path_stack.nr) { - char *path = prio_queue_get(&ctx.path_stack); + while (!ret && (path = prio_queue_get(&ctx.path_stack))) { paths_nr++; ret = walk_path(&ctx, path);
diff --git a/path.c b/path.c index d7e17bf..c3a709a 100644 --- a/path.c +++ b/path.c
@@ -1579,6 +1579,75 @@ char *xdg_cache_home(const char *filename) return NULL; } +void format_path(struct strbuf *dest, const char *path, + const char *prefix, enum path_format format) +{ + strbuf_reset(dest); + + switch (format) { + case PATH_FORMAT_UNMODIFIED: + strbuf_addstr(dest, path); + break; + + case PATH_FORMAT_RELATIVE: { + struct strbuf relative_buf = STRBUF_INIT; + struct strbuf real_path = STRBUF_INIT; + struct strbuf real_prefix = STRBUF_INIT; + char *cwd = NULL; + + /* + * We don't ever produce a relative path if prefix is NULL, + * so set the prefix to the current directory so that we can + * produce a relative path whenever possible. + */ + if (!prefix) + prefix = cwd = xgetcwd(); + + if (!is_absolute_path(path)) { + strbuf_realpath_forgiving(&real_path, path, 1); + path = real_path.buf; + } + if (!is_absolute_path(prefix)) { + strbuf_realpath_forgiving(&real_prefix, prefix, 1); + prefix = real_prefix.buf; + } + + strbuf_addstr(dest, relative_path(path, prefix, &relative_buf)); + + strbuf_release(&relative_buf); + strbuf_release(&real_path); + strbuf_release(&real_prefix); + free(cwd); + break; + } + + case PATH_FORMAT_RELATIVE_IF_SHARED: { + struct strbuf relative_buf = STRBUF_INIT; + + /* + * If we're using RELATIVE_IF_SHARED mode, then we want an + * absolute path unless the two share a common prefix, so don't + * default the prefix to the current working directory. Doing so + * would cause a relative path to always be produced if possible. + */ + strbuf_addstr(dest, relative_path(path, prefix, &relative_buf)); + strbuf_release(&relative_buf); + break; + } + + case PATH_FORMAT_CANONICAL: + /* + * strbuf_realpath_forgiving inherently resets the destination + * buffer, safely aligning with our replace semantics. + */ + strbuf_realpath_forgiving(dest, path, 1); + break; + + default: + BUG("unknown path_format value %d", format); + } +} + REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG") REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG") REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
diff --git a/path.h b/path.h index 4c2958a..7e7408d 100644 --- a/path.h +++ b/path.h
@@ -262,6 +262,36 @@ enum scld_error safe_create_leading_directories_no_share(char *path); int safe_create_file_with_leading_directories(struct repository *repo, const char *path); +/** + * The formatting strategy to apply when writing a path into a buffer. + */ +enum path_format { + /* Output the path exactly as-is without any modifications. */ + PATH_FORMAT_UNMODIFIED, + + /* Output a path relative to the provided directory prefix. */ + PATH_FORMAT_RELATIVE, + + /* Output a relative path only if the path shares a root with the prefix. */ + PATH_FORMAT_RELATIVE_IF_SHARED, + + /* Output a fully resolved, absolute canonical path. */ + PATH_FORMAT_CANONICAL +}; + +/** + * Format a path according to the specified formatting strategy and store + * the result in the given strbuf, replacing any existing contents. + * + * `dest` : The string buffer to store the formatted path into. + * `path` : The path string that needs to be formatted. + * `prefix` : The directory prefix to calculate relative offsets against. + * Pass NULL to default to the current working directory where applicable. + * `format` : The formatting behavior rule to execute. + */ +void format_path(struct strbuf *dest, const char *path, + const char *prefix, enum path_format format); + # ifdef USE_THE_REPOSITORY_VARIABLE # include "strbuf.h" # include "repository.h"
diff --git a/prio-queue.c b/prio-queue.c index 9748528..199775d 100644 --- a/prio-queue.c +++ b/prio-queue.c
@@ -22,40 +22,19 @@ void prio_queue_reverse(struct prio_queue *queue) if (queue->compare) BUG("prio_queue_reverse() on non-LIFO queue"); - if (!queue->nr) + if (!queue->nr_) return; - for (i = 0; i < (j = (queue->nr - 1) - i); i++) + for (i = 0; i < (j = (queue->nr_ - 1) - i); i++) swap(queue, i, j); } void clear_prio_queue(struct prio_queue *queue) { FREE_AND_NULL(queue->array); - queue->nr = 0; + queue->nr_ = 0; queue->alloc = 0; queue->insertion_ctr = 0; -} - -void prio_queue_put(struct prio_queue *queue, void *thing) -{ - size_t ix, parent; - - /* Append at the end */ - ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc); - queue->array[queue->nr].ctr = queue->insertion_ctr++; - queue->array[queue->nr].data = thing; - queue->nr++; - if (!queue->compare) - return; /* LIFO */ - - /* Bubble up the new one */ - for (ix = queue->nr - 1; ix; ix = parent) { - parent = (ix - 1) / 2; - if (compare(queue, parent, ix) <= 0) - break; - - swap(queue, parent, ix); - } + queue->get_pending = 0; } static void sift_down_root(struct prio_queue *queue) @@ -63,9 +42,9 @@ static void sift_down_root(struct prio_queue *queue) size_t ix, child; /* Push down the one at the root */ - for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) { + for (ix = 0; ix * 2 + 1 < queue->nr_; ix = child) { child = ix * 2 + 1; /* left */ - if (child + 1 < queue->nr && + if (child + 1 < queue->nr_ && compare(queue, child, child + 1) >= 0) child++; /* use right child */ @@ -76,43 +55,72 @@ static void sift_down_root(struct prio_queue *queue) } } +static inline void flush_get(struct prio_queue *queue) +{ + if (!queue->get_pending) + return; + queue->get_pending = 0; + queue->array[0] = queue->array[--queue->nr_]; + sift_down_root(queue); +} + +void prio_queue_put(struct prio_queue *queue, void *thing) +{ + size_t ix, parent; + + if (queue->get_pending) { + queue->get_pending = 0; + queue->array[0].ctr = queue->insertion_ctr++; + queue->array[0].data = thing; + sift_down_root(queue); + return; + } + + /* Append at the end */ + ALLOC_GROW(queue->array, queue->nr_ + 1, queue->alloc); + queue->array[queue->nr_].ctr = queue->insertion_ctr++; + queue->array[queue->nr_].data = thing; + queue->nr_++; + if (!queue->compare) + return; /* LIFO */ + + /* Bubble up the new one */ + for (ix = queue->nr_ - 1; ix; ix = parent) { + parent = (ix - 1) / 2; + if (compare(queue, parent, ix) <= 0) + break; + + swap(queue, parent, ix); + } +} + void *prio_queue_get(struct prio_queue *queue) { - void *result; - - if (!queue->nr) + if (queue->nr_ <= queue->get_pending) { + queue->nr_ = 0; + queue->get_pending = 0; return NULL; + } if (!queue->compare) - return queue->array[--queue->nr].data; /* LIFO */ + return queue->array[--queue->nr_].data; /* LIFO */ - result = queue->array[0].data; - if (!--queue->nr) - return result; + flush_get(queue); - queue->array[0] = queue->array[queue->nr]; - sift_down_root(queue); - return result; + queue->get_pending = 1; + return queue->array[0].data; } void *prio_queue_peek(struct prio_queue *queue) { - if (!queue->nr) + if (queue->nr_ <= queue->get_pending) { + queue->nr_ = 0; + queue->get_pending = 0; return NULL; - if (!queue->compare) - return queue->array[queue->nr - 1].data; - return queue->array[0].data; -} - -void prio_queue_replace(struct prio_queue *queue, void *thing) -{ - if (!queue->nr) { - prio_queue_put(queue, thing); - } else if (!queue->compare) { - queue->array[queue->nr - 1].ctr = queue->insertion_ctr++; - queue->array[queue->nr - 1].data = thing; - } else { - queue->array[0].ctr = queue->insertion_ctr++; - queue->array[0].data = thing; - sift_down_root(queue); } + if (!queue->compare) + return queue->array[queue->nr_ - 1].data; + + flush_get(queue); + + return queue->array[0].data; }
diff --git a/prio-queue.h b/prio-queue.h index da7fad2..570b48e 100644 --- a/prio-queue.h +++ b/prio-queue.h
@@ -30,8 +30,9 @@ struct prio_queue { prio_queue_compare_fn compare; size_t insertion_ctr; void *cb_data; - size_t alloc, nr; + size_t alloc, nr_; /* use prio_queue_size() for logical count */ struct prio_queue_entry *array; + unsigned get_pending; }; /* @@ -52,13 +53,15 @@ void *prio_queue_get(struct prio_queue *); */ void *prio_queue_peek(struct prio_queue *); -/* - * Replace the "thing" that compares the smallest with a new "thing", - * like prio_queue_get()+prio_queue_put() would do, but in a more - * efficient way. Does the same as prio_queue_put() if the queue is - * empty. - */ -void prio_queue_replace(struct prio_queue *queue, void *thing); +static inline size_t prio_queue_size(const struct prio_queue *queue) +{ + return queue->nr_ - queue->get_pending; +} + +#define prio_queue_for_each(queue, it) \ + for (size_t pq_ix_ = (queue)->get_pending; \ + pq_ix_ < (queue)->nr_ && ((it) = (queue)->array[pq_ix_].data, 1); \ + pq_ix_++) void clear_prio_queue(struct prio_queue *);
diff --git a/promisor-remote.c b/promisor-remote.c index f34856d..43505d1 100644 --- a/promisor-remote.c +++ b/promisor-remote.c
@@ -12,7 +12,9 @@ #include "packfile.h" #include "environment.h" #include "url.h" +#include "urlmatch.h" #include "version.h" +#include "wildmatch.h" struct promisor_remote_config { struct promisor_remote *promisors; @@ -434,13 +436,14 @@ static struct string_list *fields_stored(void) * Struct for promisor remotes involved in the "promisor-remote" * protocol capability. * - * Except for "name", each <member> in this struct and its <value> - * should correspond (either on the client side or on the server side) - * to a "remote.<name>.<member>" config variable set to <value> where - * "<name>" is a promisor remote name. + * Except for "name" and "local_name", each <member> in this struct + * and its <value> should correspond (either on the client side or on + * the server side) to a "remote.<name>.<member>" config variable set + * to <value> where "<name>" is a promisor remote name. */ struct promisor_info { - const char *name; + const char *name; /* name the server advertised */ + const char *local_name; /* name used locally (may be auto-generated) */ const char *url; const char *filter; const char *token; @@ -449,6 +452,7 @@ struct promisor_info { static void promisor_info_free(struct promisor_info *p) { free((char *)p->name); + free((char *)p->local_name); free((char *)p->url); free((char *)p->filter); free((char *)p->token); @@ -462,6 +466,11 @@ static void promisor_info_list_clear(struct string_list *list) string_list_clear(list, 0); } +static const char *promisor_info_local_name(struct promisor_info *p) +{ + return p->local_name ? p->local_name : p->name; +} + static void set_one_field(struct promisor_info *p, const char *field, const char *value) { @@ -650,9 +659,351 @@ static bool has_control_char(const char *s) return false; } -static int should_accept_remote(enum accept_promisor accept, +struct allowed_url { + char *remote_name; + char *url_pattern; + struct url_info pattern_info; +}; + +static void allowed_url_free(void *util, const char *str UNUSED) +{ + struct allowed_url *allowed = util; + + if (!allowed) + return; + + /* Depending on prefix, free either remote_name or url_pattern */ + free(allowed->remote_name ? allowed->remote_name : allowed->url_pattern); + free(allowed->pattern_info.url); + free(allowed); +} + +static struct allowed_url *valid_accept_url(const char *url) +{ + char *dup, *p; + struct allowed_url *allowed; + + if (!url) + return NULL; + + dup = xstrdup(url); + p = strchr(dup, '='); + if (p) { + *p = '\0'; + if (!valid_remote_name(dup)) { + warning(_("invalid remote name '%s' before '=' sign " + "in '%s' from promisor.acceptFromServerUrl config"), + dup, url); + free(dup); + return NULL; + } + p++; + } else { + p = dup; + } + + if (has_control_char(p)) { + warning(_("invalid url pattern '%s' " + "in '%s' from promisor.acceptFromServerUrl config"), p, url); + free(dup); + return NULL; + } + + allowed = xmalloc(sizeof(*allowed)); + allowed->remote_name = (p == dup) ? NULL : dup; + allowed->url_pattern = p; + allowed->pattern_info.url = url_normalize_pattern(p, &allowed->pattern_info); + if (!allowed->pattern_info.url) { + warning(_("invalid url pattern '%s' " + "in '%s' from promisor.acceptFromServerUrl config"), p, url); + free(dup); + free(allowed); + return NULL; + } + + return allowed; +} + +static void load_accept_from_server_url(struct repository *repo, + struct string_list *accept_urls) +{ + const struct string_list *config_urls; + + if (!repo_config_get_string_multi(repo, "promisor.acceptfromserverurl", &config_urls)) { + struct string_list_item *item; + + for_each_string_list_item(item, config_urls) { + struct allowed_url *allowed = valid_accept_url(item->string); + if (allowed) { + struct string_list_item *new; + new = string_list_append(accept_urls, item->string); + new->util = allowed; + } + } + } +} + +static bool match_pattern_url(const char *pat, size_t pat_len, + const char *url, size_t url_len) +{ + char *p_str = xstrndup(pat, pat_len); + char *u_str = xstrndup(url, url_len); + bool res = !wildmatch(p_str, u_str, 0); + + free(p_str); + free(u_str); + + return res; +} + +static bool match_one_url(const struct url_info *pi, const struct url_info *ui) +{ + const char *pat = pi->url; + const char *url = ui->url; + + /* + * Schemes must match exactly. They are case-folded by + * url_normalize(), so strncmp() suffices. + */ + if (pi->scheme_len != ui->scheme_len || strncmp(pat, url, pi->scheme_len)) + return false; + + /* + * Ports must match exactly. url_normalize() strips default + * ports (like 443 for https), so length and content + * comparisons are sufficient. + */ + if (pi->port_len != ui->port_len || + strncmp(pat + pi->port_off, url + ui->port_off, pi->port_len)) + return false; + + /* + * Match host and path separately to prevent a '*' in the host + * portion of the pattern from matching across the '/' + * boundary into the path. + */ + + return match_pattern_url(pat + pi->host_off, pi->host_len, + url + ui->host_off, ui->host_len) && + match_pattern_url(pat + pi->path_off, pi->path_len, + url + ui->path_off, ui->path_len); +} + +static struct allowed_url *url_matches_accept_list( + struct string_list *accept_urls, const char *url) +{ + struct string_list_item *item; + struct url_info url_info; + + url_info.url = url_normalize(url, &url_info); + + if (!url_info.url) + return NULL; + + for_each_string_list_item(item, accept_urls) { + struct allowed_url *allowed = item->util; + + if (match_one_url(&allowed->pattern_info, &url_info)) { + free(url_info.url); + return allowed; + } + } + + free(url_info.url); + return NULL; +} + +/* + * Sanitize the buffer to make it a valid remote name coming from the + * server by: + * + * - replacing any non alphanumeric character with a '-' + * - stripping any leading '-', + * - condensing multiple '-' into one, + * - prepending "promisor-auto-", + * - validating the result. + */ +static int sanitize_remote_name(struct strbuf *buf, const char *url) +{ + char prev = '-'; + for (size_t i = 0; i < buf->len; ) { + if (!isalnum(buf->buf[i])) + buf->buf[i] = '-'; + if (prev == '-' && buf->buf[i] == '-') { + strbuf_remove(buf, i, 1); + } else { + prev = buf->buf[i]; + i++; + } + } + + strbuf_strip_suffix(buf, "-"); + + if (!buf->len) { + warning(_("couldn't generate a valid remote name from " + "advertised url '%s', ignoring this remote"), url); + return -1; + } + + strbuf_insertstr(buf, 0, "promisor-auto-"); + + if (!valid_remote_name(buf->buf)) { + warning(_("generated remote name '%s' from advertised url '%s' " + "is invalid, ignoring this remote"), buf->buf, url); + return -1; + } + + return 0; +} + +static char *promisor_remote_name_from_url(const char *url) +{ + struct url_info url_info = { 0 }; + char *normalized = url_normalize(url, &url_info); + struct strbuf buf = STRBUF_INIT; + + if (!normalized) { + warning(_("couldn't normalize advertised url '%s', " + "ignoring this remote"), url); + return NULL; + } + + if (url_info.host_len) { + strbuf_add(&buf, normalized + url_info.host_off, url_info.host_len); + strbuf_addch(&buf, '-'); + } + + if (url_info.port_len) { + strbuf_add(&buf, normalized + url_info.port_off, url_info.port_len); + strbuf_addch(&buf, '-'); + } + + if (url_info.path_len) { + strbuf_add(&buf, normalized + url_info.path_off, url_info.path_len); + strbuf_trim_trailing_dir_sep(&buf); + strbuf_strip_suffix(&buf, ".git"); + } + + free(normalized); + + if (sanitize_remote_name(&buf, url)) { + strbuf_release(&buf); + return NULL; + } + + return strbuf_detach(&buf, NULL); +} + +static void configure_auto_promisor_remote(struct repository *repo, + const char *name, + const char *url, + const char *advertised_as, + bool reuse) +{ + char *key; + + if (!reuse) { + fprintf(stderr, _("Auto-creating promisor remote '%s' for URL '%s'\n"), + name, url); + + key = xstrfmt("remote.%s.url", name); + repo_config_set_gently(repo, key, url); + free(key); + } + + /* NB: when reusing, this promotes an existing non-promisor remote */ + key = xstrfmt("remote.%s.promisor", name); + repo_config_set_gently(repo, key, "true"); + free(key); + + if (advertised_as) { + key = xstrfmt("remote.%s.advertisedAs", name); + repo_config_set_gently(repo, key, advertised_as); + free(key); + } +} + +#define MAX_REMOTES_WITH_SIMILAR_NAMES 20 + +/* Return the allocated local name, or NULL on failure */ +static char *handle_matching_allowed_url(struct repository *repo, + char *allowed_name, + const char *remote_url, + const char *remote_name) +{ + char *name; + char *basename = allowed_name ? + xstrdup(allowed_name) : + promisor_remote_name_from_url(remote_url); + int i = 0; + bool reuse = false; + + if (!basename) + return NULL; + + name = xstrdup(basename); + + while (i < MAX_REMOTES_WITH_SIMILAR_NAMES) { + char *url_key = xstrfmt("remote.%s.url", name); + const char *existing_url; + int exists = !repo_config_get_string_tmp(repo, url_key, &existing_url); + + free(url_key); + + if (!exists) + break; /* Free to use */ + + if (!strcmp(existing_url, remote_url)) { + reuse = true; + break; /* Same URL, so safe to reuse */ + } + + i++; + free(name); + name = xstrfmt("%s-%d", basename, i); + } + + if (i < MAX_REMOTES_WITH_SIMILAR_NAMES) { + configure_auto_promisor_remote(repo, name, + remote_url, remote_name, + reuse); + } else { + warning(_("too many remotes accepted with name like '%s-X', " + "ignoring this remote"), basename); + FREE_AND_NULL(name); + } + + free(basename); + return name; +} + +static int should_accept_new_remote_url(struct repository *repo, + struct string_list *accept_urls, + struct promisor_info *advertised) +{ + struct allowed_url *allowed = url_matches_accept_list(accept_urls, + advertised->url); + if (allowed) { + char *name = handle_matching_allowed_url(repo, + allowed->remote_name, + advertised->url, + advertised->name); + if (name) { + free((char *)advertised->local_name); + advertised->local_name = name; + return 1; + } + } + + return 0; +} + +static int should_accept_remote(struct repository *repo, + enum accept_promisor accept, struct promisor_info *advertised, - struct string_list *config_info) + struct string_list *accept_urls, + struct string_list *config_info, + bool *reload_config) { struct promisor_info *p; struct string_list_item *item; @@ -664,23 +1015,34 @@ static int should_accept_remote(enum accept_promisor accept, "this remote should have been rejected earlier", remote_name); - if (accept == ACCEPT_ALL) - return all_fields_match(advertised, config_info, NULL); - /* Get config info for that promisor remote */ item = string_list_lookup(config_info, remote_name); - if (!item) + if (!item) { /* We don't know about that remote */ + + int res = should_accept_new_remote_url(repo, accept_urls, advertised); + if (res) { + *reload_config = true; + return res; + } + + if (accept == ACCEPT_ALL) + return all_fields_match(advertised, config_info, NULL); return 0; + } p = item->util; - if (accept == ACCEPT_KNOWN_NAME) + /* Known remote in the allowlist? */ + if (!strcmp(p->url, remote_url) && url_matches_accept_list(accept_urls, remote_url)) return all_fields_match(advertised, config_info, p); - if (accept != ACCEPT_KNOWN_URL) - BUG("Unhandled 'enum accept_promisor' value '%d'", accept); + if (accept == ACCEPT_ALL) + return all_fields_match(advertised, config_info, NULL); + + if (accept == ACCEPT_KNOWN_NAME) + return all_fields_match(advertised, config_info, p); if (strcmp(p->url, remote_url)) { warning(_("known remote named '%s' but with URL '%s' instead of '%s', " @@ -689,7 +1051,13 @@ static int should_accept_remote(enum accept_promisor accept, return 0; } - return all_fields_match(advertised, config_info, p); + if (accept == ACCEPT_KNOWN_URL) + return all_fields_match(advertised, config_info, p); + + if (accept != ACCEPT_NONE) + BUG("Unhandled 'enum accept_promisor' value '%d'", accept); + + return 0; } static int skip_field_name_prefix(const char *elem, const char *field_name, const char **value) @@ -829,7 +1197,7 @@ static bool promisor_store_advertised_fields(struct promisor_info *advertised, { struct promisor_info *p; struct string_list_item *item; - const char *remote_name = advertised->name; + const char *remote_name = promisor_info_local_name(advertised); bool reload_config = false; if (!(store_info->store_filter || store_info->store_token)) @@ -894,8 +1262,12 @@ static void filter_promisor_remote(struct repository *repo, struct string_list_item *item; bool reload_config = false; enum accept_promisor accept = accept_from_server(repo); + struct string_list accept_urls = STRING_LIST_INIT_DUP; - if (accept == ACCEPT_NONE) + /* Load and validate the acceptFromServerUrl config */ + load_accept_from_server_url(repo, &accept_urls); + + if (accept == ACCEPT_NONE && !accept_urls.nr) return; /* Parse remote info received */ @@ -915,7 +1287,8 @@ static void filter_promisor_remote(struct repository *repo, string_list_sort(&config_info); } - if (should_accept_remote(accept, advertised, &config_info)) { + if (should_accept_remote(repo, accept, advertised, &accept_urls, + &config_info, &reload_config)) { if (!store_info) store_info = store_info_new(repo); if (promisor_store_advertised_fields(advertised, store_info)) @@ -927,6 +1300,7 @@ static void filter_promisor_remote(struct repository *repo, } } + string_list_clear_func(&accept_urls, allowed_url_free); promisor_info_list_clear(&config_info); string_list_clear(&remote_info, 0); store_info_free(store_info); @@ -937,7 +1311,8 @@ static void filter_promisor_remote(struct repository *repo, /* Apply accepted remotes to the stable repo state */ for_each_string_list_item(item, accepted_remotes) { struct promisor_info *info = item->util; - struct promisor_remote *r = repo_promisor_remote_find(repo, info->name); + const char *remote_name = promisor_info_local_name(info); + struct promisor_remote *r = repo_promisor_remote_find(repo, remote_name); if (r) { r->accepted = 1;
diff --git a/reachable.c b/reachable.c index 101cfc2..caadacc 100644 --- a/reachable.c +++ b/reachable.c
@@ -234,8 +234,9 @@ static int add_recent_object(const struct object_id *oid, add_pending_object(data->revs, obj, ""); if (data->cb) { - if (oi->whence == OI_PACKED) - data->cb(obj, oi->u.packed.pack, oi->u.packed.offset, *oi->mtimep); + if (oi->source_infop->source->type == ODB_SOURCE_PACKED) + data->cb(obj, oi->source_infop->u.packed.pack, + oi->source_infop->u.packed.offset, *oi->mtimep); else data->cb(obj, NULL, 0, *oi->mtimep); } @@ -252,9 +253,11 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs, unsigned flags; enum object_type type; time_t mtime; + struct odb_source_info source_info; struct object_info oi = { .mtimep = &mtime, .typep = &type, + .source_infop = &source_info, }; int r;
diff --git a/read-cache-ll.h b/read-cache-ll.h index 2c8b4b2..71b8761 100644 --- a/read-cache-ll.h +++ b/read-cache-ll.h
@@ -309,6 +309,7 @@ int write_locked_index(struct index_state *, struct lock_file *lock, unsigned fl void discard_index(struct index_state *); void move_index_extensions(struct index_state *dst, struct index_state *src); int unmerged_index(const struct index_state *); +int index_state_unmerged_to_stage0(struct index_state *istate); /** * Returns 1 if istate differs from tree, 0 otherwise. If tree is NULL,
diff --git a/read-cache.c b/read-cache.c index 21ca58b..797fdfc 100644 --- a/read-cache.c +++ b/read-cache.c
@@ -760,12 +760,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, * case of the file being added to the repository matches (is folded into) the existing * entry's directory case. */ - if (ignore_case) { + if (repo_ignore_case(the_repository)) { adjust_dirname_case(istate, ce->name); } if (!(flags & ADD_CACHE_RENORMALIZE)) { alias = index_file_exists(istate, ce->name, - ce_namelen(ce), ignore_case); + ce_namelen(ce), repo_ignore_case(the_repository)); if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) { @@ -786,7 +786,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, } else set_object_name_for_intent_to_add_entry(ce); - if (ignore_case && alias && different_name(ce, alias)) + if (repo_ignore_case(the_repository) && alias && different_name(ce, alias)) ce = create_alias_ce(istate, ce, alias); ce->ce_flags |= CE_ADDED; @@ -1002,7 +1002,7 @@ static enum verify_path_result verify_path_internal(const char *path, return PATH_OK; if (is_dir_sep(c)) { inside: - if (protect_hfs) { + if (repo_protect_hfs(the_repository)) { if (is_hfs_dotgit(path)) return PATH_INVALID; @@ -1011,7 +1011,7 @@ static enum verify_path_result verify_path_internal(const char *path, return PATH_INVALID; } } - if (protect_ntfs) { + if (repo_protect_ntfs(the_repository)) { #if defined GIT_WINDOWS_NATIVE || defined __CYGWIN__ if (c == '\\') return PATH_INVALID; @@ -1035,7 +1035,8 @@ static enum verify_path_result verify_path_internal(const char *path, if (c == '\0') return S_ISDIR(mode) ? PATH_DIR_WITH_SEP : PATH_INVALID; - } else if (c == '\\' && protect_ntfs) { + } else if (c == '\\' && + repo_protect_ntfs(the_repository)) { if (is_ntfs_dotgit(path)) return PATH_INVALID; if (S_ISLNK(mode)) { @@ -1721,7 +1722,7 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size) if (oideq(&oid, null_oid(the_hash_algo))) return 0; - the_hash_algo->init_fn(&c); + git_hash_init(&c, the_hash_algo); git_hash_update(&c, hdr, size - the_hash_algo->rawsz); git_hash_final(hash, &c); if (!hasheq(hash, start, the_repository->hash_algo)) @@ -2956,7 +2957,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, */ if (offset && record_eoie()) { CALLOC_ARRAY(eoie_c, 1); - the_hash_algo->init_fn(eoie_c); + git_hash_init(eoie_c, the_hash_algo); } /* @@ -3403,13 +3404,15 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, */ int repo_read_index_unmerged(struct repository *repo) { - struct index_state *istate; - int i; + repo_read_index(repo); + return index_state_unmerged_to_stage0(repo->index); +} + +int index_state_unmerged_to_stage0(struct index_state *istate) +{ int unmerged = 0; - repo_read_index(repo); - istate = repo->index; - for (i = 0; i < istate->cache_nr; i++) { + for (unsigned int i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; struct cache_entry *new_ce; int len; @@ -3597,7 +3600,7 @@ static size_t read_eoie_extension(const char *mmap, size_t mmap_size) * "REUC" + <binary representation of M>) */ src_offset = offset; - the_hash_algo->init_fn(&c); + git_hash_init(&c, the_hash_algo); while (src_offset < mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER) { /* After an array of active_nr index entries, * there can be arbitrary number of extended
diff --git a/ref-filter.c b/ref-filter.c index 8ba91c7..284796c 100644 --- a/ref-filter.c +++ b/ref-filter.c
@@ -3316,15 +3316,14 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, refs_for if (prefix) { struct ref_iterator *iter; + struct ref_store *store = get_main_ref_store(the_repository); - iter = refs_ref_iterator_begin(get_main_ref_store(the_repository), - "", NULL, 0, 0); - - if (filter->start_after) + if (filter->start_after) { + iter = refs_ref_iterator_begin(store, "", NULL, 0, 0); ret = start_ref_iterator_after(iter, filter->start_after); - else - ret = ref_iterator_seek(iter, prefix, - REF_ITERATOR_SEEK_SET_PREFIX); + } else { + iter = refs_ref_iterator_begin(store, prefix, NULL, 0, 0); + } if (!ret) ret = do_for_each_ref_iterator(iter, fn, cb_data);
diff --git a/refs.c b/refs.c index d3caa9a..1d24637 100644 --- a/refs.c +++ b/refs.c
@@ -1053,6 +1053,15 @@ static char *normalize_reflog_message(const char *msg) return strbuf_detach(&sb, NULL); } +enum log_refs_config refs_parse_log_all_ref_updates_config(const char *value) +{ + if (value && !strcasecmp(value, "always")) + return LOG_REFS_ALWAYS; + else if (git_config_bool("core.logallrefupdates", value)) + return LOG_REFS_NORMAL; + return LOG_REFS_NONE; +} + int should_autocreate_reflog(enum log_refs_config log_all_ref_updates, const char *refname) { @@ -2327,7 +2336,6 @@ static struct ref_store *ref_store_init(struct repository *repo, struct ref_store *refs; struct ref_store_init_options opts = { .access_flags = flags, - .log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo), }; be = find_ref_storage_backend(format); @@ -2351,15 +2359,22 @@ void ref_store_release(struct ref_store *ref_store) struct ref_store *get_main_ref_store(struct repository *r) { + static bool initializing; + if (r->refs_private) return r->refs_private; if (!r->gitdir) BUG("attempting to get main_ref_store outside of repository"); + if (initializing) + BUG("initialization of main ref store is recursing"); + initializing = true; r->refs_private = ref_store_init(r, r->ref_storage_format, r->gitdir, REF_STORE_ALL_CAPS); r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private); + initializing = false; + return r->refs_private; }
diff --git a/refs.h b/refs.h index 71d5c18..a381022 100644 --- a/refs.h +++ b/refs.h
@@ -146,6 +146,15 @@ enum ref_transaction_error refs_verify_refname_available(struct ref_store *refs, int refs_ref_exists(struct ref_store *refs, const char *refname); +enum log_refs_config { + LOG_REFS_UNSET = -1, + LOG_REFS_NONE = 0, + LOG_REFS_NORMAL, + LOG_REFS_ALWAYS +}; + +enum log_refs_config refs_parse_log_all_ref_updates_config(const char *value); + int should_autocreate_reflog(enum log_refs_config log_all_ref_updates, const char *refname);
diff --git a/refs/files-backend.c b/refs/files-backend.c index a4c7858..e1d963a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c
@@ -84,12 +84,21 @@ struct files_ref_store { unsigned int store_flags; char *gitcommondir; - enum log_refs_config log_all_ref_updates; - int prefer_symlink_refs; - struct ref_cache *loose; - struct ref_store *packed_ref_store; + + /* + * Options used when writing references. These are parsed from the + * config lazily on first use via `files_ref_store_write_options()` so + * that we don't have to access the configuration when initializing the + * ref store. Do not access these fields directly, but use the accessor + * instead. + */ + struct files_ref_store_write_options { + enum log_refs_config log_all_ref_updates; + int prefer_symlink_refs; + bool initialized; + } write_opts_lazy_loaded; }; static void clear_loose_ref_cache(struct files_ref_store *refs) @@ -100,6 +109,52 @@ static void clear_loose_ref_cache(struct files_ref_store *refs) } } +static void files_ref_store_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *payload) +{ + struct files_ref_store *refs = payload; + char *tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->base.gitdir); + free(refs->base.gitdir); + refs->base.gitdir = tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->gitcommondir); + free(refs->gitcommondir); + refs->gitcommondir = tmp; +} + +static int files_ref_store_config(const char *var, const char *value, + const struct config_context *ctx UNUSED, + void *payload) +{ + struct files_ref_store_write_options *opts = payload; + + if (!strcmp(var, "core.prefersymlinkrefs")) { + opts->prefer_symlink_refs = git_config_bool(var, value); + } else if (!strcmp(var, "core.logallrefupdates")) { + opts->log_all_ref_updates = refs_parse_log_all_ref_updates_config(value); + } + + return 0; +} + +static const struct files_ref_store_write_options *files_ref_store_write_options(struct files_ref_store *refs) +{ + struct files_ref_store_write_options *opts = &refs->write_opts_lazy_loaded; + + if (opts->initialized) + return opts; + + opts->log_all_ref_updates = LOG_REFS_UNSET; + repo_config(refs->base.repo, files_ref_store_config, opts); + + opts->initialized = true; + return opts; +} + /* * Create a new submodule ref cache and add it to the internal * set of caches. @@ -124,13 +179,8 @@ static struct ref_store *files_ref_store_init(struct repository *repo, refs->packed_ref_store = packed_ref_store_init(repo, NULL, refs->gitcommondir, opts); refs->store_flags = opts->access_flags; - refs->log_all_ref_updates = opts->log_all_ref_updates; - repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs); - - chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir); - chdir_notify_reparent("files-backend $GIT_COMMONDIR", - &refs->gitcommondir); + chdir_notify_register(NULL, files_ref_store_reparent, refs); strbuf_release(&refdir); @@ -182,6 +232,7 @@ static void files_ref_store_release(struct ref_store *ref_store) free(refs->gitcommondir); ref_store_release(refs->packed_ref_store); free(refs->packed_ref_store); + chdir_notify_unregister(NULL, files_ref_store_reparent, refs); } static void files_reflog_path(struct files_ref_store *refs, @@ -806,7 +857,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs, } else { unable_to_lock_message(ref_file.buf, myerr, err); if (myerr == EEXIST) { - if (ignore_case && + if (repo_ignore_case(the_repository) && transaction_has_case_conflicting_update(transaction, update)) { /* * In case-insensitive filesystems, ensure that conflicts within a @@ -920,7 +971,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs, * conflicts between 'foo' and 'Foo/bar'. So let's lowercase * the refname. */ - if (ignore_case) { + if (repo_ignore_case(the_repository)) { struct strbuf lower = STRBUF_INIT; strbuf_addstr(&lower, refname); @@ -1860,12 +1911,12 @@ static int log_ref_setup(struct files_ref_store *refs, const char *refname, int force_create, int *logfd, struct strbuf *err) { - enum log_refs_config log_refs_cfg = refs->log_all_ref_updates; + enum log_refs_config log_refs_cfg = files_ref_store_write_options(refs)->log_all_ref_updates; struct strbuf logfile_sb = STRBUF_INIT; char *logfile; if (log_refs_cfg == LOG_REFS_UNSET) - log_refs_cfg = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL; + log_refs_cfg = is_bare_repository(the_repository) ? LOG_REFS_NONE : LOG_REFS_NORMAL; files_reflog_path(refs, &logfile_sb, refname); logfile = strbuf_detach(&logfile_sb, NULL); @@ -3271,6 +3322,7 @@ static int files_transaction_finish(struct ref_store *ref_store, { struct files_ref_store *refs = files_downcast(ref_store, 0, "ref_transaction_finish"); + const struct files_ref_store_write_options *write_opts = files_ref_store_write_options(refs); size_t i; int ret = 0; struct strbuf sb = STRBUF_INIT; @@ -3310,7 +3362,7 @@ static int files_transaction_finish(struct ref_store *ref_store, * We try creating a symlink, if that succeeds we continue to the * next update. If not, we try and create a regular symref. */ - if (update->new_target && refs->prefer_symlink_refs) + if (update->new_target && write_opts->prefer_symlink_refs) /* * By using the `NOT_CONSTANT()` trick, we can avoid * errors by `clang`'s `-Wunreachable` logic that would
diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 0acde48..499cb55 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c
@@ -211,6 +211,19 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot) return snapshot->refs->base.repo->hash_algo->hexsz; } +static void packed_ref_store_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *payload) +{ + struct packed_ref_store *refs = payload; + char *tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->path); + free(refs->path); + refs->path = tmp; +} + /* * Since packed-refs is only stored in the common dir, don't parse the * payload and rely on the files-backend to set 'gitdir' correctly. @@ -229,7 +242,7 @@ struct ref_store *packed_ref_store_init(struct repository *repo, strbuf_addf(&sb, "%s/packed-refs", gitdir); refs->path = strbuf_detach(&sb, NULL); - chdir_notify_reparent("packed-refs", &refs->path); + chdir_notify_register(NULL, packed_ref_store_reparent, refs); return ref_store; } @@ -274,6 +287,7 @@ static void packed_ref_store_release(struct ref_store *ref_store) clear_snapshot(refs); rollback_lock_file(&refs->lock); delete_tempfile(&refs->tempfile); + chdir_notify_unregister(NULL, packed_ref_store_reparent, refs); free(refs->path); }
diff --git a/refs/refs-internal.h b/refs/refs-internal.h index a08d589..c3ac7b5 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h
@@ -406,12 +406,6 @@ struct ref_store; struct ref_store_init_options { /* The kind of operations that the ref_store is allowed to perform. */ unsigned int access_flags; - - /* - * Denotes under what conditions reflogs should be created when updating - * references. - */ - enum log_refs_config log_all_ref_updates; }; /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 4ae2292..028f021 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "../git-compat-util.h" #include "../abspath.h" #include "../chdir-notify.h" @@ -48,9 +46,9 @@ static void reftable_backend_on_reload(void *payload) static int reftable_backend_init(struct reftable_backend *be, const char *path, - const struct reftable_write_options *_opts) + const struct reftable_stack_options *_opts) { - struct reftable_write_options opts = *_opts; + struct reftable_stack_options opts = *_opts; opts.on_reload = reftable_backend_on_reload; opts.on_reload_payload = be; return reftable_new_stack(&be->stack, path, &opts); @@ -86,7 +84,8 @@ static int reftable_backend_read_ref(struct reftable_backend *be, if (ret) goto done; - if (strcmp(ref.refname, refname)) { + if (strcmp(ref.refname, refname) || + reftable_ref_record_is_deletion(&ref)) { ret = 1; goto done; } @@ -112,7 +111,6 @@ static int reftable_backend_read_ref(struct reftable_backend *be, oidread(oid, reftable_ref_record_val1(&ref), &hash_algos[hash_id]); } else { - /* We got a tombstone, which should not happen. */ BUG("unhandled reference value type %d", ref.value_type); } @@ -140,10 +138,22 @@ struct reftable_ref_store { * is populated lazily when we try to resolve `worktrees/$worktree` refs. */ struct strmap worktree_backends; - struct reftable_write_options write_options; + struct reftable_stack_options stack_options; + + /* + * Options used when writing to or compacting the reftable stacks. + * These are parsed from the configuration lazily on first use via + * `reftable_be_write_options()` so that we don't have to access the + * configuration when initializing the ref store. Do not access these + * fields directly, but use the accessor instead. + */ + struct reftable_be_write_options { + struct reftable_write_options opts; + enum log_refs_config log_all_ref_updates; + bool initialized; + } write_opts_lazy_loaded; unsigned int store_flags; - enum log_refs_config log_all_ref_updates; int err; }; @@ -190,7 +200,7 @@ static int backend_for_worktree(struct reftable_backend **out, CALLOC_ARRAY(*out, 1); store->err = ret = reftable_backend_init(*out, worktree_dir.buf, - &store->write_options); + &store->stack_options); if (ret < 0) { free(*out); goto out; @@ -284,26 +294,6 @@ static int backend_for(struct reftable_backend **out, return ret; } -static int should_write_log(struct reftable_ref_store *refs, const char *refname) -{ - enum log_refs_config log_refs_cfg = refs->log_all_ref_updates; - if (log_refs_cfg == LOG_REFS_UNSET) - log_refs_cfg = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL; - - switch (log_refs_cfg) { - case LOG_REFS_NONE: - return refs_reflog_exists(&refs->base, refname); - case LOG_REFS_ALWAYS: - return 1; - case LOG_REFS_NORMAL: - if (should_autocreate_reflog(log_refs_cfg, refname)) - return 1; - return refs_reflog_exists(&refs->base, refname); - default: - BUG("unhandled core.logAllRefUpdates value %d", log_refs_cfg); - } -} - static void fill_reftable_log_record(struct reftable_log_record *log, const struct ident_split *split) { const char *tz_begin; @@ -332,39 +322,88 @@ static void fill_reftable_log_record(struct reftable_log_record *log, const stru static int reftable_be_config(const char *var, const char *value, const struct config_context *ctx, - void *_opts) + void *payload) { - struct reftable_write_options *opts = _opts; + struct reftable_ref_store *refs = payload; + struct reftable_be_write_options *opts = &refs->write_opts_lazy_loaded; if (!strcmp(var, "reftable.blocksize")) { unsigned long block_size = git_config_ulong(var, value, ctx->kvi); if (block_size > 16777215) die("reftable block size cannot exceed 16MB"); - opts->block_size = block_size; + opts->opts.block_size = block_size; } else if (!strcmp(var, "reftable.restartinterval")) { unsigned long restart_interval = git_config_ulong(var, value, ctx->kvi); if (restart_interval > UINT16_MAX) die("reftable block size cannot exceed %u", (unsigned)UINT16_MAX); - opts->restart_interval = restart_interval; + opts->opts.restart_interval = restart_interval; } else if (!strcmp(var, "reftable.indexobjects")) { - opts->skip_index_objects = !git_config_bool(var, value); + opts->opts.skip_index_objects = !git_config_bool(var, value); } else if (!strcmp(var, "reftable.geometricfactor")) { unsigned long factor = git_config_ulong(var, value, ctx->kvi); if (factor > UINT8_MAX) die("reftable geometric factor cannot exceed %u", (unsigned)UINT8_MAX); - opts->auto_compaction_factor = factor; + opts->opts.auto_compaction_factor = factor; } else if (!strcmp(var, "reftable.locktimeout")) { int64_t lock_timeout = git_config_int64(var, value, ctx->kvi); if (lock_timeout > LONG_MAX) die("reftable lock timeout cannot exceed %"PRIdMAX, (intmax_t)LONG_MAX); if (lock_timeout < 0 && lock_timeout != -1) die("reftable lock timeout does not support negative values other than -1"); - opts->lock_timeout_ms = lock_timeout; + opts->opts.lock_timeout_ms = lock_timeout; + } else if (!strcmp(var, "core.logallrefupdates")) { + opts->log_all_ref_updates = refs_parse_log_all_ref_updates_config(value); } return 0; } +static const struct reftable_be_write_options *reftable_be_write_options(struct reftable_ref_store *refs) +{ + struct reftable_be_write_options *opts = &refs->write_opts_lazy_loaded; + mode_t mask; + + if (opts->initialized) + return opts; + + mask = umask(0); + umask(mask); + + opts->opts.default_permissions = calc_shared_perm(refs->base.repo, 0666 & ~mask); + opts->opts.disable_auto_compact = + !git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1); + opts->opts.lock_timeout_ms = 100; + opts->log_all_ref_updates = LOG_REFS_UNSET; + + repo_config(refs->base.repo, reftable_be_config, refs); + + /* + * It is somewhat unfortunate that we have to mirror the default block + * size of the reftable library here. But given that the write options + * wouldn't be updated by the library here, and given that we require + * the proper block size to trim reflog message so that they fit, we + * must set up a proper value here. + */ + if (!opts->opts.block_size) + opts->opts.block_size = 4096; + + opts->initialized = true; + return opts; +} + +static void reftable_be_reparent(const char *name UNUSED, + const char *old_cwd, + const char *new_cwd, + void *payload) +{ + struct reftable_ref_store *refs = payload; + char *tmp; + + tmp = reparent_relative_path(old_cwd, new_cwd, refs->base.gitdir); + free(refs->base.gitdir); + refs->base.gitdir = tmp; +} + static struct ref_store *reftable_be_init(struct repository *repo, const char *payload, const char *gitdir, @@ -375,45 +414,24 @@ static struct ref_store *reftable_be_init(struct repository *repo, struct strbuf refdir = STRBUF_INIT; struct strbuf path = STRBUF_INIT; bool is_worktree; - mode_t mask; - - mask = umask(0); - umask(mask); refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir, &ref_common_dir); base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable); strmap_init(&refs->worktree_backends); - refs->log_all_ref_updates = opts->log_all_ref_updates; refs->store_flags = opts->access_flags; switch (repo->hash_algo->format_id) { case GIT_SHA1_FORMAT_ID: - refs->write_options.hash_id = REFTABLE_HASH_SHA1; + refs->stack_options.hash_id = REFTABLE_HASH_SHA1; break; case GIT_SHA256_FORMAT_ID: - refs->write_options.hash_id = REFTABLE_HASH_SHA256; + refs->stack_options.hash_id = REFTABLE_HASH_SHA256; break; default: BUG("unknown hash algorithm %d", repo->hash_algo->format_id); } - refs->write_options.default_permissions = calc_shared_perm(repo, 0666 & ~mask); - refs->write_options.disable_auto_compact = - !git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1); - refs->write_options.lock_timeout_ms = 100; - - repo_config(repo, reftable_be_config, &refs->write_options); - - /* - * It is somewhat unfortunate that we have to mirror the default block - * size of the reftable library here. But given that the write options - * wouldn't be updated by the library here, and given that we require - * the proper block size to trim reflog message so that they fit, we - * must set up a proper value here. - */ - if (!refs->write_options.block_size) - refs->write_options.block_size = 4096; /* * Set up the main reftable stack that is hosted in GIT_COMMON_DIR. @@ -426,7 +444,7 @@ static struct ref_store *reftable_be_init(struct repository *repo, } strbuf_addstr(&path, "/reftable"); refs->err = reftable_backend_init(&refs->main_backend, path.buf, - &refs->write_options); + &refs->stack_options); if (refs->err) goto done; @@ -442,12 +460,12 @@ static struct ref_store *reftable_be_init(struct repository *repo, strbuf_addstr(&refdir, "/reftable"); refs->err = reftable_backend_init(&refs->worktree_backend, refdir.buf, - &refs->write_options); + &refs->stack_options); if (refs->err) goto done; } - chdir_notify_reparent("reftables-backend $GIT_DIR", &refs->base.gitdir); + chdir_notify_register(NULL, reftable_be_reparent, refs); done: assert(refs->err != REFTABLE_API_ERROR); @@ -474,6 +492,7 @@ static void reftable_be_release(struct ref_store *ref_store) free(be); } strmap_clear(&refs->worktree_backends, 0); + chdir_notify_unregister(NULL, reftable_be_reparent, refs); } static int reftable_be_create_on_disk(struct ref_store *ref_store, @@ -633,6 +652,9 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) break; } + if (iter->ref.value_type == REFTABLE_REF_DELETION) + continue; + if (iter->exclude_patterns && should_exclude_current_ref(iter)) continue; @@ -981,6 +1003,7 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out, struct reftable_addition *addition; ret = reftable_stack_new_addition(&addition, be->stack, + &reftable_be_write_options(refs)->opts, REFTABLE_STACK_NEW_ADDITION_RELOAD); if (ret) { if (ret == REFTABLE_LOCK_ERROR) @@ -1419,6 +1442,26 @@ static int transaction_update_cmp(const void *a, const void *b) return strcmp(update_a->update->refname, update_b->update->refname); } +static int should_write_log(struct reftable_ref_store *refs, const char *refname) +{ + enum log_refs_config log_refs_cfg = reftable_be_write_options(refs)->log_all_ref_updates; + if (log_refs_cfg == LOG_REFS_UNSET) + log_refs_cfg = is_bare_repository(refs->base.repo) ? LOG_REFS_NONE : LOG_REFS_NORMAL; + + switch (log_refs_cfg) { + case LOG_REFS_NONE: + return refs_reflog_exists(&refs->base, refname); + case LOG_REFS_ALWAYS: + return 1; + case LOG_REFS_NORMAL: + if (should_autocreate_reflog(log_refs_cfg, refname)) + return 1; + return refs_reflog_exists(&refs->base, refname); + default: + BUG("unhandled core.logAllRefUpdates value %d", log_refs_cfg); + } +} + static int write_transaction_table(struct reftable_writer *writer, void *cb_data) { struct write_transaction_table_arg *arg = cb_data; @@ -1492,6 +1535,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data ret = 0; break; } + if (reftable_log_record_is_deletion(&log)) + continue; ALLOC_GROW(logs, logs_nr + 1, logs_alloc); tombstone = &logs[logs_nr++]; @@ -1553,7 +1598,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ); log->value.update.message = - xstrndup(u->msg, arg->refs->write_options.block_size / 2); + xstrndup(u->msg, reftable_be_write_options(arg->refs)->opts.block_size / 2); } } @@ -1669,9 +1714,9 @@ static int reftable_be_optimize(struct ref_store *ref_store, stack = refs->main_backend.stack; if (opts->flags & REFS_OPTIMIZE_AUTO) - ret = reftable_stack_auto_compact(stack); + ret = reftable_stack_auto_compact(stack, &reftable_be_write_options(refs)->opts); else - ret = reftable_stack_compact_all(stack, NULL); + ret = reftable_stack_compact_all(stack, &reftable_be_write_options(refs)->opts, NULL); if (ret < 0) { ret = error(_("unable to compact stack: %s"), reftable_error_str(ret)); @@ -1705,8 +1750,8 @@ static int reftable_be_optimize_required(struct ref_store *ref_store, if (opts->flags & REFS_OPTIMIZE_AUTO) use_heuristics = true; - return reftable_stack_compaction_required(stack, use_heuristics, - required); + return reftable_stack_compaction_required(stack, &reftable_be_write_options(refs)->opts, + use_heuristics, required); } struct write_create_symref_arg { @@ -1825,7 +1870,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data) logs[logs_nr].refname = xstrdup(arg->newname); logs[logs_nr].update_index = deletion_ts; logs[logs_nr].value.update.message = - xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2); + xstrndup(arg->logmsg, reftable_be_write_options(arg->refs)->opts.block_size / 2); memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ); logs_nr++; @@ -1864,7 +1909,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data) logs[logs_nr].refname = xstrdup(arg->newname); logs[logs_nr].update_index = creation_ts; logs[logs_nr].value.update.message = - xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2); + xstrndup(arg->logmsg, reftable_be_write_options(arg->refs)->opts.block_size / 2); memcpy(logs[logs_nr].value.update.new_hash, old_ref.value.val1, GIT_MAX_RAWSZ); logs_nr++; @@ -1889,6 +1934,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data) ret = 0; break; } + if (reftable_log_record_is_deletion(&old_log)) + continue; free(old_log.refname); @@ -1963,6 +2010,7 @@ static int reftable_be_rename_ref(struct ref_store *ref_store, if (ret) goto done; ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg, + &reftable_be_write_options(refs)->opts, REFTABLE_STACK_NEW_ADDITION_RELOAD); done: @@ -1993,6 +2041,7 @@ static int reftable_be_copy_ref(struct ref_store *ref_store, if (ret) goto done; ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg, + &reftable_be_write_options(refs)->opts, REFTABLE_STACK_NEW_ADDITION_RELOAD); done: @@ -2019,6 +2068,9 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator) if (iter->err) break; + if (reftable_log_record_is_deletion(&iter->log)) + continue; + /* * We want the refnames that we have reflogs for, so we skip if * we've already produced this name. This could be faster by @@ -2178,6 +2230,8 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store, ret = 0; break; } + if (reftable_log_record_is_deletion(&log)) + continue; ret = yield_log_record(refs, &log, fn, cb_data); if (ret) @@ -2230,6 +2284,10 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store, ret = 0; break; } + if (reftable_log_record_is_deletion(&log)) { + reftable_log_record_release(&log); + continue; + } ALLOC_GROW(logs, logs_nr + 1, logs_alloc); logs[logs_nr++] = log; @@ -2276,18 +2334,26 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store, goto done; /* - * Check whether we get at least one log record for the given ref name. - * If so, the reflog exists, otherwise it doesn't. + * Check whether we get at least one non-deleted log record for the + * given ref name. If so, the reflog exists, otherwise it doesn't. */ - ret = reftable_iterator_next_log(&it, &log); - if (ret < 0) - goto done; - if (ret > 0) { - ret = 0; - goto done; + while (1) { + ret = reftable_iterator_next_log(&it, &log); + if (ret < 0) + goto done; + if (ret > 0) { + ret = 0; + goto done; + } + if (strcmp(log.refname, refname)) { + ret = 0; + goto done; + } + if (!reftable_log_record_is_deletion(&log)) + break; } - ret = strcmp(log.refname, refname) == 0; + ret = 1; done: reftable_iterator_destroy(&it); @@ -2358,6 +2424,7 @@ static int reftable_be_create_reflog(struct ref_store *ref_store, arg.stack = be->stack; ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg, + &reftable_be_write_options(refs)->opts, REFTABLE_STACK_NEW_ADDITION_RELOAD); done: @@ -2399,6 +2466,8 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da ret = 0; break; } + if (reftable_log_record_is_deletion(&log)) + continue; tombstone.refname = (char *)arg->refname; tombstone.value_type = REFTABLE_LOG_DELETION; @@ -2430,6 +2499,7 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store, arg.stack = be->stack; ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg, + &reftable_be_write_options(refs)->opts, REFTABLE_STACK_NEW_ADDITION_RELOAD); assert(ret != REFTABLE_API_ERROR); @@ -2552,6 +2622,7 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, goto done; ret = reftable_stack_new_addition(&add, be->stack, + &reftable_be_write_options(refs)->opts, REFTABLE_STACK_NEW_ADDITION_RELOAD); if (ret < 0) goto done; @@ -2580,6 +2651,10 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, reftable_log_record_release(&log); break; } + if (reftable_log_record_is_deletion(&log)) { + reftable_log_record_release(&log); + continue; + } oidread(&old_oid, log.value.update.old_hash, ref_store->repo->hash_algo); @@ -2746,6 +2821,8 @@ static int reftable_be_fsck(struct ref_store *ref_store, struct fsck_options *o, report.path = refname.buf; switch (ref.value_type) { + case REFTABLE_REF_DELETION: + continue; case REFTABLE_REF_VAL1: case REFTABLE_REF_VAL2: { struct object_id oid;
diff --git a/reftable/basics.c b/reftable/basics.c index e969927..f0442a4 100644 --- a/reftable/basics.c +++ b/reftable/basics.c
@@ -152,6 +152,9 @@ size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args) size_t lo = 0; size_t hi = sz; + if (!sz) + return 0; + /* Invariants: * * (hi == sz) || f(hi) == true
diff --git a/reftable/block.c b/reftable/block.c index 920b3f4..1fa8140 100644 --- a/reftable/block.c +++ b/reftable/block.c
@@ -260,6 +260,15 @@ int reftable_block_init(struct reftable_block *block, goto done; } + /* + * Verify that the block size covers at least the table header, block + * header and the 2 byte restart counter. + */ + if (block_size < header_size + 4 + 2) { + err = REFTABLE_FORMAT_ERROR; + goto done; + } + if (block_type == REFTABLE_BLOCK_TYPE_LOG) { uint32_t block_header_skip = 4 + header_size; uLong dst_len = block_size - block_header_skip; @@ -331,8 +340,21 @@ int reftable_block_init(struct reftable_block *block, full_block_size = block_size; } + /* + * Ensure that we have sufficient data available now to satisfy the + * claimed block size. + */ + if (block_size > block->block_data.len) { + err = REFTABLE_FORMAT_ERROR; + goto done; + } + restart_count = reftable_get_be16(block->block_data.data + block_size - 2); restart_off = block_size - 2 - 3 * restart_count; + if (restart_off < header_size + 4 || restart_off > block_size - 2) { + err = REFTABLE_FORMAT_ERROR; + goto done; + } block->block_type = block_type; block->hash_size = hash_size; @@ -419,6 +441,15 @@ static int restart_needle_less(size_t idx, void *_args) int n; /* + * The restart offset must point to a record, which is stored before + * the restart table. Verify that this is the case. + */ + if (off >= args->block->restart_off) { + args->error = 1; + return -1; + } + + /* * Records at restart points are stored without prefix compression, so * there is no need to fully decode the record key here. This removes * the need for allocating memory. @@ -495,6 +526,10 @@ int block_iter_seek_key(struct block_iter *it, struct reftable_buf *want) int err = 0; size_t i; + err = reftable_record_init(&rec, reftable_block_type(it->block)); + if (err < 0) + goto done; + /* * Perform a binary search over the block's restart points, which * avoids doing a linear scan over the whole block. Like this, we @@ -536,10 +571,6 @@ int block_iter_seek_key(struct block_iter *it, struct reftable_buf *want) else it->next_off = it->block->header_off + 4; - err = reftable_record_init(&rec, reftable_block_type(it->block)); - if (err < 0) - goto done; - /* * We're looking for the last entry less than the wanted key so that * the next call to `block_reader_next()` would yield the wanted
diff --git a/reftable/record.c b/reftable/record.c index fcd387b..1fce441 100644 --- a/reftable/record.c +++ b/reftable/record.c
@@ -388,7 +388,6 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key, r->refname[key.len] = 0; r->update_index = update_index; - r->value_type = val_type; switch (val_type) { case REFTABLE_REF_VAL1: if (in.len < hash_size) { @@ -426,9 +425,10 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key, case REFTABLE_REF_DELETION: break; default: - abort(); - break; + err = REFTABLE_FORMAT_ERROR; + goto done; } + r->value_type = val_type; return start.len - in.len;
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h index 5f7be57..5d22d84 100644 --- a/reftable/reftable-stack.h +++ b/reftable/reftable-stack.h
@@ -26,11 +26,31 @@ */ struct reftable_stack; +/* Options related to opening a stack. */ +struct reftable_stack_options { + /* + * 4-byte identifier ("sha1", "s256") of the hash. Defaults to SHA1 if + * unset. + */ + enum reftable_hash hash_id; + + /* + * Callback function to execute whenever the stack is being reloaded. + * This can be used e.g. to discard cached information that relies on + * the old stack's data. The payload data will be passed as argument to + * the callback. + */ + void (*on_reload)(void *payload); + void *on_reload_payload; + + int suppress_deletions; +}; + /* open a new reftable stack. The tables along with the table list will be * stored in 'dir'. Typically, this should be .git/reftables. */ int reftable_new_stack(struct reftable_stack **dest, const char *dir, - const struct reftable_write_options *opts); + const struct reftable_stack_options *opts); /* returns the update_index at which a next table should be written. */ uint64_t reftable_stack_next_update_index(struct reftable_stack *st); @@ -52,6 +72,7 @@ enum { */ int reftable_stack_new_addition(struct reftable_addition **dest, struct reftable_stack *st, + const struct reftable_write_options *opts, unsigned int flags); /* Adds a reftable to transaction. */ @@ -77,7 +98,9 @@ void reftable_addition_destroy(struct reftable_addition *add); int reftable_stack_add(struct reftable_stack *st, int (*write_table)(struct reftable_writer *wr, void *write_arg), - void *write_arg, unsigned flags); + void *write_arg, + const struct reftable_write_options *opts, + unsigned flags); struct reftable_iterator; @@ -122,6 +145,7 @@ struct reftable_log_expiry_config { /* compacts all reftables into a giant table. Expire reflog entries if config is * non-NULL */ int reftable_stack_compact_all(struct reftable_stack *st, + const struct reftable_write_options *opts, struct reftable_log_expiry_config *config); /* @@ -132,11 +156,13 @@ int reftable_stack_compact_all(struct reftable_stack *st, * compacted to maintain geometric progression. */ int reftable_stack_compaction_required(struct reftable_stack *st, + const struct reftable_write_options *opts, bool use_heuristics, bool *required); /* heuristically compact unbalanced table stack. */ -int reftable_stack_auto_compact(struct reftable_stack *st); +int reftable_stack_auto_compact(struct reftable_stack *st, + const struct reftable_write_options *opts); /* delete stale .ref tables. */ int reftable_stack_clean(struct reftable_stack *st);
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h index a66db41..6ff4ddf 100644 --- a/reftable/reftable-writer.h +++ b/reftable/reftable-writer.h
@@ -28,11 +28,6 @@ struct reftable_write_options { /* how often to write complete keys in each block. */ uint16_t restart_interval; - /* 4-byte identifier ("sha1", "s256") of the hash. - * Defaults to SHA1 if unset - */ - enum reftable_hash hash_id; - /* Default mode for creating files. If unset, use 0666 (+umask) */ unsigned int default_permissions; @@ -60,15 +55,6 @@ struct reftable_write_options { * negative value will cause us to block indefinitely. */ long lock_timeout_ms; - - /* - * Callback function to execute whenever the stack is being reloaded. - * This can be used e.g. to discard cached information that relies on - * the old stack's data. The payload data will be passed as argument to - * the callback. - */ - void (*on_reload)(void *payload); - void *on_reload_payload; }; /* reftable_block_stats holds statistics for a single block type */ @@ -114,7 +100,8 @@ struct reftable_writer; int reftable_writer_new(struct reftable_writer **out, ssize_t (*writer_func)(void *, const void *, size_t), int (*flush_func)(void *), - void *writer_arg, const struct reftable_write_options *opts); + void *writer_arg, enum reftable_hash hash_id, + const struct reftable_write_options *opts); /* * Set the range of update indices for the records we will add. When writing a
diff --git a/reftable/stack.c b/reftable/stack.c index 1fba96d..308f957 100644 --- a/reftable/stack.c +++ b/reftable/stack.c
@@ -171,7 +171,8 @@ void reftable_stack_destroy(struct reftable_stack *st) st->merged = NULL; } - err = read_lines(st->list_file, &names); + if (st->list_file) + err = read_lines(st->list_file, &names); if (err < 0) { REFTABLE_FREE_AND_NULL(names); } @@ -337,7 +338,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, /* Update the stack to point to the new tables. */ if (st->merged) reftable_merged_table_free(st->merged); - new_merged->suppress_deletions = 1; + new_merged->suppress_deletions = st->opts.suppress_deletions; st->merged = new_merged; if (st->tables) @@ -501,10 +502,10 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st, } int reftable_new_stack(struct reftable_stack **dest, const char *dir, - const struct reftable_write_options *_opts) + const struct reftable_stack_options *_opts) { struct reftable_buf list_file_name = REFTABLE_BUF_INIT; - struct reftable_write_options opts = { 0 }; + struct reftable_stack_options opts = { 0 }; struct reftable_stack *p; int err; @@ -629,6 +630,7 @@ int reftable_stack_reload(struct reftable_stack *st) struct reftable_addition { struct reftable_flock tables_list_lock; struct reftable_stack *stack; + struct reftable_write_options opts; char **new_tables; size_t new_tables_len, new_tables_cap; @@ -657,6 +659,7 @@ static void reftable_addition_close(struct reftable_addition *add) static int reftable_stack_init_addition(struct reftable_addition *add, struct reftable_stack *st, + const struct reftable_write_options *opts, unsigned int flags) { struct reftable_buf lock_file_name = REFTABLE_BUF_INIT; @@ -664,15 +667,17 @@ static int reftable_stack_init_addition(struct reftable_addition *add, memset(add, 0, sizeof(*add)); add->stack = st; + if (opts) + add->opts = *opts; err = flock_acquire(&add->tables_list_lock, st->list_file, - st->opts.lock_timeout_ms); + add->opts.lock_timeout_ms); if (err < 0) goto done; - if (st->opts.default_permissions) { + if (add->opts.default_permissions) { if (chmod(add->tables_list_lock.path, - st->opts.default_permissions) < 0) { + add->opts.default_permissions) < 0) { err = REFTABLE_IO_ERROR; goto done; } @@ -702,12 +707,14 @@ static int reftable_stack_init_addition(struct reftable_addition *add, static int stack_try_add(struct reftable_stack *st, int (*write_table)(struct reftable_writer *wr, void *arg), - void *arg, unsigned flags) + void *arg, + const struct reftable_write_options *opts, + unsigned flags) { struct reftable_addition add; int err; - err = reftable_stack_init_addition(&add, st, flags); + err = reftable_stack_init_addition(&add, st, opts, flags); if (err < 0) goto done; @@ -723,9 +730,11 @@ static int stack_try_add(struct reftable_stack *st, int reftable_stack_add(struct reftable_stack *st, int (*write)(struct reftable_writer *wr, void *arg), - void *arg, unsigned flags) + void *arg, + const struct reftable_write_options *opts, + unsigned flags) { - int err = stack_try_add(st, write, arg, flags); + int err = stack_try_add(st, write, arg, opts, flags); if (err < 0) { if (err == REFTABLE_OUTDATED_ERROR) { /* Ignore error return, we want to propagate @@ -810,7 +819,7 @@ int reftable_addition_commit(struct reftable_addition *add) if (err) goto done; - if (!add->stack->opts.disable_auto_compact) { + if (!add->opts.disable_auto_compact) { /* * Auto-compact the stack to keep the number of tables in * control. It is possible that a concurrent writer is already @@ -820,7 +829,7 @@ int reftable_addition_commit(struct reftable_addition *add) * concurrent writer, which causes `REFTABLE_OUTDATED_ERROR`. * Both of these errors are benign, so we simply ignore them. */ - err = reftable_stack_auto_compact(add->stack); + err = reftable_stack_auto_compact(add->stack, &add->opts); if (err < 0 && err != REFTABLE_LOCK_ERROR && err != REFTABLE_OUTDATED_ERROR) goto done; @@ -834,6 +843,7 @@ int reftable_addition_commit(struct reftable_addition *add) int reftable_stack_new_addition(struct reftable_addition **dest, struct reftable_stack *st, + const struct reftable_write_options *opts, unsigned int flags) { int err; @@ -842,7 +852,7 @@ int reftable_stack_new_addition(struct reftable_addition **dest, if (!*dest) return REFTABLE_OUT_OF_MEMORY_ERROR; - err = reftable_stack_init_addition(*dest, st, flags); + err = reftable_stack_init_addition(*dest, st, opts, flags); if (err) { reftable_free(*dest); *dest = NULL; @@ -862,7 +872,7 @@ int reftable_addition_add(struct reftable_addition *add, struct reftable_writer *wr = NULL; struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT; struct fd_writer writer = { - .opts = &add->stack->opts, + .opts = &add->opts, }; int err = 0; @@ -883,9 +893,9 @@ int reftable_addition_add(struct reftable_addition *add, err = tmpfile_from_pattern(&tab_file, temp_tab_file_name.buf); if (err < 0) goto done; - if (add->stack->opts.default_permissions) { + if (add->opts.default_permissions) { if (chmod(tab_file.path, - add->stack->opts.default_permissions)) { + add->opts.default_permissions)) { err = REFTABLE_IO_ERROR; goto done; } @@ -893,7 +903,7 @@ int reftable_addition_add(struct reftable_addition *add, writer.fd = tab_file.fd; err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush, - &writer, &add->stack->opts); + &writer, add->stack->opts.hash_id, &add->opts); if (err < 0) goto done; @@ -1066,13 +1076,14 @@ static int stack_write_compact(struct reftable_stack *st, static int stack_compact_locked(struct reftable_stack *st, size_t first, size_t last, struct reftable_log_expiry_config *config, + const struct reftable_write_options *opts, struct reftable_tmpfile *tab_file_out) { struct reftable_buf next_name = REFTABLE_BUF_INIT; struct reftable_buf tab_file_path = REFTABLE_BUF_INIT; struct reftable_writer *wr = NULL; struct fd_writer writer= { - .opts = &st->opts, + .opts = opts, }; struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT; int err = 0; @@ -1094,15 +1105,15 @@ static int stack_compact_locked(struct reftable_stack *st, if (err < 0) goto done; - if (st->opts.default_permissions && - chmod(tab_file.path, st->opts.default_permissions) < 0) { + if (opts->default_permissions && + chmod(tab_file.path, opts->default_permissions) < 0) { err = REFTABLE_IO_ERROR; goto done; } writer.fd = tab_file.fd; err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush, - &writer, &st->opts); + &writer, st->opts.hash_id, opts); if (err < 0) goto done; @@ -1150,6 +1161,7 @@ enum stack_compact_range_flags { static int stack_compact_range(struct reftable_stack *st, size_t first, size_t last, struct reftable_log_expiry_config *expiry, + const struct reftable_write_options *opts, unsigned int flags) { struct reftable_buf tables_list_buf = REFTABLE_BUF_INIT; @@ -1175,7 +1187,7 @@ static int stack_compact_range(struct reftable_stack *st, * Hold the lock so that we can read "tables.list" and lock all tables * which are part of the user-specified range. */ - err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms); + err = flock_acquire(&tables_list_lock, st->list_file, opts->lock_timeout_ms); if (err < 0) goto done; @@ -1274,7 +1286,7 @@ static int stack_compact_range(struct reftable_stack *st, * these tables may end up with an empty new table in case tombstones * end up cancelling out all refs in that range. */ - err = stack_compact_locked(st, first, last, expiry, &new_table); + err = stack_compact_locked(st, first, last, expiry, opts, &new_table); if (err < 0) { if (err != REFTABLE_EMPTY_TABLE_ERROR) goto done; @@ -1286,13 +1298,13 @@ static int stack_compact_range(struct reftable_stack *st, * "tables.list". We'll then replace the compacted range of tables with * the new table. */ - err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms); + err = flock_acquire(&tables_list_lock, st->list_file, opts->lock_timeout_ms); if (err < 0) goto done; - if (st->opts.default_permissions) { + if (opts->default_permissions) { if (chmod(tables_list_lock.path, - st->opts.default_permissions) < 0) { + opts->default_permissions) < 0) { err = REFTABLE_IO_ERROR; goto done; } @@ -1513,10 +1525,16 @@ static int stack_compact_range(struct reftable_stack *st, } int reftable_stack_compact_all(struct reftable_stack *st, + const struct reftable_write_options *opts, struct reftable_log_expiry_config *config) { + struct reftable_write_options opts_default = { 0 }; size_t last = st->merged->tables_len ? st->merged->tables_len - 1 : 0; - return stack_compact_range(st, 0, last, config, 0); + + if (!opts) + opts = &opts_default; + + return stack_compact_range(st, 0, last, config, opts, 0); } static int segment_size(struct segment *s) @@ -1601,6 +1619,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n, } static int stack_segments_for_compaction(struct reftable_stack *st, + const struct reftable_write_options *opts, struct segment *seg) { int version = (st->opts.hash_id == REFTABLE_HASH_SHA1) ? 1 : 2; @@ -1615,13 +1634,14 @@ static int stack_segments_for_compaction(struct reftable_stack *st, sizes[i] = st->tables[i]->size - overhead; *seg = suggest_compaction_segment(sizes, st->merged->tables_len, - st->opts.auto_compaction_factor); + opts->auto_compaction_factor); reftable_free(sizes); return 0; } static int update_segment_if_compaction_required(struct reftable_stack *st, + const struct reftable_write_options *opts, struct segment *seg, bool use_geometric, bool *required) @@ -1638,7 +1658,7 @@ static int update_segment_if_compaction_required(struct reftable_stack *st, return 0; } - err = stack_segments_for_compaction(st, seg); + err = stack_segments_for_compaction(st, opts, seg); if (err) return err; @@ -1647,27 +1667,40 @@ static int update_segment_if_compaction_required(struct reftable_stack *st, } int reftable_stack_compaction_required(struct reftable_stack *st, + const struct reftable_write_options *opts, bool use_heuristics, bool *required) { + struct reftable_write_options opts_default = { 0 }; struct segment seg; - return update_segment_if_compaction_required(st, &seg, use_heuristics, - required); + + if (!opts) + opts = &opts_default; + + return update_segment_if_compaction_required(st, opts, &seg, + use_heuristics, required); } -int reftable_stack_auto_compact(struct reftable_stack *st) +int reftable_stack_auto_compact(struct reftable_stack *st, + const struct reftable_write_options *opts) { + struct reftable_write_options opts_default = { 0 }; struct segment seg; bool required; int err; - err = update_segment_if_compaction_required(st, &seg, true, &required); + if (!opts) + opts = &opts_default; + + err = update_segment_if_compaction_required(st, opts, &seg, true, + &required); if (err) return err; if (required) return stack_compact_range(st, seg.start, seg.end - 1, - NULL, STACK_COMPACT_RANGE_BEST_EFFORT); + NULL, opts, + STACK_COMPACT_RANGE_BEST_EFFORT); return 0; } @@ -1807,7 +1840,7 @@ static int reftable_stack_clean_locked(struct reftable_stack *st) int reftable_stack_clean(struct reftable_stack *st) { struct reftable_addition *add = NULL; - int err = reftable_stack_new_addition(&add, st, 0); + int err = reftable_stack_new_addition(&add, st, NULL, 0); if (err < 0) { goto done; }
diff --git a/reftable/stack.h b/reftable/stack.h index bc28f29..f7901e6 100644 --- a/reftable/stack.h +++ b/reftable/stack.h
@@ -20,7 +20,7 @@ struct reftable_stack { char *reftable_dir; - struct reftable_write_options opts; + struct reftable_stack_options opts; struct reftable_table **tables; size_t tables_len;
diff --git a/reftable/table.c b/reftable/table.c index 56362df..ea1e410 100644 --- a/reftable/table.c +++ b/reftable/table.c
@@ -242,6 +242,8 @@ static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ) int err; err = table_init_block(ti->table, &ti->block, off, typ); + if (err > 0) + return REFTABLE_FORMAT_ERROR; if (err != 0) return err; @@ -560,6 +562,11 @@ int reftable_table_new(struct reftable_table **out, goto done; } + if (file_size < header_size(t->version) + footer_size(t->version)) { + err = REFTABLE_FORMAT_ERROR; + goto done; + } + t->size = file_size - footer_size(t->version); t->source = *source; t->name = reftable_strdup(name); @@ -709,6 +716,10 @@ static int reftable_table_refs_for_unindexed(struct reftable_table *t, if (ti) table_iter_close(ti); reftable_free(ti); + if (filter) { + reftable_buf_release(&filter->oid); + reftable_free(filter); + } } return err; }
diff --git a/reftable/writer.c b/reftable/writer.c index 0133b64..d969a6a 100644 --- a/reftable/writer.c +++ b/reftable/writer.c
@@ -80,9 +80,6 @@ static void options_set_defaults(struct reftable_write_options *opts) opts->restart_interval = 16; } - if (opts->hash_id == 0) { - opts->hash_id = REFTABLE_HASH_SHA1; - } if (opts->block_size == 0) { opts->block_size = DEFAULT_BLOCK_SIZE; } @@ -90,7 +87,7 @@ static void options_set_defaults(struct reftable_write_options *opts) static int writer_version(struct reftable_writer *w) { - return (w->opts.hash_id == 0 || w->opts.hash_id == REFTABLE_HASH_SHA1) ? + return (w->hash_id == 0 || w->hash_id == REFTABLE_HASH_SHA1) ? 1 : 2; } @@ -107,7 +104,7 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest) if (writer_version(w) == 2) { uint32_t hash_id; - switch (w->opts.hash_id) { + switch (w->hash_id) { case REFTABLE_HASH_SHA1: hash_id = REFTABLE_FORMAT_ID_SHA1; break; @@ -134,7 +131,7 @@ static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ) reftable_buf_reset(&w->last_key); ret = block_writer_init(&w->block_writer_data, typ, w->block, w->opts.block_size, block_start, - hash_size(w->opts.hash_id)); + hash_size(w->hash_id)); if (ret < 0) return ret; @@ -147,21 +144,26 @@ static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ) int reftable_writer_new(struct reftable_writer **out, ssize_t (*writer_func)(void *, const void *, size_t), int (*flush_func)(void *), - void *writer_arg, const struct reftable_write_options *_opts) + void *writer_arg, + enum reftable_hash hash_id, + const struct reftable_write_options *_opts) { struct reftable_write_options opts = {0}; struct reftable_writer *wp; - wp = reftable_calloc(1, sizeof(*wp)); - if (!wp) - return REFTABLE_OUT_OF_MEMORY_ERROR; - if (_opts) opts = *_opts; options_set_defaults(&opts); if (opts.block_size >= (1 << 24)) return REFTABLE_API_ERROR; + if (!hash_id) + hash_id = REFTABLE_HASH_SHA1; + + wp = reftable_calloc(1, sizeof(*wp)); + if (!wp) + return REFTABLE_OUT_OF_MEMORY_ERROR; + reftable_buf_init(&wp->block_writer_data.last_key); reftable_buf_init(&wp->last_key); reftable_buf_init(&wp->scratch); @@ -173,6 +175,7 @@ int reftable_writer_new(struct reftable_writer **out, wp->write = writer_func; wp->write_arg = writer_arg; wp->opts = opts; + wp->hash_id = hash_id; wp->flush = flush_func; writer_reinit_block_writer(wp, REFTABLE_BLOCK_TYPE_REF); @@ -367,7 +370,7 @@ int reftable_writer_add_ref(struct reftable_writer *w, if (!w->opts.skip_index_objects && reftable_ref_record_val1(ref)) { reftable_buf_reset(&w->scratch); err = reftable_buf_add(&w->scratch, (char *)reftable_ref_record_val1(ref), - hash_size(w->opts.hash_id)); + hash_size(w->hash_id)); if (err < 0) goto out; @@ -379,7 +382,7 @@ int reftable_writer_add_ref(struct reftable_writer *w, if (!w->opts.skip_index_objects && reftable_ref_record_val2(ref)) { reftable_buf_reset(&w->scratch); err = reftable_buf_add(&w->scratch, reftable_ref_record_val2(ref), - hash_size(w->opts.hash_id)); + hash_size(w->hash_id)); if (err < 0) goto out;
diff --git a/reftable/writer.h b/reftable/writer.h index 9f53610..c08fc41 100644 --- a/reftable/writer.h +++ b/reftable/writer.h
@@ -27,6 +27,7 @@ struct reftable_writer { uint64_t next; uint64_t min_update_index, max_update_index; struct reftable_write_options opts; + enum reftable_hash hash_id; /* memory buffer for writing */ uint8_t *block;
diff --git a/remote.c b/remote.c index 00723b3..b17648d 100644 --- a/remote.c +++ b/remote.c
@@ -2315,6 +2315,8 @@ static void format_branch_comparison(struct strbuf *sb, bool up_to_date, int ours, int theirs, const char *branch_name, + const char *push_remote_name, + const char *push_branch_name, enum ahead_behind_flags abf, unsigned flags) { @@ -2350,9 +2352,15 @@ static void format_branch_comparison(struct strbuf *sb, "and can be fast-forwarded.\n", theirs), branch_name, theirs); - if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS)) - strbuf_addstr(sb, - _(" (use \"git pull\" to update your local branch)\n")); + if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS)) { + if (push_remote_name && push_branch_name) + strbuf_addf(sb, + _(" (use \"git pull %s %s\" to update your local branch)\n"), + push_remote_name, push_branch_name); + else + strbuf_addstr(sb, + _(" (use \"git pull\" to update your local branch)\n")); + } } else { strbuf_addf(sb, Q_("Your branch and '%s' have diverged,\n" @@ -2363,9 +2371,15 @@ static void format_branch_comparison(struct strbuf *sb, "respectively.\n", ours + theirs), branch_name, ours, theirs); - if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS)) - strbuf_addstr(sb, - _(" (use \"git pull\" if you want to integrate the remote branch with yours)\n")); + if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS)) { + if (push_remote_name && push_branch_name) + strbuf_addf(sb, + _(" (use \"git pull %s %s\" if you want to integrate the remote branch with yours)\n"), + push_remote_name, push_branch_name); + else + strbuf_addstr(sb, + _(" (use \"git pull\" if you want to integrate the remote branch with yours)\n")); + } } } @@ -2403,6 +2417,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb, int ours, theirs, cmp; int is_upstream, is_push; unsigned flags = 0; + const char *push_remote_name = NULL; + const char *push_branch_name = NULL; full_ref = resolve_compare_branch(branch, branches.items[i].string); @@ -2446,11 +2462,27 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb, if (is_upstream) flags |= ENABLE_ADVICE_PULL; - if (is_push) - flags |= ENABLE_ADVICE_PUSH; if (show_divergence_advice && is_upstream) flags |= ENABLE_ADVICE_DIVERGENCE; + if (is_push) { + flags |= ENABLE_ADVICE_PUSH; + if (!upstream_ref || strcmp(upstream_ref, full_ref)) { + push_remote_name = pushremote_for_branch(branch, NULL); + if (push_remote_name && + skip_prefix(full_ref, "refs/remotes/", &push_branch_name) && + skip_prefix(push_branch_name, push_remote_name, &push_branch_name) && + *push_branch_name == '/') { + push_branch_name++; + flags |= ENABLE_ADVICE_PULL; + } else { + push_remote_name = NULL; + } + } else { + flags |= ENABLE_ADVICE_PULL; + } + } format_branch_comparison(sb, !cmp, ours, theirs, short_ref, + push_remote_name, push_branch_name, abf, flags); reported = 1; @@ -2681,6 +2713,8 @@ static int remote_tracking(struct remote *remote, const char *refname, { char *dst; + if (!remote) + BUG("remote_tracking() called with NULL remote"); dst = apply_refspecs(&remote->fetch, refname); if (!dst) return -1; /* no tracking ref for refname at remote */
diff --git a/remote.h b/remote.h index 54b17e4..72a54d8 100644 --- a/remote.h +++ b/remote.h
@@ -62,12 +62,14 @@ struct remote_state { void remote_state_clear(struct remote_state *remote_state); struct remote_state *remote_state_new(void); - enum follow_remote_head_settings { - FOLLOW_REMOTE_NEVER = -1, - FOLLOW_REMOTE_CREATE = 0, - FOLLOW_REMOTE_WARN = 1, - FOLLOW_REMOTE_ALWAYS = 2, - }; +#define BUILTIN_FOLLOW_REMOTE_HEAD_DFLT FOLLOW_REMOTE_CREATE +enum follow_remote_head_settings { + FOLLOW_REMOTE_UNCONFIGURED = 0, + FOLLOW_REMOTE_NEVER, + FOLLOW_REMOTE_CREATE, + FOLLOW_REMOTE_WARN, + FOLLOW_REMOTE_ALWAYS, +}; struct remote { struct hashmap_entry ent;
diff --git a/repack-geometry.c b/repack-geometry.c index 2064683..15b3412 100644 --- a/repack-geometry.c +++ b/repack-geometry.c
@@ -32,7 +32,8 @@ void pack_geometry_init(struct pack_geometry *geometry, { struct packed_git *p; struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_multi_pack_index(existing->source); + struct odb_source_files *files = odb_source_files_downcast(existing->source); + struct multi_pack_index *m = get_multi_pack_index(files->packed); repo_for_each_pack(existing->repo, p) { if (geometry->midx_layer_threshold_set && m &&
diff --git a/repack-midx.c b/repack-midx.c index b6b1de7..7c7c362 100644 --- a/repack-midx.c +++ b/repack-midx.c
@@ -557,13 +557,14 @@ static void repack_make_midx_append_plan(struct repack_write_midx_opts *opts, struct midx_compaction_step **steps_p, size_t *steps_nr_p) { + struct odb_source_files *files = odb_source_files_downcast(opts->existing->source); struct multi_pack_index *m; struct midx_compaction_step *steps = NULL; struct midx_compaction_step *step; size_t steps_nr = 0, steps_alloc = 0; odb_reprepare(opts->existing->repo->objects); - m = get_multi_pack_index(opts->existing->source); + m = get_multi_pack_index(files->packed); if (opts->names->nr) { struct strbuf buf = STRBUF_INIT; @@ -606,6 +607,7 @@ static int repack_make_midx_compaction_plan(struct repack_write_midx_opts *opts, struct midx_compaction_step **steps_p, size_t *steps_nr_p) { + struct odb_source_files *files = odb_source_files_downcast(opts->existing->source); struct multi_pack_index *m; struct midx_compaction_step *steps = NULL; struct midx_compaction_step step = { 0 }; @@ -618,7 +620,7 @@ static int repack_make_midx_compaction_plan(struct repack_write_midx_opts *opts, opts->existing->repo); odb_reprepare(opts->existing->repo->objects); - m = get_multi_pack_index(opts->existing->source); + m = get_multi_pack_index(files->packed); for (i = 0; m && i < m->num_packs + m->num_packs_in_base; i++) { if (prepare_midx_pack(m, i)) { @@ -938,6 +940,7 @@ static int repack_make_midx_compaction_plan(struct repack_write_midx_opts *opts, static int write_midx_incremental(struct repack_write_midx_opts *opts) { + struct odb_source_files *files = odb_source_files_downcast(opts->existing->source); struct midx_compaction_step *steps = NULL; struct strbuf lock_name = STRBUF_INIT; struct lock_file lf; @@ -946,7 +949,7 @@ static int write_midx_incremental(struct repack_write_midx_opts *opts) size_t i; int ret = 0; - get_midx_chain_filename(opts->existing->source, &lock_name); + get_midx_chain_filename(files->packed, &lock_name); if (safe_create_leading_directories(opts->existing->repo, lock_name.buf)) die_errno(_("unable to create leading directories of %s"),
diff --git a/repack.c b/repack.c index 571dabb..d2aa58e 100644 --- a/repack.c +++ b/repack.c
@@ -59,10 +59,10 @@ void repack_remove_redundant_pack(struct repository *repo, const char *dir_name, bool wrote_incremental_midx) { struct strbuf buf = STRBUF_INIT; - struct odb_source *source = repo->objects->sources; - struct multi_pack_index *m = get_multi_pack_index(source); + struct odb_source_files *files = odb_source_files_downcast(repo->objects->sources); + struct multi_pack_index *m = get_multi_pack_index(files->packed); strbuf_addf(&buf, "%s.pack", base_name); - if (m && source->local && midx_contains_pack(m, buf.buf)) { + if (m && files->base.local && midx_contains_pack(m, buf.buf)) { clear_midx_file(repo); if (!wrote_incremental_midx) clear_incremental_midx_files(repo, NULL);
diff --git a/replay.c b/replay.c index da531d5..463c900 100644 --- a/replay.c +++ b/replay.c
@@ -36,12 +36,16 @@ static struct commit *peel_committish(struct repository *repo, { struct object *obj; struct object_id oid; + struct commit *commit; if (repo_get_oid(repo, name, &oid)) die(_("'%s' is not a valid commit-ish for %s"), name, mode); obj = parse_object_or_die(repo, &oid, name); - return (struct commit *)repo_peel_to_type(repo, name, 0, obj, - OBJ_COMMIT); + commit = (struct commit *)repo_peel_to_type(repo, name, 0, obj, + OBJ_COMMIT); + if (!commit) + die(_("'%s' does not point to a commit for %s"), name, mode); + return commit; } static char *get_author(const char *message) @@ -351,10 +355,10 @@ void replay_result_release(struct replay_result *result) free(result->updates); } -static void replay_result_queue_update(struct replay_result *result, - const char *refname, - const struct object_id *old_oid, - const struct object_id *new_oid) +void replay_result_queue_update(struct replay_result *result, + const char *refname, + const struct object_id *old_oid, + const struct object_id *new_oid) { ALLOC_GROW(result->updates, result->updates_nr + 1, result->updates_alloc); result->updates[result->updates_nr].refname = xstrdup(refname);
diff --git a/replay.h b/replay.h index faf95c7..491db14 100644 --- a/replay.h +++ b/replay.h
@@ -80,6 +80,11 @@ struct replay_result { void replay_result_release(struct replay_result *result); +void replay_result_queue_update(struct replay_result *result, + const char *refname, + const struct object_id *old_oid, + const struct object_id *new_oid); + /* * Replay a set of commits onto a new location. Leaves both the working tree, * index and references untouched. Reference updates caused by the replay will
diff --git a/repo-settings.c b/repo-settings.c index 208e09f..f3be3b8 100644 --- a/repo-settings.c +++ b/repo-settings.c
@@ -177,22 +177,6 @@ void repo_settings_set_big_file_threshold(struct repository *repo, unsigned long repo->settings.big_file_threshold = value; } -enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo) -{ - const char *value; - - if (!repo_config_get_string_tmp(repo, "core.logallrefupdates", &value)) { - if (value && !strcasecmp(value, "always")) - return LOG_REFS_ALWAYS; - else if (git_config_bool("core.logallrefupdates", value)) - return LOG_REFS_NORMAL; - else - return LOG_REFS_NONE; - } - - return LOG_REFS_UNSET; -} - int repo_settings_get_warn_ambiguous_refs(struct repository *repo) { prepare_repo_settings(repo);
diff --git a/repo-settings.h b/repo-settings.h index cad9c3f..e5253ea 100644 --- a/repo-settings.h +++ b/repo-settings.h
@@ -16,13 +16,6 @@ enum fetch_negotiation_setting { FETCH_NEGOTIATION_NOOP, }; -enum log_refs_config { - LOG_REFS_UNSET = -1, - LOG_REFS_NONE = 0, - LOG_REFS_NORMAL, - LOG_REFS_ALWAYS -}; - struct repo_settings { int initialized; @@ -86,8 +79,6 @@ struct repo_settings { void prepare_repo_settings(struct repository *r); void repo_settings_clear(struct repository *r); -/* Read the value for "core.logAllRefUpdates". */ -enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo); /* Read the value for "core.warnAmbiguousRefs". */ int repo_settings_get_warn_ambiguous_refs(struct repository *repo); /* Read the value for "core.hooksPath". */
diff --git a/repository.c b/repository.c index 187dd47..2ef0778 100644 --- a/repository.c +++ b/repository.c
@@ -73,6 +73,7 @@ void initialize_repository(struct repository *repo) ALLOC_ARRAY(repo->index, 1); index_state_init(repo->index, repo); repo->check_deprecated_config = true; + repo->bare_cfg = -1; repo_config_values_init(&repo->config_values_private_); /* @@ -375,6 +376,7 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->gitdir); FREE_AND_NULL(repo->commondir); + FREE_AND_NULL(repo->prefix); FREE_AND_NULL(repo->graft_file); FREE_AND_NULL(repo->index_file); FREE_AND_NULL(repo->worktree); @@ -421,6 +423,11 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->remote_state); } + if (repo->refs_private) { + ref_store_release(repo->refs_private); + FREE_AND_NULL(repo->refs_private); + } + strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e) ref_store_release(e->value); strmap_clear(&repo->submodule_ref_stores, 1);
diff --git a/repository.h b/repository.h index 36e2db2..b767307 100644 --- a/repository.h +++ b/repository.h
@@ -53,6 +53,14 @@ struct repository { char *commondir; /* + * The "prefix", a path to the current working directory relative to + * the work tree root, or NULL, if the current working directory is not + * a strict subdirectory of the work tree root. The prefix always ends + * with a '/' character. + */ + char *prefix; + + /* * Holds any information related to accessing the raw object content. */ struct object_database *objects; @@ -118,6 +126,13 @@ struct repository { bool worktree_config_is_bogus; /* + * Whether the repository is bare, as set by "core.bare" config or + * inferred during repository discovery. -1 means unset/unknown, 0 + * means non-bare, 1 means bare. + */ + int bare_cfg; + + /* * Path from the root of the top-level superproject down to this * repository. This is only non-NULL if the repository is initialized * as a submodule of another repository.
diff --git a/rerere.c b/rerere.c index 8232542..2161009 100644 --- a/rerere.c +++ b/rerere.c
@@ -439,7 +439,7 @@ static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_siz struct strbuf buf = STRBUF_INIT, out = STRBUF_INIT; int has_conflicts = 0; if (hash) - the_hash_algo->init_fn(&ctx); + git_hash_init(&ctx, the_hash_algo); while (!io->getline(&buf, io)) { if (is_cmarker(buf.buf, '<', marker_size)) {
diff --git a/reset.c b/reset.c index 46e30e6..71254bd 100644 --- a/reset.c +++ b/reset.c
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "cache-tree.h" #include "gettext.h" @@ -13,13 +11,14 @@ #include "unpack-trees.h" #include "hook.h" -static int update_refs(const struct reset_head_opts *opts, +static int update_refs(struct repository *repo, + const struct reset_working_tree_options *opts, const struct object_id *oid, const struct object_id *head) { - unsigned detach_head = opts->flags & RESET_HEAD_DETACH; - unsigned run_hook = opts->flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; - unsigned update_orig_head = opts->flags & RESET_ORIG_HEAD; + unsigned detach_head = opts->flags & RESET_WORKING_TREE_DETACH; + unsigned run_hook = opts->flags & RESET_WORKING_TREE_RUN_POST_CHECKOUT_HOOK; + unsigned update_orig_head = opts->flags & RESET_WORKING_TREE_UPDATE_ORIG_HEAD; const struct object_id *orig_head = opts->orig_head; const char *switch_to_branch = opts->branch; const char *reflog_branch = opts->branch_msg; @@ -42,19 +41,19 @@ static int update_refs(const struct reset_head_opts *opts, prefix_len = msg.len; if (update_orig_head) { - if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig)) + if (!repo_get_oid(repo, "ORIG_HEAD", &oid_old_orig)) old_orig = &oid_old_orig; if (head) { if (!reflog_orig_head) { strbuf_addstr(&msg, "updating ORIG_HEAD"); reflog_orig_head = msg.buf; } - refs_update_ref(get_main_ref_store(the_repository), + refs_update_ref(get_main_ref_store(repo), reflog_orig_head, "ORIG_HEAD", orig_head ? orig_head : head, old_orig, 0, UPDATE_REFS_MSG_ON_ERR); } else if (old_orig) - refs_delete_ref(get_main_ref_store(the_repository), + refs_delete_ref(get_main_ref_store(repo), NULL, "ORIG_HEAD", old_orig, 0); } @@ -64,40 +63,45 @@ static int update_refs(const struct reset_head_opts *opts, reflog_head = msg.buf; } if (!switch_to_branch) - ret = refs_update_ref(get_main_ref_store(the_repository), + ret = refs_update_ref(get_main_ref_store(repo), reflog_head, "HEAD", oid, head, detach_head ? REF_NO_DEREF : 0, UPDATE_REFS_MSG_ON_ERR); else { - ret = refs_update_ref(get_main_ref_store(the_repository), + ret = refs_update_ref(get_main_ref_store(repo), reflog_branch ? reflog_branch : reflog_head, switch_to_branch, oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR); if (!ret) - ret = refs_update_symref(get_main_ref_store(the_repository), + ret = refs_update_symref(get_main_ref_store(repo), "HEAD", switch_to_branch, reflog_head); } if (!ret && run_hook) - run_hooks_l(the_repository, "post-checkout", - oid_to_hex(head ? head : null_oid(the_hash_algo)), + run_hooks_l(repo, "post-checkout", + oid_to_hex(head ? head : null_oid(repo->hash_algo)), oid_to_hex(oid), "1", NULL); strbuf_release(&msg); return ret; } -int reset_head(struct repository *r, const struct reset_head_opts *opts) +int reset_working_tree(struct repository *r, + const struct reset_working_tree_options *opts) { const struct object_id *oid = opts->oid; const char *switch_to_branch = opts->branch; - unsigned reset_hard = opts->flags & RESET_HEAD_HARD; - unsigned refs_only = opts->flags & RESET_HEAD_REFS_ONLY; - unsigned update_orig_head = opts->flags & RESET_ORIG_HEAD; + unsigned reset_hard = opts->flags & RESET_WORKING_TREE_HARD; + unsigned refs_only = opts->flags & RESET_WORKING_TREE_REFS_ONLY; + unsigned update_head = opts->flags & RESET_WORKING_TREE_UPDATE_HEAD; + unsigned update_orig_head = opts->flags & RESET_WORKING_TREE_UPDATE_ORIG_HEAD; + unsigned dry_run = opts->flags & RESET_WORKING_TREE_DRY_RUN; struct object_id *head = NULL, head_oid; struct tree_desc desc[2] = { { NULL }, { NULL } }; struct lock_file lock = LOCK_INIT; struct unpack_trees_options unpack_tree_opts = { 0 }; struct tree *tree; + struct index_state scratch_index = INDEX_STATE_INIT(r); + struct index_state *istate; const char *action; int ret = 0, nr = 0; @@ -110,12 +114,18 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts) if (opts->branch_msg && !opts->branch) BUG("branch reflog message given without a branch"); - if (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) { + if (update_orig_head && !update_head) + BUG("cannot update ORIG_HEAD without updating HEAD"); + + if (!refs_only && !dry_run && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) { ret = -1; goto leave_reset_head; } - if (!repo_get_oid(r, "HEAD", &head_oid)) { + if (opts->oid_from) { + oidcpy(&head_oid, opts->oid_from); + head = &head_oid; + } else if (!repo_get_oid(r, "HEAD", &head_oid)) { head = &head_oid; } else if (!oid || !reset_hard) { ret = error(_("could not determine HEAD revision")); @@ -125,26 +135,42 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts) if (!oid) oid = &head_oid; - if (refs_only) - return update_refs(opts, oid, head); + if (refs_only) { + if (!dry_run && update_head) + return update_refs(r, opts, oid, head); + return 0; + } + + if (dry_run) { + if (read_index_from(&scratch_index, r->index_file, r->gitdir) < 0 || + index_state_unmerged_to_stage0(&scratch_index) < 0) { + ret = error(_("could not read index")); + goto leave_reset_head; + } + + istate = &scratch_index; + } else { + if (repo_read_index_unmerged(r) < 0) { + ret = error(_("could not read index")); + goto leave_reset_head; + } + istate = r->index; + } action = reset_hard ? "reset" : "checkout"; setup_unpack_trees_porcelain(&unpack_tree_opts, action); unpack_tree_opts.head_idx = 1; - unpack_tree_opts.src_index = r->index; - unpack_tree_opts.dst_index = r->index; + unpack_tree_opts.src_index = istate; + unpack_tree_opts.dst_index = istate; unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update = 1; + unpack_tree_opts.update = !dry_run; + unpack_tree_opts.dry_run = dry_run; unpack_tree_opts.merge = 1; unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */ - unpack_tree_opts.skip_cache_tree_update = 1; init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL); - if (reset_hard) + if (reset_hard) { + unpack_tree_opts.skip_cache_tree_update = 1; unpack_tree_opts.reset = UNPACK_RESET_PROTECT_UNTRACKED; - - if (repo_read_index_unmerged(r) < 0) { - ret = error(_("could not read index")); - goto leave_reset_head; } if (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) { @@ -163,25 +189,31 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts) goto leave_reset_head; } - tree = repo_parse_tree_indirect(the_repository, oid); + if (dry_run) + goto leave_reset_head; + + tree = repo_parse_tree_indirect(r, 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 (reset_hard) + prime_cache_tree(r, r->index, tree); if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) { ret = error(_("could not write index")); goto leave_reset_head; } - if (oid != &head_oid || update_orig_head || switch_to_branch) - ret = update_refs(opts, oid, head); + if (update_head && + (oid != &head_oid || update_orig_head || switch_to_branch)) + ret = update_refs(r, opts, oid, head); leave_reset_head: rollback_lock_file(&lock); clear_unpack_trees_porcelain(&unpack_tree_opts); + release_index(&scratch_index); while (nr) free((void *)desc[--nr].buffer); return ret;
diff --git a/reset.h b/reset.h index a28f818..4c992ba 100644 --- a/reset.h +++ b/reset.h
@@ -6,23 +6,43 @@ #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" -/* Request a detached checkout */ -#define RESET_HEAD_DETACH (1<<0) -/* Request a reset rather than a checkout */ -#define RESET_HEAD_HARD (1<<1) -/* Run the post-checkout hook */ -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) -/* Only update refs, do not touch the worktree */ -#define RESET_HEAD_REFS_ONLY (1<<3) -/* Update ORIG_HEAD as well as HEAD */ -#define RESET_ORIG_HEAD (1<<4) +enum reset_working_tree_flags { + /* Request a detached checkout */ + RESET_WORKING_TREE_DETACH = (1 << 0), -struct reset_head_opts { + /* Request a reset rather than a checkout */ + RESET_WORKING_TREE_HARD = (1 << 1), + + /* Run the post-checkout hook */ + RESET_WORKING_TREE_RUN_POST_CHECKOUT_HOOK = (1 << 2), + + /* Only update refs, do not touch the worktree */ + RESET_WORKING_TREE_REFS_ONLY = (1 << 3), + + /* Update HEAD */ + RESET_WORKING_TREE_UPDATE_HEAD = (1 << 4), + + /* Update ORIG_HEAD */ + RESET_WORKING_TREE_UPDATE_ORIG_HEAD = (1 << 5), + + /* + * Perform a dry-run by performing the operation without updating + * any user-visible state. + */ + RESET_WORKING_TREE_DRY_RUN = (1 << 6), +}; + +struct reset_working_tree_options { /* * The commit to checkout/reset to. Defaults to HEAD. */ const struct object_id *oid; /* + * The commit to checkout/reset from when doing a two-way merge. This + * is used as one of the sides to merge. + */ + const struct object_id *oid_from; + /* * Optional value to set ORIG_HEAD. Defaults to HEAD. */ const struct object_id *orig_head; @@ -33,7 +53,7 @@ struct reset_head_opts { /* * Flags defined above. */ - unsigned flags; + enum reset_working_tree_flags flags; /* * Optional reflog message for branch, defaults to head_msg. */ @@ -45,7 +65,8 @@ struct reset_head_opts { const char *head_msg; /* * Optional reflog message for ORIG_HEAD, if this omitted and flags - * contains RESET_ORIG_HEAD then default_reflog_action must be given. + * contains RESET_WORKING_TREE_UPDATE_ORIG_HEAD then + * default_reflog_action must be given. */ const char *orig_head_msg; /* @@ -55,6 +76,6 @@ struct reset_head_opts { const char *default_reflog_action; }; -int reset_head(struct repository *r, const struct reset_head_opts *opts); +int reset_working_tree(struct repository *r, const struct reset_working_tree_options *opts); #endif
diff --git a/revision.c b/revision.c index e91d7e1..8425f67 100644 --- a/revision.c +++ b/revision.c
@@ -26,6 +26,7 @@ #include "decorate.h" #include "string-list.h" #include "line-log.h" +#include "log-tree.h" #include "mailmap.h" #include "commit-slab.h" #include "cache-tree.h" @@ -476,16 +477,15 @@ static struct commit *handle_commit(struct rev_info *revs, static int everybody_uninteresting(struct prio_queue *orig, struct commit **interesting_cache) { - size_t i; + struct commit *commit; if (*interesting_cache) { - struct commit *commit = *interesting_cache; + commit = *interesting_cache; if (!(commit->object.flags & UNINTERESTING)) return 0; } - for (i = 0; i < orig->nr; i++) { - struct commit *commit = orig->array[i].data; + prio_queue_for_each(orig, commit) { if (commit->object.flags & UNINTERESTING) continue; @@ -707,6 +707,8 @@ static int convert_pathspec_to_bloom_keyvec(struct bloom_keyvec **out, static void prepare_to_use_bloom_filter(struct rev_info *revs) { + release_revisions_bloom_keyvecs(revs); + if (!revs->commits) return; @@ -1442,7 +1444,7 @@ static int limit_list(struct rev_info *revs) struct commit_list *original_list = revs->commits; struct commit_list *newlist = NULL; struct commit_list **p = &newlist; - struct commit *interesting_cache = NULL; + struct commit *commit, *interesting_cache = NULL; struct prio_queue queue = { .compare = compare_commits_by_commit_date }; if (revs->ancestry_path_implicit_bottoms) { @@ -1457,8 +1459,7 @@ static int limit_list(struct rev_info *revs) prio_queue_put(&queue, commit); } - while (queue.nr) { - struct commit *commit = prio_queue_get(&queue); + while ((commit = prio_queue_get(&queue))) { struct object *obj = &commit->object; if (commit == interesting_cache) @@ -1903,8 +1904,13 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags, return 0; while (1) { it = get_reference(revs, arg, &oid, 0); - if (!it && revs->ignore_missing) - return 0; + if (!it) { + if (revs->ignore_missing) + return 0; + if (revs->do_not_die_on_missing_objects) + return 0; + return -1; + } if (it->type != OBJ_TAG) break; if (!((struct tag*)it)->tagged) @@ -3304,6 +3310,7 @@ void release_revisions(struct rev_info *revs) line_log_free(revs); oidset_clear(&revs->missing_commits); release_revisions_bloom_keyvecs(revs); + release_follow_pathspec_slab(revs); } static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child) @@ -4056,8 +4063,9 @@ static enum rewrite_result rewrite_one_1(struct rev_info *revs, static void merge_queue_into_prio_queue(struct prio_queue *from, struct prio_queue *to) { - while (from->nr) - prio_queue_put(to, prio_queue_get(from)); + struct commit *item; + while ((item = prio_queue_get(from))) + prio_queue_put(to, item); } static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
diff --git a/revision.h b/revision.h index 00c392b..569b3fa 100644 --- a/revision.h +++ b/revision.h
@@ -66,6 +66,7 @@ struct repository; struct rev_info; struct string_list; struct saved_parents; +struct follow_pathspec_slab; struct bloom_keyvec; struct bloom_filter_settings; struct option; @@ -363,6 +364,9 @@ struct rev_info { /* copies of the parent lists, for --full-diff display */ struct saved_parents *saved_parents_slab; + /* per-commit pathspec for --follow across merges */ + struct follow_pathspec_slab *follow_pathspec_slab; + struct commit_list *previous_parents; struct commit_list *ancestry_path_bottoms; const char *break_bar;
diff --git a/run-command.c b/run-command.c index e70a8a3..ce84db8 100644 --- a/run-command.c +++ b/run-command.c
@@ -706,7 +706,7 @@ int start_command(struct child_process *cmd) failed_errno = errno; if (need_in) close_pair(fdin); - else if (cmd->in) + else if (cmd->in > 0) close(cmd->in); str = "standard output"; goto fail_pipe; @@ -720,11 +720,11 @@ int start_command(struct child_process *cmd) failed_errno = errno; if (need_in) close_pair(fdin); - else if (cmd->in) + else if (cmd->in > 0) close(cmd->in); if (need_out) close_pair(fdout); - else if (cmd->out) + else if (cmd->out > 0) close(cmd->out); str = "standard error"; fail_pipe:
diff --git a/sequencer.c b/sequencer.c index 57855b0..1355a99 100644 --- a/sequencer.c +++ b/sequencer.c
@@ -2627,6 +2627,27 @@ static int is_command(enum todo_command command, const char **bol) return 0; } +bool sequencer_parse_todo_command(const char **p, enum todo_command *cmd) +{ + const char *s = *p; + + for (int i = 0; i < TODO_COMMENT; i++) + if (is_command(i, p)) { + *cmd = i; + return true; + } + + if (starts_with(s, comment_line_str)) { + *cmd = TODO_COMMENT; + return true; + } else if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n') || !s[0]) { + *cmd = TODO_COMMENT; + return true; + } + + return false; +} + static int check_label_or_ref_arg(enum todo_command command, const char *arg) { switch (command) { @@ -2716,30 +2737,24 @@ static int parse_insn_line(struct repository *r, struct replay_opts *opts, { struct object_id commit_oid; char *end_of_object_name; - int i, saved, status, padding; + int saved, status, padding; item->flags = 0; /* left-trim */ bol += strspn(bol, " \t"); - if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) { - item->command = TODO_COMMENT; + if (!sequencer_parse_todo_command(&bol, &item->command)) + return error(_("invalid command '%.*s'"), + (int)strcspn(bol, " \t\r\n"), bol); + + if (item->command == TODO_COMMENT) { item->commit = NULL; item->arg_offset = bol - buf; item->arg_len = eol - bol; return 0; } - for (i = 0; i < TODO_COMMENT; i++) - if (is_command(i, &bol)) { - item->command = i; - break; - } - if (i >= TODO_COMMENT) - return error(_("invalid command '%.*s'"), - (int)strcspn(bol, " \t\r\n"), bol); - /* Eat up extra spaces/ tabs before object name */ padding = strspn(bol, " \t"); bol += padding; @@ -4677,7 +4692,10 @@ static void create_autostash_internal(struct repository *r, if (has_unstaged_changes(r, 1) || has_uncommitted_changes(r, 1)) { struct child_process stash = CHILD_PROCESS_INIT; - struct reset_head_opts ropts = { .flags = RESET_HEAD_HARD }; + struct reset_working_tree_options ropts = { + .flags = RESET_WORKING_TREE_HARD | + RESET_WORKING_TREE_UPDATE_HEAD, + }; struct object_id oid; strvec_pushl(&stash.args, @@ -4707,7 +4725,7 @@ static void create_autostash_internal(struct repository *r, if (!silent) printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(r, &ropts) < 0) + if (reset_working_tree(r, &ropts) < 0) die(_("could not reset --hard")); discard_index(r->index); if (repo_read_index(r) < 0) @@ -4867,16 +4885,18 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts, const char *onto_name, const struct object_id *onto, const struct object_id *orig_head) { - struct reset_head_opts ropts = { + struct reset_working_tree_options ropts = { .oid = onto, .orig_head = orig_head, - .flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD | - RESET_HEAD_RUN_POST_CHECKOUT_HOOK, + .flags = RESET_WORKING_TREE_DETACH | + RESET_WORKING_TREE_UPDATE_HEAD | + RESET_WORKING_TREE_UPDATE_ORIG_HEAD | + RESET_WORKING_TREE_RUN_POST_CHECKOUT_HOOK, .head_msg = reflog_message(opts, "start", "checkout %s", onto_name), .default_reflog_action = sequencer_reflog_action(opts) }; - if (reset_head(r, &ropts)) { + if (reset_working_tree(r, &ropts)) { apply_autostash(rebase_path_autostash()); sequencer_remove_state(opts); return error(_("could not detach HEAD"));
diff --git a/sequencer.h b/sequencer.h index 3164bd4..64a9c7f 100644 --- a/sequencer.h +++ b/sequencer.h
@@ -265,6 +265,14 @@ int read_author_script(const char *path, char **name, char **email, char **date, int write_basic_state(struct replay_opts *opts, const char *head_name, struct commit *onto, const struct object_id *orig_head); void sequencer_post_commit_cleanup(struct repository *r, int verbose); + +/* + * Try to parse the todo command pointed to by *p. On success sets cmd, + * advances p and returns true. On failure returns false, leaves p and + * cmd unchanged. + */ +bool sequencer_parse_todo_command(const char **p, enum todo_command *cmd); + int sequencer_get_last_command(struct repository* r, enum replay_action *action); int sequencer_determine_whence(struct repository *r, enum commit_whence *whence);
diff --git a/setup.c b/setup.c index b465265..d318081 100644 --- a/setup.c +++ b/setup.c
@@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" @@ -11,7 +10,6 @@ #include "object-file.h" #include "object-name.h" #include "refs.h" -#include "replace-object.h" #include "repository.h" #include "config.h" #include "dir.h" @@ -750,9 +748,9 @@ static int check_repo_format(const char *var, const char *value, return read_worktree_config(var, value, ctx, vdata); } -static int check_repository_format_gently(const char *gitdir, - struct repository_format *candidate, - int *nongit_ok) +static int read_and_verify_repository_format(struct repository_format *format, + const char *gitdir, + int *nongit_ok) { struct strbuf sb = STRBUF_INIT; struct strbuf err = STRBUF_INIT; @@ -760,7 +758,7 @@ static int check_repository_format_gently(const char *gitdir, has_common = get_common_dir(&sb, gitdir); strbuf_addstr(&sb, "/config"); - read_repository_format(candidate, sb.buf); + read_repository_format(format, sb.buf); strbuf_release(&sb); /* @@ -768,10 +766,10 @@ static int check_repository_format_gently(const char *gitdir, * we treat a missing config as a silent "ok", even when nongit_ok * is unset. */ - if (candidate->version < 0) + if (format->version < 0) return 0; - if (verify_repository_format(candidate, &err) < 0) { + if (verify_repository_format(format, &err) < 0) { if (nongit_ok) { warning("%s", err.buf); strbuf_release(&err); @@ -781,28 +779,37 @@ static int check_repository_format_gently(const char *gitdir, die("%s", err.buf); } - string_list_clear(&candidate->unknown_extensions, 0); - string_list_clear(&candidate->v1_only_extensions, 0); + string_list_clear(&format->unknown_extensions, 0); + string_list_clear(&format->v1_only_extensions, 0); - if (candidate->worktree_config) { + if (format->worktree_config) { /* * pick up core.bare and core.worktree from per-worktree * config if present */ strbuf_addf(&sb, "%s/config.worktree", gitdir); - git_config_from_file(read_worktree_config, sb.buf, candidate); + git_config_from_file(read_worktree_config, sb.buf, format); strbuf_release(&sb); has_common = 0; } - if (!has_common) { - if (candidate->is_bare != -1) { - is_bare_repository_cfg = candidate->is_bare; - } - if (candidate->work_tree) { - free(git_work_tree_cfg); - git_work_tree_cfg = xstrdup(candidate->work_tree); - } + if (startup_info->force_bare_repository) { + format->is_bare = 1; + FREE_AND_NULL(format->work_tree); + } else if (has_common) { + /* + * When sharing a common dir with another repository (e.g. a + * linked worktree), do not let this repository's config + * dictate bareness; it is inherited from the main worktree. + */ + format->is_bare = -1; + + /* + * Furthermore, "core.worktree" is supposed to be ignored when + * we have a commondir configured, unless it comes from the + * per-worktree configuration. + */ + FREE_AND_NULL(format->work_tree); } return 0; @@ -917,7 +924,7 @@ int verify_repository_format(const struct repository_format *format, return 0; } -void read_gitfile_error_die(int error_code, const char *path, const char *dir) +void read_gitfile_error_die(int error_code, const char *path) { switch (error_code) { case READ_GITFILE_ERR_NOT_A_FILE: @@ -937,7 +944,8 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir) case READ_GITFILE_ERR_NO_PATH: die(_("no path in gitfile: %s"), path); case READ_GITFILE_ERR_NOT_A_REPO: - die(_("not a git repository: %s"), dir); + die(_("gitfile does not point to a valid repository: %s"), + path); default: BUG("unknown error code"); } @@ -1028,49 +1036,25 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) if (return_error_code) *return_error_code = error_code; else if (error_code) - read_gitfile_error_die(error_code, path, dir); + read_gitfile_error_die(error_code, path); free(buf); return error_code ? NULL : path; } -static void setup_git_env_internal(struct repository *repo, - const char *git_dir) +static void apply_gitdir_and_environment(struct repository *repo, const char *path) { - char *git_replace_ref_base; - const char *shallow_file; - const char *replace_ref_base; - struct set_gitdir_args args = { NULL }; struct strvec to_free = STRVEC_INIT; + struct set_gitdir_args args = { + .commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT), + .graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT), + .index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT), + .disable_ref_updates = !!getenv(GIT_QUARANTINE_ENVIRONMENT), + }; - args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT); - args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT); - args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT); - if (getenv(GIT_QUARANTINE_ENVIRONMENT)) - args.disable_ref_updates = true; + repo_set_gitdir(repo, path, &args); - repo_set_gitdir(repo, git_dir, &args); strvec_clear(&to_free); - - if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) - disable_replace_refs(); - replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT); - git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base - : "refs/replace/"); - update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base); - - shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); - if (shallow_file) - set_alternate_shallow_file(repo, shallow_file, 0); - - if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0)) - fetch_if_missing = 0; -} - -static void set_git_dir_1(struct repository *repo, const char *path) -{ - xsetenv(GIT_DIR_ENVIRONMENT, path, 1); - setup_git_env_internal(repo, path); } static void update_relative_gitdir(const char *name UNUSED, @@ -1084,11 +1068,12 @@ static void update_relative_gitdir(const char *name UNUSED, trace_printf_key(&trace_setup_key, "setup: move $GIT_DIR to '%s'", path); - set_git_dir_1(repo, path); + apply_gitdir_and_environment(repo, path); + xsetenv(GIT_DIR_ENVIRONMENT, path, 1); free(path); } -static void set_git_dir(struct repository *repo, const char *path, int make_realpath) +static void apply_and_export_relative_gitdir(struct repository *repo, const char *path, int make_realpath) { struct strbuf realpath = STRBUF_INIT; @@ -1097,21 +1082,61 @@ static void set_git_dir(struct repository *repo, const char *path, int make_real path = realpath.buf; } - set_git_dir_1(repo, path); + apply_gitdir_and_environment(repo, path); + xsetenv(GIT_DIR_ENVIRONMENT, path, 1); + if (!is_absolute_path(path)) chdir_notify_register(NULL, update_relative_gitdir, repo); strbuf_release(&realpath); } -static const char *setup_explicit_git_dir(struct repository *repo, +struct repo_discovery { + struct repository_format format; + char *gitdir; + char *worktree; + char *prefix; +}; + +#define REPO_DISCOVERY_INIT { \ + .format = REPOSITORY_FORMAT_INIT, \ +} + +static void repo_discovery_release(struct repo_discovery *r) +{ + clear_repository_format(&r->format); + free(r->gitdir); + free(r->worktree); + free(r->prefix); +} + +static void repo_discovery_set_gitdir(struct repo_discovery *r, + const char *gitdir, + int make_realpath) +{ + free(r->gitdir); + if (make_realpath) { + struct strbuf realpath = STRBUF_INIT; + strbuf_realpath(&realpath, gitdir, 1); + r->gitdir = strbuf_detach(&realpath, NULL); + } else { + r->gitdir = xstrdup(gitdir); + } +} + +static void repo_discovery_set_worktree(struct repo_discovery *r, + const char *worktree) +{ + free(r->worktree); + r->worktree = real_pathdup(worktree, 1); +} + +static void repo_discover_explicit_gitdir(struct repo_discovery *discovery, const char *gitdirenv, struct strbuf *cwd, - struct repository_format *repo_fmt, int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); - const char *worktree; char *gitfile; int offset; @@ -1127,150 +1152,136 @@ static const char *setup_explicit_git_dir(struct repository *repo, if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; - free(gitfile); - return NULL; + goto out; } die(_("not a git repository: '%s'"), gitdirenv); } - if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) { - free(gitfile); - return NULL; - } + if (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok)) + goto out; /* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */ - if (work_tree_env) - set_git_work_tree(repo, work_tree_env); - else if (is_bare_repository_cfg > 0) { - if (git_work_tree_cfg) { - /* #22.2, #30 */ - warning("core.bare and core.worktree do not make sense"); - repo->worktree_config_is_bogus = true; - } - + if (work_tree_env) { + /* + * The environment variable overrides "core.worktree". This + * also has the consequence that we don't want to flag cases as + * bogus where we have both "core.worktree" and "core.bare", so + * we have to explicitly unset the configuration. + */ + FREE_AND_NULL(discovery->format.work_tree); + repo_discovery_set_worktree(discovery, work_tree_env); + } else if (discovery->format.is_bare > 0) { /* #18, #26 */ - set_git_dir(repo, gitdirenv, 0); - free(gitfile); - return NULL; - } - else if (git_work_tree_cfg) { /* #6, #14 */ - if (is_absolute_path(git_work_tree_cfg)) - set_git_work_tree(repo, git_work_tree_cfg); - else { + repo_discovery_set_gitdir(discovery, gitdirenv, 0); + goto out; + } else if (discovery->format.work_tree) { /* #6, #14 */ + if (is_absolute_path(discovery->format.work_tree)) { + repo_discovery_set_worktree(discovery, discovery->format.work_tree); + } else { char *core_worktree; if (chdir(gitdirenv)) die_errno(_("cannot chdir to '%s'"), gitdirenv); - if (chdir(git_work_tree_cfg)) - die_errno(_("cannot chdir to '%s'"), git_work_tree_cfg); + if (chdir(discovery->format.work_tree)) + die_errno(_("cannot chdir to '%s'"), discovery->format.work_tree); core_worktree = xgetcwd(); if (chdir(cwd->buf)) die_errno(_("cannot come back to cwd")); - set_git_work_tree(repo, core_worktree); + repo_discovery_set_worktree(discovery, core_worktree); free(core_worktree); } - } - else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) { + } else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) { /* #16d */ - set_git_dir(repo, gitdirenv, 0); - free(gitfile); - return NULL; - } - else /* #2, #10 */ - set_git_work_tree(repo, "."); - - /* set_git_work_tree() must have been called by now */ - worktree = repo_get_work_tree(repo); - - /* both repo_get_work_tree() and cwd are already normalized */ - if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */ - set_git_dir(repo, gitdirenv, 0); - free(gitfile); - return NULL; + repo_discovery_set_gitdir(discovery, gitdirenv, 0); + goto out; + } else { /* #2, #10 */ + repo_discovery_set_worktree(discovery, "."); } - offset = dir_inside_of(cwd->buf, worktree); - if (offset >= 0) { /* cwd inside worktree? */ - set_git_dir(repo, gitdirenv, 1); - if (chdir(worktree)) - die_errno(_("cannot chdir to '%s'"), worktree); - strbuf_addch(cwd, '/'); - free(gitfile); - return cwd->buf + offset; + /* both the worktree and cwd are already normalized */ + if (!strcmp(cwd->buf, discovery->worktree)) { /* cwd == worktree */ + repo_discovery_set_gitdir(discovery, gitdirenv, 0); + goto out; + } + + offset = dir_inside_of(cwd->buf, discovery->worktree); + if (offset >= 0) { /* cwd inside discovery->worktree? */ + repo_discovery_set_gitdir(discovery, gitdirenv, 1); + if (chdir(discovery->worktree)) + die_errno(_("cannot chdir to '%s'"), discovery->worktree); + discovery->prefix = xstrfmt("%s/", cwd->buf + offset); + goto out; } /* cwd outside worktree */ - set_git_dir(repo, gitdirenv, 0); + repo_discovery_set_gitdir(discovery, gitdirenv, 0); + +out: free(gitfile); - return NULL; } -static const char *setup_discovered_git_dir(struct repository *repo, - const char *gitdir, - struct strbuf *cwd, int offset, - struct repository_format *repo_fmt, - int *nongit_ok) +static void repo_discover_implicit_gitdir(struct repo_discovery *discovery, + const char *gitdir, + struct strbuf *cwd, int offset, + int *nongit_ok) { - if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok)) - return NULL; + if (read_and_verify_repository_format(&discovery->format, gitdir, nongit_ok)) + return; /* --work-tree is set without --git-dir; use discovered one */ - if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { + if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) { char *to_free = NULL; - const char *ret; if (offset != cwd->len && !is_absolute_path(gitdir)) gitdir = to_free = real_pathdup(gitdir, 1); if (chdir(cwd->buf)) die_errno(_("cannot come back to cwd")); - ret = setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok); + repo_discover_explicit_gitdir(discovery, gitdir, cwd, + nongit_ok); free(to_free); - return ret; + return; } /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */ - if (is_bare_repository_cfg > 0) { - set_git_dir(repo, gitdir, (offset != cwd->len)); + if (discovery->format.is_bare > 0) { + repo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len)); if (chdir(cwd->buf)) die_errno(_("cannot come back to cwd")); - return NULL; + return; } /* #0, #1, #5, #8, #9, #12, #13 */ - set_git_work_tree(repo, "."); + repo_discovery_set_worktree(discovery, "."); if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT)) - set_git_dir(repo, gitdir, 0); + repo_discovery_set_gitdir(discovery, gitdir, 0); if (offset >= cwd->len) - return NULL; + return; /* Make "offset" point past the '/' (already the case for root dirs) */ if (offset != offset_1st_component(cwd->buf)) offset++; - /* Add a '/' at the end */ - strbuf_addch(cwd, '/'); - return cwd->buf + offset; + discovery->prefix = xstrfmt("%s/", cwd->buf + offset); } /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */ -static const char *setup_bare_git_dir(struct repository *repo, +static void repo_discover_bare_gitdir(struct repo_discovery *discovery, struct strbuf *cwd, int offset, - struct repository_format *repo_fmt, int *nongit_ok) { int root_len; - if (check_repository_format_gently(".", repo_fmt, nongit_ok)) - return NULL; + if (read_and_verify_repository_format(&discovery->format, ".", nongit_ok)) + return; setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1); /* --work-tree is set without --git-dir; use discovered one */ - if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { - static const char *gitdir; - - gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset); + if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) { + char *gitdir = offset == cwd->len ? xstrdup(".") : xmemdupz(cwd->buf, offset); if (chdir(cwd->buf)) die_errno(_("cannot come back to cwd")); - return setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok); + repo_discover_explicit_gitdir(discovery, gitdir, cwd, nongit_ok); + free(gitdir); + return; } if (offset != cwd->len) { @@ -1278,11 +1289,10 @@ static const char *setup_bare_git_dir(struct repository *repo, die_errno(_("cannot come back to cwd")); root_len = offset_1st_component(cwd->buf); strbuf_setlen(cwd, offset > root_len ? offset : root_len); - set_git_dir(repo, cwd->buf, 0); + repo_discovery_set_gitdir(discovery, cwd->buf, 0); + } else { + repo_discovery_set_gitdir(discovery, ".", 0); } - else - set_git_dir(repo, ".", 0); - return NULL; } static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len) @@ -1540,10 +1550,10 @@ static int is_implicit_bare_repo(const char *path) * the discovered .git/ directory, if any. If `gitdir` is not absolute, it * is relative to `dir` (i.e. *not* necessarily the cwd). */ -static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, - struct strbuf *gitdir, - struct strbuf *report, - int die_on_error) +static enum discovery_result repo_discovery_find_dir(struct strbuf *dir, + struct strbuf *gitdir, + struct strbuf *report, + int die_on_error) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; @@ -1629,7 +1639,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, return GIT_DIR_INVALID_GITFILE; default: if (die_on_error) - read_gitfile_error_die(error_code, dir->buf, NULL); + read_gitfile_error_die(error_code, dir->buf); else return GIT_DIR_INVALID_GITFILE; } @@ -1710,7 +1720,7 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir, return GIT_DIR_CWD_FAILURE; cwd_len = dir.len; - result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0); + result = repo_discovery_find_dir(&dir, gitdir, NULL, 0); if (result <= 0) { strbuf_release(&dir); return result; @@ -1760,11 +1770,23 @@ int apply_repository_format(struct repository *repo, if (verify_repository_format(format, err) < 0) return -1; - if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) { - object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT)); - alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT)); + if (format->is_bare > 0 && format->work_tree) { + /* #22.2, #30 */ + warning("core.bare and core.worktree do not make sense"); + repo->worktree_config_is_bogus = true; } + if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) { + const char *shallow_file; + + object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT)); + alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT)); + shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); + if (shallow_file) + set_alternate_shallow_file(repo, shallow_file); + } + + repo->bare_cfg = format->is_bare; repo_set_hash_algo(repo, format->hash_algo); repo->objects = odb_new(repo, object_directory, alternate_object_directories); @@ -1788,32 +1810,6 @@ int apply_repository_format(struct repository *repo, return 0; } -/* - * Check the repository format version in the path found in repo_get_git_dir(repo), - * and die if it is a version we don't understand. Generally one would - * set_git_dir() before calling this, and use it only for "are we in a valid - * repo?". - * - * If successful and fmt is not NULL, fill fmt with data. - */ -static void check_and_apply_repository_format(struct repository *repo, - struct repository_format *fmt, - enum apply_repository_format_flags flags) -{ - struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; - struct strbuf err = STRBUF_INIT; - - if (!fmt) - fmt = &repo_fmt; - - check_repository_format_gently(repo_get_git_dir(repo), fmt, NULL); - if (apply_repository_format(repo, fmt, flags, &err) < 0) - die("%s", err.buf); - startup_info->have_repository = 1; - - clear_repository_format(&repo_fmt); -} - const char *enter_repo(struct repository *repo, const char *path, unsigned flags) { static struct strbuf validated_path = STRBUF_INIT; @@ -1887,9 +1883,17 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags } if (is_git_directory(".")) { - set_git_dir(repo, ".", 0); - check_and_apply_repository_format(repo, NULL, - APPLY_REPOSITORY_FORMAT_HONOR_ENV); + struct repository_format fmt = REPOSITORY_FORMAT_INIT; + struct strbuf err = STRBUF_INIT; + + apply_and_export_relative_gitdir(repo, ".", 0); + read_and_verify_repository_format(&fmt, ".", NULL); + if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) + die("%s", err.buf); + startup_info->have_repository = 1; + + clear_repository_format(&fmt); + strbuf_release(&err); return path; } @@ -1901,7 +1905,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags * primarily to support git-clone to work in a new repository it just * created, and is not meant to flip between different work trees. */ -void set_git_work_tree(struct repository *repo, const char *new_work_tree) +static void set_git_work_tree(struct repository *repo, const char *new_work_tree) { if (repo->worktree_initialized) { struct strbuf realpath = STRBUF_INIT; @@ -1919,22 +1923,10 @@ void set_git_work_tree(struct repository *repo, const char *new_work_tree) repo_set_worktree(repo, new_work_tree); } -const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) +static void repo_discover(struct repo_discovery *discovery, int *nongit_ok) { - static struct strbuf cwd = STRBUF_INIT; + struct strbuf cwd = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT; - const char *prefix = NULL; - const char *ref_backend_uri; - struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; - - /* - * We may have read an incomplete configuration before - * setting-up the git directory. If so, clear the cache so - * that the next queries to the configuration reload complete - * configuration (including the per-repo config file that we - * ignored previously). - */ - repo_config_clear(repo); /* * Let's assume that we are in a git repository. @@ -1948,20 +1940,21 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) die_errno(_("Unable to read current working directory")); strbuf_addbuf(&dir, &cwd); - switch (setup_git_directory_gently_1(&dir, &gitdir, &report, 1)) { + switch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) { case GIT_DIR_EXPLICIT: - prefix = setup_explicit_git_dir(repo, gitdir.buf, &cwd, &repo_fmt, nongit_ok); + repo_discover_explicit_gitdir(discovery, gitdir.buf, &cwd, + nongit_ok); break; case GIT_DIR_DISCOVERED: if (dir.len < cwd.len && chdir(dir.buf)) die(_("cannot change to '%s'"), dir.buf); - prefix = setup_discovered_git_dir(repo, gitdir.buf, &cwd, dir.len, - &repo_fmt, nongit_ok); + repo_discover_implicit_gitdir(discovery, gitdir.buf, &cwd, dir.len, + nongit_ok); break; case GIT_DIR_BARE: if (dir.len < cwd.len && chdir(dir.buf)) die(_("cannot change to '%s'"), dir.buf); - prefix = setup_bare_git_dir(repo, &cwd, dir.len, &repo_fmt, nongit_ok); + repo_discover_bare_gitdir(discovery, &cwd, dir.len, nongit_ok); break; case GIT_DIR_HIT_CEILING: if (!nongit_ok) @@ -2002,22 +1995,43 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) case GIT_DIR_CWD_FAILURE: case GIT_DIR_INVALID_FORMAT: /* - * As a safeguard against setup_git_directory_gently_1 returning + * As a safeguard against repo_discovery_find_dir returning * these values, fallthrough to BUG. Otherwise it is possible to * set startup_info->have_repository to 1 when we did nothing to * find a repository. */ default: - BUG("unhandled setup_git_directory_gently_1() result"); + BUG("unhandled repo_discovery_find_dir() result"); } + strbuf_release(&dir); + strbuf_release(&cwd); + strbuf_release(&gitdir); + strbuf_release(&report); +} + +const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) +{ + struct repo_discovery discovery = REPO_DISCOVERY_INIT; + + /* + * We may have read an incomplete configuration before + * setting-up the git directory. If so, clear the cache so + * that the next queries to the configuration reload complete + * configuration (including the per-repo config file that we + * ignored previously). + */ + repo_config_clear(repo); + + repo_discover(&discovery, nongit_ok); + /* * At this point, nongit_ok is stable. If it is non-NULL and points * to a non-zero value, then this means that we haven't found a * repository and that the caller expects startup_info to reflect * this. * - * Regardless of the state of nongit_ok, startup_info->prefix and + * Regardless of the state of nongit_ok, the_repository->prefix and * the GIT_PREFIX environment variable must always match. For details * see Documentation/config/alias.adoc. */ @@ -2027,10 +2041,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) startup_info->have_repository = 1; /* - * Not all paths through the setup code will call 'set_git_dir()' (which - * directly sets up the environment) so in order to guarantee that the - * environment is in a consistent state after setup, explicitly setup - * the environment if we have a repository. + * Not all paths through the setup code will have recorded a gitdir + * above, so in order to guarantee that the environment is in a + * consistent state after setup, explicitly set up the gitdir and + * environment if we have a repository. * * NEEDSWORK: currently we allow bogus GIT_DIR values to be set in some * code paths so we also need to explicitly setup the environment if @@ -2041,21 +2055,45 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) startup_info->have_repository || /* GIT_DIR_EXPLICIT */ getenv(GIT_DIR_ENVIRONMENT)) { - if (!repo->gitdir) { + if (discovery.worktree) + set_git_work_tree(repo, discovery.worktree); + + if (discovery.gitdir) { + apply_and_export_relative_gitdir(repo, discovery.gitdir, 0); + } else { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); if (!gitdir) gitdir = DEFAULT_GIT_DIR_ENVIRONMENT; - setup_git_env_internal(repo, gitdir); + apply_gitdir_and_environment(repo, gitdir); } if (startup_info->have_repository) { struct strbuf err = STRBUF_INIT; + const char *ref_backend_uri; - if (apply_repository_format(repo, &repo_fmt, + /* + * The env variable should override the repository config + * for 'extensions.refStorage'. + */ + ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); + if (ref_backend_uri) { + char *format; + + free(discovery.format.ref_storage_payload); + + parse_reference_uri(ref_backend_uri, &format, &discovery.format.ref_storage_payload); + discovery.format.ref_storage_format = ref_storage_format_by_name(format); + if (discovery.format.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format: '%s'"), format); + + free(format); + } + + if (apply_repository_format(repo, &discovery.format, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) die("%s", err.buf); - clear_repository_format(&repo_fmt); + clear_repository_format(&discovery.format); strbuf_release(&err); } } @@ -2066,42 +2104,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok) * out where the repository is, i.e. a preparation * for calling repo_config_get_bool(). */ - if (prefix) { - prefix = precompose_string_if_needed(prefix); - startup_info->prefix = prefix; - setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1); + if (discovery.prefix) { + const char *prefix = precompose_string_if_needed(discovery.prefix); + repo->prefix = xstrdup(prefix); + setenv(GIT_PREFIX_ENVIRONMENT, repo->prefix, 1); } else { - startup_info->prefix = NULL; + FREE_AND_NULL(repo->prefix); setenv(GIT_PREFIX_ENVIRONMENT, "", 1); } - /* - * The env variable should override the repository config - * for 'extensions.refStorage'. - */ - ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); - if (ref_backend_uri) { - char *backend, *payload; - enum ref_storage_format format; - - parse_reference_uri(ref_backend_uri, &backend, &payload); - format = ref_storage_format_by_name(backend); - if (format == REF_STORAGE_FORMAT_UNKNOWN) - die(_("unknown ref storage format: '%s'"), backend); - repo_set_ref_storage_format(repo, format, payload); - - free(backend); - free(payload); - } - setup_original_cwd(repo); - strbuf_release(&dir); - strbuf_release(&gitdir); - strbuf_release(&report); - clear_repository_format(&repo_fmt); - - return prefix; + repo_discovery_release(&discovery); + return repo->prefix; } int git_config_perm(const char *var, const char *value) @@ -2574,7 +2589,7 @@ static int create_default_files(struct repository *repo, repo_settings_set_shared_repository(repo, init_shared_repository); - is_bare_repository_cfg = !work_tree; + repo->bare_cfg = !work_tree; /* * We would have created the above under user's umask -- under @@ -2600,13 +2615,17 @@ static int create_default_files(struct repository *repo, } repo_config_set(repo, "core.filemode", filemode ? "true" : "false"); - if (is_bare_repository()) + if (is_bare_repository(repo)) repo_config_set(repo, "core.bare", "true"); else { + const char *value; + repo_config_set(repo, "core.bare", "false"); + /* allow template config file to override the default */ - if (repo_settings_get_log_all_ref_updates(repo) == LOG_REFS_UNSET) + if (repo_config_get_string_tmp(repo, "core.logallrefupdates", &value)) repo_config_set(repo, "core.logallrefupdates", "true"); + if (needs_work_tree_config(original_git_dir, work_tree)) repo_config_set(repo, "core.worktree", work_tree); } @@ -2728,8 +2747,7 @@ static int read_default_format_config(const char *key, const char *value, return ret; } -static void repository_format_configure(struct repository *repo, - struct repository_format *repo_fmt, +static void repository_format_configure(struct repository_format *repo_fmt, int hash, enum ref_storage_format ref_format) { struct default_format_config cfg = { @@ -2766,7 +2784,6 @@ static void repository_format_configure(struct repository *repo, } else if (cfg.hash != GIT_HASH_UNKNOWN) { repo_fmt->hash_algo = cfg.hash; } - repo_set_hash_algo(repo, repo_fmt->hash_algo); env = getenv("GIT_DEFAULT_REF_FORMAT"); if (repo_fmt->version >= 0 && @@ -2804,13 +2821,12 @@ static void repository_format_configure(struct repository *repo, free(backend); } - - repo_set_ref_storage_format(repo, repo_fmt->ref_storage_format, - repo_fmt->ref_storage_payload); } int init_db(struct repository *repo, - const char *git_dir, const char *real_git_dir, + const char *git_dir, + const char *real_git_dir, + const char *worktree, const char *template_dir, int hash, enum ref_storage_format ref_storage_format, const char *initial_branch, @@ -2820,6 +2836,7 @@ int init_db(struct repository *repo, int exist_ok = flags & INIT_DB_EXIST_OK; char *original_git_dir = real_pathdup(git_dir, 1); struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; + struct strbuf err = STRBUF_INIT; if (real_git_dir) { struct stat st; @@ -2830,15 +2847,16 @@ int init_db(struct repository *repo, if (!exist_ok && !stat(real_git_dir, &st)) die(_("%s already exists"), real_git_dir); - set_git_dir(repo, real_git_dir, 1); + apply_and_export_relative_gitdir(repo, real_git_dir, 1); git_dir = repo_get_git_dir(repo); separate_git_dir(git_dir, original_git_dir); - } - else { - set_git_dir(repo, git_dir, 1); + } else { + apply_and_export_relative_gitdir(repo, git_dir, 1); git_dir = repo_get_git_dir(repo); } - startup_info->have_repository = 1; + + if (worktree) + set_git_work_tree(repo, worktree); /* * Check to see if the repository version is right. @@ -2846,10 +2864,11 @@ int init_db(struct repository *repo, * config file, so this will not fail. What we are catching * is an attempt to reinitialize new repository with an old tool. */ - check_and_apply_repository_format(repo, &repo_fmt, - APPLY_REPOSITORY_FORMAT_HONOR_ENV); - - repository_format_configure(repo, &repo_fmt, hash, ref_storage_format); + read_and_verify_repository_format(&repo_fmt, repo_get_git_dir(repo), NULL); + repository_format_configure(&repo_fmt, hash, ref_storage_format); + if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0) + die("%s", err.buf); + startup_info->have_repository = 1; /* * Ensure `core.hidedotfiles` is processed. This must happen after we @@ -2904,6 +2923,7 @@ int init_db(struct repository *repo, } clear_repository_format(&repo_fmt); + strbuf_release(&err); free(original_git_dir); return 0; }
diff --git a/setup.h b/setup.h index 705d1d6..654f10e 100644 --- a/setup.h +++ b/setup.h
@@ -38,7 +38,7 @@ int is_nonbare_repository_dir(struct strbuf *path); #define READ_GITFILE_ERR_TOO_LARGE 8 #define READ_GITFILE_ERR_MISSING 9 #define READ_GITFILE_ERR_IS_A_DIR 10 -void read_gitfile_error_die(int error_code, const char *path, const char *dir); +void read_gitfile_error_die(int error_code, const char *path); const char *read_gitfile_gently(const char *path, int *return_error_code); #define read_gitfile(path) read_gitfile_gently((path), NULL) const char *resolve_gitdir_gently(const char *suspect, int *return_error_code); @@ -96,8 +96,6 @@ static inline int discover_git_directory(struct strbuf *commondir, return 0; } -void set_git_work_tree(struct repository *repo, const char *tree); - /* Flags that can be passed to `enter_repo()`. */ enum { /* @@ -263,7 +261,9 @@ const char *get_template_dir(const char *option_template); #define INIT_DB_SKIP_REFDB (1 << 2) int init_db(struct repository *repo, - const char *git_dir, const char *real_git_dir, + const char *git_dir, + const char *real_git_dir, + const char *worktree, const char *template_dir, int hash_algo, enum ref_storage_format ref_storage_format, const char *initial_branch, int init_shared_repository, @@ -292,8 +292,13 @@ enum sharedrepo { int git_config_perm(const char *var, const char *value); struct startup_info { + /* + * Whether the user is asking us to treat the repository as bare via + * `git --bare`, even if it's not. + */ + bool force_bare_repository; + int have_repository; - const char *prefix; const char *original_cwd; }; extern struct startup_info *startup_info;
diff --git a/sha1/openssl.h b/sha1/openssl.h index 1038af4..48deeb7 100644 --- a/sha1/openssl.h +++ b/sha1/openssl.h
@@ -40,12 +40,18 @@ static inline void openssl_SHA1_Clone(struct openssl_SHA1_CTX *dst, EVP_MD_CTX_copy_ex(dst->ectx, src->ectx); } +static inline void openssl_SHA1_Discard(struct openssl_SHA1_CTX *ctx) +{ + EVP_MD_CTX_free(ctx->ectx); +} + #ifndef platform_SHA_CTX #define platform_SHA_CTX openssl_SHA1_CTX #define platform_SHA1_Init openssl_SHA1_Init #define platform_SHA1_Clone openssl_SHA1_Clone #define platform_SHA1_Update openssl_SHA1_Update #define platform_SHA1_Final openssl_SHA1_Final +#define platform_SHA1_Discard openssl_SHA1_Discard #endif #endif /* SHA1_OPENSSL_H */
diff --git a/sha1dc_git.c b/sha1dc_git.c index 9b675a0..fe58d79 100644 --- a/sha1dc_git.c +++ b/sha1dc_git.c
@@ -27,10 +27,9 @@ void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx) /* * Same as SHA1DCUpdate, but adjust types to match git's usual interface. */ -void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len) +void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, size_t len) { const char *data = vdata; - /* We expect an unsigned long, but sha1dc only takes an int */ while (len > INT_MAX) { SHA1DCUpdate(ctx, data, INT_MAX); data += INT_MAX;
diff --git a/sha1dc_git.h b/sha1dc_git.h index f6f880c..0bcf1aa 100644 --- a/sha1dc_git.h +++ b/sha1dc_git.h
@@ -15,7 +15,7 @@ void git_SHA1DCInit(SHA1_CTX *); #endif void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *); -void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len); +void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, size_t len); #define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */
diff --git a/sha256/gcrypt.h b/sha256/gcrypt.h index 17a90f1..d91ffe7 100644 --- a/sha256/gcrypt.h +++ b/sha256/gcrypt.h
@@ -27,13 +27,20 @@ static inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX static inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src) { + gcry_md_close(*dst); gcry_md_copy(dst, *src); } +static inline void gcrypt_SHA256_Discard(gcrypt_SHA256_CTX *ctx) +{ + gcry_md_close(*ctx); +} + #define platform_SHA256_CTX gcrypt_SHA256_CTX #define platform_SHA256_Init gcrypt_SHA256_Init #define platform_SHA256_Clone gcrypt_SHA256_Clone #define platform_SHA256_Update gcrypt_SHA256_Update #define platform_SHA256_Final gcrypt_SHA256_Final +#define platform_SHA256_Discard gcrypt_SHA256_Discard #endif
diff --git a/sha256/openssl.h b/sha256/openssl.h index c1083d9..3d457ca 100644 --- a/sha256/openssl.h +++ b/sha256/openssl.h
@@ -40,10 +40,16 @@ static inline void openssl_SHA256_Clone(struct openssl_SHA256_CTX *dst, EVP_MD_CTX_copy_ex(dst->ectx, src->ectx); } +static inline void openssl_SHA256_Discard(struct openssl_SHA256_CTX *ctx) +{ + EVP_MD_CTX_free(ctx->ectx); +} + #define platform_SHA256_CTX openssl_SHA256_CTX #define platform_SHA256_Init openssl_SHA256_Init #define platform_SHA256_Clone openssl_SHA256_Clone #define platform_SHA256_Update openssl_SHA256_Update #define platform_SHA256_Final openssl_SHA256_Final +#define platform_SHA256_Discard openssl_SHA256_Discard #endif /* SHA256_OPENSSL_H */
diff --git a/shallow.c b/shallow.c index 07cae44..8e244a5 100644 --- a/shallow.c +++ b/shallow.c
@@ -21,12 +21,10 @@ #include "statinfo.h" #include "trace.h" -void set_alternate_shallow_file(struct repository *r, const char *path, int override) +void set_alternate_shallow_file(struct repository *r, const char *path) { if (r->parsed_objects->is_shallow != -1) BUG("is_repository_shallow must not be called before set_alternate_shallow_file"); - if (r->parsed_objects->alternate_shallow_file && !override) - return; free(r->parsed_objects->alternate_shallow_file); r->parsed_objects->alternate_shallow_file = xstrdup_or_null(path); } @@ -359,7 +357,9 @@ struct write_shallow_data { static int write_one_shallow(const struct commit_graft *graft, void *cb_data) { struct write_shallow_data *data = cb_data; - const char *hex = oid_to_hex(&graft->oid); + char hex[GIT_MAX_HEXSZ + 1]; + + oid_to_hex_r(hex, &graft->oid); if (graft->nr_parent != -1) return 0; if (data->flags & QUICK) { @@ -370,8 +370,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data) struct commit *c = lookup_commit(the_repository, &graft->oid); if (!c || !(c->object.flags & SEEN)) { if (data->flags & VERBOSE) - printf("Removing %s from .git/shallow\n", - oid_to_hex(&c->object.oid)); + printf("Removing %s from .git/shallow\n", hex); return 0; } }
diff --git a/shallow.h b/shallow.h index e20ca4c..6a64db4 100644 --- a/shallow.h +++ b/shallow.h
@@ -10,7 +10,7 @@ struct oid_array; struct strvec; -void set_alternate_shallow_file(struct repository *r, const char *path, int override); +void set_alternate_shallow_file(struct repository *r, const char *path); int register_shallow(struct repository *r, const struct object_id *oid); int unregister_shallow(const struct object_id *oid); int is_repository_shallow(struct repository *r);
diff --git a/sideband.c b/sideband.c index 1523a53..4a2c5f8 100644 --- a/sideband.c +++ b/sideband.c
@@ -163,6 +163,10 @@ static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n) * * ESC [ [<n> [; <n>]*] m * + * where <n> can be either zero-length, or a decimal number, or a + * series of decimal numbers separated by a colon (for 256-color or + * true-color codes). + * * These are part of the Select Graphic Rendition sequences which * contain more than just color sequences, for more details see * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR. @@ -210,7 +214,7 @@ static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n) strbuf_add(dest, src, i + 1); return i; } - if (!isdigit(src[i]) && src[i] != ';') + if (!isdigit(src[i]) && src[i] != ':' && src[i] != ';') break; }
diff --git a/submodule.c b/submodule.c index fd91201..b30bb96 100644 --- a/submodule.c +++ b/submodule.c
@@ -2390,7 +2390,7 @@ static int validate_submodule_encoded_git_dir(char *git_dir, const char *submodu /* Prevent conflicts on case-folding filesystems */ repo_config_get_bool(the_repository, "core.ignorecase", &config_ignorecase); - if (ignore_case || config_ignorecase) { + if (repo_ignore_case(the_repository) || config_ignorecase) { bool suffixes_match = !strcmp(last_submodule_name, submodule_name); return check_casefolding_conflict(git_dir, submodule_name, suffixes_match); @@ -2579,7 +2579,7 @@ void absorb_git_dir_into_superproject(const char *path, if (err_code != READ_GITFILE_ERR_NOT_A_REPO) /* We don't know what broke here. */ - read_gitfile_error_die(err_code, path, NULL); + read_gitfile_error_die(err_code, path); /* * Maybe populated, but no git directory was found? @@ -2627,13 +2627,12 @@ int get_superproject_working_tree(struct strbuf *buf) * We might have a superproject, but it is harder * to determine. */ - return 0; + goto out; if (!strbuf_realpath(&one_up, "../", 0)) - return 0; + goto out; subpath = relative_path(cwd, one_up.buf, &sb); - strbuf_release(&one_up); prepare_submodule_repo_env(&cp.env); strvec_pop(&cp.env); @@ -2678,20 +2677,22 @@ int get_superproject_working_tree(struct strbuf *buf) ret = 1; free(super_wt); } - free(cwd); - strbuf_release(&sb); code = finish_command(&cp); if (code == 128) /* '../' is not a git repository */ - return 0; - if (code == 0 && len == 0) + ret = 0; + else if (code == 0 && len == 0) /* There is an unrelated git repository at '../' */ - return 0; - if (code) + ret = 0; + else if (code) die(_("ls-tree returned unexpected return code %d"), code); +out: + strbuf_release(&sb); + strbuf_release(&one_up); + free(cwd); return ret; }
diff --git a/t/.gitattributes b/t/.gitattributes index 7664c6e..e867f38 100644 --- a/t/.gitattributes +++ b/t/.gitattributes
@@ -1,5 +1,7 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /chainlint/*.expect eol=lf -whitespace +/greplint/*.expect eol=lf -whitespace +/greplint/*.test eol=lf -whitespace /t0110/url-* binary /t3206/* eol=lf /t3900/*.txt eol=lf
diff --git a/t/Makefile b/t/Makefile index ab8a5b5..e324987 100644 --- a/t/Makefile +++ b/t/Makefile
@@ -27,9 +27,11 @@ ifdef TEST_OUTPUT_DIRECTORY TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results CHAINLINTTMP = $(TEST_OUTPUT_DIRECTORY)/chainlinttmp +GREPLINTTMP = $(TEST_OUTPUT_DIRECTORY)/greplinttmp else TEST_RESULTS_DIRECTORY = test-results CHAINLINTTMP = chainlinttmp +GREPLINTTMP = greplinttmp endif # Shell quote; @@ -38,6 +40,7 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY)) CHAINLINTTMP_SQ = $(subst ','\'',$(CHAINLINTTMP)) +GREPLINTTMP_SQ = $(subst ','\'',$(GREPLINTTMP)) T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh))) @@ -45,6 +48,7 @@ TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh)) TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh)) CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test))) +GREPLINTTESTS = $(sort $(patsubst greplint/%.test,%,$(wildcard greplint/*.test))) CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c) UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES)) @@ -63,8 +67,8 @@ $(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup ifneq ($(PERL_PATH),) -test: check-chainlint -prove: check-chainlint +test: check-chainlint check-greplint +prove: check-chainlint check-greplint endif failed: @@ -102,7 +106,7 @@ pre-clean: $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)' -clean-except-prove-cache: clean-chainlint +clean-except-prove-cache: clean-chainlint clean-greplint $(RM) -r 'trash directory'.* $(RM) -r valgrind/bin @@ -120,6 +124,17 @@ { $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests >'$(CHAINLINTTMP_SQ)'/actual || true; } && \ diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual +clean-greplint: + $(RM) -r '$(GREPLINTTMP_SQ)' + +check-greplint: + @mkdir -p '$(GREPLINTTMP_SQ)' && \ + '$(PERL_PATH_SQ)' greplint-cat.pl '$(GREPLINTTMP_SQ)' $(GREPLINTTESTS) && \ + { '$(PERL_PATH_SQ)' greplint.pl \ + $(patsubst %,greplint/%.test,$(GREPLINTTESTS)) \ + >'$(GREPLINTTMP_SQ)'/actual 2>&1 || true; } && \ + diff -u '$(GREPLINTTMP_SQ)'/expect '$(GREPLINTTMP_SQ)'/actual + check-meson: @# awk acts up when trying to match single quotes, so we use \047 instead. @mkdir -p mesontmp && \ @@ -139,7 +154,7 @@ test-lint: test-lint-duplicates test-lint-executable \ test-lint-filenames ifneq ($(PERL_PATH),) -test-lint: test-lint-shell-syntax +test-lint: test-lint-shell-syntax test-greplint else GIT_TEST_CHAIN_LINT = 0 endif @@ -160,6 +175,9 @@ test-lint-shell-syntax: @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS) $(TPERF) +test-greplint: + @'$(PERL_PATH_SQ)' greplint.pl $(T) $(THELPERS) $(TPERF) + test-lint-filenames: @# We do *not* pass a glob to ls-files but use grep instead, to catch @# non-ASCII characters (which are quoted within double-quotes) @@ -185,7 +203,8 @@ $(MAKE) -C perf/ all .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \ - check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS) + check-chainlint clean-chainlint test-chainlint \ + check-greplint clean-greplint test-greplint $(UNIT_TESTS) .PHONY: libgit-sys-test libgit-rs-test libgit-sys-test:
diff --git a/t/README b/t/README index 085921b..4252774 100644 --- a/t/README +++ b/t/README
@@ -1039,6 +1039,40 @@ Check whether a file has the length it is expected to. + - test_grep [!] [<grep-options>] <pattern> <file> + + Check whether <file> contains a line matching <pattern>, or + with '!' that no line matches. Use this instead of bare + 'grep <pattern> <file>' in test assertions. On failure, + test_grep prints the contents of <file> for easier debugging, + whereas a bare 'grep' would fail silently. + + For negation, pass '!' as the first argument: + + test_grep ! "^diff --git" actual + + Do not negate by writing '! test_grep', as that suppresses the + diagnostic output. + + test_grep should only be used as a test assertion. When grep + is used as a data filter (e.g. 'grep -v "^index" actual >filtered') + or inside a command substitution (e.g. '$(grep -c ...)'), plain + 'grep' is the right choice because the exit code is not the + assertion itself. + + test_grep requires <file> to exist and will BUG otherwise, so + use it only where the file is guaranteed to exist at that point. + When a file's presence is conditional (a backend-specific file, + or a path that only exists on some platforms, such as an NTFS + 8.3 short name), guard the assertion on that condition (a + prerequisite, or a 'test -e' on the path) and use test_grep + inside the guard: + + if test_have_prereq REFFILES + then + test_grep ! "$refname" .git/packed-refs + fi + - test_path_is_file <path> test_path_is_dir <path> test_path_is_missing <path>
diff --git a/t/chainlint.pl b/t/chainlint.pl index 2d07a99..cededf1 100755 --- a/t/chainlint.pl +++ b/t/chainlint.pl
@@ -23,458 +23,9 @@ my $show_stats; my $emit_all; -# Lexer tokenizes POSIX shell scripts. It is roughly modeled after section 2.3 -# "Token Recognition" of POSIX chapter 2 "Shell Command Language". Although -# similar to lexical analyzers for other languages, this one differs in a few -# substantial ways due to quirks of the shell command language. -# -# For instance, in many languages, newline is just whitespace like space or -# TAB, but in shell a newline is a command separator, thus a distinct lexical -# token. A newline is significant and returned as a distinct token even at the -# end of a shell comment. -# -# In other languages, `1+2` would typically be scanned as three tokens -# (`1`, `+`, and `2`), but in shell it is a single token. However, the similar -# `1 + 2`, which embeds whitespace, is scanned as three token in shell, as well. -# In shell, several characters with special meaning lose that meaning when not -# surrounded by whitespace. For instance, the negation operator `!` is special -# when standing alone surrounded by whitespace; whereas in `foo!uucp` it is -# just a plain character in the longer token "foo!uucp". In many other -# languages, `"string"/foo:'string'` might be scanned as five tokens ("string", -# `/`, `foo`, `:`, and 'string'), but in shell, it is just a single token. -# -# The lexical analyzer for the shell command language is also somewhat unusual -# in that it recursively invokes the parser to handle the body of `$(...)` -# expressions which can contain arbitrary shell code. Such expressions may be -# encountered both inside and outside of double-quoted strings. -# -# The lexical analyzer is responsible for consuming shell here-doc bodies which -# extend from the line following a `<<TAG` operator until a line consisting -# solely of `TAG`. Here-doc consumption begins when a newline is encountered. -# It is legal for multiple here-doc `<<TAG` operators to be present on a single -# line, in which case their bodies must be present one following the next, and -# are consumed in the (left-to-right) order the `<<TAG` operators appear on the -# line. A special complication is that the bodies of all here-docs must be -# consumed when the newline is encountered even if the parse context depth has -# changed. For instance, in `cat <<A && x=$(cat <<B &&\n`, bodies of here-docs -# "A" and "B" must be consumed even though "A" was introduced outside the -# recursive parse context in which "B" was introduced and in which the newline -# is encountered. -package Lexer; - -sub new { - my ($class, $parser, $s) = @_; - bless { - parser => $parser, - buff => $s, - lineno => 1, - heretags => [] - } => $class; -} - -sub scan_heredoc_tag { - my $self = shift @_; - ${$self->{buff}} =~ /\G(-?)/gc; - my $indented = $1; - my $token = $self->scan_token(); - return "<<$indented" unless $token; - my $tag = $token->[0]; - $tag =~ s/['"\\]//g; - $$token[0] = $indented ? "\t$tag" : "$tag"; - push(@{$self->{heretags}}, $token); - return "<<$indented$tag"; -} - -sub scan_op { - my ($self, $c) = @_; - my $b = $self->{buff}; - return $c unless $$b =~ /\G(.)/sgc; - my $cc = $c . $1; - return scan_heredoc_tag($self) if $cc eq '<<'; - return $cc if $cc =~ /^(?:&&|\|\||>>|;;|<&|>&|<>|>\|)$/; - pos($$b)--; - return $c; -} - -sub scan_sqstring { - my $self = shift @_; - ${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc; - my $s = $1; - $self->{lineno} += () = $s =~ /\n/sg; - return "'" . $s; -} - -sub scan_dqstring { - my $self = shift @_; - my $b = $self->{buff}; - my $s = '"'; - while (1) { - # slurp up non-special characters - $s .= $1 if $$b =~ /\G([^"\$\\]+)/gc; - # handle special characters - last unless $$b =~ /\G(.)/sgc; - my $c = $1; - $s .= '"', last if $c eq '"'; - $s .= '$' . $self->scan_dollar(), next if $c eq '$'; - if ($c eq '\\') { - $s .= '\\', last unless $$b =~ /\G(.)/sgc; - $c = $1; - $self->{lineno}++, next if $c eq "\n"; # line splice - # backslash escapes only $, `, ", \ in dq-string - $s .= '\\' unless $c =~ /^[\$`"\\]$/; - $s .= $c; - next; - } - die("internal error scanning dq-string '$c'\n"); - } - $self->{lineno} += () = $s =~ /\n/sg; - return $s; -} - -sub scan_balanced { - my ($self, $c1, $c2) = @_; - my $b = $self->{buff}; - my $depth = 1; - my $s = $c1; - while ($$b =~ /\G([^\Q$c1$c2\E]*(?:[\Q$c1$c2\E]|\z))/gc) { - $s .= $1; - $depth++, next if $s =~ /\Q$c1\E$/; - $depth--; - last if $depth == 0; - } - $self->{lineno} += () = $s =~ /\n/sg; - return $s; -} - -sub scan_subst { - my $self = shift @_; - my @tokens = $self->{parser}->parse(qr/^\)$/); - $self->{parser}->next_token(); # closing ")" - return @tokens; -} - -sub scan_dollar { - my $self = shift @_; - my $b = $self->{buff}; - return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...)) - return '(' . join(' ', map {$_->[0]} $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...) - return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...} - return $1 if $$b =~ /\G(\w+)/gc; # $var - return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc. - return ''; -} - -sub swallow_heredocs { - my $self = shift @_; - my $b = $self->{buff}; - my $tags = $self->{heretags}; - while (my $tag = shift @$tags) { - my $start = pos($$b); - my $indent = $$tag[0] =~ s/^\t// ? '\\s*' : ''; - $$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc; - if (pos($$b) > $start) { - my $body = substr($$b, $start, pos($$b) - $start); - $self->{parser}->{heredocs}->{$$tag[0]} = { - content => substr($body, 0, length($body) - length($&)), - start_line => $self->{lineno}, - }; - $self->{lineno} += () = $body =~ /\n/sg; - next; - } - push(@{$self->{parser}->{problems}}, ['HEREDOC', $tag]); - $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input - my $body = substr($$b, $start, pos($$b) - $start); - $self->{lineno} += () = $body =~ /\n/sg; - last; - } -} - -sub scan_token { - my $self = shift @_; - my $b = $self->{buff}; - my $token = ''; - my ($start, $startln); -RESTART: - $startln = $self->{lineno}; - $$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline) - $start = pos($$b) || 0; - $self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment - while (1) { - # slurp up non-special characters - $token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc; - # handle special characters - last unless $$b =~ /\G(.)/sgc; - my $c = $1; - pos($$b)--, last if $c =~ /^[ \t]$/; # whitespace ends token - pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/; - $token .= $self->scan_sqstring(), next if $c eq "'"; - $token .= $self->scan_dqstring(), next if $c eq '"'; - $token .= $c . $self->scan_dollar(), next if $c eq '$'; - $self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n"; - $token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/; - $token = $c, last if $c =~ /^[(){}]$/; - if ($c eq '\\') { - $token .= '\\', last unless $$b =~ /\G(.)/sgc; - $c = $1; - $self->{lineno}++, next if $c eq "\n" && length($token); # line splice - $self->{lineno}++, goto RESTART if $c eq "\n"; # line splice - $token .= '\\' . $c; - next; - } - die("internal error scanning character '$c'\n"); - } - return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef; -} - -# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It -# is a recursive descent parser very roughly modeled after section 2.10 "Shell -# Grammar" of POSIX chapter 2 "Shell Command Language". -package ShellParser; - -sub new { - my ($class, $s) = @_; - my $self = bless { - buff => [], - stop => [], - output => [], - heredocs => {}, - insubshell => 0, - } => $class; - $self->{lexer} = Lexer->new($self, $s); - return $self; -} - -sub next_token { - my $self = shift @_; - return pop(@{$self->{buff}}) if @{$self->{buff}}; - return $self->{lexer}->scan_token(); -} - -sub untoken { - my $self = shift @_; - push(@{$self->{buff}}, @_); -} - -sub peek { - my $self = shift @_; - my $token = $self->next_token(); - return undef unless defined($token); - $self->untoken($token); - return $token; -} - -sub stop_at { - my ($self, $token) = @_; - return 1 unless defined($token); - my $stop = ${$self->{stop}}[-1] if @{$self->{stop}}; - return defined($stop) && $token->[0] =~ $stop; -} - -sub expect { - my ($self, $expect) = @_; - my $token = $self->next_token(); - return $token if defined($token) && $token->[0] eq $expect; - push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token->[0] : "<end-of-input>") . "'\n"); - $self->untoken($token) if defined($token); - return (); -} - -sub optional_newlines { - my $self = shift @_; - my @tokens; - while (my $token = $self->peek()) { - last unless $token->[0] eq "\n"; - push(@tokens, $self->next_token()); - } - return @tokens; -} - -sub parse_group { - my $self = shift @_; - return ($self->parse(qr/^}$/), - $self->expect('}')); -} - -sub parse_subshell { - my $self = shift @_; - $self->{insubshell}++; - my @tokens = ($self->parse(qr/^\)$/), - $self->expect(')')); - $self->{insubshell}--; - return @tokens; -} - -sub parse_case_pattern { - my $self = shift @_; - my @tokens; - while (defined(my $token = $self->next_token())) { - push(@tokens, $token); - last if $token->[0] eq ')'; - } - return @tokens; -} - -sub parse_case { - my $self = shift @_; - my @tokens; - push(@tokens, - $self->next_token(), # subject - $self->optional_newlines(), - $self->expect('in'), - $self->optional_newlines()); - while (1) { - my $token = $self->peek(); - last unless defined($token) && $token->[0] ne 'esac'; - push(@tokens, - $self->parse_case_pattern(), - $self->optional_newlines(), - $self->parse(qr/^(?:;;|esac)$/)); # item body - $token = $self->peek(); - last unless defined($token) && $token->[0] ne 'esac'; - push(@tokens, - $self->expect(';;'), - $self->optional_newlines()); - } - push(@tokens, $self->expect('esac')); - return @tokens; -} - -sub parse_for { - my $self = shift @_; - my @tokens; - push(@tokens, - $self->next_token(), # variable - $self->optional_newlines()); - my $token = $self->peek(); - if (defined($token) && $token->[0] eq 'in') { - push(@tokens, - $self->expect('in'), - $self->optional_newlines()); - } - push(@tokens, - $self->parse(qr/^do$/), # items - $self->expect('do'), - $self->optional_newlines(), - $self->parse_loop_body(), - $self->expect('done')); - return @tokens; -} - -sub parse_if { - my $self = shift @_; - my @tokens; - while (1) { - push(@tokens, - $self->parse(qr/^then$/), # if/elif condition - $self->expect('then'), - $self->optional_newlines(), - $self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body - my $token = $self->peek(); - last unless defined($token) && $token->[0] eq 'elif'; - push(@tokens, $self->expect('elif')); - } - my $token = $self->peek(); - if (defined($token) && $token->[0] eq 'else') { - push(@tokens, - $self->expect('else'), - $self->optional_newlines(), - $self->parse(qr/^fi$/)); # else body - } - push(@tokens, $self->expect('fi')); - return @tokens; -} - -sub parse_loop_body { - my $self = shift @_; - return $self->parse(qr/^done$/); -} - -sub parse_loop { - my $self = shift @_; - return ($self->parse(qr/^do$/), # condition - $self->expect('do'), - $self->optional_newlines(), - $self->parse_loop_body(), - $self->expect('done')); -} - -sub parse_func { - my $self = shift @_; - return ($self->expect('('), - $self->expect(')'), - $self->optional_newlines(), - $self->parse_cmd()); # body -} - -sub parse_bash_array_assignment { - my $self = shift @_; - my @tokens = $self->expect('('); - while (defined(my $token = $self->next_token())) { - push(@tokens, $token); - last if $token->[0] eq ')'; - } - return @tokens; -} - -my %compound = ( - '{' => \&parse_group, - '(' => \&parse_subshell, - 'case' => \&parse_case, - 'for' => \&parse_for, - 'if' => \&parse_if, - 'until' => \&parse_loop, - 'while' => \&parse_loop); - -sub parse_cmd { - my $self = shift @_; - my $cmd = $self->next_token(); - return () unless defined($cmd); - return $cmd if $cmd->[0] eq "\n"; - - my $token; - my @tokens = $cmd; - if ($cmd->[0] eq '!') { - push(@tokens, $self->parse_cmd()); - return @tokens; - } elsif (my $f = $compound{$cmd->[0]}) { - push(@tokens, $self->$f()); - } elsif (defined($token = $self->peek()) && $token->[0] eq '(') { - if ($cmd->[0] !~ /\w=$/) { - push(@tokens, $self->parse_func()); - return @tokens; - } - my @array = $self->parse_bash_array_assignment(); - $tokens[-1]->[0] .= join(' ', map {$_->[0]} @array); - $tokens[-1]->[2] = $array[$#array][2] if @array; - } - - while (defined(my $token = $self->next_token())) { - $self->untoken($token), last if $self->stop_at($token); - push(@tokens, $token); - last if $token->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/; - } - push(@tokens, $self->next_token()) if $tokens[-1]->[0] ne "\n" && defined($token = $self->peek()) && $token->[0] eq "\n"; - return @tokens; -} - -sub accumulate { - my ($self, $tokens, $cmd) = @_; - push(@$tokens, @$cmd); -} - -sub parse { - my ($self, $stop) = @_; - push(@{$self->{stop}}, $stop); - goto DONE if $self->stop_at($self->peek()); - my @tokens; - while (my @cmd = $self->parse_cmd()) { - $self->accumulate(\@tokens, \@cmd); - last if $self->stop_at($self->peek()); - } -DONE: - pop(@{$self->{stop}}); - return @tokens; -} +use File::Basename; +do(dirname($0) . "/lib-shell-parser.pl") + or die "$0: failed to load lib-shell-parser.pl: $@$!\n"; # TestParser is a subclass of ShellParser which, beyond parsing shell script # code, is also imbued with semantic knowledge of test construction, and checks @@ -482,9 +33,10 @@ # the tests themselves or in behaviors being exercised by the tests. As such, # TestParser is only called upon to parse test bodies, not the top-level # scripts in which the tests are defined. + package TestParser; -use base 'ShellParser'; +our @ISA = ('ShellParser'); sub new { my $class = shift @_; @@ -578,51 +130,10 @@ $self->SUPER::accumulate($tokens, $cmd); } -# ScriptParser is a subclass of ShellParser which identifies individual test -# definitions within test scripts, and passes each test body through TestParser -# to identify possible problems. ShellParser detects test definitions not only -# at the top-level of test scripts but also within compound commands such as -# loops and function definitions. -package ScriptParser; +# ChainlintParser extends ScriptParser with &&-chain checking +package ChainlintParser; -use base 'ShellParser'; - -sub new { - my $class = shift @_; - my $self = $class->SUPER::new(@_); - $self->{ntests} = 0; - $self->{nerrs} = 0; - return $self; -} - -# extract the raw content of a token, which may be a single string or a -# composition of multiple strings and non-string character runs; for instance, -# `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d` -sub unwrap { - my $token = (@_ ? shift @_ : $_)->[0]; - # simple case: 'sqstring' or "dqstring" - return $token if $token =~ s/^'([^']*)'$/$1/; - return $token if $token =~ s/^"([^"]*)"$/$1/; - - # composite case - my ($s, $q, $escaped); - while (1) { - # slurp up non-special characters - $s .= $1 if $token =~ /\G([^\\'"]*)/gc; - # handle special characters - last unless $token =~ /\G(.)/sgc; - my $c = $1; - $q = undef, next if defined($q) && $c eq $q; - $q = $c, next if !defined($q) && $c =~ /^['"]$/; - if ($c eq '\\') { - last unless $token =~ /\G(.)/sgc; - $c = $1; - $s .= '\\' if $c eq "\n"; # preserve line splice - } - $s .= $c; - } - return $s -} +our @ISA = ('ScriptParser'); sub format_problem { local $_ = shift; @@ -635,10 +146,10 @@ sub check_test { my $self = shift @_; - my $title = unwrap(shift @_); + my $title = ScriptParser::unwrap(shift @_); my $body = shift @_; my $lineno = $body->[3]; - $body = unwrap($body); + $body = ScriptParser::unwrap($body); if ($body eq '-') { my $herebody = shift @_; $body = $herebody->{content}; @@ -673,24 +184,8 @@ push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked"); } -sub parse_cmd { - my $self = shift @_; - my @tokens = $self->SUPER::parse_cmd(); - return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/; - my $n = $#tokens; - $n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/; - my $herebody; - if ($n >= 2 && $tokens[$n-1]->[0] eq '-' && $tokens[$n]->[0] =~ /^<<-?(.+)$/) { - $herebody = $self->{heredocs}->{$1}; - $n--; - } - $self->check_test($tokens[1], $tokens[2], $herebody) if $n == 2; # title body - $self->check_test($tokens[2], $tokens[3], $herebody) if $n > 2; # prereq title body - return @tokens; -} - # main contains high-level functionality for processing command-line switches, -# feeding input test scripts to ScriptParser, and reporting results. +# feeding input test scripts to ChainlintParser, and reporting results. package main; my $getnow = sub { return time(); }; @@ -803,7 +298,7 @@ } my $s = do { local $/; <$fh> }; close($fh); - my $parser = ScriptParser->new(\$s); + my $parser = ChainlintParser->new(\$s); 1 while $parser->parse_cmd(); if (@{$parser->{output}}) { my $c = fd_colors(1);
diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh index bd2d45c..b1a10dd 100644 --- a/t/for-each-ref-tests.sh +++ b/t/for-each-ref-tests.sh
@@ -522,8 +522,8 @@ ' test_expect_success 'Give help even with invalid sort atoms' ' - test_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 && - grep "^usage: ${git_for_each_ref}" actual + ${git_for_each_ref} --sort=bogus -h >actual 2>&1 && + test_grep "^usage: ${git_for_each_ref}" actual ' cat >expected <<\EOF @@ -622,7 +622,7 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do test_expect_success "more than one quoting style: $i" " test_must_fail ${git_for_each_ref} $i 2>err && - grep '^error: more than one quoting style' err + test_grep '^error: more than one quoting style' err " done @@ -1892,7 +1892,7 @@ >in && test_must_fail ${git_for_each_ref} --format="%(refname)" \ --stdin refs/heads/extra <in 2>err && - grep "unknown arguments supplied with --stdin" err + test_grep "unknown arguments supplied with --stdin" err ' test_expect_success "${git_for_each_ref} --stdin: matches" ' @@ -1955,11 +1955,11 @@ test_expect_success 'is-base atom with non-commits' ' ${git_for_each_ref} --format="%(is-base:HEAD) %(refname)" >out 2>err && - grep "(HEAD) refs/heads/main" out && + test_grep "(HEAD) refs/heads/main" out && test_line_count = 2 err && - grep "error: object .* is a commit, not a blob" err && - grep "error: bad tag pointer to" err + test_grep "error: object .* is a commit, not a blob" err && + test_grep "error: bad tag pointer to" err ' GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
diff --git a/t/greplint-cat.pl b/t/greplint-cat.pl new file mode 100644 index 0000000..fa9c3b8 --- /dev/null +++ b/t/greplint-cat.pl
@@ -0,0 +1,27 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +# Assemble expected output for check-greplint target. +# Usage: greplint-cat.pl <outdir> <test-name> ... +# +# For each <test-name>, reads greplint/<test-name>.expect and +# prepends "greplint/<test-name>.test:" to every non-empty line, +# matching the output format of greplint.pl. Writes combined +# expected output to <outdir>/expect. + +my $outdir = shift; +open(my $expect, '>', "$outdir/expect") + or die "unable to open $outdir/expect: $!"; + +for my $name (@ARGV) { + open(my $fh, '<', "greplint/$name.expect") + or die "unable to open greplint/$name.expect: $!"; + while (<$fh>) { + print $expect "greplint/$name.test:$_"; + } + close $fh; +} + +close $expect;
diff --git a/t/greplint.pl b/t/greplint.pl new file mode 100644 index 0000000..23efe27 --- /dev/null +++ b/t/greplint.pl
@@ -0,0 +1,258 @@ +#!/usr/bin/env perl + +# Detect bare 'grep' used as a test assertion where 'test_grep' +# should be used, and '! test_grep' where 'test_grep !' should +# be used. +# +# The shared shell parser tokenizes test bodies so that 'grep' +# inside heredocs, command substitutions like $(grep ...), and +# quoted strings is collapsed into a single token and never seen +# by our check. A line-oriented approach would need to track +# heredoc delimiters, nested $() depth, and cross-line pipe +# state to avoid false positives on patterns like: +# +# write_script foo.sh <<-\EOF +# grep pattern file # data, not an assertion +# EOF +# +# The Lexer already handles these. + +use warnings; +use strict; +use File::Basename; +do(dirname($0) . "/lib-shell-parser.pl") + or die "$0: failed to load lib-shell-parser.pl: $@$!\n"; + +my $exit_code = 0; + +# GrepLintParser inherits ScriptParser's ability to find +# test_expect_success/failure blocks and call check_test() +# on each body. We override check_test() to walk the token +# stream looking for bare grep assertions. +package GrepLintParser; + +our @ISA = ('ScriptParser'); + +# After these tokens, the next token is a command word. +# For example, in 'echo foo && grep bar file', the 'grep' +# after '&&' is at command position and should be flagged. +my %cmd_start = map { $_ => 1 } qw(&& || ; ;; do then else elif), "\n", '{', '('; + +# Tokens indicating grep's output is piped or redirected. +my %filter_op = map { $_ => 1 } qw(| > >> <); + +# A token is at "command word" position if the shell would +# interpret it as a program name rather than an argument. +# Only 'grep' at command position is an assertion we should +# flag; 'grep' as an argument ('test_must_fail grep') or +# value ('for cmd in grep sed') is not. +sub is_command_word { + my ($tokens, $pos) = @_; + return 1 if $pos == 0; + for (my $j = $pos - 1; $j >= 0; $j--) { + my $t = $tokens->[$j]->[0]; + # After a separator or pipe, a new command starts. + return 1 if $cmd_start{$t} || $t eq '|'; + # After '}' or ')', what follows is a separator or + # redirect on the compound command, not a new command. + return 0 if $t eq '}' || $t eq ')'; + # '!' is a prefix that does not consume command + # position; keep scanning to find what precedes it. + next if $t eq '!'; + # Any other word means we are past the command word. + return 0; + } + return 1; +} + +# lint_ok() reports whether a bare grep carries a trailing +# '# lint-ok' comment telling this linter to skip it. +# +# In practice this is needed for just one case: a grep acting +# as a data filter whose output is consumed by a redirect or +# pipe on an enclosing compound command (such as a subshell or +# brace group) rather than by grep's own pipeline, e.g. +# +# ( grep ... && # lint-ok +# sed ... ) >out +# +# { grep ... || : # lint-ok +# } >out +# +# is_filter() only scans grep's own pipeline: it stops at the +# separator before the compound command closes and never sees +# the outer redirect, so it would flag such a grep as an +# assertion. A grep that really is an assertion is better +# written as test_grep (or a guarded test_grep when the file's +# presence is conditional) than annotated with lint-ok. +sub lint_ok { + my ($raw_lines, $ln) = @_; + if ($ln < 1 || $ln > @$raw_lines) { + warn "lint_ok: line number $ln out of range (1.." . + scalar(@$raw_lines) . ")\n"; + return 0; + } + return $raw_lines->[$ln - 1] =~ /lint-ok/; +} + +# Grep is a filter (not an assertion) if it receives piped +# input or sends its output to a pipe or redirect. Check +# both directions from grep's position in the token stream. +sub is_filter { + my ($tokens, $pos) = @_; + # Backward: is grep receiving piped input? + # Newlines don't break pipes ('cmd |\n grep' is one + # pipeline), so skip past them. + for (my $j = $pos - 1; $j >= 0; $j--) { + my $t = $tokens->[$j]->[0]; + return 1 if $t eq '|'; + next if $t eq "\n"; + last if $cmd_start{$t} || $t eq '}' || $t eq ')'; + } + # Forward: is grep piping or redirecting output? + # Unlike the backward scan, we do not skip newlines here: + # a bare newline is a command boundary, and redirects or + # pipes must appear on the same line as grep (or after a + # line continuation, which the Lexer consumes). + for (my $j = $pos + 1; $j < @$tokens; $j++) { + my $t = $tokens->[$j]->[0]; + return 0 if $cmd_start{$t}; + return 1 if $filter_op{$t}; + } + return 0; +} + +# Map a body-relative line number to a file line number. +# For double-quoted bodies, backslash-continuation lines +# (\<newline>) are consumed by the Lexer without appearing +# in the body text, so the inner parser sees fewer lines +# than the source file has. We walk the source lines to +# count continuations and adjust accordingly. +sub body_to_file_line { + my ($body_lineno, $body_token, $raw_lines, $body_start) = @_; + my $body_text = $body_token->[0]; + my $body_end_line = $body_token->[4]; + unless ($body_start && $body_start >= 1) { + warn "body_start is not a positive integer\n"; + return $body_lineno; + } + my $file_lineno = $body_lineno + $body_start - 1; + # Only double-quoted bodies have line splices. + return $file_lineno unless $body_text =~ /^"/; + my $adj = 0; + my $lines_seen = 0; + unless ($body_end_line && $body_end_line >= $body_start) { + warn "body_end_line is not set for double-quoted body\n"; + return $file_lineno; + } + my $end = $body_end_line; + if ($end > @$raw_lines) { + warn "body_end_line ($end) exceeds file length (" . + scalar(@$raw_lines) . ")\n"; + return $file_lineno; + } + my $src_ln = $body_start; + while ($src_ln <= $end && $lines_seen < $body_lineno) { + my $line = $raw_lines->[$src_ln - 1]; + # Odd trailing backslashes = continuation (\<nl>). + # Even = escaped backslashes (\\), not a continuation. + if ($line =~ /(\\*)$/ && length($1) % 2 == 1) { + $adj++; + } else { + $lines_seen++; + } + $src_ln++; + } + if ($lines_seen < $body_lineno) { + warn "body_lineno ($body_lineno) not found within body range " . + "($body_start..$end)\n"; + } + return $file_lineno + $adj; +} + +# ScriptParser calls this for each test body found in the script. +sub check_test { + my $self = shift @_; + my $title = ScriptParser::unwrap(shift @_); + my $body_token = shift @_; + my $body_start = $body_token->[3]; + my $body = ScriptParser::unwrap($body_token); + # Handle heredoc-style test bodies: + # test_expect_success 'title' - <<\EOF + # grep pattern file + # EOF + # The '-' signals that the body follows as a heredoc. + if ($body eq '-') { + my $herebody = shift @_; + if ($herebody) { + $body = $herebody->{content}; + $body_start = $herebody->{start_line}; + } + } + return unless $body; + + my $raw_lines = $self->{raw_lines}; + + # The outer parser gives us the body as an opaque string. + # Parse it to get individual tokens with command boundaries. + my $parser = ShellParser->new(\$body); + my @tokens = $parser->parse(); + + my $file = $self->{file}; + + for (my $i = 0; $i < @tokens; $i++) { + my $text = $tokens[$i]->[0]; + next unless is_command_word(\@tokens, $i); + + my $token_lineno = $tokens[$i]->[3]; + unless (defined($token_lineno) && $token_lineno >= 1) { + warn "token has no line number\n"; + next; + } + my $file_lineno = body_to_file_line( + $token_lineno, + $body_token, $raw_lines, $body_start); + + # '!' negates the exit code without consuming command + # position. '! test_grep' is an anti-pattern because + # test_grep only prints diagnostics on grep failure, + # and '!' inverts after that decision is already made. + if ($text eq '!') { + if ($i + 1 < @tokens && + $tokens[$i + 1]->[0] eq 'test_grep' && + !lint_ok($raw_lines, $file_lineno)) { + print "$file:$file_lineno: error: ", + 'use "test_grep !" instead of ', + '"! test_grep"', "\n"; + $exit_code = 1; + } + next; + } + + # Bare grep as a command (not a filter) is a test + # assertion that should use test_grep for better + # failure diagnostics. + if ($text eq 'grep' && + !is_filter(\@tokens, $i) && + !lint_ok($raw_lines, $file_lineno)) { + print "$file:$file_lineno: error: ", + "bare grep outside pipeline ", + "(use test_grep)\n"; + $exit_code = 1; + } + } +} + +package main; + +for my $file (@ARGV) { + open(my $fh, '<:unix:crlf', $file) or die "$0: $file: $!\n"; + my @raw_lines = <$fh>; + close $fh; + my $s = join('', @raw_lines); + my $parser = GrepLintParser->new(\$s); + $parser->{file} = $file; + $parser->{raw_lines} = \@raw_lines; + $parser->parse(); +} +exit $exit_code;
diff --git a/t/greplint/bare-grep-after-and.expect b/t/greplint/bare-grep-after-and.expect new file mode 100644 index 0000000..7da1b21 --- /dev/null +++ b/t/greplint/bare-grep-after-and.expect
@@ -0,0 +1 @@ +3: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-after-and.test b/t/greplint/bare-grep-after-and.test new file mode 100644 index 0000000..c5b26e4 --- /dev/null +++ b/t/greplint/bare-grep-after-and.test
@@ -0,0 +1,4 @@ +test_expect_success 'grep after && is flagged' ' + cmd && + grep pattern file +'
diff --git a/t/greplint/bare-grep-after-semicolon.expect b/t/greplint/bare-grep-after-semicolon.expect new file mode 100644 index 0000000..7da1b21 --- /dev/null +++ b/t/greplint/bare-grep-after-semicolon.expect
@@ -0,0 +1 @@ +3: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-after-semicolon.test b/t/greplint/bare-grep-after-semicolon.test new file mode 100644 index 0000000..c1e468d --- /dev/null +++ b/t/greplint/bare-grep-after-semicolon.test
@@ -0,0 +1,4 @@ +test_expect_success 'grep after semicolon is flagged' ' + echo hello; + grep pattern file +'
diff --git a/t/greplint/bare-grep-compound-body.expect b/t/greplint/bare-grep-compound-body.expect new file mode 100644 index 0000000..eb6210f --- /dev/null +++ b/t/greplint/bare-grep-compound-body.expect
@@ -0,0 +1,3 @@ +4: error: bare grep outside pipeline (use test_grep) +8: error: bare grep outside pipeline (use test_grep) +15: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-compound-body.test b/t/greplint/bare-grep-compound-body.test new file mode 100644 index 0000000..72472ed --- /dev/null +++ b/t/greplint/bare-grep-compound-body.test
@@ -0,0 +1,17 @@ +test_expect_success 'grep after then/do/else is flagged' ' + if true + then + grep pattern file + fi && + while true + do + grep pattern file && + break + done && + if true + then + echo yes + else + grep pattern file + fi +'
diff --git a/t/greplint/bare-grep-count-mode.expect b/t/greplint/bare-grep-count-mode.expect new file mode 100644 index 0000000..8922d35 --- /dev/null +++ b/t/greplint/bare-grep-count-mode.expect
@@ -0,0 +1 @@ +2: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-count-mode.test b/t/greplint/bare-grep-count-mode.test new file mode 100644 index 0000000..b9c0f18 --- /dev/null +++ b/t/greplint/bare-grep-count-mode.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep -c is flagged (not special-cased)' ' + grep -c pattern file +'
diff --git a/t/greplint/bare-grep-explicit-pattern.expect b/t/greplint/bare-grep-explicit-pattern.expect new file mode 100644 index 0000000..8922d35 --- /dev/null +++ b/t/greplint/bare-grep-explicit-pattern.expect
@@ -0,0 +1 @@ +2: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-explicit-pattern.test b/t/greplint/bare-grep-explicit-pattern.test new file mode 100644 index 0000000..69d8651 --- /dev/null +++ b/t/greplint/bare-grep-explicit-pattern.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep -e is flagged' ' + grep -e pattern file +'
diff --git a/t/greplint/bare-grep-flags.expect b/t/greplint/bare-grep-flags.expect new file mode 100644 index 0000000..8922d35 --- /dev/null +++ b/t/greplint/bare-grep-flags.expect
@@ -0,0 +1 @@ +2: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-flags.test b/t/greplint/bare-grep-flags.test new file mode 100644 index 0000000..9ca0e10 --- /dev/null +++ b/t/greplint/bare-grep-flags.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep -E is flagged' ' + grep -E "pat+ern" file +'
diff --git a/t/greplint/bare-grep-lint-ok.expect b/t/greplint/bare-grep-lint-ok.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/bare-grep-lint-ok.expect
diff --git a/t/greplint/bare-grep-lint-ok.test b/t/greplint/bare-grep-lint-ok.test new file mode 100644 index 0000000..335b902 --- /dev/null +++ b/t/greplint/bare-grep-lint-ok.test
@@ -0,0 +1,4 @@ +test_expect_success 'grep with lint-ok annotation is not flagged' ' + grep pattern file && # lint-ok + echo done +'
diff --git a/t/greplint/bare-grep-negated.expect b/t/greplint/bare-grep-negated.expect new file mode 100644 index 0000000..8922d35 --- /dev/null +++ b/t/greplint/bare-grep-negated.expect
@@ -0,0 +1 @@ +2: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-negated.test b/t/greplint/bare-grep-negated.test new file mode 100644 index 0000000..ef789bc --- /dev/null +++ b/t/greplint/bare-grep-negated.test
@@ -0,0 +1,3 @@ +test_expect_success 'negated grep is flagged' ' + ! grep pattern file +'
diff --git a/t/greplint/bare-grep-pattern-file.expect b/t/greplint/bare-grep-pattern-file.expect new file mode 100644 index 0000000..8922d35 --- /dev/null +++ b/t/greplint/bare-grep-pattern-file.expect
@@ -0,0 +1 @@ +2: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-pattern-file.test b/t/greplint/bare-grep-pattern-file.test new file mode 100644 index 0000000..d70035e --- /dev/null +++ b/t/greplint/bare-grep-pattern-file.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep -f is flagged' ' + grep -f patterns.txt file +'
diff --git a/t/greplint/bare-grep-simple.expect b/t/greplint/bare-grep-simple.expect new file mode 100644 index 0000000..8922d35 --- /dev/null +++ b/t/greplint/bare-grep-simple.expect
@@ -0,0 +1 @@ +2: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-simple.test b/t/greplint/bare-grep-simple.test new file mode 100644 index 0000000..3a8d5f9 --- /dev/null +++ b/t/greplint/bare-grep-simple.test
@@ -0,0 +1,3 @@ +test_expect_success 'bare grep is flagged' ' + grep pattern file +'
diff --git a/t/greplint/bare-grep-subshell.expect b/t/greplint/bare-grep-subshell.expect new file mode 100644 index 0000000..7da1b21 --- /dev/null +++ b/t/greplint/bare-grep-subshell.expect
@@ -0,0 +1 @@ +3: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/bare-grep-subshell.test b/t/greplint/bare-grep-subshell.test new file mode 100644 index 0000000..1fab2d3 --- /dev/null +++ b/t/greplint/bare-grep-subshell.test
@@ -0,0 +1,5 @@ +test_expect_success 'grep in subshell is flagged' ' + ( + grep pattern file + ) +'
diff --git a/t/greplint/dqstring-continuation-offset.expect b/t/greplint/dqstring-continuation-offset.expect new file mode 100644 index 0000000..8185bfd --- /dev/null +++ b/t/greplint/dqstring-continuation-offset.expect
@@ -0,0 +1 @@ +10: error: bare grep outside pipeline (use test_grep)
diff --git a/t/greplint/dqstring-continuation-offset.test b/t/greplint/dqstring-continuation-offset.test new file mode 100644 index 0000000..b332bd8 --- /dev/null +++ b/t/greplint/dqstring-continuation-offset.test
@@ -0,0 +1,11 @@ +# Double-quoted test bodies with backslash-continuation lines: +# the splice adjustment in check_test compensates for \<newline> +# lines that the lexer consumes without emitting into the body +# text, so the reported line number matches the source. +test_expect_success 'dqstring continuation offset' " + x=\$(echo \ + hello) && + y=\$(echo \ + world) && + grep pattern file +"
diff --git a/t/greplint/filter-command-substitution.expect b/t/greplint/filter-command-substitution.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/filter-command-substitution.expect
diff --git a/t/greplint/filter-command-substitution.test b/t/greplint/filter-command-substitution.test new file mode 100644 index 0000000..bd9a100 --- /dev/null +++ b/t/greplint/filter-command-substitution.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep in command substitution is not flagged' ' + x=$(grep pattern file) +'
diff --git a/t/greplint/filter-pipe-input.expect b/t/greplint/filter-pipe-input.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/filter-pipe-input.expect
diff --git a/t/greplint/filter-pipe-input.test b/t/greplint/filter-pipe-input.test new file mode 100644 index 0000000..cb5c0d9 --- /dev/null +++ b/t/greplint/filter-pipe-input.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep receiving pipe input is not flagged' ' + cmd | grep pattern +'
diff --git a/t/greplint/filter-pipe-output.expect b/t/greplint/filter-pipe-output.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/filter-pipe-output.expect
diff --git a/t/greplint/filter-pipe-output.test b/t/greplint/filter-pipe-output.test new file mode 100644 index 0000000..0134008 --- /dev/null +++ b/t/greplint/filter-pipe-output.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep piping to another command is not flagged' ' + grep pattern file | wc -l +'
diff --git a/t/greplint/filter-redirect-output.expect b/t/greplint/filter-redirect-output.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/filter-redirect-output.expect
diff --git a/t/greplint/filter-redirect-output.test b/t/greplint/filter-redirect-output.test new file mode 100644 index 0000000..ee70ba7 --- /dev/null +++ b/t/greplint/filter-redirect-output.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep with output redirect is not flagged' ' + grep pattern file >output +'
diff --git a/t/greplint/filter-stdin-redirect.expect b/t/greplint/filter-stdin-redirect.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/filter-stdin-redirect.expect
diff --git a/t/greplint/filter-stdin-redirect.test b/t/greplint/filter-stdin-redirect.test new file mode 100644 index 0000000..292db77 --- /dev/null +++ b/t/greplint/filter-stdin-redirect.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep reading from stdin redirect is not flagged' ' + grep pattern <input +'
diff --git a/t/greplint/grep-as-argument.expect b/t/greplint/grep-as-argument.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/grep-as-argument.expect
diff --git a/t/greplint/grep-as-argument.test b/t/greplint/grep-as-argument.test new file mode 100644 index 0000000..7157228 --- /dev/null +++ b/t/greplint/grep-as-argument.test
@@ -0,0 +1,3 @@ +test_expect_success 'grep as argument to another command is not flagged' ' + test_must_fail grep pattern file +'
diff --git a/t/greplint/grep-as-value.expect b/t/greplint/grep-as-value.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/t/greplint/grep-as-value.expect
diff --git a/t/greplint/grep-as-value.test b/t/greplint/grep-as-value.test new file mode 100644 index 0000000..92926f6 --- /dev/null +++ b/t/greplint/grep-as-value.test
@@ -0,0 +1,6 @@ +test_expect_success 'grep as value in for-loop is not flagged' ' + for cmd in grep sed awk + do + echo $cmd + done +'
diff --git a/t/greplint/wrong-negation.expect b/t/greplint/wrong-negation.expect new file mode 100644 index 0000000..7dc52f6 --- /dev/null +++ b/t/greplint/wrong-negation.expect
@@ -0,0 +1 @@ +2: error: use "test_grep !" instead of "! test_grep"
diff --git a/t/greplint/wrong-negation.test b/t/greplint/wrong-negation.test new file mode 100644 index 0000000..542fbd9 --- /dev/null +++ b/t/greplint/wrong-negation.test
@@ -0,0 +1,3 @@ +test_expect_success 'wrong negation of test_grep is flagged' ' + ! test_grep pattern file +'
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c index b130832..8547ef6 100644 --- a/t/helper/test-bitmap.c +++ b/t/helper/test-bitmap.c
@@ -52,7 +52,7 @@ static int add_packed_object(const struct object_id *oid, entry = packlist_alloc(packed, oid); entry->idx.offset = nth_packed_object_offset(pack, pos); - if (packed_object_info(pack, entry->idx.offset, &oi) < 0) + if (packed_object_info(NULL, pack, entry->idx.offset, &oi) < 0) die("could not get type of object %s", oid_to_hex(oid)); oe_set_type(entry, type);
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c index fbf67fe..89b0268 100644 --- a/t/helper/test-hash-speed.c +++ b/t/helper/test-hash-speed.c
@@ -5,7 +5,7 @@ static inline void compute_hash(const struct git_hash_algo *algo, struct git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len) { - algo->init_fn(ctx); + git_hash_init(ctx, algo); git_hash_update(ctx, p, len); git_hash_final(final, ctx); }
diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c index f0ee61c..1f71636 100644 --- a/t/helper/test-hash.c +++ b/t/helper/test-hash.c
@@ -29,7 +29,7 @@ int cmd_hash_impl(int ac, const char **av, int algo, int unsafe) die("OOPS"); } - algop->init_fn(&ctx); + git_hash_init(&ctx, algop); while (1) { ssize_t sz, this_sz;
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c index e542985..43cead6 100644 --- a/t/helper/test-lazy-init-name-hash.c +++ b/t/helper/test-lazy-init-name-hash.c
@@ -218,7 +218,7 @@ int cmd__lazy_init_name_hash(int argc, const char **argv) /* * istate->dir_hash is only created when ignore_case is set. */ - ignore_case = 1; + repo_config_values(the_repository)->ignore_case = 1; if (dump) { if (perf || analyze > 0)
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index 15eb444..f77b3f9 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c
@@ -250,6 +250,7 @@ static int protect_ntfs_hfs_benchmark(int argc, const char **argv) double m[3][2], v[3][2]; uint64_t cumul; double cumul2; + int ntfs, hfs; if (argc > 1 && !strcmp(argv[1], "--with-symlink-mode")) { file_mode = 0120000; @@ -276,8 +277,13 @@ static int protect_ntfs_hfs_benchmark(int argc, const char **argv) names[i][--len] = (char)(' ' + (my_random() % ('\x7f' - ' '))); } - for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++) - for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) { + if (!the_repository->gitdir) + the_repository->gitdir = xstrdup(".git"); + + for (ntfs = 0; ntfs < 2; ntfs++) + for (hfs = 0; hfs < 2; hfs++) { + repo_config_values(the_repository)->protect_ntfs = ntfs; + repo_config_values(the_repository)->protect_hfs = hfs; cumul = 0; cumul2 = 0; for (i = 0; i < repetitions; i++) { @@ -285,18 +291,18 @@ static int protect_ntfs_hfs_benchmark(int argc, const char **argv) for (j = 0; j < nr; j++) verify_path(names[j], file_mode); end = getnanotime(); - printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", protect_ntfs, protect_hfs, (end-begin) / (double)1e6); + printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", ntfs, hfs, (end-begin) / (double)1e6); cumul += end - begin; cumul2 += (end - begin) * (end - begin); } - m[protect_ntfs][protect_hfs] = cumul / (double)repetitions; - v[protect_ntfs][protect_hfs] = my_sqrt(cumul2 / (double)repetitions - m[protect_ntfs][protect_hfs] * m[protect_ntfs][protect_hfs]); - printf("mean: %lfms, stddev: %lfms\n", m[protect_ntfs][protect_hfs] / (double)1e6, v[protect_ntfs][protect_hfs] / (double)1e6); + m[ntfs][hfs] = cumul / (double)repetitions; + v[ntfs][hfs] = my_sqrt(cumul2 / (double)repetitions - m[ntfs][hfs] * m[ntfs][hfs]); + printf("mean: %lfms, stddev: %lfms\n", m[ntfs][hfs] / (double)1e6, v[ntfs][hfs] / (double)1e6); } - for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++) - for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) - printf("ntfs=%d/hfs=%d: %lf%% slower\n", protect_ntfs, protect_hfs, (m[protect_ntfs][protect_hfs] - m[0][0]) * 100 / m[0][0]); + for (ntfs = 0; ntfs < 2; ntfs++) + for (hfs = 0; hfs < 2; hfs++) + printf("ntfs=%d/hfs=%d: %lf%% slower\n", ntfs, hfs, (m[ntfs][hfs] - m[0][0]) * 100 / m[0][0]); return 0; }
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c index 790000f..fb16ec0 100644 --- a/t/helper/test-read-midx.c +++ b/t/helper/test-read-midx.c
@@ -13,13 +13,16 @@ static struct multi_pack_index *setup_midx(const char *object_dir) { + struct odb_source_files *files; struct odb_source *source; setup_git_directory(the_repository); source = odb_find_source(the_repository->objects, object_dir); if (!source) source = odb_add_to_alternates_memory(the_repository->objects, object_dir); - return load_multi_pack_index(source); + files = odb_source_files_downcast(source); + + return load_multi_pack_index(files->packed); } static int read_midx_file(const char *object_dir, const char *checksum, @@ -70,7 +73,7 @@ static int read_midx_file(const char *object_dir, const char *checksum, for (i = 0; i < m->num_packs; i++) printf("%s\n", m->pack_names[i]); - printf("object-dir: %s\n", m->source->path); + printf("object-dir: %s\n", m->source->base.path); if (show_objects) { struct object_id oid;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c index b16c072..fc49faf 100644 --- a/t/helper/test-reftable.c +++ b/t/helper/test-reftable.c
@@ -174,7 +174,7 @@ static int dump_table(struct reftable_merged_table *mt) static int dump_stack(const char *stackdir, uint32_t hash_id) { struct reftable_stack *stack = NULL; - struct reftable_write_options opts = { .hash_id = hash_id }; + struct reftable_stack_options opts = { .hash_id = hash_id }; struct reftable_merged_table *merged = NULL; int err = reftable_new_stack(&stack, stackdir, &opts);
diff --git a/t/helper/test-synthesize.c b/t/helper/test-synthesize.c index 3fa534f..fd116c8 100644 --- a/t/helper/test-synthesize.c +++ b/t/helper/test-synthesize.c
@@ -25,8 +25,7 @@ static const unsigned char zeros[BLOCK_SIZE]; * Updates the pack checksum context. */ static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx, - const void *data, size_t len, - const struct git_hash_algo *algo) + const void *data, size_t len) { unsigned char zlib_header[2] = { 0x78, 0x01 }; /* CMF, FLG */ unsigned char block_header[5]; @@ -37,7 +36,7 @@ static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx, /* Write zlib header */ fwrite_or_die(f, zlib_header, sizeof(zlib_header)); - algo->update_fn(pack_ctx, zlib_header, 2); + git_hash_update(pack_ctx, zlib_header, 2); /* Write uncompressed blocks (max 64KB each) */ do { @@ -52,11 +51,11 @@ static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx, block_header[4] = block_header[2] ^ 0xff; fwrite_or_die(f, block_header, sizeof(block_header)); - algo->update_fn(pack_ctx, block_header, 5); + git_hash_update(pack_ctx, block_header, 5); if (block_len) { fwrite_or_die(f, block_data, block_len); - algo->update_fn(pack_ctx, block_data, block_len); + git_hash_update(pack_ctx, block_data, block_len); adler = adler32(adler, block_data, block_len); } @@ -68,7 +67,7 @@ static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx, /* Write adler32 checksum */ put_be32(adler_buf, adler); fwrite_or_die(f, adler_buf, sizeof(adler_buf)); - algo->update_fn(pack_ctx, adler_buf, 4); + git_hash_update(pack_ctx, adler_buf, 4); } /* @@ -92,24 +91,24 @@ static void write_pack_object(FILE *f, struct git_hash_ctx *pack_ctx, sizeof(pack_header), type, len); fwrite_or_die(f, pack_header, pack_header_len); - algo->update_fn(pack_ctx, pack_header, pack_header_len); + git_hash_update(pack_ctx, pack_header, pack_header_len); /* Write the data as uncompressed zlib */ - write_uncompressed_zlib(f, pack_ctx, data, len, algo); + write_uncompressed_zlib(f, pack_ctx, data, len); - algo->init_fn(&ctx); + git_hash_init(&ctx, algo); object_header_len = format_object_header(object_header, sizeof(object_header), type, len); - algo->update_fn(&ctx, object_header, object_header_len); + git_hash_update(&ctx, object_header, object_header_len); if (data) - algo->update_fn(&ctx, data, len); + git_hash_update(&ctx, data, len); else { for (size_t i = len / BLOCK_SIZE; i; i--) - algo->update_fn(&ctx, zeros, BLOCK_SIZE); - algo->update_fn(&ctx, zeros, len % BLOCK_SIZE); + git_hash_update(&ctx, zeros, BLOCK_SIZE); + git_hash_update(&ctx, zeros, len % BLOCK_SIZE); } - algo->final_oid_fn(oid, &ctx); + git_hash_final_oid(oid, &ctx); } /* @@ -430,11 +429,11 @@ static int generate_pack_with_large_object(const char *path, size_t blob_size, f = xfopen(path, "wb"); - algo->init_fn(&pack_ctx); + git_hash_init(&pack_ctx, algo); /* Write pack header */ fwrite_or_die(f, &pack_header, sizeof(pack_header)); - algo->update_fn(&pack_ctx, &pack_header, sizeof(pack_header)); + git_hash_update(&pack_ctx, &pack_header, sizeof(pack_header)); /* 1. Write the large blob */ write_pack_object(f, &pack_ctx, OBJ_BLOB, NULL, blob_size, &blob_oid, algo); @@ -472,7 +471,7 @@ static int generate_pack_with_large_object(const char *path, size_t blob_size, write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &final_commit_oid, algo); /* Write pack trailer (checksum) */ - algo->final_fn(pack_hash, &pack_ctx); + git_hash_final(pack_hash, &pack_ctx); fwrite_or_die(f, pack_hash, algo->rawsz); if (fclose(f)) die_errno(_("could not close '%s'"), path);
diff --git a/t/lib-bitmap.sh b/t/lib-bitmap.sh index 62aa674..2de6646 100644 --- a/t/lib-bitmap.sh +++ b/t/lib-bitmap.sh
@@ -173,7 +173,7 @@ test_expect_success "bitmap --objects handles non-commit objects ($state, $branch)" ' git rev-list --objects --use-bitmap-index $branch tagged-blob >actual && - grep $blob actual + test_grep $blob actual ' } @@ -242,16 +242,16 @@ GIT_PROGRESS_DELAY=0 \ git pack-objects --all --stdout --progress \ </dev/null >/dev/null 2>stderr && - grep "Enumerating objects: $count, done" stderr && - grep "pack-reused $count" stderr && + test_grep "Enumerating objects: $count, done" stderr && + test_grep "pack-reused $count" stderr && # now the same but with one non-reused object git commit --allow-empty -m "an extra commit object" && GIT_PROGRESS_DELAY=0 \ git pack-objects --all --stdout --progress \ </dev/null >/dev/null 2>stderr && - grep "Enumerating objects: $((count+1)), done" stderr && - grep "pack-reused $count" stderr + test_grep "Enumerating objects: $((count+1)), done" stderr && + test_grep "pack-reused $count" stderr ' } @@ -283,7 +283,7 @@ then test_path_is_file $midx-$(midx_checksum $objdir).rev fi && - grep "\"category\":\"load_midx_revindex\",\"key\":\"source\",\"value\":\"$kind\"" event.trace + test_grep "\"category\":\"load_midx_revindex\",\"key\":\"source\",\"value\":\"$kind\"" event.trace ' }
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh index de09b6b..794478a 100644 --- a/t/lib-bundle-uri-protocol.sh +++ b/t/lib-bundle-uri-protocol.sh
@@ -63,9 +63,9 @@ >actual 2>err && # Server responded using protocol v2 - grep "< version 2" log && + test_grep "< version 2" log && - ! grep bundle-uri log + test_grep ! bundle-uri log ' test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: have bundle-uri" ' @@ -78,10 +78,10 @@ >actual 2>err && # Server responded using protocol v2 - grep "< version 2" log && + test_grep "< version 2" log && # Server advertised bundle-uri capability - grep "< bundle-uri" log + test_grep "< bundle-uri" log ' test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: request bundle-uris" ' @@ -95,13 +95,13 @@ >actual 2>err && # Server responded using protocol v2 - grep "< version 2" log && + test_grep "< version 2" log && # Server advertised bundle-uri capability - grep "< bundle-uri" log && + test_grep "< bundle-uri" log && # Client did not issue bundle-uri command - ! grep "> command=bundle-uri" log && + test_grep ! "> command=bundle-uri" log && GIT_TRACE_PACKET="$PWD/log" \ git \ @@ -111,13 +111,13 @@ >actual 2>err && # Server responded using protocol v2 - grep "< version 2" log && + test_grep "< version 2" log && # Server advertised bundle-uri capability - grep "< bundle-uri" log && + test_grep "< bundle-uri" log && # Client issued bundle-uri command - grep "> command=bundle-uri" log && + test_grep "> command=bundle-uri" log && GIT_TRACE_PACKET="$PWD/log3" \ git \ @@ -128,13 +128,13 @@ >actual 2>err && # Server responded using protocol v2 - grep "< version 2" log3 && + test_grep "< version 2" log3 && # Server advertised bundle-uri capability - grep "< bundle-uri" log3 && + test_grep "< bundle-uri" log3 && # Client did not issue bundle-uri command (--bundle-uri override) - ! grep "> command=bundle-uri" log3 + test_grep ! "> command=bundle-uri" log3 ' # The remaining tests will all assume transfer.bundleURI=true @@ -214,3 +214,26 @@ >actual && test_cmp_config_output expect actual ' + +test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 with empty value" ' + test_config -C "$BUNDLE_URI_PARENT" \ + bundle.bundle1.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl" && + test_config -C "$BUNDLE_URI_PARENT" \ + bundle.bundle2.uri "" && + + # The empty bundle.bundle2.uri value is invalid configuration and the + # server must not advertise it to the client. + cat >expect <<-EOF && + [bundle] + version = 1 + mode = all + [bundle "bundle1"] + uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl + EOF + + test-tool bundle-uri \ + ls-remote \ + "$BUNDLE_URI_REPO_URI" \ + >actual && + test_cmp_config_output expect actual +'
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index fc64644..a216e53 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh
@@ -275,7 +275,7 @@ ' test_expect_success 'non-fast-forward push show ref status' ' - grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" output + test_grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" output ' test_expect_success 'non-fast-forward push shows help message' '
diff --git a/t/lib-shell-parser.pl b/t/lib-shell-parser.pl new file mode 100644 index 0000000..d78e185 --- /dev/null +++ b/t/lib-shell-parser.pl
@@ -0,0 +1,534 @@ +# Copyright (c) 2021-2022 Eric Sunshine <sunshine@sunshineco.com> +# +# Shared shell script parser for test lint tools. Provides Lexer, +# ShellParser, and ScriptParser. Subclass ScriptParser and override +# check_test() to implement lint checks. + +use strict; +use warnings; + +# Lexer tokenizes POSIX shell scripts. It is roughly modeled after section 2.3 +# "Token Recognition" of POSIX chapter 2 "Shell Command Language". Although +# similar to lexical analyzers for other languages, this one differs in a few +# substantial ways due to quirks of the shell command language. +# +# For instance, in many languages, newline is just whitespace like space or +# TAB, but in shell a newline is a command separator, thus a distinct lexical +# token. A newline is significant and returned as a distinct token even at the +# end of a shell comment. +# +# In other languages, `1+2` would typically be scanned as three tokens +# (`1`, `+`, and `2`), but in shell it is a single token. However, the similar +# `1 + 2`, which embeds whitepaces, is scanned as three token in shell, as well. +# In shell, several characters with special meaning lose that meaning when not +# surrounded by whitespace. For instance, the negation operator `!` is special +# when standing alone surrounded by whitespace; whereas in `foo!uucp` it is +# just a plain character in the longer token "foo!uucp". In many other +# languages, `"string"/foo:'string'` might be scanned as five tokens ("string", +# `/`, `foo`, `:`, and 'string'), but in shell, it is just a single token. +# +# The lexical analyzer for the shell command language is also somewhat unusual +# in that it recursively invokes the parser to handle the body of `$(...)` +# expressions which can contain arbitrary shell code. Such expressions may be +# encountered both inside and outside of double-quoted strings. +# +# The lexical analyzer is responsible for consuming shell here-doc bodies which +# extend from the line following a `<<TAG` operator until a line consisting +# solely of `TAG`. Here-doc consumption begins when a newline is encountered. +# It is legal for multiple here-doc `<<TAG` operators to be present on a single +# line, in which case their bodies must be present one following the next, and +# are consumed in the (left-to-right) order the `<<TAG` operators appear on the +# line. A special complication is that the bodies of all here-docs must be +# consumed when the newline is encountered even if the parse context depth has +# changed. For instance, in `cat <<A && x=$(cat <<B &&\n`, bodies of here-docs +# "A" and "B" must be consumed even though "A" was introduced outside the +# recursive parse context in which "B" was introduced and in which the newline +# is encountered. +package Lexer; + +sub new { + my ($class, $parser, $s) = @_; + bless { + parser => $parser, + buff => $s, + lineno => 1, + heretags => [] + } => $class; +} + +sub scan_heredoc_tag { + my $self = shift @_; + ${$self->{buff}} =~ /\G(-?)/gc; + my $indented = $1; + my $token = $self->scan_token(); + return "<<$indented" unless $token; + my $tag = $token->[0]; + $tag =~ s/['"\\]//g; + $$token[0] = $indented ? "\t$tag" : "$tag"; + push(@{$self->{heretags}}, $token); + return "<<$indented$tag"; +} + +sub scan_op { + my ($self, $c) = @_; + my $b = $self->{buff}; + return $c unless $$b =~ /\G(.)/sgc; + my $cc = $c . $1; + return scan_heredoc_tag($self) if $cc eq '<<'; + return $cc if $cc =~ /^(?:&&|\|\||>>|;;|<&|>&|<>|>\|)$/; + pos($$b)--; + return $c; +} + +sub scan_sqstring { + my $self = shift @_; + ${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc; + my $s = $1; + $self->{lineno} += () = $s =~ /\n/sg; + return "'" . $s; +} + +sub scan_dqstring { + my $self = shift @_; + my $b = $self->{buff}; + my $s = '"'; + while (1) { + # Slurp non-special characters; count newlines here because + # newlines inside $() are already counted by the recursive parse. + if ($$b =~ /\G([^"\$\\]+)/gc) { + $s .= $1; + $self->{lineno} += $1 =~ tr/\n//; + } + # handle special characters + last unless $$b =~ /\G(.)/sgc; + my $c = $1; + $s .= '"', last if $c eq '"'; + $s .= '$' . $self->scan_dollar(), next if $c eq '$'; + if ($c eq '\\') { + $s .= '\\', last unless $$b =~ /\G(.)/sgc; + $c = $1; + $self->{lineno}++, next if $c eq "\n"; # line splice + # backslash escapes only $, `, ", \ in dq-string + $s .= '\\' unless $c =~ /^[\$`"\\]$/; + $s .= $c; + next; + } + die("internal error scanning dq-string '$c'\n"); + } + return $s; +} + +sub scan_balanced { + my ($self, $c1, $c2) = @_; + my $b = $self->{buff}; + my $depth = 1; + my $s = $c1; + while ($$b =~ /\G([^\Q$c1$c2\E]*(?:[\Q$c1$c2\E]|\z))/gc) { + $s .= $1; + $depth++, next if $s =~ /\Q$c1\E$/; + $depth--; + last if $depth == 0; + } + $self->{lineno} += () = $s =~ /\n/sg; + return $s; +} + +sub scan_subst { + my $self = shift @_; + my @tokens = $self->{parser}->parse(qr/^\)$/); + $self->{parser}->next_token(); # closing ")" + return @tokens; +} + +sub scan_dollar { + my $self = shift @_; + my $b = $self->{buff}; + return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...)) + return '(' . join(' ', map {$_->[0]} $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...) + return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...} + return $1 if $$b =~ /\G(\w+)/gc; # $var + return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc. + return ''; +} + +sub swallow_heredocs { + my $self = shift @_; + my $b = $self->{buff}; + my $tags = $self->{heretags}; + while (my $tag = shift @$tags) { + my $start = pos($$b); + my $indent = $$tag[0] =~ s/^\t// ? '\\s*' : ''; + $$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc; + if (pos($$b) > $start) { + my $body = substr($$b, $start, pos($$b) - $start); + $self->{parser}->{heredocs}->{$$tag[0]} = { + content => substr($body, 0, length($body) - length($&)), + start_line => $self->{lineno}, + }; + $self->{lineno} += () = $body =~ /\n/sg; + next; + } + push(@{$self->{parser}->{problems}}, ['HEREDOC', $tag]); + $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input + my $body = substr($$b, $start, pos($$b) - $start); + $self->{lineno} += () = $body =~ /\n/sg; + last; + } +} + +sub scan_token { + my $self = shift @_; + my $b = $self->{buff}; + my $token = ''; + my ($start, $startln); +RESTART: + $startln = $self->{lineno}; + $$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline) + $start = pos($$b) || 0; + $self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment + while (1) { + # slurp up non-special characters + $token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc; + # handle special characters + last unless $$b =~ /\G(.)/sgc; + my $c = $1; + pos($$b)--, last if $c =~ /^[ \t]$/; # whitespace ends token + pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/; + $token .= $self->scan_sqstring(), next if $c eq "'"; + $token .= $self->scan_dqstring(), next if $c eq '"'; + $token .= $c . $self->scan_dollar(), next if $c eq '$'; + $self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n"; + $token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/; + $token = $c, last if $c =~ /^[(){}]$/; + if ($c eq '\\') { + $token .= '\\', last unless $$b =~ /\G(.)/sgc; + $c = $1; + $self->{lineno}++, next if $c eq "\n" && length($token); # line splice + $self->{lineno}++, goto RESTART if $c eq "\n"; # line splice + $token .= '\\' . $c; + next; + } + die("internal error scanning character '$c'\n"); + } + return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef; +} + +# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It +# is a recursive descent parser very roughly modeled after section 2.10 "Shell +# Grammar" of POSIX chapter 2 "Shell Command Language". + +package ShellParser; + +sub new { + my ($class, $s) = @_; + my $self = bless { + buff => [], + stop => [], + output => [], + heredocs => {}, + insubshell => 0, + } => $class; + $self->{lexer} = Lexer->new($self, $s); + return $self; +} + +sub next_token { + my $self = shift @_; + return pop(@{$self->{buff}}) if @{$self->{buff}}; + return $self->{lexer}->scan_token(); +} + +sub untoken { + my $self = shift @_; + push(@{$self->{buff}}, @_); +} + +sub peek { + my $self = shift @_; + my $token = $self->next_token(); + return undef unless defined($token); + $self->untoken($token); + return $token; +} + +sub stop_at { + my ($self, $token) = @_; + return 1 unless defined($token); + my $stop = ${$self->{stop}}[-1] if @{$self->{stop}}; + return defined($stop) && $token->[0] =~ $stop; +} + +sub expect { + my ($self, $expect) = @_; + my $token = $self->next_token(); + return $token if defined($token) && $token->[0] eq $expect; + push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token->[0] : "<end-of-input>") . "'\n"); + $self->untoken($token) if defined($token); + return (); +} + +sub optional_newlines { + my $self = shift @_; + my @tokens; + while (my $token = $self->peek()) { + last unless $token->[0] eq "\n"; + push(@tokens, $self->next_token()); + } + return @tokens; +} + +sub parse_group { + my $self = shift @_; + return ($self->parse(qr/^}$/), + $self->expect('}')); +} + +sub parse_subshell { + my $self = shift @_; + $self->{insubshell}++; + my @tokens = ($self->parse(qr/^\)$/), + $self->expect(')')); + $self->{insubshell}--; + return @tokens; +} + +sub parse_case_pattern { + my $self = shift @_; + my @tokens; + while (defined(my $token = $self->next_token())) { + push(@tokens, $token); + last if $token->[0] eq ')'; + } + return @tokens; +} + +sub parse_case { + my $self = shift @_; + my @tokens; + push(@tokens, + $self->next_token(), # subject + $self->optional_newlines(), + $self->expect('in'), + $self->optional_newlines()); + while (1) { + my $token = $self->peek(); + last unless defined($token) && $token->[0] ne 'esac'; + push(@tokens, + $self->parse_case_pattern(), + $self->optional_newlines(), + $self->parse(qr/^(?:;;|esac)$/)); # item body + $token = $self->peek(); + last unless defined($token) && $token->[0] ne 'esac'; + push(@tokens, + $self->expect(';;'), + $self->optional_newlines()); + } + push(@tokens, $self->expect('esac')); + return @tokens; +} + +sub parse_for { + my $self = shift @_; + my @tokens; + push(@tokens, + $self->next_token(), # variable + $self->optional_newlines()); + my $token = $self->peek(); + if (defined($token) && $token->[0] eq 'in') { + push(@tokens, + $self->expect('in'), + $self->optional_newlines()); + } + push(@tokens, + $self->parse(qr/^do$/), # items + $self->expect('do'), + $self->optional_newlines(), + $self->parse_loop_body(), + $self->expect('done')); + return @tokens; +} + +sub parse_if { + my $self = shift @_; + my @tokens; + while (1) { + push(@tokens, + $self->parse(qr/^then$/), # if/elif condition + $self->expect('then'), + $self->optional_newlines(), + $self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body + my $token = $self->peek(); + last unless defined($token) && $token->[0] eq 'elif'; + push(@tokens, $self->expect('elif')); + } + my $token = $self->peek(); + if (defined($token) && $token->[0] eq 'else') { + push(@tokens, + $self->expect('else'), + $self->optional_newlines(), + $self->parse(qr/^fi$/)); # else body + } + push(@tokens, $self->expect('fi')); + return @tokens; +} + +sub parse_loop_body { + my $self = shift @_; + return $self->parse(qr/^done$/); +} + +sub parse_loop { + my $self = shift @_; + return ($self->parse(qr/^do$/), # condition + $self->expect('do'), + $self->optional_newlines(), + $self->parse_loop_body(), + $self->expect('done')); +} + +sub parse_func { + my $self = shift @_; + return ($self->expect('('), + $self->expect(')'), + $self->optional_newlines(), + $self->parse_cmd()); # body +} + +sub parse_bash_array_assignment { + my $self = shift @_; + my @tokens = $self->expect('('); + while (defined(my $token = $self->next_token())) { + push(@tokens, $token); + last if $token->[0] eq ')'; + } + return @tokens; +} + +my %compound = ( + '{' => \&parse_group, + '(' => \&parse_subshell, + 'case' => \&parse_case, + 'for' => \&parse_for, + 'if' => \&parse_if, + 'until' => \&parse_loop, + 'while' => \&parse_loop); + +sub parse_cmd { + my $self = shift @_; + my $cmd = $self->next_token(); + return () unless defined($cmd); + return $cmd if $cmd->[0] eq "\n"; + + my $token; + my @tokens = $cmd; + if ($cmd->[0] eq '!') { + push(@tokens, $self->parse_cmd()); + return @tokens; + } elsif (my $f = $compound{$cmd->[0]}) { + push(@tokens, $self->$f()); + } elsif (defined($token = $self->peek()) && $token->[0] eq '(') { + if ($cmd->[0] !~ /\w=$/) { + push(@tokens, $self->parse_func()); + return @tokens; + } + my @array = $self->parse_bash_array_assignment(); + $tokens[-1]->[0] .= join(' ', map {$_->[0]} @array); + $tokens[-1]->[2] = $array[$#array][2] if @array; + } + + while (defined(my $token = $self->next_token())) { + $self->untoken($token), last if $self->stop_at($token); + push(@tokens, $token); + last if $token->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/; + } + push(@tokens, $self->next_token()) if $tokens[-1]->[0] ne "\n" && defined($token = $self->peek()) && $token->[0] eq "\n"; + return @tokens; +} + +sub accumulate { + my ($self, $tokens, $cmd) = @_; + push(@$tokens, @$cmd); +} + +sub parse { + my ($self, $stop) = @_; + push(@{$self->{stop}}, $stop); + goto DONE if $self->stop_at($self->peek()); + my @tokens; + while (my @cmd = $self->parse_cmd()) { + $self->accumulate(\@tokens, \@cmd); + last if $self->stop_at($self->peek()); + } +DONE: + pop(@{$self->{stop}}); + return @tokens; +} + +# ScriptParser is a subclass of ShellParser which identifies individual test +# definitions within test scripts and passes each test body to check_test(). +# ScriptParser detects test definitions not only at the top-level of test +# scripts but also within compound commands such as loops and function +# definitions. + +package ScriptParser; + +our @ISA = ('ShellParser'); + +sub new { + my $class = shift @_; + my $self = $class->SUPER::new(@_); + $self->{ntests} = 0; + $self->{nerrs} = 0; + return $self; +} + +# extract the raw content of a token, which may be a single string or a +# composition of multiple strings and non-string character runs; for instance, +# `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d` +sub unwrap { + my $token = (@_ ? shift @_ : $_)->[0]; + # simple case: 'sqstring' or "dqstring" + return $token if $token =~ s/^'([^']*)'$/$1/; + return $token if $token =~ s/^"([^"]*)"$/$1/; + + # composite case + my ($s, $q, $escaped); + while (1) { + # slurp up non-special characters + $s .= $1 if $token =~ /\G([^\\'"]*)/gc; + # handle special characters + last unless $token =~ /\G(.)/sgc; + my $c = $1; + $q = undef, next if defined($q) && $c eq $q; + $q = $c, next if !defined($q) && $c =~ /^['"]$/; + if ($c eq '\\') { + last unless $token =~ /\G(.)/sgc; + $c = $1; + $s .= '\\' if $c eq "\n"; # preserve line splice + } + $s .= $c; + } + return $s +} + +sub check_test { + # no-op; subclass and override to implement lint checks +} + +sub parse_cmd { + my $self = shift @_; + my @tokens = $self->SUPER::parse_cmd(); + return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/; + my $n = $#tokens; + $n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/; + my $herebody; + if ($n >= 2 && $tokens[$n-1]->[0] eq '-' && $tokens[$n]->[0] =~ /^<<-?(.+)$/) { + $herebody = $self->{heredocs}->{$1}; + $n--; + } + $self->check_test($tokens[1], $tokens[2], $herebody) if $n == 2; # title body + $self->check_test($tokens[2], $tokens[3], $herebody) if $n > 2; # prereq title body + return @tokens; +} + +1;
diff --git a/t/meson.build b/t/meson.build index 3219264..8ae6ab6 100644 --- a/t/meson.build +++ b/t/meson.build
@@ -125,6 +125,7 @@ 't0090-cache-tree.sh', 't0091-bugreport.sh', 't0092-diagnose.sh', + 't0093-verify-cache-df-gap.sh', 't0095-bloom.sh', 't0100-previous.sh', 't0101-at-syntax.sh', @@ -223,6 +224,10 @@ 't1461-refs-list.sh', 't1462-refs-exists.sh', 't1463-refs-optimize.sh', + 't1464-refs-delete.sh', + 't1465-refs-update.sh', + 't1466-refs-create.sh', + 't1467-refs-rename.sh', 't1500-rev-parse.sh', 't1501-work-tree.sh', 't1502-rev-parse-parseopt.sh', @@ -399,6 +404,7 @@ 't3451-history-reword.sh', 't3452-history-split.sh', 't3453-history-fixup.sh', + 't3454-history-drop.sh', 't3500-cherry.sh', 't3501-revert-cherry-pick.sh', 't3502-cherry-pick-merge.sh', @@ -576,6 +582,7 @@ 't4215-log-skewed-merges.sh', 't4216-log-bloom.sh', 't4217-log-limit.sh', + 't4219-log-follow-merge.sh', 't4252-am-options.sh', 't4253-am-keep-cr-dos.sh', 't4254-am-corrupt.sh',
diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh index d76b087..a800177 100644 --- a/t/pack-refs-tests.sh +++ b/t/pack-refs-tests.sh
@@ -195,7 +195,7 @@ test_expect_success 'pack ref directly below refs/' ' git update-ref refs/top HEAD && git ${pack_refs} --all --prune && - grep refs/top .git/packed-refs && + test_grep refs/top .git/packed-refs && test_path_is_missing .git/refs/top '
diff --git a/t/perf/p0009-diff-pathspec.sh b/t/perf/p0009-diff-pathspec.sh new file mode 100755 index 0000000..6079db5 --- /dev/null +++ b/t/perf/p0009-diff-pathspec.sh
@@ -0,0 +1,32 @@ +#!/bin/sh + +test_description='Tests performance of diffing the working tree with a pathspec' + +. ./perf-lib.sh + +test_perf_fresh_repo + +count=10000 +if test_have_prereq EXPENSIVE +then + count=100000 +fi + +# The entries exist only in the index, which is enough to +# exercise the index scan. +test_expect_success 'setup' ' + blob=$(echo content | git hash-object -w --stdin) && + { + printf "100644 $blob\taaa/file\n" && + printf "100644 $blob\tf%s\n" $(test_seq $count) + } | git update-index --index-info && + git commit -q -m initial && + mkdir -p aaa && + echo content >aaa/file +' + +test_perf 'diff pathspec subtree' ' + git diff HEAD -- aaa/file +' + +test_done
diff --git a/t/perf/p1401-ref-store-tombstones.sh b/t/perf/p1401-ref-store-tombstones.sh new file mode 100755 index 0000000..9e3d803 --- /dev/null +++ b/t/perf/p1401-ref-store-tombstones.sh
@@ -0,0 +1,46 @@ +#!/bin/sh + +test_description="Tests performance of ref operations with many tombstones" + +. ./perf-lib.sh + +test_expect_success "setup" ' + git init --ref-format=reftable repo && + blob=$(echo foo | git -C repo hash-object -w --stdin) && + for i in $(test_seq 8000) + do + printf "create refs/tags/tag-%d %s\n" "$i" "$blob" || + return 1 + done >repo/input && + git -C repo update-ref --stdin <repo/input && + git -C repo for-each-ref --format="delete %(refname)" | + git -C repo update-ref --stdin +' + +test_perf "recreate refs after mass delete" ' + git -C repo update-ref --stdin <repo/input && + git -C repo for-each-ref --format="delete %(refname)" | + git -C repo update-ref --stdin +' + +test_expect_success "setup asymmetric" ' + git init --ref-format=reftable repo2 && + blob=$(echo foo | git -C repo2 hash-object -w --stdin) && + for i in $(test_seq 8000) + do + printf "create refs/tags/old-%d %s\n" "$i" "$blob" || + return 1 + done >repo2/input-old && + sed "s/old-/new-/" <repo2/input-old >repo2/input-new && + git -C repo2 update-ref --stdin <repo2/input-old && + git -C repo2 for-each-ref --format="delete %(refname)" | + git -C repo2 update-ref --stdin +' + +test_perf "create new refs after deleting differently-named refs" ' + git -C repo2 update-ref --stdin <repo2/input-new && + git -C repo2 for-each-ref --format="delete %(refname)" refs/tags/ | + git -C repo2 update-ref --stdin +' + +test_done
diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh index 047efb9..5050621 100755 --- a/t/perf/p5311-pack-bitmaps-fetch.sh +++ b/t/perf/p5311-pack-bitmaps-fetch.sh
@@ -4,16 +4,22 @@ . ./perf-lib.sh test_fetch_bitmaps () { + argv=$1 + export argv + test_expect_success 'setup test directory' ' rm -fr * .git ' test_perf_default_repo - test_expect_success 'create bitmapped server repo' ' + test_expect_success "create bitmapped server repo ${argv:+($argv)}" ' git config pack.writebitmaps true && - git config pack.writeBitmapLookupTable '"$1"' && - git repack -ad + git repack -adF $argv + ' + + test_size "size of bitmapped pack ${argv:+($argv)}" ' + test_file_size .git/objects/pack/pack-*.pack ' # simulate a fetch from a repository that last fetched N days ago, for @@ -21,7 +27,7 @@ # and assume the first entry in the chain that is N days older than the current # HEAD is where the HEAD would have been then. for days in 1 2 4 8 16 32 64 128; do - title=$(printf '%10s' "($days days)") + title=$(printf '%10s' "($days days${argv:+, $argv})") test_expect_success "setup revs from $days days ago" ' now=$(git log -1 --format=%ct HEAD) && then=$(($now - ($days * 86400))) && @@ -32,7 +38,7 @@ } >revs ' - test_perf "server $title (lookup=$1)" ' + test_perf "server $title" ' git pack-objects --stdout --revs \ --thin --delta-base-offset \ <revs >tmp.pack @@ -42,13 +48,15 @@ test_file_size tmp.pack ' - test_perf "client $title (lookup=$1)" ' + test_perf "client $title" ' git index-pack --stdin --fix-thin <tmp.pack ' done } -test_fetch_bitmaps true -test_fetch_bitmaps false +for argv in '' --path-walk +do + test_fetch_bitmaps $argv || return 1 +done test_done
diff --git a/t/perf/p6300-for-each-ref.sh b/t/perf/p6300-for-each-ref.sh index fa7289c..25ffa5e 100755 --- a/t/perf/p6300-for-each-ref.sh +++ b/t/perf/p6300-for-each-ref.sh
@@ -1,6 +1,6 @@ #!/bin/sh -test_description='performance of for-each-ref' +test_description='performance of ref-filter users' . ./perf-lib.sh test_perf_fresh_repo @@ -84,4 +84,41 @@ ' run_tests "packed" +test_expect_success 'setup many unrelated refs' ' + git init scoped && + test_commit -C scoped --no-tag base && + test_seq $ref_count_per_type | + sed "s,.*,update refs/custom/unrelated_& HEAD," | + git -C scoped update-ref --stdin && + git -C scoped update-ref refs/remotes/origin/main HEAD && + git -C scoped update-ref refs/tags/only HEAD +' + +test_perf "branch (many unrelated refs)" " + ( + cd scoped && + for i in \$(test_seq $test_iteration_count); do + git branch --format='%(refname)' >/dev/null + done + ) +" + +test_perf "branch --remotes (many unrelated refs)" " + ( + cd scoped && + for i in \$(test_seq $test_iteration_count); do + git branch --remotes --format='%(refname)' >/dev/null + done + ) +" + +test_perf "tag (many unrelated refs)" " + ( + cd scoped && + for i in \$(test_seq $test_iteration_count); do + git tag --format='%(refname)' >/dev/null + done + ) +" + test_done
diff --git a/t/show-ref-exists-tests.sh b/t/show-ref-exists-tests.sh index 36e8e9d..1351f69 100644 --- a/t/show-ref-exists-tests.sh +++ b/t/show-ref-exists-tests.sh
@@ -19,7 +19,7 @@ test_expect_success '--exists does not use DWIM' ' test_expect_code 2 ${git_show_ref_exists} $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err && - grep "reference does not exist" err + test_grep "reference does not exist" err ' test_expect_success '--exists with HEAD' '
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 2b63e1c..7bd1f4e 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh
@@ -743,7 +743,7 @@ test_done EOF - grep "echo trace" lazy-prereq-and-tracing/err + test_grep "echo trace" lazy-prereq-and-tracing/err ' test_expect_success 'subtest: tests clean up after themselves' ' @@ -815,7 +815,7 @@ test_expect_success 'test_oid provides sane info by default' ' test_oid zero >actual && - grep "^00*\$" actual && + test_grep "^00*\$" actual && rawsz="$(test_oid rawsz)" && hexsz="$(test_oid hexsz)" && # +1 accounts for the trailing newline @@ -827,7 +827,7 @@ test_when_finished "test_detect_hash" && test_set_hash sha1 && test_oid zero >actual && - grep "^00*\$" actual && + test_grep "^00*\$" actual && rawsz="$(test_oid rawsz)" && hexsz="$(test_oid hexsz)" && test $(wc -c <actual) -eq 41 && @@ -839,7 +839,7 @@ test_when_finished "test_detect_hash" && test_set_hash sha256 && test_oid zero >actual && - grep "^00*\$" actual && + test_grep "^00*\$" actual && rawsz="$(test_oid rawsz)" && hexsz="$(test_oid hexsz)" && test $(wc -c <actual) -eq 65 && @@ -884,11 +884,11 @@ # test script, hence the redirection of fd 7, and aborts # with "exit 1", hence the subshell. ! ( test_bool_env envvar true ) 7>err && - grep "error: test_bool_env requires bool values" err && + test_grep "error: test_bool_env requires bool values" err && envvar=true && ! ( test_bool_env envvar invalid ) 7>err && - grep "error: test_bool_env requires bool values" err + test_grep "error: test_bool_env requires bool values" err ) ' @@ -1242,12 +1242,12 @@ test_expect_success 'test_must_fail rejects a non-git command' ' ! test_must_fail grep ^$ notafile 2>err && - grep -F "test_must_fail: only '"'"'git'"'"' is allowed" err + test_grep -F "test_must_fail: only '"'"'git'"'"' is allowed" err ' test_expect_success 'test_must_fail rejects a non-git command with env' ' ! test_must_fail env var1=a var2=b grep ^$ notafile 2>err && - grep -F "test_must_fail: only '"'"'git'"'"' is allowed" err + test_grep -F "test_must_fail: only '"'"'git'"'"' is allowed" err ' test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh index e89feca..5cf2e5a 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh
@@ -278,9 +278,9 @@ git init --bare --shared=0660 newdir/a/b/c && test_path_is_dir newdir/a/b/c/refs && ls -ld newdir/a newdir/a/b > lsab.out && - ! grep -v "^drwxrw[sx]r-x" lsab.out && + test_grep ! -v "^drwxrw[sx]r-x" lsab.out && ls -ld newdir/a/b/c > lsc.out && - ! grep -v "^drwxrw[sx]---" lsc.out + test_grep ! -v "^drwxrw[sx]---" lsc.out ) ' @@ -619,7 +619,7 @@ git init refstorage && git -C refstorage config extensions.refStorage files && test_must_fail git -C refstorage rev-parse 2>err && - grep "repo version is 0, but v1-only extension found" err + test_grep "repo version is 0, but v1-only extension found" err ' test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' ' @@ -637,7 +637,7 @@ git -C refstorage config core.repositoryformatversion 1 && git -C refstorage config extensions.refStorage garbage && test_must_fail git -C refstorage rev-parse 2>err && - grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err + test_grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err ' test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' @@ -848,8 +848,8 @@ GIT_REDIRECT_STDOUT=output.txt \ GIT_REDIRECT_STDERR="2>&1" \ git rev-parse --git-dir --verify refs/invalid && - grep "^\\.git\$" output.txt && - grep "Needed a single revision" output.txt + test_grep "^\\.git\$" output.txt && + test_grep "Needed a single revision" output.txt ' test_expect_success '--initial-branch' ' @@ -862,14 +862,14 @@ git init --initial-branch=ignore initial-branch-option 2>err && test_grep "ignored --initial-branch" err && git -C initial-branch-option symbolic-ref HEAD >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'overridden default initial branch name (config)' ' test_config_global init.defaultBranch nmb && GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= git init initial-branch-config && git -C initial-branch-config symbolic-ref HEAD >actual && - grep nmb actual + test_grep nmb actual ' test_expect_success 'advice on unconfigured init.defaultBranch' ' @@ -907,7 +907,7 @@ test_config_global init.defaultBranch nmb && GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=env git init main-branch-env && git -C main-branch-env symbolic-ref HEAD >actual && - grep env actual + test_grep env actual ' test_expect_success 'invalid default branch name' '
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh index dfbcddd..6356e9e 100755 --- a/t/t0002-gitfile.sh +++ b/t/t0002-gitfile.sh
@@ -27,7 +27,7 @@ test_expect_success 'bad setup: invalid .git file path' ' echo "gitdir: $REAL.not" >.git && test_must_fail git rev-parse 2>.err && - test_grep "not a git repository" .err + test_grep "gitfile does not point to a valid repository" .err ' test_expect_success 'final setup + check rev-parse --git-dir' '
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index d77a179..ed95faf 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh
@@ -790,8 +790,8 @@ >one && mkdir top-level-dir && git check-ignore one top-level-dir >actual && - grep one actual && - grep top-level-dir actual + test_grep one actual && + test_grep top-level-dir actual ' test_expect_success 'existing directory and file' ' @@ -800,8 +800,8 @@ >one && mkdir top-level-dir && git check-ignore top-level-dir one >actual && - grep one actual && - grep top-level-dir actual + test_grep one actual && + test_grep top-level-dir actual ' test_expect_success 'exact prefix matching (with root)' '
diff --git a/t/t0009-git-dir-validation.sh b/t/t0009-git-dir-validation.sh index 33d21ed..4cba478 100755 --- a/t/t0009-git-dir-validation.sh +++ b/t/t0009-git-dir-validation.sh
@@ -35,7 +35,7 @@ cd parent/fifo-trap && mkfifo .git && test_must_fail git rev-parse --git-dir 2>stderr && - grep "not a regular file" stderr + test_grep "not a regular file" stderr ) ' @@ -47,7 +47,7 @@ mkfifo target-fifo && ln -s target-fifo .git && test_must_fail git rev-parse --git-dir 2>stderr && - grep "not a regular file" stderr + test_grep "not a regular file" stderr ) ' @@ -58,7 +58,7 @@ cd parent/garbage-trap && echo "garbage" >.git && test_must_fail git rev-parse --git-dir 2>stderr && - grep "invalid gitfile format" stderr + test_grep "invalid gitfile format" stderr ) '
diff --git a/t/t0012-help.sh b/t/t0012-help.sh index c33501b..b0a3fdb 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh
@@ -131,8 +131,8 @@ test_expect_success 'git help --user-interfaces' ' git help --user-interfaces >help.output && - grep "^ attributes " help.output && - grep "^ mailmap " help.output + test_grep "^ attributes " help.output && + test_grep "^ mailmap " help.output ' test_expect_success 'git help -c' ' @@ -260,7 +260,7 @@ ( GIT_CEILING_DIRECTORIES=$(pwd) && export GIT_CEILING_DIRECTORIES && - test_expect_code 129 git -C sub $builtin -h >output 2>err + git -C sub $builtin -h >output 2>err ) && test_must_be_empty err && test_grep usage output
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh index ce3d812..3ea3169 100755 --- a/t/t0013-sha1dc.sh +++ b/t/t0013-sha1dc.sh
@@ -16,7 +16,7 @@ test_expect_success 'test-sha1 detects shattered pdf' ' test_must_fail test-tool sha1 <"$TEST_DATA/shattered-1.pdf" 2>err && test_grep collision err && - grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err + test_grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err ' test_done
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh index 32fe848..da02bce 100755 --- a/t/t0017-env-helper.sh +++ b/t/t0017-env-helper.sh
@@ -88,7 +88,7 @@ test_must_fail \ env HOME="$(pwd)/home" \ git config -l 2>err && - grep "exceeded maximum include depth" err && + test_grep "exceeded maximum include depth" err && # This validates that the assumption that we attempt to # read the configuration and fail very early in the start-up @@ -100,7 +100,7 @@ test-tool -C no-such-directory \ env-helper --type=bool --default=0 \ --exit-code GIT_TEST_ENV_HELPER 2>err && - grep "exceeded maximum include depth" err + test_grep "exceeded maximum include depth" err ' test_done
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 033b00a..6329150 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh
@@ -296,7 +296,7 @@ test_cmp expect actual ' -test_expect_success EXPENSIVE 'filter large file' ' +test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'filter large file' ' test_config filter.largefile.smudge cat && test_config filter.largefile.clean cat && test_seq -f "%1048576d" 1 2048 >2GB && @@ -731,7 +731,7 @@ rm -f debug.log && git checkout --quiet --no-progress . 2>git-stderr.log && - grep "smudge write error" git-stderr.log && + test_grep "smudge write error" git-stderr.log && test_grep "error: external filter" git-stderr.log && cat >expected.log <<-EOF && @@ -853,7 +853,7 @@ cp "$TEST_ROOT/test.o" test.r && test_must_fail git add . 2>git-stderr.log && - grep "expected git-filter-server" git-stderr.log + test_grep "expected git-filter-server" git-stderr.log ) ' @@ -970,7 +970,7 @@ rm -rf repo-cloned && test_must_fail git clone repo repo-cloned 2>git-stderr.log && - grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log + test_grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log ' test_expect_success 'invalid file in delayed checkout' ' @@ -991,7 +991,7 @@ rm -rf repo-cloned && test_must_fail git clone repo repo-cloned 2>git-stderr.log && - grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log + test_grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log ' for mode in 'case' 'utf-8' @@ -1032,7 +1032,7 @@ git clone $mode-collision $mode-collision-cloned && # Make sure z was really delayed - grep "IN: smudge $dir/z .* \\[DELAYED\\]" $mode-collision-cloned/delayed.log && + test_grep "IN: smudge $dir/z .* \\[DELAYED\\]" $mode-collision-cloned/delayed.log && # Should not create $dir/z at $symlink/z test_path_is_missing $mode-collision/target-dir/z @@ -1070,7 +1070,7 @@ git commit -m super && git checkout --recurse-submodules . && - grep "IN: smudge A/B/y .* \\[DELAYED\\]" delayed.log && + test_grep "IN: smudge A/B/y .* \\[DELAYED\\]" delayed.log && test_path_is_missing target-dir/y ) ' @@ -1161,9 +1161,9 @@ rm *.a && git checkout . 2>err && - grep "IN: smudge test-delay10.a .* \\[DELAYED\\]" delayed.log && - grep "IN: smudge test-delay11.a .* \\[DELAYED\\]" delayed.log && - grep "Updated 2 paths from the index" err + test_grep "IN: smudge test-delay10.a .* \\[DELAYED\\]" delayed.log && + test_grep "IN: smudge test-delay11.a .* \\[DELAYED\\]" delayed.log && + test_grep "Updated 2 paths from the index" err ) '
diff --git a/t/t0029-core-unsetenvvars.sh b/t/t0029-core-unsetenvvars.sh index baa1b7e..975620b 100755 --- a/t/t0029-core-unsetenvvars.sh +++ b/t/t0029-core-unsetenvvars.sh
@@ -20,10 +20,10 @@ HOBBES=Calvin && export HOBBES && git commit --allow-empty -m with 2>err && - grep Calvin err && + test_grep Calvin err && git -c core.unsetenvvars=FINDUS,HOBBES,CALVIN \ commit --allow-empty -m without 2>err && - ! grep Calvin err + test_grep ! Calvin err ' test_done
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index 43155f6..d6e2f63 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh
@@ -407,12 +407,12 @@ test_expect_success 'newline as commentchar is forbidden' ' test_must_fail git -c core.commentChar="$LF" stripspace -s 2>err && - grep "core.commentchar cannot contain newline" err + test_grep "core.commentchar cannot contain newline" err ' test_expect_success 'empty commentchar is forbidden' ' test_must_fail git -c core.commentchar= stripspace -s 2>err && - grep "core.commentchar must have at least one character" err + test_grep "core.commentchar must have at least one character" err ' test_expect_success '-c with single line' '
diff --git a/t/t0031-lockfile-pid.sh b/t/t0031-lockfile-pid.sh index 8ef87ad..e9e2f04 100755 --- a/t/t0031-lockfile-pid.sh +++ b/t/t0031-lockfile-pid.sh
@@ -29,7 +29,7 @@ test_must_fail git add . 2>err && # Should not crash, just show normal error without PID test_grep "Unable to create" err && - ! test_grep "is held by process" err + test_grep ! "is held by process" err ) '
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index ca55ea8..a22533f 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh
@@ -68,7 +68,7 @@ EOF test_expect_success 'test help' ' - test_must_fail test-tool parse-options -h >output 2>output.err && + test-tool parse-options -h >output 2>output.err && test_must_be_empty output.err && test_cmp expect output ' @@ -324,13 +324,13 @@ test_expect_success 'Alias options do not contribute to abbreviation' ' test-tool parse-options --alias-source 123 >output && - grep "^string: 123" output && + test_grep "^string: 123" output && test-tool parse-options --alias-target 123 >output && - grep "^string: 123" output && + test_grep "^string: 123" output && test_must_fail test-tool parse-options --alias && GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \ test-tool parse-options --alias 123 >output && - grep "^string: 123" output + test_grep "^string: 123" output ' cat >typo.err <<\EOF @@ -582,16 +582,16 @@ test_expect_success 'NO_INTERNAL_HELP works for -h' ' test_expect_code 129 test-tool parse-options-flags --no-internal-help cmd -h 2>err && - grep "^error: unknown switch \`h$SQ" err && - grep "^usage: " err + test_grep "^error: unknown switch \`h$SQ" err && + test_grep "^usage: " err ' for help_opt in help help-all do test_expect_success "NO_INTERNAL_HELP works for --$help_opt" " test_expect_code 129 test-tool parse-options-flags --no-internal-help cmd --$help_opt 2>err && - grep '^error: unknown option \`'$help_opt\' err && - grep '^usage: ' err + test_grep '^error: unknown option \`'$help_opt\' err && + test_grep '^usage: ' err " done @@ -608,38 +608,38 @@ test_expect_success 'subcommand - no subcommand shows error and usage' ' test_expect_code 129 test-tool parse-subcommand cmd 2>err && - grep "^error: need a subcommand" err && - grep ^usage: err + test_grep "^error: need a subcommand" err && + test_grep ^usage: err ' test_expect_success 'subcommand - subcommand after -- shows error and usage' ' test_expect_code 129 test-tool parse-subcommand cmd -- subcmd-one 2>err && - grep "^error: need a subcommand" err && - grep ^usage: err + test_grep "^error: need a subcommand" err && + test_grep ^usage: err ' test_expect_success 'subcommand - subcommand after --end-of-options shows error and usage' ' test_expect_code 129 test-tool parse-subcommand cmd --end-of-options subcmd-one 2>err && - grep "^error: need a subcommand" err && - grep ^usage: err + test_grep "^error: need a subcommand" err && + test_grep ^usage: err ' test_expect_success 'subcommand - unknown subcommand shows error and usage' ' test_expect_code 129 test-tool parse-subcommand cmd nope 2>err && - grep "^error: unknown subcommand: \`nope$SQ" err && - grep ^usage: err + test_grep "^error: unknown subcommand: \`nope$SQ" err && + test_grep ^usage: err ' test_expect_success 'subcommand - subcommands cannot be abbreviated' ' test_expect_code 129 test-tool parse-subcommand cmd subcmd-o 2>err && - grep "^error: unknown subcommand: \`subcmd-o$SQ$" err && - grep ^usage: err + test_grep "^error: unknown subcommand: \`subcmd-o$SQ$" err && + test_grep ^usage: err ' test_expect_success 'subcommand - no negated subcommands' ' test_expect_code 129 test-tool parse-subcommand cmd no-subcmd-one 2>err && - grep "^error: unknown subcommand: \`no-subcmd-one$SQ" err && - grep ^usage: err + test_grep "^error: unknown subcommand: \`no-subcmd-one$SQ" err && + test_grep ^usage: err ' test_expect_success 'subcommand - simple' ' @@ -709,8 +709,8 @@ test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given + unknown option' ' test_expect_code 129 test-tool parse-subcommand --subcommand-optional cmd --subcommand-opt 2>err && - grep "^error: unknown option" err && - grep ^usage: err + test_grep "^error: unknown option" err && + test_grep ^usage: err ' test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + subcommand not given + unknown option' ' @@ -778,28 +778,28 @@ test_expect_success 'subcommands are incompatible with STOP_AT_NON_OPTION' ' test_must_fail test-tool parse-subcommand --stop-at-non-option cmd subcmd-one 2>err && - grep ^BUG err + test_grep ^BUG err ' test_expect_success 'subcommands are incompatible with KEEP_UNKNOWN_OPT unless in combination with SUBCOMMAND_OPTIONAL' ' test_must_fail test-tool parse-subcommand --keep-unknown-opt cmd subcmd-two 2>err && - grep ^BUG err + test_grep ^BUG err ' test_expect_success 'subcommands are incompatible with KEEP_DASHDASH unless in combination with SUBCOMMAND_OPTIONAL' ' test_must_fail test-tool parse-subcommand --keep-dashdash cmd subcmd-two 2>err && - grep ^BUG err + test_grep ^BUG err ' test_expect_success 'negative unsigned' ' test_must_fail test-tool parse-options --unsigned -1 >out 2>err && - grep "non-negative integer" err && + test_grep "non-negative integer" err && test_must_be_empty out ' test_expect_success 'unsigned with units but no numbers' ' test_must_fail test-tool parse-options --unsigned m >out 2>err && - grep "non-negative integer" err && + test_grep "non-negative integer" err && test_must_be_empty out '
diff --git a/t/t0041-usage.sh b/t/t0041-usage.sh index a0f6f13..51af7cc 100755 --- a/t/t0041-usage.sh +++ b/t/t0041-usage.sh
@@ -13,7 +13,7 @@ test_expect_success 'tag --contains <existent_tag>' ' git tag --contains "v1.0" >actual 2>actual.err && - grep "v1.0" actual && + test_grep "v1.0" actual && test_line_count = 0 actual.err '
diff --git a/t/t0052-simple-ipc.sh b/t/t0052-simple-ipc.sh index ff98be3..14cea84 100755 --- a/t/t0052-simple-ipc.sh +++ b/t/t0052-simple-ipc.sh
@@ -33,19 +33,19 @@ test_expect_success 'big response' ' test-tool simple-ipc send --token=big >actual && test_line_count -ge 10000 actual && - grep -q "big: [0]*9999\$" actual + test_grep -q "big: [0]*9999\$" actual ' test_expect_success 'chunk response' ' test-tool simple-ipc send --token=chunk >actual && test_line_count -ge 10000 actual && - grep -q "big: [0]*9999\$" actual + test_grep -q "big: [0]*9999\$" actual ' test_expect_success 'slow response' ' test-tool simple-ipc send --token=slow >actual && test_line_count -ge 100 actual && - grep -q "big: [0]*99\$" actual + test_grep -q "big: [0]*99\$" actual ' # Send an IPC with n=100,000 bytes of ballast. This should be large enough @@ -54,7 +54,7 @@ # test_expect_success 'sendbytes' ' test-tool simple-ipc sendbytes --bytecount=100000 --byte=A >actual && - grep "sent:A00100000 rcvd:A00100000" actual + test_grep "sent:A00100000 rcvd:A00100000" actual ' # Start a series of <threads> client threads that each make <batchsize> @@ -93,7 +93,7 @@ --batchsize=13 \ >actual && test_line_count = 92 actual && - grep "good 91" actual && + test_grep "good 91" actual && grep "sent:A" <actual >actual_a && cat >expect_a <<-EOF && sent:A00000019 rcvd:A00000019
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 60cfe65..dc9d77b 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh
@@ -97,7 +97,7 @@ chmod -x hello.sh && test_must_fail test-tool run-command run-command ./hello.sh 2>err && - grep "fatal: cannot exec.*hello.sh" err + test_grep "fatal: cannot exec.*hello.sh" err ' test_expect_success POSIXPERM,SANITY 'unreadable directory in PATH' '
diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh index df3e9f5..9fbb41b 100755 --- a/t/t0066-dir-iterator.sh +++ b/t/t0066-dir-iterator.sh
@@ -127,7 +127,7 @@ test_expect_success SYMLINKS 'dir-iterator does not resolve top-level symlinks' ' test_must_fail test-tool dir-iterator ./dir5 >out && - grep "ENOTDIR" out + test_grep "ENOTDIR" out ' test_done
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh index 80b163e..55b37d8 100755 --- a/t/t0068-for-each-repo.sh +++ b/t/t0068-for-each-repo.sh
@@ -21,23 +21,23 @@ git for-each-repo --config=run.key commit --allow-empty -m "ran" && git -C one log -1 --pretty=format:%s >message && - grep ran message && + test_grep ran message && git -C two log -1 --pretty=format:%s >message && - ! grep ran message && + test_grep ! ran message && git -C three log -1 --pretty=format:%s >message && - grep ran message && + test_grep ran message && git -C ~/four log -1 --pretty=format:%s >message && - grep ran message && + test_grep ran message && git for-each-repo --config=run.key -- commit --allow-empty -m "ran again" && git -C one log -1 --pretty=format:%s >message && - grep again message && + test_grep again message && git -C two log -1 --pretty=format:%s >message && - ! grep again message && + test_grep ! again message && git -C three log -1 --pretty=format:%s >message && - grep again message && + test_grep again message && git -C ~/four log -1 --pretty=format:%s >message && - grep again message && + test_grep again message && git -C three for-each-repo --config=run.key -- \ commit --allow-empty -m "ran from worktree" &&
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh index 6b9dcf9..8f573c2 100755 --- a/t/t0070-fundamental.sh +++ b/t/t0070-fundamental.sh
@@ -10,7 +10,7 @@ test_expect_success 'mktemp to nonexistent directory prints filename' ' test_must_fail test-tool mktemp doesnotexist/testXXXXXX 2>err && - grep "doesnotexist/test" err + test_grep "doesnotexist/test" err ' test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' ' @@ -18,7 +18,7 @@ test_when_finished "chmod +w cannotwrite" && chmod -w cannotwrite && test_must_fail test-tool mktemp cannotwrite/testXXXXXX 2>err && - grep "cannotwrite/test" err + test_grep "cannotwrite/test" err ' test_expect_success 'git_mkstemps_mode does not fail if fd 0 is not open' ' @@ -33,7 +33,7 @@ test_expect_success 'incomplete sideband messages are reassembled' ' test-tool pkt-line send-split-sideband >split-sideband && test-tool pkt-line receive-sideband <split-sideband 2>err && - grep "Hello, world" err + test_grep "Hello, world" err ' test_expect_success 'eof on sideband message is reported' '
diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh index 26f0174..ff9d56f 100755 --- a/t/t0081-find-pack.sh +++ b/t/t0081-find-pack.sh
@@ -52,17 +52,17 @@ # HEAD^{tree} is in 2 packfiles test-tool find-pack HEAD^{tree} >head_tree_packs && - grep "$head_commit_pack" head_tree_packs && - grep mypackname1 head_tree_packs && - ! grep mypackname2 head_tree_packs && + test_grep "$head_commit_pack" head_tree_packs && + test_grep mypackname1 head_tree_packs && + test_grep ! mypackname2 head_tree_packs && test-tool find-pack --check-count 2 HEAD^{tree} && ! test-tool find-pack --check-count 1 HEAD^{tree} && # HEAD:five.t is also in 2 packfiles test-tool find-pack HEAD:five.t >five_packs && - grep "$head_commit_pack" five_packs && - ! grep mypackname1 five_packs && - grep mypackname2 five_packs && + test_grep "$head_commit_pack" five_packs && + test_grep ! mypackname1 five_packs && + test_grep mypackname2 five_packs && test-tool find-pack -c 2 HEAD:five.t && ! test-tool find-pack --check-count=0 HEAD:five.t '
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh index e38ca7a..81ed417 100755 --- a/t/t0091-bugreport.sh +++ b/t/t0091-bugreport.sh
@@ -40,15 +40,15 @@ # The beginning should match "git version --build-options" verbatim, # but rather than checking bit-for-bit equality, just test some basics. - grep "git version " system && - grep "shell-path: ." system && + test_grep "git version " system && + test_grep "shell-path: ." system && # After the version, there should be some more info. # This is bound to differ from environment to environment, # so we just do some rather high-level checks. - grep "uname: ." system && - grep "compiler info: ." system && - grep "zlib." system + test_grep "uname: ." system && + test_grep "compiler info: ." system && + test_grep "zlib." system ' test_expect_success 'dies if file with same name as report already exists' ' @@ -112,7 +112,7 @@ git bugreport --diagnose -o report -s test >out && zip_path=report/git-diagnostics-test.zip && - grep "Available space" out && + test_grep "Available space" out && test_path_is_file "$zip_path" && # Check zipped archive content @@ -120,10 +120,10 @@ test_file_not_empty out && "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out && - grep ".git/objects" out && + test_grep ".git/objects" out && "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out && - grep "^Total: [0-9][0-9]*" out && + test_grep "^Total: [0-9][0-9]*" out && # Should not include .git directory contents by default ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/" @@ -136,7 +136,7 @@ # Includes pack quantity/size info "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out && - grep ".git/objects" out && + test_grep ".git/objects" out && # Does not include .git directory contents ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
diff --git a/t/t0092-diagnose.sh b/t/t0092-diagnose.sh index 6cabd6e..0bc2b1e 100755 --- a/t/t0092-diagnose.sh +++ b/t/t0092-diagnose.sh
@@ -8,7 +8,7 @@ test_when_finished rm -rf report && git diagnose -o report -s test >out && - grep "Available space" out && + test_grep "Available space" out && zip_path=report/git-diagnostics-test.zip && test_path_is_file "$zip_path" && @@ -18,10 +18,10 @@ test_file_not_empty out && "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out && - grep ".git/objects" out && + test_grep ".git/objects" out && "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out && - grep "^Total: [0-9][0-9]*" out && + test_grep "^Total: [0-9][0-9]*" out && # Should not include .git directory contents by default ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/" @@ -34,7 +34,7 @@ git diagnose -o test-count -s 1 >out && zip_path=test-count/git-diagnostics-1.zip && "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out && - grep "^Total: [1-9][0-9]* loose objects" out + test_grep "^Total: [1-9][0-9]* loose objects" out ' test_expect_success UNZIP '--mode=stats excludes .git dir contents' ' @@ -45,7 +45,7 @@ # Includes pack quantity/size info zip_path=report/git-diagnostics-test.zip && "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out && - grep ".git/objects" out && + test_grep ".git/objects" out && # Does not include .git directory contents ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/" @@ -59,7 +59,7 @@ # Includes pack quantity/size info zip_path=report/git-diagnostics-test.zip && "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out && - grep ".git/objects" out && + test_grep ".git/objects" out && # Includes .git directory contents "$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
diff --git a/t/t0093-direct-index-write.pl b/t/t0093-direct-index-write.pl new file mode 100644 index 0000000..2881a3e --- /dev/null +++ b/t/t0093-direct-index-write.pl
@@ -0,0 +1,38 @@ +#!/usr/bin/perl +# +# Build a v2 index file from entries listed on stdin. +# Each line: "octalmode hex-oid name" +# Output: binary index written to stdout. +# +# This bypasses all D/F safety checks in add_index_entry(), simulating +# what happens when code uses ADD_CACHE_JUST_APPEND to bulk-load entries. +use strict; +use warnings; +use Digest::SHA qw(sha1 sha256); + +my $hash_algo = $ENV{'GIT_DEFAULT_HASH'} || 'sha1'; +my $hash_func = $hash_algo eq 'sha256' ? \&sha256 : \&sha1; + +my @entries; +while (my $line = <STDIN>) { + chomp $line; + my ($mode, $oid_hex, $name) = split(/ /, $line, 3); + push @entries, [$mode, $oid_hex, $name]; +} + +my $body = "DIRC" . pack("NN", 2, scalar @entries); + +for my $ent (@entries) { + my ($mode, $oid_hex, $name) = @{$ent}; + # 10 x 32-bit stat fields (zeroed), with mode in position 7 + my $stat = pack("N10", 0, 0, 0, 0, 0, 0, oct($mode), 0, 0, 0); + my $oid = pack("H*", $oid_hex); + my $flags = pack("n", length($name) & 0xFFF); + my $entry = $stat . $oid . $flags . $name . "\0"; + # Pad to 8-byte boundary + while (length($entry) % 8) { $entry .= "\0"; } + $body .= $entry; +} + +binmode STDOUT; +print $body . $hash_func->($body);
diff --git a/t/t0093-verify-cache-df-gap.sh b/t/t0093-verify-cache-df-gap.sh new file mode 100755 index 0000000..0b6829d --- /dev/null +++ b/t/t0093-verify-cache-df-gap.sh
@@ -0,0 +1,59 @@ +#!/bin/sh + +test_description='verify_cache() must catch non-adjacent D/F conflicts + +Ensure that verify_cache() can complain about bad entries like: + + docs <-- submodule + docs-internal/... <-- sorts here because "-" < "/" + docs/... <-- D/F conflict with "docs" above, not adjacent + +In order to test verify_cache, we directly construct a corrupt index +(bypassing the D/F safety checks in add_index_entry) and verify that +write-tree rejects it. +' + +. ./test-lib.sh + +if ! test_have_prereq PERL +then + skip_all='skipping verify_cache D/F tests; Perl not available' + test_done +fi + +# Build a v2 index from entries on stdin, bypassing D/F checks. +# Each line: "octalmode hex-oid name" (entries must be pre-sorted). +build_corrupt_index () { + perl "$TEST_DIRECTORY/t0093-direct-index-write.pl" >"$1" +} + +test_expect_success 'setup objects' ' + test_commit base && + BLOB=$(git rev-parse HEAD:base.t) && + SUB_COMMIT=$(git rev-parse HEAD) +' + +test_expect_success 'adjacent D/F conflict is caught by verify_cache' ' + cat >index-entries <<-EOF && + 0160000 $SUB_COMMIT docs + 0100644 $BLOB docs/requirements.txt + EOF + build_corrupt_index .git/index <index-entries && + + test_must_fail git write-tree 2>err && + test_grep "You have both docs and docs/requirements.txt" err +' + +test_expect_success 'non-adjacent D/F conflict is caught by verify_cache' ' + cat >index-entries <<-EOF && + 0160000 $SUB_COMMIT docs + 0100644 $BLOB docs-internal/README.md + 0100644 $BLOB docs/requirements.txt + EOF + build_corrupt_index .git/index <index-entries && + + test_must_fail git write-tree 2>err && + test_grep "You have both docs and docs/requirements.txt" err +' + +test_done
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh index dd5d9b4..87a9995 100755 --- a/t/t0100-previous.sh +++ b/t/t0100-previous.sh
@@ -50,7 +50,7 @@ git checkout main && git merge @{-1}~1 && git cat-file commit HEAD >actual && - grep "Merge branch '\''other'\''" actual + test_grep "Merge branch '\''other'\''" actual ' test_expect_success 'merge @{-100} before checking out that many branches yet' '
diff --git a/t/t0200-gettext-basic.sh b/t/t0200-gettext-basic.sh index 8853d8a..8db26c1 100755 --- a/t/t0200-gettext-basic.sh +++ b/t/t0200-gettext-basic.sh
@@ -16,7 +16,7 @@ ' test_expect_success 'xgettext sanity: Perl _() strings are not extracted' ' - ! grep "A Perl string xgettext will not get" "$GIT_PO_PATH"/is.po + test_grep ! "A Perl string xgettext will not get" "$GIT_PO_PATH"/is.po ' test_expect_success 'xgettext sanity: Comment extraction with --add-comments' ' @@ -26,8 +26,8 @@ ' test_expect_success 'xgettext sanity: Comment extraction with --add-comments stops at statements' ' - ! grep "This is a phony" "$GIT_PO_PATH"/is.po && - ! grep "the above comment" "$GIT_PO_PATH"/is.po + test_grep ! "This is a phony" "$GIT_PO_PATH"/is.po && + test_grep ! "the above comment" "$GIT_PO_PATH"/is.po ' test_expect_success GETTEXT 'sanity: $TEXTDOMAINDIR exists without NO_GETTEXT=YesPlease' ' @@ -44,10 +44,10 @@ test_expect_success GETTEXT_LOCALE 'sanity: gettext("") metadata is OK' ' # Return value may be non-zero LANGUAGE=is LC_ALL="$is_IS_locale" gettext "" >zero-expect && - grep "Project-Id-Version: Git" zero-expect && - grep "Git Mailing List <git@vger.kernel.org>" zero-expect && - grep "Content-Type: text/plain; charset=UTF-8" zero-expect && - grep "Content-Transfer-Encoding: 8bit" zero-expect + test_grep "Project-Id-Version: Git" zero-expect && + test_grep "Git Mailing List <git@vger.kernel.org>" zero-expect && + test_grep "Content-Type: text/plain; charset=UTF-8" zero-expect && + test_grep "Content-Transfer-Encoding: 8bit" zero-expect ' test_expect_success GETTEXT_LOCALE 'sanity: gettext(unknown) is passed through' '
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh index 0ce1f22..670082b 100755 --- a/t/t0203-gettext-setlocale-sanity.sh +++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -12,7 +12,7 @@ test_commit "iso-c-commit" iso-under-c && git show >out 2>err && test_must_be_empty err && - grep -q "iso-c-commit" out + test_grep -q "iso-c-commit" out ' test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 locale' ' @@ -20,7 +20,7 @@ test_commit "iso-utf8-commit" iso-under-utf8 && LANGUAGE=is LC_ALL="$is_IS_locale" git show >out 2>err && test_must_be_empty err && - grep -q "iso-utf8-commit" out + test_grep -q "iso-utf8-commit" out ' test_done
diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh index 28d92bb..48ccdda 100755 --- a/t/t0204-gettext-reencode-sanity.sh +++ b/t/t0204-gettext-reencode-sanity.sh
@@ -66,22 +66,22 @@ # eyes. test_expect_success GETTEXT_ISO_LOCALE 'gettext: Fetching a UTF-8 msgid -> ISO-8859-1' ' LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "TEST: ‘single’ and “double” quotes" >actual && - grep "einfaldar" actual && - grep "$(echo tvöfaldar | iconv -f UTF-8 -t ISO8859-1)" actual + test_grep "einfaldar" actual && + test_grep "$(echo tvöfaldar | iconv -f UTF-8 -t ISO8859-1)" actual ' test_expect_success GETTEXT_LOCALE 'gettext.c: git init UTF-8 -> UTF-8' ' printf "Bjó til tóma Git lind" >expect && LANGUAGE=is LC_ALL="$is_IS_locale" git init repo >actual && test_when_finished "rm -rf repo" && - grep "^$(cat expect) " actual + test_grep "^$(cat expect) " actual ' test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1' ' printf "Bjó til tóma Git lind" >expect && LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual && test_when_finished "rm -rf repo" && - grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual + test_grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual ' test_done
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh index 7e1e7af..beed2b2 100755 --- a/t/t0210-trace2-normal.sh +++ b/t/t0210-trace2-normal.sh
@@ -333,12 +333,12 @@ GIT_TRACE2="$(pwd)/trace.normal" \ git clone https://user:pwd@example.com/ clone && - ! grep user:pwd trace.normal && + test_grep ! user:pwd trace.normal && GIT_TRACE2_REDACT=0 GIT_TRACE2="$(pwd)/unredacted.normal" \ git clone https://user:pwd@example.com/ clone2 && - grep "start .* clone https://user:pwd@example.com" unredacted.normal && - grep "remote.origin.url=https://user:pwd@example.com" unredacted.normal + test_grep "start .* clone https://user:pwd@example.com" unredacted.normal && + test_grep "remote.origin.url=https://user:pwd@example.com" unredacted.normal ' test_done
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh index 760cf69..665c8b3 100755 --- a/t/t0211-trace2-perf.sh +++ b/t/t0211-trace2-perf.sh
@@ -283,13 +283,13 @@ GIT_TRACE2_PERF="$(pwd)/trace.perf" \ git clone https://user:pwd@example.com/ clone && - ! grep user:pwd trace.perf && + test_grep ! user:pwd trace.perf && GIT_TRACE2_REDACT=0 GIT_TRACE2_PERF="$(pwd)/unredacted.perf" \ git clone https://user:pwd@example.com/ clone2 && perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <unredacted.perf >actual && - grep "d0|main|start|.* clone https://user:pwd@example.com" actual && - grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual + test_grep "d0|main|start|.* clone https://user:pwd@example.com" actual && + test_grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual ' # Confirm that the requested command produces a "cmd_name" and a @@ -358,13 +358,13 @@ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual && - grep "d0|main|cmd_name|.*|_run_dashed_" actual && - grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + test_grep "d0|main|cmd_name|.*|_run_dashed_" actual && + test_grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && - grep "d1|main|cmd_name|.*|remote-curl" actual && - grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual + test_grep "d1|main|cmd_name|.*|remote-curl" actual && + test_grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual ' # Similarly, `git-http-fetch` is not built from git.c so do a @@ -389,13 +389,13 @@ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual && - grep "d0|main|cmd_name|.*|_run_dashed_" actual && - grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + test_grep "d0|main|cmd_name|.*|_run_dashed_" actual && + test_grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && - grep "d1|main|cmd_name|.*|http-fetch" actual && - grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual + test_grep "d1|main|cmd_name|.*|http-fetch" actual && + test_grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual ' # Historically, alias expansion explicitly emitted the def_param @@ -421,22 +421,22 @@ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual && # "git xxx" is first mapped to "git-xxx" and the child will fail. - grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && + test_grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && # We unpeel that and substitute "version" into "xxx" (giving # "git version") and update the cmd_name event. - grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual && + test_grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual && # These def_param events could be associated with either of the # above cmd_name events. It does not matter. - grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + test_grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && # The "git version" child sees a different cmd_name hierarchy. # Also test the def_param (only for completeness). - grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual && - grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual + test_grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual && + test_grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual ' test_expect_success 'expect def_params during shell alias expansion' ' @@ -456,25 +456,25 @@ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual && # "git xxx" is first mapped to "git-xxx" and the child will fail. - grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && + test_grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && # We unpeel that and substitute "git version" for "git xxx" (as a # shell command. Another cmd_name event is emitted as we unpeel. - grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual && + test_grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual && # These def_param events could be associated with either of the # above cmd_name events. It does not matter. - grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + test_grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && # We get the following only because we used a git command for the # shell command. In general, it could have been a shell script and # we would see nothing. # # The child knows the cmd_name hierarchy so it includes it. - grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual && - grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual + test_grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual && + test_grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual ' test_expect_success 'expect def_params during nested git alias expansion' ' @@ -496,33 +496,33 @@ # "git xxx" is first mapped to "git-xxx" and try to spawn "git-xxx" # and the child will fail. - grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && - grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual && + test_grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && + test_grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual && # We unpeel that and substitute "yyy" into "xxx" (giving "git yyy") # and spawn "git-yyy" and the child will fail. - grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual && - grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual && - grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual && + test_grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual && + test_grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual && + test_grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual && # We unpeel that and substitute "version" into "xxx" (giving # "git version") and update the cmd_name event. - grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual && - grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual && + test_grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual && + test_grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual && # These def_param events could be associated with any of the # above cmd_name events. It does not matter. grep "d0|main|def_param|.*|cfg.prop.foo:red" actual >actual.matches && - grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + test_grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && # However, we do not want them repeated each time we unpeel. test_line_count = 1 actual.matches && # The "git version" child sees a different cmd_name hierarchy. # Also test the def_param (only for completeness). - grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual && - grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && - grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual + test_grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual && + test_grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + test_grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual ' test_done
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh index 1211db9..f5358a1 100755 --- a/t/t0212-trace2-event.sh +++ b/t/t0212-trace2-event.sh
@@ -332,7 +332,7 @@ GIT_TRACE2_EVENT="$(pwd)/trace.event" \ test-tool trace2 300redact_start git clone https://user:pwd@example.com/ clone2 && - ! grep user:pwd trace.event + test_grep ! user:pwd trace.event ' test_expect_success 'unsafe URLs are redacted by default in child_start events' ' @@ -341,7 +341,7 @@ GIT_TRACE2_EVENT="$(pwd)/trace.event" \ test-tool trace2 301redact_child_start git clone https://user:pwd@example.com/ clone2 && - ! grep user:pwd trace.event + test_grep ! user:pwd trace.event ' test_expect_success 'unsafe URLs are redacted by default in exec events' ' @@ -350,7 +350,7 @@ GIT_TRACE2_EVENT="$(pwd)/trace.event" \ test-tool trace2 302redact_exec git clone https://user:pwd@example.com/ clone2 && - ! grep user:pwd trace.event + test_grep ! user:pwd trace.event ' test_expect_success 'unsafe URLs are redacted by default in def_param events' ' @@ -359,7 +359,7 @@ GIT_TRACE2_EVENT="$(pwd)/trace.event" \ test-tool trace2 303redact_def_param url https://user:pwd@example.com/ && - ! grep user:pwd trace.event + test_grep ! user:pwd trace.event ' test_done
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 64ead15..ea11cdb 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh
@@ -1014,7 +1014,7 @@ do git -c credential.$partial.helper=yep \ credential fill <stdin >stdout && - grep yep stdout || + test_grep yep stdout || return 1 done && @@ -1030,7 +1030,7 @@ do git -c credential.$partial.helper=yep \ credential fill <stdin >stdout && - ! grep yep stdout || + test_grep ! yep stdout || return 1 done &&
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index dff442d..788e9a1 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh
@@ -206,7 +206,7 @@ git -C repo cat-file -p "$HASH" 2>err && # Ensure that no spurious FETCH_HEAD messages are written - ! grep FETCH_HEAD err && + test_grep ! FETCH_HEAD err && # Ensure that the .promisor file is written, and check that its # associated packfile contains the object @@ -214,7 +214,7 @@ test_line_count = 1 promisorlist && IDX=$(sed "s/promisor$/idx/" promisorlist) && git verify-pack --verbose "$IDX" >out && - grep "$HASH" out + test_grep "$HASH" out ' test_expect_success 'fetching of a promised object that promisor remote no longer has' ' @@ -228,7 +228,7 @@ rm -rf unreliable-server/.git/objects/* && test_must_fail git -C unreliable-client checkout HEAD 2>err && - grep "could not fetch.*from promisor remote" err + test_grep "could not fetch.*from promisor remote" err ' test_expect_success 'fetching of missing objects works with ref-in-want enabled' ' @@ -240,7 +240,7 @@ rm -rf repo/.git/objects/* && rm -f trace && GIT_TRACE_PACKET="$(pwd)/trace" git -C repo cat-file -p "$HASH" && - grep "fetch< fetch=.*ref-in-want" trace + test_grep "fetch< fetch=.*ref-in-want" trace ' test_expect_success 'fetching from another promisor remote' ' @@ -263,7 +263,7 @@ test_line_count = 1 promisorlist && IDX=$(sed "s/promisor$/idx/" promisorlist) && git verify-pack --verbose "$IDX" >out && - grep "$HASH2" out + test_grep "$HASH2" out ' test_expect_success 'fetching with --filter configures a promisor remote' ' @@ -286,7 +286,7 @@ test_line_count = 1 promisorlist && IDX=$(sed "s/promisor$/idx/" promisorlist) && git verify-pack --verbose "$IDX" >out && - grep "$HASH3" out + test_grep "$HASH3" out ' test_expect_success 'fetching of missing blobs works' ' @@ -327,8 +327,8 @@ # Ensure that the tree, but not the blob, is fetched git -C repo rev-list --objects --missing=print $(cat treehash) >objects && - grep "^$(cat treehash)" objects && - grep "^[?]$(cat blobhash)" objects + test_grep "^$(cat treehash)" objects && + test_grep "^[?]$(cat blobhash)" objects ' test_expect_success 'rev-list stops traversal at missing and promised commit' ' @@ -343,8 +343,8 @@ git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && git -C repo rev-list --exclude-promisor-objects --objects bar >out && - grep $(git -C repo rev-parse bar) out && - ! grep $FOO out + test_grep $(git -C repo rev-parse bar) out && + test_grep ! $FOO out ' test_expect_success 'missing tree objects with --missing=allow-promisor and --exclude-promisor-objects' ' @@ -413,10 +413,10 @@ git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && git -C repo rev-list --exclude-promisor-objects --objects HEAD >out && - grep $(git -C repo rev-parse foo) out && - ! grep $TREE out && - grep $(git -C repo rev-parse HEAD) out && - ! grep $TREE2 out + test_grep $(git -C repo rev-parse foo) out && + test_grep ! $TREE out && + test_grep $(git -C repo rev-parse HEAD) out && + test_grep ! $TREE2 out ' test_expect_success 'rev-list stops traversal at missing and promised blob' ' @@ -432,8 +432,8 @@ git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && git -C repo rev-list --exclude-promisor-objects --objects HEAD >out && - grep $(git -C repo rev-parse HEAD) out && - ! grep $BLOB out + test_grep $(git -C repo rev-parse HEAD) out && + test_grep ! $BLOB out ' test_expect_success 'rev-list stops traversal at promisor commit, tree, and blob' ' @@ -451,10 +451,10 @@ git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && git -C repo rev-list --exclude-promisor-objects --objects HEAD >out && - ! grep $COMMIT out && - ! grep $TREE out && - ! grep $BLOB out && - grep $(git -C repo rev-parse bar) out # sanity check that some walking was done + test_grep ! $COMMIT out && + test_grep ! $TREE out && + test_grep ! $BLOB out && + test_grep $(git -C repo rev-parse bar) out # sanity check that some walking was done ' test_expect_success 'rev-list dies for missing objects on cmd line' ' @@ -489,6 +489,24 @@ done ' +test_expect_success '--exclude-promisor-objects with ^@ on missing object' ' + rm -rf repo && + test_create_repo repo && + test_commit -C repo foo && + test_commit -C repo bar && + + COMMIT=$(git -C repo rev-parse foo) && + promise_and_delete "$COMMIT" && + + git -C repo config core.repositoryformatversion 1 && + git -C repo config extensions.partialclone "arbitrary string" && + + # Ensure that "$COMMIT^@" is handled gracefully even though the + # actual commits are missing. + git -C repo rev-list --exclude-promisor-objects "$COMMIT^@" >out && + test_must_be_empty out +' + test_expect_success 'single promisor remote can be re-initialized gracefully' ' # ensure one promisor is in the promisors list rm -rf repo && @@ -523,10 +541,10 @@ test_line_count = 1 promisorlist && PROMISOR_PACKFILE=$(sed "s/.promisor/.pack/" <promisorlist) && git verify-pack $PROMISOR_PACKFILE -v >out && - grep "$TREE_ONE" out && - grep "$TREE_TWO" out && - ! grep "$(git -C repo rev-parse one)" out && - ! grep "$(git -C repo rev-parse two)" out && + test_grep "$TREE_ONE" out && + test_grep "$TREE_TWO" out && + test_grep ! "$(git -C repo rev-parse one)" out && + test_grep ! "$(git -C repo rev-parse two)" out && # Remove the promisor packfile and associated files rm $(sed "s/.promisor//" <promisorlist).* && @@ -536,10 +554,10 @@ ls repo/.git/objects/pack/pack-*.pack >packlist && test_line_count = 1 packlist && git verify-pack repo/.git/objects/pack/pack-*.pack -v >out && - grep "$(git -C repo rev-parse one)" out && - grep "$(git -C repo rev-parse two)" out && - ! grep "$TREE_ONE" out && - ! grep "$TREE_TWO" out + test_grep "$(git -C repo rev-parse one)" out && + test_grep "$(git -C repo rev-parse two)" out && + test_grep ! "$TREE_ONE" out && + test_grep ! "$TREE_TWO" out ' test_expect_success 'gc does not repack promisor objects if there are none' ' @@ -616,8 +634,8 @@ ls repo/.git/objects/pack/pack-*.pack >packlist && test_line_count = 1 packlist && git verify-pack repo/.git/objects/pack/pack-*.pack -v >out && - grep "$(git -C repo rev-parse HEAD)" out && - ! grep "$TREE_HASH" out + test_grep "$(git -C repo rev-parse HEAD)" out && + test_grep ! "$TREE_HASH" out ' test_expect_success 'do not fetch when checking existence of tree we construct ourselves' ' @@ -647,10 +665,10 @@ git clone --filter=blob:none --bare "file://$(pwd)/repo" partial.git && git -C partial.git rev-list --objects --missing=print HEAD >out && - grep "[?]$FILE_HASH" out && + test_grep "[?]$FILE_HASH" out && git -C partial.git log --follow -- new-file.txt && git -C partial.git rev-list --objects --missing=print HEAD >out && - grep "[?]$FILE_HASH" out + test_grep "[?]$FILE_HASH" out ' test_expect_success 'lazy-fetch when accessing object not in the_repository' ' @@ -665,7 +683,7 @@ # Sanity check that the file is missing git -C partial.git rev-list --objects --missing=print HEAD >out && - grep "[?]$FILE_HASH" out && + test_grep "[?]$FILE_HASH" out && # The no-lazy-fetch mechanism prevents Git from fetching test_must_fail env GIT_NO_LAZY_FETCH=1 \ @@ -680,7 +698,7 @@ # Sanity check that the file is still missing git -C partial.git rev-list --objects --missing=print HEAD >out && - grep "[?]$FILE_HASH" out && + test_grep "[?]$FILE_HASH" out && git -C full cat-file -s "$FILE_HASH" >expect && test-tool partial-clone object-info partial.git "$FILE_HASH" >actual && @@ -688,7 +706,7 @@ # Sanity check that the file is now present git -C partial.git rev-list --objects --missing=print HEAD >out && - ! grep "[?]$FILE_HASH" out + test_grep ! "[?]$FILE_HASH" out ' test_expect_success 'push should not fetch new commit objects' ' @@ -705,9 +723,9 @@ COMMIT=$(git -C server rev-parse server2) && test_must_fail git -C client push 2>err && - grep "fetch first" err && + test_grep "fetch first" err && git -C client rev-list --objects --missing=print "$COMMIT" >objects && - grep "^[?]$COMMIT" objects + test_grep "^[?]$COMMIT" objects ' test_expect_success 'setup for promisor.quiet tests' ' @@ -750,7 +768,7 @@ test_terminal git -C repo cat-file -p foo:foo.t 2>err && # Ensure that progress messages are written - grep "Receiving objects" err + test_grep "Receiving objects" err ' test_expect_success TTY 'promisor.quiet=true does not show progress messages' ' @@ -761,7 +779,7 @@ test_terminal git -C repo cat-file -p foo:foo.t 2>err && # Ensure that no progress messages are written - ! grep "Receiving objects" err + test_grep ! "Receiving objects" err ' test_expect_success TTY 'promisor.quiet=unconfigured shows progress messages' ' @@ -771,7 +789,7 @@ test_terminal git -C repo cat-file -p foo:foo.t 2>err && # Ensure that progress messages are written - grep "Receiving objects" err + test_grep "Receiving objects" err ' test_expect_success 'promisor.quiet from submodule repo is honored' ' @@ -819,7 +837,7 @@ test_line_count = 1 promisorlist && IDX=$(sed "s/promisor$/idx/" promisorlist) && git verify-pack --verbose "$IDX" >out && - grep "$HASH" out + test_grep "$HASH" out ' # DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh index 822b0d5..55c0fb3 100755 --- a/t/t0450-txt-doc-vs-help.sh +++ b/t/t0450-txt-doc-vs-help.sh
@@ -29,7 +29,7 @@ return 0 fi && mkdir -p "$out_dir" && - test_expect_code 129 git $builtin -h >"$out.raw" 2>&1 && + test_might_fail git $builtin -h >"$out.raw" 2>&1 && sed -n \ -e '1,/^$/ { /^$/d; @@ -87,7 +87,7 @@ # -h output assertions test_expect_success "$builtin -h output has no \t" ' h2s="$(help_to_synopsis "$builtin")" && - ! grep "$HT" "$h2s" + test_grep ! "$HT" "$h2s" ' test_expect_success "$builtin -h output has dashed labels" '
diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh index d1a498a..fe2232c 100755 --- a/t/t0500-progress-display.sh +++ b/t/t0500-progress-display.sh
@@ -320,8 +320,8 @@ # t0212/parse_events.perl intentionally omits regions and data. test_region progress "Working hard" trace.event && - grep "\"key\":\"total_objects\",\"value\":\"40\"" trace.event && - grep "\"key\":\"total_bytes\",\"value\":\"409600\"" trace.event + test_grep "\"key\":\"total_objects\",\"value\":\"40\"" trace.event && + test_grep "\"key\":\"total_bytes\",\"value\":\"409600\"" trace.event ' test_expect_success 'progress generates traces: stop / start' ' @@ -344,8 +344,8 @@ LSAN_OPTIONS=detect_leaks=0 \ test-tool progress \ <in 2>stderr && - grep region_enter.*progress trace-start.event && - ! grep region_leave.*progress trace-start.event + test_grep region_enter.*progress trace-start.event && + test_grep ! region_leave.*progress trace-start.event ' test_expect_success 'progress generates traces: stop without start' ' @@ -355,8 +355,8 @@ GIT_TRACE2_EVENT="$PWD/trace-stop.event" test-tool progress \ <in 2>stderr && - ! grep region_enter.*progress trace-stop.event && - ! grep region_leave.*progress trace-stop.event + test_grep ! region_enter.*progress trace-stop.event && + test_grep ! region_leave.*progress trace-stop.event ' test_expect_success 'progress generates traces: start with active progress bar (no stops)' ' @@ -369,9 +369,9 @@ LSAN_OPTIONS=detect_leaks=0 \ test-tool progress \ <in 2>stderr && - grep region_enter.*progress.*One trace-2start.event && - grep region_enter.*progress.*Two trace-2start.event && - ! grep region_leave trace-2start.event + test_grep region_enter.*progress.*One trace-2start.event && + test_grep region_enter.*progress.*Two trace-2start.event && + test_grep ! region_leave trace-2start.event ' test_done
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh index 74bfa2e..bbbf6fa 100755 --- a/t/t0600-reffiles-backend.sh +++ b/t/t0600-reffiles-backend.sh
@@ -519,4 +519,25 @@ test_cmp expect actual ' +test_expect_success SYMLINKS,!MINGW,!WITH_BREAKING_CHANGES 'core.preferSymlinkRefs can be set up via onbranch condition' ' + test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" && + test_when_finished "rm -f .git/include" && + git update-ref refs/heads/new @ && + cat >.git/include <<-\EOF && + [core] + preferSymlinkRefs = true + EOF + test_config includeIf.onbranch:"$(git branch --show-current)".path \ + "$(pwd)/.git/include" && + cat >stdin <<-EOF && + start + symref-create TEST_SYMREF_HEAD refs/heads/new + prepare + commit + EOF + git update-ref --no-deref --stdin <stdin && + test_path_is_symlink .git/TEST_SYMREF_HEAD && + test "$(test_readlink .git/TEST_SYMREF_HEAD)" = refs/heads/new +' + test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh index e19e036..35e98b4 100755 --- a/t/t0610-reftable-basics.sh +++ b/t/t0610-reftable-basics.sh
@@ -15,9 +15,9 @@ INVALID_OID=$(test_oid 001) test_expect_success 'pack-refs does not crash with -h' ' - test_expect_code 129 git pack-refs -h >usage && + git pack-refs -h >usage && test_grep "[Uu]sage: git pack-refs " usage && - test_expect_code 129 nongit git pack-refs -h >usage && + nongit git pack-refs -h >usage && test_grep "[Uu]sage: git pack-refs " usage ' @@ -776,11 +776,11 @@ test_commit file3 && test_commit file4 && git reflog >actual && - grep file3 actual && + test_grep file3 actual && git reflog delete HEAD@{1} && git reflog >actual && - ! grep file3 actual + test_grep ! file3 actual ) ' @@ -902,8 +902,8 @@ done && git reflog refs/heads/main >actual && test_line_count = 10 actual && - grep "commit (initial): number 1" actual && - grep "commit: number 10" actual && + test_grep "commit (initial): number 1" actual && + test_grep "commit: number 10" actual && git gc && git reflog refs/heads/main >actual &&
diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh index 26b716c..a65960d 100755 --- a/t/t0613-reftable-write-options.sh +++ b/t/t0613-reftable-write-options.sh
@@ -278,4 +278,23 @@ ) ' +test_expect_success 'write options can be set up via onbranch condition' ' + test_config_global core.logAllRefUpdates false && + test_when_finished "rm -rf repo" && + init_repo && + ( + cd repo && + test_commit A && + test_commit B && + cat >.git/include <<-\EOF && + [reftable] + blockSize = 123 + EOF + git config includeIf.onbranch:master.path "$(pwd)/.git/include" && + git refs optimize && + test-tool dump-reftable -b .git/reftable/*.ref >stats && + test_grep "block_size: 123" stats + ) +' + test_done
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh index 11bf104..70ba3ee 100755 --- a/t/t1004-read-tree-m-u-wf.sh +++ b/t/t1004-read-tree-m-u-wf.sh
@@ -142,8 +142,8 @@ echo >>file1 "local changes" && read_tree_u_must_succeed -m -u branch-point side-a side-b && - grep "new line to be kept" file1 && - grep "local changes" file1 + test_grep "new line to be kept" file1 && + test_grep "local changes" file1 ' @@ -156,8 +156,8 @@ echo >>file2 "local changes" && read_tree_u_must_fail -m -u branch-point side-a side-b && - ! grep "new line to be kept" file2 && - grep "local changes" file2 + test_grep ! "new line to be kept" file2 && + test_grep "local changes" file2 '
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 8e2c526..762c77c 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh
@@ -696,8 +696,8 @@ git repack -ad && git cat-file --batch-check="%(deltabase)" <blobs >actual && { - grep "$(git rev-parse HEAD:foo)" actual || - grep "$(git rev-parse HEAD:foo-plus)" actual + test_grep "$(git rev-parse HEAD:foo)" actual || + test_grep "$(git rev-parse HEAD:foo-plus)" actual } ' @@ -826,7 +826,7 @@ # Swap the two to corrupt the repository mv -f "$other_path" "$empty_path" && test_must_fail git fsck 2>err.fsck && - grep "hash-path mismatch" err.fsck && + test_grep "hash-path mismatch" err.fsck && # confirm that cat-file is reading the new swapped-in # blob... @@ -1318,37 +1318,37 @@ test_expect_success 'batch-command empty command' ' echo "" >cmd && test_expect_code 128 git cat-file --batch-command <cmd 2>err && - grep "^fatal:.*empty command in input.*" err + test_grep "^fatal:.*empty command in input.*" err ' test_expect_success 'batch-command whitespace before command' ' echo " info deadbeef" >cmd && test_expect_code 128 git cat-file --batch-command <cmd 2>err && - grep "^fatal:.*whitespace before command.*" err + test_grep "^fatal:.*whitespace before command.*" err ' test_expect_success 'batch-command unknown command' ' echo unknown_command >cmd && test_expect_code 128 git cat-file --batch-command <cmd 2>err && - grep "^fatal:.*unknown command.*" err + test_grep "^fatal:.*unknown command.*" err ' test_expect_success 'batch-command missing arguments' ' echo "info" >cmd && test_expect_code 128 git cat-file --batch-command <cmd 2>err && - grep "^fatal:.*info requires arguments.*" err + test_grep "^fatal:.*info requires arguments.*" err ' test_expect_success 'batch-command flush with arguments' ' echo "flush arg" >cmd && test_expect_code 128 git cat-file --batch-command --buffer <cmd 2>err && - grep "^fatal:.*flush takes no arguments.*" err + test_grep "^fatal:.*flush takes no arguments.*" err ' test_expect_success 'batch-command flush without --buffer' ' echo "flush" >cmd && test_expect_code 128 git cat-file --batch-command <cmd 2>err && - grep "^fatal:.*flush is only for --buffer mode.*" err + test_grep "^fatal:.*flush is only for --buffer mode.*" err ' perl_script='
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index de07629..463b38f 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh
@@ -49,6 +49,9 @@ example sha1:ddd3f836d3e3fbb7ae289aa9ae83536f76956399 example sha256:b44fe1fe65589848253737db859bd490453510719d7424daab03daf0767b85ae + + large5GB sha1:0be2be10a4c8764f32c4bf372a98edc731a4b204 + large5GB sha256:dc18ca621300c8d3cfa505a275641ebab00de189859e022a975056882d313e64 EOF ' @@ -202,7 +205,7 @@ test_expect_success 'too-short tree' ' echo abc >malformed-tree && test_must_fail git hash-object -t tree malformed-tree 2>err && - grep "too-short tree object" err + test_grep "too-short tree object" err ' test_expect_success PERL_TEST_HELPERS 'malformed mode in tree' ' @@ -210,7 +213,7 @@ bin_oid=$(echo $hex_oid | hex2oct) && printf "9100644 \0$bin_oid" >tree-with-malformed-mode && test_must_fail git hash-object -t tree tree-with-malformed-mode 2>err && - grep "malformed mode in tree entry" err + test_grep "malformed mode in tree entry" err ' test_expect_success PERL_TEST_HELPERS 'empty filename in tree' ' @@ -218,7 +221,7 @@ bin_oid=$(echo $hex_oid | hex2oct) && printf "100644 \0$bin_oid" >tree-with-empty-filename && test_must_fail git hash-object -t tree tree-with-empty-filename 2>err && - grep "empty filename in tree entry" err + test_grep "empty filename in tree entry" err ' test_expect_success PERL_TEST_HELPERS 'duplicate filename in tree' ' @@ -229,7 +232,7 @@ printf "100644 file\0$bin_oid" } >tree-with-duplicate-filename && test_must_fail git hash-object -t tree tree-with-duplicate-filename 2>err && - grep "duplicateEntries" err + test_grep "duplicateEntries" err ' test_expect_success 'corrupt commit' ' @@ -258,4 +261,40 @@ test_cmp expect actual ' +test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \ + 'files over 4GB hash literally' ' + test-tool genzeros $((5*1024*1024*1024)) >big && + test_oid large5GB >expect && + git hash-object --stdin --literally <big >actual && + test_cmp expect actual +' + +test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \ + 'files over 4GB hash correctly via --stdin' ' + { test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } && + test_oid large5GB >expect && + git hash-object --stdin <big >actual && + test_cmp expect actual +' + +test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \ + 'files over 4GB hash correctly' ' + { test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } && + test_oid large5GB >expect && + git hash-object -- big >actual && + test_cmp expect actual +' + +# This clean filter does nothing, other than excercising the interface. +# We ensure that cleaning doesn't mangle large files on 64-bit Windows. +test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \ + 'hash filtered files over 4GB correctly' ' + { test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } && + test_oid large5GB >expect && + test_config filter.null-filter.clean "cat" && + echo "big filter=null-filter" >.gitattributes && + git hash-object -- big >actual && + test_cmp expect actual +' + test_done
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 742f0fa..93244ee 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -196,7 +196,7 @@ echo dirty >init.t && read_tree_u_must_fail -m -u HEAD^ && test_path_is_file init.t && - grep -q dirty init.t + test_grep -q dirty init.t ' test_expect_success 'read-tree will not throw away dirty changes, sparse' ' @@ -207,7 +207,7 @@ echo sub/added >.git/info/sparse-checkout && read_tree_u_must_fail -m -u HEAD^ && test_path_is_file init.t && - grep -q dirty init.t + test_grep -q dirty init.t ' test_expect_success 'read-tree updates worktree, dirty case' ' @@ -215,7 +215,7 @@ git checkout -f top && echo dirty >init.t && read_tree_u_must_fail -m -u HEAD^ && - grep -q dirty init.t && + test_grep -q dirty init.t && rm init.t ' @@ -224,7 +224,7 @@ git checkout -f top && echo dirty >added && read_tree_u_must_succeed -m -u HEAD^ && - grep -q dirty added + test_grep -q dirty added ' test_expect_success 'read-tree adds to worktree, absent case' ' @@ -240,7 +240,7 @@ mkdir sub && echo dirty >sub/added && read_tree_u_must_succeed -u -m HEAD^ && - grep -q dirty sub/added + test_grep -q dirty sub/added ' test_expect_success 'index removal and worktree narrowing at the same time' '
diff --git a/t/t1050-large.sh b/t/t1050-large.sh index 7d40d08..d295c26 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh
@@ -8,7 +8,7 @@ test_expect_success 'core.bigFileThreshold must be non-negative' ' : >input && test_must_fail git -c core.bigFileThreshold=-1 hash-object input >out 2>err && - grep "bad numeric config value" err && + test_grep "bad numeric config value" err && test_must_be_empty out ' @@ -148,12 +148,12 @@ test_expect_success 'diff' ' git diff HEAD^ HEAD >actual && - grep "Binary files.*differ" actual + test_grep "Binary files.*differ" actual ' test_expect_success 'diff --cached' ' git diff --cached HEAD^ >actual && - grep "Binary files.*differ" actual + test_grep "Binary files.*differ" actual ' test_expect_success 'hash-object' '
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh index cd0aed9..74b1761 100755 --- a/t/t1091-sparse-checkout-builtin.sh +++ b/t/t1091-sparse-checkout-builtin.sh
@@ -129,7 +129,7 @@ git sparse-checkout add dir && git config --worktree core.sparseCheckoutCone true && test_must_fail git sparse-checkout add dir 2>err && - grep "existing sparse-checkout patterns do not use cone mode" err + test_grep "existing sparse-checkout patterns do not use cone mode" err ) ' @@ -803,7 +803,7 @@ # When an untracked file is in the way, all untracked files # (even ignored files) are preserved. git -C repo sparse-checkout set folder1 2>err && - grep "contains untracked files" err && + test_grep "contains untracked files" err && test_path_is_file repo/deep/deeper2/ignored.o && test_path_is_file repo/deep/deeper2/untracked && @@ -882,8 +882,8 @@ # of using the cone-mode translation to a set of directories. git -C repo sparse-checkout list >actual 2>err && test_cmp repo/.git/info/sparse-checkout actual && - grep "warning: your sparse-checkout file may have issues: pattern .* is repeated" err && - grep "warning: disabling cone pattern matching" err + test_grep "warning: your sparse-checkout file may have issues: pattern .* is repeated" err && + test_grep "warning: disabling cone pattern matching" err ' test_expect_success 'set from subdir pays attention to prefix' ' @@ -917,34 +917,34 @@ git -C repo sparse-checkout disable && test_must_fail git -C repo/deep sparse-checkout set --no-cone deeper2 ../folder1 2>error && - grep "run from the toplevel directory in non-cone mode" error + test_grep "run from the toplevel directory in non-cone mode" error ' test_expect_success 'set from subdir in non-cone mode throws an error' ' git -C repo sparse-checkout set --no-cone deep/deeper2 && test_must_fail git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 2>error && - grep "run from the toplevel directory in non-cone mode" error + test_grep "run from the toplevel directory in non-cone mode" error ' test_expect_success 'by default, cone mode will error out when passed files' ' git -C repo sparse-checkout reapply --cone && test_must_fail git -C repo sparse-checkout add .gitignore 2>error && - grep ".gitignore.*is not a directory" error + test_grep ".gitignore.*is not a directory" error ' test_expect_success 'error on mistyped command line options' ' test_must_fail git -C repo sparse-checkout add --sikp-checks .gitignore 2>error && - grep "unknown option.*sikp-checks" error + test_grep "unknown option.*sikp-checks" error ' test_expect_success 'by default, non-cone mode will warn on individual files' ' git -C repo sparse-checkout reapply --no-cone && git -C repo sparse-checkout add .gitignore 2>warning && - grep "pass a leading slash before paths.*if you want a single file" warning + test_grep "pass a leading slash before paths.*if you want a single file" warning ' test_expect_success 'setup bare repo' ' @@ -1108,11 +1108,11 @@ touch repo/folder1/extra/inside/file && test_must_fail git -C repo sparse-checkout clean 2>err && - grep "refusing to clean" err && + test_grep "refusing to clean" err && git -C repo config clean.requireForce true && test_must_fail git -C repo sparse-checkout clean 2>err && - grep "refusing to clean" err && + test_grep "refusing to clean" err && cat >expect <<-\EOF && Would remove deep/deeper2/ @@ -1255,7 +1255,7 @@ test_must_fail git merge -m "will-conflict" right && test_must_fail git sparse-checkout clean -f 2>err && - grep "failed to convert index to a sparse index" err && + test_grep "failed to convert index to a sparse index" err && echo merged >folder1/even/more/dirs/file && git add --sparse folder1 &&
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index 8186da5..9814431 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -454,10 +454,10 @@ run_on_sparse ../edit-contents folder1/a && run_on_sparse ../edit-contents folder1/newfile && test_sparse_match test_must_fail git add folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && test_sparse_match test_must_fail git add folder1/newfile && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/newfile ' @@ -509,13 +509,13 @@ # Adding the path outside of the sparse-checkout cone should fail. test_sparse_match test_must_fail git add folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && test_all_match git add --refresh folder1/a && test_must_be_empty sparse-checkout-err && test_sparse_unstaged folder1/a && test_sparse_match test_must_fail git add folder1/new && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/new && test_sparse_match git add --sparse folder1/a && test_sparse_match git add --sparse folder1/new && @@ -661,8 +661,8 @@ # in sparse-checkout or sparse-index. git -C full-checkout reset update-folder1 >full-checkout-out && test_sparse_match git reset update-folder1 && - grep "M folder1/a" full-checkout-out && - ! grep "M folder1/a" sparse-checkout-out && + test_grep "M folder1/a" full-checkout-out && + test_grep ! "M folder1/a" sparse-checkout-out && run_on_sparse test_path_is_missing folder1 ' @@ -880,8 +880,8 @@ # update-index will exit silently when provided with a directory name # containing a trailing slash test_all_match git update-index deep/ folder1/ && - grep "Ignoring path deep/" sparse-checkout-err && - grep "Ignoring path folder1/" sparse-checkout-err && + test_grep "Ignoring path deep/" sparse-checkout-err && + test_grep "Ignoring path folder1/" sparse-checkout-err && # When update-index is given a directory name WITHOUT a trailing slash, it will # behave in different ways depending on the status of the directory on disk: @@ -1067,7 +1067,7 @@ # 2. Add the file with conflict markers test_sparse_match test_must_fail git add folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && test_all_match git add --sparse folder1/a && test_all_match git status --porcelain=v2 && @@ -1076,7 +1076,7 @@ # accept conflict markers as resolved content. run_on_all mv folder2/a folder2/z && test_sparse_match test_must_fail git add folder2 && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder2/z && test_all_match git add --sparse folder2 && test_all_match git status --porcelain=v2 && @@ -1107,7 +1107,7 @@ # SKIP_WORKTREE bit from the index entry for folder1/a, we should # warn that this is a problematic add. test_sparse_match test_must_fail git add folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && test_all_match git add --sparse folder1/a && test_all_match git status --porcelain=v2 && @@ -1119,7 +1119,7 @@ # existing index entry with the SKIP_WORKTREE bit cleared. run_on_all mv folder2/a folder2/z && test_sparse_match test_must_fail git add folder2 && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder2/z && test_all_match git add --sparse folder2 && test_all_match git status --porcelain=v2 && @@ -1266,7 +1266,7 @@ run_on_all test_must_fail git checkout-index -f -- folder1/ && test_cmp full-checkout-err sparse-checkout-err && ! test_cmp full-checkout-err sparse-index-err && - grep "is a sparse directory" sparse-index-err + test_grep "is a sparse directory" sparse-index-err ' test_expect_success 'checkout-index --all' ' @@ -1374,8 +1374,8 @@ # having a submodule prevents "modules" from collapse test_sparse_match git sparse-checkout set deep/deeper1 && git -C sparse-index ls-files --sparse --stage >cache && - grep "100644 .* modules/a" cache && - grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache + test_grep "100644 .* modules/a" cache && + test_grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache ' test_expect_success 'git apply functionality' ' @@ -1392,7 +1392,7 @@ # Apply a patch to a file outside the sparse definition test_sparse_match test_must_fail git apply ../patch-outside && - grep "No such file or directory" sparse-checkout-err && + test_grep "No such file or directory" sparse-checkout-err && # But it works with --index and --cached test_all_match git apply --index --stat ../patch-outside && @@ -2013,9 +2013,9 @@ test_all_match git status --porcelain=v2 && test_sparse_match git ls-files -t && git -C sparse-checkout ls-files -t >actual && - grep -e "H deep/folder1/0/0/0" actual && - grep -e "H deep/folder1/0/1" actual && - grep -e "H deep/folder1/a" actual && + test_grep -e "H deep/folder1/0/0/0" actual && + test_grep -e "H deep/folder1/0/1" actual && + test_grep -e "H deep/folder1/a" actual && test_all_match git reset --hard && @@ -2025,8 +2025,8 @@ test_sparse_match git status --porcelain=v2 && test_sparse_match git ls-files -t && git -C sparse-checkout ls-files -t >actual && - grep -e "H deep/0/0/0" actual && - grep -e "H deep/0/1" actual + test_grep -e "H deep/0/0/0" actual && + test_grep -e "H deep/0/1" actual ' test_expect_success 'rm pathspec inside sparse definition' ' @@ -2517,7 +2517,7 @@ mkdir -p sparse-index/deep/deeper2/deepest && touch sparse-index/deep/deeper2/deepest/bogus && git -C sparse-index status 2>err && - grep "The sparse index is expanding to a full index" err && + test_grep "The sparse index is expanding to a full index" err && git -C sparse-index sparse-checkout disable 2>err && test_line_count = 0 err
diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 87ca11a..b99f782 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh
@@ -856,7 +856,7 @@ printf "[a] g = h\\n" } >y && test_must_fail git config ${mode_prefix}rename-section -f y a xyz 2>err && - grep "refusing to work with overly long line in .y. on line 2" err + test_grep "refusing to work with overly long line in .y. on line 2" err ' cat >> .git/config << EOF @@ -1671,9 +1671,9 @@ test_expect_success 'git --config-env with missing value' ' test_must_fail env ENVVAR=value git --config-env 2>error && - grep "no config key given for --config-env" error && + test_grep "no config key given for --config-env" error && test_must_fail env ENVVAR=value git --config-env config core.name 2>error && - grep "invalid config format: config" error + test_grep "invalid config format: config" error ' test_expect_success 'git --config-env fails with invalid parameters' ' @@ -2104,7 +2104,7 @@ key = true EOF git config ${mode_unset} two.key && - ! grep two .git/config && + test_grep ! two .git/config && q_to_tab >.git/config <<-\EOF && [one] @@ -2124,7 +2124,7 @@ Qkey = true EOF git config ${mode_unset} two.key && - grep two .git/config && + test_grep two .git/config && q_to_tab >.git/config <<-\EOF && [one] @@ -2655,7 +2655,7 @@ test_expect_success '--type=int requires at least one digit' ' test_must_fail git config --type int --default m some.key >out 2>error && - grep "bad numeric config value" error && + test_grep "bad numeric config value" error && test_must_be_empty out ' @@ -2967,12 +2967,12 @@ # test with any Git command test_must_fail git -C hasremoteurlTest status 2>err && - grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err + test_grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err ' test_expect_success 'negated mode causes failure' ' test_must_fail git config --no-get 2>err && - grep "unknown option \`no-get${SQ}" err + test_grep "unknown option \`no-get${SQ}" err ' test_expect_success 'specifying multiple modes causes failure' '
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index f389257..f611526 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh
@@ -353,7 +353,7 @@ git -C cycle --git-dir=. config include.path cycle && git config -f cycle/cycle include.path config && test_must_fail git -C cycle --git-dir=. config --get-all test.value 2>stderr && - grep "exceeded maximum include depth" stderr + test_grep "exceeded maximum include depth" stderr ' test_expect_success 'onbranch with unborn branch' '
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index e0e4905..de95161 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh
@@ -180,7 +180,7 @@ test_expect_success 'non parse-able integer value during iteration' ' check_config expect_code 128 git_config_int lamb.head 2>result && - grep "fatal: bad numeric config value .* in file \.git/config" result + test_grep "fatal: bad numeric config value .* in file \.git/config" result ' test_expect_success 'find bool value for the entered key' ' @@ -302,7 +302,7 @@ echo "Error (-1) reading configuration file a-directory." >expect && mkdir a-directory && test_expect_code 2 test-tool config configset_get_value foo.bar a-directory 2>output && - grep "^warning:" output && + test_grep "^warning:" output && grep "^Error" output >actual && test_cmp expect actual ' @@ -312,7 +312,7 @@ test_when_finished "chmod +r .git/config" && echo "Error (-1) reading configuration file .git/config." >expect && test_expect_code 2 test-tool config configset_get_value foo.bar .git/config 2>output && - grep "^warning:" output && + test_grep "^warning:" output && grep "^Error" output >actual && test_cmp expect actual '
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index f059d63..269fdaa 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh
@@ -92,7 +92,7 @@ git update-ref -m delete-$m -d $m && test_must_fail git show-ref --verify -q $m && test-tool ref-store main for-each-reflog-ent HEAD >actual && - grep "delete-$m$" actual + test_grep "delete-$m$" actual ' test_expect_success "deleting by HEAD adds message to HEAD's log" ' @@ -102,7 +102,7 @@ git update-ref -m delete-by-head -d HEAD && test_must_fail git show-ref --verify -q $m && test-tool ref-store main for-each-reflog-ent HEAD >actual && - grep "delete-by-head$" actual + test_grep "delete-by-head$" actual ' test_expect_success 'update-ref does not create reflogs by default' ' @@ -178,6 +178,18 @@ test_must_fail git reflog exists $outside ' +test_expect_success 'core.logAllRefUpdates can be set up via onbranch condition' ' + test_when_finished "git update-ref -d $outside" && + test_when_finished "rm -f .git/include" && + cat >.git/include <<-\EOF && + [core] + logAllRefUpdates = always + EOF + test_config includeIf.onbranch:main.path "$(pwd)/.git/include" && + git update-ref $outside $A && + git reflog exists $outside +' + test_expect_success "create $m (by HEAD)" ' git update-ref HEAD $A && test $A = $(git show-ref -s --verify $m) @@ -192,7 +204,10 @@ test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" ' test_when_finished "git update-ref -d $m" && git update-ref -d HEAD $B && - ! grep "$m" .git/packed-refs && + if test_have_prereq REFFILES + then + test_grep ! "$m" .git/packed-refs + fi && test_must_fail git show-ref --verify -q $m ' @@ -575,103 +590,103 @@ test_expect_success 'stdin fails on empty line' ' echo "" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: empty command in input" err + test_grep "fatal: empty command in input" err ' test_expect_success 'stdin fails on only whitespace' ' echo " " >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: whitespace before command: " err + test_grep "fatal: whitespace before command: " err ' test_expect_success 'stdin fails on leading whitespace' ' echo " create $a $m" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: whitespace before command: create $a $m" err + test_grep "fatal: whitespace before command: create $a $m" err ' test_expect_success 'stdin fails on unknown command' ' echo "unknown $a" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: unknown command: unknown $a" err + test_grep "fatal: unknown command: unknown $a" err ' test_expect_success 'stdin fails on unbalanced quotes' ' echo "create $a \"main" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: badly quoted argument: \\\"main" err + test_grep "fatal: badly quoted argument: \\\"main" err ' test_expect_success 'stdin fails on invalid escape' ' echo "create $a \"ma\zn\"" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: badly quoted argument: \\\"ma\\\\zn\\\"" err + test_grep "fatal: badly quoted argument: \\\"ma\\\\zn\\\"" err ' test_expect_success 'stdin fails on junk after quoted argument' ' echo "create \"$a\"main" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: unexpected character after quoted argument: \\\"$a\\\"main" err + test_grep "fatal: unexpected character after quoted argument: \\\"$a\\\"main" err ' test_expect_success 'stdin fails create with no ref' ' echo "create " >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: create: missing <ref>" err + test_grep "fatal: create: missing <ref>" err ' test_expect_success 'stdin fails create with no new value' ' echo "create $a" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: create $a: missing <new-oid>" err + test_grep "fatal: create $a: missing <new-oid>" err ' test_expect_success 'stdin fails create with too many arguments' ' echo "create $a $m $m" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: create $a: extra input: $m" err + test_grep "fatal: create $a: extra input: $m" err ' test_expect_success 'stdin fails update with no ref' ' echo "update " >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: update: missing <ref>" err + test_grep "fatal: update: missing <ref>" err ' test_expect_success 'stdin fails update with no new value' ' echo "update $a" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: update $a: missing <new-oid>" err + test_grep "fatal: update $a: missing <new-oid>" err ' test_expect_success 'stdin fails update with too many arguments' ' echo "update $a $m $m $m" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: update $a: extra input: $m" err + test_grep "fatal: update $a: extra input: $m" err ' test_expect_success 'stdin fails delete with no ref' ' echo "delete " >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: delete: missing <ref>" err + test_grep "fatal: delete: missing <ref>" err ' test_expect_success 'stdin fails delete with too many arguments' ' echo "delete $a $m $m" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: delete $a: extra input: $m" err + test_grep "fatal: delete $a: extra input: $m" err ' test_expect_success 'stdin fails verify with too many arguments' ' echo "verify $a $m $m" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: verify $a: extra input: $m" err + test_grep "fatal: verify $a: extra input: $m" err ' test_expect_success 'stdin fails option with unknown name' ' echo "option unknown" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: option unknown: unknown" err + test_grep "fatal: option unknown: unknown" err ' test_expect_success 'stdin fails with duplicate refs' ' @@ -759,28 +774,28 @@ test_expect_success 'stdin update ref fails with wrong old value' ' echo "update $c $m $m~1" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$c'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$c'"'"'" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin update ref fails with bad old value' ' echo "update $c $m does-not-exist" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: update $c: invalid <old-oid>: does-not-exist" err && + test_grep "fatal: update $c: invalid <old-oid>: does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin create ref fails with bad new value' ' echo "create $c does-not-exist" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: create $c: invalid <new-oid>: does-not-exist" err && + test_grep "fatal: create $c: invalid <new-oid>: does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin create ref fails with zero new value' ' echo "create $c " >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: create $c: zero <new-oid>" err && + test_grep "fatal: create $c: zero <new-oid>" err && test_must_fail git rev-parse --verify -q $c ' @@ -795,7 +810,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' ' echo "delete $a $m~1" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$a'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$a'"'"'" err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual @@ -804,7 +819,7 @@ test_expect_success 'stdin delete ref fails with zero old value' ' echo "delete $a " >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: delete $a: zero <old-oid>" err && + test_grep "fatal: delete $a: zero <old-oid>" err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual @@ -965,7 +980,7 @@ update $c '' EOF test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$c'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$c'"'"'" err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual && @@ -998,123 +1013,123 @@ test_expect_success 'stdin -z fails on empty line' ' echo "" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: whitespace before command: " err + test_grep "fatal: whitespace before command: " err ' test_expect_success 'stdin -z fails on empty command' ' printf $F "" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: empty command in input" err + test_grep "fatal: empty command in input" err ' test_expect_success 'stdin -z fails on only whitespace' ' printf $F " " >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: whitespace before command: " err + test_grep "fatal: whitespace before command: " err ' test_expect_success 'stdin -z fails on leading whitespace' ' printf $F " create $a" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: whitespace before command: create $a" err + test_grep "fatal: whitespace before command: create $a" err ' test_expect_success 'stdin -z fails on unknown command' ' printf $F "unknown $a" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: unknown command: unknown $a" err + test_grep "fatal: unknown command: unknown $a" err ' test_expect_success 'stdin -z fails create with no ref' ' printf $F "create " >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: create: missing <ref>" err + test_grep "fatal: create: missing <ref>" err ' test_expect_success 'stdin -z fails create with no new value' ' printf $F "create $a" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: create $a: unexpected end of input when reading <new-oid>" err + test_grep "fatal: create $a: unexpected end of input when reading <new-oid>" err ' test_expect_success 'stdin -z fails create with too many arguments' ' printf $F "create $a" "$m" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: unknown command: $m" err + test_grep "fatal: unknown command: $m" err ' test_expect_success 'stdin -z fails update with no ref' ' printf $F "update " >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: update: missing <ref>" err + test_grep "fatal: update: missing <ref>" err ' test_expect_success 'stdin -z fails update with too few args' ' printf $F "update $a" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: update $a: unexpected end of input when reading <old-oid>" err + test_grep "fatal: update $a: unexpected end of input when reading <old-oid>" err ' test_expect_success 'stdin -z emits warning with empty new value' ' git update-ref $a $m && printf $F "update $a" "" "" >stdin && git update-ref -z --stdin <stdin 2>err && - grep "warning: update $a: missing <new-oid>, treating as zero" err && + test_grep "warning: update $a: missing <new-oid>, treating as zero" err && test_must_fail git rev-parse --verify -q $a ' test_expect_success 'stdin -z fails update with no new value' ' printf $F "update $a" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: update $a: unexpected end of input when reading <new-oid>" err + test_grep "fatal: update $a: unexpected end of input when reading <new-oid>" err ' test_expect_success 'stdin -z fails update with no old value' ' printf $F "update $a" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: update $a: unexpected end of input when reading <old-oid>" err + test_grep "fatal: update $a: unexpected end of input when reading <old-oid>" err ' test_expect_success 'stdin -z fails update with too many arguments' ' printf $F "update $a" "$m" "$m" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: unknown command: $m" err + test_grep "fatal: unknown command: $m" err ' test_expect_success 'stdin -z fails delete with no ref' ' printf $F "delete " >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: delete: missing <ref>" err + test_grep "fatal: delete: missing <ref>" err ' test_expect_success 'stdin -z fails delete with no old value' ' printf $F "delete $a" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: delete $a: unexpected end of input when reading <old-oid>" err + test_grep "fatal: delete $a: unexpected end of input when reading <old-oid>" err ' test_expect_success 'stdin -z fails delete with too many arguments' ' printf $F "delete $a" "$m" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: unknown command: $m" err + test_grep "fatal: unknown command: $m" err ' test_expect_success 'stdin -z fails verify with too many arguments' ' printf $F "verify $a" "$m" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: unknown command: $m" err + test_grep "fatal: unknown command: $m" err ' test_expect_success 'stdin -z fails verify with no old value' ' printf $F "verify $a" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: verify $a: unexpected end of input when reading <old-oid>" err + test_grep "fatal: verify $a: unexpected end of input when reading <old-oid>" err ' test_expect_success 'stdin -z fails option with unknown name' ' printf $F "option unknown" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: option unknown: unknown" err + test_grep "fatal: option unknown: unknown" err ' test_expect_success 'stdin -z fails with duplicate refs' ' @@ -1160,14 +1175,14 @@ test_expect_success 'stdin -z update ref fails with wrong old value' ' printf $F "update $c" "$m" "$m~1" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$c'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$c'"'"'" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin -z update ref fails with bad old value' ' printf $F "update $c" "$m" "does-not-exist" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: update $c: invalid <old-oid>: does-not-exist" err && + test_grep "fatal: update $c: invalid <old-oid>: does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' @@ -1176,7 +1191,7 @@ git rev-parse "$c" >expect && printf $F "create $c" "$m~1" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$c'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$c'"'"'" err && git rev-parse "$c" >actual && test_cmp expect actual ' @@ -1185,28 +1200,28 @@ git update-ref -d "$c" && printf $F "create $c" "does-not-exist" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: create $c: invalid <new-oid>: does-not-exist" err && + test_grep "fatal: create $c: invalid <new-oid>: does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin -z create ref fails with empty new value' ' printf $F "create $c" "" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: create $c: missing <new-oid>" err && + test_grep "fatal: create $c: missing <new-oid>" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin -z create ref fails with non commit object' ' printf $F "create $c" "$(test_oid 001)" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: trying to write ref ${SQ}$c${SQ} with nonexistent object" err && + test_grep "fatal: trying to write ref ${SQ}$c${SQ} with nonexistent object" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin -z update ref fails with non commit object' ' printf $F "update $b" "$(test_oid 001)" "" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: trying to write ref ${SQ}$b${SQ} with nonexistent object" err && + test_grep "fatal: trying to write ref ${SQ}$b${SQ} with nonexistent object" err && test_must_fail git rev-parse --verify -q $c ' @@ -1221,7 +1236,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' ' printf $F "delete $a" "$m~1" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$a'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$a'"'"'" err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual @@ -1230,7 +1245,7 @@ test_expect_success 'stdin -z delete ref fails with zero old value' ' printf $F "delete $a" "$Z" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: delete $a: zero <old-oid>" err && + test_grep "fatal: delete $a: zero <old-oid>" err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual @@ -1336,7 +1351,7 @@ git update-ref $c $m && printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$m" "$Z" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: cannot lock ref '"'"'$c'"'"'" err && + test_grep "fatal: cannot lock ref '"'"'$c'"'"'" err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual && @@ -1415,13 +1430,13 @@ cd worktree && git commit --allow-empty -m "test commit" && git for-each-ref >for-each-ref.out && - ! grep refs/bisect for-each-ref.out && + test_grep ! refs/bisect for-each-ref.out && git update-ref refs/bisect/something HEAD && git rev-parse refs/bisect/something >../worktree-head && git for-each-ref | grep refs/bisect/something ) && git show-ref >actual && - ! grep 'refs/bisect' actual && + test_grep ! 'refs/bisect' actual && test_must_fail git rev-parse refs/bisect/something && git update-ref refs/bisect/something HEAD && git rev-parse refs/bisect/something >main-head && @@ -1477,7 +1492,7 @@ test_must_fail git update-ref --stdin <stdin >actual 2>err && printf "%s: ok\n" abort >expect && test_cmp expect actual && - grep "fatal: transaction is closed" err + test_grep "fatal: transaction is closed" err ' test_expect_success 'transaction exits on start after prepare' ' @@ -1488,7 +1503,7 @@ test_must_fail git update-ref --stdin <stdin 2>err >actual && printf "%s: ok\n" prepare >expect && test_cmp expect actual && - grep "fatal: prepared transactions can only be closed" err + test_grep "fatal: prepared transactions can only be closed" err ' test_expect_success 'transaction handles empty abort with missing prepare' ' @@ -1649,7 +1664,7 @@ # This must now fail given that we have locked the ref. test_must_fail git update-ref refs/heads/flush $B 2>stderr && - grep "fatal: update_ref failed for ref ${SQ}refs/heads/flush${SQ}: cannot lock ref" stderr && + test_grep "fatal: update_ref failed for ref ${SQ}refs/heads/flush${SQ}: cannot lock ref" stderr && echo commit >&9 && echo "commit: ok" >expected && @@ -1675,7 +1690,7 @@ git symbolic-ref refs/heads/symref $a && format_command $type "symref-verify refs/heads/symref" "$a" >stdin && test_must_fail git update-ref --stdin $type <stdin 2>err && - grep "fatal: symref-verify: cannot operate with deref mode" err + test_grep "fatal: symref-verify: cannot operate with deref mode" err ' test_expect_success "stdin $type symref-verify fails with too many arguments" ' @@ -1683,9 +1698,9 @@ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && if test "$type" = "-z" then - grep "fatal: unknown command: $a" err + test_grep "fatal: unknown command: $a" err else - grep "fatal: symref-verify refs/heads/symref: extra input: $a" err + test_grep "fatal: symref-verify refs/heads/symref: extra input: $a" err fi ' @@ -1718,7 +1733,7 @@ test-tool ref-store main for-each-reflog-ent refs/heads/symref >before && format_command $type "symref-verify refs/heads/missing" "refs/heads/unknown" >stdin && test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && - grep "fatal: cannot lock ref ${SQ}refs/heads/missing${SQ}: unable to resolve reference ${SQ}refs/heads/missing${SQ}" err && + test_grep "fatal: cannot lock ref ${SQ}refs/heads/missing${SQ}: unable to resolve reference ${SQ}refs/heads/missing${SQ}" err && test_must_fail git rev-parse --verify -q refs/heads/missing && test-tool ref-store main for-each-reflog-ent refs/heads/symref >after && test_cmp before after @@ -1744,13 +1759,13 @@ git symbolic-ref refs/heads/symref $a && format_command $type "symref-delete refs/heads/symref" "$a" >stdin && test_must_fail git update-ref --stdin $type <stdin 2>err && - grep "fatal: symref-delete: cannot operate with deref mode" err + test_grep "fatal: symref-delete: cannot operate with deref mode" err ' test_expect_success "stdin $type symref-delete fails with no ref" ' format_command $type "symref-delete " >stdin && test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && - grep "fatal: symref-delete: missing <ref>" err + test_grep "fatal: symref-delete: missing <ref>" err ' test_expect_success "stdin $type symref-delete fails deleting regular ref" ' @@ -1758,7 +1773,7 @@ git update-ref refs/heads/regularref $a && format_command $type "symref-delete refs/heads/regularref" "$a" >stdin && test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && - grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: expected symref with target ${SQ}$a${SQ}: but is a regular ref" err + test_grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: expected symref with target ${SQ}$a${SQ}: but is a regular ref" err ' test_expect_success "stdin $type symref-delete fails with too many arguments" ' @@ -1766,16 +1781,16 @@ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && if test "$type" = "-z" then - grep "fatal: unknown command: $a" err + test_grep "fatal: unknown command: $a" err else - grep "fatal: symref-delete refs/heads/symref: extra input: $a" err + test_grep "fatal: symref-delete refs/heads/symref: extra input: $a" err fi ' test_expect_success "stdin $type symref-delete fails with wrong old value" ' format_command $type "symref-delete refs/heads/symref" "$m" >stdin && test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && - grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err && + test_grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err && git symbolic-ref refs/heads/symref >expect && echo $a >actual && test_cmp expect actual @@ -1813,9 +1828,9 @@ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && if test "$type" = "-z" then - grep "fatal: unknown command: $a" err + test_grep "fatal: unknown command: $a" err else - grep "fatal: symref-create refs/heads/symref: extra input: $a" err + test_grep "fatal: symref-create refs/heads/symref: extra input: $a" err fi ' @@ -1878,16 +1893,16 @@ test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && if test "$type" = "-z" then - grep "fatal: unknown command: $a" err + test_grep "fatal: unknown command: $a" err else - grep "fatal: symref-update refs/heads/symref: extra input: $a" err + test_grep "fatal: symref-update refs/heads/symref: extra input: $a" err fi ' test_expect_success "stdin $type symref-update fails with wrong old value argument" ' format_command $type "symref-update refs/heads/symref" "$a" "foo" "$a" "$a" >stdin && test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && - grep "fatal: symref-update refs/heads/symref: invalid arg ${SQ}foo${SQ} for old value" err + test_grep "fatal: symref-update refs/heads/symref: invalid arg ${SQ}foo${SQ} for old value" err ' test_expect_success "stdin $type symref-update creates with zero old value" ' @@ -1923,7 +1938,7 @@ git symbolic-ref refs/heads/symref $a && format_command $type "symref-update refs/heads/symref" "$m" "ref" "$b" >stdin && test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err && - grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err && + test_grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err && test_must_fail git rev-parse --verify -q $c ' @@ -1998,7 +2013,7 @@ git symbolic-ref --no-recurse refs/heads/symref >actual && test_cmp expect actual && test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual && - grep "$Z $(git rev-parse $a)" actual + test_grep "$Z $(git rev-parse $a)" actual ' test_expect_success "stdin $type symref-update regular ref to symref with correct old-oid" ' @@ -2010,7 +2025,7 @@ git symbolic-ref --no-recurse refs/heads/regularref >actual && test_cmp expect actual && test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual && - grep "$(git rev-parse $a) $(git rev-parse $a)" actual + test_grep "$(git rev-parse $a) $(git rev-parse $a)" actual ' test_expect_success "stdin $type symref-update regular ref to symref fails with wrong old-oid" ' @@ -2018,7 +2033,7 @@ git update-ref --no-deref refs/heads/regularref $a && format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse refs/heads/target2)" >stdin && test_must_fail git update-ref --stdin $type <stdin 2>err && - grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: is at $(git rev-parse $a) but expected $(git rev-parse refs/heads/target2)" err && + test_grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: is at $(git rev-parse $a) but expected $(git rev-parse refs/heads/target2)" err && echo $(git rev-parse $a) >expect && git rev-parse refs/heads/regularref >actual && test_cmp expect actual @@ -2029,7 +2044,7 @@ git update-ref --no-deref refs/heads/regularref $a && format_command $type "symref-update refs/heads/regularref" "$a" "oid" "not-a-ref-oid" >stdin && test_must_fail git update-ref --stdin $type <stdin 2>err && - grep "fatal: symref-update refs/heads/regularref: invalid oid: not-a-ref-oid" err && + test_grep "fatal: symref-update refs/heads/regularref: invalid oid: not-a-ref-oid" err && echo $(git rev-parse $a) >expect && git rev-parse refs/heads/regularref >actual && test_cmp expect actual @@ -2040,7 +2055,7 @@ git symbolic-ref refs/heads/symref refs/heads/target2 && format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin && test_must_fail git update-ref --stdin $type <stdin 2>err && - grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: reference already exists" err && + test_grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: reference already exists" err && echo refs/heads/target2 >expect && git symbolic-ref refs/heads/symref >actual && test_cmp expect actual @@ -2060,7 +2075,7 @@ git symbolic-ref --no-recurse refs/heads/symref >actual && test_cmp expect actual && test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual && - grep "$(git rev-parse $a) $(git rev-parse $a)" actual + test_grep "$(git rev-parse $a) $(git rev-parse $a)" actual ' test_expect_success "stdin $type symref-update regular ref to symref" ' @@ -2072,7 +2087,7 @@ git symbolic-ref --no-recurse refs/heads/regularref >actual && test_cmp expect actual && test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual && - grep "$(git rev-parse $a) $(git rev-parse $a)" actual + test_grep "$(git rev-parse $a) $(git rev-parse $a)" actual ' test_expect_success "stdin $type batch-updates" '
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh index 36c903c..226b3ec 100755 --- a/t/t1403-show-ref.sh +++ b/t/t1403-show-ref.sh
@@ -165,7 +165,7 @@ ' test_expect_success 'show-ref --heads is deprecated and hidden' ' - test_expect_code 129 git show-ref -h >short-help && + git show-ref -h >short-help && test_grep ! -e --heads short-help && git show-ref --heads >actual 2>warning && test_grep ! deprecated warning && @@ -213,19 +213,19 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' ' test_must_fail git show-ref --verify --exclude-existing 2>err && - grep "verify" err && - grep "exclude-existing" err && - grep "cannot be used together" err && + test_grep "verify" err && + test_grep "exclude-existing" err && + test_grep "cannot be used together" err && test_must_fail git show-ref --verify --exists 2>err && - grep "verify" err && - grep "exists" err && - grep "cannot be used together" err && + test_grep "verify" err && + test_grep "exists" err && + test_grep "cannot be used together" err && test_must_fail git show-ref --exclude-existing --exists 2>err && - grep "exclude-existing" err && - grep "exists" err && - grep "cannot be used together" err + test_grep "exclude-existing" err && + test_grep "exists" err && + test_grep "cannot be used together" err ' test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index ce71f9a..8f78cf4 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh
@@ -107,13 +107,13 @@ ' test_expect_success 'correct usage on sub-command -h' ' - test_expect_code 129 git reflog expire -h >err && - grep "git reflog expire" err + git reflog expire -h >err && + test_grep "git reflog expire" err ' test_expect_success 'correct usage on "git reflog show -h"' ' - test_expect_code 129 git reflog show -h >err && - grep -F "git reflog [show]" err + git reflog show -h >err && + test_grep -F "git reflog [show]" err ' test_expect_success 'pass through -- to sub-command' ' @@ -244,30 +244,22 @@ test_tick && git commit -m tiger C && - HEAD_entry_count=$(git reflog | wc -l) && - main_entry_count=$(git reflog show main | wc -l) && - - test $HEAD_entry_count = 5 && - test $main_entry_count = 5 && - + test_stdout_line_count = 5 git reflog && + test_stdout_line_count = 5 git reflog show main && git reflog delete main@{1} && + test_stdout_line_count = 4 git reflog show main && + test_stdout_line_count = 5 git reflog && git reflog show main > output && - test_line_count = $(($main_entry_count - 1)) output && - test $HEAD_entry_count = $(git reflog | wc -l) && ! grep ox < output && - main_entry_count=$(wc -l < output) && - git reflog delete HEAD@{1} && - test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) && - test $main_entry_count = $(git reflog show main | wc -l) && - - HEAD_entry_count=$(git reflog | wc -l) && + test_stdout_line_count = 4 git reflog && + test_stdout_line_count = 4 git reflog show main && git reflog delete main@{07.04.2005.15:15:00.-0700} && + test_stdout_line_count = 3 git reflog show main && git reflog show main > output && - test_line_count = $(($main_entry_count - 1)) output && ! grep dragon < output ' @@ -321,11 +313,11 @@ ' test_expect_success 'checkout should not delete log for packed ref' ' - test $(git reflog main | wc -l) = 4 && + test_stdout_line_count = 4 git reflog main && git branch foo && git pack-refs --all && git checkout foo && - test $(git reflog main | wc -l) = 4 + test_stdout_line_count = 4 git reflog main ' test_expect_success 'stale dirs do not cause d/f conflicts (reflogs on)' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh index 51d79ba..6b2ad04 100755 --- a/t/t1415-worktree-refs.sh +++ b/t/t1415-worktree-refs.sh
@@ -32,7 +32,7 @@ test_when_finished git update-ref -d refs/heads/main-worktree/HEAD && git update-ref refs/heads/main-worktree/HEAD $(git rev-parse HEAD) && git rev-parse main-worktree/HEAD 2>warn && - grep "main-worktree/HEAD.*ambiguous" warn + test_grep "main-worktree/HEAD.*ambiguous" warn ' test_expect_success 'resolve worktrees/xx/HEAD' ' @@ -45,7 +45,7 @@ git update-ref refs/heads/worktrees/wt1/HEAD $(git rev-parse HEAD) && test_when_finished git update-ref -d refs/heads/worktrees/wt1/HEAD && git rev-parse worktrees/wt1/HEAD 2>warn && - grep "worktrees/wt1/HEAD.*ambiguous" warn + test_grep "worktrees/wt1/HEAD.*ambiguous" warn ' test_expect_success 'reflog of main-worktree/HEAD' '
diff --git a/t/t1418-reflog-exists.sh b/t/t1418-reflog-exists.sh index d51ecd5..1038779 100755 --- a/t/t1418-reflog-exists.sh +++ b/t/t1418-reflog-exists.sh
@@ -12,7 +12,7 @@ test_expect_success 'usage' ' test_expect_code 129 git reflog exists && - test_expect_code 129 git reflog exists -h + git reflog exists -h ' test_expect_success 'usage: unknown option' '
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index 3ab65f7..1ed4c7d 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh
@@ -47,7 +47,7 @@ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" && git branch >output 2>error && test_grep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'branch -d can delete badly named ref' ' @@ -55,8 +55,8 @@ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" && git branch -d broken...ref && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'branch -D can delete badly named ref' ' @@ -64,8 +64,8 @@ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" && git branch -D broken...ref && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'branch -D cannot delete non-ref in .git dir' ' @@ -93,8 +93,8 @@ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" && test_must_fail git branch broken...ref && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'branch -m cannot rename to a bad ref name' ' @@ -104,8 +104,8 @@ test_must_fail git branch -m goodref broken...ref && test_cmp_rev main goodref && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_failure 'branch -m can rename from a bad ref name' ' @@ -115,16 +115,16 @@ git branch -m broken...ref renamed && test_cmp_rev main renamed && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'push cannot create a badly named ref' ' test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" && test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_failure 'push --mirror can delete badly named ref' ' @@ -144,8 +144,8 @@ ) && git -C src push --mirror "file://$top/dest" && git -C dest branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'rev-parse skips symref pointing to broken name' ' @@ -168,9 +168,9 @@ test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main && test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" && git for-each-ref >output 2>error && - ! grep -e "broken\.\.\.ref" output && - ! grep -e "badname" output && - ! grep -e "broken\.\.\.symref" output && + test_grep ! -e "broken\.\.\.ref" output && + test_grep ! -e "badname" output && + test_grep ! -e "broken\.\.\.symref" output && test_grep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error && test_grep ! "ignoring broken ref refs/heads/badname" error && test_grep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error @@ -183,8 +183,8 @@ test_must_be_empty output && test_must_be_empty error && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'branch -d can delete broken name' ' @@ -194,8 +194,8 @@ test_grep "Deleted branch broken...ref (was broken)" output && test_must_be_empty error && git branch >output 2>error && - ! grep -e "broken\.\.\.ref" error && - ! grep -e "broken\.\.\.ref" output + test_grep ! -e "broken\.\.\.ref" error && + test_grep ! -e "broken\.\.\.ref" output ' test_expect_success 'update-ref --no-deref -d can delete symref to broken name' ' @@ -313,37 +313,37 @@ test_expect_success 'update-ref --stdin fails create with bad ref name' ' echo "create ~a refs/heads/main" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: invalid ref format: ~a" err + test_grep "fatal: invalid ref format: ~a" err ' test_expect_success 'update-ref --stdin fails update with bad ref name' ' echo "update ~a refs/heads/main" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: invalid ref format: ~a" err + test_grep "fatal: invalid ref format: ~a" err ' test_expect_success 'update-ref --stdin fails delete with bad ref name' ' echo "delete ~a refs/heads/main" >stdin && test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: invalid ref format: ~a" err + test_grep "fatal: invalid ref format: ~a" err ' test_expect_success 'update-ref --stdin -z fails create with bad ref name' ' printf "%s\0" "create ~a " refs/heads/main >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: invalid ref format: ~a " err + test_grep "fatal: invalid ref format: ~a " err ' test_expect_success 'update-ref --stdin -z fails update with bad ref name' ' printf "%s\0" "update ~a" refs/heads/main "" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: invalid ref format: ~a" err + test_grep "fatal: invalid ref format: ~a" err ' test_expect_success 'update-ref --stdin -z fails delete with bad ref name' ' printf "%s\0" "delete ~a" refs/heads/main >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: invalid ref format: ~a" err + test_grep "fatal: invalid ref format: ~a" err ' test_expect_success 'branch rejects HEAD as a branch name' '
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 54e81c2..77cd96d 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh
@@ -68,7 +68,7 @@ git update-ref refs/heads/bogus $cmt && test_must_fail git fsck 2>out && - grep "$oldoid: hash-path mismatch, found at: .*$new" out + test_grep "$oldoid: hash-path mismatch, found at: .*$new" out ) ' @@ -172,7 +172,7 @@ test_when_finished "git update-ref -d refs/heads/bogus" && git fsck 2>out && cat out && - ! grep "commit $new" out + test_grep ! "commit $new" out ' test_expect_success 'email without @ is okay' ' @@ -183,7 +183,7 @@ git update-ref refs/heads/bogus "$new" && test_when_finished "git update-ref -d refs/heads/bogus" && git fsck 2>out && - ! grep "commit $new" out + test_grep ! "commit $new" out ' test_expect_success 'email with embedded > is not okay' ' @@ -538,6 +538,23 @@ test_grep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out ' +test_expect_success 'rev-list --verify-objects with truncated loose blob' ' + git init truncated-blob && + ( + cd truncated-blob && + blob=$(test-tool genrandom one 5k | git hash-object -t blob -w --stdin) && + obj=.git/objects/$(test_oid_to_path $blob) && + + # Truncate the loose blob such that its header can still be + # parsed, but reading the object data fails mid-stream. + test_copy_bytes 64 <"$obj" >obj.tmp && + mv obj.tmp "$obj" && + + test_must_fail git rev-list --verify-objects "$blob" 2>err && + test_grep "hash mismatch" err + ) +' + # An actual bit corruption is more likely than swapped commits, but # this provides an easy way to have commits which don't match their purported # hashes, but which aren't so broken we can't read them at all. @@ -626,7 +643,7 @@ cd large-name && test_commit a-long-name && git -c fsck.largePathname=warn:10 fsck 2>out && - grep "warning.*large pathname" out + test_grep "warning.*large pathname" out ) ' @@ -849,7 +866,7 @@ test_must_fail git fsck 2>out && test_grep "error in commit $one.* - bad name" out && test_grep "error in commit $two.* - bad name" out && - ! grep corrupt out + test_grep ! corrupt out ' test_expect_success 'fsck handles multiple packfiles with big blobs' ' @@ -1027,7 +1044,7 @@ test_when_finished "git rm --cached foo" && remove_object $blob && test_must_fail git fsck $ZERO_OID >out 2>&1 && - ! grep $blob out + test_grep ! $blob out ' # Corrupt the checksum on the index.
diff --git a/t/t1451-fsck-buffer.sh b/t/t1451-fsck-buffer.sh index 3a3d33f..14151ea 100755 --- a/t/t1451-fsck-buffer.sh +++ b/t/t1451-fsck-buffer.sh
@@ -46,7 +46,7 @@ echo "$content" } >input && test_must_fail git hash-object -t "$type" input 2>err && - grep "$fsck" err + test_grep "$fsck" err ' } @@ -125,7 +125,7 @@ test_expect_success 'truncated tree (short hash)' ' printf "100644 foo\0\1\1\1\1" >input && test_must_fail git hash-object -t tree input 2>err && - grep badTree err + test_grep badTree err ' test_expect_success 'truncated tree (missing nul)' ' @@ -135,7 +135,7 @@ # parser does not walk past the end of the buffer). printf "100644 a long filename, or a hash with missing nul?" >input && test_must_fail git hash-object -t tree input 2>err && - grep badTree err + test_grep badTree err ' test_done
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh index 5246468..8f42697 100755 --- a/t/t1460-refs-migrate.sh +++ b/t/t1460-refs-migrate.sh
@@ -212,7 +212,7 @@ test_commit -C repo initial && git -C repo refs migrate --dry-run \ --ref-format=$to_format >output && - grep "Finished dry-run migration of refs" output && + test_grep "Finished dry-run migration of refs" output && test_path_is_dir repo/.git/ref_migration.* && echo $from_format >expect && git -C repo rev-parse --show-ref-format >actual &&
diff --git a/t/t1464-refs-delete.sh b/t/t1464-refs-delete.sh new file mode 100755 index 0000000..c88063e --- /dev/null +++ b/t/t1464-refs-delete.sh
@@ -0,0 +1,152 @@ +#!/bin/sh + +test_description='git refs delete' + +. ./test-lib.sh + +setup_repo () { + git init "$1" && + test_commit -C "$1" A && + test_commit -C "$1" B +} + +test_expect_success 'delete without oldvalue verification' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + git refs delete refs/heads/foo && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete with matching oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + git refs delete refs/heads/foo $A && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete with stale oldvalue fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git update-ref refs/heads/foo $A && + test_must_fail git refs delete refs/heads/foo $B 2>err && + test_grep " but expected " err && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete with null oldvalue fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + test_must_fail git refs delete refs/heads/foo $ZERO_OID 2>err && + test_grep "null old object ID" err && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete with invalid oldvalue fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + test_must_fail git refs delete refs/heads/foo invalid-oid 2>err && + test_grep "invalid old object ID" err && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete symref with --no-deref leaves target intact' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + git symbolic-ref refs/heads/symref refs/heads/foo && + git refs delete --no-deref refs/heads/symref && + test_must_fail git refs exists refs/heads/symref && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete symref with --no-deref verifies target OID' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git update-ref refs/heads/foo $A && + git symbolic-ref refs/heads/symref refs/heads/foo && + + test_must_fail git refs delete --no-deref refs/heads/symref $B && + git refs exists refs/heads/symref && + + git refs delete --no-deref refs/heads/symref $A && + test_must_fail git refs exists refs/heads/symref && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete with message records reason in reflog' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + git symbolic-ref HEAD refs/heads/foo && + git refs delete --message=delete-reason refs/heads/foo && + test_must_fail git refs exists refs/heads/foo && + test-tool ref-store main for-each-reflog-ent HEAD >actual && + test_grep "delete-reason$" actual + ) +' + +test_expect_success 'delete with empty message fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git update-ref refs/heads/foo $A && + test_must_fail git refs delete --message= refs/heads/foo 2>err && + test_grep "empty message" err && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'delete without arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git -C repo refs delete 2>err && + test_grep "requires reference name" err +' + +test_expect_success 'delete with too many arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git refs delete one two three 2>err && + test_grep "requires reference name" err +' + +test_done
diff --git a/t/t1465-refs-update.sh b/t/t1465-refs-update.sh new file mode 100755 index 0000000..a9becdd --- /dev/null +++ b/t/t1465-refs-update.sh
@@ -0,0 +1,268 @@ +#!/bin/sh + +test_description='git refs update' + +. ./test-lib.sh + +setup_repo () { + git init "$1" && + test_commit -C "$1" A && + test_commit -C "$1" B +} + +test_ref_matches () { + git rev-parse "$1" >expect && + echo "$2" >actual && + test_cmp expect actual +} + +test_expect_success 'update creates a new reference' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A && + test_ref_matches refs/heads/foo "$A" + ) +' + +test_expect_success 'update an existing reference without oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + git refs update refs/heads/foo $B && + test_ref_matches refs/heads/foo $B + ) +' + +test_expect_success 'update with matching oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + git refs update refs/heads/foo $B $A && + test_ref_matches refs/heads/foo $B + ) +' + +test_expect_success 'update with stale oldvalue fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + test_must_fail git refs update refs/heads/foo $B $B 2>err && + test_grep " but expected " err && + test_ref_matches refs/heads/foo $A + ) +' + +test_expect_success 'update can create a new branch with oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A $ZERO_OID 2>err && + test_ref_matches refs/heads/foo $A + ) +' + +test_expect_success 'update can create a new branch without oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A 2>err && + test_ref_matches refs/heads/foo $A + ) +' + +test_expect_success 'update refuses to create preexisting branch' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + test_must_fail git refs update refs/heads/foo $B $ZERO_OID 2>err && + test_grep "reference already exists" err && + test_ref_matches refs/heads/foo $A + ) +' + +test_expect_success 'update can delete a branch with oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A 2>err && + git refs update refs/heads/foo $ZERO_OID $A 2>err && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'update can delete a branch without oldvalue' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A 2>err && + git refs update refs/heads/foo $ZERO_OID 2>err && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'update refuses to delete a branch with mismatching value' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A 2>err && + test_must_fail git refs update refs/heads/foo $ZERO_OID $B 2>err && + test_grep " but expected " err && + git refs exists refs/heads/foo + ) +' + +test_expect_success 'update refuses to create preexisting branch' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + test_must_fail git refs update refs/heads/foo $B $ZERO_OID 2>err && + test_grep "reference already exists" err && + test_ref_matches refs/heads/foo $A + ) +' + + +test_expect_success 'update with invalid new value fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + test_must_fail git refs update refs/heads/foo invalid-oid 2>err && + test_grep "invalid new object ID" err && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'update with invalid old value fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + test_must_fail git refs update refs/heads/foo $B invalid-oid 2>err && + test_grep "invalid old object ID" err && + test_ref_matches refs/heads/foo $A + ) +' + +test_expect_success 'update --no-deref rewrites the symref itself' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + git symbolic-ref refs/heads/symref refs/heads/foo && + git refs update --no-deref refs/heads/symref $B && + test_must_fail git symbolic-ref refs/heads/symref && + test_ref_matches refs/heads/symref $B && + test_ref_matches refs/heads/foo $A + ) +' + +test_expect_success 'update does not create a reflog by default' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/foo $A && + test_must_fail git reflog exists refs/foo + ) +' + +test_expect_success 'update creates a reflog with --create-reflog' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update --create-reflog refs/foo $A && + git reflog exists refs/foo + ) +' + +test_expect_success 'update with message records reason in reflog' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + git refs update --message=update-reason refs/heads/foo $B && + git reflog show refs/heads/foo >actual && + test_grep "update-reason$" actual + ) +' + +test_expect_success 'update with empty message fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + test_must_fail git refs update --message= refs/heads/foo $B 2>err && + test_grep "empty message" err + ) +' + +test_expect_success 'update with too few arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git -C repo refs update refs/heads/foo 2>err && + test_grep "requires reference name, new value" err +' + +test_expect_success 'update with too many arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + test_must_fail git refs update refs/heads/foo $A $B extra 2>err && + test_grep "requires reference name, new value" err + ) +' + +test_done
diff --git a/t/t1466-refs-create.sh b/t/t1466-refs-create.sh new file mode 100755 index 0000000..cfb21bf --- /dev/null +++ b/t/t1466-refs-create.sh
@@ -0,0 +1,151 @@ +#!/bin/sh + +test_description='git refs create' + +. ./test-lib.sh + +setup_repo () { + git init "$1" && + test_commit -C "$1" A && + test_commit -C "$1" B +} + +test_ref_matches () { + git rev-parse "$1" >expect && + echo "$2" >actual && + test_cmp expect actual +} + +test_expect_success 'create a new reference' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs create refs/heads/foo $A && + test_ref_matches refs/heads/foo "$A" + ) +' + +test_expect_success 'create fails when the reference already exists' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs create refs/heads/foo $A && + test_must_fail git refs create refs/heads/foo $B 2>err && + test_grep "reference already exists" err && + test_ref_matches refs/heads/foo "$A" + ) +' + +test_expect_success 'create with null new value fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + test_must_fail git refs create refs/heads/foo $ZERO_OID 2>err && + test_grep "null new object ID" err && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'create with invalid new value fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + test_must_fail git refs create refs/heads/foo invalid-oid 2>err && + test_grep "invalid object ID" err && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'create does not create a reflog by default' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs create refs/foo $A && + test_must_fail git reflog exists refs/foo + ) +' + +test_expect_success 'create creates a reflog with --create-reflog' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs create --create-reflog refs/foo $A && + git reflog exists refs/foo + ) +' + +test_expect_success 'create with message records reason in reflog' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs create --message="create reason" refs/heads/foo $A && + git reflog show refs/heads/foo >actual && + test_grep "create reason$" actual + ) +' + +test_expect_success 'create with symref target creates target reference' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git symbolic-ref refs/heads/symref refs/heads/target && + git refs create refs/heads/symref $A && + git reflog exists refs/heads/target + ) +' + +test_expect_success 'create with symref target and --no-deref refuses to create reference' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git symbolic-ref refs/heads/symref refs/heads/target && + test_must_fail git refs create --no-deref refs/heads/symref $A 2>err && + test_grep "dangling symref already exists" err && + test_must_fail git reflog exists refs/heads/target + ) +' + +test_expect_success 'create with empty message fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + test_must_fail git refs create --message= refs/heads/foo $A 2>err && + test_grep "empty message" err && + test_must_fail git refs exists refs/heads/foo + ) +' + +test_expect_success 'create without arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git -C repo refs create 2>err && + test_grep "requires reference name" err +' + +test_expect_success 'create with too many arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git -C repo refs create refs/heads/foo a b 2>err && + test_grep "requires reference name" err +' + +test_done
diff --git a/t/t1467-refs-rename.sh b/t/t1467-refs-rename.sh new file mode 100755 index 0000000..2b28be7 --- /dev/null +++ b/t/t1467-refs-rename.sh
@@ -0,0 +1,144 @@ +#!/bin/sh + +test_description='git refs rename' + +. ./test-lib.sh + +setup_repo () { + git init "$1" && + test_commit -C "$1" A && + test_commit -C "$1" B +} + +test_ref_matches () { + git rev-parse "$1" >expect && + echo "$2" >actual && + test_cmp expect actual +} + +test_expect_success 'rename an existing reference' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A && + git refs rename refs/heads/foo refs/heads/bar && + test_must_fail git refs exists refs/heads/foo && + test_ref_matches refs/heads/bar $A + ) +' + +test_expect_success 'rename moves the reflog along with the reference' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update --message="rename me" refs/heads/foo $A && + git refs rename refs/heads/foo refs/heads/bar && + git reflog show refs/heads/bar >reflog && + test_grep "rename me" reflog && + test_must_fail git reflog exists refs/heads/foo + ) +' + +test_expect_success 'rename with message records reason in reflog' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A && + git refs rename --message="rename reason" refs/heads/foo refs/heads/bar && + git reflog show refs/heads/bar >actual && + test_grep "rename reason" actual + ) +' + +test_expect_success 'rename a nonexistent reference fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + test_must_fail git refs rename refs/heads/foo refs/heads/bar 2>err && + test_grep "reference does not exist" err + ) +' + +test_expect_success 'rename to an existing reference fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + B=$(git rev-parse B) && + git refs update refs/heads/foo $A && + git refs update refs/heads/bar $B && + test_must_fail git refs rename refs/heads/foo refs/heads/bar 2>err && + test_grep "reference already exists" err + ) +' + +test_expect_success 'rename with symbolic ref fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs create refs/heads/target $A && + git symbolic-ref refs/heads/symref refs/heads/target && + ! git refs rename refs/heads/symref refs/heads/renamed 2>err && + test_grep "is a symbolic ref, .* not supported" err + ) +' + +test_expect_success 'rename with empty message fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A && + test_must_fail git refs rename --message= refs/heads/foo refs/heads/bar 2>err && + test_grep "empty message" err + ) +' + +test_expect_success 'rename with invalid old reference name fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + test_must_fail git refs rename "refs/heads/foo..bar" refs/heads/bar 2>err && + test_grep "invalid ref format" err + ) +' + +test_expect_success 'rename with invalid new reference name fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + ( + cd repo && + A=$(git rev-parse A) && + git refs update refs/heads/foo $A && + test_must_fail git refs rename refs/heads/foo "refs/heads/bar..baz" 2>err && + test_grep "invalid ref format" err + ) +' + +test_expect_success 'rename with too few arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git -C repo refs rename refs/heads/foo 2>err && + test_grep "requires old and new reference name" err +' + +test_expect_success 'rename with too many arguments fails' ' + test_when_finished "rm -rf repo" && + setup_repo repo && + test_must_fail git -C repo refs rename refs/heads/foo refs/heads/bar refs/heads/baz 2>err && + test_grep "requires old and new reference name" err +' + +test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 38067d9..4174ca4 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh
@@ -204,7 +204,7 @@ git rev-parse --show-object-format=output >actual && test_cmp expect actual && test_must_fail git rev-parse --show-object-format=squeamish-ossifrage 2>err && - grep "unknown mode for --show-object-format: squeamish-ossifrage" err + test_grep "unknown mode for --show-object-format: squeamish-ossifrage" err ' @@ -228,7 +228,7 @@ git rev-parse --show-object-format=compat >actual && test_cmp expect actual && test_must_fail git rev-parse --show-object-format=squeamish-ossifrage 2>err && - grep "unknown mode for --show-object-format: squeamish-ossifrage" err + test_grep "unknown mode for --show-object-format: squeamish-ossifrage" err ) && mkdir repo2 && ( @@ -254,7 +254,7 @@ cd repo && git config extensions.refstorage broken && test_must_fail git rev-parse --show-ref-format 2>err && - grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err + test_grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err ) '
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index 3962f1d2..7167adf 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh
@@ -12,7 +12,7 @@ cat <<-\EOF && error: unknown option `'${opt#--}\'' EOF - sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/$spec.help" + sed -e 1d -e /EOF/d -e \$d <"$TEST_DIRECTORY/t1502/$spec.help" } >expect && test_expect_code 129 git rev-parse --parseopt -- $opt \ 2>output <"$TEST_DIRECTORY/t1502/$spec" && @@ -75,7 +75,7 @@ ' test_expect_success 'test --parseopt help output' ' - test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec && + git rev-parse --parseopt -- -h > output < optionspec && test_cmp "$TEST_DIRECTORY/t1502/optionspec.help" output ' @@ -87,8 +87,9 @@ | some-command does foo and bar! | |EOF +|exit 0 END_EXPECT - test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches && + git rev-parse --parseopt -- -h > output < optionspec_no_switches && test_cmp expect output ' @@ -100,8 +101,9 @@ | some-command does foo and bar! | |EOF +|exit 0 END_EXPECT - test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches && + git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches && test_cmp expect output ' @@ -115,8 +117,9 @@ | --[no-]hidden1 A hidden switch | |EOF +|exit 0 END_EXPECT - test_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches && + git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches && test_cmp expect output ' @@ -125,7 +128,7 @@ cat <<-\EOF && error: unknown option `does-not-exist'\'' EOF - sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/optionspec.help" + sed -e 1d -e /EOF/d -e \$d <"$TEST_DIRECTORY/t1502/optionspec.help" } >expect && test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec && test_cmp expect output @@ -252,9 +255,10 @@ | -h, --help show the help | |EOF + |exit 0 END_EXPECT - test_must_fail git rev-parse --parseopt -- -h <spec >actual && + git rev-parse --parseopt -- -h <spec >actual && test_cmp expect actual ' @@ -289,14 +293,15 @@ | -h, --help show the help | |EOF + |exit 0 END_EXPECT - test_must_fail git rev-parse --parseopt -- -h <spec >actual && + git rev-parse --parseopt -- -h <spec >actual && test_cmp expect actual ' test_expect_success 'test --parseopt help output for optionspec-neg' ' - test_expect_code 129 git rev-parse --parseopt -- \ + git rev-parse --parseopt -- \ -h >output <"$TEST_DIRECTORY/t1502/optionspec-neg" && test_cmp "$TEST_DIRECTORY/t1502/optionspec-neg.help" output ' @@ -331,7 +336,7 @@ EOF test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \ git rev-parse --parseopt -- <spec 2>err --no && - grep "error: ambiguous option: no (could be --noble or --no-noble)" err + test_grep "error: ambiguous option: no (could be --noble or --no-noble)" err ' test_done
diff --git a/t/t1502/optionspec-neg.help b/t/t1502/optionspec-neg.help index 7a29f8c..f85be7b 100644 --- a/t/t1502/optionspec-neg.help +++ b/t/t1502/optionspec-neg.help
@@ -10,3 +10,4 @@ --no-negative cannot be positivated EOF +exit 0
diff --git a/t/t1502/optionspec.help b/t/t1502/optionspec.help index cbdd54d..ded35eb 100755 --- a/t/t1502/optionspec.help +++ b/t/t1502/optionspec.help
@@ -34,3 +34,4 @@ --[no-]extra1 line above used to cause a segfault but no longer does EOF +exit 0
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh index 75a708f..87638a4 100755 --- a/t/t1503-rev-parse-verify.sh +++ b/t/t1503-rev-parse-verify.sh
@@ -62,15 +62,15 @@ test_expect_success 'fails with any bad rev or many good revs' ' test_must_fail git rev-parse --verify 2>error && - grep "single revision" error && + test_grep "single revision" error && test_must_fail git rev-parse --verify foo 2>error && - grep "single revision" error && + test_grep "single revision" error && test_must_fail git rev-parse --verify HEAD bar 2>error && - grep "single revision" error && + test_grep "single revision" error && test_must_fail git rev-parse --verify baz HEAD 2>error && - grep "single revision" error && + test_grep "single revision" error && test_must_fail git rev-parse --verify $HASH2 HEAD 2>error && - grep "single revision" error + test_grep "single revision" error ' test_expect_success 'fails silently when using -q' '
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh index bbfe05b..d330b8d 100755 --- a/t/t1510-repo-setup.sh +++ b/t/t1510-repo-setup.sh
@@ -604,7 +604,7 @@ cd 20b/.git && test_must_fail git status >/dev/null ) 2>message && - grep "core.bare and core.worktree" message + test_grep "core.bare and core.worktree" message ' test_expect_success '#20d: core.worktree and core.bare OK when working tree not needed' ' @@ -721,8 +721,8 @@ export GIT_DIR && test_must_fail git status 2>result ) && - grep "core.bare and core.worktree" 22/.git/result && - grep "core.bare and core.worktree" 22/result + test_grep "core.bare and core.worktree" 22/.git/result && + test_grep "core.bare and core.worktree" 22/result ' # Case #23: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses bareness. @@ -767,7 +767,7 @@ cd 28 && test_must_fail git status ) 2>message && - grep "core.bare and core.worktree" message + test_grep "core.bare and core.worktree" message ' # Case #29: GIT_WORK_TREE(+core.worktree) overrides core.bare (gitfile case). @@ -791,7 +791,7 @@ cd 30 && test_must_fail env GIT_DIR=.git git status 2>result ) && - grep "core.bare and core.worktree" 30/result + test_grep "core.bare and core.worktree" 30/result ' # Case #31: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh index 1a380a4..0e6b42e 100755 --- a/t/t1512-rev-parse-disambiguation.sh +++ b/t/t1512-rev-parse-disambiguation.sh
@@ -378,7 +378,7 @@ VAL=$(git commit-tree $TREE </dev/null) && git update-ref refs/heads/$REF $VAL && test $(git rev-parse $REF 2>err) = $REF && - grep "refname.*${REF}.*ambiguous" err + test_grep "refname.*${REF}.*ambiguous" err ' test_expect_success 'ambiguous short sha1 ref' ' @@ -387,7 +387,7 @@ VAL=$(git commit-tree $TREE </dev/null) && git update-ref refs/heads/$REF $VAL && test $(git rev-parse $REF 2>err) = $VAL && - grep "refname.*${REF}.*ambiguous" err + test_grep "refname.*${REF}.*ambiguous" err ' test_expect_success 'ambiguity errors are not repeated (raw)' '
diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh index 75e89c4..2cb3c7c 100755 --- a/t/t1515-rev-parse-outside-repo.sh +++ b/t/t1515-rev-parse-outside-repo.sh
@@ -32,7 +32,7 @@ git rev-parse --local-env-vars >actual && # we do not want to depend on the complete list here, # so just look for something plausible - grep ^GIT_DIR actual + test_grep ^GIT_DIR actual ' test_expect_success 'rev-parse --resolve-git-dir' '
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh index c557f2f..d1e9157 100755 --- a/t/t1517-outside-repo.sh +++ b/t/t1517-outside-repo.sh
@@ -4,6 +4,13 @@ . ./test-lib.sh +test_lazy_prereq SVN ' + test_have_prereq PERL && test -z "$NO_SVN_TESTS" && perl -w -e " + use SVN::Core; + use SVN::Repos; + " +' + test_expect_success 'set up a non-repo directory and test file' ' GIT_CEILING_DIRECTORIES=$(pwd) && export GIT_CEILING_DIRECTORIES && @@ -122,39 +129,48 @@ archimport | citool | credential-netrc | credential-libsecret | \ credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \ daemon | \ - difftool--helper | filter-branch | format-rev | fsck-objects | \ - get-tar-commit-id | \ + difftool--helper | format-rev | fsck-objects | get-tar-commit-id | \ gui | gui--askpass | \ http-backend | http-fetch | http-push | init-db | \ - merge-octopus | merge-one-file | merge-resolve | mergetool | \ mktag | p4 | p4.py | pickaxe | remote-ftp | remote-ftps | \ remote-http | remote-https | replay | send-email | \ - sh-i18n--envsubst | shell | show | stage | submodule | svn | \ - upload-archive--writer | upload-pack | web--browse | whatchanged) - expect_outcome=expect_failure ;; + sh-i18n--envsubst | shell | show | stage | \ + upload-archive--writer | upload-pack | whatchanged) + h_expect_outcome=expect_failure + all_expect_outcome=expect_failure + ;; + filter-branch | merge-octopus | merge-one-file | merge-resolve | \ + mergetool | submodule | svn | web--browse) + h_expect_outcome=expect_success + all_expect_outcome=expect_failure + ;; *) - expect_outcome=expect_success ;; + h_expect_outcome=expect_success + all_expect_outcome=expect_success + ;; esac case "$cmd" in instaweb) prereq=PERL ;; + svn) + prereq=SVN ;; *) prereq= ;; esac - test_$expect_outcome $prereq "'git $cmd -h' outside a repository" ' - test_expect_code 129 nongit git $cmd -h >usage && + test_$h_expect_outcome $prereq "'git $cmd -h' outside a repository" ' + nongit git $cmd -h >usage && test_grep "[Uu]sage: git $cmd " usage ' - test_$expect_outcome $prereq "'git $cmd --help-all' outside a repository" ' - test_expect_code 129 nongit git $cmd --help-all >usage && + test_$all_expect_outcome $prereq "'git $cmd --help-all' outside a repository" ' + nongit git $cmd --help-all >usage && test_grep "[Uu]sage: git $cmd " usage ' done test_expect_success 'fmt-merge-msg does not crash with -h' ' - test_expect_code 129 git fmt-merge-msg -h >usage && + git fmt-merge-msg -h >usage && test_grep "[Uu]sage: git fmt-merge-msg " usage && - test_expect_code 129 nongit git fmt-merge-msg -h >usage && + nongit git fmt-merge-msg -h >usage && test_grep "[Uu]sage: git fmt-merge-msg " usage '
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh index 0132e77..7d4b298 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh
@@ -75,11 +75,11 @@ test_expect_success 'git hook usage' ' test_expect_code 129 git hook && test_expect_code 129 git hook run && - test_expect_code 129 git hook run -h && + git hook run -h && test_expect_code 129 git hook run --unknown 2>err && test_expect_code 129 git hook list && - test_expect_code 129 git hook list -h && - grep "unknown option" err + git hook list -h && + test_grep "unknown option" err ' test_expect_success 'git hook list: unknown hook name is rejected' ' @@ -361,9 +361,9 @@ # 'ghi' should be included in both 'pre-commit' and 'test-hook' git hook list pre-commit >actual && - grep "ghi" actual && + test_grep "ghi" actual && git hook list --allow-unknown-hook-name test-hook >actual && - grep "ghi" actual + test_grep "ghi" actual ' test_expect_success 'git hook list shows hooks from the hookdir' ' @@ -569,7 +569,7 @@ # TODO: We should emit the same (or at least a more similar) # error on MINGW (essentially Git for Windows) and all other # platforms.. See the OS-specific code in start_command() - grep -E "^(error|fatal): cannot (exec|spawn) .*bad-hooks/test-hook" err + test_grep -E "^(error|fatal): cannot (exec|spawn) .*bad-hooks/test-hook" err ' test_expect_success 'stdin to hooks' ' @@ -826,8 +826,8 @@ # neither hook has parallel=true git hook run --allow-unknown-hook-name -j2 test-hook >out 2>err && - grep "hook .hook-1. is not marked as parallel=true" err && - grep "hook .hook-2. is not marked as parallel=true" err + test_grep "hook .hook-1. is not marked as parallel=true" err && + test_grep "hook .hook-2. is not marked as parallel=true" err ' test_expect_success 'hook.jobs=1 config runs hooks in series' ' @@ -1068,7 +1068,7 @@ cpus=$(test-tool online-cpus) && GIT_TRACE2_EVENT="$(pwd)/trace.txt" \ git hook run --allow-unknown-hook-name test-hook >out 2>err && - grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt + test_grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt ' test_expect_success 'hook.<event>.jobs=-1 resolves to online_cpus()' ' @@ -1081,7 +1081,7 @@ cpus=$(test-tool online-cpus) && GIT_TRACE2_EVENT="$(pwd)/trace.txt" \ git hook run --allow-unknown-hook-name test-hook >out 2>err && - grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt + test_grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt ' test_expect_success 'git hook run -j-1 resolves to online_cpus()' ' @@ -1092,7 +1092,7 @@ cpus=$(test-tool online-cpus) && GIT_TRACE2_EVENT="$(pwd)/trace.txt" \ git hook run --allow-unknown-hook-name -j-1 test-hook >out 2>err && - grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt + test_grep "\"region_enter\".*\"hook\".*\"test-hook\".*\"max:$cpus\"" trace.txt ' test_expect_success 'hook.jobs rejects values less than -1' '
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh index 39bb77d..c85d390 100755 --- a/t/t1900-repo-info.sh +++ b/t/t1900-repo-info.sh
@@ -150,9 +150,67 @@ ' test_expect_success 'git repo info -h shows only repo info usage' ' - test_must_fail git repo info -h >actual && + git repo info -h >actual && test_grep "git repo info" actual && test_grep ! "git repo structure" actual ' +# Helper function to test path keys in both absolute and relative formats. +# $1: label for the test +# $2: field_name (e.g., commondir) +# $3: expected_dir (the directory name, e.g., .git or custom-common) +# $4: init_command (extra setup like exporting env vars) +test_repo_info_path () { + label=$1 + field_name=$2 + expected_dir=$3 + init_command=$4 + + test_expect_success "absolute: $label" ' + test_when_finished "rm -rf repo" && + git init repo && + ( + mkdir -p repo/sub && + cd repo/sub && + ROOT="$(test-tool path-utils real_path ..)" && export ROOT && + eval "$init_command" && + echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect && + git repo info "path.$field_name.absolute" >actual && + test_cmp expect actual + ) + ' + + test_expect_success "relative: $label" ' + test_when_finished "rm -rf repo" && + git init repo && + ( + mkdir -p repo/sub && + cd repo/sub && + ROOT="$(test-tool path-utils real_path ..)" && export ROOT && + eval "$init_command" && + echo "path.$field_name.relative=../$expected_dir" >expect && + git repo info "path.$field_name.relative" >actual && + test_cmp expect actual + ) + ' +} + +test_repo_info_path 'commondir standard' 'commondir' '.git' + +test_repo_info_path 'commondir with GIT_COMMON_DIR and GIT_DIR' 'commondir' \ + 'custom-common' \ + 'GIT_COMMON_DIR="$ROOT/custom-common" && export GIT_COMMON_DIR && + GIT_DIR="../.git" && export GIT_DIR && + git init --bare "$ROOT/custom-common"' + +test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \ + '.git' \ + 'GIT_DIR="../.git" && export GIT_DIR' + +test_repo_info_path 'gitdir standard' 'gitdir' '.git' + +test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \ + '.git' \ + 'GIT_DIR="../.git" && export GIT_DIR' + test_done
diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh index 10050ab..02cc2b5 100755 --- a/t/t1901-repo-structure.sh +++ b/t/t1901-repo-structure.sh
@@ -225,7 +225,7 @@ ' test_expect_success 'git repo structure -h shows only repo structure usage' ' - test_must_fail git repo structure -h >actual && + git repo structure -h >actual && test_grep "git repo structure" actual && test_grep ! "git repo info" actual '
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh index 0afe0ff..db06c32 100755 --- a/t/t2004-checkout-cache-temp.sh +++ b/t/t2004-checkout-cache-temp.sh
@@ -132,8 +132,8 @@ test_expect_success '--stage=all --no-temp is rejected' ' rm -f path* .merge_* actual && test_must_fail git checkout-index --stage=all --no-temp -- path1 2>err && - grep -v "already exists" err && - grep "options .--stage=all. and .--no-temp. cannot be used together" err + test_grep -v "already exists" err && + test_grep "options .--stage=all. and .--no-temp. cannot be used together" err ' test_expect_success 'checkout some stages/one file to temporary files' '
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh index fedd2cc..6538a24 100755 --- a/t/t2006-checkout-index-basic.sh +++ b/t/t2006-checkout-index-basic.sh
@@ -16,15 +16,15 @@ cd broken && git init && >.git/index && - test_expect_code 129 git checkout-index -h >usage 2>&1 + git checkout-index -h >usage 2>&1 ) && test_grep "[Uu]sage" broken/usage ' test_expect_success 'checkout-index does not crash with -h' ' - test_expect_code 129 git checkout-index -h >usage && + git checkout-index -h >usage && test_grep "[Uu]sage: git checkout-index " usage && - test_expect_code 129 nongit git checkout-index -h >usage && + nongit git checkout-index -h >usage && test_grep "[Uu]sage: git checkout-index " usage '
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh index 1fcef4b..0ac3579 100755 --- a/t/t2019-checkout-ambiguous-ref.sh +++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -19,7 +19,7 @@ ' test_expect_success 'checkout produces ambiguity warning' ' - grep "warning.*ambiguous" stderr + test_grep "warning.*ambiguous" stderr ' test_expect_success 'checkout chooses branch over tag' ' @@ -41,7 +41,7 @@ ' test_expect_success VAGUENESS_SUCCESS 'checkout produces ambiguity warning' ' - grep "warning.*ambiguous" stderr + test_grep "warning.*ambiguous" stderr ' test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh index a3b1449..752e7f0 100755 --- a/t/t2024-checkout-dwim.sh +++ b/t/t2024-checkout-dwim.sh
@@ -311,7 +311,7 @@ git checkout loose >actual.raw 2>&1 && sed -e "s/loose/BRANCHNAME/g" <actual.raw >actual && status_uno_is_clean && - grep BRANCHNAME actual && + test_grep BRANCHNAME actual && test_cmp expect actual ' @@ -324,7 +324,7 @@ echo bar >dwim-arg && test_must_fail git checkout dwim-arg && test_must_fail git rev-parse refs/heads/dwim-arg -- && - grep bar dwim-arg + test_grep bar dwim-arg ' test_expect_success 'disambiguate dwim branch and checkout path (1)' ' @@ -334,7 +334,7 @@ echo bar >dwim-arg1 && git checkout -- dwim-arg1 && test_must_fail git rev-parse refs/heads/dwim-arg1 -- && - grep foo dwim-arg1 + test_grep foo dwim-arg1 ' test_expect_success 'disambiguate dwim branch and checkout path (2)' ' @@ -344,7 +344,7 @@ echo bar >dwim-arg2 && git checkout dwim-arg2 -- && git rev-parse refs/heads/dwim-arg2 -- && - grep bar dwim-arg2 + test_grep bar dwim-arg2 ' test_done
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index be3fcdd..9af24b1 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh
@@ -122,7 +122,7 @@ check_resolve_undo removed && echo the index and the work tree is unmerged again && git diff >actual && - grep "^++<<<<<<<" actual + test_grep "^++<<<<<<<" actual ' test_expect_success 'unmerge with plumbing' ' @@ -173,7 +173,7 @@ test -f .git/rr-cache/$rerere_id/postimage && git checkout -m fi/le && echo resurrect the conflict && - grep "^=======" fi/le && + test_grep "^=======" fi/le && echo reresolve the conflict && git rerere && test "z$(cat fi/le)" = zdifferent && @@ -199,7 +199,7 @@ test -f .git/rr-cache/$rerere_id/postimage && (cd fi && git checkout -m le) && echo resurrect the conflict && - grep "^=======" fi/le && + test_grep "^=======" fi/le && echo reresolve the conflict && (cd fi && git rerere) && test "z$(cat fi/le)" = zdifferent &&
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh index c91c4db..cec2fe6 100755 --- a/t/t2060-switch.sh +++ b/t/t2060-switch.sh
@@ -34,13 +34,13 @@ test_expect_success 'suggestion to detach' ' test_must_fail git switch main^{commit} 2>stderr && - grep "try again with the --detach option" stderr + test_grep "try again with the --detach option" stderr ' test_expect_success 'suggestion to detach is suppressed with advice.suggestDetachingHead=false' ' test_config advice.suggestDetachingHead false && test_must_fail git switch main^{commit} 2>stderr && - ! grep "try again with the --detach option" stderr + test_grep ! "try again with the --detach option" stderr ' test_expect_success 'switch and detach current branch' ' @@ -76,7 +76,7 @@ git switch --orphan new-orphan && test_commit orphan && git cat-file commit refs/heads/new-orphan >commit && - ! grep ^parent commit && + test_grep ! ^parent commit && git ls-files >tracked-files && echo orphan.t >expected && test_cmp expected tracked-files
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh index 16d6348..2c222fb 100755 --- a/t/t2070-restore.sh +++ b/t/t2070-restore.sh
@@ -216,7 +216,7 @@ "--staged --worktree --conflict=zdiff3" do test_must_fail git restore $opts . 2>err && - grep "cannot be used" err || return + test_grep "cannot be used" err || return done '
diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh index 5ffe1a4..88a6ec1 100755 --- a/t/t2080-parallel-checkout-basics.sh +++ b/t/t2080-parallel-checkout-basics.sh
@@ -200,13 +200,13 @@ # We expect 0 workers because there is nothing to be done test_checkout_workers 0 git checkout HEAD && test_path_is_file D && - grep changed D && - grep changed F.t && + test_grep changed D && + test_grep changed F.t && test_checkout_workers 2 git checkout --force HEAD && test_path_is_dir D && - grep D/F D/F.t && - grep F F.t + test_grep D/F D/F.t && + test_grep F F.t ) ' @@ -224,8 +224,8 @@ test_checkout_workers 2 git checkout --force HEAD && ! test -h D && - grep D/A D/A.t && - grep D/B D/B.t + test_grep D/A D/A.t && + test_grep D/B D/B.t ) ' @@ -268,7 +268,7 @@ # - missing-delay.a: the delay filter will drop this path # - parallel-*.a: the blob will be missing # - grep "Updated 3 paths from the index" err && + test_grep "Updated 3 paths from the index" err && test_stdout_line_count = 3 ls *.b && ! ls *.a )
diff --git a/t/t2081-parallel-checkout-collisions.sh b/t/t2081-parallel-checkout-collisions.sh index f6fcfc0..db45f5f 100755 --- a/t/t2081-parallel-checkout-collisions.sh +++ b/t/t2081-parallel-checkout-collisions.sh
@@ -78,8 +78,8 @@ # Check that it used the right number of workers and detected the collisions test_workers_in_event_trace 2 trace && - grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/B.}" trace && - grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/C.}" trace + test_grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/B.}" trace && + test_grep "category.:.pcheckout.,.key.:.collision/dirname.,.value.:.A/C.}" trace ' test_expect_success SYMLINKS,CASE_INSENSITIVE_FS 'do not follow symlinks colliding with leading dir' ' @@ -115,11 +115,11 @@ set_checkout_config 2 0 && test_checkout_workers 2 git clone . clone-repo 2>stderr && - grep FILE_X stderr && - grep FILE_x stderr && - grep file_X stderr && - grep file_x stderr && - grep "the following paths have collided" stderr + test_grep FILE_X stderr && + test_grep FILE_x stderr && + test_grep file_X stderr && + test_grep file_x stderr && + test_grep "the following paths have collided" stderr ' # This test ensures that the collision report code is correctly looking for @@ -148,11 +148,11 @@ test_checkout_workers 2 \ git -c core.ignoreCase=false clone . clone-repo 2>stderr && - grep FILE_X stderr && - grep FILE_x stderr && - grep file_X stderr && - grep file_x stderr && - grep "the following paths have collided" stderr && + test_grep FILE_X stderr && + test_grep FILE_x stderr && + test_grep file_X stderr && + test_grep file_x stderr && + test_grep "the following paths have collided" stderr && # Check that only "file_x" was filtered echo file_x >expected.log &&
diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh index 79fb11f..1f43c2a 100755 --- a/t/t2082-parallel-checkout-attributes.sh +++ b/t/t2082-parallel-checkout-attributes.sh
@@ -28,8 +28,8 @@ rm A B && test_checkout_workers 2 git reset --hard && hexsz=$(test_oid hexsz) && - grep -E "\\\$Id: [0-9a-f]{$hexsz} \\\$" A && - grep "\\\$Id\\\$" B + test_grep -E "\\\$Id: [0-9a-f]{$hexsz} \\\$" A && + test_grep "\\\$Id\\\$" B ) ' @@ -175,15 +175,15 @@ verify_checkout delayed && # Check that the *.d files got to the delay queue and were filtered - grep "smudge W.d .* \[DELAYED\]" delayed.log && - grep "smudge X.d .* \[DELAYED\]" delayed.log && + test_grep "smudge W.d .* \[DELAYED\]" delayed.log && + test_grep "smudge X.d .* \[DELAYED\]" delayed.log && test_cmp delayed/W.d original && test_cmp delayed/X.d original && # Check that the parallel-eligible entries went to the right queue and # were not filtered - ! grep "smudge Y .* \[DELAYED\]" delayed.log && - ! grep "smudge Z .* \[DELAYED\]" delayed.log && + test_grep ! "smudge Y .* \[DELAYED\]" delayed.log && + test_grep ! "smudge Z .* \[DELAYED\]" delayed.log && test_cmp delayed/Y original && test_cmp delayed/Z original '
diff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh index 6938ecc..fbfc86b 100755 --- a/t/t2103-update-index-ignore-missing.sh +++ b/t/t2103-update-index-ignore-missing.sh
@@ -63,9 +63,9 @@ git update-index --unmerged --refresh && echo 2 >two && test_must_fail git update-index --unmerged --refresh >actual && - grep two actual && - ! grep one actual && - ! grep three actual + test_grep two actual && + test_grep ! one actual && + test_grep ! three actual ' test_expect_success '--ignore-submodules --refresh (1)' '
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 3bffe5d..0048783 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh
@@ -23,7 +23,7 @@ cd broken && git init && >.git/index && - test_expect_code 129 git update-index -h >usage 2>&1 + git update-index -h >usage 2>&1 ) && test_grep "[Uu]sage: git update-index" broken/usage '
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index 0a96655..bb019d7 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh
@@ -241,7 +241,7 @@ test_expect_success '"add -u non-existent" should fail' ' test_must_fail git add -u non-existent && git ls-files >actual && - ! grep "non-existent" actual + test_grep ! "non-existent" actual ' test_expect_success '"commit -a" implies "add -u" if index becomes empty' '
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 44c1936..69fef7e 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh
@@ -57,7 +57,7 @@ git add -N file elif && empty=$(git hash-object --stdin </dev/null) && git ls-files -s >actual && - ! grep "$empty" actual + test_grep ! "$empty" actual ' test_expect_success 'i-t-a entry is simply ignored' ' @@ -124,7 +124,7 @@ git add -N 2/1 && git commit -m committed && git ls-tree -r HEAD >actual && - grep 2/2 actual + test_grep 2/2 actual ) ' @@ -312,7 +312,7 @@ echo new >new-ita && git add -N new-ita && git diff >expected && - grep "new file" expected && + test_grep "new file" expected && git reset --hard && git apply --intent-to-add expected && git diff >actual &&
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 58b4445..87b9267 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh
@@ -122,7 +122,7 @@ ( cd here && test_must_fail git checkout newmain 2>actual && - grep "already used by worktree at" actual + test_grep "already used by worktree at" actual ) ' @@ -139,7 +139,7 @@ git rev-parse --verify refs/heads/newmain >new.branch && git rev-parse --verify HEAD >new.head && - grep "already used by worktree at" error && + test_grep "already used by worktree at" error && test_cmp old.branch new.branch && test_cmp old.head new.head && @@ -328,7 +328,7 @@ local opts="$*" && test_expect_success "'worktree add' with '$opts' has mutually exclusive options" ' test_must_fail git worktree add $opts 2>actual && - grep -E "fatal:( options)? .* cannot be used together" actual + test_grep -E "fatal:( options)? .* cannot be used together" actual ' } @@ -436,13 +436,13 @@ (cd repo && test_commit commit) && git -C repo switch --orphan noref && test_must_fail git -C repo worktree add $opts foobar/ 2>actual && - ! grep "error: unknown switch" actual && - grep "hint: If you meant to create a worktree containing a new unborn branch" actual && + test_grep ! "error: unknown switch" actual && + test_grep "hint: If you meant to create a worktree containing a new unborn branch" actual && if [ $use_branch -eq 1 ] then - grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual + test_grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual else - grep -E "^hint: +git worktree add --orphan [^ ]+$" actual + test_grep -E "^hint: +git worktree add --orphan [^ ]+$" actual fi ' @@ -457,8 +457,8 @@ git init repo && (cd repo && test_commit commit) && test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual && - ! grep "error: unknown switch" actual && - ! grep "hint: If you meant to create a worktree containing a new unborn branch" actual + test_grep ! "error: unknown switch" actual && + test_grep ! "hint: If you meant to create a worktree containing a new unborn branch" actual ' test_expect_success 'local clone from linked checkout' ' @@ -469,7 +469,7 @@ test_expect_success 'local clone --shared from linked checkout' ' git -C bare worktree add --detach ../baretree && git clone --local --shared baretree bare-clone && - grep /bare/ bare-clone/.git/objects/info/alternates + test_grep /bare/ bare-clone/.git/objects/info/alternates ' test_expect_success '"add" worktree with --no-checkout' ' @@ -491,7 +491,7 @@ set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && git worktree list >actual && - grep "under-rebase.*detached HEAD" actual + test_grep "under-rebase.*detached HEAD" actual ) ' @@ -533,7 +533,7 @@ git bisect bad && git bisect good HEAD~2 && git worktree list >actual && - grep "under-bisect.*detached HEAD" actual && + test_grep "under-bisect.*detached HEAD" actual && test_must_fail git worktree add new-bisect under-bisect && test_path_is_missing new-bisect )
diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh index e0c6abd..eeaf800 100755 --- a/t/t2402-worktree-list.sh +++ b/t/t2402-worktree-list.sh
@@ -101,8 +101,8 @@ git worktree lock locked && test_when_finished "git worktree unlock locked" && git worktree list >out && - grep "/locked *[0-9a-f].* locked$" out && - ! grep "/unlocked *[0-9a-f].* locked$" out + test_grep "/locked *[0-9a-f].* locked$" out && + test_grep ! "/unlocked *[0-9a-f].* locked$" out ' test_expect_success '"list" all worktrees --porcelain with locked' ' @@ -143,8 +143,8 @@ git worktree add --detach unprunable && rm -rf prunable && git worktree list >out && - grep "/prunable *[0-9a-f].* prunable$" out && - ! grep "/unprunable *[0-9a-f].* prunable$" + test_grep "/prunable *[0-9a-f].* prunable$" out && + test_grep ! "/unprunable *[0-9a-f].* prunable$" out ' test_expect_success '"list" all worktrees --porcelain with prunable' ' @@ -162,8 +162,8 @@ git worktree add --detach unprunable && rm -rf prunable && git worktree list >out && - grep "/prunable *[0-9a-f].* prunable$" out && - ! grep "/unprunable *[0-9a-f].* unprunable$" out && + test_grep "/prunable *[0-9a-f].* prunable$" out && + test_grep ! "/unprunable *[0-9a-f].* unprunable$" out && git worktree prune --verbose 2>out && test_grep "^Removing worktrees/prunable" out && test_grep ! "^Removing worktrees/unprunable" out @@ -184,7 +184,7 @@ echo "$(git -C locked2 rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect && printf "\tlocked: with reason\n" >>expect && git worktree list --verbose >out && - grep "/locked1 *[0-9a-f].* locked$" out && + test_grep "/locked1 *[0-9a-f].* locked$" out && sed -n "s/ */ /g;/\/locked2 *[0-9a-f].*$/,/locked: .*$/p" <out >actual && test_cmp actual expect ' @@ -266,7 +266,7 @@ test_cmp ../expected actual && git worktree list >out && head -n 1 out >actual.2 && - grep -F "(error)" actual.2 + test_grep -F "(error)" actual.2 ) '
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh index 0bb33e8..69768c1 100755 --- a/t/t2403-worktree-move.sh +++ b/t/t2403-worktree-move.sh
@@ -75,8 +75,8 @@ git worktree move source destination && test_path_is_missing source && git worktree list --porcelain >out && - grep "^worktree.*/destination$" out && - ! grep "^worktree.*/source$" out && + test_grep "^worktree.*/destination$" out && + test_grep ! "^worktree.*/source$" out && git -C destination log --format=%s >actual2 && echo init >expected2 && test_cmp expected2 actual2 @@ -92,7 +92,7 @@ test_when_finished "git worktree move some-dir/destination destination" && test_path_is_missing destination && git worktree list --porcelain >out && - grep "^worktree.*/some-dir/destination$" out && + test_grep "^worktree.*/some-dir/destination$" out && git -C some-dir/destination log --format=%s >actual2 && echo init >expected2 && test_cmp expected2 actual2
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh index 11018f3..f5c94d4 100755 --- a/t/t2405-worktree-submodule.sh +++ b/t/t2405-worktree-submodule.sh
@@ -36,7 +36,7 @@ test_expect_failure 'submodule is checked out just after worktree add' ' git -C worktree diff --submodule main"^!" >out && - grep "file1 updated" out + test_grep "file1 updated" out ' test_expect_success 'add superproject worktree and initialize submodules' ' @@ -46,7 +46,7 @@ test_expect_success 'submodule is checked out just after submodule update in linked worktree' ' git -C worktree-submodule-update diff --submodule main"^!" >out && - grep "file1 updated" out + test_grep "file1 updated" out ' test_expect_success 'add superproject worktree and manually add submodule worktree' ' @@ -56,7 +56,7 @@ test_expect_success 'submodule is checked out after manually adding submodule worktree' ' git -C linked_submodule diff --submodule main"^!" >out && - grep "file1 updated" out + test_grep "file1 updated" out ' test_expect_success 'checkout --recurse-submodules uses $GIT_DIR for submodules in a linked worktree' '
diff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh index 57c2018..a6d7e0f 100755 --- a/t/t2407-worktree-heads.sh +++ b/t/t2407-worktree-heads.sh
@@ -41,10 +41,10 @@ for i in 1 2 3 4 do test_must_fail git branch -f wt-$i HEAD 2>err && - grep "cannot force update the branch" err && + test_grep "cannot force update the branch" err && test_must_fail git branch -D wt-$i 2>err && - grep "cannot delete branch" err || return 1 + test_grep "cannot delete branch" err || return 1 done ' @@ -57,7 +57,7 @@ git -C wt-4 bisect good wt-1 && test_must_fail git branch -f wt-4 HEAD 2>err && - grep "cannot force update the branch '\''wt-4'\'' used by worktree at.*wt-4" err + test_grep "cannot force update the branch '\''wt-4'\'' used by worktree at.*wt-4" err ' test_expect_success 'refuse to overwrite: worktree in rebase (apply)' ' @@ -67,7 +67,7 @@ test_must_fail git -C wt-2 rebase --apply conflict-2 && test_must_fail git branch -f wt-2 HEAD 2>err && - grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err + test_grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err ' test_expect_success 'refuse to overwrite: worktree in rebase (merge)' ' @@ -77,7 +77,7 @@ test_must_fail git -C wt-2 rebase conflict-2 && test_must_fail git branch -f wt-2 HEAD 2>err && - grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err + test_grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err ' test_expect_success 'refuse to overwrite: worktree in rebase with --update-refs' ' @@ -89,19 +89,19 @@ for i in 3 4 do test_must_fail git branch -f can-be-updated HEAD 2>err && - grep "cannot force update the branch '\''can-be-updated'\'' used by worktree at.*wt-3" err || + test_grep "cannot force update the branch '\''can-be-updated'\'' used by worktree at.*wt-3" err || return 1 done ' test_expect_success 'refuse to fetch over ref: checked out' ' test_must_fail git fetch server +refs/heads/wt-3:refs/heads/wt-3 2>err && - grep "refusing to fetch into branch '\''refs/heads/wt-3'\''" err && + test_grep "refusing to fetch into branch '\''refs/heads/wt-3'\''" err && # General fetch into refs/heads/ will fail on first ref, # so use a generic error message check. test_must_fail git fetch server +refs/heads/*:refs/heads/* 2>err && - grep "refusing to fetch into branch" err + test_grep "refusing to fetch into branch" err ' test_expect_success 'refuse to fetch over ref: worktree in bisect' ' @@ -113,7 +113,7 @@ git -C wt-4 bisect good wt-1 && test_must_fail git fetch server +refs/heads/wt-4:refs/heads/wt-4 2>err && - grep "refusing to fetch into branch" err + test_grep "refusing to fetch into branch" err ' test_expect_success 'refuse to fetch over ref: worktree in rebase' ' @@ -123,7 +123,7 @@ test_must_fail git -C wt-3 rebase conflict-3 && test_must_fail git fetch server +refs/heads/wt-3:refs/heads/wt-3 2>err && - grep "refusing to fetch into branch" err + test_grep "refusing to fetch into branch" err ' test_expect_success 'refuse to overwrite when in error states' ' @@ -149,7 +149,7 @@ for i in 1 2 do test_must_fail git branch -f fake-$i HEAD 2>err && - grep "cannot force update the branch '\''fake-$i'\'' used by worktree at" err || + test_grep "cannot force update the branch '\''fake-$i'\'' used by worktree at" err || return 1 done ' @@ -161,13 +161,13 @@ ( set_cat_todo_editor && test_must_fail git rebase -i --update-refs HEAD~3 >todo && - ! grep "update-refs" todo + test_grep ! "update-refs" todo ) && git branch -f allow-update HEAD~2 && ( set_cat_todo_editor && test_must_fail git rebase -i --update-refs HEAD~3 >todo && - grep "update-ref refs/heads/allow-update" todo + test_grep "update-ref refs/heads/allow-update" todo ) '
diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh index 5c0bf4d..8e84f29 100755 --- a/t/t2500-untracked-overwriting.sh +++ b/t/t2500-untracked-overwriting.sh
@@ -51,7 +51,7 @@ test_must_fail git reset --merge work 2>error && test_cmp expect foo.t/file && - grep "Updating .foo.t. would lose untracked files" error + test_grep "Updating .foo.t. would lose untracked files" error ) ' @@ -66,7 +66,7 @@ test_must_fail git reset --merge work 2>error && test_cmp expect foo.t/file && - grep "Updating.*foo.t.*would lose untracked files" error + test_grep "Updating.*foo.t.*would lose untracked files" error ) ' @@ -214,7 +214,7 @@ test_must_fail git am --abort 2>errors && test_path_is_dir filler && - grep "Updating .filler. would lose untracked files in it" errors + test_grep "Updating .filler. would lose untracked files in it" errors ) ' @@ -237,7 +237,7 @@ # Change our mind about resolutions, just skip this patch test_must_fail git am --skip 2>errors && test_path_is_dir newfile && - grep "Updating .newfile. would lose untracked files in it" errors + test_grep "Updating .newfile. would lose untracked files in it" errors ) '
diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh index be9140b..eb6451d 100755 --- a/t/t2501-cwd-empty.sh +++ b/t/t2501-cwd-empty.sh
@@ -114,7 +114,7 @@ ) && test_path_is_dir dirORfile && - grep "Refusing to remove the current working directory" error + test_grep "Refusing to remove the current working directory" error ' test_expect_success 'merge fails if cwd needs to be removed' ' @@ -179,7 +179,7 @@ test_expect_success 'clean does not remove cwd incidentally' ' test_incidental_untracked_dir_removal \ git -C .. clean -fd -e warnings . >warnings && - grep "Refusing to remove current working directory" warnings + test_grep "Refusing to remove current working directory" warnings ' test_expect_success 'stash does not remove cwd incidentally' '
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index 202fb8d..29a0a25 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh
@@ -161,21 +161,21 @@ >two && git ls-files --others --exclude=two/ >output && - grep "^two" output + test_grep "^two" output ' test_expect_success 'trailing slash in exclude forces directory match (2)' ' git ls-files --others --exclude=one/a.1/ >output && - grep "^one/a.1" output + test_grep "^one/a.1" output ' test_expect_success 'negated exclude matches can override previous ones' ' git ls-files --others --exclude="a.*" --exclude="!a.1" >output && - grep "^a.1" output + test_grep "^a.1" output ' test_expect_success 'excluded directory overrides content patterns' '
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh index 4034a5a..c57afcb 100755 --- a/t/t3004-ls-files-basic.sh +++ b/t/t3004-ls-files-basic.sh
@@ -29,15 +29,15 @@ cd broken && git init && >.git/index && - test_expect_code 129 git ls-files -h >usage 2>&1 + git ls-files -h >usage 2>&1 ) && test_grep "[Uu]sage: git ls-files " broken/usage ' test_expect_success 'ls-files does not crash with -h' ' - test_expect_code 129 git ls-files -h >usage && + git ls-files -h >usage && test_grep "[Uu]sage: git ls-files " usage && - test_expect_code 129 nongit git ls-files -h >usage && + nongit git ls-files -h >usage && test_grep "[Uu]sage: git ls-files " usage '
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh index 61771ee..218964e 100755 --- a/t/t3007-ls-files-recurse-submodules.sh +++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -302,7 +302,7 @@ test_expect_success '--recurse-submodules parses submodule repo config' ' test_config -C submodule index.sparse "invalid non-boolean value" && test_must_fail git ls-files --recurse-submodules 2>err && - grep "bad boolean config value" err + test_grep "bad boolean config value" err ' test_expect_success '--recurse-submodules parses submodule worktree config' ' @@ -310,7 +310,7 @@ test_config -C submodule --worktree index.sparse "invalid non-boolean value" && test_must_fail git ls-files --recurse-submodules 2>err && - grep "bad boolean config value" err + test_grep "bad boolean config value" err ' test_expect_success '--recurse-submodules submodules ignore super project worktreeConfig extension' ' @@ -329,7 +329,7 @@ # With extensions.worktreeConfig disabled in the submodule, the invalid # worktree config is not picked up. git ls-files --recurse-submodules 2>err && - ! grep "bad boolean config value" err + test_grep ! "bad boolean config value" err ' test_incompatible_with_recurse_submodules () {
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index e7829c2..1ecbafb 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh
@@ -33,7 +33,7 @@ cd broken && git init -b main && >.git/refs/heads/main && - test_expect_code 129 git branch -h >usage 2>&1 + git branch -h >usage 2>&1 ) && test_grep "[Uu]sage" broken/usage ' @@ -204,7 +204,7 @@ test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' ' git reflog show HEAD >actual && - grep "HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam" actual + test_grep "HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam" actual ' test_expect_success 'git branch -M should leave orphaned HEAD alone' ' @@ -339,7 +339,7 @@ test_when_finished "git branch -D to-delete" && git branch to-delete main && test_must_fail git branch -d to-delete 2>err && - grep "not fully merged" err + test_grep "not fully merged" err ' test_expect_success 'git branch -d on orphan HEAD (unmerged, graph)' ' @@ -350,7 +350,7 @@ test_when_finished "rm -rf .git/objects/commit-graph*" && git commit-graph write --reachable && test_must_fail git branch -d to-delete 2>err && - grep "not fully merged" err + test_grep "not fully merged" err ' test_expect_success 'git branch -v -d t should work' ' @@ -712,7 +712,7 @@ test_expect_success 'git branch -C c1 c2 should never touch HEAD' ' msg="Branch: copied refs/heads/c1 to refs/heads/c2" && git reflog HEAD >actual && - ! grep "$msg$" actual + test_grep ! "$msg$" actual ' test_expect_success 'git branch -C main should work when main is checked out' ' @@ -930,7 +930,7 @@ git worktree add -b my7 my7 && test_must_fail git -C my7 branch -d my7 && test_must_fail git branch -d my7 2>actual && - grep "^error: cannot delete branch .my7. used by worktree at " actual && + test_grep "^error: cannot delete branch .my7. used by worktree at " actual && rm -r my7 && git worktree prune ' @@ -941,7 +941,7 @@ git -C my7 bisect start HEAD HEAD~2 && test_must_fail git -C my7 branch -d my7 && test_must_fail git branch -d my7 2>actual && - grep "^error: cannot delete branch .my7. used by worktree at " actual && + test_grep "^error: cannot delete branch .my7. used by worktree at " actual && rm -r my7 && git worktree prune ' @@ -1022,6 +1022,44 @@ test_cmp expect err ' +test_expect_success '--set-upstream-to suggests <remote>/<branch> on slip' ' + test_when_finished "git remote remove slip-remote" && + git remote add slip-remote . && + git update-ref refs/remotes/slip-remote/slip-feature HEAD && + test_must_fail git branch --set-upstream-to slip-remote slip-feature 2>err && + test_grep "takes a single <remote>/<branch> argument" err && + test_grep "hint: Did you mean to use: git branch --set-upstream-to=slip-remote/slip-feature?" err && + test_must_fail git -c advice.setUpstreamFailure=false \ + branch --set-upstream-to slip-remote slip-feature 2>err && + test_grep ! "Did you mean" err +' + +test_expect_success '--set-upstream-to does not suggest when no matching remote ref' ' + test_when_finished "git remote remove slip-remote" && + git remote add slip-remote . && + test_must_fail git branch --set-upstream-to slip-remote no-such-branch 2>err && + test_grep "branch ${SQ}no-such-branch${SQ} does not exist" err && + test_grep ! "Did you mean" err +' + +test_expect_success '--set-upstream-to to a local branch is not mistaken for a slip' ' + git branch slip-local-upstream && + git branch slip-local-target && + git branch --set-upstream-to=slip-local-upstream slip-local-target 2>err && + test_grep ! "Did you mean" err && + echo refs/heads/slip-local-upstream >expect && + git config branch.slip-local-target.merge >actual && + test_cmp expect actual +' + +test_expect_success '--set-upstream-to slip suggestion keeps a slashed branch name' ' + test_when_finished "git remote remove slip-remote" && + git remote add slip-remote . && + git update-ref refs/remotes/slip-remote/slip/feature HEAD && + test_must_fail git branch --set-upstream-to slip-remote slip/feature 2>err && + test_grep "hint: Did you mean to use: git branch --set-upstream-to=slip-remote/slip/feature?" err +' + test_expect_success '--set-upstream-to fails on a missing src branch' ' test_must_fail git branch --set-upstream-to does-not-exist main 2>err && test_grep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh index a1139f7..f402334 100755 --- a/t/t3202-show-branch.sh +++ b/t/t3202-show-branch.sh
@@ -123,13 +123,13 @@ git commit --allow-empty -m "another" && git show-branch --sparse >out && - grep "merge 1 and 10 to make A" out && + test_grep "merge 1 and 10 to make A" out && git show-branch >out && - ! grep "merge 1 and 10 to make A" out && + test_grep ! "merge 1 and 10 to make A" out && git show-branch --no-sparse >out && - ! grep "merge 1 and 10 to make A" out + test_grep ! "merge 1 and 10 to make A" out ' test_expect_success 'setup show branch --list' ' @@ -189,7 +189,7 @@ do test_expect_success "show-branch $combo (should fail)" ' test_must_fail git show-branch $combo 2>error && - grep -e "cannot be used together" -e "usage:" error + test_grep -e "cannot be used together" -e "usage:" error ' done <<\EOF --all --reflog @@ -203,7 +203,7 @@ do test_expect_success "show-branch --no-$opt (should fail)" ' test_must_fail git show-branch --no-$opt 2>err && - grep "unknown option .no-$opt." err + test_grep "unknown option .no-$opt." err ' done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index a6bd88a..7c44186 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh
@@ -64,7 +64,7 @@ test_expect_success 'git branch --no-remotes is rejected' ' test_must_fail git branch --no-remotes 2>err && - grep "unknown option .no-remotes." err + test_grep "unknown option .no-remotes." err ' cat >expect <<'EOF' @@ -85,7 +85,7 @@ test_expect_success 'git branch --no-all is rejected' ' test_must_fail git branch --no-all 2>err && - grep "unknown option .no-all." err + test_grep "unknown option .no-all." err ' cat >expect <<'EOF'
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index 1e812df..ef92704 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh
@@ -538,10 +538,10 @@ main..unmodified >actual && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && - grep "= 1: .* s/5/A" 0000-* && - grep "= 2: .* s/4/A" 0000-* && - grep "= 3: .* s/11/B" 0000-* && - grep "= 4: .* s/12/B" 0000-* + test_grep "= 1: .* s/5/A" 0000-* && + test_grep "= 2: .* s/4/A" 0000-* && + test_grep "= 3: .* s/11/B" 0000-* && + test_grep "= 4: .* s/12/B" 0000-* ' done @@ -564,7 +564,7 @@ git format-patch --range-diff=HEAD~1 HEAD~1 >actual && test_line_count = 1 actual && test_grep "^Range-diff:$" 0001-* && - grep "> 1: .* new message" 0001-* + test_grep "> 1: .* new message" 0001-* ' test_expect_success 'format-patch --range-diff reroll-count with a non-integer' ' @@ -572,7 +572,7 @@ git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual && test_line_count = 1 actual && test_grep "^Range-diff:$" v2.9-0001-* && - grep "> 1: .* new message" v2.9-0001-* + test_grep "> 1: .* new message" v2.9-0001-* ' test_expect_success 'format-patch --range-diff reroll-count with a integer' ' @@ -580,7 +580,7 @@ git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual && test_line_count = 1 actual && test_grep "^Range-diff ..* v1:$" v2-0001-* && - grep "> 1: .* new message" v2-0001-* + test_grep "> 1: .* new message" v2-0001-* ' test_expect_success 'format-patch --range-diff with v0' ' @@ -588,7 +588,7 @@ git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual && test_line_count = 1 actual && test_grep "^Range-diff:$" v0-0001-* && - grep "> 1: .* new message" v0-0001-* + test_grep "> 1: .* new message" v0-0001-* ' test_expect_success 'range-diff overrides diff.noprefix internally' ' @@ -686,8 +686,8 @@ git notes --ref=custom add -m "unmodified note" unmodified && git range-diff --notes=custom main..topic main..unmodified \ >actual && - ! grep "## Notes ##" actual && - grep "## Notes (custom) ##" actual + test_grep ! "## Notes ##" actual && + test_grep "## Notes (custom) ##" actual ' test_expect_success 'format-patch --range-diff does not compare notes by default' ' @@ -699,12 +699,12 @@ main..unmodified >actual && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && - grep "= 1: .* s/5/A" 0000-* && - grep "= 2: .* s/4/A" 0000-* && - grep "= 3: .* s/11/B" 0000-* && - grep "= 4: .* s/12/B" 0000-* && - ! grep "Notes" 0000-* && - ! grep "note" 0000-* + test_grep "= 1: .* s/5/A" 0000-* && + test_grep "= 2: .* s/4/A" 0000-* && + test_grep "= 3: .* s/11/B" 0000-* && + test_grep "= 4: .* s/12/B" 0000-* && + test_grep ! "Notes" 0000-* && + test_grep ! "note" 0000-* ' test_expect_success 'format-patch --notes=custom --range-diff --cover-letter only compares custom notes' ' @@ -717,8 +717,8 @@ test_when_finished "rm -f 000?-*" && git format-patch --notes=custom --cover-letter --range-diff=$prev \ main..unmodified >actual && - grep "## Notes (custom) ##" 0000-* && - ! grep "## Notes ##" 0000-* + test_grep "## Notes (custom) ##" 0000-* && + test_grep ! "## Notes ##" 0000-* ' # --range-diff on a single commit requires --no-cover-letter @@ -744,12 +744,12 @@ main..unmodified >actual && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && - grep "= 1: .* s/5/A" 0000-* && - grep "= 2: .* s/4/A" 0000-* && - grep "= 3: .* s/11/B" 0000-* && - grep "= 4: .* s/12/B" 0000-* && - ! grep "Notes" 0000-* && - ! grep "note" 0000-* + test_grep "= 1: .* s/5/A" 0000-* && + test_grep "= 2: .* s/4/A" 0000-* && + test_grep "= 3: .* s/11/B" 0000-* && + test_grep "= 4: .* s/12/B" 0000-* && + test_grep ! "Notes" 0000-* && + test_grep ! "note" 0000-* ' test_expect_success 'format-patch --range-diff with --notes' ' @@ -761,10 +761,10 @@ main..unmodified >actual && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && - grep "= 1: .* s/5/A" 0000-* && - grep "= 2: .* s/4/A" 0000-* && - grep "= 3: .* s/11/B" 0000-* && - grep "! 4: .* s/12/B" 0000-* && + test_grep "= 1: .* s/5/A" 0000-* && + test_grep "= 2: .* s/4/A" 0000-* && + test_grep "= 3: .* s/11/B" 0000-* && + test_grep "! 4: .* s/12/B" 0000-* && sed s/Z/\ /g >expect <<-EOF && @@ Commit message Z @@ -790,10 +790,10 @@ main..unmodified >actual && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && - grep "= 1: .* s/5/A" 0000-* && - grep "= 2: .* s/4/A" 0000-* && - grep "= 3: .* s/11/B" 0000-* && - grep "! 4: .* s/12/B" 0000-* && + test_grep "= 1: .* s/5/A" 0000-* && + test_grep "= 2: .* s/4/A" 0000-* && + test_grep "= 3: .* s/11/B" 0000-* && + test_grep "! 4: .* s/12/B" 0000-* && sed s/Z/\ /g >expect <<-EOF && @@ Commit message Z @@ -821,10 +821,10 @@ main..unmodified >actual && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && - grep "= 1: .* s/5/A" 0000-* && - grep "= 2: .* s/4/A" 0000-* && - grep "= 3: .* s/11/B" 0000-* && - grep "! 4: .* s/12/B" 0000-* && + test_grep "= 1: .* s/5/A" 0000-* && + test_grep "= 2: .* s/4/A" 0000-* && + test_grep "= 3: .* s/11/B" 0000-* && + test_grep "! 4: .* s/12/B" 0000-* && sed s/Z/\ /g >expect <<-EOF && @@ Commit message Z @@ -866,9 +866,9 @@ topic_oid=$(git rev-parse --short topic) && mode_change_oid=$(git rev-parse --short mode-only-change^) && file_change_oid=$(git rev-parse --short mode-only-change) && - grep "$mode_change_oid" actual && - ! grep "$file_change_oid" actual && - ! grep "$topic_oid" actual + test_grep "$mode_change_oid" actual && + test_grep ! "$file_change_oid" actual && + test_grep ! "$topic_oid" actual ' test_expect_success 'submodule changes are shown irrespective of diff.submodule' '
diff --git a/t/t3207-branch-submodule.sh b/t/t3207-branch-submodule.sh index fe72b24..4afb2d3 100755 --- a/t/t3207-branch-submodule.sh +++ b/t/t3207-branch-submodule.sh
@@ -136,7 +136,7 @@ git -C sub branch branch-a && test_must_fail git branch --recurse-submodules branch-a 2>actual && test_no_branch . branch-a && - grep "submodule .sub.: fatal: a branch named .branch-a. already exists" actual + test_grep "submodule .sub.: fatal: a branch named .branch-a. already exists" actual ) ' @@ -251,7 +251,7 @@ git branch --recurse-submodules branch-a origin/branch-a && # This should fail because super-clone does not have sub2 .git/modules test_must_fail git branch --recurse-submodules branch-b origin/branch-b 2>actual && - grep "fatal: submodule .sub2.: unable to find submodule" actual && + test_grep "fatal: submodule .sub2.: unable to find submodule" actual && test_no_branch . branch-b && test_no_branch sub branch-b && # User can fix themselves by initializing the submodule
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index d6c5046..18a6000 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh
@@ -164,7 +164,7 @@ ${indent}b1 EOF git cat-file commit HEAD >commits && - ! grep b1 commits && + test_grep ! b1 commits && git log -1 >actual && test_cmp expect actual ' @@ -248,17 +248,17 @@ test_expect_success 'git log --no-notes' ' git log -1 --no-notes >actual && - ! grep xyzzy actual + test_grep ! xyzzy actual ' test_expect_success 'git format-patch does not show notes' ' git format-patch -1 --stdout >actual && - ! grep xyzzy actual + test_grep ! xyzzy actual ' test_expect_success 'git format-patch --show-notes does show notes' ' git format-patch --show-notes -1 --stdout >actual && - grep xyzzy actual + test_grep xyzzy actual ' for pretty in \ @@ -281,36 +281,36 @@ test_expect_success 'git log --notes shows default notes' ' git log -1 --notes >actual && - grep xyzzy actual && - ! grep alternate actual + test_grep xyzzy actual && + test_grep ! alternate actual ' test_expect_success 'git log --notes=X shows only X' ' git log -1 --notes=alternate >actual && - ! grep xyzzy actual && - grep alternate actual + test_grep ! xyzzy actual && + test_grep alternate actual ' test_expect_success 'git log --notes --notes=X shows both' ' git log -1 --notes --notes=alternate >actual && - grep xyzzy actual && - grep alternate actual + test_grep xyzzy actual && + test_grep alternate actual ' test_expect_success 'git log --no-notes resets default state' ' git log -1 --notes --notes=alternate \ --no-notes --notes=alternate \ >actual && - ! grep xyzzy actual && - grep alternate actual + test_grep ! xyzzy actual && + test_grep alternate actual ' test_expect_success 'git log --no-notes resets ref list' ' git log -1 --notes --notes=alternate \ --no-notes --notes \ >actual && - grep xyzzy actual && - ! grep alternate actual + test_grep xyzzy actual && + test_grep ! alternate actual ' test_expect_success 'show -m notes' ' @@ -543,7 +543,7 @@ test_expect_success '"git notes" without subcommand does not take arguments' ' test_expect_code 129 git notes HEAD^^ 2>err && - grep "^error: unknown subcommand" err + test_grep "^error: unknown subcommand" err ' test_expect_success 'list specific note with "git notes list <object>"' ' @@ -1464,7 +1464,7 @@ GIT_NOTES_REWRITE_REF=refs/notes/commits \ git notes copy --for-rewrite=foo <copy && git log -1 >actual && - grep "replacement note 3" actual + test_grep "replacement note 3" actual ' test_expect_success 'git notes copy diagnoses too many or too few arguments' '
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh index 0bb366f..1d61c99 100755 --- a/t/t3310-notes-merge-manual-resolve.sh +++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -382,12 +382,12 @@ test_cmp pre_merge_z actual && # Merge commit mentions the notes refs merged git log -1 --format=%B refs/notes/m > merge_commit_msg && - grep -q refs/notes/m merge_commit_msg && - grep -q refs/notes/z merge_commit_msg && + test_grep -q refs/notes/m merge_commit_msg && + test_grep -q refs/notes/z merge_commit_msg && # Merge commit mentions conflicting notes - grep -q "Conflicts" merge_commit_msg && + test_grep -q "Conflicts" merge_commit_msg && ( for sha1 in $(cat expect_conflicts); do - grep -q "$sha1" merge_commit_msg || + test_grep -q "$sha1" merge_commit_msg || exit 1 done ) && # Verify contents of merge result @@ -512,12 +512,12 @@ test_cmp pre_merge_z actual && # Merge commit mentions the notes refs merged git log -1 --format=%B refs/notes/m > merge_commit_msg && - grep -q refs/notes/m merge_commit_msg && - grep -q refs/notes/z merge_commit_msg && + test_grep -q refs/notes/m merge_commit_msg && + test_grep -q refs/notes/z merge_commit_msg && # Merge commit mentions conflicting notes - grep -q "Conflicts" merge_commit_msg && + test_grep -q "Conflicts" merge_commit_msg && ( for sha1 in $(cat expect_conflicts); do - grep -q "$sha1" merge_commit_msg || + test_grep -q "$sha1" merge_commit_msg || exit 1 done ) && # Verify contents of merge result
diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh index 96243b7..9a2c3ac 100755 --- a/t/t3320-notes-merge-worktrees.sh +++ b/t/t3320-notes-merge-worktrees.sh
@@ -67,7 +67,7 @@ git config core.notesRef refs/notes/x && test_must_fail git notes merge z >out 2>&1 && test_grep "Automatic notes merge failed" out && - grep -v "A notes merge into refs/notes/x is already in-progress in" out + test_grep -v "A notes merge into refs/notes/x is already in-progress in" out ) && echo "refs/notes/x" >expect && git -C worktree2 symbolic-ref NOTES_MERGE_REF >actual &&
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index c0c00fb..e62e07b 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh
@@ -287,16 +287,16 @@ git commit --date="@34567 +0600" -m "Old three" && git cat-file commit HEAD^^ >actual && - grep "author .* 12345 +0400$" actual && + test_grep "author .* 12345 +0400$" actual && git cat-file commit HEAD^ >actual && - grep "author .* 23456 +0500$" actual && + test_grep "author .* 23456 +0500$" actual && git cat-file commit HEAD >actual && - grep "author .* 34567 +0600$" actual && + test_grep "author .* 34567 +0600$" actual && git rebase --onto HEAD^^ HEAD^ && git cat-file commit HEAD >actual && - grep "author .* 34567 +0600$" actual + test_grep "author .* 34567 +0600$" actual ' test_expect_success 'rebase with "From " line in commit message' ' @@ -333,7 +333,7 @@ git tag two && test_must_fail git rebase --apply -f --onto init HEAD^ && GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr && - grep "show.*$(git rev-parse two)" stderr + test_grep "show.*$(git rev-parse two)" stderr ) ' @@ -364,12 +364,12 @@ git checkout test && git rebase main && - grep "smudged" a.txt && + test_grep "smudged" a.txt && git checkout removal && git reset --hard && git rebase main && - grep "clean" a.txt + test_grep "clean" a.txt ) ' @@ -386,7 +386,7 @@ test_must_fail git rebase --merge --onto init HEAD^ && git rebase --show-current-patch >actual.patch && GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr && - grep "show.*REBASE_HEAD" stderr && + test_grep "show.*REBASE_HEAD" stderr && test "$(git rev-parse REBASE_HEAD)" = "$(git rev-parse two)" ) '
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index 761de63..41ebcde 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh
@@ -84,8 +84,8 @@ echo "AB $T" >> original && git commit -mconflicting original && git rebase -Xtheirs main && - grep AB original && - ! grep 11 original + test_grep AB original && + test_grep ! 11 original ' test_expect_success 'rebase -Xtheirs from orphan' ' @@ -93,8 +93,8 @@ echo "AB $T" >> original && git commit -morphan-conflicting original && git rebase -Xtheirs main && - grep AB original && - ! grep 11 original + test_grep AB original && + test_grep ! 11 original ' test_expect_success 'merge and rebase should match' ' @@ -210,15 +210,15 @@ git -C client rev-list --objects --all --missing=print >missing_list && MERGE_BASE_BLOB=$(git -C server rev-parse main^^:file.txt) && ADD_11_BLOB=$(git -C server rev-parse main^:file.txt) && - grep "[?]$MERGE_BASE_BLOB" missing_list && - grep "[?]$ADD_11_BLOB" missing_list && + test_grep "[?]$MERGE_BASE_BLOB" missing_list && + test_grep "[?]$ADD_11_BLOB" missing_list && git -C client rebase --merge --reapply-cherry-picks origin/main && # The blob from the merge base had to be fetched, but not "add 11" git -C client rev-list --objects --all --missing=print >missing_list && - ! grep "[?]$MERGE_BASE_BLOB" missing_list && - grep "[?]$ADD_11_BLOB" missing_list + test_grep ! "[?]$MERGE_BASE_BLOB" missing_list && + test_grep "[?]$ADD_11_BLOB" missing_list ' test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 58b3bb0..e648167 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh
@@ -144,7 +144,7 @@ mkdir subdir && git rebase -x "(cd subdir && git rev-parse --show-toplevel)" HEAD^ \ >actual && - ! grep "/subdir$" actual + test_grep ! "/subdir$" actual ' test_expect_success 'rebase -i with the exec command checks tree cleanness' ' @@ -196,7 +196,7 @@ test_must_fail env FAKE_LINES="exec_this-command-does-not-exist 1" \ git rebase -i HEAD^ >actual 2>&1 ) && - ! grep "Maybe git-rebase is broken" actual + test_grep ! "Maybe git-rebase is broken" actual ' test_expect_success 'implicit interactive rebase does not invoke sequence editor' ' @@ -293,7 +293,7 @@ test_expect_success 'show conflicted patch' ' GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr && - grep "show.*REBASE_HEAD" stderr && + test_grep "show.*REBASE_HEAD" stderr && # the original stopped-sha1 is abbreviated stopped_sha1="$(git rev-parse $(cat ".git/rebase-merge/stopped-sha"))" && test "$(git rev-parse REBASE_HEAD)" = "$stopped_sha1" @@ -326,7 +326,7 @@ git tag twerp && git rebase -i --onto primary HEAD^ && git show HEAD >actual && - grep "^Author: Twerp Snog" actual + test_grep "^Author: Twerp Snog" actual ' test_expect_success 'retain authorship w/ conflicts' ' @@ -348,7 +348,7 @@ git rebase --continue && test_cmp_rev conflict-a^0 HEAD^ && git show >out && - grep AttributeMe out + test_grep AttributeMe out ' test_expect_success 'squash' ' @@ -368,7 +368,7 @@ test_expect_success 'retain authorship when squashing' ' git show HEAD >actual && - grep "^Author: Twerp Snog" actual + test_grep "^Author: Twerp Snog" actual ' test_expect_success '--continue tries to commit' ' @@ -383,7 +383,7 @@ ) && test_cmp_rev HEAD^ new-branch1 && git show HEAD >actual && - grep chouette actual + test_grep chouette actual ' test_expect_success 'verbose flag is heeded, even after --continue' ' @@ -393,7 +393,7 @@ echo resolved > file1 && git add file1 && git rebase --continue > output && - grep "^ file1 | 2 +-$" output + test_grep "^ file1 | 2 +-$" output ' test_expect_success 'multi-squash only fires up editor once' ' @@ -422,7 +422,7 @@ ) && test $base = $(git rev-parse HEAD^) && git show >output && - ! grep NEVER output && + test_grep ! NEVER output && git checkout @{-1} && git branch -D multi-fixup ' @@ -487,9 +487,9 @@ git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup && test_cmp expect-squash-fixup actual-squash-fixup && git cat-file commit HEAD@{2} >actual && - grep "^# This is a combination of 3 commits\." actual && + test_grep "^# This is a combination of 3 commits\." actual && git cat-file commit HEAD@{3} >actual && - grep "^# This is a combination of 2 commits\." actual && + test_grep "^# This is a combination of 2 commits\." actual && git checkout @{-1} && git branch -D squash-fixup ' @@ -593,7 +593,7 @@ ) && test edited = $(git show HEAD:file7) && git show HEAD >actual && - grep chouette actual && + test_grep chouette actual && test $parent = $(git rev-parse HEAD^) ' @@ -779,22 +779,22 @@ FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" \ git rebase -i A && git show HEAD >actual && - grep "E changed" actual && + test_grep "E changed" actual && test $(git rev-parse primary) != $(git rev-parse HEAD) && test_cmp_rev primary^ HEAD^ && FAKE_LINES="1 2 reword 3 4" FAKE_COMMIT_MESSAGE="D changed" \ git rebase -i A && git show HEAD^ >actual && - grep "D changed" actual && + test_grep "D changed" actual && FAKE_LINES="reword 1 2 3 4" FAKE_COMMIT_MESSAGE="B changed" \ git rebase -i A && git show HEAD~3 >actual && - grep "B changed" actual && + test_grep "B changed" actual && FAKE_LINES="1 r 2 pick 3 p 4" FAKE_COMMIT_MESSAGE="C changed" \ git rebase -i A ) && git show HEAD~2 >actual && - grep "C changed" actual + test_grep "C changed" actual ' test_expect_success 'reword fast-forwarded empty commit' ' @@ -1043,9 +1043,9 @@ FAKE_LINES="2" git rebase -i --root ) && git cat-file commit HEAD >output && - grep -q "^author Twerp Snog" output && + test_grep -q "^author Twerp Snog" output && git cat-file commit HEAD >actual && - grep -q "^different author$" actual + test_grep -q "^different author$" actual ' test_expect_success 'rebase -i --root temporary sentinel commit' ' @@ -1055,7 +1055,7 @@ test_must_fail env FAKE_LINES="2" git rebase -i --root ) && git cat-file commit HEAD >actual && - grep "^tree $EMPTY_TREE" actual && + test_grep "^tree $EMPTY_TREE" actual && git rebase --abort ' @@ -1079,7 +1079,7 @@ git rebase -i --root ) && git show HEAD^ >actual && - grep "A changed" actual && + test_grep "A changed" actual && test -z "$(git show -s --format=%p HEAD^)" ' @@ -1092,7 +1092,7 @@ git rebase -i --root ) && git show HEAD^ >actual && - grep "C changed" actual && + test_grep "C changed" actual && test -z "$(git show -s --format=%p HEAD^)" ' @@ -1315,11 +1315,11 @@ FAKE_COMMIT_MESSAGE="collide2 $(test_oid t3404_collider)" \ FAKE_LINES="reword 1 break 2" git rebase -i HEAD~2 && test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" && - grep "^pick $colliding_id " \ + test_grep "^pick $colliding_id " \ .git/rebase-merge/git-rebase-todo.tmp && - grep -E "^pick [0-9a-f]{$hexsz}" \ + test_grep -E "^pick [0-9a-f]{$hexsz}" \ .git/rebase-merge/git-rebase-todo && - grep -E "^pick [0-9a-f]{$hexsz}" \ + test_grep -E "^pick [0-9a-f]{$hexsz}" \ .git/rebase-merge/git-rebase-todo.backup && git rebase --continue ) && @@ -1371,7 +1371,7 @@ echo changed >file1 && git add file1 && test_must_fail git rebase --continue 2>err && - grep "error: you have staged changes in your working tree" err && + test_grep "error: you have staged changes in your working tree" err && git reset --hard HEAD && git rebase --continue && test_cmp_rev HEAD D && @@ -1398,7 +1398,7 @@ echo changed >file1 && git add file1 && test_must_fail git rebase --continue 2>err && - grep "error: you have staged changes in your working tree" err && + test_grep "error: you have staged changes in your working tree" err && git reset --hard HEAD && git rebase --continue && test $(git cat-file commit HEAD | sed -ne \$p) = I && @@ -1423,7 +1423,7 @@ echo changed >file1 && git add file1 && test_must_fail git rebase --continue 2>err && - grep "error: you have staged changes in your working tree" err && + test_grep "error: you have staged changes in your working tree" err && git reset --hard HEAD && git rebase --continue && test $(git cat-file commit HEAD | sed -ne \$p) = I @@ -1709,15 +1709,15 @@ set_replace_editor orig && test_must_fail git rebase -i A 2>actual ) && - grep "cannot .fixup. without a previous commit" actual && - grep "You can fix this with .git rebase --edit-todo.." actual && + test_grep "cannot .fixup. without a previous commit" actual && + test_grep "You can fix this with .git rebase --edit-todo.." actual && # verify that the todo list has not been truncated grep -v "^#" .git/rebase-merge/git-rebase-todo >actual && test_cmp orig actual && test_must_fail git rebase --edit-todo 2>actual && - grep "cannot .fixup. without a previous commit" actual && - grep "You can fix this with .git rebase --edit-todo.." actual && + test_grep "cannot .fixup. without a previous commit" actual && + test_grep "You can fix this with .git rebase --edit-todo.." actual && # verify that the todo list has not been truncated grep -v "^#" .git/rebase-merge/git-rebase-todo >actual && test_cmp orig actual @@ -2252,7 +2252,7 @@ git update-ref refs/heads/second third && test_must_fail git rebase --continue 2>err && - grep "update_ref failed for ref '\''refs/heads/second'\''" err && + test_grep "update_ref failed for ref '\''refs/heads/second'\''" err && q_to_tab >expect <<-\EOF && Updated the following refs with --update-refs: @@ -2283,10 +2283,10 @@ set_replace_editor todo && test_must_fail git rebase -i HEAD 2>err ) && - grep "'\''#'\'' is not a valid label" err && - grep "'\'':invalid'\'' is not a valid label" err && - grep "'\'':bad'\'' is not a valid refname" err && - grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \ + test_grep "'\''#'\'' is not a valid label" err && + test_grep "'\'':invalid'\'' is not a valid label" err && + test_grep "'\'':bad'\'' is not a valid refname" err && + test_grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \ err && test_path_is_missing execed '
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh index bc51a9d..7b61188 100755 --- a/t/t3406-rebase-message.sh +++ b/t/t3406-rebase-message.sh
@@ -62,21 +62,21 @@ test_expect_success 'rebase --stat' ' git reset --hard start && git rebase --stat main >diffstat.txt && - grep "^ fileX | *1 +$" diffstat.txt + test_grep "^ fileX | *1 +$" diffstat.txt ' test_expect_success 'rebase w/config rebase.stat' ' git reset --hard start && git config rebase.stat true && git rebase main >diffstat.txt && - grep "^ fileX | *1 +$" diffstat.txt + test_grep "^ fileX | *1 +$" diffstat.txt ' test_expect_success 'rebase -n overrides config rebase.stat config' ' git reset --hard start && git config rebase.stat true && git rebase -n main >diffstat.txt && - ! grep "^ fileX | *1 +$" diffstat.txt + test_grep ! "^ fileX | *1 +$" diffstat.txt ' test_expect_success 'rebase --onto outputs the invalid ref' '
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index 5033411..07a5a11 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh
@@ -223,7 +223,7 @@ git cat-file blob HEAD^:file1 >actual && test_cmp expect actual && git cat-file commit HEAD^ >commit && - ! grep "squash" commit && + test_grep ! "squash" commit && grep "^extra para" commit >actual && test_line_count = 1 actual ' @@ -245,7 +245,7 @@ git cat-file blob HEAD^:file1 >actual && test_cmp expect actual && git cat-file commit HEAD^ >commit && - ! grep "squash" commit && + test_grep ! "squash" commit && grep "^extra para" commit >actual && test_line_count = 1 actual ' @@ -377,7 +377,7 @@ git cat-file blob HEAD^:file1 >actual && test_cmp expect actual && git cat-file commit HEAD^ >commit && - ! grep "squash" commit && + test_grep ! "squash" commit && grep first commit >actual && test_line_count = 3 actual ' @@ -424,7 +424,7 @@ set_backup_editor && GIT_USE_REBASE_HELPER=false \ git rebase -i --force-rebase --autosquash HEAD~4 && - grep empty2 .git/backup-git-rebase-todo + test_grep empty2 .git/backup-git-rebase-todo ) ' @@ -467,7 +467,7 @@ git commit --allow-empty --amend -m edited-first && git rebase --skip && git show >actual && - ! grep first actual + test_grep ! first actual ' test_expect_success 'fixup a fixup' '
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh index ea501f2..e7f725b 100755 --- a/t/t3416-rebase-onto-threedots.sh +++ b/t/t3416-rebase-onto-threedots.sh
@@ -107,7 +107,7 @@ git reset --hard K && test_must_fail git rebase -i --onto main...side J 2>err && - grep "need exactly one merge base" err + test_grep "need exactly one merge base" err ' test_expect_success 'rebase --keep-base --onto incompatible' ' @@ -196,7 +196,7 @@ git reset --hard K && test_must_fail git rebase -i --keep-base main 2>err && - grep "need exactly one merge base with branch" err + test_grep "need exactly one merge base with branch" err ' test_expect_success 'rebase --keep-base keeps cherry picks' '
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index f9b8999..03e0714 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh
@@ -289,18 +289,18 @@ test_expect_success '--reschedule-failed-exec' ' test_when_finished "git rebase --abort" && test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ && - grep "^exec false" .git/rebase-merge/git-rebase-todo && + test_grep "^exec false" .git/rebase-merge/git-rebase-todo && git rebase --abort && test_must_fail git -c rebase.rescheduleFailedExec=true \ rebase -x false HEAD^ 2>err && - grep "^exec false" .git/rebase-merge/git-rebase-todo && + test_grep "^exec false" .git/rebase-merge/git-rebase-todo && test_grep "has been rescheduled" err ' test_expect_success 'rebase.rescheduleFailedExec only affects `rebase -i`' ' test_config rebase.rescheduleFailedExec true && test_must_fail git rebase -x false HEAD^ && - grep "^exec false" .git/rebase-merge/git-rebase-todo && + test_grep "^exec false" .git/rebase-merge/git-rebase-todo && git rebase --abort && git rebase HEAD^ ' @@ -310,7 +310,7 @@ test_config rebase.rescheduleFailedExec true && test_must_fail git rebase -x false --no-reschedule-failed-exec HEAD~2 && test_must_fail git rebase --continue 2>err && - ! grep "has been rescheduled" err + test_grep ! "has been rescheduled" err ' test_expect_success 'new rebase.rescheduleFailedExec=true setting in an ongoing rebase is ignored' ' @@ -318,7 +318,7 @@ test_must_fail git rebase -x false HEAD~2 && test_config rebase.rescheduleFailedExec true && test_must_fail git rebase --continue 2>err && - ! grep "has been rescheduled" err + test_grep ! "has been rescheduled" err ' test_expect_success 'there is no --no-reschedule-failed-exec in an ongoing rebase' '
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh index f0bbc47..1e4deb2 100755 --- a/t/t3420-rebase-autostash.sh +++ b/t/t3420-rebase-autostash.sh
@@ -141,8 +141,8 @@ git checkout -b rebased-feature-branch feature-branch && echo dirty >>file3 && git rebase$type unrelated-onto-branch >actual 2>&1 && - grep unrelated file4 && - grep dirty file3 && + test_grep unrelated file4 && + test_grep dirty file3 && git checkout feature-branch ' @@ -165,8 +165,8 @@ echo dirty >>file3 && git add file3 && git rebase$type unrelated-onto-branch && - grep unrelated file4 && - grep dirty file3 && + test_grep unrelated file4 && + test_grep dirty file3 && git checkout feature-branch ' @@ -197,7 +197,7 @@ git add file2 && git rebase --continue && test_path_is_missing $dotest/autostash && - grep dirty file3 && + test_grep dirty file3 && git checkout feature-branch ' @@ -212,7 +212,7 @@ test_path_is_missing file3 && git rebase --skip && test_path_is_missing $dotest/autostash && - grep dirty file3 && + test_grep dirty file3 && git checkout feature-branch ' @@ -227,7 +227,7 @@ test_path_is_missing file3 && git rebase --abort && test_path_is_missing $dotest/autostash && - grep dirty file3 && + test_grep dirty file3 && git checkout feature-branch ' @@ -244,7 +244,7 @@ git rebase --quit && test_when_finished git stash drop && test_path_is_missing $dotest/autostash && - ! grep dirty file3 && + test_path_is_missing file3 && git stash show -p >actual && test_cmp expect actual && git reset --hard && @@ -260,11 +260,11 @@ git rebase$type unrelated-onto-branch >actual 2>&1 && test_path_is_missing $dotest && git reset --hard && - grep unrelated file4 && - ! grep dirty file4 && + test_grep unrelated file4 && + test_grep ! dirty file4 && git checkout feature-branch && git stash pop && - grep dirty file4 + test_grep dirty file4 ' test_expect_success "rebase$type: check output with conflicting stash" ' @@ -286,7 +286,7 @@ test_when_finished git branch -D behind-feature-branch && echo dirty >>file1 && git rebase feature-branch && - grep dirty file1 && + test_grep dirty file1 && git checkout feature-branch ' @@ -297,7 +297,7 @@ test_when_finished git branch -D same-feature-branch && echo dirty >>file1 && git rebase feature-branch && - grep dirty file1 && + test_grep dirty file1 && git checkout feature-branch '
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh index b9408f9..d6830b8 100755 --- a/t/t3422-rebase-incompatible-options.sh +++ b/t/t3422-rebase-incompatible-options.sh
@@ -102,13 +102,13 @@ test_expect_success "$opt incompatible with rebase.rebaseMerges" " git checkout B^0 && test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err && - grep -e --no-rebase-merges err + test_grep -e --no-rebase-merges err " test_expect_success "$opt incompatible with rebase.updateRefs" " git checkout B^0 && test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err && - grep -e --no-update-refs err + test_grep -e --no-update-refs err " test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
diff --git a/t/t3429-rebase-edit-todo.sh b/t/t3429-rebase-edit-todo.sh index abd66f3..28e4bdd 100755 --- a/t/t3429-rebase-edit-todo.sh +++ b/t/t3429-rebase-edit-todo.sh
@@ -19,7 +19,7 @@ test_expect_success 'rebase exec with an empty list does not exec anything' ' git rebase HEAD -x "true" 2>output && - ! grep "Executing: true" output + test_grep ! "Executing: true" output ' test_expect_success 'loose object cache vs re-reading todo list' '
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 84b2d0e..9bd61c5 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh
@@ -161,7 +161,7 @@ test_when_finished "test_might_fail git rebase --abort" && test_must_fail env GIT_SEQUENCE_EDITOR="echo reset A^{tree} >" \ git rebase -i B C >out 2>err && - grep "object .* is a tree" err && + test_grep "object .* is a tree" err && test_must_be_empty out ' @@ -170,7 +170,7 @@ git branch refs/rewritten/my-label A && test_must_fail env GIT_SEQUENCE_EDITOR="echo reset my-label >" \ git rebase -i B C >out 2>err && - grep "could not resolve ${SQ}my-label${SQ}" err && + test_grep "could not resolve ${SQ}my-label${SQ}" err && test_must_be_empty out ' @@ -185,18 +185,18 @@ test_tick && test_must_fail git rebase -ir HEAD && test_cmp_rev REBASE_HEAD H^0 && - grep "^merge -C .* G$" .git/rebase-merge/done && - grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo && + test_grep "^merge -C .* G$" .git/rebase-merge/done && + test_grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo && test_path_is_missing .git/rebase-merge/patch && echo changed >file1 && git add file1 && test_must_fail git rebase --continue 2>err && - grep "error: you have staged changes in your working tree" err && + test_grep "error: you have staged changes in your working tree" err && : fail because of merge conflict && git reset --hard conflicting-G && test_must_fail git rebase --continue && - ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo && + test_grep ! "^merge -C .* G$" .git/rebase-merge/git-rebase-todo && test_path_is_file .git/rebase-merge/patch ' @@ -208,8 +208,8 @@ test_config sequence.editor \""$PWD"/replace-editor.sh\" && test_tick && test_must_fail git rebase -ir HEAD && - ! grep "^merge G$" .git/rebase-merge/git-rebase-todo && - grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message + test_grep ! "^merge G$" .git/rebase-merge/git-rebase-todo && + test_grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message ' test_expect_success 'merge -c commits before rewording and reloads todo-list' ' @@ -481,8 +481,8 @@ test_config sequence.editor \""$PWD"/replace-editor.sh\" && test_tick && git rebase -i -r A && - grep "^label $third-" .git/ORIGINAL-TODO && - ! grep "^label $third$" .git/ORIGINAL-TODO + test_grep "^label $third-" .git/ORIGINAL-TODO && + test_grep ! "^label $third$" .git/ORIGINAL-TODO ' test_expect_success 'octopus merges' ' @@ -533,9 +533,9 @@ EOF test_tick && git rebase -ir --autosquash --exec ./show.sh A >actual && - grep "B: +Booh" actual && - grep "E: +Booh" actual && - grep "G: +G" actual + test_grep "B: +Booh" actual && + test_grep "E: +Booh" actual && + test_grep "G: +G" actual ' test_expect_success '--continue after resolving conflicts after a merge' ' @@ -546,7 +546,7 @@ git checkout -b conflicts-in-merge H && test_commit H2 H2.t conflicts H2-conflict && test_must_fail git rebase -r already-has-g && - grep conflicts H2.t && + test_grep conflicts H2.t && echo resolved >H2.t && git add -u && git rebase --continue && @@ -616,9 +616,9 @@ done="$(git rev-parse --git-path rebase-merge/done)" && git -c rebase.maxLabelLength=14 rebase --rebase-merges -x "cp \"$done\" out" --root && - grep "label 0123456789-我$" out && + test_grep "label 0123456789-我$" out && git -c rebase.maxLabelLength=13 rebase --rebase-merges -x "cp \"$done\" out" --root && - grep "label 0123456789-$" out + test_grep "label 0123456789-$" out ' test_expect_success 'reword fast-forwarded empty merge commit' '
diff --git a/t/t3454-history-drop.sh b/t/t3454-history-drop.sh new file mode 100755 index 0000000..68a86d1 --- /dev/null +++ b/t/t3454-history-drop.sh
@@ -0,0 +1,561 @@ +#!/bin/sh + +test_description='tests for git-history drop subcommand' + +. ./test-lib.sh +. "$TEST_DIRECTORY/lib-log-graph.sh" + +expect_graph () { + cat >expect && + lib_test_cmp_graph --format=%s "$@" +} + +expect_log () { + git log --format="%s" "$@" >actual && + cat >expect && + test_cmp expect actual +} + +test_expect_success 'errors on missing commit argument' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + test_must_fail git history drop 2>err && + test_grep "command expects a single revision" err + ) +' + +test_expect_success 'errors on too many arguments' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + test_must_fail git history drop HEAD HEAD 2>err && + test_grep "command expects a single revision" err + ) +' + +test_expect_success 'errors on unknown revision' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + test_must_fail git history drop does-not-exist 2>err && + test_grep "commit cannot be found: does-not-exist" err + ) +' + +test_expect_success 'errors with invalid --empty= value' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + test_commit second && + test_must_fail git history drop --empty=bogus HEAD 2>err && + test_grep "unrecognized.*--empty.*bogus" err + ) +' + +test_expect_success 'drops a commit in the middle and replays descendants' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + test_commit second && + test_commit third && + + git symbolic-ref HEAD >expect && + git history drop HEAD~ && + git symbolic-ref HEAD >actual && + test_cmp expect actual && + + expect_log <<-\EOF && + third + first + EOF + + test_must_fail git show HEAD:second.t && + test_path_is_missing second.t && + + git reflog >reflog && + test_grep "drop: dropping HEAD~" reflog + ) +' + +test_expect_success 'drops the HEAD commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + test_commit second && + + git history drop HEAD && + + expect_log <<-\EOF + first + EOF + ) +' + +test_expect_success 'drops a commit on detached HEAD' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + test_commit second && + test_commit third && + git checkout --detach HEAD && + + git history drop HEAD~ && + + expect_log <<-\EOF + third + first + EOF + ) +' + +# Note: in this case it would actually be fine to drop the root commit, as we +# do have a descendant commit, and no reference points to the root commit +# directly. So this is something that we may relax eventually. +test_expect_success 'refuses to drop the root commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + test_commit second && + + test_must_fail git history drop HEAD~ 2>err && + test_grep "cannot drop root commit" err + ) +' + +# In contrast to the above case, we actually don't want to drop the root commit +# here as that would cause us to end up with an empty commit graph. +test_expect_success 'refuses to drop the root commit when branch becomes empty' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + + test_must_fail git history drop HEAD 2>err && + test_grep "cannot drop root commit" err + ) +' + +test_expect_success 'refuses to drop a merge commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + git branch branch && + test_commit ours && + git switch branch && + test_commit theirs && + git switch - && + git merge theirs && + + test_must_fail git history drop HEAD 2>err && + test_grep "cannot drop merge commit" err + ) +' + +test_expect_success 'refuses when descendants contain a merge commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + test_commit middle && + git branch branch && + test_commit ours && + git switch branch && + test_commit theirs && + git switch - && + git merge theirs && + + test_must_fail git history drop middle 2>err && + test_grep "replaying merge commits is not supported yet" err + ) +' + +test_expect_success 'works in a bare repository' ' + test_when_finished "rm -rf repo repo.git" && + + git init repo && + test_commit -C repo first && + test_commit -C repo second && + test_commit -C repo third && + + git clone --bare repo repo.git && + ( + cd repo.git && + + git history drop HEAD~ && + expect_log <<-\EOF + third + first + EOF + ) +' + +test_expect_success 'updates branches on other lines of descent' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + test_commit target && + git branch theirs && + test_commit ours && + git switch theirs && + test_commit theirs && + + expect_graph --branches <<-\EOF && + * theirs + | * ours + |/ + * target + * base + EOF + + git history drop target && + + expect_graph --branches <<-\EOF + * ours + | * theirs + |/ + * base + EOF + ) +' + +test_expect_success 'moves branch pointing at dropped commit to its parent' ' + test_when_finished "rm -rf repo" && + git init repo --initial-branch=main && + ( + cd repo && + test_commit first && + test_commit second && + git branch points-at-second && + test_commit third && + + git rev-parse first >expect && + git history drop second && + git rev-parse points-at-second >actual && + test_cmp expect actual && + + expect_log --format="%s %D" --branches <<-\EOF + third HEAD -> main + first tag: first, points-at-second + EOF + ) +' + +test_expect_success '--dry-run prints ref updates without modifying repo' ' + test_when_finished "rm -rf repo" && + git init repo --initial-branch=main && + ( + cd repo && + test_commit base && + git branch branch && + test_commit middle && + test_commit ours && + git switch branch && + test_commit theirs && + + git refs list >refs-expect && + git history drop --dry-run main~ >updates && + git refs list >refs-actual && + test_cmp refs-expect refs-actual && + test_grep "update refs/heads/main" updates && + + git update-ref --stdin <updates && + expect_log main <<-\EOF + ours + base + EOF + ) +' + +test_expect_success '--dry-run detects conflicts with modified working tree' ' + test_when_finished "rm -rf repo" && + git init repo --initial-branch=main && + ( + cd repo && + test_commit first && + test_commit second modify-me && + echo modified >modify-me && + + git refs list >refs-expect && + git diff >diff-expect && + test_must_fail git history drop --dry-run HEAD 2>err && + test_grep "dropping this commit would overwrite local changes" err && + git diff >diff-actual && + git refs list >refs-actual && + + test_cmp diff-expect diff-actual && + test_cmp refs-expect refs-actual + ) +' + +test_expect_success '--update-refs=head updates only HEAD' ' + test_when_finished "rm -rf repo" && + git init repo --initial-branch=main && + ( + cd repo && + test_commit base && + test_commit target && + git branch theirs && + test_commit ours && + git switch theirs && + test_commit theirs && + + # When told to update HEAD only, the command refuses to + # rewrite commits that are not an ancestor of HEAD. + test_must_fail git history drop --update-refs=head main 2>err && + test_grep "rewritten commit must be an ancestor of HEAD" err && + + expect_graph --branches <<-\EOF && + * theirs + | * ours + |/ + * target + * base + EOF + + git switch main && + git history drop --update-refs=head target && + + expect_graph --branches <<-\EOF + * ours + | * theirs + | * target + |/ + * base + EOF + ) +' + +test_expect_success '--update-refs=head can rewrite detached HEAD' ' + test_when_finished "rm -rf repo" && + git init repo --initial-branch=main && + ( + cd repo && + test_commit first && + test_commit second && + test_commit third && + git switch --detach HEAD && + + git history drop --update-refs=head second && + + expect_log HEAD <<-\EOF && + third + first + EOF + expect_log main <<-\EOF + third + second + first + EOF + ) +' + +test_expect_success 'conflict with replayed commit aborts cleanly' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + test_commit conflict-a file && + test_commit conflict-b file && + + git refs list >refs-expect && + test_must_fail git history drop HEAD~ 2>err && + test_grep "failed replaying descendants" err && + git refs list >refs-actual && + test_cmp refs-expect refs-actual + ) +' + +# Build a history where a descendant of the drop target reverts the change +# introduced by the drop target. After dropping, the descendant's diff applies +# against a tree that already lacks the change, so it becomes empty. +setup_empty_descendant_repo () { + git init "$1" && + ( + cd "$1" && + echo C1 >file && + git add file && + git commit -m "base" && + git tag base && + echo C2 >file && + git add file && + git commit -m "drop-me" && + git tag drop-me && + test_commit middle && + echo C1 >file && + git add file && + git commit -m "revert-drop-me" && + git tag revert-drop-me + ) +} + +test_expect_success '--empty=drop drops descendants that become empty' ' + test_when_finished "rm -rf repo" && + setup_empty_descendant_repo repo && + ( + cd repo && + + git history drop --empty=drop drop-me && + + expect_log <<-\EOF + middle + base + EOF + ) +' + +test_expect_success '--empty=keep keeps descendants that become empty' ' + test_when_finished "rm -rf repo" && + setup_empty_descendant_repo repo && + ( + cd repo && + + git history drop --empty=keep drop-me && + + expect_log <<-\EOF && + revert-drop-me + middle + base + EOF + git diff HEAD~ HEAD >diff && + test_must_be_empty diff + ) +' + +test_expect_success '--empty=abort errors out when a descendant becomes empty' ' + test_when_finished "rm -rf repo" && + setup_empty_descendant_repo repo && + ( + cd repo && + + test_must_fail git history drop --empty=abort drop-me 2>err && + test_grep "became empty after replay" err + ) +' + +test_expect_success 'updates index and worktree when HEAD moves' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + test_commit second && + test_commit third && + + git history drop second && + + # Worktree should no longer contain second.t. + test_path_is_missing second.t && + test_path_is_file first.t && + test_path_is_file third.t && + + # Index and worktree should both match the new HEAD. + git status --porcelain --untracked-files=no >status && + test_must_be_empty status + ) +' + +test_expect_success 'updates worktree when dropping HEAD itself' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + test_commit second && + + git history drop HEAD && + + test_path_is_missing second.t && + test_path_is_file first.t && + + git status --porcelain --untracked-files=no >status && + test_must_be_empty status + ) +' + +test_expect_success 'preserves unrelated unstaged modifications' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + echo first-content >unrelated.txt && + git add unrelated.txt && + git commit -m "add unrelated" && + test_commit second && + test_commit third && + + echo locally-modified >unrelated.txt && + + git diff >diff-expect && + git history drop second && + git diff >diff-actual && + test_cmp diff-expect diff-actual && + test_path_is_missing second.t + ) +' + +test_expect_success 'preserves unrelated staged changes' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit first && + echo first-content >unrelated.txt && + git add unrelated.txt && + git commit -m "add unrelated" && + test_commit second && + test_commit third && + + echo staged-change >unrelated.txt && + git add unrelated.txt && + + git diff --cached >diff-expect && + git history drop second && + git diff --cached >diff-actual && + test_cmp diff-expect diff-actual && + test_path_is_missing second.t + ) +' + +test_expect_success 'aborts when local modifications would be overwritten' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + test_commit conflict && + + echo local-edit >conflict.t && + git diff >diff-expect && + test_must_fail git history drop HEAD 2>err && + test_grep "would overwrite local changes" err && + git diff >diff-actual && + test_cmp diff-expect diff-actual + ) +' + +test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh index 3e66827..03655c8 100755 --- a/t/t3500-cherry.sh +++ b/t/t3500-cherry.sh
@@ -100,8 +100,8 @@ GIT_TRACE2_EVENT="$(pwd)/trace2.output" git cherry upstream-with-space feature-without-space >actual && test_cmp ../expect actual && - ! grep "child_start.*fetch.negotiationAlgorithm" trace2.output && - ! grep "\"key\":\"fetch_count\"" trace2.output + test_grep ! "child_start.*fetch.negotiationAlgorithm" trace2.output && + test_grep ! "\"key\":\"fetch_count\"" trace2.output ) '
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 8025a28..939e7a1 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh
@@ -67,7 +67,7 @@ git checkout rename2 && git cherry-pick added && test_cmp_rev rename2 HEAD^ && - grep "Add extra line at the end" opos && + test_grep "Add extra line at the end" opos && git reflog -1 | grep cherry-pick ' @@ -162,7 +162,7 @@ echo modified >renamed && git cherry-pick refs/heads/unrelated && test $(git rev-parse :0:renamed) = $(git rev-parse HEAD~2:to-rename.t) && - grep -q "^modified$" renamed + test_grep -q "^modified$" renamed ' test_expect_success 'advice from failed revert' ' @@ -253,7 +253,7 @@ test_expect_success 'cherry-pick is unaware of --reference (for now)' ' test_when_finished "git reset --hard" && test_must_fail git cherry-pick --reference HEAD 2>actual && - grep "^usage: git cherry-pick" actual + test_grep "^usage: git cherry-pick" actual ' test_done
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh index 18aeba1..462973f 100755 --- a/t/t3504-cherry-pick-rerere.sh +++ b/t/t3504-cherry-pick-rerere.sh
@@ -98,9 +98,9 @@ test_expect_success 'cherry-pick conflict without rerere' ' test_config rerere.enabled false && test_must_fail git cherry-pick foo-main && - grep ===== foo && - grep foo-dev foo && - grep foo-main foo + test_grep ===== foo && + test_grep foo-dev foo && + test_grep foo-main foo ' test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 66ff9db..5777dff 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh
@@ -580,10 +580,10 @@ git cat-file commit HEAD~1 >picked_msg && git cat-file commit HEAD~2 >unrelatedpick_msg && git cat-file commit HEAD~3 >initial_msg && - ! grep "cherry picked from" initial_msg && - grep "cherry picked from" unrelatedpick_msg && - grep "cherry picked from" picked_msg && - grep "cherry picked from" anotherpick_msg + test_grep ! "cherry picked from" initial_msg && + test_grep "cherry picked from" unrelatedpick_msg && + test_grep "cherry picked from" picked_msg && + test_grep "cherry picked from" anotherpick_msg ' test_expect_success '--continue of single-pick respects -x' ' @@ -594,7 +594,7 @@ git cherry-pick --continue && test_path_is_missing .git/sequencer && git cat-file commit HEAD >msg && - grep "cherry picked from" msg + test_grep "cherry picked from" msg ' test_expect_success '--continue respects -x in first commit in multi-pick' ' @@ -606,7 +606,7 @@ test_path_is_missing .git/sequencer && git cat-file commit HEAD^ >msg && picked=$(git rev-parse --verify picked) && - grep "cherry picked from.*$picked" msg + test_grep "cherry picked from.*$picked" msg ' test_expect_failure '--signoff is automatically propagated to resolved conflict' ' @@ -621,10 +621,10 @@ git cat-file commit HEAD~1 >picked_msg && git cat-file commit HEAD~2 >unrelatedpick_msg && git cat-file commit HEAD~3 >initial_msg && - ! grep "Signed-off-by:" initial_msg && - grep "Signed-off-by:" unrelatedpick_msg && - ! grep "Signed-off-by:" picked_msg && - grep "Signed-off-by:" anotherpick_msg + test_grep ! "Signed-off-by:" initial_msg && + test_grep "Signed-off-by:" unrelatedpick_msg && + test_grep ! "Signed-off-by:" picked_msg && + test_grep "Signed-off-by:" anotherpick_msg ' test_expect_failure '--signoff dropped for implicit commit of resolution, multi-pick case' ' @@ -637,7 +637,7 @@ git diff --exit-code HEAD && test_cmp_rev initial HEAD^^ && git cat-file commit HEAD^ >msg && - ! grep Signed-off-by: msg + test_grep ! Signed-off-by: msg ' test_expect_failure 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' ' @@ -650,7 +650,7 @@ git diff --exit-code HEAD && test_cmp_rev initial HEAD^ && git cat-file commit HEAD >msg && - ! grep Signed-off-by: msg + test_grep ! Signed-off-by: msg ' test_expect_success 'malformed instruction sheet 1' '
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh index 02c7acd..252df28 100755 --- a/t/t3602-rm-sparse-checkout.sh +++ b/t/t3602-rm-sparse-checkout.sh
@@ -79,8 +79,8 @@ git reset --hard && git sparse-checkout set a && test_must_fail git rm nonexistent 2>stderr && - grep "fatal: pathspec .nonexistent. did not match any files" stderr && - ! grep -F -f sparse_error_header stderr + test_grep "fatal: pathspec .nonexistent. did not match any files" stderr && + test_grep ! -F -f sparse_error_header stderr ' test_expect_success 'do not warn about sparse entries when pathspec matches dense entries' '
diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh index 53a4782..64ad7a2 100755 --- a/t/t3705-add-sparse-checkout.sh +++ b/t/t3705-add-sparse-checkout.sh
@@ -149,8 +149,8 @@ test_expect_success 'do not advice about sparse entries when they do not match the pathspec' ' setup_sparse_entry && test_must_fail git add nonexistent 2>stderr && - grep "fatal: pathspec .nonexistent. did not match any files" stderr && - ! grep -F -f sparse_error_header stderr + test_grep "fatal: pathspec .nonexistent. did not match any files" stderr && + test_grep ! -F -f sparse_error_header stderr ' test_expect_success 'do not warn when pathspec matches dense entries' ' @@ -184,19 +184,19 @@ git -c core.autocrlf=input add --sparse sparse_entry 2>stderr && test_must_be_empty stderr && git ls-files --stage >actual && - grep "^100644 .*sparse_entry\$" actual && + test_grep "^100644 .*sparse_entry\$" actual && git add --sparse --chmod=+x sparse_entry 2>stderr && test_must_be_empty stderr && git ls-files --stage >actual && - grep "^100755 .*sparse_entry\$" actual && + test_grep "^100755 .*sparse_entry\$" actual && git reset && # This will print a message over stderr on Windows. git add --sparse --renormalize sparse_entry && git status --porcelain >actual && - grep "^M sparse_entry\$" actual + test_grep "^M sparse_entry\$" actual ' test_expect_success 'add obeys advice.updateSparsePath' '
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh index e3cf0ff..d3ddf07 100755 --- a/t/t3800-mktag.sh +++ b/t/t3800-mktag.sh
@@ -535,9 +535,9 @@ git fsck && git -c fsck.extraHeaderEntry=warn fsck 2>err && - grep "warning .*extraHeaderEntry:" err && + test_grep "warning .*extraHeaderEntry:" err && test_must_fail git -c fsck.extraHeaderEntry=error 2>err fsck && - grep "error .* extraHeaderEntry:" err + test_grep "error .* extraHeaderEntry:" err ' cat >tag.sig <<EOF
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index ef7d7e1..b2f9301 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh
@@ -81,10 +81,10 @@ git format-patch --stdout main..HEAD^ >out-l1 && git format-patch --stdout HEAD^ >out-l2 && - grep "^Content-Type: text/plain; charset=ISO8859-1" out-l1 && - grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 && - grep "^Content-Type: text/plain; charset=ISO8859-1" out-l2 && - grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2 + test_grep "^Content-Type: text/plain; charset=ISO8859-1" out-l1 && + test_grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 && + test_grep "^Content-Type: text/plain; charset=ISO8859-1" out-l2 && + test_grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2 ' test_expect_success 'format-patch output (UTF-8)' ' @@ -92,10 +92,10 @@ git format-patch --stdout main..HEAD^ >out-u1 && git format-patch --stdout HEAD^ >out-u2 && - grep "^Content-Type: text/plain; charset=UTF-8" out-u1 && - grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u1 && - grep "^Content-Type: text/plain; charset=UTF-8" out-u2 && - grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u2 + test_grep "^Content-Type: text/plain; charset=UTF-8" out-u1 && + test_grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u1 && + test_grep "^Content-Type: text/plain; charset=UTF-8" out-u2 && + test_grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u2 ' test_expect_success 'rebase (U/U)' '
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index ecc35aa..da27a65 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh
@@ -20,21 +20,21 @@ test_expect_success 'usage on cmd and subcommand invalid option' ' test_expect_code 129 git stash --invalid-option 2>usage && - grep "or: git stash" usage && + test_grep "or: git stash" usage && test_expect_code 129 git stash push --invalid-option 2>usage && - ! grep "or: git stash" usage + test_grep ! "or: git stash" usage ' test_expect_success 'usage on main command -h emits a summary of subcommands' ' - test_expect_code 129 git stash -h >usage && - grep -F "usage: git stash list" usage && - grep -F "or: git stash show" usage + git stash -h >usage && + test_grep -F "usage: git stash list" usage && + test_grep -F "or: git stash show" usage ' test_expect_success 'usage for subcommands should emit subcommand usage' ' - test_expect_code 129 git stash push -h >usage && - grep -F "usage: git stash [push" usage + git stash push -h >usage && + test_grep -F "usage: git stash [push" usage ' diff_cmp () { @@ -965,7 +965,7 @@ test $(git rev-parse stash) = $STASH_ID && git reflog --format=%H stash| grep $STASH_ID && git stash pop && - grep quux bazzy + test_grep quux bazzy ' test_expect_success 'handle stash specification with spaces' ' @@ -977,7 +977,7 @@ echo cow >file && git stash && git stash apply "stash@{$stamp}" && - grep pig file + test_grep pig file ' test_expect_success 'setup stash with index and worktree changes' ' @@ -1500,9 +1500,9 @@ test_expect_success 'stash export rejects invalid arguments' ' test_must_fail git stash export --print --to-ref refs/heads/invalid 2>err && - grep "exactly one of --print and --to-ref is required" err && + test_grep "exactly one of --print and --to-ref is required" err && test_must_fail git stash export 2>err2 && - grep "exactly one of --print and --to-ref is required" err2 + test_grep "exactly one of --print and --to-ref is required" err2 ' test_expect_success 'stash can import and export zero stashes' ' @@ -1519,7 +1519,7 @@ git stash import foo && test_must_fail git stash import HEAD 2>output && oid=$(git rev-parse HEAD) && - grep "$oid is not a valid exported stash commit" output && + test_grep "$oid is not a valid exported stash commit" output && test_cmp_rev stash@{0} t-stash0 && git checkout --orphan orphan && @@ -1527,7 +1527,7 @@ git update-ref refs/heads/orphan "$(cat fake-commit)" && oid=$(git rev-parse HEAD) && test_must_fail git stash import orphan 2>output && - grep "found stash commit $oid without expected prefix" output && + test_grep "found stash commit $oid without expected prefix" output && test_cmp_rev stash@{0} t-stash0 && git checkout --orphan orphan2 && @@ -1535,7 +1535,7 @@ git update-ref refs/heads/orphan2 "$(cat fake-commit)" && oid=$(git rev-parse HEAD) && test_must_fail git stash import orphan2 2>output && - grep "found root commit $oid with invalid data" output && + test_grep "found root commit $oid with invalid data" output && test_cmp_rev stash@{0} t-stash0 ' @@ -1741,7 +1741,7 @@ git stash push -m "custom stash for work_branch" && git stash list >../actual_stash_list.txt && - grep "On work_branch: custom stash for work_branch" ../actual_stash_list.txt + test_grep "On work_branch: custom stash for work_branch" ../actual_stash_list.txt ) ' @@ -1751,7 +1751,7 @@ test_expect_success 'controlled error return on unrecognized option' ' test_expect_code 129 git stash show -p --invalid 2>usage && - grep -e "^usage: git stash show" usage + test_grep -e "^usage: git stash show" usage ' test_expect_success 'stash.index=true implies --index' '
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh index 90a4ff2..adc45c7 100755 --- a/t/t3904-stash-patch.sh +++ b/t/t3904-stash-patch.sh
@@ -103,8 +103,8 @@ printf "%s\n" s n y q | git stash -p 2>error && test_must_be_empty error && - grep "added line 1" test && - ! grep "added line 2" test + test_grep "added line 1" test && + test_grep ! "added line 2" test ' test_expect_success 'stash -p not confused by GIT_PAGER_IN_USE' '
diff --git a/t/t3908-stash-in-worktree.sh b/t/t3908-stash-in-worktree.sh index 2b2b366..e7ae838 100755 --- a/t/t3908-stash-in-worktree.sh +++ b/t/t3908-stash-in-worktree.sh
@@ -21,7 +21,7 @@ git stash && git stash apply >out ) && - grep "\.\.\/initial\.t" wt/subdir/out + test_grep "\.\.\/initial\.t" wt/subdir/out ' test_done
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh index 6d5918c..ea75fb4 100755 --- a/t/t3910-mac-os-precompose.sh +++ b/t/t3910-mac-os-precompose.sh
@@ -207,6 +207,22 @@ git commit -m "Long filename" ' +test_expect_success "status with long non-ASCII filename" ' + test_when_finished "rm -rf long-utf8-status" && + git init long-utf8-status && + ( + cd long-utf8-status && + test "$(git config --bool core.precomposeunicode)" = true && + long_utf8_name=$( + printf "%253s\342\200\224" "" | + tr " " a + ) && + test "$(printf "%s" "$long_utf8_name" | wc -c | tr -d " ")" = 256 && + printf "content\n" >"$long_utf8_name" && + git status --porcelain=v1 >actual + ) +' + test_expect_failure 'handle existing decomposed filenames' ' echo content >"verbatim.$Adiarnfd" && git -c core.precomposeunicode=false add "verbatim.$Adiarnfd" &&
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index 32b14e3..74dec7d 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh
@@ -84,7 +84,7 @@ test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' ' git diff-files --no-patch --patch-with-raw >actual && - grep -q "^:100644 100755 .* $ZERO_OID M path0\$" actual && + test_grep -q "^:100644 100755 .* $ZERO_OID M path0\$" actual && tail -n +4 actual >actual-patch && compare_diff_patch expected actual-patch '
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 4f520d6..ad47410 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh
@@ -189,7 +189,7 @@ M path1 EOF test_cmp expect actual.munged && - grep warning actual.err + test_grep warning actual.err ' test_expect_success 'rename pretty print with nothing in common' ' @@ -258,7 +258,7 @@ git diff-tree -M -l0 HEAD HEAD^ >actual && # Verify that a rename from myotherfile to myfile was detected - grep "myotherfile.*myfile" actual + test_grep "myotherfile.*myfile" actual ' test_expect_success 'basename similarity vs best similarity' '
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh index ac837b6..4103bd6 100755 --- a/t/t4011-diff-symlink.sh +++ b/t/t4011-diff-symlink.sh
@@ -140,7 +140,7 @@ ln -s narf pinky && ln -s take\ over brain && test_must_fail git diff --no-index pinky brain >output 2>output.err && - grep narf output && + test_grep narf output && test_must_be_empty output.err '
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index d35695f..b7a382c 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh
@@ -778,7 +778,7 @@ test_expect_success 'diff --no-renames cannot be abbreviated' ' test_expect_code 129 git diff --no-rename >actual 2>error && test_must_be_empty actual && - grep "invalid option: --no-rename" error + test_grep "invalid option: --no-rename" error ' test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 0b89d12..4afcd85 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh
@@ -119,17 +119,17 @@ test_expect_success 'commit did not screw up the log message' ' git cat-file commit side >actual && - grep "^Side .* with .* backslash-n" actual + test_grep "^Side .* with .* backslash-n" actual ' test_expect_success 'format-patch did not screw up the log message' ' - grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 && - grep "^Subject: .*Side changes #3 with .* backslash-n" patch1 + test_grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 && + test_grep "^Subject: .*Side changes #3 with .* backslash-n" patch1 ' test_expect_success 'replay did not screw up the log message' ' git cat-file commit rebuild-1 >actual && - grep "^Side .* with .* backslash-n" actual + test_grep "^Side .* with .* backslash-n" actual ' test_expect_success 'format-patch empty commit' ' @@ -145,8 +145,8 @@ " && git format-patch --stdout main..side >patch2 && sed -e "/^\$/q" patch2 >hdrs2 && - grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 && - grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2 + test_grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 && + test_grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2 ' test_expect_success 'extra headers without newlines' ' @@ -154,8 +154,8 @@ git config --add format.headers "Cc: S E Cipient <scipient@example.com>" && git format-patch --stdout main..side >patch3 && sed -e "/^\$/q" patch3 >hdrs3 && - grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 && - grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3 + test_grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 && + test_grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3 ' test_expect_success 'extra headers with multiple To:s' ' @@ -163,79 +163,79 @@ git config --add format.headers "To: S E Cipient <scipient@example.com>" && git format-patch --stdout main..side >patch4 && sed -e "/^\$/q" patch4 >hdrs4 && - grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 && - grep "^ *S E Cipient <scipient@example.com>\$" hdrs4 + test_grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 && + test_grep "^ *S E Cipient <scipient@example.com>\$" hdrs4 ' test_expect_success 'additional command line cc (ascii)' ' git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && git format-patch --cc="S E Cipient <scipient@example.com>" --stdout main..side >patch5 && sed -e "/^\$/q" patch5 >hdrs5 && - grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 && - grep "^ *S E Cipient <scipient@example.com>\$" hdrs5 + test_grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 && + test_grep "^ *S E Cipient <scipient@example.com>\$" hdrs5 ' test_expect_failure 'additional command line cc (rfc822)' ' git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout main..side >patch5 && sed -e "/^\$/q" patch5 >hdrs5 && - grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 && - grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" hdrs5 + test_grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 && + test_grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" hdrs5 ' test_expect_success 'command line headers' ' git config --unset-all format.headers && git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout main..side >patch6 && sed -e "/^\$/q" patch6 >hdrs6 && - grep "^Cc: R E Cipient <rcipient@example.com>\$" hdrs6 + test_grep "^Cc: R E Cipient <rcipient@example.com>\$" hdrs6 ' test_expect_success 'configuration headers and command line headers' ' git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout main..side >patch7 && sed -e "/^\$/q" patch7 >hdrs7 && - grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs7 && - grep "^ *S E Cipient <scipient@example.com>\$" hdrs7 + test_grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs7 && + test_grep "^ *S E Cipient <scipient@example.com>\$" hdrs7 ' test_expect_success 'command line To: header (ascii)' ' git config --unset-all format.headers && git format-patch --to="R E Cipient <rcipient@example.com>" --stdout main..side >patch8 && sed -e "/^\$/q" patch8 >hdrs8 && - grep "^To: R E Cipient <rcipient@example.com>\$" hdrs8 + test_grep "^To: R E Cipient <rcipient@example.com>\$" hdrs8 ' test_expect_failure 'command line To: header (rfc822)' ' git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout main..side >patch8 && sed -e "/^\$/q" patch8 >hdrs8 && - grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs8 + test_grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs8 ' test_expect_failure 'command line To: header (rfc2047)' ' git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout main..side >patch8 && sed -e "/^\$/q" patch8 >hdrs8 && - grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs8 + test_grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs8 ' test_expect_success 'configuration To: header (ascii)' ' git config format.to "R E Cipient <rcipient@example.com>" && git format-patch --stdout main..side >patch9 && sed -e "/^\$/q" patch9 >hdrs9 && - grep "^To: R E Cipient <rcipient@example.com>\$" hdrs9 + test_grep "^To: R E Cipient <rcipient@example.com>\$" hdrs9 ' test_expect_failure 'configuration To: header (rfc822)' ' git config format.to "R. E. Cipient <rcipient@example.com>" && git format-patch --stdout main..side >patch9 && sed -e "/^\$/q" patch9 >hdrs9 && - grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs9 + test_grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs9 ' test_expect_failure 'configuration To: header (rfc2047)' ' git config format.to "R Ä Cipient <rcipient@example.com>" && git format-patch --stdout main..side >patch9 && sed -e "/^\$/q" patch9 >hdrs9 && - grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9 + test_grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9 ' # check_patch <patch>: Verify that <patch> looks like a half-sane @@ -250,35 +250,35 @@ git -c format.from=false format-patch --stdout main..side >patch && sed -e "/^\$/q" patch >hdrs && check_patch patch && - ! grep "^From: C O Mitter <committer@example.com>\$" hdrs + test_grep ! "^From: C O Mitter <committer@example.com>\$" hdrs ' test_expect_success 'format.from=true' ' git -c format.from=true format-patch --stdout main..side >patch && sed -e "/^\$/q" patch >hdrs && check_patch hdrs && - grep "^From: C O Mitter <committer@example.com>\$" hdrs + test_grep "^From: C O Mitter <committer@example.com>\$" hdrs ' test_expect_success 'format.from with address' ' git -c format.from="F R Om <from@example.com>" format-patch --stdout main..side >patch && sed -e "/^\$/q" patch >hdrs && check_patch hdrs && - grep "^From: F R Om <from@example.com>\$" hdrs + test_grep "^From: F R Om <from@example.com>\$" hdrs ' test_expect_success '--no-from overrides format.from' ' git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout main..side >patch && sed -e "/^\$/q" patch >hdrs && check_patch hdrs && - ! grep "^From: F R Om <from@example.com>\$" hdrs + test_grep ! "^From: F R Om <from@example.com>\$" hdrs ' test_expect_success '--from overrides format.from' ' git -c format.from="F R Om <from@example.com>" format-patch --from --stdout main..side >patch && sed -e "/^\$/q" patch >hdrs && check_patch hdrs && - ! grep "^From: F R Om <from@example.com>\$" hdrs + test_grep ! "^From: F R Om <from@example.com>\$" hdrs ' test_expect_success '--no-to overrides config.to' ' @@ -287,7 +287,7 @@ git format-patch --no-to --stdout main..side >patch10 && sed -e "/^\$/q" patch10 >hdrs10 && check_patch hdrs10 && - ! grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10 + test_grep ! "^To: R E Cipient <rcipient@example.com>\$" hdrs10 ' test_expect_success '--no-to and --to replaces config.to' ' @@ -297,8 +297,8 @@ --stdout main..side >patch11 && sed -e "/^\$/q" patch11 >hdrs11 && check_patch hdrs11 && - ! grep "^To: Someone <someone@out.there>\$" hdrs11 && - grep "^To: Someone Else <else@out.there>\$" hdrs11 + test_grep ! "^To: Someone <someone@out.there>\$" hdrs11 && + test_grep "^To: Someone Else <else@out.there>\$" hdrs11 ' test_expect_success '--no-cc overrides config.cc' ' @@ -307,7 +307,7 @@ git format-patch --no-cc --stdout main..side >patch12 && sed -e "/^\$/q" patch12 >hdrs12 && check_patch hdrs12 && - ! grep "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12 + test_grep ! "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12 ' test_expect_success '--no-add-header overrides config.headers' ' @@ -316,7 +316,7 @@ git format-patch --no-add-header --stdout main..side >patch13 && sed -e "/^\$/q" patch13 >hdrs13 && check_patch hdrs13 && - ! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13 + test_grep ! "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13 ' test_expect_success 'multiple files' ' @@ -508,41 +508,41 @@ test_expect_success 'reroll count' ' rm -fr patches && git format-patch -o patches --cover-letter --reroll-count 4 main..side >list && - ! grep -v "^patches/v4-000[0-3]-" list && + test_grep ! -v "^patches/v4-000[0-3]-" list && sed -n -e "/^Subject: /p" $(cat list) >subjects && - ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects + test_grep ! -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects ' test_expect_success 'reroll count (-v)' ' rm -fr patches && git format-patch -o patches --cover-letter -v4 main..side >list && - ! grep -v "^patches/v4-000[0-3]-" list && + test_grep ! -v "^patches/v4-000[0-3]-" list && sed -n -e "/^Subject: /p" $(cat list) >subjects && - ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects + test_grep ! -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects ' test_expect_success 'reroll count (-v) with a fractional number' ' rm -fr patches && git format-patch -o patches --cover-letter -v4.4 main..side >list && - ! grep -v "^patches/v4.4-000[0-3]-" list && + test_grep ! -v "^patches/v4.4-000[0-3]-" list && sed -n -e "/^Subject: /p" $(cat list) >subjects && - ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects + test_grep ! -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects ' test_expect_success 'reroll (-v) count with a non number' ' rm -fr patches && git format-patch -o patches --cover-letter -v4rev2 main..side >list && - ! grep -v "^patches/v4rev2-000[0-3]-" list && + test_grep ! -v "^patches/v4rev2-000[0-3]-" list && sed -n -e "/^Subject: /p" $(cat list) >subjects && - ! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects + test_grep ! -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects ' test_expect_success 'reroll (-v) count with a non-pathname character' ' rm -fr patches && git format-patch -o patches --cover-letter -v4---..././../--1/.2// main..side >list && - ! grep -v "patches/v4-\.-\.-\.-1-\.2-000[0-3]-" list && + test_grep ! -v "patches/v4-\.-\.-\.-1-\.2-000[0-3]-" list && sed -n -e "/^Subject: /p" $(cat list) >subjects && - ! grep -v "^Subject: \[PATCH v4---\.\.\./\./\.\./--1/\.2// [0-3]/3\] " subjects + test_grep ! -v "^Subject: \[PATCH v4---\.\.\./\./\.\./--1/\.2// [0-3]/3\] " subjects ' check_threading () { @@ -813,9 +813,9 @@ git commit -m foo && git format-patch --no-renames --cover-letter -1 && check_patch 0000-cover-letter.patch && - ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch && + test_grep ! "file => foo .* 0 *\$" 0000-cover-letter.patch && git format-patch --cover-letter -1 -M && - grep "file => foo .* 0 *\$" 0000-cover-letter.patch + test_grep "file => foo .* 0 *\$" 0000-cover-letter.patch ' cat >expect <<EOF @@ -924,13 +924,13 @@ test_expect_success 'format-patch --in-reply-to' ' git format-patch -1 --stdout --in-reply-to "baz@foo.bar" >patch8 && - grep "^In-Reply-To: <baz@foo.bar>" patch8 && - grep "^References: <baz@foo.bar>" patch8 + test_grep "^In-Reply-To: <baz@foo.bar>" patch8 && + test_grep "^References: <baz@foo.bar>" patch8 ' test_expect_success 'format-patch --signoff' ' git format-patch -1 --signoff --stdout >out && - grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" out + test_grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" out ' test_expect_success 'format-patch --notes --signoff' ' @@ -949,27 +949,27 @@ git notes add -m "notes config message" HEAD && git format-patch -1 --stdout >out && - ! grep "notes config message" out && + test_grep ! "notes config message" out && git format-patch -1 --stdout --notes >out && - grep "notes config message" out && + test_grep "notes config message" out && git format-patch -1 --stdout --no-notes >out && - ! grep "notes config message" out && + test_grep ! "notes config message" out && git format-patch -1 --stdout --notes --no-notes >out && - ! grep "notes config message" out && + test_grep ! "notes config message" out && git format-patch -1 --stdout --no-notes --notes >out && - grep "notes config message" out && + test_grep "notes config message" out && test_config format.notes true && git format-patch -1 --stdout >out && - grep "notes config message" out && + test_grep "notes config message" out && git format-patch -1 --stdout --notes >out && - grep "notes config message" out && + test_grep "notes config message" out && git format-patch -1 --stdout --no-notes >out && - ! grep "notes config message" out && + test_grep ! "notes config message" out && git format-patch -1 --stdout --notes --no-notes >out && - ! grep "notes config message" out && + test_grep ! "notes config message" out && git format-patch -1 --stdout --no-notes --notes >out && - grep "notes config message" out + test_grep "notes config message" out ' test_expect_success 'format-patch with multiple notes refs' ' @@ -979,39 +979,39 @@ git notes --ref note2 add -m "this is note 2" HEAD && git format-patch -1 --stdout >out && - ! grep "this is note 1" out && - ! grep "this is note 2" out && + test_grep ! "this is note 1" out && + test_grep ! "this is note 2" out && git format-patch -1 --stdout --notes=note1 >out && - grep "this is note 1" out && - ! grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep ! "this is note 2" out && git format-patch -1 --stdout --notes=note2 >out && - ! grep "this is note 1" out && - grep "this is note 2" out && + test_grep ! "this is note 1" out && + test_grep "this is note 2" out && git format-patch -1 --stdout --notes=note1 --notes=note2 >out && - grep "this is note 1" out && - grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep "this is note 2" out && test_config format.notes note1 && git format-patch -1 --stdout >out && - grep "this is note 1" out && - ! grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep ! "this is note 2" out && git format-patch -1 --stdout --no-notes >out && - ! grep "this is note 1" out && - ! grep "this is note 2" out && + test_grep ! "this is note 1" out && + test_grep ! "this is note 2" out && git format-patch -1 --stdout --notes=note2 >out && - grep "this is note 1" out && - grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep "this is note 2" out && git format-patch -1 --stdout --no-notes --notes=note2 >out && - ! grep "this is note 1" out && - grep "this is note 2" out && + test_grep ! "this is note 1" out && + test_grep "this is note 2" out && git config --add format.notes note2 && git format-patch -1 --stdout >out && - grep "this is note 1" out && - grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep "this is note 2" out && git format-patch -1 --stdout --no-notes >out && - ! grep "this is note 1" out && - ! grep "this is note 2" out + test_grep ! "this is note 1" out && + test_grep ! "this is note 2" out ' test_expect_success 'format-patch with multiple notes refs in config' ' @@ -1024,26 +1024,26 @@ git config format.notes note1 && git format-patch -1 --stdout >out && - grep "this is note 1" out && - ! grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep ! "this is note 2" out && git config format.notes note2 && git format-patch -1 --stdout >out && - ! grep "this is note 1" out && - grep "this is note 2" out && + test_grep ! "this is note 1" out && + test_grep "this is note 2" out && git config --add format.notes note1 && git format-patch -1 --stdout >out && - grep "this is note 1" out && - grep "this is note 2" out && + test_grep "this is note 1" out && + test_grep "this is note 2" out && git config --replace-all format.notes note1 && git config --add format.notes false && git format-patch -1 --stdout >out && - ! grep "this is note 1" out && - ! grep "this is note 2" out && + test_grep ! "this is note 1" out && + test_grep ! "this is note 2" out && git config --add format.notes note2 && git format-patch -1 --stdout >out && - ! grep "this is note 1" out && - grep "this is note 2" out + test_grep ! "this is note 1" out && + test_grep "this is note 2" out ' echo "fatal: --name-only does not make sense" >expect.name-only @@ -1095,7 +1095,7 @@ git format-patch main..pathspec -- file_a >output && test_cmp expect output && - ! grep file_b *.patch + test_grep ! file_b *.patch ' test_expect_success 'format-patch --ignore-if-in-upstream HEAD' ' @@ -1129,14 +1129,14 @@ test_expect_success 'format-patch with format.signature config' ' git config format.signature "config sig" && git format-patch --stdout -1 >output && - grep "config sig" output + test_grep "config sig" output ' test_expect_success 'format-patch --signature overrides format.signature' ' git config format.signature "config sig" && git format-patch --stdout --signature="overrides" -1 >output && - ! grep "config sig" output && - grep "overrides" output + test_grep ! "config sig" output && + test_grep "overrides" output ' test_expect_success 'format-patch --no-signature ignores format.signature' ' @@ -1144,9 +1144,9 @@ git format-patch --stdout --signature="my sig" --no-signature \ -1 >output && check_patch output && - ! grep "config sig" output && - ! grep "my sig" output && - ! grep "^-- \$" output + test_grep ! "config sig" output && + test_grep ! "my sig" output && + test_grep ! "^-- \$" output ' test_expect_success 'format-patch --signature --cover-letter' ' @@ -1161,20 +1161,20 @@ git config format.signature "" && git format-patch --stdout -1 >output && check_patch output && - ! grep "^-- \$" output + test_grep ! "^-- \$" output ' test_expect_success 'format-patch --no-signature suppresses signatures' ' git config --unset-all format.signature && git format-patch --stdout --no-signature -1 >output && check_patch output && - ! grep "^-- \$" output + test_grep ! "^-- \$" output ' test_expect_success 'format-patch --signature="" suppresses signatures' ' git format-patch --stdout --signature="" -1 >output && check_patch output && - ! grep "^-- \$" output + test_grep ! "^-- \$" output ' test_expect_success 'prepare mail-signature input' ' @@ -1213,7 +1213,7 @@ test_config format.signaturefile mail-signature && git format-patch --stdout --no-signature -1 >output && check_patch output && - ! grep "^-- \$" output + test_grep ! "^-- \$" output ' test_expect_success '--signature-file overrides format.signaturefile' ' @@ -1235,7 +1235,7 @@ test_config format.signaturefile mail-signature && git format-patch --stdout --signature="my sig" -1 >output && check_patch output && - grep "my sig" output + test_grep "my sig" output ' test_expect_success TTY 'format-patch --stdout paginates' ' @@ -2001,10 +2001,10 @@ test_config format.coverFromDescription default && git checkout rebuild-1 && git format-patch --stdout --cover-letter main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with --cover-from-description default' ' @@ -2013,10 +2013,10 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description default main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with format.coverFromDescription = none' ' @@ -2026,10 +2026,10 @@ test_config format.coverFromDescription none && git checkout rebuild-1 && git format-patch --stdout --cover-letter main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - ! grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep ! "^body$" actual ' test_expect_success 'cover letter with --cover-from-description none' ' @@ -2038,10 +2038,10 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description none main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - ! grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep ! "^body$" actual ' test_expect_success 'cover letter with format.coverFromDescription = message' ' @@ -2051,10 +2051,10 @@ test_config format.coverFromDescription message && git checkout rebuild-1 && git format-patch --stdout --cover-letter main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with --cover-from-description message' ' @@ -2063,10 +2063,10 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description message main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with format.coverFromDescription = subject' ' @@ -2076,10 +2076,10 @@ test_config format.coverFromDescription subject && git checkout rebuild-1 && git format-patch --stdout --cover-letter main >actual && - grep "^Subject: \[PATCH 0/2\] config subject$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] config subject$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with --cover-from-description subject' ' @@ -2088,10 +2088,10 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description subject main >actual && - grep "^Subject: \[PATCH 0/2\] config subject$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] config subject$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with --cover-from-description subject (UTF-8 subject line)' ' @@ -2100,8 +2100,8 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description subject --encode-email-headers main >actual && - grep "^Subject: \[PATCH 0/2\] =?UTF-8?q?Caf=C3=A9=3F?=$" actual && - ! grep "Café" actual + test_grep "^Subject: \[PATCH 0/2\] =?UTF-8?q?Caf=C3=A9=3F?=$" actual && + test_grep ! "Café" actual ' test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' ' @@ -2111,10 +2111,10 @@ test_config format.coverFromDescription auto && git checkout rebuild-1 && git format-patch --stdout --cover-letter main >actual && - grep "^Subject: \[PATCH 0/2\] config subject$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] config subject$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with --cover-from-description auto (short subject line)' ' @@ -2123,10 +2123,10 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description auto main >actual && - grep "^Subject: \[PATCH 0/2\] config subject$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] config subject$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with format.coverFromDescription = auto (long subject line)' ' @@ -2136,10 +2136,10 @@ test_config format.coverFromDescription auto && git checkout rebuild-1 && git format-patch --stdout --cover-letter main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with --cover-from-description auto (long subject line)' ' @@ -2148,10 +2148,10 @@ body" && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description auto main >actual && - grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter with command-line --cover-from-description overrides config' ' @@ -2161,52 +2161,52 @@ test_config format.coverFromDescription none && git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description subject main >actual && - grep "^Subject: \[PATCH 0/2\] config subject$" actual && - ! grep "^\*\*\* BLURB HERE \*\*\*$" actual && - ! grep "^config subject$" actual && - grep "^body$" actual + test_grep "^Subject: \[PATCH 0/2\] config subject$" actual && + test_grep ! "^\*\*\* BLURB HERE \*\*\*$" actual && + test_grep ! "^config subject$" actual && + test_grep "^body$" actual ' test_expect_success 'cover letter using branch description (1)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter main >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'cover letter using branch description (2)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'cover letter using branch description (3)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter ^main rebuild-1 >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'cover letter using branch description (4)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter main.. >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'cover letter using branch description (5)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter -2 HEAD >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'cover letter using branch description (6)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter -2 >actual && - grep hello actual + test_grep hello actual ' test_expect_success 'cover letter with --description-file' ' @@ -2219,8 +2219,8 @@ git checkout rebuild-1 && git format-patch --stdout --cover-letter --cover-from-description auto \ --description-file description.txt main >actual && - grep "^Subject: \[PATCH 0/2\] subject from file$" actual && - grep "^body from file$" actual + test_grep "^Subject: \[PATCH 0/2\] subject from file$" actual && + test_grep "^body from file$" actual ' test_expect_success 'cover letter with nothing' ' @@ -2467,7 +2467,7 @@ test_expect_success 'format-patch format.useAutoBase whenAble history involves criss-cross' ' test_config format.useAutoBase whenAble && git format-patch -1 >patch && - ! grep "^base-commit:" patch + test_grep ! "^base-commit:" patch ' test_expect_success 'format-patch format.useAutoBase option' ' @@ -2502,13 +2502,13 @@ test_expect_success 'format-patch --no-base overrides format.useAutoBase' ' test_config format.useAutoBase true && git format-patch --stdout --no-base -1 >patch && - ! grep "^base-commit:" patch + test_grep ! "^base-commit:" patch ' test_expect_success 'format-patch --no-base overrides format.useAutoBase whenAble' ' test_config format.useAutoBase whenAble && git format-patch --stdout --no-base -1 >patch && - ! grep "^base-commit:" patch + test_grep ! "^base-commit:" patch ' test_expect_success 'format-patch --base with --attach' ' @@ -2521,7 +2521,7 @@ test_expect_success 'format-patch --attach cover-letter only is non-multipart' ' test_when_finished "rm -fr patches" && git format-patch -o patches --cover-letter --attach=mimemime --base=HEAD~ -1 && - ! grep -E "^--+mimemime" patches/0000*.patch && + test_grep ! -E "^--+mimemime" patches/0000*.patch && grep -E "^--+mimemime$" patches/0001*.patch >output && test_line_count = 2 output && grep -E "^--+mimemime--$" patches/0001*.patch >output && @@ -2533,7 +2533,7 @@ separator=attachment-separator && test_config format.attach "$separator" && filename=$(git format-patch -o patches -1) && - grep "^Content-Type: multipart/.*$separator" "$filename" + test_grep "^Content-Type: multipart/.*$separator" "$filename" ' test_expect_success 'format-patch with format.attach=disabled' ' @@ -2543,7 +2543,7 @@ test_config format.attach "" && filename=$(git format-patch -o patches -1) && # The output should not even declare content type for text/plain. - ! grep "^Content-Type: multipart/" "$filename" + test_grep ! "^Content-Type: multipart/" "$filename" ' test_expect_success '-c format.mboxrd format-patch' ' @@ -2674,23 +2674,23 @@ test_expect_success 'format-patch does not respect diff.noprefix' ' git -c diff.noprefix format-patch -1 --stdout >actual && - grep "^--- a/blorp" actual + test_grep "^--- a/blorp" actual ' test_expect_success 'format-patch respects format.noprefix' ' git -c format.noprefix format-patch -1 --stdout >actual && - grep "^--- blorp" actual + test_grep "^--- blorp" actual ' test_expect_success 'format.noprefix=false' ' git -c format.noprefix=false format-patch -1 --stdout >actual && - grep "^--- a/blorp" actual + test_grep "^--- a/blorp" actual ' test_expect_success 'format-patch --default-prefix overrides format.noprefix' ' git -c format.noprefix \ format-patch -1 --default-prefix --stdout >actual && - grep "^--- a/blorp" actual + test_grep "^--- a/blorp" actual ' test_expect_success 'errors on format.noprefix which is not boolean' '
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index b691d29..6e7ba8a 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh
@@ -638,14 +638,14 @@ # This is indented with SP HT SP. echo " foo();" >x && test_must_fail git diff --check >check && - grep "space before tab in indent" check + test_grep "space before tab in indent" check ' test_expect_success 'check mixed tabs and spaces in indent' ' # This is indented with HT SP HT. echo " foo();" >x && test_must_fail git diff --check >check && - grep "space before tab in indent" check + test_grep "space before tab in indent" check ' test_expect_success 'check with no whitespace errors' ' @@ -891,14 +891,14 @@ echo "" >x && echo "foo(); " >>x && test_must_fail git diff --check >check && - grep "x:2:" check + test_grep "x:2:" check ' test_expect_success 'checkdiff detects new trailing blank lines (1)' ' echo "foo();" >x && echo "" >>x && test_must_fail git diff --check >check && - grep "new blank line" check + test_grep "new blank line" check ' test_expect_success 'checkdiff detects new trailing blank lines (2)' ' @@ -906,7 +906,7 @@ git add x && test_write_lines a "" "" "" "" >x && test_must_fail git diff --check >check && - grep "new blank line" check + test_grep "new blank line" check ' test_expect_success 'checkdiff allows new blank lines' ' @@ -1018,7 +1018,7 @@ git diff >actual.raw && sed -e "1,/^@@@/d" actual.raw >actual && - ! grep "^-" actual + test_grep ! "^-" actual ' @@ -2166,8 +2166,8 @@ # no move detection as the moved line is across repository boundaries. test_decode_color <actual >decoded_actual && - ! grep BGREEN decoded_actual && - ! grep BRED decoded_actual && + test_grep ! BGREEN decoded_actual && + test_grep ! BRED decoded_actual && # nor did we mess with it another way git diff --submodule=diff --color >expect.raw &&
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh index c2863c9..5ec6f5c 100755 --- a/t/t4017-diff-retval.sh +++ b/t/t4017-diff-retval.sh
@@ -139,7 +139,7 @@ test_expect_success 'option errors are not confused by --exit-code' ' test_must_fail git diff --exit-code --nonsense 2>err && - grep '^usage:' err + test_grep '^usage:' err ' for option in --exit-code --quiet
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index e026fac..b538b71 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh
@@ -115,7 +115,7 @@ do test_expect_success "hunk header: $i" " git diff -U1 $i >actual && - grep '@@ .* @@.*RIGHT' actual + test_grep '@@ .* @@.*RIGHT' actual " done
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh index 4001dac..939ec8a 100755 --- a/t/t4019-diff-wserror.sh +++ b/t/t4019-diff-wserror.sh
@@ -251,8 +251,8 @@ rm -f .gitattributes && test_must_fail git diff --check >output && - grep "new blank line at" output && - grep "trailing whitespace" output + test_grep "new blank line at" output && + test_grep "trailing whitespace" output ' @@ -280,8 +280,8 @@ git add G && echo BBBQ | tr Q "\015" >>G && git diff --color G | tr "\015" Q >output && - grep "BBB.*${blue_grep}Q" output && - grep "AAA.*\[mQ" output + test_grep "BBB.*${blue_grep}Q" output && + test_grep "AAA.*\[mQ" output '
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 7ec5854..93d3723 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh
@@ -34,13 +34,13 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' ' GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' ' GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' @@ -86,13 +86,13 @@ test_expect_success 'diff.external should apply only to diff' ' test_config diff.external echo && git log -p -1 HEAD >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' test_expect_success 'diff.external and --no-ext-diff' ' test_config diff.external echo && git diff --no-ext-diff >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' test_expect_success 'diff attribute' ' @@ -113,13 +113,13 @@ test_expect_success 'diff attribute should apply only to diff' ' git log -p -1 HEAD >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' test_expect_success 'diff attribute and --no-ext-diff' ' git diff --no-ext-diff >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' @@ -140,13 +140,13 @@ test_expect_success 'diff attribute should apply only to diff' ' git log -p -1 HEAD >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' test_expect_success 'diff attribute and --no-ext-diff' ' git diff --no-ext-diff >out && - grep "^diff --git a/file b/file" out + test_grep "^diff --git a/file b/file" out ' @@ -178,7 +178,7 @@ test_expect_success 'no diff with -diff' ' echo >.gitattributes "file -diff" && git diff >out && - grep Binary out + test_grep Binary out ' check_external_diff () {
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh index 9be65fd..204851f 100755 --- a/t/t4021-format-patch-numbered.sh +++ b/t/t4021-format-patch-numbered.sh
@@ -94,7 +94,7 @@ test_expect_success 'format.numbered && --keep-subject' ' git format-patch --keep-subject --stdout HEAD^ >patch4a && - grep "^Subject: Third" patch4a + test_grep "^Subject: Third" patch4a ' @@ -123,7 +123,7 @@ test_expect_success '--start-number && --numbered' ' git format-patch --start-number 3 --numbered --stdout HEAD~1 > patch8 && - grep "^Subject: \[PATCH 3/3\]" patch8 + test_grep "^Subject: \[PATCH 3/3\]" patch8 ' test_expect_success 'single patch with cover-letter defaults to numbers' '
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index 6fed993..b5c1b68 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh
@@ -58,13 +58,13 @@ test_expect_success 'show deletion diff with -B' ' git diff -B -- test >actual && - grep "Linus Torvalds" actual + test_grep "Linus Torvalds" actual ' test_expect_success 'suppress deletion diff with -B -D' ' git diff -B -D -- test >actual && - grep -v "Linus Torvalds" actual + test_grep -v "Linus Torvalds" actual ' test_expect_success 'prepare a file that ends with an incomplete line' ' @@ -82,19 +82,19 @@ test_expect_success 'confirm that sequence file is considered a rewrite' ' git diff -B seq >res && - grep "dissimilarity index" res + test_grep "dissimilarity index" res ' test_expect_success 'no newline at eof is on its own line without -B' ' git diff seq >res && - grep "^\\\\ " res && - ! grep "^..*\\\\ " res + test_grep "^\\\\ " res && + test_grep ! "^..*\\\\ " res ' test_expect_success 'no newline at eof is on its own line with -B' ' git diff -B seq >res && - grep "^\\\\ " res && - ! grep "^..*\\\\ " res + test_grep "^\\\\ " res && + test_grep ! "^..*\\\\ " res ' test_done
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh index a06a747..39344cf 100755 --- a/t/t4028-format-patch-mime-headers.sh +++ b/t/t4028-format-patch-mime-headers.sh
@@ -17,15 +17,15 @@ test_expect_success 'patch has mime headers' ' rm -f 0001-two.patch && git format-patch HEAD^ && - grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch + test_grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch ' test_expect_success 'patch has mime and extra headers' ' rm -f 0001-two.patch && git config format.headers "x-foo: bar" && git format-patch HEAD^ && - grep -i "x-foo: bar" 0001-two.patch && - grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch + test_grep -i "x-foo: bar" 0001-two.patch && + test_grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch ' test_done
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh index 15e012c..5b31ca0 100755 --- a/t/t4031-diff-rewrite-binary.sh +++ b/t/t4031-diff-rewrite-binary.sh
@@ -29,32 +29,32 @@ test_expect_success 'vanilla diff is binary' ' git diff >diff && - grep "Binary files a/file and b/file differ" diff + test_grep "Binary files a/file and b/file differ" diff ' test_expect_success 'rewrite diff is binary' ' git diff -B >diff && - grep "dissimilarity index" diff && - grep "Binary files a/file and b/file differ" diff + test_grep "dissimilarity index" diff && + test_grep "Binary files a/file and b/file differ" diff ' test_expect_success 'rewrite diff can show binary patch' ' git diff -B --binary >diff && - grep "dissimilarity index" diff && - grep "GIT binary patch" diff + test_grep "dissimilarity index" diff && + test_grep "GIT binary patch" diff ' test_expect_success 'rewrite diff --numstat shows binary changes' ' git diff -B --numstat --summary >diff && - grep -e "- - " diff && - grep " rewrite file" diff + test_grep -e "- - " diff && + test_grep " rewrite file" diff ' test_expect_success 'diff --stat counts binary rewrite as 0 lines' ' git diff -B --stat --summary >diff && - grep "Bin" diff && + test_grep "Bin" diff && test_grep "0 insertions.*0 deletions" diff && - grep " rewrite file" diff + test_grep " rewrite file" diff ' test_expect_success 'setup textconv' '
diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh index 113304d..8002c0f 100755 --- a/t/t4033-diff-patience.sh +++ b/t/t4033-diff-patience.sh
@@ -10,7 +10,7 @@ printf "a\nbX\nc\n" >post && test_must_fail git diff --no-index \ --patience --ignore-space-at-eol pre post >diff && - grep "^+.*X" diff + test_grep "^+.*X" diff ' test_diff_frobnitz "patience"
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh index 98d9713..f57d3dc 100755 --- a/t/t4036-format-patch-signer-mime.sh +++ b/t/t4036-format-patch-signer-mime.sh
@@ -19,14 +19,14 @@ test_expect_success 'format normally' ' git format-patch --stdout -1 >output && - ! grep Content-Type output + test_grep ! Content-Type output ' test_expect_success 'format with signoff without funny signer name' ' git format-patch -s --stdout -1 >output && - ! grep Content-Type output + test_grep ! Content-Type output ' @@ -34,7 +34,7 @@ GIT_COMMITTER_NAME="はまの ふにおう" \ git format-patch -s --stdout -1 >output && - grep Content-Type output + test_grep Content-Type output '
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh index 2ce26e5..e11b711 100755 --- a/t/t4038-diff-combined.sh +++ b/t/t4038-diff-combined.sh
@@ -100,7 +100,7 @@ git add file && git commit --amend -C HEAD && git show >out && - grep "diff --cc file" out + test_grep "diff --cc file" out ' test_expect_success 'setup for --cc --raw' ' @@ -118,13 +118,13 @@ four_trees=$(echo "$trees" | sed -e 4q) && git diff --cc --raw $four_trees $base_tree >out && # Check for four leading colons in the output: - grep "^::::[^:]" out + test_grep "^::::[^:]" out ' test_expect_success 'check --cc --raw with forty trees' ' git diff --cc --raw $trees $base_tree >out && # Check for forty leading colons in the output: - grep "^::::::::::::::::::::::::::::::::::::::::[^:]" out + test_grep "^::::::::::::::::::::::::::::::::::::::::[^:]" out ' test_expect_success 'setup combined ignore spaces' '
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh index 4838a1d..117eaed 100755 --- a/t/t4051-diff-function-context.sh +++ b/t/t4051-diff-function-context.sh
@@ -86,15 +86,15 @@ check_diff changed_hello 'changed function' test_expect_success ' context includes comment' ' - grep "^ .*Hello comment" changed_hello.diff + test_grep "^ .*Hello comment" changed_hello.diff ' test_expect_success ' context includes begin' ' - grep "^ .*Begin of hello" changed_hello.diff + test_grep "^ .*Begin of hello" changed_hello.diff ' test_expect_success ' context includes end' ' - grep "^ .*End of hello" changed_hello.diff + test_grep "^ .*End of hello" changed_hello.diff ' test_expect_success ' context does not include other functions' ' @@ -112,11 +112,11 @@ check_diff changed_includes 'changed includes' test_expect_success ' context includes begin' ' - grep "^ .*Begin.h" changed_includes.diff + test_grep "^ .*Begin.h" changed_includes.diff ' test_expect_success ' context includes end' ' - grep "^ .*End.h" changed_includes.diff + test_grep "^ .*End.h" changed_includes.diff ' test_expect_success ' context does not include other functions' ' @@ -130,11 +130,11 @@ check_diff appended 'appended function' test_expect_success ' context includes begin' ' - grep "^[+].*Begin of first part" appended.diff + test_grep "^[+].*Begin of first part" appended.diff ' test_expect_success ' context includes end' ' - grep "^[+].*End of first part" appended.diff + test_grep "^[+].*End of first part" appended.diff ' test_expect_success ' context does not include other functions' ' @@ -144,11 +144,11 @@ check_diff extended 'appended function part' test_expect_success ' context includes begin' ' - grep "^ .*Begin of first part" extended.diff + test_grep "^ .*Begin of first part" extended.diff ' test_expect_success ' context includes end' ' - grep "^[+].*End of second part" extended.diff + test_grep "^[+].*End of second part" extended.diff ' test_expect_success ' context does not include other functions' ' @@ -162,11 +162,11 @@ check_diff long_common_tail 'change with long common tail and no context' -U0 test_expect_success ' context includes begin' ' - grep "^ .*Begin of first part" long_common_tail.diff + test_grep "^ .*Begin of first part" long_common_tail.diff ' test_expect_success ' context includes end' ' - grep "^ .*End of second part" long_common_tail.diff + test_grep "^ .*End of second part" long_common_tail.diff ' test_expect_success ' context does not include other functions' ' @@ -180,13 +180,13 @@ check_diff changed_hello_appended 'changed function plus appended function' test_expect_success ' context includes begin' ' - grep "^ .*Begin of hello" changed_hello_appended.diff && - grep "^[+].*Begin of first part" changed_hello_appended.diff + test_grep "^ .*Begin of hello" changed_hello_appended.diff && + test_grep "^[+].*Begin of first part" changed_hello_appended.diff ' test_expect_success ' context includes end' ' - grep "^ .*End of hello" changed_hello_appended.diff && - grep "^[+].*End of first part" changed_hello_appended.diff + test_grep "^ .*End of hello" changed_hello_appended.diff && + test_grep "^[+].*End of first part" changed_hello_appended.diff ' test_expect_success ' context does not include other functions' ' @@ -196,13 +196,13 @@ check_diff changed_hello_dummy 'changed two consecutive functions' test_expect_success ' context includes begin' ' - grep "^ .*Begin of hello" changed_hello_dummy.diff && - grep "^ .*Begin of dummy" changed_hello_dummy.diff + test_grep "^ .*Begin of hello" changed_hello_dummy.diff && + test_grep "^ .*Begin of dummy" changed_hello_dummy.diff ' test_expect_success ' context includes end' ' - grep "^ .*End of hello" changed_hello_dummy.diff && - grep "^ .*End of dummy" changed_hello_dummy.diff + test_grep "^ .*End of hello" changed_hello_dummy.diff && + test_grep "^ .*End of dummy" changed_hello_dummy.diff ' test_expect_success ' overlapping hunks are merged' '
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 15076df..8e0394c 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh
@@ -283,14 +283,14 @@ test_expect_success 'diff --no-index refuses to diff stdin and a directory' ' test_must_fail git diff --no-index -- - a </dev/null 2>err && - grep "fatal: cannot compare stdin to a directory" err + test_grep "fatal: cannot compare stdin to a directory" err ' test_expect_success PIPE 'diff --no-index refuses to diff a named pipe and a directory' ' test_when_finished "rm -f pipe" && mkfifo pipe && test_must_fail git diff --no-index -- pipe a 2>err && - grep "fatal: cannot compare a named pipe to a directory" err + test_grep "fatal: cannot compare a named pipe to a directory" err ' test_expect_success PIPE,SYMLINKS 'diff --no-index reads from pipes' '
diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh index 50fdb5e..1318cce 100755 --- a/t/t4063-diff-blobs.sh +++ b/t/t4063-diff-blobs.sh
@@ -47,7 +47,7 @@ check_paths $sha1_one $sha1_two ' test_expect_success 'sha1 diff has no mode change' ' - ! grep mode diff + test_grep ! mode diff ' test_expect_success 'diff by tree:path (run)' '
diff --git a/t/t4065-diff-anchored.sh b/t/t4065-diff-anchored.sh index b3f510f..9f06ac6 100755 --- a/t/t4065-diff-anchored.sh +++ b/t/t4065-diff-anchored.sh
@@ -10,11 +10,11 @@ # normally, c is moved to produce the smallest diff test_expect_code 1 git diff --no-index pre post >diff && - grep "^+c" diff && + test_grep "^+c" diff && # with anchor, a is moved test_expect_code 1 git diff --no-index --anchored=c pre post >diff && - grep "^+a" diff + test_grep "^+a" diff ' test_expect_success '--anchored multiple' ' @@ -23,13 +23,13 @@ # with 1 anchor, c is not moved, but f is moved test_expect_code 1 git diff --no-index --anchored=c pre post >diff && - grep "^+a" diff && # a is moved instead of c - grep "^+f" diff && + test_grep "^+a" diff && # a is moved instead of c + test_grep "^+f" diff && # with 2 anchors, c and f are not moved test_expect_code 1 git diff --no-index --anchored=c --anchored=f pre post >diff && - grep "^+a" diff && - grep "^+d" diff # d is moved instead of f + test_grep "^+a" diff && + test_grep "^+d" diff # d is moved instead of f ' test_expect_success '--anchored with nonexistent line has no effect' ' @@ -37,7 +37,7 @@ printf "c\na\nb\n" >post && test_expect_code 1 git diff --no-index --anchored=x pre post >diff && - grep "^+c" diff + test_grep "^+c" diff ' test_expect_success '--anchored with non-unique line has no effect' ' @@ -45,7 +45,7 @@ printf "c\na\nb\nc\nd\ne\n" >post && test_expect_code 1 git diff --no-index --anchored=c pre post >diff && - grep "^+c" diff + test_grep "^+c" diff ' test_expect_success 'diff still produced with impossible multiple --anchored' ' @@ -66,16 +66,16 @@ printf "c\na\nb\n" >post && test_expect_code 1 git diff --no-index --patience --anchored=c pre post >diff && - grep "^+a" diff && + test_grep "^+a" diff && test_expect_code 1 git diff --no-index --anchored=c --patience pre post >diff && - grep "^+c" diff && + test_grep "^+c" diff && test_expect_code 1 git diff --no-index --histogram --anchored=c pre post >diff && - grep "^+a" diff && + test_grep "^+a" diff && test_expect_code 1 git diff --no-index --anchored=c --histogram pre post >diff && - grep "^+c" diff + test_grep "^+c" diff ' test_expect_success '--anchored works with other commands like "git show"' ' @@ -88,7 +88,7 @@ # with anchor, a is moved git show --patience --anchored=c >diff && - grep "^+a" diff + test_grep "^+a" diff ' test_done
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh index a9dec84..f510d8d 100755 --- a/t/t4067-diff-partial-clone.sh +++ b/t/t4067-diff-partial-clone.sh
@@ -70,9 +70,9 @@ # Ensure that only a and another-a are fetched. GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD && - grep "want $(cat hash-old-a)" trace && - grep "want $(cat hash-new-a)" trace && - ! grep "want $(cat hash-b)" trace + test_grep "want $(cat hash-old-a)" trace && + test_grep "want $(cat hash-new-a)" trace && + test_grep ! "want $(cat hash-b)" trace ' test_expect_success 'when fetching missing objects, diff skips GITLINKs' ' @@ -103,8 +103,8 @@ # Ensure that a and another-a are fetched, and check (by successful # execution of the diff) that no invalid OIDs are sent. GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD && - grep "want $(cat hash-old-a)" trace && - grep "want $(cat hash-new-a)" trace + test_grep "want $(cat hash-old-a)" trace && + test_grep "want $(cat hash-new-a)" trace ' test_expect_success 'diff with rename detection batches blobs' ' @@ -127,7 +127,7 @@ # Ensure that there is exactly 1 negotiation by checking that there is # only 1 "done" line sent. ("done" marks the end of negotiation.) GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD >out && - grep ":100644 100644.*R[0-9][0-9][0-9].*b.*c" out && + test_grep ":100644 100644.*R[0-9][0-9][0-9].*b.*c" out && grep "fetch> done" trace >done_lines && test_line_count = 1 done_lines '
diff --git a/t/t4073-diff-stat-name-width.sh b/t/t4073-diff-stat-name-width.sh index ec5d3c3..2412420 100755 --- a/t/t4073-diff-stat-name-width.sh +++ b/t/t4073-diff-stat-name-width.sh
@@ -21,41 +21,41 @@ test_expect_success 'test name-width long enough for filepath' ' git diff HEAD~1 HEAD --stat --stat-name-width=12 >out && - grep "d你好/f再见 |" out && + test_grep "d你好/f再见 |" out && git diff HEAD~1 HEAD --stat --stat-name-width=11 >out && - grep "d你好/f再见 |" out + test_grep "d你好/f再见 |" out ' test_expect_success 'test name-width not long enough for dir name' ' git diff HEAD~1 HEAD --stat --stat-name-width=10 >out && - grep ".../f再见 |" out && + test_grep ".../f再见 |" out && git diff HEAD~1 HEAD --stat --stat-name-width=9 >out && - grep ".../f再见 |" out + test_grep ".../f再见 |" out ' test_expect_success 'test name-width not long enough for slash' ' git diff HEAD~1 HEAD --stat --stat-name-width=8 >out && - grep "...f再见 |" out + test_grep "...f再见 |" out ' test_expect_success 'test name-width not long enough for file name' ' git diff HEAD~1 HEAD --stat --stat-name-width=7 >out && - grep "...再见 |" out && + test_grep "...再见 |" out && git diff HEAD~1 HEAD --stat --stat-name-width=6 >out && - grep "...见 |" out && + test_grep "...见 |" out && git diff HEAD~1 HEAD --stat --stat-name-width=5 >out && - grep "...见 |" out && + test_grep "...见 |" out && git diff HEAD~1 HEAD --stat --stat-name-width=4 >out && - grep "... |" out + test_grep "... |" out ' test_expect_success 'test name-width minimum length' ' git diff HEAD~1 HEAD --stat --stat-name-width=3 >out && - grep "... |" out && + test_grep "... |" out && git diff HEAD~1 HEAD --stat --stat-name-width=2 >out && - grep "... |" out && + test_grep "... |" out && git diff HEAD~1 HEAD --stat --stat-name-width=1 >out && - grep "... |" out + test_grep "... |" out ' test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index f2d41e0..bdd4ccf 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh
@@ -181,7 +181,7 @@ do_reset && test_must_fail git apply patch.trunc 2>err && line=$(awk "END { print NR + 1 }" patch.trunc) && - grep "error: corrupt binary patch at patch.trunc:$line: " err + test_grep "error: corrupt binary patch at patch.trunc:$line: " err ' test_expect_success 'reject unrecognized binary diff' '
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index c960fdf..c7801ae 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh
@@ -18,7 +18,7 @@ git mv sub süb && echo B >süb/file1 && git diff >patch.escaped && - grep "[\]" patch.escaped && + test_grep "[\]" patch.escaped && rm süb/file1 && rmdir süb '
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 205d86d..4c487fb 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh
@@ -222,8 +222,8 @@ >target && create_patch >patch-file && git apply --whitespace=fix patch-file && - grep "newline$" target && - grep "^$" target + test_grep "newline$" target && + test_grep "^$" target ' test_expect_success 'blank at EOF with --whitespace=fix (1)' ' @@ -287,7 +287,7 @@ git checkout one && git apply --whitespace=warn patch 2>error && test_cmp expect one && - grep "new blank line at EOF" error + test_grep "new blank line at EOF" error ' test_expect_success 'blank at EOF with --whitespace=error' ' @@ -300,7 +300,7 @@ git checkout one && test_must_fail git apply --whitespace=error patch 2>error && test_cmp expect one && - grep "new blank line at EOF" error + test_grep "new blank line at EOF" error ' test_expect_success 'blank but not empty at EOF' ' @@ -313,7 +313,7 @@ git checkout one && git apply --whitespace=warn patch 2>error && test_cmp expect one && - grep "new blank line at EOF" error + test_grep "new blank line at EOF" error ' test_expect_success 'applying beyond EOF requires one non-blank context line' '
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh index 5eba15f..c772fae 100755 --- a/t/t4128-apply-root.sh +++ b/t/t4128-apply-root.sh
@@ -138,7 +138,7 @@ git add some/sub/dir/delfile && git apply --directory=some/sub/dir/ --index patch && git ls-files >out && - ! grep delfile out + test_grep ! delfile out ' cat > patch << 'EOF'
diff --git a/t/t4140-apply-ita.sh b/t/t4140-apply-ita.sh index 0b11a8a..49ebb51 100755 --- a/t/t4140-apply-ita.sh +++ b/t/t4140-apply-ita.sh
@@ -14,11 +14,11 @@ cat blueprint >test-file && git add -N test-file && git diff >creation-patch && - grep "new file mode 100644" creation-patch && + test_grep "new file mode 100644" creation-patch && rm -f test-file && git diff >deletion-patch && - grep "deleted file mode 100644" deletion-patch && + test_grep "deleted file mode 100644" deletion-patch && git rm -f test-file && test_write_lines 6 >>committed-file &&
diff --git a/t/t4141-apply-too-large.sh b/t/t4141-apply-too-large.sh index eac6f7e..6bc1c7a 100755 --- a/t/t4141-apply-too-large.sh +++ b/t/t4141-apply-too-large.sh
@@ -5,7 +5,6 @@ . ./test-lib.sh test_expect_success EXPENSIVE 'git apply rejects patches that are too large' ' - sz=$((1024 * 1024 * 1023)) && { cat <<-\EOF && diff --git a/file b/file @@ -14,9 +13,9 @@ +++ b/file @@ -0,0 +1 @@ EOF - test-tool genzeros - } | test_copy_bytes $sz | test_must_fail git apply 2>err && - grep "patch too large" err + test-tool genzeros $((1024 * 1024 * 1023)) + } | test_must_fail git apply 2>err && + test_grep "patch too large" err ' test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 699a81a..61c3ce9 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh
@@ -580,7 +580,7 @@ git am --keep patch4 && test_path_is_missing .git/rebase-apply && git cat-file commit HEAD >actual && - grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual + test_grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual ' test_expect_success 'am --keep-non-patch really keeps the non-patch part' ' @@ -590,7 +590,7 @@ git am --keep-non-patch patch4 && test_path_is_missing .git/rebase-apply && git cat-file commit HEAD >actual && - grep "^\[foo\] third" actual + test_grep "^\[foo\] third" actual ' test_expect_success 'setup am -3' ' @@ -642,7 +642,7 @@ ' test_expect_success 'am can rename a file' ' - grep "^rename from" rename.patch && + test_grep "^rename from" rename.patch && rm -fr .git/rebase-apply && git reset --hard && git checkout lorem^0 && @@ -653,7 +653,7 @@ ' test_expect_success 'am -3 can rename a file' ' - grep "^rename from" rename.patch && + test_grep "^rename from" rename.patch && rm -fr .git/rebase-apply && git reset --hard && git checkout lorem^0 && @@ -664,7 +664,7 @@ ' test_expect_success 'am -3 can rename a file after falling back to 3-way merge' ' - grep "^rename from" rename-add.patch && + test_grep "^rename from" rename-add.patch && rm -fr .git/rebase-apply && git reset --hard && git checkout lorem^0 && @@ -884,7 +884,7 @@ git am --ignore-date patch1 && git cat-file commit HEAD | sed -e "/^\$/q" >head1 && sed -ne "/^author /s/.*> //p" head1 >at && - grep "+0000" at + test_grep "+0000" at ' test_expect_success 'am into an unborn branch' ' @@ -1066,7 +1066,7 @@ INPUT_END git commit -F msg && git -c format.mboxrd format-patch --stdout -1 >mboxrd1 && - grep "^>From could trip up a loose mbox parser" mboxrd1 && + test_grep "^>From could trip up a loose mbox parser" mboxrd1 && git checkout -f first && git am --patch-format=mboxrd mboxrd1 && git cat-file commit HEAD | tail -n4 >out && @@ -1144,21 +1144,21 @@ git format-patch --stdout main..HEAD >patches && git reset --hard main && git am patches && - grep "smudged" a.txt && + test_grep "smudged" a.txt && git checkout removal && git reset --hard && git format-patch --stdout main..HEAD >patches && git reset --hard main && git am patches && - grep "clean" a.txt && + test_grep "clean" a.txt && git checkout conflict && git reset --hard && git format-patch --stdout main..HEAD >patches && git reset --hard fourth && test_must_fail git am -3 patches && - grep "<<<<<<<<<<" a.txt + test_grep "<<<<<<<<<<" a.txt ) ' @@ -1196,13 +1196,13 @@ test_expect_success 'a message without a patch is an error (default)' ' test_when_finished "git am --abort || :" && test_must_fail git am empty-commit.patch >err && - grep "Patch is empty" err + test_grep "Patch is empty" err ' test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' ' test_when_finished "git am --abort || :" && test_must_fail git am --empty=stop empty-commit.patch >err && - grep "Patch is empty." err + test_grep "Patch is empty." err ' test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' ' @@ -1210,7 +1210,7 @@ git rev-parse empty-commit^ >expected && git rev-parse HEAD >actual && test_cmp expected actual && - grep "Skipping: empty commit" output + test_grep "Skipping: empty commit" output ' test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' ' @@ -1218,15 +1218,15 @@ test_path_is_missing .git/rebase-apply && git show empty-commit --format="%B" >expected && git show HEAD --format="%B" >actual && - grep -f actual expected && - grep "Creating an empty commit: empty commit" output + test_grep -f actual expected && + test_grep "Creating an empty commit: empty commit" output ' test_expect_success 'skip an empty patch in the middle of an am session' ' git checkout empty-commit^ && test_must_fail git am empty-commit.patch >out 2>err && - grep "Patch is empty." out && - grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && + test_grep "Patch is empty." out && + test_grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && git am --skip && test_path_is_missing .git/rebase-apply && git rev-parse empty-commit^ >expected && @@ -1237,14 +1237,14 @@ test_expect_success 'record an empty patch as an empty commit in the middle of an am session' ' git checkout empty-commit^ && test_must_fail git am empty-commit.patch >out 2>err && - grep "Patch is empty." out && - grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && + test_grep "Patch is empty." out && + test_grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && git am --allow-empty >output && - grep "No changes - recorded it as an empty commit." output && + test_grep "No changes - recorded it as an empty commit." output && test_path_is_missing .git/rebase-apply && git show empty-commit --format="%B" >expected && git show HEAD --format="%B" >actual && - grep -f actual expected + test_grep -f actual expected ' test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' ' @@ -1255,7 +1255,7 @@ git am --allow-empty && git show empty-commit --format="%B" >expected && git show HEAD --format="%B" >actual && - grep -f actual expected && + test_grep -f actual expected && git diff HEAD^..HEAD --name-only ' @@ -1264,7 +1264,7 @@ git rev-parse HEAD >expected && test_must_fail git am seq.patch && test_must_fail git am --allow-empty >err && - ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && + test_grep ! "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && git rev-parse HEAD >actual && test_cmp actual expected ' @@ -1274,7 +1274,7 @@ git rev-parse HEAD >expected && test_must_fail git am -3 seq.patch && test_must_fail git am --allow-empty >err && - ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && + test_grep ! "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err && git rev-parse HEAD >actual && test_cmp actual expected '
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 1717f40..7bb601e 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh
@@ -83,7 +83,7 @@ sha1=$(sed "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && - grep "^=======\$" $rr/preimage && + test_grep "^=======\$" $rr/preimage && test_path_is_missing $rr/postimage && test_path_is_missing $rr/thisimage ' @@ -96,7 +96,7 @@ sha1=$(sed "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && - grep ^=======$ $rr/preimage + test_grep ^=======$ $rr/preimage ' test_expect_success 'set up rr-cache' ' @@ -177,7 +177,7 @@ test_must_fail git merge first && # rerere kicked in - ! grep "^=======\$" a1 && + test_grep ! "^=======\$" a1 && test_cmp expect a1 ' @@ -438,7 +438,7 @@ ' test_expect_success 'rerere -h' ' - test_must_fail git rerere -h >help && + git rerere -h >help && test_grep [Uu]sage help '
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 9f41d56..023fbff 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh
@@ -267,7 +267,7 @@ test_expect_success 'shortlog bogus --group' ' test_must_fail git shortlog --group=bogus HEAD 2>err && - grep "unknown group type" err + test_grep "unknown group type" err ' test_expect_success 'trailer idents are split' '
diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 75edb0e..06552a6 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh
@@ -668,12 +668,12 @@ test_expect_success 'log --raw --graph -m with merge' ' git log --raw --graph --oneline -m main | head -n 500 >actual && - grep "initial" actual + test_grep "initial" actual ' test_expect_success 'diff-tree --graph' ' git diff-tree --graph main^ | head -n 500 >actual && - grep "one" actual + test_grep "one" actual ' cat > expect <<\EOF @@ -1082,13 +1082,13 @@ test_expect_success 'decorate-refs-exclude HEAD' ' git log --decorate=full --oneline \ --decorate-refs-exclude="HEAD" >actual && - ! grep HEAD actual + test_grep ! HEAD actual ' test_expect_success 'decorate-refs focus from default' ' git log --decorate=full --oneline \ --decorate-refs="refs/heads" >actual && - ! grep HEAD actual + test_grep ! HEAD actual ' test_expect_success '--clear-decorations overrides defaults' ' @@ -2095,45 +2095,45 @@ test_expect_success GPG 'log --graph --show-signature' ' git log --graph --show-signature -n1 signed >actual && - grep "^| gpg: Signature made" actual && - grep "^| gpg: Good signature" actual + test_grep "^| gpg: Signature made" actual && + test_grep "^| gpg: Good signature" actual ' test_expect_success GPGSM 'log --graph --show-signature x509' ' git log --graph --show-signature -n1 signed-x509 >actual && - grep "^| gpgsm: Signature made" actual && - grep "^| gpgsm: Good signature" actual + test_grep "^| gpgsm: Signature made" actual && + test_grep "^| gpgsm: Good signature" actual ' test_expect_success GPGSSH 'log --graph --show-signature ssh' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git log --graph --show-signature -n1 signed-ssh >actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on expired signature key' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git log --graph --show-signature -n1 expired-signed >actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on not yet valid signature key' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git log --graph --show-signature -n1 notyetvalid-signed >actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log show success with commit date and key validity matching' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git log --graph --show-signature -n1 timeboxedvalid-signed >actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure with commit date outside of key validity' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git log --graph --show-signature -n1 timeboxedinvalid-signed >actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPG 'log --graph --show-signature for merged tag' ' @@ -2150,9 +2150,9 @@ git checkout plain && git merge --no-ff -m msg signed_tag && git log --graph --show-signature -n1 plain >actual && - grep "^|\\\ merged tag" actual && - grep "^| | gpg: Signature made" actual && - grep "^| | gpg: Good signature" actual + test_grep "^|\\\ merged tag" actual && + test_grep "^| | gpg: Signature made" actual && + test_grep "^| | gpg: Good signature" actual ' test_expect_success GPG 'log --graph --show-signature for merged tag in shallow clone' ' @@ -2172,7 +2172,7 @@ git clone --depth 1 --no-local . shallow && test_when_finished "rm -rf shallow" && git -C shallow log --graph --show-signature -n1 plain-shallow >actual && - grep "tag signed_tag_shallow names a non-parent $hash" actual + test_grep "tag signed_tag_shallow names a non-parent $hash" actual ' test_expect_success GPG 'log --graph --show-signature for merged tag with missing key' ' @@ -2189,9 +2189,9 @@ git checkout plain-nokey && git merge --no-ff -m msg signed_tag_nokey && GNUPGHOME=. git log --graph --show-signature -n1 plain-nokey >actual && - grep "^|\\\ merged tag" actual && - grep "^| | gpg: Signature made" actual && - grep -E "^| | gpg: Can'"'"'t check signature: (public key not found|No public key)" actual + test_grep "^|\\\ merged tag" actual && + test_grep "^| | gpg: Signature made" actual && + test_grep -E "^| | gpg: Can'"'"'t check signature: (public key not found|No public key)" actual ' test_expect_success GPG 'log --graph --show-signature for merged tag with bad signature' ' @@ -2211,9 +2211,9 @@ git checkout plain-bad && git merge --no-ff -m msg "$(cat forged.tag)" && git log --graph --show-signature -n1 plain-bad >actual && - grep "^|\\\ merged tag" actual && - grep "^| | gpg: Signature made" actual && - grep "^| | gpg: BAD signature from" actual + test_grep "^|\\\ merged tag" actual && + test_grep "^| | gpg: Signature made" actual && + test_grep "^| | gpg: BAD signature from" actual ' test_expect_success GPG 'log --show-signature for merged tag with GPG failure' ' @@ -2232,9 +2232,9 @@ if ! test_have_prereq VALGRIND then TMPDIR="$(pwd)/bogus" git log --show-signature -n1 plain-fail >actual && - grep "^merged tag" actual && - grep "^No signature" actual && - ! grep "^gpg: Signature made" actual + test_grep "^merged tag" actual && + test_grep "^No signature" actual && + test_grep ! "^gpg: Signature made" actual fi ' @@ -2254,9 +2254,9 @@ git checkout plain-x509 && git merge --no-ff -m msg signed_tag_x509 && git log --graph --show-signature -n1 plain-x509 >actual && - grep "^|\\\ merged tag" actual && - grep "^| | gpgsm: Signature made" actual && - grep "^| | gpgsm: Good signature" actual + test_grep "^|\\\ merged tag" actual && + test_grep "^| | gpgsm: Signature made" actual && + test_grep "^| | gpgsm: Good signature" actual ' test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 missing key' ' @@ -2275,8 +2275,8 @@ git checkout plain-x509-nokey && git merge --no-ff -m msg signed_tag_x509_nokey && GNUPGHOME=. git log --graph --show-signature -n1 plain-x509-nokey >actual && - grep "^|\\\ merged tag" actual && - grep -e "^| | gpgsm: certificate not found" \ + test_grep "^|\\\ merged tag" actual && + test_grep -e "^| | gpgsm: certificate not found" \ -e "^| | gpgsm: failed to find the certificate: Not found" actual ' @@ -2299,35 +2299,35 @@ git checkout plain-x509-bad && git merge --no-ff -m msg "$(cat forged.tag)" && git log --graph --show-signature -n1 plain-x509-bad >actual && - grep "^|\\\ merged tag" actual && - grep "^| | gpgsm: Signature made" actual && - grep "^| | gpgsm: invalid signature" actual + test_grep "^|\\\ merged tag" actual && + test_grep "^| | gpgsm: Signature made" actual && + test_grep "^| | gpgsm: invalid signature" actual ' test_expect_success GPG '--no-show-signature overrides --show-signature' ' git log -1 --show-signature --no-show-signature signed >actual && - ! grep "^gpg:" actual + test_grep ! "^gpg:" actual ' test_expect_success GPG 'log.showsignature=true behaves like --show-signature' ' test_config log.showsignature true && git log -1 signed >actual && - grep "gpg: Signature made" actual && - grep "gpg: Good signature" actual + test_grep "gpg: Signature made" actual && + test_grep "gpg: Good signature" actual ' test_expect_success GPG '--no-show-signature overrides log.showsignature=true' ' test_config log.showsignature true && git log -1 --no-show-signature signed >actual && - ! grep "^gpg:" actual + test_grep ! "^gpg:" actual ' test_expect_success GPG '--show-signature overrides log.showsignature=false' ' test_config log.showsignature false && git log -1 --show-signature signed >actual && - grep "gpg: Signature made" actual && - grep "gpg: Good signature" actual + test_grep "gpg: Signature made" actual && + test_grep "gpg: Good signature" actual ' test_expect_success 'log --graph --no-walk is forbidden' ' @@ -2423,7 +2423,7 @@ git log --decorate=full --oneline >actual && # None of the refs are visible: - ! grep /fake actual + test_grep ! /fake actual ' test_expect_success 'log --end-of-options' '
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 605faea..90e661f 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh
@@ -44,7 +44,7 @@ test_expect_success 'patch-id output is well-formed' ' git log -p -1 >log.output && git patch-id <log.output >output && - grep "^$OID_REGEX $(git rev-parse HEAD)$" output + test_grep "^$OID_REGEX $(git rev-parse HEAD)$" output ' #calculate patch id. Make sure output is not empty.
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 3865f6a..4be5c51 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh
@@ -535,7 +535,7 @@ git log --color --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)%C(auto)%d" | test_decode_color | nul_to_q >actual && - ! grep Q actual + test_grep ! Q actual ' # --date=[XXX] and corresponding %a[X] %c[X] format equivalency
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh index 0e2f80a..802597e 100755 --- a/t/t4209-log-pickaxe.sh +++ b/t/t4209-log-pickaxe.sh
@@ -64,21 +64,21 @@ test_grep "switch.*requires a value" err && test_expect_code 128 git log -Gregex -Sstring 2>err && - grep "cannot be used together" err && + test_grep "cannot be used together" err && test_expect_code 128 git log -Gregex --find-object=HEAD 2>err && - grep "cannot be used together" err && + test_grep "cannot be used together" err && test_expect_code 128 git log -Sstring --find-object=HEAD 2>err && - grep "cannot be used together" err && + test_grep "cannot be used together" err && test_expect_code 128 git log --pickaxe-all --find-object=HEAD 2>err && - grep "cannot be used together" err + test_grep "cannot be used together" err ' test_expect_success 'usage: --pickaxe-regex' ' test_expect_code 128 git log -Gregex --pickaxe-regex 2>err && - grep "cannot be used together" err + test_grep "cannot be used together" err ' test_expect_success 'usage: --no-pickaxe-regex' '
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index ca4eb7b..e61ef9b 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh
@@ -393,14 +393,14 @@ git log -L:func2:file.c --format= >actual && # Output should contain index headers (not present in old code path) - grep "^index $head_blob_old\.\.$head_blob_new 100644" actual && + test_grep "^index $head_blob_old\.\.$head_blob_new 100644" actual && # Root commit should show new file mode and null index - grep "^new file mode 100644" actual && - grep "^index $null_blob\.\.$root_blob$" actual && + test_grep "^new file mode 100644" actual && + test_grep "^index $null_blob\.\.$root_blob$" actual && # Hunk headers should include funcname context - grep "^@@ .* @@ int func1()" actual + test_grep "^@@ .* @@ int func1()" actual ' test_expect_success '-L with --word-diff' ' @@ -431,15 +431,15 @@ test_expect_success '-L with --no-prefix' ' git log -L:func2:file.c --no-prefix --format= >actual && - grep "^diff --git file.c file.c" actual && - grep "^--- file.c" actual && - ! grep "^--- a/" actual + test_grep "^diff --git file.c file.c" actual && + test_grep "^--- file.c" actual && + test_grep ! "^--- a/" actual ' test_expect_success '-L with --full-index' ' git log -L:func2:file.c --full-index --format= >actual && - grep "^index $head_blob_old_full\.\.$head_blob_new_full 100644" actual && - grep "^index $null_blob_full\.\.$root_blob_full$" actual + test_grep "^index $head_blob_old_full\.\.$head_blob_new_full 100644" actual && + test_grep "^index $null_blob_full\.\.$root_blob_full$" actual ' test_expect_success 'setup -L with whitespace change' ' @@ -548,29 +548,29 @@ git log -L:func2:file.c --word-diff \ --word-diff-regex="[a-zA-Z0-9_]+" --format= >actual && # Word-diff markers must be present - grep "{+" actual && - grep "+}" actual && + test_grep "{+" actual && + test_grep "+}" actual && # No line-level +/- markers (word-diff replaces them); # exclude --- header lines from the check - ! grep "^+[^+]" actual && - ! grep "^-[^-]" actual + test_grep ! "^+[^+]" actual && + test_grep ! "^-[^-]" actual ' test_expect_success '-L with --src-prefix and --dst-prefix' ' git checkout parent-oids && git log -L:func2:file.c --src-prefix=old/ --dst-prefix=new/ \ --format= >actual && - grep "^diff --git old/file.c new/file.c" actual && - grep "^--- old/file.c" actual && - grep "^+++ new/file.c" actual && - ! grep "^--- a/" actual + test_grep "^diff --git old/file.c new/file.c" actual && + test_grep "^--- old/file.c" actual && + test_grep "^+++ new/file.c" actual && + test_grep ! "^--- a/" actual ' test_expect_success '-L with --abbrev' ' git checkout parent-oids && git log -L:func2:file.c --abbrev=4 --format= -1 >actual && # 4-char abbreviated hashes on index line - grep "^index [0-9a-f]\{4\}\.\.[0-9a-f]\{4\}" actual + test_grep "^index [0-9a-f]\{4\}\.\.[0-9a-f]\{4\}" actual ' test_expect_success '-L with -b suppresses whitespace-only diff' ' @@ -586,24 +586,24 @@ git log -L:func2:file.c --output-indicator-new=">" \ --output-indicator-old="<" --output-indicator-context="|" \ --format= -1 >actual && - grep "^>" actual && - grep "^<" actual && - grep "^|" actual && + test_grep "^>" actual && + test_grep "^<" actual && + test_grep "^|" actual && # No standard +/-/space content markers; exclude ---/+++ headers - ! grep "^+[^+]" actual && - ! grep "^-[^-]" actual && - ! grep "^ " actual + test_grep ! "^+[^+]" actual && + test_grep ! "^-[^-]" actual && + test_grep ! "^ " actual ' test_expect_success '-L with -R reverses diff' ' git checkout parent-oids && git log -L:func2:file.c -R --format= -1 >actual && - grep "^diff --git b/file.c a/file.c" actual && - grep "^--- b/file.c" actual && - grep "^+++ a/file.c" actual && + test_grep "^diff --git b/file.c a/file.c" actual && + test_grep "^--- b/file.c" actual && + test_grep "^+++ a/file.c" actual && # The modification added "F2 + 2", so reversed it is removed - grep "^-.*F2 + 2" actual && - grep "^+.*return F2;" actual + test_grep "^-.*F2 + 2" actual && + test_grep "^+.*return F2;" actual ' test_expect_success 'setup for color-moved test' ' @@ -629,8 +629,8 @@ --color=always --format= -1 >actual.raw && test_decode_color <actual.raw >actual && # Old moved lines: bold magenta; new moved lines: bold cyan - grep "BOLD;MAGENTA" actual && - grep "BOLD;CYAN" actual + test_grep "BOLD;MAGENTA" actual && + test_grep "BOLD;CYAN" actual ' test_expect_success 'setup for no-newline-at-eof tests' ' @@ -650,14 +650,14 @@ # newline, the "\ No newline at end of file" marker should appear. test_expect_success '-L no-newline-at-eof appears in tracked range' ' git log -L:bot:noeol.c --format= -1 HEAD~1 >actual && - grep "No newline at end of file" actual + test_grep "No newline at end of file" actual ' # When tracking a function that ends before the no-newline content, # the marker should not appear in the output. test_expect_success '-L no-newline-at-eof suppressed outside range' ' git log -L:top:noeol.c --format= >actual && - ! grep "No newline at end of file" actual + test_grep ! "No newline at end of file" actual ' # When a commit removes a no-newline last line and replaces it with @@ -665,7 +665,7 @@ # old side of the diff). test_expect_success '-L no-newline-at-eof marker with deleted line' ' git log -L:bot:noeol.c --format= -1 >actual && - grep "No newline at end of file" actual + test_grep "No newline at end of file" actual ' test_expect_success 'setup for range boundary deletion test' ' @@ -725,7 +725,7 @@ # combined with the -L range walk, this selects commits that # both touch func2 and change the count of "F2 + 2" in the file. test $(grep -c "^diff --git" actual) = 1 && - grep "F2 + 2" actual + test_grep "F2 + 2" actual ' test_expect_success '-L with -G filters to diff-text matches' ' @@ -735,7 +735,7 @@ # combined with -L, this selects commits that both touch func2 # and have "F2 + 2" in their diff. test $(grep -c "^diff --git" actual) = 1 && - grep "F2 + 2" actual + test_grep "F2 + 2" actual ' test_expect_success '-L with --diff-filter=M excludes root commit' '
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh index 16bc39c..ad26866 100755 --- a/t/t4216-log-bloom.sh +++ b/t/t4216-log-bloom.sh
@@ -224,10 +224,10 @@ GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=9 \ GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY=15 \ git commit-graph write --reachable --changed-paths && - grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2.txt && + test_grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2.txt && GIT_TRACE2_EVENT="$(pwd)/trace2-auto.txt" \ git commit-graph write --reachable --changed-paths && - grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2-auto.txt + test_grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2-auto.txt ' test_max_changed_paths () { @@ -494,7 +494,7 @@ >expect 2>err && git -C $repo log --oneline --no-decorate -- file >actual 2>err && test_cmp expect actual && - grep "disabling Bloom filters for commit-graph layer .$layer." err + test_grep "disabling Bloom filters for commit-graph layer .$layer." err ' test_expect_success 'merge graph layers with incompatible Bloom settings' ' @@ -503,8 +503,8 @@ >trace2.txt && GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ git -C $repo commit-graph write --reachable --changed-paths 2>err && - grep "disabling Bloom filters for commit-graph layer .$layer." err && - grep "{\"hash_version\":1,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt && + test_grep "disabling Bloom filters for commit-graph layer .$layer." err && + test_grep "{\"hash_version\":1,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt && test_path_is_file $repo/$graph && test_dir_is_empty $repo/$graphdir && @@ -516,7 +516,7 @@ git -C $repo log --oneline --no-decorate -- file >actual 2>err && test_cmp expect actual && - grep "statistics:{\"filter_not_present\":0," trace.perf && + test_grep "statistics:{\"filter_not_present\":0," trace.perf && test_must_be_empty err ' @@ -554,8 +554,8 @@ >trace2.txt && GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ git -C $repo -c commitGraph.changedPathsVersion=2 commit-graph write --reachable --changed-paths 2>err && - grep "disabling Bloom filters for commit-graph layer .$layer." err && - grep "{\"hash_version\":2,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt + test_grep "disabling Bloom filters for commit-graph layer .$layer." err && + test_grep "{\"hash_version\":2,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt ' get_first_changed_path_filter () { @@ -755,7 +755,7 @@ test_expect_success PERL_TEST_HELPERS 'Bloom reader notices out-of-bounds filter offsets' ' check_corrupt_graph BIDX 12 FFFFFFFF && # use grep to avoid depending on exact chunk size - grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err + test_grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err ' test_expect_success PERL_TEST_HELPERS 'Bloom reader notices too-small index chunk' '
diff --git a/t/t4219-log-follow-merge.sh b/t/t4219-log-follow-merge.sh new file mode 100755 index 0000000..e370f82 --- /dev/null +++ b/t/t4219-log-follow-merge.sh
@@ -0,0 +1,129 @@ +#!/bin/sh + +test_description='Test --follow follows renames across merges' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh + +test_expect_success 'setup subtree-merged repository' ' + git init inner && + echo inner >inner/inner.txt && + git -C inner add inner.txt && + git -C inner commit -m "inner init" && + + git init outer && + echo outer >outer/outer.txt && + git -C outer add outer.txt && + git -C outer commit -m "outer init" && + + git -C outer fetch ../inner master && + git -C outer merge -s ours --no-commit --allow-unrelated-histories \ + FETCH_HEAD && + git -C outer read-tree --prefix=inner/ -u FETCH_HEAD && + git -C outer commit -m "Merge inner repo into inner/ subdirectory" +' + +test_expect_success '--follow finds the pre-merge commit through a subtree merge' ' + git -C outer log --follow --pretty=tformat:%s inner/inner.txt >actual && + echo "inner init" >expect && + test_cmp expect actual +' + +test_expect_success 'setup merge of two branches that both renamed a file to README' ' + git init foo && + mkdir foo/foo && + echo "foo readme" >foo/foo/README && + git -C foo add foo/README && + git -C foo commit -m "add foo README" && + + git -C foo mv foo/README README && + git -C foo commit -m "promote foo README to toplevel" && + + echo "foo c" >foo/foo.c && + git -C foo add foo.c && + git -C foo commit -m "add foo C impl" && + + git init bar && + mkdir bar/bar && + echo "bar readme" >bar/bar/README && + git -C bar add bar/README && + git -C bar commit -m "add bar README" && + + git -C bar mv bar/README README && + git -C bar commit -m "promote bar README to toplevel" && + + echo "bar c" >bar/bar.c && + git -C bar add bar.c && + git -C bar commit -m "add bar C impl" && + + git -C foo fetch ../bar master && + git -C foo merge -s ours --no-commit --allow-unrelated-histories \ + FETCH_HEAD && + git -C foo checkout FETCH_HEAD -- bar.c && + git -C foo commit -m "merge bar into foo" +' + +test_expect_success '--follow follows renames across both sides of a merge' ' + git -C foo log --follow --pretty=tformat:%s README >actual && + sort actual >actual.sorted && + cat >expect <<-\EOF && + add bar README + add foo README + promote bar README to toplevel + promote foo README to toplevel + EOF + test_cmp expect actual.sorted +' + +test_expect_success 'setup diamond with renames on both sides of a fork' ' + git init diamond && + test_lines="line 1\nline 2\nline 3\nline 4\nline 5\n" && + + printf "$test_lines" >diamond/path0 && + git -C diamond add path0 && + git -C diamond commit -m "A: add path0" && + + git -C diamond checkout -b upper && + printf "line 1\nline 2\nline 3 modified by B\nline 4\nline 5\n" \ + >diamond/path0 && + git -C diamond commit -am "B: modify path0 on upper" && + git -C diamond mv path0 path1 && + git -C diamond commit -m "X: rename path0 to path1" && + + git -C diamond checkout -b lower master && + printf "line 1\nline 2\nline 3 modified by C\nline 4\nline 5\n" \ + >diamond/path0 && + git -C diamond commit -am "C: modify path0 on lower" && + git -C diamond mv path0 path2 && + git -C diamond commit -m "Y: rename path0 to path2" && + + git -C diamond checkout upper && + git -C diamond merge -s ours --no-commit lower && + git -C diamond rm path1 && + printf "line 1\nline 2\nline 3 merged\nline 4\nline 5\n" \ + >diamond/path && + git -C diamond add path && + git -C diamond commit -m "M: merge with rename to path" && + + printf "line 1\nline 2\nline 3 merged again\nline 4\nline 5\n" \ + >diamond/path && + git -C diamond commit -am "Z: modify path" +' + +test_expect_success '--follow follows renames through a fork in a single history' ' + git -C diamond log --follow --pretty=tformat:%s path >actual && + sort actual >actual.sorted && + cat >expect <<-\EOF && + A: add path0 + B: modify path0 on upper + C: modify path0 on lower + X: rename path0 to path1 + Y: rename path0 to path2 + Z: modify path + EOF + test_cmp expect actual.sorted +' + +test_done
diff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh index bda8822..c36c7e9 100755 --- a/t/t4252-am-options.sh +++ b/t/t4252-am-options.sh
@@ -20,8 +20,8 @@ git reset --hard initial && test_must_fail git am --whitespace=fix "$tm"/am-test-1-? && git am --skip && - grep 3 file-1 && - grep "^Six$" file-2 + test_grep 3 file-1 && + test_grep "^Six$" file-2 ' test_expect_success 'interrupted am -C1' ' @@ -29,8 +29,8 @@ git reset --hard initial && test_must_fail git am -C1 "$tm"/am-test-2-? && git am --skip && - grep 3 file-1 && - grep "^Three$" file-2 + test_grep 3 file-1 && + test_grep "^Three$" file-2 ' test_expect_success 'interrupted am -p2' ' @@ -38,8 +38,8 @@ git reset --hard initial && test_must_fail git am -p2 "$tm"/am-test-3-? && git am --skip && - grep 3 file-1 && - grep "^Three$" file-2 + test_grep 3 file-1 && + test_grep "^Three$" file-2 ' test_expect_success 'interrupted am -C1 -p2' ' @@ -47,8 +47,8 @@ git reset --hard initial && test_must_fail git am -p2 -C1 "$tm"/am-test-4-? && git am --skip && - grep 3 file-1 && - grep "^Three$" file-2 + test_grep 3 file-1 && + test_grep "^Three$" file-2 ' test_expect_success 'interrupted am --directory="frotz nitfol"' ' @@ -56,7 +56,7 @@ git reset --hard initial && test_must_fail git am --directory="frotz nitfol" "$tm"/am-test-5-? && git am --skip && - grep One "frotz nitfol/file-5" + test_grep One "frotz nitfol/file-5" ' test_expect_success 'apply to a funny path' ' @@ -71,9 +71,9 @@ rm -rf .git/rebase-apply && git reset --hard initial && test_must_fail git am --reject "$tm"/am-test-6-1 && - grep "@@ -1,3 +1,3 @@" file-2.rej && + test_grep "@@ -1,3 +1,3 @@" file-2.rej && test_must_fail git diff-files --exit-code --quiet file-2 && - grep "[-]-reject" .git/rebase-apply/apply-opt + test_grep "[-]-reject" .git/rebase-apply/apply-opt ' test_done
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh index 96ddf3c..f68b5d3 100755 --- a/t/t4254-am-corrupt.sh +++ b/t/t4254-am-corrupt.sh
@@ -73,16 +73,16 @@ test_when_finished "git am --abort" && make_mbox_with_nul body >body.patch && test_must_fail git am body.patch 2>err && - grep "a NUL byte in commit log message not allowed" err + test_grep "a NUL byte in commit log message not allowed" err ' test_expect_success "NUL in commit message's header" " test_when_finished 'git am --abort' && make_mbox_with_nul subject >subject.patch && test_must_fail git mailinfo msg patch <subject.patch 2>err && - grep \"a NUL byte in 'Subject' is not allowed\" err && + test_grep \"a NUL byte in 'Subject' is not allowed\" err && test_must_fail git am subject.patch 2>err && - grep \"a NUL byte in 'Subject' is not allowed\" err + test_grep \"a NUL byte in 'Subject' is not allowed\" err " test_done
diff --git a/t/t4258-am-quoted-cr.sh b/t/t4258-am-quoted-cr.sh index 201915b..50e81b9 100755 --- a/t/t4258-am-quoted-cr.sh +++ b/t/t4258-am-quoted-cr.sh
@@ -16,7 +16,7 @@ test_expect_success 'am warn if quoted-cr is found' ' git reset --hard one && test_must_fail git am "$DATA/mbox" 2>err && - grep "quoted CRLF detected" err + test_grep "quoted CRLF detected" err ' test_expect_success 'am --quoted-cr=strip' '
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh index 6e117ee..b268491 100755 --- a/t/t4301-merge-tree-write-tree.sh +++ b/t/t4301-merge-tree-write-tree.sh
@@ -88,7 +88,7 @@ # Repeat the previous test, but turn off rename detection test_expect_success 'Failed merge without rename detection' ' test_must_fail git -c diff.renames=false merge-tree --write-tree side1 side3 >out && - grep "CONFLICT (modify/delete): numbers deleted" out + test_grep "CONFLICT (modify/delete): numbers deleted" out ' test_expect_success '--quiet on conflicted merge' ' @@ -161,13 +161,13 @@ # Mis-spell with single "s" instead of double "s" test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect && - grep "error: unknown option.*mesages" expect + test_grep "error: unknown option.*mesages" expect ' test_expect_success 'Barf on too many arguments' ' test_expect_code 129 git merge-tree --write-tree side1 side2 invalid 2>expect && - grep "^usage: git merge-tree" expect + test_grep "^usage: git merge-tree" expect ' anonymize_hash() { @@ -352,7 +352,7 @@ # hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") && git rev-list --objects --all >all_blobs && - ! grep $hash all_blobs && + test_grep ! $hash all_blobs && # # Second, check anonymized hash output against expectation @@ -419,7 +419,7 @@ # hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") && git rev-list --objects --all >all_blobs && - ! grep $hash all_blobs && + test_grep ! $hash all_blobs && # # Second, check anonymized hash output against expectation @@ -670,8 +670,8 @@ hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") && hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") && git rev-list --objects --all >all_blobs && - ! grep $hash1 all_blobs && - ! grep $hash2 all_blobs && + test_grep ! $hash1 all_blobs && + test_grep ! $hash2 all_blobs && # # Now compare anonymized hash output with expectation @@ -857,7 +857,7 @@ test_expect_success 'error out by default for unrelated histories' ' test_expect_code 128 git merge-tree --write-tree side1 unrelated 2>error && - grep "refusing to merge unrelated histories" error + test_grep "refusing to merge unrelated histories" error ' test_expect_success 'can override merge of unrelated histories' ' @@ -924,7 +924,7 @@ test_expect_success '--merge-base is incompatible with --stdin' ' test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect && - grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect + test_grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect ' # specify merge-base as parent of branch2
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index a8c2853..3ad600c 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh
@@ -323,14 +323,14 @@ test_expect_success 'archive --list mentions user filter' ' git archive --list >output && - grep "^tar\.foo\$" output && - grep "^bar\$" output + test_grep "^tar\.foo\$" output && + test_grep "^bar\$" output ' test_expect_success 'archive --list shows only enabled remote filters' ' git archive --list --remote=. >output && - ! grep "^tar\.foo\$" output && - grep "^bar\$" output + test_grep ! "^tar\.foo\$" output && + test_grep "^bar\$" output ' test_expect_success 'invoke tar filter by format' ' @@ -440,7 +440,7 @@ test_expect_success 'reject paths outside the current directory' ' test_must_fail git -C a/bin archive HEAD .. >/dev/null 2>err && - grep "outside the current directory" err + test_grep "outside the current directory" err ' test_expect_success 'allow pathspecs that resolve to the current directory' '
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index df513a4..768b0ff 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh
@@ -205,7 +205,7 @@ "$GIT_UNZIP" -t big.zip && "$ZIPINFO" big.zip >big.lst && - grep $size big.lst + test_grep $size big.lst ' build_tree() {
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index e57e1ae..e01078a 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh
@@ -265,7 +265,7 @@ check_quoted_cr_mail quoted-cr/0001 && test_must_be_empty quoted-cr/0001.err && check_quoted_cr_mail quoted-cr/0002 && - grep "quoted CRLF detected" quoted-cr/0002.err && + test_grep "quoted CRLF detected" quoted-cr/0002.err && check_quoted_cr_mail quoted-cr/0001 --quoted-cr=nowarn && test_must_be_empty quoted-cr/0001.err && check_quoted_cr_mail quoted-cr/0002 --quoted-cr=nowarn &&
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh index 270ce6e..67c5dc7 100755 --- a/t/t5150-request-pull.sh +++ b/t/t5150-request-pull.sh
@@ -130,8 +130,8 @@ test_must_fail git request-pull initial "$downstream_url" \ 2>../err ) && - grep "No match for commit .*" err && - grep "Are you sure you pushed" err + test_grep "No match for commit .*" err && + test_grep "Are you sure you pushed" err ' @@ -230,7 +230,7 @@ cd local && git request-pull initial "$downstream_url" full ) >request && - grep " tags/full\$" request + test_grep " tags/full\$" request ' test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' ' @@ -260,8 +260,8 @@ test_must_fail git request-pull initial "$downstream_url" tags/v2.0 \ 2>../err ) && - grep "No match for commit .*" err && - grep "Are you sure you pushed" err + test_grep "No match for commit .*" err && + test_grep "Are you sure you pushed" err ' @@ -277,8 +277,8 @@ test_must_fail git request-pull initial "$downstream_url" tags/full \ 2>../err ) && - grep "points to a different object" err && - grep "Are you sure you pushed" err + test_grep "points to a different object" err && + test_grep "Are you sure you pushed" err ' @@ -295,8 +295,8 @@ test_must_fail git request-pull initial "$downstream_url" tags/full \ 2>../err ) && - grep "points to a different object" err && - grep "Are you sure you pushed" err + test_grep "points to a different object" err && + test_grep "Are you sure you pushed" err '
diff --git a/t/t5200-update-server-info.sh b/t/t5200-update-server-info.sh index a551e95..a0630cc 100755 --- a/t/t5200-update-server-info.sh +++ b/t/t5200-update-server-info.sh
@@ -47,7 +47,7 @@ ' test_expect_success 'update-server-info does not crash with -h' ' - test_expect_code 129 git update-server-info -h >usage && + git update-server-info -h >usage && test_grep "[Uu]sage: git update-server-info " usage '
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 7344578..9dabb36 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh
@@ -548,18 +548,18 @@ test_must_fail git index-pack --threads=2 2>err && grep ^warning: err >warnings && test_line_count = 1 warnings && - grep -F "no threads support, ignoring --threads=2" err && + test_grep -F "no threads support, ignoring --threads=2" err && test_must_fail git -c pack.threads=2 index-pack 2>err && grep ^warning: err >warnings && test_line_count = 1 warnings && - grep -F "no threads support, ignoring pack.threads" err && + test_grep -F "no threads support, ignoring pack.threads" err && test_must_fail git -c pack.threads=2 index-pack --threads=4 2>err && grep ^warning: err >warnings && test_line_count = 2 warnings && - grep -F "no threads support, ignoring --threads=4" err && - grep -F "no threads support, ignoring pack.threads" err + test_grep -F "no threads support, ignoring --threads=4" err && + test_grep -F "no threads support, ignoring pack.threads" err ' test_expect_success !PTHREADS,!FAIL_PREREQS \ @@ -567,18 +567,18 @@ git pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err && grep ^warning: err >warnings && test_line_count = 1 warnings && - grep -F "no threads support, ignoring --threads" err && + test_grep -F "no threads support, ignoring --threads" err && git -c pack.threads=2 pack-objects --stdout --all </dev/null >/dev/null 2>err && grep ^warning: err >warnings && test_line_count = 1 warnings && - grep -F "no threads support, ignoring pack.threads" err && + test_grep -F "no threads support, ignoring pack.threads" err && git -c pack.threads=2 pack-objects --threads=4 --stdout --all </dev/null >/dev/null 2>err && grep ^warning: err >warnings && test_line_count = 2 warnings && - grep -F "no threads support, ignoring --threads" err && - grep -F "no threads support, ignoring pack.threads" err + test_grep -F "no threads support, ignoring --threads" err && + test_grep -F "no threads support, ignoring pack.threads" err ' test_expect_success 'pack-objects in too-many-packs mode' ' @@ -720,14 +720,14 @@ # --stdout option silently removes --write-bitmap-index git pack-objects --stdout --all --name-hash-version=2 --write-bitmap-index >out 2>err && - ! test_grep "currently, --write-bitmap-index requires --name-hash-version=1" err + test_grep ! "currently, --write-bitmap-index requires --name-hash-version=1" err ' test_expect_success '--path-walk pack everything' ' git -C server rev-parse HEAD >in && GIT_PROGRESS_DELAY=0 git -C server pack-objects \ --stdout --revs --path-walk --progress <in >out.pack 2>err && - grep "Compressing objects by path" err && + test_grep "Compressing objects by path" err && git -C server index-pack --stdin <out.pack ' @@ -738,7 +738,7 @@ EOF GIT_PROGRESS_DELAY=0 git -C server pack-objects \ --thin --stdout --revs --path-walk --progress <in >out.pack 2>err && - grep "Compressing objects by path" err && + test_grep "Compressing objects by path" err && git -C server index-pack --fix-thin --stdin <out.pack '
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 9697448..735de10 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh
@@ -270,12 +270,12 @@ thirtyeight=${tag#??} && rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight && git index-pack --strict tag-test-${pack1}.pack 2>err && - grep "^warning:.* expected .tagger. line" err + test_grep "^warning:.* expected .tagger. line" err ' test_expect_success 'index-pack --fsck-objects also warns upon missing tagger in tag' ' git index-pack --fsck-objects tag-test-${pack1}.pack 2>err && - grep "^warning:.* expected .tagger. line" err + test_grep "^warning:.* expected .tagger. line" err ' test_expect_success 'index-pack -v --stdin produces progress for both phases' ' @@ -290,7 +290,7 @@ sz="$(test_file_size pack-$pack.pack)" && test "$sz" -gt 20 && test_must_fail git index-pack --max-input-size=20 pack-$pack.pack 2>err && - grep "maximum allowed size (20 bytes)" err + test_grep "maximum allowed size (20 bytes)" err ' # git-index-pack(1) uses the default hash algorithm outside of the repository,
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index 2be7cd3..a4ad545 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh
@@ -120,7 +120,7 @@ git checkout --quiet main && git reflog expire --all && git prune -v >prune_actual && - grep "$head_oid" prune_actual + test_grep "$head_oid" prune_actual ' test_expect_success 'prune: do not prune heads listed as an argument' ' @@ -214,7 +214,7 @@ >.git/objects/pack/fake2.keep && >.git/objects/pack/fake3.idx && git count-objects -v 2>stderr && - grep "index file .git/objects/pack/fake.idx is too small" stderr && + test_grep "index file .git/objects/pack/fake.idx is too small" stderr && grep "^warning:" stderr | sort >actual && cat >expected <<\EOF && warning: garbage found: .git/objects/pack/fake.bar @@ -252,8 +252,8 @@ oid=$(echo hi|git commit-tree HEAD^{tree}) && echo $oid >.git/shallow && git prune --dry-run >out && - grep $oid .git/shallow && - grep $oid out && + test_grep $oid .git/shallow && + test_grep $oid out && git prune && test_path_is_missing .git/shallow ' @@ -365,7 +365,7 @@ ' test_expect_success 'prune does not crash with -h' ' - test_expect_code 129 git prune -h >usage && + git prune -h >usage && test_grep "[Uu]sage: git prune " usage '
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index efeb715..b75a357 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh
@@ -81,8 +81,8 @@ git repack -ad && ls .git/objects/pack/ | grep bitmap >output && test_line_count = 1 output && - grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace && - grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace + test_grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace && + test_grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace ' basic_bitmap_tests @@ -532,8 +532,8 @@ test_line_count = 2 bitmaps && GIT_TRACE2_EVENT=$(pwd)/trace2.txt git rev-list --use-bitmap-index HEAD && - grep "opened bitmap" trace2.txt && - grep "ignoring extra bitmap" trace2.txt + test_grep "opened bitmap" trace2.txt && + test_grep "ignoring extra bitmap" trace2.txt ) ' @@ -577,6 +577,42 @@ sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL +test_expect_success 'path-walk repack can write and use bitmap indexes' ' + test_when_finished "rm -rf path-walk-bitmap" && + git init path-walk-bitmap && + ( + cd path-walk-bitmap && + test_commit first && + test_commit second && + test_commit third && + + git repack -a -d -b --path-walk && + git rev-list --test-bitmap --use-bitmap-index HEAD && + + git rev-parse HEAD >in && + + git rev-list --objects --no-object-names HEAD >expect.raw && + sort expect.raw >expect && + + for reuse in true false + do + : >trace.txt && + + GIT_TRACE2_EVENT="$(pwd)/trace.txt" \ + git -c pack.allowPackReuse=$reuse pack-objects \ + --stdout --revs --path-walk --use-bitmap-index \ + <in >out.pack && + test_grep "\"category\":\"bitmap\",\"key\":\"bitmap/hits\"" trace.txt && + + git index-pack out.pack && + + list_packed_objects out.idx >actual.raw && + sort actual.raw >actual && + test_cmp expect actual || return 1 + done + ) +' + test_expect_success 'incremental repack fails when bitmaps are requested' ' test_commit more-1 && test_must_fail git repack -d 2>err && @@ -598,7 +634,7 @@ do eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \ --use-bitmap-index second..other 2>perf" && - grep "\"region_enter\".*\"label\":\"haves/boundary\"" perf || + test_grep "\"region_enter\".*\"label\":\"haves/boundary\"" perf || return 1 done && @@ -610,7 +646,7 @@ do eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \ --use-bitmap-index second..other 2>perf" && - grep "\"region_enter\".*\"label\":\"haves/classic\"" perf || + test_grep "\"region_enter\".*\"label\":\"haves/classic\"" perf || return 1 done ' @@ -632,7 +668,7 @@ test_expect_success 'verify writing bitmap lookup table when enabled' ' GIT_TRACE2_EVENT="$(pwd)/trace2" \ git repack -ad && - grep "\"label\":\"writing_lookup_table\"" trace2 + test_grep "\"label\":\"writing_lookup_table\"" trace2 ' test_expect_success 'truncated bitmap fails gracefully (lookup table)' '
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh index dddb79b..2948a7c 100755 --- a/t/t5317-pack-objects-filter-objects.sh +++ b/t/t5317-pack-objects-filter-objects.sh
@@ -49,7 +49,7 @@ git -C r1 index-pack ../filter.pack && git -C r1 verify-pack -v ../filter.pack >verify_result && - ! grep blob verify_result + test_grep ! blob verify_result ' test_expect_success 'verify blob:none packfile without --stdout' ' @@ -57,7 +57,7 @@ HEAD EOF git -C r1 verify-pack -v "mypackname-$(cat packhash).pack" >verify_result && - ! grep blob verify_result + test_grep ! blob verify_result ' test_expect_success 'verify normal and blob:none packfiles have same commits/trees' ' @@ -85,7 +85,7 @@ test_must_fail git -C r5 pack-objects --revs --stdout 2>bad_tree <<-EOF && HEAD EOF - grep "bad tree object" bad_tree + test_grep "bad tree object" bad_tree ' test_expect_success 'setup for tests of tree:0' ' @@ -101,7 +101,7 @@ EOF git -C r1 index-pack ../commitsonly.pack && git -C r1 verify-pack -v ../commitsonly.pack >objs && - ! grep -E "tree|blob" objs + test_grep ! -E "tree|blob" objs ' test_expect_success 'grab tree directly when using tree:0' ' @@ -156,7 +156,7 @@ git -C r2 index-pack ../filter.pack && git -C r2 verify-pack -v ../filter.pack >verify_result && - ! grep blob verify_result + test_grep ! blob verify_result ' test_expect_success 'verify blob:limit=1000' ' @@ -166,7 +166,7 @@ git -C r2 index-pack ../filter.pack && git -C r2 verify-pack -v ../filter.pack >verify_result && - ! grep blob verify_result + test_grep ! blob verify_result ' test_expect_success 'verify blob:limit=1001' '
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 1c40f90..6cecfbe 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh
@@ -14,7 +14,7 @@ test_expect_success 'usage shown without sub-command' ' test_expect_code 129 git commit-graph 2>err && - grep usage: err + test_grep usage: err ' test_expect_success 'usage shown with an error on unknown sub-command' ' @@ -679,12 +679,12 @@ test_expect_success 'git fsck shows commit-graph output with --progress' ' git -C "$TRASH_DIRECTORY/full" fsck --progress 2>err && - grep "Verifying commits in commit graph" err + test_grep "Verifying commits in commit graph" err ' test_expect_success 'git fsck suppresses commit-graph output with --no-progress' ' git -C "$TRASH_DIRECTORY/full" fsck --no-progress 2>err && - ! grep "Verifying commits in commit graph" err + test_grep ! "Verifying commits in commit graph" err ' test_expect_success 'setup non-the_repository tests' ' @@ -962,7 +962,7 @@ git rev-parse HEAD~2 && # ... but fail when we are paranoid. test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error && - grep "error: commit $oid exists in commit-graph but not in the object database" error + test_grep "error: commit $oid exists in commit-graph but not in the object database" error ) '
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index fa0d404..68143cb 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh
@@ -130,7 +130,7 @@ test_copy_bytes 1064 <backup-$idx >$objdir/pack/$idx && git -c core.multiPackIndex=true rev-list --objects --all 2>err && - grep "index unavailable" err + test_grep "index unavailable" err ' test_expect_success 'add more objects' ' @@ -326,7 +326,7 @@ test_must_fail git multi-pack-index write \ --preferred-pack=pack-$empty.pack 2>err && - grep "with no objects" err + test_grep "with no objects" err ) ' @@ -548,14 +548,14 @@ test_expect_success 'git fsck shows MIDX output with --progress' ' git fsck --progress 2>err && - grep "Verifying OID order in multi-pack-index" err && - grep "Verifying object offsets" err + test_grep "Verifying OID order in multi-pack-index" err && + test_grep "Verifying object offsets" err ' test_expect_success 'git fsck suppresses MIDX output with --no-progress' ' git fsck --no-progress 2>err && - ! grep "Verifying OID order in multi-pack-index" err && - ! grep "Verifying object offsets" err + test_grep ! "Verifying OID order in multi-pack-index" err && + test_grep ! "Verifying object offsets" err ' test_expect_success 'corrupt MIDX is not reused' ' @@ -1175,12 +1175,12 @@ test_expect_success 'usage shown without sub-command' ' test_expect_code 129 git multi-pack-index 2>err && - ! test_grep "unrecognized subcommand" err + test_grep ! "unrecognized subcommand" err ' test_expect_success 'complains when run outside of a repository' ' nongit test_must_fail git multi-pack-index write 2>err && - grep "not a git repository" err + test_grep "not a git repository" err ' test_expect_success 'repack with delta islands' '
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh index 2c961c7..9b28344 100755 --- a/t/t5320-delta-islands.sh +++ b/t/t5320-delta-islands.sh
@@ -53,6 +53,35 @@ ! is_delta_base $two $one ' +test_expect_success 'path-walk island repack respects islands' ' + GIT_TRACE2_EVENT="$(pwd)/trace.path-walk-islands" \ + git -c "pack.island=refs/heads/(.*)" repack -adfi \ + --path-walk 2>err && + test_region pack-objects path-walk trace.path-walk-islands && + test_grep ! "cannot use --delta-islands with --path-walk" err && + ! is_delta_base $one $two && + ! is_delta_base $two $one +' + +test_expect_success 'path-walk island bitmap repack respects islands' ' + GIT_TRACE2_EVENT="$(pwd)/trace.path-walk-island-bitmap" \ + git -c "pack.island=refs/heads/(.*)" repack -a -d -f -i -b \ + --path-walk 2>err && + test_region pack-objects path-walk trace.path-walk-island-bitmap && + test_path_is_file .git/objects/pack/*.bitmap && + git rev-list --test-bitmap --use-bitmap-index one && + test_grep ! "cannot use --delta-islands with --path-walk" err && + ! is_delta_base $one $two && + ! is_delta_base $two $one +' + +test_expect_success 'path-walk same island allows delta' ' + GIT_TRACE2_EVENT="$(pwd)/trace.path-walk-same-island" \ + git -c "pack.island=refs/heads" repack -adfi --path-walk && + test_region pack-objects path-walk trace.path-walk-same-island && + is_delta_base $one $two +' + test_expect_success 'same island allows delta' ' git -c "pack.island=refs/heads" repack -adfi && is_delta_base $one $two
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh index 49a057c..bf7ba0e 100755 --- a/t/t5324-split-commit-graph.sh +++ b/t/t5324-split-commit-graph.sh
@@ -297,7 +297,7 @@ echo "garbage" >$base_file && test_must_fail git commit-graph verify 2>test_err && grep -v "^+" test_err >err && - grep "commit-graph file is too small" err + test_grep "commit-graph file is too small" err ) ' @@ -310,7 +310,7 @@ echo "garbage" >$tip_file && test_must_fail git commit-graph verify 2>test_err && grep -v "^+" test_err >err && - grep "commit-graph file is too small" err + test_grep "commit-graph file is too small" err ) ' @@ -379,7 +379,7 @@ echo "garbage" >$graphdir/commit-graph-chain && test_must_fail git commit-graph verify 2>test_err && grep -v "^+" test_err >err && - grep "commit-graph chain file too small" err + test_grep "commit-graph chain file too small" err ) ' @@ -410,7 +410,7 @@ git -c core.commitGraph=false log >expect.out && git -c core.commitGraph=true log >out 2>err && test_cmp expect.out out && - grep "commit-graph base graphs chunk is too small" err + test_grep "commit-graph base graphs chunk is too small" err ) ' @@ -421,7 +421,7 @@ git commit-graph write --reachable --split && git commit-graph verify --progress 2>err && test_line_count = 1 err && - grep "Verifying commits in commit graph: 100% (18/18)" err && + test_grep "Verifying commits in commit graph: 100% (18/18)" err && test_grep ! warning err && test_line_count = 3 $graphdir/commit-graph-chain ' @@ -718,6 +718,30 @@ ) ' +test_expect_success 'incremental write reads topo levels from all layers' ' + git init topo-from-lower && + ( + cd topo-from-lower && + + for i in $(test_seq 5) + do + test_commit base-$i || return 1 + done && + git commit-graph write --reachable && + + test_commit extra && + git commit-graph write --reachable --split=no-merge && + + git checkout base-3 && + test_commit new-branch && + + GIT_TRACE2_EVENT="$(pwd)/trace.txt" \ + git commit-graph write --reachable --split=no-merge && + + test_trace2_data commit-graph generation-dfs-steps 1 <trace.txt + ) +' + test_expect_success 'temporary graph layer is discarded upon failure' ' git init layer-discard && (
diff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh index 285c8b4..5493791 100755 --- a/t/t5325-reverse-index.sh +++ b/t/t5325-reverse-index.sh
@@ -65,7 +65,7 @@ test_must_fail git index-pack --rev-index --verify \ $packdir/pack-$pack.pack 2>err && - grep "validation error" err + test_grep "validation error" err ' test_expect_success 'index-pack infers reverse index name with -o' '
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh index 62bd973..86beab1 100755 --- a/t/t5326-multi-pack-bitmaps.sh +++ b/t/t5326-multi-pack-bitmaps.sh
@@ -121,7 +121,7 @@ EOF test_must_fail git multi-pack-index write --bitmap 2>err && - grep "doesn.t have full closure" err && + test_grep "doesn.t have full closure" err && test_path_is_missing $midx ) ' @@ -215,8 +215,8 @@ test_path_is_file $midx-$(midx_checksum $objdir).bitmap && test-tool bitmap list-commits | sort >bitmaps && - grep "$(git rev-parse one)" bitmaps && - grep "$(git rev-parse two)" bitmaps && + test_grep "$(git rev-parse one)" bitmaps && + test_grep "$(git rev-parse two)" bitmaps && rm -fr $midx-$(midx_checksum $objdir).bitmap && rm -fr $midx && @@ -229,8 +229,8 @@ test_path_is_file $midx-$(midx_checksum $objdir).bitmap && test-tool bitmap list-commits | sort >bitmaps && - grep "$(git rev-parse one)" bitmaps && - ! grep "$(git rev-parse two)" bitmaps + test_grep "$(git rev-parse one)" bitmaps && + test_grep ! "$(git rev-parse two)" bitmaps ) ' @@ -258,7 +258,7 @@ test_line_count = 1 before && ( - grep -vf before commits.raw && + grep -vf before commits.raw && # lint-ok: data filter # mark missing commits as preferred sed "s/^/+/" before ) >snapshot && @@ -321,7 +321,7 @@ git multi-pack-index write --bitmap --stdin-packs \ <packs 2>err && - grep "bitmap without any objects" err && + test_grep "bitmap without any objects" err && test_path_is_file $midx && test_path_is_missing $midx-$(midx_checksum $objdir).bitmap @@ -344,7 +344,7 @@ GIT_TEST_MIDX_READ_RIDX=0 \ git rev-list --use-bitmap-index HEAD 2>err && - ! grep "ignoring extra bitmap file" err + test_grep ! "ignoring extra bitmap file" err ) ' } @@ -364,7 +364,7 @@ git repack -ad && GIT_TRACE2_EVENT="$(pwd)/trace" \ git multi-pack-index write --bitmap && - grep "\"label\":\"writing_lookup_table\"" trace + test_grep "\"label\":\"writing_lookup_table\"" trace ) ' @@ -432,7 +432,7 @@ git rev-parse HEAD >want && test-tool bitmap list-commits >actual && - grep $(cat want) actual + test_grep $(cat want) actual ) ' @@ -488,17 +488,17 @@ corrupt_file "$packbitmap" && test_must_fail git fsck 2>err && - grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err && + test_grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err && cp "$packbitmap.bak" "$packbitmap" && corrupt_file "$midxbitmap" && test_must_fail git fsck 2>err && - grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err && + test_grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err && corrupt_file "$packbitmap" && test_must_fail git fsck 2>err && - grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err && - grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err + test_grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err && + test_grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err ' test_expect_success 'corrupt MIDX with bitmap causes fallback' '
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh index bc651b6..0919417 100755 --- a/t/t5328-commit-graph-64bit-time.sh +++ b/t/t5328-commit-graph-64bit-time.sh
@@ -89,7 +89,7 @@ git commit-graph write --reachable && corrupt_chunk_file $graph GDO2 clear && test_must_fail git log 2>err && - grep "commit-graph overflow generation data is too small" err + test_grep "commit-graph overflow generation data is too small" err ' test_done
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh index 25ddda5..12cda06 100755 --- a/t/t5329-pack-objects-cruft.sh +++ b/t/t5329-pack-objects-cruft.sh
@@ -468,8 +468,8 @@ test-tool pack-mtimes "$(basename $(ls $packdir/pack-*.mtimes))" \ >objects && - ! grep $object objects && - grep $cruft objects + test_grep ! $object objects && + test_grep $cruft objects ) ' @@ -515,7 +515,7 @@ test_path_is_missing "$loose" && test-tool pack-mtimes "$(basename "$(ls $packdir/pack-*.mtimes)")" >cruft && - grep "$blob" cruft && + test_grep "$blob" cruft && # write the same object again git hash-object -w -t blob contents && @@ -657,7 +657,7 @@ # ensure that a dirty exit halts cruft pack generation git config --add gc.recentObjectsHook ./extra-tips.c && test_must_fail git repack --cruft --cruft-expiration=now -d 2>err && - grep "unable to enumerate additional recent objects" err && + test_grep "unable to enumerate additional recent objects" err && # and that the existing cruft pack is left alone test_path_is_file "$mtimes"
diff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh index 68a103d..f0b82b5 100755 --- a/t/t5334-incremental-multi-pack-index.sh +++ b/t/t5334-incremental-multi-pack-index.sh
@@ -29,7 +29,7 @@ test_path_is_missing $packdir/multi-pack-index && test_path_is_file $midx_chain && test_line_count = 2 $midx_chain && - grep $old_hash $midx_chain + test_grep $old_hash $midx_chain ' compare_results_with_midx 'incremental MIDX'
diff --git a/t/t5335-compact-multi-pack-index.sh b/t/t5335-compact-multi-pack-index.sh index ec1dafe..0f75ef2 100755 --- a/t/t5335-compact-multi-pack-index.sh +++ b/t/t5335-compact-multi-pack-index.sh
@@ -321,8 +321,8 @@ } >$midx_chain && test-tool read-midx $objdir $layer >midx.data && - grep "^pack-B-.*\.idx" midx.data && - grep "^pack-C-.*\.idx" midx.data + test_grep "^pack-B-.*\.idx" midx.data && + test_grep "^pack-C-.*\.idx" midx.data ) '
diff --git a/t/t5351-unpack-large-objects.sh b/t/t5351-unpack-large-objects.sh index d76eb4b..7f621c5 100755 --- a/t/t5351-unpack-large-objects.sh +++ b/t/t5351-unpack-large-objects.sh
@@ -32,7 +32,7 @@ test_expect_success 'unpack-objects failed under memory limitation' ' prepare_dest 2m && test_must_fail git -C dest.git unpack-objects <pack-$PACK.pack 2>err && - grep "fatal: attempting to allocate" err + test_grep "fatal: attempting to allocate" err ' test_expect_success 'unpack-objects works with memory limitation in dry-run mode' '
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index b32a0a6..6aa5838 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh
@@ -56,9 +56,9 @@ git log' test_expect_success 'send-pack does not crash with -h' ' - test_expect_code 129 git send-pack -h >usage && + git send-pack -h >usage && test_grep "[Uu]sage: git send-pack " usage && - test_expect_code 129 nongit git send-pack -h >usage && + nongit git send-pack -h >usage && test_grep "[Uu]sage: git send-pack " usage '
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh index 915af2d..c77aa56 100755 --- a/t/t5402-post-merge-hook.sh +++ b/t/t5402-post-merge-hook.sh
@@ -46,7 +46,7 @@ ' test_expect_success 'post-merge from normal merge receives the right argument ' ' - grep 0 clone1/.git/post-merge.args + test_grep 0 clone1/.git/post-merge.args ' test_expect_success 'post-merge from squash merge runs as expected ' ' @@ -55,7 +55,7 @@ ' test_expect_success 'post-merge from squash merge receives the right argument ' ' - grep 1 clone2/.git/post-merge.args + test_grep 1 clone2/.git/post-merge.args ' test_done
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index cb0300b..39009ce 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh
@@ -100,7 +100,7 @@ echo untracked >three.t && test_when_finished "rm three.t" && test_must_fail git rebase $args HEAD rebase-fast-forward 2>err && - grep "untracked working tree files would be overwritten by checkout" err && + test_grep "untracked working tree files would be overwritten by checkout" err && test_path_is_missing .git/post-checkout.args '
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh index cc07889..a1661d3 100755 --- a/t/t5404-tracking-branches.sh +++ b/t/t5404-tracking-branches.sh
@@ -59,7 +59,7 @@ test_expect_success 'already deleted tracking branches ignored' ' git branch -d -r origin/b3 && git push origin :b3 >output 2>&1 && - ! grep "^error: " output + test_grep ! "^error: " output ' test_done
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh index dcbeb42..bb293b7 100755 --- a/t/t5406-remote-rejects.sh +++ b/t/t5406-remote-rejects.sh
@@ -19,6 +19,6 @@ test_expect_success 'push reports error' 'test_must_fail git push 2>stderr' -test_expect_success 'individual ref reports error' 'grep rejected stderr' +test_expect_success 'individual ref reports error' 'test_grep rejected stderr' test_done
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh index ad7f8c6..ed9896e 100755 --- a/t/t5407-post-rewrite-hook.sh +++ b/t/t5407-post-rewrite-hook.sh
@@ -195,19 +195,19 @@ set_replace_editor todo && test_must_fail git rebase -i D D 2>err ) && - grep "would be overwritten" err && + test_grep "would be overwritten" err && rm bar && test_must_fail git rebase --continue 2>err && - grep "would be overwritten" err && + test_grep "would be overwritten" err && rm G && test_must_fail git rebase --continue 2>err && - grep "would be overwritten" err && + test_grep "would be overwritten" err && rm H && test_must_fail git rebase --continue 2>err && - grep "would be overwritten" err && + test_grep "would be overwritten" err && rm I && git rebase --continue &&
diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh index 3010913..5da8b0a 100755 --- a/t/t5409-colorize-remote-messages.sh +++ b/t/t5409-colorize-remote-messages.sh
@@ -32,70 +32,70 @@ test_expect_success 'keywords' ' git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/keywords 2>output && test_decode_color <output >decoded && - grep "<BOLD;RED>error<RESET>: error" decoded && - grep "<YELLOW>hint<RESET>:" decoded && - grep "<BOLD;GREEN>success<RESET>:" decoded && - grep "<BOLD;GREEN>SUCCESS<RESET>" decoded && - grep "<BOLD;YELLOW>warning<RESET>:" decoded + test_grep "<BOLD;RED>error<RESET>: error" decoded && + test_grep "<YELLOW>hint<RESET>:" decoded && + test_grep "<BOLD;GREEN>success<RESET>:" decoded && + test_grep "<BOLD;GREEN>SUCCESS<RESET>" decoded && + test_grep "<BOLD;YELLOW>warning<RESET>:" decoded ' test_expect_success 'whole words at line start' ' git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/whole-words 2>output && test_decode_color <output >decoded && - grep "<YELLOW>hint<RESET>:" decoded && - grep "hinting: not highlighted" decoded && - grep "prefixerror: error" decoded + test_grep "<YELLOW>hint<RESET>:" decoded && + test_grep "hinting: not highlighted" decoded && + test_grep "prefixerror: error" decoded ' test_expect_success 'short line' ' git -C child -c color.remote=always push -f origin HEAD:short-line 2>output && test_decode_color <output >decoded && - grep "remote: Err" decoded + test_grep "remote: Err" decoded ' test_expect_success 'case-insensitive' ' git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/case-insensitive 2>output && test_decode_color <output >decoded && - grep "<BOLD;RED>error<RESET>: error" decoded && - grep "<BOLD;RED>ERROR<RESET>: also highlighted" decoded + test_grep "<BOLD;RED>error<RESET>: error" decoded && + test_grep "<BOLD;RED>ERROR<RESET>: also highlighted" decoded ' test_expect_success 'leading space' ' git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/leading-space 2>output && test_decode_color <output >decoded && - grep " <BOLD;RED>error<RESET>: leading space" decoded + test_grep " <BOLD;RED>error<RESET>: leading space" decoded ' test_expect_success 'spaces only' ' git -C child -c color.remote=always push -f origin HEAD:only-space 2>output && test_decode_color <output >decoded && - grep "remote: " decoded + test_grep "remote: " decoded ' test_expect_success 'no coloring for redirected output' ' git --git-dir child/.git push -f origin HEAD:refs/heads/redirected-output 2>output && test_decode_color <output >decoded && - grep "error: error" decoded + test_grep "error: error" decoded ' test_expect_success 'push with customized color' ' git --git-dir child/.git -c color.remote=always -c color.remote.error=blue push -f origin HEAD:refs/heads/customized-color 2>output && test_decode_color <output >decoded && - grep "<BLUE>error<RESET>:" decoded && - grep "<BOLD;GREEN>success<RESET>:" decoded + test_grep "<BLUE>error<RESET>:" decoded && + test_grep "<BOLD;GREEN>success<RESET>:" decoded ' test_expect_success 'error in customized color' ' git --git-dir child/.git -c color.remote=always -c color.remote.error=i-am-not-a-color push -f origin HEAD:refs/heads/error-customized-color 2>output && test_decode_color <output >decoded && - grep "<BOLD;GREEN>success<RESET>:" decoded + test_grep "<BOLD;GREEN>success<RESET>:" decoded ' test_expect_success 'fallback to color.ui' ' git --git-dir child/.git -c color.ui=always push -f origin HEAD:refs/heads/fallback-color-ui 2>output && test_decode_color <output >decoded && - grep "<BOLD;RED>error<RESET>: error" decoded + test_grep "<BOLD;RED>error<RESET>: error" decoded ' test_expect_success 'disallow (color) control sequences in sideband' '
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 649a615..6c1edf2 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh
@@ -135,15 +135,15 @@ GIT_TRACE2_EVENT="$(pwd)/branch-a/trace2_event" \ git clone --single-branch --branch A "file://$(pwd)/." branch-a && test_must_fail git --git-dir=branch-a/.git rev-parse origin/B && - grep \"fetch-info\".*\"haves\":0 branch-a/trace2_event && - grep \"fetch-info\".*\"wants\":1 branch-a/trace2_event + test_grep \"fetch-info\".*\"haves\":0 branch-a/trace2_event && + test_grep \"fetch-info\".*\"wants\":1 branch-a/trace2_event ' test_expect_success 'clone shallow depth 1' ' GIT_TRACE2_EVENT="$(pwd)/shallow0/trace2_event" \ git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 && test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1 && - grep \"fetch-info\".*\"depth\":1 shallow0/trace2_event + test_grep \"fetch-info\".*\"depth\":1 shallow0/trace2_event ' test_expect_success 'clone shallow depth 1 with fsck' ' @@ -167,7 +167,7 @@ cd shallow && git count-objects -v ) > count.shallow && - grep "^in-pack: 12" count.shallow + test_grep "^in-pack: 12" count.shallow ' test_expect_success 'clone shallow object count (part 2)' ' @@ -230,7 +230,7 @@ cd shallow && git count-objects -v ) > count.shallow && - grep "^count: 6" count.shallow + test_grep "^count: 6" count.shallow ' test_expect_success 'add two more (part 2)' ' @@ -243,8 +243,8 @@ cd shallow && GIT_TRACE2_EVENT="$(pwd)/trace2_event" \ git pull --depth 4 .. B && - grep \"fetch-info\".*\"depth\":4 trace2_event && - grep \"fetch-info\".*\"shallows\":2 trace2_event + test_grep \"fetch-info\".*\"depth\":4 trace2_event && + test_grep \"fetch-info\".*\"shallows\":2 trace2_event ) ' @@ -253,7 +253,7 @@ cd shallow && git count-objects -v ) > count.shallow && - grep "^count: 12" count.shallow + test_grep "^count: 12" count.shallow ' test_expect_success 'deepening fetch in shallow repo' ' @@ -268,7 +268,7 @@ cd shallow && git count-objects -v ) > count.shallow && - grep "^count: 18" count.shallow + test_grep "^count: 18" count.shallow ' test_expect_success 'pull in shallow repo with missing merge base' ' @@ -298,7 +298,7 @@ git prune && git count-objects -v ) > count.shallow && - grep "^count: 54" count.shallow + test_grep "^count: 54" count.shallow ' test_expect_success 'fetch --no-shallow on full repo' ' @@ -319,8 +319,8 @@ git fetch --unshallow && ! test -f .git/shallow && git fsck --full && - grep \"fetch-info\".*\"shallows\":2 trace2_event && - grep \"fetch-info\".*\"depth\":2147483647 trace2_event + test_grep \"fetch-info\".*\"shallows\":2 trace2_event && + test_grep \"fetch-info\".*\"depth\":2147483647 trace2_event ) ' @@ -333,7 +333,7 @@ cd shallow2 && git count-objects -v ) > count.shallow2 && - grep "^in-pack: 3" count.shallow2 + test_grep "^in-pack: 3" count.shallow2 ' test_expect_success 'clone shallow with --branch' ' @@ -446,7 +446,7 @@ # and should reset in_vain. This allows negotiation to continue until # the client reports that first_anotherbranch_commit is common. GIT_TRACE2_EVENT="$(pwd)/trace2" git -C myclient fetch --progress origin main 2>log && - grep \"key\":\"total_rounds\",\"value\":\"6\" trace2 && + test_grep \"key\":\"total_rounds\",\"value\":\"6\" trace2 && test_grep "Total 3 " log ' @@ -852,7 +852,7 @@ two EOF test_cmp expected actual && - grep \"fetch-info\".*\"deepen-since\":true shallow11/trace2_event + test_grep \"fetch-info\".*\"deepen-since\":true shallow11/trace2_event ' test_expect_success 'clone shallow since selects no commits' ' @@ -935,7 +935,7 @@ test_when_finished rm -f rev err && git -C shallow-exclude rev-parse one >rev && test_must_fail git -C shallow12 fetch --shallow-exclude $(cat rev) origin 2>err && - grep "deepen-not is not a ref:" err + test_grep "deepen-not is not a ref:" err ' test_expect_success 'fetching deepen' ' @@ -1059,9 +1059,9 @@ commit=$(git -C server rev-parse HEAD) && blob=$(git hash-object server/one.t) && git -C client rev-list --objects --missing=allow-any "$commit" >oids && - ! grep "$blob" oids && + test_grep ! "$blob" oids && - grep \"fetch-info\".*\"filter\":\"blob:limit\" client/trace2_event + test_grep \"fetch-info\".*\"filter\":\"blob:limit\" client/trace2_event ' test_expect_success 'filtering by size has no effect if support for it is not advertised' ' @@ -1076,7 +1076,7 @@ commit=$(git -C server rev-parse HEAD) && blob=$(git hash-object server/one.t) && git -C client rev-list --objects --missing=allow-any "$commit" >oids && - grep "$blob" oids && + test_grep "$blob" oids && test_grep "filtering not recognized by server" err '
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 438250c..75b2b87 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh
@@ -298,13 +298,13 @@ git --git-dir=dst/.git config \ receive.fsck.missingEmail warn && git push --porcelain dst bogus >act 2>&1 && - grep "missingEmail" act && + test_grep "missingEmail" act && test_grep "skipping unknown msg id.*whatever" act && git --git-dir=dst/.git branch -D bogus && git --git-dir=dst/.git config --add \ receive.fsck.missingEmail ignore && git push --porcelain dst bogus >act 2>&1 && - ! grep "missingEmail" act + test_grep ! "missingEmail" act ' test_expect_success 'fetch with fetch.fsck.missingEmail=warn' ' @@ -326,7 +326,7 @@ git --git-dir=dst/.git config \ fetch.fsck.missingEmail warn && git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 && - grep "missingEmail" act && + test_grep "missingEmail" act && test_grep "Skipping unknown msg id.*whatever" act && rm -rf dst && git init dst && @@ -334,7 +334,7 @@ git --git-dir=dst/.git config \ fetch.fsck.missingEmail ignore && git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 && - ! grep "missingEmail" act + test_grep ! "missingEmail" act ' test_expect_success \ @@ -345,7 +345,7 @@ git --git-dir=dst/.git config \ receive.fsck.unterminatedheader warn && test_must_fail git push --porcelain dst HEAD >act 2>&1 && - grep "Cannot demote unterminatedheader" act + test_grep "Cannot demote unterminatedheader" act ' test_expect_success \ @@ -356,7 +356,7 @@ git --git-dir=dst/.git config \ fetch.fsck.unterminatedheader warn && test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" HEAD && - grep "Cannot demote unterminatedheader" act + test_grep "Cannot demote unterminatedheader" act ' test_expect_success PERL_TEST_HELPERS 'badFilemode is not a strict error' ' @@ -373,7 +373,7 @@ git -C dst.git config transfer.fsckObjects true && git -C badmode.git push ../dst.git $tree:refs/tags/tree 2>err && - grep "$tree: badFilemode" err + test_grep "$tree: badFilemode" err ' test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index e592c0b..6f5e86d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh
@@ -91,28 +91,28 @@ test_when_finished "rm -rf pc" && git clone --filter=blob:none "file://$(pwd)/srv.bare" pc && git -C pc remote -v >out && - grep "srv.bare (fetch) \[blob:none\]" out && + test_grep "srv.bare (fetch) \[blob:none\]" out && git -C pc config remote.origin.partialCloneFilter object:type=commit && git -C pc remote -v >out && - grep "srv.bare (fetch) \[object:type=commit\]" out + test_grep "srv.bare (fetch) \[object:type=commit\]" out ' test_expect_success 'filters should not be listed for non promisor remotes (remote -v)' ' test_when_finished "rm -rf pc" && git clone one pc && git -C pc remote -v >out && - ! grep "(fetch) \[.*\]" out + test_grep ! "(fetch) \[.*\]" out ' test_expect_success 'filters are listed by git remote -v only' ' test_when_finished "rm -rf pc" && git clone --filter=blob:none "file://$(pwd)/srv.bare" pc && git -C pc remote >out && - ! grep "\[blob:none\]" out && + test_grep ! "\[blob:none\]" out && git -C pc remote show >out && - ! grep "\[blob:none\]" out + test_grep ! "\[blob:none\]" out ' test_expect_success 'check remote-tracking' ' @@ -261,7 +261,7 @@ test_expect_success 'without subcommand does not take arguments' ' test_expect_code 129 git -C test remote origin 2>err && - grep "^error: unknown subcommand:" err + test_grep "^error: unknown subcommand:" err ' cat >test/expect <<EOF @@ -798,7 +798,7 @@ cd add-no-tags && git init && git remote add -f --no-tags origin ../one && - grep tagOpt .git/config && + test_grep tagOpt .git/config && git tag -l some-tag >../test/output && git tag -l foobar-tag >../test/output && git config remote.origin.tagopt >>../test/output @@ -972,7 +972,7 @@ GIT_TRACE2_EVENT=$(pwd)/trace \ git remote rename --progress origin upstream && test_region progress "Renaming remote references" trace && - grep "pushRemote" .git/config && + test_grep "pushRemote" .git/config && test -z "$(git for-each-ref refs/remotes/origin)" && test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" && test "$(git rev-parse upstream/main)" = "$(git rev-parse main)" && @@ -989,7 +989,7 @@ cd four.1 && git config remote.pushDefault origin && git remote rename origin upstream && - grep pushDefault .git/config && + test_grep pushDefault .git/config && test "$(git config --local remote.pushDefault)" = "upstream" ) ' @@ -1198,7 +1198,7 @@ cd eight && git branch -a ) 2>err && - ! grep "points nowhere" err && + test_grep ! "points nowhere" err && ( cd eight && test_must_fail git branch nomore origin
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index eca9a97..a8d38d9 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh
@@ -140,6 +140,16 @@ ) ' +test_expect_success "fetch test default followRemoteHEAD never" ' + git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && + test_config -C two fetch.followRemoteHEAD "never" && + GIT_TRACE_PACKET=$PWD/trace.out git -C two fetch && + # Confirm that we do not even ask for HEAD when we are + # not going to act on it. + test_grep ! "ref-prefix HEAD" trace.out && + test_must_fail git -C two rev-parse --verify refs/remotes/origin/HEAD +' + test_expect_success "fetch test followRemoteHEAD never" ' git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && test_config -C two remote.origin.followRemoteHEAD "never" && @@ -150,6 +160,21 @@ test_must_fail git -C two rev-parse --verify refs/remotes/origin/HEAD ' +test_expect_success "fetch test default followRemoteHEAD warn no change" ' + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two fetch.followRemoteHEAD "warn" && + git -C two fetch >output && + echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ + "but we have ${SQ}other${SQ} locally." >expect && + test_cmp expect output && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/other) && + test "z$head" = "z$branch" +' + test_expect_success "fetch test followRemoteHEAD warn no change" ' git -C two rev-parse --verify refs/remotes/origin/other && git -C two remote set-head origin other && @@ -165,6 +190,17 @@ test "z$head" = "z$branch" ' +test_expect_success "fetch test default followRemoteHEAD warn create" ' + git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && + test_config -C two fetch.followRemoteHEAD "warn" && + git -C two rev-parse --verify refs/remotes/origin/main && + output=$(git -C two fetch) && + test "z" = "z$output" && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/main) && + test "z$head" = "z$branch" +' + test_expect_success "fetch test followRemoteHEAD warn create" ' git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && test_config -C two remote.origin.followRemoteHEAD "warn" && @@ -176,6 +212,18 @@ test "z$head" = "z$branch" ' +test_expect_success "fetch test default followRemoteHEAD warn detached" ' + git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && + git -C two update-ref refs/remotes/origin/HEAD HEAD && + HEAD=$(git -C two log --pretty="%H") && + test_config -C two fetch.followRemoteHEAD "warn" && + git -C two fetch >output && + echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ + "but we have a detached HEAD pointing to" \ + "${SQ}${HEAD}${SQ} locally." >expect && + test_cmp expect output +' + test_expect_success "fetch test followRemoteHEAD warn detached" ' git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && git -C two update-ref refs/remotes/origin/HEAD HEAD && @@ -188,6 +236,19 @@ test_cmp expect output ' +test_expect_success "fetch test default followRemoteHEAD warn quiet" ' + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two fetch.followRemoteHEAD "warn" && + output=$(git -C two fetch --quiet) && + test "z" = "z$output" && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/other) && + test "z$head" = "z$branch" +' + test_expect_success "fetch test followRemoteHEAD warn quiet" ' git -C two rev-parse --verify refs/remotes/origin/other && git -C two remote set-head origin other && @@ -229,6 +290,18 @@ test "z$head" = "z$branch" ' +test_expect_success "fetch test default followRemoteHEAD always" ' + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two fetch.followRemoteHEAD "always" && + git -C two fetch && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/main) && + test "z$head" = "z$branch" +' + test_expect_success "fetch test followRemoteHEAD always" ' git -C two rev-parse --verify refs/remotes/origin/other && git -C two remote set-head origin other && @@ -241,6 +314,28 @@ test "z$head" = "z$branch" ' +test_expect_success 'per-remote followRemoteHEAD takes priority over fetch default' ' + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two fetch.followRemoteHEAD "never" && + test_config -C two remote.origin.followRemoteHEAD "always" && + git -C two fetch && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/main) && + test "z$head" = "z$branch" +' + +test_expect_success 'default followRemoteHEAD does not kick in with refspecs' ' + git -C two remote set-head origin other && + test_config -C two fetch.followRemoteHEAD always && + git -C two fetch origin refs/heads/main:refs/remotes/origin/main && + echo refs/remotes/origin/other >expect && + git -C two symbolic-ref refs/remotes/origin/HEAD >actual && + test_cmp expect actual +' + test_expect_success 'followRemoteHEAD does not kick in with refspecs' ' git -C two remote set-head origin other && test_config -C two remote.origin.followRemoteHEAD always && @@ -250,7 +345,18 @@ test_cmp expect actual ' +test_expect_success 'default followRemoteHEAD create does not overwrite dangling symref' ' + test_when_finished "git -C two remote remove custom-head" && + git -C two remote add -m does-not-exist custom-head ../one && + test_config -C two fetch.followRemoteHEAD create && + git -C two fetch custom-head && + echo refs/remotes/custom-head/does-not-exist >expect && + git -C two symbolic-ref refs/remotes/custom-head/HEAD >actual && + test_cmp expect actual +' + test_expect_success 'followRemoteHEAD create does not overwrite dangling symref' ' + test_when_finished "git -C two remote remove custom-head" && git -C two remote add -m does-not-exist custom-head ../one && test_config -C two remote.custom-head.followRemoteHEAD create && git -C two fetch custom-head && @@ -750,7 +856,7 @@ # the strange name is: a\!'b test_expect_success 'quoting of a strangely named repo' ' test_must_fail git fetch "a\\!'\''b" > result 2>&1 && - grep "fatal: '\''a\\\\!'\''b'\''" result + test_grep "fatal: '\''a\\\\!'\''b'\''" result ' test_expect_success 'bundle should record HEAD correctly' ' @@ -904,14 +1010,14 @@ rm -f .git/FETCH_HEAD err && git fetch --dry-run . 2>err && ! test -f .git/FETCH_HEAD && - grep FETCH_HEAD err + test_grep FETCH_HEAD err ' test_expect_success '--no-write-fetch-head does not touch FETCH_HEAD, and does not print what would be written' ' rm -f .git/FETCH_HEAD err && git fetch --no-write-fetch-head . 2>err && ! test -f .git/FETCH_HEAD && - ! grep FETCH_HEAD err + test_grep ! FETCH_HEAD err ' test_expect_success '--write-fetch-head gets defeated by --dry-run' ' @@ -1329,7 +1435,7 @@ git config maintenance.strategy gc && GIT_ASK_YESNO="$TRASH_DIRECTORY/askyesno" git fetch --verbose >fetch.out 2>&1 && test_grep "Auto packing the repository" fetch.out && - ! grep "Should I try again" fetch.out + test_grep ! "Should I try again" fetch.out ) ' @@ -1338,7 +1444,7 @@ test_expect_success "$section.hideRefs affects connectivity check" ' GIT_TRACE="$PWD"/trace git -c $section.hideRefs=refs -c \ $section.hideRefs="!refs/tags/" fetch && - grep "git rev-list .*--exclude-hidden=fetch" trace + test_grep "git rev-list .*--exclude-hidden=fetch" trace ' done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 5930f55..2e1d4f7 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh
@@ -86,7 +86,7 @@ ' test_expect_success 'ls-remote --heads is deprecated and hidden w/o warning' ' - test_expect_code 129 git ls-remote -h >short-help && + git ls-remote -h >short-help && test_grep ! -e --head short-help && git ls-remote --heads self >actual 2>warning && test_cmp expected.branches actual && @@ -245,8 +245,8 @@ git config --add $configsection.hiderefs "!refs/tags/magic" && git config --add $configsection.hiderefs refs/tags/magic/one && git ls-remote . >actual && - grep refs/tags/magic/two actual && - ! grep refs/tags/magic/one actual + test_grep refs/tags/magic/two actual && + test_grep ! refs/tags/magic/one actual ' done @@ -255,13 +255,13 @@ test_config uploadpack.hiderefs refs/tags && test_config transfer.hiderefs "!refs/tags/magic" && git ls-remote . >actual && - grep refs/tags/magic actual + test_grep refs/tags/magic actual ' test_expect_success 'protocol v2 supports hiderefs' ' test_config uploadpack.hiderefs refs/tags && git -c protocol.version=2 ls-remote . >actual && - ! grep refs/tags actual + test_grep ! refs/tags actual ' test_expect_success 'ls-remote --symref' '
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh index 523aff6..5d79472 100755 --- a/t/t5514-fetch-multiple.sh +++ b/t/t5514-fetch-multiple.sh
@@ -217,7 +217,7 @@ test_must_fail env GIT_TRACE="$PWD/trace" \ git fetch --jobs=2 --multiple one two 2>err && - grep "preparing to run up to 2 tasks" trace && + test_grep "preparing to run up to 2 tasks" trace && test_grep "could not fetch .one.*128" err && test_grep "could not fetch .two.*128" err '
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 1b98634..f3b3efc 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh
@@ -124,7 +124,7 @@ do test_expect_success "reject 'git $cmd --no-$opt'" ' test_must_fail git $cmd --no-$opt 2>err && - grep "unknown option .no-$opt" err + test_grep "unknown option .no-$opt" err ' done done @@ -212,7 +212,7 @@ GIT_TRACE2_EVENT="$(pwd)/event" \ git -c protocol.version=2 -c push.negotiate=1 \ push testrepo refs/heads/main:refs/remotes/origin/main && - grep \"key\":\"total_rounds\",\"value\":\"1\" event && + test_grep \"key\":\"total_rounds\",\"value\":\"1\" event && grep_wrote 2 event # 1 commit, 1 tree ' @@ -250,8 +250,8 @@ GIT_TRACE2_EVENT="$(pwd)/event" git -c submodule.recurse=true \ -c protocol.version=2 -c push.negotiate=1 \ push testrepo refs/heads/main:refs/remotes/origin/main 2>err && - grep \"key\":\"total_rounds\",\"value\":\"1\" event && - ! grep "Fetching submodule" err + test_grep \"key\":\"total_rounds\",\"value\":\"1\" event && + test_grep ! "Fetching submodule" err ' test_expect_success 'push with negotiation and remote.<name>.negotiationInclude' ' @@ -637,7 +637,7 @@ test_config branch.main.remote one && test_config branch.main.merge refs/heads/main && test_must_fail git push 2>err && - grep "bad config variable .branch\.\." err + test_grep "bad config variable .branch\.\." err ' test_expect_success 'push ignores "branch." config without subsection' ' @@ -923,7 +923,7 @@ git config receive.denyCurrentBranch warn ) && git push testrepo main 2>stderr && - grep "warning: updating the current branch" stderr + test_grep "warning: updating the current branch" stderr ' test_expect_success 'deny push to HEAD of non-bare repository' ' @@ -945,7 +945,7 @@ git config core.bare true ) && git push testrepo main 2>stderr && - ! grep "warning: updating the current branch" stderr + test_grep ! "warning: updating the current branch" stderr ' test_expect_success 'allow push to HEAD of non-bare repository (config)' ' @@ -956,7 +956,7 @@ git config receive.denyCurrentBranch false ) && git push testrepo main 2>stderr && - ! grep "warning: updating the current branch" stderr + test_grep ! "warning: updating the current branch" stderr ' test_expect_success !WITH_BREAKING_CHANGES 'fetch with branches' ' @@ -1068,7 +1068,7 @@ git commit -a -m child2 && git branch bar && test_must_fail git push ../child1 foo bar 2>stderr && - grep "refusing inconsistent update" stderr + test_grep "refusing inconsistent update" stderr ) ' @@ -1161,7 +1161,7 @@ test_expect_success 'push --porcelain bad url' ' mk_empty testrepo && test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/main:refs/remotes/origin/main && - ! grep -q Done .git/bar + test_grep ! -q Done .git/bar ' test_expect_success 'push --porcelain rejected' '
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 0e00193..27f38ab 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh
@@ -356,8 +356,8 @@ echo "dirty" >>dst/file && test_config -C dst rebase.autostash true && git -C dst pull --rebase >actual 2>&1 && - grep -q "Fast-forward" actual && - grep -q "Applied autostash." actual + test_grep -q "Fast-forward" actual && + test_grep -q "Applied autostash." actual ' test_expect_success '--rebase with conflicts shows advice' '
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh index b2be360..493156a 100755 --- a/t/t5524-pull-msg.sh +++ b/t/t5524-pull-msg.sh
@@ -31,7 +31,7 @@ git pull --no-rebase --log && git log -2 && git cat-file commit HEAD >result && - grep Dollar result + test_grep Dollar result ) ' @@ -44,8 +44,8 @@ git pull --no-rebase --log=1 && git log -3 && git cat-file commit HEAD >result && - grep Dollar result && - ! grep "second commit" result + test_grep Dollar result && + test_grep ! "second commit" result ) '
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 1242ee9..7b3b735 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh
@@ -199,7 +199,7 @@ ) && test_must_be_empty actual.out && verify_fetch_result actual.err && - grep "2 tasks" trace.out + test_grep "2 tasks" trace.out ' test_expect_success "fetch alone only fetches superproject" ' @@ -723,25 +723,25 @@ ( cd downstream && GIT_TRACE=$(pwd)/trace.out git fetch && - grep "1 tasks" trace.out && + test_grep "1 tasks" trace.out && >trace.out && GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 && - grep "7 tasks" trace.out && + test_grep "7 tasks" trace.out && >trace.out && git config submodule.fetchJobs 8 && GIT_TRACE=$(pwd)/trace.out git fetch && - grep "8 tasks" trace.out && + test_grep "8 tasks" trace.out && >trace.out && GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 && - grep "9 tasks" trace.out && + test_grep "9 tasks" trace.out && >trace.out && GIT_TRACE=$(pwd)/trace.out git -c submodule.fetchJobs=0 fetch && - grep "preparing to run up to [0-9]* tasks" trace.out && - ! grep "up to 0 tasks" trace.out && + test_grep "preparing to run up to [0-9]* tasks" trace.out && + test_grep ! "up to 0 tasks" trace.out && >trace.out ) ' @@ -1259,7 +1259,7 @@ git config submodule.recurse true && git fetch --all --no-recurse-submodules 2>../fetch-log ) && - ! grep "Fetching submodule" fetch-log + test_grep ! "Fetching submodule" fetch-log ' test_done
diff --git a/t/t5529-push-errors.sh b/t/t5529-push-errors.sh index 80b06a0..5f8aa1e 100755 --- a/t/t5529-push-errors.sh +++ b/t/t5529-push-errors.sh
@@ -44,14 +44,45 @@ # 'builtin/push.c:set_refspecs()' and we want to test that regression. test_expect_success 'detect empty remote with existing local ref' ' test_must_fail git push "" main 2> stderr && - grep "fatal: bad repository ${SQ}${SQ}" stderr + test_grep "fatal: bad repository ${SQ}${SQ}" stderr ' # While similar to the previous test, here we want to ensure that # even targeted refspecs are handled. test_expect_success 'detect empty remote with targeted refspec' ' test_must_fail git push "" HEAD:refs/heads/main 2> stderr && - grep "fatal: bad repository ${SQ}${SQ}" stderr + test_grep "fatal: bad repository ${SQ}${SQ}" stderr +' + +test_expect_success 'suggest <remote> <branch> for a <remote>/<branch> slip' ' + test_must_fail git push origin/main 2>stderr && + test_grep "${SQ}origin/main${SQ} is not a valid push target" stderr && + test_grep "hint: Did you mean to use: git push origin main?" stderr && + test_must_fail git -c advice.pushRepoLooksLikeRef=false push origin/main 2>stderr && + test_grep ! "Did you mean" stderr +' + +test_expect_success 'suggest <remote> <branch> when the branch has slashes' ' + test_must_fail git push origin/feature/x 2>stderr && + test_grep "hint: Did you mean to use: git push origin feature/x?" stderr +' + +test_expect_success 'no suggestion when prefix is not a configured remote' ' + test_must_fail git push not-a-remote/main 2>stderr && + test_grep ! "Did you mean" stderr +' + +test_expect_success 'no suggestion for a trailing slash with no branch' ' + test_must_fail git push origin/ 2>stderr && + test_grep ! "Did you mean" stderr +' + +test_expect_success 'no suggestion when the argument is an existing path' ' + test_when_finished "rm -rf origin" && + git init --bare origin/main && + git push origin/main HEAD:refs/heads/pushed 2>stderr && + test_grep ! "Did you mean" stderr && + git -C origin/main rev-parse --verify refs/heads/pushed ' test_expect_success 'detect ambiguous refs early' '
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index d40292c..25d409f 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh
@@ -44,16 +44,16 @@ $(($hexsz + 10)) $(git rev-parse HEAD) \ $(($hexsz + 12)) $(git rev-parse HEAD^) >input && test_must_fail git upload-pack . <input >/dev/null 2>output.err && - grep "bad tree object" output.err + test_grep "bad tree object" output.err ' test_expect_success 'upload-pack fails due to bad want (no object)' ' printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \ $(($hexsz + 29)) $(test_oid deadbeef) >input && test_must_fail git upload-pack . <input >output 2>output.err && - grep "not our ref" output.err && - grep "ERR" output && - ! grep multi_ack_detailed output.err + test_grep "not our ref" output.err && + test_grep "ERR" output && + test_grep ! multi_ack_detailed output.err ' test_expect_success 'upload-pack fails due to bad want (not tip)' ' @@ -61,17 +61,17 @@ printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \ $(($hexsz + 29)) "$oid" >input && test_must_fail git upload-pack . <input >output 2>output.err && - grep "not our ref" output.err && - grep "ERR" output && - ! grep multi_ack_detailed output.err + test_grep "not our ref" output.err && + test_grep "ERR" output && + test_grep ! multi_ack_detailed output.err ' test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' printf "%04xwant %s\n00000009done\n0000" \ $((hexsz + 10)) $(git rev-parse HEAD) >input && test_must_fail git upload-pack . <input >/dev/null 2>output.err && - grep "bad tree object" output.err && - grep "pack-objects died" output.err + test_grep "bad tree object" output.err && + test_grep "pack-objects died" output.err ' test_expect_success 'upload-pack tolerates EOF just after stateless client wants' '
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index 05debd1..7d239dd 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh
@@ -559,7 +559,7 @@ test_must_fail git -C upstream rev-parse refs/heads/downstream-branch && git -C upstream/sub rev-parse refs/heads/downstream-branch && git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch && - grep "recursing into submodule with push.recurseSubmodules=only; using on-demand instead" err + test_grep "recursing into submodule with push.recurseSubmodules=only; using on-demand instead" err ' test_expect_success 'push propagating the remotes name to a submodule' '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh index 95d0f33..ccbca7c 100755 --- a/t/t5532-fetch-proxy.sh +++ b/t/t5532-fetch-proxy.sh
@@ -52,7 +52,7 @@ test_expect_success 'funny hostnames are rejected before running proxy' ' test_must_fail git fetch git://-remote/repo.git 2>stderr && - ! grep "proxying for" stderr + test_grep ! "proxying for" stderr ' test_done
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh index cba26a8..2e14aec 100755 --- a/t/t5533-push-cas.sh +++ b/t/t5533-push-cas.sh
@@ -69,7 +69,7 @@ cd dst && test_commit D && test_must_fail git push --force-with-lease=main:main origin main 2>err && - grep "stale info" err + test_grep "stale info" err ) && git ls-remote . refs/heads/main >expect && git ls-remote src refs/heads/main >actual && @@ -82,7 +82,7 @@ cd dst && test_commit D && git push --force --force-with-lease=main:main origin main 2>err && - grep "forced update" err + test_grep "forced update" err ) && git ls-remote dst refs/heads/main >expect && git ls-remote src refs/heads/main >actual && @@ -147,7 +147,7 @@ cd dst && test_commit D && git push --force-with-lease=main origin main 2>err && - ! grep "forced update" err + test_grep ! "forced update" err ) && git ls-remote dst refs/heads/main >expect && git ls-remote src refs/heads/main >actual && @@ -161,7 +161,7 @@ git reset --hard HEAD^ && test_commit D && git push --force-with-lease=main origin main 2>err && - grep "forced update" err + test_grep "forced update" err ) && git ls-remote dst refs/heads/main >expect && git ls-remote src refs/heads/main >actual && @@ -194,7 +194,7 @@ ( cd dst && git push --force-with-lease=main origin :main 2>err && - grep deleted err + test_grep deleted err ) && git ls-remote src refs/heads/main >actual && test_must_be_empty actual @@ -350,7 +350,7 @@ remote_head="$(git rev-parse refs/remotes/origin/main)" && git fetch --all && test_must_fail git push --force-if-includes --force-with-lease="main:$remote_head" 2>err && - grep "stale info" err + test_grep "stale info" err ) && git ls-remote dst refs/heads/main >actual.main && test_cmp expect.main actual.main
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh index 2a78221..21f0262 100755 --- a/t/t5534-push-signed.sh +++ b/t/t5534-push-signed.sh
@@ -126,8 +126,8 @@ noop=$(git rev-parse noop) && ff=$(git rev-parse ff) && noff=$(git rev-parse noff) && - grep "$noop $ff refs/heads/ff" dst/push-cert && - grep "$noop $noff refs/heads/noff" dst/push-cert && + test_grep "$noop $ff refs/heads/ff" dst/push-cert && + test_grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status ' @@ -172,8 +172,8 @@ noop=$(git rev-parse noop) && ff=$(git rev-parse ff) && noff=$(git rev-parse noff) && - grep "$noop $ff refs/heads/ff" dst/push-cert && - grep "$noop $noff refs/heads/noff" dst/push-cert && + test_grep "$noop $ff refs/heads/ff" dst/push-cert && + test_grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status ' @@ -211,7 +211,7 @@ git -C dst config receive.advertisepushoptions 1 && git receive-pack dst <push.tweak >out && git -C dst rev-parse ff && - grep "inconsistent push options" out + test_grep "inconsistent push options" out ' test_expect_success GPG 'fail without key and heed user.signingkey' ' @@ -257,8 +257,8 @@ noop=$(git rev-parse noop) && ff=$(git rev-parse ff) && noff=$(git rev-parse noff) && - grep "$noop $ff refs/heads/ff" dst/push-cert && - grep "$noop $noff refs/heads/noff" dst/push-cert && + test_grep "$noop $ff refs/heads/ff" dst/push-cert && + test_grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status ' @@ -309,8 +309,8 @@ noop=$(git rev-parse noop) && ff=$(git rev-parse ff) && noff=$(git rev-parse noff) && - grep "$noop $ff refs/heads/ff" dst/push-cert && - grep "$noop $noff refs/heads/noff" dst/push-cert && + test_grep "$noop $ff refs/heads/ff" dst/push-cert && + test_grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status ' @@ -362,8 +362,8 @@ noop=$(git rev-parse noop) && ff=$(git rev-parse ff) && noff=$(git rev-parse noff) && - grep "$noop $ff refs/heads/ff" dst/push-cert && - grep "$noop $noff refs/heads/noff" dst/push-cert && + test_grep "$noop $ff refs/heads/ff" dst/push-cert && + test_grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status '
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh index 9982dd2..f323cee 100755 --- a/t/t5537-fetch-shallow.sh +++ b/t/t5537-fetch-shallow.sh
@@ -244,7 +244,7 @@ origin "+refs/heads/*:refs/remotes/origin/*" && git -C shallow-client repack -adfl && test_must_fail git -C shallow-client rev-parse --verify $d^0 && - ! grep $d shallow-client/.git/shallow && + test_grep ! $d shallow-client/.git/shallow && git -C shallow-server branch branch-orig $d && git -C shallow-client fetch --prune --depth=2 \
diff --git a/t/t5538-push-shallow.sh b/t/t5538-push-shallow.sh index dc0e972..afab456 100755 --- a/t/t5538-push-shallow.sh +++ b/t/t5538-push-shallow.sh
@@ -65,7 +65,7 @@ ( cd shallow2 && test_must_fail git push ../.git +main:refs/remotes/shallow2/main 2>err && - grep "shallow2/main.*shallow update not allowed" err + test_grep "shallow2/main.*shallow update not allowed" err ) && test_must_fail git rev-parse shallow2/main && git fsck
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh index 3ea75d3..44e6f60 100755 --- a/t/t5539-fetch-http-shallow.sh +++ b/t/t5539-fetch-http-shallow.sh
@@ -76,8 +76,8 @@ # might be able to run this test in all protocol versions. GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" GIT_TEST_PROTOCOL_VERSION=0 \ git fetch --depth=2 && - grep "fetch-pack< ACK .* ready" ../trace && - ! grep "fetch-pack> done" ../trace + test_grep "fetch-pack< ACK .* ready" ../trace && + test_grep ! "fetch-pack> done" ../trace ) '
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh index 538b603..8c34833 100755 --- a/t/t5541-http-push-smart.sh +++ b/t/t5541-http-push-smart.sh
@@ -56,8 +56,8 @@ git commit -m path2 && HEAD=$(git rev-parse --verify HEAD) && GIT_TRACE_CURL=true git push -v -v 2>err && - ! grep "Expect: 100-continue" err && - grep "POST git-receive-pack ([0-9]* bytes)" err && + test_grep ! "Expect: 100-continue" err && + test_grep "POST git-receive-pack ([0-9]* bytes)" err && (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git && test $HEAD = $(git rev-parse --verify HEAD)) ' @@ -84,7 +84,7 @@ git commit -m path_lang && HEAD=$(git rev-parse --verify HEAD) && GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" git push -v -v 2>err && - ! grep "Expect: 100-continue" err && + test_grep ! "Expect: 100-continue" err && grep "=> Send header: Accept-Language:" err >err.language && test_cmp exp err.language @@ -148,8 +148,8 @@ test_must_fail git push -v origin +main main:niam >output 2>&1' test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' ' - grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *main -> main (forced update)$" output && - grep "^ ! \[rejected\] *main -> niam (non-fast-forward)$" output + test_grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *main -> main (forced update)$" output && + test_grep "^ ! \[rejected\] *main -> niam (non-fast-forward)$" output ' test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' ' @@ -163,7 +163,7 @@ HEAD=$(git rev-parse --verify HEAD) && test_config http.postbuffer 4 && git push -v -v origin $BRANCH 2>err && - grep "POST git-receive-pack (chunked)" err && + test_grep "POST git-receive-pack (chunked)" err && (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git && test $HEAD = $(git rev-parse --verify HEAD)) ' @@ -215,15 +215,15 @@ test_cmp expected actual && # the failed refs should be indicated to the user - grep "^ ! .*rejected.* main -> main" output && + test_grep "^ ! .*rejected.* main -> main" output && # the collateral failure refs should be indicated to the user - grep "^ ! .*rejected.* atomic -> atomic .*atomic push failed" output && - grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output && + test_grep "^ ! .*rejected.* atomic -> atomic .*atomic push failed" output && + test_grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output && # never report what we do not push - ! grep "^ ! .*rejected.* atomic1 " output && - ! grep "^ ! .*rejected.* other " output + test_grep ! "^ ! .*rejected.* atomic1 " output && + test_grep ! "^ ! .*rejected.* other " output ' test_expect_success 'push --atomic fails on server-side errors' ' @@ -247,10 +247,10 @@ test_must_fail git -C "$d" show-ref --verify refs/heads/other && # the failed refs should be indicated to the user - grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output && + test_grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output && # the collateral failure refs should be indicated to the user - grep "^ ! .*rejected.* atomic -> atomic .*atomic transaction failed" output + test_grep "^ ! .*rejected.* atomic -> atomic .*atomic transaction failed" output ' test_expect_success 'push --all can push to empty repo' ' @@ -442,7 +442,7 @@ "$HTTPD_URL_USER_PASS/smart/test_repo.git" \ +HEAD:scrub >status && # should have been scrubbed down to vanilla URL - grep "^To $HTTPD_URL/smart/test_repo.git" status + test_grep "^To $HTTPD_URL/smart/test_repo.git" status ' test_expect_success 'clone/fetch scrubs password from reflogs' ' @@ -456,8 +456,8 @@ +main:main && # should have been scrubbed down to vanilla URL git log -g main >reflog && - grep "$HTTPD_URL" reflog && - ! grep "$HTTPD_URL_USER_PASS" reflog + test_grep "$HTTPD_URL" reflog && + test_grep ! "$HTTPD_URL_USER_PASS" reflog ' test_expect_success 'Non-ASCII branch name can be used with --force-with-lease' '
diff --git a/t/t5544-pack-objects-hook.sh b/t/t5544-pack-objects-hook.sh index 89147a0..908d3b8 100755 --- a/t/t5544-pack-objects-hook.sh +++ b/t/t5544-pack-objects-hook.sh
@@ -27,7 +27,7 @@ clear_hook_results && test_config_global uploadpack.packObjectsHook ./hook && git clone --no-local . dst.git 2>stderr && - grep "hook running" stderr + test_grep "hook running" stderr ' test_expect_success 'hook outputs are sane' ' @@ -38,7 +38,7 @@ # the full argument list or the exact pack contents, as it would make # the test brittle. So just sanity check that we could replay # the packing procedure. - grep "^git" .git/hook.args && + test_grep "^git" .git/hook.args && $(cat .git/hook.args) <.git/hook.stdin >replay ' @@ -47,14 +47,14 @@ git clone --no-local \ -u "git -c uploadpack.packObjectsHook=./hook upload-pack" \ . dst.git 2>stderr && - grep "hook running" stderr + test_grep "hook running" stderr ' test_expect_success 'hook does not run from repo config' ' clear_hook_results && test_config uploadpack.packObjectsHook "./hook" && git clone --no-local . dst.git 2>stderr && - ! grep "hook running" stderr && + test_grep ! "hook running" stderr && test_path_is_missing .git/hook.args && test_path_is_missing .git/hook.stdin && test_path_is_missing .git/hook.stdout && @@ -62,7 +62,7 @@ # check that global config is used instead test_config_global uploadpack.packObjectsHook ./hook && git clone --no-local . dst2.git 2>stderr && - grep "hook running" stderr + test_grep "hook running" stderr ' test_expect_success 'hook works with partial clone' ' @@ -72,7 +72,7 @@ git clone --bare --no-local --filter=blob:none . dst.git && git -C dst.git rev-list --objects --missing=allow-any --no-object-names --all >objects && git -C dst.git cat-file --batch-check="%(objecttype)" <objects >types && - ! grep blob types + test_grep ! blob types ' test_done
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index b0080bf..f00eeae 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh
@@ -124,7 +124,7 @@ set_netrc 127.0.0.1 forbidden-user@host pass@host && test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-netrc-403 2>err && expect_askpass none && - grep "The requested URL returned error: 403" err + test_grep "The requested URL returned error: 403" err ' test_expect_success 'http auth can use user/pass in URL' ' @@ -280,7 +280,7 @@ --index-pack-arg=--keep \ "$HTTPD_URL"/dumb/repo_pack.git/$p >out && - grep -E "^keep.[0-9a-f]{16,}$" out && + test_grep -E "^keep.[0-9a-f]{16,}$" out && cut -c6- out >packhash && # Ensure that the expected files are generated
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index dcff0bc..805bec0 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh
@@ -164,8 +164,8 @@ test_expect_success 'used upload-pack service' ' strip_access_log >log && - grep "GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/[0-9.]* 200" log && - grep "POST /smart/repo.git/git-upload-pack HTTP/[0-9.]* 200" log + test_grep "GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/[0-9.]* 200" log && + test_grep "POST /smart/repo.git/git-upload-pack HTTP/[0-9.]* 200" log ' test_expect_success 'follow redirects (301)' ' @@ -264,8 +264,8 @@ # Ensure that there is no "Basic" followed by a base64 string, but that # the auth details are redacted - ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace && - grep -i "Authorization: Basic <redacted>" trace + test_grep ! -i "Authorization: Basic [0-9a-zA-Z+/]" trace && + test_grep -i "Authorization: Basic <redacted>" trace ' test_expect_success 'GIT_CURL_VERBOSE redacts auth details' ' @@ -276,8 +276,8 @@ # Ensure that there is no "Basic" followed by a base64 string, but that # the auth details are redacted - ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace && - grep -i "Authorization: Basic <redacted>" trace + test_grep ! -i "Authorization: Basic [0-9a-zA-Z+/]" trace && + test_grep -i "Authorization: Basic <redacted>" trace ' test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_REDACT=0' ' @@ -287,7 +287,7 @@ git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth && expect_askpass both user%40host && - grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace + test_grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace ' test_expect_success 'disable dumb http on server' ' @@ -419,7 +419,7 @@ clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err && { test_have_prereq HTTP2 || - grep "^=> Send header: Transfer-Encoding: chunked" err + test_grep "^=> Send header: Transfer-Encoding: chunked" err } ' @@ -554,10 +554,10 @@ GIT_TRACE_CURL=true \ git -c "http.cookieFile=$(pwd)/cookies" clone \ $HTTPD_URL/smart/repo.git clone 2>err && - grep -i "Cookie:.*Foo=<redacted>" err && - grep -i "Cookie:.*Bar=<redacted>" err && - ! grep -i "Cookie:.*Foo=1" err && - ! grep -i "Cookie:.*Bar=2" err + test_grep -i "Cookie:.*Foo=<redacted>" err && + test_grep -i "Cookie:.*Bar=<redacted>" err && + test_grep ! -i "Cookie:.*Foo=1" err && + test_grep ! -i "Cookie:.*Bar=2" err ' test_expect_success 'empty values of cookies are also redacted' ' @@ -566,7 +566,7 @@ GIT_TRACE_CURL=true \ git -c "http.cookieFile=$(pwd)/cookies" clone \ $HTTPD_URL/smart/repo.git clone 2>err && - grep -i "Cookie:.*Foo=<redacted>" err + test_grep -i "Cookie:.*Foo=<redacted>" err ' test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' ' @@ -576,20 +576,20 @@ GIT_TRACE_REDACT=0 GIT_TRACE_CURL=true \ git -c "http.cookieFile=$(pwd)/cookies" clone \ $HTTPD_URL/smart/repo.git clone 2>err && - grep -i "Cookie:.*Foo=1" err && - grep -i "Cookie:.*Bar=2" err + test_grep -i "Cookie:.*Foo=1" err && + test_grep -i "Cookie:.*Bar=2" err ' test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' ' rm -rf clone && GIT_TRACE_CURL=true \ git clone $HTTPD_URL/smart/repo.git clone 2>err && - grep "=> Send data" err && + test_grep "=> Send data" err && rm -rf clone && GIT_TRACE_CURL=true GIT_TRACE_CURL_NO_DATA=1 \ git clone $HTTPD_URL/smart/repo.git clone 2>err && - ! grep "=> Send data" err + test_grep ! "=> Send data" err ' test_expect_success 'server-side error detected' ' @@ -644,7 +644,7 @@ git clone $HTTPD_URL/smart_v0/repo.git repo-v0 && # check for v0; there the HEAD symref is communicated in the capability # line; v2 uses a different syntax on each ref advertisement line - grep symref=HEAD:refs/heads/ trace + test_grep symref=HEAD:refs/heads/ trace ' test_expect_success 'create empty http-accessible SHA-256 repository' ' @@ -692,7 +692,7 @@ git -c transfer.credentialsInUrl=allow \ clone $url_userpass attempt1 2>err && - ! grep "$message" err && + test_grep ! "$message" err && git -c transfer.credentialsInUrl=warn \ clone $url_userpass attempt2 2>err && @@ -721,12 +721,12 @@ esac && git -c transfer.credentialsInUrl=warn clone $url_user attempt1 2>err && - ! grep "uses plaintext credentials" err + test_grep ! "uses plaintext credentials" err ' test_expect_success 'fetch warns or fails when using username:password' ' git -c transfer.credentialsInUrl=allow fetch $url_userpass 2>err && - ! grep "$message" err && + test_grep ! "$message" err && git -c transfer.credentialsInUrl=warn fetch $url_userpass 2>err && grep "warning: $message" err >warnings && @@ -746,7 +746,7 @@ test_expect_success 'push warns or fails when using username:password' ' git -c transfer.credentialsInUrl=allow push $url_userpass 2>err && - ! grep "$message" err && + test_grep ! "$message" err && git -c transfer.credentialsInUrl=warn push $url_userpass 2>err && grep "warning: $message" err >warnings && @@ -763,7 +763,7 @@ git clone $HTTPD_URL/smart/repo.git/ clone-with-slash && strip_access_log >log && - ! grep "//" log + test_grep ! "//" log ' test_expect_success 'tag following always works over v0 http' '
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh index eeddb85..96e2241 100755 --- a/t/t5552-skipping-fetch-negotiator.sh +++ b/t/t5552-skipping-fetch-negotiator.sh
@@ -201,7 +201,7 @@ export GIT_TEST_PROTOCOL_VERSION && trace_fetch client "$(pwd)/server" to_fetch ) && - grep " fetch" trace && + test_grep " fetch" trace && # fetch-pack sends 2 requests each containing 16 "have" lines before # processing the first response. In these 2 requests, 4 commits from @@ -211,8 +211,8 @@ # While fetch-pack is processing the first response, it should read that # the server ACKs b1.c19 and b1.c17. - grep "fetch< ACK $(git -C client rev-parse b1.c19) common" trace && - grep "fetch< ACK $(git -C client rev-parse b1.c17) common" trace && + test_grep "fetch< ACK $(git -C client rev-parse b1.c19) common" trace && + test_grep "fetch< ACK $(git -C client rev-parse b1.c17) common" trace && # fetch-pack should thus not send any more commits in the b1 branch, but # should still send the others (in this test, just check b2).
diff --git a/t/t5554-noop-fetch-negotiator.sh b/t/t5554-noop-fetch-negotiator.sh index 17e73b6..d50ffe5 100755 --- a/t/t5554-noop-fetch-negotiator.sh +++ b/t/t5554-noop-fetch-negotiator.sh
@@ -16,8 +16,8 @@ test_config -C client fetch.negotiationalgorithm noop && GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch "$(pwd)/server" && - ! grep "fetch> have" trace && - grep "fetch> done" trace + test_grep ! "fetch> have" trace && + test_grep "fetch> done" trace ' test_done
diff --git a/t/t5557-http-get.sh b/t/t5557-http-get.sh index 67fcc23..e8ffe3f 100755 --- a/t/t5557-http-get.sh +++ b/t/t5557-http-get.sh
@@ -18,7 +18,7 @@ test_must_fail git remote-http $url <input 2>err && test_path_is_missing file1 && - grep "failed to download file at URL" err + test_grep "failed to download file at URL" err ' test_expect_success 'get by URL: 200' '
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh index 7a0943b..f81f1f8 100755 --- a/t/t5558-clone-bundle-uri.sh +++ b/t/t5558-clone-bundle-uri.sh
@@ -8,14 +8,14 @@ test_expect_success 'fail to clone from non-existent file' ' test_when_finished rm -rf test && git clone --bundle-uri="$(pwd)/does-not-exist" . test 2>err && - grep "failed to download bundle from URI" err + test_grep "failed to download bundle from URI" err ' test_expect_success 'fail to clone from non-bundle file' ' test_when_finished rm -rf test && echo bogus >bogus && git clone --bundle-uri="$(pwd)/bogus" . test 2>err && - grep "is not a bundle" err + test_grep "is not a bundle" err ' test_expect_success 'create bundle' ' @@ -197,7 +197,7 @@ git clone --bundle-uri="file://$(pwd)/bundle-list" \ clone-from clone-list-file 2>err && - ! grep "Repository lacks these prerequisite commits" err && + test_grep ! "Repository lacks these prerequisite commits" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-list-file cat-file --batch-check <oids && @@ -242,9 +242,9 @@ GIT_TRACE2_PERF=1 \ git clone --bundle-uri="file://$(pwd)/bundle-list" \ clone-from clone-all-some 2>err && - ! grep "Repository lacks these prerequisite commits" err && - ! grep "fatal" err && - grep "warning: failed to download bundle from URI" err && + test_grep ! "Repository lacks these prerequisite commits" err && + test_grep ! "fatal" err && + test_grep "warning: failed to download bundle from URI" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-all-some cat-file --batch-check <oids && @@ -275,15 +275,15 @@ git clone --bundle-uri="file://$(pwd)/bundle-list" \ clone-from clone-all-fail 2>err && - ! grep "Repository lacks these prerequisite commits" err && - ! grep "fatal" err && - grep "warning: failed to download bundle from URI" err && + test_grep ! "Repository lacks these prerequisite commits" err && + test_grep ! "fatal" err && + test_grep "warning: failed to download bundle from URI" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-all-fail cat-file --batch-check <oids && git -C clone-all-fail for-each-ref --format="%(refname)" >refs && - ! grep "refs/bundles/heads/" refs + test_grep ! "refs/bundles/heads/" refs ' test_expect_success 'clone bundle list (file, any mode)' ' @@ -306,7 +306,7 @@ git clone --bundle-uri="file://$(pwd)/bundle-list" \ clone-from clone-any-file 2>err && - ! grep "Repository lacks these prerequisite commits" err && + test_grep ! "Repository lacks these prerequisite commits" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-any-file cat-file --batch-check <oids && @@ -336,14 +336,14 @@ git clone --bundle-uri="file://$(pwd)/bundle-list" \ clone-from clone-any-fail 2>err && - ! grep "fatal" err && - grep "warning: failed to download bundle from URI" err && + test_grep ! "fatal" err && + test_grep "warning: failed to download bundle from URI" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-any-fail cat-file --batch-check <oids && git -C clone-any-fail for-each-ref --format="%(refname)" >refs && - ! grep "refs/bundles/heads/" refs + test_grep ! "refs/bundles/heads/" refs ' test_expect_success 'negotiation: bundle with part of wanted commits' ' @@ -477,14 +477,14 @@ test_expect_success 'fail to fetch from non-existent HTTP URL' ' test_when_finished rm -rf test && git clone --bundle-uri="$HTTPD_URL/does-not-exist" . test 2>err && - grep "failed to download bundle from URI" err + test_grep "failed to download bundle from URI" err ' test_expect_success 'fail to fetch from non-bundle HTTP URL' ' test_when_finished rm -rf test && echo bogus >"$HTTPD_DOCUMENT_ROOT_PATH/bogus" && git clone --bundle-uri="$HTTPD_URL/bogus" . test 2>err && - grep "is not a bundle" err + test_grep "is not a bundle" err ' test_expect_success 'clone HTTP bundle' ' @@ -536,7 +536,7 @@ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \ git clone --bundle-uri="$HTTPD_URL/bundle-list" \ clone-from clone-list-http 2>err && - ! grep "Repository lacks these prerequisite commits" err && + test_grep ! "Repository lacks these prerequisite commits" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-list-http cat-file --batch-check <oids && @@ -576,8 +576,8 @@ git clone --bundle-uri="$HTTPD_URL/bundle-list" \ clone-from clone-any-http 2>err && - ! grep "fatal" err && - grep "warning: failed to download bundle from URI" err && + test_grep ! "fatal" err && + test_grep "warning: failed to download bundle from URI" err && git -C clone-from for-each-ref --format="%(objectname)" >oids && git -C clone-any-http cat-file --batch-check <oids && @@ -1302,6 +1302,35 @@ test_path_is_missing escape ' +test_expect_success 'bundles advertised with missing URI' ' + git clone --no-local --mirror clone-from \ + "$HTTPD_DOCUMENT_ROOT_PATH/no-uri.git" && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/no-uri.git" config uploadpack.advertiseBundleURIs true && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/no-uri.git" config bundle.version 1 && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/no-uri.git" config bundle.mode all && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/no-uri.git" config bundle.bundle-1.creationToken 1 && + + git -c transfer.bundleURI=true clone \ + "$HTTPD_URL/smart/no-uri.git" target-no-uri 2>err && + test_grep "bundle ${SQ}bundle-1${SQ} has no uri" err && + test_grep ! "expected packfile" err +' + +test_expect_success 'bundles advertised with empty URI' ' + git clone --no-local --mirror clone-from \ + "$HTTPD_DOCUMENT_ROOT_PATH/empty-uri.git" && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/empty-uri.git" config uploadpack.advertiseBundleURIs true && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/empty-uri.git" config bundle.version 1 && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/empty-uri.git" config bundle.mode all && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/empty-uri.git" config bundle.bundle-1.uri "" && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/empty-uri.git" config bundle.bundle-1.creationToken 1 && + + git -c transfer.bundleURI=true clone \ + "$HTTPD_URL/smart/empty-uri.git" target-empty-uri 2>err && + test_grep "bundle ${SQ}bundle-1${SQ} has no uri" err && + test_grep ! "expected packfile" err +' + # Do not add tests here unless they use the HTTP server, as they will # not run unless the HTTP dependencies exist.
diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh index b6ee06f..59c6a50 100755 --- a/t/t5562-http-backend-content-length.sh +++ b/t/t5562-http-backend-content-length.sh
@@ -163,7 +163,7 @@ REQUEST_METHOD=POST \ CONTENT_LENGTH="$NOT_FIT_IN_SSIZE" \ git http-backend </dev/null >/dev/null 2>err && - grep "fatal:.*CONTENT_LENGTH" err + test_grep "fatal:.*CONTENT_LENGTH" err ' test_expect_success 'empty CONTENT_LENGTH' '
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh index 817cd1f..866e2b4 100755 --- a/t/t5564-http-proxy.sh +++ b/t/t5564-http-proxy.sh
@@ -20,14 +20,14 @@ test_expect_success 'proxy requires password' ' test_config_global http.proxy $HTTPD_DEST && test_must_fail git clone $HTTPD_URL/smart/repo.git 2>err && - grep "error.*407" err + test_grep "error.*407" err ' test_expect_success 'clone through proxy with auth' ' test_when_finished "rm -rf clone" && test_config_global http.proxy http://proxuser:proxpass@$HTTPD_DEST && GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone && - grep -i "Proxy-Authorization: Basic <redacted>" trace + test_grep -i "Proxy-Authorization: Basic <redacted>" trace ' test_expect_success 'clone can prompt for proxy password' ' @@ -82,7 +82,7 @@ GIT_TRACE_CURL=$PWD/trace \ GIT_TRACE_CURL_COMPONENTS=socks \ git clone "$HTTPD_URL/smart/repo.git" clone 2>err && - grep -i "SOCKS4 request granted" trace + test_grep -i "SOCKS4 request granted" trace } || old_libcurl_error err } @@ -90,14 +90,14 @@ test_expect_success 'Unix socket requires socks*:' - <<\EOT ! git clone -c http.proxy=localhost/path https://example.com/repo.git 2>err && { - grep -Fx "fatal: Invalid proxy URL 'localhost/path': only SOCKS proxies support paths" err || + test_grep -Fx "fatal: Invalid proxy URL 'localhost/path': only SOCKS proxies support paths" err || old_libcurl_error err } EOT test_expect_success 'Unix socket requires localhost' - <<\EOT ! git clone -c http.proxy=socks4://127.0.0.1/path https://example.com/repo.git 2>err && { - grep -Fx "fatal: Invalid proxy URL 'socks4://127.0.0.1/path': host must be localhost if a path is present" err || + test_grep -Fx "fatal: Invalid proxy URL 'socks4://127.0.0.1/path': host must be localhost if a path is present" err || old_libcurl_error err } EOT
diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh index cded79c..6788cf8 100755 --- a/t/t5581-http-curl-verbose.sh +++ b/t/t5581-http-curl-verbose.sh
@@ -23,7 +23,7 @@ test_might_fail env GIT_CURL_VERBOSE=1 \ git clone "$HTTPD_URL/error_git_upload_pack/smart/repo.git" \ 2>curl_log && - grep "<= Recv header: HTTP/1.1 500 Intentional Breakage" curl_log + test_grep "<= Recv header: HTTP/1.1 500 Intentional Breakage" curl_log ' test_done
diff --git a/t/t5583-push-branches.sh b/t/t5583-push-branches.sh index e7e1b6d..1ccfaee 100755 --- a/t/t5583-push-branches.sh +++ b/t/t5583-push-branches.sh
@@ -60,21 +60,21 @@ test_must_fail git push remote-1 --all main >actual.all 2>&1 && test_must_fail git push remote-1 --branches main >actual.branches 2>&1 && test_cmp actual.all actual.branches && - grep "be combined with refspecs" actual.all + test_grep "be combined with refspecs" actual.all ' test_expect_success '--all or --branches can not be combined with --mirror' ' test_must_fail git push remote-1 --all --mirror >actual.all 2>&1 && test_must_fail git push remote-1 --branches --mirror >actual.branches 2>&1 && test_cmp actual.all actual.branches && - grep "cannot be used together" actual.all + test_grep "cannot be used together" actual.all ' test_expect_success '--all or --branches can not be combined with --tags' ' test_must_fail git push remote-1 --all --tags >actual.all 2>&1 && test_must_fail git push remote-1 --branches --tags >actual.branches 2>&1 && test_cmp actual.all actual.branches && - grep "cannot be used together" actual.all + test_grep "cannot be used together" actual.all ' @@ -82,7 +82,7 @@ test_must_fail git push remote-1 --all --delete >actual.all 2>&1 && test_must_fail git push remote-1 --branches --delete >actual.branches 2>&1 && test_cmp actual.all actual.branches && - grep "cannot be used together" actual.all + test_grep "cannot be used together" actual.all ' test_expect_success '--all or --branches combines with --follow-tags have same behavior' '
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 3dd229c..b616758 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh
@@ -159,8 +159,8 @@ git clone --mirror src mirror2 && (cd mirror2 && git show-ref 2> clone.err > clone.out) && - ! grep Duplicate mirror2/clone.err && - grep some-tag mirror2/clone.out + test_grep ! Duplicate mirror2/clone.err && + test_grep some-tag mirror2/clone.out ' @@ -224,12 +224,12 @@ cd src-0 && git init ) && git clone "file://$(pwd)/src-0" target-6 2>err-6 && - ! grep "fatal:" err-6 && + test_grep ! "fatal:" err-6 && ( cd src-0 && test_commit A ) && git clone "file://$(pwd)/src-0" target-7 2>err-7 && - ! grep "fatal:" err-7 && + test_grep ! "fatal:" err-7 && # There is no reason to insist they are bit-for-bit # identical, but this test should suffice for now. test_cmp target-6/.git/config target-7/.git/config @@ -298,22 +298,22 @@ rm -rf dst && echo foo=bar >>realgitdir/config && test_must_fail git clone --separate-git-dir realgitdir src dst && - grep foo=bar realgitdir/config + test_grep foo=bar realgitdir/config ' test_expect_success 'clone --reference from original' ' git clone --shared --bare src src-1 && git clone --bare src src-2 && git clone --reference=src-2 --bare src-1 target-8 && - grep /src-2/ target-8/objects/info/alternates + test_grep /src-2/ target-8/objects/info/alternates ' test_expect_success 'clone with more than one --reference' ' git clone --bare src src-3 && git clone --bare src src-4 && git clone --reference=src-3 --reference=src-4 src target-9 && - grep /src-3/ target-9/.git/objects/info/alternates && - grep /src-4/ target-9/.git/objects/info/alternates + test_grep /src-3/ target-9/.git/objects/info/alternates && + test_grep /src-4/ target-9/.git/objects/info/alternates ' test_expect_success 'clone from original with relative alternate' ' @@ -321,7 +321,7 @@ git clone --bare src nest/src-5 && echo ../../../src/.git/objects >nest/src-5/objects/info/alternates && git clone --bare nest/src-5 target-10 && - grep /src/\\.git/objects target-10/objects/info/alternates + test_grep /src/\\.git/objects target-10/objects/info/alternates ' test_expect_success 'clone checking out a tag' ' @@ -663,8 +663,8 @@ ' test_expect_success PERL_TEST_HELPERS,CASE_INSENSITIVE_FS 'colliding file detection' ' - grep X icasefs/warning && - grep x icasefs/warning && + test_grep X icasefs/warning && + test_grep x icasefs/warning && test_grep "the following paths have collided" icasefs/warning ' @@ -857,7 +857,7 @@ cat >pattern <<-EOF && "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\] EOF - grep -f pattern trace.txt + test_grep -f pattern trace.txt ' test_expect_success 'auto-discover multiple bundles from HTTP clone' ' @@ -888,11 +888,11 @@ cat >pattern <<-EOF && "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\] EOF - grep -f pattern trace.txt && + test_grep -f pattern trace.txt && cat >pattern <<-EOF && "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/new.bundle"\] EOF - grep -f pattern trace.txt + test_grep -f pattern trace.txt ' test_expect_success 'auto-discover multiple bundles from HTTP clone: creationToken heuristic' '
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh index c232ab8..39a0c31 100755 --- a/t/t5604-clone-reference.sh +++ b/t/t5604-clone-reference.sh
@@ -65,7 +65,7 @@ test_expect_success 'fetched no objects' ' test -s "$U.D" && - ! grep " want" "$U.D" + test_grep ! " want" "$U.D" ' test_expect_success 'existence of info/alternates' ' @@ -157,9 +157,9 @@ ) && main_object=$(git -C A rev-parse --verify refs/heads/main) && test -s "$U.K" && - ! grep " want $main_object" "$U.K" && + test_grep ! " want $main_object" "$U.K" && tag_object=$(git -C A rev-parse --verify refs/tags/foo) && - ! grep " want $tag_object" "$U.K" + test_grep ! " want $tag_object" "$U.K" ' test_expect_success 'clone using repo with gitfile as a reference' ' @@ -357,7 +357,7 @@ test_must_fail git clone --local malicious clone 2>err && test_path_is_missing clone && - grep "is a symlink, refusing to clone with --local" err + test_grep "is a symlink, refusing to clone with --local" err ' test_expect_success 'dissociate from repo with commit graph' '
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh index 2397f8f..156362f 100755 --- a/t/t5605-clone-local.sh +++ b/t/t5605-clone-local.sh
@@ -172,7 +172,7 @@ echo a >corrupt/.git/refs/heads/topic && test_must_fail git clone corrupt working 2>err && - grep "has neither a valid OID nor a target" err + test_grep "has neither a valid OID nor a target" err ' test_done
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh index 8a15237..1b48da4 100755 --- a/t/t5606-clone-options.sh +++ b/t/t5606-clone-options.sh
@@ -63,7 +63,7 @@ for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD do test_must_fail git clone --bundle-uri=bundle $option from to 2>err && - grep "bundle-uri.* cannot be used together" err || return 1 + test_grep "bundle-uri.* cannot be used together" err || return 1 done ' @@ -147,7 +147,7 @@ test_expect_success 'redirected clone does not show progress' ' git clone "file://$(pwd)/parent" clone-redirected >out 2>err && - ! grep % err && + test_grep ! % err && test_grep ! "Checking connectivity" err ' @@ -156,7 +156,7 @@ git clone --progress "file://$(pwd)/parent" clone-redirected-progress \ >out 2>err && - grep % err + test_grep % err '
diff --git a/t/t5608-clone-2gb.sh b/t/t5608-clone-2gb.sh index 4f8a95d..5d56deb 100755 --- a/t/t5608-clone-2gb.sh +++ b/t/t5608-clone-2gb.sh
@@ -10,45 +10,47 @@ fi test_expect_success 'setup' ' - - git config pack.compression 0 && - git config pack.depth 0 && - blobsize=$((100*1024*1024)) && - blobcount=$((2*1024*1024*1024/$blobsize+1)) && - i=1 && - (while test $i -le $blobcount - do - printf "Generating blob $i/$blobcount\r" >&2 && - printf "blob\nmark :$i\ndata $blobsize\n" && - #test-tool genrandom $i $blobsize && - printf "%-${blobsize}s" $i && - echo "M 100644 :$i $i" >> commit && - i=$(($i+1)) || - echo $? > exit-status - done && - echo "commit refs/heads/main" && - echo "author A U Thor <author@email.com> 123456789 +0000" && - echo "committer C O Mitter <committer@email.com> 123456789 +0000" && - echo "data 5" && - echo ">2gb" && - cat commit) | - git fast-import --big-file-threshold=2 && - test ! -f exit-status - + git init 2gb-repo && + ( + cd 2gb-repo && + git config pack.compression 0 && + git config pack.depth 0 && + blobsize=$((100*1024*1024)) && + blobcount=$((2*1024*1024*1024/$blobsize+1)) && + i=1 && + (while test $i -le $blobcount + do + printf "Generating blob $i/$blobcount\r" >&2 && + printf "blob\nmark :$i\ndata $blobsize\n" && + #test-tool genrandom $i $blobsize && + printf "%-${blobsize}s" $i && + echo "M 100644 :$i $i" >> commit && + i=$(($i+1)) || + echo $? > exit-status + done && + echo "commit refs/heads/main" && + echo "author A U Thor <author@email.com> 123456789 +0000" && + echo "committer C O Mitter <committer@email.com> 123456789 +0000" && + echo "data 5" && + echo ">2gb" && + cat commit) | + git fast-import --big-file-threshold=2 && + test ! -f exit-status + ) ' test_expect_success 'clone - bare' ' - - git clone --bare --no-hardlinks . clone-bare - + test_when_finished rm -rf clone-bare && + git clone --bare --no-hardlinks 2gb-repo clone-bare ' test_expect_success 'clone - with worktree, file:// protocol' ' - - git clone "file://$(pwd)" clone-wt - + test_when_finished rm -rf clone-wt && + git clone "file://$(pwd)/2gb-repo" clone-wt ' +rm -rf 2gb-repo 2>/dev/null + test_expect_success SIZE_T_IS_64BIT,EXPENSIVE 'set up repo with >4GB object' ' large_blob_size=$((4*1024*1024*1024+1)) && git init --bare 4gb-repo && @@ -61,6 +63,7 @@ ' test_expect_success SIZE_T_IS_64BIT,EXPENSIVE 'clone >4GB object via unpack-objects' ' + test_when_finished rm -rf 4gb-clone-unpack && # The synthesized pack has five objects, so a large unpack limit keeps # fetch-pack on the unpack-objects path. git -c fetch.unpackLimit=100 clone --bare \ @@ -77,6 +80,7 @@ ' test_expect_success SIZE_T_IS_64BIT,EXPENSIVE 'clone with >4GB object via index-pack' ' + test_when_finished rm -rf 4gb-clone-index && # Force fetch-pack to hand the pack to index-pack instead. git -c fetch.unpackLimit=1 clone --bare \ "file://$(pwd)/4gb-repo" 4gb-clone-index &&
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh index 3126cfd..dd1fe0a 100755 --- a/t/t5612-clone-refspec.sh +++ b/t/t5612-clone-refspec.sh
@@ -97,7 +97,7 @@ test_expect_success 'clone with --no-tags' ' ( cd dir_all_no_tags && - grep tagOpt .git/config && + test_grep tagOpt .git/config && git fetch && git for-each-ref refs/tags >../actual ) &&
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index 1c2805a..7cc6f58 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh
@@ -60,8 +60,8 @@ ls pc1/.git/objects/pack/pack-*.promisor >promisorlist && test_line_count = 1 promisorlist && git -C srv.bare rev-parse --verify HEAD >headhash && - grep "$(cat headhash) HEAD" $(cat promisorlist) && - grep "$(cat headhash) refs/heads/main" $(cat promisorlist) + test_grep "$(cat headhash) HEAD" $(cat promisorlist) && + test_grep "$(cat headhash) refs/heads/main" $(cat promisorlist) ' # checkout main to force dynamic object fetch of blobs at HEAD. @@ -97,6 +97,30 @@ test_line_count = 5 observed ' +test_expect_success 'partial fetch does not spawn rev-list connectivity check' ' + test_when_finished "rm -rf connectivity-remote connectivity-client" && + git init connectivity-remote && + test_commit -C connectivity-remote one && + git -C connectivity-remote config uploadpack.allowfilter 1 && + git -C connectivity-remote config uploadpack.allowanysha1inwant 1 && + + git clone --no-checkout --filter=blob:none \ + "file://$(pwd)/connectivity-remote" connectivity-client && + + # When doing a partial fetch where all tips are part of a promisor pack + # we want to skip the connectivity check, as these objects are allowed + # to not be fully connected. + test_commit -C connectivity-remote two && + GIT_TRACE2_EVENT="$(pwd)/partial.trace" git -C connectivity-client fetch origin && + test_subcommand_flex ! git rev-list --objects --stdin <partial.trace && + + # Otherwise, when doing a fetch where any of the tips is not part of a + # promisor pack, then we must run the connectivity check. + test_commit -C connectivity-remote three && + GIT_TRACE2_EVENT="$(pwd)/full.trace" git -C connectivity-client fetch --no-filter origin && + test_subcommand_flex git rev-list --objects --stdin <full.trace +' + # force dynamic object fetch using diff. # we should only get 1 new blob (for the file in origin/main). test_expect_success 'verify diff causes dynamic object fetch' ' @@ -230,8 +254,8 @@ GIT_TRACE2_EVENT="$PWD/trace1.event" \ git -C pc1 fetch --refetch origin && test_subcommand git maintenance run --auto --no-quiet --no-detach <trace1.event && - grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace1.event && - grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace1.event && + test_grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace1.event && + test_grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace1.event && GIT_TRACE2_EVENT="$PWD/trace2.event" \ git -c protocol.version=0 \ @@ -239,8 +263,8 @@ -c maintenance.incremental-repack.auto=1234 \ -C pc1 fetch --refetch origin && test_subcommand git maintenance run --auto --no-quiet --no-detach <trace2.event && - grep \"param\":\"gc.autopacklimit\",\"value\":\"0\" trace2.event && - grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace2.event && + test_grep \"param\":\"gc.autopacklimit\",\"value\":\"0\" trace2.event && + test_grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace2.event && GIT_TRACE2_EVENT="$PWD/trace3.event" \ git -c protocol.version=0 \ @@ -248,8 +272,8 @@ -c maintenance.incremental-repack.auto=0 \ -C pc1 fetch --refetch origin && test_subcommand git maintenance run --auto --no-quiet --no-detach <trace3.event && - grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace3.event && - grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"0\" trace3.event + test_grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace3.event && + test_grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"0\" trace3.event ' test_expect_success 'partial clone with transfer.fsckobjects=1 works with submodules' ' @@ -284,7 +308,7 @@ GIT_TRACE="$(pwd)/trace" git -c transfer.fsckobjects=1 \ clone --filter="blob:none" "file://$(pwd)/src" dst && - grep "git index-pack.*--fsck-objects" trace + test_grep "git index-pack.*--fsck-objects" trace ' test_expect_success 'use fsck before and after manually fetching a missing subtree' ' @@ -312,7 +336,7 @@ # Auto-fetch a tree with cat-file. git -C dst cat-file -p $SUBTREE >tree_contents && - grep file.txt tree_contents && + test_grep file.txt tree_contents && # fsck still works after an auto-fetch of a tree. git -C dst fsck && @@ -333,14 +357,14 @@ GIT_TRACE=$(pwd)/trace git clone --bare \ --filter=blob:none --filter=tree:1 \ "file://$(pwd)/srv.bare" pc2 && - grep "trace:.* git pack-objects .*--filter=combine:blob:none+tree:1" \ + test_grep "trace:.* git pack-objects .*--filter=combine:blob:none+tree:1" \ trace && git -C pc2 rev-list --objects --missing=allow-any HEAD >objects && # We should have gotten some root trees. - grep " $" objects && + test_grep " $" objects && # Should not have gotten any non-root trees or blobs. - ! grep " ." objects && + test_grep ! " ." objects && xargs -n 1 git -C pc2 cat-file -t <objects >types && sort -u types >unique_types.actual && @@ -409,7 +433,7 @@ git -C src tag myblob "$BLOB" && git clone --filter="blob:none" "file://$(pwd)/src" dst 2>err && - ! grep "does not point to a valid object" err && + test_grep ! "does not point to a valid object" err && git -C dst fsck ' @@ -424,10 +448,10 @@ git clone --bare --filter=blob:none "file://$(pwd)/src" dst.git && git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_before && - grep "?$(cat blob)" missing_before && + test_grep "?$(cat blob)" missing_before && git -C dst.git fetch origin $(cat blob) && git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_after && - ! grep "?$(cat blob)" missing_after + test_grep ! "?$(cat blob)" missing_after ' test_expect_success 'setup src repo for sparse filter' ' @@ -449,8 +473,8 @@ ( cd dst.git && git rev-list --objects --missing=print HEAD >out && - grep "^$(git rev-parse HEAD:one.t)" out && - grep "^?$(git rev-parse HEAD:two.t)" out + test_grep "^$(git rev-parse HEAD:one.t)" out && + test_grep "^?$(git rev-parse HEAD:two.t)" out ) ' @@ -521,7 +545,7 @@ # Verify the assumption that the client needed to fetch the delta base # to resolve the delta. git -C server rev-parse HEAD~1^{tree} >hash && - grep "want $(cat hash)" trace + test_grep "want $(cat hash)" trace ' test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' ' @@ -538,12 +562,12 @@ fetch "file://$(pwd)/server" main && # Verify that protocol version 2 was used. - grep "fetch< version 2" trace && + test_grep "fetch< version 2" trace && # Verify the assumption that the client needed to fetch the delta base # to resolve the delta. git -C server rev-parse HEAD~1^{tree} >hash && - grep "want $(cat hash)" trace + test_grep "want $(cat hash)" trace ' test_expect_success 'fetch does not lazy-fetch missing targets of its refs' ' @@ -563,7 +587,7 @@ --no-tags --recurse-submodules=no \ origin refs/tags/bar && FOO_HASH=$(git -C server rev-parse foo) && - ! grep "want $FOO_HASH" trace + test_grep ! "want $FOO_HASH" trace ' # The following two tests must be in this order. It is important that @@ -621,7 +645,7 @@ test_config -C client protocol.version 0 && test_commit -C client bar && GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch "file://$(pwd)/server" && - ! grep "version 2" trace + test_grep ! "version 2" trace ' test_expect_success 'fetch from a partial clone, protocol v2' ' @@ -640,7 +664,7 @@ test_config -C client protocol.version 2 && test_commit -C client bar && GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch "file://$(pwd)/server" && - grep "version 2" trace + test_grep "version 2" trace ' test_expect_success 'repack does not loosen promisor objects' ' @@ -648,7 +672,7 @@ git clone --bare --filter=blob:none "file://$(pwd)/srv.bare" client && test_when_finished "rm -rf client trace" && GIT_TRACE2_PERF="$(pwd)/trace" git -C client repack -A -d && - grep "loosen_unused_packed_objects/loosened:0" trace + test_grep "loosen_unused_packed_objects/loosened:0" trace ' test_expect_success 'lazy-fetch in submodule succeeds' ' @@ -824,7 +848,7 @@ # Exercise to make sure it works. git -c protocol.version=2 clone \ --filter=blob:none $HTTPD_URL/one_time_script/server repo 2> err && - ! grep "missing object referenced by" err && + test_grep ! "missing object referenced by" err && # Ensure that the one-time-script script was used. ! test -e "$HTTPD_ROOT_PATH/one-time-script" @@ -881,13 +905,13 @@ # by any 3 nybbles, then the OID of the delta base. printf "f.,..%s" $(intersperse "," <deltabase_missing) >want && hex_unpack <thin.pack | intersperse "," >have && - grep $(cat want) have && + test_grep $(cat want) have && # Ensure that the pack contains one delta against HEAD^:have.txt, # similar to the above. printf "f.,..%s" $(intersperse "," <deltabase_have) >want && hex_unpack <thin.pack | intersperse "," >have && - grep $(cat want) have && + test_grep $(cat want) have && replace_packfile thin.pack && @@ -901,8 +925,8 @@ # Ensure that the missing delta base was directly fetched, but not the # one that the client has. - grep "want $(cat deltabase_missing)" trace && - ! grep "want $(cat deltabase_have)" trace && + test_grep "want $(cat deltabase_missing)" trace && + test_grep ! "want $(cat deltabase_have)" trace && # Ensure that the one-time-script script was used. ! test -e "$HTTPD_ROOT_PATH/one-time-script"
diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh index cce62bf..e4218e5 100755 --- a/t/t5619-clone-local-ambiguous-transport.sh +++ b/t/t5619-clone-local-ambiguous-transport.sh
@@ -64,7 +64,7 @@ # # This works for now, and if we ever fix the URL detection, it # is OK to change this to detect the transport error. - grep "protocol .* is not supported" err + test_grep "protocol .* is not supported" err ' test_done
diff --git a/t/t5620-backfill.sh b/t/t5620-backfill.sh index d2ea68e..7462280 100755 --- a/t/t5620-backfill.sh +++ b/t/t5620-backfill.sh
@@ -144,7 +144,7 @@ test_expect_success 'backfill --sparse without sparse-checkout fails' ' git init not-sparse && test_must_fail git -C not-sparse backfill --sparse 2>err && - grep "problem loading sparse-checkout" err + test_grep "problem loading sparse-checkout" err ' test_expect_success 'backfill --sparse' ' @@ -422,7 +422,7 @@ -C backfill-log log -p HEAD~2..HEAD >log-output && # No promisor fetches should have been needed. - ! grep "fetch_count" log-trace + test_grep ! "fetch_count" log-trace ' test_expect_success 'backfill range without include edges causes on-demand fetches in git-log' ' @@ -439,7 +439,7 @@ GIT_TRACE2_EVENT="$(pwd)/log-no-bdy-trace" git \ -C backfill-log-no-bdy log -p HEAD~2..HEAD >log-output && - grep "fetch_count" log-no-bdy-trace + test_grep "fetch_count" log-no-bdy-trace ' test_expect_success 'backfill range enables fetch-free replay' ' @@ -470,7 +470,7 @@ GIT_TRACE2_EVENT="$(pwd)/replay-trace" git -C replay-dest.git \ replay --onto main topic~1..topic >replay-out && - ! grep "fetch_count" replay-trace + test_grep ! "fetch_count" replay-trace ' test_expect_success 'backfill enables fetch-free merge' ' @@ -501,7 +501,7 @@ GIT_TRACE2_EVENT="$(pwd)/merge-trace" git -C merge-dest \ merge origin/side -m "test merge" && - ! grep "fetch_count" merge-trace + test_grep ! "fetch_count" merge-trace ' . "$TEST_DIRECTORY"/lib-httpd.sh @@ -530,7 +530,7 @@ awk "{print \$1;}" <rev-list-out >oids && GIT_TRACE2_EVENT="$(pwd)/walk-trace" git -C backfill-http \ cat-file --batch-check <oids >batch-out && - ! grep missing batch-out + test_grep ! missing batch-out ' # DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh index a73b4d4..ffeb01a 100755 --- a/t/t5700-protocol-v1.sh +++ b/t/t5700-protocol-v1.sh
@@ -33,9 +33,9 @@ test_cmp expect actual && # Client requested to use protocol v1 - grep "clone> .*\\\0\\\0version=1\\\0$" log && + test_grep "clone> .*\\\0\\\0version=1\\\0$" log && # Server responded using protocol v1 - grep "clone< version 1" log + test_grep "clone< version 1" log ' test_expect_success 'fetch with git:// using protocol v1' ' @@ -49,9 +49,9 @@ test_cmp expect actual && # Client requested to use protocol v1 - grep "fetch> .*\\\0\\\0version=1\\\0$" log && + test_grep "fetch> .*\\\0\\\0version=1\\\0$" log && # Server responded using protocol v1 - grep "fetch< version 1" log + test_grep "fetch< version 1" log ' test_expect_success 'pull with git:// using protocol v1' ' @@ -63,9 +63,9 @@ test_cmp expect actual && # Client requested to use protocol v1 - grep "fetch> .*\\\0\\\0version=1\\\0$" log && + test_grep "fetch> .*\\\0\\\0version=1\\\0$" log && # Server responded using protocol v1 - grep "fetch< version 1" log + test_grep "fetch< version 1" log ' test_expect_success 'push with git:// using protocol v1' ' @@ -81,9 +81,9 @@ test_cmp expect actual && # Client requested to use protocol v1 - grep "push> .*\\\0\\\0version=1\\\0$" log && + test_grep "push> .*\\\0\\\0version=1\\\0$" log && # Server responded using protocol v1 - grep "push< version 1" log + test_grep "push< version 1" log ' stop_git_daemon @@ -104,7 +104,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "clone< version 1" log + test_grep "clone< version 1" log ' test_expect_success 'fetch with file:// using protocol v1' ' @@ -118,7 +118,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "fetch< version 1" log + test_grep "fetch< version 1" log ' test_expect_success 'pull with file:// using protocol v1' ' @@ -130,7 +130,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "fetch< version 1" log + test_grep "fetch< version 1" log ' test_expect_success 'push with file:// using protocol v1' ' @@ -146,7 +146,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "push< version 1" log + test_grep "push< version 1" log ' test_expect_success 'cloning branchless tagless but not refless remote' ' @@ -196,7 +196,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "clone< version 1" log + test_grep "clone< version 1" log ' test_expect_success 'fetch with ssh:// using protocol v1' ' @@ -211,7 +211,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "fetch< version 1" log + test_grep "fetch< version 1" log ' test_expect_success 'pull with ssh:// using protocol v1' ' @@ -224,7 +224,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "fetch< version 1" log + test_grep "fetch< version 1" log ' test_expect_success 'push with ssh:// using protocol v1' ' @@ -241,7 +241,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "push< version 1" log + test_grep "push< version 1" log ' test_expect_success 'clone propagates object-format from empty repo' ' @@ -277,9 +277,9 @@ test_cmp expect actual && # Client requested to use protocol v1 - grep "Git-Protocol: version=1" log && + test_grep "Git-Protocol: version=1" log && # Server responded using protocol v1 - grep "git< version 1" log + test_grep "git< version 1" log ' test_expect_success 'clone with http:// using protocol v1 with empty SHA-256 repo' ' @@ -291,9 +291,9 @@ test_cmp expect actual && # Client requested to use protocol v1 - grep "Git-Protocol: version=1" log && + test_grep "Git-Protocol: version=1" log && # Server responded using protocol v1 - grep "git< version 1" log + test_grep "git< version 1" log ' test_expect_success 'fetch with http:// using protocol v1' ' @@ -307,7 +307,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "git< version 1" log + test_grep "git< version 1" log ' test_expect_success 'pull with http:// using protocol v1' ' @@ -319,7 +319,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "git< version 1" log + test_grep "git< version 1" log ' test_expect_success 'push with http:// using protocol v1' ' @@ -335,7 +335,7 @@ test_cmp expect actual && # Server responded using protocol v1 - grep "git< version 1" log + test_grep "git< version 1" log ' # DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh index d4c28ba..108eb30 100755 --- a/t/t5701-git-serve.sh +++ b/t/t5701-git-serve.sh
@@ -97,7 +97,7 @@ 0000 EOF test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && - grep invalid.command.*agent err + test_grep invalid.command.*agent err ' test_expect_success 'request command as capability' ' @@ -108,7 +108,7 @@ 0000 EOF test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && - grep unknown.capability err + test_grep unknown.capability err ' test_expect_success 'requested command is command=value' ' @@ -118,7 +118,7 @@ 0000 EOF test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && - grep invalid.command.*ls-refs=whatever err + test_grep invalid.command.*ls-refs=whatever err ' test_expect_success 'wrong object-format' ' @@ -175,7 +175,7 @@ EOF test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && - grep unexpected.line.*no-such-arg err + test_grep unexpected.line.*no-such-arg err ' test_expect_success 'basic ref-prefixes' ' @@ -334,7 +334,7 @@ cd server && test_must_fail test-tool serve-v2 --stateless-rpc ) <in >/dev/null 2>err && - grep "unexpected line: .this-is-not-a-command." err + test_grep "unexpected line: .this-is-not-a-command." err ' # Test the basics of object-info @@ -410,7 +410,7 @@ --advertise-capabilities >out && test-tool pkt-line unpack <out >actual && - ! grep object.info actual + test_grep ! object.info actual ' test_expect_success 'object-info commands rejected when disabled' ' @@ -421,7 +421,7 @@ EOF test_must_fail test-tool serve-v2 --stateless-rpc <in 2>err && - grep invalid.command err + test_grep invalid.command err ' test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 9f6cf41..74a2b77 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh
@@ -27,9 +27,9 @@ ls-remote --symref "$GIT_DAEMON_URL/parent" >actual && # Client requested to use protocol v2 - grep "ls-remote> .*\\\0\\\0version=2\\\0$" log && + test_grep "ls-remote> .*\\\0\\\0version=2\\\0$" log && # Server responded using protocol v2 - grep "ls-remote< version 2" log && + test_grep "ls-remote< version 2" log && git ls-remote --symref "$GIT_DAEMON_URL/parent" >expect && test_cmp expect actual @@ -59,9 +59,9 @@ test_cmp expect actual && # Client requested to use protocol v2 - grep "clone> .*\\\0\\\0version=2\\\0$" log && + test_grep "clone> .*\\\0\\\0version=2\\\0$" log && # Server responded using protocol v2 - grep "clone< version 2" log + test_grep "clone< version 2" log ' test_expect_success 'fetch with git:// using protocol v2' ' @@ -77,9 +77,9 @@ test_cmp expect actual && # Client requested to use protocol v2 - grep "fetch> .*\\\0\\\0version=2\\\0$" log && + test_grep "fetch> .*\\\0\\\0version=2\\\0$" log && # Server responded using protocol v2 - grep "fetch< version 2" log + test_grep "fetch< version 2" log ' test_expect_success 'fetch by hash without tag following with protocol v2 does not list refs' ' @@ -91,8 +91,8 @@ GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \ fetch --no-tags origin $(cat two_a_hash) && - grep "fetch< version 2" log && - ! grep "fetch> command=ls-refs" log + test_grep "fetch< version 2" log && + test_grep ! "fetch> command=ls-refs" log ' test_expect_success 'pull with git:// using protocol v2' ' @@ -106,9 +106,9 @@ test_cmp expect actual && # Client requested to use protocol v2 - grep "fetch> .*\\\0\\\0version=2\\\0$" log && + test_grep "fetch> .*\\\0\\\0version=2\\\0$" log && # Server responded using protocol v2 - grep "fetch< version 2" log + test_grep "fetch< version 2" log ' test_expect_success 'push with git:// and a config of v2 does not request v2' ' @@ -130,9 +130,9 @@ test_cmp expect actual && # Client requested to use protocol v2 - ! grep "push> .*\\\0\\\0version=2\\\0$" log && + test_grep ! "push> .*\\\0\\\0version=2\\\0$" log && # Server responded using protocol v2 - ! grep "push< version 2" log + test_grep ! "push< version 2" log ' stop_git_daemon @@ -151,7 +151,7 @@ ls-remote --symref "file://$(pwd)/file_parent" >actual && # Server responded using protocol v2 - grep "ls-remote< version 2" log && + test_grep "ls-remote< version 2" log && git ls-remote --symref "file://$(pwd)/file_parent" >expect && test_cmp expect actual @@ -181,8 +181,8 @@ EOF test_cmp expect actual && - grep "server-option=hello" log && - grep "server-option=world" log + test_grep "server-option=hello" log && + test_grep "server-option=world" log ' test_expect_success 'server-options from configuration are used by ls-remote' ' @@ -241,12 +241,12 @@ test_cmp expect actual && # Server responded using protocol v2 - grep "clone< version 2" log && + test_grep "clone< version 2" log && # Client sent ref-prefixes to filter the ref-advertisement - grep "ref-prefix HEAD" log && - grep "ref-prefix refs/heads/" log && - grep "ref-prefix refs/tags/" log + test_grep "ref-prefix HEAD" log && + test_grep "ref-prefix refs/heads/" log && + test_grep "ref-prefix refs/tags/" log ' test_expect_success 'clone of empty repo propagates name of default branch' ' @@ -311,7 +311,7 @@ echo "refs/heads/mydefaultbranch" >expect && git -C file_unborn_child symbolic-ref HEAD >actual && test_cmp expect actual && - grep "warning: remote HEAD refers to nonexistent ref" stderr + test_grep "warning: remote HEAD refers to nonexistent ref" stderr ' test_expect_success 'clone propagates object-format from empty repo' ' @@ -343,7 +343,7 @@ echo "refs/heads/mydefaultbranch" >expect && git -C file_unborn_child.git symbolic-ref HEAD >actual && test_cmp expect actual && - ! grep "warning:" stderr + test_grep ! "warning:" stderr ' test_expect_success 'defaulted HEAD uses remote branch if available' ' @@ -366,7 +366,7 @@ git -C file_unborn_child symbolic-ref HEAD >actual && test_cmp expect actual && test_path_is_file file_unborn_child/stuff.t && - ! grep "warning:" stderr + test_grep ! "warning:" stderr ' test_expect_success 'fetch with file:// using protocol v2' ' @@ -382,7 +382,7 @@ test_cmp expect actual && # Server responded using protocol v2 - grep "fetch< version 2" log + test_grep "fetch< version 2" log ' test_expect_success 'ref advertisement is filtered during fetch using protocol v2' ' @@ -398,8 +398,8 @@ git -C file_parent log -1 --format=%s >expect && test_cmp expect actual && - grep "refs/heads/main" log && - ! grep "refs/heads/unwanted-branch" log + test_grep "refs/heads/main" log && + test_grep ! "refs/heads/unwanted-branch" log ' test_expect_success 'server-options are sent when fetching' ' @@ -414,8 +414,8 @@ git -C file_parent log -1 --format=%s >expect && test_cmp expect actual && - grep "server-option=hello" log && - grep "server-option=world" log + test_grep "server-option=hello" log && + test_grep "server-option=world" log ' test_expect_success 'server-options are sent when fetch multiple remotes' ' @@ -485,8 +485,8 @@ clone --server-option=hello --server-option=world \ "file://$(pwd)/file_parent" myclone && - grep "server-option=hello" log && - grep "server-option=world" log + test_grep "server-option=hello" log && + test_grep "server-option=world" log ' test_expect_success 'server-options from configuration are used by git-clone' ' @@ -578,12 +578,12 @@ test_expect_success 'partial clone' ' GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \ clone --filter=blob:none "file://$(pwd)/server" client && - grep "version 2" trace && + test_grep "version 2" trace && # Ensure that the old version of the file is missing git -C client rev-list --quiet --objects --missing=print main \ >observed.oids && - grep "$(git -C server rev-parse message1:a.txt)" observed.oids && + test_grep "$(git -C server rev-parse message1:a.txt)" observed.oids && # Ensure that client passes fsck git -C client fsck @@ -593,11 +593,11 @@ rm "$(pwd)/trace" && GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ cat-file -p $(git -C server rev-parse message1:a.txt) && - grep "version 2" trace + test_grep "version 2" trace ' test_expect_success 'when dynamically fetching missing object, do not list refs' ' - ! grep "git> command=ls-refs" trace + test_grep ! "git> command=ls-refs" trace ' test_expect_success 'partial fetch' ' @@ -607,12 +607,12 @@ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ fetch --filter=blob:none "$SERVER" main:refs/heads/other && - grep "version 2" trace && + test_grep "version 2" trace && # Ensure that the old version of the file is missing git -C client rev-list --quiet --objects --missing=print other \ >observed.oids && - grep "$(git -C server rev-parse message1:a.txt)" observed.oids && + test_grep "$(git -C server rev-parse message1:a.txt)" observed.oids && # Ensure that client passes fsck git -C client fsck @@ -625,14 +625,14 @@ git -C server config uploadpack.allowfilter 1 && GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \ ls-remote "$SERVER" && - grep "fetch=.*filter" trace && + test_grep "fetch=.*filter" trace && rm "$(pwd)/trace" && git -C server config uploadpack.allowfilter 0 && GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \ ls-remote "$SERVER" && grep "fetch=" trace >fetch_capabilities && - ! grep filter fetch_capabilities + test_grep ! filter fetch_capabilities ' test_expect_success 'partial clone warns if filter is not advertised' ' @@ -658,7 +658,7 @@ test_must_fail test-tool -C server serve-v2 --stateless-rpc \ <in >/dev/null 2>err && - grep "unexpected line: .filter blob:none." err && + test_grep "unexpected line: .filter blob:none." err && # Exercise to ensure that if advertised, filter works git -C server config uploadpack.allowfilter 1 && @@ -675,8 +675,8 @@ git -C file_parent log -1 --format=%s three >expect && test_cmp expect actual && - grep "ref-prefix refs/heads/" log && - grep "ref-prefix refs/tags/" log + test_grep "ref-prefix refs/heads/" log && + test_grep "ref-prefix refs/tags/" log ' test_expect_success 'set up parent for prefix tests' ' @@ -750,9 +750,9 @@ "$(git -C server rev-parse fetch-by-sha1)" && # Ensure that the appropriate prefixes are sent (using a sample) - grep "fetch> ref-prefix dwim" trace && - grep "fetch> ref-prefix refs/heads/dwim" trace && - grep "fetch> ref-prefix refs/tags/prefix" trace && + test_grep "fetch> ref-prefix dwim" trace && + test_grep "fetch> ref-prefix refs/heads/dwim" trace && + test_grep "fetch> ref-prefix refs/tags/prefix" trace && # Ensure that the correct objects are returned git -C client cat-file -e $(git -C server rev-parse dwim) && @@ -779,9 +779,9 @@ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ fetch "$(pwd)/server" to_fetch:to_fetch && - grep "fetch> ref-prefix to_fetch" trace && - grep "fetch> ref-prefix refs/tags/" trace && - grep "fetch> include-tag" trace && + test_grep "fetch> ref-prefix to_fetch" trace && + test_grep "fetch> ref-prefix refs/tags/" trace && + test_grep "fetch> include-tag" trace && git -C client cat-file -e $(git -C client rev-parse annotated_tag) ' @@ -805,7 +805,7 @@ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ fetch origin newbranch && # Ensure that protocol v2 is used - grep "fetch< version 2" trace + test_grep "fetch< version 2" trace ' test_expect_success 'ensure that multiple fetches in same process from a shallow repo works' ' @@ -823,7 +823,7 @@ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ fetch --shallow-exclude one origin && # Ensure that protocol v2 is used - grep "fetch< version 2" trace + test_grep "fetch< version 2" trace ' test_expect_success 'deepen-relative' ' @@ -844,7 +844,7 @@ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ fetch --deepen=1 origin && # Ensure that protocol v2 is used - grep "fetch< version 2" trace && + test_grep "fetch< version 2" trace && git -C client log --pretty=tformat:%s origin/main >actual && cat >expected <<-\EOF && @@ -909,7 +909,7 @@ --negotiation-tip=$(git -C client rev-parse HEAD) \ origin >out && COMMON=$(git -C "$SERVER" rev-parse two) && - grep "$COMMON" out + test_grep "$COMMON" out ' test_expect_success 'file:// --negotiate-only with protocol v0' ' @@ -932,7 +932,7 @@ --receive-pack="env >../env.trace; git-receive-pack" \ origin HEAD:refs/heads/custom-push-test && test_path_is_file env.trace && - ! grep ^GIT_PROTOCOL env.trace + test_grep ! ^GIT_PROTOCOL env.trace ' test_expect_success 'fetch with custom path does request v2' ' @@ -940,7 +940,7 @@ git -C client fetch \ --upload-pack="env >../env.trace; git-upload-pack" \ origin HEAD && - grep ^GIT_PROTOCOL=version=2 env.trace + test_grep ^GIT_PROTOCOL=version=2 env.trace ' test_expect_success 'archive with custom path does not request v2' ' @@ -950,7 +950,7 @@ --remote=origin \ HEAD >/dev/null && test_path_is_file env.trace && - ! grep ^GIT_PROTOCOL env.trace + test_grep ! ^GIT_PROTOCOL env.trace ' test_expect_success 'reject client packfile-uris if not advertised' ' @@ -994,11 +994,11 @@ test_cmp expect actual && # Client requested to use protocol v2 - grep "Git-Protocol: version=2" log && + test_grep "Git-Protocol: version=2" log && # Server responded using protocol v2 - grep "git< version 2" log && + test_grep "git< version 2" log && # Verify that the chunked encoding sending codepath is NOT exercised - ! grep "Send header: Transfer-Encoding: chunked" log + test_grep ! "Send header: Transfer-Encoding: chunked" log ' test_expect_success 'clone repository with http:// using protocol v2 with incomplete pktline length' ' @@ -1011,9 +1011,9 @@ clone "$HTTPD_URL/smart/incomplete_length" incomplete_length_child 2>err && # Client requested to use protocol v2 - grep "Git-Protocol: version=2" log && + test_grep "Git-Protocol: version=2" log && # Server responded using protocol v2 - grep "git< version 2" log && + test_grep "git< version 2" log && # Client reported appropriate failure test_grep "bytes of length header were received" err ' @@ -1028,9 +1028,9 @@ clone "$HTTPD_URL/smart/incomplete_body" incomplete_body_child 2>err && # Client requested to use protocol v2 - grep "Git-Protocol: version=2" log && + test_grep "Git-Protocol: version=2" log && # Server responded using protocol v2 - grep "git< version 2" log && + test_grep "git< version 2" log && # Client reported appropriate failure test_grep "bytes of body are still expected" err ' @@ -1043,9 +1043,9 @@ clone --shallow-since=20151012 "$HTTPD_URL/smart/http_parent" http_child_invalid && # Client requested to use protocol v2 - grep "Git-Protocol: version=2" log && + test_grep "Git-Protocol: version=2" log && # Server responded using protocol v2 - grep "git< version 2" log + test_grep "git< version 2" log ' test_expect_success 'clone big repository with http:// using protocol v2' ' @@ -1070,11 +1070,11 @@ clone "$HTTPD_URL/smart/big" big_child && # Client requested to use protocol v2 - grep "Git-Protocol: version=2" log && + test_grep "Git-Protocol: version=2" log && # Server responded using protocol v2 - grep "git< version 2" log && + test_grep "git< version 2" log && # Verify that the chunked encoding sending codepath is exercised - grep "Send header: Transfer-Encoding: chunked" log + test_grep "Send header: Transfer-Encoding: chunked" log ' test_expect_success 'fetch with http:// using protocol v2' ' @@ -1090,7 +1090,7 @@ test_cmp expect actual && # Server responded using protocol v2 - grep "git< version 2" log + test_grep "git< version 2" log ' test_expect_success 'fetch with http:// by hash without tag following with protocol v2 does not list refs' ' @@ -1102,8 +1102,8 @@ GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \ fetch --no-tags origin $(cat two_a_hash) && - grep "fetch< version 2" log && - ! grep "fetch> command=ls-refs" log + test_grep "fetch< version 2" log && + test_grep ! "fetch> command=ls-refs" log ' test_expect_success 'fetch from namespaced repo respects namespaces' ' @@ -1120,7 +1120,7 @@ refs/heads/main:refs/heads/theirs && # Server responded using protocol v2 - grep "fetch< version 2" log && + test_grep "fetch< version 2" log && git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" rev-parse one >expect && git -C http_child rev-parse theirs >actual && @@ -1157,9 +1157,9 @@ test_cmp expect actual && # Client did not request to use protocol v2 - ! grep "Git-Protocol: version=2" log && + test_grep ! "Git-Protocol: version=2" log && # Server did not respond using protocol v2 - ! grep "git< version 2" log + test_grep ! "git< version 2" log ' test_expect_success 'when server sends "ready", expect DELIM' ' @@ -1207,8 +1207,8 @@ test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \ -c protocol.version=2 \ fetch "$HTTPD_URL/one_time_script/http_parent" 2> err && - grep "fetch< .*acknowledgments" log && - ! grep "fetch< .*ready" log && + test_grep "fetch< .*acknowledgments" log && + test_grep ! "fetch< .*ready" log && test_grep "expected no other sections to be sent after no .ready." err ' @@ -1247,7 +1247,7 @@ do git verify-pack --object-format=$(test_oid algo) --verbose $idx >out && { - grep -E "^[0-9a-f]{16,} " out || : + grep -E "^[0-9a-f]{16,} " out || : # lint-ok: data filter } >out.objectlist && if test_line_count = 1 out.objectlist then @@ -1446,7 +1446,7 @@ -c fetch.uriprotocols=http,https \ clone "$HTTPD_URL/smart/http_parent" http_child && - grep -F "clone< \\1$(cat packh) $HTTPD_URL/<redacted>" log + test_grep -F "clone< \\1$(cat packh) $HTTPD_URL/<redacted>" log ' test_expect_success 'packfile-uri path not redacted in trace when GIT_TRACE_REDACT=0' ' @@ -1472,7 +1472,7 @@ -c fetch.uriprotocols=http,https \ clone "$HTTPD_URL/smart/http_parent" http_child && - grep -F "clone< \\1$(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" log + test_grep -F "clone< \\1$(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" log ' test_expect_success 'http:// --negotiate-only' ' @@ -1487,7 +1487,7 @@ --negotiation-tip=$(git -C client rev-parse HEAD) \ origin >out && COMMON=$(git -C "$SERVER" rev-parse two) && - grep "$COMMON" out + test_grep "$COMMON" out ' test_expect_success 'http:// --negotiate-only without wait-for-done support' '
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh index 249137b..330d049 100755 --- a/t/t5703-upload-pack-ref-in-want.sh +++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -101,7 +101,7 @@ test-tool pkt-line pack <pkt >in && test_must_fail test-tool serve-v2 --stateless-rpc 2>out <in && - grep "unknown ref" out + test_grep "unknown ref" out ' test_expect_success 'basic want-ref' ' @@ -235,11 +235,11 @@ git -C local fetch origin \ "$oid":refs/heads/actual && - grep \"key\":\"total_rounds\",\"value\":\"2\" trace2 && + test_grep \"key\":\"total_rounds\",\"value\":\"2\" trace2 && git -C "$REPO" rev-parse "d" >expected && git -C local rev-parse refs/heads/actual >actual && test_cmp expected actual && - grep "want $oid" log + test_grep "want $oid" log ' test_expect_success 'fetching multiple refs' ' @@ -252,8 +252,8 @@ git -C "$REPO" rev-parse "main" "baz" >expected && git -C local rev-parse refs/remotes/origin/main refs/remotes/origin/baz >actual && test_cmp expected actual && - grep "want-ref refs/heads/main" log && - grep "want-ref refs/heads/baz" log + test_grep "want-ref refs/heads/main" log && + test_grep "want-ref refs/heads/baz" log ' test_expect_success 'fetching ref and exact OID' ' @@ -268,8 +268,8 @@ git -C "$REPO" rev-parse "main" "b" >expected && git -C local rev-parse refs/remotes/origin/main refs/heads/actual >actual && test_cmp expected actual && - grep "want $oid" log && - grep "want-ref refs/heads/main" log + test_grep "want $oid" log && + test_grep "want-ref refs/heads/main" log ' test_expect_success 'fetching with wildcard that does not match any refs' ' @@ -291,8 +291,8 @@ git -C "$REPO" rev-parse "o/foo" "o/bar" >expected && git -C local rev-parse "o/foo" "o/bar" >actual && test_cmp expected actual && - grep "want-ref refs/heads/o/foo" log && - grep "want-ref refs/heads/o/bar" log + test_grep "want-ref refs/heads/o/foo" log && + test_grep "want-ref refs/heads/o/bar" log ' REPO="$(pwd)/repo-ns" @@ -345,7 +345,7 @@ test_must_fail env GIT_NAMESPACE=ns \ test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in && - grep "unknown ref" out + test_grep "unknown ref" out ' # Cross-check refs/heads/ns-no indeed exists @@ -381,7 +381,7 @@ test_must_fail env GIT_NAMESPACE=ns \ test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in && - grep "unknown ref" out + test_grep "unknown ref" out ' # Cross-check refs/heads/hidden indeed exists
diff --git a/t/t5705-session-id-in-capabilities.sh b/t/t5705-session-id-in-capabilities.sh index ed38c76..ef0db57 100755 --- a/t/t5705-session-id-in-capabilities.sh +++ b/t/t5705-session-id-in-capabilities.sh
@@ -55,8 +55,8 @@ git -c protocol.version=$PROTO -C local fetch \ --upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \ origin && - grep \"key\":\"server-sid\" tr2-client-events && - grep \"key\":\"client-sid\" tr2-server-events + test_grep \"key\":\"server-sid\" tr2-client-events && + test_grep \"key\":\"client-sid\" tr2-server-events ' test_expect_success "session IDs advertised (push v${PROTO})" ' @@ -68,8 +68,8 @@ git -c protocol.version=$PROTO -C local push \ --receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \ origin HEAD:new-branch && - grep \"key\":\"server-sid\" tr2-client-events && - grep \"key\":\"client-sid\" tr2-server-events + test_grep \"key\":\"server-sid\" tr2-client-events && + test_grep \"key\":\"client-sid\" tr2-server-events ' test_expect_success "client & server log negotiated version (v${PROTO})" ' @@ -79,8 +79,8 @@ git -c protocol.version=$PROTO -C local fetch \ --upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \ origin && - grep \"key\":\"negotiated-version\",\"value\":\"$PROTO\" tr2-client-events && - grep \"key\":\"negotiated-version\",\"value\":\"$PROTO\" tr2-server-events + test_grep \"key\":\"negotiated-version\",\"value\":\"$PROTO\" tr2-client-events && + test_grep \"key\":\"negotiated-version\",\"value\":\"$PROTO\" tr2-server-events ' done
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh index b404ad9..549acff 100755 --- a/t/t5710-promisor-remote-capability.sh +++ b/t/t5710-promisor-remote-capability.sh
@@ -177,8 +177,7 @@ git -C server config promisor.advertise true && test_when_finished "rm -rf client" && - mkdir client && - git -C client init && + git init client && git -C client config remote.lop.promisor true && git -C client config remote.lop.fetch "+refs/heads/*:refs/remotes/lop/*" && git -C client config remote.lop.url "$TRASH_DIRECTORY_URL/lop" && @@ -231,8 +230,7 @@ # Create a promisor that will be configured but not be used git init --bare unused_lop && - mkdir client && - git -C client init && + git init client && git -C client config remote.unused_lop.promisor true && git -C client config remote.unused_lop.fetch "+refs/heads/*:refs/remotes/unused_lop/*" && git -C client config remote.unused_lop.url "$TRASH_DIRECTORY_URL/unused_lop" && @@ -389,6 +387,202 @@ check_missing_objects server 1 "$oid" ' +test_expect_success "clone with 'None' but URL allowlisted" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \ + -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with 'None' but URL not in allowlist" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \ + -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="https://example.com/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that the largest object is not missing on the server + check_missing_objects server 0 "" && + + # Reinitialize server so that the largest object is missing again + initialize_server 1 "$oid" +' + +test_expect_success "clone with 'None' but URL allowlisted in one pattern out of two" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \ + -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="https://example.com/*" \ + -c promisor.acceptFromServerUrl="$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with 'None', URL allowlisted, but client has different URL" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + # The client configures "lop" with a different URL (serverTwo) than + # what the server advertises (lop). Even though the advertised URL + # matches the allowlist, the remote is rejected because the + # configured URL does not match the advertised one. + GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \ + -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.lop.url="$TRASH_DIRECTORY_URL/serverTwo" \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that the largest object is not missing on the server + check_missing_objects server 0 "" && + + # Reinitialize server so that the largest object is missing again + initialize_server 1 "$oid" +' + +test_expect_success "clone with URL allowlisted and no remote already configured" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + test_when_finished "rm -f full_names" && + + GIT_NO_LAZY_FETCH=0 git clone \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that exactly one remote has been auto-created, identified + # by "remote.<name>.advertisedAs" == "lop". + git -C client config get --all --show-names --regexp \ + "remote\..*\.advertisedas" >full_names && + test_line_count = 1 full_names && + REMOTE_NAME=$(sed "s/^remote\.\(.*\)\.advertisedas .*$/\1/" full_names) && + + # Check ".url" and ".promisor" values + printf "%s\n" "$TRASH_DIRECTORY_URL/lop" "true" >expect && + git -C client config "remote.$REMOTE_NAME.url" >actual && + git -C client config "remote.$REMOTE_NAME.promisor" >>actual && + test_cmp expect actual && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with named URL allowlisted and no pre-configured remote" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + GIT_NO_LAZY_FETCH=0 git clone \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="cdn=$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that a remote has been auto-created with the right "cdn" name and fields. + printf "%s\n" "$TRASH_DIRECTORY_URL/lop" "true" "lop" >expect && + git -C client config "remote.cdn.url" >actual && + git -C client config "remote.cdn.promisor" >>actual && + git -C client config "remote.cdn.advertisedAs" >>actual && + test_cmp expect actual && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with URL allowlisted but colliding name" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + GIT_NO_LAZY_FETCH=0 git clone -c remote.cdn.promisor=true \ + -c remote.cdn.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.cdn.url="https://example.com/cdn" \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="cdn=$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that a remote has been auto-created with the right "cdn-1" name and fields. + printf "%s\n" "$TRASH_DIRECTORY_URL/lop" "true" "lop" >expect && + git -C client config "remote.cdn-1.url" >actual && + git -C client config "remote.cdn-1.promisor" >>actual && + git -C client config "remote.cdn-1.advertisedAs" >>actual && + test_cmp expect actual && + + # Check that the original "cdn" remote was not overwritten. + printf "%s\n" "https://example.com/cdn" "true" >expect && + git -C client config "remote.cdn.url" >actual && + git -C client config "remote.cdn.promisor" >>actual && + test_cmp expect actual && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with URL allowlisted and reusable remote" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + GIT_NO_LAZY_FETCH=0 git clone \ + -c remote.cdn.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.cdn.url="$TRASH_DIRECTORY_URL/lop" \ + -c promisor.acceptfromserver=None \ + -c promisor.acceptFromServerUrl="cdn=$ENCODED_TRASH_DIRECTORY_URL/*" \ + --no-local --filter="blob:limit=5k" server client && + + # Check that the existing "cdn" remote has been properly updated. + printf "%s\n" "$TRASH_DIRECTORY_URL/lop" "true" "lop" "+refs/heads/*:refs/remotes/lop/*" >expect && + git -C client config "remote.cdn.url" >actual && + git -C client config "remote.cdn.promisor" >>actual && + git -C client config "remote.cdn.advertisedAs" >>actual && + git -C client config "remote.cdn.fetch" >>actual && + test_cmp expect actual && + + # Check that no new "cdn-1" remote has been created. + test_must_fail git -C client config "remote.cdn-1.url" && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with invalid promisor.acceptFromServerUrl" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + # As "bad name" contains a space, which is not a valid remote name, + # the pattern should be rejected with a warning and no remote created. + GIT_NO_LAZY_FETCH=0 git clone \ + -c promisor.acceptfromserver=None \ + -c "promisor.acceptFromServerUrl=bad name=https://example.com/*" \ + --no-local --filter="blob:limit=5k" server client 2>err && + + # Check that a warning was emitted + test_grep "invalid remote name '\''bad name'\''" err && + + # Check that no remote was auto-created + test_must_fail git -C client config get --regexp "remote\..*\.advertisedas" && + + # Check that the largest object is not missing on the server + check_missing_objects server 0 "" && + + # Reinitialize server so that the largest object is missing again + initialize_server 1 "$oid" +' + test_expect_success "clone with promisor.sendFields" ' git -C server config promisor.advertise true && test_when_finished "rm -rf client" &&
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh index 294f9d9..70ae94c 100755 --- a/t/t5750-bundle-uri-parse.sh +++ b/t/t5750-bundle-uri-parse.sh
@@ -78,7 +78,7 @@ # now until the interface for relative_url() allows for reporting # an error instead of die()ing. test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err && - grep "fatal: cannot strip one component off url" err + test_grep "fatal: cannot strip one component off url" err ' test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' ' @@ -283,7 +283,7 @@ EOF test-tool bundle-uri parse-config expect >actual 2>err && - grep "could not parse bundle list key creationToken with value '\''bogus'\''" err + test_grep "could not parse bundle list key creationToken with value '\''bogus'\''" err ' test_expect_success 'parse config format: bundle with missing uri' ' @@ -296,7 +296,7 @@ EOF test_must_fail test-tool bundle-uri parse-config input 2>err && - grep "bundle '\''missing-uri'\'' has no uri" err + test_grep "bundle '\''missing-uri'\'' has no uri" err ' test_expect_success 'parse config format: bundle with url instead of uri' ' @@ -309,7 +309,7 @@ EOF test_must_fail test-tool bundle-uri parse-config input 2>err && - grep "bundle '\''typo'\'' has no uri" err + test_grep "bundle '\''typo'\'' has no uri" err ' test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh index d218771..5fafc85 100755 --- a/t/t5801-remote-helpers.sh +++ b/t/t5801-remote-helpers.sh
@@ -306,7 +306,7 @@ echo new >>file && git commit -a -m new && git push origin new_branch 2> msg && - ! grep "\[new branch\]" msg + test_grep ! "\[new branch\]" msg ) ' @@ -352,7 +352,7 @@ test_must_fail \ env PATH="$PWD:$PATH" \ git clone broken://example.com/foo.git 2>stderr && - grep aborted stderr + test_grep aborted stderr ' test_done
diff --git a/t/t5810-proto-disable-local.sh b/t/t5810-proto-disable-local.sh index 96a2c46..cb3b13f 100755 --- a/t/t5810-proto-disable-local.sh +++ b/t/t5810-proto-disable-local.sh
@@ -28,7 +28,7 @@ test_expect_success 'repo names starting with dash are rejected' ' rm -f trace.out && test_must_fail env GIT_TRACE="$PWD/trace.out" git fetch -- -repo.git && - ! grep upload-pack trace.out + test_grep ! upload-pack trace.out ' test_expect_success 'full paths still work' '
diff --git a/t/t5813-proto-disable-ssh.sh b/t/t5813-proto-disable-ssh.sh index 045e2fe..1800d27 100755 --- a/t/t5813-proto-disable-ssh.sh +++ b/t/t5813-proto-disable-ssh.sh
@@ -24,7 +24,7 @@ # simply confirm from its output that it did not run at all. test_expect_success 'hostnames starting with dash are rejected' ' test_must_fail git clone ssh://-remote/repo.git dash-host 2>stderr && - ! grep ^ssh: stderr + test_grep ! ^ssh: stderr ' test_expect_success 'setup repo with dash' ' @@ -34,7 +34,7 @@ test_expect_success 'repo names starting with dash are rejected' ' test_must_fail git clone remote:-repo.git dash-path 2>stderr && - ! grep ^ssh: stderr + test_grep ! ^ssh: stderr ' test_expect_success 'full paths still work' '
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index a95ba57..ce968f6 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh
@@ -17,8 +17,8 @@ test_expect_success 'rev-list --objects heeds pathspecs' ' git rev-list --objects HEAD -- wanted_file >output && - grep wanted_file output && - ! grep unwanted_file output + test_grep wanted_file output && + test_grep ! unwanted_file output ' test_expect_success 'rev-list --objects with pathspecs and deeper paths' ' @@ -29,11 +29,11 @@ git commit -m two && git rev-list --objects HEAD -- foo >output && - grep foo/file output && + test_grep foo/file output && git rev-list --objects HEAD -- foo/file >output && - grep foo/file output && - ! grep unwanted_file output + test_grep foo/file output && + test_grep ! unwanted_file output ' test_expect_success 'rev-list --objects with pathspecs and copied files' ' @@ -49,28 +49,28 @@ ONE=$(git rev-parse HEAD:one) && git rev-list --objects HEAD two >output && - grep "$ONE two/three" output && - ! grep one output + test_grep "$ONE two/three" output && + test_grep ! one output ' test_expect_success 'rev-list --objects --no-object-names has no space/names' ' git rev-list --objects --no-object-names HEAD >output && - ! grep wanted_file output && - ! grep unwanted_file output && - ! grep " " output + test_grep ! wanted_file output && + test_grep ! unwanted_file output && + test_grep ! " " output ' test_expect_success 'rev-list --objects --no-object-names works with cat-file' ' git rev-list --objects --no-object-names --all >list-output && git cat-file --batch-check <list-output >cat-output && - ! grep missing cat-output + test_grep ! missing cat-output ' test_expect_success '--no-object-names and --object-names are last-one-wins' ' git rev-list --objects --no-object-names --object-names --all >output && - grep wanted_file output && + test_grep wanted_file output && git rev-list --objects --object-names --no-object-names --all >output && - ! grep wanted_file output + test_grep ! wanted_file output ' test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh index 6cde997..0bef583 100755 --- a/t/t6005-rev-list-count.sh +++ b/t/t6005-rev-list-count.sh
@@ -18,7 +18,7 @@ test_expect_success '--max-count' ' test_must_fail git rev-list --max-count=1q HEAD 2>error && - grep "not an integer" error && + test_grep "not an integer" error && test_stdout_line_count = 0 git rev-list HEAD --max-count=0 && test_stdout_line_count = 3 git rev-list HEAD --max-count=3 && @@ -29,10 +29,10 @@ test_expect_success '--max-count all forms' ' test_must_fail git rev-list -1q HEAD 2>error && - grep "not an integer" error && + test_grep "not an integer" error && test_must_fail git rev-list --1 HEAD && test_must_fail git rev-list -n 1q HEAD 2>error && - grep "not an integer" error && + test_grep "not an integer" error && test_stdout_line_count = 1 git rev-list HEAD --max-count=1 && test_stdout_line_count = 1 git rev-list HEAD -1 && @@ -43,7 +43,7 @@ test_expect_success '--skip' ' test_must_fail git rev-list --skip 1q HEAD 2>error && - grep "not an integer" error && + test_grep "not an integer" error && test_stdout_line_count = 5 git rev-list HEAD --skip=0 && test_stdout_line_count = 2 git rev-list HEAD --skip=3 &&
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 5819844..d2648e1 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh
@@ -537,7 +537,7 @@ test_expect_success 'del LF before empty (2)' ' git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD >actual && test_line_count = 6 actual && - grep "^$" actual + test_grep "^$" actual ' test_expect_success 'add LF before non-empty (1)' ' @@ -548,7 +548,7 @@ test_expect_success 'add LF before non-empty (2)' ' git show -s --pretty=format:"%s%+b%nThanks%n" HEAD >actual && test_line_count = 6 actual && - grep "^$" actual + test_grep "^$" actual ' test_expect_success 'add SP before non-empty (1)' '
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index 9c9a845..fd6ee2e 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh
@@ -64,10 +64,10 @@ test_expect_success 'parse --max-parents & --min-parents' ' test_must_fail git rev-list --max-parents=1q HEAD 2>error && - grep "not an integer" error && + test_grep "not an integer" error && test_must_fail git rev-list --min-parents=1q HEAD 2>error && - grep "not an integer" error && + test_grep "not an integer" error && git rev-list --max-parents=1 --min-parents=1 HEAD && git rev-list --max-parents=-1 --min-parents=-1 HEAD
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh index 500c81b..939d421 100755 --- a/t/t6020-bundle-misc.sh +++ b/t/t6020-bundle-misc.sh
@@ -612,7 +612,7 @@ --all \ --filter=blob:none && test_must_fail git clone --bare partial.bdl partial 2>err && - grep "cannot clone from filtered bundle" err + test_grep "cannot clone from filtered bundle" err ' test_expect_success 'verify catches unreachable, broken prerequisites' ' @@ -644,13 +644,13 @@ # Verify should fail test_must_fail git bundle verify \ ../clone-from/tip.bundle 2>err && - grep "some prerequisite commits .* are not connected" err && + test_grep "some prerequisite commits .* are not connected" err && test_line_count = 1 err && # Unbundling should fail test_must_fail git bundle unbundle \ ../clone-from/tip.bundle 2>err && - grep "some prerequisite commits .* are not connected" err && + test_grep "some prerequisite commits .* are not connected" err && test_line_count = 1 err ) ' @@ -658,7 +658,7 @@ test_expect_success 'bundle progress includes write phase' ' GIT_PROGRESS_DELAY=0 \ git bundle create --progress out.bundle --all 2>err && - grep 'Writing' err + test_grep 'Writing' err ' test_expect_success TTY 'create --quiet disables all bundle progress' ' @@ -670,7 +670,7 @@ test_expect_success 'bundle progress with --no-quiet' ' GIT_PROGRESS_DELAY=0 \ git bundle create --no-quiet out.bundle --all 2>err && - grep "%" err + test_grep "%" err ' test_expect_success 'create bundle with duplicate refnames' ' @@ -730,7 +730,7 @@ git bundle create some.bundle HEAD && git bundle verify - <some.bundle 2>err && - grep "<stdin> is okay" err && + test_grep "<stdin> is okay" err && git bundle list-heads some.bundle >expect && git bundle list-heads - <some.bundle >actual &&
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh index 08e92dd..1e472a4 100755 --- a/t/t6022-rev-list-missing.sh +++ b/t/t6022-rev-list-missing.sh
@@ -68,7 +68,7 @@ allow-any) ;; print) - grep ?$oid actual.raw && + test_grep ?$oid actual.raw && echo ?$oid >>expect.raw ;; esac && @@ -132,7 +132,7 @@ allow-any) ;; print) - grep ?$oid actual.raw && + test_grep ?$oid actual.raw && echo ?$oid >>expect.raw ;; esac &&
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 0811162..338df4f 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh
@@ -118,8 +118,8 @@ test_expect_success 'bisect start without -- takes unknown arg as pathspec' ' git bisect reset && git bisect start foo bar && - grep foo ".git/BISECT_NAMES" && - grep bar ".git/BISECT_NAMES" + test_grep foo ".git/BISECT_NAMES" && + test_grep bar ".git/BISECT_NAMES" ' test_expect_success 'bisect reset: back in a branch checked out also elsewhere' ' @@ -258,7 +258,7 @@ git bisect start $HASH4 $HASH1 && git bisect skip && git bisect bad > my_bisect_log.txt && - grep "$HASH2 is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep "$HASH2 is the first '\''bad'\'' commit" my_bisect_log.txt ' # $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2 @@ -269,11 +269,11 @@ git bisect start $HASH4 $HASH1 && git bisect skip && test_expect_code 2 git bisect skip >my_bisect_log.txt && - grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && - ! grep $HASH1 my_bisect_log.txt && - grep $HASH2 my_bisect_log.txt && - grep $HASH3 my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt + test_grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && + test_grep ! $HASH1 my_bisect_log.txt && + test_grep $HASH2 my_bisect_log.txt && + test_grep $HASH3 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt ' # $HASH1 is good, $HASH4 is bad, we skip $HASH3 @@ -285,11 +285,11 @@ git bisect start $HASH4 $HASH1 && git bisect skip && test_expect_code 2 git bisect good >my_bisect_log.txt && - grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && - ! grep $HASH1 my_bisect_log.txt && - ! grep $HASH2 my_bisect_log.txt && - grep $HASH3 my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt + test_grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && + test_grep ! $HASH1 my_bisect_log.txt && + test_grep ! $HASH2 my_bisect_log.txt && + test_grep $HASH3 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt ' # $HASH1 is good, $HASH4 is both skipped and bad, we skip $HASH3 @@ -304,11 +304,11 @@ git bisect good $HASH1 && git bisect skip && test_expect_code 2 git bisect good >my_bisect_log.txt && - grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && - ! grep $HASH1 my_bisect_log.txt && - ! grep $HASH2 my_bisect_log.txt && - grep $HASH3 my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt + test_grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && + test_grep ! $HASH1 my_bisect_log.txt && + test_grep ! $HASH2 my_bisect_log.txt && + test_grep $HASH3 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt ' test_bisect_run_args () { @@ -439,7 +439,7 @@ git bisect good $HASH1 && git bisect bad $HASH4 && git bisect run ./test_script.sh >my_bisect_log.txt && - grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt && + test_grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt && git bisect reset ' @@ -449,7 +449,7 @@ git bisect good $HASH1 && git bisect bad $HASH4 && git bisect run printf "%s %s\n" reset --bisect-skip >my_bisect_log.txt && - grep -e "reset --bisect-skip" my_bisect_log.txt && + test_grep -e "reset --bisect-skip" my_bisect_log.txt && git bisect reset ' @@ -461,7 +461,7 @@ EOF git bisect start $HASH4 $HASH1 && git bisect run ./test_script.sh >my_bisect_log.txt && - grep "$HASH4 is the first '\''bad'\'' commit" my_bisect_log.txt && + test_grep "$HASH4 is the first '\''bad'\'' commit" my_bisect_log.txt && git bisect reset ' @@ -474,7 +474,7 @@ git bisect good $HASH1 && git bisect bad $HASH4 && git bisect run ./test_script.sh >my_bisect_log.txt && - grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt ' test_expect_success POSIXPERM 'bisect run fails with non-executable test script' ' @@ -485,7 +485,7 @@ git bisect good $HASH1 && git bisect bad $HASH4 && test_must_fail git bisect run ./not-executable.sh >my_bisect_log.txt && - ! grep "is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep ! "is the first '\''bad'\'' commit" my_bisect_log.txt ' test_expect_success 'bisect run accepts exit code 127 as bad' ' @@ -497,7 +497,7 @@ git bisect good $HASH1 && git bisect bad $HASH4 && git bisect run ./test_script.sh >my_bisect_log.txt && - grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep "$HASH3 is the first '\''bad'\'' commit" my_bisect_log.txt ' test_expect_success 'bisect run fails with missing test script' ' @@ -507,7 +507,7 @@ git bisect good $HASH1 && git bisect bad $HASH4 && test_must_fail git bisect run ./does-not-exist.sh >my_bisect_log.txt && - ! grep "is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep ! "is the first '\''bad'\'' commit" my_bisect_log.txt ' # $HASH1 is good, $HASH5 is bad, we skip $HASH3 @@ -520,14 +520,14 @@ git bisect start $HASH5 $HASH1 && git bisect skip && git bisect good > my_bisect_log.txt && - grep "$HASH5 is the first '\''bad'\'' commit" my_bisect_log.txt && + test_grep "$HASH5 is the first '\''bad'\'' commit" my_bisect_log.txt && git bisect log > log_to_replay.txt && git bisect reset ' test_expect_success 'bisect skip and bisect replay' ' git bisect replay log_to_replay.txt > my_bisect_log.txt && - grep "$HASH5 is the first '\''bad'\'' commit" my_bisect_log.txt && + test_grep "$HASH5 is the first '\''bad'\'' commit" my_bisect_log.txt && git bisect reset ' @@ -541,11 +541,11 @@ EOF git bisect start $HASH6 $HASH1 && test_expect_code 2 git bisect run ./test_script.sh >my_bisect_log.txt && - grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && - ! grep $HASH3 my_bisect_log.txt && - ! grep $HASH6 my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt && - grep $HASH5 my_bisect_log.txt + test_grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt && + test_grep ! $HASH3 my_bisect_log.txt && + test_grep ! $HASH6 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt && + test_grep $HASH5 my_bisect_log.txt ' HASH7= @@ -560,7 +560,7 @@ EOF git bisect start $HASH7 $HASH1 && git bisect run ./test_script.sh >my_bisect_log.txt && - grep "$HASH6 is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep "$HASH6 is the first '\''bad'\'' commit" my_bisect_log.txt ' test_expect_success 'bisect skip only one range' ' @@ -569,7 +569,7 @@ git bisect skip $HASH1..$HASH5 && test "$HASH6" = "$(git rev-parse --verify HEAD)" && test_must_fail git bisect bad > my_bisect_log.txt && - grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt + test_grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt ' test_expect_success 'bisect skip many ranges' ' @@ -578,7 +578,7 @@ git bisect skip $HASH2 $HASH2.. ..$HASH5 && test "$HASH6" = "$(git rev-parse --verify HEAD)" && test_must_fail git bisect bad > my_bisect_log.txt && - grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt + test_grep "first '\''bad'\'' commit could be any of" my_bisect_log.txt ' test_expect_success 'bisect starting with a detached HEAD' ' @@ -610,7 +610,7 @@ rev_hash6=$(git rev-parse --verify HEAD) && test "$rev_hash6" = "$HASH6" && git bisect good > my_bisect_log.txt && - grep "$HASH7 is the first '\''bad'\'' commit" my_bisect_log.txt && + test_grep "$HASH7 is the first '\''bad'\'' commit" my_bisect_log.txt && git bisect reset && rev_hash6=$(git rev-parse --verify bisect) && test "$rev_hash6" = "$HASH6" && @@ -637,26 +637,26 @@ test_expect_success 'good merge base when good and bad are siblings' ' git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt && test_grep "merge base must be tested" my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt && git bisect good > my_bisect_log.txt && - ! grep "merge base must be tested" my_bisect_log.txt && - grep $HASH6 my_bisect_log.txt && + test_grep ! "merge base must be tested" my_bisect_log.txt && + test_grep $HASH6 my_bisect_log.txt && git bisect reset ' test_expect_success 'skipped merge base when good and bad are siblings' ' git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt && test_grep "merge base must be tested" my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt && git bisect skip > my_bisect_log.txt 2>&1 && - grep "warning" my_bisect_log.txt && - grep $SIDE_HASH6 my_bisect_log.txt && + test_grep "warning" my_bisect_log.txt && + test_grep $SIDE_HASH6 my_bisect_log.txt && git bisect reset ' test_expect_success 'bad merge base when good and bad are siblings' ' git bisect start "$HASH7" HEAD > my_bisect_log.txt && test_grep "merge base must be tested" my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt && + test_grep $HASH4 my_bisect_log.txt && test_must_fail git bisect bad > my_bisect_log.txt 2>&1 && test_grep "merge base $HASH4 is bad" my_bisect_log.txt && test_grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt && @@ -686,8 +686,8 @@ B_HASH=$(git rev-parse --verify HEAD) && git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt && test_line_count = 2 merge_bases.txt && - grep "$HASH5" merge_bases.txt && - grep "$SIDE_HASH5" merge_bases.txt + test_grep "$HASH5" merge_bases.txt && + test_grep "$SIDE_HASH5" merge_bases.txt ' # We want to automatically find the merge that @@ -703,7 +703,7 @@ git bisect good $HASH4 && git bisect bad $B_HASH && git bisect run ./test_script.sh >my_bisect_log.txt && - grep "$B_HASH is the first '\''bad'\'' commit" my_bisect_log.txt && + test_grep "$B_HASH is the first '\''bad'\'' commit" my_bisect_log.txt && git bisect reset && test_path_is_missing .git/BISECT_FIRST_PARENT ' @@ -715,11 +715,11 @@ test_grep "merge base must be tested" my_bisect_log2.txt && { { - grep "$SIDE_HASH5" my_bisect_log.txt && - grep "$HASH5" my_bisect_log2.txt + test_grep "$SIDE_HASH5" my_bisect_log.txt && + test_grep "$HASH5" my_bisect_log2.txt } || { - grep "$SIDE_HASH5" my_bisect_log2.txt && - grep "$HASH5" my_bisect_log.txt + test_grep "$SIDE_HASH5" my_bisect_log2.txt && + test_grep "$HASH5" my_bisect_log.txt } } && git bisect reset @@ -728,7 +728,7 @@ test_expect_success 'optimized merge base checks' ' git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt && test_grep "merge base must be tested" my_bisect_log.txt && - grep "$HASH4" my_bisect_log.txt && + test_grep "$HASH4" my_bisect_log.txt && git bisect good > my_bisect_log2.txt && test -f ".git/BISECT_ANCESTORS_OK" && test "$HASH6" = $(git rev-parse --verify HEAD) && @@ -777,7 +777,7 @@ para1=$(git rev-parse --verify HEAD) && test "$para1" = "$PARA_HASH1" && git bisect bad > my_bisect_log.txt && - grep "$PARA_HASH1 is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep "$PARA_HASH1 is the first '\''bad'\'' commit" my_bisect_log.txt ' test_expect_success 'restricting bisection on one dir and a file' ' @@ -795,7 +795,7 @@ para1=$(git rev-parse --verify HEAD) && test "$para1" = "$PARA_HASH1" && git bisect good > my_bisect_log.txt && - grep "$PARA_HASH4 is the first '\''bad'\'' commit" my_bisect_log.txt + test_grep "$PARA_HASH4 is the first '\''bad'\'' commit" my_bisect_log.txt ' test_expect_success 'skipping away from skipped commit' ' @@ -826,7 +826,7 @@ "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \ >../nocheckout.log ) && - grep "$HASH3 is the first '\''bad'\'' commit" nocheckout.log + test_grep "$HASH3 is the first '\''bad'\'' commit" nocheckout.log ' @@ -841,7 +841,7 @@ "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \ >../defaulted.log ) && - grep "$HASH3 is the first '\''bad'\'' commit" defaulted.log + test_grep "$HASH3 is the first '\''bad'\'' commit" defaulted.log ' # @@ -1031,21 +1031,21 @@ git bisect new $HASH4 && git bisect new && git bisect new >bisect_result && - grep "$HASH2 is the first '\''new'\'' commit" bisect_result && + test_grep "$HASH2 is the first '\''new'\'' commit" bisect_result && git bisect log >log_to_replay.txt && git bisect reset ' test_expect_success 'bisect replay with old and new' ' git bisect replay log_to_replay.txt >bisect_result && - grep "$HASH2 is the first '\''new'\'' commit" bisect_result && + test_grep "$HASH2 is the first '\''new'\'' commit" bisect_result && git bisect reset ' test_expect_success 'bisect replay with CRLF log' ' append_cr <log_to_replay.txt >log_to_replay_crlf.txt && git bisect replay log_to_replay_crlf.txt >bisect_result_crlf && - grep "$HASH2 is the first '\''new'\'' commit" bisect_result_crlf && + test_grep "$HASH2 is the first '\''new'\'' commit" bisect_result_crlf && git bisect reset ' @@ -1092,16 +1092,16 @@ test_expect_success 'bogus command does not start bisect' ' git bisect reset && test_must_fail git bisect --bisect-terms 1 2 2>out && - ! grep "You need to start" out && + test_grep ! "You need to start" out && test_must_fail git bisect --bisect-terms 2>out && - ! grep "You need to start" out && - grep "git bisect.*visualize" out && + test_grep ! "You need to start" out && + test_grep "git bisect.*visualize" out && git bisect reset ' test_expect_success 'bisect replay with term1 and term2' ' git bisect replay log_to_replay.txt >bisect_result && - grep "$HASH2 is the first '\''term1'\'' commit" bisect_result && + test_grep "$HASH2 is the first '\''term1'\'' commit" bisect_result && git bisect reset ' @@ -1120,7 +1120,7 @@ git bisect start --term-new term1 --term-old term2 $HASH4 $HASH1 && git bisect term1 && git bisect term1 >bisect_result && - grep "$HASH2 is the first '\''term1'\'' commit" bisect_result && + test_grep "$HASH2 is the first '\''term1'\'' commit" bisect_result && git bisect log >log_to_replay.txt && git bisect reset ' @@ -1212,7 +1212,7 @@ git bisect good tag-one && git bisect bad tag-two >output && bad=$(git rev-parse --verify tag-two^{commit}) && - grep "$bad is the first '\''bad'\'' commit" output + test_grep "$bad is the first '\''bad'\'' commit" output ' test_expect_success 'bisect run fails with exit code equals or greater than 128' ' @@ -1236,29 +1236,29 @@ test_expect_success 'bisect state output with multiple good commits' ' git bisect reset && git bisect start >output && - grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && + test_grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && git bisect log >output && - grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && + test_grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && git bisect good "$HASH1" >output && - grep "waiting for '\''bad'\'' commit, 1 '\''good'\'' commit known" output && + test_grep "waiting for '\''bad'\'' commit, 1 '\''good'\'' commit known" output && git bisect log >output && - grep "waiting for '\''bad'\'' commit, 1 '\''good'\'' commit known" output && + test_grep "waiting for '\''bad'\'' commit, 1 '\''good'\'' commit known" output && git bisect good "$HASH2" >output && - grep "waiting for '\''bad'\'' commit, 2 '\''good'\'' commits known" output && + test_grep "waiting for '\''bad'\'' commit, 2 '\''good'\'' commits known" output && git bisect log >output && - grep "waiting for '\''bad'\'' commit, 2 '\''good'\'' commits known" output + test_grep "waiting for '\''bad'\'' commit, 2 '\''good'\'' commits known" output ' test_expect_success 'bisect state output with bad commit' ' git bisect reset && git bisect start >output && - grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && + test_grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && git bisect log >output && - grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && + test_grep "waiting for both '\''good'\'' and '\''bad'\'' commits" output && git bisect bad "$HASH4" >output && - grep -F "waiting for '\''good'\'' commit(s), '\''bad'\'' commit known" output && + test_grep -F "waiting for '\''good'\'' commit(s), '\''bad'\'' commit known" output && git bisect log >output && - grep -F "waiting for '\''good'\'' commit(s), '\''bad'\'' commit known" output + test_grep -F "waiting for '\''good'\'' commit(s), '\''bad'\'' commit known" output ' test_expect_success 'verify correct error message' ' @@ -1268,7 +1268,7 @@ rm .git/BISECT* EOF test_must_fail git bisect run ./test_script.sh 2>error && - grep "git bisect good.*exited with error code" error + test_grep "git bisect good.*exited with error code" error ' test_done
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 0242b5b..e95d420 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh
@@ -275,7 +275,7 @@ git branch --set-upstream-to main from-topic_2 && git config branch.from-main.merge > actual && git rev-parse from-topic_2 >actual2 && - grep -q "^refs/heads/main$" actual && + test_grep -q "^refs/heads/main$" actual && cmp expect2 actual2 ' @@ -646,4 +646,104 @@ test_cmp expect actual ' +test_expect_success 'status.compareBranches behind both upstream and push' ' + test_config -C test push.default current && + test_config -C test remote.pushDefault origin && + test_config -C test status.compareBranches "@{upstream} @{push}" && + git -C test checkout -b feature13 upstream/main && + (cd test && advance work13) && + git -C test push origin && + git -C test branch --set-upstream-to upstream/ahead && + git -C test reset --hard HEAD^ && + git -C test status >actual && + cat >expect <<-EOF && + On branch feature13 + Your branch is behind ${SQ}upstream/ahead${SQ} by 1 commit, and can be fast-forwarded. + (use "git pull" to update your local branch) + + Your branch is behind ${SQ}origin/feature13${SQ} by 1 commit, and can be fast-forwarded. + (use "git pull origin feature13" to update your local branch) + + nothing to commit, working tree clean + EOF + test_cmp expect actual +' + +test_expect_success 'status.compareBranches with remapped push and behind push branch' ' + test_config -C test remote.pushDefault origin && + test_config -C test remote.origin.push refs/heads/feature14:refs/heads/remapped14 && + test_config -C test status.compareBranches "@{push}" && + git -C test checkout -b feature14 upstream/main && + (cd test && advance work14) && + git -C test push && + git -C test reset --hard HEAD^ && + git -C test status >actual && + cat >expect <<-EOF && + On branch feature14 + Your branch is behind ${SQ}origin/remapped14${SQ} by 1 commit, and can be fast-forwarded. + (use "git pull origin remapped14" to update your local branch) + + nothing to commit, working tree clean + EOF + test_cmp expect actual +' + +test_expect_success 'status.compareBranches with behind push branch and no upstream' ' + test_config -C test push.default current && + test_config -C test remote.pushDefault origin && + test_config -C test status.compareBranches "@{push}" && + git -C test checkout --no-track -b feature15 upstream/main && + (cd test && advance work15) && + git -C test push origin && + git -C test reset --hard HEAD^ && + git -C test status >actual && + cat >expect <<-EOF && + On branch feature15 + Your branch is behind ${SQ}origin/feature15${SQ} by 1 commit, and can be fast-forwarded. + (use "git pull origin feature15" to update your local branch) + + nothing to commit, working tree clean + EOF + test_cmp expect actual +' + +test_expect_success 'status.compareBranches behind upstream-equals-push suggests plain pull' ' + test_config -C test status.compareBranches "@{upstream} @{push}" && + git -C test checkout -b feature16 origin/main && + (cd test && advance work16) && + git -C test push origin HEAD:main && + git -C test reset --hard HEAD^ && + git -C test status >actual && + cat >expect <<-EOF && + On branch feature16 + Your branch is behind ${SQ}origin/main${SQ} by 1 commit, and can be fast-forwarded. + (use "git pull" to update your local branch) + + nothing to commit, working tree clean + EOF + test_cmp expect actual +' + +test_expect_success 'status.compareBranches suppresses advice when push tracking ref is unconventional' ' + test_config -C test push.default current && + test_config -C test remote.imported.url ../. && + test_config -C test remote.imported.fetch "+refs/heads/*:refs/imported/imported/*" && + test_config -C test branch.feature17.pushRemote imported && + test_config -C test status.compareBranches "@{push}" && + git -C test fetch imported && + git -C test checkout --no-track -b feature17 refs/imported/imported/main && + (cd test && advance work17) && + git -C test push imported HEAD:feature17 && + git -C test fetch imported && + git -C test reset --hard HEAD^ && + git -C test status >actual && + cat >expect <<-EOF && + On branch feature17 + Your branch is behind ${SQ}imported/imported/feature17${SQ} by 1 commit, and can be fast-forwarded. + + nothing to commit, working tree clean + EOF + test_cmp expect actual +' + test_done
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index e0a825b..a1c244c 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh
@@ -45,8 +45,8 @@ awk -f print_2.awk) && git -C r1 rev-list --objects --filter=blob:none HEAD $file_3 >observed && - grep "$file_3" observed && - ! grep "$file_4" observed + test_grep "$file_3" observed && + test_grep ! "$file_4" observed ' test_expect_success 'verify emitted+omitted == all' ' @@ -324,7 +324,7 @@ git -C r3.b rev-list --missing=allow-any --objects HEAD \ >objs 2>rev_list_err && - ! grep $TREE objs && + test_grep ! $TREE objs && test_must_be_empty rev_list_err ' @@ -353,7 +353,7 @@ test_line_count = 2 actual && # Make sure no other trees were considered besides the root. - ! grep "Skipping contents of tree [^.]" filter_trace && + test_grep ! "Skipping contents of tree [^.]" filter_trace && # Try this again with "combine:". If both sub-filters are skipping # trees, the composite filter should also skip trees. This is not @@ -539,7 +539,7 @@ HEAD >actual && test_cmp expect actual && - grep "Add to combine filter-spec: sparse:oid=main:p%3bat%25ter%2bn" \ + test_grep "Add to combine filter-spec: sparse:oid=main:p%3bat%25ter%2bn" \ trace1 && # Repeat the above test, but this time, the characters to encode are in @@ -551,7 +551,7 @@ HEAD >actual && test_cmp expect actual && - grep "Add to combine filter-spec: sparse:oid=main:%5e%7epattern" \ + test_grep "Add to combine filter-spec: sparse:oid=main:%5e%7epattern" \ trace2 ' @@ -623,9 +623,9 @@ omitted_2=$(echo a | git hash-object --stdin) && omitted_3=$(echo abcde | git hash-object --stdin) && - grep "~$omitted_1" actual && - grep "~$omitted_2" actual && - grep "~$omitted_3" actual && + test_grep "~$omitted_1" actual && + test_grep "~$omitted_2" actual && + test_grep "~$omitted_3" actual && test_line_count = 3 actual ' @@ -654,7 +654,7 @@ blob_hash=$(git -C r4 rev-parse HEAD:subdir/bar) && git -C r4 rev-list --objects --filter=tree:1 HEAD $blob_hash >actual && - grep ^$blob_hash actual + test_grep ^$blob_hash actual ' # Delete some loose objects and use rev-list, but WITHOUT any filtering. @@ -693,8 +693,8 @@ git -C r2 config --local uploadpack.allowfilter 1 && GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 clone \ --filter=blob:limit=1k "file://$(pwd)/r2" limit && - ! grep "blob:limit=1k" trace && - grep "blob:limit=1024" trace + test_grep ! "blob:limit=1k" trace && + test_grep "blob:limit=1024" trace ' test_expect_success EXPENSIVE 'large sparse filter file ignored' '
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh index 04c577d..b9650b8 100755 --- a/t/t6115-rev-list-du.sh +++ b/t/t6115-rev-list-du.sh
@@ -61,13 +61,13 @@ test_expect_success 'rev-list --disk-usage=human' ' git rev-list --objects HEAD --disk-usage=human >actual && disk_usage_slow --objects HEAD >actual_size && - grep "$(cat actual_size) bytes" actual + test_grep "$(cat actual_size) bytes" actual ' test_expect_success 'rev-list --disk-usage=human with bitmaps' ' git rev-list --objects HEAD --use-bitmap-index --disk-usage=human >actual && disk_usage_slow --objects HEAD >actual_size && - grep "$(cat actual_size) bytes" actual + test_grep "$(cat actual_size) bytes" actual ' test_expect_success 'rev-list use --disk-usage unproperly' '
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 4d72033..7a7c466 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh
@@ -128,7 +128,7 @@ warning: tag 'Q' is externally known as 'A' EOF test_cmp expected err && - grep -E '^A-8-g[0-9a-f]+$' out + test_grep -E '^A-8-g[0-9a-f]+$' out " test_expect_success 'misnamed annotated tag forces long output' ' @@ -160,7 +160,7 @@ test_expect_success 'describe works from outside repo using --git-dir' ' git clone --bare "$TRASH_DIRECTORY" "$TRASH_DIRECTORY/bare" && git --git-dir "$TRASH_DIRECTORY/bare" describe >out && - grep -E "^A-8-g[0-9a-f]+$" out + test_grep -E "^A-8-g[0-9a-f]+$" out ' check_describe "A-8-gHASH" --dirty @@ -170,7 +170,7 @@ cd "$TEST_DIRECTORY" && git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out" ) && - grep -E "^A-8-g[0-9a-f]+$" out + test_grep -E "^A-8-g[0-9a-f]+$" out ' test_expect_success 'set-up dirty work tree' ' @@ -183,7 +183,7 @@ cd "$TEST_DIRECTORY" && git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out" ) && - grep -E "^A-8-g[0-9a-f]+-dirty$" out && + test_grep -E "^A-8-g[0-9a-f]+-dirty$" out && test_cmp expected out ' @@ -193,7 +193,7 @@ cd "$TEST_DIRECTORY" && git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty=.mod >"$TRASH_DIRECTORY/out" ) && - grep -E "^A-8-g[0-9a-f]+.mod$" out && + test_grep -E "^A-8-g[0-9a-f]+.mod$" out && test_cmp expected out ' @@ -399,7 +399,7 @@ test_expect_success 'describe ignoring a broken submodule' ' git describe --broken >out && - grep broken out + test_grep broken out ' test_expect_success 'describe with --work-tree ignoring a broken submodule' ' @@ -408,7 +408,7 @@ git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --broken >"$TRASH_DIRECTORY/out" ) && test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" && - grep broken out + test_grep broken out ' test_expect_success 'describe a blob at a directly tagged commit' '
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 011e5df..79fe1f2 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh
@@ -125,20 +125,20 @@ git checkout main && git fetch . signed-good-tag && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}signed-good-tag${apos}" actual && - grep "^signed-tag-msg" actual && - grep "^# gpg: Signature made" actual && - grep "^# gpg: Good signature from" actual + test_grep "^Merge tag ${apos}signed-good-tag${apos}" actual && + test_grep "^signed-tag-msg" actual && + test_grep "^# gpg: Signature made" actual && + test_grep "^# gpg: Good signature from" actual ' test_expect_success GPG 'message for merging local tag signed by unknown key' ' git checkout main && git fetch . signed-good-tag && GNUPGHOME=. git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}signed-good-tag${apos}" actual && - grep "^signed-tag-msg" actual && - grep "^# gpg: Signature made" actual && - grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual + test_grep "^Merge tag ${apos}signed-good-tag${apos}" actual && + test_grep "^signed-tag-msg" actual && + test_grep "^# gpg: Signature made" actual && + test_grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual ' test_expect_success GPGSSH 'message for merging local tag signed by good ssh key' ' @@ -146,10 +146,10 @@ git checkout main && git fetch . signed-good-ssh-tag && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}signed-good-ssh-tag${apos}" actual && - grep "^signed-ssh-tag-msg" actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual + test_grep "^Merge tag ${apos}signed-good-ssh-tag${apos}" actual && + test_grep "^signed-ssh-tag-msg" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual ' test_expect_success GPGSSH 'message for merging local tag signed by unknown ssh key' ' @@ -157,11 +157,11 @@ git checkout main && git fetch . signed-untrusted-ssh-tag && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}signed-untrusted-ssh-tag${apos}" actual && - grep "^signed-ssh-tag-msg-untrusted" actual && - grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && - grep "${GPGSSH_KEY_NOT_TRUSTED}" actual + test_grep "^Merge tag ${apos}signed-untrusted-ssh-tag${apos}" actual && + test_grep "^signed-ssh-tag-msg-untrusted" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_KEY_NOT_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by expired ssh key' ' @@ -169,9 +169,9 @@ git checkout main && git fetch . expired-signed && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}expired-signed${apos}" actual && - grep "^expired-signed" actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep "^Merge tag ${apos}expired-signed${apos}" actual && + test_grep "^expired-signed" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by not yet valid ssh key' ' @@ -179,9 +179,9 @@ git checkout main && git fetch . notyetvalid-signed && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}notyetvalid-signed${apos}" actual && - grep "^notyetvalid-signed" actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep "^Merge tag ${apos}notyetvalid-signed${apos}" actual && + test_grep "^notyetvalid-signed" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by valid timeboxed ssh key' ' @@ -189,10 +189,10 @@ git checkout main && git fetch . timeboxedvalid-signed && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}timeboxedvalid-signed${apos}" actual && - grep "^timeboxedvalid-signed" actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual + test_grep "^Merge tag ${apos}timeboxedvalid-signed${apos}" actual && + test_grep "^timeboxedvalid-signed" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by invalid timeboxed ssh key' ' @@ -200,9 +200,9 @@ git checkout main && git fetch . timeboxedinvalid-signed && git fmt-merge-msg <.git/FETCH_HEAD >actual && - grep "^Merge tag ${apos}timeboxedinvalid-signed${apos}" actual && - grep "^timeboxedinvalid-signed" actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep "^Merge tag ${apos}timeboxedinvalid-signed${apos}" actual && + test_grep "^timeboxedinvalid-signed" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success 'message for merging external branch' ' @@ -651,21 +651,21 @@ git show -s --format="%s" >full.0 && head -n1 full.0 >actual && # expect that HEAD is shown as-is - grep -e "Merge branch .side. into HEAD$" actual && + test_grep -e "Merge branch .side. into HEAD$" actual && git reset --hard main && git merge --no-ff --into-name=main side && git show -s --format="%s" >full.1 && head -n1 full.1 >actual && # expect that we pretend to be merging to main, that is suppressed - grep -e "Merge branch .side.$" actual && + test_grep -e "Merge branch .side.$" actual && git checkout -b throwaway main && git merge --no-ff --into-name=main side && git show -s --format="%s" >full.2 && head -n1 full.2 >actual && # expect that we pretend to be merging to main, that is suppressed - grep -e "Merge branch .side.$" actual + test_grep -e "Merge branch .side.$" actual ' test_expect_success 'merge.suppressDest configuration' ' @@ -677,28 +677,28 @@ git -c merge.suppressDest="" fmt-merge-msg <.git/FETCH_HEAD >full.1 && head -n1 full.1 >actual && - grep -e "Merge branch .side. into main" actual && + test_grep -e "Merge branch .side. into main" actual && git -c merge.suppressDest="mast" fmt-merge-msg <.git/FETCH_HEAD >full.2 && head -n1 full.2 >actual && - grep -e "Merge branch .side. into main$" actual && + test_grep -e "Merge branch .side. into main$" actual && git -c merge.suppressDest="ma?*[rn]" fmt-merge-msg <.git/FETCH_HEAD >full.3 && head -n1 full.3 >actual && - grep -e "Merge branch .side." actual && - ! grep -e " into main$" actual && + test_grep -e "Merge branch .side." actual && + test_grep ! -e " into main$" actual && git checkout --detach HEAD && git -c merge.suppressDest="main" fmt-merge-msg <.git/FETCH_HEAD >full.4 && head -n1 full.4 >actual && - grep -e "Merge branch .side. into HEAD$" actual && + test_grep -e "Merge branch .side. into HEAD$" actual && git -c merge.suppressDest="main" fmt-merge-msg \ --into-name=main <.git/FETCH_HEAD >full.5 && head -n1 full.5 >actual && - grep -e "Merge branch .side." actual && - ! grep -e " into main$" actual && - ! grep -e " into HEAD$" actual + test_grep -e "Merge branch .side." actual && + test_grep ! -e " into main$" actual && + test_grep ! -e " into HEAD$" actual ' test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 1d98091..6d27b42 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh
@@ -8,9 +8,9 @@ . ./test-lib.sh test_expect_success "for-each-ref does not crash with -h" ' - test_expect_code 129 git for-each-ref -h >usage && + git for-each-ref -h >usage && test_grep "[Uu]sage: git for-each-ref " usage && - test_expect_code 129 nongit git for-each-ref -h >usage && + nongit git for-each-ref -h >usage && test_grep "[Uu]sage: git for-each-ref " usage '
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh index ff00b74..fb1595a 100755 --- a/t/t6402-merge-rename.sh +++ b/t/t6402-merge-rename.sh
@@ -332,7 +332,7 @@ git checkout -q dir-in-way^0 && test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors && - ! grep "error: refusing to lose untracked file at" errors && + test_grep ! "error: refusing to lose untracked file at" errors && test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output && test_grep "Auto-merging dir" output && test_grep "moving it to dir~renamed-file-has-no-conflicts instead" output && @@ -883,7 +883,7 @@ test_expect_success 'no spurious "refusing to lose untracked" message' ' git checkout main^0 && test_must_fail git merge rename^0 2>errors.txt && - ! grep "refusing to lose untracked file" errors.txt + test_grep ! "refusing to lose untracked file" errors.txt ' test_expect_success 'do not follow renames for empty files' '
diff --git a/t/t6403-merge-file.sh b/t/t6403-merge-file.sh index 801284c..dcc5c2a 100755 --- a/t/t6403-merge-file.sh +++ b/t/t6403-merge-file.sh
@@ -356,7 +356,7 @@ test_expect_success 'binary files cannot be merged' ' test_must_fail git merge-file -p \ orig.txt "$TEST_DIRECTORY"/test-binary-1.png new1.txt 2> merge.err && - grep "Cannot merge binary files" merge.err + test_grep "Cannot merge binary files" merge.err ' test_expect_success 'binary files cannot be merged with --object-id' ' @@ -364,7 +364,7 @@ git add orig.txt new1.txt test-binary-1.png && test_must_fail git merge-file --object-id \ :orig.txt :test-binary-1.png :new1.txt 2> merge.err && - grep "Cannot merge binary files" merge.err + test_grep "Cannot merge binary files" merge.err ' test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' ' @@ -539,7 +539,7 @@ test_expect_success '--object-id fails without repository' ' empty="$(test_oid empty_blob)" && nongit test_must_fail git merge-file --object-id $empty $empty $empty 2>err && - grep "not a git repository" err + test_grep "not a git repository" err ' test_expect_success 'run in a linked worktree with --object-id' '
diff --git a/t/t6404-recursive-merge.sh b/t/t6404-recursive-merge.sh index 346f360..ce420fe 100755 --- a/t/t6404-recursive-merge.sh +++ b/t/t6404-recursive-merge.sh
@@ -109,7 +109,7 @@ git add binary-file && git commit -m binary2 && test_must_fail git merge F >merge_output && - grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge_output + test_grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge_output ' test_expect_success 'mark rename/delete as unmerged' '
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh index 8f6fbef..7f49c59 100755 --- a/t/t6406-merge-attr.sh +++ b/t/t6406-merge-attr.sh
@@ -97,11 +97,11 @@ test_expect_success 'check merge result in working tree' ' git cat-file -p HEAD:binary >binary-orig && - grep "<<<<<<<" text && + test_grep "<<<<<<<" text && cmp binary-orig binary && - ! grep "<<<<<<<" union && - grep Main union && - grep Side union + test_grep ! "<<<<<<<" union && + test_grep Main union && + test_grep Side union ' @@ -112,9 +112,9 @@ s/ .*$// p }" >actual text && - grep ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" actual && - grep "================================" actual && - grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual + test_grep ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" actual && + test_grep "================================" actual && + test_grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual ' test_expect_success 'invalid conflict-marker-size 3a' ' @@ -193,7 +193,7 @@ >./please-abort && echo "* merge=custom" >.gitattributes && test_expect_code 2 git merge main 2>err && - grep "^error: failed to execute internal merge" err && + test_grep "^error: failed to execute internal merge" err && git ls-files -u >output && git diff --name-only HEAD >>output && test_must_be_empty output @@ -260,7 +260,7 @@ git commit -am two && test_must_fail git merge bin-main >output && - grep -i "warning.*cannot merge.*HEAD vs. bin-main" output + test_grep -i "warning.*cannot merge.*HEAD vs. bin-main" output ' test_expect_success !WINDOWS 'custom merge driver that is killed with a signal on recursive merge' ' @@ -297,7 +297,7 @@ >./please-abort && echo "* merge=custom" >.gitattributes && test_expect_code 2 git merge recursive-a 2>err && - grep "error: failed to execute internal merge" err && + test_grep "error: failed to execute internal merge" err && git ls-files -u >output && git diff --name-only HEAD >>output && test_must_be_empty output
diff --git a/t/t6417-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh index 62d1406..9486251 100755 --- a/t/t6417-merge-ours-theirs.sh +++ b/t/t6417-merge-ours-theirs.sh
@@ -26,31 +26,31 @@ test_expect_success 'plain recursive - should conflict' ' git reset --hard main && test_must_fail git merge -s recursive side && - grep nine file && - grep nueve file && - ! grep 9 file && - grep one file && - ! grep 1 file + test_grep nine file && + test_grep nueve file && + test_grep ! 9 file && + test_grep one file && + test_grep ! 1 file ' test_expect_success 'recursive favouring theirs' ' git reset --hard main && git merge -s recursive -Xtheirs side && - ! grep nine file && - grep nueve file && - ! grep 9 file && - grep one file && - ! grep 1 file + test_grep ! nine file && + test_grep nueve file && + test_grep ! 9 file && + test_grep one file && + test_grep ! 1 file ' test_expect_success 'recursive favouring ours' ' git reset --hard main && git merge -s recursive -X ours side && - grep nine file && - ! grep nueve file && - ! grep 9 file && - grep one file && - ! grep 1 file + test_grep nine file && + test_grep ! nueve file && + test_grep ! 9 file && + test_grep one file && + test_grep ! 1 file ' test_expect_success 'binary file with -Xours/-Xtheirs' '
diff --git a/t/t6418-merge-text-auto.sh b/t/t6418-merge-text-auto.sh index 41288a6..c86a1be 100755 --- a/t/t6418-merge-text-auto.sh +++ b/t/t6418-merge-text-auto.sh
@@ -226,7 +226,7 @@ git checkout rename^0 && test_must_fail git -c merge.renormalize=true merge nuke >out && - grep "rename/delete" out + test_grep "rename/delete" out ) '
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh index e18d5a2..82b76da 100755 --- a/t/t6422-merge-rename-corner-cases.sh +++ b/t/t6422-merge-rename-corner-cases.sh
@@ -916,8 +916,8 @@ # bar should have two-way merged contents of the different # versions of bar; check that content from both sides is # present. - grep original bar && - grep different bar + test_grep original bar && + test_grep different bar ) ' @@ -991,8 +991,8 @@ # baz should have two-way merged contents of the original # contents of foo and bar; check that content from both sides # is present. - grep foo baz && - grep bar baz + test_grep foo baz && + test_grep bar baz ) ' @@ -1525,4 +1525,58 @@ ) ' +# Testcase: submodule/directory conflict with duplicate tree entries +# One side has a path as a gitlink (submodule). The other side replaces +# the gitlink with a directory. A third-party tool creates a tree on the +# submodule side that has *both* a gitlink and a tree entry for the same +# path (adding a file inside the submodule path ignoring that there's a +# gitlink there). collect_merge_info_callback() should detect the +# duplicate and abort rather than silently corrupting its bookkeeping. + +test_expect_success 'duplicate tree entries trigger an error' ' + test_when_finished "rm -rf duplicate-entry" && + git init duplicate-entry && + ( + cd duplicate-entry && + + # Base commit: "docs" is a gitlink (submodule) + empty_tree=$(git mktree </dev/null) && + fake_commit=$(git commit-tree $empty_tree </dev/null) && + git update-index --add --cacheinfo 160000,$fake_commit,docs && + echo base >file.txt && + git add file.txt && + git commit -m base && + + # side1: remove the gitlink, replace with a directory + git checkout -b side1 && + git rm --cached docs && + mkdir -p docs && + echo hello >docs/requirements.txt && + git add docs/requirements.txt && + git commit -m "side1: submodule to directory" && + + # side2: keep the gitlink but craft a tree that also + # contains a tree entry for "docs" (simulating a tool + # that adds files inside a submodule path without + # removing the gitlink first). + git checkout main && + git checkout -b side2 && + blob_oid=$(echo world | git hash-object -w --stdin) && + docs_tree=$(printf "100644 blob %s\trequirements.txt\n" \ + "$blob_oid" | git mktree) && + cur_tree=$(git rev-parse HEAD^{tree}) && + git cat-file -p $cur_tree >tree-listing && + printf "040000 tree %s\tdocs\n" "$docs_tree" >>tree-listing && + new_tree=$(git mktree <tree-listing) && + side2_commit=$(git commit-tree $new_tree -p HEAD \ + -m "side2: add file alongside submodule") && + git update-ref refs/heads/side2 $side2_commit && + + # Merging must detect the duplicate and abort + git checkout side1 && + test_must_fail git merge side2 2>err && + test_grep "duplicate entries" err + ) +' + test_done
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh index 53535a8..24d4c54 100755 --- a/t/t6423-merge-rename-directories.sh +++ b/t/t6423-merge-rename-directories.sh
@@ -1113,7 +1113,7 @@ test_cmp expect actual && test_path_is_missing x/d && test_path_is_file y/d && - grep -q "<<<<" y/d # conflict markers should be present + test_grep -q "<<<<" y/d # conflict markers should be present ) ' @@ -2841,10 +2841,10 @@ test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line && - grep -q dir1/yo error_line && - grep -q dir2/yo error_line && - grep -q dir3/yo error_line && - grep -q dirN/yo error_line && + test_grep -q dir1/yo error_line && + test_grep -q dir2/yo error_line && + test_grep -q dir3/yo error_line && + test_grep -q dirN/yo error_line && git ls-files -s >out && test_line_count = 16 out && @@ -3578,7 +3578,7 @@ test_path_is_missing .git/MERGE_HEAD && test_grep "error: Your local changes to the following files would be overwritten by merge" err && - grep -q stuff z/c && + test_grep -q stuff z/c && test_seq 1 10 >expected && echo stuff >>expected && test_cmp expected z/c @@ -3636,7 +3636,7 @@ test_path_is_missing .git/MERGE_HEAD && test_grep "error: Your local changes to the following files would be overwritten by merge" err && - grep -q stuff y/c && + test_grep -q stuff y/c && test_seq 1 10 >expected && echo stuff >>expected && test_cmp expected y/c && @@ -3705,7 +3705,7 @@ test_path_is_missing .git/MERGE_HEAD && test_grep "error: Your local changes to the following files would be overwritten by merge" err && - grep -q stuff z/c && + test_grep -q stuff z/c && test_seq 1 10 >expected && echo stuff >>expected && test_cmp expected z/c @@ -4779,7 +4779,7 @@ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && - grep "CONFLICT (implicit dir rename).*source/bar in the way" out && + test_grep "CONFLICT (implicit dir rename).*source/bar in the way" out && test_path_is_missing source/bar && test_path_is_file source/subdir/bar && test_path_is_file source/baz && @@ -4920,7 +4920,7 @@ # error about there being a file in the way. test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && - grep "CONFLICT (implicit dir rename).*bar in the way" out && + test_grep "CONFLICT (implicit dir rename).*bar in the way" out && test_path_is_missing bar && test_path_is_file subdir/bar && @@ -4999,7 +4999,7 @@ # error about there being a file in the way. test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && - grep "CONFLICT (implicit dir rename).*dirA/bar in the way" out && + test_grep "CONFLICT (implicit dir rename).*dirA/bar in the way" out && test_path_is_missing dirA/bar && test_path_is_file dirB/bar && @@ -5402,7 +5402,7 @@ test_stdout_line_count = 1 git ls-files -s B/stuff && test_stdout_line_count = 1 git ls-files -s D/file2 && - grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && + test_grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && test_stdout_line_count = 1 git ls-files -s C/file1 ) ' @@ -5489,7 +5489,7 @@ test_stdout_line_count = 1 git ls-files -s B/stuff && test_stdout_line_count = 1 git ls-files -s D/file2 && - grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && + test_grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && test_stdout_line_count = 1 git ls-files -s C/file1 ) ' @@ -5570,8 +5570,8 @@ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && - grep "CONFLICT (rename/delete).*A/file1.*D/file2" out && - grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && + test_grep "CONFLICT (rename/delete).*A/file1.*D/file2" out && + test_grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && test_stdout_line_count = 6 git ls-files -s && test_stdout_line_count = 1 git ls-files -s A/other && @@ -5586,7 +5586,7 @@ # the dir rename of C/file1 -> A/file1 would mean modifying # the code so that renames do not adjust both their source # and target paths in all cases. - ! grep "CONFLICT (file location)" out && + test_grep ! "CONFLICT (file location)" out && test_stdout_line_count = 1 git ls-files -s C/file1 ) ' @@ -5652,8 +5652,8 @@ test_grep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out && git ls-files >paths && - ! grep z/ paths && - grep "y/[de]" paths && + test_grep ! z/ paths && + test_grep "y/[de]" paths && test_path_is_missing z/d && test_path_is_file y/d && @@ -5676,8 +5676,8 @@ test_grep Path.updated:.*z/d.added.in.B^0.*y/d out && git ls-files >paths && - ! grep z/ paths && - grep "y/[de]" paths && + test_grep ! z/ paths && + test_grep "y/[de]" paths && test_path_is_missing z/d && test_path_is_file y/d && @@ -5742,8 +5742,8 @@ test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out && git ls-files >paths && - ! grep z/ paths && - grep "y/d" paths && + test_grep ! z/ paths && + test_grep "y/d" paths && test_path_is_missing z/d && test_path_is_file y/d @@ -5764,8 +5764,8 @@ test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out && git ls-files >paths && - ! grep z/ paths && - grep "y/d" paths && + test_grep ! z/ paths && + test_grep "y/d" paths && test_path_is_missing z/d && test_path_is_file y/d @@ -5827,8 +5827,8 @@ test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out && git ls-files >paths && - ! grep z/ paths && - grep "y/d" paths && + test_grep ! z/ paths && + test_grep "y/d" paths && test_path_is_missing z/d && test_path_is_file y/d @@ -5848,8 +5848,8 @@ test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out && git ls-files >paths && - ! grep z/ paths && - grep "y/d" paths && + test_grep ! z/ paths && + test_grep "y/d" paths && test_path_is_missing z/d && test_path_is_file y/d @@ -5917,9 +5917,9 @@ test_grep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out && git ls-files >paths && - ! grep b/ paths && - ! grep c/ paths && - grep "d/y" paths && + test_grep ! b/ paths && + test_grep ! c/ paths && + test_grep "d/y" paths && test_path_is_missing b/y && test_path_is_missing c/y && @@ -5941,9 +5941,9 @@ test_grep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out && git ls-files >paths && - ! grep b/ paths && - ! grep c/ paths && - grep "d/y" paths && + test_grep ! b/ paths && + test_grep ! c/ paths && + test_grep "d/y" paths && test_path_is_missing b/y && test_path_is_missing c/y && @@ -6058,8 +6058,8 @@ test_must_be_empty err && git ls-files >paths && - ! grep a/x paths && - grep b/x paths + test_grep ! a/x paths && + test_grep b/x paths ) '
diff --git a/t/t6424-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh index ddc7524..12e58bc 100755 --- a/t/t6424-merge-unrelated-index-changes.sh +++ b/t/t6424-merge-unrelated-index-changes.sh
@@ -298,9 +298,9 @@ test_must_fail git merge -s ort -s octopus C^0 >output 2>&1 && - grep "Trying merge strategy ort..." output && - grep "Trying merge strategy octopus..." output && - grep "No merge strategy handled the merge." output && + test_grep "Trying merge strategy ort..." output && + test_grep "Trying merge strategy octopus..." output && + test_grep "No merge strategy handled the merge." output && # Changes to "a" should remain staged git rev-parse :a >actual &&
diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh index 57569c4..818550b 100755 --- a/t/t6427-diff3-conflict-markers.sh +++ b/t/t6427-diff3-conflict-markers.sh
@@ -39,7 +39,7 @@ test_must_fail git -c merge.conflictstyle=diff3 merge --allow-unrelated-histories -s recursive R^0 && - grep "|||||| empty tree" content + test_grep "|||||| empty tree" content ) ' @@ -97,7 +97,7 @@ test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R^0 && - grep "|||||| $MAIN:content" renamed + test_grep "|||||| $MAIN:content" renamed ) ' @@ -185,7 +185,7 @@ test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R^0 && - grep "|||||| merged common ancestors:content" renamed + test_grep "|||||| merged common ancestors:content" renamed ) ' @@ -198,7 +198,7 @@ git checkout -b side HEAD^ && test_commit side file && test_must_fail git -c merge.conflictstyle=diff3 rebase --merge main && - grep "||||||| parent of" file + test_grep "||||||| parent of" file ) ' @@ -207,7 +207,7 @@ cd rebase && git rebase --abort && test_must_fail git -c merge.conflictstyle=diff3 rebase --apply main && - grep "||||||| constructed fake ancestor" file + test_grep "||||||| constructed fake ancestor" file ) '
diff --git a/t/t6432-merge-recursive-space-options.sh b/t/t6432-merge-recursive-space-options.sh index db4b77e..110b48c 100755 --- a/t/t6432-merge-recursive-space-options.sh +++ b/t/t6432-merge-recursive-space-options.sh
@@ -109,7 +109,7 @@ git read-tree --reset -u HEAD && test_must_fail git merge-recursive HEAD^ -- HEAD remote && test_must_fail git update-index --refresh && - grep "<<<<<<" text.txt + test_grep "<<<<<<" text.txt ' test_expect_success '--ignore-space-change makes merge succeed' ' @@ -123,7 +123,7 @@ git read-tree --reset -u HEAD && test_must_fail git cherry-pick remote && test_must_fail git update-index --refresh && - grep "<<<<<<" text.txt + test_grep "<<<<<<" text.txt ' test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh index 70b5d2d..5e39a15 100755 --- a/t/t6436-merge-overwrite.sh +++ b/t/t6436-merge-overwrite.sh
@@ -189,10 +189,10 @@ test_expect_success 'will not clobber WT/index when merging into unborn' ' git merge main && - grep foo tracked-file && + test_grep foo tracked-file && git show :tracked-file >expect && - grep foo expect && - grep bar untracked-file + test_grep foo expect && + test_grep bar untracked-file ' test_done
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh index a564758..107e13a 100755 --- a/t/t6437-submodule-merge.sh +++ b/t/t6437-submodule-merge.sh
@@ -111,7 +111,7 @@ git checkout -b test-nonforward-a b && 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 + test_grep "$sub_expect" actual ) ' @@ -148,7 +148,7 @@ git rev-parse --short sub-d > ../expect) && 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" sub-actual && + test_grep "$sub_expect" sub-actual && grep $(cat expect) actual > /dev/null && git reset --hard) ' @@ -164,7 +164,7 @@ ) && 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" sub-actual && + test_grep "$sub_expect" sub-actual && grep $(cat expect1) actual > /dev/null && grep $(cat expect2) actual > /dev/null && git reset --hard) @@ -207,7 +207,7 @@ git checkout -b test-backward e && test_must_fail git merge f 2>actual && sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" && - grep "$sub_expect" actual + test_grep "$sub_expect" actual ) ' @@ -513,7 +513,7 @@ git commit -m "b" && test_must_fail git merge a 2>actual && sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" && - grep "$sub_expect" actual + test_grep "$sub_expect" actual ) '
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index ea9aaad..a7142ab 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh
@@ -35,7 +35,7 @@ cd broken && git init && echo "[gc] pruneexpire = CORRUPT" >>.git/config && - test_expect_code 129 git gc -h >usage 2>&1 + git gc -h >usage 2>&1 ) && test_grep "[Uu]sage" broken/usage ' @@ -176,15 +176,15 @@ # Check that git-pack-refs is run as a sanity check (done via # gc_before_repack()) but that git-expire is not. - grep -E "^trace: (built-in|exec|run_command): git pack-refs --" trace.out && - ! grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out + test_grep -E "^trace: (built-in|exec|run_command): git pack-refs --" trace.out && + test_grep ! -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out ' test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "expire" via "gc"' ' >trace.out && test_config gc.reflogExpire never && GIT_TRACE=$(pwd)/trace.out git gc && - grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out + test_grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out ' test_expect_success 'gc.repackFilter launches repack with a filter' ' @@ -196,7 +196,7 @@ GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \ -c repack.writeBitmaps=false -c gc.cruftPacks=false gc && test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack && - grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out + test_grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out ' test_expect_success 'gc.repackFilterTo store filtered out objects' '
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh index b5b314e..019fed9 100755 --- a/t/t6600-test-reach.sh +++ b/t/t6600-test-reach.sh
@@ -49,6 +49,42 @@ git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i || return 1 done done && + # Build a topology with clock skew to test the !FIND_ALL early + # exit in paint_down_to_common(). M2 is the correct merge base + # of P1 and P2, but its ancestor M1 has a higher committer date + # due to clock skew. With date-only ordering (v1 commit graph + # without corrected commit dates), M1 pops from the queue first, + # gets both paint sides, and the early exit fires before M2 is + # ever visited. + # + # P1 P2 @7000 + # | / \ + # A B D @6000 + # / \ | | + # | M2--+ | @2000 (correct merge base) + # \ | | + # M1--------+ @5000 (clock skew: date > M2) + # | + # root @1000 + # + git checkout --orphan skew-orphan && + skew_tree=$(git mktree </dev/null) && + skew_commit () { + GIT_COMMITTER_DATE="@$1 +0000" GIT_AUTHOR_DATE="@$1 +0000" \ + git commit-tree -m "$2" "$skew_tree" $3 $4 $5 $6 + } && + skew_root=$(skew_commit 1000 root) && + skew_M1=$(skew_commit 5000 M1 -p "$skew_root") && + skew_M2=$(skew_commit 2000 M2 -p "$skew_M1") && + skew_A=$(skew_commit 6000 A -p "$skew_M1" -p "$skew_M2") && + skew_B=$(skew_commit 6000 B -p "$skew_M2") && + skew_D=$(skew_commit 6000 D -p "$skew_M1") && + skew_P1=$(skew_commit 7000 P1 -p "$skew_A") && + skew_P2=$(skew_commit 7000 P2 -p "$skew_B" -p "$skew_D") && + git branch -f skew-P1 "$skew_P1" && + git branch -f skew-P2 "$skew_P2" && + git tag skew-M2 "$skew_M2" && + git commit-graph write --reachable && mv .git/objects/info/commit-graph commit-graph-full && chmod u+w commit-graph-full && @@ -958,13 +994,18 @@ git merge-base --all commit-5-7 commit-4-8 commit-6-6 commit-8-3 >all && git merge-base commit-5-7 commit-4-8 commit-6-6 commit-8-3 >single && test_line_count = 1 single && - grep -F -f single all && + test_grep -F -f single all && cp commit-graph-half .git/objects/info/commit-graph && git merge-base --all commit-5-7 commit-4-8 commit-6-6 commit-8-3 >all && git merge-base commit-5-7 commit-4-8 commit-6-6 commit-8-3 >single && test_line_count = 1 single && - grep -F -f single all + test_grep -F -f single all +' + +test_expect_success 'merge-base without --all, clock skew, v1 commit-graph' ' + git rev-parse skew-M2 >expect && + merge_base_all_modes skew-P1 skew-P2 ' test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 920479e..7cf4aa5 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh
@@ -46,7 +46,7 @@ test_expect_success 'checking the commit' ' git diff-tree -r -M --name-status HEAD^ HEAD >actual && - grep "^R100..*path0/COPYING..*path1/COPYING" actual + test_grep "^R100..*path0/COPYING..*path1/COPYING" actual ' test_expect_success 'moving the file back into subdirectory' ' @@ -60,7 +60,7 @@ test_expect_success 'checking the commit' ' git diff-tree -r -M --name-status HEAD^ HEAD >actual && - grep "^R100..*path1/COPYING..*path0/COPYING" actual + test_grep "^R100..*path1/COPYING..*path0/COPYING" actual ' test_expect_success 'mv --dry-run does not move file' ' @@ -147,8 +147,8 @@ test_expect_success 'checking the commit' ' git diff-tree -r -M --name-status HEAD^ HEAD >actual && - grep "^R100..*path0/COPYING..*path2/COPYING" actual && - grep "^R100..*path0/README..*path2/README" actual + test_grep "^R100..*path0/COPYING..*path2/COPYING" actual && + test_grep "^R100..*path0/README..*path2/README" actual ' test_expect_success 'succeed when source is a prefix of destination' ' @@ -165,8 +165,8 @@ test_expect_success 'checking the commit' ' git diff-tree -r -M --name-status HEAD^ HEAD >actual && - grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual && - grep "^R100..*path2/README..*path1/path2/README" actual + test_grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual && + test_grep "^R100..*path2/README..*path1/path2/README" actual ' test_expect_success 'do not move directory over existing directory' ' @@ -200,7 +200,7 @@ T=$(git write-tree) && git ls-tree -r $T >out && - grep partA/outline.txt out + test_grep partA/outline.txt out ' rm -fr papers partA path? @@ -504,7 +504,7 @@ mkdir dummy dest && git mv -k dummy sub dest && git status --porcelain >actual && - grep "^R sub -> dest/sub" actual && + test_grep "^R sub -> dest/sub" actual && git reset --hard && git checkout . '
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh index 4d3f221..9c0e82b 100755 --- a/t/t7002-mv-sparse-checkout.sh +++ b/t/t7002-mv-sparse-checkout.sh
@@ -319,8 +319,8 @@ test_path_is_missing sub/d && test_path_is_missing folder1/d && git ls-files -t >actual && - ! grep "^H sub/d\$" actual && - grep "S folder1/d" actual + test_grep ! "^H sub/d\$" actual && + test_grep "S folder1/d" actual ' test_expect_success 'move clean path from in-cone to out-of-cone overwrite' ' @@ -346,8 +346,8 @@ test_path_is_missing sub/file1 && test_path_is_missing folder1/file1 && git ls-files -t >actual && - ! grep "H sub/file1" actual && - grep "S folder1/file1" actual && + test_grep ! "H sub/file1" actual && + test_grep "S folder1/file1" actual && # compare file content before move and after move echo "sub/file1 overwrite" >expect && @@ -382,8 +382,8 @@ test_path_is_missing sub/file1 && test_path_is_missing folder1/file1 && git ls-files -t >actual && - ! grep "H sub/file1" actual && - grep "S folder1/file1" actual && + test_grep ! "H sub/file1" actual && + test_grep "S folder1/file1" actual && # compare file content before move and after move echo "sub/file1 overwrite" >expect && @@ -421,9 +421,9 @@ test_path_is_missing sub/dir/e && test_path_is_missing folder1/file1 && git ls-files -t >actual && - ! grep "H sub/dir/file1" actual && - ! grep "H sub/dir/e" actual && - grep "S folder1/dir/file1" actual && + test_grep ! "H sub/dir/file1" actual && + test_grep ! "H sub/dir/e" actual && + test_grep "S folder1/dir/file1" actual && # compare file content before move and after move echo test >expect && @@ -452,8 +452,8 @@ test_path_is_missing sub/d && test_path_is_file folder1/d && git ls-files -t >actual && - ! grep "^H sub/d\$" actual && - grep "H folder1/d" actual + test_grep ! "^H sub/d\$" actual && + test_grep "H folder1/d" actual ' test_expect_success 'move dir from in-cone to out-of-cone' ' @@ -473,8 +473,8 @@ test_path_is_missing sub/dir && test_path_is_missing folder1 && git ls-files -t >actual && - ! grep "H sub/dir/e" actual && - grep "S folder1/dir/e" actual + test_grep ! "H sub/dir/e" actual && + test_grep "S folder1/dir/e" actual ' test_expect_success 'move partially-dirty dir from in-cone to out-of-cone' ' @@ -506,12 +506,12 @@ test_path_is_file folder1/dir/e2 && test_path_is_file folder1/dir/e3 && git ls-files -t >actual && - ! grep "H sub/dir/e" actual && - ! grep "H sub/dir/e2" actual && - ! grep "H sub/dir/e3" actual && - grep "S folder1/dir/e" actual && - grep "H folder1/dir/e2" actual && - grep "H folder1/dir/e3" actual + test_grep ! "H sub/dir/e" actual && + test_grep ! "H sub/dir/e2" actual && + test_grep ! "H sub/dir/e3" actual && + test_grep "S folder1/dir/e" actual && + test_grep "H folder1/dir/e2" actual && + test_grep "H folder1/dir/e3" actual ' test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index c475769..86011e7 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh
@@ -49,7 +49,7 @@ test_expect_success 'rewrite bare repository identically' ' (git config core.bare true && cd .git && git filter-branch branch > filter-output 2>&1 && - ! grep fatal filter-output) + test_grep ! fatal filter-output) ' git config core.bare false test_expect_success 'result is really identical' ' @@ -65,7 +65,7 @@ git filter-branch -d "$TRASHDIR/dfoo" \ --index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \ ) && - grep drepo "$TRASHDIR/backup-refs" + test_grep drepo "$TRASHDIR/backup-refs" ' test_expect_success 'tree-filter works with -d' ' @@ -503,7 +503,7 @@ git tag -a -m "tag to a tree" treetag $new_tree && git reset --hard HEAD && git filter-branch -f -- --all >filter-output 2>&1 && - ! grep fatal filter-output + test_grep ! fatal filter-output ' test_expect_success 'filter-branch handles ref deletion' ' @@ -512,7 +512,7 @@ git tag empty && git branch to-delete && git filter-branch -f --prune-empty to-delete >out 2>&1 && - grep "to-delete.*was deleted" out && + test_grep "to-delete.*was deleted" out && test_must_fail git rev-parse --verify to-delete ' @@ -523,8 +523,8 @@ git filter-branch -f \ --index-filter "git rm --ignore-unmatch --cached to-drop.t" \ rewrite >out 2>&1 && - grep "rewrite.*was rewritten" out && - ! grep -i warning out && + test_grep "rewrite.*was rewritten" out && + test_grep ! -i warning out && git diff-tree empty rewrite ' @@ -532,8 +532,8 @@ test_commit to-exclude && git branch ancestor && git filter-branch -f ancestor -- :^to-exclude.t >out 2>&1 && - grep "ancestor.*was rewritten" out && - ! grep -i warning out && + test_grep "ancestor.*was rewritten" out && + test_grep ! -i warning out && git diff-tree HEAD^ ancestor '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index d918005..2ad3004 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh
@@ -937,7 +937,7 @@ test_cmp expect actual.focus && # Error reported for tags that point to non-commits. - grep "error: object [0-9a-f]* is a blob, not a commit" err + test_grep "error: object [0-9a-f]* is a blob, not a commit" err ' # trying to verify annotated non-signed tags:
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh index 9717e82..75ca7a2 100755 --- a/t/t7006-pager.sh +++ b/t/t7006-pager.sh
@@ -45,8 +45,8 @@ test_terminal git log ) && - grep ^LESS= pager-env.out && - grep ^LV= pager-env.out + test_grep ^LESS= pager-env.out && + test_grep ^LV= pager-env.out ' test_expect_success !MINGW,TTY 'LESS and LV envvars set by git-sh-setup' ' @@ -58,8 +58,8 @@ export PATH && test_terminal sh -c ". git-sh-setup && git_pager" ) && - grep ^LESS= pager-env.out && - grep ^LV= pager-env.out + test_grep ^LESS= pager-env.out && + test_grep ^LV= pager-env.out ' test_expect_success TTY 'some commands do not use a pager' ' @@ -685,7 +685,7 @@ grep child_exit trace.normal >child-exits && test_line_count = 1 child-exits && - grep " code:0 " child-exits && + test_grep " code:0 " child-exits && test_path_is_file pager-used ' @@ -706,7 +706,7 @@ grep child_exit trace.normal >child-exits && test_line_count = 1 child-exits && - grep " code:1 " child-exits && + test_grep " code:1 " child-exits && test_path_is_file pager-used ' @@ -721,7 +721,7 @@ grep child_exit trace.normal >child-exits && test_line_count = 1 child-exits && - grep " code:1 " child-exits && + test_grep " code:1 " child-exits && test_path_is_file pager-used ' @@ -749,7 +749,7 @@ grep child_exit trace.normal >child-exits && test_line_count = 1 child-exits && - grep " code:143 " child-exits && + test_grep " code:143 " child-exits && test_path_is_file pager-used '
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh index cd5c20f..526cc4e 100755 --- a/t/t7012-skip-worktree-writing.sh +++ b/t/t7012-skip-worktree-writing.sh
@@ -30,7 +30,7 @@ git update-index --skip-worktree init.t && echo dirty >> init.t && test_must_fail git read-tree -m -u HEAD^ && - grep -q dirty init.t && + test_grep -q dirty init.t && test "$(git ls-files -t init.t)" = "S init.t" && git update-index --no-skip-worktree init.t ' @@ -48,7 +48,7 @@ git update-index --skip-worktree added && echo dirty >> added && test_must_fail git read-tree -m -u HEAD^ && - grep -q dirty added && + test_grep -q dirty added && test "$(git ls-files -t added)" = "S added" && git update-index --no-skip-worktree added ' @@ -173,7 +173,7 @@ echo in the way >modified && test_must_fail git stash apply 2>error&& - grep "changes.*would be overwritten by merge" error && + test_grep "changes.*would be overwritten by merge" error && echo in the way >expect && test_cmp expect modified &&
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh index 2c14707..909878d 100755 --- a/t/t7030-verify-tag.sh +++ b/t/t7030-verify-tag.sh
@@ -8,9 +8,9 @@ . "$TEST_DIRECTORY/lib-gpg.sh" test_expect_success GPG 'verify-tag does not crash with -h' ' - test_expect_code 129 git verify-tag -h >usage && + git verify-tag -h >usage && test_grep "[Uu]sage: git verify-tag " usage && - test_expect_code 129 nongit git verify-tag -h >usage && + nongit git verify-tag -h >usage && test_grep "[Uu]sage: git verify-tag " usage ' @@ -63,8 +63,8 @@ for tag in initial second merge fourth-signed sixth-signed seventh-signed do git verify-tag $tag 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo $tag OK || exit 1 done ) && @@ -72,8 +72,8 @@ for tag in fourth-unsigned fifth-unsigned sixth-unsigned do test_must_fail git verify-tag $tag 2>actual && - ! grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep ! "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo $tag OK || exit 1 done ) && @@ -81,9 +81,9 @@ for tag in eighth-signed-alt do git verify-tag $tag 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && - grep "not certified" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && + test_grep "not certified" actual && echo $tag OK || exit 1 done ) @@ -91,32 +91,32 @@ test_expect_success GPGSM 'verify and show signatures x509' ' git verify-tag ninth-signed-x509 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo ninth-signed-x509 OK ' test_expect_success GPGSM 'verify and show signatures x509 with low minTrustLevel' ' test_config gpg.minTrustLevel undefined && git verify-tag ninth-signed-x509 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo ninth-signed-x509 OK ' test_expect_success GPGSM 'verify and show signatures x509 with matching minTrustLevel' ' test_config gpg.minTrustLevel fully && git verify-tag ninth-signed-x509 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo ninth-signed-x509 OK ' test_expect_success GPGSM 'verify and show signatures x509 with high minTrustLevel' ' test_config gpg.minTrustLevel ultimate && test_must_fail git verify-tag ninth-signed-x509 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo ninth-signed-x509 OK ' @@ -125,8 +125,8 @@ sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 && git hash-object -w -t tag forged1 >forged1.tag && test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 && - grep "BAD signature from" actual1 && - ! grep "Good signature from" actual1 + test_grep "BAD signature from" actual1 && + test_grep ! "Good signature from" actual1 ' test_expect_success GPG 'verify signatures with --raw' ' @@ -134,8 +134,8 @@ for tag in initial second merge fourth-signed sixth-signed seventh-signed do git verify-tag --raw $tag 2>actual && - grep "GOODSIG" actual && - ! grep "BADSIG" actual && + test_grep "GOODSIG" actual && + test_grep ! "BADSIG" actual && echo $tag OK || exit 1 done ) && @@ -143,8 +143,8 @@ for tag in fourth-unsigned fifth-unsigned sixth-unsigned do test_must_fail git verify-tag --raw $tag 2>actual && - ! grep "GOODSIG" actual && - ! grep "BADSIG" actual && + test_grep ! "GOODSIG" actual && + test_grep ! "BADSIG" actual && echo $tag OK || exit 1 done ) && @@ -152,9 +152,9 @@ for tag in eighth-signed-alt do git verify-tag --raw $tag 2>actual && - grep "GOODSIG" actual && - ! grep "BADSIG" actual && - grep "TRUST_UNDEFINED" actual && + test_grep "GOODSIG" actual && + test_grep ! "BADSIG" actual && + test_grep "TRUST_UNDEFINED" actual && echo $tag OK || exit 1 done ) @@ -162,8 +162,8 @@ test_expect_success GPGSM 'verify signatures with --raw x509' ' git verify-tag --raw ninth-signed-x509 2>actual && - grep "GOODSIG" actual && - ! grep "BADSIG" actual && + test_grep "GOODSIG" actual && + test_grep ! "BADSIG" actual && echo ninth-signed-x509 OK '
diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh index 80359d4..3000beb 100755 --- a/t/t7031-verify-tag-signed-ssh.sh +++ b/t/t7031-verify-tag-signed-ssh.sh
@@ -71,8 +71,8 @@ for tag in initial second merge fourth-signed sixth-signed seventh-signed do git verify-tag $tag 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $tag OK || exit 1 done ) && @@ -80,8 +80,8 @@ for tag in fourth-unsigned fifth-unsigned sixth-unsigned do test_must_fail git verify-tag $tag 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $tag OK || exit 1 done ) && @@ -89,9 +89,9 @@ for tag in eighth-signed-alt do test_must_fail git verify-tag $tag 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && - grep "${GPGSSH_KEY_NOT_TRUSTED}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_KEY_NOT_TRUSTED}" actual && echo $tag OK || exit 1 done ) @@ -100,26 +100,26 @@ test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag exits failure on expired signature key' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-tag expired-signed 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag exits failure on not yet valid signature key' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-tag notyetvalid-signed 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag succeeds with tag date and key validity matching' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git verify-tag timeboxedvalid-signed 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag fails with tag date outside of key validity' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-tag timeboxedinvalid-signed 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH 'detect fudged ssh signature' ' @@ -128,9 +128,9 @@ sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 && git hash-object -w -t tag forged1 >forged1.tag && test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 && - grep "${GPGSSH_BAD_SIGNATURE}" actual1 && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 && - ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1 + test_grep "${GPGSSH_BAD_SIGNATURE}" actual1 && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1 ' test_expect_success GPGSSH 'verify ssh signatures with --raw' ' @@ -139,8 +139,8 @@ for tag in initial second merge fourth-signed sixth-signed seventh-signed do git verify-tag --raw $tag 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $tag OK || exit 1 done ) && @@ -148,8 +148,8 @@ for tag in fourth-unsigned fifth-unsigned sixth-unsigned do test_must_fail git verify-tag --raw $tag 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $tag OK || exit 1 done ) && @@ -157,8 +157,8 @@ for tag in eighth-signed-alt do test_must_fail git verify-tag --raw $tag 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $tag OK || exit 1 done ) @@ -167,8 +167,8 @@ test_expect_success GPGSSH 'verify signatures with --raw ssh' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git verify-tag --raw sixth-signed 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo sixth-signed OK '
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 0503a64..298272c 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh
@@ -87,7 +87,7 @@ test_expect_success "no 'git reset --no-$opt'" ' test_when_finished "rm -f err" && test_must_fail git reset --no-$opt 2>err && - grep "error: unknown option .no-$opt." err + test_grep "error: unknown option .no-$opt." err ' done
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh index 9a33507..a241ecf 100755 --- a/t/t7110-reset-merge.sh +++ b/t/t7110-reset-merge.sh
@@ -29,16 +29,16 @@ # file2: C D D D --merge C D D test_expect_success 'reset --merge is ok with changes in file it does not touch' ' git reset --merge HEAD^ && - ! grep 4 file1 && - grep 4 file2 && + test_grep ! 4 file1 && + test_grep 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && test -z "$(git diff --cached)" ' test_expect_success 'reset --merge is ok when switching back' ' git reset --merge second && - grep 4 file1 && - grep 4 file2 && + test_grep 4 file1 && + test_grep 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && test -z "$(git diff --cached)" ' @@ -53,16 +53,16 @@ git reset --hard second && cat file1 >file2 && git reset --keep HEAD^ && - ! grep 4 file1 && - grep 4 file2 && + test_grep ! 4 file1 && + test_grep 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && test -z "$(git diff --cached)" ' test_expect_success 'reset --keep is ok when switching back' ' git reset --keep second && - grep 4 file1 && - grep 4 file2 && + test_grep 4 file1 && + test_grep 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && test -z "$(git diff --cached)" ' @@ -79,9 +79,9 @@ echo "line 5" >> file1 && git add file1 && git reset --merge HEAD^ && - ! grep 4 file1 && - ! grep 5 file1 && - grep 4 file2 && + test_grep ! 4 file1 && + test_grep ! 5 file1 && + test_grep 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && test -z "$(git diff --cached)" ' @@ -91,9 +91,9 @@ echo "line 5" >> file2 && git add file2 && git reset --merge second && - ! grep 4 file2 && - ! grep 5 file1 && - grep 4 file1 && + test_grep ! 4 file2 && + test_grep ! 5 file1 && + test_grep 4 file1 && test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && test -z "$(git diff --cached)" ' @@ -121,7 +121,7 @@ echo "line 4" >> file2 && git add file2 && git reset --merge HEAD^ && - ! grep 4 file2 && + test_grep ! 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && test -z "$(git diff)" && test -z "$(git diff --cached)" @@ -130,8 +130,8 @@ test_expect_success 'reset --merge is ok again when switching back (2)' ' git reset --hard initial && git reset --merge second && - ! grep 4 file2 && - grep 4 file1 && + test_grep ! 4 file2 && + test_grep 4 file1 && test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && test -z "$(git diff --cached)" ' @@ -147,15 +147,15 @@ echo "line 4" >> file2 && git add file2 && git reset --keep HEAD^ && - grep 4 file2 && + test_grep 4 file2 && test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && test -z "$(git diff --cached)" ' test_expect_success 'reset --keep keeps changes when switching back' ' git reset --keep second && - grep 4 file2 && - grep 4 file1 && + test_grep 4 file2 && + test_grep 4 file1 && test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && test -z "$(git diff --cached)" '
diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 7613b1d..0ddd1ad 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh
@@ -318,7 +318,7 @@ git checkout -f renamer && git clean -f && git checkout renamer^ 2>messages && - grep "HEAD is now at $rev" messages && + test_grep "HEAD is now at $rev" messages && test_line_count -gt 1 messages && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/main) && @@ -794,7 +794,7 @@ git checkout main^0 && echo modified >one && test_must_fail git checkout renamer 2>error.log && - ! grep "^Previous HEAD" error.log + test_grep ! "^Previous HEAD" error.log ' ( @@ -844,7 +844,7 @@ ( for t in filfre-common left right do - grep $t arm || exit 1 + test_grep $t arm || exit 1 done ) &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 00d4070..13ad3ee 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh
@@ -793,7 +793,7 @@ git clean -fX modules/foobar >../output && - grep Removing ../output && + test_grep Removing ../output && test_path_is_missing modules/foobar/src/generated/code.c && test_path_is_file modules/foobar/Makefile
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh index f743e5b..de59ba3 100755 --- a/t/t7301-clean-interactive.sh +++ b/t/t7301-clean-interactive.sh
@@ -479,7 +479,7 @@ test_decode_color | head -n 1 >header && # not i18ngrep - grep "^<BOLD>" header + test_grep "^<BOLD>" header ' test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 65fcfae..eefdecb 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh
@@ -20,13 +20,13 @@ test_expect_success 'submodule usage: -h' ' git submodule -h >out 2>err && - grep "^usage: git submodule" out && + test_grep "^usage: git submodule" out && test_must_be_empty err ' test_expect_success 'submodule usage: --recursive' ' test_expect_code 1 git submodule --recursive >out 2>err && - grep "^usage: git submodule" err && + test_grep "^usage: git submodule" err && test_must_be_empty out ' @@ -214,14 +214,14 @@ test_expect_success 'redirected submodule add does not show progress' ' git -C addtest submodule add "file://$submodurl/parent" submod-redirected \ 2>err && - ! grep % err && + test_grep ! % err && test_grep ! "Checking connectivity" err ' test_expect_success 'redirected submodule add --progress does show progress' ' git -C addtest submodule add --progress "file://$submodurl/parent" \ submod-redirected-progress 2>err && \ - grep % err + test_grep % err ' test_expect_success 'submodule add to .gitignored path fails' ' @@ -506,7 +506,7 @@ test_expect_success 'status should initially be "missing"' ' git submodule status >lines && - grep "^-$rev1" lines + test_grep "^-$rev1" lines ' test_expect_success 'init should register submodule url in .git/config' ' @@ -524,7 +524,7 @@ mkdir init && git submodule status >lines && rm -fr init && - grep "^-$rev1" lines + test_grep "^-$rev1" lines ' test_failure_with_unknown_submodule () { @@ -584,7 +584,7 @@ test_expect_success 'status should be "up-to-date" after update' ' git submodule status >list && - grep "^ $rev1" list + test_grep "^ $rev1" list ' test_expect_success 'status "up-to-date" from subdirectory' ' @@ -593,8 +593,8 @@ cd sub && git submodule status >../list ) && - grep "^ $rev1" list && - grep "\\.\\./init" list + test_grep "^ $rev1" list && + test_grep "\\.\\./init" list ' test_expect_success 'status "up-to-date" from subdirectory with path' ' @@ -603,8 +603,8 @@ cd sub && git submodule status ../init >../list ) && - grep "^ $rev1" list && - grep "\\.\\./init" list + test_grep "^ $rev1" list && + test_grep "\\.\\./init" list ' test_expect_success 'status should be "modified" after submodule commit' ' @@ -619,7 +619,7 @@ test -n "$rev2" && git submodule status >list && - grep "^+$rev2" list + test_grep "^+$rev2" list ' test_expect_success '"submodule --cached" command forms should be identical' ' @@ -634,12 +634,12 @@ test_expect_success 'the --cached sha1 should be rev1' ' git submodule --cached status >list && - grep "^+$rev1" list + test_grep "^+$rev1" list ' test_expect_success 'git diff should report the SHA1 of the new submodule commit' ' git diff >diff && - grep "^+Subproject commit $rev2" diff + test_grep "^+Subproject commit $rev2" diff ' test_expect_success 'update should checkout rev1' ' @@ -654,7 +654,7 @@ test_expect_success 'status should be "up-to-date" after update' ' git submodule status >list && - grep "^ $rev1" list + test_grep "^ $rev1" list ' test_expect_success 'checkout superproject with subproject already present' ' @@ -1407,7 +1407,7 @@ done && test_must_fail git -C multisuper_clone submodule update 2>err && - grep "cannot clone submodule .sub[0-3]. without a URL" err + test_grep "cannot clone submodule .sub[0-3]. without a URL" err ' test_expect_success 'clone --recurse-submodules with a pathspec works' '
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh index 06cee34..872eb2d 100755 --- a/t/t7402-submodule-rebase.sh +++ b/t/t7402-submodule-rebase.sh
@@ -125,7 +125,7 @@ ) >expect && test_cmp expect actual && sub_expect="go to submodule (submodule), and either merge commit $(git -C submodule rev-parse --short HEAD^0)" && - grep "$sub_expect" actual_output + test_grep "$sub_expect" actual_output ' test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 6abb008..9554720 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh
@@ -627,10 +627,10 @@ compare_head ) && git diff --name-only >out && - grep ^submodule$ out && + test_grep ^submodule$ out && git submodule update && git diff --name-only >out && - grep ^submodule$ out && + test_grep ^submodule$ out && (cd submodule && compare_head ) && @@ -647,10 +647,10 @@ compare_head ) && git diff --name-only >out && - grep ^submodule$ out && + test_grep ^submodule$ out && git submodule update --checkout && git diff --name-only >out && - ! grep ^submodule$ out && + test_grep ! ^submodule$ out && (cd submodule && ! compare_head ) && @@ -679,10 +679,10 @@ git submodule init submodule && git submodule update submodule 2>err && - ! grep "Submodule path .* not initialized" err && + test_grep ! "Submodule path .* not initialized" err && git submodule update rebasing 2>err && - grep "Submodule path .rebasing. not initialized" err && + test_grep "Submodule path .rebasing. not initialized" err && test_path_exists submodule/.git && test_path_is_missing rebasing/.git @@ -699,7 +699,7 @@ git submodule update 2>err && test_path_exists submodule/.git && test_path_is_missing rebasing/.git && - ! grep "Submodule path .* not initialized" err + test_grep ! "Submodule path .* not initialized" err ) ' @@ -1046,12 +1046,12 @@ test_expect_success 'submodule update can be run in parallel' ' (cd super2 && GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 7 && - grep "7 tasks" trace.out && + test_grep "7 tasks" trace.out && git config submodule.fetchJobs 8 && GIT_TRACE=$(pwd)/trace.out git submodule update && - grep "8 tasks" trace.out && + test_grep "8 tasks" trace.out && GIT_TRACE=$(pwd)/trace.out git submodule update --jobs 9 && - grep "9 tasks" trace.out + test_grep "9 tasks" trace.out ) ' @@ -1066,14 +1066,14 @@ test_expect_success 'git clone passes the parallel jobs config on to submodules' ' test_when_finished "rm -rf super4" && GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 7 . super4 && - grep "7 tasks" trace.out && + test_grep "7 tasks" trace.out && rm -rf super4 && git config --global submodule.fetchJobs 8 && GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules . super4 && - grep "8 tasks" trace.out && + test_grep "8 tasks" trace.out && rm -rf super4 && GIT_TRACE=$(pwd)/trace.out git clone --recurse-submodules --jobs 9 . super4 && - grep "9 tasks" trace.out && + test_grep "9 tasks" trace.out && rm -rf super4 '
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh index 3d944a0..9f4af88 100755 --- a/t/t7416-submodule-dash-url.sh +++ b/t/t7416-submodule-dash-url.sh
@@ -49,7 +49,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'trailing backslash is handled correctly' ' @@ -79,7 +79,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects relative URL resolving to missing scheme' ' @@ -95,7 +95,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects empty URL scheme' ' @@ -111,7 +111,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects relative URL resolving to empty scheme' ' @@ -127,7 +127,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects empty hostname' ' @@ -143,7 +143,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects relative url that produced empty hostname' ' @@ -159,7 +159,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck permits embedded newline with unrecognized scheme' ' @@ -189,7 +189,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects embedded newline in relative url' ' @@ -204,7 +204,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_expect_success 'fsck rejects embedded newline in git url' ' @@ -219,7 +219,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesUrl err + test_grep gitmodulesUrl err ' test_done
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh index 5e3051d..7a6f615 100755 --- a/t/t7417-submodule-path-url.sh +++ b/t/t7417-submodule-path-url.sh
@@ -29,7 +29,7 @@ git init --bare dst && git -C dst config transfer.fsckObjects true && test_must_fail git push dst HEAD 2>err && - grep gitmodulesPath err + test_grep gitmodulesPath err ' test_expect_success MINGW 'submodule paths disallows trailing spaces' '
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh index 8cc8652..72c7f6f 100755 --- a/t/t7450-bad-git-dotfiles.sh +++ b/t/t7450-bad-git-dotfiles.sh
@@ -109,7 +109,7 @@ test_expect_success 'clone evil superproject' ' git clone --recurse-submodules . victim >output 2>&1 && - ! grep "RUNNING POST CHECKOUT" output + test_grep ! "RUNNING POST CHECKOUT" output ' test_expect_success 'fsck detects evil superproject' ' @@ -167,7 +167,7 @@ test_must_fail git -C dst.git index-pack --strict odd.pack 2>output && # Make sure we fail due to bad gitmodules content, not because we # could not read the blob in the first place. - grep gitmodulesName output + test_grep gitmodulesName output ' check_dotx_symlink () { @@ -216,7 +216,7 @@ # Check not only that we fail, but that it is due to the # symlink detector $fsck_must_fail git fsck 2>output && - grep "$fsck_prefix.*tree $tree: ${name}Symlink" output + test_grep "$fsck_prefix.*tree $tree: ${name}Symlink" output ) ' @@ -228,7 +228,7 @@ -c core.protectntfs \ -c core.protecthfs \ read-tree $tree 2>err && - grep "invalid path.*$name" err && + test_grep "invalid path.*$name" err && git -C $dir ls-files -s >out && test_must_be_empty out ' @@ -317,7 +317,13 @@ test_must_fail git -c core.protectNTFS=false \ clone --recurse-submodules squatting squatting-clone 2>err && test_grep -e "directory not empty" -e "not an empty directory" err && - ! grep gitdir squatting-clone/d/a/git~2 + # git~2 is an 8.3 short name, present only when 8.3 name + # generation is enabled. The "directory not empty" check + # above is the primary assertion. + if test -f squatting-clone/d/a/git~2 + then + test_grep ! gitdir squatting-clone/d/a/git~2 + fi fi ' @@ -350,7 +356,7 @@ test_expect_success 'submodule git dir nesting detection must work with parallel cloning' ' test_must_fail git clone --recurse-submodules --jobs=2 nested clone_parallel 2>err && cat err && - grep -E "(already exists|is inside git dir|not a git repository)" err && + test_grep -E "(already exists|is inside git dir|does not point to a valid repository)" err && { test_path_is_missing .git/modules/hippo/HEAD || test_path_is_missing .git/modules/hippo/hooks/HEAD @@ -370,7 +376,7 @@ ) && test_must_fail git -C nested_checkout checkout -f --recurse-submodules HEAD 2>err && cat err && - grep "is inside git dir" err && + test_grep "is inside git dir" err && test_path_is_missing nested_checkout/thing2/.git '
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh index a37509f..7794bab 100755 --- a/t/t7501-commit-basic-functionality.sh +++ b/t/t7501-commit-basic-functionality.sh
@@ -342,12 +342,12 @@ echo gak >file && git commit -m author \ --author "Rubber Duck <rduck@convoy.org>" -a >output 2>&1 && - grep Rubber.Duck output + test_grep Rubber.Duck output ' test_expect_success 'interactive add' ' echo 7 | test_must_fail git commit --interactive >out && - grep "What now" out + test_grep "What now" out ' test_expect_success "commit --interactive doesn't change index if editor aborts" ' @@ -376,13 +376,13 @@ EDITOR=./editor git commit -a -F msg && git show -s --pretty=format:%s >subject && - grep -q good subject && + test_grep -q good subject && echo quack >file && echo Another good message. | EDITOR=./editor git commit -a -F - && git show -s --pretty=format:%s >subject && - grep -q good subject + test_grep -q good subject ' test_expect_success 'partial commit that involves removal (1)' ' @@ -471,7 +471,7 @@ test_expect_success 'commit mentions forced date in output' ' git commit --amend --date=2010-01-02T03:04:05 >output && - grep "Date: *Sat Jan 2 03:04:05 2010" output + test_grep "Date: *Sat Jan 2 03:04:05 2010" output ' test_expect_success 'commit complains about completely bogus dates' ' @@ -660,9 +660,9 @@ git add chz && git commit elif -m "tacocat is a palindrome" && git show --stat >stat && - grep elif stat && + test_grep elif stat && git diff --cached >diff && - grep chz diff + test_grep chz diff ' test_expect_success 'same tree (single parent)' ' @@ -676,7 +676,7 @@ git commit --allow-empty -m "forced empty" && git cat-file commit HEAD >commit && - grep forced commit + test_grep forced commit '
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh index 05f6da4..62e3970 100755 --- a/t/t7502-commit-porcelain.sh +++ b/t/t7502-commit-porcelain.sh
@@ -529,7 +529,7 @@ test_config diff.noprefix true && git status -v >actual && - grep "diff --git negative negative" actual + test_grep "diff --git negative negative" actual ' mesg_with_comment_and_newlines='
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh index b53d71c..8e9d8ff 100755 --- a/t/t7507-commit-verbose.sh +++ b/t/t7507-commit-verbose.sh
@@ -106,13 +106,13 @@ export GIT_EDITOR && test_must_fail git -c core.commentchar="foo>" commit -a -v >out 2>err ) && - grep "^foo> " out && + test_grep "^foo> " out && test_grep "Aborting commit due to empty commit message." err ' test_expect_success 'status does not verbose without --verbose' ' git status >actual && - ! grep "^diff --git" actual + test_grep ! "^diff --git" actual ' test_expect_success 'setup -v -v' ' @@ -163,7 +163,7 @@ test_expect_success "status ignores commit.verbose=true" ' git -c commit.verbose=true status >actual && - ! grep "^diff --git actual" + test_grep ! "^diff --git" actual ' test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh index c2057bc..0fd7c79 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh
@@ -16,7 +16,7 @@ cd broken && git init && echo "[status] showuntrackedfiles = CORRUPT" >>.git/config && - test_expect_code 129 git status -h >usage 2>&1 + git status -h >usage 2>&1 ) && test_grep "[Uu]sage" broken/usage ' @@ -28,7 +28,7 @@ cd broken && git init && echo "[status] showuntrackedfiles = CORRUPT" >>.git/config && - test_expect_code 129 git commit -h >usage 2>&1 + git commit -h >usage 2>&1 ) && test_grep "[Uu]sage" broken/usage ' @@ -863,7 +863,7 @@ test_cmp expect output && git status -s --ignored >output && - grep "^!! \"expect with spaces\"$" output && + test_grep "^!! \"expect with spaces\"$" output && grep -v "^!! " output >output-wo-ignored && test_cmp expect output-wo-ignored ' @@ -908,7 +908,7 @@ git clone upstream downstream && git -C downstream checkout @{u} && git -C downstream status >actual && - grep -E "HEAD detached at [0-9a-f]+" actual + test_grep -E "HEAD detached at [0-9a-f]+" actual ' test_expect_success 'setup status submodule summary' ' @@ -1127,7 +1127,7 @@ # make dir1/tracked stat-dirty >dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked && git status -s >output && - ! grep dir1/tracked output && + test_grep ! dir1/tracked output && # make sure "status" succeeded without writing index out git diff-files | grep dir1/tracked ) @@ -1773,7 +1773,7 @@ ) ' -test_expect_success EXPENSIVE 'status does not re-read unchanged 4 or 8 GiB file' ' +test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'status does not re-read unchanged 4 or 8 GiB file' ' ( mkdir large-file && cd large-file &&
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index aa9108d..0c9a26d 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh
@@ -9,9 +9,9 @@ . "$TEST_DIRECTORY/lib-gpg.sh" test_expect_success GPG 'verify-commit does not crash with -h' ' - test_expect_code 129 git verify-commit -h >usage && + git verify-commit -h >usage && test_grep "[Uu]sage: git verify-commit " usage && - test_expect_code 129 nongit git verify-commit -h >usage && + nongit git verify-commit -h >usage && test_grep "[Uu]sage: git verify-commit " usage ' @@ -89,8 +89,8 @@ do git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo $commit OK || exit 1 done ) && @@ -100,8 +100,8 @@ do test_must_fail git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && - ! grep "Good signature from" actual && - ! grep "BAD signature from" actual && + test_grep ! "Good signature from" actual && + test_grep ! "BAD signature from" actual && echo $commit OK || exit 1 done ) && @@ -109,9 +109,9 @@ for commit in eighth-signed-alt twelfth-signed-alt do git show --pretty=short --show-signature $commit >actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && - grep "not certified" actual && + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && + test_grep "not certified" actual && echo $commit OK || exit 1 done ) @@ -119,16 +119,16 @@ test_expect_success GPG 'verify-commit exits failure on unknown signature' ' test_must_fail env GNUPGHOME="$GNUPGHOME_NOT_USED" git verify-commit initial 2>actual && - ! grep "Good signature from" actual && - ! grep "BAD signature from" actual && - grep -q -F -e "No public key" -e "public key not found" actual + test_grep ! "Good signature from" actual && + test_grep ! "BAD signature from" actual && + test_grep -q -F -e "No public key" -e "public key not found" actual ' test_expect_success GPG 'verify-commit exits success on untrusted signature' ' git verify-commit eighth-signed-alt 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual && - grep "not certified" actual + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual && + test_grep "not certified" actual ' test_expect_success GPG 'verify-commit exits success with matching minTrustLevel' ' @@ -151,8 +151,8 @@ for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed do git verify-commit --raw $commit 2>actual && - grep "GOODSIG" actual && - ! grep "BADSIG" actual && + test_grep "GOODSIG" actual && + test_grep ! "BADSIG" actual && echo $commit OK || exit 1 done ) && @@ -160,8 +160,8 @@ for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned do test_must_fail git verify-commit --raw $commit 2>actual && - ! grep "GOODSIG" actual && - ! grep "BADSIG" actual && + test_grep ! "GOODSIG" actual && + test_grep ! "BADSIG" actual && echo $commit OK || exit 1 done ) && @@ -169,9 +169,9 @@ for commit in eighth-signed-alt do git verify-commit --raw $commit 2>actual && - grep "GOODSIG" actual && - ! grep "BADSIG" actual && - grep "TRUST_UNDEFINED" actual && + test_grep "GOODSIG" actual && + test_grep ! "BADSIG" actual && + test_grep "TRUST_UNDEFINED" actual && echo $commit OK || exit 1 done ) @@ -179,7 +179,7 @@ test_expect_success GPG 'proper header is used for hash algorithm' ' git cat-file commit fourth-signed >output && - grep "^$(test_oid header) -----BEGIN PGP SIGNATURE-----" output + test_grep "^$(test_oid header) -----BEGIN PGP SIGNATURE-----" output ' test_expect_success GPG 'show signed commit with signature' ' @@ -201,8 +201,8 @@ git hash-object -w -t commit forged1 >forged1.commit && test_must_fail git verify-commit $(cat forged1.commit) && git show --pretty=short --show-signature $(cat forged1.commit) >actual1 && - grep "BAD signature from" actual1 && - ! grep "Good signature from" actual1 + test_grep "BAD signature from" actual1 && + test_grep ! "Good signature from" actual1 ' test_expect_success GPG 'detect fudged signature with NUL' ' @@ -212,8 +212,8 @@ git hash-object --literally -w -t commit forged2 >forged2.commit && test_must_fail git verify-commit $(cat forged2.commit) && git show --pretty=short --show-signature $(cat forged2.commit) >actual2 && - grep "BAD signature from" actual2 && - ! grep "Good signature from" actual2 + test_grep "BAD signature from" actual2 && + test_grep ! "Good signature from" actual2 ' test_expect_success GPG 'amending already signed commit' ' @@ -221,8 +221,8 @@ git commit --amend -S --no-edit && git verify-commit HEAD && git show -s --show-signature HEAD >actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual ' test_expect_success GPG2 'bare signature' ' @@ -326,8 +326,8 @@ test_expect_success GPG 'log.showsignature behaves like --show-signature' ' test_config log.showsignature true && git show initial >actual && - grep "gpg: Signature made" actual && - grep "gpg: Good signature" actual + test_grep "gpg: Signature made" actual && + test_grep "gpg: Good signature" actual ' test_expect_success GPG 'check config gpg.format values' ' @@ -349,8 +349,8 @@ git hash-object -w -t commit double-commit >double-commit.commit && test_must_fail git verify-commit $(cat double-commit.commit) && git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual && - grep "BAD signature from" double-actual && - grep "Good signature from" double-actual + test_grep "BAD signature from" double-actual && + test_grep "Good signature from" double-actual ' test_expect_success GPG 'show double signature with custom format' ' @@ -404,8 +404,8 @@ head=$(git hash-object -t commit -w commit) && git reset --hard $head && git verify-commit $head 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual ' test_expect_success 'custom `gpg.program`' ' @@ -449,7 +449,7 @@ test_must_fail env LET_GPG_PROGRAM_FAIL=1 \ git commit -S --allow-empty -m must-fail 2>err && - grep zOMG err && + test_grep zOMG err && # `gpg.program` starts with `~`, the path should be interpreted to be relative to `$HOME` test_config gpg.program "~/fake-gpg" &&
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh index 08e82f7..aca4b6d 100755 --- a/t/t7512-status-help.sh +++ b/t/t7512-status-help.sh
@@ -224,7 +224,7 @@ COMMIT3=$(git rev-parse --short split_commit) && test_commit four_split main.txt four && COMMIT4=$(git rev-parse --short split_commit) && - FAKE_LINES="1 edit 2 3" && + FAKE_LINES="reword 1 edit 2 fixup_-C 3" && export FAKE_LINES && test_when_finished "git rebase --abort" && ONTO=$(git rev-parse --short HEAD~3) && @@ -233,10 +233,10 @@ cat >expected <<EOF && interactive rebase in progress; onto $ONTO Last commands done (2 commands done): - pick $COMMIT2 # two_split + reword $COMMIT2 # two_split edit $COMMIT3 # three_split Next command to do (1 remaining command): - pick $COMMIT4 # four_split + fixup -C $COMMIT4 # four_split (use "git rebase --edit-todo" to view and edit) You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''. (Once your working directory is clean, run "git rebase --continue") @@ -297,7 +297,7 @@ test_expect_success 'status: (continue first edit) second edit' ' - FAKE_LINES="edit 1 edit 2 3" && + FAKE_LINES="edit 1 edit 2 drop 3" && export FAKE_LINES && test_when_finished "git rebase --abort" && COMMIT2=$(git rev-parse --short several_edits^^) && @@ -312,7 +312,7 @@ edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits Next command to do (1 remaining command): - pick $COMMIT4 # four_edits + drop $COMMIT4 # four_edits (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (use "git commit --amend" to amend the current commit) @@ -327,7 +327,7 @@ test_expect_success 'status: (continue first edit) second edit and split' ' git reset --hard several_edits && - FAKE_LINES="edit 1 edit 2 3" && + FAKE_LINES="edit 1 edit 2 squash 3" && export FAKE_LINES && test_when_finished "git rebase --abort" && COMMIT2=$(git rev-parse --short several_edits^^) && @@ -343,7 +343,7 @@ edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits Next command to do (1 remaining command): - pick $COMMIT4 # four_edits + squash $COMMIT4 # four_edits (use "git rebase --edit-todo" to view and edit) You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (Once your working directory is clean, run "git rebase --continue") @@ -362,7 +362,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' ' git reset --hard several_edits && - FAKE_LINES="edit 1 edit 2 3" && + FAKE_LINES="edit 1 edit 2 fixup 3" && export FAKE_LINES && test_when_finished "git rebase --abort" && COMMIT2=$(git rev-parse --short several_edits^^) && @@ -378,7 +378,7 @@ edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits Next command to do (1 remaining command): - pick $COMMIT4 # four_edits + fixup $COMMIT4 # four_edits (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (use "git commit --amend" to amend the current commit) @@ -393,7 +393,7 @@ test_expect_success 'status: (amend first edit) second edit' ' git reset --hard several_edits && - FAKE_LINES="edit 1 edit 2 3" && + FAKE_LINES="edit 1 edit 2 fixup_-c 3" && export FAKE_LINES && test_when_finished "git rebase --abort" && COMMIT2=$(git rev-parse --short several_edits^^) && @@ -409,7 +409,7 @@ edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits Next command to do (1 remaining command): - pick $COMMIT4 # four_edits + fixup -c $COMMIT4 # four_edits (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (use "git commit --amend" to amend the current commit) @@ -460,14 +460,20 @@ test_expect_success 'status: (amend first edit) second edit and amend' ' git reset --hard several_edits && - FAKE_LINES="edit 1 edit 2 3" && - export FAKE_LINES && test_when_finished "git rebase --abort" && COMMIT2=$(git rev-parse --short several_edits^^) && COMMIT3=$(git rev-parse --short several_edits^) && COMMIT4=$(git rev-parse --short several_edits) && ONTO=$(git rev-parse --short HEAD~3) && - git rebase -i HEAD~3 && + cat >todo <<-EOF && + edit several_edits^^ # two_edits + edit several_edits^ # three_edits + merge $(git rev-parse main) $(git rev-parse several_edits) + EOF + ( + set_replace_editor todo && + git rebase -i HEAD~3 + ) && git commit --amend -m "c" && git rebase --continue && git commit --amend -m "d" && @@ -477,7 +483,7 @@ edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits Next command to do (1 remaining command): - pick $COMMIT4 # four_edits + merge $(git rev-parse --short main) $COMMIT4 (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (use "git commit --amend" to amend the current commit) @@ -525,14 +531,21 @@ test_expect_success 'status: (split first edit) second edit and split' ' git reset --hard several_edits && - FAKE_LINES="edit 1 edit 2 3" && - export FAKE_LINES && test_when_finished "git rebase --abort" && COMMIT2=$(git rev-parse --short several_edits^^) && COMMIT3=$(git rev-parse --short several_edits^) && COMMIT4=$(git rev-parse --short several_edits) && + cat >todo <<-EOF && + edit several_edits^^ # two_edits + edit several_edits^ # three_edits + reset $(git rev-parse main) + merge -C several_edits topic # title + EOF ONTO=$(git rev-parse --short HEAD~3) && - git rebase -i HEAD~3 && + ( + set_replace_editor todo && + git rebase -i HEAD~3 + ) && git reset HEAD^ && git add main.txt && git commit --amend -m "f" && @@ -543,8 +556,9 @@ Last commands done (2 commands done): edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits -Next command to do (1 remaining command): - pick $COMMIT4 # four_edits +Next commands to do (2 remaining commands): + reset $(git rev-parse --short main) + merge -C $COMMIT4 topic # title (use "git rebase --edit-todo" to view and edit) You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (Once your working directory is clean, run "git rebase --continue") @@ -563,14 +577,21 @@ test_expect_success 'status: (split first edit) second edit and amend' ' git reset --hard several_edits && - FAKE_LINES="edit 1 edit 2 3" && - export FAKE_LINES && test_when_finished "git rebase --abort" && + git branch cafe main && COMMIT2=$(git rev-parse --short several_edits^^) && COMMIT3=$(git rev-parse --short several_edits^) && - COMMIT4=$(git rev-parse --short several_edits) && + cat >todo <<-EOF && + edit several_edits^^ # two_edits + edit several_edits^ # three_edits + update-ref refs/heads/main + reset cafe + EOF ONTO=$(git rev-parse --short HEAD~3) && - git rebase -i HEAD~3 && + ( + set_replace_editor todo && + git rebase -i HEAD~3 + ) && git reset HEAD^ && git add main.txt && git commit --amend -m "g" && @@ -581,8 +602,9 @@ Last commands done (2 commands done): edit $COMMIT2 # two_edits edit $COMMIT3 # three_edits -Next command to do (1 remaining command): - pick $COMMIT4 # four_edits +Next commands to do (2 remaining commands): + update-ref refs/heads/main + reset cafe (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. (use "git commit --amend" to amend the current commit)
diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh index de7c4ca..29669f6 100755 --- a/t/t7516-commit-races.sh +++ b/t/t7516-commit-races.sh
@@ -10,7 +10,7 @@ EOF test_must_fail env EDITOR=./hare-editor git commit --allow-empty -m tortoise -e && git show -s --pretty=format:%s >subject && - grep hare subject && + test_grep hare subject && git show -s --pretty=format:%P >out && test_must_be_empty out ' @@ -24,7 +24,7 @@ git rev-parse HEAD >base && test_must_fail env EDITOR=./airplane-editor git commit --allow-empty -m ship -e && git show -s --pretty=format:%s >subject && - grep airplane subject && + test_grep airplane subject && git rev-parse HEAD^ >parent && test_cmp base parent '
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh index 7ee69ec..93973ed 100755 --- a/t/t7519-status-fsmonitor.sh +++ b/t/t7519-status-fsmonitor.sh
@@ -63,19 +63,19 @@ test_must_fail \ git -C ./bare-clone -c core.fsmonitor=foo \ update-index --fsmonitor 2>actual && - grep "bare repository .* is incompatible with fsmonitor" actual && + test_grep "bare repository .* is incompatible with fsmonitor" actual && test_must_fail \ git -C ./bare-clone -c core.fsmonitor=true \ update-index --fsmonitor 2>actual && - grep "bare repository .* is incompatible with fsmonitor" actual + test_grep "bare repository .* is incompatible with fsmonitor" actual ' test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' ' test_when_finished "rm -rf ./bare-clone actual" && git init --bare bare-clone && test_must_fail git -C ./bare-clone fsmonitor--daemon run 2>actual && - grep "bare repository .* is incompatible with fsmonitor" actual + test_grep "bare repository .* is incompatible with fsmonitor" actual ' test_expect_success MINGW,FSMONITOR_DAEMON 'run fsmonitor-daemon in virtual repo' ' @@ -84,7 +84,7 @@ test_must_fail git -C ./fake-virtual-clone \ -c core.virtualfilesystem=true \ fsmonitor--daemon run 2>actual && - grep "virtual repository .* is incompatible with fsmonitor" actual + test_grep "virtual repository .* is incompatible with fsmonitor" actual ' test_expect_success 'setup' ' @@ -111,21 +111,21 @@ # test that the fsmonitor extension is off by default test_expect_success 'fsmonitor extension is off by default' ' test-tool dump-fsmonitor >actual && - grep "^no fsmonitor" actual + test_grep "^no fsmonitor" actual ' # test that "update-index --fsmonitor" adds the fsmonitor extension test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' ' git update-index --fsmonitor && test-tool dump-fsmonitor >actual && - grep "^fsmonitor last update" actual + test_grep "^fsmonitor last update" actual ' # test that "update-index --no-fsmonitor" removes the fsmonitor extension test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' ' git update-index --no-fsmonitor && test-tool dump-fsmonitor >actual && - grep "^no fsmonitor" actual + test_grep "^no fsmonitor" actual ' cat >expect <<EOF &&
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh index d881e27..8619577 100755 --- a/t/t7527-builtin-fsmonitor.sh +++ b/t/t7527-builtin-fsmonitor.sh
@@ -158,7 +158,7 @@ GIT_TRACE2_EVENT="$PWD/.git/trace" \ test-tool -C test_implicit fsmonitor-client query --token 0 >actual && nul_to_q <actual >actual.filtered && - grep "builtin:" actual.filtered && + test_grep "builtin:" actual.filtered && # confirm that a daemon was started in the background. # @@ -312,7 +312,7 @@ start_daemon -C test_multiple && test_must_fail git -C test_multiple fsmonitor--daemon start 2>actual && - grep "fsmonitor--daemon is already running" actual && + test_grep "fsmonitor--daemon is already running" actual && git -C test_multiple fsmonitor--daemon stop && test_must_fail git -C test_multiple fsmonitor--daemon status @@ -622,7 +622,7 @@ test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_1 && nul_to_q <actual_1 >actual_q1 && - grep "file_1" actual_q1 && + test_grep "file_1" actual_q1 && # Force a flush. This will change the <token_id>, reset the <seq_nr>, and # flush the file data. Then create some events and ensure that the file @@ -630,19 +630,19 @@ test-tool -C test_flush fsmonitor-client flush >flush_0 && nul_to_q <flush_0 >flush_q0 && - grep "^builtin:test_00000002:0Q/Q$" flush_q0 && + test_grep "^builtin:test_00000002:0Q/Q$" flush_q0 && test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_2 && nul_to_q <actual_2 >actual_q2 && - grep "^builtin:test_00000002:0Q$" actual_q2 && + test_grep "^builtin:test_00000002:0Q$" actual_q2 && >test_flush/file_3 && test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_3 && nul_to_q <actual_3 >actual_q3 && - grep "file_3" actual_q3 + test_grep "file_3" actual_q3 ' # The next few test cases create repos where the .git directory is NOT @@ -815,7 +815,7 @@ start_daemon -C "$u" && git -C "$u" status >actual && - grep "new file: file1" actual + test_grep "new file: file1" actual ' done @@ -1069,9 +1069,9 @@ # directories and files that we touched. We may or may not get a # trailing slash on modified directories. # - grep -E "^event: abc/?$" ./insensitive.trace && - grep -E "^event: abc/def/?$" ./insensitive.trace && - grep -E "^event: abc/def/xyz$" ./insensitive.trace + test_grep -E "^event: abc/?$" ./insensitive.trace && + test_grep -E "^event: abc/def/?$" ./insensitive.trace && + test_grep -E "^event: abc/def/xyz$" ./insensitive.trace ' # The variable "unicode_debug" is defined in the following library @@ -1113,20 +1113,20 @@ then # We should have seen NFC event from OS. # We should not have synthesized an NFD event. - grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace && - grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace + test_grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace && + test_grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace else # We should have seen NFD event from OS. # We should have synthesized an NFC event. - grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace && - grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace + test_grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace && + test_grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace fi && # We assume UNICODE_NFD_PRESERVED. # We should have seen explicit NFD from OS. # We should have synthesized an NFC event. - grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace && - grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace + test_grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace && + test_grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace ' test_expect_success 'split-index and FSMonitor work well together' ' @@ -1240,21 +1240,21 @@ <"$PWD/subdir_case_wrong.log" \ >"$PWD/subdir_case_wrong.log1" && - grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" && - grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" && + test_grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" && + test_grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" && - grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" && + test_grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" && # Verify that we get a mapping event to correct the case. - grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \ + test_grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \ "$PWD/subdir_case_wrong.log1" && # The refresh-callbacks should have caused "git status" to clear # the CE_FSMONITOR_VALID bit on each of those files and caused # the worktree scan to visit them and mark them as modified. - grep -q " M AAA" "$PWD/subdir_case_wrong.out" && - grep -q " M zzz" "$PWD/subdir_case_wrong.out" && - grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out" + test_grep -q " M AAA" "$PWD/subdir_case_wrong.out" && + test_grep -q " M zzz" "$PWD/subdir_case_wrong.out" && + test_grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out" ' test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' ' @@ -1327,10 +1327,10 @@ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \ git -C file_case_wrong status --short \ >"$PWD/file_case_wrong-try1.out" && - grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" && - grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4" "$PWD/file_case_wrong-try1.log" && - grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6" "$PWD/file_case_wrong-try1.log" && - grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" && + test_grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" && + test_grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4" "$PWD/file_case_wrong-try1.log" && + test_grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6" "$PWD/file_case_wrong-try1.log" && + test_grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" && # FSM refresh will have invalidated the FSM bit and cause a regular # (real) scan of these tracked files, so they should have "H" status. @@ -1338,8 +1338,8 @@ # command).) git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" && - grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" && - grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" && + test_grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" && + test_grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" && # Try the status again. We assume that the above status command @@ -1348,17 +1348,17 @@ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \ git -C file_case_wrong status --short \ >"$PWD/file_case_wrong-try2.out" && - ! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" && - ! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" && - ! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" && - ! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" && + test_grep ! -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" && + test_grep ! -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" && + test_grep ! -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" && + test_grep ! -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" && # FSM refresh saw nothing, so it will mark all files as valid, # so they should now have "h" status. git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" && - grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" && - grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" && + test_grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" && + test_grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" && # We now have files with clean content, but with case-incorrect @@ -1373,20 +1373,20 @@ >"$PWD/file_case_wrong-try3.out" && # Verify that we get a mapping event to correct the case. - grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \ + test_grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \ "$PWD/file_case_wrong-try3.log" && - grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \ + test_grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \ "$PWD/file_case_wrong-try3.log" && # FSEvents are in observed case. - grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" && - grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" && + test_grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" && + test_grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" && # The refresh-callbacks should have caused "git status" to clear # the CE_FSMONITOR_VALID bit on each of those files and caused # the worktree scan to visit them and mark them as modified. - grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" && - grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out" + test_grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" && + test_grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out" ' test_done
diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh index b50306b..4439646 100755 --- a/t/t7528-signed-commit-ssh.sh +++ b/t/t7528-signed-commit-ssh.sh
@@ -133,8 +133,8 @@ do git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $commit OK || exit 1 done ) && @@ -144,8 +144,8 @@ do test_must_fail git verify-commit $commit && git show --pretty=short --show-signature $commit >actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $commit OK || exit 1 done ) && @@ -153,9 +153,9 @@ for commit in eighth-signed-alt twelfth-signed-alt do git show --pretty=short --show-signature $commit >actual && - grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && - grep "${GPGSSH_KEY_NOT_TRUSTED}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_KEY_NOT_TRUSTED}" actual && echo $commit OK || exit 1 done ) @@ -164,34 +164,34 @@ test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-commit eighth-signed-alt 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && - grep "${GPGSSH_KEY_NOT_TRUSTED}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_KEY_NOT_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on expired signature key' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-commit expired-signed 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on not yet valid signature key' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-commit notyetvalid-signed 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit succeeds with commit date and key validity matching' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && git verify-commit timeboxedvalid-signed 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure with commit date outside of key validity' ' test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_must_fail git verify-commit timeboxedinvalid-signed 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH 'verify-commit exits success with matching minTrustLevel' ' @@ -217,8 +217,8 @@ for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed do git verify-commit --raw $commit 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $commit OK || exit 1 done ) && @@ -226,8 +226,8 @@ for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned do test_must_fail git verify-commit --raw $commit 2>actual && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $commit OK || exit 1 done ) && @@ -235,8 +235,8 @@ for commit in eighth-signed-alt do test_must_fail git verify-commit --raw $commit 2>actual && - grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + test_grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual && echo $commit OK || exit 1 done ) @@ -244,7 +244,7 @@ test_expect_success GPGSSH 'proper header is used for hash algorithm' ' git cat-file commit fourth-signed >output && - grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output + test_grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output ' test_expect_success GPGSSH 'show signed commit with signature' ' @@ -268,9 +268,9 @@ git hash-object -w -t commit forged1 >forged1.commit && test_must_fail git verify-commit $(cat forged1.commit) && git show --pretty=short --show-signature $(cat forged1.commit) >actual1 && - grep "${GPGSSH_BAD_SIGNATURE}" actual1 && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 && - ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1 + test_grep "${GPGSSH_BAD_SIGNATURE}" actual1 && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1 ' test_expect_success GPGSSH 'detect fudged signature with NUL' ' @@ -281,8 +281,8 @@ git hash-object --literally -w -t commit forged2 >forged2.commit && test_must_fail git verify-commit $(cat forged2.commit) && git show --pretty=short --show-signature $(cat forged2.commit) >actual2 && - grep "${GPGSSH_BAD_SIGNATURE}" actual2 && - ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2 + test_grep "${GPGSSH_BAD_SIGNATURE}" actual2 && + test_grep ! "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2 ' test_expect_success GPGSSH 'amending already signed commit' ' @@ -293,8 +293,8 @@ git commit --amend -S --no-edit && git verify-commit HEAD && git show -s --show-signature HEAD >actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && - ! grep "${GPGSSH_BAD_SIGNATURE}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + test_grep ! "${GPGSSH_BAD_SIGNATURE}" actual ' test_expect_success GPGSSH 'show good signature with custom format' ' @@ -386,7 +386,7 @@ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && test_config log.showsignature true && git show initial >actual && - grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual + test_grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual ' test_expect_success GPGSSH 'check config gpg.format values' ' @@ -410,8 +410,8 @@ git hash-object -w -t commit double-commit >double-commit.commit && test_must_fail git verify-commit $(cat double-commit.commit) && git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual && - grep "BAD signature from" double-actual && - grep "Good signature from" double-actual + test_grep "BAD signature from" double-actual && + test_grep "Good signature from" double-actual ' test_expect_failure GPGSSH 'show double signature with custom format (TODO)' ' @@ -463,8 +463,8 @@ head=$(git hash-object -t commit -w commit) && git reset --hard $head && git verify-commit $head 2>actual && - grep "Good signature from" actual && - ! grep "BAD signature from" actual + test_grep "Good signature from" actual && + test_grep ! "BAD signature from" actual ' test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index f877d9a..7f2a1db 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh
@@ -173,7 +173,7 @@ cd broken && git init && >.git/index && - test_expect_code 129 git merge -h >usage + git merge -h >usage ) && test_grep "[Uu]sage: git merge" broken/usage ' @@ -342,8 +342,8 @@ >unrelated && git add unrelated && test_must_fail git merge --squash c7 --autostash >out 2>err && - ! grep "Applying autostash resulted in conflicts." err && - grep "When finished, apply stashed changes with \`git stash pop\`" out + test_grep ! "Applying autostash resulted in conflicts." err && + test_grep "When finished, apply stashed changes with \`git stash pop\`" out ' test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' ' @@ -990,7 +990,7 @@ EDITOR=./editor git merge --no-ff --edit c1 && verify_parents $c0 $c1 && git cat-file commit HEAD >raw && - grep "work done on the side branch" raw && + test_grep "work done on the side branch" raw && sed "1,/^$/d" >actual raw && test_cmp expected actual ' @@ -1157,13 +1157,13 @@ git pack-refs --all --prune && test_must_fail git merge not-local 2>stderr && - grep origin/not-local stderr + test_grep origin/not-local stderr ' test_expect_success 'suggested names are not ambiguous' ' git update-ref refs/heads/origin/not-local HEAD && test_must_fail git merge not-local 2>stderr && - grep remotes/origin/not-local stderr + test_grep remotes/origin/not-local stderr ' test_done
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh index 1f8c3b7..92007dc 100755 --- a/t/t7603-merge-reduce-heads.sh +++ b/t/t7603-merge-reduce-heads.sh
@@ -59,11 +59,11 @@ test_path_is_file c4.c && test_path_is_file c5.c && git show --format=%s -s >actual && - ! grep c1 actual && - grep c2 actual && - grep c3 actual && - ! grep c4 actual && - grep c5 actual + test_grep ! c1 actual && + test_grep c2 actual && + test_grep c3 actual && + test_grep ! c4 actual && + test_grep c5 actual ' test_expect_success 'pull c2, c3, c4, c5 into c1' ' @@ -82,11 +82,11 @@ test_path_is_file c4.c && test_path_is_file c5.c && git show --format=%s -s >actual && - ! grep c1 actual && - grep c2 actual && - grep c3 actual && - ! grep c4 actual && - grep c5 actual + test_grep ! c1 actual && + test_grep c2 actual && + test_grep c3 actual && + test_grep ! c4 actual && + test_grep c5 actual ' test_expect_success 'setup' '
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh index 81fb7c4..0ae2f59 100755 --- a/t/t7606-merge-custom.sh +++ b/t/t7606-merge-custom.sh
@@ -60,7 +60,7 @@ test_cmp second-parent.expected second-parent && test_cmp tree.expected tree && test -f c0.c && - grep c1c1 c1.c && + test_grep c1c1 c1.c && test -f c2.c '
diff --git a/t/t7607-merge-state.sh b/t/t7607-merge-state.sh index 89a62ac..bfe782d 100755 --- a/t/t7607-merge-state.sh +++ b/t/t7607-merge-state.sh
@@ -20,8 +20,8 @@ # just hit conflicts, it completely fails and says that it cannot # handle this type of merge. test_expect_code 2 git merge branch2 branch3 >output 2>&1 && - grep "fatal: merge program failed" output && - grep "Should not be doing an octopus" output && + test_grep "fatal: merge program failed" output && + test_grep "Should not be doing an octopus" output && # Make sure we did not leave stray changes around when no appropriate # merge strategy was found
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 957f8e2..0128b14 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh
@@ -738,7 +738,7 @@ test_config mergetool.myecho.trustExitCode true && test_must_fail git merge main && git mergetool --no-prompt --tool myecho -- both >actual && - grep ^\./both_LOCAL_ actual + test_grep ^\./both_LOCAL_ actual ' test_lazy_prereq MKTEMP ' @@ -755,8 +755,8 @@ test_config mergetool.myecho.trustExitCode true && test_must_fail git merge main && git mergetool --no-prompt --tool myecho -- both >actual && - ! grep ^\./both_LOCAL_ actual && - grep /both_LOCAL_ actual + test_grep ! ^\./both_LOCAL_ actual && + test_grep /both_LOCAL_ actual ' test_expect_success 'diff.orderFile configuration is honored' ' @@ -816,12 +816,12 @@ test_expect_success 'mergetool --tool-help shows recognized tools' ' # Check a few known tools are correctly shown git mergetool --tool-help >mergetools && - grep vimdiff mergetools && - grep vimdiff3 mergetools && - grep gvimdiff2 mergetools && - grep araxis mergetools && - grep xxdiff mergetools && - grep meld mergetools + test_grep vimdiff mergetools && + test_grep vimdiff3 mergetools && + test_grep gvimdiff2 mergetools && + test_grep araxis mergetools && + test_grep xxdiff mergetools && + test_grep meld mergetools ' test_expect_success 'mergetool hideResolved' '
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index 63ef63f..f0a390e 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh
@@ -177,7 +177,7 @@ rm -f .git/objects/pack/* && mv pack-* .git/objects/pack/ && git verify-pack -v -- .git/objects/pack/*.idx >packlist && - ! grep "^$coid " packlist && + test_grep ! "^$coid " packlist && echo >.git/objects/info/alternates && test_must_fail git show $coid ' @@ -194,7 +194,7 @@ rm -f .git/objects/pack/* && mv pack-* .git/objects/pack/ && git verify-pack -v -- .git/objects/pack/*.idx >packlist && - ! grep "^$coid " && + test_grep ! "^$coid " packlist && echo >.git/objects/info/alternates && test_must_fail git show $coid ' @@ -226,8 +226,8 @@ test_line_count = 4 old-counts && git repack -a -d --keep-pack $P1 --keep-pack $P4 && ls .git/objects/pack/*.pack >new-counts && - grep -q $P1 new-counts && - grep -q $P4 new-counts && + test_grep -q $P1 new-counts && + test_grep -q $P4 new-counts && test_line_count = 3 new-counts && git fsck && @@ -276,7 +276,7 @@ test_must_fail git fsck && test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git repack --cruft -d 2>err && - grep "bad object" err && + test_grep "bad object" err && # Before failing, the repack did not modify the # pack directory. @@ -460,8 +460,8 @@ # Object bar is in both the old .keep pack and the new # pack that contained the filtered out objects - grep "$bar_pack" bar_pack_1 && - grep "$foo_pack_1" bar_pack_1 && + test_grep "$bar_pack" bar_pack_1 && + test_grep "$foo_pack_1" bar_pack_1 && test "$foo_pack_1" != "$head_pack_1" ) '
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh index 04d5d8f..f3a0650 100755 --- a/t/t7703-repack-geometric.sh +++ b/t/t7703-repack-geometric.sh
@@ -112,7 +112,7 @@ find $objdir/pack -name "*.pack" | sort >after && test_line_count = 3 after && comm -3 small before | tr -d "\t" >large && - grep -qFf large after + test_grep -qFf large after ) ' @@ -210,7 +210,7 @@ # Packs should not have changed (only one non-kept pack, no # loose objects), but $midx should now exist. - grep "Nothing new to pack" out && + test_grep "Nothing new to pack" out && test_path_is_file $midx && test_cmp packs.before packs.after &&
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh index 9e03b04..b342e82 100755 --- a/t/t7704-repack-cruft.sh +++ b/t/t7704-repack-cruft.sh
@@ -166,9 +166,9 @@ test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects && test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects && - grep "^$foo" foo.objects && + test_grep "^$foo" foo.objects && test_line_count = 1 foo.objects && - grep "^$bar" bar.objects && + test_grep "^$bar" bar.objects && test_line_count = 1 bar.objects ) ' @@ -188,8 +188,8 @@ cruft=$(ls $packdir/pack-*.mtimes) && test-tool pack-mtimes $(basename "$cruft") >cruft.objects && - grep "^$foo" cruft.objects && - grep "^$bar" cruft.objects && + test_grep "^$foo" cruft.objects && + test_grep "^$bar" cruft.objects && test_line_count = 2 cruft.objects ) ' @@ -693,7 +693,7 @@ git -c pack.window=1 -c repack.cruftWindow=2 repack \ --cruft --window=3 && - grep "pack-objects.*--window=2.*--cruft" event.trace + test_grep "pack-objects.*--window=2.*--cruft" event.trace ) ' @@ -708,7 +708,7 @@ GIT_TRACE2_EVENT=$(pwd)/event.trace \ git -c pack.window=2 repack --cruft --window=3 && - grep "pack-objects.*--window=3.*--cruft" event.trace + test_grep "pack-objects.*--window=3.*--cruft" event.trace ) '
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 8a91ff3..0079aa9 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh
@@ -27,12 +27,11 @@ } test_expect_success 'basic usage requires no repo' ' - test_expect_code 129 git difftool -h >output && + git difftool -h >output && test_grep ^usage: output && # create a ceiling directory to prevent Git from finding a repo mkdir -p not/repo && test_when_finished rm -r not && - test_expect_code 129 \ env GIT_CEILING_DIRECTORIES="$(pwd)/not" \ git -C not/repo difftool -h >output && test_grep ^usage: output @@ -431,32 +430,32 @@ test_expect_success 'say no to the first file' ' (echo n && echo) >input && git difftool -x cat branch <input >output && - grep m2 output && - grep br2 output && - ! grep main output && - ! grep branch output + test_grep m2 output && + test_grep br2 output && + test_grep ! main output && + test_grep ! branch output ' test_expect_success 'say no to the second file' ' (echo && echo n) >input && git difftool -x cat branch <input >output && - grep main output && - grep branch output && - ! grep m2 output && - ! grep br2 output + test_grep main output && + test_grep branch output && + test_grep ! m2 output && + test_grep ! br2 output ' test_expect_success 'ending prompt input with EOF' ' git difftool -x cat branch </dev/null >output && - ! grep main output && - ! grep branch output && - ! grep m2 output && - ! grep br2 output + test_grep ! main output && + test_grep ! branch output && + test_grep ! m2 output && + test_grep ! br2 output ' test_expect_success 'difftool --tool-help' ' git difftool --tool-help >output && - grep tool output + test_grep tool output ' test_expect_success 'setup change in subdirectory' '
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index d61c4a4..ac7ea9c 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh
@@ -1101,19 +1101,19 @@ ' test_expect_success ' includes preceding comment' ' - grep "# Say hello" function-context-userdiff-actual + test_grep "# Say hello" function-context-userdiff-actual ' test_expect_success ' includes function line' ' - grep "=function hello" function-context-userdiff-actual + test_grep "=function hello" function-context-userdiff-actual ' test_expect_success ' includes matching line' ' - grep ": echo" function-context-userdiff-actual + test_grep ": echo" function-context-userdiff-actual ' test_expect_success ' includes last line of the function' ' - grep "} # hello" function-context-userdiff-actual + test_grep "} # hello" function-context-userdiff-actual ' for threads in $(test_seq 0 10) @@ -1137,16 +1137,16 @@ git grep --threads=2 Hello hello_world 2>err && grep ^warning: err >warnings && test_line_count = 1 warnings && - grep -F "no threads support, ignoring --threads" err && + test_grep -F "no threads support, ignoring --threads" err && git -c grep.threads=2 grep Hello hello_world 2>err && grep ^warning: err >warnings && test_line_count = 1 warnings && - grep -F "no threads support, ignoring grep.threads" err && + test_grep -F "no threads support, ignoring grep.threads" err && git -c grep.threads=2 grep --threads=4 Hello hello_world 2>err && grep ^warning: err >warnings && test_line_count = 2 warnings && - grep -F "no threads support, ignoring --threads" err && - grep -F "no threads support, ignoring grep.threads" err && + test_grep -F "no threads support, ignoring --threads" err && + test_grep -F "no threads support, ignoring grep.threads" err && git -c grep.threads=0 grep --threads=0 Hello hello_world 2>err && test_line_count = 0 err ' @@ -1267,21 +1267,21 @@ export GIT_CEILING_DIRECTORIES && cd non/git && test_expect_code 128 git grep --no-index search .. 2>error && - grep "is outside the directory tree" error + test_grep "is outside the directory tree" error ) && ( GIT_CEILING_DIRECTORIES="$(pwd)/non" && export GIT_CEILING_DIRECTORIES && cd non/git && test_expect_code 128 git grep --no-index search ../tig 2>error && - grep "is outside the directory tree" error + test_grep "is outside the directory tree" error ) && ( GIT_CEILING_DIRECTORIES="$(pwd)/non" && export GIT_CEILING_DIRECTORIES && cd non/git && test_expect_code 128 git grep --no-index search ../non 2>error && - grep "no such path in the working tree" error + test_grep "no such path in the working tree" error ) '
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 167fe66..e1cf53d 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh
@@ -590,7 +590,7 @@ --recurse-submodules HEAD^ >actual && test_cmp expect actual && # Verify that we actually fetched data from the promisor remote: - grep \"category\":\"promisor\",\"key\":\"fetch_count\",\"value\":\"1\" trace2.log + test_grep \"category\":\"promisor\",\"key\":\"fetch_count\",\"value\":\"1\" trace2.log ) '
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index d7f82e1..a8d6917 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh
@@ -35,7 +35,7 @@ } test_expect_success 'help text' ' - test_expect_code 129 git maintenance -h >actual && + git maintenance -h >actual && test_grep "usage: git maintenance <subcommand>" actual && test_expect_code 129 git maintenance barf 2>err && test_grep "unknown subcommand: \`barf'\''" err && @@ -305,7 +305,7 @@ test_cmp_rev refs/remotes/remote2/two refs/prefetch/remotes/remote2/two && git log --oneline --decorate --all >log && - ! grep "prefetch" log && + test_grep ! "prefetch" log && test_when_finished git config --unset remote.remote1.skipFetchAll && git config remote.remote1.skipFetchAll true && @@ -395,15 +395,15 @@ GIT_PROGRESS_DELAY=0 \ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err && - grep "Enumerating objects: 50, done." err && + test_grep "Enumerating objects: 50, done." err && GIT_PROGRESS_DELAY=0 \ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err && - grep "Enumerating objects: 50, done." err && + test_grep "Enumerating objects: 50, done." err && GIT_PROGRESS_DELAY=0 \ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err && - grep "Enumerating objects: 2, done." err && + test_grep "Enumerating objects: 2, done." err && GIT_PROGRESS_DELAY=0 \ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err && @@ -461,36 +461,42 @@ ' test_expect_success EXPENSIVE 'incremental-repack 2g limit' ' - test_config core.compression 0 && + test_when_finished rm -rf expensive-repo && + git init expensive-repo && + ( + cd expensive-repo && + git config set core.compression 0 && + git config set maintenance.auto false && - for i in $(test_seq 1 5) - do - test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big || - return 1 - done && - git add big && - git commit -qm "Add big file (1)" && + for i in $(test_seq 1 5) + do + test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big || + return 1 + done && + git add big && + git commit -qm "Add big file (1)" && - # ensure any possible loose objects are in a pack-file - git maintenance run --task=loose-objects && + # ensure any possible loose objects are in a pack-file + git maintenance run --task=loose-objects && - rm big && - for i in $(test_seq 6 10) - do - test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big || - return 1 - done && - git add big && - git commit -qm "Add big file (2)" && + rm big && + for i in $(test_seq 6 10) + do + test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big || + return 1 + done && + git add big && + git commit -qm "Add big file (2)" && - # ensure any possible loose objects are in a pack-file - git maintenance run --task=loose-objects && + # ensure any possible loose objects are in a pack-file + git maintenance run --task=loose-objects && - # Now run the incremental-repack task and check the batch-size - GIT_TRACE2_EVENT="$(pwd)/run-2g.txt" git maintenance run \ - --task=incremental-repack 2>/dev/null && - test_subcommand git multi-pack-index repack \ - --no-progress --batch-size=2147483647 <run-2g.txt + # Now run the incremental-repack task and check the batch-size + GIT_TRACE2_EVENT="$(pwd)/run-2g.txt" git maintenance run \ + --task=incremental-repack 2>/dev/null && + test_subcommand git multi-pack-index repack \ + --no-progress --batch-size=2147483647 <run-2g.txt + ) ' run_incremental_repack_and_verify () { @@ -1067,7 +1073,7 @@ test_when_finished git config --global --unset-all maintenance.repo && test_must_fail git maintenance unregister 2>err && - grep "is not registered" err && + test_grep "is not registered" err && git maintenance unregister --force && git config --global --add maintenance.repo /existing1 && @@ -1101,11 +1107,11 @@ test_cmp before actual && test_must_fail git maintenance unregister 2>err && - grep "is not registered" err && + test_grep "is not registered" err && git maintenance unregister --force && test_must_fail git maintenance unregister --config-file ./other 2>err && - grep "is not registered" err && + test_grep "is not registered" err && git maintenance unregister --config-file ./other --force ' @@ -1194,9 +1200,9 @@ # start registers the repo git config --get --global --fixed-value maintenance.repo "$(pwd)" && - grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=daily" cron.txt && - grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=hourly" cron.txt && - grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=weekly" cron.txt + test_grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=daily" cron.txt && + test_grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=hourly" cron.txt && + test_grep "for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=weekly" cron.txt ' test_expect_success 'stop from existing schedule' ' @@ -1213,7 +1219,7 @@ test_expect_success 'start preserves existing schedule' ' echo "Important information!" >cron.txt && GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start --scheduler=crontab && - grep "Important information!" cron.txt + test_grep "Important information!" cron.txt ' test_expect_success 'magic markers are correct' ' @@ -1228,8 +1234,8 @@ test_expect_success 'stop preserves surrounding schedule' ' echo "Crucial information!" >>cron.txt && GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop && - grep "Important information!" cron.txt && - grep "Crucial information!" cron.txt + test_grep "Important information!" cron.txt && + test_grep "Crucial information!" cron.txt ' test_expect_success 'start and stop macOS maintenance' ' @@ -1259,7 +1265,7 @@ do PLIST="$pfx/Library/LaunchAgents/org.git-scm.git.$frequency.plist" && test_xmllint "$PLIST" && - grep schedule=$frequency "$PLIST" && + test_grep schedule=$frequency "$PLIST" && echo "bootout gui/[UID] $PLIST" >>expect && echo "bootstrap gui/[UID] $PLIST" >>expect || return 1 done && @@ -1316,7 +1322,7 @@ for frequency in hourly daily weekly do - grep "/create /tn Git Maintenance ($frequency) /f /xml" args && + test_grep "/create /tn Git Maintenance ($frequency) /f /xml" args && file=$(ls .git/schedule_${frequency}*.xml) && test_xmllint "$file" || return 1 done && @@ -1355,8 +1361,8 @@ test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" && test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" && - grep "core.askPass=true" "systemd/user/git-maintenance@.service" && - grep "credential.interactive=false" "systemd/user/git-maintenance@.service" && + test_grep "core.askPass=true" "systemd/user/git-maintenance@.service" && + test_grep "credential.interactive=false" "systemd/user/git-maintenance@.service" && printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect && test_cmp expect args &&
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index 7822947..bf04b82 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh
@@ -113,8 +113,7 @@ ' test_expect_success 'blame --abbrev=<n> works' ' - # non-boundary commits get +1 for alignment - check_abbrev 31 --abbrev=30 HEAD && + check_abbrev 30 --abbrev=30 HEAD && check_abbrev 30 --abbrev=30 ^HEAD ' @@ -141,10 +140,8 @@ ' test_expect_success 'blame --abbrev -b truncates the blank boundary' ' - # Note that `--abbrev=` always gets incremented by 1, which is why we - # expect 11 leading spaces and not 10. cat >expect <<-EOF && - $(printf "%11s" "") (<author@example.com> 2005-04-07 15:45:13 -0700 1) abbrev + $(printf "%10s" "") (<author@example.com> 2005-04-07 15:45:13 -0700 1) abbrev EOF git blame -b --abbrev=10 ^HEAD -- abbrev.t >actual && test_cmp expect actual
diff --git a/t/t8008-blame-formats.sh b/t/t8008-blame-formats.sh index c12a419..016a837 100755 --- a/t/t8008-blame-formats.sh +++ b/t/t8008-blame-formats.sh
@@ -103,7 +103,7 @@ "committer C <c@d.e> 123456789 +0000" | git hash-object -w -t commit --stdin) && git blame --porcelain $commit -- single-file >output && - grep "^summary oneline$" output + test_grep "^summary oneline$" output ) '
diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh index b3be2aa..a4afb33 100755 --- a/t/t8010-cat-file-filters.sh +++ b/t/t8010-cat-file-filters.sh
@@ -49,7 +49,7 @@ sha1=$(git rev-parse -q --verify HEAD:world.txt) && test_must_fail git cat-file --path=hello.txt blob $sha1 >actual 2>err && test_must_be_empty actual && - grep "path.*needs.*filters" err + test_grep "path.*needs.*filters" err ' test_expect_success '--textconv/--filters complain without path' '
diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh index 5562eba..d2653b3 100755 --- a/t/t8012-blame-colors.sh +++ b/t/t8012-blame-colors.sh
@@ -66,7 +66,7 @@ grep "<CYAN>" <actual >colored && test_line_count = 1 colored && - grep qfunc colored + test_grep qfunc colored ' test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index e7ab645..e9d814a 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh
@@ -348,9 +348,9 @@ --smtp-server="$(pwd)/fake.sendmail" \ $patches \ 2>errors && - grep "^From: A U Thor <author@example.com>\$" msgtxt1 && - grep "^To: to@example.com\$" msgtxt1 && - grep "^In-Reply-To: <my-message-id@example.com>" msgtxt1 + test_grep "^From: A U Thor <author@example.com>\$" msgtxt1 && + test_grep "^To: to@example.com\$" msgtxt1 && + test_grep "^In-Reply-To: <my-message-id@example.com>" msgtxt1 ' test_expect_success $PREREQ,AUTOIDENT 'implicit ident is allowed' ' @@ -403,7 +403,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ tocmd.patch \ && - grep "^To: tocmd@example.com" msgtxt1 + test_grep "^To: tocmd@example.com" msgtxt1 ' test_expect_success $PREREQ 'cccmd works' ' @@ -417,7 +417,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ cccmd.patch \ && - grep "^ cccmd@example.com" msgtxt1 + test_grep "^ cccmd@example.com" msgtxt1 ' test_expect_success $PREREQ 'headercmd works' ' @@ -431,7 +431,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ headercmd.patch \ && - grep "^X-Debbugs-CC: dummy@example.com" msgtxt1 + test_grep "^X-Debbugs-CC: dummy@example.com" msgtxt1 ' test_expect_success $PREREQ '--no-header-cmd works' ' @@ -446,7 +446,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ headercmd.patch \ && - ! grep "^X-Debbugs-CC: dummy@example.com" msgtxt1 + test_grep ! "^X-Debbugs-CC: dummy@example.com" msgtxt1 ' test_expect_success $PREREQ 'multiline fields are correctly unfolded' ' @@ -464,7 +464,7 @@ --header-cmd=./headercmd-multiline \ --smtp-server="$(pwd)/fake.sendmail" \ headercmd.patch && - grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1 + test_grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1 ' # Blank lines in the middle of the output of a command are invalid. @@ -521,7 +521,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ $patches && sed "1,/^\$/d" <msgtxt1 >msgbody1 && - grep "From: A <author@example.com>" msgbody1 + test_grep "From: A <author@example.com>" msgbody1 ' test_expect_success $PREREQ 'Author From: not in message body' ' @@ -532,7 +532,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ $patches && sed "1,/^\$/d" <msgtxt1 >msgbody1 && - ! grep "From: A <author@example.com>" msgbody1 + test_grep ! "From: A <author@example.com>" msgbody1 ' test_expect_success $PREREQ 'allow long lines with --no-validate' ' @@ -553,7 +553,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ --transfer-encoding=auto \ $patches && - grep "Content-Transfer-Encoding: 8bit" msgtxt1 + test_grep "Content-Transfer-Encoding: 8bit" msgtxt1 ' test_expect_success $PREREQ 'long lines with auto encoding are quoted-printable' ' @@ -565,7 +565,7 @@ --transfer-encoding=auto \ --no-validate \ longline.patch && - grep "Content-Transfer-Encoding: quoted-printable" msgtxt1 + test_grep "Content-Transfer-Encoding: quoted-printable" msgtxt1 ' test_expect_success $PREREQ 'carriage returns with auto encoding are quoted-printable' ' @@ -579,7 +579,7 @@ --transfer-encoding=auto \ --no-validate \ cr.patch && - grep "Content-Transfer-Encoding: quoted-printable" msgtxt1 + test_grep "Content-Transfer-Encoding: quoted-printable" msgtxt1 ' for enc in auto quoted-printable base64 @@ -705,7 +705,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ --transfer-encoding=$enc \ $patches && - grep "Content-Transfer-Encoding: $enc" msgtxt1 + test_grep "Content-Transfer-Encoding: $enc" msgtxt1 ' done @@ -718,7 +718,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ $patches \ 2>errors && - ! grep "^In-Reply-To: < *>" msgtxt1 + test_grep ! "^In-Reply-To: < *>" msgtxt1 ' test_expect_success $PREREQ 'Valid In-Reply-To when prompting' ' @@ -729,7 +729,7 @@ ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \ --smtp-server="$(pwd)/fake.sendmail" \ $patches 2>errors && - ! grep "^In-Reply-To: < *>" msgtxt1 + test_grep ! "^In-Reply-To: < *>" msgtxt1 ' test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' ' @@ -792,8 +792,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches 2>err && - grep "I am about to error" err && - grep "the editor exited uncleanly, aborting everything" err + test_grep "I am about to error" err && + test_grep "the editor exited uncleanly, aborting everything" err ' test_expect_success $PREREQ 'setup fake editor' ' @@ -814,11 +814,11 @@ ' test_expect_success $PREREQ 'first message is compose text' ' - grep "^fake edit" msgtxt1 + test_grep "^fake edit" msgtxt1 ' test_expect_success $PREREQ 'second message is patch' ' - grep "Subject:.*Second" msgtxt2 + test_grep "Subject:.*Second" msgtxt2 ' test_expect_success $PREREQ 'setup expect' " @@ -1237,8 +1237,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^utf8 body" msgtxt1 && - grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1 + test_grep "^utf8 body" msgtxt1 && + test_grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1 ' test_expect_success $PREREQ '--compose respects user mime type' ' @@ -1260,9 +1260,9 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^utf8 body" msgtxt1 && - grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 && - ! grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1 + test_grep "^utf8 body" msgtxt1 && + test_grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 && + test_grep ! "^Content-Type: text/plain; charset=UTF-8" msgtxt1 ' test_expect_success $PREREQ '--compose adds MIME for utf8 subject' ' @@ -1274,8 +1274,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^fake edit" msgtxt1 && - grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 + test_grep "^fake edit" msgtxt1 && + test_grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' test_expect_success $PREREQ 'utf8 author is correctly passed on' ' @@ -1288,7 +1288,7 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ funny_name.patch && - grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1 + test_grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1 ' test_expect_success $PREREQ 'utf8 sender is not duplicated' ' @@ -1361,8 +1361,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^utf8 body" msgtxt1 && - grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 + test_grep "^utf8 body" msgtxt1 && + test_grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 ' test_expect_success $PREREQ '--compose-encoding works' ' @@ -1378,8 +1378,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^utf8 body" msgtxt1 && - grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 + test_grep "^utf8 body" msgtxt1 && + test_grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 ' test_expect_success $PREREQ '--compose-encoding overrides sendemail.composeencoding' ' @@ -1396,8 +1396,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^utf8 body" msgtxt1 && - grep "^Content-Type: text/plain; charset=iso-8859-2" msgtxt1 + test_grep "^utf8 body" msgtxt1 && + test_grep "^Content-Type: text/plain; charset=iso-8859-2" msgtxt1 ' test_expect_success $PREREQ '--compose-encoding adds correct MIME for subject' ' @@ -1410,8 +1410,8 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - grep "^fake edit" msgtxt1 && - grep "^Subject: =?iso-8859-2?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 + test_grep "^fake edit" msgtxt1 && + test_grep "^Subject: =?iso-8859-2?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' test_expect_success $PREREQ 'detects ambiguous reference/file conflict' ' @@ -1419,7 +1419,7 @@ git add main && git commit -m"add main" && test_must_fail git send-email --dry-run main 2>errors && - grep disambiguate errors + test_grep disambiguate errors ' test_expect_success $PREREQ 'feed two files' ' @@ -1443,7 +1443,7 @@ --in-reply-to="<in-reply-id@example.com>" \ --no-thread \ $patches >out && - grep "In-Reply-To: <in-reply-id@example.com>" out + test_grep "In-Reply-To: <in-reply-id@example.com>" out ' test_expect_success $PREREQ 'no in-reply-to and no threading' ' @@ -1453,7 +1453,7 @@ --to=nobody@example.com \ --no-thread \ $patches >stdout && - ! grep "In-Reply-To: " stdout + test_grep ! "In-Reply-To: " stdout ' test_expect_success $PREREQ 'threading but no chain-reply-to' ' @@ -1464,7 +1464,7 @@ --thread \ --no-chain-reply-to \ $patches $patches >stdout && - grep "In-Reply-To: " stdout + test_grep "In-Reply-To: " stdout ' test_expect_success $PREREQ 'override in-reply-to if no threading' ' @@ -1475,7 +1475,7 @@ --no-thread \ --in-reply-to="override" \ $threaded_patches >stdout && - grep "In-Reply-To: <override>" stdout + test_grep "In-Reply-To: <override>" stdout ' test_expect_success $PREREQ 'sendemail.to works' ' @@ -1484,7 +1484,7 @@ --dry-run \ --from="Example <nobody@example.com>" \ $patches >stdout && - grep "To: Somebody <somebody@ex.com>" stdout + test_grep "To: Somebody <somebody@ex.com>" stdout ' test_expect_success $PREREQ 'setup sendemail.identity' ' @@ -1498,7 +1498,7 @@ --dry-run \ --from="nobody@example.com" \ $patches >stdout && - grep "To: cloud@example.com" stdout + test_grep "To: cloud@example.com" stdout ' test_expect_success $PREREQ 'sendemail.identity: identity overrides sendemail.identity' ' @@ -1507,7 +1507,7 @@ --dry-run \ --from="nobody@example.com" \ $patches >stdout && - grep "To: isp@example.com" stdout + test_grep "To: isp@example.com" stdout ' test_expect_success $PREREQ 'sendemail.identity: --no-identity clears previous identity' ' @@ -1516,7 +1516,7 @@ --dry-run \ --from="nobody@example.com" \ $patches >stdout && - grep "To: default@example.com" stdout + test_grep "To: default@example.com" stdout ' test_expect_success $PREREQ 'sendemail.identity: bool identity variable existence overrides' ' @@ -1527,8 +1527,8 @@ --dry-run \ --from="nobody@example.com" \ $patches >stdout && - grep "To: cloud@example.com" stdout && - ! grep "X-Mailer" stdout + test_grep "To: cloud@example.com" stdout && + test_grep ! "X-Mailer" stdout ' test_expect_success $PREREQ 'sendemail.identity: bool variable fallback' ' @@ -1538,8 +1538,8 @@ --dry-run \ --from="nobody@example.com" \ $patches >stdout && - grep "To: cloud@example.com" stdout && - ! grep "X-Mailer" stdout + test_grep "To: cloud@example.com" stdout && + test_grep ! "X-Mailer" stdout ' test_expect_success $PREREQ 'sendemail.identity: bool variable without a value' ' @@ -1548,8 +1548,8 @@ --dry-run \ --from="nobody@example.com" \ $patches >stdout && - grep "To: default@example.com" stdout && - grep "X-Mailer" stdout + test_grep "To: default@example.com" stdout && + test_grep "X-Mailer" stdout ' test_expect_success $PREREQ '--no-to overrides sendemail.to' ' @@ -1559,8 +1559,8 @@ --no-to \ --to=nobody@example.com \ $patches >stdout && - grep "To: nobody@example.com" stdout && - ! grep "To: Somebody <somebody@ex.com>" stdout + test_grep "To: nobody@example.com" stdout && + test_grep ! "To: Somebody <somebody@ex.com>" stdout ' test_expect_success $PREREQ 'sendemail.cc works' ' @@ -1570,7 +1570,7 @@ --from="Example <nobody@example.com>" \ --to=nobody@example.com \ $patches >stdout && - grep "Cc: Somebody <somebody@ex.com>" stdout + test_grep "Cc: Somebody <somebody@ex.com>" stdout ' test_expect_success $PREREQ '--no-cc overrides sendemail.cc' ' @@ -1581,8 +1581,8 @@ --cc=bodies@example.com \ --to=nobody@example.com \ $patches >stdout && - grep "Cc: bodies@example.com" stdout && - ! grep "Cc: Somebody <somebody@ex.com>" stdout + test_grep "Cc: bodies@example.com" stdout && + test_grep ! "Cc: Somebody <somebody@ex.com>" stdout ' test_expect_success $PREREQ 'sendemail.bcc works' ' @@ -1593,7 +1593,7 @@ --to=nobody@example.com \ --smtp-server relay.example.com \ $patches >stdout && - grep "RCPT TO:<other@ex.com>" stdout + test_grep "RCPT TO:<other@ex.com>" stdout ' test_expect_success $PREREQ '--no-bcc overrides sendemail.bcc' ' @@ -1605,8 +1605,8 @@ --to=nobody@example.com \ --smtp-server relay.example.com \ $patches >stdout && - grep "RCPT TO:<bodies@example.com>" stdout && - ! grep "RCPT TO:<other@ex.com>" stdout + test_grep "RCPT TO:<bodies@example.com>" stdout && + test_grep ! "RCPT TO:<other@ex.com>" stdout ' test_expect_success $PREREQ 'patches To headers are used by default' ' @@ -1617,7 +1617,7 @@ --from="Example <nobody@example.com>" \ --smtp-server relay.example.com \ $patch >stdout && - grep "RCPT TO:<bodies@example.com>" stdout + test_grep "RCPT TO:<bodies@example.com>" stdout ' test_expect_success $PREREQ 'patches To headers are appended to' ' @@ -1629,8 +1629,8 @@ --to=nobody@example.com \ --smtp-server relay.example.com \ $patch >stdout && - grep "RCPT TO:<bodies@example.com>" stdout && - grep "RCPT TO:<nobody@example.com>" stdout + test_grep "RCPT TO:<bodies@example.com>" stdout && + test_grep "RCPT TO:<nobody@example.com>" stdout ' test_expect_success $PREREQ 'To headers from files reset each patch' ' @@ -1691,9 +1691,9 @@ git send-email --from=author@example.com --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ email-using-8bit >stdout && - grep "do not declare a Content-Transfer-Encoding" stdout && - grep email-using-8bit stdout && - grep "Declare which 8bit encoding to use" stdout && + test_grep "do not declare a Content-Transfer-Encoding" stdout && + test_grep email-using-8bit stdout && + test_grep "Declare which 8bit encoding to use" stdout && grep -E "Content|MIME" msgtxt1 >actual && test_cmp content-type-decl actual ' @@ -1790,7 +1790,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ email-using-8bit \ 2>errors >out && - grep "cannot send message as 7bit" errors && + test_grep "cannot send message as 7bit" errors && test -z "$(ls msgtxt*)" ' @@ -1801,7 +1801,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ email-using-8bit \ 2>errors >out && - grep "cannot send message as 7bit" errors && + test_grep "cannot send message as 7bit" errors && test -z "$(ls msgtxt*)" ' @@ -1812,7 +1812,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ email-using-8bit \ 2>errors >out && - grep "cannot send message as 7bit" errors && + test_grep "cannot send message as 7bit" errors && test -z "$(ls msgtxt*)" ' @@ -1939,7 +1939,7 @@ outdir/0000-*.patch \ outdir/0001-*.patch \ 2>errors >out && - grep "SUBJECT HERE" errors && + test_grep "SUBJECT HERE" errors && test -z "$(ls msgtxt*)" ' @@ -1956,7 +1956,7 @@ outdir/0000-*.patch \ outdir/0001-*.patch \ 2>errors >out && - ! grep "SUBJECT HERE" errors && + test_grep ! "SUBJECT HERE" errors && test -n "$(ls msgtxt*)" ' @@ -2016,8 +2016,8 @@ --smtp-server="$(pwd)/fake.sendmail" \ outdir/0001-*.patch \ 2>errors >out && - grep "^!somebody@example\.org!$" commandline1 && - grep -F "To: \"Dot U. Sir\" <somebody@example.org>" out + test_grep "^!somebody@example\.org!$" commandline1 && + test_grep -F "To: \"Dot U. Sir\" <somebody@example.org>" out ' test_expect_success $PREREQ 'sendemail.aliasfiletype=mailrc' ' @@ -2031,7 +2031,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ outdir/0001-*.patch \ 2>errors >out && - grep "^!somebody@example\.org!$" commandline1 + test_grep "^!somebody@example\.org!$" commandline1 ' test_expect_success $PREREQ 'sendemail.aliasesfile=~/.mailrc' ' @@ -2045,7 +2045,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ outdir/0001-*.patch \ 2>errors >out && - grep "^!someone@example\.org!$" commandline1 + test_grep "^!someone@example\.org!$" commandline1 ' test_dump_aliases () { @@ -2264,7 +2264,7 @@ 2>errors >out && for i in $expect do - grep "^!$i!$" commandline1 || return 1 + test_grep "^!$i!$" commandline1 || return 1 done ' } @@ -2328,7 +2328,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ aliased.patch \ 2>errors >out && - grep "^!someone@example\.org!$" commandline1 + test_grep "^!someone@example\.org!$" commandline1 ' test_expect_success $PREREQ 'alias support in Cc header' ' @@ -2342,7 +2342,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ aliased.patch \ 2>errors >out && - grep "^!someone@example\.org!$" commandline1 + test_grep "^!someone@example\.org!$" commandline1 ' test_expect_success $PREREQ 'tocmd works with aliases' ' @@ -2358,7 +2358,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ tocmd.patch \ 2>errors >out && - grep "^!someone@example\.org!$" commandline1 + test_grep "^!someone@example\.org!$" commandline1 ' test_expect_success $PREREQ 'cccmd works with aliases' ' @@ -2374,7 +2374,7 @@ --smtp-server="$(pwd)/fake.sendmail" \ cccmd.patch \ 2>errors >out && - grep "^!someone@example\.org!$" commandline1 + test_grep "^!someone@example\.org!$" commandline1 ' do_xmailer_test () { @@ -2511,7 +2511,7 @@ --mailmap \ a.patch \ 2>errors >out && - grep "^!someone@example\.com!$" commandline1 + test_grep "^!someone@example\.com!$" commandline1 ' test_expect_success $PREREQ 'sendemail.mailmap configuration' ' @@ -2528,7 +2528,7 @@ --to=someone@example.org \ a.patch \ 2>errors >out && - grep "^!someone@example\.com!$" commandline1 + test_grep "^!someone@example\.com!$" commandline1 ' test_expect_success $PREREQ 'sendemail.mailmap.file configuration' ' @@ -2545,7 +2545,7 @@ --to=someone@example.org \ a.patch \ 2>errors >out && - grep "^!someone@example\.com!$" commandline1 + test_grep "^!someone@example\.com!$" commandline1 ' test_expect_success $PREREQ 'sendemail.mailmap identity overrides configuration' ' @@ -2564,7 +2564,7 @@ --to=someone@example.org \ a.patch \ 2>errors >out && - grep "^!someone@example\.com!$" commandline1 + test_grep "^!someone@example\.com!$" commandline1 ' test_expect_success $PREREQ '--no-mailmap overrides configuration' ' @@ -2584,7 +2584,7 @@ --no-mailmap \ a.patch \ 2>errors >out && - grep "^!someone@example\.org!$" commandline1 + test_grep "^!someone@example\.org!$" commandline1 ' test_expect_success $PREREQ 'mailmap support in To header' ' @@ -2600,7 +2600,7 @@ --mailmap \ a.patch \ 2>errors >out && - grep "^!someone@example\.com!$" commandline1 + test_grep "^!someone@example\.com!$" commandline1 ' test_expect_success $PREREQ 'mailmap support in Cc header' ' @@ -2616,7 +2616,7 @@ --mailmap \ a.patch \ 2>errors >out && - grep "^!someone@example\.com!$" commandline1 + test_grep "^!someone@example\.com!$" commandline1 ' test_expect_success $PREREQ 'test using command name with --sendmail-cmd' ' @@ -2676,9 +2676,9 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ has-reply.patch no-reply.patch && - grep "In-Reply-To: <replied.to@example.com>" msgtxt1 && - grep "References: <replied.to@example.com>" msgtxt1 && - ! grep replied.to@example.com msgtxt2 + test_grep "In-Reply-To: <replied.to@example.com>" msgtxt1 && + test_grep "References: <replied.to@example.com>" msgtxt1 && + test_grep ! replied.to@example.com msgtxt2 ' test_expect_success $PREREQ 'cmdline in-reply-to used with --no-thread' ' @@ -2689,10 +2689,10 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ has-reply.patch no-reply.patch && - grep "In-Reply-To: <cmdline.reply@example.com>" msgtxt1 && - grep "References: <cmdline.reply@example.com>" msgtxt1 && - grep "In-Reply-To: <cmdline.reply@example.com>" msgtxt2 && - grep "References: <cmdline.reply@example.com>" msgtxt2 + test_grep "In-Reply-To: <cmdline.reply@example.com>" msgtxt1 && + test_grep "References: <cmdline.reply@example.com>" msgtxt1 && + test_grep "In-Reply-To: <cmdline.reply@example.com>" msgtxt2 && + test_grep "References: <cmdline.reply@example.com>" msgtxt2 ' test_expect_success $PREREQ 'invoke hook' ' @@ -2775,7 +2775,7 @@ test_expect_success $PREREQ 'send-email relays -v 3 to format-patch' ' test_when_finished "rm -f out" && git send-email --dry-run -v 3 -1 >out && - grep "PATCH v3" out + test_grep "PATCH v3" out ' test_expect_success $PREREQ 'test that sendmail config is rejected' ' @@ -2819,7 +2819,7 @@ --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ HEAD^ && - grep "From: edited-from@example.com" msgtxt1 + test_grep "From: edited-from@example.com" msgtxt1 ' test_expect_success $PREREQ '--compose handles to headers' '
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh index 8da318d..87b438f 100755 --- a/t/t9003-help-autocorrect.sh +++ b/t/t9003-help-autocorrect.sh
@@ -34,10 +34,10 @@ git config help.autocorrect $show && test_must_fail git lfg 2>actual && - grep "^ lgf" actual && + test_grep "^ lgf" actual && test_must_fail git distimdist 2>actual && - grep "^ distimdistim" actual + test_grep "^ distimdistim" actual ' done @@ -60,7 +60,7 @@ git config help.autocorrect never && test_must_fail git lfg 2>actual && - grep "is not a git command" actual && + test_grep "is not a git command" actual && test_line_count = 1 actual '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh index bca496c..376100f 100755 --- a/t/t9106-git-svn-commit-diff-clobber.sh +++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -94,7 +94,7 @@ test_expect_success 'check that rebase really failed' ' git status >output && - grep currently.rebasing output + test_grep currently.rebasing output ' test_expect_success 'resolve, continue the rebase and dcommit' "
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index aa908bb..6d7d2aa 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh
@@ -46,7 +46,7 @@ test_expect_success 'initialize a multi-repository repo' ' git svn init "$svnrepo" -T trunk -t tags -b branches && git config --get-all svn-remote.svn.fetch > fetch.out && - grep "^trunk:refs/remotes/origin/trunk$" fetch.out && + test_grep "^trunk:refs/remotes/origin/trunk$" fetch.out && test -n "$(git config --get svn-remote.svn.branches \ "^branches/\*:refs/remotes/origin/\*$")" && test -n "$(git config --get svn-remote.svn.tags \ @@ -63,13 +63,13 @@ $i:refs/remotes/origin/$i || return 1 done && git config --get-all svn-remote.svn.fetch > fetch.out && - grep "^trunk:refs/remotes/origin/trunk$" fetch.out && - grep "^branches/a:refs/remotes/origin/a$" fetch.out && - grep "^branches/b:refs/remotes/origin/b$" fetch.out && - grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out && - grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out && - grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out && - grep "^:refs/remotes/git-svn" fetch.out + test_grep "^trunk:refs/remotes/origin/trunk$" fetch.out && + test_grep "^branches/a:refs/remotes/origin/a$" fetch.out && + test_grep "^branches/b:refs/remotes/origin/b$" fetch.out && + test_grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out && + test_grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out && + test_grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out && + test_grep "^:refs/remotes/git-svn" fetch.out ' # refs should all be different, but the trees should all be the same: @@ -109,13 +109,13 @@ git svn migrate --minimize && test -z "$(git config -l | grep "^svn-remote\.git-svn\.")" && git config --get-all svn-remote.svn.fetch > fetch.out && - grep "^trunk:refs/remotes/origin/trunk$" fetch.out && - grep "^branches/a:refs/remotes/origin/a$" fetch.out && - grep "^branches/b:refs/remotes/origin/b$" fetch.out && - grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out && - grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out && - grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out && - grep "^:refs/remotes/git-svn" fetch.out + test_grep "^trunk:refs/remotes/origin/trunk$" fetch.out && + test_grep "^branches/a:refs/remotes/origin/a$" fetch.out && + test_grep "^branches/b:refs/remotes/origin/b$" fetch.out && + test_grep "^tags/0\.1:refs/remotes/origin/tags/0\.1$" fetch.out && + test_grep "^tags/0\.2:refs/remotes/origin/tags/0\.2$" fetch.out && + test_grep "^tags/0\.3:refs/remotes/origin/tags/0\.3$" fetch.out && + test_grep "^:refs/remotes/git-svn" fetch.out ' test_expect_success ".rev_db auto-converted to .rev_map.UUID" '
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh index ad37d98..9dc4a31 100755 --- a/t/t9110-git-svn-use-svm-props.sh +++ b/t/t9110-git-svn-use-svm-props.sh
@@ -22,36 +22,36 @@ bar_url=http://mayonaise/svnrepo/bar test_expect_success 'verify metadata for /bar' " git cat-file commit refs/remotes/bar >actual && - grep '^git-svn-id: $bar_url@12 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@12 $uuid$' actual && git cat-file commit refs/remotes/bar~1 >actual && - grep '^git-svn-id: $bar_url@11 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@11 $uuid$' actual && git cat-file commit refs/remotes/bar~2 >actual && - grep '^git-svn-id: $bar_url@10 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@10 $uuid$' actual && git cat-file commit refs/remotes/bar~3 >actual && - grep '^git-svn-id: $bar_url@9 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@9 $uuid$' actual && git cat-file commit refs/remotes/bar~4 >actual && - grep '^git-svn-id: $bar_url@6 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@6 $uuid$' actual && git cat-file commit refs/remotes/bar~5 >actual && - grep '^git-svn-id: $bar_url@1 $uuid$' actual + test_grep '^git-svn-id: $bar_url@1 $uuid$' actual " e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e test_expect_success 'verify metadata for /dir/a/b/c/d/e' " git cat-file commit refs/remotes/e >actual && - grep '^git-svn-id: $e_url@1 $uuid$' actual + test_grep '^git-svn-id: $e_url@1 $uuid$' actual " dir_url=http://mayonaise/svnrepo/dir test_expect_success 'verify metadata for /dir' " git cat-file commit refs/remotes/dir >actual && - grep '^git-svn-id: $dir_url@2 $uuid$' actual && + test_grep '^git-svn-id: $dir_url@2 $uuid$' actual && git cat-file commit refs/remotes/dir~1 >actual && - grep '^git-svn-id: $dir_url@1 $uuid$' actual + test_grep '^git-svn-id: $dir_url@1 $uuid$' actual " test_expect_success 'find commit based on SVN revision number' " git svn find-rev r12 >actual && - grep $(git rev-parse HEAD) actual + test_grep $(git rev-parse HEAD) actual " test_expect_success 'empty rebase' "
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh index 6c93073..e45d81b 100755 --- a/t/t9111-git-svn-use-svnsync-props.sh +++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -21,31 +21,31 @@ bar_url=http://mayonaise/svnrepo/bar test_expect_success 'verify metadata for /bar' " git cat-file commit refs/remotes/bar >actual && - grep '^git-svn-id: $bar_url@12 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@12 $uuid$' actual && git cat-file commit refs/remotes/bar~1 >actual && - grep '^git-svn-id: $bar_url@11 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@11 $uuid$' actual && git cat-file commit refs/remotes/bar~2 >actual && - grep '^git-svn-id: $bar_url@10 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@10 $uuid$' actual && git cat-file commit refs/remotes/bar~3 >actual && - grep '^git-svn-id: $bar_url@9 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@9 $uuid$' actual && git cat-file commit refs/remotes/bar~4 >actual && - grep '^git-svn-id: $bar_url@6 $uuid$' actual && + test_grep '^git-svn-id: $bar_url@6 $uuid$' actual && git cat-file commit refs/remotes/bar~5 >actual && - grep '^git-svn-id: $bar_url@1 $uuid$' actual + test_grep '^git-svn-id: $bar_url@1 $uuid$' actual " e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e test_expect_success 'verify metadata for /dir/a/b/c/d/e' " git cat-file commit refs/remotes/e >actual && - grep '^git-svn-id: $e_url@1 $uuid$' actual + test_grep '^git-svn-id: $e_url@1 $uuid$' actual " dir_url=http://mayonaise/svnrepo/dir test_expect_success 'verify metadata for /dir' " git cat-file commit refs/remotes/dir >actual && - grep '^git-svn-id: $dir_url@2 $uuid$' actual && + test_grep '^git-svn-id: $dir_url@2 $uuid$' actual && git cat-file commit refs/remotes/dir~1 >actual && - grep '^git-svn-id: $dir_url@1 $uuid$' actual + test_grep '^git-svn-id: $dir_url@1 $uuid$' actual " test_done
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index e06538b..2240b58 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -69,7 +69,7 @@ test x\$(git rev-parse --verify refs/heads/svn^2) = \ x\$(git rev-parse --verify refs/heads/merge) && git cat-file commit refs/heads/svn^ >actual && - grep '^friend$' actual + test_grep '^friend$' actual " test_expect_success 'git svn dcommit merges' " @@ -84,12 +84,12 @@ test x\$(git rev-parse --verify refs/heads/svn^2) = \ x\$(git rev-parse --verify refs/heads/merge) && git cat-file commit refs/heads/svn^ >actual && - grep '^friend$' actual + test_grep '^friend$' actual " test_expect_success 'verify merge commit message' " git rev-list --pretty=raw -1 refs/heads/svn >actual && - grep \" Merge branch 'merge' into svn\" actual + test_grep \" Merge branch 'merge' into svn\" actual " test_done
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh index d74d7b2..b2e1248 100755 --- a/t/t9116-git-svn-log.sh +++ b/t/t9116-git-svn-log.sh
@@ -45,17 +45,17 @@ test_expect_success 'run log' " git reset --hard origin/a && git svn log -r2 origin/trunk >out && - grep ^r2 out && + test_grep ^r2 out && git svn log -r4 origin/trunk >out && - grep ^r4 out && + test_grep ^r4 out && git svn log -r3 >out && - grep ^r3 out + test_grep ^r3 out " test_expect_success 'run log against a from trunk' " git reset --hard origin/trunk && git svn log -r3 origin/a >out && - grep ^r3 out + test_grep ^r3 out " printf 'r1 \nr2 \nr4 \n' > expected-range-r1-r2-r4
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index 3b038c3..b2815b6 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh
@@ -49,7 +49,7 @@ test_expect_success 'init without -s/-T/-b/-t does not warn' ' test ! -d trunk && git svn init "$svnrepo"/project/trunk trunk 2>warning && - ! grep -q prefix warning && + test_grep ! -q prefix warning && rm -rf trunk && rm -f warning ' @@ -57,7 +57,7 @@ test_expect_success 'clone without -s/-T/-b/-t does not warn' ' test ! -d trunk && git svn clone "$svnrepo"/project/trunk 2>warning && - ! grep -q prefix warning && + test_grep ! -q prefix warning && rm -rf trunk && rm -f warning ' @@ -80,7 +80,7 @@ test_expect_success 'init with -s/-T/-b/-t assumes --prefix=origin/' ' test ! -d project && git svn init -s "$svnrepo"/project project 2>warning && - ! grep -q prefix warning && + test_grep ! -q prefix warning && test_svn_configured_prefix "origin/" && rm -rf project && rm -f warning @@ -89,7 +89,7 @@ test_expect_success 'clone with -s/-T/-b/-t assumes --prefix=origin/' ' test ! -d project && git svn clone -s "$svnrepo"/project 2>warning && - ! grep -q prefix warning && + test_grep ! -q prefix warning && test_svn_configured_prefix "origin/" && rm -rf project && rm -f warning @@ -98,7 +98,7 @@ test_expect_success 'init with -s/-T/-b/-t and --prefix "" still works' ' test ! -d project && git svn init -s "$svnrepo"/project project --prefix "" 2>warning && - ! grep -q prefix warning && + test_grep ! -q prefix warning && test_svn_configured_prefix "" && rm -rf project && rm -f warning @@ -107,7 +107,7 @@ test_expect_success 'clone with -s/-T/-b/-t and --prefix "" still works' ' test ! -d project && git svn clone -s "$svnrepo"/project --prefix "" 2>warning && - ! grep -q prefix warning && + test_grep ! -q prefix warning && test_svn_configured_prefix "" && rm -rf project && rm -f warning
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh index 088d1c5..0dbb2e4 100755 --- a/t/t9119-git-svn-info.sh +++ b/t/t9119-git-svn-info.sh
@@ -333,27 +333,27 @@ echo two > gitwc/unknown-file && (cd gitwc && test_must_fail git svn info unknown-file) \ 2> actual.info-unknown-file && - grep unknown-file actual.info-unknown-file + test_grep unknown-file actual.info-unknown-file " test_expect_success 'info --url unknown-file' ' echo two > gitwc/unknown-file && (cd gitwc && test_must_fail git svn info --url unknown-file) \ 2> actual.info-url-unknown-file && - grep unknown-file actual.info-url-unknown-file + test_grep unknown-file actual.info-url-unknown-file ' test_expect_success 'info unknown-directory' " mkdir gitwc/unknown-directory svnwc/unknown-directory && (cd gitwc && test_must_fail git svn info unknown-directory) \ 2> actual.info-unknown-directory && - grep unknown-directory actual.info-unknown-directory + test_grep unknown-directory actual.info-unknown-directory " test_expect_success 'info --url unknown-directory' ' (cd gitwc && test_must_fail git svn info --url unknown-directory) \ 2> actual.info-url-unknown-directory && - grep unknown-directory actual.info-url-unknown-directory + test_grep unknown-directory actual.info-url-unknown-directory ' test_expect_success 'info unknown-symlink-file' " @@ -363,13 +363,13 @@ ) && (cd gitwc && test_must_fail git svn info unknown-symlink-file) \ 2> actual.info-unknown-symlink-file && - grep unknown-symlink-file actual.info-unknown-symlink-file + test_grep unknown-symlink-file actual.info-unknown-symlink-file " test_expect_success 'info --url unknown-symlink-file' ' (cd gitwc && test_must_fail git svn info --url unknown-symlink-file) \ 2> actual.info-url-unknown-symlink-file && - grep unknown-symlink-file actual.info-url-unknown-symlink-file + test_grep unknown-symlink-file actual.info-url-unknown-symlink-file ' test_expect_success 'info unknown-symlink-directory' " @@ -379,13 +379,13 @@ ) && (cd gitwc && test_must_fail git svn info unknown-symlink-directory) \ 2> actual.info-unknown-symlink-directory && - grep unknown-symlink-directory actual.info-unknown-symlink-directory + test_grep unknown-symlink-directory actual.info-unknown-symlink-directory " test_expect_success 'info --url unknown-symlink-directory' ' (cd gitwc && test_must_fail git svn info --url unknown-symlink-directory) \ 2> actual.info-url-unknown-symlink-directory && - grep unknown-symlink-directory actual.info-url-unknown-symlink-directory + test_grep unknown-symlink-directory actual.info-url-unknown-symlink-directory ' test_done
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh index 0fc289a..21401ad 100755 --- a/t/t9122-git-svn-author.sh +++ b/t/t9122-git-svn-author.sh
@@ -57,15 +57,15 @@ test "z$myself" = "z$unaffected" && # Make sure lack of --add-author-from did not add cruft - ! grep "^ From: A U Thor " actual.2 && + test_grep ! "^ From: A U Thor " actual.2 && # Make sure --add-author-from added cruft - grep "^ From: A U Thor " actual.3 && - grep "^ From: A U Thor " actual.4 && + test_grep "^ From: A U Thor " actual.3 && + test_grep "^ From: A U Thor " actual.4 && # Make sure --add-author-from with --use-log-author affected # the authorship information - grep "^Author: A U Thor " actual.4 && + test_grep "^Author: A U Thor " actual.4 && # Make sure there are no commit messages with excess blank lines test $(grep "^ " actual.2 | wc -l) = 3 &&
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh index 90325db..67a4d88 100755 --- a/t/t9130-git-svn-authors-file.sh +++ b/t/t9130-git-svn-authors-file.sh
@@ -29,9 +29,9 @@ git rev-list refs/remotes/git-svn >actual && test_line_count = 2 actual && git rev-list -1 --pretty=raw refs/remotes/git-svn >actual && - grep "^author BBBBBBB BBBBBBB <bb@example\.com> " actual && + test_grep "^author BBBBBBB BBBBBBB <bb@example\.com> " actual && git rev-list -1 --pretty=raw refs/remotes/git-svn~1 >actual && - grep "^author AAAAAAA AAAAAAA <aa@example\.com> " actual + test_grep "^author AAAAAAA AAAAAAA <aa@example\.com> " actual ) ' @@ -47,9 +47,9 @@ git rev-list refs/remotes/git-svn >actual && test_line_count = 4 actual && git rev-list -1 --pretty=raw refs/remotes/git-svn >actual && - grep "^author DDDDDDD DDDDDDD <dd@example\.com> " actual && + test_grep "^author DDDDDDD DDDDDDD <dd@example\.com> " actual && git rev-list -1 --pretty=raw refs/remotes/git-svn~1 >actual && - grep "^author CCCCCCC CCCCCCC <cc@example\.com> " actual + test_grep "^author CCCCCCC CCCCCCC <cc@example\.com> " actual ) '
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh index 5bb38cb..5fdf324 100755 --- a/t/t9138-git-svn-authors-prog.sh +++ b/t/t9138-git-svn-authors-prog.sh
@@ -48,15 +48,15 @@ ( cd x && git rev-list -1 --pretty=raw refs/remotes/git-svn~1 >actual && - grep "^author ee-foo <ee-foo@example\.com> " actual && + test_grep "^author ee-foo <ee-foo@example\.com> " actual && git rev-list -1 --pretty=raw refs/remotes/git-svn~2 >actual && - grep "^author dd <dd@sub\.example\.com> " actual && + test_grep "^author dd <dd@sub\.example\.com> " actual && git rev-list -1 --pretty=raw refs/remotes/git-svn~3 >actual && - grep "^author cc <cc@sub\.example\.com> " actual && + test_grep "^author cc <cc@sub\.example\.com> " actual && git rev-list -1 --pretty=raw refs/remotes/git-svn~4 >actual && - grep "^author bb <bb@example\.com> " actual && + test_grep "^author bb <bb@example\.com> " actual && git rev-list -1 --pretty=raw refs/remotes/git-svn~5 >actual && - grep "^author aa <aa@example\.com> " actual + test_grep "^author aa <aa@example\.com> " actual ) ' @@ -64,7 +64,7 @@ ( cd x && git rev-list -1 --pretty=raw refs/remotes/git-svn >actual && - grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> " actual + test_grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> " actual ) ' @@ -99,7 +99,7 @@ cd x && git svn --authors-prog=../svn-authors-prog fetch && git rev-list -1 --pretty=raw refs/remotes/git-svn >actual && - grep "^author xyz; touch evil <xyz; touch evil@example\.com> " actual && + test_grep "^author xyz; touch evil <xyz; touch evil@example\.com> " actual && ! test -f evil ) '
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh index a420b2a..c70a3b3 100755 --- a/t/t9140-git-svn-reset.sh +++ b/t/t9140-git-svn-reset.sh
@@ -43,7 +43,7 @@ git svn find-rev refs/remotes/git-svn > ../expect && test_must_fail git svn fetch 2> ../errors && git svn find-rev refs/remotes/git-svn > ../expect2 ) && - grep "not found in commit" errors && + test_grep "not found in commit" errors && test_cmp expect expect2 ' @@ -59,7 +59,7 @@ ( cd g && git svn fetch && git svn rebase && - grep "mod hidden" hid/hid.txt + test_grep "mod hidden" hid/hid.txt ) '
diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh index 8cb2b5c..2387c7d 100755 --- a/t/t9153-git-svn-rewrite-uuid.sh +++ b/t/t9153-git-svn-rewrite-uuid.sh
@@ -17,9 +17,9 @@ test_expect_success 'verify uuid' " git cat-file commit refs/remotes/git-svn~0 >actual && - grep '^git-svn-id: .*@2 $uuid$' actual && + test_grep '^git-svn-id: .*@2 $uuid$' actual && git cat-file commit refs/remotes/git-svn~1 >actual && - grep '^git-svn-id: .*@1 $uuid$' actual + test_grep '^git-svn-id: .*@1 $uuid$' actual " test_done
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 581cf3d..8e0272d 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh
@@ -336,7 +336,7 @@ echo Hello >> " space" && git commit -m "Another change" " space" && git cvsexportcommit -W -p -u -c HEAD && - grep Hello " space" && + test_grep Hello " space" && git diff-files) '
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh index f2a6df7..c8463d0 100755 --- a/t/t9210-scalar.sh +++ b/t/t9210-scalar.sh
@@ -21,13 +21,13 @@ # Register scalar register ${enlistment_root} && scalar list >out && - grep "$(pwd)/${enlistment_root}/src\$" out && + test_grep "$(pwd)/${enlistment_root}/src\$" out && # Delete (including enlistment root) scalar delete $enlistment_root && test_path_is_missing $enlistment_root && scalar list >out && - ! grep "^$(pwd)/${enlistment_root}/src\$" out || return 1 + test_grep ! "^$(pwd)/${enlistment_root}/src\$" out || return 1 done ' @@ -41,13 +41,13 @@ # Register scalar register ${enlistment_root}/src && scalar list >out && - grep "$(pwd)/${enlistment_root}/src\$" out && + test_grep "$(pwd)/${enlistment_root}/src\$" out && # Delete (will not include enlistment root) scalar delete ${enlistment_root}/src && test_path_is_dir $enlistment_root && scalar list >out && - ! grep "^$(pwd)/${enlistment_root}/src\$" out || return 1 + test_grep ! "^$(pwd)/${enlistment_root}/src\$" out || return 1 done ' @@ -61,13 +61,13 @@ # Register scalar register ${enlistment_root} && scalar list >out && - grep "$(pwd)/${enlistment_root}\$" out && + test_grep "$(pwd)/${enlistment_root}\$" out && # Delete (will not include enlistment root) scalar delete ${enlistment_root} && test_path_is_missing $enlistment_root && scalar list >out && - ! grep "^$(pwd)/${enlistment_root}\$" out && + test_grep ! "^$(pwd)/${enlistment_root}\$" out && # Make sure we did not accidentally delete the trash dir test_path_is_dir "$TRASH_DIRECTORY" || return 1 @@ -81,7 +81,7 @@ mkdir -p test/src/deep && GIT_CEILING_DIRECTORIES="$(pwd)/test/src" && ! scalar register test/src/deep 2>err && - grep "not a git repository" err + test_grep "not a git repository" err ' test_expect_success 'scalar enlistments need a worktree' ' @@ -89,11 +89,11 @@ git init --bare bare/src && ! scalar register bare/src 2>err && - grep "Scalar enlistments require a worktree" err && + test_grep "Scalar enlistments require a worktree" err && git init test/src && ! scalar register test/src/.git 2>err && - grep "Scalar enlistments require a worktree" err + test_grep "Scalar enlistments require a worktree" err ' test_expect_success FSMONITOR_DAEMON 'scalar register starts fsmon daemon' ' @@ -108,7 +108,7 @@ git init register-repo && GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \ scalar register register-repo 2>err && - grep "could not toggle maintenance" err + test_grep "could not toggle maintenance" err ' test_expect_success 'scalar unregister' ' @@ -117,13 +117,13 @@ git config --get --global --fixed-value \ maintenance.repo "$(pwd)/vanish/src" && scalar list >scalar.repos && - grep -F "$(pwd)/vanish/src" scalar.repos && + test_grep -F "$(pwd)/vanish/src" scalar.repos && rm -rf vanish/src/.git && scalar unregister vanish && test_must_fail git config --get --global --fixed-value \ maintenance.repo "$(pwd)/vanish/src" && scalar list >scalar.repos && - ! grep -F "$(pwd)/vanish/src" scalar.repos && + test_grep ! -F "$(pwd)/vanish/src" scalar.repos && # scalar unregister should be idempotent scalar unregister vanish @@ -278,7 +278,7 @@ git init stale/src && scalar register stale && scalar list >scalar.repos && - grep stale scalar.repos && + test_grep stale scalar.repos && grep -v stale scalar.repos >expect && @@ -307,7 +307,7 @@ test_expect_success '`scalar [...] <dir>` errors out when dir is missing' ' ! scalar run config cloned 2>err && - grep "cloned. does not exist" err + test_grep "cloned. does not exist" err ' SQ="'" @@ -318,7 +318,7 @@ echo "$(pwd)/.git/objects/" >>cloned/src/.git/objects/info/alternates && test_commit -C cloned/src loose && scalar diagnose cloned >out 2>err && - grep "Available space" out && + test_grep "Available space" out && sed -n "s/.*$SQ\\(.*\\.zip\\)$SQ.*/\\1/p" <err >zip_path && zip_path=$(cat zip_path) && test -n "$zip_path" && @@ -328,9 +328,9 @@ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out && test_file_not_empty out && "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out && - grep "$(pwd)/.git/objects" out && + test_grep "$(pwd)/.git/objects" out && "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out && - grep "^Total: [1-9]" out + test_grep "^Total: [1-9]" out ' test_done
diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh index 2043f48..002d6ec 100755 --- a/t/t9211-scalar-clone.sh +++ b/t/t9211-scalar-clone.sh
@@ -86,7 +86,7 @@ test_config -C to-clone uploadpack.allowanysha1inwant false && scalar clone "file://$(pwd)/to-clone" $enlistment 2>err && - grep "filtering not recognized by server, ignoring" err && + test_grep "filtering not recognized by server, ignoring" err && ( cd $enlistment/src && @@ -133,7 +133,7 @@ cd $enlistment/src && git for-each-ref refs/remotes/origin >out && test_line_count = 2 out && - grep "refs/remotes/origin/base" out + test_grep "refs/remotes/origin/base" out ) && cleanup_clone $enlistment @@ -147,8 +147,8 @@ cd $enlistment/src && git for-each-ref refs/remotes/origin >out && test_line_count = 3 out && - grep "refs/remotes/origin/base" out && - grep "refs/remotes/origin/parallel" out + test_grep "refs/remotes/origin/base" out && + test_grep "refs/remotes/origin/parallel" out ) && cleanup_clone $enlistment @@ -174,15 +174,15 @@ test_config -C to-clone uploadpack.allowanysha1inwant true && GIT_PROGRESS_DELAY=0 scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr && - ! grep "Enumerating objects" stderr && - ! grep "Updating files" stderr && + test_grep ! "Enumerating objects" stderr && + test_grep ! "Updating files" stderr && cleanup_clone $enlistment ' test_expect_success 'scalar clone warns when background maintenance fails' ' GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \ scalar clone "file://$(pwd)/to-clone" maint-fail 2>err && - grep "could not toggle maintenance" err + test_grep "could not toggle maintenance" err ' test_expect_success 'scalar clone --no-maintenance' ' @@ -190,7 +190,7 @@ GIT_TRACE2_EVENT="$(pwd)/no-maint.event" \ GIT_TRACE2_EVENT_DEPTH=100 \ scalar clone --no-maintenance "file://$(pwd)/to-clone" no-maint 2>err && - ! grep "could not toggle maintenance" err && + test_grep ! "could not toggle maintenance" err && test_subcommand ! git maintenance unregister --force <no-maint.event '
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 4794377..fe6c261 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh
@@ -436,7 +436,7 @@ git init invalid-timezone && git -C invalid-timezone fast-import --date-format=raw-permissive <input && git -C invalid-timezone cat-file -p invalid-timezone >out && - grep "1234567890 [+]051800" out + test_grep "1234567890 [+]051800" out ' test_expect_success 'B: accept and fixup committer with no name' ' @@ -2326,7 +2326,7 @@ EOF git fast-import --allow-unsafe-features <input && - grep :1 git.marks + test_grep :1 git.marks ' test_expect_success 'R: export-marks options can be overridden by commandline options' ' @@ -2340,7 +2340,7 @@ EOF git fast-import --allow-unsafe-features \ --export-marks=cmdline-sub/other.marks <input && - grep :1 cmdline-sub/other.marks && + test_grep :1 cmdline-sub/other.marks && test_path_is_missing feature-sub ' @@ -3816,9 +3816,9 @@ sed -e s/othername/somename/ tmp >tmp2 && git fast-import --force <tmp2 2>msgs && - grep "dropping.*since it would point to itself" msgs && + test_grep "dropping.*since it would point to itself" msgs && git show-ref >refs && - ! grep refs/replace refs + test_grep ! refs/replace refs ) '
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 784d68b..a487476 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh
@@ -139,9 +139,9 @@ test $(($(test_oid hexsz) + 181)) -eq "$(git -C new cat-file -s i18n)" && # ...and for the expected translation of bytes. git -C new cat-file commit i18n >actual && - grep $(printf "\317\200") actual && + test_grep $(printf "\317\200") actual && # Also make sure the commit does not have the "encoding" header - ! grep ^encoding actual + test_grep ! ^encoding actual ' test_expect_success 'aborting on iso-8859-7' ' @@ -170,9 +170,9 @@ test $(($(test_oid hexsz) + 200)) -eq "$(git -C new cat-file -s i18n-no-recoding)" && # ...as well as the expected byte. git -C new cat-file commit i18n-no-recoding >actual && - grep $(printf "\360") actual && + test_grep $(printf "\360") actual && # Also make sure the commit has the "encoding" header - grep ^encoding actual + test_grep ^encoding actual ' test_expect_success 'encoding preserved if reencoding fails' ' @@ -186,13 +186,13 @@ git -C new cat-file commit i18n-invalid >actual && # Make sure the commit still has the encoding header - grep ^encoding actual && + test_grep ^encoding actual && # Verify that the commit has the expected size; i.e. # that no bytes were re-encoded to a different encoding. test $(($(test_oid hexsz) + 212)) -eq "$(git -C new cat-file -s i18n-invalid)" && # ...and check for the original special bytes - grep $(printf "\360") actual && - grep $(printf "\377") actual + test_grep $(printf "\360") actual && + test_grep $(printf "\377") actual ' test_expect_success 'import/export-marks' ' @@ -244,14 +244,14 @@ test_expect_success 'signed-tags=verbatim' ' git fast-export --signed-tags=verbatim sign-your-name > output && - grep PGP output + test_grep PGP output ' test_expect_success 'signed-tags=warn-verbatim' ' git fast-export --signed-tags=warn-verbatim sign-your-name >output 2>err && - grep PGP output && + test_grep PGP output && test -s err ' @@ -261,7 +261,7 @@ test_expect_success 'signed-tags=warn' ' git fast-export --signed-tags=warn sign-your-name >output 2>err && - grep PGP output && + test_grep PGP output && test -s err ' @@ -269,13 +269,13 @@ test_expect_success 'signed-tags=strip' ' git fast-export --signed-tags=strip sign-your-name > output && - ! grep PGP output + test_grep ! PGP output ' test_expect_success 'signed-tags=warn-strip' ' git fast-export --signed-tags=warn-strip sign-your-name >output 2>err && - ! grep PGP output && + test_grep ! PGP output && test -s err ' @@ -345,7 +345,7 @@ git fast-export --signed-commits=verbatim --reencode=no commit-signing >output && test_grep -E "^gpgsig $GIT_DEFAULT_HASH openpgp" output && - grep "encoding ISO-8859-1" output && + test_grep "encoding ISO-8859-1" output && git -C new fast-import <output && STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-signing) && test $COMMIT_SIGNING = $STRIPPED @@ -356,7 +356,7 @@ git fast-export --signed-commits=warn-verbatim --reencode=no commit-signing >output 2>err && test_grep -E "^gpgsig $GIT_DEFAULT_HASH openpgp" output && - grep "encoding ISO-8859-1" output && + test_grep "encoding ISO-8859-1" output && test -s err && git -C new fast-import <output && STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-signing) && @@ -367,8 +367,8 @@ test_expect_success GPG 'signed-commits=strip' ' git fast-export --signed-commits=strip --reencode=no commit-signing >output && - ! grep ^gpgsig output && - grep "^encoding ISO-8859-1" output && + test_grep ! ^gpgsig output && + test_grep "^encoding ISO-8859-1" output && sed "s/commit-signing/commit-strip-signing/" output | git -C new fast-import && STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-strip-signing) && test $COMMIT_SIGNING != $STRIPPED @@ -378,8 +378,8 @@ test_expect_success GPG 'signed-commits=warn-strip' ' git fast-export --signed-commits=warn-strip --reencode=no commit-signing >output 2>err && - ! grep ^gpgsig output && - grep "^encoding ISO-8859-1" output && + test_grep ! ^gpgsig output && + test_grep "^encoding ISO-8859-1" output && test -s err && sed "s/commit-signing/commit-strip-signing/" output | git -C new fast-import && STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-strip-signing) && @@ -406,7 +406,7 @@ test_grep -E "^gpgsig $GIT_DEFAULT_HASH x509" output && git -C new fast-import <output && git -C new cat-file commit refs/heads/x509-signing >actual && - grep "^gpgsig" actual && + test_grep "^gpgsig" actual && IMPORTED=$(git -C new rev-parse refs/heads/x509-signing) && test $X509_COMMIT = $IMPORTED @@ -431,7 +431,7 @@ test_grep -E "^gpgsig $GIT_DEFAULT_HASH ssh" output && git -C new fast-import <output && git -C new cat-file commit refs/heads/ssh-signing >actual && - grep "^gpgsig" actual && + test_grep "^gpgsig" actual && IMPORTED=$(git -C new rev-parse refs/heads/ssh-signing) && test $SSH_COMMIT = $IMPORTED @@ -520,7 +520,7 @@ mkdir new && git --git-dir=new/.git init && git fast-export -C -C --signed-tags=strip --all > output && - grep "^C file2 file4\$" output && + test_grep "^C file2 file4\$" output && git -C new fast-import <output && test $ENTRY = $(git -C new rev-parse --verify refs/heads/copy) @@ -548,11 +548,11 @@ ANNOTATED_TAG_COUNT=$((ANNOTATED_TAG_COUNT + 1)) && git fast-export -C -C --signed-tags=strip --all > output && test $(grep -c "^tag " output) = $ANNOTATED_TAG_COUNT && - ! grep "Unspecified Tagger" output && + test_grep ! "Unspecified Tagger" output && git fast-export -C -C --signed-tags=strip --all \ --fake-missing-tagger > output && test $(grep -c "^tag " output) = $ANNOTATED_TAG_COUNT && - grep "Unspecified Tagger" output + test_grep "Unspecified Tagger" output ' @@ -628,7 +628,7 @@ test_commit bar && git fast-export --tag-of-filtered-object=rewrite --all -- bar.t >output && - grep from.$ZERO_OID output + test_grep from.$ZERO_OID output ) ' @@ -670,7 +670,7 @@ test_tick && git commit -mnext file && git fast-export --import-marks=marks simple -- file file0 >actual && - grep file0 actual + test_grep file0 actual ' test_expect_success 'path limiting works' ' @@ -753,7 +753,7 @@ test_expect_success 'handling nested tags' ' git tag -a -m "This is a nested tag" nested muss && git fast-export --mark-tags nested >output && - grep "^from $ZERO_OID$" output && + test_grep "^from $ZERO_OID$" output && grep "^tag nested$" output >tag_lines && test_line_count = 2 tag_lines ' @@ -926,7 +926,7 @@ echo ":1 $(git rev-parse HEAD^^)" >marks && git fast-export --import-marks=marks main >out && - grep Yeah out + test_grep Yeah out ) '
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh index 156a647..ed784a5 100755 --- a/t/t9351-fast-export-anonymize.sh +++ b/t/t9351-fast-export-anonymize.sh
@@ -33,44 +33,44 @@ # this also covers commit messages test_expect_success 'stream omits path names' ' - ! grep base stream && - ! grep foo stream && - ! grep subdir stream && - ! grep bar stream && - ! grep xyzzy stream + test_grep ! base stream && + test_grep ! foo stream && + test_grep ! subdir stream && + test_grep ! bar stream && + test_grep ! xyzzy stream ' test_expect_success 'stream contains user-specified names' ' - grep retain-me stream && - ! grep should-not-appear stream && - grep custom-name stream + test_grep retain-me stream && + test_grep ! should-not-appear stream && + test_grep custom-name stream ' test_expect_success 'stream omits gitlink oids' ' # avoid relying on the whole oid to remain hash-agnostic; this is # plenty to be unique within our test case - ! grep a000000000000000000 stream + test_grep ! a000000000000000000 stream ' test_expect_success 'stream retains other as refname' ' - grep other stream + test_grep other stream ' test_expect_success 'stream omits other refnames' ' - ! grep main stream && - ! grep mytag stream && - ! grep longtag stream + test_grep ! main stream && + test_grep ! mytag stream && + test_grep ! longtag stream ' test_expect_success 'stream omits identities' ' - ! grep "$GIT_COMMITTER_NAME" stream && - ! grep "$GIT_COMMITTER_EMAIL" stream && - ! grep "$GIT_AUTHOR_NAME" stream && - ! grep "$GIT_AUTHOR_EMAIL" stream + test_grep ! "$GIT_COMMITTER_NAME" stream && + test_grep ! "$GIT_COMMITTER_EMAIL" stream && + test_grep ! "$GIT_AUTHOR_NAME" stream && + test_grep ! "$GIT_AUTHOR_EMAIL" stream ' test_expect_success 'stream omits tag message' ' - ! grep "annotated tag" stream + test_grep ! "annotated tag" stream ' # NOTE: we chdir to the new, anonymized repository
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 4b45398..b2379e4 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh
@@ -254,7 +254,7 @@ else true fi && - grep "GITCVS emulation disabled" cvs.log && + test_grep "GITCVS emulation disabled" cvs.log && test ! -d cvswork2' rm -fr cvswork2 @@ -276,7 +276,7 @@ else true fi && - grep "GITCVS emulation disabled" cvs.log && + test_grep "GITCVS emulation disabled" cvs.log && test ! -d cvswork2 '
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh index 32814e7..1093fda 100755 --- a/t/t9501-gitweb-standalone-http-status.sh +++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -37,13 +37,13 @@ test_expect_success \ 'snapshots: tgz only default format enabled' \ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" && - grep "Status: 200 OK" gitweb.output && + test_grep "Status: 200 OK" gitweb.output && gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" && - grep "403 - Unsupported snapshot format" gitweb.output && + test_grep "403 - Unsupported snapshot format" gitweb.output && gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" && - grep "403 - Snapshot format not allowed" gitweb.output && + test_grep "403 - Snapshot format not allowed" gitweb.output && gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" && - grep "403 - Unsupported snapshot format" gitweb.output' + test_grep "403 - Unsupported snapshot format" gitweb.output' cat >>gitweb_config.perl <<\EOF @@ -53,13 +53,13 @@ test_expect_success \ 'snapshots: all enabled in default, use default disabled value' \ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" && - grep "Status: 200 OK" gitweb.output && + test_grep "Status: 200 OK" gitweb.output && gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" && - grep "Status: 200 OK" gitweb.output && + test_grep "Status: 200 OK" gitweb.output && gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" && - grep "403 - Snapshot format not allowed" gitweb.output && + test_grep "403 - Snapshot format not allowed" gitweb.output && gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" && - grep "Status: 200 OK" gitweb.output' + test_grep "Status: 200 OK" gitweb.output' cat >>gitweb_config.perl <<\EOF @@ -69,7 +69,7 @@ test_expect_success \ 'snapshots: zip explicitly disabled' \ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" && - grep "403 - Snapshot format not allowed" gitweb.output' + test_grep "403 - Snapshot format not allowed" gitweb.output' test_debug 'cat gitweb.output' @@ -80,7 +80,7 @@ test_expect_success \ 'snapshots: tgz explicitly enabled' \ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" && - grep "Status: 200 OK" gitweb.output' + test_grep "Status: 200 OK" gitweb.output' test_debug 'cat gitweb.headers' @@ -89,13 +89,13 @@ test_expect_success 'snapshots: good tree-ish id' ' gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" && - grep "Status: 200 OK" gitweb.output + test_grep "Status: 200 OK" gitweb.output ' test_debug 'cat gitweb.headers' test_expect_success 'snapshots: bad tree-ish id' ' gitweb_run "p=.git;a=snapshot;h=frizzumFrazzum;sf=tgz" && - grep "404 - Object does not exist" gitweb.output + test_grep "404 - Object does not exist" gitweb.output ' test_debug 'cat gitweb.output' @@ -105,20 +105,20 @@ test_tick && git commit -m "Object to be tagged" && git tag tagged-object $(git hash-object tag-object) && gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" && - grep "400 - Object is not a tree-ish" gitweb.output + test_grep "400 - Object is not a tree-ish" gitweb.output ' test_debug 'cat gitweb.output' test_expect_success 'snapshots: good object id' ' ID=$(git rev-parse --verify HEAD) && gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" && - grep "Status: 200 OK" gitweb.output + test_grep "Status: 200 OK" gitweb.output ' test_debug 'cat gitweb.headers' test_expect_success 'snapshots: bad object id' ' gitweb_run "p=.git;a=snapshot;h=abcdef01234;sf=tgz" && - grep "404 - Object does not exist" gitweb.output + test_grep "404 - Object does not exist" gitweb.output ' test_debug 'cat gitweb.output' @@ -127,8 +127,8 @@ test_expect_success DATE_PARSER 'modification: feed last-modified' ' gitweb_run "p=.git;a=atom;h=main" && - grep "Status: 200 OK" gitweb.headers && - grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers + test_grep "Status: 200 OK" gitweb.headers && + test_grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -137,7 +137,7 @@ export HTTP_IF_MODIFIED_SINCE && test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && gitweb_run "p=.git;a=atom;h=main" && - grep "Status: 200 OK" gitweb.headers + test_grep "Status: 200 OK" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -146,14 +146,14 @@ export HTTP_IF_MODIFIED_SINCE && test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && gitweb_run "p=.git;a=atom;h=main" && - grep "Status: 304 Not Modified" gitweb.headers + test_grep "Status: 304 Not Modified" gitweb.headers ' test_debug 'cat gitweb.headers' test_expect_success DATE_PARSER 'modification: snapshot last-modified' ' gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" && - grep "Status: 200 OK" gitweb.headers && - grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers + test_grep "Status: 200 OK" gitweb.headers && + test_grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -162,7 +162,7 @@ export HTTP_IF_MODIFIED_SINCE && test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" && - grep "Status: 200 OK" gitweb.headers + test_grep "Status: 200 OK" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -171,7 +171,7 @@ export HTTP_IF_MODIFIED_SINCE && test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" && - grep "Status: 304 Not Modified" gitweb.headers + test_grep "Status: 304 Not Modified" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -181,8 +181,8 @@ export HTTP_IF_MODIFIED_SINCE && test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" && - grep "Status: 200 OK" gitweb.headers && - ! grep -i "last-modified" gitweb.headers + test_grep "Status: 200 OK" gitweb.headers && + test_grep ! -i "last-modified" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -196,8 +196,8 @@ test_expect_success 'load checking: load too high (default action)' ' gitweb_run "p=.git" && - grep "Status: 503 Service Unavailable" gitweb.headers && - grep "503 - The load average on the server is too high" gitweb.body + test_grep "Status: 503 Service Unavailable" gitweb.headers && + test_grep "503 - The load average on the server is too high" gitweb.body ' test_debug 'cat gitweb.headers' @@ -212,8 +212,8 @@ test_expect_success 'invalid arguments: invalid regexp (in project search)' ' gitweb_run "a=project_list;s=*\.git;sr=1" && - grep "Status: 400" gitweb.headers && - grep "400 - Invalid.*regexp" gitweb.body + test_grep "Status: 400" gitweb.headers && + test_grep "400 - Invalid.*regexp" gitweb.body ' test_debug 'cat gitweb.headers'
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh index 81d5625..9587ce2 100755 --- a/t/t9502-gitweb-standalone-parse-output.sh +++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -111,7 +111,7 @@ test_expect_success 'snapshot: hierarchical branch name (xx/test)' ' gitweb_run "p=.git;a=snapshot;h=xx/test;sf=tar" && - ! grep "filename=.*/" gitweb.headers + test_grep ! "filename=.*/" gitweb.headers ' test_debug 'cat gitweb.headers' @@ -139,13 +139,13 @@ test_expect_success 'forks: not skipped unless "forks" feature enabled' ' gitweb_run "a=project_list" && - grep -q ">\\.git<" gitweb.body && - grep -q ">foo\\.git<" gitweb.body && - grep -q ">foo_baz\\.git<" gitweb.body && - grep -q ">foo\\.bar\\.git<" gitweb.body && - grep -q ">foo_baz\\.git<" gitweb.body && - grep -q ">foo/foo-forked\\.git<" gitweb.body && - grep -q ">fork of .*<" gitweb.body + test_grep -q ">\\.git<" gitweb.body && + test_grep -q ">foo\\.git<" gitweb.body && + test_grep -q ">foo_baz\\.git<" gitweb.body && + test_grep -q ">foo\\.bar\\.git<" gitweb.body && + test_grep -q ">foo_baz\\.git<" gitweb.body && + test_grep -q ">foo/foo-forked\\.git<" gitweb.body && + test_grep -q ">fork of .*<" gitweb.body ' test_expect_success 'enable forks feature' ' @@ -156,25 +156,25 @@ test_expect_success 'forks: forks skipped if "forks" feature enabled' ' gitweb_run "a=project_list" && - grep -q ">\\.git<" gitweb.body && - grep -q ">foo\\.git<" gitweb.body && - grep -q ">foo_baz\\.git<" gitweb.body && - grep -q ">foo\\.bar\\.git<" gitweb.body && - grep -q ">foo_baz\\.git<" gitweb.body && - grep -v ">foo/foo-forked\\.git<" gitweb.body && - grep -v ">fork of .*<" gitweb.body + test_grep -q ">\\.git<" gitweb.body && + test_grep -q ">foo\\.git<" gitweb.body && + test_grep -q ">foo_baz\\.git<" gitweb.body && + test_grep -q ">foo\\.bar\\.git<" gitweb.body && + test_grep -q ">foo_baz\\.git<" gitweb.body && + test_grep -v ">foo/foo-forked\\.git<" gitweb.body && + test_grep -v ">fork of .*<" gitweb.body ' test_expect_success 'forks: "forks" action for forked repository' ' gitweb_run "p=foo.git;a=forks" && - grep -q ">foo/foo-forked\\.git<" gitweb.body && - grep -q ">fork of foo<" gitweb.body + test_grep -q ">foo/foo-forked\\.git<" gitweb.body && + test_grep -q ">fork of foo<" gitweb.body ' test_expect_success 'forks: can access forked repository' ' gitweb_run "p=foo/foo-forked.git;a=summary" && - grep -q "200 OK" gitweb.headers && - grep -q ">fork of foo<" gitweb.body + test_grep -q "200 OK" gitweb.headers && + test_grep -q ">fork of foo<" gitweb.body ' test_expect_success 'forks: project_index lists all projects (incl. forks)' '
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 0816763..140a3a7 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh
@@ -35,7 +35,7 @@ test_expect_success 'depot typo error' ' test_must_fail git p4 clone --dest="$git" /depot 2>errs && - grep "Depot paths must start with" errs + test_grep "Depot paths must start with" errs ' test_expect_success 'git p4 clone @all' ' @@ -356,7 +356,7 @@ P4PORT=nosuchhost:65537 && export P4PORT && test_expect_code 1 git p4 sync >out 2>err && - grep "connect to nosuchhost" err + test_grep "connect to nosuchhost" err ) ' @@ -374,7 +374,7 @@ git commit -m "add hello.txt" && git config git-p4.skipSubmitEdit true && git p4 submit --dry-run >out && - grep "Would apply" out + test_grep "Would apply" out ) && test_hook -C "$git" p4-pre-submit <<-\EOF && exit 0 @@ -382,7 +382,7 @@ ( cd "$git" && git p4 submit --dry-run >out && - grep "Would apply" out + test_grep "Would apply" out ) && test_hook -C "$git" --clobber p4-pre-submit <<-\EOF && exit 1 @@ -390,7 +390,7 @@ ( cd "$git" && test_must_fail git p4 submit --dry-run >errs 2>&1 && - ! grep "Would apply" errs + test_grep ! "Would apply" errs ) '
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index c598011..9912b48 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh
@@ -216,17 +216,17 @@ test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch2 && test_path_is_file file1 && test_path_is_file file2 && test ! -f file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch3 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - grep update file2 && + test_grep update file2 && cd "$cli" && cd branch1 && p4 edit file2 && @@ -235,7 +235,7 @@ cd "$git" && git reset --hard p4/depot/branch1 && git p4 rebase && - grep file2_ file2 + test_grep file2_ file2 ) ' @@ -292,29 +292,29 @@ test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch2 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_missing file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch3 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch4 && git diff-tree --quiet HEAD && test_path_is_file file1 && test_path_is_file file2 && test_path_is_missing file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch5 && git diff-tree --quiet HEAD && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - ! grep update file2 && + test_grep ! update file2 && test_must_fail git show-ref --verify refs/git-p4-tmp ) ' @@ -346,27 +346,27 @@ test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch2 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_missing file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch3 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_missing file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch4 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch5 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - ! grep update file2 && + test_grep ! update file2 && test_must_fail git show-ref --verify refs/git-p4-tmp ) ' @@ -398,27 +398,27 @@ test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch2 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_missing file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch3 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_missing file3 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch4 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch5 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch6 && test_path_is_file file1 && test_path_is_missing file2 && @@ -622,19 +622,19 @@ test_path_is_file file2 && test_path_is_file file3 && test_path_is_file sub_file1 && - grep update file2 && + test_grep update file2 && git reset --hard p4/depot/branch2 && test_path_is_file file1 && test_path_is_file file2 && test ! -f file3 && test_path_is_file sub_file1 && - ! grep update file2 && + test_grep ! update file2 && git reset --hard p4/depot/branch3 && test_path_is_file file1 && test_path_is_file file2 && test_path_is_file file3 && test_path_is_file sub_file1 && - grep update file2 && + test_grep update file2 && cd "$cli" && cd branch1 && p4 edit file2 && @@ -643,7 +643,7 @@ cd "$git" && git reset --hard p4/depot/branch1 && git p4 rebase && - grep file2_ file2 + test_grep file2_ file2 ) ' @@ -661,7 +661,7 @@ cd "$cli" && p4 sync ... && cd branch1 && - grep "client spec" file1 + test_grep "client spec" file1 ) '
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index c26d297..dce710e 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh
@@ -49,7 +49,7 @@ ( cd "$git" && test_must_fail git p4 sync 2>err && - grep "Error: no branch refs/remotes/p4/master" err + test_grep "Error: no branch refs/remotes/p4/master" err ) ' @@ -85,9 +85,9 @@ git p4 sync --branch=refs/remotes/p4/b2 //depot@2 && git p4 sync && git show -s --format=%s refs/remotes/p4/b1 >show && - grep "Initial import" show && + test_grep "Initial import" show && git show -s --format=%s refs/remotes/p4/b2 >show && - grep "Initial import" show + test_grep "Initial import" show ) ' @@ -100,9 +100,9 @@ git p4 sync --branch=b2 //depot@2 && git p4 sync --branch=b2 && git show -s --format=%s refs/remotes/p4/b1 >show && - grep "Initial import" show && + test_grep "Initial import" show && git show -s --format=%s refs/remotes/p4/b2 >show && - grep "change 3" show + test_grep "change 3" show ) '
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index 6ae7ced..adbdf93 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh
@@ -542,7 +542,7 @@ cd "$cli" && change=$(last_shelve) && p4 unshelve -c $change -s $change && - grep -q updated-line shelf.t && + test_grep -q updated-line shelf.t && p4 describe -S $change | grep added-file.t && test_path_is_missing shelved-change-1.t && p4 revert ...
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index 5fe8331..70355a3 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh
@@ -299,12 +299,12 @@ echo "\$Revision\$" >kwdelfile.c && p4 add -t ktext kwdelfile.c && p4 submit -d "Add file to be deleted" && - grep 1 kwdelfile.c + test_grep 1 kwdelfile.c ) && git p4 clone --dest="$git" //depot && ( cd "$git" && - grep Revision kwdelfile.c && + test_grep Revision kwdelfile.c && git rm -f kwdelfile.c && git commit -m "Delete a file containing RCS keywords" && git config git-p4.skipSubmitEdit true && @@ -336,7 +336,7 @@ cd "$cli" && p4 sync && test -f kwfile1.c && - grep "NewKW.*Revision.*[0-9]" kwfile1.c + test_grep "NewKW.*Revision.*[0-9]" kwfile1.c ) ' @@ -359,7 +359,7 @@ ( cd "$cli" && p4 sync && - grep "NewKW2.*Revision.*[0-9]" kwfile1.c + test_grep "NewKW2.*Revision.*[0-9]" kwfile1.c ) '
diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh index fd018c8..838ddd3 100755 --- a/t/t9813-git-p4-preserve-users.sh +++ b/t/t9813-git-p4-preserve-users.sh
@@ -119,20 +119,20 @@ P4EDITOR=cat P4USER=alice P4PASSWD=secret && export P4EDITOR P4USER P4PASSWD && git p4 commit >actual && - grep "git author derek@example.com does not match" actual && + test_grep "git author derek@example.com does not match" actual && make_change_by_user usernamefile3 Charlie charlie@example.com && git p4 commit >actual && - grep "git author charlie@example.com does not match" actual && + test_grep "git author charlie@example.com does not match" actual && make_change_by_user usernamefile3 alice alice@example.com && git p4 commit >actual && - ! grep "git author.*does not match" actual && + test_grep ! "git author.*does not match" actual && git config git-p4.skipUserNameCheck true && make_change_by_user usernamefile3 Charlie charlie@example.com && git p4 commit >actual && - ! grep "git author.*does not match" actual && + test_grep ! "git author.*does not match" actual && p4_check_commit_author usernamefile3 alice )
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh index 2a9838f..6c5ecb9 100755 --- a/t/t9814-git-p4-rename.sh +++ b/t/t9814-git-p4-rename.sh
@@ -66,7 +66,7 @@ git diff-tree -r -M HEAD && git p4 submit && p4 filelog //depot/file4 >filelog && - ! grep " from //depot" filelog && + test_grep ! " from //depot" filelog && git mv file4 file5 && git commit -a -m "Rename file4 to file5" && @@ -74,7 +74,7 @@ git config git-p4.detectRenames true && git p4 submit && p4 filelog //depot/file5 >filelog && - grep " from //depot/file4" filelog && + test_grep " from //depot/file4" filelog && git mv file5 file6 && echo update >>file6 && @@ -86,7 +86,7 @@ git config git-p4.detectRenames $(($level + 2)) && git p4 submit && p4 filelog //depot/file6 >filelog && - ! grep " from //depot" filelog && + test_grep ! " from //depot" filelog && git mv file6 file7 && echo update >>file7 && @@ -98,7 +98,7 @@ git config git-p4.detectRenames $(($level - 2)) && git p4 submit && p4 filelog //depot/file7 >filelog && - grep " from //depot/file6" filelog + test_grep " from //depot/file6" filelog ) '
diff --git a/t/t9827-git-p4-change-filetype.sh b/t/t9827-git-p4-change-filetype.sh index d3670bd..9a8231a 100755 --- a/t/t9827-git-p4-change-filetype.sh +++ b/t/t9827-git-p4-change-filetype.sh
@@ -36,7 +36,7 @@ git commit -m "symlink file1 to file2" && git p4 submit && p4 filelog -m 1 //depot/file2 >filelog && - grep "(symlink)" filelog + test_grep "(symlink)" filelog ) ' @@ -55,7 +55,7 @@ git commit -m "re-write file2" && git p4 submit && p4 filelog -m 1 //depot/file2 >filelog && - grep "(text)" filelog + test_grep "(text)" filelog ) '
diff --git a/t/t9832-unshelve.sh b/t/t9832-unshelve.sh index 6b3cb04..f85a60a 100755 --- a/t/t9832-unshelve.sh +++ b/t/t9832-unshelve.sh
@@ -69,7 +69,7 @@ change=$(last_shelved_change) && git p4 unshelve $change && git show refs/remotes/p4-unshelved/$change >actual && - grep -q "Further description" actual && + test_grep -q "Further description" actual && git cherry-pick refs/remotes/p4-unshelved/$change && test_path_is_file file2 && test_cmp file1 "$cli"/file1 && @@ -141,7 +141,7 @@ shelve_one_file //depot/file1 && change=$(last_shelved_change) && p4 describe -S $change >out.txt && - grep -q "Change to be unshelved" out.txt + test_grep -q "Change to be unshelved" out.txt ) ' @@ -152,7 +152,7 @@ change=$(last_shelved_change) && cd "$git" && git p4 unshelve $change >out.txt && - grep -q "unshelved changelist $change" out.txt + test_grep -q "unshelved changelist $change" out.txt ) '
diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh index e22369c..b312669 100755 --- a/t/t9833-errors.sh +++ b/t/t9833-errors.sh
@@ -29,7 +29,7 @@ P4PASSWD=badpassword && export P4PASSWD && test_must_fail git p4 clone //depot/foo 2>errmsg && - grep -q "failure accessing depot.*P4PASSWD" errmsg + test_grep -q "failure accessing depot.*P4PASSWD" errmsg ) ' @@ -41,7 +41,7 @@ test_commit "ticket-auth-check" && p4 logout && test_must_fail git p4 submit 2>errmsg && - grep -q "failure accessing depot" errmsg + test_grep -q "failure accessing depot" errmsg ) '
diff --git a/t/t9835-git-p4-metadata-encoding-python2.sh b/t/t9835-git-p4-metadata-encoding-python2.sh index b969c7e..7eb0c8e 100755 --- a/t/t9835-git-p4-metadata-encoding-python2.sh +++ b/t/t9835-git-p4-metadata-encoding-python2.sh
@@ -84,7 +84,7 @@ test_when_finished cleanup_git && test_when_finished remove_user_cache && test_must_fail git -c git-p4.metadataDecodingStrategy=strict p4-python2 clone --dest="$git" //depot@all 2>err && - grep "Decoding perforce metadata failed!" err + test_grep "Decoding perforce metadata failed!" err ' test_expect_success 'check utf-8 contents with passthrough strategy' ' @@ -94,8 +94,8 @@ ( cd "$git" && git log >actual && - grep "some utf-8 tǣxt" actual && - grep "ǣuthor" actual + test_grep "some utf-8 tǣxt" actual && + test_grep "ǣuthor" actual ) ' @@ -107,9 +107,9 @@ cd "$git" && git log >actual && badly_encoded_in_git=$(echo "some latin-1 tæxt" | iconv -f utf8 -t latin1) && - grep "$badly_encoded_in_git" actual && + test_grep "$badly_encoded_in_git" actual && bad_author_in_git="$(echo æuthor | iconv -f utf8 -t latin1)" && - grep "$bad_author_in_git" actual + test_grep "$bad_author_in_git" actual ) ' @@ -120,8 +120,8 @@ ( cd "$git" && git log >actual && - grep "some utf-8 tǣxt" actual && - grep "ǣuthor" actual + test_grep "some utf-8 tǣxt" actual && + test_grep "ǣuthor" actual ) ' @@ -132,8 +132,8 @@ ( cd "$git" && git log >actual && - grep "some latin-1 tæxt" actual && - grep "æuthor" actual + test_grep "some latin-1 tæxt" actual && + test_grep "æuthor" actual ) ' @@ -144,8 +144,8 @@ ( cd "$git" && git log >actual && - grep "sœme cp-1252 tæxt" actual && - grep "æuthœr" actual + test_grep "sœme cp-1252 tæxt" actual && + test_grep "æuthœr" actual ) ' @@ -156,8 +156,8 @@ ( cd "$git" && git log >actual && - grep "hÅs some cp850 text" actual && - grep "Åuthor" actual + test_grep "hÅs some cp850 text" actual && + test_grep "Åuthor" actual ) ' @@ -168,8 +168,8 @@ ( cd "$git" && git log >actual && - grep "h%8Fs some cp850 text" actual && - grep "%8Futhor" actual + test_grep "h%8Fs some cp850 text" actual && + test_grep "%8Futhor" actual ) ' @@ -191,8 +191,8 @@ git p4-python2 sync --branch=master && git log p4/master >actual && - grep "sœme more cp-1252 tæxt" actual && - grep "æuthœr" actual + test_grep "sœme more cp-1252 tæxt" actual && + test_grep "æuthœr" actual ) ' @@ -208,7 +208,7 @@ cd "$git" && git log >actual && badly_encoded_in_git=$(echo "some latin-1 tæxt" | iconv -f utf8 -t latin1) && - grep "$badly_encoded_in_git" actual + test_grep "$badly_encoded_in_git" actual ) '
diff --git a/t/t9836-git-p4-metadata-encoding-python3.sh b/t/t9836-git-p4-metadata-encoding-python3.sh index da6669b..1611849 100755 --- a/t/t9836-git-p4-metadata-encoding-python3.sh +++ b/t/t9836-git-p4-metadata-encoding-python3.sh
@@ -84,7 +84,7 @@ test_when_finished cleanup_git && test_when_finished remove_user_cache && test_must_fail git -c git-p4.metadataDecodingStrategy=strict p4-python3 clone --dest="$git" //depot@all 2>err && - grep "Decoding perforce metadata failed!" err + test_grep "Decoding perforce metadata failed!" err ' test_expect_success 'check utf-8 contents with passthrough strategy' ' @@ -94,8 +94,8 @@ ( cd "$git" && git log >actual && - grep "some utf-8 tǣxt" actual && - grep "ǣuthor" actual + test_grep "some utf-8 tǣxt" actual && + test_grep "ǣuthor" actual ) ' @@ -107,9 +107,9 @@ cd "$git" && git log >actual && badly_encoded_in_git=$(echo "some latin-1 tæxt" | iconv -f utf8 -t latin1) && - grep "$badly_encoded_in_git" actual && + test_grep "$badly_encoded_in_git" actual && bad_author_in_git="$(echo æuthor | iconv -f utf8 -t latin1)" && - grep "$bad_author_in_git" actual + test_grep "$bad_author_in_git" actual ) ' @@ -120,8 +120,8 @@ ( cd "$git" && git log >actual && - grep "some utf-8 tǣxt" actual && - grep "ǣuthor" actual + test_grep "some utf-8 tǣxt" actual && + test_grep "ǣuthor" actual ) ' @@ -132,8 +132,8 @@ ( cd "$git" && git log >actual && - grep "some latin-1 tæxt" actual && - grep "æuthor" actual + test_grep "some latin-1 tæxt" actual && + test_grep "æuthor" actual ) ' @@ -144,8 +144,8 @@ ( cd "$git" && git log >actual && - grep "sœme cp-1252 tæxt" actual && - grep "æuthœr" actual + test_grep "sœme cp-1252 tæxt" actual && + test_grep "æuthœr" actual ) ' @@ -156,8 +156,8 @@ ( cd "$git" && git log >actual && - grep "hÅs some cp850 text" actual && - grep "Åuthor" actual + test_grep "hÅs some cp850 text" actual && + test_grep "Åuthor" actual ) ' @@ -168,8 +168,8 @@ ( cd "$git" && git log >actual && - grep "h%8Fs some cp850 text" actual && - grep "%8Futhor" actual + test_grep "h%8Fs some cp850 text" actual && + test_grep "%8Futhor" actual ) ' @@ -191,8 +191,8 @@ git p4-python3 sync --branch=master && git log p4/master >actual && - grep "sœme more cp-1252 tæxt" actual && - grep "æuthœr" actual + test_grep "sœme more cp-1252 tæxt" actual && + test_grep "æuthœr" actual ) ' @@ -208,8 +208,8 @@ ( cd "$git" && git log >actual && - grep "sœme cp-1252 tæxt" actual && - grep "æuthœr" actual + test_grep "sœme cp-1252 tæxt" actual && + test_grep "æuthœr" actual ) '
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh index 21c3af4..27cc49b 100755 --- a/t/t9850-shell.sh +++ b/t/t9850-shell.sh
@@ -32,7 +32,7 @@ test_expect_success 'shell complains of overlong commands' ' test-tool genzeros | tr "\000" "a" | test_must_fail git shell 2>err && - grep "too long" err + test_grep "too long" err ' test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 55dc9ea..9ae3c48 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh
@@ -1381,7 +1381,7 @@ test_when_finished "git worktree remove other_wt" && git worktree add --orphan other_wt && run_completion "git worktree remove " && - grep other_wt out + test_grep other_wt out ' test_expect_success '__git_complete_worktree_paths - not a git repository' ' @@ -1397,7 +1397,7 @@ test_when_finished "git -C otherrepo worktree remove otherrepo_wt" && git -C otherrepo worktree add --orphan otherrepo_wt && run_completion "git -C otherrepo worktree remove " && - grep otherrepo_wt out + test_grep otherrepo_wt out ' test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' ' @@ -2554,14 +2554,14 @@ test_expect_success 'basic' ' run_completion "git " && # built-in - grep -q "^add \$" out && + test_grep -q "^add \$" out && # script - grep -q "^rebase \$" out && + test_grep -q "^rebase \$" out && # plumbing - ! grep -q "^ls-files \$" out && + test_grep ! -q "^ls-files \$" out && run_completion "git r" && - ! grep -q -v "^r" out + test_grep ! -q -v "^r" out ' test_expect_success 'double dash "git" itself' ' @@ -2656,7 +2656,7 @@ test_expect_success 'completion.commands removes multiple commands' ' test_config completion.commands "-cherry -mergetool" && git --list-cmds=list-mainporcelain,list-complete,config >out && - ! grep -E "^(cherry|mergetool)$" out + test_grep ! -E "^(cherry|mergetool)$" out ' test_expect_success 'setup for integration tests' ' @@ -3179,8 +3179,8 @@ # Just mainporcelain, not plumbing commands run_completion "git c" && - grep checkout out && - ! grep cat-file out + test_grep checkout out && + test_grep ! cat-file out ) ' @@ -3193,13 +3193,13 @@ # Both mainporcelain and plumbing commands run_completion "git c" && - grep checkout out && - grep cat-file out && + test_grep checkout out && + test_grep cat-file out && # Check "gitk", a "main" command, but not a built-in + more plumbing run_completion "git g" && - grep gitk out && - grep get-tar-commit-id out + test_grep gitk out && + test_grep get-tar-commit-id out ) '
diff --git a/t/test-lib.sh b/t/test-lib.sh index ceefb99..1f0505e 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh
@@ -210,7 +210,7 @@ -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t ;; -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests) - GIT_TEST_LONG=t; export GIT_TEST_LONG ;; + GIT_TEST_LONG=true; export GIT_TEST_LONG ;; -r) mark_option_requires_arg "$opt" run_list ;; @@ -1217,14 +1217,14 @@ then return fi && - say_color error "$(cat "$TEST_RESULTS_SAN_FILE".*)" && + say_color >&4 error "$(cat "$TEST_RESULTS_SAN_FILE".*)" && if test "$test_failure" = 0 then - say "Our logs revealed a memory leak, exit non-zero!" && + say >&4 "Our logs revealed a memory leak, exit non-zero!" && invert_exit_code=t else - say "Our logs revealed a memory leak..." + say >&4 "Our logs revealed a memory leak..." fi } @@ -1849,7 +1849,7 @@ ' test_lazy_prereq EXPENSIVE ' - test -n "$GIT_TEST_LONG" + test_bool_env GIT_TEST_LONG false ' test_lazy_prereq EXPENSIVE_ON_WINDOWS '
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c index fdb5b11..19a3ac8 100644 --- a/t/unit-tests/lib-reftable.c +++ b/t/unit-tests/lib-reftable.c
@@ -25,11 +25,12 @@ static int strbuf_writer_flush(void *arg UNUSED) } struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf, + enum reftable_hash hash_id, struct reftable_write_options *opts) { struct reftable_writer *writer; int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush, - buf, opts); + buf, hash_id, opts); cl_assert(!ret); return writer; } @@ -39,6 +40,7 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf, size_t nrefs, struct reftable_log_record *logs, size_t nlogs, + enum reftable_hash hash_id, struct reftable_write_options *_opts) { struct reftable_write_options opts = { 0 }; @@ -66,7 +68,7 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf, min = ui; } - writer = cl_reftable_strbuf_writer(buf, &opts); + writer = cl_reftable_strbuf_writer(buf, hash_id, &opts); ret = reftable_writer_set_limits(writer, min, max); cl_assert(!ret); @@ -88,7 +90,7 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf, size_t off = i * (opts.block_size ? opts.block_size : DEFAULT_BLOCK_SIZE); if (!off) - off = header_size(opts.hash_id == REFTABLE_HASH_SHA256 ? 2 : 1); + off = header_size(hash_id == REFTABLE_HASH_SHA256 ? 2 : 1); cl_assert(buf->buf[off] == 'r'); }
diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h index d7e6d31..caf443d 100644 --- a/t/unit-tests/lib-reftable.h +++ b/t/unit-tests/lib-reftable.h
@@ -10,6 +10,7 @@ struct reftable_buf; void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id); struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf, + enum reftable_hash hash_id, struct reftable_write_options *opts); void cl_reftable_write_to_buf(struct reftable_buf *buf, @@ -17,4 +18,5 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf, size_t nrecords, struct reftable_log_record *logs, size_t nlogs, + enum reftable_hash hash_id, struct reftable_write_options *opts);
diff --git a/t/unit-tests/u-hash.c b/t/unit-tests/u-hash.c index bd4ac6a..19f4efd 100644 --- a/t/unit-tests/u-hash.c +++ b/t/unit-tests/u-hash.c
@@ -12,7 +12,7 @@ static void check_hash_data(const void *data, size_t data_length, unsigned char hash[GIT_MAX_HEXSZ]; const struct git_hash_algo *algop = &hash_algos[i]; - algop->init_fn(&ctx); + git_hash_init(&ctx, algop); git_hash_update(&ctx, data, data_length); git_hash_final(hash, &ctx);
diff --git a/t/unit-tests/u-prio-queue.c b/t/unit-tests/u-prio-queue.c index 63e5811..af3e0b8 100644 --- a/t/unit-tests/u-prio-queue.c +++ b/t/unit-tests/u-prio-queue.c
@@ -53,13 +53,13 @@ static void test_prio_queue(int *input, size_t input_size, prio_queue_reverse(&pq); break; case REPLACE: - peek = prio_queue_peek(&pq); + get = prio_queue_get(&pq); cl_assert(i + 1 < input_size); cl_assert(input[i + 1] >= 0); cl_assert(j < result_size); - cl_assert_equal_i(result[j], show(peek)); + cl_assert_equal_i(result[j], show(get)); j++; - prio_queue_replace(&pq, &input[++i]); + prio_queue_put(&pq, &input[++i]); break; default: prio_queue_put(&pq, &input[i]);
diff --git a/t/unit-tests/u-reftable-basics.c b/t/unit-tests/u-reftable-basics.c index 73566ed..c5d83b6 100644 --- a/t/unit-tests/u-reftable-basics.c +++ b/t/unit-tests/u-reftable-basics.c
@@ -60,6 +60,17 @@ void test_reftable_basics__binsearch(void) } } +static int unreachable_lesseq(size_t i UNUSED, void *args UNUSED) +{ + cl_fail("comparison function called for empty range"); + return 0; +} + +void test_reftable_basics__binsearch_empty(void) +{ + cl_assert_equal_i(binsearch(0, &unreachable_lesseq, NULL), 0); +} + void test_reftable_basics__names_length(void) { const char *a[] = { "a", "b", NULL };
diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c index f4bded7..99b155d 100644 --- a/t/unit-tests/u-reftable-block.c +++ b/t/unit-tests/u-reftable-block.c
@@ -14,6 +14,31 @@ license that can be found in the LICENSE file or at #include "reftable/reftable-error.h" #include "strbuf.h" +static int cl_reftable_write_block(struct reftable_buf *buf, + uint8_t block_type, + struct reftable_record *recs, + size_t nrecs) +{ + struct block_writer writer = { + .last_key = REFTABLE_BUF_INIT, + }; + uint8_t block[1024]; + int block_end; + + cl_must_pass(block_writer_init(&writer, block_type, block, 1024, + 0, hash_size(REFTABLE_HASH_SHA1))); + for (size_t i = 0; i < nrecs; i++) + cl_must_pass(block_writer_add(&writer, &recs[i])); + + block_end = block_writer_finish(&writer); + cl_assert(block_end > 0); + + cl_must_pass(reftable_buf_add(buf, block, block_end)); + + block_writer_release(&writer); + return block_end; +} + void test_reftable_block__read_write(void) { const int header_off = 21; /* random */ @@ -381,25 +406,13 @@ void test_reftable_block__ref_read_write(void) void test_reftable_block__iterator(void) { struct reftable_block_source source = { 0 }; - struct block_writer writer = { - .last_key = REFTABLE_BUF_INIT, - }; struct reftable_record expected_refs[20]; struct reftable_ref_record ref = { 0 }; struct reftable_iterator it = { 0 }; struct reftable_block block = { 0 }; - struct reftable_buf data; + struct reftable_buf data = REFTABLE_BUF_INIT; int err; - data.len = 1024; - REFTABLE_CALLOC_ARRAY(data.buf, data.len); - cl_assert(data.buf != NULL); - - err = block_writer_init(&writer, REFTABLE_BLOCK_TYPE_REF, - (uint8_t *) data.buf, data.len, - 0, hash_size(REFTABLE_HASH_SHA1)); - cl_assert(!err); - for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++) { expected_refs[i] = (struct reftable_record) { .type = REFTABLE_BLOCK_TYPE_REF, @@ -409,13 +422,10 @@ void test_reftable_block__iterator(void) }, }; memset(expected_refs[i].u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1); - - err = block_writer_add(&writer, &expected_refs[i]); - cl_assert_equal_i(err, 0); } - err = block_writer_finish(&writer); - cl_assert(err > 0); + cl_reftable_write_block(&data, REFTABLE_BLOCK_TYPE_REF, + expected_refs, ARRAY_SIZE(expected_refs)); block_source_from_buf(&source, &data); reftable_block_init(&block, &source, 0, 0, data.len, @@ -453,6 +463,147 @@ void test_reftable_block__iterator(void) reftable_ref_record_release(&ref); reftable_iterator_destroy(&it); reftable_block_release(&block); - block_writer_release(&writer); + reftable_buf_release(&data); +} + +void test_reftable_block__corrupt_log_block_size(void) +{ + struct reftable_block_source source = { 0 }; + struct reftable_record rec = { + .type = REFTABLE_BLOCK_TYPE_LOG, + .u.log = { + .refname = (char *) "refs/heads/main", + .update_index = 1, + .value_type = REFTABLE_LOG_UPDATE, + }, + }; + struct reftable_block block = { 0 }; + struct reftable_buf data = REFTABLE_BUF_INIT; + + cl_reftable_write_block(&data, REFTABLE_BLOCK_TYPE_LOG, &rec, 1); + + /* + * Log blocks store their inflated size as a big-endian 24-bit integer + * right after the one-byte block type. Rewrite it to claim a size that + * is smaller than the block header. + */ + reftable_put_be24((uint8_t *) data.buf + 1, 1); + + block_source_from_buf(&source, &data); + cl_assert_equal_i(reftable_block_init(&block, &source, 0, 0, data.len, + REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_LOG), + REFTABLE_FORMAT_ERROR); + + reftable_block_release(&block); + reftable_buf_release(&data); +} + +void test_reftable_block__corrupt_block_size(void) +{ + struct reftable_block_source source = { 0 }; + struct reftable_record rec = { + .type = REFTABLE_BLOCK_TYPE_REF, + .u.ref = { + .value_type = REFTABLE_REF_VAL1, + .refname = (char *) "refs/heads/main", + }, + }; + struct reftable_block block = { 0 }; + struct reftable_buf data = REFTABLE_BUF_INIT; + uint32_t block_size; + unsigned char *p; + + cl_reftable_write_block(&data, REFTABLE_BLOCK_TYPE_REF, &rec, 1); + + /* + * The block size is stored as a big-endian 24-bit integer right after + * the one-byte block type at the start of the block. Corrupt it to + * claim a size that is larger than the data we actually have. Reading + * the restart count and restart table relative to such a bogus block + * size must not access out-of-bounds memory. + */ + p = (unsigned char *) data.buf + 1; + block_size = reftable_get_be24(p); + cl_assert_equal_i(block_size, 47); + reftable_put_be24(p, block_size + 1); + + block_source_from_buf(&source, &data); + cl_assert_equal_i(reftable_block_init(&block, &source, 0, 0, data.len, + REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_REF), + REFTABLE_FORMAT_ERROR); + + reftable_block_release(&block); + reftable_buf_release(&data); +} + +void test_reftable_block__corrupt_restart_count(void) +{ + struct reftable_block_source source = { 0 }; + struct reftable_record rec = { + .type = REFTABLE_BLOCK_TYPE_REF, + .u.ref = { + .value_type = REFTABLE_REF_VAL1, + .refname = (char *) "refs/heads/main", + }, + }; + struct reftable_block block = { 0 }; + struct reftable_buf data = REFTABLE_BUF_INIT; + int block_size; + + block_size = cl_reftable_write_block(&data, REFTABLE_BLOCK_TYPE_REF, &rec, 1); + + /* + * Corrupt the restart count to claim a bogus number of restart points. + * Note that this would only cause us to perform an out-of-bounds + * access when seeking into the block, but we want to refuse such a + * block outright. + */ + reftable_put_be16((uint8_t *) data.buf + block_size - 2, 0xffff); + + block_source_from_buf(&source, &data); + cl_assert_equal_i(reftable_block_init(&block, &source, 0, 0, data.len, + REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_REF), + REFTABLE_FORMAT_ERROR); + + reftable_block_release(&block); + reftable_buf_release(&data); +} + +void test_reftable_block__corrupt_restart_offset(void) +{ + struct reftable_block_source source = { 0 }; + struct reftable_record rec = { + .type = REFTABLE_BLOCK_TYPE_REF, + .u.ref = { + .value_type = REFTABLE_REF_VAL1, + .refname = (char *) "refs/heads/main", + }, + }; + struct reftable_block block = { 0 }; + struct block_iter it = BLOCK_ITER_INIT; + struct reftable_buf want = REFTABLE_BUF_INIT; + struct reftable_buf data = REFTABLE_BUF_INIT; + + cl_reftable_write_block(&data, REFTABLE_BLOCK_TYPE_REF, &rec, 1); + + block_source_from_buf(&source, &data); + cl_must_pass(reftable_block_init(&block, &source, 0, 0, data.len, + REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_REF)); + + /* + * Corrupt the first restart offset, stored as a big-endian 24-bit + * integer at the start of the restart table, to point past the end of + * the records section. Seeking such a block must fail gracefully. + */ + reftable_put_be24((uint8_t *) block.block_data.data + block.restart_off, + 0xffffff); + + block_iter_init(&it, &block); + cl_must_pass(reftable_buf_addstr(&want, "refs/heads/main")); + cl_assert_equal_i(block_iter_seek_key(&it, &want), REFTABLE_FORMAT_ERROR); + + reftable_buf_release(&want); + block_iter_close(&it); + reftable_block_release(&block); reftable_buf_release(&data); }
diff --git a/t/unit-tests/u-reftable-merged.c b/t/unit-tests/u-reftable-merged.c index 54cb7fc..21232c1 100644 --- a/t/unit-tests/u-reftable-merged.c +++ b/t/unit-tests/u-reftable-merged.c
@@ -34,7 +34,8 @@ merged_table_from_records(struct reftable_ref_record **refs, cl_assert(*source != NULL); for (size_t i = 0; i < n; i++) { - cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts); + cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, + REFTABLE_HASH_SHA1, &opts); block_source_from_buf(&(*source)[i], &buf[i]); err = reftable_table_new(&(*tables)[i], &(*source)[i], @@ -357,7 +358,8 @@ merged_table_from_log_records(struct reftable_log_record **logs, cl_assert(*source != NULL); for (size_t i = 0; i < n; i++) { - cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts); + cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], + REFTABLE_HASH_SHA1, &opts); block_source_from_buf(&(*source)[i], &buf[i]); err = reftable_table_new(&(*tables)[i], &(*source)[i], @@ -487,7 +489,8 @@ void test_reftable_merged__default_write_opts(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, &opts); struct reftable_ref_record rec = { .refname = (char *) "master", .update_index = 1,
diff --git a/t/unit-tests/u-reftable-readwrite.c b/t/unit-tests/u-reftable-readwrite.c index 4d8c4be..5794b46 100644 --- a/t/unit-tests/u-reftable-readwrite.c +++ b/t/unit-tests/u-reftable-readwrite.c
@@ -48,7 +48,6 @@ static void write_table(char ***names, struct reftable_buf *buf, int N, { struct reftable_write_options opts = { .block_size = block_size, - .hash_id = hash_id, }; struct reftable_ref_record *refs; struct reftable_log_record *logs; @@ -78,7 +77,7 @@ static void write_table(char ***names, struct reftable_buf *buf, int N, logs[i].value.update.message = (char *) "message"; } - cl_reftable_write_to_buf(buf, refs, N, logs, N, &opts); + cl_reftable_write_to_buf(buf, refs, N, logs, N, hash_id, &opts); reftable_free(refs); reftable_free(logs); @@ -103,6 +102,7 @@ void test_reftable_readwrite__log_buffer_size(void) .message = (char *) "commit: 9\n", } } }; struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, &opts); /* This tests buffer extension for log compression. Must use a random @@ -143,6 +143,7 @@ void test_reftable_readwrite__log_overflow(void) }, }; struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, &opts); memset(msg, 'x', sizeof(msg) - 1); @@ -157,6 +158,7 @@ void test_reftable_readwrite__log_write_limits(void) struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, &opts); struct reftable_log_record log = { .refname = (char *)"refs/head/master", @@ -202,7 +204,9 @@ void test_reftable_readwrite__log_write_read(void) struct reftable_table *table; struct reftable_block_source source = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, + &opts); const struct reftable_stats *stats = NULL; int N = 2, i; char **names; @@ -299,6 +303,7 @@ void test_reftable_readwrite__log_zlib_corruption(void) struct reftable_block_source source = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, &opts); const struct reftable_stats *stats = NULL; char message[100] = { 0 }; @@ -531,6 +536,7 @@ static void t_table_refs_for(int indexed) struct reftable_block_source source = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, &opts); struct reftable_iterator it = { 0 }; int N = 50, j, i; @@ -622,7 +628,9 @@ void test_reftable_readwrite__write_empty_table(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, + &opts); struct reftable_block_source source = { 0 }; struct reftable_table *table = NULL; struct reftable_ref_record rec = { 0 }; @@ -660,7 +668,9 @@ void test_reftable_readwrite__write_object_id_min_length(void) .block_size = 75, }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, + &opts); struct reftable_ref_record ref = { .update_index = 1, .value_type = REFTABLE_REF_VAL1, @@ -691,7 +701,9 @@ void test_reftable_readwrite__write_object_id_length(void) .block_size = 75, }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, + &opts); struct reftable_ref_record ref = { .update_index = 1, .value_type = REFTABLE_REF_VAL1, @@ -721,7 +733,9 @@ void test_reftable_readwrite__write_empty_key(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, + &opts); struct reftable_ref_record ref = { .refname = (char *) "", .update_index = 1, @@ -740,7 +754,9 @@ void test_reftable_readwrite__write_key_order(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + REFTABLE_HASH_SHA1, + &opts); struct reftable_ref_record refs[2] = { { .refname = (char *) "b", @@ -787,7 +803,8 @@ void test_reftable_readwrite__write_multiple_indices(void) int i; int err; - writer = cl_reftable_strbuf_writer(&writer_buf, &opts); + writer = cl_reftable_strbuf_writer(&writer_buf, REFTABLE_HASH_SHA1, + &opts); reftable_writer_set_limits(writer, 1, 1); for (i = 0; i < 100; i++) { struct reftable_ref_record ref = { @@ -861,7 +878,8 @@ void test_reftable_readwrite__write_multi_level_index(void) struct reftable_table *table; int err; - writer = cl_reftable_strbuf_writer(&writer_buf, &opts); + writer = cl_reftable_strbuf_writer(&writer_buf, REFTABLE_HASH_SHA1, + &opts); reftable_writer_set_limits(writer, 1, 1); for (size_t i = 0; i < 200; i++) { struct reftable_ref_record ref = {
diff --git a/t/unit-tests/u-reftable-record.c b/t/unit-tests/u-reftable-record.c index 1bf2e17..9c95083 100644 --- a/t/unit-tests/u-reftable-record.c +++ b/t/unit-tests/u-reftable-record.c
@@ -11,6 +11,7 @@ #include "reftable/basics.h" #include "reftable/constants.h" #include "reftable/record.h" +#include "reftable/reftable-error.h" static void t_copy(struct reftable_record *rec) { @@ -202,6 +203,29 @@ void test_reftable_record__ref_record_roundtrip(void) reftable_buf_release(&scratch); } +void test_reftable_record__ref_record_decode_invalid_value_type(void) +{ + struct reftable_buf scratch = REFTABLE_BUF_INIT; + struct reftable_record out = { + .type = REFTABLE_BLOCK_TYPE_REF, + }; + struct reftable_buf key = REFTABLE_BUF_INIT; + uint8_t buffer[1024] = { 0 }; + struct string_view dest = { + .buf = buffer, + .len = sizeof(buffer), + }; + + cl_must_pass(reftable_buf_addstr(&key, "refs/heads/master")); + cl_assert_equal_i(reftable_record_decode(&out, key, REFTABLE_NR_REF_VALUETYPES, + dest, REFTABLE_HASH_SIZE_SHA1, &scratch), + REFTABLE_FORMAT_ERROR); + + reftable_record_release(&out); + reftable_buf_release(&key); + reftable_buf_release(&scratch); +} + void test_reftable_record__log_record_comparison(void) { struct reftable_record in[3] = {
diff --git a/t/unit-tests/u-reftable-stack.c b/t/unit-tests/u-reftable-stack.c index b8110cd..e6c1635 100644 --- a/t/unit-tests/u-reftable-stack.c +++ b/t/unit-tests/u-reftable-stack.c
@@ -111,10 +111,9 @@ static int write_test_ref(struct reftable_writer *wr, void *arg) static void write_n_ref_tables(struct reftable_stack *st, size_t n) { - int disable_auto_compact; - - disable_auto_compact = st->opts.disable_auto_compact; - st->opts.disable_auto_compact = 1; + struct reftable_write_options opts = { + .disable_auto_compact = 1, + }; for (size_t i = 0; i < n; i++) { struct reftable_ref_record ref = { @@ -128,10 +127,8 @@ static void write_n_ref_tables(struct reftable_stack *st, cl_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1); cl_assert_equal_i(reftable_stack_add(st, - &write_test_ref, &ref, 0), 0); + &write_test_ref, &ref, &opts, 0), 0); } - - st->opts.disable_auto_compact = disable_auto_compact; } struct write_log_arg { @@ -168,10 +165,10 @@ void test_reftable_stack__add_one(void) struct stat stat_result = { 0 }; int err; - err = reftable_new_stack(&st, dir, &opts); + err = reftable_new_stack(&st, dir, NULL); cl_assert(!err); - err = reftable_stack_add(st, write_test_ref, &ref, 0); + err = reftable_stack_add(st, write_test_ref, &ref, &opts, 0); cl_assert(!err); err = reftable_stack_read_ref(st, ref.refname, &dest); @@ -210,7 +207,6 @@ void test_reftable_stack__add_one(void) void test_reftable_stack__uptodate(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL; struct reftable_stack *st2 = NULL; char *dir = get_tmp_dir(__LINE__); @@ -232,15 +228,15 @@ void test_reftable_stack__uptodate(void) /* simulate multi-process access to the same stack by creating two stacks for the same directory. */ - cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); - cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st1, write_test_ref, - &ref1, 0), 0); + &ref1, NULL, 0), 0); cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, - &ref2, 0), REFTABLE_OUTDATED_ERROR); + &ref2, NULL, 0), REFTABLE_OUTDATED_ERROR); cl_assert_equal_i(reftable_stack_reload(st2), 0); cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, - &ref2, 0), 0); + &ref2, NULL, 0), 0); reftable_stack_destroy(st1); reftable_stack_destroy(st2); clear_dir(dir); @@ -249,7 +245,6 @@ void test_reftable_stack__uptodate(void) void test_reftable_stack__transaction_api(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_addition *add = NULL; @@ -261,11 +256,11 @@ void test_reftable_stack__transaction_api(void) }; struct reftable_ref_record dest = { 0 }; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); reftable_addition_destroy(add); - cl_assert_equal_i(reftable_stack_new_addition(&add, st, 0), 0); + cl_assert_equal_i(reftable_stack_new_addition(&add, st, NULL, 0), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &ref), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); @@ -306,7 +301,7 @@ void test_reftable_stack__transaction_with_reload(void) cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); - cl_assert_equal_i(reftable_stack_new_addition(&add, st1, 0), 0); + cl_assert_equal_i(reftable_stack_new_addition(&add, st1, NULL, 0), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &refs[0]), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); @@ -317,9 +312,9 @@ void test_reftable_stack__transaction_with_reload(void) * create the addition and lock the stack by default, but allow the * reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set. */ - cl_assert_equal_i(reftable_stack_new_addition(&add, st2, 0), + cl_assert_equal_i(reftable_stack_new_addition(&add, st2, NULL, 0), REFTABLE_OUTDATED_ERROR); - cl_assert_equal_i(reftable_stack_new_addition(&add, st2, + cl_assert_equal_i(reftable_stack_new_addition(&add, st2, NULL, REFTABLE_STACK_NEW_ADDITION_RELOAD), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &refs[1]), 0); @@ -342,12 +337,11 @@ void test_reftable_stack__transaction_with_reload(void) void test_reftable_stack__transaction_api_performs_auto_compaction(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = {0}; struct reftable_addition *add = NULL; struct reftable_stack *st = NULL; size_t n = 20; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (size_t i = 0; i <= n; i++) { struct reftable_ref_record ref = { @@ -356,6 +350,9 @@ void test_reftable_stack__transaction_api_performs_auto_compaction(void) .value.symref = (char *) "master", }; char name[100]; + struct reftable_write_options write_opts = { + .disable_auto_compact = (i != n), + }; snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); ref.refname = name; @@ -365,10 +362,8 @@ void test_reftable_stack__transaction_api_performs_auto_compaction(void) * we can ensure that we indeed honor this setting and have * better control over when exactly auto compaction runs. */ - st->opts.disable_auto_compact = i != n; - cl_assert_equal_i(reftable_stack_new_addition(&add, - st, 0), 0); + st, &write_opts, 0), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &ref), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); @@ -398,15 +393,14 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void) .value_type = REFTABLE_REF_VAL1, .value.val1 = {0x01}, }; - struct reftable_write_options opts = { 0 }; struct reftable_stack *st; struct reftable_buf table_path = REFTABLE_BUF_INIT; char *dir = get_tmp_dir(__LINE__); int err; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref, 0), 0); + &ref, NULL, 0), 0); cl_assert_equal_i(st->merged->tables_len, 1); cl_assert_equal_i(st->stats.attempts, 0); cl_assert_equal_i(st->stats.failures, 0); @@ -424,7 +418,7 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void) write_file_buf(table_path.buf, "", 0); ref.update_index = 2; - err = reftable_stack_add(st, write_test_ref, &ref, 0); + err = reftable_stack_add(st, write_test_ref, &ref, NULL, 0); cl_assert(!err); cl_assert_equal_i(st->merged->tables_len, 2); cl_assert_equal_i(st->stats.attempts, 1); @@ -443,7 +437,6 @@ static int write_error(struct reftable_writer *wr UNUSED, void *arg) void test_reftable_stack__update_index_check(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_ref_record ref1 = { .refname = (char *) "name1", @@ -458,11 +451,11 @@ void test_reftable_stack__update_index_check(void) .value.symref = (char *) "master", }; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref1, 0), 0); + &ref1, NULL, 0), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref2, 0), REFTABLE_API_ERROR); + &ref2, NULL, 0), REFTABLE_API_ERROR); reftable_stack_destroy(st); clear_dir(dir); } @@ -470,14 +463,13 @@ void test_reftable_stack__update_index_check(void) void test_reftable_stack__lock_failure(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; int i; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) cl_assert_equal_i(reftable_stack_add(st, write_error, - &i, 0), i); + &i, NULL, 0), i); reftable_stack_destroy(st); clear_dir(dir); @@ -499,7 +491,7 @@ void test_reftable_stack__add(void) size_t i, N = ARRAY_SIZE(refs); int err = 0; - err = reftable_new_stack(&st, dir, &opts); + err = reftable_new_stack(&st, dir, NULL); cl_assert(!err); for (i = 0; i < N; i++) { @@ -521,7 +513,7 @@ void test_reftable_stack__add(void) for (i = 0; i < N; i++) cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &refs[i], 0), 0); + &refs[i], &opts, 0), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -529,10 +521,10 @@ void test_reftable_stack__add(void) .update_index = reftable_stack_next_update_index(st), }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), 0); + &arg, &opts, 0), 0); } - cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0); + cl_assert_equal_i(reftable_stack_compact_all(st, &opts, NULL), 0); for (i = 0; i < N; i++) { struct reftable_ref_record dest = { 0 }; @@ -584,7 +576,6 @@ void test_reftable_stack__add(void) void test_reftable_stack__iterator(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); struct reftable_ref_record refs[10] = { 0 }; @@ -593,7 +584,7 @@ void test_reftable_stack__iterator(void) size_t N = ARRAY_SIZE(refs), i; int err; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (i = 0; i < N; i++) { refs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i); @@ -613,7 +604,7 @@ void test_reftable_stack__iterator(void) for (i = 0; i < N; i++) cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &refs[i], 0), 0); + &refs[i], NULL, 0), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -622,7 +613,7 @@ void test_reftable_stack__iterator(void) }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), 0); + &arg, NULL, 0), 0); } reftable_stack_init_ref_iterator(st, &it); @@ -669,9 +660,6 @@ void test_reftable_stack__iterator(void) void test_reftable_stack__log_normalize(void) { - struct reftable_write_options opts = { - 0, - }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); struct reftable_log_record input = { @@ -693,15 +681,15 @@ void test_reftable_stack__log_normalize(void) .update_index = 1, }; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); input.value.update.message = (char *) "one\ntwo"; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), REFTABLE_API_ERROR); + &arg, NULL, 0), REFTABLE_API_ERROR); input.value.update.message = (char *) "one"; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), 0); + &arg, NULL, 0), 0); cl_assert_equal_i(reftable_stack_read_log(st, input.refname, &dest), 0); cl_assert_equal_s(dest.value.update.message, "one\n"); @@ -709,7 +697,7 @@ void test_reftable_stack__log_normalize(void) input.value.update.message = (char *) "two\n"; arg.update_index = 2; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), 0); + &arg, NULL, 0), 0); cl_assert_equal_i(reftable_stack_read_log(st, input.refname, &dest), 0); cl_assert_equal_s(dest.value.update.message, "two\n"); @@ -723,7 +711,6 @@ void test_reftable_stack__log_normalize(void) void test_reftable_stack__tombstone(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_ref_record refs[2] = { 0 }; struct reftable_log_record logs[2] = { 0 }; @@ -731,7 +718,7 @@ void test_reftable_stack__tombstone(void) struct reftable_ref_record dest = { 0 }; struct reftable_log_record log_dest = { 0 }; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); /* even entries add the refs, odd entries delete them. */ for (i = 0; i < N; i++) { @@ -760,7 +747,7 @@ void test_reftable_stack__tombstone(void) } for (i = 0; i < N; i++) cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &refs[i], 0), 0); + &refs[i], NULL, 0), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -768,7 +755,7 @@ void test_reftable_stack__tombstone(void) .update_index = reftable_stack_next_update_index(st), }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), 0); + &arg, NULL, 0), 0); } cl_assert_equal_i(reftable_stack_read_ref(st, "branch", @@ -779,7 +766,7 @@ void test_reftable_stack__tombstone(void) &log_dest), 1); reftable_log_record_release(&log_dest); - cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL, NULL), 0); cl_assert_equal_i(reftable_stack_read_ref(st, "branch", &dest), 1); cl_assert_equal_i(reftable_stack_read_log(st, "branch", @@ -799,7 +786,6 @@ void test_reftable_stack__tombstone(void) void test_reftable_stack__hash_id(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_ref_record ref = { @@ -808,15 +794,14 @@ void test_reftable_stack__hash_id(void) .value.symref = (char *) "target", .update_index = 1, }; - struct reftable_write_options opts32 = { .hash_id = REFTABLE_HASH_SHA256 }; + struct reftable_stack_options opts32 = { .hash_id = REFTABLE_HASH_SHA256 }; struct reftable_stack *st32 = NULL; - struct reftable_write_options opts_default = { 0 }; struct reftable_stack *st_default = NULL; struct reftable_ref_record dest = { 0 }; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref, 0), 0); + &ref, NULL, 0), 0); /* can't read it with the wrong hash ID. */ cl_assert_equal_i(reftable_new_stack(&st32, dir, @@ -824,7 +809,7 @@ void test_reftable_stack__hash_id(void) /* check that we can read it back with default opts too. */ cl_assert_equal_i(reftable_new_stack(&st_default, dir, - &opts_default), 0); + NULL), 0); cl_assert_equal_i(reftable_stack_read_ref(st_default, "master", &dest), 0); cl_assert(reftable_ref_record_equal(&ref, &dest, @@ -855,7 +840,6 @@ void test_reftable_stack__suggest_compaction_segment_nothing(void) void test_reftable_stack__reflog_expire(void) { char *dir = get_tmp_dir(__LINE__); - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_log_record logs[20] = { 0 }; size_t i, N = ARRAY_SIZE(logs) - 1; @@ -864,7 +848,7 @@ void test_reftable_stack__reflog_expire(void) }; struct reftable_log_record log = { 0 }; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (i = 1; i <= N; i++) { char buf[256]; @@ -885,18 +869,18 @@ void test_reftable_stack__reflog_expire(void) .update_index = reftable_stack_next_update_index(st), }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, 0), 0); + &arg, NULL, 0), 0); } - cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0); - cl_assert_equal_i(reftable_stack_compact_all(st, &expiry), 0); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL, NULL), 0); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL, &expiry), 0); cl_assert_equal_i(reftable_stack_read_log(st, logs[9].refname, &log), 1); cl_assert_equal_i(reftable_stack_read_log(st, logs[11].refname, &log), 0); expiry.min_update_index = 15; - cl_assert_equal_i(reftable_stack_compact_all(st, &expiry), 0); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL, &expiry), 0); cl_assert_equal_i(reftable_stack_read_log(st, logs[14].refname, &log), 1); cl_assert_equal_i(reftable_stack_read_log(st, logs[16].refname, @@ -918,15 +902,14 @@ static int write_nothing(struct reftable_writer *wr, void *arg UNUSED) void test_reftable_stack__empty_add(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); struct reftable_stack *st2 = NULL; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_nothing, - NULL, 0), 0); - cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); + NULL, NULL, 0), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); clear_dir(dir); reftable_stack_destroy(st); reftable_stack_destroy(st2); @@ -952,7 +935,7 @@ void test_reftable_stack__auto_compaction(void) size_t i, N = 100; int err; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (i = 0; i < N; i++) { char name[100]; @@ -964,10 +947,10 @@ void test_reftable_stack__auto_compaction(void) }; snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); - err = reftable_stack_add(st, write_test_ref, &ref, 0); + err = reftable_stack_add(st, write_test_ref, &ref, &opts, 0); cl_assert(!err); - err = reftable_stack_auto_compact(st); + err = reftable_stack_auto_compact(st, &opts); cl_assert(!err); cl_assert(i < 2 || st->merged->tables_len < 2 * fastlogN(i, 2)); } @@ -989,7 +972,7 @@ void test_reftable_stack__auto_compaction_factor(void) size_t N = 100; int err; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (size_t i = 0; i < N; i++) { char name[20]; @@ -1000,7 +983,7 @@ void test_reftable_stack__auto_compaction_factor(void) }; xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); - err = reftable_stack_add(st, &write_test_ref, &ref, 0); + err = reftable_stack_add(st, &write_test_ref, &ref, &opts, 0); cl_assert(!err); cl_assert(i < 5 || st->merged->tables_len < 5 * fastlogN(i, 5)); @@ -1020,7 +1003,7 @@ void test_reftable_stack__auto_compaction_with_locked_tables(void) char *dir = get_tmp_dir(__LINE__); int err; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); write_n_ref_tables(st, 5); cl_assert_equal_i(st->merged->tables_len, 5); @@ -1042,7 +1025,7 @@ void test_reftable_stack__auto_compaction_with_locked_tables(void) * would in theory compact all tables, due to the preexisting lock we * only compact the newest two tables. */ - err = reftable_stack_auto_compact(st); + err = reftable_stack_auto_compact(st, &opts); cl_assert(!err); cl_assert_equal_i(st->stats.failures, 0); cl_assert_equal_i(st->merged->tables_len, 4); @@ -1054,12 +1037,11 @@ void test_reftable_stack__auto_compaction_with_locked_tables(void) void test_reftable_stack__add_performs_auto_compaction(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); size_t i, n = 20; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (i = 0; i <= n; i++) { struct reftable_ref_record ref = { @@ -1067,6 +1049,9 @@ void test_reftable_stack__add_performs_auto_compaction(void) .value_type = REFTABLE_REF_SYMREF, .value.symref = (char *) "master", }; + struct reftable_write_options write_opts = { + .disable_auto_compact = (i != n), + }; bool required = false; char buf[128]; @@ -1075,20 +1060,18 @@ void test_reftable_stack__add_performs_auto_compaction(void) * we can ensure that we indeed honor this setting and have * better control over when exactly auto compaction runs. */ - st->opts.disable_auto_compact = i != n; - snprintf(buf, sizeof(buf), "branch-%04"PRIuMAX, (uintmax_t)i); ref.refname = buf; cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref, 0), 0); + &ref, &write_opts, 0), 0); /* * The stack length should grow continuously for all runs where * auto compaction is disabled. When enabled, we should merge * all tables in the stack. */ - cl_assert_equal_i(reftable_stack_compaction_required(st, true, &required), 0); + cl_assert_equal_i(reftable_stack_compaction_required(st, NULL, true, &required), 0); if (i != n) { cl_assert_equal_i(st->merged->tables_len, i + 1); if (i < 1) @@ -1115,7 +1098,7 @@ void test_reftable_stack__compaction_with_locked_tables(void) char *dir = get_tmp_dir(__LINE__); int err; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); write_n_ref_tables(st, 3); cl_assert_equal_i(st->merged->tables_len, 3); @@ -1131,7 +1114,7 @@ void test_reftable_stack__compaction_with_locked_tables(void) * Compaction is expected to fail given that we were not able to * compact all tables. */ - err = reftable_stack_compact_all(st, NULL); + err = reftable_stack_compact_all(st, &opts, NULL); cl_assert_equal_i(err, REFTABLE_LOCK_ERROR); cl_assert_equal_i(st->stats.failures, 1); cl_assert_equal_i(st->merged->tables_len, 3); @@ -1143,15 +1126,14 @@ void test_reftable_stack__compaction_with_locked_tables(void) void test_reftable_stack__compaction_concurrent(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL, *st2 = NULL; char *dir = get_tmp_dir(__LINE__); - cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); write_n_ref_tables(st1, 3); - cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); - cl_assert_equal_i(reftable_stack_compact_all(st1, NULL), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); + cl_assert_equal_i(reftable_stack_compact_all(st1, NULL, NULL), 0); reftable_stack_destroy(st1); reftable_stack_destroy(st2); @@ -1171,20 +1153,19 @@ static void unclean_stack_close(struct reftable_stack *st) void test_reftable_stack__compaction_concurrent_clean(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL; char *dir = get_tmp_dir(__LINE__); - cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); write_n_ref_tables(st1, 3); - cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); - cl_assert_equal_i(reftable_stack_compact_all(st1, NULL), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); + cl_assert_equal_i(reftable_stack_compact_all(st1, NULL, NULL), 0); unclean_stack_close(st1); unclean_stack_close(st2); - cl_assert_equal_i(reftable_new_stack(&st3, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st3, dir, NULL), 0); cl_assert_equal_i(reftable_stack_clean(st3), 0); cl_assert_equal_i(count_dir_entries(dir), 2); @@ -1197,7 +1178,6 @@ void test_reftable_stack__compaction_concurrent_clean(void) void test_reftable_stack__read_across_reload(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL, *st2 = NULL; struct reftable_ref_record rec = { 0 }; struct reftable_iterator it = { 0 }; @@ -1205,17 +1185,17 @@ void test_reftable_stack__read_across_reload(void) int err; /* Create a first stack and set up an iterator for it. */ - cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); write_n_ref_tables(st1, 2); cl_assert_equal_i(st1->merged->tables_len, 2); reftable_stack_init_ref_iterator(st1, &it); cl_assert_equal_i(reftable_iterator_seek_ref(&it, ""), 0); /* Set up a second stack for the same directory and compact it. */ - err = reftable_new_stack(&st2, dir, &opts); + err = reftable_new_stack(&st2, dir, NULL); cl_assert(!err); cl_assert_equal_i(st2->merged->tables_len, 2); - err = reftable_stack_compact_all(st2, NULL); + err = reftable_stack_compact_all(st2, NULL, NULL); cl_assert(!err); cl_assert_equal_i(st2->merged->tables_len, 1); @@ -1244,7 +1224,6 @@ void test_reftable_stack__read_across_reload(void) void test_reftable_stack__reload_with_missing_table(void) { - struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_ref_record rec = { 0 }; struct reftable_iterator it = { 0 }; @@ -1253,7 +1232,7 @@ void test_reftable_stack__reload_with_missing_table(void) int err; /* Create a first stack and set up an iterator for it. */ - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); write_n_ref_tables(st, 2); cl_assert_equal_i(st->merged->tables_len, 2); reftable_stack_init_ref_iterator(st, &it); @@ -1320,11 +1299,11 @@ void test_reftable_stack__invalid_limit_updates(void) char *dir = get_tmp_dir(__LINE__); struct reftable_stack *st = NULL; - cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); reftable_addition_destroy(add); - cl_assert_equal_i(reftable_stack_new_addition(&add, st, 0), 0); + cl_assert_equal_i(reftable_stack_new_addition(&add, st, &opts, 0), 0); /* * write_limits_after_ref also updates the update indexes after adding
diff --git a/t/unit-tests/u-reftable-table.c b/t/unit-tests/u-reftable-table.c index 14fae8b..d4251fa 100644 --- a/t/unit-tests/u-reftable-table.c +++ b/t/unit-tests/u-reftable-table.c
@@ -1,8 +1,11 @@ #include "unit-test.h" #include "lib-reftable.h" +#include "reftable/basics.h" +#include "reftable/block.h" #include "reftable/blocksource.h" #include "reftable/constants.h" #include "reftable/iter.h" +#include "reftable/reftable-error.h" #include "reftable/table.h" #include "strbuf.h" @@ -22,7 +25,8 @@ void test_reftable_table__seek_once(void) struct reftable_buf buf = REFTABLE_BUF_INIT; int ret; - cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL); + cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, + REFTABLE_HASH_SHA1, NULL); block_source_from_buf(&source, &buf); ret = reftable_table_new(&table, &source, "name"); @@ -64,7 +68,7 @@ void test_reftable_table__reseek(void) int ret; cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), - NULL, 0, NULL); + NULL, 0, REFTABLE_HASH_SHA1, NULL); block_source_from_buf(&source, &buf); ret = reftable_table_new(&table, &source, "name"); @@ -147,7 +151,8 @@ void test_reftable_table__block_iterator(void) (uintmax_t) i); } - cl_reftable_write_to_buf(&buf, records, nrecords, NULL, 0, NULL); + cl_reftable_write_to_buf(&buf, records, nrecords, NULL, 0, + REFTABLE_HASH_SHA1, NULL); block_source_from_buf(&source, &buf); ret = reftable_table_new(&table, &source, "name"); @@ -199,3 +204,92 @@ void test_reftable_table__block_iterator(void) reftable_buf_release(&buf); reftable_free(records); } + +void test_reftable_table__seek_invalid_log_offset(void) +{ + struct reftable_ref_record refs[] = { + { + .refname = (char *) "refs/heads/main", + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 42 }, + }, + }; + struct reftable_log_record logs[] = { + { + .refname = (char *) "refs/heads/main", + .update_index = 1, + .value_type = REFTABLE_LOG_UPDATE, + .value.update = { + .name = (char *) "user", + .email = (char *) "user@example.com", + .message = (char *) "message\n", + }, + }, + }; + struct reftable_block_source source = { 0 }; + struct reftable_log_record log = { 0 }; + struct reftable_iterator it = { 0 }; + struct reftable_table *table; + struct reftable_buf buf = REFTABLE_BUF_INIT; + size_t fsize = footer_size(1); + uint8_t *footer; + + cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs), + logs, ARRAY_SIZE(logs), REFTABLE_HASH_SHA1, NULL); + + /* + * Corrupt the log section offset stored in the footer so that it points + * past the end of the table. The footer is checksummed, so we also have + * to recompute and rewrite the CRC. + */ + footer = (uint8_t *) buf.buf + buf.len - fsize; + reftable_put_be64(footer + header_size(1) + 24, UINT64_MAX); + reftable_put_be32(footer + fsize - 4, crc32(0, footer, fsize - 4)); + + block_source_from_buf(&source, &buf); + cl_must_pass(reftable_table_new(&table, &source, "name")); + + /* + * Seeking the log iterator must not crash even though the log section + * offset is bogus. As the offset points past the end of the table we + * know that the table is corrupt, so the seek must report a format + * error instead of pretending that the section is empty. + */ + reftable_table_init_log_iterator(table, &it); + cl_assert_equal_i(reftable_iterator_seek_log(&it, ""), + REFTABLE_FORMAT_ERROR); + + reftable_log_record_release(&log); + reftable_iterator_destroy(&it); + reftable_table_decref(table); + reftable_buf_release(&buf); +} + +void test_reftable_table__new_with_truncated_table(void) +{ + struct reftable_ref_record refs[] = { + { + .refname = (char *) "refs/heads/main", + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 42 }, + }, + }; + struct reftable_block_source source = { 0 }; + struct reftable_table *table; + struct reftable_buf buf = REFTABLE_BUF_INIT; + + cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs), NULL, 0, + REFTABLE_HASH_SHA1, NULL); + + /* + * Truncate the table so that it is large enough to read the header, but + * too small to also contain the footer. + */ + buf.len = footer_size(1) - 1; + block_source_from_buf(&source, &buf); + + cl_assert_equal_i(reftable_table_new(&table, &source, "name"), + REFTABLE_FORMAT_ERROR); + + reftable_buf_release(&buf); +}
diff --git a/tools/coccinelle/hash.cocci b/tools/coccinelle/hash.cocci new file mode 100644 index 0000000..d0e2e5f --- /dev/null +++ b/tools/coccinelle/hash.cocci
@@ -0,0 +1,63 @@ +@@ +identifier f != git_hash_init; +expression ALGO; +struct git_hash_ctx *CTX; +@@ + f(...) {<... +- ALGO->init_fn(CTX); ++ git_hash_init(CTX, ALGO); + ...>} + +@@ +identifier f != git_hash_clone; +expression ALGO; +struct git_hash_ctx *SRC; +struct git_hash_ctx *DST; +@@ + f(...) {<... +- ALGO->clone_fn(DST, SRC); ++ git_hash_clone(DST, SRC); + ...>} + +@@ +identifier f != git_hash_update; +expression ALGO; +struct git_hash_ctx *CTX; +expression list ARGS; +@@ + f(...) {<... +- ALGO->update_fn(CTX, ARGS); ++ git_hash_update(CTX, ARGS); + ...>} + +@@ +identifier f != git_hash_final; +expression ALGO; +struct git_hash_ctx *CTX; +expression list ARGS; +@@ + f(...) {<... +- ALGO->final_fn(ARGS, CTX); ++ git_hash_final(ARGS, CTX); + ...>} + +@@ +identifier f != git_hash_final_oid; +expression ALGO; +struct git_hash_ctx *CTX; +expression list ARGS; +@@ + f(...) {<... +- ALGO->final_oid_fn(ARGS, CTX); ++ git_hash_final_oid(ARGS, CTX); + ...>} + +@@ +identifier f != git_hash_discard; +expression ALGO; +struct git_hash_ctx *CTX; +@@ + f(...) {<... +- ALGO->discard_fn(CTX); ++ git_hash_discard(CTX); + ...>}
diff --git a/trace.c b/trace.c index 9b99460..515b99e 100644 --- a/trace.c +++ b/trace.c
@@ -299,7 +299,7 @@ static const char *quote_crnl(const char *path) void trace_repo_setup(struct repository *r) { - const char *git_work_tree, *prefix = startup_info->prefix; + const char *git_work_tree, *prefix = r->prefix; char *cwd; if (!trace_want(&trace_setup_key)) @@ -310,7 +310,7 @@ void trace_repo_setup(struct repository *r) if (!(git_work_tree = repo_get_work_tree(r))) git_work_tree = "(null)"; - if (!startup_info->prefix) + if (!r->prefix) prefix = "(null)"; trace_printf_key(&trace_setup_key, "setup: git_dir: %s\n", quote_crnl(repo_get_git_dir(r)));
diff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c index 1c1d27b..131b4f5 100644 --- a/trace2/tr2_sid.c +++ b/trace2/tr2_sid.c
@@ -45,7 +45,7 @@ static void tr2_sid_append_my_sid_component(void) if (xgethostname(hostname, sizeof(hostname))) strbuf_add(&tr2sid_buf, "Localhost", 9); else { - algo->init_fn(&ctx); + git_hash_init(&ctx, algo); git_hash_update(&ctx, hostname, strlen(hostname)); git_hash_final(hash, &ctx); hash_to_hex_algop_r(hex, hash, algo);
diff --git a/transport.c b/transport.c index 0f5ec30..fc144f0 100644 --- a/transport.c +++ b/transport.c
@@ -1482,7 +1482,7 @@ int transport_push(struct repository *r, if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND | TRANSPORT_RECURSE_SUBMODULES_ONLY)) && - !is_bare_repository()) { + !is_bare_repository(the_repository)) { struct ref *ref = remote_refs; struct oid_array commits = OID_ARRAY_INIT; @@ -1509,7 +1509,7 @@ int transport_push(struct repository *r, if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) || ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND | TRANSPORT_RECURSE_SUBMODULES_ONLY)) && - !pretend)) && !is_bare_repository()) { + !pretend)) && !is_bare_repository(the_repository)) { struct ref *ref = remote_refs; struct string_list needs_pushing = STRING_LIST_INIT_DUP; struct oid_array commits = OID_ARRAY_INIT;
diff --git a/unpack-trees.c b/unpack-trees.c index b42020f..154d6d4 100644 --- a/unpack-trees.c +++ b/unpack-trees.c
@@ -671,8 +671,10 @@ static struct cache_entry *next_cache_entry(struct unpack_trees_options *o) while (pos < index->cache_nr) { struct cache_entry *ce = index->cache[pos]; - if (!(ce->ce_flags & CE_UNPACKED)) + if (!(ce->ce_flags & CE_UNPACKED)) { + o->internal.cache_bottom = pos; return ce; + } pos++; } return NULL; @@ -2428,7 +2430,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype, * * Ignore that lstat() if it matches. */ - if (ignore_case && icase_exists(o, name, len, st)) + if (repo_ignore_case(the_repository) && icase_exists(o, name, len, st)) return 0; if (o->internal.dir &&
diff --git a/urlmatch.c b/urlmatch.c index bf8cce6..20bc2d0 100644 --- a/urlmatch.c +++ b/urlmatch.c
@@ -112,7 +112,7 @@ static int match_host(const struct url_info *url_info, return (!url_len && !pat_len); } -static char *url_normalize_1(const char *url, struct url_info *out_info, char allow_globs) +static char *url_normalize_1(const char *url, struct url_info *out_info, bool allow_globs) { /* * Normalize NUL-terminated url using the following rules: @@ -438,7 +438,12 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, char al char *url_normalize(const char *url, struct url_info *out_info) { - return url_normalize_1(url, out_info, 0); + return url_normalize_1(url, out_info, false); +} + +char *url_normalize_pattern(const char *url, struct url_info *out_info) +{ + return url_normalize_1(url, out_info, true); } char *url_parse(const char *url_orig, struct url_info *out_info) @@ -704,7 +709,7 @@ int urlmatch_config_entry(const char *var, const char *value, struct url_info norm_info; config_url = xmemdupz(key, dot - key); - norm_url = url_normalize_1(config_url, &norm_info, 1); + norm_url = url_normalize_1(config_url, &norm_info, true); if (norm_url) retval = match_urls(url, &norm_info, &matched); else if (collect->fallback_match_fn)
diff --git a/urlmatch.h b/urlmatch.h index 6b3ce42..db1a335 100644 --- a/urlmatch.h +++ b/urlmatch.h
@@ -37,6 +37,18 @@ struct url_info { char *url_normalize(const char *, struct url_info *); char *url_parse(const char *, struct url_info *); +/* + * Like url_normalize(), but also allows '*' glob characters in the host + * portion. Use this when normalizing URL patterns from user configuration. + * + * Note that '*' is a valid path character per RFC 3986 (as a sub-delim), + * so glob patterns using '*' in the path are also accepted. + * + * Returns a newly allocated normalized string and fills out_info if + * non-NULL, or NULL if the pattern is invalid. + */ +char *url_normalize_pattern(const char *url, struct url_info *out_info); + struct urlmatch_item { size_t hostmatch_len; size_t pathmatch_len;
diff --git a/usage.c b/usage.c index 527edb1..3f0118a 100644 --- a/usage.c +++ b/usage.c
@@ -188,7 +188,7 @@ static void show_usage_if_asked_helper(const char *err, ...) va_start(params, err); vfreportf(stdout, _("usage: "), err, params); va_end(params); - exit(129); + exit(0); } void show_usage_if_asked(int ac, const char **av, const char *err)
diff --git a/walker.c b/walker.c index e98eb6d..e3de77f 100644 --- a/walker.c +++ b/walker.c
@@ -84,12 +84,12 @@ static struct prio_queue complete = { compare_commits_by_commit_date }; static int process_commit(struct walker *walker, struct commit *commit) { struct commit_list *parents; + struct commit *item; if (repo_parse_commit(the_repository, commit)) return -1; - while (complete.nr) { - struct commit *item = prio_queue_peek(&complete); + while ((item = prio_queue_peek(&complete))) { if (item->date < commit->date) break; pop_most_recent_commit(&complete, COMPLETE);
diff --git a/worktree.c b/worktree.c index 97eddc3..3012582 100644 --- a/worktree.c +++ b/worktree.c
@@ -123,8 +123,8 @@ static struct worktree *get_main_worktree(int skip_reading_head) worktree->repo = the_repository; worktree->path = strbuf_detach(&worktree_path, NULL); worktree->is_current = is_current_worktree(worktree); - worktree->is_bare = (is_bare_repository_cfg == 1) || - is_bare_repository() || + worktree->is_bare = (the_repository->bare_cfg == 1) || + is_bare_repository(the_repository) || /* * When in a secondary worktree we have to also verify if the main * worktree is bare in $commondir/config.worktree.
diff --git a/wt-status.c b/wt-status.c index b173723..58461e0 100644 --- a/wt-status.c +++ b/wt-status.c
@@ -1366,34 +1366,138 @@ static int split_commit_in_progress(struct wt_status *s) } /* + * If the whitespace-delimited token starting at or just after *pp + * is a hex object id that is longer than its default abbreviation, + * abbreviate it in-place, shrinking `line` accordingly. On return + * *pp points one past the (possibly abbreviated) token. Leaves both + * `line` and *pp-advanced-past-the-token unchanged in all other cases + * (non-hex token, label name, unresolvable, or a refname that happens + * to consist only of hex digits). + */ +static void abbrev_oid_in_line(struct repository *r, struct strbuf *scratch, + struct strbuf *line, bool maybe_label, char **pp) +{ + char *p = *pp; + char *end_of_object_name, saved; + const char *abbrev; + struct object_id oid; + bool have_oid; + + p += strspn(p, " \t"); + end_of_object_name = p + strcspn(p, " \t"); + /* + * For "merge" and "reset" the object name may be a label or + * ref rather than a hex object id. Only abbreviate the object + * name if it is a hex object id. + */ + for (const char *q = p; q < end_of_object_name; q++) { + if (!isxdigit(*q)) + goto out; + } + if (maybe_label) { + strbuf_reset(scratch); + strbuf_addf(scratch, "refs/rewritten/%.*s", + (int)(end_of_object_name - p), p); + if (refs_ref_exists(get_main_ref_store(r), scratch->buf)) + goto out; /* object name was a label */ + } + saved = *end_of_object_name; + *end_of_object_name = '\0'; + have_oid = !repo_get_oid(r, p, &oid); + *end_of_object_name = saved; + if (!have_oid) + goto out; /* invalid object name */ + abbrev = repo_find_unique_abbrev(r, &oid, DEFAULT_ABBREV); + if (!starts_with(p, abbrev)) + goto out; /* object name was a refname containing only xdigits */ + p += strlen(abbrev); + strbuf_remove(line, p - line->buf, end_of_object_name - p); + end_of_object_name = p; +out: + *pp = end_of_object_name; +} + +/* Skip "[ \t]*(-[cC])?", returns true if "-c/-C" was skipped. */ +static bool skip_dash_c(char **pp) +{ + bool ret; + char *p = *pp; + + p += strspn(p, " \t"); + ret = skip_prefix(p, "-C", &p) || skip_prefix(p, "-c", &p); + *pp = p; + + return ret; +} + +/* * Turn * "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message" * into * "pick d6a2f03 some message" * - * The function assumes that the line does not contain useless spaces - * before or after the command. + * Returns false on comment lines, true otherwise */ -static void abbrev_oid_in_line(struct repository *r, struct strbuf *line) +static bool format_todo_line(struct repository *r, struct strbuf *line) { - struct string_list split = STRING_LIST_INIT_DUP; - struct object_id oid; + enum todo_command cmd; + struct strbuf scratch = STRBUF_INIT; + char *p = line->buf; - if (starts_with(line->buf, "exec ") || - starts_with(line->buf, "x ") || - starts_with(line->buf, "label ") || - starts_with(line->buf, "l ")) - return; + if (!sequencer_parse_todo_command((const char **)&p, &cmd)) + return true; /* keep invalid lines */ - if ((2 <= string_list_split(&split, line->buf, " ", 2)) && - !repo_get_oid(r, split.items[1].string, &oid)) { - strbuf_reset(line); - strbuf_addf(line, "%s ", split.items[0].string); - strbuf_add_unique_abbrev(line, &oid, DEFAULT_ABBREV); - for (size_t i = 2; i < split.nr; i++) - strbuf_addf(line, " %s", split.items[i].string); + switch (cmd) { + case TODO_COMMENT: + return false; + + case TODO_MERGE: { + /* + * The argument to -C cannot be a label, but the parents + * can be labels. + */ + bool maybe_label = !skip_dash_c(&p); + + while (true) { + p += strspn(p, " \t"); + if (!p[0] || (p[0] == '#' && (!p[1] || isspace(p[1])))) + break; + abbrev_oid_in_line(r, &scratch, line, maybe_label, &p); + maybe_label = true; + } + break; } - string_list_clear(&split, 0); + + case TODO_FIXUP: + skip_dash_c(&p); + /* fallthrough */ + case TODO_DROP: + case TODO_EDIT: + case TODO_PICK: + case TODO_REVERT: + case TODO_REWORD: + case TODO_SQUASH: + abbrev_oid_in_line(r, &scratch, line, false, &p); + break; + + case TODO_RESET: + abbrev_oid_in_line(r, &scratch, line, true, &p); + break; + /* + * Avoid "default" and instead list all the other commands so + * that -Wswitch (which is included in -Wall) warns if a new + * command is added without handling it in this function. + */ + case TODO_BREAK: + case TODO_EXEC: + case TODO_LABEL: + case TODO_NOOP: + case TODO_UPDATE_REF: + break; + } + + strbuf_release(&scratch); + return true; } static int read_rebase_todolist(struct repository *r, const char *fname, struct string_list *lines) @@ -1411,13 +1515,9 @@ static int read_rebase_todolist(struct repository *r, const char *fname, struct repo_git_path_replace(r, &buf, "%s", fname)); } while (!strbuf_getline_lf(&buf, f)) { - if (starts_with(buf.buf, comment_line_str)) - continue; strbuf_trim(&buf); - if (!buf.len) - continue; - abbrev_oid_in_line(r, &buf); - string_list_append(lines, buf.buf); + if (format_todo_line(r, &buf)) + string_list_append(lines, buf.buf); } fclose(f);