[PATCH v6 1/7] refs.c: add err arguments to reflog functions

2015-06-29 Thread David Turner
Add an err argument to log_ref_setup that can explain the reason
for a failure. This then eliminates the need to manage errno through
this function since we can just add strerror(errno) to the err string
when meaningful. No callers relied on errno from this function for
anything else than the error message.

Also add err arguments to private functions write_ref_to_lockfile,
log_ref_write_1, commit_ref_update. This again eliminates the need to
manage errno in these functions.

Update of a patch by Ronnie Sahlberg.

Signed-off-by: Ronnie Sahlberg 
Signed-off-by: David Turner 
---
 builtin/checkout.c |   8 ++--
 refs.c | 111 -
 refs.h |   4 +-
 3 files changed, 66 insertions(+), 57 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index c018ab3..93f63d3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -624,16 +624,18 @@ static void update_refs_for_switch(const struct 
checkout_opts *opts,
struct strbuf log_file = STRBUF_INIT;
int ret;
const char *ref_name;
+   struct strbuf err = STRBUF_INIT;
 
ref_name = mkpath("refs/heads/%s", 
opts->new_orphan_branch);
temp = log_all_ref_updates;
log_all_ref_updates = 1;
-   ret = log_ref_setup(ref_name, &log_file);
+   ret = log_ref_setup(ref_name, &log_file, &err);
log_all_ref_updates = temp;
strbuf_release(&log_file);
if (ret) {
-   fprintf(stderr, _("Can not do reflog 
for '%s'\n"),
-   opts->new_orphan_branch);
+   fprintf(stderr, _("Can not do reflog 
for '%s'. %s\n"),
+   opts->new_orphan_branch, 
err.buf);
+   strbuf_release(&err);
return;
}
}
diff --git a/refs.c b/refs.c
index fb568d7..c97ca02 100644
--- a/refs.c
+++ b/refs.c
@@ -2975,9 +2975,11 @@ static int rename_ref_available(const char *oldname, 
const char *newname)
return ret;
 }
 
-static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char 
*sha1);
+static int write_ref_to_lockfile(struct ref_lock *lock,
+const unsigned char *sha1, struct strbuf *err);
 static int commit_ref_update(struct ref_lock *lock,
-const unsigned char *sha1, const char *logmsg);
+const unsigned char *sha1, const char *logmsg,
+struct strbuf *err);
 
 int rename_ref(const char *oldrefname, const char *newrefname, const char 
*logmsg)
 {
@@ -3038,9 +3040,10 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
}
hashcpy(lock->old_oid.hash, orig_sha1);
 
-   if (write_ref_to_lockfile(lock, orig_sha1) ||
-   commit_ref_update(lock, orig_sha1, logmsg)) {
-   error("unable to write current sha1 into %s", newrefname);
+   if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
+   commit_ref_update(lock, orig_sha1, logmsg, &err)) {
+   error("unable to write current sha1 into %s: %s", newrefname, 
err.buf);
+   strbuf_release(&err);
goto rollback;
}
 
@@ -3056,9 +3059,11 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
 
flag = log_all_ref_updates;
log_all_ref_updates = 0;
-   if (write_ref_to_lockfile(lock, orig_sha1) ||
-   commit_ref_update(lock, orig_sha1, NULL))
-   error("unable to write current sha1 into %s", oldrefname);
+   if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
+   commit_ref_update(lock, orig_sha1, NULL, &err)) {
+   error("unable to write current sha1 into %s: %s", oldrefname, 
err.buf);
+   strbuf_release(&err);
+   }
log_all_ref_updates = flag;
 
  rollbacklog:
@@ -3113,8 +3118,8 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
 }
 
