Merge branch 'nd/i18n'
authorJunio C Hamano <gitster@pobox.com>
Wed, 2 May 2012 20:51:35 +0000 (13:51 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 2 May 2012 20:51:35 +0000 (13:51 -0700)
More message strings marked for i18n.

By Nguyễn Thái Ngọc Duy (10) and Jonathan Nieder (1)
* nd/i18n:
help: replace underlining "help -a" headers using hyphens with a blank line
i18n: bundle: mark strings for translation
i18n: index-pack: mark strings for translation
i18n: apply: update say_patch_name to give translators complete sentence
i18n: apply: mark strings for translation
i18n: remote: mark strings for translation
i18n: make warn_dangling_symref() automatically append \n
i18n: help: mark strings for translation
i18n: mark relative dates for translation
strbuf: convenience format functions with \n automatically appended
Makefile: feed all header files to xgettext

1  2 
Makefile
builtin/apply.c
builtin/fetch.c
builtin/remote.c
bundle.c
cache.h
refs.c
diff --combined Makefile
index a14732c4cdfb4ce9990e1965c1b180b67e2d6938,455446b191d2d0363889cafa7e9e60df3b11ec10..7e243085da4c3f50e4fbef10eb247e0d150a5907
+++ b/Makefile
@@@ -386,6 -386,7 +386,7 @@@ XDIFF_OBJS 
  VCSSVN_H =
  VCSSVN_OBJS =
  VCSSVN_TEST_OBJS =
+ MISC_H =
  EXTRA_CPPFLAGS =
  LIB_H =
  LIB_OBJS =
@@@ -440,7 -441,6 +441,7 @@@ SCRIPT_PERL += git-send-email.per
  SCRIPT_PERL += git-svn.perl
  
  SCRIPT_PYTHON += git-remote-testgit.py
 +SCRIPT_PYTHON += git-p4.py
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@@ -481,11 -481,9 +482,11 @@@ TEST_PROGRAMS_NEED_X += test-genrando
  TEST_PROGRAMS_NEED_X += test-index-version
  TEST_PROGRAMS_NEED_X += test-line-buffer
  TEST_PROGRAMS_NEED_X += test-match-trees
 +TEST_PROGRAMS_NEED_X += test-mergesort
  TEST_PROGRAMS_NEED_X += test-mktemp
  TEST_PROGRAMS_NEED_X += test-parse-options
  TEST_PROGRAMS_NEED_X += test-path-utils
 +TEST_PROGRAMS_NEED_X += test-revision-walking
  TEST_PROGRAMS_NEED_X += test-run-command
  TEST_PROGRAMS_NEED_X += test-sha1
  TEST_PROGRAMS_NEED_X += test-sigchain
@@@ -546,6 -544,36 +547,36 @@@ LIB_FILE=libgit.
  XDIFF_LIB=xdiff/lib.a
  VCSSVN_LIB=vcs-svn/lib.a
  
+ XDIFF_H += xdiff/xinclude.h
+ XDIFF_H += xdiff/xmacros.h
+ XDIFF_H += xdiff/xdiff.h
+ XDIFF_H += xdiff/xtypes.h
+ XDIFF_H += xdiff/xutils.h
+ XDIFF_H += xdiff/xprepare.h
+ XDIFF_H += xdiff/xdiffi.h
+ XDIFF_H += xdiff/xemit.h
+ VCSSVN_H += vcs-svn/line_buffer.h
+ VCSSVN_H += vcs-svn/sliding_window.h
+ VCSSVN_H += vcs-svn/repo_tree.h
+ VCSSVN_H += vcs-svn/fast_export.h
+ VCSSVN_H += vcs-svn/svndiff.h
+ VCSSVN_H += vcs-svn/svndump.h
+ MISC_H += branch.h
+ MISC_H += bundle.h
+ MISC_H += bisect.h
+ MISC_H += common-cmds.h
+ MISC_H += fetch-pack.h
+ MISC_H += thread-utils.h
+ MISC_H += send-pack.h
+ MISC_H += shortlog.h
+ MISC_H += reachable.h
+ MISC_H += wt-status.h
+ MISC_H += tar.h
+ MISC_H += url.h
+ MISC_H += walker.h
  LIB_H += advice.h
  LIB_H += archive.h
  LIB_H += argv-array.h
@@@ -593,7 -621,6 +624,7 @@@ LIB_H += log-tree.
  LIB_H += mailmap.h
  LIB_H += merge-file.h
  LIB_H += merge-recursive.h
 +LIB_H += mergesort.h
  LIB_H += notes.h
  LIB_H += notes-cache.h
  LIB_H += notes-merge.h
@@@ -631,7 -658,6 +662,7 @@@ LIB_H += tree-walk.
  LIB_H += unpack-trees.h
  LIB_H += userdiff.h
  LIB_H += utf8.h
 +LIB_H += varint.h
  LIB_H += xdiff-interface.h
  LIB_H += xdiff/xdiff.h
  
@@@ -699,7 -725,6 +730,7 @@@ LIB_OBJS += mailmap.
  LIB_OBJS += match-trees.o
  LIB_OBJS += merge-file.o
  LIB_OBJS += merge-recursive.o
 +LIB_OBJS += mergesort.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += notes.o
  LIB_OBJS += notes-cache.o
@@@ -758,7 -783,6 +789,7 @@@ LIB_OBJS += url.
  LIB_OBJS += usage.o
  LIB_OBJS += userdiff.o
  LIB_OBJS += utf8.o
 +LIB_OBJS += varint.o
  LIB_OBJS += walker.o
  LIB_OBJS += wrapper.o
  LIB_OBJS += write_or_die.o
@@@ -2184,24 -2208,8 +2215,8 @@@ connect.o transport.o url.o http-backen
  http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
  http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
  
- XDIFF_H += xdiff/xinclude.h
- XDIFF_H += xdiff/xmacros.h
- XDIFF_H += xdiff/xdiff.h
- XDIFF_H += xdiff/xtypes.h
- XDIFF_H += xdiff/xutils.h
- XDIFF_H += xdiff/xprepare.h
- XDIFF_H += xdiff/xdiffi.h
- XDIFF_H += xdiff/xemit.h
  xdiff-interface.o $(XDIFF_OBJS): $(XDIFF_H)
  
- VCSSVN_H += vcs-svn/line_buffer.h
- VCSSVN_H += vcs-svn/sliding_window.h
- VCSSVN_H += vcs-svn/repo_tree.h
- VCSSVN_H += vcs-svn/fast_export.h
- VCSSVN_H += vcs-svn/svndiff.h
- VCSSVN_H += vcs-svn/svndump.h
  $(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) $(VCSSVN_H)
  endif
  
@@@ -2272,8 -2280,6 +2287,8 @@@ $(XDIFF_LIB): $(XDIFF_OBJS
  $(VCSSVN_LIB): $(VCSSVN_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
  
 +export DEFAULT_EDITOR DEFAULT_PAGER
 +
  doc:
        $(MAKE) -C Documentation all
  
@@@ -2298,7 -2304,7 +2313,7 @@@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
  XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell
  XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
- LOCALIZED_C := $(C_OBJ:o=c)
+ LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(XDIFF_H) $(VCSSVN_H) $(MISC_H)
  LOCALIZED_SH := $(SCRIPT_SH)
  LOCALIZED_PERL := $(SCRIPT_PERL)
  
diff --combined builtin/apply.c
index 799bb5e906c1e6de6c95cad4b9f4ee8fedf258f0,c768b7fb028813c114e0d28244307d5453b20a77..725712d7888c1a5503c2249c6dbd50ee796c2851
@@@ -103,7 -103,7 +103,7 @@@ static void parse_whitespace_option(con
                ws_error_action = correct_ws_error;
                return;
        }
-       die("unrecognized whitespace option '%s'", option);
+       die(_("unrecognized whitespace option '%s'"), option);
  }
  
  static void parse_ignorewhitespace_option(const char *option)
                ws_ignore_action = ignore_ws_change;
                return;
        }
-       die("unrecognized whitespace ignore option '%s'", option);
+       die(_("unrecognized whitespace ignore option '%s'"), option);
  }
  
  static void set_default_whitespace_mode(const char *whitespace_option)
@@@ -152,14 -152,9 +152,14 @@@ struct fragment 
        unsigned long leading, trailing;
        unsigned long oldpos, oldlines;
        unsigned long newpos, newlines;
 +      /*
 +       * 'patch' is usually borrowed from buf in apply_patch(),
 +       * but some codepaths store an allocated buffer.
 +       */
        const char *patch;
 +      unsigned free_patch:1,
 +              rejected:1;
        int size;
 -      int rejected;
        int linenr;
        struct fragment *next;
  };
@@@ -201,36 -196,6 +201,36 @@@ struct patch 
        struct patch *next;
  };
  
 +static void free_fragment_list(struct fragment *list)
 +{
 +      while (list) {
 +              struct fragment *next = list->next;
 +              if (list->free_patch)
 +                      free((char *)list->patch);
 +              free(list);
 +              list = next;
 +      }
 +}
 +
 +static void free_patch(struct patch *patch)
 +{
 +      free_fragment_list(patch->fragments);
 +      free(patch->def_name);
 +      free(patch->old_name);
 +      free(patch->new_name);
 +      free(patch->result);
 +      free(patch);
 +}
 +
 +static void free_patch_list(struct patch *list)
 +{
 +      while (list) {
 +              struct patch *next = list->next;
 +              free_patch(list);
 +              list = next;
 +      }
 +}
 +
  /*
   * A line in a file, len-bytes long (includes the terminating LF,
   * except for an incomplete line at the end if the file ends with
@@@ -337,11 -302,6 +337,11 @@@ static void add_line_info(struct image 
        img->nr++;
  }
  
 +/*
 + * "buf" has the file contents to be patched (read from various sources).
 + * attach it to "image" and add line-based index to it.
 + * "image" now owns the "buf".
 + */
  static void prepare_image(struct image *image, char *buf, size_t len,
                          int prepare_linetable)
  {
@@@ -375,24 -335,28 +375,27 @@@ static void clear_image(struct image *i
        image->len = 0;
  }
  
- static void say_patch_name(FILE *output, const char *pre,
                         struct patch *patch, const char *post)
+ /* fmt must contain _one_ %s and no other substitution */
static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
  {
-       fputs(pre, output);
+       struct strbuf sb = STRBUF_INIT;
        if (patch->old_name && patch->new_name &&
            strcmp(patch->old_name, patch->new_name)) {
-               quote_c_style(patch->old_name, NULL, output, 0);
-               fputs(" => ", output);
-               quote_c_style(patch->new_name, NULL, output, 0);
+               quote_c_style(patch->old_name, &sb, NULL, 0);
+               strbuf_addstr(&sb, " => ");
+               quote_c_style(patch->new_name, &sb, NULL, 0);
        } else {
                const char *n = patch->new_name;
                if (!n)
                        n = patch->old_name;
-               quote_c_style(n, NULL, output, 0);
+               quote_c_style(n, &sb, NULL, 0);
        }
-       fputs(post, output);
+       fprintf(output, fmt, sb.buf);
+       fputc('\n', output);
+       strbuf_release(&sb);
  }
  
 -#define CHUNKSIZE (8192)
  #define SLOP (16)
  
  static void read_patch_file(struct strbuf *sb, int fd)
@@@ -455,7 -419,7 +458,7 @@@ static char *squash_slash(char *name
        return name;
  }
  
 -static char *find_name_gnu(const char *line, char *def, int p_value)
 +static char *find_name_gnu(const char *line, const char *def, int p_value)
  {
        struct strbuf name = STRBUF_INIT;
        char *cp;
                cp++;
        }
  
 -      /* name can later be freed, so we need
 -       * to memmove, not just return cp
 -       */
        strbuf_remove(&name, 0, cp - name.buf);
 -      free(def);
        if (root)
                strbuf_insert(&name, 0, root, root_len);
        return squash_slash(strbuf_detach(&name, NULL));
@@@ -643,13 -611,8 +646,13 @@@ static size_t diff_timestamp_len(const 
        return line + len - end;
  }
  
 -static char *find_name_common(const char *line, char *def, int p_value,
 -                              const char *end, int terminate)
 +static char *null_strdup(const char *s)
 +{
 +      return s ? xstrdup(s) : NULL;
 +}
 +
 +static char *find_name_common(const char *line, const char *def,
 +                            int p_value, const char *end, int terminate)
  {
        int len;
        const char *start = NULL;
                        start = line;
        }
        if (!start)
 -              return squash_slash(def);
 +              return squash_slash(null_strdup(def));
        len = line - start;
        if (!len)
 -              return squash_slash(def);
 +              return squash_slash(null_strdup(def));
  
        /*
         * Generally we prefer the shorter name, especially
        if (def) {
                int deflen = strlen(def);
                if (deflen < len && !strncmp(start, def, deflen))
 -                      return squash_slash(def);
 -              free(def);
 +                      return squash_slash(xstrdup(def));
        }
  
        if (root) {
@@@ -809,7 -773,7 +812,7 @@@ static int has_epoch_timestamp(const ch
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
-                       warning("Cannot prepare timestamp regexp %s",
+                       warning(_("Cannot prepare timestamp regexp %s"),
                                stamp_regexp);
                        return 0;
                }
        status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
        if (status) {
                if (status != REG_NOMATCH)
-                       warning("regexec returned %d for input: %s",
+                       warning(_("regexec returned %d for input: %s"),
                                status, timestamp);
                return 0;
        }
@@@ -881,10 -845,8 +884,10 @@@ static void parse_traditional_patch(con
                name = find_name_traditional(first, NULL, p_value);
                patch->old_name = name;
        } else {
 -              name = find_name_traditional(first, NULL, p_value);
 -              name = find_name_traditional(second, name, p_value);
 +              char *first_name;
 +              first_name = find_name_traditional(first, NULL, p_value);
 +              name = find_name_traditional(second, first_name, p_value);
 +              free(first_name);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
                        patch->is_delete = 0;
                        patch->is_delete = 1;
                        patch->old_name = name;
                } else {
 -                      patch->old_name = patch->new_name = name;
 +                      patch->old_name = name;
 +                      patch->new_name = xstrdup(name);
                }
        }
        if (!name)
-               die("unable to find filename in patch at line %d", linenr);
+               die(_("unable to find filename in patch at line %d"), linenr);
  }
  
  static int gitdiff_hdrend(const char *line, struct patch *patch)
@@@ -928,36 -889,30 +931,36 @@@ static char *gitdiff_verify_name(const 
                name = orig_name;
                len = strlen(name);
                if (isnull)
-                       die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
+                       die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
                another = find_name(line, NULL, p_value, TERM_TAB);
                if (!another || memcmp(another, name, len + 1))
-                       die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
+                       die(_("git apply: bad git-diff - inconsistent %s filename on line %d"), oldnew, linenr);
                free(another);
                return orig_name;
        }
        else {
                /* expect "/dev/null" */
                if (memcmp("/dev/null", line, 9) || line[9] != '\n')
-                       die("git apply: bad git-diff - expected /dev/null on line %d", linenr);
+                       die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
                return NULL;
        }
  }
  
  static int gitdiff_oldname(const char *line, struct patch *patch)
  {
 +      char *orig = patch->old_name;
        patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old");
 +      if (orig != patch->old_name)
 +              free(orig);
        return 0;
  }
  
  static int gitdiff_newname(const char *line, struct patch *patch)
  {
 +      char *orig = patch->new_name;
        patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new");
 +      if (orig != patch->new_name)
 +              free(orig);
        return 0;
  }
  
