#include "cache.h"
+#include "config.h"
#include "remote.h"
#include "refs.h"
#include "commit.h"
#include "tag.h"
#include "string-list.h"
#include "mergesort.h"
+#include "argv-array.h"
enum map_direction { FROM_SRC, FROM_DST };
"refs/tags/*"
};
+/* See TAG_REFSPEC for the string version */
const struct refspec *tag_refspec = &s_tag_refspec;
struct counted_string {
static struct remote **remotes;
static int remotes_alloc;
static int remotes_nr;
+static struct hashmap remotes_hash;
static struct branch **branches;
static int branches_alloc;
static int branches_nr;
static struct branch *current_branch;
-static const char *default_remote_name;
-static const char *branch_pushremote_name;
static const char *pushremote_name;
-static int explicit_default_remote_name;
static struct rewrites rewrites;
static struct rewrites rewrites_push;
-#define BUF_SIZE (2048)
-static char buffer[BUF_SIZE];
-
static int valid_remote(const struct remote *remote)
{
return (!!remote->url) || (!!remote->foreign_vcs);
static const char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
- char *ret;
struct counted_string *longest;
int longest_i;
if (!longest)
return url;
- ret = xmalloc(r->rewrite[longest_i]->baselen +
- (strlen(url) - longest->len) + 1);
- strcpy(ret, r->rewrite[longest_i]->base);
- strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
- return ret;
+ return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
}
static void add_push_refspec(struct remote *remote, const char *ref)
remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
}
+void add_prune_tags_to_fetch_refspec(struct remote *remote)
+{
+ int nr = remote->fetch_refspec_nr;
+ int bufsize = nr + 1;
+ int size = sizeof(struct refspec);
+
+ remote->fetch = xrealloc(remote->fetch, size * bufsize);
+ memcpy(&remote->fetch[nr], tag_refspec, size);
+ add_fetch_refspec(remote, xstrdup(TAG_REFSPEC));
+}
+
static void add_url(struct remote *remote, const char *url)
{
ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
add_pushurl_alias(remote, url);
}
+struct remotes_hash_key {
+ const char *str;
+ int len;
+};
+
+static int remotes_hash_cmp(const void *unused_cmp_data,
+ const void *entry,
+ const void *entry_or_key,
+ const void *keydata)
+{
+ const struct remote *a = entry;
+ const struct remote *b = entry_or_key;
+ const struct remotes_hash_key *key = keydata;
+
+ if (key)
+ return strncmp(a->name, key->str, key->len) || a->name[key->len];
+ else
+ return strcmp(a->name, b->name);
+}
+
+static inline void init_remotes_hash(void)
+{
+ if (!remotes_hash.cmpfn)
+ hashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);
+}
+
static struct remote *make_remote(const char *name, int len)
{
- struct remote *ret;
- int i;
+ struct remote *ret, *replaced;
+ struct remotes_hash_key lookup;
+ struct hashmap_entry lookup_entry;
- for (i = 0; i < remotes_nr; i++) {
- if (len ? (!strncmp(name, remotes[i]->name, len) &&
- !remotes[i]->name[len]) :
- !strcmp(name, remotes[i]->name))
- return remotes[i];
- }
+ if (!len)
+ len = strlen(name);
+
+ init_remotes_hash();
+ lookup.str = name;
+ lookup.len = len;
+ hashmap_entry_init(&lookup_entry, memhash(name, len));
+
+ if ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL)
+ return ret;
ret = xcalloc(1, sizeof(struct remote));
ret->prune = -1; /* unspecified */
+ ret->prune_tags = -1; /* unspecified */
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
remotes[remotes_nr++] = ret;
- if (len)
- ret->name = xstrndup(name, len);
- else
- ret->name = xstrdup(name);
+ ret->name = xstrndup(name, len);
+
+ hashmap_entry_init(ret, lookup_entry.hash);
+ replaced = hashmap_put(&remotes_hash, ret);
+ assert(replaced == NULL); /* no previous entry overwritten */
return ret;
}
rewrite->instead_of_nr++;
}
+static const char *skip_spaces(const char *s)
+{
+ while (isspace(*s))
+ s++;
+ return s;
+}
+
static void read_remotes_file(struct remote *remote)
{
- FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
+ struct strbuf buf = STRBUF_INIT;
+ FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r");
if (!f)
return;
+ remote->configured_in_repo = 1;
remote->origin = REMOTE_REMOTES;
- while (fgets(buffer, BUF_SIZE, f)) {
- int value_list;
- char *s, *p;
-
- if (starts_with(buffer, "URL:")) {
- value_list = 0;
- s = buffer + 4;
- } else if (starts_with(buffer, "Push:")) {
- value_list = 1;
- s = buffer + 5;
- } else if (starts_with(buffer, "Pull:")) {
- value_list = 2;
- s = buffer + 5;
- } else
- continue;
-
- while (isspace(*s))
- s++;
- if (!*s)
- continue;
+ while (strbuf_getline(&buf, f) != EOF) {
+ const char *v;
- p = s + strlen(s);
- while (isspace(p[-1]))
- *--p = 0;
+ strbuf_rtrim(&buf);
- switch (value_list) {
- case 0:
- add_url_alias(remote, xstrdup(s));
- break;
- case 1:
- add_push_refspec(remote, xstrdup(s));
- break;
- case 2:
- add_fetch_refspec(remote, xstrdup(s));
- break;
- }
+ if (skip_prefix(buf.buf, "URL:", &v))
+ add_url_alias(remote, xstrdup(skip_spaces(v)));
+ else if (skip_prefix(buf.buf, "Push:", &v))
+ add_push_refspec(remote, xstrdup(skip_spaces(v)));
+ else if (skip_prefix(buf.buf, "Pull:", &v))
+ add_fetch_refspec(remote, xstrdup(skip_spaces(v)));
}
+ strbuf_release(&buf);
fclose(f);
}
static void read_branches_file(struct remote *remote)
{
char *frag;
- struct strbuf branch = STRBUF_INIT;
- int n = 1000;
- FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
- char *s, *p;
- int len;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
if (!f)
return;
- s = fgets(buffer, BUF_SIZE, f);
+
+ strbuf_getline_lf(&buf, f);
fclose(f);
- if (!s)
- return;
- while (isspace(*s))
- s++;
- if (!*s)
+ strbuf_trim(&buf);
+ if (!buf.len) {
+ strbuf_release(&buf);
return;
+ }
+
+ remote->configured_in_repo = 1;
remote->origin = REMOTE_BRANCHES;
- p = s + strlen(s);
- while (isspace(p[-1]))
- *--p = 0;
- len = p - s;
- p = xmalloc(len + 1);
- strcpy(p, s);
/*
* The branches file would have URL and optionally
* #branch specified. The "master" (or specified) branch is
- * fetched and stored in the local branch of the same name.
+ * fetched and stored in the local branch matching the
+ * remote name.
*/
- frag = strchr(p, '#');
- if (frag) {
+ frag = strchr(buf.buf, '#');
+ if (frag)
*(frag++) = '\0';
- strbuf_addf(&branch, "refs/heads/%s", frag);
- } else
- strbuf_addstr(&branch, "refs/heads/master");
+ else
+ frag = "master";
+
+ add_url_alias(remote, strbuf_detach(&buf, NULL));
+ add_fetch_refspec(remote, xstrfmt("refs/heads/%s:refs/heads/%s",
+ frag, remote->name));
- strbuf_addf(&branch, ":refs/heads/%s", remote->name);
- add_url_alias(remote, p);
- add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
/*
* Cogito compatible push: push current HEAD to remote #branch
* (master if missing)
*/
- strbuf_init(&branch, 0);
- strbuf_addstr(&branch, "HEAD");
- if (frag)
- strbuf_addf(&branch, ":refs/heads/%s", frag);
- else
- strbuf_addstr(&branch, ":refs/heads/master");
- add_push_refspec(remote, strbuf_detach(&branch, NULL));
+ add_push_refspec(remote, xstrfmt("HEAD:refs/heads/%s", frag));
remote->fetch_tags = 1; /* always auto-follow */
}
static int handle_config(const char *key, const char *value, void *cb)
{
const char *name;
+ int namelen;
const char *subkey;
struct remote *remote;
struct branch *branch;
- if (starts_with(key, "branch.")) {
- name = key + 7;
- subkey = strrchr(name, '.');
- if (!subkey)
+ if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
+ if (!name)
return 0;
- branch = make_branch(name, subkey - name);
- if (!strcmp(subkey, ".remote")) {
- if (git_config_string(&branch->remote_name, key, value))
- return -1;
- if (branch == current_branch) {
- default_remote_name = branch->remote_name;
- explicit_default_remote_name = 1;
- }
- } else if (!strcmp(subkey, ".pushremote")) {
- if (branch == current_branch)
- if (git_config_string(&branch_pushremote_name, key, value))
- return -1;
- } else if (!strcmp(subkey, ".merge")) {
+ branch = make_branch(name, namelen);
+ if (!strcmp(subkey, "remote")) {
+ return git_config_string(&branch->remote_name, key, value);
+ } else if (!strcmp(subkey, "pushremote")) {
+ return git_config_string(&branch->pushremote_name, key, value);
+ } else if (!strcmp(subkey, "merge")) {
if (!value)
return config_error_nonbool(key);
add_merge(branch, xstrdup(value));
}
return 0;
}
- if (starts_with(key, "url.")) {
+ if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) {
struct rewrite *rewrite;
- name = key + 4;
- subkey = strrchr(name, '.');
- if (!subkey)
+ if (!name)
return 0;
- if (!strcmp(subkey, ".insteadof")) {
- rewrite = make_rewrite(&rewrites, name, subkey - name);
+ if (!strcmp(subkey, "insteadof")) {
+ rewrite = make_rewrite(&rewrites, name, namelen);
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
- } else if (!strcmp(subkey, ".pushinsteadof")) {
- rewrite = make_rewrite(&rewrites_push, name, subkey - name);
+ } else if (!strcmp(subkey, "pushinsteadof")) {
+ rewrite = make_rewrite(&rewrites_push, name, namelen);
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
}
}
- if (!starts_with(key, "remote."))
+ if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0)
return 0;
- name = key + 7;
/* Handle remote.* variables */
- if (!strcmp(name, "pushdefault"))
+ if (!name && !strcmp(subkey, "pushdefault"))
return git_config_string(&pushremote_name, key, value);
+ if (!name)
+ return 0;
/* Handle remote.<name>.* variables */
if (*name == '/') {
warning("Config remote shorthand cannot begin with '/': %s",
name);
return 0;
}
- subkey = strrchr(name, '.');
- if (!subkey)
- return 0;
- remote = make_remote(name, subkey - name);
+ remote = make_remote(name, namelen);
remote->origin = REMOTE_CONFIG;
- if (!strcmp(subkey, ".mirror"))
+ if (current_config_scope() == CONFIG_SCOPE_REPO)
+ remote->configured_in_repo = 1;
+ if (!strcmp(subkey, "mirror"))
remote->mirror = git_config_bool(key, value);
- else if (!strcmp(subkey, ".skipdefaultupdate"))
+ else if (!strcmp(subkey, "skipdefaultupdate"))
remote->skip_default_update = git_config_bool(key, value);
- else if (!strcmp(subkey, ".skipfetchall"))
+ else if (!strcmp(subkey, "skipfetchall"))
remote->skip_default_update = git_config_bool(key, value);
- else if (!strcmp(subkey, ".prune"))
+ else if (!strcmp(subkey, "prune"))
remote->prune = git_config_bool(key, value);
- else if (!strcmp(subkey, ".url")) {
+ else if (!strcmp(subkey, "prunetags"))
+ remote->prune_tags = git_config_bool(key, value);
+ else if (!strcmp(subkey, "url")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_url(remote, v);
- } else if (!strcmp(subkey, ".pushurl")) {
+ } else if (!strcmp(subkey, "pushurl")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_pushurl(remote, v);
- } else if (!strcmp(subkey, ".push")) {
+ } else if (!strcmp(subkey, "push")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_push_refspec(remote, v);
- } else if (!strcmp(subkey, ".fetch")) {
+ } else if (!strcmp(subkey, "fetch")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
add_fetch_refspec(remote, v);
- } else if (!strcmp(subkey, ".receivepack")) {
+ } else if (!strcmp(subkey, "receivepack")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
remote->receivepack = v;
else
error("more than one receivepack given, using the first");
- } else if (!strcmp(subkey, ".uploadpack")) {
+ } else if (!strcmp(subkey, "uploadpack")) {
const char *v;
if (git_config_string(&v, key, value))
return -1;
remote->uploadpack = v;
else
error("more than one uploadpack given, using the first");
- } else if (!strcmp(subkey, ".tagopt")) {
+ } else if (!strcmp(subkey, "tagopt")) {
if (!strcmp(value, "--no-tags"))
remote->fetch_tags = -1;
else if (!strcmp(value, "--tags"))
remote->fetch_tags = 2;
- } else if (!strcmp(subkey, ".proxy")) {
+ } else if (!strcmp(subkey, "proxy")) {
return git_config_string((const char **)&remote->http_proxy,
key, value);
- } else if (!strcmp(subkey, ".vcs")) {
+ } else if (!strcmp(subkey, "proxyauthmethod")) {
+ return git_config_string((const char **)&remote->http_proxy_authmethod,
+ key, value);
+ } else if (!strcmp(subkey, "vcs")) {
return git_config_string(&remote->foreign_vcs, key, value);
}
return 0;
static void read_config(void)
{
- unsigned char sha1[20];
- const char *head_ref;
+ static int loaded;
int flag;
- if (default_remote_name) /* did this already */
+
+ if (loaded)
return;
- default_remote_name = "origin";
+ loaded = 1;
+
current_branch = NULL;
- head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
- if (head_ref && (flag & REF_ISSYMREF) &&
- skip_prefix(head_ref, "refs/heads/", &head_ref)) {
- current_branch = make_branch(head_ref, 0);
+ if (startup_info->have_repository) {
+ const char *head_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flag);
+ if (head_ref && (flag & REF_ISSYMREF) &&
+ skip_prefix(head_ref, "refs/heads/", &head_ref)) {
+ current_branch = make_branch(head_ref, 0);
+ }
}
git_config(handle_config, NULL);
- if (branch_pushremote_name) {
- free((char *)pushremote_name);
- pushremote_name = branch_pushremote_name;
- }
alias_all_urls();
}
-/*
- * This function frees a refspec array.
- * Warning: code paths should be checked to ensure that the src
- * and dst pointers are always freeable pointers as well
- * as the refspec pointer itself.
- */
-static void free_refspecs(struct refspec *refspec, int nr_refspec)
-{
- int i;
-
- if (!refspec)
- return;
-
- for (i = 0; i < nr_refspec; i++) {
- free(refspec[i].src);
- free(refspec[i].dst);
- }
- free(refspec);
-}
-
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
if (fetch) {
- unsigned char unused[40];
+ struct object_id unused;
/* LHS */
if (!*rs[i].src)
; /* empty is ok; it means "HEAD" */
- else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
+ else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))
rs[i].exact_sha1 = 1; /* ok */
else if (!check_refname_format(rs[i].src, flags))
; /* valid looking ref is ok */
* since it is only possible to reach this point from within
* the for loop above.
*/
- free_refspecs(rs, i+1);
+ free_refspec(i+1, rs);
return NULL;
}
die("Invalid refspec '%s'", refspec[i]);
struct refspec *refspec;
refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
- free_refspecs(refspec, 1);
+ free_refspec(1, refspec);
return !!refspec;
}
return parse_refspec_internal(nr_refspec, refspec, 1, 0);
}
-static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
{
return parse_refspec_internal(nr_refspec, refspec, 0, 0);
}
void free_refspec(int nr_refspec, struct refspec *refspec)
{
int i;
+
+ if (!refspec)
+ return;
+
for (i = 0; i < nr_refspec; i++) {
free(refspec[i].src);
free(refspec[i].dst);
{
if (!name[0] || is_dot_or_dotdot(name))
return 0;
- return !strchr(name, '/'); /* no slash */
+
+ /* remote nicknames cannot contain slashes */
+ while (*name)
+ if (is_dir_sep(*name++))
+ return 0;
+ return 1;
+}
+
+const char *remote_for_branch(struct branch *branch, int *explicit)
+{
+ if (branch && branch->remote_name) {
+ if (explicit)
+ *explicit = 1;
+ return branch->remote_name;
+ }
+ if (explicit)
+ *explicit = 0;
+ return "origin";
+}
+
+const char *pushremote_for_branch(struct branch *branch, int *explicit)
+{
+ if (branch && branch->pushremote_name) {
+ if (explicit)
+ *explicit = 1;
+ return branch->pushremote_name;
+ }
+ if (pushremote_name) {
+ if (explicit)
+ *explicit = 1;
+ return pushremote_name;
+ }
+ return remote_for_branch(branch, explicit);
}
-static struct remote *remote_get_1(const char *name, const char *pushremote_name)
+const char *remote_ref_for_branch(struct branch *branch, int for_push,
+ int *explicit)
+{
+ if (branch) {
+ if (!for_push) {
+ if (branch->merge_nr) {
+ if (explicit)
+ *explicit = 1;
+ return branch->merge_name[0];
+ }
+ } else {
+ const char *dst, *remote_name =
+ pushremote_for_branch(branch, NULL);
+ struct remote *remote = remote_get(remote_name);
+
+ if (remote && remote->push_refspec_nr &&
+ (dst = apply_refspecs(remote->push,
+ remote->push_refspec_nr,
+ branch->refname))) {
+ if (explicit)
+ *explicit = 1;
+ return dst;
+ }
+ }
+ }
+ if (explicit)
+ *explicit = 0;
+ return "";
+}
+
+static struct remote *remote_get_1(const char *name,
+ const char *(*get_default)(struct branch *, int *))
{
struct remote *ret;
int name_given = 0;
+ read_config();
+
if (name)
name_given = 1;
- else {
- if (pushremote_name) {
- name = pushremote_name;
- name_given = 1;
- } else {
- name = default_remote_name;
- name_given = explicit_default_remote_name;
- }
- }
+ else
+ name = get_default(current_branch, &name_given);
ret = make_remote(name, 0);
- if (valid_remote_nick(name)) {
+ if (valid_remote_nick(name) && have_git_dir()) {
if (!valid_remote(ret))
read_remotes_file(ret);
if (!valid_remote(ret))
struct remote *remote_get(const char *name)
{
- read_config();
- return remote_get_1(name, NULL);
+ return remote_get_1(name, remote_for_branch);
}
struct remote *pushremote_get(const char *name)
{
- read_config();
- return remote_get_1(name, pushremote_name);
+ return remote_get_1(name, pushremote_for_branch);
}
-int remote_is_configured(const char *name)
+int remote_is_configured(struct remote *remote, int in_repo)
{
- int i;
- read_config();
-
- for (i = 0; i < remotes_nr; i++)
- if (!strcmp(name, remotes[i]->name))
- return 1;
- return 0;
+ if (!remote)
+ return 0;
+ if (in_repo)
+ return remote->configured_in_repo;
+ return !!remote->origin;
}
int for_each_remote(each_remote_fn fn, void *priv)
ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
if (ret && value) {
+ struct strbuf sb = STRBUF_INIT;
const char *vstar = strchr(value, '*');
- size_t vlen;
- size_t vsuffixlen;
if (!vstar)
die("Value '%s' of pattern has no '*'", value);
- vlen = vstar - value;
- vsuffixlen = strlen(vstar + 1);
- *result = xmalloc(vlen + vsuffixlen +
- strlen(name) -
- klen - ksuffixlen + 1);
- strncpy(*result, value, vlen);
- strncpy(*result + vlen,
- name + klen, namelen - klen - ksuffixlen);
- strcpy(*result + vlen + namelen - klen - ksuffixlen,
- vstar + 1);
+ strbuf_add(&sb, value, vstar - value);
+ strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
+ strbuf_addstr(&sb, vstar + 1);
+ *result = strbuf_detach(&sb, NULL);
}
return ret;
}
const char *name)
{
size_t len = strlen(name);
- struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
+ struct ref *ref = xcalloc(1, st_add4(sizeof(*ref), prefixlen, len, 1));
memcpy(ref->name, prefix, prefixlen);
memcpy(ref->name + prefixlen, name, len);
return ref;
size_t len;
if (!ref)
return NULL;
- len = strlen(ref->name);
- cpy = xmalloc(sizeof(struct ref) + len + 1);
- memcpy(cpy, ref, sizeof(struct ref) + len + 1);
+ len = st_add3(sizeof(struct ref), strlen(ref->name), 1);
+ cpy = xmalloc(len);
+ memcpy(cpy, ref, len);
cpy->next = NULL;
- cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
- cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
+ cpy->symref = xstrdup_or_null(ref->symref);
+ cpy->remote_status = xstrdup_or_null(ref->remote_status);
cpy->peer_ref = copy_ref(ref->peer_ref);
return cpy;
}
static struct ref *alloc_delete_ref(void)
{
struct ref *ref = alloc_ref("(delete)");
- hashclr(ref->new_sha1);
+ oidclr(&ref->new_oid);
return ref;
}
static int try_explicit_object_name(const char *name,
struct ref **match)
{
- unsigned char sha1[20];
+ struct object_id oid;
if (!*name) {
if (match)
return 0;
}
- if (get_sha1(name, sha1))
+ if (get_oid(name, &oid))
return -1;
if (match) {
*match = alloc_ref(name);
- hashcpy((*match)->new_sha1, sha1);
+ oidcpy(&(*match)->new_oid, &oid);
}
return 0;
}
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,
+ NULL, NULL);
if (!r)
return NULL;
return -1;
if (!dst_value) {
- 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,
+ NULL, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
case 0:
if (starts_with(dst_value, "refs/"))
matched_dst = make_linked_ref(dst_value, dst_tail);
- else if (is_null_sha1(matched_src->new_sha1))
+ else if (is_null_oid(&matched_src->new_oid))
error("unable to delete '%s': remote ref does not exist",
dst_value);
- else if ((dst_guess = guess_ref(dst_value, matched_src)))
+ else if ((dst_guess = guess_ref(dst_value, matched_src))) {
matched_dst = make_linked_ref(dst_guess, dst_tail);
- else
+ free(dst_guess);
+ } else
error("unable to push to unqualified destination: %s\n"
"The destination refspec neither matches an "
"existing ref on the remote nor\n"
int nr, alloc;
};
-static void add_to_tips(struct tips *tips, const unsigned char *sha1)
+static void add_to_tips(struct tips *tips, const struct object_id *oid)
{
struct commit *commit;
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
if (!commit || (commit->object.flags & TMP_MARK))
return;
commit->object.flags |= TMP_MARK;
memset(&sent_tips, 0, sizeof(sent_tips));
for (ref = *dst; ref; ref = ref->next) {
if (ref->peer_ref &&
- !is_null_sha1(ref->peer_ref->new_sha1))
- add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
+ !is_null_oid(&ref->peer_ref->new_oid))
+ add_to_tips(&sent_tips, &ref->peer_ref->new_oid);
else
- add_to_tips(&sent_tips, ref->old_sha1);
+ add_to_tips(&sent_tips, &ref->old_oid);
if (starts_with(ref->name, "refs/tags/"))
string_list_append(&dst_tag, ref->name);
}
clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
- sort_string_list(&dst_tag);
+ string_list_sort(&dst_tag);
/* Collect tags they do not have. */
for (ref = src; ref; ref = ref->next) {
continue; /* not a tag */
if (string_list_has_string(&dst_tag, ref->name))
continue; /* they already have it */
- if (sha1_object_info(ref->new_sha1, NULL) != OBJ_TAG)
+ if (sha1_object_info(ref->new_oid.hash, NULL) != OBJ_TAG)
continue; /* be conservative */
item = string_list_append(&src_tag, ref->name);
item->util = ref;
struct ref *dst_ref;
struct commit *commit;
- if (is_null_sha1(ref->new_sha1))
+ if (is_null_oid(&ref->new_oid))
continue;
- commit = lookup_commit_reference_gently(ref->new_sha1, 1);
+ commit = lookup_commit_reference_gently(&ref->new_oid,
+ 1);
if (!commit)
/* not pushing a commit, which is not an error */
continue;
/* Add it in */
dst_ref = make_linked_ref(ref->name, dst_tail);
- hashcpy(dst_ref->new_sha1, ref->new_sha1);
+ oidcpy(&dst_ref->new_oid, &ref->new_oid);
dst_ref->peer_ref = copy_ref(ref);
}
}
for ( ; ref; ref = ref->next)
string_list_append_nodup(ref_index, ref->name)->util = ref;
- sort_string_list(ref_index);
+ string_list_sort(ref_index);
}
/*
/* Create a new one and link it */
dst_peer = make_linked_ref(dst_name, &dst_tail);
- hashcpy(dst_peer->new_sha1, ref->new_sha1);
+ oidcpy(&dst_peer->new_oid, &ref->new_oid);
string_list_insert(&dst_ref_index,
dst_peer->name)->util = dst_peer;
}
int reject_reason = 0;
if (ref->peer_ref)
- hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+ oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
else if (!send_mirror)
continue;
- ref->deletion = is_null_sha1(ref->new_sha1);
+ ref->deletion = is_null_oid(&ref->new_oid);
if (!ref->deletion &&
- !hashcmp(ref->old_sha1, ref->new_sha1)) {
+ !oidcmp(&ref->old_oid, &ref->new_oid)) {
ref->status = REF_STATUS_UPTODATE;
continue;
}
/*
- * Bypass the usual "must fast-forward" check but
- * replace it with a weaker "the old value must be
- * this value we observed". If the remote ref has
- * moved and is now different from what we expect,
- * reject any push.
+ * If the remote ref has moved and is now different
+ * from what we expect, reject any push.
*
* It also is an error if the user told us to check
* with the remote-tracking branch to find the value
* branch.
*/
if (ref->expect_old_sha1) {
- if (ref->expect_old_no_trackback ||
- hashcmp(ref->old_sha1, ref->old_sha1_expect))
+ if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
reject_reason = REF_STATUS_REJECT_STALE;
+ else
+ /* If the ref isn't stale then force the update. */
+ force_ref_update = 1;
}
/*
- * The usual "must fast-forward" rules.
+ * If the update isn't already rejected then check
+ * the usual "must fast-forward" rules.
*
* Decide whether an individual refspec A:B can be
* pushed. The push will succeed if any of the
* passing the --force argument
*/
- else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
+ if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
if (starts_with(ref->name, "refs/tags/"))
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
- else if (!has_sha1_file(ref->old_sha1))
+ else if (!has_object_file(&ref->old_oid))
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
- else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
- !lookup_commit_reference_gently(ref->new_sha1, 1))
+ else if (!lookup_commit_reference_gently(&ref->old_oid, 1) ||
+ !lookup_commit_reference_gently(&ref->new_oid, 1))
reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
- else if (!ref_newer(ref->new_sha1, ref->old_sha1))
+ else if (!ref_newer(&ref->new_oid, &ref->old_oid))
reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
}
}
}
+static void set_merge(struct branch *ret)
+{
+ struct remote *remote;
+ char *ref;
+ struct object_id oid;
+ int i;
+
+ if (!ret)
+ return; /* no branch */
+ if (ret->merge)
+ return; /* already run */
+ if (!ret->remote_name || !ret->merge_nr) {
+ /*
+ * no merge config; let's make sure we don't confuse callers
+ * with a non-zero merge_nr but a NULL merge
+ */
+ ret->merge_nr = 0;
+ return;
+ }
+
+ remote = remote_get(ret->remote_name);
+
+ ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
+ for (i = 0; i < ret->merge_nr; i++) {
+ ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
+ ret->merge[i]->src = xstrdup(ret->merge_name[i]);
+ if (!remote_find_tracking(remote, ret->merge[i]) ||
+ strcmp(ret->remote_name, "."))
+ continue;
+ if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
+ &oid, &ref) == 1)
+ ret->merge[i]->dst = ref;
+ else
+ ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
+ }
+}
+
struct branch *branch_get(const char *name)
{
struct branch *ret;
ret = current_branch;
else
ret = make_branch(name, 0);
- if (ret && ret->remote_name) {
- ret->remote = remote_get(ret->remote_name);
- if (ret->merge_nr) {
- int i;
- ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
- for (i = 0; i < ret->merge_nr; i++) {
- ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
- ret->merge[i]->src = xstrdup(ret->merge_name[i]);
- if (remote_find_tracking(ret->remote, ret->merge[i])
- && !strcmp(ret->remote_name, "."))
- ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
- }
- }
- }
+ set_merge(ret);
return ret;
}
return refname_match(branch->merge[i]->src, refname);
}
+__attribute__((format (printf,2,3)))
+static const char *error_buf(struct strbuf *err, const char *fmt, ...)
+{
+ if (err) {
+ va_list ap;
+ va_start(ap, fmt);
+ strbuf_vaddf(err, fmt, ap);
+ va_end(ap);
+ }
+ return NULL;
+}
+
+const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
+{
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ if (!branch->merge || !branch->merge[0]) {
+ /*
+ * no merge config; is it because the user didn't define any,
+ * or because it is not a real branch, and get_branch
+ * auto-vivified it?
+ */
+ if (!ref_exists(branch->refname))
+ return error_buf(err, _("no such branch: '%s'"),
+ branch->name);
+ return error_buf(err,
+ _("no upstream configured for branch '%s'"),
+ branch->name);
+ }
+
+ if (!branch->merge[0]->dst)
+ return error_buf(err,
+ _("upstream branch '%s' not stored as a remote-tracking branch"),
+ branch->merge[0]->src);
+
+ return branch->merge[0]->dst;
+}
+
+static const char *tracking_for_push_dest(struct remote *remote,
+ const char *refname,
+ struct strbuf *err)
+{
+ char *ret;
+
+ ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
+ if (!ret)
+ return error_buf(err,
+ _("push destination '%s' on remote '%s' has no local tracking branch"),
+ refname, remote->name);
+ return ret;
+}
+
+static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
+{
+ struct remote *remote;
+
+ remote = remote_get(pushremote_for_branch(branch, NULL));
+ if (!remote)
+ return error_buf(err,
+ _("branch '%s' has no remote for pushing"),
+ branch->name);
+
+ if (remote->push_refspec_nr) {
+ char *dst;
+ const char *ret;
+
+ dst = apply_refspecs(remote->push, remote->push_refspec_nr,
+ branch->refname);
+ if (!dst)
+ return error_buf(err,
+ _("push refspecs for '%s' do not include '%s'"),
+ remote->name, branch->name);
+
+ ret = tracking_for_push_dest(remote, dst, err);
+ free(dst);
+ return ret;
+ }
+
+ if (remote->mirror)
+ return tracking_for_push_dest(remote, branch->refname, err);
+
+ switch (push_default) {
+ case PUSH_DEFAULT_NOTHING:
+ return error_buf(err, _("push has no destination (push.default is 'nothing')"));
+
+ case PUSH_DEFAULT_MATCHING:
+ case PUSH_DEFAULT_CURRENT:
+ return tracking_for_push_dest(remote, branch->refname, err);
+
+ case PUSH_DEFAULT_UPSTREAM:
+ return branch_get_upstream(branch, err);
+
+ case PUSH_DEFAULT_UNSPECIFIED:
+ case PUSH_DEFAULT_SIMPLE:
+ {
+ const char *up, *cur;
+
+ up = branch_get_upstream(branch, err);
+ if (!up)
+ return NULL;
+ cur = tracking_for_push_dest(remote, branch->refname, err);
+ if (!cur)
+ return NULL;
+ if (strcmp(cur, up))
+ return error_buf(err,
+ _("cannot resolve 'simple' push to a single destination"));
+ return cur;
+ }
+ }
+
+ die("BUG: unhandled push situation");
+}
+
+const char *branch_get_push(struct branch *branch, struct strbuf *err)
+{
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ if (!branch->push_tracking_ref)
+ branch->push_tracking_ref = branch_get_push_1(branch, err);
+ return branch->push_tracking_ref;
+}
+
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, NULL, &flag))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
}
if (refspec->exact_sha1) {
ref_map = alloc_ref(name);
- get_sha1_hex(name, ref_map->old_sha1);
+ get_oid_hex(name, &ref_map->old_oid);
} else {
ref_map = get_remote_ref(remote_refs, name);
}
return 0;
for (; list; list = list->next)
if (!strcmp(ref->symref, list->name)) {
- hashcpy(ref->old_sha1, list->old_sha1);
+ oidcpy(&ref->old_oid, &list->old_oid);
return 0;
}
return 1;
static void unmark_and_free(struct commit_list *list, unsigned int mark)
{
while (list) {
- struct commit_list *temp = list;
- temp->item->object.flags &= ~mark;
- list = temp->next;
- free(temp);
+ struct commit *commit = pop_commit(&list);
+ commit->object.flags &= ~mark;
}
}
-int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
+int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
{
struct object *o;
- struct commit *old, *new;
+ struct commit *old_commit, *new_commit;
struct commit_list *list, *used;
int found = 0;
/*
- * Both new and old must be commit-ish and new is descendant of
- * old. Otherwise we require --force.
+ * Both new_commit and old_commit must be commit-ish and new_commit is descendant of
+ * old_commit. Otherwise we require --force.
*/
- o = deref_tag(parse_object(old_sha1), NULL, 0);
+ o = deref_tag(parse_object(old_oid), NULL, 0);
if (!o || o->type != OBJ_COMMIT)
return 0;
- old = (struct commit *) o;
+ old_commit = (struct commit *) o;
- o = deref_tag(parse_object(new_sha1), NULL, 0);
+ o = deref_tag(parse_object(new_oid), NULL, 0);
if (!o || o->type != OBJ_COMMIT)
return 0;
- new = (struct commit *) o;
+ new_commit = (struct commit *) o;
- if (parse_commit(new) < 0)
+ if (parse_commit(new_commit) < 0)
return 0;
used = list = NULL;
- commit_list_insert(new, &list);
+ commit_list_insert(new_commit, &list);
while (list) {
- new = pop_most_recent_commit(&list, TMP_MARK);
- commit_list_insert(new, &used);
- if (new == old) {
+ new_commit = pop_most_recent_commit(&list, TMP_MARK);
+ commit_list_insert(new_commit, &used);
+ if (new_commit == old_commit) {
found = 1;
break;
}
}
/*
- * Compare a branch with its upstream, and save their differences (number
- * of commits) in *num_ours and *num_theirs.
+ * Lookup the upstream branch for the given branch and if present, optionally
+ * compute the commit ahead/behind values for the pair.
+ *
+ * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the
+ * counts in *num_ours and *num_theirs. If abf is AHEAD_BEHIND_QUICK, skip
+ * the (potentially expensive) a/b computation (*num_ours and *num_theirs are
+ * set to zero).
+ *
+ * The name of the upstream branch (or NULL if no upstream is defined) is
+ * returned via *upstream_name, if it is not itself NULL.
*
- * Return 0 if branch has no upstream (no base), -1 if upstream is missing
- * (with "gone" base), otherwise 1 (with base).
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
+ * upstream defined, or ref does not exist). Returns 0 if the commits are
+ * identical. Returns 1 if commits are different.
*/
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+ const char **upstream_name, enum ahead_behind_flags abf)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *ours, *theirs;
- char symmetric[84];
struct rev_info revs;
- const char *rev_argv[10], *base;
- int rev_argc;
+ const char *base;
+ struct argv_array argv = ARGV_ARRAY_INIT;
/* Cannot stat unless we are marked to build on top of somebody else. */
- if (!branch ||
- !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
- return 0;
+ base = branch_get_upstream(branch, NULL);
+ if (upstream_name)
+ *upstream_name = base;
+ if (!base)
+ return -1;
/* Cannot stat if what we used to build on no longer exists */
- base = branch->merge[0]->dst;
- if (read_ref(base, sha1))
+ if (read_ref(base, &oid))
return -1;
- theirs = lookup_commit_reference(sha1);
+ theirs = lookup_commit_reference(&oid);
if (!theirs)
return -1;
- if (read_ref(branch->refname, sha1))
+ if (read_ref(branch->refname, &oid))
return -1;
- ours = lookup_commit_reference(sha1);
+ ours = lookup_commit_reference(&oid);
if (!ours)
return -1;
+ *num_theirs = *num_ours = 0;
+
/* are we the same? */
- if (theirs == ours) {
- *num_theirs = *num_ours = 0;
+ if (theirs == ours)
+ return 0;
+ if (abf == AHEAD_BEHIND_QUICK)
return 1;
- }
+ if (abf != AHEAD_BEHIND_FULL)
+ BUG("stat_tracking_info: invalid abf '%d'", abf);
/* Run "rev-list --left-right ours...theirs" internally... */
- rev_argc = 0;
- rev_argv[rev_argc++] = NULL;
- rev_argv[rev_argc++] = "--left-right";
- rev_argv[rev_argc++] = symmetric;
- rev_argv[rev_argc++] = "--";
- rev_argv[rev_argc] = NULL;
-
- strcpy(symmetric, sha1_to_hex(ours->object.sha1));
- strcpy(symmetric + 40, "...");
- strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
+ argv_array_push(&argv, ""); /* ignored */
+ argv_array_push(&argv, "--left-right");
+ argv_array_pushf(&argv, "%s...%s",
+ oid_to_hex(&ours->object.oid),
+ oid_to_hex(&theirs->object.oid));
+ argv_array_push(&argv, "--");
init_revisions(&revs, NULL);
- setup_revisions(rev_argc, rev_argv, &revs, NULL);
- prepare_revision_walk(&revs);
+ setup_revisions(argv.argc, argv.argv, &revs, NULL);
+ if (prepare_revision_walk(&revs))
+ die("revision walk setup failed");
/* ... and count the commits on each side. */
- *num_ours = 0;
- *num_theirs = 0;
while (1) {
struct commit *c = get_revision(&revs);
if (!c)
/* clear object flags smudged by the above traversal */
clear_commit_marks(ours, ALL_REV_FLAGS);
clear_commit_marks(theirs, ALL_REV_FLAGS);
+
+ argv_array_clear(&argv);
return 1;
}
/*
* Return true when there is anything to report, otherwise false.
*/
-int format_tracking_info(struct branch *branch, struct strbuf *sb)
+int format_tracking_info(struct branch *branch, struct strbuf *sb,
+ enum ahead_behind_flags abf)
{
- int ours, theirs;
- const char *base;
+ int ours, theirs, sti;
+ const char *full_base;
+ char *base;
int upstream_is_gone = 0;
- switch (stat_tracking_info(branch, &ours, &theirs)) {
- case 0:
- /* no base */
- return 0;
- case -1:
- /* with "gone" base */
+ sti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf);
+ if (sti < 0) {
+ if (!full_base)
+ return 0;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
- base = branch->merge[0]->dst;
- base = shorten_unambiguous_ref(base, 0);
+ base = shorten_unambiguous_ref(full_base, 0);
if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
base);
if (advice_status_hints)
- strbuf_addf(sb,
+ strbuf_addstr(sb,
_(" (use \"git branch --unset-upstream\" to fixup)\n"));
- } else if (!ours && !theirs) {
+ } else if (!sti) {
strbuf_addf(sb,
- _("Your branch is up-to-date with '%s'.\n"),
+ _("Your branch is up to date with '%s'.\n"),
base);
+ } else if (abf == AHEAD_BEHIND_QUICK) {
+ strbuf_addf(sb,
+ _("Your branch and '%s' refer to different commits.\n"),
+ base);
+ if (advice_status_hints)
+ strbuf_addf(sb, _(" (use \"%s\" for details)\n"),
+ "git status --ahead-behind");
} else if (!theirs) {
strbuf_addf(sb,
Q_("Your branch is ahead of '%s' by %d commit.\n",
ours),
base, ours);
if (advice_status_hints)
- strbuf_addf(sb,
+ strbuf_addstr(sb,
_(" (use \"git push\" to publish your local commits)\n"));
} else if (!ours) {
strbuf_addf(sb,
theirs),
base, theirs);
if (advice_status_hints)
- strbuf_addf(sb,
+ strbuf_addstr(sb,
_(" (use \"git pull\" to update your local branch)\n"));
} else {
strbuf_addf(sb,
"Your branch and '%s' have diverged,\n"
"and have %d and %d different commits each, "
"respectively.\n",
- theirs),
+ ours + theirs),
base, ours, theirs);
if (advice_status_hints)
- strbuf_addf(sb,
+ strbuf_addstr(sb,
_(" (use \"git pull\" to merge the remote branch into yours)\n"));
}
+ free(base);
return 1;
}
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int one_local_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct ref ***local_tail = cb_data;
struct ref *ref;
- int len;
/* we already know it starts with refs/ to get here */
if (check_refname_format(refname + 5, 0))
return 0;
- len = strlen(refname) + 1;
- ref = xcalloc(1, sizeof(*ref) + len);
- hashcpy(ref->new_sha1, sha1);
- memcpy(ref->name, refname, len);
+ ref = alloc_ref(refname);
+ oidcpy(&ref->new_oid, oid);
**local_tail = ref;
*local_tail = &ref->next;
return 0;
struct ref *get_local_heads(void)
{
struct ref *local_refs = NULL, **local_tail = &local_refs;
+
for_each_ref(one_local_ref, &local_tail);
return local_refs;
}
/* If refs/heads/master could be right, it is. */
if (!all) {
r = find_ref_by_name(refs, "refs/heads/master");
- if (r && !hashcmp(r->old_sha1, head->old_sha1))
+ if (r && !oidcmp(&r->old_oid, &head->old_oid))
return copy_ref(r);
}
for (r = refs; r; r = r->next) {
if (r != head &&
starts_with(r->name, "refs/heads/") &&
- !hashcmp(r->old_sha1, head->old_sha1)) {
+ !oidcmp(&r->old_oid, &head->old_oid)) {
*tail = copy_ref(r);
tail = &((*tail)->next);
if (!all)
int ref_count;
};
-static int get_stale_heads_cb(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
if (stale) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- hashcpy(ref->new_sha1, sha1);
+ oidcpy(&ref->new_oid, oid);
}
clean_exit:
struct ref *ref, *stale_refs = NULL;
struct string_list ref_names = STRING_LIST_INIT_NODUP;
struct stale_heads_info info;
+
info.ref_names = &ref_names;
info.stale_refs_tail = &stale_refs;
info.refs = refs;
info.ref_count = ref_count;
for (ref = fetch_map; ref; ref = ref->next)
string_list_append(&ref_names, ref->name);
- sort_string_list(&ref_names);
+ string_list_sort(&ref_names);
for_each_ref(get_stale_heads_cb, &info);
string_list_clear(&ref_names, 0);
return stale_refs;
/*
* Compare-and-swap
*/
-void clear_cas_option(struct push_cas_option *cas)
+static void clear_cas_option(struct push_cas_option *cas)
{
int i;
return entry;
}
-int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
+static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
{
const char *colon;
struct push_cas *entry;
entry = add_cas_entry(cas, arg, colon - arg);
if (!*colon)
entry->use_tracking = 1;
- else if (get_sha1(colon + 1, entry->expect))
+ else if (!colon[1])
+ oidclr(&entry->expect);
+ else if (get_oid(colon + 1, &entry->expect))
return error("cannot parse expected object name '%s'", colon + 1);
return 0;
}
* If we cannot do so, return negative to signal an error.
*/
static int remote_tracking(struct remote *remote, const char *refname,
- unsigned char sha1[20])
+ struct object_id *oid)
{
char *dst;
dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
if (!dst)
return -1; /* no tracking ref for refname at remote */
- if (read_ref(dst, sha1))
+ if (read_ref(dst, oid))
return -1; /* we know what the tracking ref is but we cannot read it */
return 0;
}
continue;
ref->expect_old_sha1 = 1;
if (!entry->use_tracking)
- hashcpy(ref->old_sha1_expect, cas->entry[i].expect);
- else if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
- ref->expect_old_no_trackback = 1;
+ oidcpy(&ref->old_oid_expect, &entry->expect);
+ else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
+ oidclr(&ref->old_oid_expect);
return;
}
return;
ref->expect_old_sha1 = 1;
- if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
- ref->expect_old_no_trackback = 1;
+ if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
+ oidclr(&ref->old_oid_expect);
}
void apply_push_cas(struct push_cas_option *cas,