-/* This function must set a meaningful errno on failure */
-int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
+/* This function will fill in *err and return -1 on failure */
+int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct 
strbuf *err)
 {
int logfd, oflags = O_APPEND | O_WRONLY;
char *logfile;
@@ -3129,9 +3134,8 @@ int log_ref_setup(const char *refname, struct strbuf 
*sb_logfile)
 starts_with(refname, "refs/notes/") ||
 !strcmp(refname, "HEAD

Re: [PATCH v6 1/7] refs.c: add err arguments to reflog functions

2015-07-06 Thread Michael Haggerty
On 06/29/2015 10:17 PM, David Turner wrote:
> Add an err argument to log_ref_setup that can explain the reason
> for a failure. This then eliminates the need to manage errno through
> this function since we can just add strerror(errno) to the err string
> when meaningful. No callers relied on errno from this function for
> anything else than the error message.
> 
> Also add err arguments to private functions write_ref_to_lockfile,
> log_ref_write_1, commit_ref_update. This again eliminates the need to
> manage errno in these functions.
> 
> Update of a patch by Ronnie Sahlberg.

First a general comment: we have a convention, not yet very well adhered
to, that error messages should start with lower-case letters. I know
that in many cases you are just carrying along pre-existing error
messages that are capitalized. But at a minimum, please avoid adding new
error messages that are capitalized. And if you are feeling ambitious,
feel free to lower-case some existing ones :-)

> Signed-off-by: Ronnie Sahlberg 
> Signed-off-by: David Turner 
> ---
>  builtin/checkout.c |   8 ++--
>  refs.c | 111 
> -
>  refs.h |   4 +-
>  3 files changed, 66 insertions(+), 57 deletions(-)
> 
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> [...]
> diff --git a/refs.c b/refs.c
> index fb568d7..c97ca02 100644
> --- a/refs.c
> +++ b/refs.c
> [...]
> @@ -3216,26 +3217,25 @@ static int log_ref_write_1(const char *refname, const 
> unsigned char *old_sha1,
>   result = log_ref_write_fd(logfd, old_sha1, new_sha1,
> git_committer_info(0), msg);
>   if (result) {
> - int save_errno = errno;
>   close(logfd);
> - error("Unable to append to %s", log_file);
> - errno = save_errno;
> + strbuf_addf(err, "Unable to append to %s. %s", log_file,
> + strerror(errno));
>   return -1;

Above, the call to close(logfd) could clobber errno. It would be better
to exchange the calls.

>   }
>   if (close(logfd)) {
> - int save_errno = errno;
> - error("Unable to append to %s", log_file);
> - errno = save_errno;
> + strbuf_addf(err, "Unable to append to %s. %s", log_file,
> + strerror(errno));
>   return -1;
>   }
>   return 0;
>  }
>  
>  static int log_ref_write(const char *refname, const unsigned char *old_sha1,
> -  const unsigned char *new_sha1, const char *msg)
> +  const unsigned char *new_sha1, const char *msg,
> +  struct strbuf *err)
>  {
>   struct strbuf sb = STRBUF_INIT;
> - int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
> + int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, err);
>   strbuf_release(&sb);
>   return ret;
>  }
> [...]
> @@ -3288,12 +3290,15 @@ static int write_ref_to_lockfile(struct ref_lock 
> *lock,
>   * necessary, using the specified lockmsg (which can be NULL).
>   */
>  static int commit_ref_update(struct ref_lock *lock,
> -  const unsigned char *sha1, const char *logmsg)
> +  const unsigned char *sha1, const char *logmsg,
> +  struct strbuf *err)
>  {
>   clear_loose_ref_cache(&ref_cache);
> - if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 
> ||
> + if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 
> err) < 0 ||
>   (strcmp(lock->ref_name, lock->orig_ref_name) &&
> -  log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, 
> logmsg) < 0)) {
> +  log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, 
> logmsg, err) < 0)) {
> + strbuf_addf(err, "Cannot update the ref '%s'.",
> + lock->ref_name);
>   unlock_ref(lock);
>   return -1;
>   }

Since you are passing err into log_ref_write(), I assume that it would
already have an error message written to it by the time you enter into
this block. Yet in the block you append more text to it.

It seems to me that you either want to clear err and replace it with
your own message, or create a new message that looks like

Cannot update the ref '%s': %s

where the second "%s" is replaced with the error from log_ref_write().

> @@ -3317,7 +3322,8 @@ static int commit_ref_update(struct ref_lock *lock,
> head_sha1, &head_flag);
>   if (head_ref && (head_flag & REF_ISSYMREF) &&
>   !strcmp(head_ref, lock->ref_name))
> - log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
> + log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg,
> +   err);

Here you don't check for errors from log_ref_wr

Re: [PATCH v6 1/7] refs.c: add err arguments to reflog functions

2015-07-07 Thread David Turner
On Mon, 2015-07-06 at 17:53 +0200, Michael Haggerty wrote:
> On 06/29/2015 10:17 PM, David Turner wrote:
> > Add an err argument to log_ref_setup that can explain the reason
> > for a failure. This then eliminates the need to manage errno through
> > this function since we can just add strerror(errno) to the err string
> > when meaningful. No callers relied on errno from this function for
> > anything else than the error message.
> > 
> > Also add err arguments to private functions write_ref_to_lockfile,
> > log_ref_write_1, commit_ref_update. This again eliminates the need to
> > manage errno in these functions.
> > 
> > Update of a patch by Ronnie Sahlberg.
> 
> First a general comment: we have a convention, not yet very well adhered
> to, that error messages should start with lower-case letters. I know
> that in many cases you are just carrying along pre-existing error
> messages that are capitalized. But at a minimum, please avoid adding new
> error messages that are capitalized. And if you are feeling ambitious,
> feel free to lower-case some existing ones :-)

I'll save lowercasing messages for other patches, but I'll try to take
care with new messages.

...

> Above, the call to close(logfd) could clobber errno. It would be better
> to exchange the calls.

Done, thanks.

> Since you are passing err into log_ref_write(), I assume that it would
> already have an error message written to it by the time you enter into
> this block. Yet in the block you append more text to it.
> 
> It seems to me that you either want to clear err and replace it with
> your own message, or create a new message that looks like
> 
> Cannot update the ref '%s': %s
> 
> where the second "%s" is replaced with the error from log_ref_write().

Done, thanks.

> > @@ -3317,7 +3322,8 @@ static int commit_ref_update(struct ref_lock *lock,
> >   head_sha1, &head_flag);
> > if (head_ref && (head_flag & REF_ISSYMREF) &&
> > !strcmp(head_ref, lock->ref_name))
> > -   log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
> > +   log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg,
> > + err);
> 
> Here you don't check for errors from log_ref_write(). So it might or
> might not have written something to err. If it has, is that error
> handled correctly?

That was the case before this patch too. But I guess I might as well
check.

> Previously, the error generated here was "cannot update the ref '%s'."
> But now that you are letting write_ref_to_lockfile() fill err, the error
> message will be something from that function. This might be OK or it
> might not. If you think it is, it would be worth a word or two of
> justification in the commit message.

Will adjust commit message.

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v6 1/7] refs.c: add err arguments to reflog functions

2015-07-08 Thread Michael Haggerty
On 07/08/2015 12:41 AM, David Turner wrote:
> On Mon, 2015-07-06 at 17:53 +0200, Michael Haggerty wrote:
>> On 06/29/2015 10:17 PM, David Turner wrote:
>>> [...]
>>> @@ -3317,7 +3322,8 @@ static int commit_ref_update(struct ref_lock *lock,
>>>   head_sha1, &head_flag);
>>> if (head_ref && (head_flag & REF_ISSYMREF) &&
>>> !strcmp(head_ref, lock->ref_name))
>>> -   log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
>>> +   log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg,
>>> + err);
>>
>> Here you don't check for errors from log_ref_write(). So it might or
>> might not have written something to err. If it has, is that error
>> handled correctly?
> 
> That was the case before this patch too. But I guess I might as well
> check.

This isn't about fixing a pre-existing bug. Your patch introduced a
regression.

Before this patch, commit_ref_update() didn't really need to know
whether log_ref_write() failed, because it didn't take any further
remedial action anyway. log_ref_write() would have already written an
error message to stderr, and that was all the error handling that was
needed.

But now, log_ref_write() *doesn't* write its error message to stderr; it
only stores it to `err`. So now it is the caller's responsibility to
check whether there was an error, and if so write `err` to stderr.
Otherwise the error message will get lost entirely, I think.

I think your v7 of this patch goes too far, by turning a failure to
write to the reflog into a failure of the whole transaction. The problem
is that this failure comes too late, in the commit phase of the
transaction. Aborting at this late stage can leave some references
changed and others rolled back, violating the promise of atomicity.

The old code reported a failure to write the reflog to stderr but didn't
fail the transaction. I think that behavior is more appropriate. The
reflog is of lower importance than the references themselves. Junio, do
you agree?

If so, it might be that adding err arguments to the reflog functions
does not bring any benefits.

Michael

-- 
Michael Haggerty
mhag...@alum.mit.edu

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v6 1/7] refs.c: add err arguments to reflog functions

2015-07-08 Thread Junio C Hamano
Michael Haggerty  writes:

> I think your v7 of this patch goes too far, by turning a failure to
> write to the reflog into a failure of the whole transaction. The problem
> is that this failure comes too late, in the commit phase of the
> transaction. Aborting at this late stage can leave some references
> changed and others rolled back, violating the promise of atomicity.

Yeah, that sounds problematic.

> The old code reported a failure to write the reflog to stderr but didn't
> fail the transaction. I think that behavior is more appropriate. The
> reflog is of lower importance than the references themselves. Junio, do
> you agree?

That is actually a loaded question.

Do I agree that the current (i.e. before this change) behaviour is
more appropriate given the current choice of representation of refs
and reflogs on the filesystem, treating a failure to update reflog
as lower importance event and accept it as a limitation that it
cannot abort the whole transaction atomically?  Compared to leaving
the repository in a half-updated state where some refs and their
logs are updated already, other remaining proposed updates are
ignored, and the transaction claims to have failed even though some
things have already changed and we cannot rollback, I would say that
is a better compromise to treat reflog update as a lower importance.

Do I agree that reflog writing should stay to be best-effort in the
longer term?  Not really.  If we are moving the ref API in the
direction where we can plug a backend that is different from the
traditional filesystem based one for storing refs, I think the
backend should also be responsible for storing logs for the refs it
stores, and if the backend wants to promise atomicity, then we
should be able to fail the whole transaction when updates to refs
could proceed but updates to the log of one of these updated refs
cannot.  So I do not agree to cast in stone "the reflog is of lower
importance" as a general rule.

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v6 1/7] refs.c: add err arguments to reflog functions

2015-07-08 Thread Michael Haggerty
On 07/08/2015 07:11 PM, Junio C Hamano wrote:
> Michael Haggerty  writes:
> 
>> I think your v7 of this patch goes too far, by turning a failure to
>> write to the reflog into a failure of the whole transaction. The problem
>> is that this failure comes too late, in the commit phase of the
>> transaction. Aborting at this late stage can leave some references
>> changed and others rolled back, violating the promise of atomicity.
> 
> Yeah, that sounds problematic.
> 
>> The old code reported a failure to write the reflog to stderr but didn't
>> fail the transaction. I think that behavior is more appropriate. The
>> reflog is of lower importance than the references themselves. Junio, do
>> you agree?
> 
> That is actually a loaded question.
> 
> Do I agree that the current (i.e. before this change) behaviour is
> more appropriate given the current choice of representation of refs
> and reflogs on the filesystem, treating a failure to update reflog
> as lower importance event and accept it as a limitation that it
> cannot abort the whole transaction atomically?  Compared to leaving
> the repository in a half-updated state where some refs and their
> logs are updated already, other remaining proposed updates are
> ignored, and the transaction claims to have failed even though some
> things have already changed and we cannot rollback, I would say that
> is a better compromise to treat reflog update as a lower importance.
> 
> Do I agree that reflog writing should stay to be best-effort in the
> longer term?  Not really.  If we are moving the ref API in the
> direction where we can plug a backend that is different from the
> traditional filesystem based one for storing refs, I think the
> backend should also be responsible for storing logs for the refs it
> stores, and if the backend wants to promise atomicity, then we
> should be able to fail the whole transaction when updates to refs
> could proceed but updates to the log of one of these updated refs
> cannot.  So I do not agree to cast in stone "the reflog is of lower
> importance" as a general rule.

Junio,

You make a good distinction. I was describing a compromise that we have
to make now due to the limitations of the current ref/reflog backend.
But I agree 100% that a future storage backend that can do correct
rollback of refs *and* reflogs can fail a transaction if the reflog
updates cannot be committed.

Thanks,
Michael

-- 
Michael Haggerty
mhag...@alum.mit.edu

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html