@@@ -976,23 -931,20 +979,23 @@@ static int gitdiff_newmode(const char *
  static int gitdiff_delete(const char *line, struct patch *patch)
  {
        patch->is_delete = 1;
 -      patch->old_name = patch->def_name;
 +      free(patch->old_name);
 +      patch->old_name = null_strdup(patch->def_name);
        return gitdiff_oldmode(line, patch);
  }
  
  static int gitdiff_newfile(const char *line, struct patch *patch)
  {
        patch->is_new = 1;
 -      patch->new_name = patch->def_name;
 +      free(patch->new_name);
 +      patch->new_name = null_strdup(patch->def_name);
        return gitdiff_newmode(line, patch);
  }
  
  static int gitdiff_copysrc(const char *line, struct patch *patch)
  {
        patch->is_copy = 1;
 +      free(patch->old_name);
        patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
  static int gitdiff_copydst(const char *line, struct patch *patch)
  {
        patch->is_copy = 1;
 +      free(patch->new_name);
        patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
  static int gitdiff_renamesrc(const char *line, struct patch *patch)
  {
        patch->is_rename = 1;
 +      free(patch->old_name);
        patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
  static int gitdiff_renamedst(const char *line, struct patch *patch)
  {
        patch->is_rename = 1;
 +      free(patch->new_name);
        patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
@@@ -1098,7 -1047,7 +1101,7 @@@ static const char *stop_at_slash(const 
   * creation or deletion of an empty file.  In any of these cases,
   * both sides are the same name under a/ and b/ respectively.
   */
 -static char *git_header_name(char *line, int llen)
 +static char *git_header_name(const char *line, int llen)
  {
        const char *name;
        const char *second = NULL;
  }
  
  /* Verify that we recognize the lines following a git header */
 -static int parse_git_header(char *line, int len, unsigned int size, struct patch *patch)
 +static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
  {
        unsigned long offset;
  
@@@ -1341,7 -1290,7 +1344,7 @@@ static int parse_range(const char *line
        return offset + ex;
  }
  
 -static void recount_diff(char *line, int size, struct fragment *fragment)
 +static void recount_diff(const char *line, int size, struct fragment *fragment)
  {
        int oldlines = 0, newlines = 0, ret = 0;
  
                        break;
                }
                if (ret) {
-                       warning("recount: unexpected line: %.*s",
+                       warning(_("recount: unexpected line: %.*s"),
                                (int)linelen(line, size), line);
                        return;
                }
   * Parse a unified diff fragment header of the
   * form "@@ -a,b +c,d @@"
   */
 -static int parse_fragment_header(char *line, int len, struct fragment *fragment)
 +static int parse_fragment_header(const char *line, int len, struct fragment *fragment)
  {
        int offset;
  
        return offset;
  }
  
 -static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch)
 +static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
  {
        unsigned long offset, len;
  
                        struct fragment dummy;
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
-                       die("patch fragment without header at line %d: %.*s",
+                       die(_("patch fragment without header at line %d: %.*s"),
                            linenr, (int)len-1, line);
                }
  
                                continue;
                        if (!patch->old_name && !patch->new_name) {
                                if (!patch->def_name)
-                                       die("git diff header lacks filename information when removing "
-                                           "%d leading pathname components (line %d)" , p_value, linenr);
+                                       die(Q_("git diff header lacks filename information when removing "
+                                              "%d leading pathname component (line %d)",
+                                              "git diff header lacks filename information when removing "
+                                              "%d leading pathname components (line %d)",
+                                              p_value),
+                                           p_value, linenr);
 -                              patch->old_name = patch->new_name = patch->def_name;
 +                              patch->old_name = xstrdup(patch->def_name);
 +                              patch->new_name = xstrdup(patch->def_name);
                        }
                        if (!patch->is_delete && !patch->new_name)
                                die("git diff header lacks filename information "
@@@ -1521,7 -1473,7 +1528,7 @@@ static void check_whitespace(const cha
   * between a "---" that is part of a patch, and a "---" that starts
   * the next patch is to look at the line counts..
   */
 -static int parse_fragment(char *line, unsigned long size,
 +static int parse_fragment(const char *line, unsigned long size,
                          struct patch *patch, struct fragment *fragment)
  {
        int added, deleted;
        patch->lines_deleted += deleted;
  
        if (0 < patch->is_new && oldlines)
-               return error("new file depends on old contents");
+               return error(_("new file depends on old contents"));
        if (0 < patch->is_delete && newlines)
-               return error("deleted file still has contents");
+               return error(_("deleted file still has contents"));
        return offset;
  }
  
 -static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
 +/*
 + * We have seen "diff --git a/... b/..." header (or a traditional patch
 + * header).  Read hunks that belong to this patch into fragments and hang
 + * them to the given patch structure.
 + *
 + * The (fragment->patch, fragment->size) pair points into the memory given
 + * by the caller, not a copy, when we return.
 + */
 +static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
  {
        unsigned long offset = 0;
        unsigned long oldlines = 0, newlines = 0, context = 0;
                fragment->linenr = linenr;
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
-                       die("corrupt patch at line %d", linenr);
+                       die(_("corrupt patch at line %d"), linenr);
                fragment->patch = line;
                fragment->size = len;
                oldlines += fragment->oldlines;
                patch->is_delete = 0;
  
        if (0 < patch->is_new && oldlines)
-               die("new file %s depends on old contents", patch->new_name);
+               die(_("new file %s depends on old contents"), patch->new_name);
        if (0 < patch->is_delete && newlines)
-               die("deleted file %s still has contents", patch->old_name);
+               die(_("deleted file %s still has contents"), patch->old_name);
        if (!patch->is_delete && !newlines && context)
-               fprintf(stderr, "** warning: file %s becomes empty but "
-                       "is not deleted\n", patch->new_name);
+               fprintf_ln(stderr,
+                          _("** warning: "
+                            "file %s becomes empty but is not deleted"),
+                          patch->new_name);
  
        return offset;
  }
@@@ -1718,11 -1664,6 +1727,11 @@@ static char *inflate_it(const void *dat
        return out;
  }
  
 +/*
 + * Read a binary hunk and return a new fragment; fragment->patch
 + * points at an allocated memory that the caller must free, so
 + * it is marked as "->free_patch = 1".
 + */
  static struct fragment *parse_binary_hunk(char **buf_p,
                                          unsigned long *sz_p,
                                          int *status_p,
  
        frag = xcalloc(1, sizeof(*frag));
        frag->patch = inflate_it(data, hunk_size, origlen);
 +      frag->free_patch = 1;
        if (!frag->patch)
                goto corrupt;
        free(data);
   corrupt:
        free(data);
        *status_p = -1;
-       error("corrupt binary patch at line %d: %.*s",
+       error(_("corrupt binary patch at line %d: %.*s"),
              linenr-1, llen-1, buffer);
        return NULL;
  }
@@@ -1853,7 -1793,7 +1862,7 @@@ static int parse_binary(char *buffer, u
        forward = parse_binary_hunk(&buffer, &size, &status, &used);
        if (!forward && !status)
                /* there has to be one hunk (forward hunk) */
-               return error("unrecognized binary patch at line %d", linenr-1);
+               return error(_("unrecognized binary patch at line %d"), linenr-1);
        if (status)
                /* otherwise we already gave an error message */
                return status;
        return used;
  }
  
 +/*
 + * Read the patch text in "buffer" taht extends for "size" bytes; stop
 + * reading after seeing a single patch (i.e. changes to a single file).
 + * Create fragments (i.e. patch hunks) and hang them to the given patch.
 + * Return the number of bytes consumed, so that the caller can call us
 + * again for the next patch.
 + */
  static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
  {
        int hdrsize, patchsize;
                 */
                if ((apply || check) &&
                    (!patch->is_binary && !metadata_changes(patch)))
-                       die("patch with only garbage at line %d", linenr);
+                       die(_("patch with only garbage at line %d"), linenr);
        }
  
        return offset + hdrsize + patchsize;
@@@ -2029,11 -1962,11 +2038,11 @@@ static int read_old_data(struct stat *s
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
-                       return error("unable to read symlink %s", path);
+                       return error(_("unable to read symlink %s"), path);
                return 0;
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
-                       return error("unable to open or read %s", path);
+                       return error(_("unable to open or read %s"), path);
                convert_to_git(path, buf->buf, buf->len, buf, 0);
                return 0;
        default:
@@@ -2104,7 -2037,7 +2113,7 @@@ static void update_pre_post_images(stru
                        ctx++;
                }
                if (preimage->nr <= ctx)
-                       die("oops");
+                       die(_("oops"));
  
                /* and copy it in, while fixing the line length */
                len = preimage->line[ctx].len;
@@@ -2443,11 -2376,6 +2452,11 @@@ static void remove_last_line(struct ima
        img->len -= img->line[--img->nr].len;
  }
  
 +/*
 + * The change from "preimage" and "postimage" has been found to
 + * apply at applied_pos (counts in line numbers) in "img".
 + * Update "img" to remove "preimage" and replace it with "postimage".
 + */
  static void update_image(struct image *img,
                         int applied_pos,
                         struct image *preimage,
        img->nr = nr;
  }
  
 +/*
 + * Use the patch-hunk text in "frag" to prepare two images (preimage and
 + * postimage) for the hunk.  Find lines that match "preimage" in "img" and
 + * replace the part of "img" with "postimage" text.
 + */
  static int apply_one_fragment(struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule,
                              int nth_fragment)
                        break;
                default:
                        if (apply_verbosely)
-                               error("invalid start of line: '%c'", first);
+                               error(_("invalid start of line: '%c'"), first);
                        return -1;
                }
                if (added_blank_line) {
                        int offset = applied_pos - pos;
                        if (apply_in_reverse)
                                offset = 0 - offset;
-                       fprintf(stderr,
-                               "Hunk #%d succeeded at %d (offset %d lines).\n",
-                               nth_fragment, applied_pos + 1, offset);
+                       fprintf_ln(stderr,
+                                  Q_("Hunk #%d succeeded at %d (offset %d line).",
+                                     "Hunk #%d succeeded at %d (offset %d lines).",
+                                     offset),
+                                  nth_fragment, applied_pos + 1, offset);
                }
  
                /*
                 */
                if ((leading != frag->leading) ||
                    (trailing != frag->trailing))
-                       fprintf(stderr, "Context reduced to (%ld/%ld)"
-                               " to apply fragment at %d\n",
-                               leading, trailing, applied_pos+1);
+                       fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
+                                            " to apply fragment at %d"),
+                                  leading, trailing, applied_pos+1);
                update_image(img, applied_pos, &preimage, &postimage);
        } else {
                if (apply_verbosely)
-                       error("while searching for:\n%.*s",
+                       error(_("while searching for:\n%.*s"),
                              (int)(old - oldlines), oldlines);
        }
  
@@@ -2779,7 -2704,7 +2790,7 @@@ static int apply_binary_fragment(struc
        void *dst;
  
        if (!fragment)
-               return error("missing binary patch data for '%s'",
+               return error(_("missing binary patch data for '%s'"),
                             patch->new_name ?
                             patch->new_name :
                             patch->old_name);
        return -1;
  }
  
 +/*
 + * Replace "img" with the result of applying the binary patch.
 + * The binary patch data itself in patch->fragment is still kept
 + * but the preimage prepared by the caller in "img" is freed here
 + * or in the helper function apply_binary_fragment() this calls.
 + */
  static int apply_binary(struct image *img, struct patch *patch)
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
                 * in the patch->fragments->{patch,size}.
                 */
                if (apply_binary_fragment(img, patch))
-                       return error("binary patch does not apply to '%s'",
+                       return error(_("binary patch does not apply to '%s'"),
                                     name);
  
                /* verify that the result matches */
                hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
+                       return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
                                name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
  
@@@ -2909,7 -2828,7 +2920,7 @@@ static int apply_fragments(struct imag
        while (frag) {
                nth++;
                if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
-                       error("patch failed: %s:%ld", name, frag->oldpos);
+                       error(_("patch failed: %s:%ld"), name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
                        frag->rejected = 1;
@@@ -3024,14 -2943,14 +3035,14 @@@ static int apply_data(struct patch *pat
        if (!(patch->is_copy || patch->is_rename) &&
            (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
                if (was_deleted(tpatch)) {
-                       return error("patch %s has been renamed/deleted",
+                       return error(_("patch %s has been renamed/deleted"),
                                patch->old_name);
                }
 -              /* We have a patched copy in memory use that */
 +              /* We have a patched copy in memory; use that. */
                strbuf_add(&buf, tpatch->result, tpatch->resultsize);
        } else if (cached) {
                if (read_file_or_gitlink(ce, &buf))
-                       return error("read of %s failed", patch->old_name);
+                       return error(_("read of %s failed"), patch->old_name);
        } else if (patch->old_name) {
                if (S_ISGITLINK(patch->old_mode)) {
                        if (ce) {
                                /*
                                 * There is no way to apply subproject
                                 * patch without looking at the index.
 +                               * NEEDSWORK: shouldn't this be flagged
 +                               * as an error???
                                 */
 +                              free_fragment_list(patch->fragments);
                                patch->fragments = NULL;
                        }
                } else {
                        if (read_old_data(st, patch->old_name, &buf))
-                               return error("read of %s failed", patch->old_name);
+                               return error(_("read of %s failed"), patch->old_name);
                }
        }
  
        free(image.line_allocated);
  
        if (0 < patch->is_delete && patch->resultsize)
-               return error("removal patch leaves file contents");
+               return error(_("removal patch leaves file contents"));
  
        return 0;
  }
@@@ -3084,7 -3000,7 +3095,7 @@@ static int check_to_create_blob(const c
                if (has_symlink_leading_path(new_name, strlen(new_name)))
                        return 0;
  
-               return error("%s: already exists in working directory", new_name);
+               return error(_("%s: already exists in working directory"), new_name);
        }
        else if ((errno != ENOENT) && (errno != ENOTDIR))
                return error("%s: %s", new_name, strerror(errno));
@@@ -3122,12 -3038,12 +3133,12 @@@ static int check_preimage(struct patch 
        if (!(patch->is_copy || patch->is_rename) &&
            (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
                if (was_deleted(tpatch))
-                       return error("%s: has been deleted/renamed", old_name);
+                       return error(_("%s: has been deleted/renamed"), old_name);
                st_mode = tpatch->new_mode;
        } else if (!cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
-                       return error("%s: %s", old_name, strerror(errno));
+                       return error(_("%s: %s"), old_name, strerror(errno));
        }
  
        if (to_be_deleted(tpatch))
                if (pos < 0) {
                        if (patch->is_new < 0)
                                goto is_new;
-                       return error("%s: does not exist in index", old_name);
+                       return error(_("%s: does not exist in index"), old_name);
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
-                       return error("%s: does not match index", old_name);
+                       return error(_("%s: does not match index"), old_name);
                if (cached)
                        st_mode = (*ce)->ce_mode;
        } else if (stat_ret < 0) {
                if (patch->is_new < 0)
                        goto is_new;
-               return error("%s: %s", old_name, strerror(errno));
+               return error(_("%s: %s"), old_name, strerror(errno));
        }
  
        if (!cached && !tpatch)
        if (!patch->old_mode)
                patch->old_mode = st_mode;
        if ((st_mode ^ patch->old_mode) & S_IFMT)
-               return error("%s: wrong type", old_name);
+               return error(_("%s: wrong type"), old_name);
        if (st_mode != patch->old_mode)
-               warning("%s has type %o, expected %o",
+               warning(_("%s has type %o, expected %o"),
                        old_name, st_mode, patch->old_mode);
        if (!patch->new_mode && !patch->is_delete)
                patch->new_mode = st_mode;
   is_new:
        patch->is_new = 1;
        patch->is_delete = 0;
 +      free(patch->old_name);
        patch->old_name = NULL;
        return 0;
  }
  
 +/*
 + * Check and apply the patch in-core; leave the result in patch->result
 + * for the caller to write it out to the final destination.
 + */
  static int check_patch(struct patch *patch)
  {
        struct stat st;
                if (check_index &&
                    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
                    !ok_if_exists)
-                       return error("%s: already exists in index", new_name);
+                       return error(_("%s: already exists in index"), new_name);
                if (!cached) {
                        int err = check_to_create_blob(new_name, ok_if_exists);
                        if (err)
                if (!patch->new_mode)
                        patch->new_mode = patch->old_mode;
                if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
-                       return error("new mode (%o) of %s does not match old mode (%o)%s%s",
+                       return error(_("new mode (%o) of %s does not match old mode (%o)%s%s"),
                                patch->new_mode, new_name, patch->old_mode,
                                same ? "" : " of ", same ? "" : old_name);
        }
  
        if (apply_data(patch, &st, ce) < 0)
-               return error("%s: patch does not apply", name);
+               return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
        return 0;
  }
@@@ -3264,7 -3175,7 +3275,7 @@@ static int check_patch_list(struct patc
        while (patch) {
                if (apply_verbosely)
                        say_patch_name(stderr,
-                                      "Checking patch ", patch, "...\n");
+                                      _("Checking patch %s..."), patch);
                err |= check_patch(patch);
                patch = patch->next;
        }
@@@ -3319,7 -3230,7 +3330,7 @@@ static void build_fake_ancestor(struct 
  
                ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
                if (!ce)
-                       die("make_cache_entry failed for path '%s'", name);
+                       die(_("make_cache_entry failed for path '%s'"), name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
                        die ("Could not add %s to temporary index", name);
        }
@@@ -3462,7 -3373,7 +3473,7 @@@ static void remove_file(struct patch *p
  {
        if (update_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
-                       die("unable to remove %s from index", patch->old_name);
+                       die(_("unable to remove %s from index"), patch->old_name);
        }
        if (!cached) {
                if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
@@@ -3489,19 -3400,19 +3500,19 @@@ static void add_index_file(const char *
                const char *s = buf;
  
                if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
-                       die("corrupt patch for subproject %s", path);
+                       die(_("corrupt patch for subproject %s"), path);
        } else {
                if (!cached) {
                        if (lstat(path, &st) < 0)
-                               die_errno("unable to stat newly created file '%s'",
+                               die_errno(_("unable to stat newly created file '%s'"),
                                          path);
                        fill_stat_cache_info(ce, &st);
                }
                if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
-                       die("unable to create backing store for newly created file %s", path);
+                       die(_("unable to create backing store for newly created file %s"), path);
        }
        if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
-               die("unable to add cache entry for %s", path);
+               die(_("unable to add cache entry for %s"), path);
  }
  
  static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
        strbuf_release(&nbuf);
  
        if (close(fd) < 0)
-               die_errno("closing file '%s'", path);
+               die_errno(_("closing file '%s'"), path);
        return 0;
  }
  
@@@ -3583,7 -3494,7 +3594,7 @@@ static void create_one_file(char *path
                        ++nr;
                }
        }
-       die_errno("unable to write file '%s' mode %o", path, mode);
+       die_errno(_("unable to write file '%s' mode %o"), path, mode);
  }
  
  static void create_file(struct patch *patch)
@@@ -3628,6 -3539,7 +3639,7 @@@ static int write_out_one_reject(struct 
        char namebuf[PATH_MAX];
        struct fragment *frag;
        int cnt = 0;
+       struct strbuf sb = STRBUF_INIT;
  
        for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
                if (!frag->rejected)
        if (!cnt) {
                if (apply_verbosely)
                        say_patch_name(stderr,
-                                      "Applied patch ", patch, " cleanly.\n");
+                                      _("Applied patch %s cleanly."), patch);
                return 0;
        }
  
         * contents are marked "rejected" at the patch level.
         */
        if (!patch->new_name)
-               die("internal error");
+               die(_("internal error"));
  
        /* Say this even without --verbose */
-       say_patch_name(stderr, "Applying patch ", patch, " with");
-       fprintf(stderr, " %d rejects...\n", cnt);
+       strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...",
+                           "Applying patch %%s with %d rejects...",
+                           cnt),
+                   cnt);
+       say_patch_name(stderr, sb.buf, patch);
+       strbuf_release(&sb);
  
        cnt = strlen(patch->new_name);
        if (ARRAY_SIZE(namebuf) <= cnt + 5) {
                cnt = ARRAY_SIZE(namebuf) - 5;
-               warning("truncating .rej filename to %.*s.rej",
+               warning(_("truncating .rej filename to %.*s.rej"),
                        cnt - 1, patch->new_name);
        }
        memcpy(namebuf, patch->new_name, cnt);
  
        rej = fopen(namebuf, "w");
        if (!rej)
-               return error("cannot open %s: %s", namebuf, strerror(errno));
+               return error(_("cannot open %s: %s"), namebuf, strerror(errno));
  
        /* Normal git tools never deal with .rej, so do not pretend
         * this is a git patch by saying --git nor give extended
             frag;
             cnt++, frag = frag->next) {
                if (!frag->rejected) {
-                       fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+                       fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
                        continue;
                }
-               fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+               fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
                fprintf(rej, "%.*s", frag->size, frag->patch);
                if (frag->patch[frag->size-1] != '\n')
                        fputc('\n', rej);
@@@ -3765,8 -3681,15 +3781,8 @@@ static void prefix_patches(struct patc
        if (!prefix || p->is_toplevel_relative)
                return;
        for ( ; p; p = p->next) {
 -              if (p->new_name == p->old_name) {
 -                      char *prefixed = p->new_name;
 -                      prefix_one(&prefixed);
 -                      p->new_name = p->old_name = prefixed;
 -              }
 -              else {
 -                      prefix_one(&p->new_name);
 -                      prefix_one(&p->old_name);
 -              }
 +              prefix_one(&p->new_name);
 +              prefix_one(&p->old_name);
        }
  }
  
  static int apply_patch(int fd, const char *filename, int options)
  {
        size_t offset;
 -      struct strbuf buf = STRBUF_INIT;
 +      struct strbuf buf = STRBUF_INIT; /* owns the patch text */
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
  
 -      /* FIXME - memory leak when using multiple patch files as inputs */
 -      memset(&fn_table, 0, sizeof(struct string_list));
        patch_input_file = filename;
        read_patch_file(&buf, fd);
        offset = 0;
                        listp = &patch->next;
                }
                else {
 -                      /* perhaps free it a bit better? */
 -                      free(patch);
 +                      free_patch(patch);
                        skipped_patch++;
                }
                offset += nr;
        }
  
        if (!list && !skipped_patch)
-               die("unrecognized input");
+               die(_("unrecognized input"));
  
        if (whitespace_error && (ws_error_action == die_on_ws_error))
                apply = 0;
  
        if (check_index) {
                if (read_cache() < 0)
-                       die("unable to read index file");
+                       die(_("unable to read index file"));
        }
  
        if ((check || apply) &&
        if (summary)
                summary_patch_list(list);
  
 +      free_patch_list(list);
        strbuf_release(&buf);
 +      string_list_clear(&fn_table, 0);
        return 0;
  }
  
@@@ -4016,10 -3940,10 +4032,10 @@@ int cmd_apply(int argc, const char **ar
        if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
                apply = 0;
        if (check_index && is_not_gitdir)
-               die("--index outside a repository");
+               die(_("--index outside a repository"));
        if (cached) {
                if (is_not_gitdir)
-                       die("--cached outside a repository");
+                       die(_("--cached outside a repository"));
                check_index = 1;
        }
        for (i = 0; i < argc; i++) {
  
                fd = open(arg, O_RDONLY);
                if (fd < 0)
-                       die_errno("can't open patch '%s'", arg);
+                       die_errno(_("can't open patch '%s'"), arg);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, options);
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
-                       warning("squelched %d "
-                               "whitespace error%s",
-                               squelched,
-                               squelched == 1 ? "" : "s");
+                       warning(Q_("squelched %d whitespace error",
+                                  "squelched %d whitespace errors",
+                                  squelched),
+                               squelched);
                }
                if (ws_error_action == die_on_ws_error)
-                       die("%d line%s add%s whitespace errors.",
-                           whitespace_error,
-                           whitespace_error == 1 ? "" : "s",
-                           whitespace_error == 1 ? "s" : "");
+                       die(Q_("%d line adds whitespace errors.",
+                              "%d lines add whitespace errors.",
+                              whitespace_error),
+                           whitespace_error);
                if (applied_after_fixing_ws && apply)
                        warning("%d line%s applied after"
                                " fixing whitespace errors.",
                                applied_after_fixing_ws,
                                applied_after_fixing_ws == 1 ? "" : "s");
                else if (whitespace_error)
-                       warning("%d line%s add%s whitespace errors.",
-                               whitespace_error,
-                               whitespace_error == 1 ? "" : "s",
-                               whitespace_error == 1 ? "s" : "");
+                       warning(Q_("%d line adds whitespace errors.",
+                                  "%d lines add whitespace errors.",
+                                  whitespace_error),
+                               whitespace_error);
        }
  
        if (update_index) {
                if (write_cache(newfd, active_cache, active_nr) ||
                    commit_locked_index(&lock_file))
-                       die("Unable to write new index file");
+                       die(_("Unable to write new index file"));
        }
  
        return !!errs;
diff --combined builtin/fetch.c
index 1c8cb62445a3dc7a0ff1ff6d2663faec1bced167,a8c3e4ceb8011d78287c13c03f62970be27f025c..bb9a0743ff565f3002eb0fede8e35b7f01a73b75
@@@ -240,7 -240,6 +240,7 @@@ static int s_update_ref(const char *act
  
  static int update_local_ref(struct ref *ref,
                            const char *remote,
 +                          const struct ref *remote_ref,
                            struct strbuf *display)
  {
        struct commit *current = NULL, *updated;
                const char *msg;
                const char *what;
                int r;
 -              if (!strncmp(ref->name, "refs/tags/", 10)) {
 +              /*
 +               * Nicely describe the new ref we're fetching.
 +               * Base this on the remote's ref name, as it's
 +               * more likely to follow a standard layout.
 +               */
 +              const char *name = remote_ref ? remote_ref->name : "";
 +              if (!prefixcmp(name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
 -              }
 -              else {
 +              } else if (!prefixcmp(name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
 -                      if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
 -                          (recurse_submodules != RECURSE_SUBMODULES_ON))
 -                              check_for_new_submodule_commits(ref->new_sha1);
 +              } else {
 +                      msg = "storing ref";
 +                      what = _("[new ref]");
                }
  
 +              if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
 +                  (recurse_submodules != RECURSE_SUBMODULES_ON))
 +                      check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref(msg, ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                            r ? '!' : '*',
@@@ -475,7 -466,7 +475,7 @@@ static int store_updated_refs(const cha
  
                        strbuf_reset(&note);
                        if (ref) {
 -                              rc |= update_local_ref(ref, what, &note);
 +                              rc |= update_local_ref(ref, what, rm, &note);
                                free(ref);
                        } else
                                strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
@@@ -546,8 -537,8 +546,8 @@@ static int prune_refs(struct refspec *r
        int result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
        const char *dangling_msg = dry_run
-               ? _("   (%s will become dangling)\n")
-               : _("   (%s has become dangling)\n");
+               ? _("   (%s will become dangling)")
+               : _("   (%s has become dangling)");
  
        for (ref = stale_refs; ref; ref = ref->next) {
                if (!dry_run)
diff --combined builtin/remote.c
index b5645fe0ae89fc969c41aceb32e918fb0fa0d39f,51434fa5f3073b0525929aa5dc37c0045ed24620..0f0c594b2fc2194d841cabceaf180ee664376af1
@@@ -9,7 -9,7 +9,7 @@@
  
  static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose]",
 -      "git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>",
 +      "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>",
        "git remote rename <old> <new>",
        "git remote rm <name>",
        "git remote set-head <name> (-a | -d | <branch>)",
@@@ -17,7 -17,7 +17,7 @@@
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]",
        "git remote set-branches [--add] <name> <branch>...",
 -      "git remote set-url <name> <newurl> [<oldurl>]",
 +      "git remote set-url [--push] <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
        NULL
@@@ -95,9 -95,9 +95,9 @@@ static int fetch_remote(const char *nam
                argv[1] = "-v";
                argv[2] = name;
        }
-       printf("Updating %s\n", name);
+       printf_ln(_("Updating %s"), name);
        if (run_command_v_opt(argv, RUN_GIT_CMD))
-               return error("Could not fetch %s", name);
+               return error(_("Could not fetch %s"), name);
        return 0;
  }
  
@@@ -127,8 -127,8 +127,8 @@@ static int add_branch(const char *key, 
  }
  
  static const char mirror_advice[] =
- "--mirror is dangerous and deprecated; please\n"
"\t use --mirror=fetch or --mirror=push instead";
N_("--mirror is dangerous and deprecated; please\n"
   "\t use --mirror=fetch or --mirror=push instead");
  
  static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
  {
        if (not)
                *mirror = MIRROR_NONE;
        else if (!arg) {
-               warning("%s", mirror_advice);
+               warning("%s", _(mirror_advice));
                *mirror = MIRROR_BOTH;
        }
        else if (!strcmp(arg, "fetch"))
        else if (!strcmp(arg, "push"))
                *mirror = MIRROR_PUSH;
        else
-               return error("unknown mirror argument: %s", arg);
+               return error(_("unknown mirror argument: %s"), arg);
        return 0;
  }
  
@@@ -182,9 -182,9 +182,9 @@@ static int add(int argc, const char **a
                usage_with_options(builtin_remote_add_usage, options);
  
        if (mirror && master)
-               die("specifying a master branch makes no sense with --mirror");
+               die(_("specifying a master branch makes no sense with --mirror"));
        if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
-               die("specifying branches to track makes sense only with fetch mirrors");
+               die(_("specifying branches to track makes sense only with fetch mirrors"));
  
        name = argv[0];
        url = argv[1];
        remote = remote_get(name);
        if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
                        remote->fetch_refspec_nr))
-               die("remote %s already exists.", name);
+               die(_("remote %s already exists."), name);
  
        strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
        if (!valid_fetch_refspec(buf2.buf))
-               die("'%s' is not a valid remote name", name);
+               die(_("'%s' is not a valid remote name"), name);
  
        strbuf_addf(&buf, "remote.%s.url", name);
        if (git_config_set(buf.buf, url))
                strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
  
                if (create_symref(buf.buf, buf2.buf, "remote add"))
-                       return error("Could not setup master '%s'", master);
+                       return error(_("Could not setup master '%s'"), master);
        }
  
        strbuf_release(&buf);
@@@ -296,7 -296,7 +296,7 @@@ static int config_read_branches(const c
                info = item->util;
                if (type == REMOTE) {
                        if (info->remote_name)
-                               warning("more than one %s", orig_key);
+                               warning(_("more than one %s"), orig_key);
                        info->remote_name = xstrdup(value);
                } else if (type == MERGE) {
                        char *space = strchr(value, ' ');
@@@ -336,7 -336,7 +336,7 @@@ static int get_ref_states(const struct 
  
        for (i = 0; i < states->remote->fetch_refspec_nr; i++)
                if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
-                       die("Could not get fetch map for refspec %s",
+                       die(_("Could not get fetch map for refspec %s"),
                                states->remote->fetch_refspec[i]);
  
        states->new.strdup_strings = 1;
@@@ -437,7 -437,7 +437,7 @@@ static int get_push_ref_states_noquery(
  
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
-               item = string_list_append(&states->push, "(matching)");
+               item = string_list_append(&states->push, _("(matching)"));
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
        for (i = 0; i < remote->push_refspec_nr; i++) {
                struct refspec *spec = remote->push + i;
                if (spec->matching)
-                       item = string_list_append(&states->push, "(matching)");
+                       item = string_list_append(&states->push, _("(matching)"));
                else if (strlen(spec->src))
                        item = string_list_append(&states->push, spec->src);
                else
-                       item = string_list_append(&states->push, "(delete)");
+                       item = string_list_append(&states->push, _("(delete)"));
  
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->forced = spec->force;
@@@ -592,19 -592,19 +592,19 @@@ static int migrate_file(struct remote *
        strbuf_addf(&buf, "remote.%s.url", remote->name);
        for (i = 0; i < remote->url_nr; i++)
                if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
-                       return error("Could not append '%s' to '%s'",
+                       return error(_("Could not append '%s' to '%s'"),
                                        remote->url[i], buf.buf);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.push", remote->name);
        for (i = 0; i < remote->push_refspec_nr; i++)
                if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
-                       return error("Could not append '%s' to '%s'",
+                       return error(_("Could not append '%s' to '%s'"),
                                        remote->push_refspec[i], buf.buf);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", remote->name);
        for (i = 0; i < remote->fetch_refspec_nr; i++)
                if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
-                       return error("Could not append '%s' to '%s'",
+                       return error(_("Could not append '%s' to '%s'"),
                                        remote->fetch_refspec[i], buf.buf);
        if (remote->origin == REMOTE_REMOTES)
                path = git_path("remotes/%s", remote->name);
@@@ -636,30 -636,30 +636,30 @@@ static int mv(int argc, const char **ar
  
        oldremote = remote_get(rename.old);
        if (!oldremote)
-               die("No such remote: %s", rename.old);
+               die(_("No such remote: %s"), rename.old);
  
        if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
  
        newremote = remote_get(rename.new);
        if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
-               die("remote %s already exists.", rename.new);
+               die(_("remote %s already exists."), rename.new);
  
        strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
        if (!valid_fetch_refspec(buf.buf))
-               die("'%s' is not a valid remote name", rename.new);
+               die(_("'%s' is not a valid remote name"), rename.new);
  
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s", rename.old);
        strbuf_addf(&buf2, "remote.%s", rename.new);
        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
-               return error("Could not rename config section '%s' to '%s'",
+               return error(_("Could not rename config section '%s' to '%s'"),
                                buf.buf, buf2.buf);
  
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", rename.new);
        if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
-               return error("Could not remove config section '%s'", buf.buf);
+               return error(_("Could not remove config section '%s'"), buf.buf);
        strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
                char *ptr;
                                      strlen(rename.old), rename.new,
                                      strlen(rename.new));
                } else
-                       warning("Not updating non-default fetch respec\n"
-                               "\t%s\n"
-                               "\tPlease update the configuration manually if necessary.",
+                       warning(_("Not updating non-default fetch respec\n"
+                                 "\t%s\n"
+                                 "\tPlease update the configuration manually if necessary."),
                                buf2.buf);
  
                if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
-                       return error("Could not append '%s'", buf.buf);
+                       return error(_("Could not append '%s'"), buf.buf);
        }
  
        read_branches();
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
                        if (git_config_set(buf.buf, rename.new)) {
-                               return error("Could not set '%s'", buf.buf);
+                               return error(_("Could not set '%s'"), buf.buf);
                        }
                }
        }
                if (!(flag & REF_ISSYMREF))
                        continue;
                if (delete_ref(item->string, NULL, REF_NODEREF))
-                       die("deleting '%s' failed", item->string);
+                       die(_("deleting '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
                strbuf_addf(&buf2, "remote: renamed %s to %s",
                                item->string, buf.buf);
                if (rename_ref(item->string, buf.buf, buf2.buf))
-                       die("renaming '%s' failed", item->string);
+                       die(_("renaming '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
                strbuf_addf(&buf3, "remote: renamed %s to %s",
                                item->string, buf.buf);
                if (create_symref(buf.buf, buf2.buf, buf3.buf))
-                       die("creating '%s' failed", buf.buf);
+                       die(_("creating '%s' failed"), buf.buf);
        }
        return 0;
  }
@@@ -761,7 -761,7 +761,7 @@@ static int remove_branches(struct strin
                unsigned char *sha1 = item->util;
  
                if (delete_ref(refname, sha1, 0))
-                       result |= error("Could not remove branch %s", refname);
+                       result |= error(_("Could not remove branch %s"), refname);
        }
        return result;
  }
@@@ -789,14 -789,14 +789,14 @@@ static int rm(int argc, const char **ar
  
        remote = remote_get(argv[1]);
        if (!remote)
-               die("No such remote: %s", argv[1]);
+               die(_("No such remote: %s"), argv[1]);
  
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
  
        strbuf_addf(&buf, "remote.%s", remote->name);
        if (git_config_rename_section(buf.buf, NULL) < 1)
-               return error("Could not remove config section '%s'", buf.buf);
+               return error(_("Could not remove config section '%s'"), buf.buf);
  
        read_branches();
        for (i = 0; i < branch_list.nr; i++) {
        string_list_clear(&branches, 1);
  
        if (skipped.nr) {
-               fprintf(stderr, skipped.nr == 1 ?
-                       "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
-                       "to delete it, use:\n" :
-                       "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
-                       "to delete them, use:\n");
+               fprintf_ln(stderr,
+                          Q_("Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+                             "to delete it, use:",
+                             "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+                             "to delete them, use:",
+                             skipped.nr));
                for (i = 0; i < skipped.nr; i++)
                        fprintf(stderr, "  git branch -d %s\n",
                                skipped.items[i].string);
@@@ -886,7 -887,7 +887,7 @@@ static int get_remote_ref_states(const 
  
        states->remote = remote_get(name);
        if (!states->remote)
-               return error("No such remote: %s", name);
+               return error(_("No such remote: %s"), name);
  
        read_branches();
  
@@@ -939,14 -940,14 +940,14 @@@ static int show_remote_info_item(struc
                const char *fmt = "%s";
                const char *arg = "";
                if (string_list_has_string(&states->new, name)) {
-                       fmt = " new (next fetch will store in remotes/%s)";
+                       fmt = _(" new (next fetch will store in remotes/%s)");
                        arg = states->remote->name;
                } else if (string_list_has_string(&states->tracked, name))
-                       arg = " tracked";
+                       arg = _(" tracked");
                else if (string_list_has_string(&states->stale, name))
-                       arg = " stale (use 'git remote prune' to remove)";
+                       arg = _(" stale (use 'git remote prune' to remove)");
                else
-                       arg = " ???";
+                       arg = _(" ???");
                printf("    %-*s", info->width, name);
                printf(fmt, arg);
                printf("\n");
@@@ -987,21 -988,21 +988,21 @@@ static int show_local_info_item(struct 
        int i;
  
        if (branch_info->rebase && branch_info->merge.nr > 1) {
-               error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
+               error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
                        item->string);
                return 0;
        }
  
        printf("    %-*s ", show_info->width, item->string);
        if (branch_info->rebase) {
-               printf("rebases onto remote %s\n", merge->items[0].string);
+               printf_ln(_("rebases onto remote %s"), merge->items[0].string);
                return 0;
        } else if (show_info->any_rebase) {
-               printf(" merges with remote %s\n", merge->items[0].string);
-               also = "    and with remote";
+               printf_ln(_(" merges with remote %s"), merge->items[0].string);
+               also = _("    and with remote");
        } else {
-               printf("merges with remote %s\n", merge->items[0].string);
-               also = "   and with remote";
+               printf_ln(_("merges with remote %s"), merge->items[0].string);
+               also = _("   and with remote");
        }
        for (i = 1; i < merge->nr; i++)
                printf("    %-*s %s %s\n", show_info->width, "", also,
@@@ -1043,36 -1044,43 +1044,43 @@@ static int show_push_info_item(struct s
  {
        struct show_info *show_info = cb_data;
        struct push_info *push_info = item->util;
-       char *src = item->string, *status = NULL;
+       const char *src = item->string, *status = NULL;
  
        switch (push_info->status) {
        case PUSH_STATUS_CREATE:
-               status = "create";
+               status = _("create");
                break;
        case PUSH_STATUS_DELETE:
-               status = "delete";
-               src = "(none)";
+               status = _("delete");
+               src = _("(none)");
                break;
        case PUSH_STATUS_UPTODATE:
-               status = "up to date";
+               status = _("up to date");
                break;
        case PUSH_STATUS_FASTFORWARD:
-               status = "fast-forwardable";
+               status = _("fast-forwardable");
                break;
        case PUSH_STATUS_OUTOFDATE:
-               status = "local out of date";
+               status = _("local out of date");
                break;
        case PUSH_STATUS_NOTQUERIED:
                break;
        }
-       if (status)
-               printf("    %-*s %s to %-*s (%s)\n", show_info->width, src,
-                       push_info->forced ? "forces" : "pushes",
-                       show_info->width2, push_info->dest, status);
-       else
-               printf("    %-*s %s to %s\n", show_info->width, src,
-                       push_info->forced ? "forces" : "pushes",
-                       push_info->dest);
+       if (status) {
+               if (push_info->forced)
+                       printf_ln(_("    %-*s forces to %-*s (%s)"), show_info->width, src,
+                              show_info->width2, push_info->dest, status);
+               else
+                       printf_ln(_("    %-*s pushes to %-*s (%s)"), show_info->width, src,
+                              show_info->width2, push_info->dest, status);
+       } else {
+               if (push_info->forced)
+                       printf_ln(_("    %-*s forces to %s"), show_info->width, src,
+                              push_info->dest);
+               else
+                       printf_ln(_("    %-*s pushes to %s"), show_info->width, src,
+                              push_info->dest);
+       }
        return 0;
  }
  
@@@ -1107,9 -1115,9 +1115,9 @@@ static int show(int argc, const char **
  
                get_remote_ref_states(*argv, &states, query_flag);
  
-               printf("* remote %s\n", *argv);
-               printf("  Fetch URL: %s\n", states.remote->url_nr > 0 ?
-                       states.remote->url[0] : "(no URL)");
+               printf_ln(_("* remote %s"), *argv);
+               printf_ln(_("  Fetch URL: %s"), states.remote->url_nr > 0 ?
+                      states.remote->url[0] : _("(no URL)"));
                if (states.remote->pushurl_nr) {
                        url = states.remote->pushurl;
                        url_nr = states.remote->pushurl_nr;
                        url_nr = states.remote->url_nr;
                }
                for (i = 0; i < url_nr; i++)
-                       printf("  Push  URL: %s\n", url[i]);
+                       printf_ln(_("  Push  URL: %s"), url[i]);
                if (!i)
-                       printf("  Push  URL: %s\n", "(no URL)");
+                       printf_ln(_("  Push  URL: %s"), "(no URL)");
                if (no_query)
-                       printf("  HEAD branch: (not queried)\n");
+                       printf_ln(_("  HEAD branch: %s"), "(not queried)");
                else if (!states.heads.nr)
-                       printf("  HEAD branch: (unknown)\n");
+                       printf_ln(_("  HEAD branch: %s"), "(unknown)");
                else if (states.heads.nr == 1)
-                       printf("  HEAD branch: %s\n", states.heads.items[0].string);
+                       printf_ln(_("  HEAD branch: %s"), states.heads.items[0].string);
                else {
-                       printf("  HEAD branch (remote HEAD is ambiguous,"
-                              " may be one of the following):\n");
+                       printf(_("  HEAD branch (remote HEAD is ambiguous,"
+                                " may be one of the following):\n"));
                        for (i = 0; i < states.heads.nr; i++)
                                printf("    %s\n", states.heads.items[i].string);
                }
                for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
                for_each_string_list(&states.stale, add_remote_to_show_info, &info);
                if (info.list->nr)
-                       printf("  Remote branch%s:%s\n",
-                              info.list->nr > 1 ? "es" : "",
-                               no_query ? " (status not queried)" : "");
+                       printf_ln(Q_("  Remote branch:%s",
+                                    "  Remote branches:%s",
+                                    info.list->nr),
+                                 no_query ? _(" (status not queried)") : "");
                for_each_string_list(info.list, show_remote_info_item, &info);
                string_list_clear(info.list, 0);
  
                info.any_rebase = 0;
                for_each_string_list(&branch_list, add_local_to_show_info, &info);
                if (info.list->nr)
-                       printf("  Local branch%s configured for 'git pull':\n",
-                              info.list->nr > 1 ? "es" : "");
+                       printf_ln(Q_("  Local branch configured for 'git pull':",
+                                    "  Local branches configured for 'git pull':",
+                                    info.list->nr));
                for_each_string_list(info.list, show_local_info_item, &info);
                string_list_clear(info.list, 0);
  
                /* git push info */
                if (states.remote->mirror)
-                       printf("  Local refs will be mirrored by 'git push'\n");
+                       printf_ln(_("  Local refs will be mirrored by 'git push'"));
  
                info.width = info.width2 = 0;
                for_each_string_list(&states.push, add_push_to_show_info, &info);
                qsort(info.list->items, info.list->nr,
                        sizeof(*info.list->items), cmp_string_with_push);
                if (info.list->nr)
-                       printf("  Local ref%s configured for 'git push'%s:\n",
-                               info.list->nr > 1 ? "s" : "",
-                               no_query ? " (status not queried)" : "");
+                       printf_ln(Q_("  Local ref configured for 'git push'%s:",
+                                    "  Local refs configured for 'git push'%s:",
+                                    info.list->nr),
+                                 no_query ? _(" (status not queried)") : "");
                for_each_string_list(info.list, show_push_info_item, &info);
                string_list_clear(info.list, 0);
  
@@@ -1202,10 -1213,10 +1213,10 @@@ static int set_head(int argc, const cha
                memset(&states, 0, sizeof(states));
                get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
                if (!states.heads.nr)
-                       result |= error("Cannot determine remote HEAD");
+                       result |= error(_("Cannot determine remote HEAD"));
                else if (states.heads.nr > 1) {
-                       result |= error("Multiple remote HEAD branches. "
-                                       "Please choose one explicitly with:");
+                       result |= error(_("Multiple remote HEAD branches. "
+                                         "Please choose one explicitly with:"));
                        for (i = 0; i < states.heads.nr; i++)
                                fprintf(stderr, "  git remote set-head %s %s\n",
                                        argv[0], states.heads.items[i].string);
                free_remote_ref_states(&states);
        } else if (opt_d && !opt_a && argc == 1) {
                if (delete_ref(buf.buf, NULL, REF_NODEREF))
-                       result |= error("Could not delete %s", buf.buf);
+                       result |= error(_("Could not delete %s"), buf.buf);
        } else
                usage_with_options(builtin_remote_sethead_usage, options);
  
                strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
                /* make sure it's valid */
                if (!ref_exists(buf2.buf))
-                       result |= error("Not a valid ref: %s", buf2.buf);
+                       result |= error(_("Not a valid ref: %s"), buf2.buf);
                else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
-                       result |= error("Could not setup %s", buf.buf);
+                       result |= error(_("Could not setup %s"), buf.buf);
                if (opt_a)
                        printf("%s/HEAD set to %s\n", argv[0], head_name);
                free(head_name);
@@@ -1260,18 -1271,18 +1271,18 @@@ static int prune_remote(const char *rem
        int result = 0, i;
        struct ref_states states;
        const char *dangling_msg = dry_run
-               ? " %s will become dangling!\n"
-               : " %s has become dangling!\n";
+               ? _(" %s will become dangling!")
+               : _(" %s has become dangling!");
  
        memset(&states, 0, sizeof(states));
        get_remote_ref_states(remote, &states, GET_REF_STATES);
  
        if (states.stale.nr) {
-               printf("Pruning %s\n", remote);
-               printf("URL: %s\n",
+               printf_ln(_("Pruning %s"), remote);
+               printf_ln(_("URL: %s"),
                       states.remote->url_nr
                       ? states.remote->url[0]
-                      : "(no URL)");
+                      : _("(no URL)"));
        }
  
        for (i = 0; i < states.stale.nr; i++) {
                if (!dry_run)
                        result |= delete_ref(refname, NULL, 0);
  
-               printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
-                      abbrev_ref(refname, "refs/remotes/"));
+               if (dry_run)
+                       printf_ln(_(" * [would prune] %s"),
+                              abbrev_ref(refname, "refs/remotes/"));
+               else
+                       printf_ln(_(" * [pruned] %s"),
+                              abbrev_ref(refname, "refs/remotes/"));
                warn_dangling_symref(stdout, dangling_msg, refname);
        }
  
@@@ -1369,7 -1384,7 +1384,7 @@@ static int set_remote_branches(const ch
        strbuf_addf(&key, "remote.%s.fetch", remotename);
  
        if (!remote_is_configured(remotename))
-               die("No such remote '%s'", remotename);
+               die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
  
        if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
@@@ -1396,7 -1411,7 +1411,7 @@@ static int set_branches(int argc, cons
        argc = parse_options(argc, argv, NULL, options,
                             builtin_remote_setbranches_usage, 0);
        if (argc == 0) {
-               error("no remote specified");
+               error(_("no remote specified"));
                usage_with_options(builtin_remote_setbranches_usage, options);
        }
        argv[argc] = NULL;
@@@ -1429,7 -1444,7 +1444,7 @@@ static int set_url(int argc, const cha
                             PARSE_OPT_KEEP_ARGV0);
  
        if (add_mode && delete_mode)
-               die("--add --delete doesn't make sense");
+               die(_("--add --delete doesn't make sense"));
  
        if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3))
                usage_with_options(builtin_remote_seturl_usage, options);
                oldurl = newurl;
  
        if (!remote_is_configured(remotename))
-               die("No such remote '%s'", remotename);
+               die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
  
        if (push_mode) {
  
        /* Old URL specified. Demand that one matches. */
        if (regcomp(&old_regex, oldurl, REG_EXTENDED))
-               die("Invalid old URL pattern: %s", oldurl);
+               die(_("Invalid old URL pattern: %s"), oldurl);
  
        for (i = 0; i < urlset_nr; i++)
                if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
                else
                        negative_matches++;
        if (!delete_mode && !matches)
-               die("No such URL found: %s", oldurl);
+               die(_("No such URL found: %s"), oldurl);
        if (delete_mode && !negative_matches && !push_mode)
-               die("Will not delete all non-push URLs");
+               die(_("Will not delete all non-push URLs"));
  
        regfree(&old_regex);
  
@@@ -1580,7 -1595,7 +1595,7 @@@ int cmd_remote(int argc, const char **a
        else if (!strcmp(argv[0], "update"))
                result = update(argc, argv);
        else {
-               error("Unknown subcommand: %s", argv[0]);
+               error(_("Unknown subcommand: %s"), argv[0]);
                usage_with_options(builtin_remote_usage, options);
        }
  
diff --combined bundle.c
index 27ab32e431660a217d5dce50fe5b5b91888b5908,6d21c77421767161d26a86f744d71b5752e1be76..8d31b98f58b9e9bf156615130ec80684f788fcaa
+++ b/bundle.c
@@@ -33,7 -33,7 +33,7 @@@ static int parse_bundle_header(int fd, 
        if (strbuf_getwholeline_fd(&buf, fd, '\n') ||
            strcmp(buf.buf, bundle_signature)) {
                if (report_path)
-                       error("'%s' does not look like a v2 bundle file",
+                       error(_("'%s' does not look like a v2 bundle file"),
                              report_path);
                status = -1;
                goto abort;
@@@ -60,7 -60,7 +60,7 @@@
                    (40 <= buf.len && !isspace(buf.buf[40])) ||
                    (!is_prereq && buf.len <= 40)) {
                        if (report_path)
-                               error("unrecognized header: %s%s (%d)",
+                               error(_("unrecognized header: %s%s (%d)"),
                                      (is_prereq ? "-" : ""), buf.buf, (int)buf.len);
                        status = -1;
                        break;
@@@ -86,7 -86,7 +86,7 @@@ int read_bundle_header(const char *path
        int fd = open(path, O_RDONLY);
  
        if (fd < 0)
-               return error("could not open '%s'", path);
+               return error(_("could not open '%s'"), path);
        return parse_bundle_header(fd, header, path);
  }
  
@@@ -137,7 -137,7 +137,7 @@@ int verify_bundle(struct bundle_header 
        struct object_array refs;
        struct commit *commit;
        int i, ret = 0, req_nr;
-       const char *message = "Repository lacks these prerequisite commits:";
+       const char *message = _("Repository lacks these prerequisite commits:");
  
        init_revisions(&revs, NULL);
        for (i = 0; i < p->nr; i++) {
        revs.leak_pending = 1;
  
        if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
+               die(_("revision walk setup failed"));
  
        i = req_nr;
        while (i && (commit = get_revision(&revs)))
                struct ref_list *r;
  
                r = &header->references;
-               printf("The bundle contains %d ref%s\n",
-                      r->nr, (1 < r->nr) ? "s" : "");
+               printf_ln(Q_("The bundle contains %d ref",
+                            "The bundle contains %d refs",
+                            r->nr),
+                         r->nr);
                list_refs(r, 0, NULL);
                r = &header->prerequisites;
-               printf("The bundle requires these %d ref%s\n",
-                      r->nr, (1 < r->nr) ? "s" : "");
+               printf_ln(Q_("The bundle requires this ref",
+                            "The bundle requires these %d refs",
+                            r->nr),
+                         r->nr);
                list_refs(r, 0, NULL);
        }
        return ret;
@@@ -283,13 -287,13 +287,13 @@@ int create_bundle(struct bundle_header 
        strbuf_release(&buf);
        fclose(rls_fout);
        if (finish_command(&rls))
-               return error("rev-list died");
+               return error(_("rev-list died"));
  
        /* write references */
        argc = setup_revisions(argc, argv, &revs, NULL);
  
        if (argc > 1)
-               return error("unrecognized argument: %s", argv[1]);
 -              return error(_("unrecognized argument: %s'"), argv[1]);
++              return error(_("unrecognized argument: %s"), argv[1]);
  
        object_array_remove_duplicates(&revs.pending);
  
                 * constraints.
                 */
                if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
-                       warning("ref '%s' is excluded by the rev-list options",
+                       warning(_("ref '%s' is excluded by the rev-list options"),
                                e->name);
                        free(ref);
                        continue;
                free(ref);
        }
        if (!ref_count)
-               die ("Refusing to create empty bundle.");
+               die(_("Refusing to create empty bundle."));
  
        /* end header */
        write_or_die(bundle_fd, "\n", 1);
        rls.out = bundle_fd;
        rls.git_cmd = 1;
        if (start_command(&rls))
-               return error("Could not spawn pack-objects");
+               return error(_("Could not spawn pack-objects"));
  
        /*
         * start_command closed bundle_fd if it was > 1
        }
        close(rls.in);
        if (finish_command(&rls))
-               return error ("pack-objects died");
+               return error(_("pack-objects died"));
        if (!bundle_to_stdout) {
                if (commit_lock_file(&lock))
-                       die_errno("cannot create '%s'", path);
+                       die_errno(_("cannot create '%s'"), path);
        }
        return 0;
  }
@@@ -430,6 -434,6 +434,6 @@@ int unbundle(struct bundle_header *head
        ip.no_stdout = 1;
        ip.git_cmd = 1;
        if (run_command(&ip))
-               return error("index-pack died");
+               return error(_("index-pack died"));
        return 0;
  }
diff --combined cache.h
index 5e419a11cc7c92f60b89d6350597cb4085e34dfb,5bfa94996da622678c8c2b3bf7897ef887c1bada..e14ffcd914be8759a5f5a71fa7fc71e4f9774f0e
+++ b/cache.h
@@@ -105,9 -105,6 +105,9 @@@ struct cache_header 
        unsigned int hdr_entries;
  };
  
 +#define INDEX_FORMAT_LB 2
 +#define INDEX_FORMAT_UB 4
 +
  /*
   * The "cache_time" is just the low 32 bits of the
   * time. It doesn't matter if it overflows - we only
@@@ -118,6 -115,48 +118,6 @@@ struct cache_time 
        unsigned int nsec;
  };
  
 -/*
 - * dev/ino/uid/gid/size are also just tracked to the low 32 bits
 - * Again - this is just a (very strong in practice) heuristic that
 - * the inode hasn't changed.
 - *
 - * We save the fields in big-endian order to allow using the
 - * index file over NFS transparently.
 - */
 -struct ondisk_cache_entry {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -/*
 - * This struct is used when CE_EXTENDED bit is 1
 - * The struct must match ondisk_cache_entry exactly from
 - * ctime till flags
 - */
 -struct ondisk_cache_entry_extended {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      unsigned short flags2;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
  struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
@@@ -214,6 -253,9 +214,6 @@@ static inline size_t ce_namelen(const s
  }
  
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
 -                          ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
 -                          ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@@ -264,11 -306,13 +264,11 @@@ static inline unsigned int canon_mode(u
        return S_IFGITLINK;
  }
  
 -#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 -#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
 -#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
  struct index_state {
        struct cache_entry **cache;
 +      unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
@@@ -580,7 -624,6 +580,7 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 +      PUSH_DEFAULT_SIMPLE,
        PUSH_DEFAULT_UPSTREAM,
        PUSH_DEFAULT_CURRENT,
        PUSH_DEFAULT_UNSPECIFIED
@@@ -877,10 -920,8 +877,8 @@@ enum date_mode 
  };
  
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
- const char *show_date_relative(unsigned long time, int tz,
-                              const struct timeval *now,
-                              char *timebuf,
-                              size_t timebuf_size);
+ void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+                       struct strbuf *timebuf);
  int parse_date(const char *date, char *buf, int bufsize);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
diff --combined refs.c
index 09322fede0841e7954e3e4cb7d3d0b1f673555d7,052c2f6058a3bbf97eeff236b288bf36afe36c7f..a5802e19029747fc95939bc9110ea151c080c30c
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
  #include "tag.h"
  #include "dir.h"
  
 -/* ISSYMREF=0x01, ISPACKED=0x02 and ISBROKEN=0x04 are public interfaces */
 -#define REF_KNOWS_PEELED 0x10
 +/*
 + * Make sure "ref" is something reasonable to have under ".git/refs/";
 + * We do not like it if:
 + *
 + * - any path component of it begins with ".", or
 + * - it has double dots "..", or
 + * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 + * - it ends with a "/".
 + * - it ends with ".lock"
 + * - it contains a "\" (backslash)
 + */
  
 -struct ref_entry {
 -      unsigned char flag; /* ISSYMREF? ISPACKED? */
 +/* 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 == '?' || ch == '[') /* Unsupported */
 +              return 1;
 +      return 0;
 +}
 +
 +/*
 + * Try to read one refname component from the front of refname.  Return
 + * the length of the component found, or -1 if the component is not
 + * legal.
 + */
 +static int check_refname_component(const char *refname, int flags)
 +{
 +      const char *cp;
 +      char last = '\0';
 +
 +      for (cp = refname; ; 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;
 +      }
 +      if (cp == refname)
 +              return 0; /* Component has zero length. */
 +      if (refname[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 (refname[1] == '\0')
 +                      return -1; /* Component equals ".". */
 +      }
 +      if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
 +              return -1; /* Refname ends with ".lock". */
 +      return cp - refname;
 +}
 +
 +int check_refname_format(const char *refname, int flags)
 +{
 +      int component_len, component_count = 0;
 +
 +      while (1) {
 +              /* We are at the start of a path component. */
 +              component_len = check_refname_component(refname, flags);
 +              if (component_len <= 0) {
 +                      if ((flags & REFNAME_REFSPEC_PATTERN) &&
 +                                      refname[0] == '*' &&
 +                                      (refname[1] == '\0' || refname[1] == '/')) {
 +                              /* Accept one wildcard as a full refname component. */
 +                              flags &= ~REFNAME_REFSPEC_PATTERN;
 +                              component_len = 1;
 +                      } else {
 +                              return -1;
 +                      }
 +              }
 +              component_count++;
 +              if (refname[component_len] == '\0')
 +                      break;
 +              /* Skip to next component. */
 +              refname += component_len + 1;
 +      }
 +
 +      if (refname[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;
 +}
 +
 +struct ref_entry;
 +
 +struct ref_value {
        unsigned char sha1[20];
        unsigned char peeled[20];
 -      /* The full name of the reference (e.g., "refs/heads/master"): */
 -      char name[FLEX_ARRAY];
  };
  
 -struct ref_array {
 +struct ref_dir {
        int nr, alloc;
  
        /*
         */
        int sorted;
  
 -      struct ref_entry **refs;
 +      struct ref_entry **entries;
  };
  
 +/* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */
 +#define REF_KNOWS_PEELED 0x08
 +#define REF_DIR 0x10
 +
  /*
 - * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
 - * Return a pointer to the refname within the line (null-terminated),
 - * or NULL if there was a problem.
 + * A ref_entry represents either a reference or a "subdirectory" of
 + * references.  Each directory in the reference namespace is
 + * represented by a ref_entry with (flags & REF_DIR) set and
 + * containing a subdir member that holds the entries in that
 + * directory.  References are represented by a ref_entry with (flags &
 + * REF_DIR) unset and a value member that describes the reference's
 + * value.  The flag member is at the ref_entry level, but it is also
 + * needed to interpret the contents of the value field (in other
 + * words, a ref_value object is not very much use without the
 + * enclosing ref_entry).
 + *
 + * Reference names cannot end with slash and directories' names are
 + * always stored with a trailing slash (except for the top-level
 + * directory, which is always denoted by "").  This has two nice
 + * consequences: (1) when the entries in each subdir are sorted
 + * lexicographically by name (as they usually are), the references in
 + * a whole tree can be generated in lexicographic order by traversing
 + * the tree in left-to-right, depth-first order; (2) the names of
 + * references and subdirectories cannot conflict, and therefore the
 + * presence of an empty subdirectory does not block the creation of a
 + * similarly-named reference.  (The fact that reference names with the
 + * same leading components can conflict *with each other* is a
 + * separate issue that is regulated by is_refname_available().)
 + *
 + * Please note that the name field contains the fully-qualified
 + * reference (or subdirectory) name.  Space could be saved by only
 + * storing the relative names.  But that would require the full names
 + * to be generated on the fly when iterating in do_for_each_ref(), and
 + * would break callback functions, who have always been able to assume
 + * that the name strings that they are passed will not be freed during
 + * the iteration.
   */
 -static const char *parse_ref_line(char *line, unsigned char *sha1)
 -{
 +struct ref_entry {
 +      unsigned char flag; /* ISSYMREF? ISPACKED? */
 +      union {
 +              struct ref_value value; /* if not (flags&REF_DIR) */
 +              struct ref_dir subdir; /* if (flags&REF_DIR) */
 +      } u;
        /*
 -       * 42: the answer to everything.
 -       *
 -       * In this case, it happens to be the answer to
 -       *  40 (length of sha1 hex representation)
 -       *  +1 (space in between hex and name)
 -       *  +1 (newline at the end of the line)
 +       * The full name of the reference (e.g., "refs/heads/master")
 +       * or the full name of the directory with a trailing slash
 +       * (e.g., "refs/heads/"):
         */
 -      int len = strlen(line) - 42;
 -
 -      if (len <= 0)
 -              return NULL;
 -      if (get_sha1_hex(line, sha1) < 0)
 -              return NULL;
 -      if (!isspace(line[40]))
 -              return NULL;
 -      line += 41;
 -      if (isspace(*line))
 -              return NULL;
 -      if (line[len] != '\n')
 -              return NULL;
 -      line[len] = 0;
 -
 -      return line;
 -}
 +      char name[FLEX_ARRAY];
 +};
  
  static struct ref_entry *create_ref_entry(const char *refname,
                                          const unsigned char *sha1, int flag,
                die("Reference has invalid format: '%s'", refname);
        len = strlen(refname) + 1;
        ref = xmalloc(sizeof(struct ref_entry) + len);
 -      hashcpy(ref->sha1, sha1);
 -      hashclr(ref->peeled);
 +      hashcpy(ref->u.value.sha1, sha1);
 +      hashclr(ref->u.value.peeled);
        memcpy(ref->name, refname, len);
        ref->flag = flag;
        return ref;
  }
  
 -/* Add a ref_entry to the end of the ref_array (unsorted). */
 -static void add_ref(struct ref_array *refs, struct ref_entry *ref)
 +static void clear_ref_dir(struct ref_dir *dir);
 +
 +static void free_ref_entry(struct ref_entry *entry)
 +{
 +      if (entry->flag & REF_DIR)
 +              clear_ref_dir(&entry->u.subdir);
 +      free(entry);
 +}
 +
 +/*
 + * Add a ref_entry to the end of dir (unsorted).  Entry is always
 + * stored directly in dir; no recursion into subdirectories is
 + * done.
 + */
 +static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
  {
 -      ALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc);
 -      refs->refs[refs->nr++] = ref;
 +      ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
 +      dir->entries[dir->nr++] = entry;
 +}
 +
 +/*
 + * Clear and free all entries in dir, recursively.
 + */
 +static void clear_ref_dir(struct ref_dir *dir)
 +{
 +      int i;
 +      for (i = 0; i < dir->nr; i++)
 +              free_ref_entry(dir->entries[i]);
 +      free(dir->entries);
 +      dir->sorted = dir->nr = dir->alloc = 0;
 +      dir->entries = NULL;
 +}
 +
 +/*
 + * Create a struct ref_entry object for the specified dirname.
 + * dirname is the name of the directory with a trailing slash (e.g.,
 + * "refs/heads/") or "" for the top-level directory.
 + */
 +static struct ref_entry *create_dir_entry(const char *dirname)
 +{
 +      struct ref_entry *direntry;
 +      int len = strlen(dirname);
 +      direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
 +      memcpy(direntry->name, dirname, len + 1);
 +      direntry->flag = REF_DIR;
 +      return direntry;
  }
  
  static int ref_entry_cmp(const void *a, const void *b)
        return strcmp(one->name, two->name);
  }
  
 +static void sort_ref_dir(struct ref_dir *dir);
 +
 +/*
 + * Return the entry with the given refname from the ref_dir
 + * (non-recursively), sorting dir if necessary.  Return NULL if no
 + * such entry is found.
 + */
 +static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)
 +{
 +      struct ref_entry *e, **r;
 +      int len;
 +
 +      if (refname == NULL || !dir->nr)
 +              return NULL;
 +
 +      sort_ref_dir(dir);
 +
 +      len = strlen(refname) + 1;
 +      e = xmalloc(sizeof(struct ref_entry) + len);
 +      memcpy(e->name, refname, len);
 +
 +      r = bsearch(&e, dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
 +
 +      free(e);
 +
 +      if (r == NULL)
 +              return NULL;
 +
 +      return *r;
 +}
 +
 +/*
 + * If refname is a reference name, find the ref_dir within the dir
 + * tree that should hold refname.  If refname is a directory name
 + * (i.e., ends in '/'), then return that ref_dir itself.  dir must
 + * represent the top-level directory.  Sort ref_dirs and recurse into
 + * subdirectories as necessary.  If mkdir is set, then create any
 + * missing directories; otherwise, return NULL if the desired
 + * directory cannot be found.
 + */
 +static struct ref_dir *find_containing_dir(struct ref_dir *dir,
 +                                         const char *refname, int mkdir)
 +{
 +      char *refname_copy = xstrdup(refname);
 +      char *slash;
 +      struct ref_entry *entry;
 +      for (slash = strchr(refname_copy, '/'); slash; slash = strchr(slash + 1, '/')) {
 +              char tmp = slash[1];
 +              slash[1] = '\0';
 +              entry = search_ref_dir(dir, refname_copy);
 +              if (!entry) {
 +                      if (!mkdir) {
 +                              dir = NULL;
 +                              break;
 +                      }
 +                      entry = create_dir_entry(refname_copy);
 +                      add_entry_to_dir(dir, entry);
 +              }
 +              slash[1] = tmp;
 +              assert(entry->flag & REF_DIR);
 +              dir = &entry->u.subdir;
 +      }
 +
 +      free(refname_copy);
 +      return dir;
 +}
 +
 +/*
 + * Find the value entry with the given name in dir, sorting ref_dirs
 + * and recursing into subdirectories as necessary.  If the name is not
 + * found or it corresponds to a directory entry, return NULL.
 + */
 +static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
 +{
 +      struct ref_entry *entry;
 +      dir = find_containing_dir(dir, refname, 0);
 +      if (!dir)
 +              return NULL;
 +      entry = search_ref_dir(dir, refname);
 +      return (entry && !(entry->flag & REF_DIR)) ? entry : NULL;
 +}
 +
 +/*
 + * Add a ref_entry to the ref_dir (unsorted), recursing into
 + * subdirectories as necessary.  dir must represent the top-level
 + * directory.  Return 0 on success.
 + */
 +static int add_ref(struct ref_dir *dir, struct ref_entry *ref)
 +{
 +      dir = find_containing_dir(dir, ref->name, 1);
 +      if (!dir)
 +              return -1;
 +      add_entry_to_dir(dir, ref);
 +      return 0;
 +}
 +
  /*
   * Emit a warning and return true iff ref1 and ref2 have the same name
   * and the same sha1.  Die if they have the same name but different
   */
  static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
  {
 -      if (!strcmp(ref1->name, ref2->name)) {
 -              /* Duplicate name; make sure that the SHA1s match: */
 -              if (hashcmp(ref1->sha1, ref2->sha1))
 -                      die("Duplicated ref, and SHA1s don't match: %s",
 -                          ref1->name);
 -              warning("Duplicated ref: %s", ref1->name);
 -              return 1;
 -      } else {
 +      if (strcmp(ref1->name, ref2->name))
                return 0;
 -      }
 +
 +      /* Duplicate name; make sure that they don't conflict: */
 +
 +      if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))
 +              /* This is impossible by construction */
 +              die("Reference directory conflict: %s", ref1->name);
 +
 +      if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))
 +              die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
 +
 +      warning("Duplicated ref: %s", ref1->name);
 +      return 1;
  }
  
  /*
 - * Sort the entries in array (if they are not already sorted).
 + * Sort the entries in dir non-recursively (if they are not already
 + * sorted) and remove any duplicate entries.
   */
 -static void sort_ref_array(struct ref_array *array)
 +static void sort_ref_dir(struct ref_dir *dir)
  {
        int i, j;
 +      struct ref_entry *last = NULL;
  
        /*
         * This check also prevents passing a zero-length array to qsort(),
         * which is a problem on some platforms.
         */
 -      if (array->sorted == array->nr)
 +      if (dir->sorted == dir->nr)
                return;
  
 -      qsort(array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);
 +      qsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
  
 -      /* Remove any duplicates from the ref_array */
 -      i = 0;
 -      for (j = 1; j < array->nr; j++) {
 -              if (is_dup_ref(array->refs[i], array->refs[j])) {
 -                      free(array->refs[j]);
 -                      continue;
 +      /* Remove any duplicates: */
 +      for (i = 0, j = 0; j < dir->nr; j++) {
 +              struct ref_entry *entry = dir->entries[j];
 +              if (last && is_dup_ref(last, entry))
 +                      free_ref_entry(entry);
 +              else
 +                      last = dir->entries[i++] = entry;
 +      }
 +      dir->sorted = dir->nr = i;
 +}
 +
 +#define DO_FOR_EACH_INCLUDE_BROKEN 01
 +
 +static struct ref_entry *current_ref;
 +
 +static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 +                    int flags, void *cb_data, struct ref_entry *entry)
 +{
 +      int retval;
 +      if (prefixcmp(entry->name, base))
 +              return 0;
 +
 +      if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
 +              if (entry->flag & REF_ISBROKEN)
 +                      return 0; /* ignore broken refs e.g. dangling symref */
 +              if (!has_sha1_file(entry->u.value.sha1)) {
 +                      error("%s does not point to a valid object!", entry->name);
 +                      return 0;
                }
 -              array->refs[++i] = array->refs[j];
        }
 -      array->sorted = array->nr = i + 1;
 +      current_ref = entry;
 +      retval = fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);
 +      current_ref = NULL;
 +      return retval;
  }
  
 -static struct ref_entry *search_ref_array(struct ref_array *array, const char *refname)
 +/*
 + * Call fn for each reference in dir that has index in the range
 + * offset <= index < dir->nr.  Recurse into subdirectories that are in
 + * that index range, sorting them before iterating.  This function
 + * does not sort dir itself; it should be sorted beforehand.
 + */
 +static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
 +                                const char *base,
 +                                each_ref_fn fn, int trim, int flags, void *cb_data)
  {
 -      struct ref_entry *e, **r;
 -      int len;
 +      int i;
 +      assert(dir->sorted == dir->nr);
 +      for (i = offset; i < dir->nr; i++) {
 +              struct ref_entry *entry = dir->entries[i];
 +              int retval;
 +              if (entry->flag & REF_DIR) {
 +                      sort_ref_dir(&entry->u.subdir);
 +                      retval = do_for_each_ref_in_dir(&entry->u.subdir, 0,
 +                                                      base, fn, trim, flags, cb_data);
 +              } else {
 +                      retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
 +              }
 +              if (retval)
 +                      return retval;
 +      }
 +      return 0;
 +}
  
 -      if (refname == NULL)
 -              return NULL;
 +/*
 + * Call fn for each reference in the union of dir1 and dir2, in order
 + * by refname.  Recurse into subdirectories.  If a value entry appears
 + * in both dir1 and dir2, then only process the version that is in
 + * dir2.  The input dirs must already be sorted, but subdirs will be
 + * sorted as needed.
 + */
 +static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
 +                                 struct ref_dir *dir2,
 +                                 const char *base, each_ref_fn fn, int trim,
 +                                 int flags, void *cb_data)
 +{
 +      int retval;
 +      int i1 = 0, i2 = 0;
  
 -      if (!array->nr)
 -              return NULL;
 -      sort_ref_array(array);
 -      len = strlen(refname) + 1;
 -      e = xmalloc(sizeof(struct ref_entry) + len);
 -      memcpy(e->name, refname, len);
 +      assert(dir1->sorted == dir1->nr);
 +      assert(dir2->sorted == dir2->nr);
 +      while (1) {
 +              struct ref_entry *e1, *e2;
 +              int cmp;
 +              if (i1 == dir1->nr) {
 +                      return do_for_each_ref_in_dir(dir2, i2,
 +                                                    base, fn, trim, flags, cb_data);
 +              }
 +              if (i2 == dir2->nr) {
 +                      return do_for_each_ref_in_dir(dir1, i1,
 +                                                    base, fn, trim, flags, cb_data);
 +              }
 +              e1 = dir1->entries[i1];
 +              e2 = dir2->entries[i2];
 +              cmp = strcmp(e1->name, e2->name);
 +              if (cmp == 0) {
 +                      if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
 +                              /* Both are directories; descend them in parallel. */
 +                              sort_ref_dir(&e1->u.subdir);
 +                              sort_ref_dir(&e2->u.subdir);
 +                              retval = do_for_each_ref_in_dirs(
 +                                              &e1->u.subdir, &e2->u.subdir,
 +                                              base, fn, trim, flags, cb_data);
 +                              i1++;
 +                              i2++;
 +                      } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
 +                              /* Both are references; ignore the one from dir1. */
 +                              retval = do_one_ref(base, fn, trim, flags, cb_data, e2);
 +                              i1++;
 +                              i2++;
 +                      } else {
 +                              die("conflict between reference and directory: %s",
 +                                  e1->name);
 +                      }
 +              } else {
 +                      struct ref_entry *e;
 +                      if (cmp < 0) {
 +                              e = e1;
 +                              i1++;
 +                      } else {
 +                              e = e2;
 +                              i2++;
 +                      }
 +                      if (e->flag & REF_DIR) {
 +                              sort_ref_dir(&e->u.subdir);
 +                              retval = do_for_each_ref_in_dir(
 +                                              &e->u.subdir, 0,
 +                                              base, fn, trim, flags, cb_data);
 +                      } else {
 +                              retval = do_one_ref(base, fn, trim, flags, cb_data, e);
 +                      }
 +              }
 +              if (retval)
 +                      return retval;
 +      }
 +      if (i1 < dir1->nr)
 +              return do_for_each_ref_in_dir(dir1, i1,
 +                                            base, fn, trim, flags, cb_data);
 +      if (i2 < dir2->nr)
 +              return do_for_each_ref_in_dir(dir2, i2,
 +                                            base, fn, trim, flags, cb_data);
 +      return 0;
 +}
  
 -      r = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);
 +/*
 + * Return true iff refname1 and refname2 conflict with each other.
 + * Two reference names conflict if one of them exactly matches the
 + * leading components of the other; e.g., "foo/bar" conflicts with
 + * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
 + * "foo/barbados".
 + */
 +static int names_conflict(const char *refname1, const char *refname2)
 +{
 +      for (; *refname1 && *refname1 == *refname2; refname1++, refname2++)
 +              ;
 +      return (*refname1 == '\0' && *refname2 == '/')
 +              || (*refname1 == '/' && *refname2 == '\0');
 +}
  
 -      free(e);
 +struct name_conflict_cb {
 +      const char *refname;
 +      const char *oldrefname;
 +      const char *conflicting_refname;
 +};
  
 -      if (r == NULL)
 -              return NULL;
 +static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
 +                          int flags, void *cb_data)
 +{
 +      struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
 +      if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
 +              return 0;
 +      if (names_conflict(data->refname, existingrefname)) {
 +              data->conflicting_refname = existingrefname;
 +              return 1;
 +      }
 +      return 0;
 +}
  
 -      return *r;
 +/*
 + * Return true iff a reference named refname could be created without
 + * conflicting with the name of an existing reference in array.  If
 + * oldrefname is non-NULL, ignore potential conflicts with oldrefname
 + * (e.g., because oldrefname is scheduled for deletion in the same
 + * operation).
 + */
 +static int is_refname_available(const char *refname, const char *oldrefname,
 +                              struct ref_dir *dir)
 +{
 +      struct name_conflict_cb data;
 +      data.refname = refname;
 +      data.oldrefname = oldrefname;
 +      data.conflicting_refname = NULL;
 +
 +      sort_ref_dir(dir);
 +      if (do_for_each_ref_in_dir(dir, 0, "", name_conflict_fn,
 +                                 0, DO_FOR_EACH_INCLUDE_BROKEN,
 +                                 &data)) {
 +              error("'%s' exists; cannot create '%s'",
 +                    data.conflicting_refname, refname);
 +              return 0;
 +      }
 +      return 1;
  }
  
  /*
@@@ -594,23 -175,35 +594,23 @@@ static struct ref_cache 
        struct ref_cache *next;
        char did_loose;
        char did_packed;
 -      struct ref_array loose;
 -      struct ref_array packed;
 +      struct ref_dir loose;
 +      struct ref_dir packed;
        /* The submodule name, or "" for the main repo. */
        char name[FLEX_ARRAY];
  } *ref_cache;
  
 -static struct ref_entry *current_ref;
 -
 -static void clear_ref_array(struct ref_array *array)
 -{
 -      int i;
 -      for (i = 0; i < array->nr; i++)
 -              free(array->refs[i]);
 -      free(array->refs);
 -      array->sorted = array->nr = array->alloc = 0;
 -      array->refs = NULL;
 -}
 -
  static void clear_packed_ref_cache(struct ref_cache *refs)
  {
        if (refs->did_packed)
 -              clear_ref_array(&refs->packed);
 +              clear_ref_dir(&refs->packed);
        refs->did_packed = 0;
  }
  
  static void clear_loose_ref_cache(struct ref_cache *refs)
  {
        if (refs->did_loose)
 -              clear_ref_array(&refs->loose);
 +              clear_ref_dir(&refs->loose);
        refs->did_loose = 0;
  }
  
@@@ -643,53 -236,20 +643,53 @@@ static struct ref_cache *get_ref_cache(
                refs = refs->next;
        }
  
 -      refs = create_ref_cache(submodule);
 -      refs->next = ref_cache;
 -      ref_cache = refs;
 -      return refs;
 -}
 +      refs = create_ref_cache(submodule);
 +      refs->next = ref_cache;
 +      ref_cache = refs;
 +      return refs;
 +}
 +
 +void invalidate_ref_cache(const char *submodule)
 +{
 +      struct ref_cache *refs = get_ref_cache(submodule);
 +      clear_packed_ref_cache(refs);
 +      clear_loose_ref_cache(refs);
 +}
 +
 +/*
 + * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
 + * Return a pointer to the refname within the line (null-terminated),
 + * or NULL if there was a problem.
 + */
 +static const char *parse_ref_line(char *line, unsigned char *sha1)
 +{
 +      /*
 +       * 42: the answer to everything.
 +       *
 +       * In this case, it happens to be the answer to
 +       *  40 (length of sha1 hex representation)
 +       *  +1 (space in between hex and name)
 +       *  +1 (newline at the end of the line)
 +       */
 +      int len = strlen(line) - 42;
 +
 +      if (len <= 0)
 +              return NULL;
 +      if (get_sha1_hex(line, sha1) < 0)
 +              return NULL;
 +      if (!isspace(line[40]))
 +              return NULL;
 +      line += 41;
 +      if (isspace(*line))
 +              return NULL;
 +      if (line[len] != '\n')
 +              return NULL;
 +      line[len] = 0;
  
 -void invalidate_ref_cache(const char *submodule)
 -{
 -      struct ref_cache *refs = get_ref_cache(submodule);
 -      clear_packed_ref_cache(refs);
 -      clear_loose_ref_cache(refs);
 +      return line;
  }
  
 -static void read_packed_refs(FILE *f, struct ref_array *array)
 +static void read_packed_refs(FILE *f, struct ref_dir *dir)
  {
        struct ref_entry *last = NULL;
        char refline[PATH_MAX];
                refname = parse_ref_line(refline, sha1);
                if (refname) {
                        last = create_ref_entry(refname, sha1, flag, 1);
 -                      add_ref(array, last);
 +                      add_ref(dir, last);
                        continue;
                }
                if (last &&
                    strlen(refline) == 42 &&
                    refline[41] == '\n' &&
                    !get_sha1_hex(refline + 1, sha1))
 -                      hashcpy(last->peeled, sha1);
 +                      hashcpy(last->u.value.peeled, sha1);
        }
  }
  
 -static struct ref_array *get_packed_refs(struct ref_cache *refs)
 +static struct ref_dir *get_packed_refs(struct ref_cache *refs)
  {
        if (!refs->did_packed) {
                const char *packed_refs_file;
@@@ -750,9 -310,9 +750,9 @@@ void add_packed_ref(const char *refname
  }
  
  static void get_ref_dir(struct ref_cache *refs, const char *base,
 -                      struct ref_array *array)
 +                      struct ref_dir *dir)
  {
 -      DIR *dir;
 +      DIR *d;
        const char *path;
  
        if (*refs->name)
        else
                path = git_path("%s", base);
  
 -
 -      dir = opendir(path);
 -
 -      if (dir) {
 +      d = opendir(path);
 +      if (d) {
                struct dirent *de;
                int baselen = strlen(base);
                char *refname = xmalloc(baselen + 257);
                if (baselen && base[baselen-1] != '/')
                        refname[baselen++] = '/';
  
 -              while ((de = readdir(dir)) != NULL) {
 +              while ((de = readdir(d)) != NULL) {
                        unsigned char sha1[20];
                        struct stat st;
                        int flag;
                        if (stat(refdir, &st) < 0)
                                continue;
                        if (S_ISDIR(st.st_mode)) {
 -                              get_ref_dir(refs, refname, array);
 +                              get_ref_dir(refs, refname, dir);
                                continue;
                        }
                        if (*refs->name) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        }
 -                      add_ref(array, create_ref_entry(refname, sha1, flag, 1));
 +                      add_ref(dir, create_ref_entry(refname, sha1, flag, 1));
                }
                free(refname);
 -              closedir(dir);
 +              closedir(d);
        }
  }
  
 -struct warn_if_dangling_data {
 -      FILE *fp;
 -      const char *refname;
 -      const char *msg_fmt;
 -};
 -
 -static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
 -                                 int flags, void *cb_data)
 -{
 -      struct warn_if_dangling_data *d = cb_data;
 -      const char *resolves_to;
 -      unsigned char junk[20];
 -
 -      if (!(flags & REF_ISSYMREF))
 -              return 0;
 -
 -      resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
 -      if (!resolves_to || strcmp(resolves_to, d->refname))
 -              return 0;
 -
 -      fprintf(d->fp, d->msg_fmt, refname);
 -      fputc('\n', d->fp);
 -      return 0;
 -}
 -
 -void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 -{
 -      struct warn_if_dangling_data data;
 -
 -      data.fp = fp;
 -      data.refname = refname;
 -      data.msg_fmt = msg_fmt;
 -      for_each_rawref(warn_if_dangling_symref, &data);
 -}
 -
 -static struct ref_array *get_loose_refs(struct ref_cache *refs)
 +static struct ref_dir *get_loose_refs(struct ref_cache *refs)
  {
        if (!refs->did_loose) {
                get_ref_dir(refs, "refs", &refs->loose);
@@@ -834,13 -431,13 +834,13 @@@ static int resolve_gitlink_packed_ref(s
                                      const char *refname, unsigned char *sha1)
  {
        struct ref_entry *ref;
 -      struct ref_array *array = get_packed_refs(refs);
 +      struct ref_dir *dir = get_packed_refs(refs);
  
 -      ref = search_ref_array(array, refname);
 +      ref = find_ref(dir, refname);
        if (ref == NULL)
                return -1;
  
 -      memcpy(sha1, ref->sha1, 20);
 +      memcpy(sha1, ref->u.value.sha1, 20);
        return 0;
  }
  
@@@ -907,10 -504,10 +907,10 @@@ int resolve_gitlink_ref(const char *pat
   */
  static int get_packed_ref(const char *refname, unsigned char *sha1)
  {
 -      struct ref_array *packed = get_packed_refs(get_ref_cache(NULL));
 -      struct ref_entry *entry = search_ref_array(packed, refname);
 +      struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
 +      struct ref_entry *entry = find_ref(packed, refname);
        if (entry) {
 -              hashcpy(sha1, entry->sha1);
 +              hashcpy(sha1, entry->u.value.sha1);
                return 0;
        }
        return -1;
@@@ -1049,10 -646,23 +1049,10 @@@ int read_ref(const char *refname, unsig
        return read_ref_full(refname, sha1, 1, NULL);
  }
  
 -#define DO_FOR_EACH_INCLUDE_BROKEN 01
 -static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 -                    int flags, void *cb_data, struct ref_entry *entry)
 +int ref_exists(const char *refname)
  {
 -      if (prefixcmp(entry->name, base))
 -              return 0;
 -
 -      if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
 -              if (entry->flag & REF_ISBROKEN)
 -                      return 0; /* ignore broken refs e.g. dangling symref */
 -              if (!has_sha1_file(entry->sha1)) {
 -                      error("%s does not point to a valid object!", entry->name);
 -                      return 0;
 -              }
 -      }
 -      current_ref = entry;
 -      return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
 +      unsigned char sha1[20];
 +      return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
  }
  
  static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
@@@ -1073,10 -683,10 +1073,10 @@@ int peel_ref(const char *refname, unsig
        if (current_ref && (current_ref->name == refname
                || !strcmp(current_ref->name, refname))) {
                if (current_ref->flag & REF_KNOWS_PEELED) {
 -                      hashcpy(sha1, current_ref->peeled);
 +                      hashcpy(sha1, current_ref->u.value.peeled);
                        return 0;
                }
 -              hashcpy(base, current_ref->sha1);
 +              hashcpy(base, current_ref->u.value.sha1);
                goto fallback;
        }
  
                return -1;
  
        if ((flag & REF_ISPACKED)) {
 -              struct ref_array *array = get_packed_refs(get_ref_cache(NULL));
 -              struct ref_entry *r = search_ref_array(array, refname);
 +              struct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));
 +              struct ref_entry *r = find_ref(dir, refname);
  
                if (r != NULL && r->flag & REF_KNOWS_PEELED) {
 -                      hashcpy(sha1, r->peeled);
 +                      hashcpy(sha1, r->u.value.peeled);
                        return 0;
                }
        }
@@@ -1105,74 -715,50 +1105,75 @@@ fallback
        return -1;
  }
  
 +struct warn_if_dangling_data {
 +      FILE *fp;
 +      const char *refname;
 +      const char *msg_fmt;
 +};
 +
 +static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
 +                                 int flags, void *cb_data)
 +{
 +      struct warn_if_dangling_data *d = cb_data;
 +      const char *resolves_to;
 +      unsigned char junk[20];
 +
 +      if (!(flags & REF_ISSYMREF))
 +              return 0;
 +
 +      resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
 +      if (!resolves_to || strcmp(resolves_to, d->refname))
 +              return 0;
 +
 +      fprintf(d->fp, d->msg_fmt, refname);
++      fputc('\n', d->fp);
 +      return 0;
 +}
 +
 +void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 +{
 +      struct warn_if_dangling_data data;
 +
 +      data.fp = fp;
 +      data.refname = refname;
 +      data.msg_fmt = msg_fmt;
 +      for_each_rawref(warn_if_dangling_symref, &data);
 +}
 +
  static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
                           int trim, int flags, void *cb_data)
  {
 -      int retval = 0, p = 0, l = 0;
        struct ref_cache *refs = get_ref_cache(submodule);
 -      struct ref_array *packed = get_packed_refs(refs);
 -      struct ref_array *loose = get_loose_refs(refs);
 -
 -      sort_ref_array(packed);
 -      sort_ref_array(loose);
 -      while (p < packed->nr && l < loose->nr) {
 -              struct ref_entry *entry;
 -              int cmp = strcmp(packed->refs[p]->name, loose->refs[l]->name);
 -              if (!cmp) {
 -                      p++;
 -                      continue;
 -              }
 -              if (cmp > 0) {
 -                      entry = loose->refs[l++];
 -              } else {
 -                      entry = packed->refs[p++];
 -              }
 -              retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
 -              if (retval)
 -                      goto end_each;
 -      }
 -
 -      if (l < loose->nr) {
 -              p = l;
 -              packed = loose;
 -      }
 +      struct ref_dir *packed_dir = get_packed_refs(refs);
 +      struct ref_dir *loose_dir = get_loose_refs(refs);
 +      int retval = 0;
  
 -      for (; p < packed->nr; p++) {
 -              retval = do_one_ref(base, fn, trim, flags, cb_data, packed->refs[p]);
 -              if (retval)
 -                      goto end_each;
 +      if (base && *base) {
 +              packed_dir = find_containing_dir(packed_dir, base, 0);
 +              loose_dir = find_containing_dir(loose_dir, base, 0);
 +      }
 +
 +      if (packed_dir && loose_dir) {
 +              sort_ref_dir(packed_dir);
 +              sort_ref_dir(loose_dir);
 +              retval = do_for_each_ref_in_dirs(
 +                              packed_dir, loose_dir,
 +                              base, fn, trim, flags, cb_data);
 +      } else if (packed_dir) {
 +              sort_ref_dir(packed_dir);
 +              retval = do_for_each_ref_in_dir(
 +                              packed_dir, 0,
 +                              base, fn, trim, flags, cb_data);
 +      } else if (loose_dir) {
 +              sort_ref_dir(loose_dir);
 +              retval = do_for_each_ref_in_dir(
 +                              loose_dir, 0,
 +                              base, fn, trim, flags, cb_data);
        }
  
 -end_each:
 -      current_ref = NULL;
        return retval;
  }
  
 -
  static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
  {
        unsigned char sha1[20];
@@@ -1323,6 -909,101 +1324,6 @@@ int for_each_rawref(each_ref_fn fn, voi
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
  }
  
 -/*
 - * Make sure "ref" is something reasonable to have under ".git/refs/";
 - * We do not like it if:
 - *
 - * - any path component of it begins with ".", or
 - * - it has double dots "..", or
 - * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 - * - it ends with a "/".
 - * - it ends with ".lock"
 - * - 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 == '?' || ch == '[') /* Unsupported */
 -              return 1;
 -      return 0;
 -}
 -
 -/*
 - * Try to read one refname component from the front of refname.  Return
 - * the length of the component found, or -1 if the component is not
 - * legal.
 - */
 -static int check_refname_component(const char *refname, int flags)
 -{
 -      const char *cp;
 -      char last = '\0';
 -
 -      for (cp = refname; ; 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;
 -      }
 -      if (cp == refname)
 -              return -1; /* Component has zero length. */
 -      if (refname[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 (refname[1] == '\0')
 -                      return -1; /* Component equals ".". */
 -      }
 -      if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
 -              return -1; /* Refname ends with ".lock". */
 -      return cp - refname;
 -}
 -
 -int check_refname_format(const char *refname, int flags)
 -{
 -      int component_len, component_count = 0;
 -
 -      while (1) {
 -              /* We are at the start of a path component. */
 -              component_len = check_refname_component(refname, flags);
 -              if (component_len < 0) {
 -                      if ((flags & REFNAME_REFSPEC_PATTERN) &&
 -                                      refname[0] == '*' &&
 -                                      (refname[1] == '\0' || refname[1] == '/')) {
 -                              /* Accept one wildcard as a full refname component. */
 -                              flags &= ~REFNAME_REFSPEC_PATTERN;
 -                              component_len = 1;
 -                      } else {
 -                              return -1;
 -                      }
 -              }
 -              component_count++;
 -              if (refname[component_len] == '\0')
 -                      break;
 -              /* Skip to next component. */
 -              refname += component_len + 1;
 -      }
 -
 -      if (refname[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)
  {
        return name + (
@@@ -1392,6 -1073,35 +1393,6 @@@ static int remove_empty_directories(con
        return result;
  }
  
 -/*
 - * Return true iff a reference named refname could be created without
 - * conflicting with the name of an existing reference.  If oldrefname
 - * is non-NULL, ignore potential conflicts with oldrefname (e.g.,
 - * because oldrefname is scheduled for deletion in the same
 - * operation).
 - */
 -static int is_refname_available(const char *refname, const char *oldrefname,
 -                              struct ref_array *array)
 -{
 -      int i, namlen = strlen(refname); /* e.g. 'foo/bar' */
 -      for (i = 0; i < array->nr; i++ ) {
 -              struct ref_entry *entry = array->refs[i];
 -              /* entry->name could be 'foo' or 'foo/bar/baz' */
 -              if (!oldrefname || strcmp(oldrefname, entry->name)) {
 -                      int len = strlen(entry->name);
 -                      int cmplen = (namlen < len) ? namlen : len;
 -                      const char *lead = (namlen < len) ? entry->name : refname;
 -                      if (!strncmp(refname, entry->name, cmplen) &&
 -                          lead[cmplen] == '/') {
 -                              error("'%s' exists; cannot create '%s'",
 -                                    entry->name, refname);
 -                              return 0;
 -                      }
 -              }
 -      }
 -      return 1;
 -}
 -
  /*
   * *string and *len will only be substituted, and *string returned (for
   * later free()ing) if the string passed in is a magic short-hand form
@@@ -1577,44 -1287,36 +1578,44 @@@ struct ref_lock *lock_any_ref_for_updat
        return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
  }
  
 +struct repack_without_ref_sb {
 +      const char *refname;
 +      int fd;
 +};
 +
 +static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,
 +                               int flags, void *cb_data)
 +{
 +      struct repack_without_ref_sb *data = cb_data;
 +      char line[PATH_MAX + 100];
 +      int len;
 +
 +      if (!strcmp(data->refname, refname))
 +              return 0;
 +      len = snprintf(line, sizeof(line), "%s %s\n",
 +                     sha1_to_hex(sha1), refname);
 +      /* this should not happen but just being defensive */
 +      if (len > sizeof(line))
 +              die("too long a refname '%s'", refname);
 +      write_or_die(data->fd, line, len);
 +      return 0;
 +}
 +
  static struct lock_file packlock;
  
  static int repack_without_ref(const char *refname)
  {
 -      struct ref_array *packed;
 -      int fd, i;
 -
 -      packed = get_packed_refs(get_ref_cache(NULL));
 -      if (search_ref_array(packed, refname) == NULL)
 +      struct repack_without_ref_sb data;
 +      struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
 +      if (find_ref(packed, refname) == NULL)
                return 0;
 -      fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
 -      if (fd < 0) {
 +      data.refname = refname;
 +      data.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
 +      if (data.fd < 0) {
                unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refname);
        }
 -
 -      for (i = 0; i < packed->nr; i++) {
 -              char line[PATH_MAX + 100];
 -              int len;
 -              struct ref_entry *ref = packed->refs[i];
 -
 -              if (!strcmp(refname, ref->name))
 -                      continue;
 -              len = snprintf(line, sizeof(line), "%s %s\n",
 -                             sha1_to_hex(ref->sha1), ref->name);
 -              /* this should not happen but just being defensive */
 -              if (len > sizeof(line))
 -                      die("too long a refname '%s'", ref->name);
 -              write_or_die(fd, line, len);
 -      }
 +      do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
        return commit_lock_file(&packlock);
  }
  
@@@ -2225,10 -1927,10 +2226,10 @@@ int for_each_reflog_ent(const char *ref
  
  static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
  {
 -      DIR *dir = opendir(git_path("logs/%s", base));
 +      DIR *d = opendir(git_path("logs/%s", base));
        int retval = 0;
  
 -      if (dir) {
 +      if (d) {
                struct dirent *de;
                int baselen = strlen(base);
                char *log = xmalloc(baselen + 257);
                if (baselen && base[baselen-1] != '/')
                        log[baselen++] = '/';
  
 -              while ((de = readdir(dir)) != NULL) {
 +              while ((de = readdir(d)) != NULL) {
                        struct stat st;
                        int namelen;
  
                                break;
                }
                free(log);
 -              closedir(dir);
 +              closedir(d);
        }
        else if (*base)
                return errno;
@@@ -2303,6 -2005,12 +2304,6 @@@ int update_ref(const char *action, cons
        return 0;
  }
  
 -int ref_exists(const char *refname)
 -{
 -      unsigned char sha1[20];
 -      return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
 -}
 -
  struct ref *find_ref_by_name(const struct ref *list, const char *name)
  {
        for ( ; list; list = list->next)