diff mbox series

[RFC,6/6] ls-tree: introduce '--pattern' option

Message ID 20221117113023.65865-7-tenglong.tl@alibaba-inc.com (mailing list archive)
State New, archived
Headers show
Series ls-tree: introduce '--pattern' option | expand

Commit Message

Teng Long Nov. 17, 2022, 11:30 a.m. UTC
From: Teng Long <dyroneteng@gmail.com>

The "--pattern" option uses regular expressions to match each
entry, then filter the output of "ls-tree" .

Signed-off-by: Teng Long <dyroneteng@gmail.com>
---
 Documentation/git-ls-tree.txt |  7 ++-
 builtin/ls-tree.c             | 82 +++++++++++++++++++++++------------
 t/t3106-ls-tree-pattern.sh    | 70 ++++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+), 28 deletions(-)
 create mode 100755 t/t3106-ls-tree-pattern.sh

Comments

Ævar Arnfjörð Bjarmason Nov. 17, 2022, 2:03 p.m. UTC | #1
On Thu, Nov 17 2022, Teng Long wrote:

> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
> index 03dd3fbcb26..576fc9ad16f 100644
> --- a/builtin/ls-tree.c
> +++ b/builtin/ls-tree.c
> @@ -13,6 +13,7 @@
>  #include "builtin.h"
>  #include "parse-options.h"
>  #include "pathspec.h"
> +#include <stdio.h>

Aside from anything else I've mentionded (e.g. overall goals), don't
include "<>" headers in anything except git-compat-util.h and similar.

In this case we don't need this at all, but if we did it should be added
there...

> [...]
>  	}
> -	ret = regexec(&r, line, 1, m, 0);
> +
> +	ret = regexec(regex, line, 1, m, 0);

Some whitespace-churn after the last commit...

