Merge branch 'cr/push-force-tag-update'
authorJunio C Hamano <gitster@pobox.com>
Thu, 24 Jan 2013 05:16:49 +0000 (21:16 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 24 Jan 2013 05:16:49 +0000 (21:16 -0800)
Regression fix to stop "git push" complaining "target ref already
exists", when it is not the real reason the command rejected the
request (e.g. non-fast-forward).

* cr/push-force-tag-update:
push: fix "refs/tags/ hierarchy cannot be updated without --force"

1  2 
cache.h
remote.c
diff --combined cache.h
index c257953fa798110e0f4be9258f4f88f2d7952bdb,a942bbd59f87600b597c452f92e9faaf1a7737bf..4022b5ba0a5faca2b3d9d45741fc8ab74bd84267
+++ b/cache.h
@@@ -362,7 -362,6 +362,7 @@@ static inline enum object_type object_t
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
  #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
  #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
 +#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
  
  /*
   * Repository-local GIT_* environment variables
@@@ -474,8 -473,6 +474,8 @@@ extern int index_name_is_other(const st
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 +#define PATHSPEC_ONESTAR 1    /* the pathspec pattern sastisfies GFNM_ONESTAR */
 +
  struct pathspec {
        const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
        int nr;
        struct pathspec_item {
                const char *match;
                int len;
 -              unsigned int use_wildcard:1;
 +              int nowildcard_len;
 +              int flags;
        } *items;
  };
  
@@@ -494,8 -490,6 +494,8 @@@ extern int init_pathspec(struct pathspe
  extern void free_pathspec(struct pathspec *);
  extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
  
 +extern int limit_pathspec_to_literal(void);
 +
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@@ -720,11 -714,10 +720,11 @@@ static inline int is_absolute_path(cons
  }
  int is_directory(const char *);
  const char *real_path(const char *path);
 +const char *real_path_if_valid(const char *path);
  const char *absolute_path(const char *path);
  const char *relative_path(const char *abs, const char *base);
  int normalize_path_copy(char *dst, const char *src);
 -int longest_ancestor_length(const char *path, const char *prefix_list);
 +int longest_ancestor_length(const char *path, struct string_list *prefixes);
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
  int offset_1st_component(const char *path);
@@@ -1011,7 -1004,6 +1011,6 @@@ struct ref 
                requires_force:1,
                merge:1,
                nonfastforward:1,
-               not_forwardable:1,
                update:1,
                deletion:1;
        enum {
@@@ -1148,9 -1140,6 +1147,9 @@@ extern int check_repository_format_vers
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int config_error_nonbool(const char *);
 +#ifdef __GNUC__
 +#define config_error_nonbool(s) (config_error_nonbool(s), -1)
 +#endif
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
  
@@@ -1164,13 -1153,15 +1163,13 @@@ struct config_include_data 
  #define CONFIG_INCLUDE_INIT { 0 }
  extern int git_config_include(const char *name, const char *value, void *data);
  
 -#define IDENT_NAME_GIVEN 01
 -#define IDENT_MAIL_GIVEN 02
 -#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
 -extern int user_ident_explicitly_given;
 -extern int user_ident_sufficiently_given(void);
 +extern int committer_ident_sufficiently_given(void);
 +extern int author_ident_sufficiently_given(void);
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
  extern const char *git_mailmap_file;
 +extern const char *git_mailmap_blob;
  
  /* IO helper functions */
  extern void maybe_flush_or_die(FILE *, const char *);
diff --combined remote.c
index 4b1153f02c715ac0c441110bf5aa83aceb081aa0,d3a1ca233becf76411a4653cee4416ce97ee1217..9e21b1d78755cec7b30224f10c7f8ecbef3c9a49
+++ b/remote.c
@@@ -1279,26 -1279,6 +1279,6 @@@ 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)
  {
                }
  
                /*
-                * 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;
@@@ -1413,16 -1384,6 +1384,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);
@@@ -1512,8 -1472,8 +1483,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);
@@@ -1681,16 -1641,13 +1652,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;
  }