"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);
}
struct branch_info {
- char *remote;
+ char *remote_name;
struct string_list merge;
+ int rebase;
};
static struct string_list branch_list;
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")) {
} 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;
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) {
space = strchr(value, ' ');
}
string_list_append(xstrdup(value), &info->merge);
- }
+ } else
+ info->rebase = git_config_bool(orig_key, value);
}
return 0;
}
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,
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);
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;
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)) {
struct string_list_item *item = remote_branches.items + i;
int flag = 0;
unsigned char sha1[20];
- const char *symref;
- symref = resolve_ref(item->string, sha1, 1, &flag);
+ resolve_ref(item->string, sha1, 1, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
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);
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,
int query)
{
struct transport *transport;
- const struct ref *ref;
+ const struct ref *remote_refs;
states->remote = remote_get(name);
if (!states->remote)
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)
+ 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);
}
- 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, "");
- }
+ /* 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);
+ }
- 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) : "");
- }
- }
+ return result;
+ }
- /* NEEDSWORK: free remote */
- string_list_clear(&states.new, 0);
- string_list_clear(&states.stale, 0);
- string_list_clear(&states.tracked, 0);
+ 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;
}
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);
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;
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"))
__gitdir ()
{
if [ -z "${1-}" ]; then
- if [ -n "$__git_dir" ]; then
+ if [ -n "${__git_dir-}" ]; then
echo "$__git_dir"
elif [ -d .git ]; then
echo .git
# returns text to add to bash PS1 prompt (includes branch name)
__git_ps1 ()
{
- local g="$(git rev-parse --git-dir 2>/dev/null)"
+ local g="$(__gitdir)"
if [ -n "$g" ]; then
local r
local b
- if [ -d "$g/rebase-apply" ]
- then
- if test -f "$g/rebase-apply/rebasing"
- then
+ if [ -d "$g/rebase-apply" ]; then
+ if [ -f "$g/rebase-apply/rebasing" ]; then
r="|REBASE"
- elif test -f "$g/rebase-apply/applying"
- then
+ elif [ -f "$g/rebase-apply/applying" ]; then
r="|AM"
else
r="|AM/REBASE"
fi
b="$(git symbolic-ref HEAD 2>/dev/null)"
- elif [ -f "$g/rebase-merge/interactive" ]
- then
+ elif [ -f "$g/rebase-merge/interactive" ]; then
r="|REBASE-i"
b="$(cat "$g/rebase-merge/head-name")"
- elif [ -d "$g/rebase-merge" ]
- then
+ elif [ -d "$g/rebase-merge" ]; then
r="|REBASE-m"
b="$(cat "$g/rebase-merge/head-name")"
- elif [ -f "$g/MERGE_HEAD" ]
- then
+ elif [ -f "$g/MERGE_HEAD" ]; then
r="|MERGING"
b="$(git symbolic-ref HEAD 2>/dev/null)"
else
- if [ -f "$g/BISECT_LOG" ]
- then
+ if [ -f "$g/BISECT_LOG" ]; then
r="|BISECTING"
fi
- if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
- then
- if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
- then
- b="$(cut -c1-7 "$g/HEAD")..."
+ if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then
+ if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then
+ if [ -r "$g/HEAD" ]; then
+ b="$(cut -c1-7 "$g/HEAD")..."
+ fi
fi
fi
fi
local w
local i
+ local c
- if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then
- if test "$(git config --bool bash.showDirtyState)" != "false"; then
- git diff --no-ext-diff --ignore-submodules \
- --quiet --exit-code || w="*"
- if git rev-parse --quiet --verify HEAD >/dev/null; then
- git diff-index --cached --quiet \
- --ignore-submodules HEAD -- || i="+"
- else
- i="#"
+ if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+ if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then
+ c="BARE:"
+ else
+ b="GIT_DIR!"
+ fi
+ elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+ if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+ if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+ git diff --no-ext-diff --ignore-submodules \
+ --quiet --exit-code || w="*"
+ if git rev-parse --quiet --verify HEAD >/dev/null; then
+ git diff-index --cached --quiet \
+ --ignore-submodules HEAD -- || i="+"
+ else
+ i="#"
+ fi
fi
fi
fi
- if [ -n "${1-}" ]; then
- printf "$1" "${b##refs/heads/}$w$i$r"
- else
- printf " (%s)" "${b##refs/heads/}$w$i$r"
+ if [ -n "$b" ]; then
+ if [ -n "${1-}" ]; then
+ printf "$1" "$c${b##refs/heads/}$w$i$r"
+ else
+ printf " (%s)" "$c${b##refs/heads/}$w$i$r"
+ fi
fi
fi
}
__git_merge_strategies ()
{
- if [ -n "$__git_merge_strategylist" ]; then
+ if [ -n "${__git_merge_strategylist-}" ]; then
echo "$__git_merge_strategylist"
return
fi
esac
}
+__git_complete_remote_or_refspec ()
+{
+ local cmd="${COMP_WORDS[1]}"
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+ while [ $c -lt $COMP_CWORD ]; do
+ i="${COMP_WORDS[c]}"
+ case "$i" in
+ --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+ -*) ;;
+ *) remote="$i"; break ;;
+ esac
+ c=$((++c))
+ done
+ if [ -z "$remote" ]; then
+ __gitcomp "$(__git_remotes)"
+ return
+ fi
+ if [ $no_complete_refspec = 1 ]; then
+ COMPREPLY=()
+ return
+ fi
+ [ "$remote" = "." ] && remote=
+ case "$cur" in
+ *:*)
+ case "$COMP_WORDBREAKS" in
+ *:*) : great ;;
+ *) pfx="${cur%%:*}:" ;;
+ esac
+ cur="${cur#*:}"
+ lhs=0
+ ;;
+ +*)
+ pfx="+"
+ cur="${cur#+}"
+ ;;
+ esac
+ case "$cmd" in
+ fetch)
+ if [ $lhs = 1 ]; then
+ __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+ else
+ __gitcomp "$(__git_refs)" "$pfx" "$cur"
+ fi
+ ;;
+ pull)
+ if [ $lhs = 1 ]; then
+ __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+ else
+ __gitcomp "$(__git_refs)" "$pfx" "$cur"
+ fi
+ ;;
+ push)
+ if [ $lhs = 1 ]; then
+ __gitcomp "$(__git_refs)" "$pfx" "$cur"
+ else
+ __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+ fi
+ ;;
+ esac
+}
+
+__git_complete_strategy ()
+{
+ case "${COMP_WORDS[COMP_CWORD-1]}" in
+ -s|--strategy)
+ __gitcomp "$(__git_merge_strategies)"
+ return 0
+ esac
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ --strategy=*)
+ __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+ return 0
+ ;;
+ esac
+ return 1
+}
+
__git_all_commands ()
{
- if [ -n "$__git_all_commandlist" ]; then
+ if [ -n "${__git_all_commandlist-}" ]; then
echo "$__git_all_commandlist"
return
fi
__git_porcelain_commands ()
{
- if [ -n "$__git_porcelain_commandlist" ]; then
+ if [ -n "${__git_porcelain_commandlist-}" ]; then
echo "$__git_porcelain_commandlist"
return
fi
__git_complete_file
}
+__git_fetch_options="
+ --quiet --verbose --append --upload-pack --force --keep --depth=
+ --tags --no-tags
+"
+
_git_fetch ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
-
- if [ "$COMP_CWORD" = 2 ]; then
- __gitcomp "$(__git_remotes)"
- else
- case "$cur" in
- *:*)
- local pfx=""
- case "$COMP_WORDBREAKS" in
- *:*) : great ;;
- *) pfx="${cur%%:*}:" ;;
- esac
- __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
- ;;
- *)
- __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
- ;;
- esac
- fi
+ case "$cur" in
+ --*)
+ __gitcomp "$__git_fetch_options"
+ return
+ ;;
+ esac
+ __git_complete_remote_or_refspec
}
_git_format_patch ()
" "" "${cur##--pretty=}"
return
;;
+ --format=*)
+ __gitcomp "$__git_log_pretty_formats
+ " "" "${cur##--format=}"
+ return
+ ;;
--date=*)
__gitcomp "
relative iso8601 rfc2822 short local default
--follow
--abbrev-commit --abbrev=
--relative-date --date=
- --pretty=
+ --pretty= --format= --oneline
--cherry-pick
--graph
--decorate
__git_complete_revlist
}
+__git_merge_options="
+ --no-commit --no-stat --log --no-log --squash --strategy
+ --commit --stat --no-squash --ff --no-ff
+"
+
_git_merge ()
{
+ __git_complete_strategy && return
+
local cur="${COMP_WORDS[COMP_CWORD]}"
- case "${COMP_WORDS[COMP_CWORD-1]}" in
- -s|--strategy)
- __gitcomp "$(__git_merge_strategies)"
- return
- esac
case "$cur" in
- --strategy=*)
- __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
- return
- ;;
--*)
- __gitcomp "
- --no-commit --no-stat --log --no-log --squash --strategy
- --commit --stat --no-squash --ff --no-ff
- "
+ __gitcomp "$__git_merge_options"
return
esac
__gitcomp "$(__git_refs)"
_git_pull ()
{
- local cur="${COMP_WORDS[COMP_CWORD]}"
+ __git_complete_strategy && return
- if [ "$COMP_CWORD" = 2 ]; then
- __gitcomp "$(__git_remotes)"
- else
- __gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
- fi
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ --*)
+ __gitcomp "
+ --rebase --no-rebase
+ $__git_merge_options
+ $__git_fetch_options
+ "
+ return
+ ;;
+ esac
+ __git_complete_remote_or_refspec
}
_git_push ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
-
- if [ "$COMP_CWORD" = 2 ]; then
+ case "${COMP_WORDS[COMP_CWORD-1]}" in
+ --repo)
__gitcomp "$(__git_remotes)"
- else
- case "$cur" in
- *:*)
- local pfx=""
- case "$COMP_WORDBREAKS" in
- *:*) : great ;;
- *) pfx="${cur%%:*}:" ;;
- esac
-
- __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
- ;;
- +*)
- __gitcomp "$(__git_refs)" + "${cur#+}"
- ;;
- *)
- __gitcomp "$(__git_refs)"
- ;;
- esac
- fi
+ return
+ esac
+ case "$cur" in
+ --repo=*)
+ __gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+ return
+ ;;
+ --*)
+ __gitcomp "
+ --all --mirror --tags --dry-run --force --verbose
+ --receive-pack= --repo=
+ "
+ return
+ ;;
+ esac
+ __git_complete_remote_or_refspec
}
_git_rebase ()
__gitcomp "--continue --skip --abort"
return
fi
- case "${COMP_WORDS[COMP_CWORD-1]}" in
- -s|--strategy)
- __gitcomp "$(__git_merge_strategies)"
- return
- esac
+ __git_complete_strategy && return
case "$cur" in
- --strategy=*)
- __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
- return
- ;;
--*)
__gitcomp "--onto --merge --strategy --interactive"
return
_git_remote ()
{
- local subcommands="add rename rm show prune update"
+ local subcommands="add rename rm show prune update set-head"
local subcommand="$(__git_find_subcommand "$subcommands")"
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
" "" "${cur##--pretty=}"
return
;;
+ --format=*)
+ __gitcomp "$__git_log_pretty_formats
+ " "" "${cur##--format=}"
+ return
+ ;;
--*)
- __gitcomp "--pretty=
+ __gitcomp "--pretty= --format=
$__git_diff_common_options
"
return
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
- local g="$(git rev-parse --git-dir 2>/dev/null)"
+ local g="$(__gitdir)"
local merge=""
if [ -f $g/MERGE_HEAD ]; then
merge="--merge"
#include "diff.h"
#include "revision.h"
#include "dir.h"
+ #include "tag.h"
static struct refspec s_tag_refspec = {
0,
int is_glob;
const char *lhs, *rhs;
- llen = is_glob = 0;
+ is_glob = 0;
lhs = refspec[i];
if (*lhs == '+') {
static struct ref *copy_ref(const struct ref *ref)
{
- struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
- memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
- ret->next = NULL;
- return ret;
+ struct ref *cpy;
+ 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);
+ cpy->next = NULL;
+ cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
+ cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
+ cpy->peer_ref = copy_ref(ref->peer_ref);
+ return cpy;
}
struct ref *copy_ref_list(const struct ref *ref)
{
if (!ref)
return;
+ free_ref(ref->peer_ref);
free(ref->remote_status);
free(ref->symref);
free(ref);
struct ref *next;
while (ref) {
next = ref->next;
- free(ref->peer_ref);
free_ref(ref);
ref = next;
}
struct refspec *rs)
{
struct ref *matched_src, *matched_dst;
+ int copy_src;
const char *dst_value = rs->dst;
char *dst_guess;
matched_src = matched_dst = NULL;
switch (count_refspec_match(rs->src, src, &matched_src)) {
case 1:
+ copy_src = 1;
break;
case 0:
/* The source could be in the get_sha1() format
matched_src = try_explicit_object_name(rs->src);
if (!matched_src)
return error("src refspec %s does not match any.", rs->src);
+ copy_src = 0;
break;
default:
return error("src refspec %s matches more than one.", rs->src);
return error("dst ref %s receives from more than one src.",
matched_dst->name);
else {
- matched_dst->peer_ref = matched_src;
+ matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
matched_dst->force = rs->force;
}
return 0;
struct refspec *rs;
int send_all = flags & MATCH_REFS_ALL;
int send_mirror = flags & MATCH_REFS_MIRROR;
+ int errs;
static const char *default_refspec[] = { ":", 0 };
if (!nr_refspec) {
refspec = default_refspec;
}
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
- if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
- return -1;
+ errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
/* pick the remainder */
for ( ; src; src = src->next) {
dst_peer = make_linked_ref(dst_name, dst_tail);
hashcpy(dst_peer->new_sha1, src->new_sha1);
}
- dst_peer->peer_ref = src;
+ dst_peer->peer_ref = copy_ref(src);
dst_peer->force = pat->force;
free_name:
free(dst_name);
}
+ if (errs)
+ return -1;
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);
+ }
+ }
+
+ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
+ {
+ struct object *o;
+ struct commit *old, *new;
+ 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.
+ */
+ o = deref_tag(parse_object(old_sha1), NULL, 0);
+ if (!o || o->type != OBJ_COMMIT)
+ return 0;
+ old = (struct commit *) o;
+
+ o = deref_tag(parse_object(new_sha1), NULL, 0);
+ if (!o || o->type != OBJ_COMMIT)
+ return 0;
+ new = (struct commit *) o;
+
+ if (parse_commit(new) < 0)
+ return 0;
+
+ used = list = NULL;
+ commit_list_insert(new, &list);
+ while (list) {
+ new = pop_most_recent_commit(&list, TMP_MARK);
+ commit_list_insert(new, &used);
+ if (new == old) {
+ found = 1;
+ break;
+ }
+ }
+ unmark_and_free(list, TMP_MARK);
+ unmark_and_free(used, TMP_MARK);
+ return found;
+ }
+
/*
* Return true if there is anything to report, otherwise false.
*/
base, num_ours, num_theirs);
return 1;
}
+
+ static int one_local_ref(const char *refname, const unsigned char *sha1, 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_ref_format(refname + 5))
+ return 0;
+
+ len = strlen(refname) + 1;
+ ref = xcalloc(1, sizeof(*ref) + len);
+ hashcpy(ref->new_sha1, sha1);
+ memcpy(ref->name, refname, len);
+ **local_tail = ref;
+ *local_tail = &ref->next;
+ return 0;
+ }
+
+ struct ref *get_local_heads(void)
+ {
+ struct ref *local_refs, **local_tail = &local_refs;
+ for_each_ref(one_local_ref, &local_tail);
+ return local_refs;
+ }
+
+ struct ref *guess_remote_head(const struct ref *head,
+ const struct ref *refs,
+ int all)
+ {
+ const struct ref *r;
+ struct ref *list = NULL;
+ struct ref **tail = &list;
+
+ if (!head)
+ return NULL;
+
+ /*
+ * Some transports support directly peeking at
+ * where HEAD points; if that is the case, then
+ * we don't have to guess.
+ */
+ if (head->symref)
+ return copy_ref(find_ref_by_name(refs, head->symref));
+
+ /* 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))
+ return copy_ref(r);
+ }
+
+ /* Look for another ref that points there */
+ for (r = refs; r; r = r->next) {
+ if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
+ *tail = copy_ref(r);
+ tail = &((*tail)->next);
+ if (!all)
+ break;
+ }
+ }
+
+ return list;
+ }