diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 8baff8cba..ec536bf4e 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1167,8 +1167,10 @@ meta_fetch_on_complete (GObject *object, } /* When traversing parents, do not fail on a missing commit. - * We may be pulling from a partial repository that ends in - * a dangling parent reference. */ + * We may be pulling from a partial repository that ends in a + * dangling parent reference. This logic should match the + * local case in scan_one_metadata_object. + */ else if (objtype == OSTREE_OBJECT_TYPE_COMMIT && pull_data->maxdepth != 0 && is_parent_commit (pull_data, checksum)) @@ -1820,10 +1822,46 @@ scan_one_metadata_object (OtPullData *pull_data, return FALSE; } + g_autoptr(GError) local_error = NULL; if (!_ostree_repo_import_object (pull_data->repo, pull_data->remote_repo_local, objtype, checksum, pull_data->importflags, - cancellable, error)) - return FALSE; + cancellable, &local_error)) + { + /* When traversing parents, do not fail on a missing commit. + * We may be pulling from a partial repository that ends in a + * dangling parent reference. This logic should match the + * remote case in meta_fetch_on_complete. + * + * Note early return. + */ + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + objtype == OSTREE_OBJECT_TYPE_COMMIT && + pull_data->maxdepth != 0 && + is_parent_commit (pull_data, checksum)) + { + g_clear_error (&local_error); + + /* If the remote repo supports tombstone commits, check if + * the commit was intentionally deleted. + */ + if (pull_data->has_tombstone_commits) + { + if (!_ostree_repo_import_object (pull_data->repo, pull_data->remote_repo_local, + OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT, + checksum, pull_data->importflags, + cancellable, error)) + return FALSE; + } + + return TRUE; + } + else + { + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + } + /* The import API will fetch both the commit and detached metadata, so * add it to the hash to avoid re-fetching it below. */ diff --git a/tests/test-local-pull-depth.sh b/tests/test-local-pull-depth.sh index 96b20b9cd..80413bc92 100755 --- a/tests/test-local-pull-depth.sh +++ b/tests/test-local-pull-depth.sh @@ -25,7 +25,7 @@ set -euo pipefail setup_test_repository "archive" -echo "1..1" +echo "1..3" cd ${test_tmpdir} mkdir repo2 @@ -62,3 +62,35 @@ find repo2/state -name '*.commitpartial' | wc -l > commitpartialcount assert_file_has_content commitpartialcount "^0$" echo "ok local pull depth" + +# Check that pulling with depth != 0 succeeds with a missing parent +# commit. Prune the remote to truncate the history. +cd ${test_tmpdir} +${CMD_PREFIX} ostree --repo=repo prune --refs-only --depth=0 + +rm -rf repo2/refs/heads/* repo2/refs/remotes/* repo2/objects/*/*.commit +${CMD_PREFIX} ostree --repo=repo2 pull-local --depth=1 repo +find repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^1$" +find repo/state -name '*.commitpartial' | wc -l > commitpartialcount +assert_file_has_content commitpartialcount "^0$" + +rm -rf repo2/refs/heads/* repo2/refs/remotes/* repo2/objects/*/*.commit +${CMD_PREFIX} ostree --repo=repo2 pull-local --depth=-1 repo +find repo/objects -name '*.commit' | wc -l > commitcount +assert_file_has_content commitcount "^1$" +find repo/state -name '*.commitpartial' | wc -l > commitpartialcount +assert_file_has_content commitpartialcount "^0$" + +echo "ok local pull depth missing parent" + +# Check that it errors if the ref head commit is missing. +cd ${test_tmpdir} +rm -f repo/objects/*/*.commit + +rm -rf repo2/refs/heads/* repo2/refs/remotes/* repo2/objects/*/*.commit +if ${CMD_PREFIX} ostree --repo=repo2 pull-local --depth=-1 repo; then + fatal "Local pull with depth -1 succeeded with missing HEAD commit" +fi + +echo "ok local pull depth missing HEAD commit"