Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two small 'git repack' fixes #1098

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions Documentation/git-repack.txt
Expand Up @@ -76,8 +76,9 @@ to the new separate pack will be written.
linkgit:git-pack-objects[1].

-q::
Pass the `-q` option to 'git pack-objects'. See
linkgit:git-pack-objects[1].
--quiet::
Show no progress over the standard error stream and pass the `-q`
option to 'git pack-objects'. See linkgit:git-pack-objects[1].

-n::
Do not update the server information with
Expand Down
8 changes: 5 additions & 3 deletions builtin/repack.c
Expand Up @@ -612,7 +612,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
struct tempfile *refs_snapshot = NULL;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff King wrote (reply to this):

On Fri, Dec 17, 2021 at 04:28:46PM +0000, Derrick Stolee via GitGitGadget wrote:

> From: Derrick Stolee <dstolee@microsoft.com>
> 
> While testing some ideas in 'git repack', I ran it with '--quiet' and
> discovered that some progress output was still shown. Specifically, the
> output for writing the multi-pack-index showed the progress.
> 
> The 'show_progress' variable in cmd_repack() is initialized with
> isatty(2) and is not modified at all by the '--quiet' flag. The
> '--quiet' flag modifies the po_args.quiet option which is translated
> into a '--quiet' flag for the 'git pack-objects' child process. However,
> 'show_progress' is used to directly send progress information to the
> multi-pack-index writing logic which does not use a child process.
> 
> The fix here is to modify 'show_progress' to be false if po_opts.quiet
> is true, and isatty(2) otherwise. This new expectation simplifies a
> later condition that checks both.

Makes sense. I wondered if you might have to decide what to do with
"--progress --quiet", but we do not have an explicit progress option for
git-repack in the first place.

> This is difficult to test because the isatty(2) already prevents the
> progess indicators from appearing when we redirect stderr to a file.

You'd need test_terminal. Something like this:

diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 8c4ba6500b..b673c49650 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -5,6 +5,7 @@ test_description='git repack works correctly'
 . ./test-lib.sh
 . "${TEST_DIRECTORY}/lib-bitmap.sh"
 . "${TEST_DIRECTORY}/lib-midx.sh"
+. "${TEST_DIRECTORY}/lib-terminal.sh"
 
 commit_and_pack () {
 	test_commit "$@" 1>&2 &&
@@ -387,4 +388,10 @@ test_expect_success '--write-midx -b packs non-kept objects' '
 	)
 '
 
+test_expect_success TTY '--quiet disables progress' '
+	test_terminal env GIT_PROGRESS_DELAY=0 \
+		git -C midx repack -ad --quiet --write-midx 2>stderr &&
+	test_must_be_empty stderr
+'
+
 test_done

-Peff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Derrick Stolee wrote (reply to this):

On 12/17/2021 1:10 PM, Jeff King wrote:
> On Fri, Dec 17, 2021 at 04:28:46PM +0000, Derrick Stolee via GitGitGadget wrote:

>> This is difficult to test because the isatty(2) already prevents the
>> progess indicators from appearing when we redirect stderr to a file.
> 
> You'd need test_terminal. Something like this:
> 
> diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
> index 8c4ba6500b..b673c49650 100755
> --- a/t/t7700-repack.sh
> +++ b/t/t7700-repack.sh
> @@ -5,6 +5,7 @@ test_description='git repack works correctly'
>  . ./test-lib.sh
>  . "${TEST_DIRECTORY}/lib-bitmap.sh"
>  . "${TEST_DIRECTORY}/lib-midx.sh"
> +. "${TEST_DIRECTORY}/lib-terminal.sh"
>  
>  commit_and_pack () {
>  	test_commit "$@" 1>&2 &&
> @@ -387,4 +388,10 @@ test_expect_success '--write-midx -b packs non-kept objects' '
>  	)
>  '
>  
> +test_expect_success TTY '--quiet disables progress' '
> +	test_terminal env GIT_PROGRESS_DELAY=0 \
> +		git -C midx repack -ad --quiet --write-midx 2>stderr &&
> +	test_must_be_empty stderr
> +'
> +
>  test_done

Thanks. I added this test.

When first running the test, it failed because I didn't have the
IO::Pty Perl module installed. I'm not sure why I don't fail with
other tests that use test_terminal. If someone knows more about
what is going on, then maybe we need to expand the TTY prereq?

