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:
Junio C Hamano
2009-03-17 18:55:06 -07:00
17 changed files with 821 additions and 331 deletions

View File

@ -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"))