>  static void show_tree_common_default_long(struct show_tree_data *data)
>  {
>  	int base_len = data->base->len;
> +	struct strbuf sb = STRBUF_INIT;
> +	int sb_len = 0;

It's size_t, not int, so if you need to keep track of a strbuf's length
use the type of its "len".

This would be better named "oldlen" or something...

> +		printf("%s", sb.buf);

puts(sb.buf) instead;

> +	if (pattern && !strlen(pattern))

pattern && !*pattern is more idiomatic IMO.

> +test_expect_success 'combine with "--object-only"' '
> +	cat > expect <<-EOF &&

Style: No " " after ">"
> +		6da7993

Style: Let's avoid \t\t indenting for here-docs, just use the indenting
of the "cat".
Johannes Schindelin Dec. 12, 2022, 8:32 a.m. UTC | #2
Hi,

On Thu, 17 Nov 2022, Teng Long wrote:

> diff --git a/t/t3106-ls-tree-pattern.sh b/t/t3106-ls-tree-pattern.sh
> new file mode 100755
> index 00000000000..e4a81c8c47e
> --- /dev/null
> +++ b/t/t3106-ls-tree-pattern.sh
> @@ -0,0 +1,70 @@
> +#!/bin/sh
> +
> +test_description='ls-tree pattern'
> +
> +TEST_PASSES_SANITIZE_LEAK=true
> +. ./test-lib.sh
> +. "$TEST_DIRECTORY"/lib-t3100.sh
> +
> +test_expect_success 'setup' '
> +	setup_basic_ls_tree_data
> +'
> +
> +test_expect_success 'ls-tree pattern usage' '
> +	test_expect_code 129 git ls-tree --pattern HEAD &&
> +	test_expect_code 128 git ls-tree --pattern "" HEAD >err 2>&1 &&
> +	grep "Not a valid pattern, the value is empty" err
> +'
> +
> +test_expect_success 'combine with "--object-only"' '
> +	cat > expect <<-EOF &&
> +		6da7993
> +	EOF
> +
> +	git ls-tree --object-only --abbrev=7 --pattern "6da7993" HEAD > actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'combine with "--name-only"' '
> +	cat > expect <<-EOF &&
> +		.gitmodules
> +		top-file.t
> +	EOF
> +
> +	git ls-tree --name-only --pattern "\." HEAD > actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'combine with "--long"' '
> +	cat > expect <<-EOF &&
> +		100644 blob 6da7993      61	.gitmodules
> +		100644 blob 02dad95       9	top-file.t
> +	EOF
> +	git ls-tree --long --abbrev=7 --pattern "blob" HEAD > actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'combine with "--format"' '
> +	# Change the output format by replacing space separators with asterisks.
> +	format="%(objectmode)*%(objecttype)*%(objectname)%x09%(path)" &&
> +	pattern="100644\*blob" &&
> +
> +	cat > expect <<-EOF &&
> +		100644*blob*6da7993	.gitmodules
> +		100644*blob*02dad95	top-file.t
> +	EOF
> +
> +	git ls-tree --abbrev=7 --format "$format" --pattern "$pattern" HEAD >actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'default output format (only with "--pattern" option)' '
> +	cat > expect <<-EOF &&
> +		100644 blob 6da7993ca1a3435f63c598464f77bdc6dae15aa5	.gitmodules
> +		100644 blob 02dad956d9274a70f7fafe45a5ffa2e123acd9a8	top-file.t
> +	EOF
> +	git ls-tree --pattern "blob" HEAD > actual &&
> +	test_cmp expect actual
> +'
> +
> +test_done

The hard-coded object IDs break the `linux-sha256` job, as pointed out in
https://github.com/git/git/blob/6ab7651d8669/whats-cooking.txt#L522-L537.

Please squash this in to address this (Junio, please feel free to
cherry-pick this on top of `tl/ls-tree--pattern` to reduce the number of
CI failures):

-- snipsnap --
Subject: [PATCH] fixup! ls-tree: introduce '--pattern' option

We need to avoid hard-coding OIDs because of the SHA-256 support.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t3106-ls-tree-pattern.sh | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/t/t3106-ls-tree-pattern.sh b/t/t3106-ls-tree-pattern.sh
index e4a81c8c47e..8aaca307804 100755
--- a/t/t3106-ls-tree-pattern.sh
+++ b/t/t3106-ls-tree-pattern.sh
@@ -17,11 +17,12 @@ test_expect_success 'ls-tree pattern usage' '
 '

 test_expect_success 'combine with "--object-only"' '
+	oid="$(git rev-parse --short HEAD:.gitmodules)" &&
 	cat > expect <<-EOF &&
-		6da7993
+		$oid
 	EOF

-	git ls-tree --object-only --abbrev=7 --pattern "6da7993" HEAD > actual &&
+	git ls-tree --object-only --abbrev=7 --pattern "$oid" HEAD > actual &&
 	test_cmp expect actual
 '

@@ -36,22 +37,26 @@ test_expect_success 'combine with "--name-only"' '
 '

 test_expect_success 'combine with "--long"' '
+	oid1="$(git rev-parse --short HEAD:.gitmodules)" &&
+	oid2="$(git rev-parse --short HEAD:top-file.t)" &&
 	cat > expect <<-EOF &&
-		100644 blob 6da7993      61	.gitmodules
-		100644 blob 02dad95       9	top-file.t
+		100644 blob $oid1      61	.gitmodules
+		100644 blob $oid2       9	top-file.t
 	EOF
 	git ls-tree --long --abbrev=7 --pattern "blob" HEAD > actual &&
 	test_cmp expect actual
 '

 test_expect_success 'combine with "--format"' '
+	oid1="$(git rev-parse --short HEAD:.gitmodules)" &&
+	oid2="$(git rev-parse --short HEAD:top-file.t)" &&
 	# Change the output format by replacing space separators with asterisks.
 	format="%(objectmode)*%(objecttype)*%(objectname)%x09%(path)" &&
 	pattern="100644\*blob" &&

 	cat > expect <<-EOF &&
-		100644*blob*6da7993	.gitmodules
-		100644*blob*02dad95	top-file.t
+		100644*blob*$oid1	.gitmodules
+		100644*blob*$oid2	top-file.t
 	EOF

 	git ls-tree --abbrev=7 --format "$format" --pattern "$pattern" HEAD >actual &&
@@ -59,9 +64,11 @@ test_expect_success 'combine with "--format"' '
 '

 test_expect_success 'default output format (only with "--pattern" option)' '
+	oid1="$(git rev-parse HEAD:.gitmodules)" &&
+	oid2="$(git rev-parse HEAD:top-file.t)" &&
 	cat > expect <<-EOF &&
-		100644 blob 6da7993ca1a3435f63c598464f77bdc6dae15aa5	.gitmodules
-		100644 blob 02dad956d9274a70f7fafe45a5ffa2e123acd9a8	top-file.t
+		100644 blob $oid1	.gitmodules
+		100644 blob $oid2	top-file.t
 	EOF
 	git ls-tree --pattern "blob" HEAD > actual &&
 	test_cmp expect actual
--
2.38.1.windows.1.24.g5ff6506c583.dirty
Junio C Hamano Dec. 12, 2022, 11:57 p.m. UTC | #3
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> The hard-coded object IDs break the `linux-sha256` job, as pointed out in
> https://github.com/git/git/blob/6ab7651d8669/whats-cooking.txt#L522-L537.
>
> Please squash this in to address this (Junio, please feel free to
> cherry-pick this on top of `tl/ls-tree--pattern` to reduce the number of
> CI failures):

These days, I gather topics with known CI breakages near the tip of
'seen', and push out only the good bottom half of 'seen' until such
a topic gets rerolled, at which time it gets added back to the set
of topics pushed out on 'seen' again (and then ejected if it CI
breaks).  I excluded the part with the topic from last night's
pushout.

By the way do you know anything about xterm-256color error in win
test(6)?

https://github.com/git/git/actions/runs/3676139624/jobs/6216575838#step:5:196

I do not think we hard-code any specific terminal name (other than
dumb and possibly vt100) in our tests or binaries, so it may be
coming from the CI runner environment---some parts incorrectly think
xterm-256color is available there while there is no support for the
particular terminal?

Thanks.
Junio C Hamano Dec. 14, 2022, 5:27 a.m. UTC | #4
Junio C Hamano <gitster@pobox.com> writes:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
>> The hard-coded object IDs break the `linux-sha256` job, as pointed out in
>> https://github.com/git/git/blob/6ab7651d8669/whats-cooking.txt#L522-L537.
>>
>> Please squash this in to address this (Junio, please feel free to
>> cherry-pick this on top of `tl/ls-tree--pattern` to reduce the number of
>> CI failures):

After re-reading the patches, I am very much inclined to drop this
topic, which does not add much value to the system and adds an odd
corner case in the UI.

Who needs "git ls-tree --pattern='blob 486' HEAD" that is a synonym
to "git ls-tree HEAD | grep 'blob 486'"?  Should we end up adding
the same option to "git ls-files", "git status", "git ls-remote",
"git remote", "git branch --list", etc. for consistency?

This is simply insane and goes directly against the "one tool does
one job well, and can be combined with other such tools via pipe",
which is a key to scale the usability of a set of tools.
Ævar Arnfjörð Bjarmason Dec. 14, 2022, 10:03 a.m. UTC | #5
On Wed, Dec 14 2022, Junio C Hamano wrote:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>>
>>> The hard-coded object IDs break the `linux-sha256` job, as pointed out in
>>> https://github.com/git/git/blob/6ab7651d8669/whats-cooking.txt#L522-L537.
>>>
>>> Please squash this in to address this (Junio, please feel free to
>>> cherry-pick this on top of `tl/ls-tree--pattern` to reduce the number of
>>> CI failures):
>
> After re-reading the patches, I am very much inclined to drop this
> topic, which does not add much value to the system and adds an odd
> corner case in the UI.
>
> Who needs "git ls-tree --pattern='blob 486' HEAD" that is a synonym
> to "git ls-tree HEAD | grep 'blob 486'"?  Should we end up adding
> the same option to "git ls-files", "git status", "git ls-remote",
> "git remote", "git branch --list", etc. for consistency?
>
> This is simply insane and goes directly against the "one tool does
> one job well, and can be combined with other such tools via pipe",
> which is a key to scale the usability of a set of tools.

I agree, and FWIW I read
https://lore.kernel.org/git/20221121114150.3473-1-tenglong.tl@alibaba-inc.com/
as the submitter of the topic agreeing to drop it ~3 weeks ago.
Junio C Hamano Dec. 14, 2022, 10:38 a.m. UTC | #6
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

>> This is simply insane and goes directly against the "one tool does
>> one job well, and can be combined with other such tools via pipe",
>> which is a key to scale the usability of a set of tools.
>
> I agree, and FWIW I read
> https://lore.kernel.org/git/20221121114150.3473-1-tenglong.tl@alibaba-inc.com/
> as the submitter of the topic agreeing to drop it ~3 weeks ago.

I actually read "it's useful to me" as resisting, but I already
discarded the topic (not just "I stopped merging it to 'seen'", but
"I no longer have the topic branch with me").

Thanks.
Johannes Schindelin March 27, 2023, 10:37 a.m. UTC | #7
Hi Junio

On Tue, 13 Dec 2022, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > The hard-coded object IDs break the `linux-sha256` job, as pointed out in
> > https://github.com/git/git/blob/6ab7651d8669/whats-cooking.txt#L522-L537.
> >
> > Please squash this in to address this (Junio, please feel free to
> > cherry-pick this on top of `tl/ls-tree--pattern` to reduce the number of
> > CI failures):
>
> These days, I gather topics with known CI breakages near the tip of
> 'seen', and push out only the good bottom half of 'seen' until such
> a topic gets rerolled, at which time it gets added back to the set
> of topics pushed out on 'seen' again (and then ejected if it CI
> breaks).  I excluded the part with the topic from last night's
> pushout.
>
> By the way do you know anything about xterm-256color error in win
> test(6)?
>
> https://github.com/git/git/actions/runs/3676139624/jobs/6216575838#step:5:196

Unfortunately by the time I got back to this mail, the log had expired.
Here is a link to the same symptom in a newer build:

https://github.com/git/git/actions/runs/4523517641/jobs/7966768829#step:6:63

> I do not think we hard-code any specific terminal name (other than
> dumb and possibly vt100) in our tests or binaries, so it may be
> coming from the CI runner environment---some parts incorrectly think
> xterm-256color is available there while there is no support for the
> particular terminal?

The TERM is hard-coded in the MSYS2 runtime:
https://github.com/git-for-windows/msys2-runtime/commit/bd627864ab4189984cdb0892c00f91e39c4e8243

Note: The MSYS2 runtime merely wants to ensure that `TERM` is set; If it
already has a value, that value remains unchanged.

And to save on bandwidth/time (in a desperate attempt to counter the
ever-growing runtimes of Git's CI builds), I liberally exclude files from
the "minimal subset of Git for Windows' SDK", e.g. `/usr/lib/terminfo/`
and `/usr/share/terminfo/`. That's why `tput` cannot figure out what to do
with this `TERM` value.

If these `tput` errors become too much of a sore in your eye, I see two
ways forward:

- Set `TERM=dumb` for the Windows jobs

- Use a simple shim like this one in `ci/` (and maybe even in
  `t/test-lib.sh`):

  tput() {
  	printf '\e[%sm%s'"$(test sgr0 != $1 || echo '\x0f')" "$( \
		case $1 in
		bold) echo 1;;
		dim) echo 2;;
		rev) echo 7;;
		setaf) echo 3$2;;
		setab) echo 4$2;;
		esac \
	)"
  }