Thanks,
-Stolee

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff King wrote (reply to this):

On Mon, Dec 20, 2021 at 08:37:52AM -0500, Derrick Stolee wrote:

> > +test_expect_success TTY '--quiet disables progress' '
> > +	test_terminal env GIT_PROGRESS_DELAY=0 \
> > +		git -C midx repack -ad --quiet --write-midx 2>stderr &&
> > +	test_must_be_empty stderr
> > +'
> > +
> >  test_done
> 
> Thanks. I added this test.
> 
> When first running the test, it failed because I didn't have the
> IO::Pty Perl module installed. I'm not sure why I don't fail with
> other tests that use test_terminal. If someone knows more about
> what is going on, then maybe we need to expand the TTY prereq?

Weird. I uninstalled IO::Pty, and get:

  checking prerequisite: TTY
  
  [...prereq code...]

  
  + perl /home/peff/compile/git/t/test-terminal.perl sh -c test -t 1 && test -t 2
  + command /usr/bin/perl /home/peff/compile/git/t/test-terminal.perl sh -c test -t 1 && test -t 2
  Can't locate IO/Pty.pm in @INC (you may need to install the IO::Pty module) (@INC contains: [...etc...]
  BEGIN failed--compilation aborted at /home/peff/compile/git/t/test-terminal.perl line 5.
  prerequisite TTY not satisfied
  ok 25 # skip --quiet disables progress (missing TTY)

What does running with "-x -i" say for the prereq?

-Peff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Derrick Stolee wrote (reply to this):

On 12/20/2021 8:49 AM, Jeff King wrote:
> On Mon, Dec 20, 2021 at 08:37:52AM -0500, Derrick Stolee wrote:
> 
>>> +test_expect_success TTY '--quiet disables progress' '
>>> +	test_terminal env GIT_PROGRESS_DELAY=0 \
>>> +		git -C midx repack -ad --quiet --write-midx 2>stderr &&
>>> +	test_must_be_empty stderr
>>> +'
>>> +
>>>  test_done
>>
>> Thanks. I added this test.
>>
>> When first running the test, it failed because I didn't have the
>> IO::Pty Perl module installed. I'm not sure why I don't fail with
>> other tests that use test_terminal. If someone knows more about
>> what is going on, then maybe we need to expand the TTY prereq?
> 
> Weird. I uninstalled IO::Pty, and get:
> 
>   checking prerequisite: TTY
>   
>   [...prereq code...]
> 
>   
>   + perl /home/peff/compile/git/t/test-terminal.perl sh -c test -t 1 && test -t 2
>   + command /usr/bin/perl /home/peff/compile/git/t/test-terminal.perl sh -c test -t 1 && test -t 2
>   Can't locate IO/Pty.pm in @INC (you may need to install the IO::Pty module) (@INC contains: [...etc...]
>   BEGIN failed--compilation aborted at /home/peff/compile/git/t/test-terminal.perl line 5.
>   prerequisite TTY not satisfied
>   ok 25 # skip --quiet disables progress (missing TTY)
> 
> What does running with "-x -i" say for the prereq?

Ok, I got this same error and misread it as an error. The prereq
is working just fine. Thanks for checking.

-Stolee

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Ævar Arnfjörð Bjarmason wrote (reply to this):


On Fri, Dec 17 2021, Derrick Stolee via GitGitGadget wrote:

> From: Derrick Stolee <dstolee@microsoft.com>
>
> While testing some ideas in 'git repack', I ran it with '--quiet' and
> discovered that some progress output was still shown. Specifically, the
> output for writing the multi-pack-index showed the progress.
>
> The 'show_progress' variable in cmd_repack() is initialized with
> isatty(2) and is not modified at all by the '--quiet' flag. The
> '--quiet' flag modifies the po_args.quiet option which is translated
> into a '--quiet' flag for the 'git pack-objects' child process. However,
> 'show_progress' is used to directly send progress information to the
> multi-pack-index writing logic which does not use a child process.
>
> The fix here is to modify 'show_progress' to be false if po_opts.quiet
> is true, and isatty(2) otherwise. This new expectation simplifies a
> later condition that checks both.
>
> This is difficult to test because the isatty(2) already prevents the
> progess indicators from appearing when we redirect stderr to a file.
>
> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
> ---
>  builtin/repack.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/repack.c b/builtin/repack.c
> index 1f128b7c90b..c393a5db774 100644
> --- a/builtin/repack.c
> +++ b/builtin/repack.c
> @@ -612,7 +612,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
>  	struct tempfile *refs_snapshot = NULL;
>  	int i, ext, ret;
>  	FILE *out;
> -	int show_progress = isatty(2);
> +	int show_progress;
>  
>  	/* variables to be filled by option parsing */
>  	int pack_everything = 0;
> @@ -725,6 +725,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
>  
>  	prepare_pack_objects(&cmd, &po_args);
>  
> +	show_progress = !po_args.quiet && isatty(2);
> +
>  	strvec_push(&cmd.args, "--keep-true-parents");
>  	if (!pack_kept_objects)
>  		strvec_push(&cmd.args, "--honor-pack-keep");
> @@ -926,7 +928,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
>  			}
>  			strbuf_release(&buf);
>  		}
> -		if (!po_args.quiet && show_progress)
> +		if (show_progress)
>  			opts |= PRUNE_PACKED_VERBOSE;
>  		prune_packed_objects(opts);

