Git development
 help / color / mirror / Atom feed
* Re: Method for Calculating Statistics of Developer Contribution to a Specified Branch.
From: Taylor Blau @ 2023-10-30 21:49 UTC (permalink / raw)
  To: Hongyi Zhao; +Cc: brian m. carlson, Git List
In-Reply-To: <CAGP6PO+SeZPzD21nErX=Vq=+d6oy-kg+diu=irot3enOhpQNMg@mail.gmail.com>

On Tue, Oct 17, 2023 at 07:37:46PM +0800, Hongyi Zhao wrote:
> I want to calculate a certain developer's contribution based on
> different standards of code line count and the importance of the code.

I agree with brian that "number of lines added/removed" is not a perfect
measure of productivity ;-).

But I think that there is a slightly cleaner way to compute the result
you're after, like so:

    git rev-list --author="$who" origin/main |
    git diff-tree --stdin -r --numstat --no-commit-id |
    awk '{ s += $1 + $2 } END { print s }'

Thanks,
Taylor

^ permalink raw reply

* Re: [PATCH 2/2] doc/git-repack: don't mention nonexistent "--unpacked" option
From: Taylor Blau @ 2023-10-30 21:51 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <aa0b4fef4d8397983676394472ff86e468bfc687.1697440686.git.ps@pks.im>

On Mon, Oct 16, 2023 at 09:19:56AM +0200, Patrick Steinhardt wrote:
> The documentation for geometric repacking mentions a "--unpacked" option
> that supposedly changes how loose objects are rolled up. This option has
> never existed, and the implied behaviour, namely to include all unpacked
> objects into the resulting packfile, is in fact the default behaviour.
>
> Correct the documentation to not mention this option.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  Documentation/git-repack.txt | 7 ++-----
>  1 file changed, 2 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
> index dfd2a59c50..d61078b697 100644
> --- a/Documentation/git-repack.txt
> +++ b/Documentation/git-repack.txt
> @@ -226,11 +226,8 @@ uniquely by the set of packs being "rolled-up"; in other words, the
>  packs determined to need to be combined in order to restore a geometric
>  progression.
>  +
> -When `--unpacked` is specified, loose objects are implicitly included in
> -this "roll-up", without respect to their reachability. This is subject
> -to change in the future. This option (implying a drastically different
> -repack mode) is not guaranteed to work with all other combinations of
> -option to `git repack`.
> +Loose objects are implicitly included in this "roll-up", without respect to
> +their reachability. This is subject to change in the future.
>  +

Oops. This refers to the "--unpacked" option that pack-objects takes,
not repack. I agree that mentioning "--unpacked" is too low-level a
detail for this user-facing documentation, so even something like:

    When `repack` passes `--unpacked` down to `pack-objects` (which is
    the default) ...

would be too much detail for this man page.

I am very happy with the patch here as an alternative.

Thanks,
Taylor

^ permalink raw reply

* Re: [PATCH 0/2] doc/git-repack: small fixes for geometric repacks
From: Taylor Blau @ 2023-10-30 21:52 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <cover.1697440686.git.ps@pks.im>

On Mon, Oct 16, 2023 at 09:19:46AM +0200, Patrick Steinhardt wrote:
> Patrick Steinhardt (2):
>   doc/git-repack: fix syntax for `-g` shorthand option
>   doc/git-repack: don't mention nonexistent "--unpacked" option
>
>  Documentation/git-repack.txt | 9 +++------
>  1 file changed, 3 insertions(+), 6 deletions(-)

Sorry that this one fell off of my queue. These are both looking good to
me, thanks for finding and fixing them!

Thanks,
Taylor

^ permalink raw reply

* Re: [PATCH 2/2] Documentation/gitformat-pack.txt: fix incorrect MIDX documentation
From: Taylor Blau @ 2023-10-30 21:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King
In-Reply-To: <xmqq5y3b4id2.fsf@gitster.g>

On Thu, Oct 12, 2023 at 02:54:17PM -0700, Junio C Hamano wrote:
> Taylor Blau <me@ttaylorr.com> writes:
>
> > diff --git a/Documentation/gitformat-pack.txt b/Documentation/gitformat-pack.txt
> > index d7153962d4..54000c9412 100644
> > --- a/Documentation/gitformat-pack.txt
> > +++ b/Documentation/gitformat-pack.txt
> > @@ -392,8 +392,9 @@ CHUNK DATA:
> >  	Packfile Names (ID: {'P', 'N', 'A', 'M'})
> >  	    Stores the packfile names as concatenated, NUL-terminated strings.
>
> Not a problem this series (neither this or the previous step)
> introduces, but I had to read the implementation of
> write_midx_pack_names() to find out what "concatenated
> NUL-terminated string" really means.  The code has a list of
> strings, writes each of them as a NUL-terminated string in sequence,
> and to align the beginning of the next chunk, NULs are added to make
> the whole thing multiple of MIDX_CHUNK_ALIGNMENT bytes.
>
> A naive reader code might implement a loop like so:
>
> 	while (ptr[0] != '\0') {
> 		endp = strlen(ptr);
> 		... ptr[0..endp] is one pathname ...
> 		ptr = endp + 1;
> 	}
>
> expecting that the terminating NUL of the last entry would be
> followed by a NUL, but that is buggy.  The sum of the pathname
> strings (with one NUL after each) may happen to be multiple of
> MIDX_CHUNK_ALIGNMENT bytes, in which case no extra padding NUL bytes
> will be there.  So the reader also needs to pay attention to the
> chunk size to notice when to stop reading.  It feels somewhat
> suboptimal.

I agree.

> >  	    Packfiles must be listed in lexicographic order for fast lookups by
> > -	    name. This is the only chunk not guaranteed to be a multiple of four
> > -	    bytes in length, so should be the last chunk for alignment reasons.
> > +	    name. Individual entries in this chunk are not guarenteed to be
> > +	    aligned. The chunk is externally padded with zeros to align
> > +	    remaining chunks.
>
> I am not sure what "externally padded" means.

How about something like this, instead?

--- 8< ---
diff --git a/Documentation/gitformat-pack.txt b/Documentation/gitformat-pack.txt
index 0bc80f0d46..229490f82f 100644
--- a/Documentation/gitformat-pack.txt
+++ b/Documentation/gitformat-pack.txt
@@ -392,9 +392,10 @@ CHUNK DATA:
 	Packfile Names (ID: {'P', 'N', 'A', 'M'})
 	    Stores the packfile names as concatenated, NUL-terminated strings.
 	    Packfiles must be listed in lexicographic order for fast lookups by
-	    name. Individual entries in this chunk are not guarenteed to be
-	    aligned. The chunk is externally padded with zeros to align
-	    remaining chunks.
+	    name. Individual entries in this chunk are not guaranteed to be
+	    aligned, since the packfile names can be of arbitrary length. The
+	    chunk itself is padded at the end with NUL bytes in order to align
+	    the remaining chunks.

 	OID Fanout (ID: {'O', 'I', 'D', 'F'})
 	    The ith entry, F[i], stores the number of OIDs with first
--- >8 ---

Thanks,
Taylor

^ permalink raw reply related

* Re: Bug: Git grep -f reads the filename relative to the repository root
From: Taylor Blau @ 2023-10-30 21:58 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Erik Cervin Edin, Git Mailing List
In-Reply-To: <xmqqedhzg37z.fsf@gitster.g>

On Thu, Oct 12, 2023 at 10:28:16AM -0700, Junio C Hamano wrote:
> Erik Cervin Edin <erik@cervined.in> writes:
>
> > In the Git repository, I ran
> >
> >     echo tig > pattern-file &&
> >         echo git > xdiff/pattern-file &&
> >         cd xdfiff &&
> >         git grep -f pattern-file
> >
> > What did you expect to happen? (Expected behavior)
> >
> > Git grep -f to read the pattern-file, in the xdiff directory and
> > search for lines matching `git` in the xdiff directory.
>
> That does sound like a bug.  It should use the original directory as
> the base of the relative path computation, similar to the way how
> OPT_FILENAME() options are handled.
>
> Perhaps something along this line, but this is not even compile
> tested yet.

Just going through old mail that I didn't have a chance to respond to,
the proposed patch that you included here does compile and pass t7810
for me, and the fix looks reasonable as-is. I don't think I see this
patch on master, but would have no objections to you merging it down.

Thanks,
Taylor

^ permalink raw reply

* Screenshot (Oct 30, 2023 5:13:22 PM)
From: Yolanda Henderson @ 2023-10-30 22:16 UTC (permalink / raw)
  To: Cash Support, compliance, Cash Book Email, GCash Help Center,
	support, support, Dropbox, legalnotices, efprosessing,
	googleplay-support, GCash Acquiring Team, Googleone-support, git


