Code

Merge branch 'sp/refspec-match'
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Dec 2007 01:07:10 +0000 (17:07 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Dec 2007 01:07:10 +0000 (17:07 -0800)
* sp/refspec-match:
  refactor fetch's ref matching to use refname_match()
  push: use same rules as git-rev-parse to resolve refspecs
  add refname_match()
  push: support pushing HEAD to real branch name

1  2 
builtin-push.c
cache.h
refs.c
remote.c
sha1_name.c
t/t5510-fetch.sh
t/t5516-fetch-push.sh

diff --combined builtin-push.c
index 41df717f847ecc41bf0a695fa09fbb926b42f720,54fba0e8326cb23508d68dadfe0d31bbff41833c..c8cb63e23840915ecd7445d1fe4a18f9c6cb694d
@@@ -10,7 -10,7 +10,7 @@@
  #include "parse-options.h"
  
  static const char * const push_usage[] = {
 -      "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
 +      "git-push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
        NULL,
  };
  
@@@ -44,6 -44,15 +44,15 @@@ static void set_refspecs(const char **r
                        strcat(tag, refs[i]);
                        ref = tag;
                }
+               if (!strcmp("HEAD", ref)) {
+                       unsigned char sha1_dummy[20];
+                       ref = resolve_ref(ref, sha1_dummy, 1, NULL);
+                       if (!ref)
+                               die("HEAD cannot be resolved.");
+                       if (prefixcmp(ref, "refs/heads/"))
+                               die("HEAD cannot be resolved to branch.");
+                       ref = xstrdup(ref + 11);
+               }
                add_refspec(ref);
        }
  }
