diff options
Diffstat (limited to 'refs/files-backend.c')
| -rw-r--r-- | refs/files-backend.c | 65 | 
1 files changed, 58 insertions, 7 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index 088b52c740..dea46d07ae 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -68,6 +68,12 @@   */  #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; @@ -2421,9 +2427,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,  	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 @@ -2494,7 +2501,6 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,  	 * done when new_update is processed.  	 */  	update->flags |= REF_LOG_ONLY | REF_NO_DEREF; -	update->flags &= ~REF_HAVE_OLD;  	return 0;  } @@ -2509,8 +2515,9 @@ static enum ref_transaction_error check_old_oid(struct ref_update *update,  						struct object_id *oid,  						struct strbuf *err)  { -	if (!(update->flags & REF_HAVE_OLD) || -		   oideq(oid, &update->old_oid)) +	if (update->flags & REF_LOG_ONLY || +	    !(update->flags & REF_HAVE_OLD) || +	    oideq(oid, &update->old_oid))  		return 0;  	if (is_null_oid(&update->old_oid)) { @@ -2601,7 +2608,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re  	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 @@ -2977,6 +3013,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs,  				  struct ref_lock *lock,  				  struct strbuf *err)  { +	struct object_id *old_oid = &lock->old_oid; + +	if (update->flags & REF_LOG_USE_PROVIDED_OIDS) { +		if (!(update->flags & REF_HAVE_OLD) || +		    !(update->flags & REF_HAVE_NEW) || +		    !(update->flags & REF_LOG_ONLY)) { +			strbuf_addf(err, _("trying to write reflog for '%s'" +					   "with incomplete values"), update->refname); +			return REF_TRANSACTION_ERROR_GENERIC; +		} + +		old_oid = &update->old_oid; +	} +  	if (update->new_target) {  		/*  		 * We want to get the resolved OID for the target, to ensure @@ -2994,7 +3044,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs,  		}  	} -	if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid, +	if (files_log_ref_write(refs, lock->ref_name, old_oid,  				&update->new_oid, update->committer_info,  				update->msg, update->flags, err)) {  		char *old_msg = strbuf_detach(err, NULL); @@ -3062,7 +3112,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,  	for (i = 0; i < transaction->nr; i++) {  		struct ref_update *update = transaction->updates[i]; -		if ((update->flags & REF_HAVE_OLD) && +		if (!(update->flags & REF_LOG_ONLY) && +		    (update->flags & REF_HAVE_OLD) &&  		    !is_null_oid(&update->old_oid))  			BUG("initial ref transaction with old_sha1 set");  | 
