commit - 22d6be814cfc21a663987c0dcb547f99e48a9860
commit + 9a8e357c727600cb61ac6ec4c83259fa6d9a3081
blob - de4f9c2c78944e2a095e39c114c19e9baed240f5
blob + e22197aef200b4b37d02bf22b05649f1af0a55a4
--- gotd/gotd.h
+++ gotd/gotd.h
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
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;
};
int packidx_fd;
struct gotd_ref_updates ref_updates;
int nref_updates;
+ int nref_del;
int nref_new;
} repo_write_client;
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);
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;
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;
*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));
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;
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;
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 + bbe5f89f20242a5571907a6f813d02388bacddf4
--- gotd/session.c
+++ gotd/session.c
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;
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_resolve(&id, repo, ref);
+ if (err)
+ goto done;
+
+ if (got_object_id_cmp(id, &old_id) != 0) {
+ err = got_error_fmt(GOT_ERR_REF_BUSY,
+ "%s has been modified by someone else "
+ "while transaction was in progress",
+ got_ref_get_name(ref));
+ goto done;
+ }
+
+ err = got_ref_delete(ref, repo);
+ if (err)
+ goto done;
+
+ free(id);
+ id = NULL;
} else {
err = got_ref_open(&ref, repo, refname, 1 /* lock */);
if (err)
blob - 98f9aeb0236ac62592fd35493c4c5c761a8445d6
blob + d21d2a42eb07a9427920b7bc02e980a4ee9a5a22
--- lib/serve.c
+++ lib/serve.c
{ 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
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