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>
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: 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: 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>
refs.c: release all remaining locks during transaction_free

If there are remaining locks held when we free a transaction, make sure that
we release them instead of relying on the atexit magic to release them for
us. Otherwise, if we perform two transactions on the same ref and the first
transaction is aborted then the second transaction will fail to lock the ref.
This can currently not trigger since there are no commands that use multiple
transactions.

This sequence illustrates the bug:

t = transaction_begin()  /* first transaction */
transaction_update_sha1(t, ref, ...)  /* ref is locked */
transaction_free(t)

t = transaction_begin() /* second transaction */
transaction_update_sha1(t, ref, ...) /* will fail since ref was locked above */

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
reflog.c: use the existing transaction to also lock and update the ref

reflog.c has a transaction for updating the reflog. Use this transaction
also for locking the ref itself and updating the ref instead of
locking the file directly and writing to it. This now completely eliminates
the refs and reflog implementation from reflog.c.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: make unlock_ref static

We no longer need to export unlock_ref. Make the function static.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: make close_ref static

We no longer need to export close_ref. Make the function static.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
refs.c: make commit_ref static

We no longer need to export commit_ref. Make the function static.

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