#!/bin/sh

test_description='pack-objects multi-pack reuse'

. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bitmap.sh

GIT_TEST_MULTI_PACK_INDEX=0
GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0

# The --path-walk option does not consider the preferred pack
# at all for reusing deltas, so this variable changes the
# behavior of this test, if enabled.
GIT_TEST_PACK_PATH_WALK=0
export GIT_TEST_PACK_PATH_WALK

objdir=.git/objects
packdir=$objdir/pack

test_pack_reused () {
	test_trace2_data pack-objects pack-reused "$1"
}

test_packs_reused () {
	test_trace2_data pack-objects packs-reused "$1"
}


# pack_position <object> </path/to/pack.idx
pack_position () {
	git show-index >objects &&
	grep "$1" objects | cut -d" " -f1
}

# test_pack_objects_reused_all <pack-reused> <packs-reused>
test_pack_objects_reused_all () {
	: >trace2.txt &&
	GIT_TRACE2_EVENT="$PWD/trace2.txt" \
		git pack-objects --stdout --revs --all --delta-base-offset \
		>got.pack &&

	test_pack_reused "$1" <trace2.txt &&
	test_packs_reused "$2" <trace2.txt &&

	git index-pack --strict -o got.idx got.pack
}

# test_pack_objects_reused <pack-reused> <packs-reused>
test_pack_objects_reused () {
	: >trace2.txt &&
	GIT_TRACE2_EVENT="$PWD/trace2.txt" \
		git pack-objects --stdout --revs >got.pack &&

	test_pack_reused "$1" <trace2.txt &&
	test_packs_reused "$2" <trace2.txt &&

	git index-pack --strict -o got.idx got.pack
}

test_expect_success 'preferred pack is reused for single-pack reuse' '
	test_config pack.allowPackReuse single &&
	git config set maintenance.auto false &&

	for i in A B
	do
		test_commit "$i" &&
		git repack -d || return 1
	done &&

	git multi-pack-index write --bitmap &&

	test_pack_objects_reused_all 3 1
'

test_expect_success 'multi-pack reuse is disabled by default' '
	test_pack_objects_reused_all 3 1
'

test_expect_success 'feature.experimental implies multi-pack reuse' '
	test_config feature.experimental true &&

	test_pack_objects_reused_all 6 2
'

test_expect_success 'multi-pack reuse can be disabled with feature.experimental' '
	test_config feature.experimental true &&
	test_config pack.allowPackReuse single &&

	test_pack_objects_reused_all 3 1
'

test_expect_success 'enable multi-pack reuse' '
	git config pack.allowPackReuse multi
'

test_expect_success 'reuse all objects from subset of bitmapped packs' '
	test_commit C &&
	git repack -d &&

	git multi-pack-index write --bitmap &&

	cat >in <<-EOF &&
	$(git rev-parse C)
	^$(git rev-parse A)
	EOF

	test_pack_objects_reused 6 2 <in
'

test_expect_success 'reuse all objects from all packs' '
	test_pack_objects_reused_all 9 3
'

test_expect_success 'reuse objects from first pack with middle gap' '
	for i in D E F
	do
		test_commit "$i" || return 1
	done &&

	# Set "pack.window" to zero to ensure that we do not create any
	# deltas, which could alter the amount of pack reuse we perform
	# (if, for e.g., we are not sending one or more bases).
	D="$(git -c pack.window=0 pack-objects --all --unpacked $packdir/pack)" &&

	d_pos="$(pack_position $(git rev-parse D) <$packdir/pack-$D.idx)" &&
	e_pos="$(pack_position $(git rev-parse E) <$packdir/pack-$D.idx)" &&
	f_pos="$(pack_position $(git rev-parse F) <$packdir/pack-$D.idx)" &&

	# commits F, E, and D, should appear in that order at the
	# beginning of the pack
	test $f_pos -lt $e_pos &&
	test $e_pos -lt $d_pos &&

	# Ensure that the pack we are constructing sorts ahead of any
	# other packs in lexical/bitmap order by choosing it as the
	# preferred pack.
	git multi-pack-index write --bitmap --preferred-pack="pack-$D.idx" &&

	cat >in <<-EOF &&
	$(git rev-parse E)
	^$(git rev-parse D)
	EOF

	test_pack_objects_reused 3 1 <in
