Merge branch 'bc/maint-diff-hunk-header-fix' into bc/master-diff-hunk-header-fix
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2008 03:32:50 +0000 (20:32 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2008 03:32:50 +0000 (20:32 -0700)
* bc/maint-diff-hunk-header-fix:
diff.*.xfuncname which uses "extended" regex's for hunk header selection
diff.c: associate a flag with each pattern and use it for compiling regex
diff.c: return pattern entry pointer rather than just the hunk header pattern
Cosmetical command name fix
Start conforming code to "git subcmd" style part 3
t9700/test.pl: remove File::Temp requirement
t9700/test.pl: avoid bareword 'STDERR' in 3-argument open()
GIT 1.6.0.2
Fix some manual typos.
Use compatibility regex library also on FreeBSD
Use compatibility regex library also on AIX
Update draft release notes for 1.6.0.2
Use compatibility regex library for OSX/Darwin
git-svn: Fixes my() parameter list syntax error in pre-5.8 Perl
Git.pm: Use File::Temp->tempfile instead of ->new
t7501: always use test_cmp instead of diff
Start conforming code to "git subcmd" style part 2
diff: Help "less" hide ^M from the output
checkout: do not check out unmerged higher stages randomly

Conflicts:
Documentation/git.txt
Documentation/gitattributes.txt
Makefile
diff.c
t/t7201-co.sh

13 files changed:
1  2 
Documentation/gitattributes.txt
Makefile
builtin-checkout.c
builtin-commit-tree.c
builtin-fetch-pack.c
builtin-update-index.c
combine-diff.c
diff.c
git-svn.perl
t/t7201-co.sh
t/t9700/test.pl
xdiff-interface.c
xdiff-interface.h
index 75124d26125f736b446b8e18826af3e444503edd,9259637609da2b34d15d217daeebeccc6ba853e4..9a752570f3c2d5a9ae41f3711474e9ab86864964
@@@ -288,13 -288,13 +288,13 @@@ for paths
  *.tex diff=tex
  ------------------------
  
- Then, you would define "diff.tex.funcname" configuration to
+ Then, you would define "diff.tex.xfuncname" configuration to
  specify a regular expression that matches a line that you would
  want to appear as the hunk header, like this:
  
  ------------------------
  [diff "tex"]
-       funcname = "^\\(\\\\\\(sub\\)*section{.*\\)$"
+       xfuncname = "^(\\\\(sub)*section\\{.*)$"
  ------------------------
  
  Note.  A single level of backslashes are eaten by the
@@@ -311,16 -311,10 +311,16 @@@ patterns are available
  
  - `bibtex` suitable for files with BibTeX coded references.
  
- - `java` suitable for source code in the Java lanugage.
 +- `html` suitable for HTML/XHTML documents.
 +
+ - `java` suitable for source code in the Java language.
  
  - `pascal` suitable for source code in the Pascal/Delphi language.
  
 +- `php` suitable for source code in the PHP language.
 +
 +- `python` suitable for source code in the Python language.
 +
  - `ruby` suitable for source code in the Ruby language.
  
  - `tex` suitable for source code for LaTeX documents.
diff --combined Makefile
index dfed7bae9439675bb734c0f9fb3852ecb01fdf6d,8d81095765b1e73fa79e2950c47fd06f3c475ac7..615d1baa9f7f45e32be8dce271a1cfb37c78a832
+++ b/Makefile
@@@ -124,9 -124,6 +124,9 @@@ all:
  # Define USE_STDEV below if you want git to care about the underlying device
  # change being considered an inode change from the update-index perspective.
  #
 +# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
 +# field that counts the on-disk footprint in 512-byte blocks.
 +#
  # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
  #
  # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
@@@ -357,7 -354,6 +357,7 @@@ LIB_H += git-compat-util.
  LIB_H += graph.h
  LIB_H += grep.h
  LIB_H += hash.h
 +LIB_H += help.h
  LIB_H += list-objects.h
  LIB_H += ll-merge.h
  LIB_H += log-tree.h
@@@ -522,7 -518,6 +522,7 @@@ BUILTIN_OBJS += builtin-for-each-ref.
  BUILTIN_OBJS += builtin-fsck.o
  BUILTIN_OBJS += builtin-gc.o
  BUILTIN_OBJS += builtin-grep.o
 +BUILTIN_OBJS += builtin-help.o
  BUILTIN_OBJS += builtin-init-db.o
  BUILTIN_OBJS += builtin-log.o
  BUILTIN_OBJS += builtin-ls-files.o
@@@ -580,11 -575,9 +580,11 @@@ EXTLIBS 
  
  ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),GNU/kFreeBSD)
        NO_STRLCPY = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),UnixWare)
        CC = cc
