From: Michael Haggerty <mhagger@alum.mit.edu>
To: Ronnie Sahlberg <sahlberg@google.com>, git@vger.kernel.org
Subject: Re: [PATCH v4 1/3] refs.c: split writing and commiting a ref into two separate functions
Date: Tue, 15 Apr 2014 13:17:00 +0200 [thread overview]
Message-ID: <534D152C.7090607@alum.mit.edu> (raw)
In-Reply-To: <1397500163-7617-2-git-send-email-sahlberg@google.com>
See my comment to your cover letter where I suggest using ref
transactions instead of making callers deal with even more of the
details of updating references. But I will comment on these patches
anyway, in case you'd rather leave them closer to the current form.
On 04/14/2014 08:29 PM, Ronnie Sahlberg wrote:
> Change the function write_ref_sha1() to just write the ref but not
> commit the ref or the lockfile.
> Add a new function commit_ref_lock() that will commit the change done by
> a previous write_ref_sha1().
> Update all callers of write_ref_sha1() to call commit_ref_lock().
>
> The new pattern for updating a ref is now :
>
> lock = lock_ref_sha1_basic() (or varient of)
s/varient/variant/
> write_ref_sha1(lock)
> unlock_ref(lock) | commit_ref_lock(lock)
>
> Once write_ref_sha1() returns, the new ref has been written and the lock
> file has been closed.
> At that stage we can then either call unlock_ref() which will abort the
> update and delete the lock file withouth applying it, or call
You need a comma after "unlock_ref()".
s/withouth/without/
> commit_ref_lock() which will rename the lock file onto the ref file.
The commit message would be easier to read with better formatting; maybe
---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---
refs.c: split writing and commiting a ref into two separate functions
* Change the function write_ref_sha1() to just write the ref but not
commit the ref or the lockfile.
* Add a new function commit_ref_lock() that will commit the change done by
a previous write_ref_sha1().
* Update all callers of write_ref_sha1() to call commit_ref_lock().
The new pattern for updating a ref is now :
lock = lock_ref_sha1_basic() (or variant of)
write_ref_sha1(lock)
unlock_ref(lock) | commit_ref_lock(lock)
Once write_ref_sha1() returns, the new ref has been written and the lock
file has been closed. At that stage we can then either call unlock_ref(),
which will abort the update and delete the lock file without applying it,
or call commit_ref_lock() which will rename the lock file onto the ref
file.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---
> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
> ---
> branch.c | 10 ++++++++--
> builtin/commit.c | 5 +++++
> builtin/fetch.c | 7 ++++++-
> builtin/receive-pack.c | 4 ++++
> builtin/replace.c | 6 +++++-
> builtin/tag.c | 6 +++++-
> fast-import.c | 18 ++++++++++++++++--
> refs.c | 41 +++++++++++++++++++++++++++++++----------
> refs.h | 4 ++++
> sequencer.c | 4 ++++
> walker.c | 4 ++++
> 11 files changed, 92 insertions(+), 17 deletions(-)
>
> diff --git a/branch.c b/branch.c
> index 660097b..903ea75 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -304,9 +304,15 @@ void create_branch(const char *head,
> if (real_ref && track)
> setup_tracking(ref.buf + 11, real_ref, track, quiet);
>
> - if (!dont_change_ref)
> - if (write_ref_sha1(lock, sha1, msg) < 0)
> + if (!dont_change_ref) {
> + if (write_ref_sha1(lock, sha1, msg) < 0) {
> + unlock_ref(lock);
> die_errno(_("Failed to write ref"));
> + }
> + if (commit_ref_lock(lock) < 0) {
> + die_errno(_("Failed to commit ref"));
> + }
> + }
>
> strbuf_release(&ref);
> free(real_ref);
There are a lot of changes like this with similar duplicated error
handling. Why not define a helper function like
write_ref_sha1_and_commit() that does what the old write_ref_sha1() used
to do (for the callers who don't care about updating multiple references
at once)?
In fact, I would recommend renaming the function in a preparatory
commit, to reduce the amount of code churn in the second commit where
you extract the two new separate functions.
> diff --git a/builtin/commit.c b/builtin/commit.c
> index d9550c5..3d8a3a8 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -1686,9 +1686,14 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> die(_("cannot lock HEAD ref"));
> }
> if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
> + unlock_ref(ref_lock);
> rollback_index_files();
> die(_("cannot update HEAD ref"));
> }
> + if (commit_ref_lock(ref_lock) < 0) {
> + rollback_index_files();
> + die(_("cannot commit HEAD ref"));
> + }
>
> unlink(git_path("CHERRY_PICK_HEAD"));
> unlink(git_path("REVERT_HEAD"));
> diff --git a/builtin/fetch.c b/builtin/fetch.c
> index 55f457c..ebfb854 100644
> --- a/builtin/fetch.c
> +++ b/builtin/fetch.c
> @@ -388,7 +388,12 @@ static int s_update_ref(const char *action,
> if (!lock)
> return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
> STORE_REF_ERROR_OTHER;
> - if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
> + if (write_ref_sha1(lock, ref->new_sha1, msg) < 0) {
> + unlock_ref(lock);
> + return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
> + STORE_REF_ERROR_OTHER;
> + }
> + if (commit_ref_lock(lock) < 0)
> return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
> STORE_REF_ERROR_OTHER;
> return 0;
> diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
> index c323081..4760274 100644
> --- a/builtin/receive-pack.c
> +++ b/builtin/receive-pack.c
> @@ -587,8 +587,12 @@ static const char *update(struct command *cmd, struct shallow_info *si)
> return "failed to lock";
> }
> if (write_ref_sha1(lock, new_sha1, "push")) {
> + unlock_ref(lock);
> return "failed to write"; /* error() already called */
> }
> + if (commit_ref_lock(lock))
> + return "failed to commit"; /* error() already called */
> +
> return NULL; /* good */
> }
> }
> diff --git a/builtin/replace.c b/builtin/replace.c
> index b62420a..c09ff49 100644
> --- a/builtin/replace.c
> +++ b/builtin/replace.c
> @@ -160,8 +160,12 @@ static int replace_object(const char *object_ref, const char *replace_ref,
> lock = lock_any_ref_for_update(ref, prev, 0, NULL);
> if (!lock)
> die("%s: cannot lock the ref", ref);
> - if (write_ref_sha1(lock, repl, NULL) < 0)
> + if (write_ref_sha1(lock, repl, NULL) < 0) {
> + unlock_ref(lock);
> die("%s: cannot update the ref", ref);
> + }
> + if (commit_ref_lock(lock) < 0)
> + die("%s: cannot commit the ref", ref);
>
> return 0;
> }
> diff --git a/builtin/tag.c b/builtin/tag.c
> index 40356e3..8653a64 100644
> --- a/builtin/tag.c
> +++ b/builtin/tag.c
> @@ -644,8 +644,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
> lock = lock_any_ref_for_update(ref.buf, prev, 0, NULL);
> if (!lock)
> die(_("%s: cannot lock the ref"), ref.buf);
> - if (write_ref_sha1(lock, object, NULL) < 0)
> + if (write_ref_sha1(lock, object, NULL) < 0) {
> + unlock_ref(lock);
> die(_("%s: cannot update the ref"), ref.buf);
> + }
> + if (commit_ref_lock(lock) < 0)
> + die(_("%s: cannot commit the ref"), ref.buf);
> if (force && !is_null_sha1(prev) && hashcmp(prev, object))
> printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
>
> diff --git a/fast-import.c b/fast-import.c
> index fb4738d..f732bfb 100644
> --- a/fast-import.c
> +++ b/fast-import.c
> @@ -1706,8 +1706,13 @@ static int update_branch(struct branch *b)
> return -1;
> }
> }
> - if (write_ref_sha1(lock, b->sha1, msg) < 0)
> + if (write_ref_sha1(lock, b->sha1, msg) < 0) {
> + unlock_ref(lock);
> return error("Unable to update %s", b->name);
> + }
> + if (commit_ref_lock(lock) < 0) {
> + return error("Unable to commit %s", b->name);
> + }
> return 0;
> }
>
> @@ -1732,8 +1737,17 @@ static void dump_tags(void)
> for (t = first_tag; t; t = t->next_tag) {
> sprintf(ref_name, "tags/%s", t->name);
> lock = lock_ref_sha1(ref_name, NULL);
> - if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
> + if (!lock) {
> + failure |= error("Unable to lock %s", ref_name);
> + continue;
> + }
> + if (write_ref_sha1(lock, t->sha1, msg) < 0) {
> failure |= error("Unable to update %s", ref_name);
> + unlock_ref(lock);
> + continue;
> + }
> + if (commit_ref_lock(lock) < 0)
> + failure |= error("Unable to commit %s", ref_name);
> }
> }
>
> diff --git a/refs.c b/refs.c
> index 728a761..646afd7 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -2633,9 +2633,14 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
> lock->force_write = 1;
> hashcpy(lock->old_sha1, orig_sha1);
> if (write_ref_sha1(lock, orig_sha1, logmsg)) {
> + unlock_ref(lock);
> error("unable to write current sha1 into %s", newrefname);
> goto rollback;
> }
> + if (commit_ref_lock(lock)) {
> + error("unable to commit current sha1 into %s", newrefname);
> + goto rollback;
> + }
>
> return 0;
>
> @@ -2649,8 +2654,12 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
> lock->force_write = 1;
> flag = log_all_ref_updates;
> log_all_ref_updates = 0;
> - if (write_ref_sha1(lock, orig_sha1, NULL))
> + if (write_ref_sha1(lock, orig_sha1, NULL)) {
> + unlock_ref(lock);
> error("unable to write current sha1 into %s", oldrefname);
> + }
> + if (commit_ref_lock(lock))
> + error("unable to commit current sha1 into %s", oldrefname);
> log_all_ref_updates = flag;
>
> rollbacklog:
> @@ -2807,34 +2816,30 @@ int write_ref_sha1(struct ref_lock *lock,
> if (!lock)
> return -1;
> if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
> - unlock_ref(lock);
> + lock->skipped_write = 1;
> return 0;
> }
> o = parse_object(sha1);
> if (!o) {
> error("Trying to write ref %s with nonexistent object %s",
> lock->ref_name, sha1_to_hex(sha1));
> - unlock_ref(lock);
> return -1;
> }
> if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
> error("Trying to write non-commit object %s to branch %s",
> sha1_to_hex(sha1), lock->ref_name);
> - unlock_ref(lock);
> return -1;
> }
> if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
> write_in_full(lock->lock_fd, &term, 1) != 1
> || close_ref(lock) < 0) {
> error("Couldn't write %s", lock->lk->filename);
> - unlock_ref(lock);
> return -1;
> }
> clear_loose_ref_cache(&ref_cache);
> if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
> (strcmp(lock->ref_name, lock->orig_ref_name) &&
> log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
> - unlock_ref(lock);
> return -1;
> }
> if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
> @@ -2858,7 +2863,12 @@ int write_ref_sha1(struct ref_lock *lock,
> !strcmp(head_ref, lock->ref_name))
> log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
> }
> - if (commit_ref(lock)) {
> + return 0;
> +}
> +
> +int commit_ref_lock(struct ref_lock *lock)
> +{
> + if (!lock->skipped_write && commit_ref(lock)) {
> error("Couldn't set %s", lock->ref_name);
> unlock_ref(lock);
> return -1;
> @@ -3375,10 +3385,17 @@ int update_ref(const char *action, const char *refname,
> int flags, enum action_on_err onerr)
> {
> struct ref_lock *lock;
> + int ret;
> +
> lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
> if (!lock)
> return 1;
> - return update_ref_write(action, refname, sha1, lock, onerr);
> + ret = update_ref_write(action, refname, sha1, lock, onerr);
> + if (ret)
> + unlock_ref(lock);
> + else
> + ret = commit_ref_lock(lock);
> + return ret;
> }
>
> static int ref_update_compare(const void *r1, const void *r2)
> @@ -3453,7 +3470,11 @@ int ref_transaction_commit(struct ref_transaction *transaction,
> update->refname,
> update->new_sha1,
> update->lock, onerr);
> - update->lock = NULL; /* freed by update_ref_write */
> + if (ret)
> + unlock_ref(update->lock);
> + else
> + commit_ref_lock(update->lock);
> + update->lock = NULL;
> if (ret)
> goto cleanup;
> }
> @@ -3464,7 +3485,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
> struct ref_update *update = updates[i];
>
> if (update->lock) {
> - delnames[delnum++] = update->lock->ref_name;
> + delnames[delnum++] = update->refname;
Isn't this hunk orthogonal to the main point of this commit? If so,
please split it into a separate commit.
> ret |= delete_ref_loose(update->lock, update->type);
> }
> }
> diff --git a/refs.h b/refs.h
> index 0f08def..f14a417 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -8,6 +8,7 @@ struct ref_lock {
> unsigned char old_sha1[20];
> int lock_fd;
> int force_write;
> + int skipped_write;
> };
>
> struct ref_transaction;
> @@ -153,6 +154,9 @@ extern void unlock_ref(struct ref_lock *lock);
> /** Writes sha1 into the ref specified by the lock. **/
> extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
>
> +/** Commit any changes done to the ref specified by the lock. **/
> +extern int commit_ref_lock(struct ref_lock *lock);
> +
> /** Setup reflog before using. **/
> int log_ref_setup(const char *refname, char *logfile, int bufsize);
>
> diff --git a/sequencer.c b/sequencer.c
> index bde5f04..ffadf82 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -283,6 +283,10 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
> 0, NULL);
> strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
> ret = write_ref_sha1(ref_lock, to, sb.buf);
> + if (ret)
> + unlock_ref(ref_lock);
> + else
> + ret |= commit_ref_lock(ref_lock);
"|=" could be changed to "=" here and in the next hunk.
> strbuf_release(&sb);
> return ret;
> }
> diff --git a/walker.c b/walker.c
> index 1dd86b8..5ce5a1d 100644
> --- a/walker.c
> +++ b/walker.c
> @@ -295,6 +295,10 @@ int walker_fetch(struct walker *walker, int targets, char **target,
> if (!write_ref || !write_ref[i])
> continue;
> ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
> + if (ret)
> + unlock_ref(lock[i]);
> + else
> + ret |= commit_ref_lock(lock[i]);
> lock[i] = NULL;
> if (ret)
> goto unlock_and_fail;
>
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
next prev parent reply other threads:[~2014-04-15 11:17 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-04-14 18:29 [PATCH v4 0/3] Make update refs more atomic Ronnie Sahlberg
2014-04-14 18:29 ` [PATCH v4 1/3] refs.c: split writing and commiting a ref into two separate functions Ronnie Sahlberg
2014-04-15 11:17 ` Michael Haggerty [this message]
2014-04-14 18:29 ` [PATCH v4 2/3] refs.c: split delete_ref_loose() into a separate flag-for-deletion and commit phase Ronnie Sahlberg
2014-04-15 17:19 ` Michael Haggerty
2014-04-14 18:29 ` [PATCH v4 3/3] refs.c: change ref_transaction_commit to run the commit loops once all work is finished Ronnie Sahlberg
2014-04-14 20:24 ` [PATCH v4 0/3] Make update refs more atomic Junio C Hamano
2014-04-15 16:41 ` Ronnie Sahlberg
2014-04-15 6:36 ` Michael Haggerty
2014-04-15 16:33 ` Ronnie Sahlberg
2014-04-15 20:32 ` Michael Haggerty
2014-04-16 17:11 ` Ronnie Sahlberg
2014-04-16 19:31 ` Junio C Hamano
2014-04-16 21:31 ` Ronnie Sahlberg
2014-04-16 21:42 ` Junio C Hamano
2014-04-16 21:51 ` Michael Haggerty
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=534D152C.7090607@alum.mit.edu \
--to=mhagger@alum.mit.edu \
--cc=git@vger.kernel.org \
--cc=sahlberg@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.