Personally, I do not really want to work on this, not before much bigger
fish are fed first. For example, the friction regarding the CI build times
is becoming quite crushing.

Ciao,
Johannes
Junio C Hamano March 27, 2023, 8:42 p.m. UTC | #8
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> The TERM is hard-coded in the MSYS2 runtime:
> https://github.com/git-for-windows/msys2-runtime/commit/bd627864ab4189984cdb0892c00f91e39c4e8243
>
> Note: The MSYS2 runtime merely wants to ensure that `TERM` is set; If it
> already has a value, that value remains unchanged.
>
> And to save on bandwidth/time (in a desperate attempt to counter the
> ever-growing runtimes of Git's CI builds), I liberally exclude files from
> the "minimal subset of Git for Windows' SDK", e.g. `/usr/lib/terminfo/`
> and `/usr/share/terminfo/`. That's why `tput` cannot figure out what to do
> with this `TERM` value.

Ah, so it is not like "you ship vt100 but not xterm-color yet the
runtime insists you are by default on xterm-color", so "set it to
something your terminfo database understands" would not work.  I am
not sure what unintended fallouts there would be from setting it to
dumb, though.  Our tests are designed for unattended testing, and
they are even capable of running without control terminal, so it
should be OK to force TERM=dumb, I guess.

> If these `tput` errors become too much of a sore in your eye, I see two
> ways forward:

Not at all.  As long as it is clear that they are to be expected and
to be ignored when fishing for real breakages, it is fine by me.
Others may care more than I do, but then they are welcome to send
fixes your way ;-)

