refs.c make ref_transaction_create a wrapper to ref_transaction_update

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: make ref_transaction_delete a wrapper for ref_transaction_update

We want to allow to delete a ref even if it only exists as a packed ref
so we must tweak the REF_ISPACKONLY test in _update so it only applies
to the non-delete case.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: rename the transaction functions

Rename the transaction functions. Remove the leading ref_ from the names
and append _sha1 to the names for functions that create/delete/update sha1
refs.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: add a new update_type field to ref_update

Add a field that describes what type of update this refers to. For now
the only type is UPDATE_SHA1 but we will soon add more types.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: add a function to append a reflog entry to a fd

Break out the code to create the string and writing it to the file descriptor
from log_ref_write and into a dedicated function log_ref_write_fd. For now
this is only used from log_ref_write but later on we will call this function
from reflog transactions too which means that we will end up with only a single
place where we write a reflog entry to a file instead of the current two places
(log_ref_write and builtin/reflog.c).

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: add a transaction function to append a reflog entry

Define a new transaction update type, UPDATE_LOG, and a new function
transaction_update_reflog. This function will lock the reflog and append
an entry to it during transaction commit.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: add a flag to allow reflog updates to truncate the log

Add a flag that allows us to truncate the reflog before we write the update.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: only write reflog update if msg is non-NULL

When performing a reflog transaction update, only write to the reflog iff
msg is non-NULL. This can then be combined with REFLOG_TRUNCATE to perform
an update that only truncates but does not write.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: allow multiple reflog updates during a single transaction

Allow to make multiple reflog updates to the same ref during a transaction.
This means we only need to lock the reflog once, during the first update that
touches the reflog, and that all further updates can just write the reflog
entry since the reflog is already locked.

This allows us to write code such as:

t = transaction_begin()
transaction_reflog_update(t, "foo", REFLOG_TRUNCATE, NULL);
loop-over-somehting...
   transaction_reflog_update(t, "foo", 0, <message>);
transaction_commit(t)

where we first truncate the reflog and then build the new content one line at a
time.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
reflog.c: use a reflog transaction when writing during expire

Use a transaction when doing the updates of the reflog during expire_reflog.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: null terminate the string in copy_msg

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: track the refnames we are deleting in the transaction structure

Track the names of the refs we are deleting in the ref_transaction structure
instead of building an array of names during _commit.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: update the list of deleted refs during _update instead of _commit

When deleting refs during a transaction, update the list of the refs to be
deleted already during _update instead of waiting until the _commit stage.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: return error instead of dying when locking fails during transaction

Change lock_ref_sha1_basic to return an error instead of dying when
we fail to lock a file during a transaction.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: lock the ref during _update instead of during _commit

Try to lock the ref during the transaction update instead of later during the
commit phase. This allows us to do things such as :

t = transaction_begin()
transaction_update_sha1(t, ref, ...)   /* locking ref */
...
do stuff while we know that ref is locked
...
transaction_commit(t)

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: add an error argument to create/delete/update just like commit

TO BE DELETED

Update the signatures for transaction create/delete/update and add
a strbuf err argument we can use to pass an error back to the caller.
We will need this later once we start moving checks from _commit to _update.
Change all callers that would die() on _commit failures to handle _update
failures similarly.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: return immediately from _commit if the transaction has an error

If a previous transaction operation has failed and flagged a transaction
as having an error, then return immediately from commit and indicate failure.
Once we move checks from _commit to _update, _update will already have updated
the error string so the caller will know what failed. Thus we don't need to do
anything at all in _commit than return failure.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
tests: move tests for -z update/delete/verify to after the ref is created

The tests for stdin -z fails for update/delete/verify are of the form
update refs/heads/a $m $m $m
and rely on the fact that when parsed we will first parse everything except
the trailing $m. And once that command has completed it will start parsing
the next command, find the trailing $m from before and fail because this
would not look like a proper command. I.e. we do not detect that there are
too many arguments so much as detect that the next command looks like
garbage.

But since the ref refs/heads/a does not exist at this point in the test we
actually have two failures that trigger. We have one error that is due to
update refs/heads/a $m $m would be invalid because the ref does not exist
and we have a second error that is due to the trailing $m causing a "too
many arguments". The current test depends on the order of check for these two
which makes the test fragile (and would break after the next patch).

Move these tests further down in the test to occur after we have created
refs/heads/a so that there is only one error condition in the test and that
we no longer depend on in which order the checks for error occur.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: check for lock conflicts already in _update

Check for lock conflicts in _update and flag the transaction as errored
instead of waiting until _commit for doing these checks. If there was a lock
failure we check if this was due to a previous update in the same transaction
or not. This so that we can preserve the current error messages on lock failure
and log "Multiple updates for ref '%s' not allowed." when we have multiple
updates in the same transaction and "Cannot lock the ref '%s'." for any other
reason that the lock failed.

This also means that we no longer need to sort all the refs and check for
duplicates during _commit so all that code can be removed too.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c allow multiple updates of the same ref in a transaction

Allow multiple updates of a ref in the same transaction as long as
each update has have_old and old_sha1 matches the new_sha1 of the
previous update.

Add a test that verifies that a valid sequence such as
  create ref a
  update ref b a
  update ref c b
works and a test that an invalid sequence such as this still fails:
  update ref a c
  update ref b b
  update ref c c

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>