Download raw body.
make rewinding of branches work with gotd
On 2023/04/10 18:23:39 +0200, Stefan Sperling <stsp@stsp.name> wrote:
> Fix a spurious empty packfile error from gotd when rewinding a branch.
>
> ok?
ok
> diff 7755e2f80723b22eb4fcc203ffd75608db7055c7 8146f0364982425931d98b5cf31a12252b3dff39
> commit - 7755e2f80723b22eb4fcc203ffd75608db7055c7
> commit + 8146f0364982425931d98b5cf31a12252b3dff39
> blob - 317d11a2dde1181dd8dca488ba5ed7f353895c70
> blob + eefdc7de1087c2a2956a9fdbbd25ad81719a783c
> --- gotd/repo_write.c
> +++ gotd/repo_write.c
> @@ -97,6 +97,7 @@ static struct repo_write_client {
> int nref_updates;
> int nref_del;
> int nref_new;
> + int nref_move;
> } repo_write_client;
>
> static volatile sig_atomic_t sigint_received;
> @@ -265,6 +266,7 @@ list_refs(struct imsg *imsg)
> client->nref_updates = 0;
> client->nref_del = 0;
> client->nref_new = 0;
> + client->nref_move = 0;
>
> imsg_init(&ibuf, client_fd);
>
> @@ -971,6 +973,24 @@ recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t
> }
>
> static const struct got_error *
> +ensure_all_objects_exist_locally(struct gotd_ref_updates *ref_updates)
> +{
> + const struct got_error *err = NULL;
> + struct gotd_ref_update *ref_update;
> + struct got_object *obj;
> +
> + STAILQ_FOREACH(ref_update, ref_updates, entry) {
> + err = got_object_open(&obj, repo_write.repo,
> + &ref_update->new_id);
> + if (err)
> + return err;
> + got_object_close(obj);
> + }
> +
> + return NULL;
> +}
> +
> +static const struct got_error *
> recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t *sha1,
> int infd, int outfd)
> {
> @@ -1019,8 +1039,20 @@ recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t
> client->nref_updates == client->nref_new)
> return NULL;
>
> - return got_error_msg(GOT_ERR_BAD_PACKFILE,
> - "bad packfile with zero objects");
> + /*
> + * Clients which only move existing refs will send us an empty
> + * pack file. All referenced objects must exist locally.
> + */
> + err = ensure_all_objects_exist_locally(&client->ref_updates);
> + if (err) {
> + if (err->code != GOT_ERR_NO_OBJ)
> + return err;
> + return got_error_msg(GOT_ERR_BAD_PACKFILE,
> + "bad packfile with zero objects");
> + }
> +
> + client->nref_move = client->nref_updates;
> + return NULL;
> }
>
> log_debug("expecting %d objects", *nobj);
> @@ -1271,6 +1303,16 @@ recv_packfile(int *have_packfile, struct imsg *imsg)
> client->nref_updates == client->nref_del)
> goto done;
>
> + /*
> + * Clients which only move existing refs will send us an empty
> + * pack file. All referenced objects must exist locally.
> + */
> + if (nobj == 0 &&
> + pack_filesize == sizeof(struct got_packfile_hdr) &&
> + client->nref_move > 0 &&
> + client->nref_updates == client->nref_move)
> + goto done;
> +
> pack->filesize = pack_filesize;
> *have_packfile = 1;
>
> blob - d75e82812ee4b8e5c04144e1f6f981703b541699
> blob + daff924b47ca360160665215790f700bf0f8bfda
> --- regress/gotd/repo_write.sh
> +++ regress/gotd/repo_write.sh
> @@ -458,8 +458,95 @@ test_parseargs "$@"
> test_done "$testroot" 0
> }
>
> +test_rewind_branch() {
> + local testroot=`test_init rewind_branch 1`
> +
> + got clone -a -q ${GOTD_TEST_REPO_URL} $testroot/repo-clone
> + ret=$?
> + if [ $ret -ne 0 ]; then
> + echo "got clone failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + got checkout -q $testroot/repo-clone $testroot/wt >/dev/null
> + ret=$?
> + if [ $ret -ne 0 ]; then
> + echo "got checkout failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + (cd $testroot/wt && got branch foo) >/dev/null
> + ret=$?
> + if [ $ret -ne 0 ]; then
> + echo "got branch failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + echo modified alpha > $testroot/wt/alpha
> + (cd $testroot/wt && got commit -m 'edit alpha') >/dev/null
> + ret=$?
> + if [ $ret -ne 0 ]; then
> + echo "got commit failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + if ! got send -q -r $testroot/repo-clone -b foo; then
> + echo "got send failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + local foo_id=`git_show_branch_head "$testroot/repo-clone" foo`
> + local main_id=`git_show_branch_head "$testroot/repo-clone" main`
> + local tag_id=`got ref -r "$testroot/repo-clone" -l refs/tags/1.0 | \
> + awk '{print $2}'`
> +
> + (cd $testroot/wt && got update -c $main_id) >/dev/null
> + ret=$?
> + if [ $ret -ne 0 ]; then
> + echo "got update failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + (cd $testroot/wt && got histedit -d) >/dev/null
> + ret=$?
> + if [ $ret -ne 0 ]; then
> + echo "got histedit failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + if ! got send -q -r $testroot/repo-clone -f -b foo; then
> + echo "got send failed unexpectedly" >&2
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + got fetch -q -r $testroot/repo-clone -l >$testroot/refs
> + cat <<EOF >$testroot/refs.expected
> +HEAD: refs/heads/main
> +HEAD: $main_id
> +refs/heads/foo: $main_id
> +refs/heads/main: $main_id
> +refs/tags/1.0: $tag_id
> +EOF
> + if ! cmp -s $testroot/refs.expected $testroot/refs; then
> + diff -u $testroot/refs.expected $testroot/refs
> + test_done "$testroot" 1
> + return 1
> + fi
> +
> + test_done "$testroot" 0
> +}
> +
> test_parseargs "$@"
> run_test test_send_basic
> run_test test_fetch_more_history
> run_test test_send_new_empty_branch
> run_test test_delete_branch
> +run_test test_rewind_branch
make rewinding of branches work with gotd