This seems like a good change, but the documentation could really use an
update (also before this change). It just says about -q:

    Pass the -q option to git pack-objects. See git-pack-objects(1).

But do various things in "git-repack" itself differently if it's
supplied.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Derrick Stolee wrote (reply to this):

On 12/18/2021 4:55 AM, Ævar Arnfjörð Bjarmason wrote:
> This seems like a good change, but the documentation could really use an
> update (also before this change). It just says about -q:
> 
>     Pass the -q option to git pack-objects. See git-pack-objects(1).
> 
> But do various things in "git-repack" itself differently if it's
> supplied.

Good point. I will update the docs.

Thanks,
-Stolee

int i, ext, ret;
FILE *out;
int show_progress = isatty(2);
int show_progress;

/* variables to be filled by option parsing */
int pack_everything = 0;
Expand Down Expand Up @@ -693,7 +693,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
write_bitmaps = 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff King wrote (reply to this):

On Fri, Dec 17, 2021 at 04:28:45PM +0000, Derrick Stolee via GitGitGadget wrote:

> From: Derrick Stolee <dstolee@microsoft.com>
> 
> Historically, we needed a single packfile in order to have reachability
> bitmaps. This introduced logic that when 'git repack' had a '-b' option
> that we should stop sending the '--honor-pack-keep' option to the 'git
> pack-objects' child process, ensuring that we create a packfile
> containing all reachable objects.
> 
> In the world of multi-pack-index bitmaps, we no longer need to repack
> all objects into a single pack to have valid bitmaps. Thus, we should
> continue sending the '--honor-pack-keep' flag to 'git pack-objects'.
> 
> The fix is very simple: only disable the flag when writing bitmaps but
> also _not_ writing the multi-pack-index.
> 
> This opens the door to new repacking strategies that might want to keep
> some historical set of objects in a stable pack-file while only
> repacking more recent objects.

That all makes sense. Another way of thinking about it: we disable
--honor-pack-keep so we that keep packs do not interfere with writing a
pack bitmap. But with --write-midx, we skip the pack bitmap entirely.

In the end it's the same, but I wanted to emphasize that the important
hing is not so much that we are writing a midx bitmap as that we are
_not_ writing a pack bitmap. And that is what makes this OK to do (that
the repack code already disables the pack bitmap when writing a midx
one).

> diff --git a/builtin/repack.c b/builtin/repack.c
> index 9b0be6a6ab3..1f128b7c90b 100644
> --- a/builtin/repack.c
> +++ b/builtin/repack.c
> @@ -693,7 +693,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
>  		write_bitmaps = 0;
>  	}
>  	if (pack_kept_objects < 0)
> -		pack_kept_objects = write_bitmaps > 0;
> +		pack_kept_objects = write_bitmaps > 0 && !write_midx;

So the code change here looks good.

> diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
> index 0260ad6f0e0..8c4ba6500be 100755
> --- a/t/t7700-repack.sh
> +++ b/t/t7700-repack.sh
> @@ -372,4 +372,19 @@ test_expect_success '--write-midx with preferred bitmap tips' '
>  	)
>  '
>  
> +test_expect_success '--write-midx -b packs non-kept objects' '
> +	git init midx-kept &&
> +	test_when_finished "rm -fr midx-kept" &&
> +	(
> +		cd midx-kept &&
> +		test_commit_bulk 100 &&
> +		GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
> +			git repack --write-midx -a -b &&
> +		cat trace.txt | \
> +			grep \"event\":\"start\" | \
> +			grep pack-objects | \
> +			grep \"--honor-pack-keep\"
> +	)
> +'