@@@ -633,6 -626,8 +633,8 @@@ ifeq ($(uname_S),Darwin
        endif
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
  endif
  ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@@ -682,7 -677,8 +684,9 @@@ ifeq ($(uname_S),FreeBSD
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
 +      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
        BASIC_CFLAGS += -I/usr/pkg/include
        BASIC_LDFLAGS += -L/usr/pkg/lib
        ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
 +      THREADED_DELTA_SEARCH = YesPlease
  endif
  ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
  endif
  ifeq ($(uname_S),GNU)
        # GNU/Hurd
@@@ -760,11 -756,10 +766,11 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        NO_SVN_TESTS = YesPlease
        NO_PERL_MAKEMAKER = YesPlease
        NO_POSIX_ONLY_PROGRAMS = YesPlease
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
 +      NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
        COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-       COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o
+       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
        EXTLIBS += -lws2_32
        X = .exe
        gitexecdir = ../libexec/git-core
@@@ -875,9 -870,6 +881,9 @@@ endi
  ifdef NO_D_INO_IN_DIRENT
        BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
  endif
 +ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
 +      BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
 +endif
  ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
  endif
@@@ -1106,7 -1098,7 +1112,7 @@@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
 -help.o: help.c common-cmds.h GIT-CFLAGS
 +builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
                '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
                '-DGIT_MAN_PATH="$(mandir_SQ)"' \
@@@ -1233,9 -1225,7 +1239,9 @@@ endi
  git-%$X: %.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -git-imap-send$X: imap-send.o $(LIB_FILE)
 +git-imap-send$X: imap-send.o $(GITLIBS)
 +      $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 +              $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
  
  http.o http-walker.o http-push.o transport.o: http.h
  
@@@ -1365,7 -1355,7 +1371,7 @@@ install: al
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -      $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
  ifndef NO_TCLTK
diff --combined builtin-checkout.c
index efdb1e02bf21fb3b901d375a547d7fbf44e0894b,8e77767b49888aa2131088a810850dab7854594d..d986ac7abbd619c486a377e4433cf3fca687ea7a
@@@ -76,6 -76,15 +76,15 @@@ static int read_tree_some(struct tree *
        return 0;
  }
  
+ static int skip_same_name(struct cache_entry *ce, int pos)
+ {
+       while (++pos < active_nr &&
+              !strcmp(active_cache[pos]->name, ce->name))
+               ; /* skip */
+       return pos;
+ }
  static int checkout_paths(struct tree *source_tree, const char **pathspec)
  {
        int pos;
        if (report_path_error(ps_matched, pathspec, 0))
                return 1;
  
+       /* Any unmerged paths? */
+       for (pos = 0; pos < active_nr; pos++) {
+               struct cache_entry *ce = active_cache[pos];
+               if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+                       if (!ce_stage(ce))
+                               continue;
+                       errs = 1;
+                       error("path '%s' is unmerged", ce->name);
+                       pos = skip_same_name(ce, pos) - 1;
+               }
+       }
+       if (errs)
+               return 1;
        /* Now we are committed to check them out */
        memset(&state, 0, sizeof(state));
        state.force = 1;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (pathspec_match(pathspec, NULL, ce->name, 0)) {
-                       errs |= checkout_entry(ce, &state, NULL);
+                       if (!ce_stage(ce)) {
+                               errs |= checkout_entry(ce, &state, NULL);
+                               continue;
+                       }
+                       pos = skip_same_name(ce, pos) - 1;
                }
        }
  
@@@ -157,7 -184,7 +184,7 @@@ struct checkout_opts 
        int force;
        int writeout_error;
  
 -      char *new_branch;
 +      const char *new_branch;
        int new_branch_log;
        enum branch_track track;
  };
