Merge branch 'mm/test-grep-lint' into next
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. * mm/test-grep-lint: t: add greplint to detect bare grep assertions t: convert grep assertions to test_grep t: fix Lexer line count for $() inside double-quoted strings t: extract chainlint's parser into shared module t: fix grep assertions missing file arguments t/README: document test_grep helpernext
commit
1916c07bf5
|
|
@ -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
|
||||
|
|
|
|||
29
t/Makefile
29
t/Makefile
|
|
@ -27,9 +27,11 @@ TEST_LINT ?= test-lint
|
|||
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 @@ TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
|
|||
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 @@ TLIBS = $(sort $(wildcard lib-*.sh)) annotate-tests.sh
|
|||
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 @@ test: pre-clean check-meson $(TEST_LINT)
|
|||
$(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 @@ unit-tests-test-tool:
|
|||
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 @@ check-chainlint:
|
|||
{ $(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 @@ check-meson:
|
|||
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-executable:
|
|||
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 @@ perf:
|
|||
$(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:
|
||||
|
|
|
|||
34
t/README
34
t/README
|
|
@ -1039,6 +1039,40 @@ see test-lib-functions.sh for the full list and their options.
|
|||
|
||||
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>
|
||||
|
|
|
|||
529
t/chainlint.pl
529
t/chainlint.pl
|
|
@ -23,458 +23,9 @@ my $jobs = -1;
|
|||
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 @@ DONE:
|
|||
# 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 @@ DONE:
|
|||
$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 format_problem {
|
|||
|
||||
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 @@ sub check_test {
|
|||
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 @@ sub check_script {
|
|||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ test_expect_success 'Verify descending sort' '
|
|||
|
||||
test_expect_success 'Give help even with invalid sort atoms' '
|
||||
${git_for_each_ref} --sort=bogus -h >actual 2>&1 &&
|
||||
grep "^usage: ${git_for_each_ref}" actual
|
||||
test_grep "^usage: ${git_for_each_ref}" actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
|
|
@ -622,7 +622,7 @@ test_expect_success 'Quoting style: tcl' '
|
|||
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 @@ test_expect_success "${git_for_each_ref} --stdin: fails if extra args" '
|
|||
>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 "${git_for_each_ref} with nested tags" '
|
|||
|
||||
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)"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1 @@
|
|||
3: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
test_expect_success 'grep after && is flagged' '
|
||||
cmd &&
|
||||
grep pattern file
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
3: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
test_expect_success 'grep after semicolon is flagged' '
|
||||
echo hello;
|
||||
grep pattern file
|
||||
'
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep -c is flagged (not special-cased)' '
|
||||
grep -c pattern file
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep -e is flagged' '
|
||||
grep -e pattern file
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep -E is flagged' '
|
||||
grep -E "pat+ern" file
|
||||
'
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
test_expect_success 'grep with lint-ok annotation is not flagged' '
|
||||
grep pattern file && # lint-ok
|
||||
echo done
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'negated grep is flagged' '
|
||||
! grep pattern file
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep -f is flagged' '
|
||||
grep -f patterns.txt file
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'bare grep is flagged' '
|
||||
grep pattern file
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
3: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
test_expect_success 'grep in subshell is flagged' '
|
||||
(
|
||||
grep pattern file
|
||||
)
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
10: error: bare grep outside pipeline (use test_grep)
|
||||
|
|
@ -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
|
||||
"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep in command substitution is not flagged' '
|
||||
x=$(grep pattern file)
|
||||
'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep receiving pipe input is not flagged' '
|
||||
cmd | grep pattern
|
||||
'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep piping to another command is not flagged' '
|
||||
grep pattern file | wc -l
|
||||
'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep with output redirect is not flagged' '
|
||||
grep pattern file >output
|
||||
'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep reading from stdin redirect is not flagged' '
|
||||
grep pattern <input
|
||||
'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'grep as argument to another command is not flagged' '
|
||||
test_must_fail grep pattern file
|
||||
'
|
||||
|
|
@ -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
|
||||
'
|
||||
|
|
@ -0,0 +1 @@
|
|||
2: error: use "test_grep !" instead of "! test_grep"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
test_expect_success 'wrong negation of test_grep is flagged' '
|
||||
! test_grep pattern file
|
||||
'
|
||||
|
|
@ -173,7 +173,7 @@ rev_list_tests_head () {
|
|||
|
||||
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 @@ basic_bitmap_tests () {
|
|||
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 @@ test_rev_exists () {
|
|||
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
|
||||
'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: no
|
|||
>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 @@ test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: hav
|
|||
>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 @@ test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: reque
|
|||
>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 @@ test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: reque
|
|||
>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 @@ test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: reque
|
|||
>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
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ test_http_push_nonff () {
|
|||
'
|
||||
|
||||
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' '
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -195,7 +195,7 @@ test_expect_success 'delete ref while another dangling packed ref' '
|
|||
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
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ test_expect_success '--exists with missing reference' '
|
|||
|
||||
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' '
|
||||
|
|
|
|||
|
|
@ -743,7 +743,7 @@ test_expect_success 'subtest: lazy prereqs do not turn off tracing' '
|
|||
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 'subtest: test_atexit is run' '
|
|||
|
||||
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_expect_success 'test_oid can look up data for SHA-1' '
|
|||
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_expect_success 'test_oid can look up data for SHA-256' '
|
|||
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_expect_success 'test_bool_env' '
|
|||
# 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 on a failing git command with env' '
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -278,9 +278,9 @@ test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shar
|
|||
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 @@ test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed wi
|
|||
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 @@ test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown back
|
|||
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 @@ test_expect_success MINGW 'redirect std handles' '
|
|||
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 @@ test_expect_success '--initial-branch' '
|
|||
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_expect_success 'overridden default main branch name (env)' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -790,8 +790,8 @@ test_expect_success 'existing file and directory' '
|
|||
>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 @@ test_expect_success 'existing directory and file' '
|
|||
>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)' '
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ test_expect_success PIPE 'setup: .git as a FIFO (named pipe) is rejected' '
|
|||
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 @@ test_expect_success SYMLINKS,PIPE 'setup: .git as a symlink to a FIFO is rejecte
|
|||
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 @@ test_expect_success 'setup: .git with garbage content is rejected' '
|
|||
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
|
||||
)
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -131,8 +131,8 @@ test_expect_success 'git help succeeds without git.html' '
|
|||
|
||||
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' '
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ fi
|
|||
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
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ test_expect_success 'test-tool env-helper reads config thanks to trace2' '
|
|||
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_expect_success 'test-tool env-helper reads config thanks to trace2' '
|
|||
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
|
||||
|
|
|
|||
|
|
@ -731,7 +731,7 @@ test_expect_success 'process filter should restart after unexpected write failur
|
|||
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 @@ test_expect_success 'invalid process filter must fail (and not hang!)' '
|
|||
|
||||
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 @@ test_expect_success 'missing file in delayed checkout' '
|
|||
|
||||
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 @@ test_expect_success 'invalid file in delayed checkout' '
|
|||
|
||||
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 @@ do
|
|||
|
||||
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 @@ test_expect_success SYMLINKS,CASE_INSENSITIVE_FS \
|
|||
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 @@ test_expect_success 'delayed checkout correctly reports the number of updated en
|
|||
|
||||
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
|
||||
)
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ test_expect_success 'core.unsetenvvars works' '
|
|||
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
|
||||
|
|
|
|||
|
|
@ -407,12 +407,12 @@ test_expect_success 'strip comments with changed comment string' '
|
|||
|
||||
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' '
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ test_expect_success 'PID info not shown by default' '
|
|||
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
|
||||
)
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -324,13 +324,13 @@ test_expect_success 'non ambiguous option (after two options it abbreviates)' '
|
|||
|
||||
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 'KEEP_UNKNOWN_OPT works' '
|
|||
|
||||
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 'KEEP_UNKNOWN_OPT | NO_INTERNAL_HELP works' '
|
|||
|
||||
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 + u
|
|||
|
||||
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 'subcommand - completion helper' '
|
|||
|
||||
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
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ test_expect_success 'setup ' '
|
|||
|
||||
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
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -33,19 +33,19 @@ test_expect_success 'servers cannot share the same path' '
|
|||
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 'slow response' '
|
|||
#
|
||||
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 @@ test_expect_success 'stress test threads' '
|
|||
--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
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ test_expect_success POSIXPERM 'run_command reports EACCES' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default
|
|||
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
|
||||
|
|
|
|||
|
|
@ -21,23 +21,23 @@ test_expect_success 'run based on configured value' '
|
|||
|
||||
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" &&
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Verify wrappers and compatibility functions.
|
|||
|
||||
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_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints file
|
|||
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 'check for a bug in the regex routines' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -52,17 +52,17 @@ test_expect_success 'add more packfiles' '
|
|||
|
||||
# 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
|
||||
'
|
||||
|
|
|
|||
|
|
@ -40,15 +40,15 @@ test_expect_success 'sanity check "System Info" section' '
|
|||
|
||||
# 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 @@ test_expect_success UNZIP '--diagnose creates diagnostics zip archive' '
|
|||
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_expect_success UNZIP '--diagnose creates diagnostics zip archive' '
|
|||
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 @@ test_expect_success UNZIP '--diagnose=stats excludes .git dir contents' '
|
|||
|
||||
# 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/"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ test_expect_success UNZIP 'creates diagnostics zip archive' '
|
|||
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_expect_success UNZIP 'creates diagnostics zip archive' '
|
|||
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 @@ test_expect_success UNZIP 'counts loose objects' '
|
|||
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 @@ test_expect_success UNZIP '--mode=stats excludes .git dir contents' '
|
|||
# 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 @@ test_expect_success UNZIP '--mode=all includes .git dir contents' '
|
|||
# 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/" &&
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ test_expect_success 'merge @{-1}~1' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ test_expect_success 'sanity: $TEXTDOMAIN is git' '
|
|||
'
|
||||
|
||||
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' '
|
|||
'
|
||||
|
||||
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 'sanity: Icelandic locale was compiled' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ test_expect_success 'git show a ISO-8859-1 commit under C locale' '
|
|||
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_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 l
|
|||
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
|
||||
|
|
|
|||
|
|
@ -66,22 +66,22 @@ test_expect_success GETTEXT_LOCALE 'gettext: Fetching a UTF-8 msgid -> UTF-8' '
|
|||
# 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
|
||||
|
|
|
|||
|
|
@ -333,12 +333,12 @@ test_expect_success 'unsafe URLs are redacted by default' '
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -283,13 +283,13 @@ test_expect_success 'unsafe URLs are redacted by default' '
|
|||
|
||||
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 @@ test_expect_success LIBCURL \
|
|||
|
||||
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 @@ test_expect_success LIBCURL \
|
|||
|
||||
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 @@ test_expect_success 'expect def_params during git alias expansion' '
|
|||
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 @@ test_expect_success 'expect def_params during shell alias expansion' '
|
|||
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 @@ test_expect_success 'expect def_params during nested git alias expansion' '
|
|||
|
||||
# "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
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ test_expect_success 'unsafe URLs are redacted by default in cmd_start events' '
|
|||
|
||||
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 @@ test_expect_success 'unsafe URLs are redacted by default in child_start events'
|
|||
|
||||
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 @@ test_expect_success 'unsafe URLs are redacted by default in exec events' '
|
|||
|
||||
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 @@ test_expect_success 'unsafe URLs are redacted by default in def_param events' '
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1014,7 +1014,7 @@ test_expect_success 'credential config with partial URLs' '
|
|||
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 @@ test_expect_success 'credential config with partial URLs' '
|
|||
do
|
||||
git -c credential.$partial.helper=yep \
|
||||
credential fill <stdin >stdout &&
|
||||
! grep yep stdout ||
|
||||
test_grep ! yep stdout ||
|
||||
return 1
|
||||
done &&
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ test_expect_success 'fetching of missing objects' '
|
|||
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_expect_success 'fetching of missing objects' '
|
|||
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 @@ test_expect_success 'fetching of a promised object that promisor remote no longe
|
|||
|
||||
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 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled'
|
|||
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_expect_success 'fetching from another promisor remote' '
|
|||
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_expect_success 'fetching with --filter configures a promisor remote' '
|
|||
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 @@ test_expect_success 'fetching of missing trees does not fetch blobs' '
|
|||
|
||||
# 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 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
|
|||
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 @@ test_expect_success 'rev-list stops traversal at missing and promised tree' '
|
|||
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 @@ test_expect_success 'rev-list stops traversal at missing and promised blob' '
|
|||
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 @@ test_expect_success 'rev-list stops traversal at promisor commit, tree, and blob
|
|||
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' '
|
||||
|
|
@ -523,10 +523,10 @@ test_expect_success 'gc repacks promisor objects separately from non-promisor ob
|
|||
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 +536,10 @@ test_expect_success 'gc repacks promisor objects separately from non-promisor ob
|
|||
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 +616,8 @@ test_expect_success 'gc stops traversal when a missing but promised object is re
|
|||
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 +647,10 @@ test_expect_success 'exact rename does not need to fetch the blob lazily' '
|
|||
|
||||
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 +665,7 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
|
|||
|
||||
# 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 +680,7 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
|
|||
|
||||
# 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 +688,7 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
|
|||
|
||||
# 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 +705,9 @@ test_expect_success 'push should not fetch new commit objects' '
|
|||
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 +750,7 @@ test_expect_success TTY 'promisor.quiet=false shows progress messages' '
|
|||
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 +761,7 @@ test_expect_success TTY 'promisor.quiet=true does not show progress messages' '
|
|||
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 +771,7 @@ test_expect_success TTY 'promisor.quiet=unconfigured shows progress messages' '
|
|||
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 +819,7 @@ test_expect_success 'fetching of missing objects from an HTTP server' '
|
|||
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
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ do
|
|||
# -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" '
|
||||
|
|
|
|||
|
|
@ -320,8 +320,8 @@ test_expect_success 'progress generates traces' '
|
|||
|
||||
# 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 @@ test_expect_success 'progress generates traces: start without stop' '
|
|||
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 @@ test_expect_success 'progress generates traces: stop without start' '
|
|||
|
||||
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 @@ test_expect_success 'progress generates traces: start with active progress bar (
|
|||
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
|
||||
|
|
|
|||
|
|
@ -776,11 +776,11 @@ test_expect_success 'reflog: can delete separate reflog entries' '
|
|||
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 @@ test_expect_success 'reflog: garbage collection deletes reflog entries' '
|
|||
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 &&
|
||||
|
|
|
|||
|
|
@ -142,8 +142,8 @@ test_expect_success '3-way not overwriting local changes (our side)' '
|
|||
|
||||
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 @@ test_expect_success '3-way not overwriting local changes (their side)' '
|
|||
|
||||
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
|
||||
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -696,8 +696,8 @@ test_expect_success '%(deltabase) reports packed delta bases' '
|
|||
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 @@ test_expect_success 'cat-file -t and -s on corrupt loose object' '
|
|||
# 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 'cat-file --batch-all-objects --batch-check ignores replace'
|
|||
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='
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ done
|
|||
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' '
|
||||
|
|
@ -213,7 +213,7 @@ test_expect_success PERL_TEST_HELPERS 'malformed mode in tree' '
|
|||
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' '
|
||||
|
|
@ -221,7 +221,7 @@ test_expect_success PERL_TEST_HELPERS 'empty filename in tree' '
|
|||
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' '
|
||||
|
|
@ -232,7 +232,7 @@ test_expect_success PERL_TEST_HELPERS 'duplicate filename in tree' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ test_expect_success 'read-tree will not throw away dirty changes, non-sparse' '
|
|||
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 @@ test_expect_success 'read-tree will not throw away dirty changes, sparse' '
|
|||
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 @@ test_expect_success 'read-tree updates worktree, dirty case' '
|
|||
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 @@ test_expect_success 'read-tree removes worktree, dirty case' '
|
|||
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 @@ test_expect_success 'read-tree adds to worktree, dirty case' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ test_description='adding and checking out large blobs'
|
|||
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 --stat' '
|
|||
|
||||
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' '
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '
|
|||
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 @@ test_expect_success 'cone mode clears ignored subdirectories' '
|
|||
# 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 @@ test_expect_success 'malformed cone-mode patterns' '
|
|||
# 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 @@ test_expect_success 'set from subdir in non-cone mode throws an error' '
|
|||
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 @@ test_expect_success 'clean' '
|
|||
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_expect_success 'sparse-checkout operations with merge conflicts' '
|
|||
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 &&
|
||||
|
|
|
|||
|
|
@ -454,10 +454,10 @@ test_expect_success 'add outside sparse cone' '
|
|||
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 @@ test_expect_success 'status/add: outside sparse cone' '
|
|||
|
||||
# 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 @@ test_expect_success 'checkout and reset (mixed)' '
|
|||
# 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 @@ test_expect_success 'update-index with directories' '
|
|||
# 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 @@ test_expect_success 'merge with conflict outside cone' '
|
|||
|
||||
# 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 @@ test_expect_success 'merge with conflict outside cone' '
|
|||
# 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 @@ test_expect_success 'cherry-pick/rebase with conflict outside cone' '
|
|||
# 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 @@ test_expect_success 'cherry-pick/rebase with conflict outside cone' '
|
|||
# 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 @@ test_expect_success 'checkout-index with folders' '
|
|||
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 @@ test_expect_success 'submodule handling' '
|
|||
# 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 @@ test_expect_success 'git apply functionality' '
|
|||
|
||||
# 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_expect_success 'mv directory from out-of-cone to in-cone' '
|
|||
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_expect_success 'mv directory from out-of-cone to in-cone' '
|
|||
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 @@ test_expect_success 'advice.sparseIndexExpanded' '
|
|||
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
|
||||
|
|
|
|||
|
|
@ -856,7 +856,7 @@ test_expect_success 'renaming a section with an overly-long line' '
|
|||
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=key=envvar support' '
|
|||
|
||||
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 @@ test_expect_success '--unset last key removes section (except if commented)' '
|
|||
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 @@ test_expect_success '--unset last key removes section (except if commented)' '
|
|||
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 rejects unknown specifiers' '
|
|||
|
||||
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_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such
|
|||
|
||||
# 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' '
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ test_expect_success 'include cycles are detected' '
|
|||
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' '
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ test_expect_success 'find integer if value is non parse-able' '
|
|||
|
||||
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 @@ test_expect_success 'proper error on directory "files"' '
|
|||
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_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' '
|
|||
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
|
||||
'
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ test_expect_success "deleting current branch adds message to HEAD's log" '
|
|||
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 @@ test_expect_success "deleting by HEAD adds message to HEAD's log" '
|
|||
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' '
|
||||
|
|
@ -204,7 +204,10 @@ test_expect_success "move $m (by HEAD)" '
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -587,103 +590,103 @@ test_expect_success 'stdin works with no input' '
|
|||
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' '
|
||||
|
|
@ -771,28 +774,28 @@ test_expect_success 'stdin create ref works with path with space to blob' '
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -807,7 +810,7 @@ test_expect_success 'stdin update ref works with right old value' '
|
|||
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
|
||||
|
|
@ -816,7 +819,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' '
|
|||
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
|
||||
|
|
@ -977,7 +980,7 @@ test_expect_success 'stdin update refs fails with wrong old value' '
|
|||
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 &&
|
||||
|
|
@ -1010,123 +1013,123 @@ test_expect_success 'stdin -z works on empty input' '
|
|||
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' '
|
||||
|
|
@ -1172,14 +1175,14 @@ test_expect_success 'stdin -z create ref works with path with space to blob' '
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -1188,7 +1191,7 @@ test_expect_success 'stdin -z create ref fails when ref exists' '
|
|||
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
|
||||
'
|
||||
|
|
@ -1197,28 +1200,28 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -1233,7 +1236,7 @@ test_expect_success 'stdin -z update ref works with right old value' '
|
|||
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
|
||||
|
|
@ -1242,7 +1245,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' '
|
|||
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
|
||||
|
|
@ -1348,7 +1351,7 @@ test_expect_success 'stdin -z update refs fails with wrong old value' '
|
|||
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 &&
|
||||
|
|
@ -1427,13 +1430,13 @@ test_expect_success 'handle per-worktree refs in refs/bisect' '
|
|||
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 &&
|
||||
|
|
@ -1489,7 +1492,7 @@ test_expect_success 'transaction exits on multiple aborts' '
|
|||
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' '
|
||||
|
|
@ -1500,7 +1503,7 @@ test_expect_success 'transaction exits on start after prepare' '
|
|||
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' '
|
||||
|
|
@ -1661,7 +1664,7 @@ test_expect_success PIPE 'transaction flushes status updates' '
|
|||
|
||||
# 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 &&
|
||||
|
|
@ -1687,7 +1690,7 @@ do
|
|||
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" '
|
||||
|
|
@ -1695,9 +1698,9 @@ do
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -1730,7 +1733,7 @@ do
|
|||
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
|
||||
|
|
@ -1756,13 +1759,13 @@ do
|
|||
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" '
|
||||
|
|
@ -1770,7 +1773,7 @@ do
|
|||
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" '
|
||||
|
|
@ -1778,16 +1781,16 @@ do
|
|||
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
|
||||
|
|
@ -1825,9 +1828,9 @@ do
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -1890,16 +1893,16 @@ do
|
|||
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" '
|
||||
|
|
@ -1935,7 +1938,7 @@ do
|
|||
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
|
||||
'
|
||||
|
||||
|
|
@ -2010,7 +2013,7 @@ do
|
|||
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" '
|
||||
|
|
@ -2022,7 +2025,7 @@ do
|
|||
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" '
|
||||
|
|
@ -2030,7 +2033,7 @@ do
|
|||
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
|
||||
|
|
@ -2041,7 +2044,7 @@ do
|
|||
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
|
||||
|
|
@ -2052,7 +2055,7 @@ do
|
|||
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
|
||||
|
|
@ -2072,7 +2075,7 @@ do
|
|||
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" '
|
||||
|
|
@ -2084,7 +2087,7 @@ do
|
|||
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" '
|
||||
|
|
|
|||
|
|
@ -213,19 +213,19 @@ test_expect_success 'show-ref --verify with dangling ref' '
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -108,12 +108,12 @@ test_expect_success setup '
|
|||
|
||||
test_expect_success 'correct usage on sub-command -h' '
|
||||
git reflog expire -h >err &&
|
||||
grep "git reflog expire" err
|
||||
test_grep "git reflog expire" err
|
||||
'
|
||||
|
||||
test_expect_success 'correct usage on "git reflog show -h"' '
|
||||
git reflog show -h >err &&
|
||||
grep -F "git reflog [show]" err
|
||||
test_grep -F "git reflog [show]" err
|
||||
'
|
||||
|
||||
test_expect_success 'pass through -- to sub-command' '
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ test_expect_success 'ambiguous main-worktree/HEAD' '
|
|||
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 @@ test_expect_success 'ambiguous worktrees/xx/HEAD' '
|
|||
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' '
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue