builtin/checkout: compute checkout metadata for checkouts

Provide commit metadata for checkout code paths that use unpack_trees
and friends.  When we're checking out a commit, use the commit
information, but don't provide commit information if we're checking out
from the index, since there need not be any particular commit associated
with the index, and even if there is one, we can't know what it is.

Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
brian m. carlson 2020-03-16 18:05:04 +00:00 committed by Junio C Hamano
parent c397aac02f
commit 13e7ed6a3a
6 changed files with 68 additions and 33 deletions

View File

@ -604,7 +604,8 @@ static void describe_detached_head(const char *msg, struct commit *commit)
} }


static int reset_tree(struct tree *tree, const struct checkout_opts *o, static int reset_tree(struct tree *tree, const struct checkout_opts *o,
int worktree, int *writeout_error) int worktree, int *writeout_error,
struct branch_info *info)
{ {
struct unpack_trees_options opts; struct unpack_trees_options opts;
struct tree_desc tree_desc; struct tree_desc tree_desc;
@ -619,6 +620,11 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.verbose_update = o->show_progress; opts.verbose_update = o->show_progress;
opts.src_index = &the_index; opts.src_index = &the_index;
opts.dst_index = &the_index; opts.dst_index = &the_index;
init_checkout_metadata(&opts.meta, info->refname,
info->commit ? &info->commit->object.oid :
is_null_oid(&info->oid) ? &tree->object.oid :
&info->oid,
NULL);
parse_tree(tree); parse_tree(tree);
init_tree_desc(&tree_desc, tree->buffer, tree->size); init_tree_desc(&tree_desc, tree->buffer, tree->size);
switch (unpack_trees(1, &tree_desc, &opts)) { switch (unpack_trees(1, &tree_desc, &opts)) {
@ -677,7 +683,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
} else } else
new_tree = get_commit_tree(new_branch_info->commit); new_tree = get_commit_tree(new_branch_info->commit);
if (opts->discard_changes) { if (opts->discard_changes) {
ret = reset_tree(new_tree, opts, 1, writeout_error); ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
if (ret) if (ret)
return ret; return ret;
} else { } else {
@ -706,6 +712,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
topts.quiet = opts->merge && old_branch_info->commit; topts.quiet = opts->merge && old_branch_info->commit;
topts.verbose_update = opts->show_progress; topts.verbose_update = opts->show_progress;
topts.fn = twoway_merge; topts.fn = twoway_merge;
init_checkout_metadata(&topts.meta, new_branch_info->refname,
new_branch_info->commit ?
&new_branch_info->commit->object.oid :
&new_branch_info->oid, NULL);
if (opts->overwrite_ignore) { if (opts->overwrite_ignore) {
topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir = xcalloc(1, sizeof(*topts.dir));
topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->flags |= DIR_SHOW_IGNORED;
@ -776,7 +786,7 @@ static int merge_working_tree(const struct checkout_opts *opts,


ret = reset_tree(new_tree, ret = reset_tree(new_tree,
opts, 1, opts, 1,
writeout_error); writeout_error, new_branch_info);
if (ret) if (ret)
return ret; return ret;
o.ancestor = old_branch_info->name; o.ancestor = old_branch_info->name;
@ -796,7 +806,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
exit(128); exit(128);
ret = reset_tree(new_tree, ret = reset_tree(new_tree,
opts, 0, opts, 0,
writeout_error); writeout_error, new_branch_info);
strbuf_release(&o.obuf); strbuf_release(&o.obuf);
strbuf_release(&old_commit_shortname); strbuf_release(&old_commit_shortname);
if (ret) if (ret)

View File

@ -94,6 +94,7 @@ int checkout_fast_forward(struct repository *r,
opts.verbose_update = 1; opts.verbose_update = 1;
opts.merge = 1; opts.merge = 1;
opts.fn = twoway_merge; opts.fn = twoway_merge;
init_checkout_metadata(&opts.meta, NULL, remote, NULL);
setup_unpack_trees_porcelain(&opts, "merge"); setup_unpack_trees_porcelain(&opts, "merge");


if (unpack_trees(nr_trees, t, &opts)) { if (unpack_trees(nr_trees, t, &opts)) {

View File

@ -364,6 +364,10 @@ test_expect_success PERL 'required process filter should filter data' '
S=$(file_size test.r) && S=$(file_size test.r) &&
S2=$(file_size test2.r) && S2=$(file_size test2.r) &&
S3=$(file_size "testsubdir/test3 '\''sq'\'',\$x=.r") && S3=$(file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
M=$(git hash-object test.r) &&
M2=$(git hash-object test2.r) &&
M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
EMPTY=$(git hash-object /dev/null) &&


filter_git add . && filter_git add . &&
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
@ -378,14 +382,15 @@ test_expect_success PERL 'required process filter should filter data' '
test_cmp_count expected.log debug.log && test_cmp_count expected.log debug.log &&


git commit -m "test commit 2" && git commit -m "test commit 2" &&
META="ref=refs/heads/master treeish=$(git rev-parse --verify master)" &&
rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" && rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&


filter_git checkout --quiet --no-progress . && filter_git checkout --quiet --no-progress . &&
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK] IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
STOP STOP
EOF EOF
test_cmp_exclude_clean expected.log debug.log && test_cmp_exclude_clean expected.log debug.log &&
@ -406,10 +411,10 @@ test_expect_success PERL 'required process filter should filter data' '
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge test.r $S [OK] -- OUT: $S . [OK] IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
IN: smudge test4-empty.r 0 [OK] -- OUT: 0 [OK] IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK] IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
STOP STOP
EOF EOF
test_cmp_exclude_clean expected.log debug.log && test_cmp_exclude_clean expected.log debug.log &&
@ -519,17 +524,22 @@ test_expect_success PERL 'required process filter should process multiple packet
EOF EOF
test_cmp_count expected.log debug.log && test_cmp_count expected.log debug.log &&


rm -f *.file && M1="blob=$(git hash-object 1pkt_1__.file)" &&
M2="blob=$(git hash-object 2pkt_1+1.file)" &&
M3="blob=$(git hash-object 2pkt_2-1.file)" &&
M4="blob=$(git hash-object 2pkt_2__.file)" &&
M5="blob=$(git hash-object 3pkt_2+1.file)" &&
rm -f *.file debug.log &&


filter_git checkout --quiet --no-progress -- *.file && filter_git checkout --quiet --no-progress -- *.file &&
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK] IN: smudge 1pkt_1__.file $M1 $(($S )) [OK] -- OUT: $(($S )) . [OK]
IN: smudge 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK] IN: smudge 2pkt_1+1.file $M2 $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
IN: smudge 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK] IN: smudge 2pkt_2-1.file $M3 $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
IN: smudge 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK] IN: smudge 2pkt_2__.file $M4 $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
IN: smudge 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK] IN: smudge 3pkt_2+1.file $M5 $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
STOP STOP
EOF EOF
test_cmp_exclude_clean expected.log debug.log && test_cmp_exclude_clean expected.log debug.log &&
@ -578,6 +588,10 @@ test_expect_success PERL 'process filter should restart after unexpected write f
S=$(file_size test.r) && S=$(file_size test.r) &&
S2=$(file_size test2.r) && S2=$(file_size test2.r) &&
SF=$(file_size smudge-write-fail.r) && SF=$(file_size smudge-write-fail.r) &&
M=$(git hash-object test.r) &&
M2=$(git hash-object test2.r) &&
MF=$(git hash-object smudge-write-fail.r) &&
rm -f debug.log &&


git add . && git add . &&
rm -f *.r && rm -f *.r &&
@ -591,11 +605,11 @@ test_expect_success PERL 'process filter should restart after unexpected write f
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge smudge-write-fail.r $SF [OK] -- [WRITE FAIL] IN: smudge smudge-write-fail.r blob=$MF $SF [OK] -- [WRITE FAIL]
START START
init handshake complete init handshake complete
IN: smudge test.r $S [OK] -- OUT: $S . [OK] IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
STOP STOP
EOF EOF
test_cmp_exclude_clean expected.log debug.log && test_cmp_exclude_clean expected.log debug.log &&
@ -629,6 +643,10 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
S=$(file_size test.r) && S=$(file_size test.r) &&
S2=$(file_size test2.r) && S2=$(file_size test2.r) &&
SE=$(file_size error.r) && SE=$(file_size error.r) &&
M=$(git hash-object test.r) &&
M2=$(git hash-object test2.r) &&
ME=$(git hash-object error.r) &&
rm -f debug.log &&


git add . && git add . &&
rm -f *.r && rm -f *.r &&
@ -637,9 +655,9 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge error.r $SE [OK] -- [ERROR] IN: smudge error.r blob=$ME $SE [OK] -- [ERROR]
IN: smudge test.r $S [OK] -- OUT: $S . [OK] IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
STOP STOP
EOF EOF
test_cmp_exclude_clean expected.log debug.log && test_cmp_exclude_clean expected.log debug.log &&
@ -665,18 +683,21 @@ test_expect_success PERL 'process filter abort stops processing of all further f
echo "error this blob and all future blobs" >abort.o && echo "error this blob and all future blobs" >abort.o &&
cp abort.o abort.r && cp abort.o abort.r &&


M="blob=$(git hash-object abort.r)" &&
rm -f debug.log &&
SA=$(file_size abort.r) && SA=$(file_size abort.r) &&


git add . && git add . &&
rm -f *.r && rm -f *.r &&



# Note: This test assumes that Git filters files in alphabetical # Note: This test assumes that Git filters files in alphabetical
# order ("abort.r" before "test.r"). # order ("abort.r" before "test.r").
filter_git checkout --quiet --no-progress . && filter_git checkout --quiet --no-progress . &&
cat >expected.log <<-EOF && cat >expected.log <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge abort.r $SA [OK] -- [ABORT] IN: smudge abort.r $M $SA [OK] -- [ABORT]
STOP STOP
EOF EOF
test_cmp_exclude_clean expected.log debug.log && test_cmp_exclude_clean expected.log debug.log &&
@ -727,27 +748,28 @@ test_expect_success PERL 'delayed checkout in process filter' '
) && ) &&


S=$(file_size "$TEST_ROOT/test.o") && S=$(file_size "$TEST_ROOT/test.o") &&
M="blob=$(git -C repo rev-parse --verify master:test.a)" &&
cat >a.exp <<-EOF && cat >a.exp <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge test.a $S [OK] -- OUT: $S . [OK] IN: smudge test.a $M $S [OK] -- OUT: $S . [OK]
IN: smudge test-delay10.a $S [OK] -- [DELAYED] IN: smudge test-delay10.a $M $S [OK] -- [DELAYED]
IN: smudge test-delay11.a $S [OK] -- [DELAYED] IN: smudge test-delay11.a $M $S [OK] -- [DELAYED]
IN: smudge test-delay20.a $S [OK] -- [DELAYED] IN: smudge test-delay20.a $M $S [OK] -- [DELAYED]
IN: list_available_blobs test-delay10.a test-delay11.a [OK] IN: list_available_blobs test-delay10.a test-delay11.a [OK]
IN: smudge test-delay10.a 0 [OK] -- OUT: $S . [OK] IN: smudge test-delay10.a $M 0 [OK] -- OUT: $S . [OK]
IN: smudge test-delay11.a 0 [OK] -- OUT: $S . [OK] IN: smudge test-delay11.a $M 0 [OK] -- OUT: $S . [OK]
IN: list_available_blobs test-delay20.a [OK] IN: list_available_blobs test-delay20.a [OK]
IN: smudge test-delay20.a 0 [OK] -- OUT: $S . [OK] IN: smudge test-delay20.a $M 0 [OK] -- OUT: $S . [OK]
IN: list_available_blobs [OK] IN: list_available_blobs [OK]
STOP STOP
EOF EOF
cat >b.exp <<-EOF && cat >b.exp <<-EOF &&
START START
init handshake complete init handshake complete
IN: smudge test-delay10.b $S [OK] -- [DELAYED] IN: smudge test-delay10.b $M $S [OK] -- [DELAYED]
IN: list_available_blobs test-delay10.b [OK] IN: list_available_blobs test-delay10.b [OK]
IN: smudge test-delay10.b 0 [OK] -- OUT: $S . [OK] IN: smudge test-delay10.b $M 0 [OK] -- OUT: $S . [OK]
IN: list_available_blobs [OK] IN: list_available_blobs [OK]
STOP STOP
EOF EOF

View File

@ -136,7 +136,7 @@ while (1) {
$DELAY{$pathname}{"requested"} = 1; $DELAY{$pathname}{"requested"} = 1;
} }
} elsif ($buffer =~ /^(ref|treeish|blob)=/) { } elsif ($buffer =~ /^(ref|treeish|blob)=/) {
# Do nothing. print $debug " $buffer";
} else { } else {
# In general, filters need to be graceful about # In general, filters need to be graceful about
# new metadata, since it's documented that we # new metadata, since it's documented that we

View File

@ -371,6 +371,7 @@ static int check_updates(struct unpack_trees_options *o)
state.quiet = 1; state.quiet = 1;
state.refresh_cache = 1; state.refresh_cache = 1;
state.istate = index; state.istate = index;
clone_checkout_metadata(&state.meta, &o->meta, NULL);


if (!o->update || o->dry_run) { if (!o->update || o->dry_run) {
remove_marked_cache_entries(index, 0); remove_marked_cache_entries(index, 0);

View File

@ -85,6 +85,7 @@ struct unpack_trees_options {
struct index_state result; struct index_state result;


struct pattern_list *pl; /* for internal use */ struct pattern_list *pl; /* for internal use */
struct checkout_metadata meta;
}; };


int unpack_trees(unsigned n, struct tree_desc *t, int unpack_trees(unsigned n, struct tree_desc *t,