*/
#define REF_DELETED_RMDIR (1 << 9)
+/*
+ * Used to indicate that the reflog-only update has been created via
+ * `split_head_update()`.
+ */
+#define REF_LOG_VIA_SPLIT (1 << 14)
+
struct ref_lock {
char *ref_name;
struct lock_file lk;
new_update = ref_transaction_add_update(
transaction, "HEAD",
- update->flags | REF_LOG_ONLY | REF_NO_DEREF,
+ update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
&update->new_oid, &update->old_oid,
NULL, NULL, update->committer_info, update->msg);
+ new_update->parent_update = update;
/*
* Add "HEAD". This insertion is O(N) in the transaction
update->backend_data = lock;
- if (update->type & REF_ISSYMREF) {
+ if (update->flags & REF_LOG_VIA_SPLIT) {
+ struct ref_lock *parent_lock;
+
+ if (!update->parent_update)
+ BUG("split update without a parent");
+
+ parent_lock = update->parent_update->backend_data;
+
+ /*
+ * Check that "HEAD" didn't racily change since we have looked
+ * it up. If it did we must refuse to write the reflog entry.
+ *
+ * Note that this does not catch all races: if "HEAD" was
+ * racily changed to point to one of the refs part of the
+ * transaction then we would miss writing the split reflog
+ * entry for "HEAD".
+ */
+ if (!(update->type & REF_ISSYMREF) ||
+ strcmp(update->parent_update->refname, referent.buf)) {
+ strbuf_addstr(err, "HEAD has been racily updated");
+ ret = REF_TRANSACTION_ERROR_GENERIC;
+ goto out;
+ }
+
+ if (update->flags & REF_HAVE_OLD) {
+ oidcpy(&lock->old_oid, &update->old_oid);
+ } else {
+ oidcpy(&lock->old_oid, &parent_lock->old_oid);
+ }
+ } else if (update->type & REF_ISSYMREF) {
if (update->flags & REF_NO_DEREF) {
/*
* We won't be reading the referent as part of