Merge branch 'rs/ref-transaction'

The API to update refs have been restructured to allow introducing
a true transactional updates later.  We would even allow storing
refs in backends other than the traditional filesystem-based one.

* rs/ref-transaction: (25 commits)
  ref_transaction_commit: bail out on failure to remove a ref
  lockfile: remove unable_to_lock_error
  refs.c: do not permit err == NULL
  remote rm/prune: print a message when writing packed-refs fails
  for-each-ref: skip and warn about broken ref names
  refs.c: allow listing and deleting badly named refs
  test: put tests for handling of bad ref names in one place
  packed-ref cache: forbid dot-components in refnames
  branch -d: simplify by using RESOLVE_REF_READING
  branch -d: avoid repeated symref resolution
  reflog test: test interaction with detached HEAD
  refs.c: change resolve_ref_unsafe reading argument to be a flags field
  refs.c: make write_ref_sha1 static
  fetch.c: change s_update_ref to use a ref transaction
  refs.c: ref_transaction_commit: distinguish name conflicts from other errors
  refs.c: pass a list of names to skip to is_refname_available
  refs.c: call lock_ref_sha1_basic directly from commit
  refs.c: refuse to lock badly named refs in lock_ref_sha1_basic
  rename_ref: don't ask read_ref_full where the ref came from
  refs.c: pass the ref log message to _create/delete/update instead of _commit
  ...
This commit is contained in:
Junio C Hamano 2014-10-21 13:28:10 -07:00
Родитель 13da0fc092 65732845e8
Коммит 3c85452bb0
45 изменённых файлов: 849 добавлений и 350 удалений

Просмотреть файл

@ -170,7 +170,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."));
}
@ -285,8 +285,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);

Просмотреть файл

@ -2286,7 +2286,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
commit->date = now;
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);

Просмотреть файл

@ -130,7 +130,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)
@ -234,9 +235,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, 0, &flags);
if (!target ||
(!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
target = resolve_ref_unsafe(name,
RESOLVE_REF_READING
| 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);
@ -244,7 +248,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;
@ -264,8 +268,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);
@ -298,7 +302,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst;
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)
@ -868,7 +872,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"))

Просмотреть файл

@ -355,7 +355,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
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);
@ -775,7 +775,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;
@ -1072,7 +1072,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);
}

Просмотреть файл

@ -623,7 +623,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"));

Просмотреть файл

@ -1513,7 +1513,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);
if (!strcmp(head, "HEAD"))
head = _("detached HEAD");
else
@ -1809,8 +1809,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);
}

Просмотреть файл

@ -404,23 +404,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

Просмотреть файл

@ -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/"))

Просмотреть файл

@ -635,7 +635,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 = "";
}
@ -694,7 +695,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
@ -838,6 +840,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);

Просмотреть файл

@ -556,7 +556,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"))

Просмотреть файл

@ -1400,7 +1400,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
const char *ref, *v;
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
sha1, NULL);
if (ref && skip_prefix(ref, "refs/heads/", &v))
branch_name = xstrdup(v);
else

Просмотреть файл

@ -1101,7 +1101,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))

Просмотреть файл

@ -702,7 +702,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");

Просмотреть файл

@ -842,8 +842,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);
@ -908,7 +909,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))
@ -1069,7 +1070,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) {

Просмотреть файл

@ -567,7 +567,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
@ -703,7 +704,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))
@ -748,13 +749,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++) {
@ -1331,9 +1335,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);
}

Просмотреть файл

@ -171,8 +171,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);

Просмотреть файл

@ -728,7 +728,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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -733,8 +733,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))

Просмотреть файл

@ -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);

Просмотреть файл

@ -310,7 +310,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;

44
cache.h
Просмотреть файл

@ -950,8 +950,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);
/*
@ -963,29 +963,49 @@ 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 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) and REF_ISSYMREF (if the initial reference was a
* symbolic reference).
* packed references), REF_ISSYMREF (if the initial reference was a
* 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 or has a bad name). See refs.h for more detail
* on each flag.
*
* 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 two
* flags REF_ISBROKEN and REF_BAD_NAME 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.
* Caps and underscores refers to the special refs, such as HEAD,
* FETCH_HEAD and friends, that all live outside of the refs/ directory.
*/
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
#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);
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);

Просмотреть файл

@ -1716,8 +1716,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);
@ -1757,12 +1757,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:

Просмотреть файл