@@@ -91,7 -100,6 +100,7 @@@ int cmd_push(int argc, const char **arg
  {
        int flags = 0;
        int all = 0;
 +      int mirror = 0;
        int dry_run = 0;
        int force = 0;
        int tags = 0;
                OPT__VERBOSE(&verbose),
                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
                OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
 +              OPT_BOOLEAN( 0 , "mirror", &mirror, "mirror all refs"),
                OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
                OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
                OPT_BOOLEAN('f', "force", &force, "force updates"),
                add_refspec("refs/tags/*");
        if (all)
                flags |= TRANSPORT_PUSH_ALL;
 +      if (mirror)
 +              flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
  
        if (argc > 0) {
                repo = argv[0];
                set_refspecs(argv + 1, argc - 1);
        }
 -      if ((flags & TRANSPORT_PUSH_ALL) && refspec)
 +      if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
                usage_with_options(push_usage, options);
  
 +      if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
 +                              (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
 +              error("--all and --mirror are incompatible");
 +              usage_with_options(push_usage, options);
 +      }
 +
        return do_push(repo, flags);
  }
diff --combined cache.h
index ed8be062990410875b6a1a4cf8663bd5b8a69879,cb8f3cabbbdc70170249e96bb213c178b29389cc..ff2a1c0778253083bced51f4e4916c2c30caa4b1
+++ b/cache.h
@@@ -192,13 -192,6 +192,13 @@@ enum object_type 
        OBJ_MAX,
  };
  
 +static inline enum object_type object_type(unsigned int mode)
 +{
 +      return S_ISDIR(mode) ? OBJ_TREE :
 +              S_ISGITLINK(mode) ? OBJ_COMMIT :
 +              OBJ_BLOB;
 +}
 +
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@@ -297,7 -290,6 +297,7 @@@ extern int refresh_index(struct index_s
  
  struct lock_file {
        struct lock_file *next;
 +      int fd;
        pid_t owner;
        char on_list;
        char filename[PATH_MAX];
@@@ -423,6 -415,10 +423,10 @@@ extern const char *resolve_ref(const ch
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  
+ extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
+ extern const char *ref_rev_parse_rules[];
+ extern const char *ref_fetch_rules[];
  extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  extern int validate_headref(const char *ref);
  
@@@ -507,20 -503,8 +511,20 @@@ struct ref 
        struct ref *next;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
 -      unsigned char force;
 -      unsigned char merge;
 +      unsigned int force:1,
 +              merge:1,
 +              nonfastforward:1,
 +              deletion:1;
 +      enum {
 +              REF_STATUS_NONE = 0,
 +              REF_STATUS_OK,
 +              REF_STATUS_REJECT_NONFASTFORWARD,
 +              REF_STATUS_REJECT_NODELETE,
 +              REF_STATUS_UPTODATE,
 +              REF_STATUS_REMOTE_REJECT,
 +              REF_STATUS_EXPECTING_REPORT,
 +      } status;
 +      char *remote_status;
        struct ref *peer_ref; /* when renaming */
        char name[FLEX_ARRAY]; /* more */
  };
  #define REF_HEADS     (1u << 1)
  #define REF_TAGS      (1u << 2)
  
 +extern struct ref *find_ref_by_name(struct ref *list, const char *name);
 +
  #define CONNECT_VERBOSE       (1u << 0)
 -extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
 +extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  extern int get_ack(int fd, unsigned char *result_sha1);
@@@ -578,7 -560,6 +582,7 @@@ extern int git_config_bool(const char *
  extern int git_config_set(const char *, const char *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
 +extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value);
  
  #define MAX_GITNAME (1000)
@@@ -620,7 -601,7 +624,7 @@@ extern void alloc_report(void)
  
  /* trace.c */
  extern void trace_printf(const char *format, ...);
 -extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
 +extern void trace_argv_printf(const char **argv, const char *format, ...);
  
  /* convert.c */
  /* returns 1 if *dst was used */
diff --combined refs.c
index 54ec98d153889f40313dba9a5ee8f07ddd0e160a,6a04a667bbb84b13f895ea95f1fadc5e2cb1bb33..3e6e98c5eb20fc7a365d25b3abc275644b4c26f9
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -643,6 -643,37 +643,37 @@@ int check_ref_format(const char *ref
        }
  }
  
+ const char *ref_rev_parse_rules[] = {
+       "%.*s",
+       "refs/%.*s",
+       "refs/tags/%.*s",
+       "refs/heads/%.*s",
+       "refs/remotes/%.*s",
+       "refs/remotes/%.*s/HEAD",
+       NULL
+ };
+ const char *ref_fetch_rules[] = {
+       "%.*s",
+       "refs/%.*s",
+       "refs/heads/%.*s",
+       NULL
+ };
+ int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
+ {
+       const char **p;
+       const int abbrev_name_len = strlen(abbrev_name);
+       for (p = rules; *p; p++) {
+               if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {
+                       return 1;
+               }
+       }
+       return 0;
+ }
  static struct ref_lock *verify_lock(struct ref_lock *lock,
        const unsigned char *old_sha1, int mustexist)
  {
@@@ -1433,11 -1464,3 +1464,11 @@@ int update_ref(const char *action, cons
        }
        return 0;
  }
 +
 +struct ref *find_ref_by_name(struct ref *list, const char *name)
 +{
 +      for ( ; list; list = list->next)
 +              if (!strcmp(list->name, name))
 +                      return list;
 +      return NULL;
 +}
diff --combined remote.c
index 46e5f04243eb6075a91ac70350555d1aa521f802,48812a713e56bbc74418d2d484ee345b4dc5aa32..3fb0f99b29e7ffd927abf166cfa004c1937aaf60
+++ b/remote.c
@@@ -278,8 -278,6 +278,8 @@@ static int handle_config(const char *ke
        } else if (!strcmp(subkey, ".tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
 +      } else if (!strcmp(subkey, ".proxy")) {
 +              remote->http_proxy = xstrdup(value);
        }
        return 0;
  }
@@@ -419,25 -417,6 +419,6 @@@ int remote_has_url(struct remote *remot
        return 0;
  }
  
- /*
-  * Returns true if, under the matching rules for fetching, name is the
-  * same as the given full name.
-  */
- static int ref_matches_abbrev(const char *name, const char *full)
- {
-       if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD"))
-               return !strcmp(name, full);
-       if (prefixcmp(full, "refs/"))
-               return 0;
-       if (!prefixcmp(name, "heads/") ||
-           !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/"))
-               return !strcmp(name, full + 5);
-       if (prefixcmp(full + 5, "heads/"))
-               return 0;
-       return !strcmp(full + 11, name);
- }
  int remote_find_tracking(struct remote *remote, struct refspec *refspec)
  {
        int find_src = refspec->src == NULL;
@@@ -487,7 -466,7 +468,7 @@@ struct ref *alloc_ref(unsigned namelen
        return ret;
  }
  
 -static struct ref *copy_ref(struct ref *ref)
 +static struct ref *copy_ref(const struct ref *ref)
  {
        struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
        memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
        return ret;
  }
  
 +struct ref *copy_ref_list(const struct ref *ref)
 +{
 +      struct ref *ret = NULL;
 +      struct ref **tail = &ret;
 +      while (ref) {
 +              *tail = copy_ref(ref);
 +              ref = ref->next;
 +              tail = &((*tail)->next);
 +      }
 +      return ret;
 +}
 +
  void free_refs(struct ref *ref)
  {
        struct ref *next;
@@@ -533,10 -500,7 +514,7 @@@ static int count_refspec_match(const ch
                char *name = refs->name;
                int namelen = strlen(name);
  
-               if (namelen < patlen ||
-                   memcmp(name + namelen - patlen, pattern, patlen))
-                       continue;
-               if (namelen != patlen && name[namelen - patlen - 1] != '/')
+               if (!refname_match(pattern, name, ref_rev_parse_rules))
                        continue;
  
                /* A match is "weak" if it is with refs outside
@@@ -698,6 -662,14 +676,6 @@@ static int match_explicit_refs(struct r
        return -errs;
  }
  
 -static struct ref *find_ref_by_name(struct ref *list, const char *name)
 -{
 -      for ( ; list; list = list->next)
 -              if (!strcmp(list->name, name))
 -                      return list;
 -      return NULL;
 -}
 -
  static const struct refspec *check_pattern_match(const struct refspec *rs,
                                                 int rs_nr,
                                                 const struct ref *src)
   * without thinking.
   */
  int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 -             int nr_refspec, char **refspec, int all)
 +             int nr_refspec, const char **refspec, int flags)
  {
        struct refspec *rs =
                parse_ref_spec(nr_refspec, (const char **) refspec);
 +      int send_all = flags & MATCH_REFS_ALL;
 +      int send_mirror = flags & MATCH_REFS_MIRROR;
  
        if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
                return -1;
                        if (!pat)
                                continue;
                }
 -              else if (prefixcmp(src->name, "refs/heads/"))
 +              else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
                        /*
                         * "matching refs"; traditionally we pushed everything
                         * including refs outside refs/heads/ hierarchy, but
                if (dst_peer && dst_peer->peer_ref)
                        /* We're already sending something to this ref. */
                        goto free_name;
 -              if (!dst_peer && !nr_refspec && !all)
 -                      /* Remote doesn't have it, and we have no
 +
 +              if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
 +                      /*
 +                       * Remote doesn't have it, and we have no
                         * explicit pattern, and we don't have
 -                       * --all. */
 +                       * --all nor --mirror.
 +                       */
                        goto free_name;
                if (!dst_peer) {
                        /* Create a new one and link it */
@@@ -818,13 -785,13 +796,13 @@@ int branch_merge_matches(struct branch 
  {
        if (!branch || i < 0 || i >= branch->merge_nr)
                return 0;
-       return ref_matches_abbrev(branch->merge[i]->src, refname);
+       return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
  }
  
 -static struct ref *get_expanded_map(struct ref *remote_refs,
 +static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec *refspec)
  {
 -      struct ref *ref;
 +      const struct ref *ref;
        struct ref *ret = NULL;
        struct ref **tail = &ret;
  
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (!prefixcmp(ref->name, refspec->src)) {
 -                      char *match;
 +                      const char *match;
                        struct ref *cpy = copy_ref(ref);
                        match = ref->name + remote_prefix_len;
  
        return ret;
  }
  
 -static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
 +static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
  {
 -      struct ref *ref;
 +      const struct ref *ref;
        for (ref = refs; ref; ref = ref->next) {
-               if (ref_matches_abbrev(name, ref->name))
+               if (refname_match(name, ref->name, ref_fetch_rules))
                        return ref;
        }
        return NULL;
  }
  
 -struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
 +struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
  {
 -      struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
 +      const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
  
        if (!ref)
                return NULL;
@@@ -898,7 -865,7 +876,7 @@@ static struct ref *get_local_ref(const 
        return ret;
  }
  
 -int get_fetch_map(struct ref *remote_refs,
 +int get_fetch_map(const struct ref *remote_refs,
                  const struct refspec *refspec,
                  struct ref ***tail,
                  int missing_ok)
diff --combined sha1_name.c
index bd8fc05c4b9727ad4d14b20127012b53a9ef90ee,d364244dc4667d963f8a9b50ee01e58e9eac3b3c..13e11645e1fd6b61812cc3e88d2ad6cd42cad9ce
@@@ -239,23 -239,13 +239,13 @@@ static int ambiguous_path(const char *p
        return slash;
  }
  
- static const char *ref_fmt[] = {
-       "%.*s",
-       "refs/%.*s",
-       "refs/tags/%.*s",
-       "refs/heads/%.*s",
-       "refs/remotes/%.*s",
-       "refs/remotes/%.*s/HEAD",
-       NULL
- };
  int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
  {
        const char **p, *r;
        int refs_found = 0;
  
        *ref = NULL;
-       for (p = ref_fmt; *p; p++) {
+       for (p = ref_rev_parse_rules; *p; p++) {
                unsigned char sha1_from_ref[20];
                unsigned char *this_result;
  
@@@ -277,7 -267,7 +267,7 @@@ int dwim_log(const char *str, int len, 
        int logs_found = 0;
  
        *log = NULL;
-       for (p = ref_fmt; *p; p++) {
+       for (p = ref_rev_parse_rules; *p; p++) {
                struct stat st;
                unsigned char hash[20];
                char path[PATH_MAX];
@@@ -610,35 -600,24 +600,35 @@@ static int get_sha1_oneline(const char 
  {
        struct commit_list *list = NULL, *backup = NULL, *l;
        int retval = -1;
 +      char *temp_commit_buffer = NULL;
  
        if (prefix[0] == '!') {
                if (prefix[1] != '!')
                        die ("Invalid search pattern: %s", prefix);
                prefix++;
        }
 -      if (!save_commit_buffer)
 -              return error("Could not expand oneline-name.");
        for_each_ref(handle_one_ref, &list);
        for (l = list; l; l = l->next)
                commit_list_insert(l->item, &backup);
        while (list) {
                char *p;
                struct commit *commit;
 +              enum object_type type;
 +              unsigned long size;
  
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
                parse_object(commit->object.sha1);
 -              if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n")))
 +              if (temp_commit_buffer)
 +                      free(temp_commit_buffer);
 +              if (commit->buffer)
 +                      p = commit->buffer;
 +              else {
 +                      p = read_sha1_file(commit->object.sha1, &type, &size);
 +                      if (!p)
 +                              continue;
 +                      temp_commit_buffer = p;
 +              }
 +              if (!(p = strstr(p, "\n\n")))
                        continue;
                if (!prefixcmp(p + 2, prefix)) {
                        hashcpy(sha1, commit->object.sha1);
                        break;
                }
        }
 +      if (temp_commit_buffer)
 +              free(temp_commit_buffer);
        free_commit_list(list);
        for (l = backup; l; l = l->next)
                clear_commit_marks(l->item, ONELINE_SEEN);
diff --combined t/t5510-fetch.sh
index 35889c0a125fffdfb556ec521d61c18ac4e96c17,20257428eb0a98f639e7d373f1d00ad91d7c16ad..46a9c4d95cf889990f2e6387a9838a55642bbe64
@@@ -95,6 -95,31 +95,31 @@@ test_expect_success 'fetch following ta
  
  '
  
+ test_expect_failure 'fetch must not resolve short tag name' '
+       cd "$D" &&
+       mkdir five &&
+       cd five &&
+       git init &&
+       git fetch .. anno:five
+ '
+ test_expect_failure 'fetch must not resolve short remote name' '
+       cd "$D" &&
+       git-update-ref refs/remotes/six/HEAD HEAD
+       mkdir six &&
+       cd six &&
+       git init &&
+       git fetch .. six:six
+ '
  test_expect_success 'create bundle 1' '
        cd "$D" &&
        echo >file updated again by origin &&
@@@ -215,17 -240,4 +240,17 @@@ test_expect_success 'quoting of a stran
        grep "fatal: '\''a\\\\!'\''b'\''" result
  '
  
 +test_expect_success 'bundle should record HEAD correctly' '
 +
 +      cd "$D" &&
 +      git bundle create bundle5 HEAD master &&
 +      git bundle list-heads bundle5 >actual &&
 +      for h in HEAD refs/heads/master
 +      do
 +              echo "$(git rev-parse --verify $h) $h"
 +      done >expect &&
 +      diff -u expect actual
 +
 +'
 +
  test_done
diff --combined t/t5516-fetch-push.sh
index 987c9d21caf2a3d7653157f5e6d74f0841863c8b,fd5f284e9ae3d83a57f7ccdc74292c939bb0d352..9d2dc33cbd0d1df19b0a9003e545104a982da694
@@@ -145,11 -145,21 +145,21 @@@ test_expect_success 'push with no ambig
  test_expect_success 'push with no ambiguity (2)' '
  
        mk_test remotes/origin/master &&
-       git push testrepo master:master &&
+       git push testrepo master:origin/master &&
        check_push_result $the_commit remotes/origin/master
  
  '
  
+ test_expect_success 'push with colon-less refspec, no ambiguity' '
+       mk_test heads/master heads/t/master &&
+       git branch -f t/master master &&
+       git push testrepo master &&
+       check_push_result $the_commit heads/master &&
+       check_push_result $the_first_commit heads/t/master
+ '
  test_expect_success 'push with weak ambiguity (1)' '
  
        mk_test heads/master remotes/origin/master &&
@@@ -244,11 -254,29 +254,28 @@@ test_expect_success 'push with colon-le
  
  '
  
+ test_expect_success 'push with HEAD' '
+       mk_test heads/master &&
+       git checkout master &&
+       git push testrepo HEAD &&
+       check_push_result $the_commit heads/master
+ '
+ test_expect_success 'push with HEAD nonexisting at remote' '
+       mk_test heads/master &&
+       git checkout -b local master &&
+       git push testrepo HEAD &&
+       check_push_result $the_commit heads/local
+ '
  test_expect_success 'push with dry-run' '
  
        mk_test heads/master &&
 -      cd testrepo &&
 -      old_commit=$(git show-ref -s --verify refs/heads/master) &&
 -      cd .. &&
 +      (cd testrepo &&
 +       old_commit=$(git show-ref -s --verify refs/heads/master)) &&
        git push --dry-run testrepo &&
        check_push_result $old_commit heads/master
  '
  test_expect_success 'push updates local refs' '
  
        rm -rf parent child &&
 -      mkdir parent && cd parent && git init &&
 -              echo one >foo && git add foo && git commit -m one &&
 -      cd .. &&
 -      git clone parent child && cd child &&
 +      mkdir parent &&
 +      (cd parent && git init &&
 +              echo one >foo && git add foo && git commit -m one) &&
 +      git clone parent child &&
 +      (cd child &&
                echo two >foo && git commit -a -m two &&
                git push &&
 -      test $(git rev-parse master) = $(git rev-parse remotes/origin/master)
 +      test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
  
  '
  
  test_expect_success 'push does not update local refs on failure' '
  
        rm -rf parent child &&
 -      mkdir parent && cd parent && git init &&
 +      mkdir parent &&
 +      (cd parent && git init &&
                echo one >foo && git add foo && git commit -m one &&
                echo exit 1 >.git/hooks/pre-receive &&
 -              chmod +x .git/hooks/pre-receive &&
 -      cd .. &&
 -      git clone parent child && cd child &&
 -              echo two >foo && git commit -a -m two || exit 1
 -              git push && exit 1
 -      test $(git rev-parse master) != $(git rev-parse remotes/origin/master)
 +              chmod +x .git/hooks/pre-receive) &&
 +      git clone parent child &&
 +      (cd child &&
 +              echo two >foo && git commit -a -m two &&
 +              ! git push &&
 +              test $(git rev-parse master) != \
 +                      $(git rev-parse remotes/origin/master))
 +
 +'
 +
 +test_expect_success 'allow deleting an invalid remote ref' '
 +
 +      pwd &&
 +      rm -f testrepo/.git/objects/??/* &&
 +      git push testrepo :refs/heads/master &&
 +      (cd testrepo && ! git rev-parse --verify refs/heads/master)
  
  '