Thanks.
Jeff King March 28, 2023, 6:08 p.m. UTC | #9
On Mon, Mar 27, 2023 at 01:42:11PM -0700, Junio C Hamano wrote:

> > And to save on bandwidth/time (in a desperate attempt to counter the
> > ever-growing runtimes of Git's CI builds), I liberally exclude files from
> > the "minimal subset of Git for Windows' SDK", e.g. `/usr/lib/terminfo/`
> > and `/usr/share/terminfo/`. That's why `tput` cannot figure out what to do
> > with this `TERM` value.
> 
> Ah, so it is not like "you ship vt100 but not xterm-color yet the
> runtime insists you are by default on xterm-color", so "set it to
> something your terminfo database understands" would not work.  I am
> not sure what unintended fallouts there would be from setting it to
> dumb, though.  Our tests are designed for unattended testing, and
> they are even capable of running without control terminal, so it
> should be OK to force TERM=dumb, I guess.

We already force TERM=dumb in test-lib.sh, so the tests themselves
should behave the same. The original terminal is only used for colorized
output from the test harness itself. But I don't think we'd colorize
anyway in CI, since stdout is not a terminal (and we tend to further go
through "prove" anyway).

So it should be fine to just set TERM=dumb everywhere. What actually
confuses me is that we try to do so already:

  $ git grep -B1 dumb ci/lib.sh
  ci/lib.sh-# GitHub Action doesn't set TERM, which is required by tput
  ci/lib.sh:export TERM=${TERM:-dumb}

Pushing a stripped-down workflow file to just run "echo $TERM" shows
that it seems to already be set by Actions to "dumb" on ubuntu-latest,
but is xterm-256color on windows-latest.

So maybe we just want to make the line above unconditionally set $TERM?

-Peff
Junio C Hamano March 28, 2023, 7:31 p.m. UTC | #10
Jeff King <peff@peff.net> writes:

> So it should be fine to just set TERM=dumb everywhere. What actually
> confuses me is that we try to do so already:
>
>   $ git grep -B1 dumb ci/lib.sh
>   ci/lib.sh-# GitHub Action doesn't set TERM, which is required by tput
>   ci/lib.sh:export TERM=${TERM:-dumb}
>
> Pushing a stripped-down workflow file to just run "echo $TERM" shows
> that it seems to already be set by Actions to "dumb" on ubuntu-latest,
> but is xterm-256color on windows-latest.
>
> So maybe we just want to make the line above unconditionally set $TERM?

I thought that Dscho earlier said xterm-256 is set by mingw when
nothing is set to TERM, which explains why TERM=${TERM:-dumb} is
not good enough to "fix" this one for them.

I am fine with an unconditional assignment for CI.

Thanks.
Jeff King March 28, 2023, 7:59 p.m. UTC | #11
On Tue, Mar 28, 2023 at 12:31:59PM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > So it should be fine to just set TERM=dumb everywhere. What actually
> > confuses me is that we try to do so already:
> >
> >   $ git grep -B1 dumb ci/lib.sh
> >   ci/lib.sh-# GitHub Action doesn't set TERM, which is required by tput
> >   ci/lib.sh:export TERM=${TERM:-dumb}
> >
> > Pushing a stripped-down workflow file to just run "echo $TERM" shows
> > that it seems to already be set by Actions to "dumb" on ubuntu-latest,
> > but is xterm-256color on windows-latest.
> >
> > So maybe we just want to make the line above unconditionally set $TERM?
> 
> I thought that Dscho earlier said xterm-256 is set by mingw when
> nothing is set to TERM, which explains why TERM=${TERM:-dumb} is
> not good enough to "fix" this one for them.

Ah, I took it to mean that in our code, we'd default to xterm256-color.
But perhaps it is the outer bash which is doing so, via that runtime. I
tested with a dummy workflow like:

  name: fake ci
  on: [push, pull_request]
  jobs:
    ubuntu:
      runs-on: ubuntu-latest
      steps:
        - name: check TERM
	- run: |
	  echo TERM=$TERM
    windows:
      runs-on: windows-latest
      steps:
        - name: check TERM
          shell: bash
          run: |
            echo TERM=$TERM

and got "dumb" for ubuntu (which is not set by us; this is before
ci/lib.sh is run) and "xterm256-color" for windows (I guess because it's
mingw bash).

At any rate, unconditionally setting TERM=dumb should cover all cases,
I'd think. I'm happy to prepare a patch.

-Peff
Jeff King March 28, 2023, 8:43 p.m. UTC | #12
On Tue, Mar 28, 2023 at 03:59:42PM -0400, Jeff King wrote:

> At any rate, unconditionally setting TERM=dumb should cover all cases,
> I'd think. I'm happy to prepare a patch.

Hmph. So I started to do this, and notice there are a bunch of tput
calls in the ci/ scripts themselves.

So if I do a dummy workflow like this:

-- >8 --
name: fake ci
on: [push, pull_request]
jobs:
  ubuntu:
    runs-on: ubuntu-latest
    steps:
      - name: check TERM
        run: |
          echo TERM=$TERM
          echo $(tput setaf 1)this is setaf 1$(tput sgr0)
  windows:
    runs-on: windows-latest
    steps:
      - name: check TERM
        shell: bash
        run: |
          echo TERM=$TERM
          echo $(tput setaf 1)this is setaf 1$(tput sgr0)
-- 8< --

then I note two interesting things:

  - the attempted colorization does nothing on ubuntu, because it's
    already TERM=dumb

  - the colorization on windows works! But I thought it was exactly
    these sorts of tputs that are causing the error messages.

So now I'm more confused than ever. I have a feeling we would be better
off just using ansi codes instead of tput, as Johannes suggested
earlier. But given the twists and turns here, and the fact that nobody
really seems that bothered by the status quo, I'm inclined to just leave
it alone for now.

-Peff
Junio C Hamano March 28, 2023, 9:05 p.m. UTC | #13
Jeff King <peff@peff.net> writes:

> then I note two interesting things:
>
>   - the attempted colorization does nothing on ubuntu, because it's
>     already TERM=dumb

This is expected, I think.

>   - the colorization on windows works! But I thought it was exactly
>     these sorts of tputs that are causing the error messages.

Yeah, tput complains about unknown terminal as terminfo database is
not shipped.  But perhaps it falls back to bog-standard ANSI after
doing its complaining?  I dunno.

> ... I'm inclined to just leave it alone for now.

That's fine, too.

Thanks.
diff mbox series

Patch

diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index 0240adb8eec..39346409f2f 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -10,7 +10,7 @@  SYNOPSIS
 --------
 [verse]
 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
-	    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]
+	    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>] [--pattern=<pattern>]
 	    <tree-ish> [<path>...]
 
 DESCRIPTION
