summaryrefslogtreecommitdiff
path: root/builtin-remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin-remote.c')
-rw-r--r--builtin-remote.c224
1 files changed, 137 insertions, 87 deletions
diff --git a/builtin-remote.c b/builtin-remote.c
index 67761d5ab3..277765b864 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -16,6 +16,9 @@ static const char * const builtin_remote_usage[] = {
"git remote [-v | --verbose] show [-n] <name>",
"git remote prune [-n | --dry-run] <name>",
"git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+ "git remote set-url <name> <newurl> [<oldurl>]",
+ "git remote set-url --add <name> <newurl>",
+ "git remote set-url --delete <name> <url>",
NULL
};
@@ -54,6 +57,13 @@ static const char * const builtin_remote_update_usage[] = {
NULL
};
+static const char * const builtin_remote_seturl_usage[] = {
+ "git remote set-url [--push] <name> <newurl> [<oldurl>]",
+ "git remote set-url --add <name> <newurl>",
+ "git remote set-url --delete <name> <url>",
+ NULL
+};
+
#define GET_REF_STATES (1<<0)
#define GET_HEAD_NAMES (1<<1)
#define GET_PUSH_REF_STATES (1<<2)
@@ -261,32 +271,10 @@ struct ref_states {
int queried;
};
-static int handle_one_branch(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
-{
- struct ref_states *states = cb_data;
- struct refspec refspec;
-
- memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
- if (!remote_find_tracking(states->remote, &refspec)) {
- struct string_list_item *item;
- const char *name = abbrev_branch(refspec.src);
- /* symbolic refs pointing nowhere were handled already */
- if ((flags & REF_ISSYMREF) ||
- string_list_has_string(&states->tracked, name) ||
- string_list_has_string(&states->new, name))
- return 0;
- item = string_list_append(name, &states->stale);
- item->util = xstrdup(refname);
- }
- return 0;
-}
-
static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
{
struct ref *fetch_map = NULL, **tail = &fetch_map;
- struct ref *ref;
+ struct ref *ref, *stale_refs;
int i;
for (i = 0; i < states->remote->fetch_refspec_nr; i++)
@@ -294,7 +282,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
die("Could not get fetch map for refspec %s",
states->remote->fetch_refspec[i]);
- states->new.strdup_strings = states->tracked.strdup_strings = 1;
+ states->new.strdup_strings = 1;
+ states->tracked.strdup_strings = 1;
+ states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
unsigned char sha1[20];
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
@@ -302,11 +292,17 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
else
string_list_append(abbrev_branch(ref->name), &states->tracked);
}
+ stale_refs = get_stale_heads(states->remote, fetch_map);
+ for (ref = stale_refs; ref; ref = ref->next) {
+ struct string_list_item *item =
+ string_list_append(abbrev_branch(ref->name), &states->stale);
+ item->util = xstrdup(ref->name);
+ }
+ free_refs(stale_refs);
free_refs(fetch_map);
sort_string_list(&states->new);
sort_string_list(&states->tracked);
- for_each_ref(handle_one_branch, states);
sort_string_list(&states->stale);
return 0;
@@ -784,7 +780,7 @@ static void clear_push_info(void *util, const char *string)
static void free_remote_ref_states(struct ref_states *states)
{
string_list_clear(&states->new, 0);
- string_list_clear(&states->stale, 0);
+ string_list_clear(&states->stale, 1);
string_list_clear(&states->tracked, 0);
string_list_clear(&states->heads, 0);
string_list_clear_func(&states->push, clear_push_info);
@@ -987,7 +983,7 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)
status = "up to date";
break;
case PUSH_STATUS_FASTFORWARD:
- status = "fast forwardable";
+ status = "fast-forwardable";
break;
case PUSH_STATUS_OUTOFDATE:
status = "local out of date";
@@ -1213,94 +1209,146 @@ static int prune_remote(const char *remote, int dry_run)
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
abbrev_ref(refname, "refs/remotes/"));
- warn_dangling_symref(dangling_msg, refname);
+ warn_dangling_symref(stdout, dangling_msg, refname);
}
free_remote_ref_states(&states);
return result;
}
-static int get_one_remote_for_update(struct remote *remote, void *priv)
+static int get_remote_default(const char *key, const char *value, void *priv)
{
- struct string_list *list = priv;
- if (!remote->skip_default_update)
- string_list_append(remote->name, list);
- return 0;
-}
-
-static struct remote_group {
- const char *name;
- struct string_list *list;
-} remote_group;
-
-static int get_remote_group(const char *key, const char *value, void *num_hits)
-{
- if (!prefixcmp(key, "remotes.") &&
- !strcmp(key + 8, remote_group.name)) {
- /* split list by white space */
- int space = strcspn(value, " \t\n");
- while (*value) {
- if (space > 1) {
- string_list_append(xstrndup(value, space),
- remote_group.list);
- ++*((int *)num_hits);
- }
- value += space + (value[space] != '\0');
- space = strcspn(value, " \t\n");
- }
+ if (strcmp(key, "remotes.default") == 0) {
+ int *found = priv;
+ *found = 1;
}
-
return 0;
}
static int update(int argc, const char **argv)
{
- int i, result = 0, prune = 0;
- struct string_list list = { NULL, 0, 0, 0 };
- static const char *default_argv[] = { NULL, "default", NULL };
+ int i, prune = 0;
struct option options[] = {
OPT_BOOLEAN('p', "prune", &prune,
"prune remotes after fetching"),
OPT_END()
};
+ const char **fetch_argv;
+ int fetch_argc = 0;
+ int default_defined = 0;
+
+ fetch_argv = xmalloc(sizeof(char *) * (argc+5));
argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
PARSE_OPT_KEEP_ARGV0);
- if (argc < 2) {
- argc = 2;
- argv = default_argv;
- }
- remote_group.list = &list;
- for (i = 1; i < argc; i++) {
- int groups_found = 0;
- remote_group.name = argv[i];
- result = git_config(get_remote_group, &groups_found);
- if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
- struct remote *remote;
- if (!remote_is_configured(argv[i]))
- die("No such remote or remote group: %s",
- argv[i]);
- remote = remote_get(argv[i]);
- string_list_append(remote->name, remote_group.list);
- }
+ fetch_argv[fetch_argc++] = "fetch";
+
+ if (prune)
+ fetch_argv[fetch_argc++] = "--prune";
+ if (verbose)
+ fetch_argv[fetch_argc++] = "-v";
+ fetch_argv[fetch_argc++] = "--multiple";
+ if (argc < 2)
+ fetch_argv[fetch_argc++] = "default";
+ for (i = 1; i < argc; i++)
+ fetch_argv[fetch_argc++] = argv[i];
+
+ if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
+ git_config(get_remote_default, &default_defined);
+ if (!default_defined)
+ fetch_argv[fetch_argc-1] = "--all";
}
- if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default"))
- result = for_each_remote(get_one_remote_for_update, &list);
+ fetch_argv[fetch_argc] = NULL;
+
+ return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
+}
+
+static int set_url(int argc, const char **argv)
+{
+ int i, push_mode = 0, add_mode = 0, delete_mode = 0;
+ int matches = 0, negative_matches = 0;
+ const char *remotename = NULL;
+ const char *newurl = NULL;
+ const char *oldurl = NULL;
+ struct remote *remote;
+ regex_t old_regex;
+ const char **urlset;
+ int urlset_nr;
+ struct strbuf name_buf = STRBUF_INIT;
+ struct option options[] = {
+ OPT_BOOLEAN('\0', "push", &push_mode,
+ "manipulate push URLs"),
+ OPT_BOOLEAN('\0', "add", &add_mode,
+ "add URL"),
+ OPT_BOOLEAN('\0', "delete", &delete_mode,
+ "delete URLs"),
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+ PARSE_OPT_KEEP_ARGV0);
+
+ if (add_mode && delete_mode)
+ die("--add --delete doesn't make sense");
+
+ if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3))
+ usage_with_options(builtin_remote_seturl_usage, options);
+
+ remotename = argv[1];
+ newurl = argv[2];
+ if (argc > 3)
+ oldurl = argv[3];
- for (i = 0; i < list.nr; i++) {
- int err = fetch_remote(list.items[i].string);
- result |= err;
- if (!err && prune)
- result |= prune_remote(list.items[i].string, 0);
+ if (delete_mode)
+ oldurl = newurl;
+
+ if (!remote_is_configured(remotename))
+ die("No such remote '%s'", remotename);
+ remote = remote_get(remotename);
+
+ if (push_mode) {
+ strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
+ urlset = remote->pushurl;
+ urlset_nr = remote->pushurl_nr;
+ } else {
+ strbuf_addf(&name_buf, "remote.%s.url", remotename);
+ urlset = remote->url;
+ urlset_nr = remote->url_nr;
}
- /* all names were strdup()ed or strndup()ed */
- list.strdup_strings = 1;
- string_list_clear(&list, 0);
+ /* Special cases that add new entry. */
+ if ((!oldurl && !delete_mode) || add_mode) {
+ if (add_mode)
+ git_config_set_multivar(name_buf.buf, newurl,
+ "^$", 0);
+ else
+ git_config_set(name_buf.buf, newurl);
+ strbuf_release(&name_buf);
+ return 0;
+ }
- return result;
+ /* Old URL specified. Demand that one matches. */
+ if (regcomp(&old_regex, oldurl, REG_EXTENDED))
+ die("Invalid old URL pattern: %s", oldurl);
+
+ for (i = 0; i < urlset_nr; i++)
+ if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
+ matches++;
+ else
+ negative_matches++;
+ if (!delete_mode && !matches)
+ die("No such URL found: %s", oldurl);
+ if (delete_mode && !negative_matches && !push_mode)
+ die("Will not delete all non-push URLs");
+
+ regfree(&old_regex);
+
+ if (!delete_mode)
+ git_config_set_multivar(name_buf.buf, newurl, oldurl, 0);
+ else
+ git_config_set_multivar(name_buf.buf, NULL, oldurl, 1);
+ return 0;
}
static int get_one_entry(struct remote *remote, void *priv)
@@ -1382,6 +1430,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
result = rm(argc, argv);
else if (!strcmp(argv[0], "set-head"))
result = set_head(argc, argv);
+ else if (!strcmp(argv[0], "set-url"))
+ result = set_url(argc, argv);
else if (!strcmp(argv[0], "show"))
result = show(argc, argv);
else if (!strcmp(argv[0], "prune"))