Merge branch 'js/remote-improvements'
* js/remote-improvements: (23 commits) builtin-remote.c: no "commented out" code, please builtin-remote: new show output style for push refspecs builtin-remote: new show output style remote: make guess_remote_head() use exact HEAD lookup if it is available builtin-remote: add set-head subcommand builtin-remote: teach show to display remote HEAD builtin-remote: fix two inconsistencies in the output of "show <remote>" builtin-remote: make get_remote_ref_states() always populate states.tracked builtin-remote: rename variables and eliminate redundant function call builtin-remote: remove unused code in get_ref_states builtin-remote: refactor duplicated cleanup code string-list: new for_each_string_list() function remote: make match_refs() not short-circuit remote: make match_refs() copy src ref before assigning to peer_ref remote: let guess_remote_head() optionally return all matches remote: make copy_ref() perform a deep copy remote: simplify guess_remote_head() move locate_head() to remote.c move duplicated ref_newer() to remote.c move duplicated get_local_heads() to remote.c ... Conflicts: builtin-clone.c
This commit is contained in:
562
builtin-remote.c
562
builtin-remote.c
@ -12,12 +12,17 @@ static const char * const builtin_remote_usage[] = {
|
||||
"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
|
||||
"git remote rename <old> <new>",
|
||||
"git remote rm <name>",
|
||||
"git remote set-head <name> [-a | -d | <branch>]",
|
||||
"git remote show [-n] <name>",
|
||||
"git remote prune [-n | --dry-run] <name>",
|
||||
"git remote [-v | --verbose] update [group]",
|
||||
NULL
|
||||
};
|
||||
|
||||
#define GET_REF_STATES (1<<0)
|
||||
#define GET_HEAD_NAMES (1<<1)
|
||||
#define GET_PUSH_REF_STATES (1<<2)
|
||||
|
||||
static int verbose;
|
||||
|
||||
static int show_all(void);
|
||||
@ -143,8 +148,9 @@ static int add(int argc, const char **argv)
|
||||
}
|
||||
|
||||
struct branch_info {
|
||||
char *remote;
|
||||
char *remote_name;
|
||||
struct string_list merge;
|
||||
int rebase;
|
||||
};
|
||||
|
||||
static struct string_list branch_list;
|
||||
@ -161,10 +167,11 @@ static const char *abbrev_ref(const char *name, const char *prefix)
|
||||
static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
{
|
||||
if (!prefixcmp(key, "branch.")) {
|
||||
const char *orig_key = key;
|
||||
char *name;
|
||||
struct string_list_item *item;
|
||||
struct branch_info *info;
|
||||
enum { REMOTE, MERGE } type;
|
||||
enum { REMOTE, MERGE, REBASE } type;
|
||||
|
||||
key += 7;
|
||||
if (!postfixcmp(key, ".remote")) {
|
||||
@ -173,6 +180,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
} else if (!postfixcmp(key, ".merge")) {
|
||||
name = xstrndup(key, strlen(key) - 6);
|
||||
type = MERGE;
|
||||
} else if (!postfixcmp(key, ".rebase")) {
|
||||
name = xstrndup(key, strlen(key) - 7);
|
||||
type = REBASE;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
@ -182,10 +192,10 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
item->util = xcalloc(sizeof(struct branch_info), 1);
|
||||
info = item->util;
|
||||
if (type == REMOTE) {
|
||||
if (info->remote)
|
||||
warning("more than one branch.%s", key);
|
||||
info->remote = xstrdup(value);
|
||||
} else {
|
||||
if (info->remote_name)
|
||||
warning("more than one %s", orig_key);
|
||||
info->remote_name = xstrdup(value);
|
||||
} else if (type == MERGE) {
|
||||
char *space = strchr(value, ' ');
|
||||
value = abbrev_branch(value);
|
||||
while (space) {
|
||||
@ -196,7 +206,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
space = strchr(value, ' ');
|
||||
}
|
||||
string_list_append(xstrdup(value), &info->merge);
|
||||
}
|
||||
} else
|
||||
info->rebase = git_config_bool(orig_key, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -206,12 +217,12 @@ static void read_branches(void)
|
||||
if (branch_list.nr)
|
||||
return;
|
||||
git_config(config_read_branches, NULL);
|
||||
sort_string_list(&branch_list);
|
||||
}
|
||||
|
||||
struct ref_states {
|
||||
struct remote *remote;
|
||||
struct string_list new, stale, tracked;
|
||||
struct string_list new, stale, tracked, heads, push;
|
||||
int queried;
|
||||
};
|
||||
|
||||
static int handle_one_branch(const char *refname,
|
||||
@ -227,10 +238,8 @@ static int handle_one_branch(const char *refname,
|
||||
const char *name = abbrev_branch(refspec.src);
|
||||
/* symbolic refs pointing nowhere were handled already */
|
||||
if ((flags & REF_ISSYMREF) ||
|
||||
unsorted_string_list_has_string(&states->tracked,
|
||||
name) ||
|
||||
unsorted_string_list_has_string(&states->new,
|
||||
name))
|
||||
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);
|
||||
@ -238,39 +247,162 @@ static int handle_one_branch(const char *refname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
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;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < states->remote->fetch_refspec_nr; i++)
|
||||
if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
|
||||
if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
|
||||
die("Could not get fetch map for refspec %s",
|
||||
states->remote->fetch_refspec[i]);
|
||||
|
||||
states->new.strdup_strings = states->tracked.strdup_strings = 1;
|
||||
for (ref = fetch_map; ref; ref = ref->next) {
|
||||
struct string_list *target = &states->tracked;
|
||||
unsigned char sha1[20];
|
||||
void *util = NULL;
|
||||
|
||||
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
|
||||
target = &states->new;
|
||||
else {
|
||||
target = &states->tracked;
|
||||
if (hashcmp(sha1, ref->new_sha1))
|
||||
util = &states;
|
||||
}
|
||||
string_list_append(abbrev_branch(ref->name), target)->util = util;
|
||||
string_list_append(abbrev_branch(ref->name), &states->new);
|
||||
else
|
||||
string_list_append(abbrev_branch(ref->name), &states->tracked);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
struct push_info {
|
||||
char *dest;
|
||||
int forced;
|
||||
enum {
|
||||
PUSH_STATUS_CREATE = 0,
|
||||
PUSH_STATUS_DELETE,
|
||||
PUSH_STATUS_UPTODATE,
|
||||
PUSH_STATUS_FASTFORWARD,
|
||||
PUSH_STATUS_OUTOFDATE,
|
||||
PUSH_STATUS_NOTQUERIED,
|
||||
} status;
|
||||
};
|
||||
|
||||
static int get_push_ref_states(const struct ref *remote_refs,
|
||||
struct ref_states *states)
|
||||
{
|
||||
struct remote *remote = states->remote;
|
||||
struct ref *ref, *local_refs, *push_map, **push_tail;
|
||||
if (remote->mirror)
|
||||
return 0;
|
||||
|
||||
local_refs = get_local_heads();
|
||||
ref = push_map = copy_ref_list(remote_refs);
|
||||
while (ref->next)
|
||||
ref = ref->next;
|
||||
push_tail = &ref->next;
|
||||
|
||||
match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr,
|
||||
remote->push_refspec, MATCH_REFS_NONE);
|
||||
|
||||
states->push.strdup_strings = 1;
|
||||
for (ref = push_map; ref; ref = ref->next) {
|
||||
struct string_list_item *item;
|
||||
struct push_info *info;
|
||||
|
||||
if (!ref->peer_ref)
|
||||
continue;
|
||||
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
|
||||
|
||||
item = string_list_append(abbrev_branch(ref->peer_ref->name),
|
||||
&states->push);
|
||||
item->util = xcalloc(sizeof(struct push_info), 1);
|
||||
info = item->util;
|
||||
info->forced = ref->force;
|
||||
info->dest = xstrdup(abbrev_branch(ref->name));
|
||||
|
||||
if (is_null_sha1(ref->new_sha1)) {
|
||||
info->status = PUSH_STATUS_DELETE;
|
||||
} else if (!hashcmp(ref->old_sha1, ref->new_sha1))
|
||||
info->status = PUSH_STATUS_UPTODATE;
|
||||
else if (is_null_sha1(ref->old_sha1))
|
||||
info->status = PUSH_STATUS_CREATE;
|
||||
else if (has_sha1_file(ref->old_sha1) &&
|
||||
ref_newer(ref->new_sha1, ref->old_sha1))
|
||||
info->status = PUSH_STATUS_FASTFORWARD;
|
||||
else
|
||||
info->status = PUSH_STATUS_OUTOFDATE;
|
||||
}
|
||||
free_refs(local_refs);
|
||||
free_refs(push_map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_push_ref_states_noquery(struct ref_states *states)
|
||||
{
|
||||
int i;
|
||||
struct remote *remote = states->remote;
|
||||
struct string_list_item *item;
|
||||
struct push_info *info;
|
||||
|
||||
if (remote->mirror)
|
||||
return 0;
|
||||
|
||||
states->push.strdup_strings = 1;
|
||||
if (!remote->push_refspec_nr) {
|
||||
item = string_list_append("(matching)", &states->push);
|
||||
info = item->util = xcalloc(sizeof(struct push_info), 1);
|
||||
info->status = PUSH_STATUS_NOTQUERIED;
|
||||
info->dest = xstrdup(item->string);
|
||||
}
|
||||
for (i = 0; i < remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = remote->push + i;
|
||||
char buf[PATH_MAX];
|
||||
if (spec->matching)
|
||||
item = string_list_append("(matching)", &states->push);
|
||||
else if (spec->pattern) {
|
||||
snprintf(buf, (sizeof(buf)), "%s*", spec->src);
|
||||
item = string_list_append(buf, &states->push);
|
||||
snprintf(buf, (sizeof(buf)), "%s*", spec->dst);
|
||||
} else if (strlen(spec->src))
|
||||
item = string_list_append(spec->src, &states->push);
|
||||
else
|
||||
item = string_list_append("(delete)", &states->push);
|
||||
|
||||
info = item->util = xcalloc(sizeof(struct push_info), 1);
|
||||
info->forced = spec->force;
|
||||
info->status = PUSH_STATUS_NOTQUERIED;
|
||||
if (spec->pattern)
|
||||
info->dest = xstrdup(buf);
|
||||
else
|
||||
info->dest = xstrdup(spec->dst ? spec->dst : item->string);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
|
||||
{
|
||||
struct ref *ref, *matches;
|
||||
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
|
||||
struct refspec refspec;
|
||||
|
||||
refspec.force = 0;
|
||||
refspec.pattern = 1;
|
||||
refspec.src = refspec.dst = "refs/heads/";
|
||||
states->heads.strdup_strings = 1;
|
||||
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
|
||||
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
|
||||
fetch_map, 1);
|
||||
for(ref = matches; ref; ref = ref->next)
|
||||
string_list_append(abbrev_branch(ref->name), &states->heads);
|
||||
|
||||
free_refs(fetch_map);
|
||||
free_refs(matches);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct known_remote {
|
||||
struct known_remote *next;
|
||||
struct remote *remote;
|
||||
@ -466,7 +598,7 @@ static int mv(int argc, const char **argv)
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *item = branch_list.items + i;
|
||||
struct branch_info *info = item->util;
|
||||
if (info->remote && !strcmp(info->remote, rename.old)) {
|
||||
if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "branch.%s.remote", item->string);
|
||||
if (git_config_set(buf.buf, rename.new)) {
|
||||
@ -575,7 +707,7 @@ static int rm(int argc, const char **argv)
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *item = branch_list.items + i;
|
||||
struct branch_info *info = item->util;
|
||||
if (info->remote && !strcmp(info->remote, remote->name)) {
|
||||
if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
|
||||
const char *keys[] = { "remote", "merge", NULL }, **k;
|
||||
for (k = keys; *k; k++) {
|
||||
strbuf_reset(&buf);
|
||||
@ -617,18 +749,37 @@ static int rm(int argc, const char **argv)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void show_list(const char *title, struct string_list *list,
|
||||
const char *extra_arg)
|
||||
void clear_push_info(void *util, const char *string)
|
||||
{
|
||||
int i;
|
||||
struct push_info *info = util;
|
||||
free(info->dest);
|
||||
free(info);
|
||||
}
|
||||
|
||||
if (!list->nr)
|
||||
return;
|
||||
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->tracked, 0);
|
||||
string_list_clear(&states->heads, 0);
|
||||
string_list_clear_func(&states->push, clear_push_info);
|
||||
}
|
||||
|
||||
printf(title, list->nr > 1 ? "es" : "", extra_arg);
|
||||
printf("\n");
|
||||
for (i = 0; i < list->nr; i++)
|
||||
printf(" %s\n", list->items[i].string);
|
||||
static int append_ref_to_tracked_list(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct ref_states *states = cb_data;
|
||||
struct refspec refspec;
|
||||
|
||||
if (flags & REF_ISSYMREF)
|
||||
return 0;
|
||||
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec))
|
||||
string_list_append(abbrev_branch(refspec.src), &states->tracked);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_remote_ref_states(const char *name,
|
||||
@ -636,7 +787,7 @@ static int get_remote_ref_states(const char *name,
|
||||
int query)
|
||||
{
|
||||
struct transport *transport;
|
||||
const struct ref *ref;
|
||||
const struct ref *remote_refs;
|
||||
|
||||
states->remote = remote_get(name);
|
||||
if (!states->remote)
|
||||
@ -647,105 +798,321 @@ static int get_remote_ref_states(const char *name,
|
||||
if (query) {
|
||||
transport = transport_get(NULL, states->remote->url_nr > 0 ?
|
||||
states->remote->url[0] : NULL);
|
||||
ref = transport_get_remote_refs(transport);
|
||||
remote_refs = transport_get_remote_refs(transport);
|
||||
transport_disconnect(transport);
|
||||
|
||||
get_ref_states(ref, states);
|
||||
states->queried = 1;
|
||||
if (query & GET_REF_STATES)
|
||||
get_ref_states(remote_refs, states);
|
||||
if (query & GET_HEAD_NAMES)
|
||||
get_head_names(remote_refs, states);
|
||||
if (query & GET_PUSH_REF_STATES)
|
||||
get_push_ref_states(remote_refs, states);
|
||||
} else {
|
||||
for_each_ref(append_ref_to_tracked_list, states);
|
||||
sort_string_list(&states->tracked);
|
||||
get_push_ref_states_noquery(states);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_ref_to_tracked_list(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
struct show_info {
|
||||
struct string_list *list;
|
||||
struct ref_states *states;
|
||||
int width, width2;
|
||||
int any_rebase;
|
||||
};
|
||||
|
||||
int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct ref_states *states = cb_data;
|
||||
struct refspec refspec;
|
||||
struct show_info *info = cb_data;
|
||||
int n = strlen(item->string);
|
||||
if (n > info->width)
|
||||
info->width = n;
|
||||
string_list_insert(item->string, info->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec))
|
||||
string_list_append(abbrev_branch(refspec.src), &states->tracked);
|
||||
int show_remote_info_item(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct show_info *info = cb_data;
|
||||
struct ref_states *states = info->states;
|
||||
const char *name = item->string;
|
||||
|
||||
if (states->queried) {
|
||||
const char *fmt = "%s";
|
||||
const char *arg = "";
|
||||
if (string_list_has_string(&states->new, name)) {
|
||||
fmt = " new (next fetch will store in remotes/%s)";
|
||||
arg = states->remote->name;
|
||||
} else if (string_list_has_string(&states->tracked, name))
|
||||
arg = " tracked";
|
||||
else if (string_list_has_string(&states->stale, name))
|
||||
arg = " stale (use 'git remote prune' to remove)";
|
||||
else
|
||||
arg = " ???";
|
||||
printf(" %-*s", info->width, name);
|
||||
printf(fmt, arg);
|
||||
printf("\n");
|
||||
} else
|
||||
printf(" %s\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct ref_states *states = show_info->states;
|
||||
struct branch_info *branch_info = branch_item->util;
|
||||
struct string_list_item *item;
|
||||
int n;
|
||||
|
||||
if (!branch_info->merge.nr || !branch_info->remote_name ||
|
||||
strcmp(states->remote->name, branch_info->remote_name))
|
||||
return 0;
|
||||
if ((n = strlen(branch_item->string)) > show_info->width)
|
||||
show_info->width = n;
|
||||
if (branch_info->rebase)
|
||||
show_info->any_rebase = 1;
|
||||
|
||||
item = string_list_insert(branch_item->string, show_info->list);
|
||||
item->util = branch_info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int show_local_info_item(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct branch_info *branch_info = item->util;
|
||||
struct string_list *merge = &branch_info->merge;
|
||||
const char *also;
|
||||
int i;
|
||||
|
||||
if (branch_info->rebase && branch_info->merge.nr > 1) {
|
||||
error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
|
||||
item->string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf(" %-*s ", show_info->width, item->string);
|
||||
if (branch_info->rebase) {
|
||||
printf("rebases onto remote %s\n", merge->items[0].string);
|
||||
return 0;
|
||||
} else if (show_info->any_rebase) {
|
||||
printf(" merges with remote %s\n", merge->items[0].string);
|
||||
also = " and with remote";
|
||||
} else {
|
||||
printf("merges with remote %s\n", merge->items[0].string);
|
||||
also = " and with remote";
|
||||
}
|
||||
for (i = 1; i < merge->nr; i++)
|
||||
printf(" %-*s %s %s\n", show_info->width, "", also,
|
||||
merge->items[i].string);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct push_info *push_info = push_item->util;
|
||||
struct string_list_item *item;
|
||||
int n;
|
||||
if ((n = strlen(push_item->string)) > show_info->width)
|
||||
show_info->width = n;
|
||||
if ((n = strlen(push_info->dest)) > show_info->width2)
|
||||
show_info->width2 = n;
|
||||
item = string_list_append(push_item->string, show_info->list);
|
||||
item->util = push_item->util;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int show_push_info_item(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct push_info *push_info = item->util;
|
||||
char *src = item->string, *status = NULL;
|
||||
|
||||
switch (push_info->status) {
|
||||
case PUSH_STATUS_CREATE:
|
||||
status = "create";
|
||||
break;
|
||||
case PUSH_STATUS_DELETE:
|
||||
status = "delete";
|
||||
src = "(none)";
|
||||
break;
|
||||
case PUSH_STATUS_UPTODATE:
|
||||
status = "up to date";
|
||||
break;
|
||||
case PUSH_STATUS_FASTFORWARD:
|
||||
status = "fast forwardable";
|
||||
break;
|
||||
case PUSH_STATUS_OUTOFDATE:
|
||||
status = "local out of date";
|
||||
break;
|
||||
case PUSH_STATUS_NOTQUERIED:
|
||||
break;
|
||||
}
|
||||
if (status)
|
||||
printf(" %-*s %s to %-*s (%s)\n", show_info->width, src,
|
||||
push_info->forced ? "forces" : "pushes",
|
||||
show_info->width2, push_info->dest, status);
|
||||
else
|
||||
printf(" %-*s %s to %s\n", show_info->width, src,
|
||||
push_info->forced ? "forces" : "pushes",
|
||||
push_info->dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show(int argc, const char **argv)
|
||||
{
|
||||
int no_query = 0, result = 0;
|
||||
int no_query = 0, result = 0, query_flag = 0;
|
||||
struct option options[] = {
|
||||
OPT_GROUP("show specific options"),
|
||||
OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
|
||||
OPT_END()
|
||||
};
|
||||
struct ref_states states;
|
||||
struct string_list info_list = { NULL, 0, 0, 0 };
|
||||
struct show_info info;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
|
||||
if (argc < 1)
|
||||
return show_all();
|
||||
|
||||
if (!no_query)
|
||||
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
|
||||
|
||||
memset(&states, 0, sizeof(states));
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.states = &states;
|
||||
info.list = &info_list;
|
||||
for (; argc; argc--, argv++) {
|
||||
int i;
|
||||
|
||||
get_remote_ref_states(*argv, &states, !no_query);
|
||||
get_remote_ref_states(*argv, &states, query_flag);
|
||||
|
||||
printf("* remote %s\n URL: %s\n", *argv,
|
||||
states.remote->url_nr > 0 ?
|
||||
states.remote->url[0] : "(no URL)");
|
||||
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *branch = branch_list.items + i;
|
||||
struct branch_info *info = branch->util;
|
||||
int j;
|
||||
|
||||
if (!info->merge.nr || strcmp(*argv, info->remote))
|
||||
continue;
|
||||
printf(" Remote branch%s merged with 'git pull' "
|
||||
"while on branch %s\n ",
|
||||
info->merge.nr > 1 ? "es" : "",
|
||||
branch->string);
|
||||
for (j = 0; j < info->merge.nr; j++)
|
||||
printf(" %s", info->merge.items[j].string);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (!no_query) {
|
||||
show_list(" New remote branch%s (next fetch "
|
||||
"will store in remotes/%s)",
|
||||
&states.new, states.remote->name);
|
||||
show_list(" Stale tracking branch%s (use 'git remote "
|
||||
"prune')", &states.stale, "");
|
||||
}
|
||||
|
||||
if (no_query)
|
||||
for_each_ref(append_ref_to_tracked_list, &states);
|
||||
show_list(" Tracked remote branch%s", &states.tracked, "");
|
||||
|
||||
if (states.remote->push_refspec_nr) {
|
||||
printf(" Local branch%s pushed with 'git push'\n",
|
||||
states.remote->push_refspec_nr > 1 ?
|
||||
"es" : "");
|
||||
for (i = 0; i < states.remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = states.remote->push + i;
|
||||
printf(" %s%s%s%s\n",
|
||||
spec->force ? "+" : "",
|
||||
abbrev_branch(spec->src),
|
||||
spec->dst ? ":" : "",
|
||||
spec->dst ? abbrev_branch(spec->dst) : "");
|
||||
}
|
||||
printf(" HEAD branch: (not queried)\n");
|
||||
else if (!states.heads.nr)
|
||||
printf(" HEAD branch: (unknown)\n");
|
||||
else if (states.heads.nr == 1)
|
||||
printf(" HEAD branch: %s\n", states.heads.items[0].string);
|
||||
else {
|
||||
printf(" HEAD branch (remote HEAD is ambiguous,"
|
||||
" may be one of the following):\n");
|
||||
for (i = 0; i < states.heads.nr; i++)
|
||||
printf(" %s\n", states.heads.items[i].string);
|
||||
}
|
||||
|
||||
/* NEEDSWORK: free remote */
|
||||
string_list_clear(&states.new, 0);
|
||||
string_list_clear(&states.stale, 0);
|
||||
string_list_clear(&states.tracked, 0);
|
||||
/* remote branch info */
|
||||
info.width = 0;
|
||||
for_each_string_list(add_remote_to_show_info, &states.new, &info);
|
||||
for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
|
||||
for_each_string_list(add_remote_to_show_info, &states.stale, &info);
|
||||
if (info.list->nr)
|
||||
printf(" Remote branch%s:%s\n",
|
||||
info.list->nr > 1 ? "es" : "",
|
||||
no_query ? " (status not queried)" : "");
|
||||
for_each_string_list(show_remote_info_item, info.list, &info);
|
||||
string_list_clear(info.list, 0);
|
||||
|
||||
/* git pull info */
|
||||
info.width = 0;
|
||||
info.any_rebase = 0;
|
||||
for_each_string_list(add_local_to_show_info, &branch_list, &info);
|
||||
if (info.list->nr)
|
||||
printf(" Local branch%s configured for 'git pull':\n",
|
||||
info.list->nr > 1 ? "es" : "");
|
||||
for_each_string_list(show_local_info_item, info.list, &info);
|
||||
string_list_clear(info.list, 0);
|
||||
|
||||
/* git push info */
|
||||
if (states.remote->mirror)
|
||||
printf(" Local refs will be mirrored by 'git push'\n");
|
||||
|
||||
info.width = info.width2 = 0;
|
||||
for_each_string_list(add_push_to_show_info, &states.push, &info);
|
||||
sort_string_list(info.list);
|
||||
if (info.list->nr)
|
||||
printf(" Local ref%s configured for 'git push'%s:\n",
|
||||
info.list->nr > 1 ? "s" : "",
|
||||
no_query ? " (status not queried)" : "");
|
||||
for_each_string_list(show_push_info_item, info.list, &info);
|
||||
string_list_clear(info.list, 0);
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int set_head(int argc, const char **argv)
|
||||
{
|
||||
int i, opt_a = 0, opt_d = 0, result = 0;
|
||||
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
|
||||
char *head_name = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP("set-head specific options"),
|
||||
OPT_BOOLEAN('a', "auto", &opt_a,
|
||||
"set refs/remotes/<name>/HEAD according to remote"),
|
||||
OPT_BOOLEAN('d', "delete", &opt_d,
|
||||
"delete refs/remotes/<name>/HEAD"),
|
||||
OPT_END()
|
||||
};
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
if (argc)
|
||||
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
|
||||
|
||||
if (!opt_a && !opt_d && argc == 2) {
|
||||
head_name = xstrdup(argv[1]);
|
||||
} else if (opt_a && !opt_d && argc == 1) {
|
||||
struct ref_states states;
|
||||
memset(&states, 0, sizeof(states));
|
||||
get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
|
||||
if (!states.heads.nr)
|
||||
result |= error("Cannot determine remote HEAD");
|
||||
else if (states.heads.nr > 1) {
|
||||
result |= error("Multiple remote HEAD branches. "
|
||||
"Please choose one explicitly with:");
|
||||
for (i = 0; i < states.heads.nr; i++)
|
||||
fprintf(stderr, " git remote set-head %s %s\n",
|
||||
argv[0], states.heads.items[i].string);
|
||||
} else
|
||||
head_name = xstrdup(states.heads.items[0].string);
|
||||
free_remote_ref_states(&states);
|
||||
} else if (opt_d && !opt_a && argc == 1) {
|
||||
if (delete_ref(buf.buf, NULL, REF_NODEREF))
|
||||
result |= error("Could not delete %s", buf.buf);
|
||||
} else
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
if (head_name) {
|
||||
unsigned char sha1[20];
|
||||
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
|
||||
/* make sure it's valid */
|
||||
if (!resolve_ref(buf2.buf, sha1, 1, NULL))
|
||||
result |= error("Not a valid ref: %s", buf2.buf);
|
||||
else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
|
||||
result |= error("Could not setup %s", buf.buf);
|
||||
if (opt_a)
|
||||
printf("%s/HEAD set to %s\n", argv[0], head_name);
|
||||
free(head_name);
|
||||
}
|
||||
|
||||
strbuf_release(&buf);
|
||||
strbuf_release(&buf2);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int prune(int argc, const char **argv)
|
||||
{
|
||||
int dry_run = 0, result = 0;
|
||||
@ -770,7 +1137,7 @@ static int prune(int argc, const char **argv)
|
||||
for (; argc; argc--, argv++) {
|
||||
int i;
|
||||
|
||||
get_remote_ref_states(*argv, &states, 1);
|
||||
get_remote_ref_states(*argv, &states, GET_REF_STATES);
|
||||
|
||||
if (states.stale.nr) {
|
||||
printf("Pruning %s\n", *argv);
|
||||
@ -791,10 +1158,7 @@ static int prune(int argc, const char **argv)
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
}
|
||||
|
||||
/* NEEDSWORK: free remote */
|
||||
string_list_clear(&states.new, 0);
|
||||
string_list_clear(&states.stale, 0);
|
||||
string_list_clear(&states.tracked, 0);
|
||||
free_remote_ref_states(&states);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -919,6 +1283,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
|
||||
result = mv(argc, argv);
|
||||
else if (!strcmp(argv[0], "rm"))
|
||||
result = rm(argc, argv);
|
||||
else if (!strcmp(argv[0], "set-head"))
|
||||
result = set_head(argc, argv);
|
||||
else if (!strcmp(argv[0], "show"))
|
||||
result = show(argc, argv);
|
||||
else if (!strcmp(argv[0], "prune"))
|
||||
|
||||
Reference in New Issue
Block a user