@@ -93,6 +93,11 @@  OPTIONS
 	format-altering options, including `--long`, `--name-only`
 	and `--object-only`.
 
+--pattern=<pattern>::
+    The <pattern> is a string of regular expression format used to
+    match each entry. Unmatched entries will be filtered and not
+    dump to the output.
+
 [<path>...]::
 	When paths are given, show them (note that this isn't really raw
 	pathnames, but rather a list of patterns to match).  Otherwise
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 03dd3fbcb26..576fc9ad16f 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -13,6 +13,7 @@ 
 #include "builtin.h"
 #include "parse-options.h"
 #include "pathspec.h"
+#include <stdio.h>
 
 static int line_termination = '\n';
 #define LS_RECURSIVE 1
@@ -25,6 +26,7 @@  static int chomp_prefix;
 static const char *ls_tree_prefix;
 static const char *format;
 static const char *pattern;
+static regex_t *regex;
 struct show_tree_data {
 	unsigned mode;
 	enum object_type type;
@@ -47,29 +49,29 @@  static enum ls_tree_cmdmode {
 	MODE_OBJECT_ONLY,
 } cmdmode;
 
-__attribute__((unused))
 static int match_pattern(const char *line)
 {
 	int ret = 0;
-	regex_t r;
 	regmatch_t m[1];
 	char errbuf[64];
 
-	ret = regcomp(&r, pattern, 0);
-	if (ret) {
-		regerror(ret, &r, errbuf, sizeof(errbuf));
-		die("failed regcomp() for pattern '%s' (%s)", pattern, errbuf);
+	if (!regex) {
+		regex = xmalloc(sizeof(*regex));
+		ret = regcomp(regex, pattern, 0);
+		if (ret) {
+			regerror(ret, regex, errbuf, sizeof(errbuf));
+			die("failed regcomp() for pattern '%s' (%s)", pattern, errbuf);
+		}
 	}
-	ret = regexec(&r, line, 1, m, 0);
+
+	ret = regexec(regex, line, 1, m, 0);
 	if (ret) {
 		if (ret == REG_NOMATCH)
-			goto cleanup;
-		regerror(ret, &r, errbuf, sizeof(errbuf));
+			return ret;
+		regerror(ret, regex, errbuf, sizeof(errbuf));
 		die("failed regexec() for subject '%s' (%s)", line, errbuf);
 	}
 
-cleanup:
-	regfree(&r);
 	return ret;
 }
 
@@ -194,8 +196,12 @@  static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
 
 	baselen = base->len;
 	strbuf_expand(&sb, format, expand_show_tree, &data);
-	strbuf_addch(&sb, line_termination);
-	fwrite(sb.buf, sb.len, 1, stdout);
+
+	if (!pattern || !match_pattern(sb.buf)) {
+		strbuf_addch(&sb, line_termination);
+		fwrite(sb.buf, sb.len, 1, stdout);
+	}
+
 	strbuf_release(&sb);
 	strbuf_setlen(base, baselen);
 	return recurse;
@@ -232,19 +238,33 @@  static int show_tree_common(struct show_tree_data *data, int *recurse,
 static void show_tree_common_default_long(struct show_tree_data *data)
 {
 	int base_len = data->base->len;
+	struct strbuf sb = STRBUF_INIT;
+	int sb_len = 0;
 
 	if (data->size_text)
-		printf("%06o %s %s %7s\t", data->mode, type_name(data->type),
-		       find_unique_abbrev(data->oid, abbrev), data->size_text);
+		strbuf_addf(&sb, "%06o %s %s %7s\t", data->mode,
+			    type_name(data->type),
+			    find_unique_abbrev(data->oid, abbrev),
+			    data->size_text);
 	else
-		printf("%06o %s %s\t", data->mode, type_name(data->type),
-		       find_unique_abbrev(data->oid, abbrev));
+		strbuf_addf(&sb, "%06o %s %s\t", data->mode,
+			    type_name(data->type),
+			    find_unique_abbrev(data->oid, abbrev));
 
 	strbuf_addstr(data->base, data->pathname);
-	write_name_quoted_relative(data->base->buf,
-				   chomp_prefix ? ls_tree_prefix : NULL, stdout,
-				   line_termination);
+	sb_len = sb.len;
+	strbuf_addbuf(&sb, data->base);
+
+	if (!pattern || !match_pattern(sb.buf)) {
+		strbuf_setlen(&sb, sb_len);
+		printf("%s", sb.buf);
+		write_name_quoted_relative(data->base->buf,
+					   chomp_prefix ? ls_tree_prefix : NULL,
+					   stdout, line_termination);
+	}
 	strbuf_setlen(data->base, base_len);
+
+	strbuf_release(&sb);
 }
 
 static int show_tree_default(const struct object_id *oid, struct strbuf *base,
@@ -306,9 +326,11 @@  static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
 		return early;
 
 	strbuf_addstr(base, pathname);
-	write_name_quoted_relative(base->buf,
-				   chomp_prefix ? ls_tree_prefix : NULL,
-				   stdout, line_termination);
+	if (!pattern || !match_pattern(base->buf)) {
+		write_name_quoted_relative(base->buf,
+					   chomp_prefix ? ls_tree_prefix : NULL,
+					   stdout, line_termination);
+	}
 	strbuf_setlen(base, baselen);
 	return recurse;
 }
@@ -320,12 +342,14 @@  static int show_tree_object(const struct object_id *oid, struct strbuf *base,
 	int early;
 	int recurse;
 	struct show_tree_data data = { 0 };
+	const char *oid_text = find_unique_abbrev(oid, abbrev);
 
 	early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
 	if (early >= 0)
 		return early;
 
-	printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
+	if (!pattern || !match_pattern(oid_text))
+		printf("%s%c", oid_text, line_termination);
 	return recurse;
 }
 
@@ -391,8 +415,10 @@  int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 			 N_("list entire tree; not just current directory "
 			    "(implies --full-name)")),
 		OPT_STRING_F(0, "format", &format, N_("format"),
-					 N_("format to use for the output"),
-					 PARSE_OPT_NONEG),
+			     N_("format to use for the output"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING(0, "pattern", &pattern, "pattern",
+			   "pattern to use to match the output"),
 		OPT__ABBREV(&abbrev),
 		OPT_END()
 	};
@@ -430,10 +456,12 @@  int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 		usage_with_options(ls_tree_usage, ls_tree_options);
 	if (get_oid(argv[0], &oid))
 		die("Not a valid object name %s", argv[0]);
+	if (pattern && !strlen(pattern))
+		die("Not a valid pattern, the value is empty");
 
 	/*
 	 * show_recursive() rolls its own matching code and is
-	 * generally ignorant of 'struct pathspec'. The magic mask
+	 * generally ignorant f 'struct pathspec'. The magic mask
 	 * cannot be lifted until it is converted to use
 	 * match_pathspec() or tree_entry_interesting()
 	 */
diff --git a/t/t3106-ls-tree-pattern.sh b/t/t3106-ls-tree-pattern.sh
new file mode 100755
index 00000000000..e4a81c8c47e
--- /dev/null
+++ b/t/t3106-ls-tree-pattern.sh
@@ -0,0 +1,70 @@ 
+#!/bin/sh
+
+test_description='ls-tree pattern'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t3100.sh
+
+test_expect_success 'setup' '
+	setup_basic_ls_tree_data
+'
+
+test_expect_success 'ls-tree pattern usage' '
+	test_expect_code 129 git ls-tree --pattern HEAD &&
+	test_expect_code 128 git ls-tree --pattern "" HEAD >err 2>&1 &&
+	grep "Not a valid pattern, the value is empty" err
+'
+
+test_expect_success 'combine with "--object-only"' '
+	cat > expect <<-EOF &&
+		6da7993
+	EOF
+
+	git ls-tree --object-only --abbrev=7 --pattern "6da7993" HEAD > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'combine with "--name-only"' '
+	cat > expect <<-EOF &&
+		.gitmodules
+		top-file.t
+	EOF
+
+	git ls-tree --name-only --pattern "\." HEAD > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'combine with "--long"' '
+	cat > expect <<-EOF &&
+		100644 blob 6da7993      61	.gitmodules
+		100644 blob 02dad95       9	top-file.t
+	EOF
+	git ls-tree --long --abbrev=7 --pattern "blob" HEAD > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'combine with "--format"' '
+	# Change the output format by replacing space separators with asterisks.
+	format="%(objectmode)*%(objecttype)*%(objectname)%x09%(path)" &&
+	pattern="100644\*blob" &&
+
+	cat > expect <<-EOF &&
+		100644*blob*6da7993	.gitmodules
+		100644*blob*02dad95	top-file.t
+	EOF
+
+	git ls-tree --abbrev=7 --format "$format" --pattern "$pattern" HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'default output format (only with "--pattern" option)' '
+	cat > expect <<-EOF &&
+		100644 blob 6da7993ca1a3435f63c598464f77bdc6dae15aa5	.gitmodules
+		100644 blob 02dad956d9274a70f7fafe45a5ffa2e123acd9a8	top-file.t
+	EOF
+	git ls-tree --pattern "blob" HEAD > actual &&
+	test_cmp expect actual
+'
+
+test_done