Merge branch 'jc/resolve-undo' into maint

The resolve-undo information in the index was not protected against
GC, which has been corrected.
source: <xmqq35f7kzad.fsf@gitster.g>

* jc/resolve-undo:
  fsck: do not dereference NULL while checking resolve-undo data
  revision: mark blobs needed for resolve-undo as reachable
This commit is contained in:
Junio C Hamano 2022-08-10 21:52:32 -07:00
commit a6aeb2fef9
3 changed files with 146 additions and 0 deletions

View File

@ -19,6 +19,7 @@
#include "decorate.h"
#include "packfile.h"
#include "object-store.h"
#include "resolve-undo.h"
#include "run-command.h"
#include "worktree.h"
@ -757,6 +758,43 @@ static int fsck_cache_tree(struct cache_tree *it)
return err;
}
static int fsck_resolve_undo(struct index_state *istate)
{
struct string_list_item *item;
struct string_list *resolve_undo = istate->resolve_undo;
if (!resolve_undo)
return 0;
for_each_string_list_item(item, resolve_undo) {
const char *path = item->string;
struct resolve_undo_info *ru = item->util;
int i;
if (!ru)
continue;
for (i = 0; i < 3; i++) {
struct object *obj;
if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
continue;
obj = parse_object(the_repository, &ru->oid[i]);
if (!obj) {
error(_("%s: invalid sha1 pointer in resolve-undo"),
oid_to_hex(&ru->oid[i]));
errors_found |= ERROR_REFS;
continue;
}
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options, &ru->oid[i],
":(%d):%s", i, path);
mark_object_reachable(obj);
}
}
return 0;
}
static void mark_object_for_connectivity(const struct object_id *oid)
{
struct object *obj = lookup_unknown_object(the_repository, oid);
@ -938,6 +976,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
if (active_cache_tree)
fsck_cache_tree(active_cache_tree);
fsck_resolve_undo(&the_index);
}
check_connectivity();

View File

@ -33,6 +33,7 @@
#include "bloom.h"
#include "json-writer.h"
#include "list-objects-filter-options.h"
#include "resolve-undo.h"
volatile show_early_output_fn_t show_early_output;
@ -1696,6 +1697,39 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
}
static void add_resolve_undo_to_pending(struct index_state *istate, struct rev_info *revs)
{
struct string_list_item *item;
struct string_list *resolve_undo = istate->resolve_undo;
if (!resolve_undo)
return;
for_each_string_list_item(item, resolve_undo) {
const char *path = item->string;
struct resolve_undo_info *ru = item->util;
int i;
if (!ru)
continue;
for (i = 0; i < 3; i++) {
struct blob *blob;
if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
continue;
blob = lookup_blob(revs->repo, &ru->oid[i]);
if (!blob) {
warning(_("resolve-undo records `%s` which is missing"),
oid_to_hex(&ru->oid[i]));
continue;
}
add_pending_object_with_path(revs, &blob->object, "",
ru->mode[i], path);
}
}
}
static void do_add_index_objects_to_pending(struct rev_info *revs,
struct index_state *istate,
unsigned int flags)
@ -1724,6 +1758,8 @@ static void do_add_index_objects_to_pending(struct rev_info *revs,
add_cache_tree(istate->cache_tree, revs, &path, flags);
strbuf_release(&path);
}
add_resolve_undo_to_pending(istate, revs);
}
void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)

View File

@ -194,4 +194,75 @@ test_expect_success 'rerere forget (add-add conflict)' '
test_i18ngrep "no remembered" actual
'
test_expect_success 'resolve-undo keeps blobs from gc' '
git checkout -f main &&
# First make sure we do not have any cruft left in the object store
git repack -a -d &&
git prune --expire=now &&
git prune-packed &&
git gc --prune=now &&
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
# Now add three otherwise unreferenced blob objects to the index
git reset --hard &&
B1=$(echo "resolve undo test data 1" | git hash-object -w --stdin) &&
B2=$(echo "resolve undo test data 2" | git hash-object -w --stdin) &&
B3=$(echo "resolve undo test data 3" | git hash-object -w --stdin) &&
git update-index --add --index-info <<-EOF &&
100644 $B1 1 frotz
100644 $B2 2 frotz
100644 $B3 3 frotz
EOF
# These three blob objects are reachable (only) from the index
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
# and they should be protected from GC
git gc --prune=now &&
git cat-file -e $B1 &&
git cat-file -e $B2 &&
git cat-file -e $B3 &&
# Now resolve the conflicted path
B0=$(echo "resolve undo test data 0" | git hash-object -w --stdin) &&
git update-index --add --cacheinfo 100644,$B0,frotz &&
# These three blob objects are now reachable only from the resolve-undo
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
# and they should survive GC
git gc --prune=now &&
git cat-file -e $B0 &&
git cat-file -e $B1 &&
git cat-file -e $B2 &&
git cat-file -e $B3 &&
# Now we switch away, which nukes resolve-undo, and
# blobs B0..B3 would become dangling. fsck should
# notice that they are now unreachable.
git checkout -f side &&
git fsck --unreachable >cruft &&
sort cruft >actual &&
sort <<-EOF >expect &&
unreachable blob $B0
unreachable blob $B1
unreachable blob $B2
unreachable blob $B3
EOF
test_cmp expect actual &&
# And they should go away when gc runs.
git gc --prune=now &&
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
test_must_fail git cat-file -e $B0 &&
test_must_fail git cat-file -e $B1 &&
test_must_fail git cat-file -e $B2 &&
test_must_fail git cat-file -e $B3
'
test_done