summaryrefslogtreecommitdiff
path: root/remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'remote.c')
-rw-r--r--remote.c422
1 files changed, 264 insertions, 158 deletions
diff --git a/remote.c b/remote.c
index 68901b0070..10f1ffce0b 100644
--- a/remote.c
+++ b/remote.c
@@ -8,6 +8,7 @@
#include "tag.h"
#include "string-list.h"
#include "mergesort.h"
+#include "argv-array.h"
enum map_direction { FROM_SRC, FROM_DST };
@@ -49,17 +50,11 @@ 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);
@@ -68,7 +63,6 @@ static int valid_remote(const struct remote *remote)
static const char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
- char *ret;
struct counted_string *longest;
int longest_i;
@@ -89,11 +83,7 @@ static const char *alias_url(const char *url, struct rewrites *r)
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)
@@ -251,106 +241,77 @@ static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
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)
{
+ struct strbuf buf = STRBUF_INIT;
FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
if (!f)
return;
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, '\n') != 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(git_path("branches/%s", remote->name), "r");
if (!f)
return;
- s = fgets(buffer, BUF_SIZE, f);
+
+ strbuf_getline(&buf, f, '\n');
fclose(f);
- if (!s)
- return;
- while (isspace(*s))
- s++;
- if (!*s)
+ strbuf_trim(&buf);
+ if (!buf.len) {
+ strbuf_release(&buf);
return;
+ }
+
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 */
}
@@ -367,16 +328,9 @@ static int handle_config(const char *key, const char *value, void *cb)
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;
- }
+ return git_config_string(&branch->remote_name, key, value);
} else if (!strcmp(subkey, ".pushremote")) {
- if (branch == current_branch)
- if (git_config_string(&branch_pushremote_name, key, value))
- return -1;
+ return git_config_string(&branch->pushremote_name, key, value);
} else if (!strcmp(subkey, ".merge")) {
if (!value)
return config_error_nonbool(key);
@@ -501,12 +455,15 @@ static void alias_all_urls(void)
static void read_config(void)
{
+ static int loaded;
unsigned char sha1[20];
const char *head_ref;
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", 0, sha1, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
@@ -514,10 +471,6 @@ static void read_config(void)
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();
}
@@ -696,22 +649,45 @@ static int valid_remote_nick(const char *name)
return !strchr(name, '/'); /* no slash */
}
-static struct remote *remote_get_1(const char *name, const char *pushremote_name)
+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 *(*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)) {
@@ -731,14 +707,12 @@ static struct remote *remote_get_1(const char *name, const char *pushremote_name
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)
@@ -1633,15 +1607,31 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
static void set_merge(struct branch *ret)
{
+ struct remote *remote;
char *ref;
unsigned char sha1[20];
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(ret->remote, ret->merge[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]),
@@ -1661,11 +1651,7 @@ struct branch *branch_get(const char *name)
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)
- set_merge(ret);
- }
+ set_merge(ret);
return ret;
}
@@ -1683,6 +1669,130 @@ int branch_merge_matches(struct branch *branch,
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;
+
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ 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->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];
@@ -1829,10 +1939,8 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
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;
}
}
@@ -1877,27 +1985,30 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
/*
* Compare a branch with its upstream, and save their differences (number
- * of commits) in *num_ours and *num_theirs.
+ * of commits) in *num_ours and *num_theirs. 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), 0 otherwise.
*/
-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)
{
unsigned char sha1[20];
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))
return -1;
theirs = lookup_commit_reference(sha1);
@@ -1913,23 +2024,19 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
/* are we the same? */
if (theirs == ours) {
*num_theirs = *num_ours = 0;
- return 1;
+ return 0;
}
/* 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",
+ sha1_to_hex(ours->object.sha1),
+ sha1_to_hex(theirs->object.sha1));
+ argv_array_push(&argv, "--");
init_revisions(&revs, NULL);
- setup_revisions(rev_argc, rev_argv, &revs, NULL);
+ setup_revisions(argv.argc, argv.argv, &revs, NULL);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
@@ -1949,7 +2056,9 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
/* clear object flags smudged by the above traversal */
clear_commit_marks(ours, ALL_REV_FLAGS);
clear_commit_marks(theirs, ALL_REV_FLAGS);
- return 1;
+
+ argv_array_clear(&argv);
+ return 0;
}
/*
@@ -1958,23 +2067,17 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
int format_tracking_info(struct branch *branch, struct strbuf *sb)
{
int ours, theirs;
+ 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 */
+ if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {
+ if (!full_base)
+ return 0;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
- base = shorten_unambiguous_ref(branch->merge[0]->dst, 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"),
@@ -2024,7 +2127,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
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;
@@ -2036,7 +2140,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla
len = strlen(refname) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
- hashcpy(ref->new_sha1, sha1);
+ hashcpy(ref->new_sha1, oid->hash);
memcpy(ref->name, refname, len);
**local_tail = ref;
*local_tail = &ref->next;
@@ -2046,6 +2150,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla
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;
}
@@ -2098,8 +2203,8 @@ struct stale_heads_info {
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;
@@ -2128,7 +2233,7 @@ static int get_stale_heads_cb(const char *refname,
if (stale) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- hashcpy(ref->new_sha1, sha1);
+ hashcpy(ref->new_sha1, oid->hash);
}
clean_exit:
@@ -2141,6 +2246,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
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;