This looks correct, though:

  - do we really need this separate repo directory? The test before it
    uses one, but only because it needs a very specific set of commits.
    There is a long-running "midx" directory we could use, though I
    think just the regular test repo would be fine, too.

  - likewise, do we need 100 commits? They are not too expensive these
    days with test_commit_bulk, but I think we don't even care about the
    repo contents at all.

  - there is no actual .keep file in your test. That's OK, as we are
    just checking the args passed to pack-objects.

  - useless use of cat. :) Also, you could probably do it all with one
    grep. This is bikeshedding, of course, but it's nice to keep process
    counts low in the test suite. Also, your middle grep is looser than
    the others (it doesn't check for surrounding quotes).

So something like this would work:

  test_expect_success '--write-midx -b packs non-kept objects' '
          GIT_TRACE2_EVENT="$(pwd)/midx-keep-bitmap.trace" \
                  git -C midx repack --write-midx -a -b &&
          grep "\"event\":\"start\".*\"pack-objects\".*\"--honor-pack-keep\"" \
                  midx-keep-bitmap.trace
  '

One could perhaps argue that the combined grep is less readable. If
that's a concern, I'd suggest wrapping it in a function like:

  # usage: check_trace2_arg <trace_file> <cmd> <arg>
  check_trace2_arg () {
	grep "\"event\":\"start\".*\"$2\".*\"$3\"" "$1"
  }

All just suggestions, of course. I'd be happy enough to see the patch go
in as-is.

-Peff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Derrick Stolee wrote (reply to this):

On 12/17/2021 12:24 PM, Jeff King wrote:
> On Fri, Dec 17, 2021 at 04:28:45PM +0000, Derrick Stolee via GitGitGadget wrote:

...

>> +test_expect_success '--write-midx -b packs non-kept objects' '
>> +	git init midx-kept &&
>> +	test_when_finished "rm -fr midx-kept" &&
>> +	(
>> +		cd midx-kept &&
>> +		test_commit_bulk 100 &&
>> +		GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
>> +			git repack --write-midx -a -b &&
>> +		cat trace.txt | \
>> +			grep \"event\":\"start\" | \
>> +			grep pack-objects | \
>> +			grep \"--honor-pack-keep\"
>> +	)
>> +'
> 
> This looks correct, though:
> 
>   - do we really need this separate repo directory? The test before it
>     uses one, but only because it needs a very specific set of commits.
>     There is a long-running "midx" directory we could use, though I
>     think just the regular test repo would be fine, too.
> 
>   - likewise, do we need 100 commits? They are not too expensive these
>     days with test_commit_bulk, but I think we don't even care about the
>     repo contents at all.
> 
>   - there is no actual .keep file in your test. That's OK, as we are
>     just checking the args passed to pack-objects.
> 
>   - useless use of cat. :) Also, you could probably do it all with one
>     grep. This is bikeshedding, of course, but it's nice to keep process
>     counts low in the test suite. Also, your middle grep is looser than
>     the others (it doesn't check for surrounding quotes).
> 
> So something like this would work:
> 
>   test_expect_success '--write-midx -b packs non-kept objects' '
>           GIT_TRACE2_EVENT="$(pwd)/midx-keep-bitmap.trace" \
>                   git -C midx repack --write-midx -a -b &&
>           grep "\"event\":\"start\".*\"pack-objects\".*\"--honor-pack-keep\"" \
>                   midx-keep-bitmap.trace
>   '
> 
> One could perhaps argue that the combined grep is less readable. If
> that's a concern, I'd suggest wrapping it in a function like:
> 
>   # usage: check_trace2_arg <trace_file> <cmd> <arg>
>   check_trace2_arg () {
> 	grep "\"event\":\"start\".*\"$2\".*\"$3\"" "$1"
>   }
> 
> All just suggestions, of course. I'd be happy enough to see the patch go
> in as-is.

Thanks for the suggestions. I decided to adapt the 'test_subcommand'
helper into a 'test_subcommand_inexact' helper. The existing helper
requires the full argument list in exact order, but the new one only
cares about the given arguments (in that relative order).

Thanks,
-Stolee

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff King wrote (reply to this):

On Mon, Dec 20, 2021 at 08:40:09AM -0500, Derrick Stolee wrote:

> > One could perhaps argue that the combined grep is less readable. If
> > that's a concern, I'd suggest wrapping it in a function like:
> > 
> >   # usage: check_trace2_arg <trace_file> <cmd> <arg>
> >   check_trace2_arg () {
> > 	grep "\"event\":\"start\".*\"$2\".*\"$3\"" "$1"
> >   }
> > 
> > All just suggestions, of course. I'd be happy enough to see the patch go
> > in as-is.
> 
> Thanks for the suggestions. I decided to adapt the 'test_subcommand'
> helper into a 'test_subcommand_inexact' helper. The existing helper
> requires the full argument list in exact order, but the new one only
> cares about the given arguments (in that relative order).

Heh, I should have looked to see if we had faced this problem before.
Extending that family of helpers is better still than my suggestion.

-Peff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Ævar Arnfjörð Bjarmason wrote (reply to this):


On Fri, Dec 17 2021, Derrick Stolee via GitGitGadget wrote:

> +test_expect_success '--write-midx -b packs non-kept objects' '
> +	git init midx-kept &&
> +	test_when_finished "rm -fr midx-kept" &&

Nit: Better the other way around, so we'll clean up things if "git init"
dies midway, but unlikely to ever happen here.

> +	(
> +		cd midx-kept &&
> +		test_commit_bulk 100 &&
> +		GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
> +			git repack --write-midx -a -b &&
> +		cat trace.txt | \
> +			grep \"event\":\"start\" | \

The "cat" here should go in favor of "grep <pat> trace.txt", and can't
this all be on one grep line with "." between the relevant parts you're
extracting out?

}
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps > 0;
pack_kept_objects = write_bitmaps > 0 && !write_midx;

if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
Expand Down Expand Up @@ -725,6 +725,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)