'

test_expect_success 'reuse objects from middle pack with middle gap' '
	rm -fr $packdir/multi-pack-index* &&

	# Ensure that the pack we are constructing sort into any
	# position *but* the first one, by choosing a different pack as
	# the preferred one.
	git multi-pack-index write --bitmap --preferred-pack="pack-$A.idx" &&

	cat >in <<-EOF &&
	$(git rev-parse E)
	^$(git rev-parse D)
	EOF

	test_pack_objects_reused 3 1 <in
'

test_expect_success 'omit delta with uninteresting base (same pack)' '
	git repack -adk &&

	test_seq 32 >f &&
	git add f &&
	test_tick &&
	git commit -m "delta" &&
	delta="$(git rev-parse HEAD)" &&

	test_seq 64 >f &&
	test_tick &&
	git commit -a -m "base" &&
	base="$(git rev-parse HEAD)" &&

	test_commit other &&

	git repack -d &&

	have_delta "$(git rev-parse $delta:f)" "$(git rev-parse $base:f)" &&

	git multi-pack-index write --bitmap &&

	cat >in <<-EOF &&
	$(git rev-parse other)
	^$base
	EOF

	# We can only reuse the 3 objects corresponding to "other" from
	# the latest pack.
	#
	# This is because even though we want "delta", we do not want
	# "base", meaning that we have to inflate the delta/base-pair
	# corresponding to the blob in commit "delta", which bypasses
	# the pack-reuse mechanism.
	#
	# The remaining objects from the other pack are similarly not
	# reused because their objects are on the uninteresting side of
	# the query.
	test_pack_objects_reused 3 1 <in
'

test_expect_success 'omit delta from uninteresting base (cross pack)' '
	cat >in <<-EOF &&
	$(git rev-parse $base)
	^$(git rev-parse $delta)
	EOF

	P="$(git pack-objects --revs $packdir/pack <in)" &&

	git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&

	packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
	objects_nr="$(git rev-list --count --all --objects)" &&

	test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
'

test_expect_success 'non-omitted delta in MIDX preferred pack' '
	test_config pack.allowPackReuse single &&

	cat >p1.objects <<-EOF &&
	$(git rev-parse $base)
	^$(git rev-parse $delta^)
	EOF
	cat >p2.objects <<-EOF &&
	$(git rev-parse F)
	EOF

	p1="$(git pack-objects --revs $packdir/pack <p1.objects)" &&
	p2="$(git pack-objects --revs $packdir/pack <p2.objects)" &&

	cat >in <<-EOF &&
	pack-$p1.idx
	pack-$p2.idx
	EOF
	git multi-pack-index write --bitmap --stdin-packs \
		--preferred-pack=pack-$p1.pack <in &&

	git show-index <$packdir/pack-$p1.idx >expect &&

	test_pack_objects_reused_all $(wc -l <expect) 1
'

test_expect_success 'duplicate objects' '
	git init duplicate-objects &&
	(
		cd duplicate-objects &&

		git config pack.allowPackReuse multi &&

		test_commit base &&

		git repack -a &&

		git rev-parse HEAD^{tree} >in &&
		p="$(git pack-objects $packdir/pack <in)" &&

		git multi-pack-index write --bitmap --preferred-pack=pack-$p.idx &&

		objects_nr="$(git rev-list --count --all --objects)" &&
		packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&

		test_pack_objects_reused_all $objects_nr $packs_nr
	)
'

test_expect_success 'duplicate objects with verbatim reuse' '
	git init duplicate-objects-verbatim &&
	(
		cd duplicate-objects-verbatim &&

		git config pack.allowPackReuse multi &&

		test_commit_bulk 64 &&

		# take the first object from the main pack...
		git show-index <$(ls $packdir/pack-*.idx) >obj.raw &&
		sort -nk1 <obj.raw | head -n1 | cut -d" " -f2 >in &&

		# ...and create a separate pack containing just that object
		p="$(git pack-objects $packdir/pack <in)" &&

		git multi-pack-index write --bitmap --preferred-pack=pack-$p.idx &&

		test_pack_objects_reused_all 192 2
	)
'

test_done
