#include "iterator.h"
#include "refs.h"
#include "refs/refs-internal.h"
+#include "run-command.h"
#include "object-store.h"
#include "object.h"
#include "tag.h"
#include "submodule.h"
#include "worktree.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "repository.h"
+#include "sigchain.h"
/*
* List of all available backends
* not legal. It is legal if it is something reasonable to have under
* ".git/refs/"; We do not like it if:
*
- * - any path component of it begins with ".", or
+ * - it begins with ".", or
* - it has double dots "..", or
* - it has ASCII control characters, or
* - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
* - it ends with a "/", or
* - it ends with ".lock", or
* - it contains a "@{" portion
+ *
+ * When sanitized is not NULL, instead of rejecting the input refname
+ * as an error, try to come up with a usable replacement for the input
+ * refname in it.
*/
-static int check_refname_component(const char *refname, int *flags)
+static int check_refname_component(const char *refname, int *flags,
+ struct strbuf *sanitized)
{
const char *cp;
char last = '\0';
+ size_t component_start = 0; /* garbage - not a reasonable initial value */
+
+ if (sanitized)
+ component_start = sanitized->len;
for (cp = refname; ; cp++) {
int ch = *cp & 255;
unsigned char disp = refname_disposition[ch];
+
+ if (sanitized && disp != 1)
+ strbuf_addch(sanitized, ch);
+
switch (disp) {
case 1:
goto out;
case 2:
- if (last == '.')
- return -1; /* Refname contains "..". */
+ if (last == '.') { /* Refname contains "..". */
+ if (sanitized)
+ /* collapse ".." to single "." */
+ strbuf_setlen(sanitized, sanitized->len - 1);
+ else
+ return -1;
+ }
break;
case 3:
- if (last == '@')
- return -1; /* Refname contains "@{". */
+ if (last == '@') { /* Refname contains "@{". */
+ if (sanitized)
+ sanitized->buf[sanitized->len-1] = '-';
+ else
+ return -1;
+ }
break;
case 4:
- return -1;
+ /* forbidden char */
+ if (sanitized)
+ sanitized->buf[sanitized->len-1] = '-';
+ else
+ return -1;
+ break;
case 5:
- if (!(*flags & REFNAME_REFSPEC_PATTERN))
- return -1; /* refspec can't be a pattern */
+ if (!(*flags & REFNAME_REFSPEC_PATTERN)) {
+ /* refspec can't be a pattern */
+ if (sanitized)
+ sanitized->buf[sanitized->len-1] = '-';
+ else
+ return -1;
+ }
/*
* Unset the pattern flag so that we only accept
out:
if (cp == refname)
return 0; /* Component has zero length. */
- if (refname[0] == '.')
- return -1; /* Component starts with '.'. */
+
+ if (refname[0] == '.') { /* Component starts with '.'. */
+ if (sanitized)
+ sanitized->buf[component_start] = '-';
+ else
+ return -1;
+ }
if (cp - refname >= LOCK_SUFFIX_LEN &&
- !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
- return -1; /* Refname ends with ".lock". */
+ !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN)) {
+ if (!sanitized)
+ return -1;
+ /* Refname ends with ".lock". */
+ while (strbuf_strip_suffix(sanitized, LOCK_SUFFIX)) {
+ /* try again in case we have .lock.lock */
+ }
+ }
return cp - refname;
}
-int check_refname_format(const char *refname, int flags)
+static int check_or_sanitize_refname(const char *refname, int flags,
+ struct strbuf *sanitized)
{
int component_len, component_count = 0;
- if (!strcmp(refname, "@"))
+ if (!strcmp(refname, "@")) {
/* Refname is a single character '@'. */
- return -1;
+ if (sanitized)
+ strbuf_addch(sanitized, '-');
+ else
+ return -1;
+ }
while (1) {
+ if (sanitized && sanitized->len)
+ strbuf_complete(sanitized, '/');
+
/* We are at the start of a path component. */
- component_len = check_refname_component(refname, &flags);
- if (component_len <= 0)
+ component_len = check_refname_component(refname, &flags,
+ sanitized);
+ if (sanitized && component_len == 0)
+ ; /* OK, omit empty component */
+ else if (component_len <= 0)
return -1;
component_count++;
refname += component_len + 1;
}
- if (refname[component_len - 1] == '.')
- return -1; /* Refname ends with '.'. */
+ if (refname[component_len - 1] == '.') {
+ /* Refname ends with '.'. */
+ if (sanitized)
+ ; /* omit ending dot */
+ else
+ return -1;
+ }
if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
return -1; /* Refname has only one component. */
return 0;
}
+int check_refname_format(const char *refname, int flags)
+{
+ return check_or_sanitize_refname(refname, flags, NULL);
+}
+
+void sanitize_refname_component(const char *refname, struct strbuf *out)
+{
+ if (check_or_sanitize_refname(refname, REFNAME_ALLOW_ONELEVEL, out))
+ BUG("sanitizing refname '%s' check returned error", refname);
+}
+
int refname_is_safe(const char *refname)
{
const char *rest;
{
if (flags & REF_ISBROKEN)
return 0;
- if (!has_sha1_file(oid->hash)) {
+ if (!has_object_file(oid)) {
error(_("%s does not point to a valid object!"), refname);
return 0;
}
/* The argument to filter_refs */
struct ref_filter {
const char *pattern;
+ const char *prefix;
each_ref_fn *fn;
void *cb_data;
};
return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
}
-int ref_exists(const char *refname)
+int refs_ref_exists(struct ref_store *refs, const char *refname)
{
- return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
+ return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING, NULL, NULL);
}
-static int match_ref_pattern(const char *refname,
- const struct string_list_item *item)
-{
- int matched = 0;
- if (item->util == NULL) {
- if (!wildmatch(item->string, refname, 0))
- matched = 1;
- } else {
- const char *rest;
- if (skip_prefix(refname, item->string, &rest) &&
- (!*rest || *rest == '/'))
- matched = 1;
- }
- return matched;
-}
-
-int ref_filter_match(const char *refname,
- const struct string_list *include_patterns,
- const struct string_list *exclude_patterns)
+int ref_exists(const char *refname)
{
- struct string_list_item *item;
-
- if (exclude_patterns && exclude_patterns->nr) {
- for_each_string_list_item(item, exclude_patterns) {
- if (match_ref_pattern(refname, item))
- return 0;
- }
- }
-
- if (include_patterns && include_patterns->nr) {
- int found = 0;
- for_each_string_list_item(item, include_patterns) {
- if (match_ref_pattern(refname, item)) {
- found = 1;
- break;
- }
- }
-
- if (!found)
- return 0;
- }
- return 1;
+ return refs_ref_exists(get_main_ref_store(the_repository), refname);
}
static int filter_refs(const char *refname, const struct object_id *oid,
if (wildmatch(filter->pattern, refname, 0))
return 0;
+ if (filter->prefix)
+ skip_prefix(refname, filter->prefix, &refname);
return filter->fn(refname, oid, flags, filter->cb_data);
}
enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
{
- struct object *o = lookup_unknown_object(name->hash);
+ struct object *o = lookup_unknown_object(name);
if (o->type == OBJ_NONE) {
int type = oid_object_info(the_repository, name, NULL);
- if (type < 0 || !object_as_type(the_repository, o, type, 0))
+ if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
}
}
filter.pattern = real_pattern.buf;
+ filter.prefix = prefix;
filter.fn = fn;
filter.cb_data = cb_data;
ret = for_each_ref(filter_refs, &filter);
* Given a 'prefix' expand it by the rules in 'ref_rev_parse_rules' and add
* the results to 'prefixes'
*/
-void expand_ref_prefix(struct argv_array *prefixes, const char *prefix)
+void expand_ref_prefix(struct strvec *prefixes, const char *prefix)
{
const char **p;
int len = strlen(prefix);
for (p = ref_rev_parse_rules; *p; p++)
- argv_array_pushf(prefixes, *p, len, prefix);
+ strvec_pushf(prefixes, *p, len, prefix);
+}
+
+static const char default_branch_name_advice[] = N_(
+"Using '%s' as the name for the initial branch. This default branch name\n"
+"is subject to change. To configure the initial branch name to use in all\n"
+"of your new repositories, which will suppress this warning, call:\n"
+"\n"
+"\tgit config --global init.defaultBranch <name>\n"
+"\n"
+"Names commonly chosen instead of 'master' are 'main', 'trunk' and\n"
+"'development'. The just-created branch can be renamed via this command:\n"
+"\n"
+"\tgit branch -m <name>\n"
+);
+
+char *repo_default_branch_name(struct repository *r, int quiet)
+{
+ const char *config_key = "init.defaultbranch";
+ const char *config_display_key = "init.defaultBranch";
+ char *ret = NULL, *full_ref;
+ const char *env = getenv("GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME");
+
+ if (env && *env)
+ ret = xstrdup(env);
+ else if (repo_config_get_string(r, config_key, &ret) < 0)
+ die(_("could not retrieve `%s`"), config_display_key);
+
+ if (!ret) {
+ ret = xstrdup("master");
+ if (!quiet)
+ advise(_(default_branch_name_advice), ret);
+ }
+
+ full_ref = xstrfmt("refs/heads/%s", ret);
+ if (check_refname_format(full_ref, 0))
+ die(_("invalid branch name: %s = %s"), config_display_key, ret);
+ free(full_ref);
+
+ return ret;
+}
+
+const char *git_default_branch_name(int quiet)
+{
+ static char *ret;
+
+ if (!ret)
+ ret = repo_default_branch_name(the_repository, quiet);
+
+ return ret;
}
/*
* later free()ing) if the string passed in is a magic short-hand form
* to name a branch.
*/
-static char *substitute_branch_name(const char **string, int *len)
+static char *substitute_branch_name(struct repository *r,
+ const char **string, int *len,
+ int nonfatal_dangling_mark)
{
struct strbuf buf = STRBUF_INIT;
- int ret = interpret_branch_name(*string, *len, &buf, 0);
+ struct interpret_branch_name_options options = {
+ .nonfatal_dangling_mark = nonfatal_dangling_mark
+ };
+ int ret = repo_interpret_branch_name(r, *string, *len, &buf, &options);
if (ret == *len) {
size_t size;
return NULL;
}
-int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
+int repo_dwim_ref(struct repository *r, const char *str, int len,
+ struct object_id *oid, char **ref, int nonfatal_dangling_mark)
{
- char *last_branch = substitute_branch_name(&str, &len);
- int refs_found = expand_ref(str, len, oid, ref);
+ char *last_branch = substitute_branch_name(r, &str, &len,
+ nonfatal_dangling_mark);
+ int refs_found = expand_ref(r, str, len, oid, ref);
free(last_branch);
return refs_found;
}
-int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
+int expand_ref(struct repository *repo, const char *str, int len,
+ struct object_id *oid, char **ref)
{
const char **p, *r;
int refs_found = 0;
this_result = refs_found ? &oid_from_ref : oid;
strbuf_reset(&fullref);
strbuf_addf(&fullref, *p, len, str);
- r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
- this_result, &flag);
+ r = refs_resolve_ref_unsafe(get_main_ref_store(repo),
+ fullref.buf, RESOLVE_REF_READING,
+ this_result, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
return refs_found;
}
-int dwim_log(const char *str, int len, struct object_id *oid, char **log)
+int repo_dwim_log(struct repository *r, const char *str, int len,
+ struct object_id *oid, char **log)
{
- char *last_branch = substitute_branch_name(&str, &len);
+ struct ref_store *refs = get_main_ref_store(r);
+ char *last_branch = substitute_branch_name(r, &str, &len, 0);
const char **p;
int logs_found = 0;
struct strbuf path = STRBUF_INIT;
strbuf_reset(&path);
strbuf_addf(&path, *p, len, str);
- ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
- &hash, NULL);
+ ref = refs_resolve_ref_unsafe(refs, path.buf,
+ RESOLVE_REF_READING,
+ &hash, NULL);
if (!ref)
continue;
- if (reflog_exists(path.buf))
+ if (refs_reflog_exists(refs, path.buf))
it = path.buf;
- else if (strcmp(ref, path.buf) && reflog_exists(ref))
+ else if (strcmp(ref, path.buf) &&
+ refs_reflog_exists(refs, ref))
it = ref;
else
continue;
return logs_found;
}
+int dwim_log(const char *str, int len, struct object_id *oid, char **log)
+{
+ return repo_dwim_log(the_repository, str, len, oid, log);
+}
+
static int is_per_worktree_ref(const char *refname)
{
- return !strcmp(refname, "HEAD") ||
- starts_with(refname, "refs/bisect/") ||
- starts_with(refname, "refs/rewritten/");
+ return starts_with(refname, "refs/worktree/") ||
+ starts_with(refname, "refs/bisect/") ||
+ starts_with(refname, "refs/rewritten/");
}
static int is_pseudoref_syntax(const char *refname)
return 1;
}
+static int is_main_pseudoref_syntax(const char *refname)
+{
+ return skip_prefix(refname, "main-worktree/", &refname) &&
+ *refname &&
+ is_pseudoref_syntax(refname);
+}
+
+static int is_other_pseudoref_syntax(const char *refname)
+{
+ if (!skip_prefix(refname, "worktrees/", &refname))
+ return 0;
+ refname = strchr(refname, '/');
+ if (!refname || !refname[1])
+ return 0;
+ return is_pseudoref_syntax(refname + 1);
+}
+
enum ref_type ref_type(const char *refname)
{
if (is_per_worktree_ref(refname))
return REF_TYPE_PER_WORKTREE;
if (is_pseudoref_syntax(refname))
return REF_TYPE_PSEUDOREF;
- return REF_TYPE_NORMAL;
+ if (is_main_pseudoref_syntax(refname))
+ return REF_TYPE_MAIN_PSEUDOREF;
+ if (is_other_pseudoref_syntax(refname))
+ return REF_TYPE_OTHER_PSEUDOREF;
+ return REF_TYPE_NORMAL;
}
long get_files_ref_lock_timeout_ms(void)
return timeout_ms;
}
-static int write_pseudoref(const char *pseudoref, const struct object_id *oid,
- const struct object_id *old_oid, struct strbuf *err)
-{
- const char *filename;
- int fd;
- struct lock_file lock = LOCK_INIT;
- struct strbuf buf = STRBUF_INIT;
- int ret = -1;
-
- if (!oid)
- return 0;
-
- strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
-
- filename = git_path("%s", pseudoref);
- fd = hold_lock_file_for_update_timeout(&lock, filename, 0,
- get_files_ref_lock_timeout_ms());
- if (fd < 0) {
- strbuf_addf(err, _("could not open '%s' for writing: %s"),
- filename, strerror(errno));
- goto done;
- }
-
- if (old_oid) {
- struct object_id actual_old_oid;
-
- if (read_ref(pseudoref, &actual_old_oid)) {
- if (!is_null_oid(old_oid)) {
- strbuf_addf(err, _("could not read ref '%s'"),
- pseudoref);
- rollback_lock_file(&lock);
- goto done;
- }
- } else if (is_null_oid(old_oid)) {
- strbuf_addf(err, _("ref '%s' already exists"),
- pseudoref);
- rollback_lock_file(&lock);
- goto done;
- } else if (oidcmp(&actual_old_oid, old_oid)) {
- strbuf_addf(err, _("unexpected object ID when writing '%s'"),
- pseudoref);
- rollback_lock_file(&lock);
- goto done;
- }
- }
-
- if (write_in_full(fd, buf.buf, buf.len) < 0) {
- strbuf_addf(err, _("could not write to '%s'"), filename);
- rollback_lock_file(&lock);
- goto done;
- }
-
- commit_lock_file(&lock);
- ret = 0;
-done:
- strbuf_release(&buf);
- return ret;
-}
-
-static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
-{
- const char *filename;
-
- filename = git_path("%s", pseudoref);
-
- if (old_oid && !is_null_oid(old_oid)) {
- struct lock_file lock = LOCK_INIT;
- int fd;
- struct object_id actual_old_oid;
-
- fd = hold_lock_file_for_update_timeout(
- &lock, filename, 0,
- get_files_ref_lock_timeout_ms());
- if (fd < 0) {
- error_errno(_("could not open '%s' for writing"),
- filename);
- return -1;
- }
- if (read_ref(pseudoref, &actual_old_oid))
- die(_("could not read ref '%s'"), pseudoref);
- if (oidcmp(&actual_old_oid, old_oid)) {
- error(_("unexpected object ID when deleting '%s'"),
- pseudoref);
- rollback_lock_file(&lock);
- return -1;
- }
-
- unlink(filename);
- rollback_lock_file(&lock);
- } else {
- unlink(filename);
- }
-
- return 0;
-}
-
int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname,
const struct object_id *old_oid,
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
- assert(refs == get_main_ref_store(the_repository));
- return delete_pseudoref(refname, old_oid);
- }
-
transaction = ref_store_transaction_begin(refs, &err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_oid,
old_oid, flags);
}
-void copy_reflog_msg(struct strbuf *sb, const char *msg)
+static void copy_reflog_msg(struct strbuf *sb, const char *msg)
{
char c;
int wasspace = 1;
- strbuf_addch(sb, '\t');
while ((c = *msg++)) {
if (wasspace && isspace(c))
continue;
strbuf_rtrim(sb);
}
+static char *normalize_reflog_message(const char *msg)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ if (msg && *msg)
+ copy_reflog_msg(&sb, msg);
+ return strbuf_detach(&sb, NULL);
+}
+
int should_autocreate_reflog(const char *refname)
{
switch (log_all_ref_updates) {
*/
if (!is_null_oid(&cb->ooid)) {
oidcpy(cb->oid, noid);
- if (oidcmp(&cb->ooid, noid))
+ if (!oideq(&cb->ooid, noid))
warning(_("log for ref %s has gap after %s"),
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
}
else if (cb->date == cb->at_time)
oidcpy(cb->oid, noid);
- else if (oidcmp(noid, cb->oid))
+ else if (!oideq(noid, cb->oid))
warning(_("log for ref %s unexpectedly ended on %s"),
cb->refname, show_date(cb->date, cb->tz,
DATE_MODE(RFC2822)));
return 1;
}
-int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
+int read_ref_at(struct ref_store *refs, const char *refname,
+ unsigned int flags, timestamp_t at_time, int cnt,
struct object_id *oid, char **msg,
timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
{
cb.cutoff_cnt = cutoff_cnt;
cb.oid = oid;
- for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
+ refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
if (!cb.reccnt) {
if (flags & GET_OID_QUIETLY)
if (cb.found_it)
return 0;
- for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
+ refs_for_each_reflog_ent(refs, refname, read_ref_at_ent_oldest, &cb);
return 1;
}
oidcpy(&update->new_oid, new_oid);
if (flags & REF_HAVE_OLD)
oidcpy(&update->old_oid, old_oid);
- update->msg = xstrdup_or_null(msg);
+ update->msg = normalize_reflog_message(msg);
return update;
}
struct strbuf err = STRBUF_INIT;
int ret = 0;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
- assert(refs == get_main_ref_store(the_repository));
- ret = write_pseudoref(refname, new_oid, old_oid, &err);
- } else {
- t = ref_store_transaction_begin(refs, &err);
- if (!t ||
- ref_transaction_update(t, refname, new_oid, old_oid,
- flags, msg, &err) ||
- ref_transaction_commit(t, &err)) {
- ret = 1;
- ref_transaction_free(t);
- }
+ t = ref_store_transaction_begin(refs, &err);
+ if (!t ||
+ ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
+ &err) ||
+ ref_transaction_commit(t, &err)) {
+ ret = 1;
+ ref_transaction_free(t);
}
if (ret) {
const char *str = _("update_ref failed for ref '%s': %s");
old_oid, flags, onerr);
}
-char *shorten_unambiguous_ref(const char *refname, int strict)
+char *refs_shorten_unambiguous_ref(struct ref_store *refs,
+ const char *refname, int strict)
{
int i;
static char **scanf_fmts;
strbuf_reset(&resolved_buf);
strbuf_addf(&resolved_buf, rule,
short_name_len, short_name);
- if (ref_exists(resolved_buf.buf))
+ if (refs_ref_exists(refs, resolved_buf.buf))
break;
}
return xstrdup(refname);
}
+char *shorten_unambiguous_ref(const char *refname, int strict)
+{
+ return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+ refname, strict);
+}
+
static struct string_list *hide_refs;
int parse_hide_refs_config(const char *var, const char *value, const char *section)
* non-zero value, stop the iteration and return that value;
* otherwise, return 0.
*/
+static int do_for_each_repo_ref(struct repository *r, const char *prefix,
+ each_repo_ref_fn fn, int trim, int flags,
+ void *cb_data)
+{
+ struct ref_iterator *iter;
+ struct ref_store *refs = get_main_ref_store(r);
+
+ if (!refs)
+ return 0;
+
+ iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+
+ return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
+}
+
+struct do_for_each_ref_help {
+ each_ref_fn *fn;
+ void *cb_data;
+};
+
+static int do_for_each_ref_helper(struct repository *r,
+ const char *refname,
+ const struct object_id *oid,
+ int flags,
+ void *cb_data)
+{
+ struct do_for_each_ref_help *hp = cb_data;
+
+ return hp->fn(refname, oid, flags, hp->cb_data);
+}
+
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
each_ref_fn fn, int trim, int flags, void *cb_data)
{
struct ref_iterator *iter;
+ struct do_for_each_ref_help hp = { fn, cb_data };
if (!refs)
return 0;
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
- return do_for_each_ref_iterator(iter, fn, cb_data);
+ return do_for_each_repo_ref_iterator(the_repository, iter,
+ do_for_each_ref_helper, &hp);
}
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
}
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(get_main_ref_store(r),
- git_replace_ref_base, fn,
- strlen(git_replace_ref_base),
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+ return do_for_each_repo_ref(r, git_replace_ref_base, fn,
+ strlen(git_replace_ref_base),
+ DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
}
+static int refs_read_special_head(struct ref_store *ref_store,
+ const char *refname, struct object_id *oid,
+ struct strbuf *referent, unsigned int *type)
+{
+ struct strbuf full_path = STRBUF_INIT;
+ struct strbuf content = STRBUF_INIT;
+ int result = -1;
+ strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
+
+ if (strbuf_read_file(&content, full_path.buf, 0) < 0)
+ goto done;
+
+ result = parse_loose_ref_contents(content.buf, oid, referent, type);
+
+done:
+ strbuf_release(&full_path);
+ strbuf_release(&content);
+ return result;
+}
+
int refs_read_raw_ref(struct ref_store *ref_store,
const char *refname, struct object_id *oid,
struct strbuf *referent, unsigned int *type)
{
- return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, type);
+ if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+ return refs_read_special_head(ref_store, refname, oid, referent,
+ type);
+ }
+
+ return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
+ type);
}
/* This function needs to return a meaningful errno on failure */
struct ref_store_hash_entry
{
- struct hashmap_entry ent; /* must be the first member! */
+ struct hashmap_entry ent;
struct ref_store *refs;
};
static int ref_store_hash_cmp(const void *unused_cmp_data,
- const void *entry, const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *keydata)
{
- const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
- const char *name = keydata ? keydata : e2->name;
+ const struct ref_store_hash_entry *e1, *e2;
+ const char *name;
+
+ e1 = container_of(eptr, const struct ref_store_hash_entry, ent);
+ e2 = container_of(entry_or_key, const struct ref_store_hash_entry, ent);
+ name = keydata ? keydata : e2->name;
return strcmp(e1->name, name);
}
struct ref_store_hash_entry *entry;
FLEX_ALLOC_STR(entry, name, name);
- hashmap_entry_init(entry, strhash(name));
+ hashmap_entry_init(&entry->ent, strhash(name));
entry->refs = refs;
return entry;
}
const char *name)
{
struct ref_store_hash_entry *entry;
+ unsigned int hash;
if (!map->tablesize)
/* It's initialized on demand in register_ref_store(). */
return NULL;
- entry = hashmap_get_from_hash(map, strhash(name), name);
+ hash = strhash(name);
+ entry = hashmap_get_entry_from_hash(map, hash, name,
+ struct ref_store_hash_entry, ent);
return entry ? entry->refs : NULL;
}
struct ref_store *get_main_ref_store(struct repository *r)
{
- if (r->refs)
- return r->refs;
+ if (r->refs_private)
+ return r->refs_private;
if (!r->gitdir)
BUG("attempting to get main_ref_store outside of repository");
- r->refs = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
- return r->refs;
+ r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
+ return r->refs_private;
}
/*
struct ref_store *refs,
const char *name)
{
+ struct ref_store_hash_entry *entry;
+
if (!map->tablesize)
hashmap_init(map, ref_store_hash_cmp, NULL, 0);
- if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
+ entry = alloc_ref_store_hash_entry(name, refs);
+ if (hashmap_put(map, &entry->ent))
BUG("%s ref_store '%s' initialized twice", type, name);
}
const char *refs_heads_master,
const char *logmsg)
{
- return refs->be->create_symref(refs, ref_target,
- refs_heads_master,
- logmsg);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
+ msg);
+ free(msg);
+ return retval;
}
int create_symref(const char *ref_target, const char *refs_heads_master,
return 0;
}
+static int run_transaction_hook(struct ref_transaction *transaction,
+ const char *state)
+{
+ struct child_process proc = CHILD_PROCESS_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ const char *hook;
+ int ret = 0, i;
+
+ hook = find_hook("reference-transaction");
+ if (!hook)
+ return ret;
+
+ strvec_pushl(&proc.args, hook, state, NULL);
+ proc.in = -1;
+ proc.stdout_to_stderr = 1;
+ proc.trace2_hook_name = "reference-transaction";
+
+ ret = start_command(&proc);
+ if (ret)
+ return ret;
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "%s %s %s\n",
+ oid_to_hex(&update->old_oid),
+ oid_to_hex(&update->new_oid),
+ update->refname);
+
+ if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
+ if (errno != EPIPE)
+ ret = -1;
+ break;
+ }
+ }
+
+ close(proc.in);
+ sigchain_pop(SIGPIPE);
+ strbuf_release(&buf);
+
+ ret |= finish_command(&proc);
+ return ret;
+}
+
int ref_transaction_prepare(struct ref_transaction *transaction,
struct strbuf *err)
{
struct ref_store *refs = transaction->ref_store;
+ int ret;
switch (transaction->state) {
case REF_TRANSACTION_OPEN:
return -1;
}
- return refs->be->transaction_prepare(refs, transaction, err);
+ ret = refs->be->transaction_prepare(refs, transaction, err);
+ if (ret)
+ return ret;
+
+ ret = run_transaction_hook(transaction, "prepared");
+ if (ret) {
+ ref_transaction_abort(transaction, err);
+ die(_("ref updates aborted by hook"));
+ }
+
+ return 0;
}
int ref_transaction_abort(struct ref_transaction *transaction,
break;
}
+ run_transaction_hook(transaction, "aborted");
+
ref_transaction_free(transaction);
return ret;
}
break;
}
- return refs->be->transaction_finish(refs, transaction, err);
+ ret = refs->be->transaction_finish(refs, transaction, err);
+ if (!ret)
+ run_transaction_hook(transaction, "committed");
+ return ret;
}
int refs_verify_refname_available(struct ref_store *refs,
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
struct ref_iterator *iter;
+ struct do_for_each_ref_help hp = { fn, cb_data };
iter = refs->be->reflog_iterator_begin(refs);
- return do_for_each_ref_iterator(iter, fn, cb_data);
+ return do_for_each_repo_ref_iterator(the_repository, iter,
+ do_for_each_ref_helper, &hp);
}
int for_each_reflog(each_ref_fn fn, void *cb_data)
return refs->be->initial_transaction_commit(refs, transaction, err);
}
-int refs_delete_refs(struct ref_store *refs, const char *msg,
+int refs_delete_refs(struct ref_store *refs, const char *logmsg,
struct string_list *refnames, unsigned int flags)
{
- return refs->be->delete_refs(refs, msg, refnames, flags);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->delete_refs(refs, msg, refnames, flags);
+ free(msg);
+ return retval;
}
int delete_refs(const char *msg, struct string_list *refnames,
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
- return refs->be->rename_ref(refs, oldref, newref, logmsg);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->rename_ref(refs, oldref, newref, msg);
+ free(msg);
+ return retval;
}
int rename_ref(const char *oldref, const char *newref, const char *logmsg)
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
- return refs->be->copy_ref(refs, oldref, newref, logmsg);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->copy_ref(refs, oldref, newref, msg);
+ free(msg);
+ return retval;
}
int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)