diff options
Diffstat (limited to 'remote.c')
-rw-r--r-- | remote.c | 89 |
1 files changed, 62 insertions, 27 deletions
@@ -1285,6 +1285,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, struct ref *ref; for (ref = remote_refs; ref; ref = ref->next) { + int force_ref_update = ref->force || force_update; + if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!send_mirror) @@ -1297,34 +1299,46 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, continue; } - /* This part determines what can overwrite what. - * The rules are: - * - * (0) you can always use --force or +A:B notation to - * selectively force individual ref pairs. + /* + * Decide whether an individual refspec A:B can be + * pushed. The push will succeed if any of the + * following are true: * - * (1) if the old thing does not exist, it is OK. + * (1) the remote reference B does not exist * - * (2) if you do not have the old thing, you are not allowed - * to overwrite it; you would not know what you are losing - * otherwise. + * (2) the remote reference B is being removed (i.e., + * pushing :B where no source is specified) * - * (3) if both new and old are commit-ish, and new is a - * descendant of old, it is OK. + * (3) the destination is not under refs/tags/, and + * if the old and new value is a commit, the new + * is a descendant of the old. * - * (4) regardless of all of the above, removing :B is - * always allowed. + * (4) it is forced using the +A:B notation, or by + * passing the --force argument */ - ref->nonfastforward = + ref->update = !ref->deletion && - !is_null_sha1(ref->old_sha1) && - (!has_sha1_file(ref->old_sha1) - || !ref_newer(ref->new_sha1, ref->old_sha1)); - - if (ref->nonfastforward && !ref->force && !force_update) { - ref->status = REF_STATUS_REJECT_NONFASTFORWARD; - continue; + !is_null_sha1(ref->old_sha1); + + if (ref->update) { + ref->nonfastforward = + !has_sha1_file(ref->old_sha1) + || !ref_newer(ref->new_sha1, ref->old_sha1); + + if (!prefixcmp(ref->name, "refs/tags/")) { + ref->requires_force = 1; + if (!force_ref_update) { + ref->status = REF_STATUS_REJECT_ALREADY_EXISTS; + continue; + } + } else if (ref->nonfastforward) { + ref->requires_force = 1; + if (!force_ref_update) { + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + continue; + } + } } } } @@ -1370,6 +1384,16 @@ int branch_merge_matches(struct branch *branch, return refname_match(branch->merge[i]->src, refname, ref_fetch_rules); } +static int ignore_symref_update(const char *refname) +{ + unsigned char sha1[20]; + int flag; + + if (!resolve_ref_unsafe(refname, sha1, 0, &flag)) + return 0; /* non-existing refs are OK */ + return (flag & REF_ISSYMREF); +} + static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { @@ -1383,7 +1407,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, if (strchr(ref->name, '^')) continue; /* a dereference item */ if (match_name_with_pattern(refspec->src, ref->name, - refspec->dst, &expn_name)) { + refspec->dst, &expn_name) && + !ignore_symref_update(expn_name)) { struct ref *cpy = copy_ref(ref); cpy->peer_ref = alloc_ref(expn_name); @@ -1458,8 +1483,8 @@ int get_fetch_map(const struct ref *remote_refs, for (rmp = &ref_map; *rmp; ) { if ((*rmp)->peer_ref) { - if (check_refname_format((*rmp)->peer_ref->name + 5, - REFNAME_ALLOW_ONELEVEL)) { + if (prefixcmp((*rmp)->peer_ref->name, "refs/") || + check_refname_format((*rmp)->peer_ref->name, 0)) { struct ref *ignore = *rmp; error("* Ignoring funny ref '%s' locally", (*rmp)->peer_ref->name); @@ -1627,13 +1652,16 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base = branch->merge[0]->dst; base = shorten_unambiguous_ref(base, 0); - if (!num_theirs) + if (!num_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); - else if (!num_ours) + if (advice_status_hints) + strbuf_addf(sb, + _(" (use \"git push\" to publish your local commits)\n")); + } else if (!num_ours) { strbuf_addf(sb, Q_("Your branch is behind '%s' by %d commit, " "and can be fast-forwarded.\n", @@ -1641,7 +1669,10 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) "and can be fast-forwarded.\n", num_theirs), base, num_theirs); - else + if (advice_status_hints) + strbuf_addf(sb, + _(" (use \"git pull\" to update your local branch)\n")); + } else { strbuf_addf(sb, Q_("Your branch and '%s' have diverged,\n" "and have %d and %d different commit each, " @@ -1651,6 +1682,10 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) "respectively.\n", num_theirs), base, num_ours, num_theirs); + if (advice_status_hints) + strbuf_addf(sb, + _(" (use \"git pull\" to merge the remote branch into yours)\n")); + } return 1; } |