return 0;
}
-static inline int is_forwardable(struct ref* ref)
-{
- struct object *o;
-
- if (!prefixcmp(ref->name, "refs/tags/"))
- return 0;
-
- /* old object must be a commit */
- o = parse_object(ref->old_sha1);
- if (!o || o->type != OBJ_COMMIT)
- return 0;
-
- /* new object must be commit-ish */
- o = deref_tag(parse_object(ref->new_sha1), NULL, 0);
- if (!o || o->type != OBJ_COMMIT)
- return 0;
-
- return 1;
-}
-
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update)
{
}
/*
- * The below logic determines whether an individual
- * refspec A:B can be pushed. The push will succeed
- * if any of the following are true:
+ * Decide whether an individual refspec A:B can be
+ * pushed. The push will succeed if any of the
+ * following are true:
*
* (1) the remote reference B does not exist
*
* (2) the remote reference B is being removed (i.e.,
* pushing :B where no source is specified)
*
- * (3) the update meets all fast-forwarding criteria:
- *
- * (a) the destination is not under refs/tags/
- * (b) the old is a commit
- * (c) the new is a descendant of the old
- *
- * NOTE: We must actually have the old object in
- * order to overwrite it in the remote reference,
- * and the new object must be commit-ish. These are
- * implied by (b) and (c) respectively.
+ * (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) it is forced using the +A:B notation, or by
* passing the --force argument
*/
- ref->not_forwardable = !is_forwardable(ref);
-
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->not_forwardable) {
+ if (!prefixcmp(ref->name, "refs/tags/")) {
ref->requires_force = 1;
if (!force_ref_update) {
ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
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)
{
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);
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);
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",
"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, "
"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;
}