prepare_pack_objects(&cmd, &po_args);

show_progress = !po_args.quiet && isatty(2);

strvec_push(&cmd.args, "--keep-true-parents");
if (!pack_kept_objects)
strvec_push(&cmd.args, "--honor-pack-keep");
Expand Down Expand Up @@ -926,7 +928,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
}
if (!po_args.quiet && show_progress)
if (show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);

Expand Down
13 changes: 13 additions & 0 deletions t/t7700-repack.sh
Expand Up @@ -5,6 +5,7 @@ test_description='git repack works correctly'
. ./test-lib.sh
. "${TEST_DIRECTORY}/lib-bitmap.sh"
. "${TEST_DIRECTORY}/lib-midx.sh"
. "${TEST_DIRECTORY}/lib-terminal.sh"

commit_and_pack () {
test_commit "$@" 1>&2 &&
Expand Down Expand Up @@ -372,4 +373,16 @@ test_expect_success '--write-midx with preferred bitmap tips' '
)
'

test_expect_success '--write-midx -b packs non-kept objects' '
GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
git repack --write-midx -a -b &&
test_subcommand_inexact git pack-objects --honor-pack-keep <trace.txt
'

test_expect_success TTY '--quiet disables progress' '
test_terminal env GIT_PROGRESS_DELAY=0 \
git -C midx repack -ad --quiet --write-midx 2>stderr &&
test_must_be_empty stderr
'

test_done
34 changes: 34 additions & 0 deletions t/test-lib-functions.sh
Expand Up @@ -1759,6 +1759,40 @@ test_subcommand () {
fi
}

# Check that the given command was invoked as part of the
# trace2-format trace on stdin, but without an exact set of
# arguments.
#
# test_subcommand [!] <command> <args>... < <trace>
#
# For example, to look for an invocation of "git pack-objects"
# with the "--honor-pack-keep" argument, use
#
# GIT_TRACE2_EVENT=event.log git repack ... &&
# test_subcommand git pack-objects --honor-pack-keep <event.log
#
# If the first parameter passed is !, this instead checks that
# the given command was not called.
#
test_subcommand_inexact () {
local negate=
if test "$1" = "!"
then
negate=t
shift
fi

local expr=$(printf '"%s".*' "$@")
expr="${expr%,}"

if test -n "$negate"
then
! grep "\"event\":\"child_start\".*\[$expr\]"
else
grep "\"event\":\"child_start\".*\[$expr\]"
fi
}

# Check that the given command was invoked as part of the
# trace2-format trace on stdin.
#
Expand Down