struct string_list refs = STRING_LIST_INIT_NODUP;
struct string_list_item *item = NULL;
struct ref *prev = NULL, *next = NULL;
+
for (; ref_map; prev = ref_map, ref_map = next) {
next = ref_map->next;
if (!ref_map->peer_ref)
continue;
- item = string_list_lookup(&refs, ref_map->peer_ref->name);
- if (item) {
+ item = string_list_insert(&refs, ref_map->peer_ref->name);
+ if (item->util) {
+ /* Entry already existed */
if (strcmp(((struct ref *)item->util)->name,
ref_map->name))
die("%s tracks both %s and %s",
free(ref_map->peer_ref);
free(ref_map);
ref_map = prev; /* skip this; we freed it */
- continue;
+ } else {
+ item->util = ref_map;
}
-
- item = string_list_insert(&refs, ref_map->peer_ref->name);
- item->util = ref_map;
}
string_list_clear(&refs, 0);
}
{
int i;
int find_src = !query->src;
+ const char *needle = find_src ? query->dst : query->src;
+ char **result = find_src ? &query->src : &query->dst;
if (find_src && !query->dst)
return error("query_refspecs: need either src or dst");
struct refspec *refspec = &refs[i];
const char *key = find_src ? refspec->dst : refspec->src;
const char *value = find_src ? refspec->src : refspec->dst;
- const char *needle = find_src ? query->dst : query->src;
- char **result = find_src ? &query->src : &query->dst;
if (!refspec->dst)
continue;
return (flag & REF_ISSYMREF);
}
+/*
+ * Create and return a list of (struct ref) consisting of copies of
+ * each remote_ref that matches refspec. refspec must be a pattern.
+ * Fill in the copies' peer_ref to describe the local tracking refs to
+ * which they map. Omit any references that would map to an existing
+ * local symbolic ref.
+ */
static struct ref *get_expanded_map(const struct ref *remote_refs,
const struct refspec *refspec)
{
struct ref *ret = NULL;
struct ref **tail = &ret;
- char *expn_name;
-
for (ref = remote_refs; ref; ref = ref->next) {
+ char *expn_name = NULL;
+
if (strchr(ref->name, '^'))
continue; /* a dereference item */
if (match_name_with_pattern(refspec->src, ref->name,
struct ref *cpy = copy_ref(ref);
cpy->peer_ref = alloc_ref(expn_name);
- free(expn_name);
if (refspec->force)
cpy->peer_ref->force = 1;
*tail = cpy;
tail = &cpy->next;
}
+ free(expn_name);
}
return ret;
}
/*
- * Return true if there is anything to report, otherwise false.
+ * Compare a branch with its upstream, and save their differences (number
+ * of commits) in *num_ours and *num_theirs.
+ *
+ * Return 0 if branch has no upstream (no base), -1 if upstream is missing
+ * (with "gone" base), otherwise 1 (with base).
*/
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
{
const char *rev_argv[10], *base;
int rev_argc;
- /*
- * Nothing to report unless we are marked to build on top of
- * somebody else.
- */
+ /* Cannot stat unless we are marked to build on top of somebody else. */
if (!branch ||
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
return 0;
- /*
- * If what we used to build on no longer exists, there is
- * nothing to report.
- */
+ /* Cannot stat if what we used to build on no longer exists */
base = branch->merge[0]->dst;
if (read_ref(base, sha1))
- return 0;
+ return -1;
theirs = lookup_commit_reference(sha1);
if (!theirs)
- return 0;
+ return -1;
if (read_ref(branch->refname, sha1))
- return 0;
+ return -1;
ours = lookup_commit_reference(sha1);
if (!ours)
- return 0;
+ return -1;
/* are we the same? */
- if (theirs == ours)
- return 0;
+ if (theirs == ours) {
+ *num_theirs = *num_ours = 0;
+ return 1;
+ }
/* Run "rev-list --left-right ours...theirs" internally... */
rev_argc = 0;
*/
int format_tracking_info(struct branch *branch, struct strbuf *sb)
{
- int num_ours, num_theirs;
+ int ours, theirs;
const char *base;
+ int upstream_is_gone = 0;
- if (!stat_tracking_info(branch, &num_ours, &num_theirs))
+ switch (stat_tracking_info(branch, &ours, &theirs)) {
+ case 0:
+ /* no base */
return 0;
+ case -1:
+ /* with "gone" base */
+ upstream_is_gone = 1;
+ break;
+ default:
+ /* with base */
+ break;
+ }
base = branch->merge[0]->dst;
base = shorten_unambiguous_ref(base, 0);
- if (!num_theirs) {
+ if (upstream_is_gone) {
+ strbuf_addf(sb,
+ _("Your branch is based on '%s', but the upstream is gone.\n"),
+ base);
+ if (advice_status_hints)
+ strbuf_addf(sb,
+ _(" (use \"git branch --unset-upstream\" to fixup)\n"));
+ } else if (!ours && !theirs) {
+ strbuf_addf(sb,
+ _("Your branch is up-to-date with '%s'.\n"),
+ base);
+ } else if (!theirs) {
strbuf_addf(sb,
Q_("Your branch is ahead of '%s' by %d commit.\n",
"Your branch is ahead of '%s' by %d commits.\n",
- num_ours),
- base, num_ours);
+ ours),
+ base, ours);
if (advice_status_hints)
strbuf_addf(sb,
_(" (use \"git push\" to publish your local commits)\n"));
- } else if (!num_ours) {
+ } else if (!ours) {
strbuf_addf(sb,
Q_("Your branch is behind '%s' by %d commit, "
"and can be fast-forwarded.\n",
"Your branch is behind '%s' by %d commits, "
"and can be fast-forwarded.\n",
- num_theirs),
- base, num_theirs);
+ theirs),
+ base, theirs);
if (advice_status_hints)
strbuf_addf(sb,
_(" (use \"git pull\" to update your local branch)\n"));
"Your branch and '%s' have diverged,\n"
"and have %d and %d different commits each, "
"respectively.\n",
- num_theirs),
- base, num_ours, num_theirs);
+ theirs),
+ base, ours, theirs);
if (advice_status_hints)
strbuf_addf(sb,
_(" (use \"git pull\" to merge the remote branch into yours)\n"));