@@@ -435,28 -462,13 +462,28 @@@ int cmd_checkout(int argc, const char *
  
        git_config(git_default_config, NULL);
  
 -      opts.track = git_branch_track;
 +      opts.track = BRANCH_TRACK_UNSPECIFIED;
  
        argc = parse_options(argc, argv, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
  
 -      if (!opts.new_branch && (opts.track != git_branch_track))
 -              die("git checkout: --track and --no-track require -b");
 +      /* --track without -b should DWIM */
 +      if (0 < opts.track && !opts.new_branch) {
 +              const char *argv0 = argv[0];
 +              if (!argc || !strcmp(argv0, "--"))
 +                      die ("--track needs a branch name");
 +              if (!prefixcmp(argv0, "refs/"))
 +                      argv0 += 5;
 +              if (!prefixcmp(argv0, "remotes/"))
 +                      argv0 += 8;
 +              argv0 = strchr(argv0, '/');
 +              if (!argv0 || !argv0[1])
 +                      die ("Missing branch name; try -b");
 +              opts.new_branch = argv0 + 1;
 +      }
 +
 +      if (opts.track == BRANCH_TRACK_UNSPECIFIED)
 +              opts.track = git_branch_track;
  
        if (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
diff --combined builtin-commit-tree.c
index 8a5ba4c6671e426338eb0cb95bd92851bf24b452,9b84c48dce0938f4f2884565dc25308d51c7a423..f2684bb75e2319f2797bfe626e15bc27bd76f21a
@@@ -24,7 -24,7 +24,7 @@@ static void check_valid(unsigned char *
                    typename(expect));
  }
  
- static const char commit_tree_usage[] = "git-commit-tree <sha1> [-p <sha1>]* < changelog";
+ static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
  
  static void new_parent(struct commit *parent, struct commit_list **parents_p)
  {
@@@ -48,7 -48,6 +48,7 @@@ static const char commit_utf8_warn[] 
  int commit_tree(const char *msg, unsigned char *tree,
                struct commit_list *parents, unsigned char *ret)
  {
 +      int result;
        int encoding_is_utf8;
        struct strbuf buffer;
  
@@@ -87,9 -86,7 +87,9 @@@
        if (encoding_is_utf8 && !is_utf8(buffer.buf))
                fprintf(stderr, commit_utf8_warn);
  
 -      return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
 +      result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
 +      strbuf_release(&buffer);
 +      return result;
  }
  
  int cmd_commit_tree(int argc, const char **argv, const char *prefix)
diff --combined builtin-fetch-pack.c
index 459c6f0da35ac0a4d6cc0a914f6aa905f8a94d42,85509f5ee5884589980c9a1c5f96b66a3d49d5dd..4dfef29bcd23a174593203e521247a3d4209cf89
@@@ -540,7 -540,7 +540,7 @@@ static int get_pack(int xd[2], char **p
                        *av++ = "--fix-thin";
                if (args.lock_pack || unpack_limit) {
                        int s = sprintf(keep_arg,
 -                                      "--keep=fetch-pack %d on ", getpid());
 +                                      "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
                        if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                                strcpy(keep_arg + s, "localhost");
                        *av++ = keep_arg;
@@@ -750,7 -750,7 +750,7 @@@ int cmd_fetch_pack(int argc, const cha
        if (!ret && nr_heads) {
                /* If the heads to pull were given, we should have
                 * consumed all of them by matching the remote.
-                * Otherwise, 'git-fetch remote no-such-ref' would
+                * Otherwise, 'git fetch remote no-such-ref' would
                 * silently succeed without issuing an error.
                 */
                for (i = 0; i < nr_heads; i++)
diff --combined builtin-update-index.c
index ce832247568de1b302395ce892f1433844fea187,9d19c51e8e68d5d4c92465699c58ef0acec70209..417f9724abdce3c49df316505eff1019126a5058
@@@ -14,7 -14,7 +14,7 @@@
   * Default to not allowing changes to the list of files. The
   * tool doesn't actually care, but this makes it harder to add
   * files to the revision control by mistake by doing something
-  * like "git-update-index *" and suddenly having all the object
+  * like "git update-index *" and suddenly having all the object
   * files be revision controlled.
   */
  static int allow_add;
@@@ -194,10 -194,6 +194,10 @@@ static int process_path(const char *pat
        int len;
        struct stat st;
  
 +      len = strlen(path);
 +      if (has_symlink_leading_path(len, path))
 +              return error("'%s' is beyond a symbolic link", path);
 +
        /*
         * First things first: get the stat information, to decide
         * what to do about the pathname!
        if (lstat(path, &st) < 0)
                return process_lstat_error(path, errno);
  
 -      len = strlen(path);
        if (S_ISDIR(st.st_mode))
                return process_directory(path, len, &st);
  
@@@ -313,18 -310,18 +313,18 @@@ static void read_index_info(int line_te
                /* This reads lines formatted in one of three formats:
                 *
                 * (1) mode         SP sha1          TAB path
-                * The first format is what "git-apply --index-info"
+                * The first format is what "git apply --index-info"
                 * reports, and used to reconstruct a partial tree
                 * that is used for phony merge base tree when falling
                 * back on 3-way merge.
                 *
                 * (2) mode SP type SP sha1          TAB path
-                * The second format is to stuff git-ls-tree output
+                * The second format is to stuff "git ls-tree" output
                 * into the index file.
                 *
                 * (3) mode         SP sha1 SP stage TAB path
                 * This format is to put higher order stages into the
-                * index file and matches git-ls-files --stage output.
+                * index file and matches "git ls-files --stage" output.
                 */
                errno = 0;
                ul = strtoul(buf.buf, &ptr, 8);
diff --combined combine-diff.c
index 0cf2a830b5dad5c82d8a127cd57c8a8cba195631,aa9d79ea0bf011ea45ef629ef713e6c977781431..dcb90b1701aab6f2e22a0953067d85dcf734d6d2
@@@ -143,6 -143,8 +143,6 @@@ static void append_lost(struct sline *s
  }
  
  struct combine_diff_state {
 -      struct xdiff_emit_state xm;
 -
        unsigned int lno;
        int ob, on, nb, nn;
        unsigned long nmask;
@@@ -215,15 -217,17 +215,15 @@@ static void combine_diff(const unsigne
        parent_file.size = sz;
        xpp.flags = XDF_NEED_MINIMAL;
        memset(&xecfg, 0, sizeof(xecfg));
 -      ecb.outf = xdiff_outf;
 -      ecb.priv = &state;
        memset(&state, 0, sizeof(state));
 -      state.xm.consume = consume_line;
        state.nmask = nmask;
        state.sline = sline;
        state.lno = 1;
        state.num_parent = num_parent;
        state.n = n;
  
 -      xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
 +      xdi_diff_outf(&parent_file, result_file, consume_line, &state,
 +                    &xpp, &xecfg, &ecb);
        free(parent_file.ptr);
  
        /* Assign line numbers for this parent.
@@@ -496,6 -500,18 +496,18 @@@ static int hunk_comment_line(const cha
        return (isalpha(ch) || ch == '_' || ch == '$');
  }
  
+ static void show_line_to_eol(const char *line, int len, const char *reset)
+ {
+       int saw_cr_at_eol = 0;
+       if (len < 0)
+               len = strlen(line);
+       saw_cr_at_eol = (len && line[len-1] == '\r');
+       printf("%.*s%s%s\n", len - saw_cr_at_eol, line,
+              reset,
+              saw_cr_at_eol ? "\r" : "");
+ }
  static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                       int use_color)
  {
                                        else
                                                putchar(' ');
                                }
-                               printf("%s%s\n", ll->line, c_reset);
+                               show_line_to_eol(ll->line, -1, c_reset);
                                ll = ll->next;
                        }
                        if (cnt < lno)
                                        putchar(' ');
                                p_mask <<= 1;
                        }
-                       printf("%.*s%s\n", sl->len, sl->bol, c_reset);
+                       show_line_to_eol(sl->bol, sl->len, c_reset);
                }
        }
  }
diff --combined diff.c
index e7afbe28c1379fbd003de905e1d7cb87ff37971e,dabb4b4a021414adb89d5647afe14caaff670a76..7b24672d31b09c9f7d7de840c57f75adba598b84
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -20,7 -20,6 +20,7 @@@
  
  static int diff_detect_rename_default;
  static int diff_rename_limit_default = 200;
 +static int diff_suppress_blank_empty;
  int diff_use_color_default = -1;
  static const char *external_diff_cmd_cfg;
  int diff_auto_refresh_index = 1;
@@@ -95,32 -94,37 +95,37 @@@ static int parse_lldiff_command(const c
   * to define a customized regexp to find the beginning of a function to
   * be used for hunk header lines of "diff -p" style output.
   */
- static struct funcname_pattern {
+ struct funcname_pattern_entry {
        char *name;
        char *pattern;
-       struct funcname_pattern *next;
+       int cflags;
+ };
+ static struct funcname_pattern_list {
+       struct funcname_pattern_list *next;
+       struct funcname_pattern_entry e;
  } *funcname_pattern_list;
  
- static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
+ static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
  {
        const char *name;
        int namelen;
-       struct funcname_pattern *pp;
+       struct funcname_pattern_list *pp;
  
        name = var + 5; /* "diff." */
        namelen = ep - name;
  
        for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
+               if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
                        break;
        if (!pp) {
                pp = xcalloc(1, sizeof(*pp));
-               pp->name = xmemdupz(name, namelen);
+               pp->e.name = xmemdupz(name, namelen);
                pp->next = funcname_pattern_list;
                funcname_pattern_list = pp;
        }
-       free(pp->pattern);
-       pp->pattern = xstrdup(value);
+       free(pp->e.pattern);
+       pp->e.pattern = xstrdup(value);
+       pp->e.cflags = cflags;
        return 0;
  }
  
@@@ -177,19 -181,19 +182,25 @@@ int git_diff_basic_config(const char *v
                return 0;
        }
  
 +      /* like GNU diff's --suppress-blank-empty option  */
 +      if (!strcmp(var, "diff.suppress-blank-empty")) {
 +              diff_suppress_blank_empty = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        if (!prefixcmp(var, "diff.")) {
                const char *ep = strrchr(var, '.');
                if (ep != var + 4) {
                        if (!strcmp(ep, ".funcname")) {
                                if (!value)
                                        return config_error_nonbool(var);
-                               return parse_funcname_pattern(var, ep, value);
+                               return parse_funcname_pattern(var, ep, value,
+                                       0);
+                       } else if (!strcmp(ep, ".xfuncname")) {
+                               if (!value)
+                                       return config_error_nonbool(var);
+                               return parse_funcname_pattern(var, ep, value,
+                                       REG_EXTENDED);
                        }
                }
        }
@@@ -376,6 -380,7 +387,6 @@@ static void diff_words_append(char *lin
  }
  
  struct diff_words_data {
 -      struct xdiff_emit_state xm;
        struct diff_words_buffer minus, plus;
        FILE *file;
  };
@@@ -465,8 -470,11 +476,8 @@@ static void diff_words_show(struct diff
  
        xpp.flags = XDF_NEED_MINIMAL;
        xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
 -      ecb.outf = xdiff_outf;
 -      ecb.priv = diff_words;
 -      diff_words->xm.consume = fn_out_diff_words_aux;
 -      xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
 -
 +      xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
 +                    &xpp, &xecfg, &ecb);
        free(minus.ptr);
        free(plus.ptr);
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
  typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
  
  struct emit_callback {
 -      struct xdiff_emit_state xm;
        int nparents, color_diff;
        unsigned ws_rule;
        sane_truncate_fn truncate;
@@@ -513,13 -522,20 +524,20 @@@ const char *diff_get_color(int diff_use
  
  static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
  {
-       int has_trailing_newline = (len > 0 && line[len-1] == '\n');
+       int has_trailing_newline, has_trailing_carriage_return;
+       has_trailing_newline = (len > 0 && line[len-1] == '\n');
        if (has_trailing_newline)
                len--;
+       has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+       if (has_trailing_carriage_return)
+               len--;
  
        fputs(set, file);
        fwrite(line, len, 1, file);
        fputs(reset, file);
+       if (has_trailing_carriage_return)
+               fputc('\r', file);
        if (has_trailing_newline)
                fputc('\n', file);
  }
@@@ -582,12 -598,6 +600,12 @@@ static void fn_out_consume(void *priv, 
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
  
 +      if (diff_suppress_blank_empty
 +          && len == 2 && line[0] == ' ' && line[1] == '\n') {
 +              line[0] = '\n';
 +              len = 1;
 +      }
 +
        /* This is not really necessary for now because
         * this codepath only deals with two-way diffs.
         */
@@@ -716,6 -726,8 +734,6 @@@ static char *pprint_rename(const char *
  }
  
  struct diffstat_t {
 -      struct xdiff_emit_state xm;
 -
        int nr;
        int alloc;
        struct diffstat_file {
@@@ -1145,6 -1157,7 +1163,6 @@@ static void free_diffstat_info(struct d
  }
  
  struct checkdiff_t {
 -      struct xdiff_emit_state xm;
        const char *filename;
        int lineno;
        struct diff_options *o;
@@@ -1375,42 -1388,37 +1393,40 @@@ int diff_filespec_is_binary(struct diff
        return one->is_binary;
  }
  
- static const char *funcname_pattern(const char *ident)
+ static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
  {
-       struct funcname_pattern *pp;
+       struct funcname_pattern_list *pp;
  
        for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strcmp(ident, pp->name))
-                       return pp->pattern;
+               if (!strcmp(ident, pp->e.name))
+                       return &pp->e;
        return NULL;
  }
  
- static struct builtin_funcname_pattern {
-       const char *name;
-       const char *pattern;
- } builtin_funcname_pattern[] = {
-       { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
-       { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" },
+ static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
++      { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 },
++      { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$", 0 },
        { "java", "!^[  ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
                        "new\\|return\\|switch\\|throw\\|while\\)\n"
                        "^[     ]*\\(\\([       ]*"
                        "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
-                       "[      ]*([^;]*\\)$" },
+                       "[      ]*([^;]*\\)$", 0 },
        { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|"
                        "destructor\\|interface\\|implementation\\|"
                        "initialization\\|finalization\\)[ \t]*.*\\)$"
                        "\\|"
-                       "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$"
-                       },
-       { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)" },
-       { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" },
-       { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
-       { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
+                       "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$",
+                       0 },
 -      { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 },
 -      { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 },
++      { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)", 0 },
++      { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$", 0 },
+       { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$", 0 },
++      { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 },
  };
  
- static const char *diff_funcname_pattern(struct diff_filespec *one)
+ static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
  {
-       const char *ident, *pattern;
+       const char *ident;
+       const struct funcname_pattern_entry *pe;
        int i;
  
        diff_filespec_check_attr(one);
                return funcname_pattern("default");
  
        /* Look up custom "funcname.$ident" regexp from config. */
-       pattern = funcname_pattern(ident);
-       if (pattern)
-               return pattern;
+       pe = funcname_pattern(ident);
+       if (pe)
+               return pe;
  
        /*
         * And define built-in fallback patterns here.  Note that
         */
        for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
                if (!strcmp(ident, builtin_funcname_pattern[i].name))
-                       return builtin_funcname_pattern[i].pattern;
+                       return &builtin_funcname_pattern[i];
  
        return NULL;
  }
@@@ -1513,11 -1521,11 +1529,11 @@@ static void builtin_diff(const char *na
                xdemitconf_t xecfg;
                xdemitcb_t ecb;
                struct emit_callback ecbdata;
-               const char *funcname_pattern;
+               const struct funcname_pattern_entry *pe;
  
-               funcname_pattern = diff_funcname_pattern(one);
-               if (!funcname_pattern)
-                       funcname_pattern = diff_funcname_pattern(two);
+               pe = diff_funcname_pattern(one);
+               if (!pe)
+                       pe = diff_funcname_pattern(two);
  
                memset(&xecfg, 0, sizeof(xecfg));
                memset(&ecbdata, 0, sizeof(ecbdata));
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
-               if (funcname_pattern)
-                       xdiff_set_find_func(&xecfg, funcname_pattern);
+               if (pe)
+                       xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
                else if (!prefixcmp(diffopts, "--unified="))
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
                else if (!prefixcmp(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
 -              ecb.outf = xdiff_outf;
 -              ecb.priv = &ecbdata;
 -              ecbdata.xm.consume = fn_out_consume;
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
                        ecbdata.diff_words =
                                xcalloc(1, sizeof(struct diff_words_data));
                        ecbdata.diff_words->file = o->file;
                }
 -              xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +              xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
 +                            &xpp, &xecfg, &ecb);
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
        }
@@@ -1594,8 -1604,9 +1610,8 @@@ static void builtin_diffstat(const cha
  
                memset(&xecfg, 0, sizeof(xecfg));
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
 -              ecb.outf = xdiff_outf;
 -              ecb.priv = diffstat;
 -              xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +              xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
 +                            &xpp, &xecfg, &ecb);
        }
  
   free_and_return:
@@@ -1616,6 -1627,7 +1632,6 @@@ static void builtin_checkdiff(const cha
                return;
  
        memset(&data, 0, sizeof(data));
 -      data.xm.consume = checkdiff_consume;
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.o = o;
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = XDF_NEED_MINIMAL;
 -              ecb.outf = xdiff_outf;
 -              ecb.priv = &data;
 -              xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +              xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
 +                            &xpp, &xecfg, &ecb);
  
                if ((data.ws_rule & WS_TRAILING_SPACE) &&
                    data.trailing_blanks_start) {
@@@ -3033,6 -3046,7 +3049,6 @@@ static void diff_summary(FILE *file, st
  }
  
  struct patch_id_t {
 -      struct xdiff_emit_state xm;
        SHA_CTX *ctx;
        int patchlen;
  };
@@@ -3077,6 -3091,7 +3093,6 @@@ static int diff_get_patch_id(struct dif
        SHA1_Init(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
        data.ctx = &ctx;
 -      data.xm.consume = patch_id_consume;
  
        for (i = 0; i < q->nr; i++) {
                xpparam_t xpp;
                xpp.flags = XDF_NEED_MINIMAL;
                xecfg.ctxlen = 3;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
 -              ecb.outf = xdiff_outf;
 -              ecb.priv = &data;
 -              xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 +              xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
 +                            &xpp, &xecfg, &ecb);
        }
  
        SHA1_Final(sha1, &ctx);
@@@ -3219,6 -3235,7 +3235,6 @@@ void diff_flush(struct diff_options *op
                struct diffstat_t diffstat;
  
                memset(&diffstat, 0, sizeof(struct diffstat_t));
 -              diffstat.xm.consume = diffstat_consume;
                for (i = 0; i < q->nr; i++) {
                        struct diff_filepair *p = q->queue[i];
                        if (check_pair_status(p))
diff --combined git-svn.perl
index ee3f5edb60c756ba82e92389b9f77aae00c6768b,237895c236118f6d92a6752cce2e23cfb4abfa3e..2fe7bd3c4621bacd2f880686108d9d8829d145d4
@@@ -421,15 -421,15 +421,15 @@@ sub cmd_dcommit 
        $head ||= 'HEAD';
        my @refs;
        my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
 +      unless ($gs) {
 +              die "Unable to determine upstream SVN information from ",
 +                  "$head history.\nPerhaps the repository is empty.";
 +      }
        $url = defined $_commit_url ? $_commit_url : $gs->full_url;
        my $last_rev = $_revision if defined $_revision;
        if ($url) {
                print "Committing to $url ...\n";
        }
 -      unless ($gs) {
 -              die "Unable to determine upstream SVN information from ",
 -                  "$head history.\nPerhaps the repository is empty.";
 -      }
        my ($linear_refs, $parents) = linearize_history($gs, \@refs);
        if ($_no_rebase && scalar(@$linear_refs) > 1) {
                warn "Attempting to commit more than one change while ",
@@@ -803,28 -803,8 +803,28 @@@ sub cmd_commit_diff 
        }
  }
  
 +sub escape_uri_only {
 +      my ($uri) = @_;
 +      my @tmp;
 +      foreach (split m{/}, $uri) {
 +              s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
 +              push @tmp, $_;
 +      }
 +      join('/', @tmp);
 +}
 +
 +sub escape_url {
 +      my ($url) = @_;
 +      if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) {
 +              my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
 +              $url = "$scheme://$domain$uri";
 +      }
 +      $url;
 +}
 +
  sub cmd_info {
        my $path = canonicalize_path(defined($_[0]) ? $_[0] : ".");
 +      my $fullpath = canonicalize_path($cmd_dir_prefix . $path);
        if (exists $_[1]) {
                die "Too many arguments specified\n";
        }
        my ($file_type, $diff_status) = find_file_type_and_diff_status($path);
  
        if (!$file_type && !$diff_status) {
 -              print STDERR "$path:  (Not a versioned resource)\n\n";
 -              return;
 +              print STDERR "svn: '$path' is not under version control\n";
 +              exit 1;
        }
  
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        # canonicalize_path() will return "" to make libsvn 1.5.x happy,
        $path = "." if $path eq "";
  
 -      my $full_url = $url . ($path eq "." ? "" : "/$path");
 +      my $full_url = $url . ($fullpath eq "" ? "" : "/$fullpath");
  
        if ($_url) {
 -              print $full_url, "\n";
 +              print escape_url($full_url), "\n";
                return;
        }
  
        my $result = "Path: $path\n";
        $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir";
 -      $result .= "URL: " . $full_url . "\n";
 +      $result .= "URL: " . escape_url($full_url) . "\n";
  
        eval {
                my $repos_root = $gs->repos_root;
                Git::SVN::remove_username($repos_root);
 -              $result .= "Repository Root: $repos_root\n";
 +              $result .= "Repository Root: " . escape_url($repos_root) . "\n";
        };
        if ($@) {
                $result .= "Repository Root: (offline)\n";
        }
  
        my ($lc_author, $lc_rev, $lc_date_utc);
 -      my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path);
 +      my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $fullpath);
        my $log = command_output_pipe(@args);
        my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
        while (<$log>) {
@@@ -3304,7 -3284,7 +3304,7 @@@ sub close_file 
                                        my $out = syswrite($tmp_fh, $str, $res);
                                        defined($out) && $out == $res
                                                or croak("write ",
-                                                       $tmp_fh->filename,
+                                                       Git::temp_path($tmp_fh),
                                                        ": $!\n");
                                }
                                defined $res or croak $!;
                }
  
                $hash = $::_repository->hash_and_insert_object(
-                               $fh->filename);
+                               Git::temp_path($fh));
                $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
  
                Git::temp_release($fb->{base}, 1);
@@@ -3400,12 -3380,11 +3400,12 @@@ sub generate_diff 
        while (<$diff_fh>) {
                chomp $_; # this gets rid of the trailing "\0"
                if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
 -                                      $::sha1\s($::sha1)\s
 +                                      ($::sha1)\s($::sha1)\s
                                        ([MTCRAD])\d*$/xo) {
                        push @mods, {   mode_a => $1, mode_b => $2,
 -                                      sha1_b => $3, chg => $4 };
 -                      if ($4 =~ /^(?:C|R)$/) {
 +                                      sha1_a => $3, sha1_b => $4,
 +                                      chg => $5 };
 +                      if ($5 =~ /^(?:C|R)$/) {
                                $state = 'file_a';
                        } else {
                                $state = 'file_b';
@@@ -3657,7 -3636,6 +3657,7 @@@ sub R 
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
                                $self->url_path($m->{file_a}), $self->{r});
        print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
 +      $self->apply_autoprops($file, $fbat);
        $self->chg_file($fbat, $m);
        $self->close_file($fbat,undef,$self->{pool});
  
@@@ -3684,52 -3662,33 +3684,52 @@@ sub change_file_prop 
        $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
  }
  
 -sub chg_file {
 -      my ($self, $fbat, $m) = @_;
 -      if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
 -              $self->change_file_prop($fbat,'svn:executable','*');
 -      } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
 -              $self->change_file_prop($fbat,'svn:executable',undef);
 -      }
 -      my $fh = Git::temp_acquire('git_blob');
 -      if ($m->{mode_b} =~ /^120/) {
 +sub _chg_file_get_blob ($$$$) {
 +      my ($self, $fbat, $m, $which) = @_;
 +      my $fh = Git::temp_acquire("git_blob_$which");
 +      if ($m->{"mode_$which"} =~ /^120/) {
                print $fh 'link ' or croak $!;
                $self->change_file_prop($fbat,'svn:special','*');
 -      } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
 +      } elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) {
                $self->change_file_prop($fbat,'svn:special',undef);
        }
 -      my $size = $::_repository->cat_blob($m->{sha1_b}, $fh);
 -      croak "Failed to read object $m->{sha1_b}" if ($size < 0);
 +      my $blob = $m->{"sha1_$which"};
 +      return ($fh,) if ($blob =~ /^0{40}$/);
 +      my $size = $::_repository->cat_blob($blob, $fh);
 +      croak "Failed to read object $blob" if ($size < 0);
        $fh->flush == 0 or croak $!;
        seek $fh, 0, 0 or croak $!;
  
        my $exp = ::md5sum($fh);
        seek $fh, 0, 0 or croak $!;
 +      return ($fh, $exp);
 +}
  
 +sub chg_file {
 +      my ($self, $fbat, $m) = @_;
 +      if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
 +              $self->change_file_prop($fbat,'svn:executable','*');
 +      } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
 +              $self->change_file_prop($fbat,'svn:executable',undef);
 +      }
 +      my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a';
 +      my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b';
        my $pool = SVN::Pool->new;
 -      my $atd = $self->apply_textdelta($fbat, undef, $pool);
 -      my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
 -      die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
 -      Git::temp_release($fh, 1);
 +      my $atd = $self->apply_textdelta($fbat, $exp_a, $pool);
 +      if (-s $fh_a) {
 +              my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool);
 +              my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool);
 +              if (defined $res) {
 +                      die "Unexpected result from send_txstream: $res\n",
 +                          "(SVN::Core::VERSION: $SVN::Core::VERSION)\n";
 +              }
 +      } else {
 +              my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool);
 +              die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n"
 +                  if ($got ne $exp_b);
 +      }
 +      Git::temp_release($fh_b, 1);
 +      Git::temp_release($fh_a, 1);
        $pool->clear;
  }
  
@@@ -4424,7 -4383,7 +4424,7 @@@ sub config_pager 
  
  sub run_pager {
        return unless -t *STDOUT && defined $pager;
-       pipe my $rfd, my $wfd or return;
+       pipe my ($rfd, $wfd) or return;
        defined(my $pid = fork) or ::fatal "Can't fork: $!";
        if (!$pid) {
                open STDOUT, '>&', $wfd or
diff --combined t/t7201-co.sh
index 543b1c289866d01f8aa3f931f3579565a963de9d,fbec70d3c6abff4a97aa205aa10fbf6fe336fa5f..25181388f8a8f1730d05fce128e0e1fc66eb3c0e
@@@ -337,36 -337,26 +337,58 @@@ test_expect_success 
      test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
      test_must_fail git checkout --track -b track'
  
 +test_expect_success \
 +    'checkout with --track fakes a sensible -b <name>' '
 +    git update-ref refs/remotes/origin/koala/bear renamer &&
 +    git update-ref refs/new/koala/bear renamer &&
 +
 +    git checkout --track origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track refs/remotes/origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track remotes/origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track refs/new/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 +'
 +
 +test_expect_success \
 +    'checkout with --track, but without -b, fails with too short tracked name' '
 +    test_must_fail git checkout --track renamer'
 +
+ test_expect_success 'checkout an unmerged path should fail' '
+       rm -f .git/index &&
+       O=$(echo original | git hash-object -w --stdin) &&
+       A=$(echo ourside | git hash-object -w --stdin) &&
+       B=$(echo theirside | git hash-object -w --stdin) &&
+       (
+               echo "100644 $A 0       fild" &&
+               echo "100644 $O 1       file" &&
+               echo "100644 $A 2       file" &&
+               echo "100644 $B 3       file" &&
+               echo "100644 $A 0       filf"
+       ) | git update-index --index-info &&
+       echo "none of the above" >sample &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       test_must_fail git checkout fild file filf &&
+       test_cmp sample fild &&
+       test_cmp sample filf &&
+       test_cmp sample file
+ '
  test_done
diff --combined t/t9700/test.pl
index 851cea4a4b7c36ff4b852616108bdaf481e2e9bc,504f95a47d733e10ca71d0a669cd51e1c6e3601c..697daf3ffd33c27654ce00f780acc2c6db5f9985
@@@ -9,12 -9,14 +9,11 @@@ use Test::More qw(no_plan)
  
  use Cwd;
  use File::Basename;
- use File::Temp;
  
  BEGIN { use_ok('Git') }
  
  # set up
 -our $repo_dir = "trash directory";
  our $abs_repo_dir = Cwd->cwd;
 -die "this must be run by calling the t/t97* shell script(s)\n"
 -    if basename(Cwd->cwd) ne $repo_dir;
  ok(our $r = Git->repository(Directory => "."), "open repository");
  
  # config
@@@ -35,7 -37,7 +34,7 @@@ is($r->get_color("color.test.slot1", "r
  # Failure cases for config:
  # Save and restore STDERR; we will probably extract this into a
  # "dies_ok" method and possibly move the STDERR handling to Git.pm.
- open our $tmpstderr, ">&", STDERR or die "cannot save STDERR"; close STDERR;
+ open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
  eval { $r->config("test.dupstring") };
  ok($@, "config: duplicate entry in scalar context fails");
  eval { $r->config_bool("test.boolother") };
@@@ -66,21 -68,25 +65,25 @@@ is($r->ident_person("Name", "email", "1
  
  # objects and hashes
  ok(our $file1hash = $r->command_oneline('rev-parse', "HEAD:file1"), "(get file hash)");
- our $tmpfile = File::Temp->new;
- is($r->cat_blob($file1hash, $tmpfile), 15, "cat_blob: size");
+ my $tmpfile = "file.tmp";
+ open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
+ is($r->cat_blob($file1hash, \*TEMPFILE), 15, "cat_blob: size");
  our $blobcontents;
- { local $/; seek $tmpfile, 0, 0; $blobcontents = <$tmpfile>; }
+ { local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
  is($blobcontents, "changed file 1\n", "cat_blob: data");
seek $tmpfile, 0, 0;
close TEMPFILE or die "Failed writing to $tmpfile: $!";
  is(Git::hash_object("blob", $tmpfile), $file1hash, "hash_object: roundtrip");
- $tmpfile = File::Temp->new();
- print $tmpfile my $test_text = "test blob, to be inserted\n";
+ open TEMPFILE, ">$tmpfile" or die "Can't open $tmpfile: $!";
+ print TEMPFILE my $test_text = "test blob, to be inserted\n";
+ close TEMPFILE or die "Failed writing to $tmpfile: $!";
  like(our $newhash = $r->hash_and_insert_object($tmpfile), qr/[0-9a-fA-F]{40}/,
       "hash_and_insert_object: returns hash");
$tmpfile = File::Temp->new;
- is($r->cat_blob($newhash, $tmpfile), length $test_text, "cat_blob: roundtrip size");
- { local $/; seek $tmpfile, 0, 0; $blobcontents = <$tmpfile>; }
open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
+ is($r->cat_blob($newhash, \*TEMPFILE), length $test_text, "cat_blob: roundtrip size");
+ { local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
  is($blobcontents, $test_text, "cat_blob: roundtrip data");
+ close TEMPFILE;
+ unlink $tmpfile;
  
  # paths
  is($r->repo_path, "./.git", "repo_path");
diff --combined xdiff-interface.c
index 944ad9887f5c94ca0e63f9a6c901447634f871ce,2c81f40cb657f6e49bc6a7431f830a037713ab38..7f1a7d3ffc62450600852059f046a482af0591ab
@@@ -1,12 -1,5 +1,12 @@@
  #include "cache.h"
  #include "xdiff-interface.h"
 +#include "strbuf.h"
 +
 +struct xdiff_emit_state {
 +      xdiff_emit_consume_fn consume;
 +      void *consume_callback_data;
 +      struct strbuf remainder;
 +};
  
  static int parse_num(char **cp_p, int *num_p)
  {
@@@ -62,13 -55,13 +62,13 @@@ static void consume_one(void *priv_, ch
                unsigned long this_size;
                ep = memchr(s, '\n', size);
                this_size = (ep == NULL) ? size : (ep - s + 1);
 -              priv->consume(priv, s, this_size);
 +              priv->consume(priv->consume_callback_data, s, this_size);
                size -= this_size;
                s += this_size;
        }
  }
  
 -int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
 +static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
  {
        struct xdiff_emit_state *priv = priv_;
        int i;
        for (i = 0; i < nbuf; i++) {
                if (mb[i].ptr[mb[i].size-1] != '\n') {
                        /* Incomplete line */
 -                      priv->remainder = xrealloc(priv->remainder,
 -                                                 priv->remainder_size +
 -                                                 mb[i].size);
 -                      memcpy(priv->remainder + priv->remainder_size,
 -                             mb[i].ptr, mb[i].size);
 -                      priv->remainder_size += mb[i].size;
 +                      strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
                        continue;
                }
  
                /* we have a complete line */
 -              if (!priv->remainder) {
 +              if (!priv->remainder.len) {
                        consume_one(priv, mb[i].ptr, mb[i].size);
                        continue;
                }
 -              priv->remainder = xrealloc(priv->remainder,
 -                                         priv->remainder_size +
 -                                         mb[i].size);
 -              memcpy(priv->remainder + priv->remainder_size,
 -                     mb[i].ptr, mb[i].size);
 -              consume_one(priv, priv->remainder,
 -                          priv->remainder_size + mb[i].size);
 -              free(priv->remainder);
 -              priv->remainder = NULL;
 -              priv->remainder_size = 0;
 +              strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
 +              consume_one(priv, priv->remainder.buf, priv->remainder.len);
 +              strbuf_reset(&priv->remainder);
        }
 -      if (priv->remainder) {
 -              consume_one(priv, priv->remainder, priv->remainder_size);
 -              free(priv->remainder);
 -              priv->remainder = NULL;
 -              priv->remainder_size = 0;
 +      if (priv->remainder.len) {
 +              consume_one(priv, priv->remainder.buf, priv->remainder.len);
 +              strbuf_reset(&priv->remainder);
        }
        return 0;
  }
@@@ -134,25 -141,6 +134,25 @@@ int xdi_diff(mmfile_t *mf1, mmfile_t *m
        return xdl_diff(&a, &b, xpp, xecfg, xecb);
  }
  
 +int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
 +                xdiff_emit_consume_fn fn, void *consume_callback_data,
 +                xpparam_t const *xpp,
 +                xdemitconf_t const *xecfg, xdemitcb_t *xecb)
 +{
 +      int ret;
 +      struct xdiff_emit_state state;
 +
 +      memset(&state, 0, sizeof(state));
 +      state.consume = fn;
 +      state.consume_callback_data = consume_callback_data;
 +      xecb->outf = xdiff_outf;
 +      xecb->priv = &state;
 +      strbuf_init(&state.remainder, 0);
 +      ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
 +      strbuf_release(&state.remainder);
 +      return ret;
 +}
 +
  int read_mmfile(mmfile_t *ptr, const char *filename)
  {
        struct stat st;
@@@ -218,7 -206,7 +218,7 @@@ static long ff_regexp(const char *line
        return result;
  }
  
- void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value)
+ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)
  {
        int i;
        struct ff_regs *regs;
                        expression = buffer = xstrndup(value, ep - value);
                else
                        expression = value;
-               if (regcomp(&reg->re, expression, 0))
+               if (regcomp(&reg->re, expression, cflags))
                        die("Invalid regexp to look for hunk header: %s", expression);
                free(buffer);
                value = ep + 1;
diff --combined xdiff-interface.h
index 558492ba38c50351a98f80c16e83a058ace849ab,33cab9dd59336fabe8cdd484953544b505c1ba94..23c49b99d1208d48bb19a3fb499b5f356b25ee7e
@@@ -3,19 -3,24 +3,19 @@@
  
  #include "xdiff/xdiff.h"
  
 -struct xdiff_emit_state;
 -
  typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
  
 -struct xdiff_emit_state {
 -      xdiff_emit_consume_fn consume;
 -      char *remainder;
 -      unsigned long remainder_size;
 -};
 -
  int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 -int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
 +int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
 +                xdiff_emit_consume_fn fn, void *consume_callback_data,
 +                xpparam_t const *xpp,
 +                xdemitconf_t const *xecfg, xdemitcb_t *xecb);
  int parse_hunk_header(char *line, int len,
                      int *ob, int *on,
                      int *nb, int *nn);
  int read_mmfile(mmfile_t *ptr, const char *filename);
  int buffer_is_binary(const char *ptr, unsigned long size);
  
- extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line);
+ extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
  
  #endif