From: Jonathan Nieder <jrnieder@gmail.com>
To: Ronnie Sahlberg <sahlberg@google.com>
Cc: "git@vger.kernel.org" <git@vger.kernel.org>,
Michael Haggerty <mhagger@alum.mit.edu>
Subject: [PATCH v22 0/24] rs/ref-transaction
Date: Wed, 1 Oct 2014 18:48:17 -0700 [thread overview]
Message-ID: <20141002014817.GS1175@google.com> (raw)
In-Reply-To: <20140911030318.GD18279@google.com>
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();
next prev parent reply other threads:[~2014-10-02 1:48 UTC|newest]
Thread overview: 129+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-07-30 17:10 Transaction patch series overview Ronnie Sahlberg
2014-07-31 21:41 ` Ronnie Sahlberg
2014-08-08 16:50 ` Ronnie Sahlberg
2014-08-19 19:54 ` Ronnie Sahlberg
2014-08-19 22:28 ` Junio C Hamano
2014-08-20 23:17 ` Jonathan Nieder
2014-08-26 0:03 ` Jonathan Nieder
2014-08-26 21:01 ` Junio C Hamano
2014-08-26 22:14 ` Jonathan Nieder
2014-08-27 0:28 ` [PATCH 0/20] rs/ref-transaction-1 (Re: Transaction patch series overview) Jonathan Nieder
2014-08-27 0:29 ` [PATCH 01/20] refs.c: change ref_transaction_create to do error checking and return status Jonathan Nieder
2014-08-27 0:29 ` [PATCH 02/20] refs.c: update ref_transaction_delete to check for error " Jonathan Nieder
2014-08-27 0:30 ` [PATCH 03/20] refs.c: make ref_transaction_begin take an err argument Jonathan Nieder
2014-08-27 0:30 ` [PATCH 04/20] refs.c: add transaction.status and track OPEN/CLOSED Jonathan Nieder
2014-08-27 0:30 ` [PATCH 05/20] tag.c: use ref transactions when doing updates Jonathan Nieder
2014-08-27 0:31 ` [PATCH 06/20] replace.c: use the ref transaction functions for updates Jonathan Nieder
2014-08-27 0:31 ` [PATCH 07/20] commit.c: use ref transactions " Jonathan Nieder
2014-08-27 0:32 ` [PATCH 08/20] sequencer.c: use ref transactions for all ref updates Jonathan Nieder
2014-08-27 0:32 ` [PATCH 09/20] fast-import.c: change update_branch to use ref transactions Jonathan Nieder
2014-08-27 0:32 ` [PATCH 10/20] branch.c: use ref transaction for all ref updates Jonathan Nieder
2014-08-27 0:33 ` [PATCH 11/20] refs.c: change update_ref to use a transaction Jonathan Nieder
2014-08-27 0:33 ` [PATCH 12/20] receive-pack.c: use a reference transaction for updating the refs Jonathan Nieder
2014-08-27 0:33 ` [PATCH 13/20] fast-import.c: use a ref transaction when dumping tags Jonathan Nieder
2014-08-27 0:34 ` [PATCH 14/20] walker.c: use ref transaction for ref updates Jonathan Nieder
2014-08-27 0:34 ` [PATCH 15/20] refs.c: make lock_ref_sha1 static Jonathan Nieder
2014-08-27 0:35 ` [PATCH 16/20] refs.c: remove the update_ref_lock function Jonathan Nieder
2014-08-27 0:35 ` [PATCH 17/20] refs.c: remove the update_ref_write function Jonathan Nieder
2014-08-27 0:35 ` [PATCH 18/20] refs.c: remove lock_ref_sha1 Jonathan Nieder
2014-08-27 0:36 ` [PATCH 19/20] refs.c: make prune_ref use a transaction to delete the ref Jonathan Nieder
2014-08-27 0:36 ` [PATCH 20/20] refs.c: make delete_ref use a transaction Jonathan Nieder
2014-08-27 21:29 ` [PATCH 0/20] rs/ref-transaction-1 (Re: Transaction patch series overview) Junio C Hamano
2014-08-27 22:37 ` Junio C Hamano
2014-09-02 20:58 ` [PATCH v22 0/22] " Jonathan Nieder
2014-09-02 20:59 ` [PATCH 01/22] refs.c: change ref_transaction_create to do error checking and return status Jonathan Nieder
2014-09-02 21:00 ` [PATCH 02/22] refs.c: update ref_transaction_delete to check for error " Jonathan Nieder
2014-09-02 21:00 ` [PATCH 03/22] refs.c: make ref_transaction_begin take an err argument Jonathan Nieder
2014-09-02 21:00 ` [PATCH 04/22] refs.c: add transaction.status and track OPEN/CLOSED Jonathan Nieder
2014-09-02 21:01 ` [PATCH 05/22] tag.c: use ref transactions when doing updates Jonathan Nieder
2014-09-02 21:01 ` [PATCH 06/22] replace.c: use the ref transaction functions for updates Jonathan Nieder
2014-09-02 21:02 ` [PATCH 07/22] commit.c: use ref transactions " Jonathan Nieder
2014-09-02 21:02 ` [PATCH 08/22] sequencer.c: use ref transactions for all ref updates Jonathan Nieder
2014-09-02 21:03 ` [PATCH 09/22] fast-import.c: change update_branch to use ref transactions Jonathan Nieder
2014-09-02 21:04 ` [PATCH 10/22] branch.c: use ref transaction for all ref updates Jonathan Nieder
2014-09-02 21:04 ` [PATCH 11/22] refs.c: change update_ref to use a transaction Jonathan Nieder
2014-09-02 21:05 ` [PATCH 12/22] receive-pack.c: use a reference transaction for updating the refs Jonathan Nieder
2014-09-02 21:06 ` [PATCH 13/22] fast-import.c: use a ref transaction when dumping tags Jonathan Nieder
2014-09-02 21:07 ` [PATCH 14/22] walker.c: use ref transaction for ref updates Jonathan Nieder
2014-09-02 21:08 ` [PATCH 15/22] refs.c: make lock_ref_sha1 static Jonathan Nieder
2014-09-02 21:08 ` [PATCH 16/22] refs.c: remove the update_ref_lock function Jonathan Nieder
2014-09-02 21:08 ` [PATCH 17/22] refs.c: remove the update_ref_write function Jonathan Nieder
2014-09-02 21:09 ` [PATCH 18/22] refs.c: remove lock_ref_sha1 Jonathan Nieder
2014-09-02 21:09 ` [PATCH 19/22] refs.c: make prune_ref use a transaction to delete the ref Jonathan Nieder
2014-09-02 21:10 ` [PATCH 20/22] refs.c: make delete_ref use a transaction Jonathan Nieder
2014-09-02 21:10 ` [PATCH 21/22] update-ref --stdin: narrow scope of err strbuf Jonathan Nieder
2014-09-02 21:11 ` [PATCH 22/22] update-ref --stdin: pass transaction around explicitly Jonathan Nieder
2014-08-27 21:53 ` Using Gerrit to review Git patches (was: Re: Transaction patch series overview) Michael Haggerty
2014-08-28 5:07 ` Shawn Pearce
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 ` [PATCH 03/19] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
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 ` [PATCH 05/19] refs.c: pass the ref log message to _create/delete/update instead of _commit 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
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 ` [PATCH 08/19] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
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 ` [PATCH 10/19] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
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 ` [PATCH 12/19] refs.c: make write_ref_sha1 static 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
2014-09-11 3:10 ` [PATCH 14/19] branch -d: avoid repeated symref resolution Jonathan Nieder
2014-09-11 3:10 ` [PATCH 15/19] refs.c: fix handling of badly named refs Jonathan Nieder
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 ` [PATCH 17/19] refs.c: do not permit err == NULL Jonathan Nieder
2014-09-11 3:12 ` [PATCH 18/19] lockfile: remove unable_to_lock_error Jonathan Nieder
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 ` [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-12 19:00 ` Junio C Hamano
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-13 17:52 ` Junio C Hamano
2014-09-12 21:52 ` Michael Haggerty
2014-09-12 23:57 ` Jonathan Nieder
2014-09-17 13:23 ` Michael Haggerty
2014-09-18 16:42 ` Junio C Hamano
2014-09-18 16:57 ` Jonathan Nieder
2014-09-18 17:26 ` Junio C Hamano
2014-09-18 17:38 ` Jonathan Nieder
2014-09-25 21:35 ` Junio C Hamano
2014-09-25 21:40 ` Jonathan Nieder
2014-09-25 21:55 ` Junio C Hamano
2014-10-02 1:48 ` Jonathan Nieder [this message]
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 ` [PATCH 03/24] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
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 ` [PATCH 05/24] refs.c: pass the ref log message to _create/delete/update instead of _commit 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
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 ` [PATCH 08/24] refs.c: call lock_ref_sha1_basic directly from commit 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
2014-10-02 19:18 ` Junio C Hamano
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
2014-10-07 19:26 ` Junio C Hamano
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 ` [PATCH 11/24] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
2014-10-02 2:08 ` [PATCH 12/24] refs.c: make write_ref_sha1 static 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
2014-10-02 2:10 ` [PATCH 14/24] reflog test: test interaction with detached HEAD Jonathan Nieder
2014-10-02 2:15 ` [PATCH 15/24] branch -d: avoid repeated symref resolution Jonathan Nieder
2014-10-02 2:15 ` [PATCH 16/24] branch -d: simplify by using RESOLVE_REF_READING flag Jonathan Nieder
2014-10-02 2:16 ` [PATCH 17/24] packed-ref cache: forbid dot-components in refnames Jonathan Nieder
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 ` [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
2014-10-03 20:32 ` Ronnie Sahlberg
2014-10-03 20:39 ` Junio C Hamano
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 ` [PATCH 21/24] remote rm/prune: print a message when writing packed-refs fails Jonathan Nieder
2014-10-02 2:33 ` [PATCH 22/24] refs.c: do not permit err == NULL 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20141002014817.GS1175@google.com \
--to=jrnieder@gmail.com \
--cc=git@vger.kernel.org \
--cc=mhagger@alum.mit.edu \
--cc=sahlberg@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).