@ -326,6 +326,8 @@ static inline char *git_find_last_dir_sep(const char *path)
#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);
@ -777,11 +779,21 @@ 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).
* 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
* not exist.
*/
int rmdir_or_warn(const char *path);
/*

Просмотреть файл

@ -412,7 +412,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);

Просмотреть файл

@ -162,16 +162,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_die(const char *path, int err)
{
struct strbuf buf = STRBUF_INIT;

Просмотреть файл

@ -71,7 +71,6 @@ struct lock_file {
#define LOCK_DIE_ON_ERROR 1
#define LOCK_NO_DEREF 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_die(const char *path, int err);

Просмотреть файл

@ -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))

Просмотреть файл

@ -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");

410
refs.c
Просмотреть файл

@ -70,16 +70,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))
if (refname[0] == '.')
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 (cp - refname >= LOCK_SUFFIX_LEN &&
!memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
return -1; /* Refname ends with ".lock". */
@ -195,8 +187,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.
*/
/*
@ -204,16 +196,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
@ -282,6 +274,39 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
return dir;
}
/*
* Check if a refname is safe.
* For refs that start with "refs/" we consider it safe as long they do
* not try to resolve to outside of refs/.
*
* For all other refs we only consider them safe iff they only contain
* upper case characters and '_' (like "HEAD" AND "MERGE_HEAD", and not like
* "config").
*/
static int refname_is_safe(const char *refname)
{
if (starts_with(refname, "refs/")) {
char *buf;
int result;
buf = xmalloc(strlen(refname) + 1);
/*
* Does the refname try to escape refs/?
* For example: refs/foo/../bar is safe but refs/foo/../../bar
* is not.
*/
result = !normalize_path_copy(buf, refname + strlen("refs/"));
free(buf);
return result;
}
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)
@ -290,8 +315,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);
@ -787,13 +814,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;
};
@ -817,16 +844,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;
@ -840,12 +870,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;
@ -878,13 +908,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;
@ -1116,7 +1146,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;
@ -1248,12 +1284,19 @@ 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;
}
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);
}
@ -1372,10 +1415,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,
static int resolve_missing_loose_ref(const char *refname,
int resolve_flags,
unsigned char *sha1,
int reading,
int *flag)
int *flags)
{
struct ref_entry *entry;
@ -1386,35 +1429,51 @@ 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;
}
}
/* 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];
int bad_name = 0;
if (flag)
*flag = 0;
if (flags)
*flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
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];
struct stat st;
@ -1439,11 +1498,17 @@ 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);
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 */
@ -1461,8 +1526,12 @@ 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;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
hashclr(sha1);
return refname;
}
continue;
}
}
@ -1507,31 +1576,45 @@ 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;
}
if (bad_name) {
hashclr(sha1);
if (flags)
*flags |= REF_ISBROKEN;
}
return refname;
}
if (flag)
*flag |= REF_ISSYMREF;
if (flags)
*flags |= REF_ISSYMREF;
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 (flag)
*flag |= REF_ISBROKEN;
if (flags)
*flags |= REF_ISBROKEN;
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
!refname_is_safe(buf)) {
errno = EINVAL;
return NULL;
}
refname = strcpy(refname_buffer, buf);
bad_name = 1;
}
}
}
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;
}
@ -1542,22 +1625,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,
@ -1670,7 +1753,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;
/*
@ -1711,7 +1794,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)
@ -1836,7 +1919,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;
@ -1916,7 +1999,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);
@ -2011,7 +2094,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);
@ -2084,7 +2169,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);
@ -2113,7 +2199,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))
@ -2134,11 +2221,12 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
}
/*
* Locks a "refs/" ref returning the lock on success and NULL on failure.
* Locks a ref returning the lock on success and NULL on failure.
* On failure errno is set to something meaningful.
*/
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;
@ -2147,13 +2235,23 @@ 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;
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;
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);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@ -2166,7 +2264,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;
@ -2183,7 +2282,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;
}
@ -2241,9 +2340,7 @@ 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);
return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
}
/*
@ -2445,8 +2542,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);
@ -2510,7 +2607,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)) {
@ -2549,6 +2646,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]))
@ -2559,14 +2658,9 @@ 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);
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]);
}
packed = get_packed_refs(&ref_cache);
/* Remove refnames from the cache */
@ -2591,23 +2685,25 @@ 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;
}
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)
{
assert(err);
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/*
* loose. The loose file name is the same as the
* lockfile name, minus ".lock":
*/
char *loose_filename = get_locked_file_path(lock->lk);
int err = unlink_or_warn(loose_filename);
int res = unlink_or_msg(loose_filename, err);
free(loose_filename);
if (err && errno != ENOENT)
if (res)
return 1;
}
return 0;
@ -2621,8 +2717,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);
@ -2687,6 +2783,21 @@ 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;
}
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];
@ -2699,17 +2810,15 @@ 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);
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)))
@ -2721,7 +2830,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, RESOLVE_REF_READING, sha1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
@ -2739,7 +2848,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;
@ -2754,7 +2863,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;
@ -2934,8 +3043,11 @@ 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';
@ -2996,7 +3108,8 @@ 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);
@ -3367,7 +3480,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);
@ -3404,6 +3517,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];
};
@ -3436,6 +3550,8 @@ struct ref_transaction {
struct ref_transaction *ref_transaction_begin(struct strbuf *err)
{
assert(err);
return xcalloc(1, sizeof(struct ref_transaction));
}
@ -3446,9 +3562,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);
}
@ -3469,57 +3586,80 @@ 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;
assert(err);
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: update called for transaction that is not open");
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;
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;
assert(err);
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: create called for transaction that is not open");
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);
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;
assert(err);
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: delete called for transaction that is not open");
@ -3533,6 +3673,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;
}
@ -3546,8 +3688,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);
@ -3580,26 +3722,29 @@ 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;
}
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;
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");
@ -3613,25 +3758,31 @@ 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++) {
struct ref_update *update = updates[i];
int flags = update->flags;
update->lock = lock_any_ref_for_update(update->refname,
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),
update->flags,
NULL,
flags,
&update->type);
if (!update->lock) {
if (err)
ret = (errno == ENOTDIR)
? TRANSACTION_NAME_CONFLICT
: TRANSACTION_GENERIC_ERROR;
strbuf_addf(err, "Cannot lock the ref '%s'.",
update->refname);
ret = 1;
goto cleanup;
}
}
@ -3641,15 +3792,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,
msg);
if (write_ref_sha1(update->lock, update->new_sha1,
update->msg)) {
update->lock = NULL; /* freed by write_ref_sha1 */
if (ret) {
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 */
}
}
@ -3658,13 +3809,20 @@ 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);
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;
}
}
ret |= 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);

