Merge branch 'jc/fetch-ignore-symref'
authorJunio C Hamano <gitster@pobox.com>
Sun, 6 Jan 2013 07:41:37 +0000 (23:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 6 Jan 2013 07:41:37 +0000 (23:41 -0800)
Avoid false error from an attempt to update local symbolic ref via
fetch.

* jc/fetch-ignore-symref:
fetch: ignore wildcarded refspecs that update local symbolic refs

1  2 
remote.c
diff --combined remote.c
index 225073909a6e50a393fb9cc58791bb0f260e041b,a72748ce8e1184ade91f032973d65786a7644906..4b1153f02c715ac0c441110bf5aa83aceb081aa0
+++ b/remote.c
@@@ -1279,34 -1279,12 +1279,34 @@@ int match_push_refs(struct ref *src, st
        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)
  {
        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)
                        continue;
                }
  
 -              /* This part determines what can overwrite what.
 -               * The rules are:
 +              /*
 +               * The below logic determines 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
                 *
 -               * (0) you can always use --force or +A:B notation to
 -               *     selectively force individual ref pairs.
 +               * (2) the remote reference B is being removed (i.e.,
 +               *     pushing :B where no source is specified)
                 *
 -               * (1) if the old thing does not exist, it is OK.
 +               * (3) the update meets all fast-forwarding criteria:
                 *
 -               * (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.
 +               *     (a) the destination is not under refs/tags/
 +               *     (b) the old is a commit
 +               *     (c) the new is a descendant of the old
                 *
 -               * (3) if both new and old are commit-ish, and new is a
 -               *     descendant of old, it is OK.
 +               *     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.
                 *
 -               * (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->deletion &&
 -                      !is_null_sha1(ref->old_sha1) &&
 -                      (!has_sha1_file(ref->old_sha1)
 -                        || !ref_newer(ref->new_sha1, ref->old_sha1));
 +              ref->not_forwardable = !is_forwardable(ref);
  
 -              if (ref->nonfastforward && !ref->force && !force_update) {
 -                      ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
 -                      continue;
 +              ref->update =
 +                      !ref->deletion &&
 +                      !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 (ref->not_forwardable) {
 +                              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;
 +                              }
 +                      }
                }
        }
  }
@@@ -1413,6 -1370,16 +1413,16 @@@ int branch_merge_matches(struct 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)
  {
                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);
@@@ -1501,8 -1469,8 +1512,8 @@@ int get_fetch_map(const struct ref *rem
  
        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);
@@@ -1670,16 -1638,13 +1681,16 @@@ int format_tracking_info(struct branch 
  
        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;
  }