Download raw body.
gotd: initial implementation of the delete-refs capability
This makes "got send -d" works against gotd.
The idea is that, if a server supports this capability, clients can
delete references by sending a zero-id value as the target value of
the update.
I'm not 100% sure the locking is right, but it resambles the update
case so maybe it is.
There are some improvements that we can do on top of this:
- the ref_is_new and delete_ref fields of the struct
gotd{,_imsg}_ref_update could be merged in a single field using an
enum, and
- as a special case (but it's actually the more common one I guess),
the session process could avoid sending the fds for the pack idx
and data when a client is only deleting references.
-----------------------------------------------
commit f1477df7a63bc1f4ffbf2d4341937266b157d287 (main)
from: Omar Polo <op@omarpolo.com>
date: Fri Jan 27 16:22:04 2023 UTC
gotd: implement the delete-refs capability
diff 711bec03cb52c156a80885f2ee8ad7fce27dd57e f1477df7a63bc1f4ffbf2d4341937266b157d287
commit - 711bec03cb52c156a80885f2ee8ad7fce27dd57e
commit + f1477df7a63bc1f4ffbf2d4341937266b157d287
blob - de4f9c2c78944e2a095e39c114c19e9baed240f5
blob + e22197aef200b4b37d02bf22b05649f1af0a55a4
--- gotd/gotd.h
+++ gotd/gotd.h
@@ -325,6 +325,7 @@ struct gotd_imsg_ref_update {
uint8_t old_id[SHA1_DIGEST_LENGTH];
uint8_t new_id[SHA1_DIGEST_LENGTH];
int ref_is_new;
+ int delete_ref;
uint32_t client_id;
size_t name_len;
blob - a1082a01690cd74692583d348692d92175800c69
blob + 06a5bdff23de1154538510232e0cf2998523a312
--- gotd/repo_write.c
+++ gotd/repo_write.c
@@ -75,6 +75,7 @@ struct gotd_ref_update {
STAILQ_ENTRY(gotd_ref_update) entry;
struct got_reference *ref;
int ref_is_new;
+ int delete_ref;
struct got_object_id old_id;
struct got_object_id new_id;
};
@@ -89,6 +90,7 @@ static struct repo_write_client {
int packidx_fd;
struct gotd_ref_updates ref_updates;
int nref_updates;
+ int nref_del;
int nref_new;
} repo_write_client;
@@ -258,6 +260,7 @@ list_refs(struct imsg *imsg)
client->pack_pipe = -1;
client->packidx_fd = -1;
client->nref_updates = 0;
+ client->nref_del = 0;
client->nref_new = 0;
imsg_init(&ibuf, client_fd);
@@ -330,6 +333,7 @@ recv_ref_update(struct imsg *imsg)
static const struct got_error *
recv_ref_update(struct imsg *imsg)
{
+ static const char zero_id[SHA1_DIGEST_LENGTH];
const struct got_error *err = NULL;
struct repo_write_client *client = &repo_write_client;
struct gotd_imsg_ref_update iref;
@@ -418,6 +422,10 @@ recv_ref_update(struct imsg *imsg)
repo_write.pid);
ref_update->ref = ref;
+ if (memcmp(ref_update->new_id.sha1, zero_id, sizeof(zero_id)) == 0) {
+ ref_update->delete_ref = 1;
+ client->nref_del++;
+ }
STAILQ_INSERT_HEAD(&client->ref_updates, ref_update, entry);
client->nref_updates++;
ref = NULL;
@@ -705,6 +713,11 @@ recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t
*outsize = 0;
*nobj = 0;
+
+ /* if only deleting references there's nothing to read */
+ if (client->nref_updates == client->nref_del)
+ return NULL;
+
SHA1Init(&ctx);
err = got_poll_read_full(infd, &have, &hdr, sizeof(hdr), sizeof(hdr));
@@ -972,6 +985,15 @@ recv_packfile(int *have_packfile, struct imsg *imsg)
client->nref_updates == client->nref_new)
goto done;
+ /*
+ * Clients which are deleting references only will send
+ * no pack file.
+ */
+ if (nobj == 0 &&
+ client->nref_del > 0 &&
+ client->nref_updates == client->nref_del)
+ goto done;
+
pack->filesize = pack_filesize;
*have_packfile = 1;
@@ -1048,6 +1070,9 @@ verify_packfile(void)
return err;
STAILQ_FOREACH(ref_update, &client->ref_updates, entry) {
+ if (ref_update->delete_ref)
+ continue;
+
err = got_object_id_str(&id_str, &ref_update->new_id);
if (err)
goto done;
@@ -1121,6 +1146,7 @@ send_ref_update(struct gotd_ref_update *ref_update, st
memcpy(iref.old_id, ref_update->old_id.sha1, SHA1_DIGEST_LENGTH);
memcpy(iref.new_id, ref_update->new_id.sha1, SHA1_DIGEST_LENGTH);
iref.ref_is_new = ref_update->ref_is_new;
+ iref.delete_ref = ref_update->delete_ref;
iref.client_id = client->id;
iref.name_len = strlen(refname);
blob - c2bf744e16922aec4500424898af4499c5240308
blob + ffe16bd1114c575cd5d539f754121b08b01044f2
--- gotd/session.c
+++ gotd/session.c
@@ -432,7 +432,8 @@ update_ref(int *shut, struct gotd_session_client *clie
memcpy(old_id.sha1, iref.old_id, SHA1_DIGEST_LENGTH);
memcpy(new_id.sha1, iref.new_id, SHA1_DIGEST_LENGTH);
- err = got_object_open(&obj, repo, &new_id);
+ err = got_object_open(&obj, repo,
+ iref.delete_ref ? &old_id : &new_id);
if (err)
goto done;
@@ -454,6 +455,13 @@ update_ref(int *shut, struct gotd_session_client *clie
got_ref_get_name(ref));
goto done;
}
+ } else if (iref.delete_ref) {
+ err = got_ref_open(&ref, repo, refname, 1 /* lock */);
+ if (err)
+ goto done;
+ locked = 1;
+
+ err = got_ref_delete(ref, repo);
} else {
err = got_ref_open(&ref, repo, refname, 1 /* lock */);
if (err)
blob - 98f9aeb0236ac62592fd35493c4c5c761a8445d6
blob + d21d2a42eb07a9427920b7bc02e980a4ee9a5a22
--- lib/serve.c
+++ lib/serve.c
@@ -59,9 +59,7 @@ static const struct got_capability write_capabilities[
{ GOT_CAPA_OFS_DELTA, NULL },
{ GOT_CAPA_REPORT_STATUS, NULL },
{ GOT_CAPA_NO_THIN, NULL },
-#if 0
{ GOT_CAPA_DELETE_REFS, NULL },
-#endif
};
const struct got_error *
blob - c9dca58857cb121368aa8d3378609e72787267c1
blob + b48e2b54f360c89a3ce5d4c9e38d4dd5a7a3532f
--- regress/gotd/repo_write.sh
+++ regress/gotd/repo_write.sh
@@ -292,8 +292,98 @@ test_send_new_empty_branch() {
test_done "$testroot" "$ret"
}
+test_delete_branch() {
+ local testroot=`test_init delete_branch 1`
+ got clone -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
+
+ got send -r $testroot/repo-clone -d foo >$testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got send -d failed unexpectedly" >&2
+ test_done "$testroot" 1
+ return 1
+ fi
+
+ cat <<EOF >$testroot/stdout.expected
+Connecting to "origin" ${GOTD_TEST_REPO_URL}
+Server has deleted refs/heads/foo
+EOF
+ if ! cmp -s $testroot/stdout.expected $testroot/stdout; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" 1
+ return 1
+ fi
+
+ # now try again but while also updating another branch
+ # other than deleting `foo'.
+
+ (cd $testroot/wt && got up -b main && \
+ echo 'more alpha' > alpha && \
+ got commit -m 'edit alpha on main' && \
+ got send -q -b foo) >/dev/null
+
+ got send -r $testroot/repo-clone -d foo -b main | \
+ grep '^Server has' >$testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got send -d foo -b main failed unexpectedly" >&2
+ test_done "$testroot" 1
+ return 1
+ fi
+
+ cat <<EOF >$testroot/stdout.expected
+Server has accepted refs/heads/main
+Server has deleted refs/heads/foo
+EOF
+ if ! cmp -s $testroot/stdout.expected $testroot/stdout; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ 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
gotd: initial implementation of the delete-refs capability