[-- Attachment #1.1: Type: text/plain, Size: 133 bytes --]

Yolanda Henderson I don't know who Gmail that they got this going to but
it's not mine it's called salt something and it's not right

[-- Attachment #1.2: Type: text/html, Size: 223 bytes --]

[-- Attachment #2: Screenshot_20231030-171322.png --]
[-- Type: image/png, Size: 47210 bytes --]

^ permalink raw reply

* Re: [PATCH] reflog: fix expire --single-worktree
From: Junio C Hamano @ 2023-10-30 23:11 UTC (permalink / raw)
  To: René Scharfe; +Cc: Git List, John Cai
In-Reply-To: <82b832cb-da0b-47cf-9b5d-e8011a222151@web.de>

René Scharfe <l.s.r@web.de> writes:

> Am 29.10.23 um 23:31 schrieb Junio C Hamano:
>> René Scharfe <l.s.r@web.de> writes:
>>
>>> ... and added a non-printable short flag for it, presumably by
>>> accident.
>>
>> Very well spotted.
>>
>> FWIW, with the following patch on top of this patch, all tests pass
>> (and without your fix, of course this notices the "\001" and breaks
>> numerous tests that use "git reflog").  So you seem to have found
>> the only one broken instance (among those that are tested, anyway).
>>
>>  parse-options.c | 3 ++-
>>  1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git i/parse-options.c w/parse-options.c
>> index 093eaf2db8..be8bedba29 100644
>> --- i/parse-options.c
>> +++ w/parse-options.c
>> @@ -469,7 +469,8 @@ static void parse_options_check(const struct option *opts)
>>  			optbug(opts, "uses incompatible flags "
>>  			       "LASTARG_DEFAULT and OPTARG");
>>  		if (opts->short_name) {
>> -			if (0x7F <= opts->short_name)
>> +			if (opts->short_name &&
>> +			    (opts->short_name < 0x21 || 0x7F <= opts->short_name))
>
> Good idea.  This is equivalent to !isprint(opts->short_name), which I
> find to be more readable here.

Thanks---I didn't think of using !isprint() but you are right.  It
is much shorter.

I am not absolutely certain if it is easier to read, though.  I get
always confused when asking myself if SP, HT, and LF are printables.
(in other words, I cannot immediately answer "does 'printable' mean
'can be sent to a teletype and have it do what is expected to be
done?"---the question I should be asking myself is "is 'printable'
synonym to 'when printed, some ink is consumed'?").

> Seeing why "char short_opts[128];" a
> few lines up is big enough would become a bit harder, though.

Sorry, but I do not quite follow.  We used to allow anything below
0x7e; now we clip that range further to reject anything below 0x21.
If [128] was big enough, it still is big enough, no?

Because the type of .short_name member is "int", we could have had
negative number in there and access to short_opts[] on the next line
would have been out of bounds.  By clipping the lower bound, we get
rid of that risk, no?

>>  				optbug(opts, "invalid short name");
>>  			else if (short_opts[opts->short_name]++)
>>  				optbug(opts, "short name already used");

^ permalink raw reply

* Re: [PATCH] reflog: fix expire --single-worktree
From: Junio C Hamano @ 2023-10-30 23:24 UTC (permalink / raw)
  To: Taylor Blau; +Cc: René Scharfe, Git List, John Cai
In-Reply-To: <ZT/mN9RouiqzL9aT@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

> This makes sense to me, but obviously won't catch non-tested cases.

True, but I think we can practically ignore non-tested cases.

The parse_options_check() is used to validate the whole options[]
array that is passed to parse_options() family of API functions, and
its validation is not limited to the options that are given from the
command line in an invocation.  A non-tested case would happen when
a developer prepares and populates "struct option options[];" array
for a (possibly new) git subcommand *and* never uses that array to
call parse_options() in their implementation of that subcommand.
The compiler would catch the unused variable options[] in such a
case, and mark 1 eyeball would notice that none of the options
defined in that array are actually understood by the command, no?

^ permalink raw reply

* Re: Repository cloned using SSH does not respect bare repository initial branch
From: Sheik @ 2023-10-30 23:30 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20231030174307.GA854621@coredump.intra.peff.net>

On 31/10/23 04:43, Jeff King wrote:

> On Tue, Oct 31, 2023 at 02:24:46AM +1100, Sheik wrote:
>
>> Server version is same as client (v2.42.0) as I ran these commands all on
>> the same machine.
> OK. The next thing I'd check is running both commands with:
>
>    GIT_TRACE_PACKET=1 git clone ...
>
> to see the protocol trace, and how it differs between the two. What I
> suspect you may see is that the local clone is using the "v2" protocol
> (a capabilities report, followed by "ls-refs", which mentions the symref
> value of HEAD), and the ssh one uses the older "v0" (it goes straight to
> the ref advertisement).
>
> Quoting from 59e1205d16 (ls-refs: report unborn targets of symrefs,
> 2021-02-05), the commit I mentioned before:
>
>      This change is only for protocol v2. A similar change for protocol
>      v0 would require independent protocol design (there being no
>      analogous position to signal support for "unborn") and client-side
>      plumbing of the data required, so the scope of this patch set is
>      limited to protocol v2.
>
> So in v0 the server doesn't pass back sufficient information for the
> client to know about the name of the unborn HEAD branch.
>
> If that's the culprit, the next question of course is why we'd do v2
> locally versus v0 overssh. And that probably has to do with how we
> trigger the protocol upgrade. To see if the server supports v2, the
> client passes extra information "out of band". For git-over-http, this
> happens in an extra HTTP header. For local repositories, it happens in
> an environment variable ($GIT_PROTOCOL). For git-over-ssh it happens in
> that sameenvironment variable, which we instruct the ssh client to pass
> using "-o SendEnv". But:
>
>    1. If your ssh client doesn't look like openssh, we don't know if it
>       supports "-o" and may skip it. See the discussion in ssh.variant in
>       "git help config".
>
>    2. Some servers need to be configured to allow the client to set
>       environment variables. In the case of openssh, you'd want a line
>       like this in your sshd_config file:
>
>         AcceptEnv GIT_PROTOCOL
>
> Of the two, I'd guess that the second one is more likely to be your
> problem (since you're running Linux, where openssh is the norm).
>
> -Peff


Thanks Jeff, tracing and setting the AcceptEnv indeed did the trick and 
workflow now works as expected.

Test steps on Debian/OpenSsh:

Server
1. Edit /etc/ssh/sshd_config
2. Add AcceptEnv GIT_PROTOCOL
3. systemctl restart sshd

Client
1. Enable ssh logging:
    export GIT_SSH_COMMAND=ssh -v
2. git clone ...
3. Output from ssh shows variable being sent (although regardless if 
AcceptEnv was set or not):
    debug1: channel 0: setting env GIT_PROTOCOL = "version=2"
    debug2: channel 0: request env confirm 0

References
1. 
https://git-scm.com/docs/git/2.42.0#Documentation/git.txt-codeGITPROTOCOLcode 

2. https://git-scm.com/docs/gitprotocol-v2#_ssh_and_file_transport
3. 
https://git-scm.com/docs/git/2.42.0#Documentation/git.txt-codeGITSSHCOMMANDcode 


Thanks
Sheik


^ permalink raw reply

* Re: [PATCH v3] bugreport: reject positional arguments
From: Junio C Hamano @ 2023-10-30 23:31 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Eric Sunshine, emilyshaffer, git, Emily Shaffer, Sheik,
	Dragan Simic
In-Reply-To: <881c7fea-47bb-45a9-b6e3-314f9ed9e0cd@gmail.com>

Phillip Wood <phillip.wood123@gmail.com> writes:

> ... it is determining that grep
> failed in the first place that I find annoying. I've also found the
> output from test_i18ngrep is helpful when debugging CI test failures.

Thanks, I agree with you that it is very useful for debugging to
have an explicit "I did not find what I expected to see".  On the
other hand, the other side is not as severe a problem, I think.  If
"grep" expects not to see an error message in the output but the
output has the error we do not want to see, we will see the message
in the "-v" output when we run test.

So, when we retire test_i18ngrep, what we will primarily miss is
this form of its invocation:

    test_i18ngrep ! <arguments>... <file>

We have a bit more than 100 of them in the t/ hierarchy.

I wonder if it is better to drop support for the positive side,
which allows us to lose the '!' and give it a name better than
"grep", e.g.,  with only the negative match support, what we used to
write:

	test_must_fail git rebase -i primary >output 2>&1 &&
	test_i18ngrep "would be overwritten by checkout:" output &&
	test_i18ngrep ! "BUG" output

would become something like:

	test_must_fail git rebase -i primary >output 2>&1 &&
	grep "would be overwritten by checkout:" output &&
	test_missing "BUG" output

But perhaps it is making it too limited for very little gain.  We
could add "test_grep" that does not do anything more than "grep",
to complement, i.e.,

	test_must_fail git rebase -i primary >output 2>&1 &&
	test_grep "would be overwritten by checkout:" output &&
	test_missing "BUG" output

But if we add "test_grep", those who have experience with writing
and reading our tests will expect "test_grep !" to behave like how
test_i18ngrep behaved, so we will have to support its negation
anyway, i.e. test_missing will either become unnecessary or become a
thin wrapper,

	test_missing () { test_grep ! "$@" }

merely for readability.

I haven't made up my mind on the positive side, but the negative
side (aka "retiring use of 'test_i18ngrep !' from everywhere and use
test_missing instead") would look like this:

 t/test-lib-functions.sh             | 18 ++++++++++++++++++
 t/t0041-usage.sh                    | 12 ++++++------
 t/t1060-object-corruption.sh        |  2 +-
 t/t1091-sparse-checkout-builtin.sh  | 14 +++++++-------
 t/t1430-bad-ref-name.sh             |  2 +-
 t/t1450-fsck.sh                     |  4 ++--
 t/t1506-rev-parse-diagnosis.sh      |  2 +-
 t/t2019-checkout-ambiguous-ref.sh   |  4 ++--
 t/t2020-checkout-detach.sh          |  4 ++--
 t/t2024-checkout-dwim.sh            |  4 ++--
 t/t2402-worktree-list.sh            |  2 +-
 t/t3404-rebase-interactive.sh       |  2 +-
 t/t3507-cherry-pick-conflict.sh     |  2 +-
 t/t3600-rm.sh                       |  2 +-
 t/t4001-diff-rename.sh              |  2 +-
 t/t4014-format-patch.sh             |  2 +-
 t/t4018-diff-funcname.sh            | 12 ++++++------
 t/t4153-am-resume-override-opts.sh  |  2 +-
 t/t5324-split-commit-graph.sh       |  2 +-
 t/t5505-remote.sh                   |  2 +-
 t/t5520-pull.sh                     |  2 +-
 t/t5523-push-upstream.sh            |  8 ++++----
 t/t5534-push-signed.sh              |  2 +-
 t/t5541-http-push-smart.sh          |  4 ++--
 t/t5574-fetch-output.sh             |  2 +-
 t/t5580-unc-paths.sh                |  2 +-
 t/t5606-clone-options.sh            |  2 +-
 t/t6001-rev-list-graft.sh           |  2 +-
 t/t6040-tracking-info.sh            |  4 ++--
 t/t6050-replace.sh                  |  2 +-
 t/t6423-merge-rename-directories.sh | 12 ++++++------
 t/t6433-merge-toplevel.sh           |  2 +-
 t/t6437-submodule-merge.sh          |  2 +-
 t/t6500-gc.sh                       |  2 +-
 t/t7400-submodule-basic.sh          | 16 ++++++++--------
 t/t7414-submodule-mistakes.sh       |  6 +++---
 t/t7416-submodule-dash-url.sh       |  2 +-
 t/t7450-bad-git-dotfiles.sh         |  2 +-
 t/t7502-commit-porcelain.sh         | 10 +++++-----
 t/t7508-status.sh                   |  8 ++++----
 t/t7518-ident-corner-cases.sh       |  2 +-
 t/t7519-status-fsmonitor.sh         |  4 ++--
 t/t7520-ignored-hook-warning.sh     |  6 +++---
 t/t7601-merge-pull-config.sh        | 38 ++++++++++++++++++-------------------
 t/t7810-grep.sh                     |  2 +-
 t/t7816-grep-binary-pattern.sh      |  2 +-
 t/t9800-git-p4-basic.sh             |  2 +-
 t/t9807-git-p4-submit.sh            |  2 +-
 48 files changed, 132 insertions(+), 114 deletions(-)

diff --git i/t/test-lib-functions.sh w/t/test-lib-functions.sh
index 2f8868caa1..6cf08f6f7b 100644
--- i/t/test-lib-functions.sh
+++ w/t/test-lib-functions.sh
@@ -1245,6 +1245,24 @@ test_i18ngrep () {
 	return 1
 }
 
+test_missing () {
+	eval "last_arg=\${$#}"
+
+	test -f "$last_arg" ||
+	BUG "test_missing requires a file to read as the last parameter"
+
+	! grep "$@" && return 0
+
+	echo >&4 "error: '! grep $@' did find a match in:"
+	if test -s "$last_arg"
+	then
+		cat >&4 "$last_arg"
+	else
+		echo >&4 "<file '$last_arg' is empty>"
+	fi
+	return 1
+}
+
 # Check if the file expected to be empty is indeed empty, and barfs
 # otherwise.
 
diff --git i/t/t0041-usage.sh w/t/t0041-usage.sh
index 9ea974b0c6..292a79a484 100755
--- i/t/t0041-usage.sh
+++ w/t/t0041-usage.sh
@@ -22,7 +22,7 @@ test_expect_success 'tag --contains <inexistent_tag>' '
 	test_must_fail git tag --contains "notag" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_missing "usage" actual.err
 '
 
 test_expect_success 'tag --no-contains <existent_tag>' '
@@ -35,7 +35,7 @@ test_expect_success 'tag --no-contains <inexistent_tag>' '
 	test_must_fail git tag --no-contains "notag" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_missing "usage" actual.err
 '
 
 test_expect_success 'tag usage error' '
@@ -54,7 +54,7 @@ test_expect_success 'branch --contains <inexistent_commit>' '
 	test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_missing "usage" actual.err
 '
 
 test_expect_success 'branch --no-contains <existent_commit>' '
@@ -67,7 +67,7 @@ test_expect_success 'branch --no-contains <inexistent_commit>' '
 	test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_missing "usage" actual.err
 '
 
 test_expect_success 'branch usage error' '
@@ -86,7 +86,7 @@ test_expect_success 'for-each-ref --contains <inexistent_object>' '
 	test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_missing "usage" actual.err
 '
 
 test_expect_success 'for-each-ref --no-contains <existent_object>' '
@@ -99,7 +99,7 @@ test_expect_success 'for-each-ref --no-contains <inexistent_object>' '
 	test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_missing "usage" actual.err
 '
 
 test_expect_success 'for-each-ref usage error' '
diff --git i/t/t1060-object-corruption.sh w/t/t1060-object-corruption.sh
index 35261afc9d..8429990570 100755
--- i/t/t1060-object-corruption.sh
+++ w/t/t1060-object-corruption.sh
@@ -125,7 +125,7 @@ test_expect_success 'fetch into corrupted repo with index-pack' '
 		cd bit-error-cp &&
 		test_must_fail git -c transfer.unpackLimit=1 \
 			fetch ../no-bit-error 2>stderr &&
-		test_i18ngrep ! -i collision stderr
+		test_missing -i collision stderr
 	)
 '
 
diff --git i/t/t1091-sparse-checkout-builtin.sh w/t/t1091-sparse-checkout-builtin.sh
index 9ceb17f911..a1ec29cb16 100755
--- i/t/t1091-sparse-checkout-builtin.sh
+++ w/t/t1091-sparse-checkout-builtin.sh
@@ -230,7 +230,7 @@ test_expect_success 'cone mode: match patterns' '
 	git -C repo config --worktree core.sparseCheckoutCone true &&
 	rm -rf repo/a repo/folder1 repo/folder2 &&
 	git -C repo read-tree -mu HEAD 2>err &&
-	test_i18ngrep ! "disabling cone patterns" err &&
+	test_missing "disabling cone patterns" err &&
 	git -C repo reset --hard &&
 	check_files repo a folder1 folder2
 '
@@ -401,8 +401,8 @@ test_expect_success 'revert to old sparse-checkout on empty update' '
 		git add file &&
 		git commit -m "test" &&
 		git sparse-checkout set nothing 2>err &&
-		test_i18ngrep ! "Sparse checkout leaves no entry on working directory" err &&
-		test_i18ngrep ! ".git/index.lock" err &&
+		test_missing "Sparse checkout leaves no entry on working directory" err &&
+		test_missing ".git/index.lock" err &&
 		git sparse-checkout set --no-cone file
 	)
 '
@@ -418,7 +418,7 @@ test_expect_success '.gitignore should not warn about cone mode' '
 	git -C repo config --worktree core.sparseCheckoutCone true &&
 	echo "**/bin/*" >repo/.gitignore &&
 	git -C repo reset --hard 2>err &&
-	test_i18ngrep ! "disabling cone patterns" err
+	test_missing "disabling cone patterns" err
 '
 
 test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
@@ -495,7 +495,7 @@ test_expect_failure 'sparse-checkout reapply' '
 
 	git -C tweak checkout HEAD deep/deeper2/a &&
 	git -C tweak sparse-checkout reapply 2>err &&
-	test_i18ngrep ! "warning.*The following paths are not up to date" err &&
+	test_missing "warning.*The following paths are not up to date" err &&
 	test_path_is_missing tweak/deep/deeper2/a &&
 	test_i18ngrep "warning.*The following paths are unmerged" err &&
 	test_path_is_file tweak/folder1/a &&
@@ -578,7 +578,7 @@ test_expect_success 'check-rules interaction with submodules' '
 	git -C super ls-tree --name-only -r HEAD >all-files &&
 	git -C super sparse-checkout check-rules >check-rules-matches <all-files &&
 
-	test_i18ngrep ! "modules/" check-rules-matches &&
+	test_missing "modules/" check-rules-matches &&
 	test_i18ngrep "folder1/" check-rules-matches
 '
 
@@ -947,7 +947,7 @@ test_expect_success 'check-rules cone mode' '
 	git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
 
 	test_i18ngrep "deep/deeper1/deepest/a" check-rules-file &&
-	test_i18ngrep ! "deep/deeper2" check-rules-file &&
+	test_missing "deep/deeper2" check-rules-file &&
 
 	test_cmp check-rules-file ls-files &&
 	test_cmp check-rules-file check-rules-default
diff --git i/t/t1430-bad-ref-name.sh w/t/t1430-bad-ref-name.sh
index ff1c967d55..8953b65179 100755
--- i/t/t1430-bad-ref-name.sh
+++ w/t/t1430-bad-ref-name.sh
@@ -173,7 +173,7 @@ test_expect_success 'for-each-ref emits warnings for broken names' '
 	! grep -e "badname" output &&
 	! grep -e "broken\.\.\.symref" output &&
 	test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
-	test_i18ngrep ! "ignoring broken ref refs/heads/badname" error &&
+	test_missing "ignoring broken ref refs/heads/badname" error &&
 	test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
 '
 
diff --git i/t/t1450-fsck.sh w/t/t1450-fsck.sh
index 10a539158c..e8222bc9de 100755
--- i/t/t1450-fsck.sh
+++ w/t/t1450-fsck.sh
@@ -343,7 +343,7 @@ test_expect_success 'unparseable tree object' '
 	test_must_fail git fsck 2>out &&
 	test_i18ngrep "error: empty filename in tree entry" out &&
 	test_i18ngrep "$tree_sha1" out &&
-	test_i18ngrep ! "fatal: empty filename in tree entry" out
+	test_missing "fatal: empty filename in tree entry" out
 '
 
 test_expect_success 'tree entry with type mismatch' '
@@ -361,7 +361,7 @@ test_expect_success 'tree entry with type mismatch' '
 	git update-ref refs/heads/type_mismatch $commit &&
 	test_must_fail git fsck >out 2>&1 &&
 	test_i18ngrep "is a blob, not a tree" out &&
-	test_i18ngrep ! "dangling blob" out
+	test_missing "dangling blob" out
 '
 
 test_expect_success 'tree entry with bogus mode' '
diff --git i/t/t1506-rev-parse-diagnosis.sh w/t/t1506-rev-parse-diagnosis.sh
index 18688cae17..abd3019504 100755
--- i/t/t1506-rev-parse-diagnosis.sh
+++ w/t/t1506-rev-parse-diagnosis.sh
@@ -171,7 +171,7 @@ test_expect_success 'relative path when cwd is outside worktree' '
 
 test_expect_success '<commit>:file correctly diagnosed after a pathname' '
 	test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
-	test_i18ngrep ! "exists on disk" error &&
+	test_missing "exists on disk" error &&
 	test_i18ngrep "no such path in the working tree" error &&
 	cat >expect <<-\EOF &&
 	file.txt
diff --git i/t/t2019-checkout-ambiguous-ref.sh w/t/t2019-checkout-ambiguous-ref.sh
index 9540588664..e87d2a5f13 100755
--- i/t/t2019-checkout-ambiguous-ref.sh
+++ w/t/t2019-checkout-ambiguous-ref.sh
@@ -33,7 +33,7 @@ test_expect_success 'checkout chooses branch over tag' '
 
 test_expect_success 'checkout reports switch to branch' '
 	test_i18ngrep "Switched to branch" stderr &&
-	test_i18ngrep ! "^HEAD is now at" stderr
+	test_missing "^HEAD is now at" stderr
 '
 
 test_expect_success 'checkout vague ref succeeds' '
@@ -55,7 +55,7 @@ test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
 
 test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
 	test_i18ngrep "Switched to branch" stderr &&
-	test_i18ngrep ! "^HEAD is now at" stderr
+	test_missing "^HEAD is now at" stderr
 '
 
 test_done
diff --git i/t/t2020-checkout-detach.sh w/t/t2020-checkout-detach.sh
index 2eab6474f8..975d17c8a6 100755
--- i/t/t2020-checkout-detach.sh
+++ w/t/t2020-checkout-detach.sh
@@ -18,10 +18,10 @@ check_not_detached () {
 PREV_HEAD_DESC='Previous HEAD position was'
 check_orphan_warning() {
 	test_i18ngrep "you are leaving $2 behind" "$1" &&
-	test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
+	test_missing "$PREV_HEAD_DESC" "$1"
 }
 check_no_orphan_warning() {
-	test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
+	test_missing "you are leaving .* commit.*behind" "$1" &&
 	test_i18ngrep "$PREV_HEAD_DESC" "$1"
 }
 
diff --git i/t/t2024-checkout-dwim.sh w/t/t2024-checkout-dwim.sh
index 74049a9812..b66b2baae6 100755
--- i/t/t2024-checkout-dwim.sh
+++ w/t/t2024-checkout-dwim.sh
@@ -110,7 +110,7 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'
 		checkout foo 2>stderr &&
 	test_branch main &&
 	status_uno_is_clean &&
-	test_i18ngrep ! "^hint: " stderr
+	test_missing "^hint: " stderr
 '
 
 test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
@@ -118,7 +118,7 @@ test_expect_success PERL 'checkout -p with multiple remotes does not print advic
 	test_might_fail git branch -D foo &&
 
 	git checkout -p foo 2>stderr &&
-	test_i18ngrep ! "^hint: " stderr &&
+	test_missing "^hint: " stderr &&
 	status_uno_is_clean
 '
 
diff --git i/t/t2402-worktree-list.sh w/t/t2402-worktree-list.sh
index 9ad9be0c20..ee00343ad1 100755
--- i/t/t2402-worktree-list.sh
+++ w/t/t2402-worktree-list.sh
@@ -156,7 +156,7 @@ test_expect_success '"list" all worktrees with prunable consistent with "prune"'
 	! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
 	git worktree prune --verbose 2>out &&
 	test_i18ngrep "^Removing worktrees/prunable" out &&
-	test_i18ngrep ! "^Removing worktrees/unprunable" out
+	test_missing "^Removing worktrees/unprunable" out
 '
 
 test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
diff --git i/t/t3404-rebase-interactive.sh w/t/t3404-rebase-interactive.sh
index 8ea2bf1302..77872cc56b 100755
--- i/t/t3404-rebase-interactive.sh
+++ w/t/t3404-rebase-interactive.sh
@@ -605,7 +605,7 @@ test_expect_success 'clean error after failed "exec"' '
 	git add file7 &&
 	test_must_fail git rebase --continue 2>error &&
 	test_i18ngrep "you have staged changes in your working tree" error &&
-	test_i18ngrep ! "could not open.*for reading" error
+	test_missing "could not open.*for reading" error
 '
 
 test_expect_success 'rebase a detached HEAD' '
diff --git i/t/t3507-cherry-pick-conflict.sh w/t/t3507-cherry-pick-conflict.sh
index f32799e046..e91616ece7 100755
--- i/t/t3507-cherry-pick-conflict.sh
+++ w/t/t3507-cherry-pick-conflict.sh
@@ -563,7 +563,7 @@ test_expect_success 'cherry-pick preserves sparse-checkout' '
 	echo /unrelated >.git/info/sparse-checkout &&
 	git read-tree --reset -u HEAD &&
 	test_must_fail git cherry-pick -Xours picked>actual &&
-	test_i18ngrep ! "Changes not staged for commit:" actual
+	test_missing "Changes not staged for commit:" actual
 '
 
 test_expect_success 'cherry-pick --continue remembers --keep-redundant-commits' '
diff --git i/t/t3600-rm.sh w/t/t3600-rm.sh
index 0e8afe49ed..4de4d99464 100755
--- i/t/t3600-rm.sh
+++ w/t/t3600-rm.sh
@@ -276,7 +276,7 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '
 	blob=$(echo blob | git hash-object -w --stdin) &&
 	printf "100644 $blob %d\tblob\n" 1 2 3 | git update-index --index-info &&
 	git rm blob >msg 2>&1 &&
-	test_i18ngrep ! "needs merge" msg &&
+	test_missing "needs merge" msg &&
 	test_must_fail git ls-files -s --error-unmatch blob
 '
 
diff --git i/t/t4001-diff-rename.sh w/t/t4001-diff-rename.sh
index 3dc9047044..c0f6e1f242 100755
--- i/t/t4001-diff-rename.sh
+++ w/t/t4001-diff-rename.sh
@@ -145,7 +145,7 @@ test_expect_success 'test diff.renames=true for git status' '
 
 test_expect_success 'test diff.renames=false for git status' '
 	git -c diff.renames=false status >out &&
-	test_i18ngrep ! "renamed: .*path1 -> subdir/path1" out &&
+	test_missing "renamed: .*path1 -> subdir/path1" out &&
 	test_i18ngrep "new file: .*subdir/path1" out &&
 	test_i18ngrep "deleted: .*[^/]path1" out
 '
diff --git i/t/t4014-format-patch.sh w/t/t4014-format-patch.sh
index 0a4ab36c3a..648992741a 100755
--- i/t/t4014-format-patch.sh
+++ w/t/t4014-format-patch.sh
@@ -2404,7 +2404,7 @@ test_expect_success 'interdiff: cover-letter' '
 	EOF
 	git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
 	test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
-	test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
+	test_missing "^Interdiff:$" 0001-fleep.patch &&
 	sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual &&
 	test_cmp expect actual
 '
diff --git i/t/t4018-diff-funcname.sh w/t/t4018-diff-funcname.sh
index c8d555771d..35347e10bb 100755
--- i/t/t4018-diff-funcname.sh
+++ w/t/t4018-diff-funcname.sh
@@ -53,15 +53,15 @@ do
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index \
 			A.java B.java 2>msg &&
-		test_i18ngrep ! fatal msg &&
-		test_i18ngrep ! error msg
+		test_missing fatal msg &&
+		test_missing error msg
 	'
 	test_expect_success "builtin $p wordRegex pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index --word-diff \
 			A.java B.java 2>msg &&
-		test_i18ngrep ! fatal msg &&
-		test_i18ngrep ! error msg
+		test_missing fatal msg &&
+		test_missing error msg
 	'
 
 	test_expect_success "builtin $p pattern compiles on bare repo with --attr-source" '
@@ -79,8 +79,8 @@ do
 		git -C bare.git symbolic-ref HEAD refs/heads/master &&
 		test_expect_code 1 git -C bare.git --attr-source=branchA \
 			diff --exit-code HEAD:A.java HEAD:B.java 2>msg &&
-		test_i18ngrep ! fatal msg &&
-		test_i18ngrep ! error msg
+		test_missing fatal msg &&
+		test_missing error msg
 	'
 done
 
diff --git i/t/t4153-am-resume-override-opts.sh w/t/t4153-am-resume-override-opts.sh
index b7c3861407..664236914c 100755
--- i/t/t4153-am-resume-override-opts.sh
+++ w/t/t4153-am-resume-override-opts.sh
@@ -53,7 +53,7 @@ test_expect_success '--no-quiet overrides --quiet' '
 	# Applying side1 will be quiet.
 	test_must_fail git am --quiet side[123].eml >out &&
 	test_path_is_dir .git/rebase-apply &&
-	test_i18ngrep ! "^Applying: " out &&
+	test_missing "^Applying: " out &&
 	echo side1 >file &&
 	git add file &&
 
diff --git i/t/t5324-split-commit-graph.sh w/t/t5324-split-commit-graph.sh
index 97eb6d2e72..210039d9d5 100755
--- i/t/t5324-split-commit-graph.sh
+++ w/t/t5324-split-commit-graph.sh
@@ -422,7 +422,7 @@ test_expect_success 'add octopus merge' '
 	git commit-graph verify --progress 2>err &&
 	test_line_count = 1 err &&
 	grep "Verifying commits in commit graph: 100% (18/18)" err &&
-	test_i18ngrep ! warning err &&
+	test_missing warning err &&
 	test_line_count = 3 $graphdir/commit-graph-chain
 '
 
diff --git i/t/t5505-remote.sh w/t/t5505-remote.sh
index 43b7bcd715..8e455d94f4 100755
--- i/t/t5505-remote.sh
+++ w/t/t5505-remote.sh
@@ -1458,7 +1458,7 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
 			test_must_fail git -c advice.pushUnqualifiedRefName=false \
 				push origin $oid:dst 2>err &&
 			test_i18ngrep "error: The destination you" err &&
-			test_i18ngrep ! "hint: Did you mean" err ||
+			test_missing "hint: Did you mean" err ||
 			exit 1
 		done
 	)
diff --git i/t/t5520-pull.sh w/t/t5520-pull.sh
index 0b72112fb1..22ccf998f9 100755
--- i/t/t5520-pull.sh
+++ w/t/t5520-pull.sh
@@ -530,7 +530,7 @@ test_expect_success 'pull --rebase does not warn on --no-verify-signatures' '
 	echo new >expect &&
 	git show HEAD:file2 >actual &&
 	test_cmp expect actual &&
-	test_i18ngrep ! "verify-signatures" err
+	test_missing "verify-signatures" err
 '
 
 # add a feature branch, keep-merge, that is merged into main, so the
diff --git i/t/t5523-push-upstream.sh w/t/t5523-push-upstream.sh
index 1b8d609879..8e89b279e8 100755
--- i/t/t5523-push-upstream.sh
+++ w/t/t5523-push-upstream.sh
@@ -95,7 +95,7 @@ test_expect_success 'progress messages do not go to non-tty' '
 
 	# skip progress messages, since stderr is non-tty
 	git push -u upstream main >out 2>err &&
-	test_i18ngrep ! "Writing objects" err
+	test_missing "Writing objects" err
 '
 
 test_expect_success 'progress messages go to non-tty (forced)' '
@@ -110,15 +110,15 @@ test_expect_success TTY 'push -q suppresses progress' '
 	ensure_fresh_upstream &&
 
 	test_terminal git push -u -q upstream main >out 2>err &&
-	test_i18ngrep ! "Writing objects" err
+	test_missing "Writing objects" err
 '
 
 test_expect_success TTY 'push --no-progress suppresses progress' '
 	ensure_fresh_upstream &&
 
 	test_terminal git push -u --no-progress upstream main >out 2>err &&
-	test_i18ngrep ! "Unpacking objects" err &&
-	test_i18ngrep ! "Writing objects" err
+	test_missing "Unpacking objects" err &&
+	test_missing "Writing objects" err
 '
 
 test_expect_success TTY 'quiet push' '
diff --git i/t/t5534-push-signed.sh w/t/t5534-push-signed.sh
index 7c0a148e73..37870326e1 100755
--- i/t/t5534-push-signed.sh
+++ w/t/t5534-push-signed.sh
@@ -378,7 +378,7 @@ test_expect_success GPG 'failed atomic push does not execute GPG' '
 			--signed --atomic --porcelain \
 			dst noop ff noff >out 2>err &&
 
-	test_i18ngrep ! "gpg failed to sign" err &&
+	test_missing "gpg failed to sign" err &&
 	cat >expect <<-EOF &&
 	To dst
 	=	refs/heads/noop:refs/heads/noop	[up to date]
diff --git i/t/t5541-http-push-smart.sh w/t/t5541-http-push-smart.sh
index d0211cd8be..a3824af91c 100755
--- i/t/t5541-http-push-smart.sh
+++ w/t/t5541-http-push-smart.sh
@@ -312,7 +312,7 @@ test_expect_success TTY 'push --no-progress silences progress but not status' '
 	test_commit no-progress &&
 	test_terminal git push --no-progress >output 2>&1 &&
 	test_i18ngrep "^To http" output &&
-	test_i18ngrep ! "^Writing objects" output
+	test_missing "^Writing objects" output
 '
 
 test_expect_success 'push --progress shows progress to non-tty' '
@@ -492,7 +492,7 @@ test_expect_success 'colorize errors/hints' '
 	test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
 	test_i18ngrep "<RED>error: failed to push some refs" decoded &&
 	test_i18ngrep "<YELLOW>hint: " decoded &&
-	test_i18ngrep ! "^hint: " decoded
+	test_missing "^hint: " decoded
 '
 
 test_expect_success 'report error server does not provide ref status' '
diff --git i/t/t5574-fetch-output.sh w/t/t5574-fetch-output.sh
index 90e6dcb9a7..95c105926e 100755
--- i/t/t5574-fetch-output.sh
+++ w/t/t5574-fetch-output.sh
@@ -286,7 +286,7 @@ test_expect_success '--no-show-forced-updates' '
 	(
 		cd no-forced-update-clone &&
 		git fetch --no-show-forced-updates origin 2>output &&
-		test_i18ngrep ! "(forced update)" output
+		test_missing "(forced update)" output
 	)
 '
 
diff --git i/t/t5580-unc-paths.sh w/t/t5580-unc-paths.sh
index cd7604fff9..07c24a463d 100755
--- i/t/t5580-unc-paths.sh
+++ w/t/t5580-unc-paths.sh
@@ -75,7 +75,7 @@ test_expect_success push '
 test_expect_success MINGW 'remote nick cannot contain backslashes' '
 	BACKSLASHED="$(winpwd | tr / \\\\)" &&
 	git ls-remote "$BACKSLASHED" 2>err &&
-	test_i18ngrep ! "unable to access" err
+	test_missing "unable to access" err
 '
 
 test_expect_success 'unc alternates' '
diff --git i/t/t5606-clone-options.sh w/t/t5606-clone-options.sh
index 5890319b97..9c480c7734 100755
--- i/t/t5606-clone-options.sh
+++ w/t/t5606-clone-options.sh
@@ -149,7 +149,7 @@ test_expect_success 'redirected clone does not show progress' '
 
 	git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
 	! grep % err &&
-	test_i18ngrep ! "Checking connectivity" err
+	test_missing "Checking connectivity" err
 
 '
 
diff --git i/t/t6001-rev-list-graft.sh w/t/t6001-rev-list-graft.sh
index 16635ecc33..38565e68ff 100755
--- i/t/t6001-rev-list-graft.sh
+++ w/t/t6001-rev-list-graft.sh
@@ -121,7 +121,7 @@ test_expect_success 'show advice that grafts are deprecated' '
 	test_i18ngrep "git replace" err &&
 	test_config advice.graftFileDeprecated false &&
 	git show HEAD 2>err &&
-	test_i18ngrep ! "git replace" err
+	test_missing "git replace" err
 '
 
 test_done
diff --git i/t/t6040-tracking-info.sh w/t/t6040-tracking-info.sh
index 7ddbd96e58..9034fd4b1e 100755
--- i/t/t6040-tracking-info.sh
+++ w/t/t6040-tracking-info.sh
@@ -253,7 +253,7 @@ test_expect_success 'fail to track lightweight tags' '
 	git checkout main &&
 	git tag light &&
 	test_must_fail git branch --track lighttrack light >actual &&
-	test_i18ngrep ! "set up to track" actual &&
+	test_missing "set up to track" actual &&
 	test_must_fail git checkout lighttrack
 '
 
@@ -261,7 +261,7 @@ test_expect_success 'fail to track annotated tags' '
 	git checkout main &&
 	git tag -m heavy heavy &&
 	test_must_fail git branch --track heavytrack heavy >actual &&
-	test_i18ngrep ! "set up to track" actual &&
+	test_missing "set up to track" actual &&
 	test_must_fail git checkout heavytrack
 '
 
diff --git i/t/t6050-replace.sh w/t/t6050-replace.sh
index c9925edf20..f71812ed14 100755
--- i/t/t6050-replace.sh
+++ w/t/t6050-replace.sh
@@ -492,7 +492,7 @@ test_expect_success '--convert-graft-file' '
 	git status 2>stderr &&
 	test_i18ngrep "hint:.*grafts is deprecated" stderr &&
 	git replace --convert-graft-file 2>stderr &&
-	test_i18ngrep ! "hint:.*grafts is deprecated" stderr &&
+	test_missing "hint:.*grafts is deprecated" stderr &&
 	test_path_is_missing .git/info/grafts &&
 
 	: verify that the history is now "grafted" &&
diff --git i/t/t6423-merge-rename-directories.sh w/t/t6423-merge-rename-directories.sh
index 944de75b80..bff2690b98 100755
--- i/t/t6423-merge-rename-directories.sh
+++ w/t/t6423-merge-rename-directories.sh
@@ -591,7 +591,7 @@ test_expect_success '2b: Directory split into two on one side, with equal number
 		git rev-parse >expect \
 			 O:z/b  O:z/c  B:x/d &&
 		test_cmp expect actual &&
-		test_i18ngrep ! "CONFLICT.*directory rename split" out
+		test_missing "CONFLICT.*directory rename split" out
 	)
 '
 
@@ -727,7 +727,7 @@ test_expect_success '3b: Avoid implicit rename if involved as source on current
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
-		test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
+		test_missing CONFLICT.*rename/rename.*y/d out &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -3606,7 +3606,7 @@ test_expect_merge_algorithm failure success '10e: Does git complain about untrac
 		echo random >z/c &&
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-		test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
+		test_missing "following untracked working tree files would be overwritten by merge" err &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -5821,9 +5821,9 @@ test_expect_success '13e: directory rename detection in recursive case' '
 
 		git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&
 
-		test_i18ngrep ! CONFLICT out &&
-		test_i18ngrep ! BUG: err &&
-		test_i18ngrep ! core.dumped err &&
+		test_missing CONFLICT out &&
+		test_missing BUG: err &&
+		test_missing core.dumped err &&
 		test_must_be_empty err &&
 
 		git ls-files >paths &&
diff --git i/t/t6433-merge-toplevel.sh w/t/t6433-merge-toplevel.sh
index 2b42f095dc..393d127006 100755
--- i/t/t6433-merge-toplevel.sh
+++ w/t/t6433-merge-toplevel.sh
@@ -152,7 +152,7 @@ test_expect_success 'refuse two-project merge by default, quit before --autostas
 	echo change >>one.t &&
 	git diff >expect &&
 	test_must_fail git merge --autostash five 2>err &&
-	test_i18ngrep ! "stash" err &&
+	test_missing "stash" err &&
 	git diff >actual &&
 	test_cmp expect actual
 '
diff --git i/t/t6437-submodule-merge.sh w/t/t6437-submodule-merge.sh
index daa507862c..b675e15a11 100755
--- i/t/t6437-submodule-merge.sh
+++ w/t/t6437-submodule-merge.sh
@@ -480,7 +480,7 @@ test_expect_merge_algorithm failure success !FAIL_PREREQS 'directory/submodule c
 		# We do not want files within the submodule to prevent the
 		# merge from starting; we should not be writing to such paths
 		# anyway.
-		test_i18ngrep ! "refusing to lose untracked file at" err
+		test_missing "refusing to lose untracked file at" err
 	)
 '
 
diff --git i/t/t6500-gc.sh w/t/t6500-gc.sh
index 04acf22d93..5f48720d9a 100755
--- i/t/t6500-gc.sh
+++ w/t/t6500-gc.sh
@@ -155,7 +155,7 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
 	test_commit "$(test_oid obj4)" &&
 
 	git gc --auto 2>err &&
-	test_i18ngrep ! "^warning:" err &&
+	test_missing "^warning:" err &&
 	ls .git/objects/pack/pack-*.pack | sort >post_packs &&
 	comm -1 -3 existing_packs post_packs >new &&
 	comm -2 -3 existing_packs post_packs >del &&
diff --git i/t/t7400-submodule-basic.sh w/t/t7400-submodule-basic.sh
index d9fbabb2b9..23038b3321 100755
--- i/t/t7400-submodule-basic.sh
+++ w/t/t7400-submodule-basic.sh
@@ -196,7 +196,7 @@ 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_i18ngrep ! "Checking connectivity" err
+	test_missing "Checking connectivity" err
 '
 
 test_expect_success 'redirected submodule add --progress does show progress' '
@@ -1160,7 +1160,7 @@ test_expect_success 'submodule deinit deinits a submodule when its work tree is
 	git submodule deinit init example2 >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
 	test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-	test_i18ngrep ! "Cleared directory .init" actual &&
+	test_missing "Cleared directory .init" actual &&
 	test_i18ngrep "Cleared directory .example2" actual &&
 	rmdir init
 '
@@ -1210,19 +1210,19 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su
 	test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
 	test_i18ngrep "Cleared directory .init" actual &&
 	git submodule deinit init >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_missing "Submodule .example. (.*) unregistered for path .init" actual &&
 	test_i18ngrep "Cleared directory .init" actual &&
 	git submodule deinit . >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_missing "Submodule .example. (.*) unregistered for path .init" actual &&
 	test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
 	test_i18ngrep "Cleared directory .init" actual &&
 	git submodule deinit . >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+	test_missing "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_missing "Submodule .example2. (.*) unregistered for path .example2" actual &&
 	test_i18ngrep "Cleared directory .init" actual &&
 	git submodule deinit --all >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+	test_missing "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_missing "Submodule .example2. (.*) unregistered for path .example2" actual &&
 	test_i18ngrep "Cleared directory .init" actual &&
 	rmdir init example2
 '
diff --git i/t/t7414-submodule-mistakes.sh w/t/t7414-submodule-mistakes.sh
index 101afff30f..7c0bd0bc28 100755
--- i/t/t7414-submodule-mistakes.sh
+++ w/t/t7414-submodule-mistakes.sh
@@ -19,7 +19,7 @@ test_expect_success 'git-add on embedded repository warns' '
 test_expect_success '--no-warn-embedded-repo suppresses warning' '
 	test_when_finished "git rm --cached -f embed" &&
 	git add --no-warn-embedded-repo embed 2>stderr &&
-	test_i18ngrep ! warning stderr
+	test_missing warning stderr
 '
 
 test_expect_success 'no warning when updating entry' '
@@ -27,14 +27,14 @@ test_expect_success 'no warning when updating entry' '
 	git add embed &&
 	git -C embed commit --allow-empty -m two &&
 	git add embed 2>stderr &&
-	test_i18ngrep ! warning stderr
+	test_missing warning stderr
 '
 
 test_expect_success 'submodule add does not warn' '
 	test_when_finished "git rm -rf submodule .gitmodules" &&
 	git -c protocol.file.allow=always \
 		submodule add ./embed submodule 2>stderr &&
-	test_i18ngrep ! warning stderr
+	test_missing warning stderr
 '
 
 test_done
diff --git i/t/t7416-submodule-dash-url.sh w/t/t7416-submodule-dash-url.sh
index 7cf72b9a07..d8a08f4852 100755
--- i/t/t7416-submodule-dash-url.sh
+++ w/t/t7416-submodule-dash-url.sh
@@ -63,7 +63,7 @@ test_expect_success 'trailing backslash is handled correctly' '
 	mv .new .gitmodules &&
 	git commit -am "Add testmodule" &&
 	test_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&
-	test_i18ngrep ! "unknown option" err
+	test_missing "unknown option" err
 '
 
 test_expect_success 'fsck rejects missing URL scheme' '
diff --git i/t/t7450-bad-git-dotfiles.sh w/t/t7450-bad-git-dotfiles.sh
index 0d0c3f2c68..59a4c8f8e8 100755
--- i/t/t7450-bad-git-dotfiles.sh
+++ w/t/t7450-bad-git-dotfiles.sh
@@ -253,7 +253,7 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
 
 		git fsck 2>output &&
 		test_i18ngrep gitmodulesParse output &&
-		test_i18ngrep ! "bad config" output
+		test_missing "bad config" output
 	)
 '
 
diff --git i/t/t7502-commit-porcelain.sh w/t/t7502-commit-porcelain.sh
index b5bf7de7cd..21436fc957 100755
--- i/t/t7502-commit-porcelain.sh
+++ w/t/t7502-commit-porcelain.sh
@@ -860,7 +860,7 @@ try_commit () {
 	GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
 	case "$use_template" in
 	'')
-		test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
+		test_missing "^## Custom template" .git/COMMIT_EDITMSG ;;
 	*)
 		test_i18ngrep "^## Custom template" .git/COMMIT_EDITMSG ;;
 	esac
@@ -880,7 +880,7 @@ try_commit_status_combo () {
 
 	test_expect_success 'commit --no-status' '
 		try_commit --no-status &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_missing "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit with commit.status = yes' '
@@ -892,7 +892,7 @@ try_commit_status_combo () {
 	test_expect_success 'commit with commit.status = no' '
 		test_config commit.status no &&
 		try_commit "" &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_missing "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --status with commit.status = yes' '
@@ -904,7 +904,7 @@ try_commit_status_combo () {
 	test_expect_success 'commit --no-status with commit.status = yes' '
 		test_config commit.status yes &&
 		try_commit --no-status &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_missing "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --status with commit.status = no' '
@@ -916,7 +916,7 @@ try_commit_status_combo () {
 	test_expect_success 'commit --no-status with commit.status = no' '
 		test_config commit.status no &&
 		try_commit --no-status &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_missing "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 }
diff --git i/t/t7508-status.sh w/t/t7508-status.sh
index 6c46648e11..c8c107825e 100755
--- i/t/t7508-status.sh
+++ w/t/t7508-status.sh
@@ -1547,7 +1547,7 @@ test_expect_success 'git commit will commit a staged but ignored submodule' '
 	export GIT_EDITOR &&
 	git commit -uno &&
 	git status -s --ignore-submodules=dirty >output &&
-	test_i18ngrep ! "^M. sm" output
+	test_missing "^M. sm" output
 '
 
 test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '
@@ -1578,7 +1578,7 @@ EOF
 test_expect_success 'git commit -m will commit a staged but ignored submodule' '
 	git commit -uno -m message &&
 	git status -s --ignore-submodules=dirty >output &&
-	test_i18ngrep ! "^M. sm" output &&
+	test_missing "^M. sm" output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1625,7 +1625,7 @@ test_expect_success '"No commits yet" should not be noted in status output' '
 	git checkout --orphan empty-branch-2 &&
 	test_commit test-commit-1 &&
 	git status >output &&
-	test_i18ngrep ! "No commits yet" output
+	test_missing "No commits yet" output
 '
 
 test_expect_success '"Initial commit" should be noted in commit template' '
@@ -1642,7 +1642,7 @@ test_expect_success '"Initial commit" should not be noted in commit template' '
 	touch to_be_committed_2 &&
 	git add to_be_committed_2 &&
 	git commit --dry-run >output &&
-	test_i18ngrep ! "Initial commit" output
+	test_missing "Initial commit" output
 '
 
 test_expect_success '--no-optional-locks prevents index update' '
diff --git i/t/t7518-ident-corner-cases.sh w/t/t7518-ident-corner-cases.sh
index 9ab2ae2f3b..99543c9770 100755
--- i/t/t7518-ident-corner-cases.sh
+++ w/t/t7518-ident-corner-cases.sh
@@ -15,7 +15,7 @@ test_expect_success 'empty name and missing email' '
 		sane_unset GIT_AUTHOR_EMAIL &&
 		GIT_AUTHOR_NAME= &&
 		test_must_fail git commit --allow-empty -m foo 2>err &&
-		test_i18ngrep ! "(null)" err
+		test_missing "(null)" err
 	)
 '
 
diff --git i/t/t7519-status-fsmonitor.sh w/t/t7519-status-fsmonitor.sh
index 8348e3ae7d..a6e4f16da4 100755
--- i/t/t7519-status-fsmonitor.sh
+++ w/t/t7519-status-fsmonitor.sh
@@ -322,10 +322,10 @@ do
 			rm -f marker &&
 			git status >actual &&
 			test_path_is_file marker &&
-			test_i18ngrep ! "Changes not staged for commit:" actual &&
+			test_missing "Changes not staged for commit:" actual &&
 			if test $uc_val = true
 			then
-				test_i18ngrep ! "Untracked files:" actual
+				test_missing "Untracked files:" actual
 			fi &&
 			if test $uc_val = false
 			then
diff --git i/t/t7520-ignored-hook-warning.sh w/t/t7520-ignored-hook-warning.sh
index 184b258989..936528ff33 100755
--- i/t/t7520-ignored-hook-warning.sh
+++ w/t/t7520-ignored-hook-warning.sh
@@ -13,7 +13,7 @@ test_expect_success setup '
 
 test_expect_success 'no warning if hook is not ignored' '
 	git commit --allow-empty -m "more" 2>message &&
-	test_i18ngrep ! -e "hook was ignored" message
+	test_missing -e "hook was ignored" message
 '
 
 test_expect_success POSIXPERM 'warning if hook is ignored' '
@@ -26,14 +26,14 @@ test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
 	test_config advice.ignoredHook false &&
 	test_hook --disable pre-commit &&
 	git commit --allow-empty -m "even more" 2>message &&
-	test_i18ngrep ! -e "hook was ignored" message
+	test_missing -e "hook was ignored" message
 '
 
 test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
 	test_hook --remove pre-commit &&
 	test_unconfig advice.ignoredHook &&
 	git commit --allow-empty -m "even more" 2>message &&
-	test_i18ngrep ! -e "hook was ignored" message
+	test_missing -e "hook was ignored" message
 '
 
 test_done
diff --git i/t/t7601-merge-pull-config.sh w/t/t7601-merge-pull-config.sh
index e08767df66..0c49c752b0 100755
--- i/t/t7601-merge-pull-config.sh
+++ w/t/t7601-merge-pull-config.sh
@@ -30,58 +30,58 @@ test_expect_success 'setup' '
 test_expect_success 'pull.rebase not set, ff possible' '
 	git reset --hard c0 &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=true' '
 	git reset --hard c0 &&
 	test_config pull.ff true &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=false' '
 	git reset --hard c0 &&
 	test_config pull.ff false &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=only' '
 	git reset --hard c0 &&
 	test_config pull.ff only &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --rebase given' '
 	git reset --hard c0 &&
 	git pull --rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-rebase given' '
 	git reset --hard c0 &&
 	git pull --no-rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff given' '
 	git reset --hard c0 &&
 	git pull --ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-ff given' '
 	git reset --hard c0 &&
 	git pull --no-ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff-only given' '
 	git reset --hard c0 &&
 	git pull --ff-only . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set (not-fast-forward)' '
@@ -96,51 +96,51 @@ test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_config pull.ff true &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_config pull.ff false &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_config pull.ff only &&
 	test_must_fail git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --no-rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --no-ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_must_fail git pull --ff-only . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_missing "You have divergent branches" err
 '
 
 test_does_rebase () {
@@ -334,7 +334,7 @@ test_expect_success 'Multiple heads warns about inability to fast forward' '
 test_expect_success 'Multiple can never be fast forwarded' '
 	git reset --hard c0 &&
 	test_must_fail git -c pull.ff=only pull . c1 c2 c3 2>err &&
-	test_i18ngrep ! "You have divergent branches" err &&
+	test_missing "You have divergent branches" err &&
 	# In addition to calling out "cannot fast-forward", we very much
 	# want the "multiple branches" piece to be called out to users.
 	test_i18ngrep "Cannot fast-forward to multiple branches" err
@@ -343,7 +343,7 @@ test_expect_success 'Multiple can never be fast forwarded' '
 test_expect_success 'Cannot rebase with multiple heads' '
 	git reset --hard c0 &&
 	test_must_fail git -c pull.rebase=true pull . c1 c2 c3 2>err &&
-	test_i18ngrep ! "You have divergent branches" err &&
+	test_missing "You have divergent branches" err &&
 	test_i18ngrep "Cannot rebase onto multiple branches." err
 '
 
diff --git i/t/t7810-grep.sh w/t/t7810-grep.sh
index 84838c0fe1..a9610e1b08 100755
--- i/t/t7810-grep.sh
+++ w/t/t7810-grep.sh
@@ -1426,7 +1426,7 @@ test_expect_success 'grep --no-index prefers paths to revs' '
 
 test_expect_success 'grep --no-index does not "diagnose" revs' '
 	test_must_fail git grep --no-index o :1:hello.c 2>err &&
-	test_i18ngrep ! -i "did you mean" err
+	test_missing -i "did you mean" err
 '
 
 cat >expected <<EOF
diff --git i/t/t7816-grep-binary-pattern.sh w/t/t7816-grep-binary-pattern.sh
index fdb2355649..a22c88c8b3 100755
--- i/t/t7816-grep-binary-pattern.sh
+++ w/t/t7816-grep-binary-pattern.sh
@@ -26,7 +26,7 @@ nul_match_internal () {
 			>stderr &&
 			printf '$pattern' | q_to_nul >f &&
 			test_must_fail env LC_ALL=\"$lc_all\" git grep $extra_flags -f f $flags a 2>stderr &&
-			test_i18ngrep ! 'This is only supported with -P under PCRE v2' stderr
+			test_missing 'This is only supported with -P under PCRE v2' stderr
 		"
 	elif test "$matches" = P
 	then
diff --git i/t/t9800-git-p4-basic.sh w/t/t9800-git-p4-basic.sh
index a4b3cb9492..b44aa84b08 100755
--- i/t/t9800-git-p4-basic.sh
+++ w/t/t9800-git-p4-basic.sh
@@ -290,7 +290,7 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '
 		export PATH &&
 		test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
 	) &&
-	test_i18ngrep ! Traceback errs
+	test_missing Traceback errs
 '
 
 # Hide a file from p4d, make sure we catch its complaint.  This won't fail in
diff --git i/t/t9807-git-p4-submit.sh w/t/t9807-git-p4-submit.sh
index 7d4109f29d..dd0badd7f9 100755
--- i/t/t9807-git-p4-submit.sh
+++ w/t/t9807-git-p4-submit.sh
@@ -463,7 +463,7 @@ test_expect_success 'submit --prepare-p4-only' '
 		git p4 submit --prepare-p4-only >out &&
 		test_i18ngrep "prepared for submission" out &&
 		test_i18ngrep "must be deleted" out &&
-		test_i18ngrep ! "everything below this line is just the diff" out
+		test_missing "everything below this line is just the diff" out
 	) &&
 	(
 		cd "$cli" &&

^ permalink raw reply related

* Re: [PATCH] sequencer: remove use of comment character
From: Junio C Hamano @ 2023-10-30 23:35 UTC (permalink / raw)
  To: Elijah Newren; +Cc: Tony Tung via GitGitGadget, git, Tony Tung
In-Reply-To: <CABPp-BE6_nuMeiqOAMGwP8SH=d1+i57-STgTNKU8-Gnkv2jW=Q@mail.gmail.com>

Elijah Newren <newren@gmail.com> writes:

> I thought the point of the comment_line_char was so that commit
> messages could have lines starting with '#'.  That rationale doesn't
> apply to the TODO list generation or parsing, and I'm not sure if we
> want to add the same complexity there.

Thanks for a healthy dose of sanity.  I noticed existing use of
comment_line_char everywhere in sequencer.c and assumed we would
want to be consistent, but you are right to point out that they are
all about the COMMIT_EDITMSG kind of thing, and not about what
appears in "sequencer/todo".

^ permalink raw reply

* Re: [RFC PATCH 1/3] strbuf: make add_lines() public
From: Junio C Hamano @ 2023-10-30 23:53 UTC (permalink / raw)
  To: Jonathan Tan; +Cc: git, Phillip Wood, Dragan Simic
In-Reply-To: <d96633a2919ac619ccf29e87abc6f25314a8bfb1.1698696798.git.jonathantanmy@google.com>

Jonathan Tan <jonathantanmy@google.com> writes:

> Subsequent patches will require the ability to add different prefixes
> to different lines (depending on their contents), so make this
> functionality available from outside strbuf.c.

I do not think it is a good idea to force almost everybody to repeat
themselves.  As we can see here, all but just a single caller of
strbuf_add_lines() with this patch pass the same prefix for both
parameters.  If we need to make the current strbuf.c:add_lines()
also available to some specific callers, that is fine, but let's
keep the simpler version that almost everybody uses as-is, and give
the more complex and featureful one that is used only by selected
callers a longer and more cumbersome name.

Thanks.

^ permalink raw reply

* Re: [PATCH v2 0/1] Object ID support for git merge-file
From: Junio C Hamano @ 2023-10-31  0:03 UTC (permalink / raw)
  To: Elijah Newren
  Cc: brian m. carlson, git, Phillip Wood, Eric Sunshine, Taylor Blau
In-Reply-To: <CABPp-BF6vxU5x5VLGbRhtcTBqDu3x31=vMOd2bimZNg2mkkvuA@mail.gmail.com>

Elijah Newren <newren@gmail.com> writes:

> On Mon, Oct 30, 2023 at 9:27 AM brian m. carlson
> <sandals@crustytoothpaste.net> wrote:
>>
>> This series introduces an --object-id option to git merge-file such
>> that, instead of reading and writing from files on the system, it reads
>> from and writes to the object store using blobs.
>>
>> Changes from v1:
>> * Improve error handling
>> * Re-add `-p` argument for documentation
>>
>> brian m. carlson (1):
>>   merge-file: add an option to process object IDs
>>
>>  Documentation/git-merge-file.txt | 20 +++++++++++
>>  builtin/merge-file.c             | 62 +++++++++++++++++++++++---------
>>  t/t6403-merge-file.sh            | 58 ++++++++++++++++++++++++++++++
>>  3 files changed, 124 insertions(+), 16 deletions(-)
>
> Thanks, this version looks good to me.

Thanks, both.  Will queue.

^ permalink raw reply

* How to get started with contribution
From: Shashank Gupta @ 2023-10-31  0:08 UTC (permalink / raw)
  To: git

Respected sir/madam,
I am Shashank Gupta, a IT undergrad from Manipal university Jaipur. I am currently in the 3rd sem of my Btech programme.
I am new to open source contributions but I have good knowledge of c, java and javascript. I would like to contribute to your organisation but could you please tell me how to get started?
Hoping to hear from you soon.
Regards 
Shashank Gupta


^ permalink raw reply

* Re: [PATCH] merge: --ff-one-only to apply FF if commit is one
From: Junio C Hamano @ 2023-10-31  0:19 UTC (permalink / raw)
  To: Taylor Blau; +Cc: Ruslan Yakauleu via GitGitGadget, git, Ruslan Yakauleu
In-Reply-To: <ZUALkdSJZ70+KBYq@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

> This seems like a pretty niche feature to want to introduce a new option
> for. I would imagine the alternative is something like:
>
>     ff="--no-ff"
>     if test 1 -eq $(git rev-list @{u}..)
>     then
>         ff="--ff"
>     fi
>
>     [on upstream @{u}]
>     git merge "$ff" "$branch"
>
> I don't have a great sense of how many users might want or benefit from
> something like this. My sense is that there aren't many, but I could
> very easily be wrong here.

Another more fundamental objection is "Why do we special case only a
singleton commit?"

Why isn't a trivial two-patch series also OK to fast-forward?
Three?  There is no inherent reason to draw a line on one commit
topic---given that a single commit could be a large and involved one
that could have been a multi commmit series.

And you cannot decide if the "topic" is large enough to deserve a
binding merge commit even if it is a single commit topic, or if is
small enough and you want to allow fast-forward, without looking at
it first.  So from that point of view, too, I do not think this new
option is a good idea.  Let's not add an option to encourage a bad
discipline.

Thanks.



^ permalink raw reply

* Re: [PATCH v2 2/2] commit: detect commits that exist in commit-graph but not in the ODB
From: Junio C Hamano @ 2023-10-31  0:21 UTC (permalink / raw)
  To: Taylor Blau; +Cc: Patrick Steinhardt, git, Karthik Nayak, Jeff King
In-Reply-To: <ZUAg5dj272mhHPZf@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

> On Tue, Oct 24, 2023 at 12:10:13PM -0700, Junio C Hamano wrote:
>> > We look at a ~30% regression in general, but in general we're still a
>> > whole lot faster than without the commit graph. To counteract this, the
>> > new check can be turned off with the `GIT_COMMIT_GRAPH_PARANOIA` envvar.
>>
>> Very nicely described.  Will queue.  I'll go offline for the rest of
>> the week but if there are no significant issues discovered by the
>> time I come back, let's declare a victory and merge these two
>> patches down to 'next'.
>
> I think we're close here. There are a couple of small comments that I
> made throughout these two patches, but nothing major.

Thanks for a comment.

^ permalink raw reply

* Re: [PATCH 2/2] Documentation/gitformat-pack.txt: fix incorrect MIDX documentation
From: Junio C Hamano @ 2023-10-31  0:42 UTC (permalink / raw)
  To: Taylor Blau; +Cc: git, Jeff King
In-Reply-To: <ZUAmWWh1WHujiN05@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

>> >  	    Packfiles must be listed in lexicographic order for fast lookups by
>> > -	    name. This is the only chunk not guaranteed to be a multiple of four
>> > -	    bytes in length, so should be the last chunk for alignment reasons.
>> > +	    name. Individual entries in this chunk are not guarenteed to be
>> > +	    aligned. The chunk is externally padded with zeros to align
>> > +	    remaining chunks.
>>
>> I am not sure what "externally padded" means.
>
> How about something like this, instead?
>
> --- 8< ---
> diff --git a/Documentation/gitformat-pack.txt b/Documentation/gitformat-pack.txt
> index 0bc80f0d46..229490f82f 100644
> --- a/Documentation/gitformat-pack.txt
> +++ b/Documentation/gitformat-pack.txt
> @@ -392,9 +392,10 @@ CHUNK DATA:
>  	Packfile Names (ID: {'P', 'N', 'A', 'M'})
>  	    Stores the packfile names as concatenated, NUL-terminated strings.
>  	    Packfiles must be listed in lexicographic order for fast lookups by
> -	    name. Individual entries in this chunk are not guarenteed to be
> -	    aligned. The chunk is externally padded with zeros to align
> -	    remaining chunks.
> +	    name. Individual entries in this chunk are not guaranteed to be
> +	    aligned, since the packfile names can be of arbitrary length. The
> +	    chunk itself is padded at the end with NUL bytes in order to align
> +	    the remaining chunks.

There is no alignment requirement described, so "not guaranteed" and
"in order to align" sound hollow.  These are always byte-aligned ;-)

How about something along this line to simplify it a bit?

	Store the names of packfiles as a sequence of NUL-terminated
	strings.  There is no extra padding between the filenames,
	and they are listed in lexicographic order.  The chunk
	itself is padded at the end with NUL bytes to make the chunk
	size a multiple of 4 bytes.

I did not ccheck if the chunks need to be 4-byte aligned, so if the
number is wrong, please adjust accordingly.


^ permalink raw reply

* Re: Bug: Git grep -f reads the filename relative to the repository root
From: Junio C Hamano @ 2023-10-31  0:49 UTC (permalink / raw)
  To: Taylor Blau; +Cc: Erik Cervin Edin, Git Mailing List
In-Reply-To: <ZUAnEVk65VQQE263@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

> Just going through old mail that I didn't have a chance to respond to,
> the proposed patch that you included here does compile and pass t7810
> for me, and the fix looks reasonable as-is. I don't think I see this
> patch on master, but would have no objections to you merging it down.

Thanks.

^ permalink raw reply

* Re: [PATCH v3] bugreport: reject positional arguments
From: Junio C Hamano @ 2023-10-31  2:17 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Eric Sunshine, emilyshaffer, git, Emily Shaffer, Sheik,
	Dragan Simic
In-Reply-To: <xmqqfs1radrj.fsf@gitster.g>

Junio C Hamano <gitster@pobox.com> writes:

> Phillip Wood <phillip.wood123@gmail.com> writes:
>
>> ... it is determining that grep
>> failed in the first place that I find annoying. I've also found the
>> output from test_i18ngrep is helpful when debugging CI test failures.
>
> Thanks, I agree with you that it is very useful for debugging to
> have an explicit "I did not find what I expected to see".  On the
> other hand, the other side is not as severe a problem, I think.  If
> "grep" expects not to see an error message in the output but the
> output has the error we do not want to see, we will see the message
> in the "-v" output when we run test.

Everything I wrote above makes sense, but what I continued this
message with (below---omitted) is total opposite.  Let's discard and
maybe retry sometime later.

^ permalink raw reply

* Re: [PATCH v2 00/12] show-ref: introduce mode to check for ref existence
From: Junio C Hamano @ 2023-10-31  2:26 UTC (permalink / raw)
  To: Taylor Blau; +Cc: Patrick Steinhardt, git, Eric Sunshine, Han-Wen Nienhuys
In-Reply-To: <ZUAEyc0i6iISekiM@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

> On Thu, Oct 26, 2023 at 11:56:16AM +0200, Patrick Steinhardt wrote:
>> Hi,
>>
>> this is the second version of my patch series that introduces a new `git
>> show-ref --exists` mode to check for reference existence.
>
> All looks quite reasonable to me. I'm happy with this series as-is,
> though I left a few minor comments and suggestions throughout.

Thanks.  I'll wait for hearing from Patrick and then decide ;-)


^ permalink raw reply

* Re: Repository cloned using SSH does not respect bare repository initial branch
From: Sheik @ 2023-10-31  2:38 UTC (permalink / raw)
  To: sahibzone; +Cc: git, peff
In-Reply-To: <9cec7307-a875-4b8c-9abe-81c8879d698b@gmail.com>


Wondering whether a link to the protocol documentation should be added 
in "git init --initial-branch" to make this more obvious.

 1. https://git-scm.com/docs/git-init#Documentation/git-init.txt---initial-branchltbranch-namegt
 2. https://git-scm.com/docs/gitprotocol-v2


Thanks

Sheik


^ permalink raw reply

* Re: [PATCH] chore: fix typo in .clang-format comment
From: Junio C Hamano @ 2023-10-31  3:12 UTC (permalink / raw)
  To: Taylor Blau; +Cc: Aditya Neelamraju via GitGitGadget, git, Aditya Neelamraju
In-Reply-To: <ZT/gQZlVKfgn/+BZ@nand.local>

Taylor Blau <me@ttaylorr.com> writes:

> On Sun, Oct 29, 2023 at 08:23:07PM +0000, Aditya Neelamraju via GitGitGadget wrote:
>> From: Aditya Neelamraju <adityanv97@gmail.com>
>
> We typically prefix commit messages with the subject area they're
> working in, not with "chore", or "feat" like some Git workflows
> recommend.
> ...
> That said, the contents of this patch look obviously correct to me.
> Thanks for noticing and fixing!

As a comment for a new contributor, it is a bit unhelpful not to
suggest what the "subject area" string we would use if we were
working on this patch, I think.

I also suspected that valuve may be a valid word in some language,
as the indentation in the example looked as if the six-letter word
was meant, not typoed.  https://www.gasolineravaluve.com/ was one
of the first hits I saw in my search ;-)


^ permalink raw reply

* Re: How to get started with contribution
From: Christian Couder @ 2023-10-31  3:29 UTC (permalink / raw)
  To: Shashank Gupta; +Cc: git
In-Reply-To: <FACCA976-EAAF-4B68-8748-D13A11EC6652@gmail.com>

Hi,

On Tue, Oct 31, 2023 at 1:08 AM Shashank Gupta <shxshankgupta@gmail.com> wrote:
>
> Respected sir/madam,
> I am Shashank Gupta, a IT undergrad from Manipal university Jaipur. I am currently in the 3rd sem of my Btech programme.
> I am new to open source contributions but I have good knowledge of c, java and javascript. I would like to contribute to your organisation but could you please tell me how to get started?

Thanks for your interest in contributing to Git!

We have pointers to a lot of documentation about this on
https://git.github.io/Hacking-Git/. Especially there is a section
called "Getting started hacking and contributing" with links to
tutorials that we think can help a lot.

Best,
Christian.

^ permalink raw reply

* Re: [PATCH 0/3] Add `-p' option to `git-mv', inspired by `mkdir'
From: Junio C Hamano @ 2023-10-31  4:30 UTC (permalink / raw)
  To: Hugo Sales; +Cc: git, Derrick Stolee, Shaoxuan Yuan
In-Reply-To: <1384513657.119681.1697027599941@office.mailbox.org>

Hugo Sales <hugo@hsal.es> writes:

>> Both are plausible, and "mkdir -p" does not have such a nasty
>> ambiguity.  That is what makes me unsure about the new feature
>> (again, either with required "-p" or with implied "-p").
>
> I think the ambiguity is resolved by the inclusion of lack thereof
> of a trailing `/`.

The question is not if we can come up with a rule that the user can
use to disambiguate.  It is if the user will find that such a rule
is a naturally acceptable way to disambiguate.

When both of

    git mv file there/exists/such/a/directory
    git mv file there/exists/such/a/directory/

create "there/exists/such/a/directory/file" and removes "file", with
or without a trailing slash, "you should add a slash if you want a
directory, and otherwise you should not add a slash" is a rather
arbitrary rule.  Let's not go there.  I still view the downside more
grave than having to occasionally do "mkdir".

^ permalink raw reply

* Re: [PATCH] sequencer: remove use of comment character
From: Tony Tung @ 2023-10-31  4:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Elijah Newren, Tony Tung via GitGitGadget, git
In-Reply-To: <xmqqa5rzadlh.fsf@gitster.g>


> On Oct 30, 2023, at 4:35 PM, Junio C Hamano <gitster@pobox.com> wrote:
> 
> Elijah Newren <newren@gmail.com> writes:
> 
>> I thought the point of the comment_line_char was so that commit
>> messages could have lines starting with '#'.  That rationale doesn't
>> apply to the TODO list generation or parsing, and I'm not sure if we
>> want to add the same complexity there.
> 
> Thanks for a healthy dose of sanity.  I noticed existing use of
> comment_line_char everywhere in sequencer.c and assumed we would
> want to be consistent, but you are right to point out that they are
> all about the COMMIT_EDITMSG kind of thing, and not about what
> appears in "sequencer/todo”.

I believe comment_line_char is being applied when the sequencer reads back the instructions, which is why I ran into this problem to begin with.

If the intent is not to apply it to the sequencer, then the bugfix is in the wrong place.

Thanks


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox