* [PATCH 01/19] mv test: recreate mod/ directory instead of relying on stale copy
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
@ 2014-09-11 3:04 ` Jonathan Nieder
2014-09-11 3:04 ` [PATCH 02/19] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success Jonathan Nieder
` (21 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:04 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
The tests for 'git mv moves a submodule' functionality often run
commands like
git mv sub mod/sub
to move a submodule into a subdirectory. Just like plain /bin/mv,
this is supposed to succeed if the mod/ parent directory exists
and fail if it doesn't exist.
Usually these tests mkdir the parent directory beforehand, but some
instead rely on it being left behind by previous tests.
More precisely, when 'git reset --hard' tries to move to a state where
mod/sub is not present any more, it would perform the following
operations:
rmdir("mod/sub")
rmdir("mod")
The first fails with ENOENT because the test script removed mod/sub
with "rm -rf" already, so 'reset --hard' doesn't bother to move on to
the second, and the mod/ directory is kept around.
Better to explicitly remove and re-create the mod/ directory so later
tests don't have to depend on the directory left behind by the earlier
ones at all (making it easier to rearrange or skip some tests in the
file or to tweak 'reset --hard' behavior without breaking unrelated
tests).
Noticed while testing a patch that fixes the reset --hard behavior
described above.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
t/t7001-mv.sh | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 54d7807..69f11bd 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -350,10 +350,11 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
'
test_expect_success 'git mv moves a submodule with gitfile' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
(
cd mod &&
git mv ../sub/ .
@@ -372,11 +373,12 @@ test_expect_success 'git mv moves a submodule with gitfile' '
'
test_expect_success 'mv does not complain when no .gitmodules file is found' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git rm .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
git mv sub mod/sub 2>actual.err &&
! test -s actual.err &&
! test -e sub &&
@@ -390,11 +392,12 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
'
test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules foo.bar true &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
test_must_fail git mv sub mod/sub 2>actual.err &&
test -s actual.err &&
test -e sub &&
@@ -413,13 +416,14 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
'
test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules --remove-section submodule.sub &&
git add .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
+ mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_i18ncmp expect.err actual.err &&
! test -e sub &&
@@ -433,9 +437,10 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
'
test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
+ mkdir mod &&
git mv -n sub mod/sub 2>actual.err &&
test -f sub/.git &&
git diff-index --exit-code HEAD &&
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 02/19] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
2014-09-11 3:04 ` [PATCH 01/19] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
@ 2014-09-11 3:04 ` Jonathan Nieder
2014-09-11 3:05 ` [PATCH 03/19] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
` (20 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:04 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Simplify the function warn_if_unremovable slightly. Additionally, change
behaviour slightly. If we failed to remove the object because the object
does not exist, we can still return success back to the caller since none of
the callers depend on "fail if the file did not exist".
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
git-compat-util.h | 7 +++++--
wrapper.c | 14 ++++++--------
2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/git-compat-util.h b/git-compat-util.h
index b6f03b3..611e77b 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -706,11 +706,14 @@ void git_qsort(void *base, size_t nmemb, size_t size,
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
- * Always returns the return value of unlink(2).
+ * Returns 0 on success which includes trying to unlink an object that does
+ * not exist.
*/
int unlink_or_warn(const char *path);
/*
- * Likewise for rmdir(2).
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success which includes trying to remove a directory that does
+ * not exist.
*/
int rmdir_or_warn(const char *path);
/*
diff --git a/wrapper.c b/wrapper.c
index bc1bfb8..c9605cd 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -429,14 +429,12 @@ int xmkstemp_mode(char *template, int mode)
static int warn_if_unremovable(const char *op, const char *file, int rc)
{
- if (rc < 0) {
- int err = errno;
- if (ENOENT != err) {
- warning("unable to %s %s: %s",
- op, file, strerror(errno));
- errno = err;
- }
- }
+ int err;
+ if (!rc || errno == ENOENT)
+ return 0;
+ err = errno;
+ warning("unable to %s %s: %s", op, file, strerror(errno));
+ errno = err;
return rc;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 03/19] wrapper.c: add a new function unlink_or_msg
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
2014-09-11 3:04 ` [PATCH 01/19] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
2014-09-11 3:04 ` [PATCH 02/19] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success Jonathan Nieder
@ 2014-09-11 3:05 ` Jonathan Nieder
2014-09-11 3:06 ` [PATCH 04/19] refs.c: add an err argument to delete_ref_loose Jonathan Nieder
` (19 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:05 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 16 Jul 2014 11:20:36 -0700
This behaves like unlink_or_warn except that on failure it writes the message
to its 'err' argument, which the caller can display in an appropriate way or
ignore.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
git-compat-util.h | 9 +++++++++
wrapper.c | 14 ++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/git-compat-util.h b/git-compat-util.h
index 611e77b..5ee140c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -307,6 +307,8 @@ extern char *gitbasename(char *);
#include "wildmatch.h"
+struct strbuf;
+
/* General helper functions */
extern void vreportf(const char *prefix, const char *err, va_list params);
extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
@@ -710,6 +712,13 @@ void git_qsort(void *base, size_t nmemb, size_t size,
* not exist.
*/
int unlink_or_warn(const char *path);
+ /*
+ * Tries to unlink file. Returns 0 if unlink succeeded
+ * or the file already didn't exist. Returns -1 and
+ * appends a message to err suitable for
+ * 'error("%s", err->buf)' on error.
+ */
+int unlink_or_msg(const char *file, struct strbuf *err);
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
* Returns 0 on success which includes trying to remove a directory that does
diff --git a/wrapper.c b/wrapper.c
index c9605cd..ec7a08b 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -438,6 +438,20 @@ static int warn_if_unremovable(const char *op, const char *file, int rc)
return rc;
}
+int unlink_or_msg(const char *file, struct strbuf *err)
+{
+ int rc = unlink(file);
+
+ assert(err);
+
+ if (!rc || errno == ENOENT)
+ return 0;
+
+ strbuf_addf(err, "unable to unlink %s: %s",
+ file, strerror(errno));
+ return -1;
+}
+
int unlink_or_warn(const char *file)
{
return warn_if_unremovable("unlink", file, unlink(file));
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 04/19] refs.c: add an err argument to delete_ref_loose
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (2 preceding siblings ...)
2014-09-11 3:05 ` [PATCH 03/19] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
@ 2014-09-11 3:06 ` Jonathan Nieder
2014-09-11 3:06 ` [PATCH 05/19] refs.c: pass the ref log message to _create/delete/update instead of _commit Jonathan Nieder
` (18 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:06 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 15 May 2014 08:25:23 -0700
Add an err argument to delete_loose_ref so that we can pass a descriptive
error string back to the caller. Pass the err argument from transaction
commit to this function so that transaction users will have a nice error
string if the transaction failed due to delete_loose_ref.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/refs.c b/refs.c
index 7235574..5609622 100644
--- a/refs.c
+++ b/refs.c
@@ -2548,16 +2548,16 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
return ret;
}
-static int delete_ref_loose(struct ref_lock *lock, int flag)
+static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
{
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/* loose */
- int err, i = strlen(lock->lk->filename) - 5; /* .lock */
+ int res, i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
- err = unlink_or_warn(lock->lk->filename);
+ res = unlink_or_msg(lock->lk->filename, err);
lock->lk->filename[i] = '.';
- if (err && errno != ENOENT)
+ if (res)
return 1;
}
return 0;
@@ -3604,7 +3604,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- ret |= delete_ref_loose(update->lock, update->type);
+ ret |= delete_ref_loose(update->lock, update->type,
+ err);
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 05/19] refs.c: pass the ref log message to _create/delete/update instead of _commit
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (3 preceding siblings ...)
2014-09-11 3:06 ` [PATCH 04/19] refs.c: add an err argument to delete_ref_loose Jonathan Nieder
@ 2014-09-11 3:06 ` Jonathan Nieder
2014-09-11 3:06 ` [PATCH 06/19] rename_ref: don't ask read_ref_full where the ref came from Jonathan Nieder
` (17 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:06 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 30 Apr 2014 12:22:42 -0700
Change the reference transactions so that we pass the reflog message
through to the create/delete/update function instead of the commit message.
This allows for individual messages for each change in a multi ref
transaction.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
branch.c | 4 ++--
builtin/commit.c | 4 ++--
builtin/receive-pack.c | 5 +++--
builtin/replace.c | 5 +++--
builtin/tag.c | 4 ++--
builtin/update-ref.c | 13 +++++++------
fast-import.c | 8 ++++----
refs.c | 34 +++++++++++++++++++++-------------
refs.h | 8 ++++----
sequencer.c | 4 ++--
walker.c | 5 ++---
11 files changed, 52 insertions(+), 42 deletions(-)
diff --git a/branch.c b/branch.c
index 37ac555..76a8ec9 100644
--- a/branch.c
+++ b/branch.c
@@ -301,8 +301,8 @@ void create_branch(const char *head,
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, sha1,
- null_sha1, 0, !forcing, &err) ||
- ref_transaction_commit(transaction, msg, &err))
+ null_sha1, 0, !forcing, msg, &err) ||
+ ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
diff --git a/builtin/commit.c b/builtin/commit.c
index 9bf1003..d23e876 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1767,8 +1767,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
ref_transaction_update(transaction, "HEAD", sha1,
current_head
? current_head->object.sha1 : NULL,
- 0, !!current_head, &err) ||
- ref_transaction_commit(transaction, sb.buf, &err)) {
+ 0, !!current_head, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
rollback_index_files();
die("%s", err.buf);
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 224fadc..d1f4cf7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -585,8 +585,9 @@ static const char *update(struct command *cmd, struct shallow_info *si)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, namespaced_name,
- new_sha1, old_sha1, 0, 1, &err) ||
- ref_transaction_commit(transaction, "push", &err)) {
+ new_sha1, old_sha1, 0, 1, "push",
+ &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index 1fcd06d..9d03b84 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -169,8 +169,9 @@ static int replace_object_sha1(const char *object_ref,
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, ref, repl, prev, 0, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err))
+ ref_transaction_update(transaction, ref, repl, prev,
+ 0, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
diff --git a/builtin/tag.c b/builtin/tag.c
index f3f172f..70d28c5 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -705,8 +705,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, object, prev,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err))
+ 0, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
if (force && !is_null_sha1(prev) && hashcmp(prev, object))
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 54a48c0..6c9be05 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -14,6 +14,7 @@ static const char * const git_update_ref_usage[] = {
static char line_termination = '\n';
static int update_flags;
+static const char *msg;
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@@ -198,7 +199,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -229,7 +230,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
die("create %s: extra input: %s", refname, next);
if (ref_transaction_create(transaction, refname, new_sha1,
- update_flags, &err))
+ update_flags, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -264,7 +265,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
die("delete %s: extra input: %s", refname, next);
if (ref_transaction_delete(transaction, refname, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -300,7 +301,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
die("verify %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -354,7 +355,7 @@ static void update_refs_stdin(struct ref_transaction *transaction)
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname, *oldval, *msg = NULL;
+ const char *refname, *oldval;
unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
struct option options[] = {
@@ -385,7 +386,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (end_null)
line_termination = '\0';
update_refs_stdin(transaction);
- if (ref_transaction_commit(transaction, msg, &err))
+ if (ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
diff --git a/fast-import.c b/fast-import.c
index e7f6e37..51dfaad 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1708,8 +1708,8 @@ static int update_branch(struct branch *b)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, msg, &err)) {
+ 0, 1, msg, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&err);
@@ -1749,12 +1749,12 @@ static void dump_tags(void)
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
if (ref_transaction_update(transaction, ref_name.buf, t->sha1,
- NULL, 0, 0, &err)) {
+ NULL, 0, 0, msg, &err)) {
failure |= error("%s", err.buf);
goto cleanup;
}
}
- if (ref_transaction_commit(transaction, msg, &err))
+ if (ref_transaction_commit(transaction, &err))
failure |= error("%s", err.buf);
cleanup:
diff --git a/refs.c b/refs.c
index 5609622..99a9b86 100644
--- a/refs.c
+++ b/refs.c
@@ -2396,8 +2396,8 @@ static void prune_ref(struct ref_to_prune *r)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, r->name, r->sha1,
- REF_ISPRUNING, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err)) {
+ REF_ISPRUNING, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&err);
@@ -2571,8 +2571,8 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, refname, sha1, delopt,
- sha1 && !is_null_sha1(sha1), &err) ||
- ref_transaction_commit(transaction, NULL, &err)) {
+ sha1 && !is_null_sha1(sha1), NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
@@ -3350,6 +3350,7 @@ struct ref_update {
int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
struct ref_lock *lock;
int type;
+ char *msg;
const char refname[FLEX_ARRAY];
};
@@ -3392,9 +3393,10 @@ void ref_transaction_free(struct ref_transaction *transaction)
if (!transaction)
return;
- for (i = 0; i < transaction->nr; i++)
+ for (i = 0; i < transaction->nr; i++) {
+ free(transaction->updates[i]->msg);
free(transaction->updates[i]);
-
+ }
free(transaction->updates);
free(transaction);
}
@@ -3415,7 +3417,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
@@ -3432,13 +3434,15 @@ int ref_transaction_update(struct ref_transaction *transaction,
update->have_old = have_old;
if (have_old)
hashcpy(update->old_sha1, old_sha1);
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
- int flags,
+ int flags, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
@@ -3455,13 +3459,15 @@ int ref_transaction_create(struct ref_transaction *transaction,
hashclr(update->old_sha1);
update->flags = flags;
update->have_old = 1;
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
@@ -3479,6 +3485,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
assert(!is_null_sha1(old_sha1));
hashcpy(update->old_sha1, old_sha1);
}
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
@@ -3492,8 +3500,8 @@ int update_ref(const char *action, const char *refname,
t = ref_transaction_begin(&err);
if (!t ||
ref_transaction_update(t, refname, sha1, oldval, flags,
- !!oldval, &err) ||
- ref_transaction_commit(t, action, &err)) {
+ !!oldval, action, &err) ||
+ ref_transaction_commit(t, &err)) {
const char *str = "update_ref failed for ref '%s': %s";
ref_transaction_free(t);
@@ -3539,7 +3547,7 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
}
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err)
+ struct strbuf *err)
{
int ret = 0, delnum = 0, i;
const char **delnames;
@@ -3588,7 +3596,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (!is_null_sha1(update->new_sha1)) {
ret = write_ref_sha1(update->lock, update->new_sha1,
- msg);
+ update->msg);
update->lock = NULL; /* freed by write_ref_sha1 */
if (ret) {
if (err)
diff --git a/refs.h b/refs.h
index 69ef28c..7c1bf95 100644
--- a/refs.h
+++ b/refs.h
@@ -289,7 +289,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err);
/*
@@ -304,7 +304,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
- int flags,
+ int flags, const char *msg,
struct strbuf *err);
/*
@@ -318,7 +318,7 @@ int ref_transaction_create(struct ref_transaction *transaction,
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err);
/*
@@ -327,7 +327,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
* problem.
*/
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err);
+ struct strbuf *err);
/*
* Free an existing transaction and all associated data.
diff --git a/sequencer.c b/sequencer.c
index 5e93b6a..c5b7b8a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -286,8 +286,8 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn ? null_sha1 : from,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, sb.buf, &err)) {
+ 0, 1, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&sb);
diff --git a/walker.c b/walker.c
index b8a5441..3c6fb8a 100644
--- a/walker.c
+++ b/walker.c
@@ -298,14 +298,13 @@ int walker_fetch(struct walker *walker, int targets, char **target,
strbuf_addf(&refname, "refs/%s", write_ref[i]);
if (ref_transaction_update(transaction, refname.buf,
&sha1[20 * i], NULL, 0, 0,
+ msg ? msg : "fetch (unknown)",
&err)) {
error("%s", err.buf);
goto done;
}
}
- if (ref_transaction_commit(transaction,
- msg ? msg : "fetch (unknown)",
- &err)) {
+ if (ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
goto done;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 06/19] rename_ref: don't ask read_ref_full where the ref came from
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (4 preceding siblings ...)
2014-09-11 3:06 ` [PATCH 05/19] refs.c: pass the ref log message to _create/delete/update instead of _commit Jonathan Nieder
@ 2014-09-11 3:06 ` Jonathan Nieder
2014-09-11 3:07 ` [PATCH 07/19] refs.c: move the check for valid refname to lock_ref_sha1_basic Jonathan Nieder
` (16 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:06 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 30 Apr 2014 12:41:04 -0700
We call read_ref_full with a pointer to flags from rename_ref but since
we never actually use the returned flags we can just pass NULL here instead.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/refs.c b/refs.c
index 99a9b86..39571f5 100644
--- a/refs.c
+++ b/refs.c
@@ -2671,7 +2671,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+ if (!read_ref_full(newrefname, sha1, 1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 07/19] refs.c: move the check for valid refname to lock_ref_sha1_basic
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (5 preceding siblings ...)
2014-09-11 3:06 ` [PATCH 06/19] rename_ref: don't ask read_ref_full where the ref came from Jonathan Nieder
@ 2014-09-11 3:07 ` Jonathan Nieder
2014-09-11 3:07 ` [PATCH 08/19] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
` (15 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:07 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 10:40:10 -0700
Move the check for check_refname_format from lock_any_ref_for_update
to lock_ref_sha1_basic. At some later stage we will get rid of
lock_any_ref_for_update completely. This has no visible impact to callers,
except for inability to lock badly named refs which is not possible today
anyway but for other reasons.
Keep lock_any_ref_for_update as a no-op wrapper --- it is the public facing
version of this interface and keeping it as a separate function will make
it easier to experiment with the internal lock_ref_sha1_basic signature.
Note that if lock_ref_sha1_basic now checks the refname format and fails to
lock the ref it will not be possible to delete such a ref since deletion
implies we first lock the ref. In fact, we currently fail even earlier than
that since these refs are not even recognized to exist.
Example:
$ cp .git/refs/heads/master .git/refs/heads/echo...\*\*
$ ./git branch -D .git/refs/heads/echo...\*\*
error: branch '.git/refs/heads/echo...**' not found.
This is not a new regression and this has been broken for a while.
Later patches in the series will start repairing the handling of badly
named refs, at which time we will need to modify lock_ref_sha1_basic once more
in order to allow locking these refs for certain use cases such as rename
and delete.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/refs.c b/refs.c
index 39571f5..3c2ce57 100644
--- a/refs.c
+++ b/refs.c
@@ -2091,6 +2091,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int missing = 0;
int attempts_remaining = 3;
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
@@ -2182,8 +2187,6 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- return NULL;
return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 08/19] refs.c: call lock_ref_sha1_basic directly from commit
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (6 preceding siblings ...)
2014-09-11 3:07 ` [PATCH 07/19] refs.c: move the check for valid refname to lock_ref_sha1_basic Jonathan Nieder
@ 2014-09-11 3:07 ` Jonathan Nieder
2014-09-11 3:08 ` [PATCH 09/19] refs.c: pass a skip list to name_conflict_fn Jonathan Nieder
` (14 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:07 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 10:43:39 -0700
Skip using the lock_any_ref_for_update wrapper and call lock_ref_sha1_basic
directly from the commit function.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/refs.c b/refs.c
index 3c2ce57..f124c2b 100644
--- a/refs.c
+++ b/refs.c
@@ -3578,12 +3578,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
- update->lock = lock_any_ref_for_update(update->refname,
- (update->have_old ?
- update->old_sha1 :
- NULL),
- update->flags,
- &update->type);
+ update->lock = lock_ref_sha1_basic(update->refname,
+ (update->have_old ?
+ update->old_sha1 :
+ NULL),
+ update->flags,
+ &update->type);
if (!update->lock) {
if (err)
strbuf_addf(err, "Cannot lock the ref '%s'.",
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 09/19] refs.c: pass a skip list to name_conflict_fn
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (7 preceding siblings ...)
2014-09-11 3:07 ` [PATCH 08/19] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
@ 2014-09-11 3:08 ` Jonathan Nieder
2014-09-11 3:08 ` [PATCH 10/19] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
` (13 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:08 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 11:16:07 -0700
Allow passing a list of refs to skip checking to name_conflict_fn.
There are some conditions where we want to allow a temporary conflict and skip
checking those refs. For example if we have a transaction that
1, guarantees that m is a packed refs and there is no loose ref for m
2, the transaction will delete m from the packed ref
3, the transaction will create conflicting m/m
For this case we want to be able to lock and create m/m since we know that the
conflict is only transient. I.e. the conflict will be automatically resolved
by the transaction when it deletes m.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 41 ++++++++++++++++++++++++++++-------------
1 file changed, 28 insertions(+), 13 deletions(-)
diff --git a/refs.c b/refs.c
index f124c2b..b63ab2f 100644
--- a/refs.c
+++ b/refs.c
@@ -801,14 +801,16 @@ static int names_conflict(const char *refname1, const char *refname2)
struct name_conflict_cb {
const char *refname;
- const char *oldrefname;
const char *conflicting_refname;
+ struct string_list *skiplist;
};
static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
{
struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
- if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
+
+ if (data->skiplist &&
+ string_list_has_string(data->skiplist, entry->name))
return 0;
if (names_conflict(data->refname, entry->name)) {
data->conflicting_refname = entry->name;
@@ -822,15 +824,17 @@ static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
* conflicting with the name of an existing reference in dir. If
* oldrefname is non-NULL, ignore potential conflicts with oldrefname
* (e.g., because oldrefname is scheduled for deletion in the same
- * operation).
+ * operation). skiplist contains a list of refs we want to skip checking for
+ * conflicts with. skiplist must be sorted.
*/
-static int is_refname_available(const char *refname, const char *oldrefname,
- struct ref_dir *dir)
+static int is_refname_available(const char *refname,
+ struct ref_dir *dir,
+ struct string_list *skiplist)
{
struct name_conflict_cb data;
data.refname = refname;
- data.oldrefname = oldrefname;
data.conflicting_refname = NULL;
+ data.skiplist = skiplist;
sort_ref_dir(dir);
if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
@@ -2080,6 +2084,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
*/
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
+ struct string_list *skiplist,
int flags, int *type_p)
{
char *ref_file;
@@ -2129,7 +2134,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
* name is a proper prefix of our refname.
*/
if (missing &&
- !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
+ !is_refname_available(refname, get_packed_refs(&ref_cache),
+ skiplist)) {
last_errno = ENOTDIR;
goto error_return;
}
@@ -2187,7 +2193,7 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+ return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
}
/*
@@ -2648,6 +2654,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
struct stat loginfo;
int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
const char *symref = NULL;
+ struct string_list skiplist = STRING_LIST_INIT_NODUP;
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
@@ -2659,11 +2666,18 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (!symref)
return error("refname %s not found", oldrefname);
- if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
+ string_list_insert(&skiplist, oldrefname);
+ if (!is_refname_available(newrefname, get_packed_refs(&ref_cache),
+ &skiplist)) {
+ string_list_clear(&skiplist, 0);
return 1;
-
- if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
+ }
+ if (!is_refname_available(newrefname, get_loose_refs(&ref_cache),
+ &skiplist)) {
+ string_list_clear(&skiplist, 0);
return 1;
+ }
+ string_list_clear(&skiplist, 0);
if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
@@ -2692,7 +2706,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
logmoved = log;
- lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newrefname);
goto rollback;
@@ -2707,7 +2721,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
return 0;
rollback:
- lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for rollback", oldrefname);
goto rollbacklog;
@@ -3582,6 +3596,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
(update->have_old ?
update->old_sha1 :
NULL),
+ NULL,
update->flags,
&update->type);
if (!update->lock) {
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 10/19] refs.c: ref_transaction_commit: distinguish name conflicts from other errors
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (8 preceding siblings ...)
2014-09-11 3:08 ` [PATCH 09/19] refs.c: pass a skip list to name_conflict_fn Jonathan Nieder
@ 2014-09-11 3:08 ` Jonathan Nieder
2014-09-11 3:08 ` [PATCH 11/19] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
` (12 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:08 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Fri, 16 May 2014 14:14:38 -0700
In _commit, ENOTDIR can happen in the call to lock_ref_sha1_basic, either when
we lstat the new refname and it returns ENOTDIR or if the name checking
function reports that the same type of conflict happened. In both cases it
means that we can not create the new ref due to a name conflict.
Start defining specific return codes for _commit: assign -1 as a generic
error and UPDATE_REFS_NAME_CONFLICT (-2) as the error that refers to a name
conflict.
When "git fetch" is creating refs, name conflicts differ from other errors in
that they are likely to be resolved by running "git remote prune <remote>".
"git fetch" currently inspects errno to decide whether to give that advice.
Once it switches to the transaction API, it can check for
UPDATE_REFS_NAME_CONFLICT instead.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 18 ++++++++++++------
refs.h | 5 +++++
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/refs.c b/refs.c
index b63ab2f..86c708a 100644
--- a/refs.c
+++ b/refs.c
@@ -3584,9 +3584,10 @@ int ref_transaction_commit(struct ref_transaction *transaction,
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
- ret = ref_update_reject_duplicates(updates, n, err);
- if (ret)
+ if (ref_update_reject_duplicates(updates, n, err)) {
+ ret = -1;
goto cleanup;
+ }
/* Acquire all locks while verifying old values */
for (i = 0; i < n; i++) {
@@ -3600,10 +3601,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
update->flags,
&update->type);
if (!update->lock) {
+ int df_conflict = (errno == ENOTDIR);
+
if (err)
strbuf_addf(err, "Cannot lock the ref '%s'.",
update->refname);
- ret = 1;
+ ret = df_conflict ? UPDATE_REFS_NAME_CONFLICT : -1;
goto cleanup;
}
}
@@ -3620,6 +3623,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (err)
strbuf_addf(err, "Cannot update the ref '%s'.",
update->refname);
+ ret = -1;
goto cleanup;
}
}
@@ -3630,14 +3634,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- ret |= delete_ref_loose(update->lock, update->type,
- err);
+ if (delete_ref_loose(update->lock, update->type, err))
+ ret = -1;
+
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
}
- ret |= repack_without_refs(delnames, delnum, err);
+ if (repack_without_refs(delnames, delnum, err))
+ ret = -1;
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
diff --git a/refs.h b/refs.h
index 7c1bf95..e14aa31 100644
--- a/refs.h
+++ b/refs.h
@@ -325,7 +325,12 @@ int ref_transaction_delete(struct ref_transaction *transaction,
* Commit all of the changes that have been queued in transaction, as
* atomically as possible. Return a nonzero value if there is a
* problem.
+ *
+ * Function returns 0 on success, -1 for generic failures and
+ * UPDATE_REFS_NAME_CONFLICT (-2) if the failure was due to a naming conflict.
+ * For example, the ref names A and A/B conflict.
*/
+#define UPDATE_REFS_NAME_CONFLICT -2
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 11/19] fetch.c: change s_update_ref to use a ref transaction
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (9 preceding siblings ...)
2014-09-11 3:08 ` [PATCH 10/19] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
@ 2014-09-11 3:08 ` Jonathan Nieder
2014-09-11 3:08 ` [PATCH 12/19] refs.c: make write_ref_sha1 static Jonathan Nieder
` (11 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:08 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Mon, 28 Apr 2014 13:49:07 -0700
Change s_update_ref to use a ref transaction for the ref update.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/fetch.c | 34 ++++++++++++++++++++++++----------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 55f457c..2e3bc73 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -375,23 +375,37 @@ static int s_update_ref(const char *action,
{
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
- static struct ref_lock *lock;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+ int ret, df_conflict = 0;
if (dry_run)
return 0;
if (!rla)
rla = default_rla.buf;
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
- lock = lock_any_ref_for_update(ref->name,
- check_old ? ref->old_sha1 : NULL,
- 0, NULL);
- if (!lock)
- return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
- STORE_REF_ERROR_OTHER;
- if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
- return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
- STORE_REF_ERROR_OTHER;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref->name, ref->new_sha1,
+ ref->old_sha1, 0, check_old, msg, &err))
+ goto fail;
+
+ ret = ref_transaction_commit(transaction, &err);
+ if (ret == UPDATE_REFS_NAME_CONFLICT)
+ df_conflict = 1;
+ if (ret)
+ goto fail;
+
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
return 0;
+fail:
+ ref_transaction_free(transaction);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
+ : STORE_REF_ERROR_OTHER;
}
#define REFCOL_WIDTH 10
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 12/19] refs.c: make write_ref_sha1 static
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (10 preceding siblings ...)
2014-09-11 3:08 ` [PATCH 11/19] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
@ 2014-09-11 3:08 ` Jonathan Nieder
2014-09-11 3:09 ` [PATCH 13/19] refs.c: change resolve_ref_unsafe reading argument to be a flags field Jonathan Nieder
` (10 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:08 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Mon, 28 Apr 2014 15:36:58 -0700
No external users call write_ref_sha1 any more so lets declare it static.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 10 ++++++++--
refs.h | 3 ---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/refs.c b/refs.c
index 86c708a..c2dab4a 100644
--- a/refs.c
+++ b/refs.c
@@ -2646,6 +2646,9 @@ static int rename_tmp_log(const char *newrefname)
return 0;
}
+static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
+ const char *logmsg);
+
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
unsigned char sha1[20], orig_sha1[20];
@@ -2901,8 +2904,11 @@ static int is_branch(const char *refname)
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
-/* This function must return a meaningful errno */
-int write_ref_sha1(struct ref_lock *lock,
+/*
+ * Writes sha1 into the ref specified by the lock. Makes sure that errno
+ * is sane on error.
+ */
+static int write_ref_sha1(struct ref_lock *lock,
const unsigned char *sha1, const char *logmsg)
{
static char term = '\n';
diff --git a/refs.h b/refs.h
index e14aa31..fafa493 100644
--- a/refs.h
+++ b/refs.h
@@ -195,9 +195,6 @@ extern int commit_ref(struct ref_lock *lock);
/** Release any lock taken but not written. **/
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);
-
/*
* Setup reflog before using. Set errno to something meaningful on failure.
*/
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 13/19] refs.c: change resolve_ref_unsafe reading argument to be a flags field
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (11 preceding siblings ...)
2014-09-11 3:08 ` [PATCH 12/19] refs.c: make write_ref_sha1 static Jonathan Nieder
@ 2014-09-11 3:09 ` Jonathan Nieder
2014-09-11 3:10 ` [PATCH 14/19] branch -d: avoid repeated symref resolution Jonathan Nieder
` (9 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:09 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Tue, 15 Jul 2014 12:59:36 -0700
resolve_ref_unsafe takes a boolean argument for reading.
Change this to be a flags field instead and pass the new constant
RESOLVE_REF_READING when we want this behaviour.
Swap two of the arguments in the function to make sure that we capture any
instances where callers have not been updated/aware of the new api so that
they can be audited.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
branch.c | 2 +-
builtin/blame.c | 2 +-
builtin/branch.c | 9 +++---
builtin/checkout.c | 6 ++--
builtin/clone.c | 2 +-
builtin/commit.c | 2 +-
builtin/fmt-merge-msg.c | 2 +-
builtin/for-each-ref.c | 6 ++--
builtin/fsck.c | 2 +-
builtin/log.c | 3 +-
builtin/merge.c | 2 +-
builtin/notes.c | 2 +-
builtin/receive-pack.c | 4 +--
builtin/remote.c | 5 ++--
builtin/show-branch.c | 6 ++--
builtin/symbolic-ref.c | 2 +-
bundle.c | 2 +-
cache.h | 21 ++++++-------
http-backend.c | 3 +-
notes-merge.c | 2 +-
reflog-walk.c | 5 ++--
refs.c | 79 ++++++++++++++++++++++++++++---------------------
remote.c | 10 ++++---
sequencer.c | 4 +--
transport-helper.c | 4 ++-
transport.c | 5 ++--
upload-pack.c | 2 +-
wt-status.c | 2 +-
28 files changed, 111 insertions(+), 85 deletions(-)
diff --git a/branch.c b/branch.c
index 76a8ec9..ba3e1c1 100644
--- a/branch.c
+++ b/branch.c
@@ -186,7 +186,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", sha1, NULL, 0);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die(_("Cannot force update the current branch."));
}
diff --git a/builtin/blame.c b/builtin/blame.c
index a52a279..b8bec0a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2292,7 +2292,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
commit->object.type = OBJ_COMMIT;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", head_sha1, NULL, RESOLVE_REF_READING))
die("no such ref: HEAD");
parent_tail = append_parent(parent_tail, head_sha1);
diff --git a/builtin/branch.c b/builtin/branch.c
index 652b1d2..f144808 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -129,7 +129,8 @@ static int branch_merged(int kind, const char *name,
branch->merge[0] &&
branch->merge[0]->dst &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+ resolve_refdup(branch->merge[0]->dst, sha1,
+ NULL, RESOLVE_REF_READING)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
if (!reference_rev)
@@ -233,7 +234,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, sha1, 0, &flags);
+ target = resolve_ref_unsafe(name, sha1, &flags, 0);
if (!target ||
(!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
error(remote_branch
@@ -296,7 +297,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst, *cp;
- dst = resolve_ref_unsafe(src, sha1, 0, &flag);
+ dst = resolve_ref_unsafe(src, sha1, &flag, 0);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix && (cp = skip_prefix(dst, prefix)))
@@ -862,7 +863,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", head_sha1, 0, NULL);
+ head = resolve_refdup("HEAD", head_sha1, NULL, 0);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD")) {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1dc56e..64af1f7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -356,7 +356,7 @@ static int checkout_paths(const struct checkout_opts *opts,
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- read_ref_full("HEAD", rev, 0, &flag);
+ read_ref_full("HEAD", rev, &flag, 0);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -771,7 +771,7 @@ static int switch_branches(const struct checkout_opts *opts,
unsigned char rev[20];
int flag, writeout_error = 0;
memset(&old, 0, sizeof(old));
- old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
+ old.path = path_to_free = resolve_refdup("HEAD", rev, &flag, 0);
old.commit = lookup_commit_reference_gently(rev, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -1068,7 +1068,7 @@ static int checkout_branch(struct checkout_opts *opts,
unsigned char rev[20];
int flag;
- if (!read_ref_full("HEAD", rev, 0, &flag) &&
+ if (!read_ref_full("HEAD", rev, &flag, 0) &&
(flag & REF_ISSYMREF) && is_null_sha1(rev))
return switch_unborn_to_new_branch(opts);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index b12989d..12a78e1 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -622,7 +622,7 @@ static int checkout(void)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", sha1, 1, NULL);
+ head = resolve_refdup("HEAD", sha1, NULL, RESOLVE_REF_READING);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout.\n"));
diff --git a/builtin/commit.c b/builtin/commit.c
index d23e876..3536330 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1468,7 +1468,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", junk_sha1, NULL, 0);
printf("[%s%s ",
starts_with(head, "refs/heads/") ?
head + 11 :
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 3906eda..b2355ad 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -602,7 +602,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* get current branch */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", head_sha1, 1, NULL);
+ resolve_refdup("HEAD", head_sha1, NULL, RESOLVE_REF_READING);
if (!current_branch)
die("No current branch");
if (starts_with(current_branch, "refs/heads/"))
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 3e1d5c3..e193073 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -649,7 +649,8 @@ static void populate_value(struct refinfo *ref)
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+ ref->symref = resolve_refdup(ref->refname, unused1,
+ NULL, RESOLVE_REF_READING);
if (!ref->symref)
ref->symref = "";
}
@@ -707,7 +708,8 @@ static void populate_value(struct refinfo *ref)
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ head = resolve_ref_unsafe("HEAD", sha1,
+ NULL, RESOLVE_REF_READING);
if (!strcmp(ref->refname, head))
v->s = "*";
else
diff --git a/builtin/fsck.c b/builtin/fsck.c
index fc150c8..506e32b 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -560,7 +560,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", head_sha1, &flag, 0);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
diff --git a/builtin/log.c b/builtin/log.c
index a7ba211..230a9ef 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1395,7 +1395,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
const char *ref;
- ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ ref = resolve_ref_unsafe("HEAD", sha1, NULL,
+ RESOLVE_REF_READING);
if (ref && starts_with(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
diff --git a/builtin/merge.c b/builtin/merge.c
index 428ca24..d262279 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1108,7 +1108,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, &flag, 0);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
diff --git a/builtin/notes.c b/builtin/notes.c
index 820c341..16df78c 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -703,7 +703,7 @@ static int merge_commit(struct notes_merge_options *o)
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
+ resolve_refdup("NOTES_MERGE_REF", sha1, NULL, 0);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d1f4cf7..555a4a6 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -656,7 +656,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, sha1, &flag, 0);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -817,7 +817,7 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
+ head_name = head_name_to_free = resolve_refdup("HEAD", sha1, NULL, 0);
checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 401feb3..6eaeee7 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -568,7 +568,8 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(refname, orig_sha1, &flag,
+ RESOLVE_REF_READING);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
@@ -704,7 +705,7 @@ static int mv(int argc, const char **argv)
int flag = 0;
unsigned char sha1[20];
- read_ref_full(item->string, sha1, 1, &flag);
+ read_ref_full(item->string, sha1, &flag, RESOLVE_REF_READING);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index d873172..ef6ea52 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -727,7 +727,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
+ fake_av[0] = resolve_refdup("HEAD", sha1, NULL,
+ RESOLVE_REF_READING);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -789,7 +790,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
+ head_p = resolve_ref_unsafe("HEAD", head_sha1, NULL,
+ RESOLVE_REF_READING);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index b6a711d..1dd04af 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -13,7 +13,7 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
{
unsigned char sha1[20];
int flag;
- const char *refname = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
+ const char *refname = resolve_ref_unsafe(HEAD, sha1, &flag, 0);
if (!refname)
die("No such ref: %s", HEAD);
diff --git a/bundle.c b/bundle.c
index 1222952..32dd2f7 100644
--- a/bundle.c
+++ b/bundle.c
@@ -311,7 +311,7 @@ int create_bundle(struct bundle_header *header, const char *path,
continue;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
- if (read_ref_full(e->name, sha1, 1, &flag))
+ if (read_ref_full(e->name, sha1, &flag, RESOLVE_REF_READING))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
diff --git a/cache.h b/cache.h
index e7ec626..2d3c5ed 100644
--- a/cache.h
+++ b/cache.h
@@ -948,7 +948,7 @@ extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref_full(const char *refname, unsigned char *sha1,
- int reading, int *flags);
+ int *flags, int resolve_flags);
extern int read_ref(const char *refname, unsigned char *sha1);
/*
@@ -960,20 +960,20 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* or the input ref.
*
* If the reference cannot be resolved to an object, the behavior
- * depends on the "reading" argument:
+ * depends on the RESOLVE_REF_READING flag:
*
- * - If reading is set, return NULL.
+ * - If RESOLVE_REF_READING is set, return NULL.
*
- * - If reading is not set, clear sha1 and return the name of the last
- * reference name in the chain, which will either be a non-symbolic
+ * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
+ * the last reference name in the chain, which will either be a non-symbolic
* reference or an undefined reference. If this is a prelude to
* "writing" to the ref, the return value is the name of the ref
* that will actually be created or changed.
*
- * If flag is non-NULL, set the value that it points to the
+ * If flags is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
- * packed references) and REF_ISSYMREF (if the initial reference was a
- * symbolic reference).
+ * packed references), REF_ISSYMREF (if the initial reference was a
+ * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
*
* If ref is not a properly-formatted, normalized reference, return
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
@@ -981,8 +981,9 @@ extern int read_ref(const char *refname, unsigned char *sha1);
*
* errno is set to something meaningful on error.
*/
-extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
-extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
+#define RESOLVE_REF_READING 0x01
+extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/http-backend.c b/http-backend.c
index d2c0a62..8f94f9b 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -417,7 +417,8 @@ static int show_head_ref(const char *refname, const unsigned char *sha1,
if (flag & REF_ISSYMREF) {
unsigned char unused[20];
- const char *target = resolve_ref_unsafe(refname, unused, 1, NULL);
+ const char *target = resolve_ref_unsafe(refname, unused,
+ NULL, RESOLVE_REF_READING);
const char *target_nons = strip_namespace(target);
strbuf_addf(buf, "ref: %s\n", target_nons);
diff --git a/notes-merge.c b/notes-merge.c
index 94a1a8a..ffca602 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -549,7 +549,7 @@ int notes_merge(struct notes_merge_options *o,
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
+ if (read_ref_full(o->local_ref, local_sha1, NULL, 0))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
is_null_sha1(local_sha1))
diff --git a/reflog-walk.c b/reflog-walk.c
index 9ce8b53..feeaf0a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -48,7 +48,8 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
unsigned char sha1[20];
const char *name;
void *name_to_free;
- name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
+ name = name_to_free = resolve_refdup(ref, sha1, NULL,
+ RESOLVE_REF_READING);
if (name) {
for_each_reflog_ent(name, read_one_reflog, reflogs);
free(name_to_free);
@@ -174,7 +175,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
if (*branch == '\0') {
unsigned char sha1[20];
free(branch);
- branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ branch = resolve_refdup("HEAD", sha1, NULL, 0);
if (!branch)
die ("No current branch");
diff --git a/refs.c b/refs.c
index c2dab4a..a5f9734 100644
--- a/refs.c
+++ b/refs.c
@@ -1192,7 +1192,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- } else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
+ } else if (read_ref_full(refname.buf, sha1, &flag,
+ RESOLVE_REF_READING)) {
hashclr(sha1);
flag |= REF_ISBROKEN;
}
@@ -1344,21 +1345,20 @@ static const char *handle_missing_loose_ref(const char *refname,
}
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
+const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *flags, int resolve_flags)
{
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char refname_buffer[256];
- if (flag)
- *flag = 0;
+ if (flags)
+ *flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
errno = EINVAL;
return NULL;
}
-
for (;;) {
char path[PATH_MAX];
struct stat st;
@@ -1385,7 +1385,8 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (lstat(path, &st) < 0) {
if (errno == ENOENT)
return handle_missing_loose_ref(refname, sha1,
- reading, flag);
+ resolve_flags & RESOLVE_REF_READING,
+ flags);
else
return NULL;
}
@@ -1405,8 +1406,8 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
!check_refname_format(buffer, 0)) {
strcpy(refname_buffer, buffer);
refname = refname_buffer;
- if (flag)
- *flag |= REF_ISSYMREF;
+ if (flags)
+ *flags |= REF_ISSYMREF;
continue;
}
}
@@ -1451,21 +1452,21 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
*/
if (get_sha1_hex(buffer, sha1) ||
(buffer[40] != '\0' && !isspace(buffer[40]))) {
- if (flag)
- *flag |= REF_ISBROKEN;
+ if (flags)
+ *flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
return refname;
}
- if (flag)
- *flag |= REF_ISSYMREF;
+ if (flags)
+ *flags |= REF_ISSYMREF;
buf = buffer + 4;
while (isspace(*buf))
buf++;
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
- if (flag)
- *flag |= REF_ISBROKEN;
+ if (flags)
+ *flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
@@ -1473,9 +1474,9 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
}
}
-char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+char *resolve_refdup(const char *ref, unsigned char *sha1, int *flags, int resolve_flags)
{
- const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
+ const char *ret = resolve_ref_unsafe(ref, sha1, flags, resolve_flags);
return ret ? xstrdup(ret) : NULL;
}
@@ -1486,22 +1487,22 @@ struct ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)
+int read_ref_full(const char *refname, unsigned char *sha1, int *flags, int resolve_flags)
{
- if (resolve_ref_unsafe(refname, sha1, reading, flags))
+ if (resolve_ref_unsafe(refname, sha1, flags, resolve_flags))
return 0;
return -1;
}
int read_ref(const char *refname, unsigned char *sha1)
{
- return read_ref_full(refname, sha1, 1, NULL);
+ return read_ref_full(refname, sha1, NULL, RESOLVE_REF_READING);
}
int ref_exists(const char *refname)
{
unsigned char sha1[20];
- return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
+ return !!resolve_ref_unsafe(refname, sha1, NULL, RESOLVE_REF_READING);
}
static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
@@ -1615,7 +1616,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
return 0;
}
- if (read_ref_full(refname, base, 1, &flag))
+ if (read_ref_full(refname, base, &flag, RESOLVE_REF_READING))
return -1;
/*
@@ -1656,7 +1657,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
+ resolves_to = resolve_ref_unsafe(refname, junk, NULL, 0);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
@@ -1781,7 +1782,7 @@ static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
return 0;
}
- if (!read_ref_full("HEAD", sha1, 1, &flag))
+ if (!read_ref_full("HEAD", sha1, &flag, RESOLVE_REF_READING))
return fn("HEAD", sha1, flag, cb_data);
return 0;
@@ -1861,7 +1862,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, sha1, 1, &flag))
+ if (!read_ref_full(buf.buf, sha1, &flag, RESOLVE_REF_READING))
ret = fn(buf.buf, sha1, flag, cb_data);
strbuf_release(&buf);
@@ -1956,7 +1957,8 @@ int refname_match(const char *abbrev_name, const char *full_name)
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ if (read_ref_full(lock->ref_name, lock->old_sha1, NULL,
+ mustexist ? RESOLVE_REF_READING : 0)) {
int save_errno = errno;
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
@@ -2029,7 +2031,8 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
this_result = refs_found ? sha1_from_ref : sha1;
mksnpath(fullref, sizeof(fullref), *p, len, str);
- r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
+ r = resolve_ref_unsafe(fullref, this_result, &flag,
+ RESOLVE_REF_READING);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -2058,7 +2061,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
const char *ref, *it;
mksnpath(path, sizeof(path), *p, len, str);
- ref = resolve_ref_unsafe(path, hash, 1, NULL);
+ ref = resolve_ref_unsafe(path, hash, NULL, RESOLVE_REF_READING);
if (!ref)
continue;
if (reflog_exists(path))
@@ -2093,6 +2096,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int last_errno = 0;
int type, lflags;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
+ int resolve_flags = 0;
int missing = 0;
int attempts_remaining = 3;
@@ -2104,7 +2108,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- refname = resolve_ref_unsafe(refname, lock->old_sha1, mustexist, &type);
+ if (mustexist)
+ resolve_flags |= RESOLVE_REF_READING;
+
+ refname = resolve_ref_unsafe(refname, lock->old_sha1, &type,
+ resolve_flags);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@@ -2117,7 +2125,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
error("there are still refs under '%s'", orig_refname);
goto error_return;
}
- refname = resolve_ref_unsafe(orig_refname, lock->old_sha1, mustexist, &type);
+ refname = resolve_ref_unsafe(orig_refname, lock->old_sha1,
+ &type, resolve_flags);
}
if (type_p)
*type_p = type;
@@ -2470,7 +2479,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
unsigned char sha1[20];
int flags;
- if (read_ref_full(entry->name, sha1, 0, &flags))
+ if (read_ref_full(entry->name, sha1, &flags, 0))
/* We should at least have found the packed ref. */
die("Internal error");
if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
@@ -2662,7 +2671,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
- symref = resolve_ref_unsafe(oldrefname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(oldrefname, orig_sha1, &flag,
+ RESOLVE_REF_READING);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
oldrefname);
@@ -2691,7 +2701,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, 1, NULL) &&
+ if (!read_ref_full(newrefname, sha1, NULL, RESOLVE_REF_READING) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
@@ -2969,7 +2979,8 @@ static int write_ref_sha1(struct ref_lock *lock,
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
- head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
+ head_ref = resolve_ref_unsafe("HEAD", head_sha1, &head_flag,
+ RESOLVE_REF_READING);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -3336,7 +3347,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
retval = do_for_each_reflog(name, fn, cb_data);
} else {
unsigned char sha1[20];
- if (read_ref_full(name->buf, sha1, 0, NULL))
+ if (read_ref_full(name->buf, sha1, NULL, 0))
retval = error("bad ref for %s", name->buf);
else
retval = fn(name->buf, sha1, 0, cb_data);
diff --git a/remote.c b/remote.c
index 0e9459c..67c375d 100644
--- a/remote.c
+++ b/remote.c
@@ -486,7 +486,7 @@ static void read_config(void)
return;
default_remote_name = "origin";
current_branch = NULL;
- head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
+ head_ref = resolve_ref_unsafe("HEAD", sha1, &flag, 0);
if (head_ref && (flag & REF_ISSYMREF) &&
starts_with(head_ref, "refs/heads/")) {
current_branch =
@@ -1121,7 +1121,8 @@ static char *guess_ref(const char *name, struct ref *peer)
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
+ const char *r = resolve_ref_unsafe(peer->name, sha1, NULL,
+ RESOLVE_REF_READING);
if (!r)
return NULL;
@@ -1182,7 +1183,8 @@ static int match_explicit(struct ref *src, struct ref *dst,
unsigned char sha1[20];
int flag;
- dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
+ dst_value = resolve_ref_unsafe(matched_src->name, sha1, &flag,
+ RESOLVE_REF_READING);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
@@ -1656,7 +1658,7 @@ static int ignore_symref_update(const char *refname)
unsigned char sha1[20];
int flag;
- if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
+ if (!resolve_ref_unsafe(refname, sha1, &flag, 0))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
}
diff --git a/sequencer.c b/sequencer.c
index c5b7b8a..6a05ad4 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -366,7 +366,7 @@ static int is_index_unchanged(void)
unsigned char head_sha1[20];
struct commit *head_commit;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", head_sha1, NULL, RESOLVE_REF_READING))
return error(_("Could not resolve HEAD commit\n"));
head_commit = lookup_commit(head_sha1);
@@ -912,7 +912,7 @@ static int rollback_single_pick(void)
if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
!file_exists(git_path("REVERT_HEAD")))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", head_sha1, 0, NULL))
+ if (read_ref_full("HEAD", head_sha1, NULL, 0))
return error(_("cannot resolve HEAD"));
if (is_null_sha1(head_sha1))
return error(_("cannot abort from a branch yet to be born"));
diff --git a/transport-helper.c b/transport-helper.c
index 84c616f..8365441 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -889,7 +889,9 @@ static int push_refs_with_export(struct transport *transport,
int flag;
/* Follow symbolic refs (mainly for HEAD). */
- name = resolve_ref_unsafe(ref->peer_ref->name, sha1, 1, &flag);
+ name = resolve_ref_unsafe(
+ ref->peer_ref->name, sha1,
+ &flag, RESOLVE_REF_READING);
if (!name || !(flag & REF_ISSYMREF))
name = ref->peer_ref->name;
diff --git a/transport.c b/transport.c
index 325f03e..3ba7bbf 100644
--- a/transport.c
+++ b/transport.c
@@ -168,7 +168,8 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
+ tmp = resolve_ref_unsafe(localname, sha, &flag,
+ RESOLVE_REF_READING);
if (tmp && flag & REF_ISSYMREF &&
starts_with(tmp, "refs/heads/"))
localname = tmp;
@@ -753,7 +754,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
unsigned char head_sha1[20];
char *head;
- head = resolve_refdup("HEAD", head_sha1, 1, NULL);
+ head = resolve_refdup("HEAD", head_sha1, NULL, RESOLVE_REF_READING);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
diff --git a/upload-pack.c b/upload-pack.c
index 01de944..3b51ccb 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -743,7 +743,7 @@ static int find_symref(const char *refname, const unsigned char *sha1, int flag,
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, unused, 0, &flag);
+ symref_target = resolve_ref_unsafe(refname, unused, &flag, 0);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
diff --git a/wt-status.c b/wt-status.c
index 318a191..6819e2b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -128,7 +128,7 @@ void wt_status_prepare(struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ s->branch = resolve_refdup("HEAD", sha1, NULL, 0);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 14/19] branch -d: avoid repeated symref resolution
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (12 preceding siblings ...)
2014-09-11 3:09 ` [PATCH 13/19] refs.c: change resolve_ref_unsafe reading argument to be a flags field Jonathan Nieder
@ 2014-09-11 3:10 ` Jonathan Nieder
2014-09-11 3:10 ` [PATCH 15/19] refs.c: fix handling of badly named refs Jonathan Nieder
` (8 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:10 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
If a repository gets in a broken state with too much symref nesting,
it cannot be repaired with "git branch -d":
$ git symbolic-ref refs/heads/nonsense refs/heads/nonsense
$ git branch -d nonsense
error: branch 'nonsense' not found.
Worse, "git update-ref --no-deref -d" doesn't work for such repairs
either:
$ git update-ref -d refs/heads/nonsense
error: unable to resolve reference refs/heads/nonsense: Too many levels of symbolic links
Fix both by teaching resolve_ref_unsafe a new RESOLVE_REF_NODEREF flag
and passing it when appropriate.
Callers can still read the value of a symref (for example to print a
message about it) with that flag set --- resolve_ref_unsafe will
resolve one level of symrefs and stop there.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/branch.c | 3 ++-
cache.h | 1 +
refs.c | 10 ++++++++++
t/t1400-update-ref.sh | 16 ++++++++++++++++
t/t3200-branch.sh | 9 +++++++++
5 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index f144808..7925660 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -234,7 +234,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, sha1, &flags, 0);
+ target = resolve_ref_unsafe(name, sha1,
+ &flags, RESOLVE_REF_NODEREF);
if (!target ||
(!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
error(remote_branch
diff --git a/cache.h b/cache.h
index 2d3c5ed..03ade12 100644
--- a/cache.h
+++ b/cache.h
@@ -982,6 +982,7 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* errno is set to something meaningful on error.
*/
#define RESOLVE_REF_READING 0x01
+#define RESOLVE_REF_NODEREF 0x02
extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
extern char *resolve_refdup(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
diff --git a/refs.c b/refs.c
index a5f9734..f11df33 100644
--- a/refs.c
+++ b/refs.c
@@ -1408,6 +1408,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
refname = refname_buffer;
if (flags)
*flags |= REF_ISSYMREF;
+ if (resolve_flags & RESOLVE_REF_NODEREF) {
+ hashclr(sha1);
+ return refname;
+ }
continue;
}
}
@@ -1471,6 +1475,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
return NULL;
}
refname = strcpy(refname_buffer, buf);
+ if (resolve_flags & RESOLVE_REF_NODEREF) {
+ hashclr(sha1);
+ return refname;
+ }
}
}
@@ -2110,6 +2118,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
+ if (flags & REF_NODEREF)
+ resolve_flags |= RESOLVE_REF_NODEREF;
refname = resolve_ref_unsafe(refname, lock->old_sha1, &type,
resolve_flags);
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 0218e96..ff4607b 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -110,6 +110,22 @@ test_expect_success "delete symref without dereference when the referred ref is
cp -f .git/HEAD.orig .git/HEAD
git update-ref -d $m
+test_expect_success 'update-ref -d is not confused by self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ test_must_fail git update-ref -d refs/heads/self &&
+ test_path_is_file .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ git update-ref --no-deref -d refs/heads/self &&
+ test_path_is_missing .git/refs/heads/self
+'
+
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ac31b71..432921b 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -285,6 +285,15 @@ test_expect_success 'deleting a dangling symref' '
test_i18ncmp expect actual
'
+test_expect_success 'deleting a self-referential symref' '
+ git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
+ test_path_is_file .git/refs/heads/self-reference &&
+ echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
+ git branch -d self-reference >actual &&
+ test_path_is_missing .git/refs/heads/self-reference &&
+ test_i18ncmp expect actual
+'
+
test_expect_success 'renaming a symref is not allowed' '
git symbolic-ref refs/heads/master2 refs/heads/master &&
test_must_fail git branch -m master2 master3 &&
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 15/19] refs.c: fix handling of badly named refs
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (13 preceding siblings ...)
2014-09-11 3:10 ` [PATCH 14/19] branch -d: avoid repeated symref resolution Jonathan Nieder
@ 2014-09-11 3:10 ` Jonathan Nieder
2014-09-11 3:11 ` [PATCH 16/19] for-each-ref.c: improve message before aborting on broken ref Jonathan Nieder
` (7 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:10 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 3 Sep 2014 11:45:43 -0700
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Currently we can not really recover from a badly named ref with less than
manually deleting the .git/refs/heads/<refname> file. This will also help
if change the naming rules in the future. For example if we decide to remove
`, " and such from the set of valid characters.
The purpose of this change is to allow git branch --list to show these refs
and to allow git branch -d/-D and update-ref -d to delete them.
Other functions will continue to not handle these refs but can be changed in
later patches.
Introduce two new internal flags: RESOLVE_REF_ALLOW_BAD_NAME and REF_BADNAMEOK.
In resolving functions, refuse to resolve badly named refs unless the new
RESOLVE_REF_ALLOW_BAD_NAME flag is passed. For these cases, if the badly named
ref exists then flag it as REF_ISBROKEN and resolve it to nullsha1.
In locking functions, refuse to act on badly named refs unless the new
REF_BADNAMEOK flag is passed. This is currently used only from branch.c and
update-ref.c when deleting a ref.
Change the internal functions that read loose and packed refs to allow
badly named refs but flag them as REF_ISBROKEN just like unresolvable refs
are.
During ref iteration only include these refs during for_each_rawref but
none of the other iterators. I.e. just like unresolvable refs, flag
inclusion of these refs based on DO_FOR_EACH_INCLUDE_BROKEN during
iteration.
In the transaction API, always refuse to create or update badly named refs
and refuse to delete them unless REF_BADNAMEOK flag is passed.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/branch.c | 13 ++++++----
builtin/update-ref.c | 3 ++-
cache.h | 8 +++++-
refs.c | 63 ++++++++++++++++++++++++++++++++++++++-------
refs.h | 2 ++
t/t1402-check-ref-format.sh | 48 ++++++++++++++++++++++++++++++++++
6 files changed, 121 insertions(+), 16 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index 7925660..5d5bc56 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -234,10 +234,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, sha1,
- &flags, RESOLVE_REF_NODEREF);
+ target = resolve_ref_unsafe(name, sha1, &flags,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NODEREF
+ | RESOLVE_REF_ALLOW_BAD_NAME);
if (!target ||
- (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
+ (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
+ is_null_sha1(sha1))) {
error(remote_branch
? _("remote branch '%s' not found.")
: _("branch '%s' not found."), bname.buf);
@@ -245,14 +248,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
continue;
}
- if (!(flags & REF_ISSYMREF) &&
+ if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
force)) {
ret = 1;
continue;
}
- if (delete_ref(name, sha1, REF_NODEREF)) {
+ if (delete_ref(name, sha1, REF_NODEREF|REF_BADNAMEOK)) {
error(remote_branch
? _("Error deleting remote branch '%s'")
: _("Error deleting branch '%s'"),
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6c9be05..e379fdd 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -419,7 +419,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (no_deref)
flags = REF_NODEREF;
if (delete)
- return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
+ return delete_ref(refname, oldval ? oldsha1 : NULL,
+ flags|REF_BADNAMEOK);
else
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
flags, UPDATE_REFS_DIE_ON_ERR);
diff --git a/cache.h b/cache.h
index 03ade12..03a6144 100644
--- a/cache.h
+++ b/cache.h
@@ -979,10 +979,16 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL.
*
- * errno is set to something meaningful on error.
+ * RESOLVE_REF_ALLOW_BAD_NAME disables most of the ref name checking except
+ * for names that are absolute paths or contain ".." components. For both
+ * these cases the function will return NULL and set errno to EINVAL.
+ * If the name is bad then the function will set the REF_ISBROKEN flag and
+ * return the name, if the ref exists, or NULL, if it does not.
+ * When this flag is set, any badly named refs will resolve to nullsha1.
*/
#define RESOLVE_REF_READING 0x01
#define RESOLVE_REF_NODEREF 0x02
+#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
extern char *resolve_refdup(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
diff --git a/refs.c b/refs.c
index f11df33..6faf0c8 100644
--- a/refs.c
+++ b/refs.c
@@ -1060,7 +1060,13 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
refname = parse_ref_line(refline, sha1);
if (refname) {
- last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
+ int flag = REF_ISPACKED;
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT)) {
+ flag |= REF_ISBROKEN;
+ hashclr(sha1);
+ }
+ last = create_ref_entry(refname, sha1, flag, 0);
if (peeled == PEELED_FULLY ||
(peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
last->flag |= REF_KNOWS_PEELED;
@@ -1197,8 +1203,14 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
hashclr(sha1);
flag |= REF_ISBROKEN;
}
+ if (check_refname_format(refname.buf,
+ REFNAME_ALLOW_ONELEVEL|
+ REFNAME_DOT_COMPONENT)) {
+ hashclr(sha1);
+ flag |= REF_ISBROKEN;
+ }
add_entry_to_dir(dir,
- create_ref_entry(refname.buf, sha1, flag, 1));
+ create_ref_entry(refname.buf, sha1, flag, 0));
}
strbuf_setlen(&refname, dirnamelen);
}
@@ -1344,6 +1356,18 @@ static const char *handle_missing_loose_ref(const char *refname,
}
}
+static int escapes_cwd(const char *path) {
+ char *buf;
+ int result;
+
+ if (is_absolute_path(path))
+ return 1;
+ buf = xmalloc(strlen(path) + 1);
+ result = normalize_path_copy(buf, path);
+ free(buf);
+ return result;
+}
+
/* This function needs to return a meaningful errno on failure */
const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *flags, int resolve_flags)
{
@@ -1351,13 +1375,21 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
ssize_t len;
char buffer[256];
static char refname_buffer[256];
+ int bad_name = 0;
if (flags)
*flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- errno = EINVAL;
- return NULL;
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ escapes_cwd(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ bad_name = 1;
}
for (;;) {
char path[PATH_MAX];
@@ -1461,6 +1493,8 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
errno = EINVAL;
return NULL;
}
+ if (bad_name)
+ hashclr(sha1);
return refname;
}
if (flags)
@@ -2108,14 +2142,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int missing = 0;
int attempts_remaining = 3;
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- errno = EINVAL;
- return NULL;
- }
-
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
+ if (flags & REF_BADNAMEOK)
+ resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
+
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
if (flags & REF_NODEREF)
@@ -3472,6 +3504,13 @@ int ref_transaction_update(struct ref_transaction *transaction,
if (have_old && !old_sha1)
die("BUG: have_old is true but old_sha1 is NULL");
+ if (!is_null_sha1(new_sha1) &&
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ strbuf_addf(err, "refusing to update ref with bad name %s",
+ refname);
+ return -1;
+ }
+
update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
update->flags = flags;
@@ -3497,6 +3536,12 @@ int ref_transaction_create(struct ref_transaction *transaction,
if (!new_sha1 || is_null_sha1(new_sha1))
die("BUG: create ref with null new_sha1");
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ strbuf_addf(err, "refusing to create ref with bad name %s",
+ refname);
+ return -1;
+ }
+
update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
diff --git a/refs.h b/refs.h
index fafa493..a96e174 100644
--- a/refs.h
+++ b/refs.h
@@ -175,10 +175,12 @@ extern int peel_ref(const char *refname, unsigned char *sha1);
* ref_transaction_create(), etc.
* REF_NODEREF: act on the ref directly, instead of dereferencing
* symbolic references.
+ * REF_BADNAMEOK: allow locking a ref that has a bad name.
*
* Flags >= 0x100 are reserved for internal use.
*/
#define REF_NODEREF 0x01
+#define REF_BADNAMEOK 0x02
/*
* This function sets errno to something meaningful on failure.
*/
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index 1a5a5f3..058fa37 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -196,4 +196,52 @@ invalid_ref_normalized 'heads///foo.lock'
invalid_ref_normalized 'foo.lock/bar'
invalid_ref_normalized 'foo.lock///bar'
+test_expect_success 'git branch shows badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch >output &&
+ grep -e "broken...ref" output
+'
+
+test_expect_success 'git branch -d can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -d broken...ref &&
+ git branch >output &&
+ ! grep -e "broken...ref" output
+'
+
+test_expect_success 'git branch -D can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -D broken...ref &&
+ git branch >output &&
+ ! grep -e "broken...ref" output
+'
+
+test_expect_success 'git update-ref -d can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git update-ref -d refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken...ref" output
+'
+
+test_expect_success 'git branch can not create a badly named ref' '
+ test_must_fail git branch broken...ref
+'
+
+test_expect_success 'git branch can not rename to a bad ref name' '
+ git branch goodref &&
+ test_must_fail git branch -m goodref broken...ref
+'
+
+test_expect_failure 'git branch can rename from a bad ref name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -m broken...ref renamed &&
+ test_must_fail git rev-parse broken...ref &&
+ test_cmp_rev master renamed
+'
+
test_done
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 16/19] for-each-ref.c: improve message before aborting on broken ref
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (14 preceding siblings ...)
2014-09-11 3:10 ` [PATCH 15/19] refs.c: fix handling of badly named refs Jonathan Nieder
@ 2014-09-11 3:11 ` Jonathan Nieder
2014-09-11 3:11 ` [PATCH 17/19] refs.c: do not permit err == NULL Jonathan Nieder
` (6 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:11 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Fri, 5 Sep 2014 14:35:17 -0700
Print a warning message for any badly named refs we find in the repo.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/for-each-ref.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index e193073..090390c 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -853,6 +853,12 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
struct refinfo *ref;
int cnt;
+ if ((flag & REF_ISBROKEN) &&
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ warning("ignoring ref with broken name %s", refname);
+ return 0;
+ }
+
if (*cb->grab_pattern) {
const char **pattern;
int namelen = strlen(refname);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 17/19] refs.c: do not permit err == NULL
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (15 preceding siblings ...)
2014-09-11 3:11 ` [PATCH 16/19] for-each-ref.c: improve message before aborting on broken ref Jonathan Nieder
@ 2014-09-11 3:11 ` Jonathan Nieder
2014-09-11 3:12 ` [PATCH 18/19] lockfile: remove unable_to_lock_error Jonathan Nieder
` (5 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:11 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Some functions that take a strbuf argument to append an error to treat
!err as an indication that the message should be suppressed (e.g.,
ref_update_reject_duplicates). Others write the message to stderr on
!err (e.g., repack_without_refs). Others crash (e.g.,
ref_transaction_update).
Some of these behaviors are for historical reasons and others were
accidents. Luckily no callers pass err == NULL any more. Simplify
by consistently requiring the strbuf argument.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
refs.c | 46 +++++++++++++++++++++++++++-------------------
1 file changed, 27 insertions(+), 19 deletions(-)
diff --git a/refs.c b/refs.c
index 6faf0c8..550223c 100644
--- a/refs.c
+++ b/refs.c
@@ -2560,6 +2560,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
struct string_list_item *ref_to_delete;
int i, ret, removed = 0;
+ assert(err);
+
/* Look for a packed ref */
for (i = 0; i < n; i++)
if (get_packed_ref(refnames[i]))
@@ -2570,13 +2572,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
return 0; /* no refname exists in packed refs */
if (lock_packed_refs(0)) {
- if (err) {
- unable_to_lock_message(git_path("packed-refs"), errno,
- err);
- return -1;
- }
- unable_to_lock_error(git_path("packed-refs"), errno);
- return error("cannot delete '%s' from packed refs", refnames[i]);
+ unable_to_lock_message(git_path("packed-refs"), errno, err);
+ return -1;
}
packed = get_packed_refs(&ref_cache);
@@ -2602,7 +2599,7 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
/* Write what remains */
ret = commit_packed_refs();
- if (ret && err)
+ if (ret)
strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
strerror(errno));
return ret;
@@ -2610,6 +2607,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
{
+ assert(err);
+
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/* loose */
int res, i = strlen(lock->lk->filename) - 5; /* .lock */
@@ -3459,6 +3458,8 @@ struct ref_transaction {
struct ref_transaction *ref_transaction_begin(struct strbuf *err)
{
+ assert(err);
+
return xcalloc(1, sizeof(struct ref_transaction));
}
@@ -3498,6 +3499,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
{
struct ref_update *update;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: update called for transaction that is not open");
@@ -3530,6 +3533,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
{
struct ref_update *update;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: create called for transaction that is not open");
@@ -3561,6 +3566,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
{
struct ref_update *update;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: delete called for transaction that is not open");
@@ -3623,13 +3630,14 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
struct strbuf *err)
{
int i;
+
+ assert(err);
+
for (i = 1; i < n; i++)
if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
- const char *str =
- "Multiple updates for ref '%s' not allowed.";
- if (err)
- strbuf_addf(err, str, updates[i]->refname);
-
+ strbuf_addf(err,
+ "Multiple updates for ref '%s' not allowed.",
+ updates[i]->refname);
return 1;
}
return 0;
@@ -3643,6 +3651,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
int n = transaction->nr;
struct ref_update **updates = transaction->updates;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: commit called for transaction that is not open");
@@ -3675,9 +3685,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (!update->lock) {
int df_conflict = (errno == ENOTDIR);
- if (err)
- strbuf_addf(err, "Cannot lock the ref '%s'.",
- update->refname);
+ strbuf_addf(err, "Cannot lock the ref '%s'.",
+ update->refname);
ret = df_conflict ? UPDATE_REFS_NAME_CONFLICT : -1;
goto cleanup;
}
@@ -3692,9 +3701,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
update->msg);
update->lock = NULL; /* freed by write_ref_sha1 */
if (ret) {
- if (err)
- strbuf_addf(err, "Cannot update the ref '%s'.",
- update->refname);
+ strbuf_addf(err, "Cannot update the ref '%s'.",
+ update->refname);
ret = -1;
goto cleanup;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 18/19] lockfile: remove unable_to_lock_error
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (16 preceding siblings ...)
2014-09-11 3:11 ` [PATCH 17/19] refs.c: do not permit err == NULL Jonathan Nieder
@ 2014-09-11 3:12 ` Jonathan Nieder
2014-09-11 3:12 ` [PATCH 19/19] ref_transaction_commit: bail out on failure to remove a ref Jonathan Nieder
` (4 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:12 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
The former caller uses unable_to_lock_message now.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
cache.h | 1 -
lockfile.c | 10 ----------
2 files changed, 11 deletions(-)
diff --git a/cache.h b/cache.h
index 03a6144..995729f 100644
--- a/cache.h
+++ b/cache.h
@@ -558,7 +558,6 @@ struct lock_file {
};
#define LOCK_DIE_ON_ERROR 1
#define LOCK_NODEREF 2
-extern int unable_to_lock_error(const char *path, int err);
extern void unable_to_lock_message(const char *path, int err,
struct strbuf *buf);
extern NORETURN void unable_to_lock_index_die(const char *path, int err);
diff --git a/lockfile.c b/lockfile.c
index a921d77..dbd4101 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -176,16 +176,6 @@ void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
absolute_path(path), strerror(err));
}
-int unable_to_lock_error(const char *path, int err)
-{
- struct strbuf buf = STRBUF_INIT;
-
- unable_to_lock_message(path, err, &buf);
- error("%s", buf.buf);
- strbuf_release(&buf);
- return -1;
-}
-
NORETURN void unable_to_lock_index_die(const char *path, int err)
{
struct strbuf buf = STRBUF_INIT;
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 19/19] ref_transaction_commit: bail out on failure to remove a ref
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (17 preceding siblings ...)
2014-09-11 3:12 ` [PATCH 18/19] lockfile: remove unable_to_lock_error Jonathan Nieder
@ 2014-09-11 3:12 ` Jonathan Nieder
2014-09-11 21:40 ` [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview) Junio C Hamano
` (3 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-11 3:12 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
When removal of a loose or packed ref fails, bail out instead of
trying to finish the transaction. This way, a single error message
can be printed (instead of multiple messages being concatenated by
mistake) and the operator can try to solve the underlying problem
before there is a chance to muck things up even more.
In particular, when git fails to remove a ref, git goes on to try to
delete the reflog. Exiting early lets us keep the reflog.
When git succeeds in deleting a ref A and fails to remove a ref B, it
goes on to try to delete both reflogs. It would be better to just
remove the reflog for A, but that would be a more invasive change.
Failing early means we keep both reflogs, which puts the operator in a
good position to understand the problem and recover.
A long term goal is to avoid these problems altogether and roll back
the transaction on failure. That kind of transactionality will have
to wait for a later series (the plan for which is to make all
destructive work happen in a single update of the packed-refs file).
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
Thanks for reading.
refs.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/refs.c b/refs.c
index 550223c..3b27758 100644
--- a/refs.c
+++ b/refs.c
@@ -3714,16 +3714,20 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- if (delete_ref_loose(update->lock, update->type, err))
+ if (delete_ref_loose(update->lock, update->type, err)) {
ret = -1;
+ goto cleanup;
+ }
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
}
- if (repack_without_refs(delnames, delnum, err))
+ if (repack_without_refs(delnames, delnum, err)) {
ret = -1;
+ goto cleanup;
+ }
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (18 preceding siblings ...)
2014-09-11 3:12 ` [PATCH 19/19] ref_transaction_commit: bail out on failure to remove a ref Jonathan Nieder
@ 2014-09-11 21:40 ` Junio C Hamano
2014-09-11 22:20 ` Junio C Hamano
` (2 subsequent siblings)
22 siblings, 0 replies; 130+ messages in thread
From: Junio C Hamano @ 2014-09-11 21:40 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> Jonathan Nieder wrote:
>
>> The next series from Ronnie's collection is available at
>> https://code-review.googlesource.com/#/q/topic:ref-transaction in case
>> someone wants a fresh series to look at.
>
> Here is the outcome of that review. It could use another set of eyes
> (hint, hint) but should be mostly ready. Interdiff below. This is meant
> to replace rs/ref-transaction in 'pu' and I'd prefer to wait a little
> for more comments before merging to 'next'.
Alright. I'd assume that all the other rs/ref-transaction* topics
that depends on rs/ref-transaction series will be rerolled on top of
this new round when ready.
Thanks.
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (19 preceding siblings ...)
2014-09-11 21:40 ` [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview) Junio C Hamano
@ 2014-09-11 22:20 ` Junio C Hamano
2014-09-12 0:47 ` Jonathan Nieder
2014-09-25 21:35 ` Junio C Hamano
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
22 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-09-11 22:20 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> These patches are also available from the git repository at
>
> git://repo.or.cz/git/jrn.git tags/rs/ref-transaction
The tag fetched and built as-is seems to break 5514 among other
things ("git remote rm" segfaults).
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-11 22:20 ` Junio C Hamano
@ 2014-09-12 0:47 ` Jonathan Nieder
2014-09-12 19:00 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-12 0:47 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Junio C Hamano wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
>> These patches are also available from the git repository at
>>
>> git://repo.or.cz/git/jrn.git tags/rs/ref-transaction
>
> The tag fetched and built as-is seems to break 5514 among other
> things ("git remote rm" segfaults).
Yeah, I noticed that right after sending the series out. :/
The patch below fixes it[1].
-- >8 --
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 11 Sep 2014 08:42:57 -0700
Subject: remote rm/prune: print a message when writing packed-refs fails
Until v2.1.0-rc0~22^2~11 (refs.c: add an err argument to
repack_without_refs, 2014-06-20), repack_without_refs forgot to
provide an error message when commit_packed_refs fails. Even today,
it only provides a message for callers that pass a non-NULL err
parameter. Internal callers in refs.c pass non-NULL err but
"git remote" does not.
That means that "git remote rm" and "git remote prune" can fail
without printing a message about why. Fix them by passing in a
non-NULL err parameter and printing the returned message.
This is the last caller to a ref handling function passing err ==
NULL. A later patch can drop support for err == NULL, avoiding such
problems in the future.
Change-Id: Ifb8a726ef03d0aa282a25a102313064d2e8ec283
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
[1] https://code-review.googlesource.com/1110
https://code-review.googlesource.com/1060
builtin/remote.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/builtin/remote.c b/builtin/remote.c
index 6eaeee7..ef1ffc3 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -750,13 +750,16 @@ static int mv(int argc, const char **argv)
static int remove_branches(struct string_list *branches)
{
+ struct strbuf err = STRBUF_INIT;
const char **branch_names;
int i, result = 0;
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string;
- result |= repack_without_refs(branch_names, branches->nr, NULL);
+ if (repack_without_refs(branch_names, branches->nr, &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
free(branch_names);
for (i = 0; i < branches->nr; i++) {
@@ -1333,9 +1336,13 @@ static int prune_remote(const char *remote, int dry_run)
delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util;
- if (!dry_run)
- result |= repack_without_refs(delete_refs,
- states.stale.nr, NULL);
+ if (!dry_run) {
+ struct strbuf err = STRBUF_INIT;
+ if (repack_without_refs(delete_refs, states.stale.nr,
+ &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
+ }
free(delete_refs);
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 0:47 ` Jonathan Nieder
@ 2014-09-12 19:00 ` Junio C Hamano
2014-09-12 19:18 ` Jonathan Nieder
0 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-09-12 19:00 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> Junio C Hamano wrote:
>> Jonathan Nieder <jrnieder@gmail.com> writes:
>
>>> These patches are also available from the git repository at
>>>
>>> git://repo.or.cz/git/jrn.git tags/rs/ref-transaction
>>
>> The tag fetched and built as-is seems to break 5514 among other
>> things ("git remote rm" segfaults).
>
> Yeah, I noticed that right after sending the series out. :/
>
> The patch below fixes it[1].
Is this meant to replace anything, or is it "Oops, the previous ones
are broken, and this is to patch it up on top"?
> -- >8 --
> From: Ronnie Sahlberg <sahlberg@google.com>
> Date: Thu, 11 Sep 2014 08:42:57 -0700
> Subject: remote rm/prune: print a message when writing packed-refs fails
>
> Until v2.1.0-rc0~22^2~11 (refs.c: add an err argument to
> repack_without_refs, 2014-06-20), repack_without_refs forgot to
> provide an error message when commit_packed_refs fails. Even today,
> it only provides a message for callers that pass a non-NULL err
> parameter. Internal callers in refs.c pass non-NULL err but
> "git remote" does not.
>
> That means that "git remote rm" and "git remote prune" can fail
> without printing a message about why. Fix them by passing in a
> non-NULL err parameter and printing the returned message.
>
> This is the last caller to a ref handling function passing err ==
> NULL. A later patch can drop support for err == NULL, avoiding such
> problems in the future.
>
> Change-Id: Ifb8a726ef03d0aa282a25a102313064d2e8ec283
> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
> ---
> [1] https://code-review.googlesource.com/1110
> https://code-review.googlesource.com/1060
>
> builtin/remote.c | 15 +++++++++++----
> 1 file changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/builtin/remote.c b/builtin/remote.c
> index 6eaeee7..ef1ffc3 100644
> --- a/builtin/remote.c
> +++ b/builtin/remote.c
> @@ -750,13 +750,16 @@ static int mv(int argc, const char **argv)
>
> static int remove_branches(struct string_list *branches)
> {
> + struct strbuf err = STRBUF_INIT;
> const char **branch_names;
> int i, result = 0;
>
> branch_names = xmalloc(branches->nr * sizeof(*branch_names));
> for (i = 0; i < branches->nr; i++)
> branch_names[i] = branches->items[i].string;
> - result |= repack_without_refs(branch_names, branches->nr, NULL);
> + if (repack_without_refs(branch_names, branches->nr, &err))
> + result |= error("%s", err.buf);
> + strbuf_release(&err);
> free(branch_names);
>
> for (i = 0; i < branches->nr; i++) {
> @@ -1333,9 +1336,13 @@ static int prune_remote(const char *remote, int dry_run)
> delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
> for (i = 0; i < states.stale.nr; i++)
> delete_refs[i] = states.stale.items[i].util;
> - if (!dry_run)
> - result |= repack_without_refs(delete_refs,
> - states.stale.nr, NULL);
> + if (!dry_run) {
> + struct strbuf err = STRBUF_INIT;
> + if (repack_without_refs(delete_refs, states.stale.nr,
> + &err))
> + result |= error("%s", err.buf);
> + strbuf_release(&err);
> + }
> free(delete_refs);
> }
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 19:00 ` Junio C Hamano
@ 2014-09-12 19:18 ` Jonathan Nieder
2014-09-12 19:56 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-12 19:18 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Junio C Hamano wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
> > Junio C Hamano wrote:
>>> The tag fetched and built as-is seems to break 5514 among other
>>> things ("git remote rm" segfaults).
>>
>> Yeah, I noticed that right after sending the series out. :/
>>
>> The patch below fixes it[1].
>
> Is this meant to replace anything, or is it "Oops, the previous ones
> are broken, and this is to patch it up on top"?
It's "Oops, the next one (refs.c: do not permit err == NULL) is broken,
and this is to patch it up in advance". :)
But it should apply on top, too.
There were a few other minor changes, and I think with them the series
should be ready for "next". My "send and hope that more reviewers
appear" strategy didn't really work, so I'll send a reroll of the
series as-is in an hour or so.
Here's an interdiff as a preview.
diff --git a/builtin/branch.c b/builtin/branch.c
index 5d5bc56..4bf931e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -238,9 +238,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
RESOLVE_REF_READING
| RESOLVE_REF_NODEREF
| RESOLVE_REF_ALLOW_BAD_NAME);
- if (!target ||
- (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
- is_null_sha1(sha1))) {
+ if (!target) {
error(remote_branch
? _("remote branch '%s' not found.")
: _("branch '%s' not found."), bname.buf);
@@ -268,8 +266,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
? _("Deleted remote branch %s (was %s).\n")
: _("Deleted branch %s (was %s).\n"),
bname.buf,
- (flags & REF_ISSYMREF)
- ? target
+ (flags & REF_ISBROKEN) ? "broken"
+ : (flags & REF_ISSYMREF) ? target
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
diff --git a/builtin/remote.c b/builtin/remote.c
index 6eaeee7..ef1ffc3 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -750,13 +750,16 @@ static int mv(int argc, const char **argv)
static int remove_branches(struct string_list *branches)
{
+ struct strbuf err = STRBUF_INIT;
const char **branch_names;
int i, result = 0;
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string;
- result |= repack_without_refs(branch_names, branches->nr, NULL);
+ if (repack_without_refs(branch_names, branches->nr, &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
free(branch_names);
for (i = 0; i < branches->nr; i++) {
@@ -1333,9 +1336,13 @@ static int prune_remote(const char *remote, int dry_run)
delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util;
- if (!dry_run)
- result |= repack_without_refs(delete_refs,
- states.stale.nr, NULL);
+ if (!dry_run) {
+ struct strbuf err = STRBUF_INIT;
+ if (repack_without_refs(delete_refs, states.stale.nr,
+ &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
+ }
free(delete_refs);
}
^ permalink raw reply related [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 19:18 ` Jonathan Nieder
@ 2014-09-12 19:56 ` Junio C Hamano
2014-09-12 20:47 ` Junio C Hamano
2014-09-12 21:52 ` Michael Haggerty
0 siblings, 2 replies; 130+ messages in thread
From: Junio C Hamano @ 2014-09-12 19:56 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> It's "Oops, the next one (refs.c: do not permit err == NULL) is broken,
> and this is to patch it up in advance". :)
>
> But it should apply on top, too.
I think "in advance" makes sense for this one, too.
> There were a few other minor changes, and I think with them the series
> should be ready for "next". My "send and hope that more reviewers
> appear" strategy didn't really work,...
You sent them just a few days ago. Expecting anybody to have a
solid time to sit thru a 19-patch series inside that timeframe is
not so realistic.
> so I'll send a reroll of the series as-is in an hour or so.
OK. Thanks.
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 19:56 ` Junio C Hamano
@ 2014-09-12 20:47 ` Junio C Hamano
2014-09-13 17:52 ` Junio C Hamano
2014-09-12 21:52 ` Michael Haggerty
1 sibling, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-09-12 20:47 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Junio C Hamano <gitster@pobox.com> writes:
> Jonathan Nieder <jrnieder@gmail.com> writes:
>
>> It's "Oops, the next one (refs.c: do not permit err == NULL) is broken,
>> and this is to patch it up in advance". :)
>>
>> But it should apply on top, too.
>
> I think "in advance" makes sense for this one, too.
>
>> There were a few other minor changes, and I think with them the series
>> should be ready for "next". My "send and hope that more reviewers
>> appear" strategy didn't really work,...
>
> You sent them just a few days ago. Expecting anybody to have a
> solid time to sit thru a 19-patch series inside that timeframe is
> not so realistic.
>
>> so I'll send a reroll of the series as-is in an hour or so.
>
> OK. Thanks.
I do not think I have enough time to pick a reroll up to redo the
day's integration, so please take time to make it perfect.
I noticed that with this series merged to the version I use daily,
detaching HEAD (i.e. "git checkout HEAD^0") breaks my HEAD reflog,
which made me redo the integration ejecting the series out of 'pu'.
Not happy, as my workflow relies fairly heavily on detached HEAD
X-<.
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 20:47 ` Junio C Hamano
@ 2014-09-13 17:52 ` Junio C Hamano
0 siblings, 0 replies; 130+ messages in thread
From: Junio C Hamano @ 2014-09-13 17:52 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Junio C Hamano <gitster@pobox.com> writes:
> I noticed that with this series merged to the version I use daily,
> detaching HEAD (i.e. "git checkout HEAD^0") breaks my HEAD reflog,
> which made me redo the integration ejecting the series out of 'pu'.
>
> Not happy, as my workflow relies fairly heavily on detached HEAD
> X-<.
Just FYI.
Bisecting the series using the attached as a test script points
"branch -d: avoid repeated symref resolution" as a possible culprit.
Perhaps these tests may want to be added to t3200 which is touched
by the said commit (or add them earlier in the series)?
-- >8 --
#!/bin/sh
test_description='reflog not nuked with co HEAD^0'
. ./test-lib.sh
check_reflog () {
while read name
do
git rev-parse --verify "$name"
done >expect &&
if test -f "$2"
then
while read object rest
do
git rev-parse --verify "$object"
done >>expect <"$2"
fi &&
while read object rest
do
git rev-parse --verify "$object"
done >actual <"$1" &&
test_cmp expect actual
}
test_expect_success setup '
test_tick &&
git commit --allow-empty -m initial &&
git branch side &&
test_tick &&
git commit --allow-empty -m second &&
git log -g --oneline >baseline &&
check_reflog baseline <<-\EOF
master
master^
EOF
'
test_expect_success 'switch to branch' '
git checkout side &&
git log -g --oneline >switched &&
check_reflog switched baseline <<-\EOF
side
EOF
'
test_expect_success 'detach to other' '
git checkout master^0 &&
git log -g --oneline >detach-1 &&
check_reflog detach-1 switched <<-\EOF
master
EOF
'
test_expect_success 'attach to self' '
git checkout master &&
git log -g --oneline >detach-2 &&
check_reflog detach-2 detach-1 <<-\EOF
master
EOF
'
test_expect_success 'detach to self' '
git checkout master^0 &&
git log -g --oneline >detach-3 &&
check_reflog detach-3 detach-2 <<-\EOF
master
EOF
'
test_expect_success 'attach to other' '
git checkout HEAD^0 &&
git checkout side &&
git log -g --oneline >detach-4 &&
check_reflog detach-4 detach-3 <<-\EOF
side
EOF
'
test_done
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 19:56 ` Junio C Hamano
2014-09-12 20:47 ` Junio C Hamano
@ 2014-09-12 21:52 ` Michael Haggerty
2014-09-12 23:57 ` Jonathan Nieder
1 sibling, 1 reply; 130+ messages in thread
From: Michael Haggerty @ 2014-09-12 21:52 UTC (permalink / raw)
To: Junio C Hamano, Jonathan Nieder; +Cc: Ronnie Sahlberg, git
On 09/12/2014 09:56 PM, Junio C Hamano wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
> [...]
>> There were a few other minor changes, and I think with them the series
>> should be ready for "next". My "send and hope that more reviewers
>> appear" strategy didn't really work,...
>
> You sent them just a few days ago. Expecting anybody to have a
> solid time to sit thru a 19-patch series inside that timeframe is
> not so realistic.
It's hard to tell from my glacial (tectonic?) pace, but I really do plan
to work through all of Ronnie's ref-related patches. Of course it's up
to you whether to wait for me. I really hope to get through the third
series by the end of next week.
>> so I'll send a reroll of the series as-is in an hour or so.
Jonathan: Is a current version of this patch series set up for review in
Gerrit?
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 21:52 ` Michael Haggerty
@ 2014-09-12 23:57 ` Jonathan Nieder
2014-09-17 13:23 ` Michael Haggerty
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-12 23:57 UTC (permalink / raw)
To: Michael Haggerty; +Cc: Junio C Hamano, Ronnie Sahlberg, git
Michael Haggerty wrote:
>> Jonathan Nieder <jrnieder@gmail.com> writes:
>>> so I'll send a reroll of the series as-is in an hour or so.
>
> Jonathan: Is a current version of this patch series set up for review in
> Gerrit?
Yes.
(https://code-review.googlesource.com/#/q/project:git+topic:ref-transaction)
Thanks,
Jonathan
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-12 23:57 ` Jonathan Nieder
@ 2014-09-17 13:23 ` Michael Haggerty
2014-09-18 16:42 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Michael Haggerty @ 2014-09-17 13:23 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Junio C Hamano, Ronnie Sahlberg, git
On 09/13/2014 01:57 AM, Jonathan Nieder wrote:
> Michael Haggerty wrote:
>>> Jonathan Nieder <jrnieder@gmail.com> writes:
>
>>>> so I'll send a reroll of the series as-is in an hour or so.
>>
>> Jonathan: Is a current version of this patch series set up for review in
>> Gerrit?
>
> Yes.
> (https://code-review.googlesource.com/#/q/project:git+topic:ref-transaction)
I just worked through the patch series, leaving lots of comments in
Gerrit. Overall it looks pretty good and makes a lot of very worthwhile
progress. The only patch that gives me a bit of heartburn is
[PATCH 15/19] refs.c: fix handling of badly named refs
not because it is necessarily wrong, but because it has a lot of
non-local effects that are hard to evaluate. I made a bunch of comments
in Gerrit about that patch, too, and will wait for a response before
having another go at it.
Thanks for all your hard and detailed work, Ronnie and Jonathan!
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-17 13:23 ` Michael Haggerty
@ 2014-09-18 16:42 ` Junio C Hamano
2014-09-18 16:57 ` Jonathan Nieder
0 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-09-18 16:42 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Michael Haggerty, Ronnie Sahlberg, git
Michael Haggerty <mhagger@alum.mit.edu> writes:
> On 09/13/2014 01:57 AM, Jonathan Nieder wrote:
>> Michael Haggerty wrote:
>>>> Jonathan Nieder <jrnieder@gmail.com> writes:
>>
>>>>> so I'll send a reroll of the series as-is in an hour or so.
>>>
>>> Jonathan: Is a current version of this patch series set up for review in
>>> Gerrit?
>>
>> Yes.
>> (https://code-review.googlesource.com/#/q/project:git+topic:ref-transaction)
>
> I just worked through the patch series, leaving lots of comments in
> Gerrit. Overall it looks pretty good and makes a lot of very worthwhile
> progress. The only patch that gives me a bit of heartburn is
>
> [PATCH 15/19] refs.c: fix handling of badly named refs
>
> not because it is necessarily wrong, but because it has a lot of
> non-local effects that are hard to evaluate. I made a bunch of comments
> in Gerrit about that patch, too, and will wait for a response before
> having another go at it.
>
> Thanks for all your hard and detailed work, Ronnie and Jonathan!
>
> Michael
Jonathan: Is a current version of this patch series set up to be
fetched so that it can be reviewed outside Gerrit?
Running ls-remote against https://code.googlesource.com/git shows
many refs under refs/changes/* but it is unclear to me if there is a
coherent single "here is the latest and greatest, dependents rebased
on dependeds in the right order" thing that I can fetch and look at
with "log -p master..".
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-18 16:42 ` Junio C Hamano
@ 2014-09-18 16:57 ` Jonathan Nieder
2014-09-18 17:26 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-18 16:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Michael Haggerty, Ronnie Sahlberg, git
Junio C Hamano wrote:
> Jonathan: Is a current version of this patch series set up to be
> fetched so that it can be reviewed outside Gerrit?
The current tip is 06d707cb63e34fc55a18ecc47e668f3c44acae57 from
https://code.googlesource.com/git (fetch-by-sha1 should work). Each
reroll gets its own refname of the form refs/changes/62/1062/<number>
with <number> increasing. The "Download" widget in the top-right
corner of https://code-review.googlesource.com/1062 shows the refname.
There are plenty of unaddressed comments from Michael, so I'd hold off
on picking up the latest for pu for now.
Thanks,
Jonathan
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-18 16:57 ` Jonathan Nieder
@ 2014-09-18 17:26 ` Junio C Hamano
2014-09-18 17:38 ` Jonathan Nieder
0 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-09-18 17:26 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Michael Haggerty, Ronnie Sahlberg, git
Jonathan Nieder <jrnieder@gmail.com> writes:
> Junio C Hamano wrote:
>
>> Jonathan: Is a current version of this patch series set up to be
>> fetched so that it can be reviewed outside Gerrit?
>
> The current tip is 06d707cb63e34fc55a18ecc47e668f3c44acae57 from
> https://code.googlesource.com/git (fetch-by-sha1 should work). Each
> reroll gets its own refname of the form refs/changes/62/1062/<number>
> with <number> increasing. The "Download" widget in the top-right
> corner of https://code-review.googlesource.com/1062 shows the refname.
While I am showing my naiveté, how does one figure out that 1062 is
the last one in the series?
Does the order of changes that appear in
https://code-review.googlesource.com/#/q/project:git+branch:master+topic:ref-transaction
have any significance? e.g. is a "topic" supposed to be a single
strand of pearls on top of the "branch", and the top one is the tip,
or something?
> There are plenty of unaddressed comments from Michael, so I'd hold off
> on picking up the latest for pu for now.
Oh, the request was not for that. I simply cannot read the patch
with only limited context in the web browser without having a ready
access to a coherent whole, i.e. a full tree with history, to review
a change like this.
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (20 preceding siblings ...)
2014-09-11 22:20 ` Junio C Hamano
@ 2014-09-25 21:35 ` Junio C Hamano
2014-09-25 21:40 ` Jonathan Nieder
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
22 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-09-25 21:35 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
I know that a review-update cycle is still going in the dark at
https://code-review.googlesource.com/#/q/topic:ref-transaction
for this series. Are we almost there for v22 which hopefully be the
final before we merge it to 'next' and go incremental?
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-25 21:35 ` Junio C Hamano
@ 2014-09-25 21:40 ` Jonathan Nieder
2014-09-25 21:55 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-09-25 21:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Junio C Hamano wrote:
> I know that a review-update cycle is still going in the dark at
>
> https://code-review.googlesource.com/#/q/topic:ref-transaction
>
> for this series.
Eh, it's at least public and doesn't flood the list with rebased
versions of the series.
Would you prefer if there were some list archived on gmane with the
automated mails from gerrit, to make it easier to look back at later?
> Are we almost there for v22 which hopefully be the
> final before we merge it to 'next' and go incremental?
The patch "fix handling of badly named refs"[1] is still undergoing
heavy churn.
I think it's worth getting that one right.
Thanks,
Jonathan
[1] https://code-review.googlesource.com/1070
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview)
2014-09-25 21:40 ` Jonathan Nieder
@ 2014-09-25 21:55 ` Junio C Hamano
0 siblings, 0 replies; 130+ messages in thread
From: Junio C Hamano @ 2014-09-25 21:55 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> The patch "fix handling of badly named refs"[1] is still undergoing
> heavy churn.
>
> I think it's worth getting that one right.
Oh, no question about it. I was only making sure that I didn't miss
availability of updates for larger series we saw during this cycle.
^ permalink raw reply [flat|nested] 130+ messages in thread
* [PATCH v22 0/24] rs/ref-transaction
2014-09-11 3:03 ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
` (21 preceding siblings ...)
2014-09-25 21:35 ` Junio C Hamano
@ 2014-10-02 1:48 ` Jonathan Nieder
2014-10-02 1:50 ` [PATCH 01/24] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
` (23 more replies)
22 siblings, 24 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 1:48 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Jonathan Nieder wrote:
> Jonathan Nieder wrote:
>> The next series from Ronnie's collection is available at
>> https://code-review.googlesource.com/#/q/topic:ref-transaction in case
>> someone wants a fresh series to look at.
>
> Here is the outcome of that review. It could use another set of eyes
> (hint, hint)
Another set of eyes arrived and helped. Here's a reroll.
Jonathan Nieder (6):
mv test: recreate mod/ directory instead of relying on stale copy
branch -d: avoid repeated symref resolution
packed-ref cache: forbid dot-components in refnames
refs.c: do not permit err == NULL
lockfile: remove unable_to_lock_error
ref_transaction_commit: bail out on failure to remove a ref
Junio C Hamano (1):
reflog test: test interaction with detached HEAD
Ronnie Sahlberg (17):
wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success
wrapper.c: add a new function unlink_or_msg
refs.c: add an err argument to delete_ref_loose
refs.c: pass the ref log message to _create/delete/update instead of
_commit
rename_ref: don't ask read_ref_full where the ref came from
refs.c: refuse to lock badly named refs in lock_ref_sha1_basic
refs.c: call lock_ref_sha1_basic directly from commit
refs.c: pass a list of names to skip to is_refname_available
refs.c: ref_transaction_commit: distinguish name conflicts from other
errors
fetch.c: change s_update_ref to use a ref transaction
refs.c: make write_ref_sha1 static
refs.c: change resolve_ref_unsafe reading argument to be a flags field
branch -d: simplify by using RESOLVE_REF_READING flag
test: put tests for handling of bad ref names in one place
refs.c: allow listing and deleting badly named refs
for-each-ref.c: improve message before aborting on broken ref
remote rm/prune: print a message when writing packed-refs fails
branch.c | 6 +-
builtin/blame.c | 2 +-
builtin/branch.c | 22 ++-
builtin/checkout.c | 6 +-
builtin/clone.c | 2 +-
builtin/commit.c | 6 +-
builtin/fetch.c | 34 ++--
builtin/fmt-merge-msg.c | 2 +-
builtin/for-each-ref.c | 11 +-
builtin/fsck.c | 2 +-
builtin/log.c | 3 +-
builtin/merge.c | 2 +-
builtin/notes.c | 2 +-
builtin/receive-pack.c | 9 +-
builtin/remote.c | 20 ++-
builtin/replace.c | 5 +-
builtin/show-branch.c | 7 +-
builtin/symbolic-ref.c | 2 +-
builtin/tag.c | 4 +-
builtin/update-ref.c | 13 +-
bundle.c | 2 +-
cache.h | 42 +++--
fast-import.c | 8 +-
git-compat-util.h | 16 +-
http-backend.c | 4 +-
lockfile.c | 10 --
notes-merge.c | 2 +-
reflog-walk.c | 5 +-
refs.c | 438 ++++++++++++++++++++++++++++++++---------------
refs.h | 40 +++--
remote.c | 11 +-
sequencer.c | 8 +-
t/t1400-update-ref.sh | 62 +++----
t/t1413-reflog-detach.sh | 70 ++++++++
t/t1430-bad-ref-name.sh | 207 ++++++++++++++++++++++
t/t3200-branch.sh | 9 +
t/t7001-mv.sh | 15 +-
t/t9300-fast-import.sh | 30 ----
transport-helper.c | 5 +-
transport.c | 5 +-
upload-pack.c | 2 +-
walker.c | 5 +-
wrapper.c | 28 ++-
wt-status.c | 2 +-
44 files changed, 838 insertions(+), 348 deletions(-)
create mode 100755 t/t1413-reflog-detach.sh
create mode 100755 t/t1430-bad-ref-name.sh
--
2.0.0.450.ga793d96
---
Changes since v21:
branch.c | 2 +-
builtin/blame.c | 2 +-
builtin/branch.c | 25 ++---
builtin/checkout.c | 6 +-
builtin/clone.c | 2 +-
builtin/commit.c | 2 +-
builtin/fetch.c | 6 +-
builtin/fmt-merge-msg.c | 2 +-
builtin/for-each-ref.c | 11 +-
builtin/fsck.c | 2 +-
builtin/log.c | 4 +-
builtin/merge.c | 2 +-
builtin/notes.c | 2 +-
builtin/receive-pack.c | 4 +-
builtin/remote.c | 21 ++--
builtin/show-branch.c | 9 +-
builtin/symbolic-ref.c | 2 +-
builtin/update-ref.c | 3 +-
bundle.c | 2 +-
cache.h | 33 ++++--
http-backend.c | 5 +-
notes-merge.c | 2 +-
reflog-walk.c | 6 +-
refs.c | 263 +++++++++++++++++++++++++-------------------
refs.h | 34 +++---
remote.c | 13 ++-
sequencer.c | 4 +-
t/t1400-update-ref.sh | 46 ++------
t/t1402-check-ref-format.sh | 48 --------
t/t1413-reflog-detach.sh | 70 ++++++++++++
t/t1430-bad-ref-name.sh | 207 ++++++++++++++++++++++++++++++++++
t/t9300-fast-import.sh | 30 -----
transport-helper.c | 5 +-
transport.c | 6 +-
upload-pack.c | 2 +-
wt-status.c | 2 +-
36 files changed, 557 insertions(+), 328 deletions(-)
diff --git c/branch.c w/branch.c
index ba3e1c1..adb07c6 100644
--- c/branch.c
+++ w/branch.c
@@ -186,7 +186,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, NULL, 0);
+ head = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die(_("Cannot force update the current branch."));
}
diff --git c/builtin/blame.c w/builtin/blame.c
index b8bec0a..5cbd38f 100644
--- c/builtin/blame.c
+++ w/builtin/blame.c
@@ -2292,7 +2292,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
commit->object.type = OBJ_COMMIT;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", head_sha1, NULL, RESOLVE_REF_READING))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
die("no such ref: HEAD");
parent_tail = append_parent(parent_tail, head_sha1);
diff --git c/builtin/branch.c w/builtin/branch.c
index 5d5bc56..94aaea1 100644
--- c/builtin/branch.c
+++ w/builtin/branch.c
@@ -129,8 +129,8 @@ static int branch_merged(int kind, const char *name,
branch->merge[0] &&
branch->merge[0]->dst &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, sha1,
- NULL, RESOLVE_REF_READING)) != NULL)
+ resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+ sha1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
if (!reference_rev)
@@ -234,13 +234,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, sha1, &flags,
+ target = resolve_ref_unsafe(name,
RESOLVE_REF_READING
- | RESOLVE_REF_NODEREF
- | RESOLVE_REF_ALLOW_BAD_NAME);
- if (!target ||
- (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
- is_null_sha1(sha1))) {
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ sha1, &flags);
+ if (!target) {
error(remote_branch
? _("remote branch '%s' not found.")
: _("branch '%s' not found."), bname.buf);
@@ -255,7 +254,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
continue;
}
- if (delete_ref(name, sha1, REF_NODEREF|REF_BADNAMEOK)) {
+ if (delete_ref(name, sha1, REF_NODEREF)) {
error(remote_branch
? _("Error deleting remote branch '%s'")
: _("Error deleting branch '%s'"),
@@ -268,8 +267,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
? _("Deleted remote branch %s (was %s).\n")
: _("Deleted branch %s (was %s).\n"),
bname.buf,
- (flags & REF_ISSYMREF)
- ? target
+ (flags & REF_ISBROKEN) ? "broken"
+ : (flags & REF_ISSYMREF) ? target
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
@@ -301,7 +300,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst, *cp;
- dst = resolve_ref_unsafe(src, sha1, &flag, 0);
+ dst = resolve_ref_unsafe(src, 0, sha1, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix && (cp = skip_prefix(dst, prefix)))
@@ -867,7 +866,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", head_sha1, NULL, 0);
+ head = resolve_refdup("HEAD", 0, head_sha1, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD")) {
diff --git c/builtin/checkout.c w/builtin/checkout.c
index 64af1f7..a5fef2d 100644
--- c/builtin/checkout.c
+++ w/builtin/checkout.c
@@ -356,7 +356,7 @@ static int checkout_paths(const struct checkout_opts *opts,
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- read_ref_full("HEAD", rev, &flag, 0);
+ read_ref_full("HEAD", 0, rev, &flag);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -771,7 +771,7 @@ static int switch_branches(const struct checkout_opts *opts,
unsigned char rev[20];
int flag, writeout_error = 0;
memset(&old, 0, sizeof(old));
- old.path = path_to_free = resolve_refdup("HEAD", rev, &flag, 0);
+ old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
old.commit = lookup_commit_reference_gently(rev, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -1068,7 +1068,7 @@ static int checkout_branch(struct checkout_opts *opts,
unsigned char rev[20];
int flag;
- if (!read_ref_full("HEAD", rev, &flag, 0) &&
+ if (!read_ref_full("HEAD", 0, rev, &flag) &&
(flag & REF_ISSYMREF) && is_null_sha1(rev))
return switch_unborn_to_new_branch(opts);
}
diff --git c/builtin/clone.c w/builtin/clone.c
index 12a78e1..0f5c880 100644
--- c/builtin/clone.c
+++ w/builtin/clone.c
@@ -622,7 +622,7 @@ static int checkout(void)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", sha1, NULL, RESOLVE_REF_READING);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout.\n"));
diff --git c/builtin/commit.c w/builtin/commit.c
index 3536330..9ccc78b 100644
--- c/builtin/commit.c
+++ w/builtin/commit.c
@@ -1468,7 +1468,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref_unsafe("HEAD", junk_sha1, NULL, 0);
+ head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
printf("[%s%s ",
starts_with(head, "refs/heads/") ?
head + 11 :
diff --git c/builtin/fetch.c w/builtin/fetch.c
index 2e3bc73..30b40f6 100644
--- c/builtin/fetch.c
+++ w/builtin/fetch.c
@@ -392,10 +392,10 @@ static int s_update_ref(const char *action,
goto fail;
ret = ref_transaction_commit(transaction, &err);
- if (ret == UPDATE_REFS_NAME_CONFLICT)
- df_conflict = 1;
- if (ret)
+ if (ret) {
+ df_conflict = (ret == TRANSACTION_NAME_CONFLICT);
goto fail;
+ }
ref_transaction_free(transaction);
strbuf_release(&err);
diff --git c/builtin/fmt-merge-msg.c w/builtin/fmt-merge-msg.c
index b2355ad..afe05dc 100644
--- c/builtin/fmt-merge-msg.c
+++ w/builtin/fmt-merge-msg.c
@@ -602,7 +602,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* get current branch */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", head_sha1, NULL, RESOLVE_REF_READING);
+ resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (!current_branch)
die("No current branch");
if (starts_with(current_branch, "refs/heads/"))
diff --git c/builtin/for-each-ref.c w/builtin/for-each-ref.c
index 090390c..a88d681 100644
--- c/builtin/for-each-ref.c
+++ w/builtin/for-each-ref.c
@@ -649,8 +649,8 @@ static void populate_value(struct refinfo *ref)
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- ref->symref = resolve_refdup(ref->refname, unused1,
- NULL, RESOLVE_REF_READING);
+ ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
+ unused1, NULL);
if (!ref->symref)
ref->symref = "";
}
@@ -708,8 +708,8 @@ static void populate_value(struct refinfo *ref)
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1,
- NULL, RESOLVE_REF_READING);
+ head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
if (!strcmp(ref->refname, head))
v->s = "*";
else
@@ -853,8 +853,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
struct refinfo *ref;
int cnt;
- if ((flag & REF_ISBROKEN) &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (flag & REF_BAD_NAME) {
warning("ignoring ref with broken name %s", refname);
return 0;
}
diff --git c/builtin/fsck.c w/builtin/fsck.c
index 506e32b..7cd109a 100644
--- c/builtin/fsck.c
+++ w/builtin/fsck.c
@@ -560,7 +560,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", head_sha1, &flag, 0);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_sha1, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
diff --git c/builtin/log.c w/builtin/log.c
index 230a9ef..493440a 100644
--- c/builtin/log.c
+++ w/builtin/log.c
@@ -1395,8 +1395,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
const char *ref;
- ref = resolve_ref_unsafe("HEAD", sha1, NULL,
- RESOLVE_REF_READING);
+ ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
if (ref && starts_with(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
diff --git c/builtin/merge.c w/builtin/merge.c
index d262279..6f56967 100644
--- c/builtin/merge.c
+++ w/builtin/merge.c
@@ -1108,7 +1108,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", head_sha1, &flag, 0);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
diff --git c/builtin/notes.c w/builtin/notes.c
index 16df78c..eaf297d 100644
--- c/builtin/notes.c
+++ w/builtin/notes.c
@@ -703,7 +703,7 @@ static int merge_commit(struct notes_merge_options *o)
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", sha1, NULL, 0);
+ resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
diff --git c/builtin/receive-pack.c w/builtin/receive-pack.c
index 555a4a6..8a6e7e3 100644
--- c/builtin/receive-pack.c
+++ w/builtin/receive-pack.c
@@ -656,7 +656,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref_unsafe(buf.buf, sha1, &flag, 0);
+ dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -817,7 +817,7 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", sha1, NULL, 0);
+ head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
diff --git c/builtin/remote.c w/builtin/remote.c
index 6eaeee7..8517cfa 100644
--- c/builtin/remote.c
+++ w/builtin/remote.c
@@ -568,8 +568,8 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref_unsafe(refname, orig_sha1, &flag,
- RESOLVE_REF_READING);
+ symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
+ orig_sha1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
@@ -705,7 +705,7 @@ static int mv(int argc, const char **argv)
int flag = 0;
unsigned char sha1[20];
- read_ref_full(item->string, sha1, &flag, RESOLVE_REF_READING);
+ read_ref_full(item->string, RESOLVE_REF_READING, sha1, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
@@ -750,13 +750,16 @@ static int mv(int argc, const char **argv)
static int remove_branches(struct string_list *branches)
{
+ struct strbuf err = STRBUF_INIT;
const char **branch_names;
int i, result = 0;
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string;
- result |= repack_without_refs(branch_names, branches->nr, NULL);
+ if (repack_without_refs(branch_names, branches->nr, &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
free(branch_names);
for (i = 0; i < branches->nr; i++) {
@@ -1333,9 +1336,13 @@ static int prune_remote(const char *remote, int dry_run)
delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util;
- if (!dry_run)
- result |= repack_without_refs(delete_refs,
- states.stale.nr, NULL);
+ if (!dry_run) {
+ struct strbuf err = STRBUF_INIT;
+ if (repack_without_refs(delete_refs, states.stale.nr,
+ &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
+ }
free(delete_refs);
}
diff --git c/builtin/show-branch.c w/builtin/show-branch.c
index ef6ea52..acc8dc1 100644
--- c/builtin/show-branch.c
+++ w/builtin/show-branch.c
@@ -727,8 +727,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- fake_av[0] = resolve_refdup("HEAD", sha1, NULL,
- RESOLVE_REF_READING);
+ fake_av[0] = resolve_refdup("HEAD",
+ RESOLVE_REF_READING,
+ sha1, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -790,8 +791,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref_unsafe("HEAD", head_sha1, NULL,
- RESOLVE_REF_READING);
+ head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ head_sha1, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git c/builtin/symbolic-ref.c w/builtin/symbolic-ref.c
index 1dd04af..29fb3f1 100644
--- c/builtin/symbolic-ref.c
+++ w/builtin/symbolic-ref.c
@@ -13,7 +13,7 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
{
unsigned char sha1[20];
int flag;
- const char *refname = resolve_ref_unsafe(HEAD, sha1, &flag, 0);
+ const char *refname = resolve_ref_unsafe(HEAD, 0, sha1, &flag);
if (!refname)
die("No such ref: %s", HEAD);
diff --git c/builtin/update-ref.c w/builtin/update-ref.c
index e379fdd..6c9be05 100644
--- c/builtin/update-ref.c
+++ w/builtin/update-ref.c
@@ -419,8 +419,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (no_deref)
flags = REF_NODEREF;
if (delete)
- return delete_ref(refname, oldval ? oldsha1 : NULL,
- flags|REF_BADNAMEOK);
+ return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
else
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
flags, UPDATE_REFS_DIE_ON_ERR);
diff --git c/bundle.c w/bundle.c
index 32dd2f7..d92e49c 100644
--- c/bundle.c
+++ w/bundle.c
@@ -311,7 +311,7 @@ int create_bundle(struct bundle_header *header, const char *path,
continue;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
- if (read_ref_full(e->name, sha1, &flag, RESOLVE_REF_READING))
+ if (read_ref_full(e->name, RESOLVE_REF_READING, sha1, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
diff --git c/cache.h w/cache.h
index 995729f..f582b6c 100644
--- c/cache.h
+++ w/cache.h
@@ -946,8 +946,8 @@ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
-extern int read_ref_full(const char *refname, unsigned char *sha1,
- int *flags, int resolve_flags);
+extern int read_ref_full(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
extern int read_ref(const char *refname, unsigned char *sha1);
/*
@@ -969,27 +969,36 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* "writing" to the ref, the return value is the name of the ref
* that will actually be created or changed.
*
+ * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
+ * level of symbolic reference. The value stored in sha1 for a symbolic
+ * reference will always be null_sha1 in this case, and the return
+ * value is the reference that the symref refers to directly.
+ *
* If flags is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
* packed references), REF_ISSYMREF (if the initial reference was a
- * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
+ * symbolic reference), REF_BAD_NAME (if the reference name is ill
+ * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
+ * (if the ref is malformed).
*
* If ref is not a properly-formatted, normalized reference, return
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL.
*
- * RESOLVE_REF_ALLOW_BAD_NAME disables most of the ref name checking except
- * for names that are absolute paths or contain ".." components. For both
- * these cases the function will return NULL and set errno to EINVAL.
- * If the name is bad then the function will set the REF_ISBROKEN flag and
- * return the name, if the ref exists, or NULL, if it does not.
- * When this flag is set, any badly named refs will resolve to nullsha1.
+ * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
+ * name is invalid according to git-check-ref-format(1). If the name
+ * is bad then the value stored in sha1 will be null_sha1 and the
+ * REF_ISBROKEN and REF_BAD_NAME flags will be set.
+ *
+ * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
+ * directory and do not consist of all caps and underscores cannot be
+ * resolved. The function returns NULL for such ref names.
*/
#define RESOLVE_REF_READING 0x01
-#define RESOLVE_REF_NODEREF 0x02
+#define RESOLVE_REF_NO_RECURSE 0x02
#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
-extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
-extern char *resolve_refdup(const char *ref, unsigned char *sha1, int *flags, int resolve_flags);
+extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
+extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git c/http-backend.c w/http-backend.c
index 8f94f9b..e172886 100644
--- c/http-backend.c
+++ w/http-backend.c
@@ -417,8 +417,9 @@ static int show_head_ref(const char *refname, const unsigned char *sha1,
if (flag & REF_ISSYMREF) {
unsigned char unused[20];
- const char *target = resolve_ref_unsafe(refname, unused,
- NULL, RESOLVE_REF_READING);
+ const char *target = resolve_ref_unsafe(refname,
+ RESOLVE_REF_READING,
+ unused, NULL);
const char *target_nons = strip_namespace(target);
strbuf_addf(buf, "ref: %s\n", target_nons);
diff --git c/notes-merge.c w/notes-merge.c
index ffca602..3c88d17 100644
--- c/notes-merge.c
+++ w/notes-merge.c
@@ -549,7 +549,7 @@ int notes_merge(struct notes_merge_options *o,
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, local_sha1, NULL, 0))
+ if (read_ref_full(o->local_ref, 0, local_sha1, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
is_null_sha1(local_sha1))
diff --git c/reflog-walk.c w/reflog-walk.c
index feeaf0a..23345ea 100644
--- c/reflog-walk.c
+++ w/reflog-walk.c
@@ -48,8 +48,8 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
unsigned char sha1[20];
const char *name;
void *name_to_free;
- name = name_to_free = resolve_refdup(ref, sha1, NULL,
- RESOLVE_REF_READING);
+ name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
+ sha1, NULL);
if (name) {
for_each_reflog_ent(name, read_one_reflog, reflogs);
free(name_to_free);
@@ -175,7 +175,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
if (*branch == '\0') {
unsigned char sha1[20];
free(branch);
- branch = resolve_refdup("HEAD", sha1, NULL, 0);
+ branch = resolve_refdup("HEAD", 0, sha1, NULL);
if (!branch)
die ("No current branch");
diff --git c/refs.c w/refs.c
index 3b27758..d9d327d 100644
--- c/refs.c
+++ w/refs.c
@@ -69,16 +69,8 @@ static int check_refname_component(const char *refname, int flags)
out:
if (cp == refname)
return 0; /* Component has zero length. */
- if (refname[0] == '.') {
- if (!(flags & REFNAME_DOT_COMPONENT))
- return -1; /* Component starts with '.'. */
- /*
- * Even if leading dots are allowed, don't allow "."
- * as a component (".." is prevented by a rule above).
- */
- if (refname[1] == '\0')
- return -1; /* Component equals ".". */
- }
+ if (refname[0] == '.')
+ return -1; /* Component starts with '.'. */
if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
return -1; /* Refname ends with ".lock". */
return cp - refname;
@@ -193,8 +185,8 @@ struct ref_dir {
/*
* Bit values for ref_entry::flag. REF_ISSYMREF=0x01,
- * REF_ISPACKED=0x02, and REF_ISBROKEN=0x04 are public values; see
- * refs.h.
+ * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
+ * public values; see refs.h.
*/
/*
@@ -202,16 +194,16 @@ struct ref_dir {
* the correct peeled value for the reference, which might be
* null_sha1 if the reference is not a tag or if it is broken.
*/
-#define REF_KNOWS_PEELED 0x08
+#define REF_KNOWS_PEELED 0x10
/* ref_entry represents a directory of references */
-#define REF_DIR 0x10
+#define REF_DIR 0x20
/*
* Entry has not yet been read from disk (used only for REF_DIR
* entries representing loose references)
*/
-#define REF_INCOMPLETE 0x20
+#define REF_INCOMPLETE 0x40
/*
* A ref_entry represents either a reference or a "subdirectory" of
@@ -280,6 +272,37 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
return dir;
}
+static int escapes_cwd(const char *path) {
+ char *buf;
+ int result;
+
+ if (is_absolute_path(path))
+ return 1;
+ buf = xmalloc(strlen(path) + 1);
+ result = !!normalize_path_copy(buf, path);
+ free(buf);
+ return result;
+}
+
+/*
+ * Check if a refname is safe.
+ * For refs that start with "refs/" we consider it safe as long as the rest
+ * of the path components does not allow it to escape from this directory.
+ * For all other refs we only consider them safe iff they only contain
+ * upper case characters and '_'.
+ */
+static int refname_is_safe(const char *refname)
+{
+ if (starts_with(refname, "refs/"))
+ return !escapes_cwd(refname + strlen("refs/"));
+ while (*refname) {
+ if (!isupper(*refname) && *refname != '_')
+ return 0;
+ refname++;
+ }
+ return 1;
+}
+
static struct ref_entry *create_ref_entry(const char *refname,
const unsigned char *sha1, int flag,
int check_name)
@@ -288,8 +311,10 @@ static struct ref_entry *create_ref_entry(const char *refname,
struct ref_entry *ref;
if (check_name &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die("Reference has invalid format: '%s'", refname);
+ if (!check_name && !refname_is_safe(refname))
+ die("Reference has invalid name: '%s'", refname);
len = strlen(refname) + 1;
ref = xmalloc(sizeof(struct ref_entry) + len);
hashcpy(ref->u.value.sha1, sha1);
@@ -822,10 +847,9 @@ static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
/*
* Return true iff a reference named refname could be created without
* conflicting with the name of an existing reference in dir. If
- * oldrefname is non-NULL, ignore potential conflicts with oldrefname
- * (e.g., because oldrefname is scheduled for deletion in the same
- * operation). skiplist contains a list of refs we want to skip checking for
- * conflicts with. skiplist must be sorted.
+ * skiplist is non-NULL, ignore potential conflicts with names in
+ * skiplist (e.g., because those refs are scheduled for deletion in
+ * the same operation). skiplist must be sorted.
*/
static int is_refname_available(const char *refname,
struct ref_dir *dir,
@@ -1062,9 +1086,9 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
if (refname) {
int flag = REF_ISPACKED;
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT)) {
- flag |= REF_ISBROKEN;
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
hashclr(sha1);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
}
last = create_ref_entry(refname, sha1, flag, 0);
if (peeled == PEELED_FULLY ||
@@ -1198,16 +1222,16 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- } else if (read_ref_full(refname.buf, sha1, &flag,
- RESOLVE_REF_READING)) {
+ } else if (read_ref_full(refname.buf,
+ RESOLVE_REF_READING,
+ sha1, &flag)) {
hashclr(sha1);
flag |= REF_ISBROKEN;
}
if (check_refname_format(refname.buf,
- REFNAME_ALLOW_ONELEVEL|
- REFNAME_DOT_COMPONENT)) {
+ REFNAME_ALLOW_ONELEVEL)) {
hashclr(sha1);
- flag |= REF_ISBROKEN;
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
}
add_entry_to_dir(dir,
create_ref_entry(refname.buf, sha1, flag, 0));
@@ -1329,10 +1353,10 @@ static struct ref_entry *get_packed_ref(const char *refname)
* A loose ref file doesn't exist; check for a packed ref. The
* options are forwarded from resolve_safe_unsafe().
*/
-static const char *handle_missing_loose_ref(const char *refname,
- unsigned char *sha1,
- int reading,
- int *flag)
+static int resolve_missing_loose_ref(const char *refname,
+ int resolve_flags,
+ unsigned char *sha1,
+ int *flags)
{
struct ref_entry *entry;
@@ -1343,33 +1367,22 @@ static const char *handle_missing_loose_ref(const char *refname,
entry = get_packed_ref(refname);
if (entry) {
hashcpy(sha1, entry->u.value.sha1);
- if (flag)
- *flag |= REF_ISPACKED;
- return refname;
+ if (flags)
+ *flags |= REF_ISPACKED;
+ return 0;
}
/* The reference is not a packed reference, either. */
- if (reading) {
- return NULL;
+ if (resolve_flags & RESOLVE_REF_READING) {
+ errno = ENOENT;
+ return -1;
} else {
hashclr(sha1);
- return refname;
+ return 0;
}
}
-static int escapes_cwd(const char *path) {
- char *buf;
- int result;
-
- if (is_absolute_path(path))
- return 1;
- buf = xmalloc(strlen(path) + 1);
- result = normalize_path_copy(buf, path);
- free(buf);
- return result;
-}
-
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *flags, int resolve_flags)
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
{
int depth = MAXDEPTH;
ssize_t len;
@@ -1381,14 +1394,22 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
*flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (flags)
+ *flags |= REF_BAD_NAME;
+
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
- escapes_cwd(refname)) {
+ !refname_is_safe(refname)) {
errno = EINVAL;
return NULL;
}
- hashclr(sha1);
- if (flags)
- *flags |= REF_ISBROKEN;
+ /*
+ * dwim_ref() uses REF_ISBROKEN to distinguish between
+ * missing refs and refs that were present but invalid,
+ * to complain about the latter to stderr.
+ *
+ * We don't know whether the ref exists, so don't set
+ * REF_ISBROKEN yet.
+ */
bad_name = 1;
}
for (;;) {
@@ -1415,12 +1436,17 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
*/
stat_ref:
if (lstat(path, &st) < 0) {
- if (errno == ENOENT)
- return handle_missing_loose_ref(refname, sha1,
- resolve_flags & RESOLVE_REF_READING,
- flags);
- else
+ if (errno != ENOENT)
return NULL;
+ if (resolve_missing_loose_ref(refname, resolve_flags,
+ sha1, flags))
+ return NULL;
+ if (bad_name) {
+ hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ }
+ return refname;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
@@ -1440,7 +1466,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
refname = refname_buffer;
if (flags)
*flags |= REF_ISSYMREF;
- if (resolve_flags & RESOLVE_REF_NODEREF) {
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
hashclr(sha1);
return refname;
}
@@ -1493,8 +1519,11 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
errno = EINVAL;
return NULL;
}
- if (bad_name)
+ if (bad_name) {
hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ }
return refname;
}
if (flags)
@@ -1502,23 +1531,28 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int *fl
buf = buffer + 4;
while (isspace(*buf))
buf++;
- if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
- if (flags)
- *flags |= REF_ISBROKEN;
- errno = EINVAL;
- return NULL;
- }
refname = strcpy(refname_buffer, buf);
- if (resolve_flags & RESOLVE_REF_NODEREF) {
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
hashclr(sha1);
return refname;
}
+ if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
+ if (flags)
+ *flags |= REF_BAD_NAME | REF_ISBROKEN;
+
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(buf)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ bad_name = 1;
+ }
}
}
-char *resolve_refdup(const char *ref, unsigned char *sha1, int *flags, int resolve_flags)
+char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
{
- const char *ret = resolve_ref_unsafe(ref, sha1, flags, resolve_flags);
+ const char *ret = resolve_ref_unsafe(ref, resolve_flags, sha1, flags);
return ret ? xstrdup(ret) : NULL;
}
@@ -1529,22 +1563,22 @@ struct ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, unsigned char *sha1, int *flags, int resolve_flags)
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
{
- if (resolve_ref_unsafe(refname, sha1, flags, resolve_flags))
+ if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
return 0;
return -1;
}
int read_ref(const char *refname, unsigned char *sha1)
{
- return read_ref_full(refname, sha1, NULL, RESOLVE_REF_READING);
+ return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
}
int ref_exists(const char *refname)
{
unsigned char sha1[20];
- return !!resolve_ref_unsafe(refname, sha1, NULL, RESOLVE_REF_READING);
+ return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
}
static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
@@ -1658,7 +1692,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
return 0;
}
- if (read_ref_full(refname, base, &flag, RESOLVE_REF_READING))
+ if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
return -1;
/*
@@ -1699,7 +1733,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, junk, NULL, 0);
+ resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
@@ -1824,7 +1858,7 @@ static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
return 0;
}
- if (!read_ref_full("HEAD", sha1, &flag, RESOLVE_REF_READING))
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
return fn("HEAD", sha1, flag, cb_data);
return 0;
@@ -1904,7 +1938,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, sha1, &flag, RESOLVE_REF_READING))
+ if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
ret = fn(buf.buf, sha1, flag, cb_data);
strbuf_release(&buf);
@@ -1999,8 +2033,9 @@ int refname_match(const char *abbrev_name, const char *full_name)
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- if (read_ref_full(lock->ref_name, lock->old_sha1, NULL,
- mustexist ? RESOLVE_REF_READING : 0)) {
+ if (read_ref_full(lock->ref_name,
+ mustexist ? RESOLVE_REF_READING : 0,
+ lock->old_sha1, NULL)) {
int save_errno = errno;
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
@@ -2073,8 +2108,8 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
this_result = refs_found ? sha1_from_ref : sha1;
mksnpath(fullref, sizeof(fullref), *p, len, str);
- r = resolve_ref_unsafe(fullref, this_result, &flag,
- RESOLVE_REF_READING);
+ r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
+ this_result, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -2103,7 +2138,8 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
const char *ref, *it;
mksnpath(path, sizeof(path), *p, len, str);
- ref = resolve_ref_unsafe(path, hash, NULL, RESOLVE_REF_READING);
+ ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
+ hash, NULL);
if (!ref)
continue;
if (reflog_exists(path))
@@ -2145,16 +2181,16 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- if (flags & REF_BADNAMEOK)
- resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
-
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
- if (flags & REF_NODEREF)
- resolve_flags |= RESOLVE_REF_NODEREF;
+ if (flags & REF_DELETING) {
+ resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
+ if (flags & REF_NODEREF)
+ resolve_flags |= RESOLVE_REF_NO_RECURSE;
+ }
- refname = resolve_ref_unsafe(refname, lock->old_sha1, &type,
- resolve_flags);
+ refname = resolve_ref_unsafe(refname, resolve_flags,
+ lock->old_sha1, &type);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@@ -2167,8 +2203,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
error("there are still refs under '%s'", orig_refname);
goto error_return;
}
- refname = resolve_ref_unsafe(orig_refname, lock->old_sha1,
- &type, resolve_flags);
+ refname = resolve_ref_unsafe(orig_refname, resolve_flags,
+ lock->old_sha1, &type);
}
if (type_p)
*type_p = type;
@@ -2521,7 +2557,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
unsigned char sha1[20];
int flags;
- if (read_ref_full(entry->name, sha1, &flags, 0))
+ if (read_ref_full(entry->name, 0, sha1, &flags))
/* We should at least have found the packed ref. */
die("Internal error");
if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
@@ -2712,8 +2748,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
- symref = resolve_ref_unsafe(oldrefname, orig_sha1, &flag,
- RESOLVE_REF_READING);
+ symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
+ orig_sha1, &flag);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
oldrefname);
@@ -2742,7 +2778,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, NULL, RESOLVE_REF_READING) &&
+ if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
@@ -2956,7 +2992,7 @@ static int is_branch(const char *refname)
}
/*
- * Writes sha1 into the ref specified by the lock. Makes sure that errno
+ * Write sha1 into the ref specified by the lock. Make sure that errno
* is sane on error.
*/
static int write_ref_sha1(struct ref_lock *lock,
@@ -3020,8 +3056,8 @@ static int write_ref_sha1(struct ref_lock *lock,
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
- head_ref = resolve_ref_unsafe("HEAD", head_sha1, &head_flag,
- RESOLVE_REF_READING);
+ head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ head_sha1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -3388,7 +3424,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
retval = do_for_each_reflog(name, fn, cb_data);
} else {
unsigned char sha1[20];
- if (read_ref_full(name->buf, sha1, NULL, 0))
+ if (read_ref_full(name->buf, 0, sha1, NULL))
retval = error("bad ref for %s", name->buf);
else
retval = fn(name->buf, sha1, 0, cb_data);
@@ -3667,27 +3703,30 @@ int ref_transaction_commit(struct ref_transaction *transaction,
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
if (ref_update_reject_duplicates(updates, n, err)) {
- ret = -1;
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
/* Acquire all locks while verifying old values */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
+ int flags = update->flags;
+ if (is_null_sha1(update->new_sha1))
+ flags |= REF_DELETING;
update->lock = lock_ref_sha1_basic(update->refname,
(update->have_old ?
update->old_sha1 :
NULL),
NULL,
- update->flags,
+ flags,
&update->type);
if (!update->lock) {
- int df_conflict = (errno == ENOTDIR);
-
+ ret = (errno == ENOTDIR)
+ ? TRANSACTION_NAME_CONFLICT
+ : TRANSACTION_GENERIC_ERROR;
strbuf_addf(err, "Cannot lock the ref '%s'.",
update->refname);
- ret = df_conflict ? UPDATE_REFS_NAME_CONFLICT : -1;
goto cleanup;
}
}
@@ -3697,15 +3736,15 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (!is_null_sha1(update->new_sha1)) {
- ret = write_ref_sha1(update->lock, update->new_sha1,
- update->msg);
- update->lock = NULL; /* freed by write_ref_sha1 */
- if (ret) {
+ if (write_ref_sha1(update->lock, update->new_sha1,
+ update->msg)) {
+ update->lock = NULL; /* freed by write_ref_sha1 */
strbuf_addf(err, "Cannot update the ref '%s'.",
update->refname);
- ret = -1;
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
+ update->lock = NULL; /* freed by write_ref_sha1 */
}
}
@@ -3715,7 +3754,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (update->lock) {
if (delete_ref_loose(update->lock, update->type, err)) {
- ret = -1;
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3725,7 +3764,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
}
if (repack_without_refs(delnames, delnum, err)) {
- ret = -1;
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
for (i = 0; i < delnum; i++)
diff --git c/refs.h w/refs.h
index a96e174..3b35387 100644
--- c/refs.h
+++ w/refs.h
@@ -56,11 +56,15 @@ struct ref_transaction;
/*
* Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file, or
- * symbolic reference refers to ill-formatted reference name.
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
*/
#define REF_ISBROKEN 0x04
+/* Reference name is not well formed (see git-check-ref-format(1)). */
+#define REF_BAD_NAME 0x08
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the refname and sha1
@@ -175,12 +179,12 @@ extern int peel_ref(const char *refname, unsigned char *sha1);
* ref_transaction_create(), etc.
* REF_NODEREF: act on the ref directly, instead of dereferencing
* symbolic references.
- * REF_BADNAMEOK: allow locking a ref that has a bad name.
+ * REF_DELETING: tolerate broken refs
*
* Flags >= 0x100 are reserved for internal use.
*/
#define REF_NODEREF 0x01
-#define REF_BADNAMEOK 0x02
+#define REF_DELETING 0x02
/*
* This function sets errno to something meaningful on failure.
*/
@@ -226,7 +230,6 @@ extern int for_each_reflog(each_ref_fn, void *);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
-#define REFNAME_DOT_COMPONENT 4
/*
* Return 0 iff refname has the correct format for a refname according
@@ -234,10 +237,7 @@ extern int for_each_reflog(each_ref_fn, void *);
* If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
* reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
* allow a "*" wildcard character in place of one of the name
- * components. No leading or repeated slashes are accepted. If
- * REFNAME_DOT_COMPONENT is set in flags, then allow refname
- * components to start with "." (but not a whole component equal to
- * "." or "..").
+ * components. No leading or repeated slashes are accepted.
*/
extern int check_refname_format(const char *refname, int flags);
@@ -270,8 +270,8 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
* The following functions add a reference check or update to a
* ref_transaction. In all of them, refname is the name of the
* reference to be affected. The functions make internal copies of
- * refname, so the caller retains ownership of the parameter. flags
- * can be REF_NODEREF; it is passed to update_ref_lock().
+ * refname and msg, so the caller retains ownership of these parameters.
+ * flags can be REF_NODEREF; it is passed to update_ref_lock().
*/
/*
@@ -322,14 +322,14 @@ int ref_transaction_delete(struct ref_transaction *transaction,
/*
* Commit all of the changes that have been queued in transaction, as
- * atomically as possible. Return a nonzero value if there is a
- * problem.
+ * atomically as possible.
*
- * Function returns 0 on success, -1 for generic failures and
- * UPDATE_REFS_NAME_CONFLICT (-2) if the failure was due to a naming conflict.
- * For example, the ref names A and A/B conflict.
+ * Returns 0 for success, or one of the below error codes for errors.
*/
-#define UPDATE_REFS_NAME_CONFLICT -2
+/* Naming conflict (for example, the ref names A and A/B conflict). */
+#define TRANSACTION_NAME_CONFLICT -1
+/* All other errors. */
+#define TRANSACTION_GENERIC_ERROR -2
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
diff --git c/remote.c w/remote.c
index 67c375d..25b07ac 100644
--- c/remote.c
+++ w/remote.c
@@ -486,7 +486,7 @@ static void read_config(void)
return;
default_remote_name = "origin";
current_branch = NULL;
- head_ref = resolve_ref_unsafe("HEAD", sha1, &flag, 0);
+ head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
starts_with(head_ref, "refs/heads/")) {
current_branch =
@@ -1121,8 +1121,8 @@ static char *guess_ref(const char *name, struct ref *peer)
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- const char *r = resolve_ref_unsafe(peer->name, sha1, NULL,
- RESOLVE_REF_READING);
+ const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
+ sha1, NULL);
if (!r)
return NULL;
@@ -1183,8 +1183,9 @@ static int match_explicit(struct ref *src, struct ref *dst,
unsigned char sha1[20];
int flag;
- dst_value = resolve_ref_unsafe(matched_src->name, sha1, &flag,
- RESOLVE_REF_READING);
+ dst_value = resolve_ref_unsafe(matched_src->name,
+ RESOLVE_REF_READING,
+ sha1, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
@@ -1658,7 +1659,7 @@ static int ignore_symref_update(const char *refname)
unsigned char sha1[20];
int flag;
- if (!resolve_ref_unsafe(refname, sha1, &flag, 0))
+ if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
}
diff --git c/sequencer.c w/sequencer.c
index 6a05ad4..70fb7a8 100644
--- c/sequencer.c
+++ w/sequencer.c
@@ -366,7 +366,7 @@ static int is_index_unchanged(void)
unsigned char head_sha1[20];
struct commit *head_commit;
- if (!resolve_ref_unsafe("HEAD", head_sha1, NULL, RESOLVE_REF_READING))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
return error(_("Could not resolve HEAD commit\n"));
head_commit = lookup_commit(head_sha1);
@@ -912,7 +912,7 @@ static int rollback_single_pick(void)
if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
!file_exists(git_path("REVERT_HEAD")))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", head_sha1, NULL, 0))
+ if (read_ref_full("HEAD", 0, head_sha1, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_sha1(head_sha1))
return error(_("cannot abort from a branch yet to be born"));
diff --git c/t/t1400-update-ref.sh w/t/t1400-update-ref.sh
index ff4607b..7b4707b 100755
--- c/t/t1400-update-ref.sh
+++ w/t/t1400-update-ref.sh
@@ -126,6 +126,16 @@ test_expect_success 'update-ref --no-deref -d can delete self-reference' '
test_path_is_missing .git/refs/heads/self
'
+test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+ >.git/refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/bad" &&
+ git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/ref-to-bad" &&
+ test_path_is_file .git/refs/heads/ref-to-bad &&
+ git update-ref --no-deref -d refs/heads/ref-to-bad &&
+ test_path_is_missing .git/refs/heads/ref-to-bad
+'
+
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
@@ -390,12 +400,6 @@ test_expect_success 'stdin fails create with no ref' '
grep "fatal: create: missing <ref>" err
'
-test_expect_success 'stdin fails create with bad ref name' '
- echo "create ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails create with no new value' '
echo "create $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -414,12 +418,6 @@ test_expect_success 'stdin fails update with no ref' '
grep "fatal: update: missing <ref>" err
'
-test_expect_success 'stdin fails update with bad ref name' '
- echo "update ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails update with no new value' '
echo "update $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -438,12 +436,6 @@ test_expect_success 'stdin fails delete with no ref' '
grep "fatal: delete: missing <ref>" err
'
-test_expect_success 'stdin fails delete with bad ref name' '
- echo "delete ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails delete with too many arguments' '
echo "delete $a $m $m" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -716,12 +708,6 @@ test_expect_success 'stdin -z fails create with no ref' '
grep "fatal: create: missing <ref>" err
'
-test_expect_success 'stdin -z fails create with bad ref name' '
- printf $F "create ~a " "$m" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a " err
-'
-
test_expect_success 'stdin -z fails create with no new value' '
printf $F "create $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
@@ -746,12 +732,6 @@ test_expect_success 'stdin -z fails update with too few args' '
grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
'
-test_expect_success 'stdin -z fails update with bad ref name' '
- printf $F "update ~a" "$m" "" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin -z emits warning with empty new value' '
git update-ref $a $m &&
printf $F "update $a" "" "" >stdin &&
@@ -784,12 +764,6 @@ test_expect_success 'stdin -z fails delete with no ref' '
grep "fatal: delete: missing <ref>" err
'
-test_expect_success 'stdin -z fails delete with bad ref name' '
- printf $F "delete ~a" "$m" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin -z fails delete with no old value' '
printf $F "delete $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
diff --git c/t/t1402-check-ref-format.sh w/t/t1402-check-ref-format.sh
index 058fa37..1a5a5f3 100755
--- c/t/t1402-check-ref-format.sh
+++ w/t/t1402-check-ref-format.sh
@@ -196,52 +196,4 @@ invalid_ref_normalized 'heads///foo.lock'
invalid_ref_normalized 'foo.lock/bar'
invalid_ref_normalized 'foo.lock///bar'
-test_expect_success 'git branch shows badly named ref' '
- cp .git/refs/heads/master .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- git branch >output &&
- grep -e "broken...ref" output
-'
-
-test_expect_success 'git branch -d can delete badly named ref' '
- cp .git/refs/heads/master .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- git branch -d broken...ref &&
- git branch >output &&
- ! grep -e "broken...ref" output
-'
-
-test_expect_success 'git branch -D can delete badly named ref' '
- cp .git/refs/heads/master .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- git branch -D broken...ref &&
- git branch >output &&
- ! grep -e "broken...ref" output
-'
-
-test_expect_success 'git update-ref -d can delete badly named ref' '
- cp .git/refs/heads/master .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- git update-ref -d refs/heads/broken...ref &&
- git branch >output &&
- ! grep -e "broken...ref" output
-'
-
-test_expect_success 'git branch can not create a badly named ref' '
- test_must_fail git branch broken...ref
-'
-
-test_expect_success 'git branch can not rename to a bad ref name' '
- git branch goodref &&
- test_must_fail git branch -m goodref broken...ref
-'
-
-test_expect_failure 'git branch can rename from a bad ref name' '
- cp .git/refs/heads/master .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- git branch -m broken...ref renamed &&
- test_must_fail git rev-parse broken...ref &&
- test_cmp_rev master renamed
-'
-
test_done
diff --git c/t/t1413-reflog-detach.sh w/t/t1413-reflog-detach.sh
new file mode 100755
index 0000000..c730600
--- /dev/null
+++ w/t/t1413-reflog-detach.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='Test reflog interaction with detached HEAD'
+. ./test-lib.sh
+
+reset_state () {
+ git checkout master &&
+ cp saved_reflog .git/logs/HEAD
+}
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m initial &&
+ git branch side &&
+ test_tick &&
+ git commit --allow-empty -m second &&
+ cat .git/logs/HEAD >saved_reflog
+'
+
+test_expect_success baseline '
+ reset_state &&
+ git rev-parse master master^ >expect &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'switch to branch' '
+ reset_state &&
+ git rev-parse side master master^ >expect &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to other' '
+ reset_state &&
+ git rev-parse master side master master^ >expect &&
+ git checkout side &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to self' '
+ reset_state &&
+ git rev-parse master master master^ >expect &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to self' '
+ reset_state &&
+ git rev-parse master master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout master &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to other' '
+ reset_state &&
+ git rev-parse side master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git c/t/t1430-bad-ref-name.sh w/t/t1430-bad-ref-name.sh
new file mode 100755
index 0000000..468e856
--- /dev/null
+++ w/t/t1430-bad-ref-name.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+
+test_description='Test handling of ref names that check-ref-format rejects'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit one &&
+ test_commit two
+'
+
+test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ cat >input <<-INPUT_END &&
+ commit .badbranchname
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ corrupt
+ COMMIT
+
+ from refs/heads/master
+
+ INPUT_END
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"' '
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ cat >input <<-INPUT_END &&
+ commit bad[branch]name
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ corrupt
+ COMMIT
+
+ from refs/heads/master
+
+ INPUT_END
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'git branch shows badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch >output &&
+ grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -d can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -d broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -D broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git branch -D ../../my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'branch -D cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git branch -D "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'git branch cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git branch broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -m cannot rename to a bad ref name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_might_fail git branch -D goodref &&
+ git branch goodref &&
+ test_must_fail git branch -m goodref broken...ref &&
+ test_cmp_rev master goodref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'branch -m can rename from a bad ref name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -m broken...ref renamed &&
+ test_cmp_rev master renamed &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'push cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'push --mirror can delete badly named ref' '
+ top=$(pwd) &&
+ git init src &&
+ git init dest &&
+
+ (
+ cd src &&
+ test_commit one
+ ) &&
+ (
+ cd dest &&
+ test_commit two &&
+ git checkout --detach &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref
+ ) &&
+ git -C src push --mirror "file://$top/dest" &&
+ git -C dest branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'rev-parse skips symref pointing to broken name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch shadow one &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ git symbolic-ref refs/tags/shadow refs/heads/broken...ref &&
+
+ git rev-parse --verify one >expect &&
+ git rev-parse --verify shadow >actual 2>err &&
+ test_cmp expect actual &&
+ test_i18ngrep "ignoring.*refs/tags/shadow" err
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
+ git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ test_path_is_file .git/refs/heads/badname &&
+ git update-ref --no-deref -d refs/heads/badname &&
+ test_path_is_missing .git/refs/heads/badname
+'
+
+test_expect_success 'update-ref -d can delete broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git update-ref -d refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git update-ref -d my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'update-ref -d cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git update-ref -d "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'update-ref --stdin fails create with bad ref name' '
+ echo "create ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails update with bad ref name' '
+ echo "update ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails delete with bad ref name' '
+ echo "delete ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails create with bad ref name' '
+ printf "%s\0" "create ~a " refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a " err
+'
+
+test_expect_success 'update-ref --stdin -z fails update with bad ref name' '
+ printf "%s\0" "update ~a" refs/heads/master "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
+ printf "%s\0" "delete ~a" refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_done
diff --git c/t/t9300-fast-import.sh w/t/t9300-fast-import.sh
index 5fc9ef2..3d156f9 100755
--- c/t/t9300-fast-import.sh
+++ w/t/t9300-fast-import.sh
@@ -347,36 +347,6 @@ test_expect_success 'B: fail on invalid blob sha1' '
rm -f .git/objects/pack_* .git/objects/index_*
cat >input <<INPUT_END
-commit .badbranchname
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
- test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
-commit bad[branch]name
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
- test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
commit TEMP_TAG
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
diff --git c/transport-helper.c w/transport-helper.c
index 8365441..3497a5e 100644
--- c/transport-helper.c
+++ w/transport-helper.c
@@ -890,8 +890,9 @@ static int push_refs_with_export(struct transport *transport,
/* Follow symbolic refs (mainly for HEAD). */
name = resolve_ref_unsafe(
- ref->peer_ref->name, sha1,
- &flag, RESOLVE_REF_READING);
+ ref->peer_ref->name,
+ RESOLVE_REF_READING,
+ sha1, &flag);
if (!name || !(flag & REF_ISSYMREF))
name = ref->peer_ref->name;
diff --git c/transport.c w/transport.c
index 3ba7bbf..76e0a9a 100644
--- c/transport.c
+++ w/transport.c
@@ -168,8 +168,8 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref_unsafe(localname, sha, &flag,
- RESOLVE_REF_READING);
+ tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
+ sha, &flag);
if (tmp && flag & REF_ISSYMREF &&
starts_with(tmp, "refs/heads/"))
localname = tmp;
@@ -754,7 +754,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
unsigned char head_sha1[20];
char *head;
- head = resolve_refdup("HEAD", head_sha1, NULL, RESOLVE_REF_READING);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
diff --git c/upload-pack.c w/upload-pack.c
index 3b51ccb..4542565 100644
--- c/upload-pack.c
+++ w/upload-pack.c
@@ -743,7 +743,7 @@ static int find_symref(const char *refname, const unsigned char *sha1, int flag,
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, unused, &flag, 0);
+ symref_target = resolve_ref_unsafe(refname, 0, unused, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
diff --git c/wt-status.c w/wt-status.c
index 6819e2b..c3cbf50 100644
--- c/wt-status.c
+++ w/wt-status.c
@@ -128,7 +128,7 @@ void wt_status_prepare(struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- s->branch = resolve_refdup("HEAD", sha1, NULL, 0);
+ s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 01/24] mv test: recreate mod/ directory instead of relying on stale copy
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
@ 2014-10-02 1:50 ` Jonathan Nieder
2014-10-02 1:54 ` [PATCH 02/24] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success Jonathan Nieder
` (22 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 1:50 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Date: Wed, 10 Sep 2014 14:01:46 -0700
The tests for 'git mv moves a submodule' functionality often run
commands like
git mv sub mod/sub
to move a submodule into a subdirectory. Just like plain /bin/mv,
this is supposed to succeed if the mod/ parent directory exists
and fail if it doesn't exist.
Usually these tests mkdir the parent directory beforehand, but some
instead rely on it being left behind by previous tests.
More precisely, when 'git reset --hard' tries to move to a state where
mod/sub is not present any more, it would perform the following
operations:
rmdir("mod/sub")
rmdir("mod")
The first fails with ENOENT because the test script removed mod/sub
with "rm -rf" already, so 'reset --hard' doesn't bother to move on to
the second, and the mod/ directory is kept around.
Better to explicitly remove and re-create the mod/ directory so later
tests don't have to depend on the directory left behind by the earlier
ones at all (making it easier to rearrange or skip some tests in the
file or to tweak 'reset --hard' behavior without breaking unrelated
tests).
Noticed while testing a patch that fixes the reset --hard behavior
described above.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
As before.
t/t7001-mv.sh | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 54d7807..69f11bd 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -350,10 +350,11 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
'
test_expect_success 'git mv moves a submodule with gitfile' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
(
cd mod &&
git mv ../sub/ .
@@ -372,11 +373,12 @@ test_expect_success 'git mv moves a submodule with gitfile' '
'
test_expect_success 'mv does not complain when no .gitmodules file is found' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git rm .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
git mv sub mod/sub 2>actual.err &&
! test -s actual.err &&
! test -e sub &&
@@ -390,11 +392,12 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
'
test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules foo.bar true &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
test_must_fail git mv sub mod/sub 2>actual.err &&
test -s actual.err &&
test -e sub &&
@@ -413,13 +416,14 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
'
test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules --remove-section submodule.sub &&
git add .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
+ mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_i18ncmp expect.err actual.err &&
! test -e sub &&
@@ -433,9 +437,10 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
'
test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
+ mkdir mod &&
git mv -n sub mod/sub 2>actual.err &&
test -f sub/.git &&
git diff-index --exit-code HEAD &&
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 02/24] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
2014-10-02 1:50 ` [PATCH 01/24] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
@ 2014-10-02 1:54 ` Jonathan Nieder
2014-10-02 1:55 ` [PATCH 03/24] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
` (21 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 1:54 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 16 Jul 2014 11:01:18 -0700
Simplify the function warn_if_unremovable slightly. Additionally, change
behaviour slightly. If we failed to remove the object because the object
does not exist, we can still return success back to the caller since none of
the callers depend on "fail if the file did not exist".
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Change since v21:
- adjust caller to remove redundant errno check
git-compat-util.h | 7 +++++--
refs.c | 2 +-
wrapper.c | 14 ++++++--------
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/git-compat-util.h b/git-compat-util.h
index b6f03b3..611e77b 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -706,11 +706,14 @@ void git_qsort(void *base, size_t nmemb, size_t size,
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
- * Always returns the return value of unlink(2).
+ * Returns 0 on success which includes trying to unlink an object that does
+ * not exist.
*/
int unlink_or_warn(const char *path);
/*
- * Likewise for rmdir(2).
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success which includes trying to remove a directory that does
+ * not exist.
*/
int rmdir_or_warn(const char *path);
/*
diff --git a/refs.c b/refs.c
index 7235574..d0565b1 100644
--- a/refs.c
+++ b/refs.c
@@ -2557,7 +2557,7 @@ static int delete_ref_loose(struct ref_lock *lock, int flag)
lock->lk->filename[i] = 0;
err = unlink_or_warn(lock->lk->filename);
lock->lk->filename[i] = '.';
- if (err && errno != ENOENT)
+ if (err)
return 1;
}
return 0;
diff --git a/wrapper.c b/wrapper.c
index bc1bfb8..c9605cd 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -429,14 +429,12 @@ int xmkstemp_mode(char *template, int mode)
static int warn_if_unremovable(const char *op, const char *file, int rc)
{
- if (rc < 0) {
- int err = errno;
- if (ENOENT != err) {
- warning("unable to %s %s: %s",
- op, file, strerror(errno));
- errno = err;
- }
- }
+ int err;
+ if (!rc || errno == ENOENT)
+ return 0;
+ err = errno;
+ warning("unable to %s %s: %s", op, file, strerror(errno));
+ errno = err;
return rc;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 03/24] wrapper.c: add a new function unlink_or_msg
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
2014-10-02 1:50 ` [PATCH 01/24] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
2014-10-02 1:54 ` [PATCH 02/24] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success Jonathan Nieder
@ 2014-10-02 1:55 ` Jonathan Nieder
2014-10-02 1:58 ` [PATCH 04/24] refs.c: add an err argument to delete_ref_loose Jonathan Nieder
` (20 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 1:55 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 16 Jul 2014 11:20:36 -0700
This behaves like unlink_or_warn except that on failure it writes the message
to its 'err' argument, which the caller can display in an appropriate way or
ignore.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
---
As before.
git-compat-util.h | 9 +++++++++
wrapper.c | 14 ++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/git-compat-util.h b/git-compat-util.h
index 611e77b..5ee140c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -307,6 +307,8 @@ extern char *gitbasename(char *);
#include "wildmatch.h"
+struct strbuf;
+
/* General helper functions */
extern void vreportf(const char *prefix, const char *err, va_list params);
extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
@@ -710,6 +712,13 @@ void git_qsort(void *base, size_t nmemb, size_t size,
* not exist.
*/
int unlink_or_warn(const char *path);
+ /*
+ * Tries to unlink file. Returns 0 if unlink succeeded
+ * or the file already didn't exist. Returns -1 and
+ * appends a message to err suitable for
+ * 'error("%s", err->buf)' on error.
+ */
+int unlink_or_msg(const char *file, struct strbuf *err);
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
* Returns 0 on success which includes trying to remove a directory that does
diff --git a/wrapper.c b/wrapper.c
index c9605cd..ec7a08b 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -438,6 +438,20 @@ static int warn_if_unremovable(const char *op, const char *file, int rc)
return rc;
}
+int unlink_or_msg(const char *file, struct strbuf *err)
+{
+ int rc = unlink(file);
+
+ assert(err);
+
+ if (!rc || errno == ENOENT)
+ return 0;
+
+ strbuf_addf(err, "unable to unlink %s: %s",
+ file, strerror(errno));
+ return -1;
+}
+
int unlink_or_warn(const char *file)
{
return warn_if_unremovable("unlink", file, unlink(file));
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 04/24] refs.c: add an err argument to delete_ref_loose
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (2 preceding siblings ...)
2014-10-02 1:55 ` [PATCH 03/24] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
@ 2014-10-02 1:58 ` Jonathan Nieder
2014-10-02 1:59 ` [PATCH 05/24] refs.c: pass the ref log message to _create/delete/update instead of _commit Jonathan Nieder
` (19 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 1:58 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 15 May 2014 08:25:23 -0700
Add an err argument to delete_loose_ref so that we can pass a descriptive
error string back to the caller. Pass the err argument from transaction
commit to this function so that transaction users will have a nice error
string if the transaction failed due to delete_ref_loose.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Changes since v21:
- s/delete_loose_ref/delete_ref_loose/ once in commit message (but
the other one still needs fixing)
refs.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/refs.c b/refs.c
index d0565b1..5609622 100644
--- a/refs.c
+++ b/refs.c
@@ -2548,16 +2548,16 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
return ret;
}
-static int delete_ref_loose(struct ref_lock *lock, int flag)
+static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
{
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/* loose */
- int err, i = strlen(lock->lk->filename) - 5; /* .lock */
+ int res, i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
- err = unlink_or_warn(lock->lk->filename);
+ res = unlink_or_msg(lock->lk->filename, err);
lock->lk->filename[i] = '.';
- if (err)
+ if (res)
return 1;
}
return 0;
@@ -3604,7 +3604,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- ret |= delete_ref_loose(update->lock, update->type);
+ ret |= delete_ref_loose(update->lock, update->type,
+ err);
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 05/24] refs.c: pass the ref log message to _create/delete/update instead of _commit
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (3 preceding siblings ...)
2014-10-02 1:58 ` [PATCH 04/24] refs.c: add an err argument to delete_ref_loose Jonathan Nieder
@ 2014-10-02 1:59 ` Jonathan Nieder
2014-10-02 2:00 ` [PATCH 06/24] rename_ref: don't ask read_ref_full where the ref came from Jonathan Nieder
` (18 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 1:59 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 30 Apr 2014 12:22:42 -0700
Change the ref transaction API so that we pass the reflog message to the
create/delete/update functions instead of to ref_transaction_commit.
This allows different reflog messages for each ref update in a multi-ref
transaction.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Changes since v21:
- cleaned up commit message
- clarified ownership of msg in comment in refs.h
branch.c | 4 ++--
builtin/commit.c | 4 ++--
builtin/receive-pack.c | 5 +++--
builtin/replace.c | 5 +++--
builtin/tag.c | 4 ++--
builtin/update-ref.c | 13 +++++++------
fast-import.c | 8 ++++----
refs.c | 34 +++++++++++++++++++++-------------
refs.h | 12 ++++++------
sequencer.c | 4 ++--
walker.c | 5 ++---
11 files changed, 54 insertions(+), 44 deletions(-)
diff --git a/branch.c b/branch.c
index 37ac555..76a8ec9 100644
--- a/branch.c
+++ b/branch.c
@@ -301,8 +301,8 @@ void create_branch(const char *head,
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, sha1,
- null_sha1, 0, !forcing, &err) ||
- ref_transaction_commit(transaction, msg, &err))
+ null_sha1, 0, !forcing, msg, &err) ||
+ ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
diff --git a/builtin/commit.c b/builtin/commit.c
index 9bf1003..d23e876 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1767,8 +1767,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
ref_transaction_update(transaction, "HEAD", sha1,
current_head
? current_head->object.sha1 : NULL,
- 0, !!current_head, &err) ||
- ref_transaction_commit(transaction, sb.buf, &err)) {
+ 0, !!current_head, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
rollback_index_files();
die("%s", err.buf);
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 224fadc..d1f4cf7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -585,8 +585,9 @@ static const char *update(struct command *cmd, struct shallow_info *si)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, namespaced_name,
- new_sha1, old_sha1, 0, 1, &err) ||
- ref_transaction_commit(transaction, "push", &err)) {
+ new_sha1, old_sha1, 0, 1, "push",
+ &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index 1fcd06d..9d03b84 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -169,8 +169,9 @@ static int replace_object_sha1(const char *object_ref,
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, ref, repl, prev, 0, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err))
+ ref_transaction_update(transaction, ref, repl, prev,
+ 0, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
diff --git a/builtin/tag.c b/builtin/tag.c
index f3f172f..70d28c5 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -705,8 +705,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, object, prev,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err))
+ 0, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
if (force && !is_null_sha1(prev) && hashcmp(prev, object))
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 54a48c0..6c9be05 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -14,6 +14,7 @@ static const char * const git_update_ref_usage[] = {
static char line_termination = '\n';
static int update_flags;
+static const char *msg;
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@@ -198,7 +199,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -229,7 +230,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
die("create %s: extra input: %s", refname, next);
if (ref_transaction_create(transaction, refname, new_sha1,
- update_flags, &err))
+ update_flags, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -264,7 +265,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
die("delete %s: extra input: %s", refname, next);
if (ref_transaction_delete(transaction, refname, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -300,7 +301,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
die("verify %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
@@ -354,7 +355,7 @@ static void update_refs_stdin(struct ref_transaction *transaction)
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname, *oldval, *msg = NULL;
+ const char *refname, *oldval;
unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
struct option options[] = {
@@ -385,7 +386,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (end_null)
line_termination = '\0';
update_refs_stdin(transaction);
- if (ref_transaction_commit(transaction, msg, &err))
+ if (ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
diff --git a/fast-import.c b/fast-import.c
index e7f6e37..51dfaad 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1708,8 +1708,8 @@ static int update_branch(struct branch *b)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, msg, &err)) {
+ 0, 1, msg, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&err);
@@ -1749,12 +1749,12 @@ static void dump_tags(void)
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
if (ref_transaction_update(transaction, ref_name.buf, t->sha1,
- NULL, 0, 0, &err)) {
+ NULL, 0, 0, msg, &err)) {
failure |= error("%s", err.buf);
goto cleanup;
}
}
- if (ref_transaction_commit(transaction, msg, &err))
+ if (ref_transaction_commit(transaction, &err))
failure |= error("%s", err.buf);
cleanup:
diff --git a/refs.c b/refs.c
index 5609622..99a9b86 100644
--- a/refs.c
+++ b/refs.c
@@ -2396,8 +2396,8 @@ static void prune_ref(struct ref_to_prune *r)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, r->name, r->sha1,
- REF_ISPRUNING, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err)) {
+ REF_ISPRUNING, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&err);
@@ -2571,8 +2571,8 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, refname, sha1, delopt,
- sha1 && !is_null_sha1(sha1), &err) ||
- ref_transaction_commit(transaction, NULL, &err)) {
+ sha1 && !is_null_sha1(sha1), NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
@@ -3350,6 +3350,7 @@ struct ref_update {
int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
struct ref_lock *lock;
int type;
+ char *msg;
const char refname[FLEX_ARRAY];
};
@@ -3392,9 +3393,10 @@ void ref_transaction_free(struct ref_transaction *transaction)
if (!transaction)
return;
- for (i = 0; i < transaction->nr; i++)
+ for (i = 0; i < transaction->nr; i++) {
+ free(transaction->updates[i]->msg);
free(transaction->updates[i]);
-
+ }
free(transaction->updates);
free(transaction);
}
@@ -3415,7 +3417,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
@@ -3432,13 +3434,15 @@ int ref_transaction_update(struct ref_transaction *transaction,
update->have_old = have_old;
if (have_old)
hashcpy(update->old_sha1, old_sha1);
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
- int flags,
+ int flags, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
@@ -3455,13 +3459,15 @@ int ref_transaction_create(struct ref_transaction *transaction,
hashclr(update->old_sha1);
update->flags = flags;
update->have_old = 1;
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
@@ -3479,6 +3485,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
assert(!is_null_sha1(old_sha1));
hashcpy(update->old_sha1, old_sha1);
}
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
@@ -3492,8 +3500,8 @@ int update_ref(const char *action, const char *refname,
t = ref_transaction_begin(&err);
if (!t ||
ref_transaction_update(t, refname, sha1, oldval, flags,
- !!oldval, &err) ||
- ref_transaction_commit(t, action, &err)) {
+ !!oldval, action, &err) ||
+ ref_transaction_commit(t, &err)) {
const char *str = "update_ref failed for ref '%s': %s";
ref_transaction_free(t);
@@ -3539,7 +3547,7 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
}
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err)
+ struct strbuf *err)
{
int ret = 0, delnum = 0, i;
const char **delnames;
@@ -3588,7 +3596,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (!is_null_sha1(update->new_sha1)) {
ret = write_ref_sha1(update->lock, update->new_sha1,
- msg);
+ update->msg);
update->lock = NULL; /* freed by write_ref_sha1 */
if (ret) {
if (err)
diff --git a/refs.h b/refs.h
index 69ef28c..aded545 100644
--- a/refs.h
+++ b/refs.h
@@ -271,8 +271,8 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
* The following functions add a reference check or update to a
* ref_transaction. In all of them, refname is the name of the
* reference to be affected. The functions make internal copies of
- * refname, so the caller retains ownership of the parameter. flags
- * can be REF_NODEREF; it is passed to update_ref_lock().
+ * refname and msg, so the caller retains ownership of these parameters.
+ * flags can be REF_NODEREF; it is passed to update_ref_lock().
*/
/*
@@ -289,7 +289,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err);
/*
@@ -304,7 +304,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
- int flags,
+ int flags, const char *msg,
struct strbuf *err);
/*
@@ -318,7 +318,7 @@ int ref_transaction_create(struct ref_transaction *transaction,
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err);
/*
@@ -327,7 +327,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
* problem.
*/
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err);
+ struct strbuf *err);
/*
* Free an existing transaction and all associated data.
diff --git a/sequencer.c b/sequencer.c
index 5e93b6a..c5b7b8a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -286,8 +286,8 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn ? null_sha1 : from,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, sb.buf, &err)) {
+ 0, 1, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&sb);
diff --git a/walker.c b/walker.c
index b8a5441..3c6fb8a 100644
--- a/walker.c
+++ b/walker.c
@@ -298,14 +298,13 @@ int walker_fetch(struct walker *walker, int targets, char **target,
strbuf_addf(&refname, "refs/%s", write_ref[i]);
if (ref_transaction_update(transaction, refname.buf,
&sha1[20 * i], NULL, 0, 0,
+ msg ? msg : "fetch (unknown)",
&err)) {
error("%s", err.buf);
goto done;
}
}
- if (ref_transaction_commit(transaction,
- msg ? msg : "fetch (unknown)",
- &err)) {
+ if (ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
goto done;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 06/24] rename_ref: don't ask read_ref_full where the ref came from
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (4 preceding siblings ...)
2014-10-02 1:59 ` [PATCH 05/24] refs.c: pass the ref log message to _create/delete/update instead of _commit Jonathan Nieder
@ 2014-10-02 2:00 ` Jonathan Nieder
2014-10-02 2:01 ` [PATCH 07/24] refs.c: refuse to lock badly named refs in lock_ref_sha1_basic Jonathan Nieder
` (17 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:00 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 30 Apr 2014 12:41:04 -0700
We call read_ref_full with a pointer to flags from rename_ref but since
we never actually use the returned flags we can just pass NULL here instead.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
---
As before.
refs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/refs.c b/refs.c
index 99a9b86..39571f5 100644
--- a/refs.c
+++ b/refs.c
@@ -2671,7 +2671,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+ if (!read_ref_full(newrefname, sha1, 1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 07/24] refs.c: refuse to lock badly named refs in lock_ref_sha1_basic
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (5 preceding siblings ...)
2014-10-02 2:00 ` [PATCH 06/24] rename_ref: don't ask read_ref_full where the ref came from Jonathan Nieder
@ 2014-10-02 2:01 ` Jonathan Nieder
2014-10-02 2:02 ` [PATCH 08/24] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
` (16 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:01 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 10:40:10 -0700
Move the check for check_refname_format from lock_any_ref_for_update to
lock_ref_sha1_basic. At some later stage we will get rid of
lock_any_ref_for_update completely. This has no visible impact to callers
except for the inability to lock badly named refs, which is not possible
today already for other reasons.(*)
Keep lock_any_ref_for_update as a no-op wrapper. It is the public facing
version of this interface and keeping it as a separate function will make
it easier to experiment with the internal lock_ref_sha1_basic signature.
(*) For example, if lock_ref_sha1_basic checks the refname format and
refuses to lock badly named refs, it will not be possible to delete
such refs because the first step of deletion is to lock the ref. We
currently already fail in that case because these refs are not recognized
to exist:
$ cp .git/refs/heads/master .git/refs/heads/echo...\*\*
$ git branch -D .git/refs/heads/echo...\*\*
error: branch '.git/refs/heads/echo...**' not found.
This has been broken for a while. Later patches in the series will start
repairing the handling of badly named refs.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Change since v21:
- clarified commit message
refs.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/refs.c b/refs.c
index 39571f5..3c2ce57 100644
--- a/refs.c
+++ b/refs.c
@@ -2091,6 +2091,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int missing = 0;
int attempts_remaining = 3;
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
@@ -2182,8 +2187,6 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- return NULL;
return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 08/24] refs.c: call lock_ref_sha1_basic directly from commit
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (6 preceding siblings ...)
2014-10-02 2:01 ` [PATCH 07/24] refs.c: refuse to lock badly named refs in lock_ref_sha1_basic Jonathan Nieder
@ 2014-10-02 2:02 ` Jonathan Nieder
2014-10-02 2:03 ` [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available Jonathan Nieder
` (15 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:02 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 10:43:39 -0700
Skip using the lock_any_ref_for_update wrapper and call lock_ref_sha1_basic
directly from the commit function.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
---
As before.
refs.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/refs.c b/refs.c
index 3c2ce57..f124c2b 100644
--- a/refs.c
+++ b/refs.c
@@ -3578,12 +3578,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
- update->lock = lock_any_ref_for_update(update->refname,
- (update->have_old ?
- update->old_sha1 :
- NULL),
- update->flags,
- &update->type);
+ update->lock = lock_ref_sha1_basic(update->refname,
+ (update->have_old ?
+ update->old_sha1 :
+ NULL),
+ update->flags,
+ &update->type);
if (!update->lock) {
if (err)
strbuf_addf(err, "Cannot lock the ref '%s'.",
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (7 preceding siblings ...)
2014-10-02 2:02 ` [PATCH 08/24] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
@ 2014-10-02 2:03 ` Jonathan Nieder
2014-10-02 19:18 ` Junio C Hamano
2014-10-02 2:05 ` [PATCH 10/24] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
` (14 subsequent siblings)
23 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:03 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 11:16:07 -0700
Change is_refname_available to take a list of strings to exclude when
checking for conflicts instead of just one single name. We can already
exclude a single name for the sake of renames. This generalizes that support.
ref_transaction_commit already tracks a set of refs that are being deleted
in an array. This array is then used to exclude refs from being written to
the packed-refs file. At some stage we will want to change this array to a
struct string_list and then we can pass it to is_refname_available via the
call to lock_ref_sha1_basic. That will allow us to perform transactions
that perform multiple renames as long as there are no conflicts within the
starting or ending state.
For example, that would allow a single transaction that contains two
renames that are both individually conflicting:
m -> n/n
n -> m/m
No functional change intended yet.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- clarified commit message
- clarified comments
refs.c | 44 +++++++++++++++++++++++++++++---------------
1 file changed, 29 insertions(+), 15 deletions(-)
diff --git a/refs.c b/refs.c
index f124c2b..6820c93 100644
--- a/refs.c
+++ b/refs.c
@@ -801,14 +801,16 @@ static int names_conflict(const char *refname1, const char *refname2)
struct name_conflict_cb {
const char *refname;
- const char *oldrefname;
const char *conflicting_refname;
+ struct string_list *skiplist;
};
static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
{
struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
- if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
+
+ if (data->skiplist &&
+ string_list_has_string(data->skiplist, entry->name))
return 0;
if (names_conflict(data->refname, entry->name)) {
data->conflicting_refname = entry->name;
@@ -820,17 +822,18 @@ static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
/*
* Return true iff a reference named refname could be created without
* conflicting with the name of an existing reference in dir. If
- * oldrefname is non-NULL, ignore potential conflicts with oldrefname
- * (e.g., because oldrefname is scheduled for deletion in the same
- * operation).
+ * skiplist is non-NULL, ignore potential conflicts with names in
+ * skiplist (e.g., because those refs are scheduled for deletion in
+ * the same operation). skiplist must be sorted.
*/
-static int is_refname_available(const char *refname, const char *oldrefname,
- struct ref_dir *dir)
+static int is_refname_available(const char *refname,
+ struct ref_dir *dir,
+ struct string_list *skiplist)
{
struct name_conflict_cb data;
data.refname = refname;
- data.oldrefname = oldrefname;
data.conflicting_refname = NULL;
+ data.skiplist = skiplist;
sort_ref_dir(dir);
if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
@@ -2080,6 +2083,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
*/
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
+ struct string_list *skiplist,
int flags, int *type_p)
{
char *ref_file;
@@ -2129,7 +2133,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
* name is a proper prefix of our refname.
*/
if (missing &&
- !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
+ !is_refname_available(refname, get_packed_refs(&ref_cache),
+ skiplist)) {
last_errno = ENOTDIR;
goto error_return;
}
@@ -2187,7 +2192,7 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+ return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
}
/*
@@ -2648,6 +2653,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
struct stat loginfo;
int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
const char *symref = NULL;
+ struct string_list skiplist = STRING_LIST_INIT_NODUP;
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
@@ -2659,11 +2665,18 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (!symref)
return error("refname %s not found", oldrefname);
- if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
+ string_list_insert(&skiplist, oldrefname);
+ if (!is_refname_available(newrefname, get_packed_refs(&ref_cache),
+ &skiplist)) {
+ string_list_clear(&skiplist, 0);
return 1;
-
- if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
+ }
+ if (!is_refname_available(newrefname, get_loose_refs(&ref_cache),
+ &skiplist)) {
+ string_list_clear(&skiplist, 0);
return 1;
+ }
+ string_list_clear(&skiplist, 0);
if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
@@ -2692,7 +2705,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
logmoved = log;
- lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newrefname);
goto rollback;
@@ -2707,7 +2720,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
return 0;
rollback:
- lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for rollback", oldrefname);
goto rollbacklog;
@@ -3582,6 +3595,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
(update->have_old ?
update->old_sha1 :
NULL),
+ NULL,
update->flags,
&update->type);
if (!update->lock) {
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* Re: [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available
2014-10-02 2:03 ` [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available Jonathan Nieder
@ 2014-10-02 19:18 ` Junio C Hamano
2014-10-03 18:51 ` Jonathan Nieder
0 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-10-02 19:18 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> diff --git a/refs.c b/refs.c
> index f124c2b..6820c93 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -801,14 +801,16 @@ static int names_conflict(const char *refname1, const char *refname2)
>
> struct name_conflict_cb {
> const char *refname;
> - const char *oldrefname;
> const char *conflicting_refname;
> + struct string_list *skiplist;
> };
As cbe73331 (refs: speed up is_refname_available, 2014-09-10)
touches the same area and is now in 'master', the logic around here
in this series needs to be reworked.
Thanks.
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available
2014-10-02 19:18 ` Junio C Hamano
@ 2014-10-03 18:51 ` Jonathan Nieder
2014-10-03 19:05 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-03 18:51 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Junio C Hamano wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
>> diff --git a/refs.c b/refs.c
>> index f124c2b..6820c93 100644
>> --- a/refs.c
>> +++ b/refs.c
>> @@ -801,14 +801,16 @@ static int names_conflict(const char *refname1, const char *refname2)
>>
>> struct name_conflict_cb {
>> const char *refname;
>> - const char *oldrefname;
>> const char *conflicting_refname;
>> + struct string_list *skiplist;
>> };
>
> As cbe73331 (refs: speed up is_refname_available, 2014-09-10)
> touches the same area and is now in 'master', the logic around here
> in this series needs to be reworked.
Thanks for the heads up. (I hadn't realized the ref-transaction-1 was
part of 'master' --- yay!) Rebased and reworked:
https://code-review.googlesource.com/1027
I'll give a week or so for review comments on this version of the
series and then post a hopefully final version.
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available
2014-10-03 18:51 ` Jonathan Nieder
@ 2014-10-03 19:05 ` Junio C Hamano
2014-10-03 21:39 ` [PATCH v22.5 " Jonathan Nieder
0 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-10-03 19:05 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> Junio C Hamano wrote:
> ...
>> As cbe73331 (refs: speed up is_refname_available, 2014-09-10)
>> touches the same area and is now in 'master', the logic around here
>> in this series needs to be reworked.
>
> Thanks for the heads up. (I hadn't realized the ref-transaction-1 was
> part of 'master' --- yay!) Rebased and reworked:
> https://code-review.googlesource.com/1027
Heh, I was sort of expecting that heavy-weight contributors that
throw many and/or large patch series would at least be paying
attention to "What's cooking" reports.
Will take a look over there (I really hate having to context switch
only to review this series, though).
> I'll give a week or so for review comments on this version of the
> series and then post a hopefully final version.
Thanks.
^ permalink raw reply [flat|nested] 130+ messages in thread
* [PATCH v22.5 09/24] refs.c: pass a list of names to skip to is_refname_available
2014-10-03 19:05 ` Junio C Hamano
@ 2014-10-03 21:39 ` Jonathan Nieder
2014-10-07 19:26 ` Junio C Hamano
0 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-03 21:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Ronnie Sahlberg, git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 1 May 2014 11:16:07 -0700
commit 23acf975d74825789112a3a7ba97dbbdc76904f4 upstream.
Change is_refname_available to take a list of strings to exclude when
checking for conflicts instead of just one single name. We can already
exclude a single name for the sake of renames. This generalizes that support.
ref_transaction_commit already tracks a set of refs that are being deleted
in an array. This array is then used to exclude refs from being written to
the packed-refs file. At some stage we will want to change this array to a
struct string_list and then we can pass it to is_refname_available via the
call to lock_ref_sha1_basic. That will allow us to perform transactions
that perform multiple renames as long as there are no conflicts within the
starting or ending state.
For example, that would allow a single transaction that contains two
renames that are both individually conflicting:
m -> n/n
n -> m/m
No functional change intended yet.
Change-Id: I8c68f0a47eef25759d572436ba683cb7b1ccb7eb
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Junio C Hamano wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
>> Thanks for the heads up. (I hadn't realized the ref-transaction-1 was
>> part of 'master' --- yay!) Rebased and reworked:
>> https://code-review.googlesource.com/1027
>
> Heh, I was sort of expecting that heavy-weight contributors that
> throw many and/or large patch series would at least be paying
> attention to "What's cooking" reports.
That's fair. I don't pay enough attention to the 'graduated' section.
> Will take a look over there (I really hate having to context switch
> only to review this series, though).
Here's a copy to save a trip.
refs.c | 50 ++++++++++++++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 18 deletions(-)
diff --git a/refs.c b/refs.c
index 83b2c77..05e42a7 100644
--- a/refs.c
+++ b/refs.c
@@ -785,13 +785,13 @@ static void prime_ref_dir(struct ref_dir *dir)
}
}
-static int entry_matches(struct ref_entry *entry, const char *refname)
+static int entry_matches(struct ref_entry *entry, const struct string_list *list)
{
- return refname && !strcmp(entry->name, refname);
+ return list && string_list_has_string(list, entry->name);
}
struct nonmatching_ref_data {
- const char *skip;
+ const struct string_list *skip;
struct ref_entry *found;
};
@@ -815,16 +815,19 @@ static void report_refname_conflict(struct ref_entry *entry,
/*
* Return true iff a reference named refname could be created without
* conflicting with the name of an existing reference in dir. If
- * oldrefname is non-NULL, ignore potential conflicts with oldrefname
- * (e.g., because oldrefname is scheduled for deletion in the same
+ * skip is non-NULL, ignore potential conflicts with refs in skip
+ * (e.g., because they are scheduled for deletion in the same
* operation).
*
* Two reference names conflict if one of them exactly matches the
* leading components of the other; e.g., "foo/bar" conflicts with
* both "foo" and with "foo/bar/baz" but not with "foo/bar" or
* "foo/barbados".
+ *
+ * skip must be sorted.
*/
-static int is_refname_available(const char *refname, const char *oldrefname,
+static int is_refname_available(const char *refname,
+ const struct string_list *skip,
struct ref_dir *dir)
{
const char *slash;
@@ -838,12 +841,12 @@ static int is_refname_available(const char *refname, const char *oldrefname,
* looking for a conflict with a leaf entry.
*
* If we find one, we still must make sure it is
- * not "oldrefname".
+ * not in "skip".
*/
pos = search_ref_dir(dir, refname, slash - refname);
if (pos >= 0) {
struct ref_entry *entry = dir->entries[pos];
- if (entry_matches(entry, oldrefname))
+ if (entry_matches(entry, skip))
return 1;
report_refname_conflict(entry, refname);
return 0;
@@ -876,13 +879,13 @@ static int is_refname_available(const char *refname, const char *oldrefname,
/*
* We found a directory named "refname". It is a
* problem iff it contains any ref that is not
- * "oldrefname".
+ * in "skip".
*/
struct ref_entry *entry = dir->entries[pos];
struct ref_dir *dir = get_ref_dir(entry);
struct nonmatching_ref_data data;
- data.skip = oldrefname;
+ data.skip = skip;
sort_ref_dir(dir);
if (!do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data))
return 1;
@@ -2137,6 +2140,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
*/
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
+ const struct string_list *skip,
int flags, int *type_p)
{
char *ref_file;
@@ -2186,7 +2190,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
* name is a proper prefix of our refname.
*/
if (missing &&
- !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
+ !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) {
last_errno = ENOTDIR;
goto error_return;
}
@@ -2244,7 +2248,7 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+ return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
}
/*
@@ -2690,6 +2694,18 @@ static int rename_tmp_log(const char *newrefname)
return 0;
}
+static int rename_ref_available(const char *oldname, const char *newname)
+{
+ struct string_list skip = STRING_LIST_INIT_NODUP;
+ int ret;
+
+ string_list_insert(&skip, oldname);
+ ret = is_refname_available(newname, &skip, get_packed_refs(&ref_cache))
+ && is_refname_available(newname, &skip, get_loose_refs(&ref_cache));
+ string_list_clear(&skip, 0);
+ return ret;
+}
+
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
unsigned char sha1[20], orig_sha1[20];
@@ -2709,10 +2725,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (!symref)
return error("refname %s not found", oldrefname);
- if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
- return 1;
-
- if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
+ if (!rename_ref_available(oldrefname, newrefname))
return 1;
if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
@@ -2742,7 +2755,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
logmoved = log;
- lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newrefname);
goto rollback;
@@ -2757,7 +2770,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
return 0;
rollback:
- lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for rollback", oldrefname);
goto rollbacklog;
@@ -3636,6 +3649,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
(update->have_old ?
update->old_sha1 :
NULL),
+ NULL,
update->flags,
&update->type);
if (!update->lock) {
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* Re: [PATCH v22.5 09/24] refs.c: pass a list of names to skip to is_refname_available
2014-10-03 21:39 ` [PATCH v22.5 " Jonathan Nieder
@ 2014-10-07 19:26 ` Junio C Hamano
0 siblings, 0 replies; 130+ messages in thread
From: Junio C Hamano @ 2014-10-07 19:26 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
>> Will take a look over there (I really hate having to context switch
>> only to review this series, though).
>
> Here's a copy to save a trip.
The patch itself looks fine, but Gerrit really needs a way to convey
"these changes make a single topic and here is the tip" somehow. It
could be that there is and it is not well advertised, but the end
result is I visit the URL,
https://code-review.googlesource.com/#/q/project:git+topic:ref-transaction
pick and view a few patches at random to view the changes in pretty
side-by-side diff output (which by itself is fine), check "Download"
to see the fetch URL, when it is followed it often gives me only an
early half of the topic, get confused and scratch my head, give up
and abandon and the whole time doing so ends up being wasted.
^ permalink raw reply [flat|nested] 130+ messages in thread
* [PATCH 10/24] refs.c: ref_transaction_commit: distinguish name conflicts from other errors
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (8 preceding siblings ...)
2014-10-02 2:03 ` [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available Jonathan Nieder
@ 2014-10-02 2:05 ` Jonathan Nieder
2014-10-02 2:07 ` [PATCH 11/24] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
` (13 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:05 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Fri, 16 May 2014 14:14:38 -0700
In _commit, ENOTDIR can happen in the call to lock_ref_sha1_basic, either
when we lstat the new refname or if the name checking function reports that
the same type of conflict happened. In both cases, it means that we can not
create the new ref due to a name conflict.
Start defining specific return codes for _commit. TRANSACTION_NAME_CONFLICT
refers to a failure to create a ref due to a name conflict with another ref.
TRANSACTION_GENERIC_ERROR is for all other errors.
When "git fetch" is creating refs, name conflicts differ from other errors in
that they are likely to be resolved by running "git remote prune <remote>".
"git fetch" currently inspects errno to decide whether to give that advice.
Once it switches to the transaction API, it can check for
TRANSACTION_NAME_CONFLICT instead.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- clarified commit message and updated to match code
- small code cleanups
- clarified API doc, introduced TRANSACTION_GENERIC_ERROR so both error
codes have names
refs.c | 26 ++++++++++++++++----------
refs.h | 9 +++++++--
2 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/refs.c b/refs.c
index 6820c93..623a1ae 100644
--- a/refs.c
+++ b/refs.c
@@ -3583,9 +3583,10 @@ int ref_transaction_commit(struct ref_transaction *transaction,
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
- ret = ref_update_reject_duplicates(updates, n, err);
- if (ret)
+ if (ref_update_reject_duplicates(updates, n, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
+ }
/* Acquire all locks while verifying old values */
for (i = 0; i < n; i++) {
@@ -3599,10 +3600,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
update->flags,
&update->type);
if (!update->lock) {
+ ret = (errno == ENOTDIR)
+ ? TRANSACTION_NAME_CONFLICT
+ : TRANSACTION_GENERIC_ERROR;
if (err)
strbuf_addf(err, "Cannot lock the ref '%s'.",
update->refname);
- ret = 1;
goto cleanup;
}
}
@@ -3612,15 +3615,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (!is_null_sha1(update->new_sha1)) {
- ret = write_ref_sha1(update->lock, update->new_sha1,
- update->msg);
- update->lock = NULL; /* freed by write_ref_sha1 */
- if (ret) {
+ if (write_ref_sha1(update->lock, update->new_sha1,
+ update->msg)) {
+ update->lock = NULL; /* freed by write_ref_sha1 */
if (err)
strbuf_addf(err, "Cannot update the ref '%s'.",
update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
+ update->lock = NULL; /* freed by write_ref_sha1 */
}
}
@@ -3629,14 +3633,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- ret |= delete_ref_loose(update->lock, update->type,
- err);
+ if (delete_ref_loose(update->lock, update->type, err))
+ ret = TRANSACTION_GENERIC_ERROR;
+
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
}
- ret |= repack_without_refs(delnames, delnum, err);
+ if (repack_without_refs(delnames, delnum, err))
+ ret = TRANSACTION_GENERIC_ERROR;
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
diff --git a/refs.h b/refs.h
index aded545..fd63b47 100644
--- a/refs.h
+++ b/refs.h
@@ -323,9 +323,14 @@ int ref_transaction_delete(struct ref_transaction *transaction,
/*
* Commit all of the changes that have been queued in transaction, as
- * atomically as possible. Return a nonzero value if there is a
- * problem.
+ * atomically as possible.
+ *
+ * Returns 0 for success, or one of the below error codes for errors.
*/
+/* Naming conflict (for example, the ref names A and A/B conflict). */
+#define TRANSACTION_NAME_CONFLICT -1
+/* All other errors. */
+#define TRANSACTION_GENERIC_ERROR -2
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 11/24] fetch.c: change s_update_ref to use a ref transaction
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (9 preceding siblings ...)
2014-10-02 2:05 ` [PATCH 10/24] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
@ 2014-10-02 2:07 ` Jonathan Nieder
2014-10-02 2:08 ` [PATCH 12/24] refs.c: make write_ref_sha1 static Jonathan Nieder
` (12 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:07 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Mon, 28 Apr 2014 13:49:07 -0700
Change s_update_ref to use a ref transaction for the ref update.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- tweaked handling of ref_transaction_commit result (no functional
change)
builtin/fetch.c | 34 ++++++++++++++++++++++++----------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 55f457c..30b40f6 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -375,23 +375,37 @@ static int s_update_ref(const char *action,
{
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
- static struct ref_lock *lock;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+ int ret, df_conflict = 0;
if (dry_run)
return 0;
if (!rla)
rla = default_rla.buf;
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
- lock = lock_any_ref_for_update(ref->name,
- check_old ? ref->old_sha1 : NULL,
- 0, NULL);
- if (!lock)
- return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
- STORE_REF_ERROR_OTHER;
- if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
- return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
- STORE_REF_ERROR_OTHER;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref->name, ref->new_sha1,
+ ref->old_sha1, 0, check_old, msg, &err))
+ goto fail;
+
+ ret = ref_transaction_commit(transaction, &err);
+ if (ret) {
+ df_conflict = (ret == TRANSACTION_NAME_CONFLICT);
+ goto fail;
+ }
+
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
return 0;
+fail:
+ ref_transaction_free(transaction);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
+ : STORE_REF_ERROR_OTHER;
}
#define REFCOL_WIDTH 10
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 12/24] refs.c: make write_ref_sha1 static
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (10 preceding siblings ...)
2014-10-02 2:07 ` [PATCH 11/24] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
@ 2014-10-02 2:08 ` Jonathan Nieder
2014-10-02 2:10 ` [PATCH 13/24] refs.c: change resolve_ref_unsafe reading argument to be a flags field Jonathan Nieder
` (11 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:08 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Mon, 28 Apr 2014 15:36:58 -0700
No external users call write_ref_sha1 any more so let's declare it static.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- punctuation fix in commit message
- grammar tweak in doc comment
refs.c | 10 ++++++++--
refs.h | 3 ---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/refs.c b/refs.c
index 623a1ae..f596a9f 100644
--- a/refs.c
+++ b/refs.c
@@ -2645,6 +2645,9 @@ static int rename_tmp_log(const char *newrefname)
return 0;
}
+static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
+ const char *logmsg);
+
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
unsigned char sha1[20], orig_sha1[20];
@@ -2900,8 +2903,11 @@ static int is_branch(const char *refname)
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
-/* This function must return a meaningful errno */
-int write_ref_sha1(struct ref_lock *lock,
+/*
+ * Write sha1 into the ref specified by the lock. Make sure that errno
+ * is sane on error.
+ */
+static int write_ref_sha1(struct ref_lock *lock,
const unsigned char *sha1, const char *logmsg)
{
static char term = '\n';
diff --git a/refs.h b/refs.h
index fd63b47..3bb16db 100644
--- a/refs.h
+++ b/refs.h
@@ -195,9 +195,6 @@ extern int commit_ref(struct ref_lock *lock);
/** Release any lock taken but not written. **/
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);
-
/*
* Setup reflog before using. Set errno to something meaningful on failure.
*/
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 13/24] refs.c: change resolve_ref_unsafe reading argument to be a flags field
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (11 preceding siblings ...)
2014-10-02 2:08 ` [PATCH 12/24] refs.c: make write_ref_sha1 static Jonathan Nieder
@ 2014-10-02 2:10 ` Jonathan Nieder
2014-10-02 2:10 ` [PATCH 14/24] reflog test: test interaction with detached HEAD Jonathan Nieder
` (10 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:10 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Tue, 15 Jul 2014 12:59:36 -0700
resolve_ref_unsafe takes a boolean argument for reading (a nonexistent ref
resolves successfully for writing but not for reading). Change this to be
a flags field instead, and pass the new constant RESOLVE_REF_READING when
we want this behaviour.
While at it, swap two of the arguments in the function to put output
arguments at the end. As a nice side effect, this ensures that we can
catch callers that were unaware of the new API so they can be audited.
Give the wrapper functions resolve_refdup and read_ref_full the same
treatment for consistency.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- clarified commit message
- put output parameters last
branch.c | 2 +-
builtin/blame.c | 2 +-
builtin/branch.c | 9 ++---
builtin/checkout.c | 6 ++--
builtin/clone.c | 2 +-
builtin/commit.c | 2 +-
builtin/fmt-merge-msg.c | 2 +-
builtin/for-each-ref.c | 6 ++--
builtin/fsck.c | 2 +-
builtin/log.c | 3 +-
builtin/merge.c | 2 +-
builtin/notes.c | 2 +-
builtin/receive-pack.c | 4 +--
builtin/remote.c | 5 +--
builtin/show-branch.c | 7 ++--
builtin/symbolic-ref.c | 2 +-
bundle.c | 2 +-
cache.h | 23 ++++++------
http-backend.c | 4 ++-
notes-merge.c | 2 +-
reflog-walk.c | 5 +--
refs.c | 93 ++++++++++++++++++++++++++++---------------------
remote.c | 11 +++---
sequencer.c | 4 +--
transport-helper.c | 5 ++-
transport.c | 5 +--
upload-pack.c | 2 +-
wt-status.c | 2 +-
28 files changed, 124 insertions(+), 92 deletions(-)
diff --git a/branch.c b/branch.c
index 76a8ec9..adb07c6 100644
--- a/branch.c
+++ b/branch.c
@@ -186,7 +186,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die(_("Cannot force update the current branch."));
}
diff --git a/builtin/blame.c b/builtin/blame.c
index a52a279..5cbd38f 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2292,7 +2292,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
commit->object.type = OBJ_COMMIT;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
die("no such ref: HEAD");
parent_tail = append_parent(parent_tail, head_sha1);
diff --git a/builtin/branch.c b/builtin/branch.c
index 652b1d2..e5d1377 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -129,7 +129,8 @@ static int branch_merged(int kind, const char *name,
branch->merge[0] &&
branch->merge[0]->dst &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+ resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+ sha1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
if (!reference_rev)
@@ -233,7 +234,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, sha1, 0, &flags);
+ target = resolve_ref_unsafe(name, 0, sha1, &flags);
if (!target ||
(!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
error(remote_branch
@@ -296,7 +297,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst, *cp;
- dst = resolve_ref_unsafe(src, sha1, 0, &flag);
+ dst = resolve_ref_unsafe(src, 0, sha1, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix && (cp = skip_prefix(dst, prefix)))
@@ -862,7 +863,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", head_sha1, 0, NULL);
+ head = resolve_refdup("HEAD", 0, head_sha1, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD")) {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1dc56e..a5fef2d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -356,7 +356,7 @@ static int checkout_paths(const struct checkout_opts *opts,
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- read_ref_full("HEAD", rev, 0, &flag);
+ read_ref_full("HEAD", 0, rev, &flag);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -771,7 +771,7 @@ static int switch_branches(const struct checkout_opts *opts,
unsigned char rev[20];
int flag, writeout_error = 0;
memset(&old, 0, sizeof(old));
- old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
+ old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
old.commit = lookup_commit_reference_gently(rev, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -1068,7 +1068,7 @@ static int checkout_branch(struct checkout_opts *opts,
unsigned char rev[20];
int flag;
- if (!read_ref_full("HEAD", rev, 0, &flag) &&
+ if (!read_ref_full("HEAD", 0, rev, &flag) &&
(flag & REF_ISSYMREF) && is_null_sha1(rev))
return switch_unborn_to_new_branch(opts);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index b12989d..0f5c880 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -622,7 +622,7 @@ static int checkout(void)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", sha1, 1, NULL);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout.\n"));
diff --git a/builtin/commit.c b/builtin/commit.c
index d23e876..9ccc78b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1468,7 +1468,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
printf("[%s%s ",
starts_with(head, "refs/heads/") ?
head + 11 :
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 3906eda..afe05dc 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -602,7 +602,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* get current branch */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", head_sha1, 1, NULL);
+ resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (!current_branch)
die("No current branch");
if (starts_with(current_branch, "refs/heads/"))
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 3e1d5c3..20949b7 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -649,7 +649,8 @@ static void populate_value(struct refinfo *ref)
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+ ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
+ unused1, NULL);
if (!ref->symref)
ref->symref = "";
}
@@ -707,7 +708,8 @@ static void populate_value(struct refinfo *ref)
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
if (!strcmp(ref->refname, head))
v->s = "*";
else
diff --git a/builtin/fsck.c b/builtin/fsck.c
index fc150c8..7cd109a 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -560,7 +560,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_sha1, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
diff --git a/builtin/log.c b/builtin/log.c
index a7ba211..493440a 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1395,7 +1395,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
const char *ref;
- ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
if (ref && starts_with(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
diff --git a/builtin/merge.c b/builtin/merge.c
index 428ca24..6f56967 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1108,7 +1108,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
diff --git a/builtin/notes.c b/builtin/notes.c
index 820c341..eaf297d 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -703,7 +703,7 @@ static int merge_commit(struct notes_merge_options *o)
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
+ resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d1f4cf7..8a6e7e3 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -656,7 +656,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -817,7 +817,7 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
+ head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 401feb3..c7f82f4 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -568,7 +568,8 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
+ orig_sha1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
@@ -704,7 +705,7 @@ static int mv(int argc, const char **argv)
int flag = 0;
unsigned char sha1[20];
- read_ref_full(item->string, sha1, 1, &flag);
+ read_ref_full(item->string, RESOLVE_REF_READING, sha1, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index d873172..acc8dc1 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -727,7 +727,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
+ fake_av[0] = resolve_refdup("HEAD",
+ RESOLVE_REF_READING,
+ sha1, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -789,7 +791,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
+ head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ head_sha1, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index b6a711d..29fb3f1 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -13,7 +13,7 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
{
unsigned char sha1[20];
int flag;
- const char *refname = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
+ const char *refname = resolve_ref_unsafe(HEAD, 0, sha1, &flag);
if (!refname)
die("No such ref: %s", HEAD);
diff --git a/bundle.c b/bundle.c
index 1222952..d92e49c 100644
--- a/bundle.c
+++ b/bundle.c
@@ -311,7 +311,7 @@ int create_bundle(struct bundle_header *header, const char *path,
continue;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
- if (read_ref_full(e->name, sha1, 1, &flag))
+ if (read_ref_full(e->name, RESOLVE_REF_READING, sha1, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
diff --git a/cache.h b/cache.h
index e7ec626..5b54911 100644
--- a/cache.h
+++ b/cache.h
@@ -947,8 +947,8 @@ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
-extern int read_ref_full(const char *refname, unsigned char *sha1,
- int reading, int *flags);
+extern int read_ref_full(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
extern int read_ref(const char *refname, unsigned char *sha1);
/*
@@ -960,20 +960,20 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* or the input ref.
*
* If the reference cannot be resolved to an object, the behavior
- * depends on the "reading" argument:
+ * depends on the RESOLVE_REF_READING flag:
*
- * - If reading is set, return NULL.
+ * - If RESOLVE_REF_READING is set, return NULL.
*
- * - If reading is not set, clear sha1 and return the name of the last
- * reference name in the chain, which will either be a non-symbolic
+ * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
+ * the last reference name in the chain, which will either be a non-symbolic
* reference or an undefined reference. If this is a prelude to
* "writing" to the ref, the return value is the name of the ref
* that will actually be created or changed.
*
- * If flag is non-NULL, set the value that it points to the
+ * If flags is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
- * packed references) and REF_ISSYMREF (if the initial reference was a
- * symbolic reference).
+ * packed references), REF_ISSYMREF (if the initial reference was a
+ * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
*
* If ref is not a properly-formatted, normalized reference, return
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
@@ -981,8 +981,9 @@ extern int read_ref(const char *refname, unsigned char *sha1);
*
* errno is set to something meaningful on error.
*/
-extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
-extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
+#define RESOLVE_REF_READING 0x01
+extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
+extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/http-backend.c b/http-backend.c
index d2c0a62..e172886 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -417,7 +417,9 @@ static int show_head_ref(const char *refname, const unsigned char *sha1,
if (flag & REF_ISSYMREF) {
unsigned char unused[20];
- const char *target = resolve_ref_unsafe(refname, unused, 1, NULL);
+ const char *target = resolve_ref_unsafe(refname,
+ RESOLVE_REF_READING,
+ unused, NULL);
const char *target_nons = strip_namespace(target);
strbuf_addf(buf, "ref: %s\n", target_nons);
diff --git a/notes-merge.c b/notes-merge.c
index 94a1a8a..3c88d17 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -549,7 +549,7 @@ int notes_merge(struct notes_merge_options *o,
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
+ if (read_ref_full(o->local_ref, 0, local_sha1, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
is_null_sha1(local_sha1))
diff --git a/reflog-walk.c b/reflog-walk.c
index 9ce8b53..23345ea 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -48,7 +48,8 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
unsigned char sha1[20];
const char *name;
void *name_to_free;
- name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
+ name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
+ sha1, NULL);
if (name) {
for_each_reflog_ent(name, read_one_reflog, reflogs);
free(name_to_free);
@@ -174,7 +175,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
if (*branch == '\0') {
unsigned char sha1[20];
free(branch);
- branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ branch = resolve_refdup("HEAD", 0, sha1, NULL);
if (!branch)
die ("No current branch");
diff --git a/refs.c b/refs.c
index f596a9f..4916d16 100644
--- a/refs.c
+++ b/refs.c
@@ -1191,7 +1191,9 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- } else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
+ } else if (read_ref_full(refname.buf,
+ RESOLVE_REF_READING,
+ sha1, &flag)) {
hashclr(sha1);
flag |= REF_ISBROKEN;
}
@@ -1316,9 +1318,9 @@ static struct ref_entry *get_packed_ref(const char *refname)
* options are forwarded from resolve_safe_unsafe().
*/
static const char *handle_missing_loose_ref(const char *refname,
+ int resolve_flags,
unsigned char *sha1,
- int reading,
- int *flag)
+ int *flags)
{
struct ref_entry *entry;
@@ -1329,12 +1331,12 @@ static const char *handle_missing_loose_ref(const char *refname,
entry = get_packed_ref(refname);
if (entry) {
hashcpy(sha1, entry->u.value.sha1);
- if (flag)
- *flag |= REF_ISPACKED;
+ if (flags)
+ *flags |= REF_ISPACKED;
return refname;
}
/* The reference is not a packed reference, either. */
- if (reading) {
+ if (resolve_flags & RESOLVE_REF_READING) {
return NULL;
} else {
hashclr(sha1);
@@ -1343,21 +1345,20 @@ static const char *handle_missing_loose_ref(const char *refname,
}
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
{
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char refname_buffer[256];
- if (flag)
- *flag = 0;
+ if (flags)
+ *flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
errno = EINVAL;
return NULL;
}
-
for (;;) {
char path[PATH_MAX];
struct stat st;
@@ -1383,8 +1384,8 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
stat_ref:
if (lstat(path, &st) < 0) {
if (errno == ENOENT)
- return handle_missing_loose_ref(refname, sha1,
- reading, flag);
+ return handle_missing_loose_ref(refname,
+ resolve_flags, sha1, flags);
else
return NULL;
}
@@ -1404,8 +1405,8 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
!check_refname_format(buffer, 0)) {
strcpy(refname_buffer, buffer);
refname = refname_buffer;
- if (flag)
- *flag |= REF_ISSYMREF;
+ if (flags)
+ *flags |= REF_ISSYMREF;
continue;
}
}
@@ -1450,21 +1451,21 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
*/
if (get_sha1_hex(buffer, sha1) ||
(buffer[40] != '\0' && !isspace(buffer[40]))) {
- if (flag)
- *flag |= REF_ISBROKEN;
+ if (flags)
+ *flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
return refname;
}
- if (flag)
- *flag |= REF_ISSYMREF;
+ if (flags)
+ *flags |= REF_ISSYMREF;
buf = buffer + 4;
while (isspace(*buf))
buf++;
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
- if (flag)
- *flag |= REF_ISBROKEN;
+ if (flags)
+ *flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
@@ -1472,9 +1473,9 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
}
}
-char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
{
- const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
+ const char *ret = resolve_ref_unsafe(ref, resolve_flags, sha1, flags);
return ret ? xstrdup(ret) : NULL;
}
@@ -1485,22 +1486,22 @@ struct ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
{
- if (resolve_ref_unsafe(refname, sha1, reading, flags))
+ if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
return 0;
return -1;
}
int read_ref(const char *refname, unsigned char *sha1)
{
- return read_ref_full(refname, sha1, 1, NULL);
+ return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
}
int ref_exists(const char *refname)
{
unsigned char sha1[20];
- return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
+ return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
}
static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
@@ -1614,7 +1615,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
return 0;
}
- if (read_ref_full(refname, base, 1, &flag))
+ if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
return -1;
/*
@@ -1655,7 +1656,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
+ resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
@@ -1780,7 +1781,7 @@ static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
return 0;
}
- if (!read_ref_full("HEAD", sha1, 1, &flag))
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
return fn("HEAD", sha1, flag, cb_data);
return 0;
@@ -1860,7 +1861,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, sha1, 1, &flag))
+ if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
ret = fn(buf.buf, sha1, flag, cb_data);
strbuf_release(&buf);
@@ -1955,7 +1956,9 @@ int refname_match(const char *abbrev_name, const char *full_name)
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ if (read_ref_full(lock->ref_name,
+ mustexist ? RESOLVE_REF_READING : 0,
+ lock->old_sha1, NULL)) {
int save_errno = errno;
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
@@ -2028,7 +2031,8 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
this_result = refs_found ? sha1_from_ref : sha1;
mksnpath(fullref, sizeof(fullref), *p, len, str);
- r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
+ r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
+ this_result, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -2057,7 +2061,8 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
const char *ref, *it;
mksnpath(path, sizeof(path), *p, len, str);
- ref = resolve_ref_unsafe(path, hash, 1, NULL);
+ ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
+ hash, NULL);
if (!ref)
continue;
if (reflog_exists(path))
@@ -2092,6 +2097,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int last_errno = 0;
int type, lflags;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
+ int resolve_flags = 0;
int missing = 0;
int attempts_remaining = 3;
@@ -2103,7 +2109,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- refname = resolve_ref_unsafe(refname, lock->old_sha1, mustexist, &type);
+ if (mustexist)
+ resolve_flags |= RESOLVE_REF_READING;
+
+ refname = resolve_ref_unsafe(refname, resolve_flags,
+ lock->old_sha1, &type);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@@ -2116,7 +2126,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
error("there are still refs under '%s'", orig_refname);
goto error_return;
}
- refname = resolve_ref_unsafe(orig_refname, lock->old_sha1, mustexist, &type);
+ refname = resolve_ref_unsafe(orig_refname, resolve_flags,
+ lock->old_sha1, &type);
}
if (type_p)
*type_p = type;
@@ -2469,7 +2480,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
unsigned char sha1[20];
int flags;
- if (read_ref_full(entry->name, sha1, 0, &flags))
+ if (read_ref_full(entry->name, 0, sha1, &flags))
/* We should at least have found the packed ref. */
die("Internal error");
if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
@@ -2661,7 +2672,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
- symref = resolve_ref_unsafe(oldrefname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
+ orig_sha1, &flag);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
oldrefname);
@@ -2690,7 +2702,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, 1, NULL) &&
+ if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
@@ -2968,7 +2980,8 @@ static int write_ref_sha1(struct ref_lock *lock,
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
- head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
+ head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ head_sha1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -3335,7 +3348,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
retval = do_for_each_reflog(name, fn, cb_data);
} else {
unsigned char sha1[20];
- if (read_ref_full(name->buf, sha1, 0, NULL))
+ if (read_ref_full(name->buf, 0, sha1, NULL))
retval = error("bad ref for %s", name->buf);
else
retval = fn(name->buf, sha1, 0, cb_data);
diff --git a/remote.c b/remote.c
index 0e9459c..25b07ac 100644
--- a/remote.c
+++ b/remote.c
@@ -486,7 +486,7 @@ static void read_config(void)
return;
default_remote_name = "origin";
current_branch = NULL;
- head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
+ head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
starts_with(head_ref, "refs/heads/")) {
current_branch =
@@ -1121,7 +1121,8 @@ static char *guess_ref(const char *name, struct ref *peer)
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
+ const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
+ sha1, NULL);
if (!r)
return NULL;
@@ -1182,7 +1183,9 @@ static int match_explicit(struct ref *src, struct ref *dst,
unsigned char sha1[20];
int flag;
- dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
+ dst_value = resolve_ref_unsafe(matched_src->name,
+ RESOLVE_REF_READING,
+ sha1, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
@@ -1656,7 +1659,7 @@ static int ignore_symref_update(const char *refname)
unsigned char sha1[20];
int flag;
- if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
+ if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
}
diff --git a/sequencer.c b/sequencer.c
index c5b7b8a..70fb7a8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -366,7 +366,7 @@ static int is_index_unchanged(void)
unsigned char head_sha1[20];
struct commit *head_commit;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
return error(_("Could not resolve HEAD commit\n"));
head_commit = lookup_commit(head_sha1);
@@ -912,7 +912,7 @@ static int rollback_single_pick(void)
if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
!file_exists(git_path("REVERT_HEAD")))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", head_sha1, 0, NULL))
+ if (read_ref_full("HEAD", 0, head_sha1, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_sha1(head_sha1))
return error(_("cannot abort from a branch yet to be born"));
diff --git a/transport-helper.c b/transport-helper.c
index 84c616f..3497a5e 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -889,7 +889,10 @@ static int push_refs_with_export(struct transport *transport,
int flag;
/* Follow symbolic refs (mainly for HEAD). */
- name = resolve_ref_unsafe(ref->peer_ref->name, sha1, 1, &flag);
+ name = resolve_ref_unsafe(
+ ref->peer_ref->name,
+ RESOLVE_REF_READING,
+ sha1, &flag);
if (!name || !(flag & REF_ISSYMREF))
name = ref->peer_ref->name;
diff --git a/transport.c b/transport.c
index 325f03e..76e0a9a 100644
--- a/transport.c
+++ b/transport.c
@@ -168,7 +168,8 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
+ tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
+ sha, &flag);
if (tmp && flag & REF_ISSYMREF &&
starts_with(tmp, "refs/heads/"))
localname = tmp;
@@ -753,7 +754,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
unsigned char head_sha1[20];
char *head;
- head = resolve_refdup("HEAD", head_sha1, 1, NULL);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
diff --git a/upload-pack.c b/upload-pack.c
index 01de944..4542565 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -743,7 +743,7 @@ static int find_symref(const char *refname, const unsigned char *sha1, int flag,
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, unused, 0, &flag);
+ symref_target = resolve_ref_unsafe(refname, 0, unused, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
diff --git a/wt-status.c b/wt-status.c
index 318a191..c3cbf50 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -128,7 +128,7 @@ void wt_status_prepare(struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 14/24] reflog test: test interaction with detached HEAD
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (12 preceding siblings ...)
2014-10-02 2:10 ` [PATCH 13/24] refs.c: change resolve_ref_unsafe reading argument to be a flags field Jonathan Nieder
@ 2014-10-02 2:10 ` Jonathan Nieder
2014-10-02 2:15 ` [PATCH 15/24] branch -d: avoid repeated symref resolution Jonathan Nieder
` (9 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:10 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Junio C Hamano <gitster@pobox.com>
A proposed patch produced broken HEAD reflog entries when checking out
anything other than a branch. The testsuite still passed, so it took
a few days for the bug to be noticed.
Add tests checking the content of the reflog after detaching and
reattaching HEAD so we don't have to rely on manual testing to catch
such problems in the future.
[jn: using 'log -g --format=%H' instead of parsing --oneline output,
resetting state in each test so they can be safely reordered or
skipped]
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
New since v21. Thanks to Junio for noticing the bug.
t/t1413-reflog-detach.sh | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100755 t/t1413-reflog-detach.sh
diff --git a/t/t1413-reflog-detach.sh b/t/t1413-reflog-detach.sh
new file mode 100755
index 0000000..c730600
--- /dev/null
+++ b/t/t1413-reflog-detach.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='Test reflog interaction with detached HEAD'
+. ./test-lib.sh
+
+reset_state () {
+ git checkout master &&
+ cp saved_reflog .git/logs/HEAD
+}
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m initial &&
+ git branch side &&
+ test_tick &&
+ git commit --allow-empty -m second &&
+ cat .git/logs/HEAD >saved_reflog
+'
+
+test_expect_success baseline '
+ reset_state &&
+ git rev-parse master master^ >expect &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'switch to branch' '
+ reset_state &&
+ git rev-parse side master master^ >expect &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to other' '
+ reset_state &&
+ git rev-parse master side master master^ >expect &&
+ git checkout side &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to self' '
+ reset_state &&
+ git rev-parse master master master^ >expect &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to self' '
+ reset_state &&
+ git rev-parse master master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout master &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to other' '
+ reset_state &&
+ git rev-parse side master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_done
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 15/24] branch -d: avoid repeated symref resolution
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (13 preceding siblings ...)
2014-10-02 2:10 ` [PATCH 14/24] reflog test: test interaction with detached HEAD Jonathan Nieder
@ 2014-10-02 2:15 ` Jonathan Nieder
2014-10-02 2:15 ` [PATCH 16/24] branch -d: simplify by using RESOLVE_REF_READING flag Jonathan Nieder
` (8 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:15 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Jonathan Nieder <jrnieder@gmail.com>
Date: Wed, 10 Sep 2014 18:22:48 -0700
If a repository gets in a broken state with too much symref nesting,
it cannot be repaired with "git branch -d":
$ git symbolic-ref refs/heads/nonsense refs/heads/nonsense
$ git branch -d nonsense
error: branch 'nonsense' not found.
Worse, "git update-ref --no-deref -d" doesn't work for such repairs
either:
$ git update-ref -d refs/heads/nonsense
error: unable to resolve reference refs/heads/nonsense: Too many levels of symbolic links
Fix both by teaching resolve_ref_unsafe a new RESOLVE_REF_NO_RECURSE
flag and passing it when appropriate.
Callers can still read the value of a symref (for example to print a
message about it) with that flag set --- resolve_ref_unsafe will
resolve one level of symrefs and stop there.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
Since v21:
- renamed flag from RESOLVE_REF_NO_DEREF to _NO_RECURSE
- more detail in API docs
- only set NO_RECURSE when deleting refs. Locking refs for non-deletion
updates needs to recurse to get old_sha1 for the reflog.
- add more tests (for symrefs to refs with bad names, which should also
be deletable now)
builtin/branch.c | 3 ++-
cache.h | 6 ++++++
refs.c | 17 +++++++++++++++--
refs.h | 2 ++
t/t1400-update-ref.sh | 34 ++++++++++++++++++++++++++++++++++
t/t3200-branch.sh | 9 +++++++++
6 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index e5d1377..a334380 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -234,7 +234,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, 0, sha1, &flags);
+ target = resolve_ref_unsafe(name, RESOLVE_REF_NO_RECURSE,
+ sha1, &flags);
if (!target ||
(!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
error(remote_branch
diff --git a/cache.h b/cache.h
index 5b54911..5ca7f2b 100644
--- a/cache.h
+++ b/cache.h
@@ -970,6 +970,11 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* "writing" to the ref, the return value is the name of the ref
* that will actually be created or changed.
*
+ * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
+ * level of symbolic reference. The value stored in sha1 for a symbolic
+ * reference will always be null_sha1 in this case, and the return
+ * value is the reference that the symref refers to directly.
+ *
* If flags is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
* packed references), REF_ISSYMREF (if the initial reference was a
@@ -982,6 +987,7 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* errno is set to something meaningful on error.
*/
#define RESOLVE_REF_READING 0x01
+#define RESOLVE_REF_NO_RECURSE 0x02
extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
diff --git a/refs.c b/refs.c
index 4916d16..490e788 100644
--- a/refs.c
+++ b/refs.c
@@ -1407,6 +1407,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
refname = refname_buffer;
if (flags)
*flags |= REF_ISSYMREF;
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+ hashclr(sha1);
+ return refname;
+ }
continue;
}
}
@@ -1463,13 +1467,17 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
buf = buffer + 4;
while (isspace(*buf))
buf++;
+ refname = strcpy(refname_buffer, buf);
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+ hashclr(sha1);
+ return refname;
+ }
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
if (flags)
*flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
- refname = strcpy(refname_buffer, buf);
}
}
@@ -2111,6 +2119,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
+ if (flags & REF_NODEREF && flags & REF_DELETING)
+ resolve_flags |= RESOLVE_REF_NO_RECURSE;
refname = resolve_ref_unsafe(refname, resolve_flags,
lock->old_sha1, &type);
@@ -3610,13 +3620,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
/* Acquire all locks while verifying old values */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
+ int flags = update->flags;
+ if (is_null_sha1(update->new_sha1))
+ flags |= REF_DELETING;
update->lock = lock_ref_sha1_basic(update->refname,
(update->have_old ?
update->old_sha1 :
NULL),
NULL,
- update->flags,
+ flags,
&update->type);
if (!update->lock) {
ret = (errno == ENOTDIR)
diff --git a/refs.h b/refs.h
index 3bb16db..b62c4c4 100644
--- a/refs.h
+++ b/refs.h
@@ -175,10 +175,12 @@ extern int peel_ref(const char *refname, unsigned char *sha1);
* ref_transaction_create(), etc.
* REF_NODEREF: act on the ref directly, instead of dereferencing
* symbolic references.
+ * REF_DELETING: tolerate broken refs
*
* Flags >= 0x100 are reserved for internal use.
*/
#define REF_NODEREF 0x01
+#define REF_DELETING 0x02
/*
* This function sets errno to something meaningful on failure.
*/
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 0218e96..7c8c41a 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -110,6 +110,40 @@ test_expect_success "delete symref without dereference when the referred ref is
cp -f .git/HEAD.orig .git/HEAD
git update-ref -d $m
+test_expect_success 'update-ref -d is not confused by self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ test_must_fail git update-ref -d refs/heads/self &&
+ test_path_is_file .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ git update-ref --no-deref -d refs/heads/self &&
+ test_path_is_missing .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+ >.git/refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/bad" &&
+ git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/ref-to-bad" &&
+ test_path_is_file .git/refs/heads/ref-to-bad &&
+ git update-ref --no-deref -d refs/heads/ref-to-bad &&
+ test_path_is_missing .git/refs/heads/ref-to-bad
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
+ git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ test_path_is_file .git/refs/heads/badname &&
+ git update-ref --no-deref -d refs/heads/badname &&
+ test_path_is_missing .git/refs/heads/badname
+'
+
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ac31b71..432921b 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -285,6 +285,15 @@ test_expect_success 'deleting a dangling symref' '
test_i18ncmp expect actual
'
+test_expect_success 'deleting a self-referential symref' '
+ git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
+ test_path_is_file .git/refs/heads/self-reference &&
+ echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
+ git branch -d self-reference >actual &&
+ test_path_is_missing .git/refs/heads/self-reference &&
+ test_i18ncmp expect actual
+'
+
test_expect_success 'renaming a symref is not allowed' '
git symbolic-ref refs/heads/master2 refs/heads/master &&
test_must_fail git branch -m master2 master3 &&
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 16/24] branch -d: simplify by using RESOLVE_REF_READING flag
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (14 preceding siblings ...)
2014-10-02 2:15 ` [PATCH 15/24] branch -d: avoid repeated symref resolution Jonathan Nieder
@ 2014-10-02 2:15 ` Jonathan Nieder
2014-10-02 2:16 ` [PATCH 17/24] packed-ref cache: forbid dot-components in refnames Jonathan Nieder
` (7 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:15 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 11 Sep 2014 10:34:36 -0700
When "git branch -d" reads the branch it is about to delete, it used
to avoid passing the RESOLVE_REF_READING ('treat missing ref as
error') flag because a symref pointing to a nonexistent ref would show
up as missing instead of as something that could be deleted. To check
if a ref is actually missing, we then check
- is it a symref?
- if not, did it resolve to null_sha1?
Now we pass RESOLVE_REF_NO_RECURSE and the correct information is
returned for a symref even when it points to a missing ref. Simplify
by relying on RESOLVE_REF_READING.
No functional change intended.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Split out from the 'fix handling of badly named refs' patch.
builtin/branch.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index a334380..a0c5601 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -234,10 +234,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, RESOLVE_REF_NO_RECURSE,
+ target = resolve_ref_unsafe(name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE,
sha1, &flags);
- if (!target ||
- (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
+ if (!target) {
error(remote_branch
? _("remote branch '%s' not found.")
: _("branch '%s' not found."), bname.buf);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 17/24] packed-ref cache: forbid dot-components in refnames
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (15 preceding siblings ...)
2014-10-02 2:15 ` [PATCH 16/24] branch -d: simplify by using RESOLVE_REF_READING flag Jonathan Nieder
@ 2014-10-02 2:16 ` Jonathan Nieder
2014-10-02 2:17 ` [PATCH 18/24] test: put tests for handling of bad ref names in one place Jonathan Nieder
` (6 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:16 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Since v1.7.9-rc1~10^2 (write_head_info(): handle "extra refs" locally,
2012-01-06), this trick to keep track of ".have" refs that are only
valid on the wire and not on the filesystem is not needed any more.
Simplify by removing support for the REFNAME_DOT_COMPONENT flag.
This means we'll be slightly stricter with invalid refs found in a
packed-refs file or during clone. read_loose_refs() already checks
for and skips refnames with .components so it is not affected.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
Noticed while reviewing other patches.
refs.c | 14 +++-----------
refs.h | 6 +-----
2 files changed, 4 insertions(+), 16 deletions(-)
diff --git a/refs.c b/refs.c
index 490e788..94d6d89 100644
--- a/refs.c
+++ b/refs.c
@@ -69,16 +69,8 @@ static int check_refname_component(const char *refname, int flags)
out:
if (cp == refname)
return 0; /* Component has zero length. */
- if (refname[0] == '.') {
- if (!(flags & REFNAME_DOT_COMPONENT))
- return -1; /* Component starts with '.'. */
- /*
- * Even if leading dots are allowed, don't allow "."
- * as a component (".." is prevented by a rule above).
- */
- if (refname[1] == '\0')
- return -1; /* Component equals ".". */
- }
+ if (refname[0] == '.')
+ return -1; /* Component starts with '.'. */
if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
return -1; /* Refname ends with ".lock". */
return cp - refname;
@@ -288,7 +280,7 @@ static struct ref_entry *create_ref_entry(const char *refname,
struct ref_entry *ref;
if (check_name &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die("Reference has invalid format: '%s'", refname);
len = strlen(refname) + 1;
ref = xmalloc(sizeof(struct ref_entry) + len);
diff --git a/refs.h b/refs.h
index b62c4c4..e48d8b9 100644
--- a/refs.h
+++ b/refs.h
@@ -226,7 +226,6 @@ extern int for_each_reflog(each_ref_fn, void *);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
-#define REFNAME_DOT_COMPONENT 4
/*
* Return 0 iff refname has the correct format for a refname according
@@ -234,10 +233,7 @@ extern int for_each_reflog(each_ref_fn, void *);
* If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
* reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
* allow a "*" wildcard character in place of one of the name
- * components. No leading or repeated slashes are accepted. If
- * REFNAME_DOT_COMPONENT is set in flags, then allow refname
- * components to start with "." (but not a whole component equal to
- * "." or "..").
+ * components. No leading or repeated slashes are accepted.
*/
extern int check_refname_format(const char *refname, int flags);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 18/24] test: put tests for handling of bad ref names in one place
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (16 preceding siblings ...)
2014-10-02 2:16 ` [PATCH 17/24] packed-ref cache: forbid dot-components in refnames Jonathan Nieder
@ 2014-10-02 2:17 ` Jonathan Nieder
2014-10-02 2:28 ` [PATCH 19/24] refs.c: allow listing and deleting badly named refs Jonathan Nieder
` (5 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:17 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
There's no straightforward way to grep for all tests dealing with
invalid refs. Put them in a single test script so it is easy to see
what functionality has not been exercised with bad ref names yet.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
New.
t/t1400-update-ref.sh | 44 --------------------------
t/t1430-bad-ref-name.sh | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
t/t9300-fast-import.sh | 30 ------------------
3 files changed, 84 insertions(+), 74 deletions(-)
create mode 100755 t/t1430-bad-ref-name.sh
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 7c8c41a..7b4707b 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -136,14 +136,6 @@ test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
test_path_is_missing .git/refs/heads/ref-to-bad
'
-test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
- git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
- test_path_is_file .git/refs/heads/badname &&
- git update-ref --no-deref -d refs/heads/badname &&
- test_path_is_missing .git/refs/heads/badname
-'
-
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
@@ -408,12 +400,6 @@ test_expect_success 'stdin fails create with no ref' '
grep "fatal: create: missing <ref>" err
'
-test_expect_success 'stdin fails create with bad ref name' '
- echo "create ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails create with no new value' '
echo "create $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -432,12 +418,6 @@ test_expect_success 'stdin fails update with no ref' '
grep "fatal: update: missing <ref>" err
'
-test_expect_success 'stdin fails update with bad ref name' '
- echo "update ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails update with no new value' '
echo "update $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -456,12 +436,6 @@ test_expect_success 'stdin fails delete with no ref' '
grep "fatal: delete: missing <ref>" err
'
-test_expect_success 'stdin fails delete with bad ref name' '
- echo "delete ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails delete with too many arguments' '
echo "delete $a $m $m" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -734,12 +708,6 @@ test_expect_success 'stdin -z fails create with no ref' '
grep "fatal: create: missing <ref>" err
'
-test_expect_success 'stdin -z fails create with bad ref name' '
- printf $F "create ~a " "$m" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a " err
-'
-
test_expect_success 'stdin -z fails create with no new value' '
printf $F "create $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
@@ -764,12 +732,6 @@ test_expect_success 'stdin -z fails update with too few args' '
grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
'
-test_expect_success 'stdin -z fails update with bad ref name' '
- printf $F "update ~a" "$m" "" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin -z emits warning with empty new value' '
git update-ref $a $m &&
printf $F "update $a" "" "" >stdin &&
@@ -802,12 +764,6 @@ test_expect_success 'stdin -z fails delete with no ref' '
grep "fatal: delete: missing <ref>" err
'
-test_expect_success 'stdin -z fails delete with bad ref name' '
- printf $F "delete ~a" "$m" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin -z fails delete with no old value' '
printf $F "delete $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
new file mode 100755
index 0000000..c7b19b0
--- /dev/null
+++ b/t/t1430-bad-ref-name.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='Test handling of ref names that check-ref-format rejects'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit one
+'
+
+test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ cat >input <<-INPUT_END &&
+ commit .badbranchname
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ corrupt
+ COMMIT
+
+ from refs/heads/master
+
+ INPUT_END
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"' '
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ cat >input <<-INPUT_END &&
+ commit bad[branch]name
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ corrupt
+ COMMIT
+
+ from refs/heads/master
+
+ INPUT_END
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
+ git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ test_path_is_file .git/refs/heads/badname &&
+ git update-ref --no-deref -d refs/heads/badname &&
+ test_path_is_missing .git/refs/heads/badname
+'
+
+test_expect_success 'update-ref --stdin fails create with bad ref name' '
+ echo "create ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails update with bad ref name' '
+ echo "update ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails delete with bad ref name' '
+ echo "delete ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails create with bad ref name' '
+ printf "%s\0" "create ~a " refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a " err
+'
+
+test_expect_success 'update-ref --stdin -z fails update with bad ref name' '
+ printf "%s\0" "update ~a" refs/heads/master "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
+ printf "%s\0" "delete ~a" refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 5fc9ef2..3d156f9 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -347,36 +347,6 @@ test_expect_success 'B: fail on invalid blob sha1' '
rm -f .git/objects/pack_* .git/objects/index_*
cat >input <<INPUT_END
-commit .badbranchname
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
- test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
-commit bad[branch]name
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
- test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
commit TEMP_TAG
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 19/24] refs.c: allow listing and deleting badly named refs
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (17 preceding siblings ...)
2014-10-02 2:17 ` [PATCH 18/24] test: put tests for handling of bad ref names in one place Jonathan Nieder
@ 2014-10-02 2:28 ` Jonathan Nieder
2014-10-02 18:55 ` Junio C Hamano
2014-10-02 2:30 ` [PATCH 20/24] for-each-ref.c: improve message before aborting on broken ref Jonathan Nieder
` (4 subsequent siblings)
23 siblings, 1 reply; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:28 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Wed, 3 Sep 2014 11:45:43 -0700
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "git branch --list" to show these refs and to allow "git
branch -d/-D" and "git update-ref -d" to delete them. Other commands
(for example to rename refs) will continue to not handle these refs
but can be changed in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
resolve refs that escape the refs/ directory and do not match the
pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
In locking functions, refuse to act on badly named refs unless they
are being deleted and either are in the refs/ directory or match [A-Z_]*.
Just like other invalid refs, flag resolved, badly named refs with the
REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
in all iteration functions except for for_each_rawref.
Flag badly named refs with a REF_BAD_NAME flag to make it easier for
future callers to notice and handle them specially.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- clarified change description
- handle REF_ISBROKEN case too when printing "Deleted branch" message
- tighten fallback check, so commands like 'git branch -d ../../config'
won't work
- use REF_BAD_NAME so it's easier for callers (like for-each-ref in a
later patch) to notice that we decided a ref's name was bad
- set errno when failing to find ref in the packed-refs file
- consistently don't set REF_ISBROKEN for missing refs
- consistently clear sha1 when successfully resolving a badly named ref
- treat refs with bad names encountered as part of symref resolution the
same as such refs explicitly named
- always allow safe bad names when trying to lock a ref for deletion
(though some callers don't get that far yet)
- clearer API docs, more tests
builtin/branch.c | 9 +--
cache.h | 14 ++++-
refs.c | 148 ++++++++++++++++++++++++++++++++++++++----------
refs.h | 8 ++-
t/t1430-bad-ref-name.sh | 125 +++++++++++++++++++++++++++++++++++++++-
5 files changed, 265 insertions(+), 39 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index a0c5601..94aaea1 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -236,7 +236,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
name = mkpathdup(fmt, bname.buf);
target = resolve_ref_unsafe(name,
RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE,
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
sha1, &flags);
if (!target) {
error(remote_branch
@@ -246,7 +247,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
continue;
}
- if (!(flags & REF_ISSYMREF) &&
+ if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
force)) {
ret = 1;
@@ -266,8 +267,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
? _("Deleted remote branch %s (was %s).\n")
: _("Deleted branch %s (was %s).\n"),
bname.buf,
- (flags & REF_ISSYMREF)
- ? target
+ (flags & REF_ISBROKEN) ? "broken"
+ : (flags & REF_ISSYMREF) ? target
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
diff --git a/cache.h b/cache.h
index 5ca7f2b..0c0ac60 100644
--- a/cache.h
+++ b/cache.h
@@ -978,16 +978,26 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* If flags is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
* packed references), REF_ISSYMREF (if the initial reference was a
- * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
+ * symbolic reference), REF_BAD_NAME (if the reference name is ill
+ * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
+ * (if the ref is malformed).
*
* If ref is not a properly-formatted, normalized reference, return
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL.
*
- * errno is set to something meaningful on error.
+ * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
+ * name is invalid according to git-check-ref-format(1). If the name
+ * is bad then the value stored in sha1 will be null_sha1 and the
+ * REF_ISBROKEN and REF_BAD_NAME flags will be set.
+ *
+ * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
+ * directory and do not consist of all caps and underscores cannot be
+ * resolved. The function returns NULL for such ref names.
*/
#define RESOLVE_REF_READING 0x01
#define RESOLVE_REF_NO_RECURSE 0x02
+#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
diff --git a/refs.c b/refs.c
index 94d6d89..0d3abb1 100644
--- a/refs.c
+++ b/refs.c
@@ -185,8 +185,8 @@ struct ref_dir {
/*
* Bit values for ref_entry::flag. REF_ISSYMREF=0x01,
- * REF_ISPACKED=0x02, and REF_ISBROKEN=0x04 are public values; see
- * refs.h.
+ * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
+ * public values; see refs.h.
*/
/*
@@ -194,16 +194,16 @@ struct ref_dir {
* the correct peeled value for the reference, which might be
* null_sha1 if the reference is not a tag or if it is broken.
*/
-#define REF_KNOWS_PEELED 0x08
+#define REF_KNOWS_PEELED 0x10
/* ref_entry represents a directory of references */
-#define REF_DIR 0x10
+#define REF_DIR 0x20
/*
* Entry has not yet been read from disk (used only for REF_DIR
* entries representing loose references)
*/
-#define REF_INCOMPLETE 0x20
+#define REF_INCOMPLETE 0x40
/*
* A ref_entry represents either a reference or a "subdirectory" of
@@ -272,6 +272,37 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
return dir;
}
+static int escapes_cwd(const char *path) {
+ char *buf;
+ int result;
+
+ if (is_absolute_path(path))
+ return 1;
+ buf = xmalloc(strlen(path) + 1);
+ result = !!normalize_path_copy(buf, path);
+ free(buf);
+ return result;
+}
+
+/*
+ * Check if a refname is safe.
+ * For refs that start with "refs/" we consider it safe as long as the rest
+ * of the path components does not allow it to escape from this directory.
+ * For all other refs we only consider them safe iff they only contain
+ * upper case characters and '_'.
+ */
+static int refname_is_safe(const char *refname)
+{
+ if (starts_with(refname, "refs/"))
+ return !escapes_cwd(refname + strlen("refs/"));
+ while (*refname) {
+ if (!isupper(*refname) && *refname != '_')
+ return 0;
+ refname++;
+ }
+ return 1;
+}
+
static struct ref_entry *create_ref_entry(const char *refname,
const unsigned char *sha1, int flag,
int check_name)
@@ -282,6 +313,8 @@ static struct ref_entry *create_ref_entry(const char *refname,
if (check_name &&
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die("Reference has invalid format: '%s'", refname);
+ if (!check_name && !refname_is_safe(refname))
+ die("Reference has invalid name: '%s'", refname);
len = strlen(refname) + 1;
ref = xmalloc(sizeof(struct ref_entry) + len);
hashcpy(ref->u.value.sha1, sha1);
@@ -1051,7 +1084,13 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
refname = parse_ref_line(refline, sha1);
if (refname) {
- last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
+ int flag = REF_ISPACKED;
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ hashclr(sha1);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
+ }
+ last = create_ref_entry(refname, sha1, flag, 0);
if (peeled == PEELED_FULLY ||
(peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
last->flag |= REF_KNOWS_PEELED;
@@ -1189,8 +1228,13 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
hashclr(sha1);
flag |= REF_ISBROKEN;
}
+ if (check_refname_format(refname.buf,
+ REFNAME_ALLOW_ONELEVEL)) {
+ hashclr(sha1);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
+ }
add_entry_to_dir(dir,
- create_ref_entry(refname.buf, sha1, flag, 1));
+ create_ref_entry(refname.buf, sha1, flag, 0));
}
strbuf_setlen(&refname, dirnamelen);
}
@@ -1309,10 +1353,10 @@ static struct ref_entry *get_packed_ref(const char *refname)
* A loose ref file doesn't exist; check for a packed ref. The
* options are forwarded from resolve_safe_unsafe().
*/
-static const char *handle_missing_loose_ref(const char *refname,
- int resolve_flags,
- unsigned char *sha1,
- int *flags)
+static int resolve_missing_loose_ref(const char *refname,
+ int resolve_flags,
+ unsigned char *sha1,
+ int *flags)
{
struct ref_entry *entry;
@@ -1325,14 +1369,15 @@ static const char *handle_missing_loose_ref(const char *refname,
hashcpy(sha1, entry->u.value.sha1);
if (flags)
*flags |= REF_ISPACKED;
- return refname;
+ return 0;
}
/* The reference is not a packed reference, either. */
if (resolve_flags & RESOLVE_REF_READING) {
- return NULL;
+ errno = ENOENT;
+ return -1;
} else {
hashclr(sha1);
- return refname;
+ return 0;
}
}
@@ -1343,13 +1388,29 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
ssize_t len;
char buffer[256];
static char refname_buffer[256];
+ int bad_name = 0;
if (flags)
*flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- errno = EINVAL;
- return NULL;
+ if (flags)
+ *flags |= REF_BAD_NAME;
+
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /*
+ * dwim_ref() uses REF_ISBROKEN to distinguish between
+ * missing refs and refs that were present but invalid,
+ * to complain about the latter to stderr.
+ *
+ * We don't know whether the ref exists, so don't set
+ * REF_ISBROKEN yet.
+ */
+ bad_name = 1;
}
for (;;) {
char path[PATH_MAX];
@@ -1375,11 +1436,17 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
*/
stat_ref:
if (lstat(path, &st) < 0) {
- if (errno == ENOENT)
- return handle_missing_loose_ref(refname,
- resolve_flags, sha1, flags);
- else
+ if (errno != ENOENT)
return NULL;
+ if (resolve_missing_loose_ref(refname, resolve_flags,
+ sha1, flags))
+ return NULL;
+ if (bad_name) {
+ hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ }
+ return refname;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
@@ -1452,6 +1519,11 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
errno = EINVAL;
return NULL;
}
+ if (bad_name) {
+ hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ }
return refname;
}
if (flags)
@@ -1466,9 +1538,14 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
}
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
if (flags)
- *flags |= REF_ISBROKEN;
- errno = EINVAL;
- return NULL;
+ *flags |= REF_BAD_NAME | REF_ISBROKEN;
+
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(buf)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ bad_name = 1;
}
}
}
@@ -2101,18 +2178,16 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int missing = 0;
int attempts_remaining = 3;
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- errno = EINVAL;
- return NULL;
- }
-
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
- if (flags & REF_NODEREF && flags & REF_DELETING)
- resolve_flags |= RESOLVE_REF_NO_RECURSE;
+ if (flags & REF_DELETING) {
+ resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
+ if (flags & REF_NODEREF)
+ resolve_flags |= RESOLVE_REF_NO_RECURSE;
+ }
refname = resolve_ref_unsafe(refname, resolve_flags,
lock->old_sha1, &type);
@@ -3465,6 +3540,13 @@ int ref_transaction_update(struct ref_transaction *transaction,
if (have_old && !old_sha1)
die("BUG: have_old is true but old_sha1 is NULL");
+ if (!is_null_sha1(new_sha1) &&
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ strbuf_addf(err, "refusing to update ref with bad name %s",
+ refname);
+ return -1;
+ }
+
update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
update->flags = flags;
@@ -3490,6 +3572,12 @@ int ref_transaction_create(struct ref_transaction *transaction,
if (!new_sha1 || is_null_sha1(new_sha1))
die("BUG: create ref with null new_sha1");
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ strbuf_addf(err, "refusing to create ref with bad name %s",
+ refname);
+ return -1;
+ }
+
update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
diff --git a/refs.h b/refs.h
index e48d8b9..3b35387 100644
--- a/refs.h
+++ b/refs.h
@@ -56,11 +56,15 @@ struct ref_transaction;
/*
* Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file, or
- * symbolic reference refers to ill-formatted reference name.
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
*/
#define REF_ISBROKEN 0x04
+/* Reference name is not well formed (see git-check-ref-format(1)). */
+#define REF_BAD_NAME 0x08
+
/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the refname and sha1
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index c7b19b0..468e856 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -4,7 +4,8 @@ test_description='Test handling of ref names that check-ref-format rejects'
. ./test-lib.sh
test_expect_success setup '
- test_commit one
+ test_commit one &&
+ test_commit two
'
test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
@@ -37,6 +38,107 @@ test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"'
test_must_fail git fast-import <input
'
+test_expect_success 'git branch shows badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch >output &&
+ grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -d can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -d broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -D broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git branch -D ../../my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'branch -D cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git branch -D "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'git branch cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git branch broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -m cannot rename to a bad ref name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_might_fail git branch -D goodref &&
+ git branch goodref &&
+ test_must_fail git branch -m goodref broken...ref &&
+ test_cmp_rev master goodref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'branch -m can rename from a bad ref name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -m broken...ref renamed &&
+ test_cmp_rev master renamed &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'push cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'push --mirror can delete badly named ref' '
+ top=$(pwd) &&
+ git init src &&
+ git init dest &&
+
+ (
+ cd src &&
+ test_commit one
+ ) &&
+ (
+ cd dest &&
+ test_commit two &&
+ git checkout --detach &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref
+ ) &&
+ git -C src push --mirror "file://$top/dest" &&
+ git -C dest branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'rev-parse skips symref pointing to broken name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch shadow one &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ git symbolic-ref refs/tags/shadow refs/heads/broken...ref &&
+
+ git rev-parse --verify one >expect &&
+ git rev-parse --verify shadow >actual 2>err &&
+ test_cmp expect actual &&
+ test_i18ngrep "ignoring.*refs/tags/shadow" err
+'
+
test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
test_when_finished "rm -f .git/refs/heads/badname" &&
@@ -45,6 +147,27 @@ test_expect_success 'update-ref --no-deref -d can delete reference to broken nam
test_path_is_missing .git/refs/heads/badname
'
+test_expect_success 'update-ref -d can delete broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git update-ref -d refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git update-ref -d my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'update-ref -d cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git update-ref -d "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
test_expect_success 'update-ref --stdin fails create with bad ref name' '
echo "create ~a refs/heads/master" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* Re: [PATCH 19/24] refs.c: allow listing and deleting badly named refs
2014-10-02 2:28 ` [PATCH 19/24] refs.c: allow listing and deleting badly named refs Jonathan Nieder
@ 2014-10-02 18:55 ` Junio C Hamano
2014-10-03 20:25 ` Ronnie Sahlberg
0 siblings, 1 reply; 130+ messages in thread
From: Junio C Hamano @ 2014-10-02 18:55 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Ronnie Sahlberg, git, Michael Haggerty
Jonathan Nieder <jrnieder@gmail.com> writes:
> From: Ronnie Sahlberg <sahlberg@google.com>
> ...
> In resolving functions, refuse to resolve refs that don't pass the
> check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
> flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
> resolve refs that escape the refs/ directory and do not match the
> pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
>
> In locking functions, refuse to act on badly named refs unless they
> are being deleted and either are in the refs/ directory or match [A-Z_]*.
>
> Just like other invalid refs, flag resolved, badly named refs with the
> REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
> in all iteration functions except for for_each_rawref.
>
> Flag badly named refs with a REF_BAD_NAME flag to make it easier for
> future callers to notice and handle them specially.
>
> In the transaction API, refuse to create or update badly named refs,
> but allow deleting them (unless they escape refs/ and don't match
> [A-Z_]*).
>
> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Thanks. We originally threw all the different kind of breakages
into ISBROKEN, but a ref can have a malformed name or can contain a
bad/non value and allowing us to tell them apart is a good direction
to go.
> diff --git a/cache.h b/cache.h
> index 5ca7f2b..0c0ac60 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -978,16 +978,26 @@ extern int read_ref(const char *refname, unsigned char *sha1);
> * If flags is non-NULL, set the value that it points to the
> * combination of REF_ISPACKED (if the reference was found among the
> * packed references), REF_ISSYMREF (if the initial reference was a
> - * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
> + * symbolic reference), REF_BAD_NAME (if the reference name is ill
> + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
> + * (if the ref is malformed).
You want to define "is malformed" here.
The original defines REF_ISBROKEN as "malformed" because
(1) resolve_ref_unsafe() uses get_sha1_hex() and read_loose_refs()
uses read_ref_full(), both to catch "malformed values" stored;
(2) resolve_ref_unsafe() uses check_refname_format() and catches
"malformed names" stored as a symref target.
I _think_ you are introducing ALLOW_BAD_NAME to allow add the third
class ".git/refs/remotes/origin/mal..formed..name". I do not know
if they should be the same class as a symref with a good name
".git/refs/remotes/origin/HEAD" that points at a bad name
"mal..formed..name", which is (2) above). Perhaps not. (2) is
still above what is stored in the ref, and the ref in question may
or may not have a well-formed name, which is orthogonal.
So probably you left only the "value stored in the ref is malformed"
(in other words, "we expected to find 40-hex object name but didn't
find one") case for REF_ISBROKEN?
Do we want to separate "value is not 40-hex" and "a symref points at
a malformed refname" as separate "malformed value" errors?
> + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
> + * name is invalid according to git-check-ref-format(1). If the name
> + * is bad then the value stored in sha1 will be null_sha1 and the
> + * REF_ISBROKEN and REF_BAD_NAME flags will be set.
Viewed with that light, I am not sure if a badly named ref should
yield null_sha1[] (REF_ISBROKEN, which I am assuming is about a
value that is badly formatted and cannot be read, should keep
yielding it as before). Wouldn't it make it harder for the user if
you give null_sha1[] back to somebody who is trying to recover by
reading "refs/heads/mal..formed", creating "refs/heads/sensible" to
point at the same value and then removing the former?
Note that I am not saying we should give back the parsed value at
this step in the series. Perhaps there are some existing callers
that do not check for ISBROKEN flag and instead says "null_sha1[]
ref is to be rejected/ignored", in which case they may need to be
corrected before that happens. Or there may be some reason I
overlooked that makes it not so useful if we returned the object
name stored in a ref whose name is malformed. Just wondering.
> @@ -272,6 +272,37 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
> return dir;
> }
>
> +static int escapes_cwd(const char *path) {
> + char *buf;
> + int result;
> +
> + if (is_absolute_path(path))
> + return 1;
> + buf = xmalloc(strlen(path) + 1);
> + result = !!normalize_path_copy(buf, path);
> + free(buf);
> + return result;
> +}
I think this function is misnamed for two reasons.
- It does not have anything to do with cwd; it does not make any
difference to the outcome of this function given the same input,
if 'pwd' says "/u/jc/git" or "/u/jc/git/Documentation", no?
- Even if this had something to do with cwd, I would expect a
function whose name is escapes_cwd("/u/jc/git/Documentation") to
yield false when 'pwd' says "/u/jc/git", but the implementation
unconditionally rejects absolute path. In the context of the
(sole) caller of this function, which deals with a refname "refs/...",
it makes no sense to see an absolute path, but that does not have
anything to do with "does this path escape cwd?", no?
> +/*
> + * Check if a refname is safe.
> + * For refs that start with "refs/" we consider it safe as long as the rest
> + * of the path components does not allow it to escape from this directory.
> + * For all other refs we only consider them safe iff they only contain
> + * upper case characters and '_'.
> + */
I presume that the exception is to accomodate for "HEAD", "ORIG_HEAD",
"MERGE_HEAD" and friends, but you probably do not want the readers to
guess.
> +static int refname_is_safe(const char *refname)
> +{
> + if (starts_with(refname, "refs/"))
> + return !escapes_cwd(refname + strlen("refs/"));
> + while (*refname) {
> + if (!isupper(*refname) && *refname != '_')
> + return 0;
> + refname++;
> + }
> + return 1;
> +}
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH 19/24] refs.c: allow listing and deleting badly named refs
2014-10-02 18:55 ` Junio C Hamano
@ 2014-10-03 20:25 ` Ronnie Sahlberg
2014-10-03 20:32 ` Ronnie Sahlberg
2014-10-03 20:39 ` Junio C Hamano
0 siblings, 2 replies; 130+ messages in thread
From: Ronnie Sahlberg @ 2014-10-03 20:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jonathan Nieder, git, Michael Haggerty
On Thu, Oct 2, 2014 at 11:55 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
>
>> From: Ronnie Sahlberg <sahlberg@google.com>
>> ...
>> In resolving functions, refuse to resolve refs that don't pass the
>> check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
>> flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
>> resolve refs that escape the refs/ directory and do not match the
>> pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
>>
>> In locking functions, refuse to act on badly named refs unless they
>> are being deleted and either are in the refs/ directory or match [A-Z_]*.
>>
>> Just like other invalid refs, flag resolved, badly named refs with the
>> REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
>> in all iteration functions except for for_each_rawref.
>>
>> Flag badly named refs with a REF_BAD_NAME flag to make it easier for
>> future callers to notice and handle them specially.
>>
>> In the transaction API, refuse to create or update badly named refs,
>> but allow deleting them (unless they escape refs/ and don't match
>> [A-Z_]*).
>>
>> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
>> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
>
> Thanks. We originally threw all the different kind of breakages
> into ISBROKEN, but a ref can have a malformed name or can contain a
> bad/non value and allowing us to tell them apart is a good direction
> to go.
>
>> diff --git a/cache.h b/cache.h
>> index 5ca7f2b..0c0ac60 100644
>> --- a/cache.h
>> +++ b/cache.h
>> @@ -978,16 +978,26 @@ extern int read_ref(const char *refname, unsigned char *sha1);
>> * If flags is non-NULL, set the value that it points to the
>> * combination of REF_ISPACKED (if the reference was found among the
>> * packed references), REF_ISSYMREF (if the initial reference was a
>> - * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
>> + * symbolic reference), REF_BAD_NAME (if the reference name is ill
>> + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
>> + * (if the ref is malformed).
>
> You want to define "is malformed" here.
>
> The original defines REF_ISBROKEN as "malformed" because
>
> (1) resolve_ref_unsafe() uses get_sha1_hex() and read_loose_refs()
> uses read_ref_full(), both to catch "malformed values" stored;
>
> (2) resolve_ref_unsafe() uses check_refname_format() and catches
> "malformed names" stored as a symref target.
>
> I _think_ you are introducing ALLOW_BAD_NAME to allow add the third
> class ".git/refs/remotes/origin/mal..formed..name". I do not know
> if they should be the same class as a symref with a good name
> ".git/refs/remotes/origin/HEAD" that points at a bad name
> "mal..formed..name", which is (2) above). Perhaps not. (2) is
> still above what is stored in the ref, and the ref in question may
> or may not have a well-formed name, which is orthogonal.
>
> So probably you left only the "value stored in the ref is malformed"
> (in other words, "we expected to find 40-hex object name but didn't
> find one") case for REF_ISBROKEN?
I updated cache.h to try to clarify it better.
The intention here is to expand the use of REF_ISBROKEN.
For all cases REF_ISBROKEN will be set. This includes both "the sha1
value is bad" as well as "a name is bad".
For those cases where a name is bad then REF_BAD_NAME is also set. Bad
names are when either the ref itself has a bad name or when a bad name
is encountered while resolving a chain of symbilic refs.
I.e. REF_BAD_NAME is a special case of broken ref. All REF_BAD_NAME
refs are also REF_ISBROKEN but not the reverse.
>
> Do we want to separate "value is not 40-hex" and "a symref points at
> a malformed refname" as separate "malformed value" errors?
>
>> + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
>> + * name is invalid according to git-check-ref-format(1). If the name
>> + * is bad then the value stored in sha1 will be null_sha1 and the
>> + * REF_ISBROKEN and REF_BAD_NAME flags will be set.
>
> Viewed with that light, I am not sure if a badly named ref should
> yield null_sha1[] (REF_ISBROKEN, which I am assuming is about a
> value that is badly formatted and cannot be read, should keep
> yielding it as before). Wouldn't it make it harder for the user if
> you give null_sha1[] back to somebody who is trying to recover by
> reading "refs/heads/mal..formed", creating "refs/heads/sensible" to
> point at the same value and then removing the former?
>
> Note that I am not saying we should give back the parsed value at
> this step in the series. Perhaps there are some existing callers
> that do not check for ISBROKEN flag and instead says "null_sha1[]
> ref is to be rejected/ignored", in which case they may need to be
> corrected before that happens. Or there may be some reason I
> overlooked that makes it not so useful if we returned the object
> name stored in a ref whose name is malformed. Just wondering.
The reason for these malformed refs resolving to null_sha1 is exactly
that. There may be callers that do not check ISBROKEN, so all those
callers need to be carefully audited before we start returning
potentially valid non null_sha1 here.
Right now I only want to do the minimal changes needed to open up only
those few paths I need to in order to allow the refs to be deleted.
For the delete case, returning the null_sha1 is sufficient.
Now, IF we add support to "rename a bad-ref-name to a good-ref-name",
then for that case we would need to start allowing resolve_ref_unsafe
to actually return the sha1 for the ref.
And that would require that we do those changes and audits. Given how
rare this should be, I am not convinced we need "rename bad to good"
at this point and would thus prefer to hold off adding that.
Thus, returning the object name is not so useful for the problem I try
to solve at this stage, namely "make delete work".
>
>> @@ -272,6 +272,37 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
>> return dir;
>> }
>>
>> +static int escapes_cwd(const char *path) {
>> + char *buf;
>> + int result;
>> +
>> + if (is_absolute_path(path))
>> + return 1;
>> + buf = xmalloc(strlen(path) + 1);
>> + result = !!normalize_path_copy(buf, path);
>> + free(buf);
>> + return result;
>> +}
>
> I think this function is misnamed for two reasons.
>
> - It does not have anything to do with cwd; it does not make any
> difference to the outcome of this function given the same input,
> if 'pwd' says "/u/jc/git" or "/u/jc/git/Documentation", no?
>
> - Even if this had something to do with cwd, I would expect a
> function whose name is escapes_cwd("/u/jc/git/Documentation") to
> yield false when 'pwd' says "/u/jc/git", but the implementation
> unconditionally rejects absolute path. In the context of the
> (sole) caller of this function, which deals with a refname "refs/...",
> it makes no sense to see an absolute path, but that does not have
> anything to do with "does this path escape cwd?", no?
>
Agree.
I removed this function completely and just inline the !normalize_path_copy()
checks to make sure that refs/* paths remain within refs/.
>> +/*
>> + * Check if a refname is safe.
>> + * For refs that start with "refs/" we consider it safe as long as the rest
>> + * of the path components does not allow it to escape from this directory.
>> + * For all other refs we only consider them safe iff they only contain
>> + * upper case characters and '_'.
>> + */
>
> I presume that the exception is to accomodate for "HEAD", "ORIG_HEAD",
> "MERGE_HEAD" and friends, but you probably do not want the readers to
> guess.
>
I updated the commend to highlight that this is for "HEAD" and friends.
>> +static int refname_is_safe(const char *refname)
>> +{
>> + if (starts_with(refname, "refs/"))
>> + return !escapes_cwd(refname + strlen("refs/"));
>> + while (*refname) {
>> + if (!isupper(*refname) && *refname != '_')
>> + return 0;
>> + refname++;
>> + }
>> + return 1;
>> +}
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH 19/24] refs.c: allow listing and deleting badly named refs
2014-10-03 20:25 ` Ronnie Sahlberg
@ 2014-10-03 20:32 ` Ronnie Sahlberg
2014-10-03 20:39 ` Junio C Hamano
1 sibling, 0 replies; 130+ messages in thread
From: Ronnie Sahlberg @ 2014-10-03 20:32 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jonathan Nieder, git, Michael Haggerty
On Fri, Oct 3, 2014 at 1:25 PM, Ronnie Sahlberg <sahlberg@google.com> wrote:
> On Thu, Oct 2, 2014 at 11:55 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Jonathan Nieder <jrnieder@gmail.com> writes:
>>
>>> From: Ronnie Sahlberg <sahlberg@google.com>
>>> ...
>>> In resolving functions, refuse to resolve refs that don't pass the
>>> check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
>>> flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
>>> resolve refs that escape the refs/ directory and do not match the
>>> pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
>>>
>>> In locking functions, refuse to act on badly named refs unless they
>>> are being deleted and either are in the refs/ directory or match [A-Z_]*.
>>>
>>> Just like other invalid refs, flag resolved, badly named refs with the
>>> REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
>>> in all iteration functions except for for_each_rawref.
>>>
>>> Flag badly named refs with a REF_BAD_NAME flag to make it easier for
>>> future callers to notice and handle them specially.
>>>
>>> In the transaction API, refuse to create or update badly named refs,
>>> but allow deleting them (unless they escape refs/ and don't match
>>> [A-Z_]*).
>>>
>>> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
>>> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
>>
>> Thanks. We originally threw all the different kind of breakages
>> into ISBROKEN, but a ref can have a malformed name or can contain a
>> bad/non value and allowing us to tell them apart is a good direction
>> to go.
>>
>>> diff --git a/cache.h b/cache.h
>>> index 5ca7f2b..0c0ac60 100644
>>> --- a/cache.h
>>> +++ b/cache.h
>>> @@ -978,16 +978,26 @@ extern int read_ref(const char *refname, unsigned char *sha1);
>>> * If flags is non-NULL, set the value that it points to the
>>> * combination of REF_ISPACKED (if the reference was found among the
>>> * packed references), REF_ISSYMREF (if the initial reference was a
>>> - * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
>>> + * symbolic reference), REF_BAD_NAME (if the reference name is ill
>>> + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
>>> + * (if the ref is malformed).
>>
>> You want to define "is malformed" here.
>>
>> The original defines REF_ISBROKEN as "malformed" because
>>
>> (1) resolve_ref_unsafe() uses get_sha1_hex() and read_loose_refs()
>> uses read_ref_full(), both to catch "malformed values" stored;
>>
>> (2) resolve_ref_unsafe() uses check_refname_format() and catches
>> "malformed names" stored as a symref target.
>>
>> I _think_ you are introducing ALLOW_BAD_NAME to allow add the third
>> class ".git/refs/remotes/origin/mal..formed..name". I do not know
>> if they should be the same class as a symref with a good name
>> ".git/refs/remotes/origin/HEAD" that points at a bad name
>> "mal..formed..name", which is (2) above). Perhaps not. (2) is
>> still above what is stored in the ref, and the ref in question may
>> or may not have a well-formed name, which is orthogonal.
>>
>> So probably you left only the "value stored in the ref is malformed"
>> (in other words, "we expected to find 40-hex object name but didn't
>> find one") case for REF_ISBROKEN?
>
> I updated cache.h to try to clarify it better.
> The intention here is to expand the use of REF_ISBROKEN.
>
> For all cases REF_ISBROKEN will be set. This includes both "the sha1
> value is bad" as well as "a name is bad".
>
> For those cases where a name is bad then REF_BAD_NAME is also set. Bad
> names are when either the ref itself has a bad name or when a bad name
> is encountered while resolving a chain of symbilic refs.
>
>
> I.e. REF_BAD_NAME is a special case of broken ref. All REF_BAD_NAME
> refs are also REF_ISBROKEN but not the reverse.
Let me add some rationale why REF_BAD_NAME and REF_ISBROKEN are
notorthogonal properties.
I think almost all callers of these APIs are only concerned about "is
the ref good or bad" and they today only check REF_ISBROKEN.
I think that is a reasonable API and it allows the majority of callers
to just "check this single flag".
(The alternative would be to keep all callers in sync and use a set of
flags for all "bad conditions".)
A very small subset of callers are actually interested in knowing why
the ref was bad, and in particular if the ref was bad due to a name
component.
Those callers, that are aware that there are different types of
ISBROKEN can then inspect the REF_BAD_NAME flag in order to decide
"is the ref broken due to the ref name?".
This is why I made REF_BAD_NAME a special case of REF_ISBROKEN.
>
>
>>
>> Do we want to separate "value is not 40-hex" and "a symref points at
>> a malformed refname" as separate "malformed value" errors?
>>
>>> + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
>>> + * name is invalid according to git-check-ref-format(1). If the name
>>> + * is bad then the value stored in sha1 will be null_sha1 and the
>>> + * REF_ISBROKEN and REF_BAD_NAME flags will be set.
>>
>> Viewed with that light, I am not sure if a badly named ref should
>> yield null_sha1[] (REF_ISBROKEN, which I am assuming is about a
>> value that is badly formatted and cannot be read, should keep
>> yielding it as before). Wouldn't it make it harder for the user if
>> you give null_sha1[] back to somebody who is trying to recover by
>> reading "refs/heads/mal..formed", creating "refs/heads/sensible" to
>> point at the same value and then removing the former?
>>
>> Note that I am not saying we should give back the parsed value at
>> this step in the series. Perhaps there are some existing callers
>> that do not check for ISBROKEN flag and instead says "null_sha1[]
>> ref is to be rejected/ignored", in which case they may need to be
>> corrected before that happens. Or there may be some reason I
>> overlooked that makes it not so useful if we returned the object
>> name stored in a ref whose name is malformed. Just wondering.
>
> The reason for these malformed refs resolving to null_sha1 is exactly
> that. There may be callers that do not check ISBROKEN, so all those
> callers need to be carefully audited before we start returning
> potentially valid non null_sha1 here.
> Right now I only want to do the minimal changes needed to open up only
> those few paths I need to in order to allow the refs to be deleted.
> For the delete case, returning the null_sha1 is sufficient.
>
>
> Now, IF we add support to "rename a bad-ref-name to a good-ref-name",
> then for that case we would need to start allowing resolve_ref_unsafe
> to actually return the sha1 for the ref.
> And that would require that we do those changes and audits. Given how
> rare this should be, I am not convinced we need "rename bad to good"
> at this point and would thus prefer to hold off adding that.
>
> Thus, returning the object name is not so useful for the problem I try
> to solve at this stage, namely "make delete work".
>
>
>>
>>> @@ -272,6 +272,37 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
>>> return dir;
>>> }
>>>
>>> +static int escapes_cwd(const char *path) {
>>> + char *buf;
>>> + int result;
>>> +
>>> + if (is_absolute_path(path))
>>> + return 1;
>>> + buf = xmalloc(strlen(path) + 1);
>>> + result = !!normalize_path_copy(buf, path);
>>> + free(buf);
>>> + return result;
>>> +}
>>
>> I think this function is misnamed for two reasons.
>>
>> - It does not have anything to do with cwd; it does not make any
>> difference to the outcome of this function given the same input,
>> if 'pwd' says "/u/jc/git" or "/u/jc/git/Documentation", no?
>>
>> - Even if this had something to do with cwd, I would expect a
>> function whose name is escapes_cwd("/u/jc/git/Documentation") to
>> yield false when 'pwd' says "/u/jc/git", but the implementation
>> unconditionally rejects absolute path. In the context of the
>> (sole) caller of this function, which deals with a refname "refs/...",
>> it makes no sense to see an absolute path, but that does not have
>> anything to do with "does this path escape cwd?", no?
>>
>
> Agree.
> I removed this function completely and just inline the !normalize_path_copy()
> checks to make sure that refs/* paths remain within refs/.
>
>
>>> +/*
>>> + * Check if a refname is safe.
>>> + * For refs that start with "refs/" we consider it safe as long as the rest
>>> + * of the path components does not allow it to escape from this directory.
>>> + * For all other refs we only consider them safe iff they only contain
>>> + * upper case characters and '_'.
>>> + */
>>
>> I presume that the exception is to accomodate for "HEAD", "ORIG_HEAD",
>> "MERGE_HEAD" and friends, but you probably do not want the readers to
>> guess.
>>
>
> I updated the commend to highlight that this is for "HEAD" and friends.
>
>>> +static int refname_is_safe(const char *refname)
>>> +{
>>> + if (starts_with(refname, "refs/"))
>>> + return !escapes_cwd(refname + strlen("refs/"));
>>> + while (*refname) {
>>> + if (!isupper(*refname) && *refname != '_')
>>> + return 0;
>>> + refname++;
>>> + }
>>> + return 1;
>>> +}
^ permalink raw reply [flat|nested] 130+ messages in thread
* Re: [PATCH 19/24] refs.c: allow listing and deleting badly named refs
2014-10-03 20:25 ` Ronnie Sahlberg
2014-10-03 20:32 ` Ronnie Sahlberg
@ 2014-10-03 20:39 ` Junio C Hamano
1 sibling, 0 replies; 130+ messages in thread
From: Junio C Hamano @ 2014-10-03 20:39 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: Jonathan Nieder, git, Michael Haggerty
Ronnie Sahlberg <sahlberg@google.com> writes:
> On Thu, Oct 2, 2014 at 11:55 AM, Junio C Hamano <gitster@pobox.com> wrote:
> ...
>> Thanks. We originally threw all the different kind of breakages
>> into ISBROKEN, but a ref can have a malformed name or can contain a
>> bad/non value and allowing us to tell them apart is a good direction
>> to go.
>> ...
>
> I updated cache.h to try to clarify it better.
> The intention here is to expand the use of REF_ISBROKEN.
>
> For all cases REF_ISBROKEN will be set. This includes both "the sha1
> value is bad" as well as "a name is bad".
OK. As long as it is documented in a way to help other people who
touch the code later to tell what these REF_* mean, either way is
fine between "BADNAME is just one of the possible ways a ref is
ISBROKEN" and "BADNAME is a way and ISBROKEN is another way for a
ref to be bad", and I agree that the former (i.e. the way you
defined) is easier on the existing code.
Thanks.
^ permalink raw reply [flat|nested] 130+ messages in thread
* [PATCH 20/24] for-each-ref.c: improve message before aborting on broken ref
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (18 preceding siblings ...)
2014-10-02 2:28 ` [PATCH 19/24] refs.c: allow listing and deleting badly named refs Jonathan Nieder
@ 2014-10-02 2:30 ` Jonathan Nieder
2014-10-02 2:32 ` [PATCH 21/24] remote rm/prune: print a message when writing packed-refs fails Jonathan Nieder
` (3 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:30 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Fri, 5 Sep 2014 14:35:17 -0700
Print a warning message for any badly named refs we find in the repo.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
As before.
builtin/for-each-ref.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 20949b7..a88d681 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -853,6 +853,11 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
struct refinfo *ref;
int cnt;
+ if (flag & REF_BAD_NAME) {
+ warning("ignoring ref with broken name %s", refname);
+ return 0;
+ }
+
if (*cb->grab_pattern) {
const char **pattern;
int namelen = strlen(refname);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 21/24] remote rm/prune: print a message when writing packed-refs fails
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (19 preceding siblings ...)
2014-10-02 2:30 ` [PATCH 20/24] for-each-ref.c: improve message before aborting on broken ref Jonathan Nieder
@ 2014-10-02 2:32 ` Jonathan Nieder
2014-10-02 2:33 ` [PATCH 22/24] refs.c: do not permit err == NULL Jonathan Nieder
` (2 subsequent siblings)
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:32 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Thu, 11 Sep 2014 08:42:57 -0700
Until v2.1.0-rc0~22^2~11 (refs.c: add an err argument to
repack_without_refs, 2014-06-20), repack_without_refs forgot to
provide an error message when commit_packed_refs fails. Even today,
it only provides a message for callers that pass a non-NULL err
parameter. Internal callers in refs.c pass non-NULL err but
"git remote" does not.
That means that "git remote rm" and "git remote prune" can fail
without printing a message about why. Fix them by passing in a
non-NULL err parameter and printing the returned message.
This is the last caller to a ref handling function passing err ==
NULL. A later patch can drop support for err == NULL, avoiding such
problems in the future.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
---
Since v21:
- s/without/about/ in the commit message
builtin/remote.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/builtin/remote.c b/builtin/remote.c
index c7f82f4..8517cfa 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -750,13 +750,16 @@ static int mv(int argc, const char **argv)
static int remove_branches(struct string_list *branches)
{
+ struct strbuf err = STRBUF_INIT;
const char **branch_names;
int i, result = 0;
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string;
- result |= repack_without_refs(branch_names, branches->nr, NULL);
+ if (repack_without_refs(branch_names, branches->nr, &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
free(branch_names);
for (i = 0; i < branches->nr; i++) {
@@ -1333,9 +1336,13 @@ static int prune_remote(const char *remote, int dry_run)
delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util;
- if (!dry_run)
- result |= repack_without_refs(delete_refs,
- states.stale.nr, NULL);
+ if (!dry_run) {
+ struct strbuf err = STRBUF_INIT;
+ if (repack_without_refs(delete_refs, states.stale.nr,
+ &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
+ }
free(delete_refs);
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 22/24] refs.c: do not permit err == NULL
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (20 preceding siblings ...)
2014-10-02 2:32 ` [PATCH 21/24] remote rm/prune: print a message when writing packed-refs fails Jonathan Nieder
@ 2014-10-02 2:33 ` Jonathan Nieder
2014-10-02 2:34 ` [PATCH 23/24] lockfile: remove unable_to_lock_error Jonathan Nieder
2014-10-02 2:35 ` [PATCH 24/24] ref_transaction_commit: bail out on failure to remove a ref Jonathan Nieder
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:33 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Date: Thu, 28 Aug 2014 16:42:37 -0700
Some functions that take a strbuf argument to append an error treat
!err as an indication that the message should be suppressed (e.g.,
ref_update_reject_duplicates). Others write the message to stderr on
!err (e.g., repack_without_refs). Others crash (e.g.,
ref_transaction_update).
Some of these behaviors are for historical reasons and others were
accidents. Luckily no callers pass err == NULL any more. Simplify
by consistently requiring the strbuf argument.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Since v21:
- dropped spurious 'to' from commit message
refs.c | 46 +++++++++++++++++++++++++++-------------------
1 file changed, 27 insertions(+), 19 deletions(-)
diff --git a/refs.c b/refs.c
index 0d3abb1..e13f843 100644
--- a/refs.c
+++ b/refs.c
@@ -2596,6 +2596,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
struct string_list_item *ref_to_delete;
int i, ret, removed = 0;
+ assert(err);
+
/* Look for a packed ref */
for (i = 0; i < n; i++)
if (get_packed_ref(refnames[i]))
@@ -2606,13 +2608,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
return 0; /* no refname exists in packed refs */
if (lock_packed_refs(0)) {
- if (err) {
- unable_to_lock_message(git_path("packed-refs"), errno,
- err);
- return -1;
- }
- unable_to_lock_error(git_path("packed-refs"), errno);
- return error("cannot delete '%s' from packed refs", refnames[i]);
+ unable_to_lock_message(git_path("packed-refs"), errno, err);
+ return -1;
}
packed = get_packed_refs(&ref_cache);
@@ -2638,7 +2635,7 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
/* Write what remains */
ret = commit_packed_refs();
- if (ret && err)
+ if (ret)
strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
strerror(errno));
return ret;
@@ -2646,6 +2643,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
{
+ assert(err);
+
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/* loose */
int res, i = strlen(lock->lk->filename) - 5; /* .lock */
@@ -3495,6 +3494,8 @@ struct ref_transaction {
struct ref_transaction *ref_transaction_begin(struct strbuf *err)
{
+ assert(err);
+
return xcalloc(1, sizeof(struct ref_transaction));
}
@@ -3534,6 +3535,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
{
struct ref_update *update;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: update called for transaction that is not open");
@@ -3566,6 +3569,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
{
struct ref_update *update;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: create called for transaction that is not open");
@@ -3597,6 +3602,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
{
struct ref_update *update;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: delete called for transaction that is not open");
@@ -3659,13 +3666,14 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
struct strbuf *err)
{
int i;
+
+ assert(err);
+
for (i = 1; i < n; i++)
if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
- const char *str =
- "Multiple updates for ref '%s' not allowed.";
- if (err)
- strbuf_addf(err, str, updates[i]->refname);
-
+ strbuf_addf(err,
+ "Multiple updates for ref '%s' not allowed.",
+ updates[i]->refname);
return 1;
}
return 0;
@@ -3679,6 +3687,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
int n = transaction->nr;
struct ref_update **updates = transaction->updates;
+ assert(err);
+
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: commit called for transaction that is not open");
@@ -3715,9 +3725,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
ret = (errno == ENOTDIR)
? TRANSACTION_NAME_CONFLICT
: TRANSACTION_GENERIC_ERROR;
- if (err)
- strbuf_addf(err, "Cannot lock the ref '%s'.",
- update->refname);
+ strbuf_addf(err, "Cannot lock the ref '%s'.",
+ update->refname);
goto cleanup;
}
}
@@ -3730,9 +3739,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (write_ref_sha1(update->lock, update->new_sha1,
update->msg)) {
update->lock = NULL; /* freed by write_ref_sha1 */
- if (err)
- strbuf_addf(err, "Cannot update the ref '%s'.",
- update->refname);
+ strbuf_addf(err, "Cannot update the ref '%s'.",
+ update->refname);
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 23/24] lockfile: remove unable_to_lock_error
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (21 preceding siblings ...)
2014-10-02 2:33 ` [PATCH 22/24] refs.c: do not permit err == NULL Jonathan Nieder
@ 2014-10-02 2:34 ` Jonathan Nieder
2014-10-02 2:35 ` [PATCH 24/24] ref_transaction_commit: bail out on failure to remove a ref Jonathan Nieder
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:34 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Date: Thu, 28 Aug 2014 16:41:34 -0700
The former caller uses unable_to_lock_message now.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
As before.
cache.h | 1 -
lockfile.c | 10 ----------
2 files changed, 11 deletions(-)
diff --git a/cache.h b/cache.h
index 0c0ac60..f582b6c 100644
--- a/cache.h
+++ b/cache.h
@@ -558,7 +558,6 @@ struct lock_file {
};
#define LOCK_DIE_ON_ERROR 1
#define LOCK_NODEREF 2
-extern int unable_to_lock_error(const char *path, int err);
extern void unable_to_lock_message(const char *path, int err,
struct strbuf *buf);
extern NORETURN void unable_to_lock_index_die(const char *path, int err);
diff --git a/lockfile.c b/lockfile.c
index a921d77..dbd4101 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -176,16 +176,6 @@ void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
absolute_path(path), strerror(err));
}
-int unable_to_lock_error(const char *path, int err)
-{
- struct strbuf buf = STRBUF_INIT;
-
- unable_to_lock_message(path, err, &buf);
- error("%s", buf.buf);
- strbuf_release(&buf);
- return -1;
-}
-
NORETURN void unable_to_lock_index_die(const char *path, int err)
{
struct strbuf buf = STRBUF_INIT;
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread
* [PATCH 24/24] ref_transaction_commit: bail out on failure to remove a ref
2014-10-02 1:48 ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
` (22 preceding siblings ...)
2014-10-02 2:34 ` [PATCH 23/24] lockfile: remove unable_to_lock_error Jonathan Nieder
@ 2014-10-02 2:35 ` Jonathan Nieder
23 siblings, 0 replies; 130+ messages in thread
From: Jonathan Nieder @ 2014-10-02 2:35 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: git, Michael Haggerty
Date: Thu, 28 Aug 2014 17:01:35 -0700
When removal of a loose or packed ref fails, bail out instead of
trying to finish the transaction. This way, a single error message
can be printed (instead of multiple messages being concatenated by
mistake) and the operator can try to solve the underlying problem
before there is a chance to muck things up even more.
In particular, when git fails to remove a ref, git goes on to try to
delete the reflog. Exiting early lets us keep the reflog.
When git succeeds in deleting a ref A and fails to remove a ref B, it
goes on to try to delete both reflogs. It would be better to just
remove the reflog for A, but that would be a more invasive change.
Failing early means we keep both reflogs, which puts the operator in a
good position to understand the problem and recover.
A long term goal is to avoid these problems altogether and roll back
the transaction on failure. That kind of transactionality will have
to wait for a later series (the plan for which is to make all
destructive work happen in a single update of the packed-refs file).
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Ronnie Sahlberg <sahlberg@google.com>
---
That's the end of the series. Thanks for reading.
refs.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/refs.c b/refs.c
index e13f843..d9d327d 100644
--- a/refs.c
+++ b/refs.c
@@ -3753,16 +3753,20 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- if (delete_ref_loose(update->lock, update->type, err))
+ if (delete_ref_loose(update->lock, update->type, err)) {
ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
}
- if (repack_without_refs(delnames, delnum, err))
+ if (repack_without_refs(delnames, delnum, err)) {
ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related [flat|nested] 130+ messages in thread