diff options
Diffstat (limited to 'remote.c')
| -rw-r--r-- | remote.c | 307 |
1 files changed, 189 insertions, 118 deletions
@@ -29,6 +29,12 @@ enum map_direction { FROM_SRC, FROM_DST }; +enum { + ENABLE_ADVICE_PULL = (1 << 0), + ENABLE_ADVICE_PUSH = (1 << 1), + ENABLE_ADVICE_DIVERGENCE = (1 << 2), +}; + struct counted_string { size_t len; const char *s; @@ -272,6 +278,7 @@ static void branch_release(struct branch *branch) free((char *)branch->refname); free(branch->remote_name); free(branch->pushremote_name); + free(branch->push_tracking_ref); merge_clear(branch); } @@ -1381,12 +1388,7 @@ static struct ref **tail_ref(struct ref **head) return tail; } -struct tips { - struct commit **tip; - size_t nr, alloc; -}; - -static void add_to_tips(struct tips *tips, const struct object_id *oid) +static void add_to_tips(struct commit_stack *tips, const struct object_id *oid) { struct commit *commit; @@ -1396,8 +1398,7 @@ static void add_to_tips(struct tips *tips, const struct object_id *oid) if (!commit || (commit->object.flags & TMP_MARK)) return; commit->object.flags |= TMP_MARK; - ALLOC_GROW(tips->tip, tips->nr + 1, tips->alloc); - tips->tip[tips->nr++] = commit; + commit_stack_push(tips, commit); } static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***dst_tail) @@ -1406,13 +1407,12 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds struct string_list src_tag = STRING_LIST_INIT_NODUP; struct string_list_item *item; struct ref *ref; - struct tips sent_tips; + struct commit_stack sent_tips = COMMIT_STACK_INIT; /* * Collect everything we know they would have at the end of * this push, and collect all tags they have. */ - memset(&sent_tips, 0, sizeof(sent_tips)); for (ref = *dst; ref; ref = ref->next) { if (ref->peer_ref && !is_null_oid(&ref->peer_ref->new_oid)) @@ -1422,7 +1422,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds if (starts_with(ref->name, "refs/tags/")) string_list_append(&dst_tag, ref->name); } - clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK); + clear_commit_marks_many(sent_tips.nr, sent_tips.items, TMP_MARK); string_list_sort(&dst_tag); @@ -1450,9 +1450,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds if (sent_tips.nr) { const int reachable_flag = 1; struct commit_list *found_commits; - struct commit **src_commits; - size_t nr_src_commits = 0, alloc_src_commits = 16; - ALLOC_ARRAY(src_commits, alloc_src_commits); + struct commit_stack src_commits = COMMIT_STACK_INIT; for_each_string_list_item(item, &src_tag) { struct ref *ref = item->util; @@ -1467,12 +1465,13 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds /* not pushing a commit, which is not an error */ continue; - ALLOC_GROW(src_commits, nr_src_commits + 1, alloc_src_commits); - src_commits[nr_src_commits++] = commit; + commit_stack_push(&src_commits, commit); } - found_commits = get_reachable_subset(sent_tips.tip, sent_tips.nr, - src_commits, nr_src_commits, + found_commits = get_reachable_subset(sent_tips.items, + sent_tips.nr, + src_commits.items, + src_commits.nr, reachable_flag); for_each_string_list_item(item, &src_tag) { @@ -1502,13 +1501,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds dst_ref->peer_ref = copy_ref(ref); } - clear_commit_marks_many(nr_src_commits, src_commits, reachable_flag); - free(src_commits); - free_commit_list(found_commits); + clear_commit_marks_many(src_commits.nr, src_commits.items, + reachable_flag); + commit_stack_clear(&src_commits); + commit_list_free(found_commits); } string_list_clear(&src_tag, 0); - free(sent_tips.tip); + commit_stack_clear(&sent_tips); } struct ref *find_ref_by_name(const struct ref *list, const char *name) @@ -1723,7 +1723,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) { if (starts_with(ref->name, "refs/tags/")) reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; - else if (!odb_has_object(the_repository->objects, &ref->old_oid, HAS_OBJECT_RECHECK_PACKED)) + else if (!odb_has_object(the_repository->objects, &ref->old_oid, ODB_HAS_OBJECT_RECHECK_PACKED)) reject_reason = REF_STATUS_REJECT_FETCH_FIRST; else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) || !lookup_commit_reference_gently(the_repository, &ref->new_oid, 1)) @@ -1838,7 +1838,7 @@ int branch_merge_matches(struct branch *branch, } __attribute__((format (printf,2,3))) -static const char *error_buf(struct strbuf *err, const char *fmt, ...) +static char *error_buf(struct strbuf *err, const char *fmt, ...) { if (err) { va_list ap; @@ -1876,9 +1876,9 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err) return branch->merge[0]->dst; } -static const char *tracking_for_push_dest(struct remote *remote, - const char *refname, - struct strbuf *err) +static char *tracking_for_push_dest(struct remote *remote, + const char *refname, + struct strbuf *err) { char *ret; @@ -1890,8 +1890,8 @@ static const char *tracking_for_push_dest(struct remote *remote, return ret; } -static const char *branch_get_push_1(struct repository *repo, - struct branch *branch, struct strbuf *err) +static char *branch_get_push_1(struct repository *repo, + struct branch *branch, struct strbuf *err) { struct remote_state *remote_state = repo->remote_state; struct remote *remote; @@ -1906,7 +1906,7 @@ static const char *branch_get_push_1(struct repository *repo, if (remote->push.nr) { char *dst; - const char *ret; + char *ret; dst = apply_refspecs(&remote->push, branch->refname); if (!dst) @@ -1931,12 +1931,13 @@ static const char *branch_get_push_1(struct repository *repo, return tracking_for_push_dest(remote, branch->refname, err); case PUSH_DEFAULT_UPSTREAM: - return branch_get_upstream(branch, err); + return xstrdup_or_null(branch_get_upstream(branch, err)); case PUSH_DEFAULT_UNSPECIFIED: case PUSH_DEFAULT_SIMPLE: { - const char *up, *cur; + const char *up; + char *cur; up = branch_get_upstream(branch, err); if (!up) @@ -1944,9 +1945,11 @@ static const char *branch_get_push_1(struct repository *repo, cur = tracking_for_push_dest(remote, branch->refname, err); if (!cur) return NULL; - if (strcmp(cur, up)) + if (strcmp(cur, up)) { + free(cur); return error_buf(err, _("cannot resolve 'simple' push to a single destination")); + } return cur; } } @@ -2237,43 +2240,49 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf); } -/* - * Return true when there is anything to report, otherwise false. - */ -int format_tracking_info(struct branch *branch, struct strbuf *sb, - enum ahead_behind_flags abf, - int show_divergence_advice) +static char *resolve_compare_branch(struct branch *branch, const char *name) { - int ours, theirs, sti; - const char *full_base; - char *base; - int upstream_is_gone = 0; + const char *resolved = NULL; - sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf); - if (sti < 0) { - if (!full_base) - return 0; - upstream_is_gone = 1; + if (!branch || !name) + return NULL; + + if (!strcasecmp(name, "@{upstream}")) { + resolved = branch_get_upstream(branch, NULL); + } else if (!strcasecmp(name, "@{push}")) { + resolved = branch_get_push(branch, NULL); + } else { + warning(_("ignoring value '%s' for status.compareBranches, " + "only @{upstream} and @{push} are supported"), + name); + return NULL; } - base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), - full_base, 0); - if (upstream_is_gone) { - strbuf_addf(sb, - _("Your branch is based on '%s', but the upstream is gone.\n"), - base); - if (advice_enabled(ADVICE_STATUS_HINTS)) - strbuf_addstr(sb, - _(" (use \"git branch --unset-upstream\" to fixup)\n")); - } else if (!sti) { + if (resolved) + return xstrdup(resolved); + return NULL; +} + +static void format_branch_comparison(struct strbuf *sb, + bool up_to_date, + int ours, int theirs, + const char *branch_name, + enum ahead_behind_flags abf, + unsigned flags) +{ + bool use_push_advice = (flags & ENABLE_ADVICE_PUSH); + bool use_pull_advice = (flags & ENABLE_ADVICE_PULL); + bool use_divergence_advice = (flags & ENABLE_ADVICE_DIVERGENCE); + + if (up_to_date) { strbuf_addf(sb, _("Your branch is up to date with '%s'.\n"), - base); + branch_name); } else if (abf == AHEAD_BEHIND_QUICK) { strbuf_addf(sb, _("Your branch and '%s' refer to different commits.\n"), - base); - if (advice_enabled(ADVICE_STATUS_HINTS)) + branch_name); + if (use_push_advice && advice_enabled(ADVICE_STATUS_HINTS)) strbuf_addf(sb, _(" (use \"%s\" for details)\n"), "git status --ahead-behind"); } else if (!theirs) { @@ -2281,8 +2290,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb, Q_("Your branch is ahead of '%s' by %d commit.\n", "Your branch is ahead of '%s' by %d commits.\n", ours), - base, ours); - if (advice_enabled(ADVICE_STATUS_HINTS)) + branch_name, ours); + if (use_push_advice && advice_enabled(ADVICE_STATUS_HINTS)) strbuf_addstr(sb, _(" (use \"git push\" to publish your local commits)\n")); } else if (!ours) { @@ -2292,8 +2301,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb, "Your branch is behind '%s' by %d commits, " "and can be fast-forwarded.\n", theirs), - base, theirs); - if (advice_enabled(ADVICE_STATUS_HINTS)) + branch_name, theirs); + if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS)) strbuf_addstr(sb, _(" (use \"git pull\" to update your local branch)\n")); } else { @@ -2305,31 +2314,121 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb, "and have %d and %d different commits each, " "respectively.\n", ours + theirs), - base, ours, theirs); - if (show_divergence_advice && - advice_enabled(ADVICE_STATUS_HINTS)) + branch_name, ours, theirs); + if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS)) strbuf_addstr(sb, _(" (use \"git pull\" if you want to integrate the remote branch with yours)\n")); } - free(base); - return 1; } -static int one_local_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, - int flag UNUSED, - void *cb_data) +/* + * Return true when there is anything to report, otherwise false. + */ +int format_tracking_info(struct branch *branch, struct strbuf *sb, + enum ahead_behind_flags abf, + int show_divergence_advice) +{ + char *compare_branches = NULL; + struct string_list branches = STRING_LIST_INIT_DUP; + struct strset processed_refs = STRSET_INIT; + int reported = 0; + size_t i; + const char *upstream_ref; + const char *push_ref; + + repo_config_get_string(the_repository, "status.comparebranches", + &compare_branches); + + if (compare_branches) { + string_list_split(&branches, compare_branches, " ", -1); + string_list_remove_empty_items(&branches, 0); + } else { + string_list_append(&branches, "@{upstream}"); + } + + upstream_ref = branch_get_upstream(branch, NULL); + push_ref = branch_get_push(branch, NULL); + + for (i = 0; i < branches.nr; i++) { + char *full_ref; + char *short_ref; + int ours, theirs, cmp; + int is_upstream, is_push; + unsigned flags = 0; + + full_ref = resolve_compare_branch(branch, + branches.items[i].string); + if (!full_ref) + continue; + + if (!strset_add(&processed_refs, full_ref)) { + free(full_ref); + continue; + } + + short_ref = refs_shorten_unambiguous_ref( + get_main_ref_store(the_repository), full_ref, 0); + + is_upstream = upstream_ref && !strcmp(full_ref, upstream_ref); + is_push = push_ref && !strcmp(full_ref, push_ref); + + if (is_upstream && (!push_ref || !strcmp(upstream_ref, push_ref))) + is_push = 1; + + cmp = stat_branch_pair(branch->refname, full_ref, + &ours, &theirs, abf); + + if (cmp < 0) { + if (is_upstream) { + strbuf_addf(sb, + _("Your branch is based on '%s', but the upstream is gone.\n"), + short_ref); + if (advice_enabled(ADVICE_STATUS_HINTS)) + strbuf_addstr(sb, + _(" (use \"git branch --unset-upstream\" to fixup)\n")); + reported = 1; + } + free(full_ref); + free(short_ref); + continue; + } + + if (reported) + strbuf_addstr(sb, "\n"); + + if (is_upstream) + flags |= ENABLE_ADVICE_PULL; + if (is_push) + flags |= ENABLE_ADVICE_PUSH; + if (show_divergence_advice && is_upstream) + flags |= ENABLE_ADVICE_DIVERGENCE; + format_branch_comparison(sb, !cmp, ours, theirs, short_ref, + abf, flags); + reported = 1; + + free(full_ref); + free(short_ref); + } + + string_list_clear(&branches, 0); + strset_clear(&processed_refs); + free(compare_branches); + return reported; +} + +static int one_local_ref(const struct reference *ref, void *cb_data) { struct ref ***local_tail = cb_data; - struct ref *ref; + struct ref *local_ref; /* we already know it starts with refs/ to get here */ - if (check_refname_format(refname + 5, 0)) + if (check_refname_format(ref->name + 5, 0)) return 0; - ref = alloc_ref(refname); - oidcpy(&ref->new_oid, oid); - **local_tail = ref; - *local_tail = &ref->next; + local_ref = alloc_ref(ref->name); + oidcpy(&local_ref->new_oid, ref->oid); + **local_tail = local_ref; + *local_tail = &local_ref->next; return 0; } @@ -2402,15 +2501,14 @@ struct stale_heads_info { struct refspec *rs; }; -static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, const struct object_id *oid, - int flags, void *cb_data) +static int get_stale_heads_cb(const struct reference *ref, void *cb_data) { struct stale_heads_info *info = cb_data; struct string_list matches = STRING_LIST_INIT_DUP; struct refspec_item query; int i, stale = 1; memset(&query, 0, sizeof(struct refspec_item)); - query.dst = (char *)refname; + query.dst = (char *)ref->name; refspec_find_all_matches(info->rs, &query, &matches); if (matches.nr == 0) @@ -2423,7 +2521,7 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, * overlapping refspecs, we need to go over all of the * matching refs. */ - if (flags & REF_ISSYMREF) + if (ref->flags & REF_ISSYMREF) goto clean_exit; for (i = 0; stale && i < matches.nr; i++) @@ -2431,8 +2529,8 @@ static int get_stale_heads_cb(const char *refname, const char *referent UNUSED, stale = 0; if (stale) { - struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail); - oidcpy(&ref->new_oid, oid); + struct ref *linked_ref = make_linked_ref(ref->name, &info->stale_refs_tail); + oidcpy(&linked_ref->new_oid, ref->oid); } clean_exit: @@ -2547,36 +2645,9 @@ static int remote_tracking(struct remote *remote, const char *refname, return 0; } -/* - * The struct "reflog_commit_array" and related helper functions - * are used for collecting commits into an array during reflog - * traversals in "check_and_collect_until()". - */ -struct reflog_commit_array { - struct commit **item; - size_t nr, alloc; -}; - -#define REFLOG_COMMIT_ARRAY_INIT { 0 } - -/* Append a commit to the array. */ -static void append_commit(struct reflog_commit_array *arr, - struct commit *commit) -{ - ALLOC_GROW(arr->item, arr->nr + 1, arr->alloc); - arr->item[arr->nr++] = commit; -} - -/* Free and reset the array. */ -static void free_commit_array(struct reflog_commit_array *arr) -{ - FREE_AND_NULL(arr->item); - arr->nr = arr->alloc = 0; -} - struct check_and_collect_until_cb_data { struct commit *remote_commit; - struct reflog_commit_array *local_commits; + struct commit_stack *local_commits; timestamp_t remote_reflog_timestamp; }; @@ -2608,7 +2679,7 @@ static int check_and_collect_until(const char *refname UNUSED, return 1; if ((commit = lookup_commit_reference(the_repository, n_oid))) - append_commit(cb->local_commits, commit); + commit_stack_push(cb->local_commits, commit); /* * If the reflog entry timestamp is older than the remote ref's @@ -2636,7 +2707,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote) struct commit *commit; struct commit **chunk; struct check_and_collect_until_cb_data cb; - struct reflog_commit_array arr = REFLOG_COMMIT_ARRAY_INIT; + struct commit_stack arr = COMMIT_STACK_INIT; size_t size = 0; int ret = 0; @@ -2667,8 +2738,8 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote) * Check if the remote commit is reachable from any * of the commits in the collected array, in batches. */ - for (chunk = arr.item; chunk < arr.item + arr.nr; chunk += size) { - size = arr.item + arr.nr - chunk; + for (chunk = arr.items; chunk < arr.items + arr.nr; chunk += size) { + size = arr.items + arr.nr - chunk; if (MERGE_BASES_BATCH_SIZE < size) size = MERGE_BASES_BATCH_SIZE; @@ -2677,7 +2748,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote) } cleanup_return: - free_commit_array(&arr); + commit_stack_clear(&arr); return ret; } |
