Merge branch 'mh/check-ref-format-3'
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Oct 2011 22:56:18 +0000 (15:56 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Oct 2011 22:56:18 +0000 (15:56 -0700)
* mh/check-ref-format-3: (23 commits)
add_ref(): verify that the refname is formatted correctly
resolve_ref(): expand documentation
resolve_ref(): also treat a too-long SHA1 as invalid
resolve_ref(): emit warnings for improperly-formatted references
resolve_ref(): verify that the input refname has the right format
remote: avoid passing NULL to read_ref()
remote: use xstrdup() instead of strdup()
resolve_ref(): do not follow incorrectly-formatted symbolic refs
resolve_ref(): extract a function get_packed_ref()
resolve_ref(): turn buffer into a proper string as soon as possible
resolve_ref(): only follow a symlink that contains a valid, normalized refname
resolve_ref(): use prefixcmp()
resolve_ref(): explicitly fail if a symlink is not readable
Change check_refname_format() to reject unnormalized refnames
Inline function refname_format_print()
Make collapse_slashes() allocate memory for its result
Do not allow ".lock" at the end of any refname component
Refactor check_refname_format()
Change check_ref_format() to take a flags argument
Change bad_ref_char() to return a boolean value
...

1  2 
builtin/checkout.c
builtin/fetch-pack.c
builtin/receive-pack.c
builtin/tag.c
cache.h
refs.c
sha1_name.c
transport.c
diff --combined builtin/checkout.c
index 8210ccc13d210dbf2a6cf08ae368081a5339e6db,574d2b61813335045773a5515b8eaf2f44788a6a..04df4d786ecf9fe541b62e89d85f6b0ae684b439
@@@ -19,7 -19,6 +19,7 @@@
  #include "ll-merge.h"
  #include "resolve-undo.h"
  #include "submodule.h"
 +#include "argv-array.h"
  
  static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@@ -589,12 -588,24 +589,12 @@@ static void update_refs_for_switch(stru
                report_tracking(new);
  }
  
 -struct rev_list_args {
 -      int argc;
 -      int alloc;
 -      const char **argv;
 -};
 -
 -static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
 -{
 -      ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
 -      args->argv[args->argc++] = s;
 -}
 -
  static int add_one_ref_to_rev_list_arg(const char *refname,
                                       const unsigned char *sha1,
                                       int flags,
                                       void *cb_data)
  {
 -      add_one_rev_list_arg(cb_data, refname);
 +      argv_array_push(cb_data, refname);
        return 0;
  }
  
@@@ -674,14 -685,15 +674,14 @@@ static void suggest_reattach(struct com
   */
  static void orphaned_commit_warning(struct commit *commit)
  {
 -      struct rev_list_args args = { 0, 0, NULL };
 +      struct argv_array args = ARGV_ARRAY_INIT;
        struct rev_info revs;
  
 -      add_one_rev_list_arg(&args, "(internal)");
 -      add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
 -      add_one_rev_list_arg(&args, "--not");
 +      argv_array_push(&args, "(internal)");
 +      argv_array_push(&args, sha1_to_hex(commit->object.sha1));
 +      argv_array_push(&args, "--not");
        for_each_ref(add_one_ref_to_rev_list_arg, &args);
 -      add_one_rev_list_arg(&args, "--");
 -      add_one_rev_list_arg(&args, NULL);
 +      argv_array_push(&args, "--");
  
        init_revisions(&revs, NULL);
        if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
        else
                describe_detached_head(_("Previous HEAD position was"), commit);
  
 +      argv_array_clear(&args);
        clear_commit_marks(commit, -1);
        for_each_ref(clear_commit_marks_from_one_ref, NULL);
  }
@@@ -871,7 -882,7 +871,7 @@@ static int parse_branchname_arg(int arg
        new->name = arg;
        setup_branch_path(new);
  
-       if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+       if (!check_refname_format(new->path, 0) &&
            resolve_ref(new->path, branch_rev, 1, NULL))
                hashcpy(rev, branch_rev);
        else
@@@ -1062,8 -1073,7 +1062,8 @@@ int cmd_checkout(int argc, const char *
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
  
 -              opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, !!opts.new_branch_force);
 +              opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
 +                                                           !!opts.new_branch_force, 0);
  
                strbuf_release(&buf);
        }
diff --combined builtin/fetch-pack.c
index c8bf9b85b07690dce8af5e093e02994ae5c510be,b51e47837e71278c70e49a96e989fbab6ef98364..c6bc8eb0aa6f5a6bc35c69e7893118a17813db7d
@@@ -15,9 -15,7 +15,9 @@@ static int transfer_unpack_limit = -1
  static int fetch_unpack_limit = -1;
  static int unpack_limit = 100;
  static int prefer_ofs_delta = 1;
 -static int no_done = 0;
 +static int no_done;
 +static int fetch_fsck_objects = -1;
 +static int transfer_fsck_objects = -1;
  static struct fetch_pack_args args = {
        /* .uploadpack = */ "git-upload-pack",
  };
@@@ -546,7 -544,7 +546,7 @@@ static void filter_refs(struct ref **re
        for (ref = *refs; ref; ref = next) {
                next = ref->next;
                if (!memcmp(ref->name, "refs/", 5) &&
-                   check_ref_format(ref->name + 5))
+                   check_refname_format(ref->name + 5, 0))
                        ; /* trash */
                else if (args.fetch_all &&
                         (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
@@@ -736,12 -734,6 +736,12 @@@ static int get_pack(int xd[2], char **p
        }
        if (*hdr_arg)
                *av++ = hdr_arg;
 +      if (fetch_fsck_objects >= 0
 +          ? fetch_fsck_objects
 +          : transfer_fsck_objects >= 0
 +          ? transfer_fsck_objects
 +          : 0)
 +              *av++ = "--strict";
        *av++ = NULL;
  
        cmd.in = demux.out;
@@@ -861,16 -853,6 +861,16 @@@ static int fetch_pack_config(const cha
                return 0;
        }
  
 +      if (!strcmp(var, "fetch.fsckobjects")) {
 +              fetch_fsck_objects = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "transfer.fsckobjects")) {
 +              transfer_fsck_objects = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, cb);
  }
  
diff --combined builtin/receive-pack.c
index c1c5bac79ef21c1d1e80605f3ced34f0a5f73eac,0600efacd19f1a64c00068f63a2b86ee91dff429..e2d3f94661ffd89e08a1bf0e52ff07b366612b65
@@@ -11,7 -11,6 +11,7 @@@
  #include "transport.h"
  #include "string-list.h"
  #include "sha1-array.h"
 +#include "connected.h"
  
  static const char receive_pack_usage[] = "git receive-pack <git-dir>";
  
@@@ -26,8 -25,7 +26,8 @@@ static int deny_deletes
  static int deny_non_fast_forwards;
  static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
  static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
 -static int receive_fsck_objects;
 +static int receive_fsck_objects = -1;
 +static int transfer_fsck_objects = -1;
  static int receive_unpack_limit = -1;
  static int transfer_unpack_limit = -1;
  static int unpack_limit = 100;
@@@ -81,11 -79,6 +81,11 @@@ static int receive_pack_config(const ch
                return 0;
        }
  
 +      if (strcmp(var, "transfer.fsckobjects") == 0) {
 +              transfer_fsck_objects = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        if (!strcmp(var, "receive.denycurrentbranch")) {
                deny_current_branch = parse_deny_action(var, value);
                return 0;
@@@ -212,15 -205,21 +212,15 @@@ static int copy_to_sideband(int in, in
        return 0;
  }
  
 -static int run_receive_hook(struct command *commands, const char *hook_name)
 +typedef int (*feed_fn)(void *, const char **, size_t *);
 +static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
  {
 -      static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
 -      struct command *cmd;
        struct child_process proc;
        struct async muxer;
        const char *argv[2];
 -      int have_input = 0, code;
 -
 -      for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
 -              if (!cmd->error_string)
 -                      have_input = 1;
 -      }
 +      int code;
  
 -      if (!have_input || access(hook_name, X_OK) < 0)
 +      if (access(hook_name, X_OK) < 0)
                return 0;
  
        argv[0] = hook_name;
                return code;
        }
  
 -      for (cmd = commands; cmd; cmd = cmd->next) {
 -              if (!cmd->error_string) {
 -                      size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
 -                              sha1_to_hex(cmd->old_sha1),
 -                              sha1_to_hex(cmd->new_sha1),
 -                              cmd->ref_name);
 -                      if (write_in_full(proc.in, buf, n) != n)
 -                              break;
 -              }
 +      while (1) {
 +              const char *buf;
 +              size_t n;
 +              if (feed(feed_state, &buf, &n))
 +                      break;
 +              if (write_in_full(proc.in, buf, n) != n)
 +                      break;
        }
        close(proc.in);
        if (use_sideband)
        return finish_command(&proc);
  }
  
 +struct receive_hook_feed_state {
 +      struct command *cmd;
 +      struct strbuf buf;
 +};
 +
 +static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
 +{
 +      struct receive_hook_feed_state *state = state_;
 +      struct command *cmd = state->cmd;
 +
 +      while (cmd && cmd->error_string)
 +              cmd = cmd->next;
 +      if (!cmd)
 +              return -1; /* EOF */
 +      strbuf_reset(&state->buf);
 +      strbuf_addf(&state->buf, "%s %s %s\n",
 +                  sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
 +                  cmd->ref_name);
 +      state->cmd = cmd->next;
 +      if (bufp) {
 +              *bufp = state->buf.buf;
 +              *sizep = state->buf.len;
 +      }
 +      return 0;
 +}
 +
 +static int run_receive_hook(struct command *commands, const char *hook_name)
 +{
 +      struct receive_hook_feed_state state;
 +      int status;
 +
 +      strbuf_init(&state.buf, 0);
 +      state.cmd = commands;
 +      if (feed_receive_hook(&state, NULL, NULL))
 +              return 0;
 +      state.cmd = commands;
 +      status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
 +      strbuf_release(&state.buf);
 +      return status;
 +}
 +
  static int run_update_hook(struct command *cmd)
  {
        static const char update_hook[] = "hooks/update";
@@@ -396,7 -356,7 +396,7 @@@ static const char *update(struct comman
        struct ref_lock *lock;
  
        /* only refs/... are allowed */
-       if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
+       if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
                rp_error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
@@@ -619,43 -579,6 +619,43 @@@ static void check_aliased_updates(struc
        string_list_clear(&ref_list, 0);
  }
  
 +static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
 +{
 +      struct command **cmd_list = cb_data;
 +      struct command *cmd = *cmd_list;
 +
 +      if (!cmd)
 +              return -1; /* end of list */
 +      *cmd_list = NULL; /* this returns only one */
 +      hashcpy(sha1, cmd->new_sha1);
 +      return 0;
 +}
 +
 +static void set_connectivity_errors(struct command *commands)
 +{
 +      struct command *cmd;
 +
 +      for (cmd = commands; cmd; cmd = cmd->next) {
 +              struct command *singleton = cmd;
 +              if (!check_everything_connected(command_singleton_iterator,
 +                                              0, &singleton))
 +                      continue;
 +              cmd->error_string = "missing necessary objects";
 +      }
 +}
 +
 +static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
 +{
 +      struct command **cmd_list = cb_data;
 +      struct command *cmd = *cmd_list;
 +
 +      if (!cmd)
 +              return -1; /* end of list */
 +      *cmd_list = cmd->next;
 +      hashcpy(sha1, cmd->new_sha1);
 +      return 0;
 +}
 +
  static void execute_commands(struct command *commands, const char *unpacker_error)
  {
        struct command *cmd;
                return;
        }
  
 +      cmd = commands;
 +      if (check_everything_connected(iterate_receive_command_list,
 +                                     0, &cmd))
 +              set_connectivity_errors(commands);
 +
        if (run_receive_hook(commands, pre_receive_hook)) {
                for (cmd = commands; cmd; cmd = cmd->next)
                        cmd->error_string = "pre-receive hook declined";
@@@ -756,11 -674,6 +756,11 @@@ static const char *unpack(void
        struct pack_header hdr;
        const char *hdr_err;
        char hdr_arg[38];
 +      int fsck_objects = (receive_fsck_objects >= 0
 +                          ? receive_fsck_objects
 +                          : transfer_fsck_objects >= 0
 +                          ? transfer_fsck_objects
 +                          : 0);
  
        hdr_err = parse_pack_header(&hdr);
        if (hdr_err)
                int code, i = 0;
                const char *unpacker[4];
                unpacker[i++] = "unpack-objects";
 -              if (receive_fsck_objects)
 +              if (fsck_objects)
                        unpacker[i++] = "--strict";
                unpacker[i++] = hdr_arg;
                unpacker[i++] = NULL;
  
                keeper[i++] = "index-pack";
                keeper[i++] = "--stdin";
 -              if (receive_fsck_objects)
 +              if (fsck_objects)
                        keeper[i++] = "--strict";
                keeper[i++] = "--fix-thin";
                keeper[i++] = hdr_arg;
diff --combined builtin/tag.c
index 9d89616863f4b9102706c726c51e36acddef8997,48be74567805c8710d5f15e048fcbb350d892ea6..9b6fd95494fd80240f88d49224199b7e621717e4
@@@ -407,12 -407,12 +407,12 @@@ static int parse_msg_arg(const struct o
  static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
  {
        if (name[0] == '-')
-               return CHECK_REF_FORMAT_ERROR;
+               return -1;
  
        strbuf_reset(sb);
        strbuf_addf(sb, "refs/tags/%s", name);
  
-       return check_ref_format(sb->buf);
+       return check_refname_format(sb->buf, 0);
  }
  
  int cmd_tag(int argc, const char **argv, const char *prefix)
        struct msg_arg msg = { 0, STRBUF_INIT };
        struct commit_list *with_commit = NULL;
        struct option options[] = {
 -              OPT_BOOLEAN('l', NULL, &list, "list tag names"),
 +              OPT_BOOLEAN('l', "list", &list, "list tag names"),
                { OPTION_INTEGER, 'n', NULL, &lines, "n",
                                "print <n> lines of each tag message",
                                PARSE_OPT_OPTARG, NULL, 1 },
 -              OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
 -              OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
 +              OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
 +              OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
  
                OPT_GROUP("Tag creation options"),
 -              OPT_BOOLEAN('a', NULL, &annotate,
 +              OPT_BOOLEAN('a', "annotate", &annotate,
                                        "annotated tag, needs a message"),
 -              OPT_CALLBACK('m', NULL, &msg, "message",
 +              OPT_CALLBACK('m', "message", &msg, "message",
                             "tag message", parse_msg_arg),
 -              OPT_FILENAME('F', NULL, &msgfile, "read message from file"),
 -              OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
 -              OPT_STRING('u', NULL, &keyid, "key-id",
 +              OPT_FILENAME('F', "file", &msgfile, "read message from file"),
 +              OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
 +              OPT_STRING('u', "local-user", &keyid, "key-id",
                                        "use another key to sign the tag"),
                OPT__FORCE(&force, "replace the tag if exists"),
  
diff --combined cache.h
index 1aa682d83dfb678f43930a4fa7d816c516d8ee1d,2d82f3956f50a3e5c30ea88fd97d65c29838b0c5..e39e1600189a6daae0eb29e165b08949f66bae9f
+++ b/cache.h
@@@ -439,12 -439,12 +439,12 @@@ extern const char *get_git_namespace(vo
  extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
  extern const char *read_gitfile(const char *path);
 +extern const char *resolve_gitdir(const char *suspect);
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 -extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -819,10 -819,51 +819,51 @@@ static inline int get_sha1_with_context
  {
        return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
  }
+ /*
+  * Try to read a SHA1 in hexadecimal format from the 40 characters
+  * starting at hex.  Write the 20-byte result to sha1 in binary form.
+  * Return 0 on success.  Reading stops if a NUL is encountered in the
+  * input, so it is safe to pass this function an arbitrary
+  * null-terminated string.
+  */
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
- extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+ /*
+  * Resolve a reference, recursively following symbolic refererences.
+  *
+  * Store the referred-to object's name in sha1 and return the name of
+  * the non-symbolic reference that ultimately pointed at it.  The
+  * return value, if not NULL, is a pointer into either a static buffer
+  * or the input ref.
+  *
+  * If the reference cannot be resolved to an object, the behavior
+  * depends on the "reading" argument:
+  *
+  * - If reading is set, return NULL.
+  *
+  * - If reading is not set, clear sha1 and return the name of the last
+  *   reference name in the chain, which will either be a non-symbolic
+  *   reference or an undefined reference.  If this is a prelude to
+  *   "writing" to the ref, the return value is the name of the ref
+  *   that will actually be created or changed.
+  *
+  * If flag is non-NULL, set the value that it points to the
+  * combination of REF_ISPACKED (if the reference was found among the
+  * packed references) and REF_ISSYMREF (if the initial reference was a
+  * symbolic reference).
+  *
+  * If ref is not a properly-formatted, normalized reference, return
+  * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
+  * give up and return NULL.
+  *
+  * errno is sometimes set on errors, but not always.
+  */
+ extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
  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 interpret_branch_name(const char *str, struct strbuf *);
@@@ -1076,11 -1117,9 +1117,11 @@@ extern int git_config_bool(const char *
  extern int git_config_maybe_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
 +extern int git_config_set_in_file(const char *, const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
 +extern int git_config_set_multivar_in_file(const char *, 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, void *cb);
diff --combined refs.c
index 6e9588bf161a895f4f9adc750ba9f7ac53b84b2b,832a52f7818369bca969d49317718714a5bcabac..8c69243b2a04224e75ca34bed404782fbb4b9885
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -56,6 -56,8 +56,8 @@@ static struct ref_list *add_ref(const c
        entry = xmalloc(sizeof(struct ref_list) + len);
        hashcpy(entry->sha1, sha1);
        hashclr(entry->peeled);
+       if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+               die("Reference has invalid format: '%s'", name);
        memcpy(entry->name, name, len);
        entry->flag = flag;
        entry->next = list;
@@@ -153,15 -155,11 +155,15 @@@ static struct ref_list *sort_ref_list(s
   * when doing a full libification.
   */
  static struct cached_refs {
 +      struct cached_refs *next;
        char did_loose;
        char did_packed;
        struct ref_list *loose;
        struct ref_list *packed;
 -} cached_refs, submodule_refs;
 +      /* The submodule name, or "" for the main repo. */
 +      char name[FLEX_ARRAY];
 +} *cached_refs;
 +
  static struct ref_list *current_ref;
  
  static struct ref_list *extra_refs;
@@@ -175,8 -173,10 +177,8 @@@ static void free_ref_list(struct ref_li
        }
  }
  
 -static void invalidate_cached_refs(void)
 +static void clear_cached_refs(struct cached_refs *ca)
  {
 -      struct cached_refs *ca = &cached_refs;
 -
        if (ca->did_loose && ca->loose)
                free_ref_list(ca->loose);
        if (ca->did_packed && ca->packed)
        ca->did_loose = ca->did_packed = 0;
  }
  
 -static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
 +static struct cached_refs *create_cached_refs(const char *submodule)
 +{
 +      int len;
 +      struct cached_refs *refs;
 +      if (!submodule)
 +              submodule = "";
 +      len = strlen(submodule) + 1;
 +      refs = xmalloc(sizeof(struct cached_refs) + len);
 +      refs->next = NULL;
 +      refs->did_loose = refs->did_packed = 0;
 +      refs->loose = refs->packed = NULL;
 +      memcpy(refs->name, submodule, len);
 +      return refs;
 +}
 +
 +/*
 + * Return a pointer to a cached_refs for the specified submodule. For
 + * the main repository, use submodule==NULL. The returned structure
 + * will be allocated and initialized but not necessarily populated; it
 + * should not be freed.
 + */
 +static struct cached_refs *get_cached_refs(const char *submodule)
 +{
 +      struct cached_refs *refs = cached_refs;
 +      if (!submodule)
 +              submodule = "";
 +      while (refs) {
 +              if (!strcmp(submodule, refs->name))
 +                      return refs;
 +              refs = refs->next;
 +      }
 +
 +      refs = create_cached_refs(submodule);
 +      refs->next = cached_refs;
 +      cached_refs = refs;
 +      return refs;
 +}
 +
 +static void invalidate_cached_refs(void)
 +{
 +      struct cached_refs *refs = cached_refs;
 +      while (refs) {
 +              clear_cached_refs(refs);
 +              refs = refs->next;
 +      }
 +}
 +
 +static struct ref_list *read_packed_refs(FILE *f)
  {
        struct ref_list *list = NULL;
        struct ref_list *last = NULL;
                    !get_sha1_hex(refline + 1, sha1))
                        hashcpy(last->peeled, sha1);
        }
 -      cached_refs->packed = sort_ref_list(list);
 +      return sort_ref_list(list);
  }
  
  void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
@@@ -280,20 -233,23 +282,20 @@@ void clear_extra_refs(void
  
  static struct ref_list *get_packed_refs(const char *submodule)
  {
 -      const char *packed_refs_file;
 -      struct cached_refs *refs;
 +      struct cached_refs *refs = get_cached_refs(submodule);
  
 -      if (submodule) {
 -              packed_refs_file = git_path_submodule(submodule, "packed-refs");
 -              refs = &submodule_refs;
 -              free_ref_list(refs->packed);
 -      } else {
 -              packed_refs_file = git_path("packed-refs");
 -              refs = &cached_refs;
 -      }
 +      if (!refs->did_packed) {
 +              const char *packed_refs_file;
 +              FILE *f;
  
 -      if (!refs->did_packed || submodule) {
 -              FILE *f = fopen(packed_refs_file, "r");
 +              if (submodule)
 +                      packed_refs_file = git_path_submodule(submodule, "packed-refs");
 +              else
 +                      packed_refs_file = git_path("packed-refs");
 +              f = fopen(packed_refs_file, "r");
                refs->packed = NULL;
                if (f) {
 -                      read_packed_refs(f, refs);
 +                      refs->packed = read_packed_refs(f);
                        fclose(f);
                }
                refs->did_packed = 1;
@@@ -404,13 -360,17 +406,13 @@@ void warn_dangling_symref(FILE *fp, con
  
  static struct ref_list *get_loose_refs(const char *submodule)
  {
 -      if (submodule) {
 -              free_ref_list(submodule_refs.loose);
 -              submodule_refs.loose = get_ref_dir(submodule, "refs", NULL);
 -              return submodule_refs.loose;
 -      }
 +      struct cached_refs *refs = get_cached_refs(submodule);
  
 -      if (!cached_refs.did_loose) {
 -              cached_refs.loose = get_ref_dir(NULL, "refs", NULL);
 -              cached_refs.did_loose = 1;
 +      if (!refs->did_loose) {
 +              refs->loose = get_ref_dir(submodule, "refs", NULL);
 +              refs->did_loose = 1;
        }
 -      return cached_refs.loose;
 +      return refs->loose;
  }
  
  /* We allow "recursive" symbolic refs. Only within reason, though */
  static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)
  {
        FILE *f;
 -      struct cached_refs refs;
 +      struct ref_list *packed_refs;
        struct ref_list *ref;
        int retval;
  
        f = fopen(name, "r");
        if (!f)
                return -1;
 -      read_packed_refs(f, &refs);
 +      packed_refs = read_packed_refs(f);
        fclose(f);
 -      ref = refs.packed;
 +      ref = packed_refs;
        retval = -1;
        while (ref) {
                if (!strcmp(ref->name, refname)) {
                }
                ref = ref->next;
        }
 -      free_ref_list(refs.packed);
 +      free_ref_list(packed_refs);
        return retval;
  }
  
@@@ -508,29 -468,37 +510,37 @@@ int resolve_gitlink_ref(const char *pat
  }
  
  /*
-  * If the "reading" argument is set, this function finds out what _object_
-  * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
-  * has to exist, and if it is symbolic, it has to point at an existing ref,
-  * because the "read" goes through the symref to the ref it points at.
-  *
-  * The access that is not "reading" may often be "writing", but does not
-  * have to; it can be merely checking _where it leads to_. If it is a
-  * prelude to "writing" to the ref, a write to a symref that points at
-  * yet-to-be-born ref will create the real ref pointed by the symref.
-  * reading=0 allows the caller to check where such a symref leads to.
+  * Try to read ref from the packed references.  On success, set sha1
+  * and return 0; otherwise, return -1.
   */
+ static int get_packed_ref(const char *ref, unsigned char *sha1)
+ {
+       struct ref_list *list = get_packed_refs(NULL);
+       while (list) {
+               if (!strcmp(ref, list->name)) {
+                       hashcpy(sha1, list->sha1);
+                       return 0;
+               }
+               list = list->next;
+       }
+       return -1;
+ }
  const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
  {
        int depth = MAXDEPTH;
        ssize_t len;
        char buffer[256];
        static char ref_buffer[256];
+       char path[PATH_MAX];
  
        if (flag)
                *flag = 0;
  
+       if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
+               return NULL;
        for (;;) {
-               char path[PATH_MAX];
                struct stat st;
                char *buf;
                int fd;
                        return NULL;
  
                git_snpath(path, sizeof(path), "%s", ref);
-               /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
-                       struct ref_list *list = get_packed_refs(NULL);
-                       while (list) {
-                               if (!strcmp(ref, list->name)) {
-                                       hashcpy(sha1, list->sha1);
-                                       if (flag)
-                                               *flag |= REF_ISPACKED;
-                                       return ref;
-                               }
-                               list = list->next;
+                       if (errno != ENOENT)
+                               return NULL;
+                       /*
+                        * The loose reference file does not exist;
+                        * check for a packed reference.
+                        */
+                       if (!get_packed_ref(ref, sha1)) {
+                               if (flag)
+                                       *flag |= REF_ISPACKED;
+                               return ref;
                        }
-                       if (reading || errno != ENOENT)
+                       /* The reference is not a packed reference, either. */
+                       if (reading) {
                                return NULL;
-                       hashclr(sha1);
-                       return ref;
+                       } else {
+                               hashclr(sha1);
+                               return ref;
+                       }
                }
  
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                if (S_ISLNK(st.st_mode)) {
                        len = readlink(path, buffer, sizeof(buffer)-1);
-                       if (len >= 5 && !memcmp("refs/", buffer, 5)) {
-                               buffer[len] = 0;
+                       if (len < 0)
+                               return NULL;
+                       buffer[len] = 0;
+                       if (!prefixcmp(buffer, "refs/") &&
+                                       !check_refname_format(buffer, 0)) {
                                strcpy(ref_buffer, buffer);
                                ref = ref_buffer;
                                if (flag)
                        return NULL;
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
                close(fd);
+               if (len < 0)
+                       return NULL;
+               while (len && isspace(buffer[len-1]))
+                       len--;
+               buffer[len] = '\0';
  
                /*
                 * Is it a symbolic ref?
                 */
-               if (len < 4 || memcmp("ref:", buffer, 4))
+               if (prefixcmp(buffer, "ref:"))
                        break;
                buf = buffer + 4;
-               len -= 4;
-               while (len && isspace(*buf))
-                       buf++, len--;
-               while (len && isspace(buf[len-1]))
-                       len--;
-               buf[len] = 0;
-               memcpy(ref_buffer, buf, len + 1);
-               ref = ref_buffer;
+               while (isspace(*buf))
+                       buf++;
+               if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
+                       warning("symbolic reference in %s is formatted incorrectly",
+                               path);
+                       return NULL;
+               }
+               ref = strcpy(ref_buffer, buf);
                if (flag)
                        *flag |= REF_ISSYMREF;
        }
-       if (len < 40 || get_sha1_hex(buffer, sha1))
+       /* Please note that FETCH_HEAD has a second line containing other data. */
+       if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
+               warning("reference in %s is formatted incorrectly", path);
                return NULL;
+       }
        return ref;
  }
  
@@@ -902,70 -885,87 +927,87 @@@ int for_each_rawref(each_ref_fn fn, voi
   * - it contains a "\" (backslash)
   */
  
+ /* Return true iff ch is not allowed in reference names. */
  static inline int bad_ref_char(int ch)
  {
        if (((unsigned) ch) <= ' ' || ch == 0x7f ||
            ch == '~' || ch == '^' || ch == ':' || ch == '\\')
                return 1;
        /* 2.13 Pattern Matching Notation */
-       if (ch == '?' || ch == '[') /* Unsupported */
+       if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
                return 1;
-       if (ch == '*') /* Supported at the end */
-               return 2;
        return 0;
  }
  
- int check_ref_format(const char *ref)
+ /*
+  * Try to read one refname component from the front of ref.  Return
+  * the length of the component found, or -1 if the component is not
+  * legal.
+  */
+ static int check_refname_component(const char *ref, int flags)
  {
-       int ch, level, bad_type, last;
-       int ret = CHECK_REF_FORMAT_OK;
-       const char *cp = ref;
-       level = 0;
-       while (1) {
-               while ((ch = *cp++) == '/')
-                       ; /* tolerate duplicated slashes */
-               if (!ch)
-                       /* should not end with slashes */
-                       return CHECK_REF_FORMAT_ERROR;
-               /* we are at the beginning of the path component */
-               if (ch == '.')
-                       return CHECK_REF_FORMAT_ERROR;
-               bad_type = bad_ref_char(ch);
-               if (bad_type) {
-                       if (bad_type == 2 && (!*cp || *cp == '/') &&
-                           ret == CHECK_REF_FORMAT_OK)
-                               ret = CHECK_REF_FORMAT_WILDCARD;
-                       else
-                               return CHECK_REF_FORMAT_ERROR;
-               }
+       const char *cp;
+       char last = '\0';
  
+       for (cp = ref; ; cp++) {
+               char ch = *cp;
+               if (ch == '\0' || ch == '/')
+                       break;
+               if (bad_ref_char(ch))
+                       return -1; /* Illegal character in refname. */
+               if (last == '.' && ch == '.')
+                       return -1; /* Refname contains "..". */
+               if (last == '@' && ch == '{')
+                       return -1; /* Refname contains "@{". */
                last = ch;
-               /* scan the rest of the path component */
-               while ((ch = *cp++) != 0) {
-                       bad_type = bad_ref_char(ch);
-                       if (bad_type)
-                               return CHECK_REF_FORMAT_ERROR;
-                       if (ch == '/')
-                               break;
-                       if (last == '.' && ch == '.')
-                               return CHECK_REF_FORMAT_ERROR;
-                       if (last == '@' && ch == '{')
-                               return CHECK_REF_FORMAT_ERROR;
-                       last = ch;
-               }
-               level++;
-               if (!ch) {
-                       if (ref <= cp - 2 && cp[-2] == '.')
-                               return CHECK_REF_FORMAT_ERROR;
-                       if (level < 2)
-                               return CHECK_REF_FORMAT_ONELEVEL;
-                       if (has_extension(ref, ".lock"))
-                               return CHECK_REF_FORMAT_ERROR;
-                       return ret;
+       }
+       if (cp == ref)
+               return -1; /* Component has zero length. */
+       if (ref[0] == '.') {
+               if (!(flags & REFNAME_DOT_COMPONENT))
+                       return -1; /* Component starts with '.'. */
+               /*
+                * Even if leading dots are allowed, don't allow "."
+                * as a component (".." is prevented by a rule above).
+                */
+               if (ref[1] == '\0')
+                       return -1; /* Component equals ".". */
+       }
+       if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5))
+               return -1; /* Refname ends with ".lock". */
+       return cp - ref;
+ }
+ int check_refname_format(const char *ref, int flags)
+ {
+       int component_len, component_count = 0;
+       while (1) {
+               /* We are at the start of a path component. */
+               component_len = check_refname_component(ref, flags);
+               if (component_len < 0) {
+                       if ((flags & REFNAME_REFSPEC_PATTERN) &&
+                                       ref[0] == '*' &&
+                                       (ref[1] == '\0' || ref[1] == '/')) {
+                               /* Accept one wildcard as a full refname component. */
+                               flags &= ~REFNAME_REFSPEC_PATTERN;
+                               component_len = 1;
+                       } else {
+                               return -1;
+                       }
                }
+               component_count++;
+               if (ref[component_len] == '\0')
+                       break;
+               /* Skip to next component. */
+               ref += component_len + 1;
        }
+       if (ref[component_len - 1] == '.')
+               return -1; /* Refname ends with '.'. */
+       if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
+               return -1; /* Refname has only one component. */
+       return 0;
  }
  
  const char *prettify_refname(const char *name)
@@@ -1148,7 -1148,7 +1190,7 @@@ static struct ref_lock *lock_ref_sha1_b
  struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
  {
        char refpath[PATH_MAX];
-       if (check_ref_format(ref))
+       if (check_refname_format(ref, 0))
                return NULL;
        strcpy(refpath, mkpath("refs/%s", ref));
        return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
  
  struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
  {
-       switch (check_ref_format(ref)) {
-       default:
+       if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
                return NULL;
-       case 0:
-       case CHECK_REF_FORMAT_ONELEVEL:
-               return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
-       }
+       return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
  }
  
  static struct lock_file packlock;
diff --combined sha1_name.c
index 653b0659be8416a220268ed2bc60694140c8d472,143fd97ede19194617f1d9b808ec8eb7cd189c5b..ba976b48398d4be0635746bcabb5b1c881e77ae8
@@@ -501,6 -501,12 +501,6 @@@ struct object *peel_to_type(const char 
  {
        if (name && !namelen)
                namelen = strlen(name);
 -      if (!o) {
 -              unsigned char sha1[20];
 -              if (get_sha1_1(name, namelen, sha1))
 -                      return NULL;
 -              o = parse_object(sha1);
 -      }
        while (1) {
                if (!o || (!o->parsed && !parse_object(o->sha1)))
                        return NULL;
@@@ -966,9 -972,9 +966,9 @@@ int strbuf_check_branch_ref(struct strb
  {
        strbuf_branchname(sb, name);
        if (name[0] == '-')
-               return CHECK_REF_FORMAT_ERROR;
+               return -1;
        strbuf_splice(sb, 0, 0, "refs/heads/", 11);
-       return check_ref_format(sb->buf);
+       return check_refname_format(sb->buf, 0);
  }
  
  /*
diff --combined transport.c
index e1940615e28ebc3b16f80b8f647f216031f64ee8,feb2ff51bc984884567d6d1c7dae74fb899b3cac..c048ef179b732c2b1e1ca6531b196b313f0f05ee
@@@ -432,8 -432,7 +432,8 @@@ static int fetch_refs_from_bundle(struc
                               int nr_heads, struct ref **to_fetch)
  {
        struct bundle_transport_data *data = transport->data;
 -      return unbundle(&data->header, data->fd);
 +      return unbundle(&data->header, data->fd,
 +                      transport->progress ? BUNDLE_VERBOSE : 0);
  }
  
  static int close_bundle(struct transport *transport)
@@@ -755,18 -754,10 +755,10 @@@ void transport_verify_remote_names(int 
                        continue;
  
                remote = remote ? (remote + 1) : local;
-               switch (check_ref_format(remote)) {
-               case 0: /* ok */
-               case CHECK_REF_FORMAT_ONELEVEL:
-                       /* ok but a single level -- that is fine for
-                        * a match pattern.
-                        */
-               case CHECK_REF_FORMAT_WILDCARD:
-                       /* ok but ends with a pattern-match character */
-                       continue;
-               }
-               die("remote part of refspec is not a valid name in %s",
-                   heads[i]);
+               if (check_refname_format(remote,
+                               REFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN))
+                       die("remote part of refspec is not a valid name in %s",
+                               heads[i]);
        }
  }