44
refs.h
Просмотреть файл

@ -56,11 +56,19 @@ 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) for the definition of well formed ref names.
*/
#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
@ -177,10 +185,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.
*/
@ -197,9 +207,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.
*/
@ -230,7 +237,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
@ -238,10 +244,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);
@ -274,8 +277,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().
*/
/*
@ -292,7 +295,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);
/*
@ -307,7 +310,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);
/*
@ -321,16 +324,21 @@ 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);
/*
* 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,
const char *msg, struct strbuf *err);
struct strbuf *err);
/*
* Free an existing transaction and all associated data.

Просмотреть файл

@ -508,7 +508,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) &&
skip_prefix(head_ref, "refs/heads/", &head_ref)) {
current_branch = make_branch(head_ref, 0);
@ -1138,7 +1138,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;
@ -1199,7 +1200,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/")))
@ -1673,7 +1676,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);
}

Просмотреть файл

@ -252,8 +252,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);
@ -331,7 +331,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);
@ -871,7 +871,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"));

Просмотреть файл

@ -110,6 +110,32 @@ 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 '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
@ -374,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 &&
@ -398,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 &&
@ -422,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 &&
@ -700,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 &&
@ -730,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 &&
@ -768,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 &&

70
t/t1413-reflog-detach.sh Executable file
Просмотреть файл

@ -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

207
t/t1430-bad-ref-name.sh Executable file
Просмотреть файл

@ -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

Просмотреть файл

@ -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 &&

Просмотреть файл

@ -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 &&

Просмотреть файл

@ -346,36 +346,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

Просмотреть файл

@ -897,7 +897,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;

Просмотреть файл

@ -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;
@ -743,7 +744,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)

Просмотреть файл

@ -744,7 +744,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);

Просмотреть файл

@ -300,14 +300,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;
}

Просмотреть файл

@ -466,17 +466,29 @@ 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));
int err;
if (!rc || errno == ENOENT)
return 0;
err = errno;
warning("unable to %s %s: %s", op, file, strerror(errno));
errno = err;
}
}
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));

Просмотреть файл

@ -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();