Merge branches 'lt/crlf' and 'jc/apply-config'
authorJunio C Hamano <junkio@cox.net>
Fri, 23 Feb 2007 05:34:36 +0000 (21:34 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 23 Feb 2007 05:34:36 +0000 (21:34 -0800)
* lt/crlf:
Teach core.autocrlf to 'git apply'
t0020: add test for auto-crlf
Make AutoCRLF ternary variable.
Lazy man's auto-CRLF

* jc/apply-config:
t4119: test autocomputing -p<n> for traditional diff input.
git-apply: guess correct -p<n> value for non-git patches.
git-apply: notice "diff --git" patch again
Fix botched "leak fix"
t4119: add test for traditional patch and different p_value
apply: fix memory leak in prefix_one()
git-apply: require -p<n> when working in a subdirectory.
git-apply: do not lose cwd when run from a subdirectory.
Teach 'git apply' to look at $HOME/.gitconfig even outside of a repository
Teach 'git apply' to look at $GIT_DIR/config

1  2  3 
Makefile
builtin-apply.c
cache.h
config.c
diff.c
sha1_file.c
diff --combined Makefile
index 35be5e20496fe072c473639a00ebcaf77cda22f3,60496ff957b153320de139abf3c5ef84554fed42,40bdcff696f3270ced943945183306fc9899407d..fbe05f938ebbd713171234eaa6d956b23fba5e1d
+++ b/Makefile
@@@@ -28,10 -28,6 -28,6 +28,10 @@@@ all:
   #
   # Define NO_STRLCPY if you don't have strlcpy.
   #
 ++# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
 ++# If your compiler also does not support long long or does not have
 ++# strtoull, define NO_STRTOULL.
 ++#
   # Define NO_SETENV if you don't have setenv in the C library.
   #
   # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
@@@@ -176,7 -172,7 -172,7 +176,7 @@@@ SCRIPT_SH = 
        git-merge-one-file.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
 --     git-resolve.sh git-revert.sh git-sh-setup.sh \
 ++     git-revert.sh git-sh-setup.sh \
        git-tag.sh git-verify-tag.sh \
        git-applymbox.sh git-applypatch.sh git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@@@ -266,7 -262,8 -262,7 +266,8 @@@@ LIB_OBJS = 
        revision.o pager.o tree-walk.o xdiff-interface.o \
        write_or_die.o trace.o list-objects.o grep.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
- -     color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o
+ +     color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
+ +     convert.o
   
   BUILTIN_OBJS = \
        builtin-add.o \
        builtin-diff.o \
        builtin-diff-files.o \
        builtin-diff-index.o \
 --     builtin-diff-stages.o \
        builtin-diff-tree.o \
        builtin-fmt-merge-msg.o \
        builtin-for-each-ref.o \
        builtin-ls-tree.o \
        builtin-mailinfo.o \
        builtin-mailsplit.o \
 ++     builtin-merge-base.o \
        builtin-merge-file.o \
        builtin-mv.o \
        builtin-name-rev.o \
@@@@ -357,13 -354,11 -353,11 +358,13 @@@@ ifeq ($(uname_S),SunOS
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
 ++             NO_STRTOUMAX = YesPlease
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
 ++             NO_STRTOUMAX = YesPlease
        endif
        INSTALL = ginstall
        TAR = gtar
@@@@ -523,13 -518,6 -517,6 +524,13 @@@@ ifdef NO_STRLCP
        COMPAT_CFLAGS += -DNO_STRLCPY
        COMPAT_OBJS += compat/strlcpy.o
   endif
 ++ifdef NO_STRTOUMAX
 ++     COMPAT_CFLAGS += -DNO_STRTOUMAX
 ++     COMPAT_OBJS += compat/strtoumax.o
 ++endif
 ++ifdef NO_STRTOULL
 ++     COMPAT_CFLAGS += -DNO_STRTOULL
 ++endif
   ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
@@@@ -895,8 -883,7 -882,7 +896,8 @@@@ dist: git.spec git-archiv
        $(TAR) rf $(GIT_TARNAME).tar \
                $(GIT_TARNAME)/git.spec \
                $(GIT_TARNAME)/version \
 --             $(GIT_TARNAME)/git-gui/version
 ++             $(GIT_TARNAME)/git-gui/version \
 ++             $(GIT_TARNAME)/git-gui/credits
        @rm -rf $(GIT_TARNAME)
        gzip -f -9 $(GIT_TARNAME).tar
   
@@@@ -953,14 -940,11 -939,11 +954,14 @@@@ check-docs:
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-stupid | \
 ++             git-add--interactive | git-fsck-objects | git-init-db | \
 ++             git-repo-config | \
                git-ssh-pull | git-ssh-push ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
                echo "no doc: $$v"; \
 --             grep -q "^gitlink:$$v\[[0-9]\]::" Documentation/git.txt || \
 ++             sed -e '1,/^__DATA__/d' Documentation/cmd-list.perl | \
 ++             grep -q "^$$v[  ]" || \
                case "$$v" in \
                git) ;; \
                *) echo "no link: $$v";; \
diff --combined builtin-apply.c
index d67817834bc33b864c569c597d96e55642c8ee61,45c4acbd205d02d0c6586b925909756b4508867c,c7d4bdd47451ca0b57c701f98129a019075611e9..630d8fd6521a63320e59a5bd061720d1fc841e7a
@@@@ -28,6 -28,6 -28,7 +28,7 @@@@ static int newfd = -1
   
   static int unidiff_zero;
   static int p_value = 1;
++ static int p_value_known;
   static int check_index;
   static int write_index;
   static int cached;
@@@@ -144,6 -144,6 -145,7 +145,7 @@@@ struct patch 
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
++      unsigned int is_toplevel_relative:1;
        unsigned int inaccurate_eof:1;
        unsigned int is_binary:1;
        unsigned int is_copy:1;
@@@@ -238,7 -238,7 -240,7 +240,7 @@@@ static int name_terminate(const char *n
        return 1;
   }
   
-- static char * find_name(const char *line, char *def, int p_value, int terminate)
++ static char *find_name(const char *line, char *def, int p_value, int terminate)
   {
        int len;
        const char *start = line;
        return name;
   }
   
++ static int count_slashes(const char *cp)
++ {
++      int cnt = 0;
++      char ch;
++ 
++      while ((ch = *cp++))
++              if (ch == '/')
++                      cnt++;
++      return cnt;
++ }
++ 
++ /*
++  * Given the string after "--- " or "+++ ", guess the appropriate
++  * p_value for the given patch.
++  */
++ static int guess_p_value(const char *nameline)
++ {
++      char *name, *cp;
++      int val = -1;
++ 
++      if (is_dev_null(nameline))
++              return -1;
++      name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);
++      if (!name)
++              return -1;
++      cp = strchr(name, '/');
++      if (!cp)
++              val = 0;
++      else if (prefix) {
++              /*
++               * Does it begin with "a/$our-prefix" and such?  Then this is
++               * very likely to apply to our directory.
++               */
++              if (!strncmp(name, prefix, prefix_length))
++                      val = count_slashes(prefix);
++              else {
++                      cp++;
++                      if (!strncmp(cp, prefix, prefix_length))
++                              val = count_slashes(prefix) + 1;
++              }
++      }
++      free(name);
++      return val;
++ }
++ 
   /*
    * Get the name etc info from the --/+++ lines of a traditional patch header
    *
--  * NOTE! This hardcodes "-p1" behaviour in filename detection.
--  *
    * FIXME! The end-of-filename heuristics are kind of screwy. For existing
    * files, we can happily check the index for a match, but for creating a
    * new file we should try to match whatever "patch" does. I have no idea.
@@@@ -326,6 -326,6 -371,16 +371,16 @@@@ static void parse_traditional_patch(con
   
        first += 4;     /* skip "--- " */
        second += 4;    /* skip "+++ " */
++      if (!p_value_known) {
++              int p, q;
++              p = guess_p_value(first);
++              q = guess_p_value(second);
++              if (p < 0) p = q;
++              if (0 <= p && p == q) {
++                      p_value = p;
++                      p_value_known = 1;
++              }
++      }
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
@@@@ -787,6 -787,6 -842,7 +842,7 @@@@ static int find_header(char *line, unsi
   {
        unsigned long offset, len;
   
++      patch->is_toplevel_relative = 0;
        patch->is_rename = patch->is_copy = 0;
        patch->is_new = patch->is_delete = -1;
        patch->old_mode = patch->new_mode = 0;
                                        die("git diff header lacks filename information (line %d)", linenr);
                                patch->old_name = patch->new_name = patch->def_name;
                        }
++                      patch->is_toplevel_relative = 1;
                        *hdrsize = git_hdr_len;
                        return offset;
                }
@@@@ -1129,11 -1129,11 -1186,11 +1186,11 @@@@ static struct fragment *parse_binary_hu
   
        *status_p = 0;
   
 --     if (!strncmp(buffer, "delta ", 6)) {
 ++     if (!prefixcmp(buffer, "delta ")) {
                patch_method = BINARY_DELTA_DEFLATED;
                origlen = strtoul(buffer + 6, NULL, 10);
        }
 --     else if (!strncmp(buffer, "literal ", 8)) {
 ++     else if (!prefixcmp(buffer, "literal ")) {
                patch_method = BINARY_LITERAL_DEFLATED;
                origlen = strtoul(buffer + 8, NULL, 10);
        }
@@@@ -1393,28 -1393,39 -1450,28 +1450,39 @@@@ static void show_stats(struct patch *pa
        free(qname);
   }
   
- -static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
+ +static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
   {
        int fd;
        unsigned long got;
+ +     unsigned long nsize;
+ +     char *nbuf;
+ +     unsigned long size = *size_p;
+ +     char *buf = *buf_p;
   
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
- -             return readlink(path, buf, size);
+ +             return readlink(path, buf, size) != size;
        case S_IFREG:
                fd = open(path, O_RDONLY);
                if (fd < 0)
                        return error("unable to open %s", path);
                got = 0;
                for (;;) {
- -                     int ret = xread(fd, (char *) buf + got, size - got);
+ +                     int ret = xread(fd, buf + got, size - got);
                        if (ret <= 0)
                                break;
                        got += ret;
                }
                close(fd);
- -             return got;
- -
+ +             nsize = got;
+ +             nbuf = buf;
+ +             if (convert_to_git(path, &nbuf, &nsize)) {
+ +                     free(buf);
+ +                     *buf_p = nbuf;
+ +                     *alloc_p = nsize;
+ +                     *size_p = nsize;
+ +             }
+ +             return got != size;
        default:
                return -1;
        }
@@@@ -1910,7 -1921,7 -1967,7 +1978,7 @@@@ static int apply_data(struct patch *pat
                size = st->st_size;
                alloc = size + 8192;
                buf = xmalloc(alloc);
- -             if (read_old_data(st, patch->old_name, buf, alloc) != size)
+ +             if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
                        return error("read of %s failed", patch->old_name);
        }
   
@@@@ -1988,7 -1999,7 -2045,7 +2056,7 @@@@ static int check_patch(struct patch *pa
                        return error("%s: %s", old_name, strerror(errno));
   
                if (!cached)
 --                     st_mode = ntohl(create_ce_mode(st.st_mode));
 ++                     st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
   
                if (patch->is_new < 0)
                        patch->is_new = 0;
@@@@ -2232,7 -2243,7 -2289,7 +2300,7 @@@@ static void patch_stats(struct patch *p
        }
   }
   
-- static void remove_file(struct patch *patch)
++ static void remove_file(struct patch *patch, int rmdir_empty)
   {
        if (write_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
                cache_tree_invalidate_path(active_cache_tree, patch->old_name);
        }
        if (!cached) {
--              if (!unlink(patch->old_name)) {
++              if (!unlink(patch->old_name) && rmdir_empty) {
                        char *name = xstrdup(patch->old_name);
                        char *end = strrchr(name, '/');
                        while (end) {
@@@@ -2282,12 -2293,22 -2339,12 +2350,22 @@@@ static void add_index_file(const char *
   static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
   {
        int fd;
+ +     char *nbuf;
+ +     unsigned long nsize;
   
        if (S_ISLNK(mode))
                /* Although buf:size is counted string, it also is NUL
                 * terminated.
                 */
                return symlink(buf, path);
+ +     nsize = size;
+ +     nbuf = (char *) buf;
+ +     if (convert_to_working_tree(path, &nbuf, &nsize)) {
+ +             free((char *) buf);
+ +             buf = nbuf;
+ +             size = nsize;
+ +     }
+ +
        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
        if (fd < 0)
                return -1;
@@@@ -2373,7 -2394,7 -2430,7 +2451,7 @@@@ static void write_out_one_result(struc
   {
        if (patch->is_delete > 0) {
                if (phase == 0)
--                      remove_file(patch);
++                      remove_file(patch, 1);
                return;
        }
        if (patch->is_new > 0 || patch->is_copy) {
         * thing: remove the old, write the new
         */
        if (phase == 0)
--              remove_file(patch);
++              remove_file(patch, 0);
        if (phase == 1)
                create_file(patch);
   }
@@@@ -2508,6 -2529,6 -2565,32 +2586,32 @@@@ static int use_patch(struct patch *p
        return 1;
   }
   
++ static void prefix_one(char **name)
++ {
++      char *old_name = *name;
++      if (!old_name)
++              return;
++      *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
++      free(old_name);
++ }
++ 
++ static void prefix_patches(struct patch *p)
++ {
++      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);
++              }
++      }
++ }
++ 
   static int apply_patch(int fd, const char *filename, int inaccurate_eof)
   {
        unsigned long offset, size;
                        break;
                if (apply_in_reverse)
                        reverse_patches(patch);
++              if (prefix)
++                      prefix_patches(patch);
                if (use_patch(patch)) {
                        patch_stats(patch);
                        *listp = patch;
                        listp = &patch->next;
--              } else {
++              }
++              else {
                        /* perhaps free it a bit better? */
                        free(patch);
                        skipped_patch++;
@@@@ -2595,9 -2616,10 -2681,16 +2702,16 @@@@ int cmd_apply(int argc, const char **ar
        int read_stdin = 1;
        int inaccurate_eof = 0;
        int errs = 0;
++      int is_not_gitdir = 0;
   
        const char *whitespace_option = NULL;
   
++      prefix = setup_git_directory_gently(&is_not_gitdir);
++      prefix_length = prefix ? strlen(prefix) : 0;
++      git_config(git_apply_config);
++      if (apply_default_whitespace)
++              parse_whitespace_option(apply_default_whitespace);
+  
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                char *end;
                        read_stdin = 0;
                        continue;
                }
 --             if (!strncmp(arg, "--exclude=", 10)) {
 ++             if (!prefixcmp(arg, "--exclude=")) {
                        struct excludes *x = xmalloc(sizeof(*x));
                        x->path = arg + 10;
                        x->next = excludes;
                        excludes = x;
                        continue;
                }
 --             if (!strncmp(arg, "-p", 2)) {
 ++             if (!prefixcmp(arg, "-p")) {
                        p_value = atoi(arg + 2);
++                      p_value_known = 1;
                        continue;
                }
                if (!strcmp(arg, "--no-add")) {
                        continue;
                }
                if (!strcmp(arg, "--index")) {
++                      if (is_not_gitdir)
++                              die("--index outside a repository");
                        check_index = 1;
                        continue;
                }
                if (!strcmp(arg, "--cached")) {
++                      if (is_not_gitdir)
++                              die("--cached outside a repository");
                        check_index = 1;
                        cached = 1;
                        continue;
                        line_termination = 0;
                        continue;
                }
 --             if (!strncmp(arg, "-C", 2)) {
 ++             if (!prefixcmp(arg, "-C")) {
                        p_context = strtoul(arg + 2, &end, 0);
                        if (*end != '\0')
                                die("unrecognized context count '%s'", arg + 2);
                        continue;
                }
 --             if (!strncmp(arg, "--whitespace=", 13)) {
 ++             if (!prefixcmp(arg, "--whitespace=")) {
                        whitespace_option = arg + 13;
                        parse_whitespace_option(arg + 13);
                        continue;
                        inaccurate_eof = 1;
                        continue;
                }
-- 
--              if (check_index && prefix_length < 0) {
--                      prefix = setup_git_directory();
--                      prefix_length = prefix ? strlen(prefix) : 0;
--                      git_config(git_apply_config);
--                      if (!whitespace_option && apply_default_whitespace)
--                              parse_whitespace_option(apply_default_whitespace);
--              }
                if (0 < prefix_length)
                        arg = prefix_filename(prefix, prefix_length, arg);
   
diff --combined cache.h
index 04f8e63baf6c23d9a27618b642b98506c9db688a,9c019e8bbac86980c2264414d994d5fbbe844e2e,c62b0b090d2b3e628764557127f1629d26459fbf..8bbc14299d6a504c5d7d202aae5455afaa9295cc
+++ b/cache.h
@@@@ -106,16 -106,6 -106,6 +106,16 @@@@ static inline unsigned int create_ce_mo
                return htonl(S_IFLNK);
        return htonl(S_IFREG | ce_permissions(mode));
   }
 ++static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
 ++{
 ++     extern int trust_executable_bit;
 ++     if (!trust_executable_bit && S_ISREG(mode)) {
 ++             if (ce && S_ISREG(ntohl(ce->ce_mode)))
 ++                     return ce->ce_mode;
 ++             return create_ce_mode(0666);
 ++     }
 ++     return create_ce_mode(mode);
 ++}
   #define canon_mode(mode) \
        (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
        S_ISLNK(mode) ? S_IFLNK : S_IFDIR)
@@@@ -211,6 -201,7 -201,6 +211,7 @@@@ extern const char *apply_default_whites
   extern int zlib_compression_level;
   extern size_t packed_git_window_size;
   extern size_t packed_git_limit;
+ +extern int auto_crlf;
   
   #define GIT_REPO_VERSION 0
   extern int repository_format_version;
@@@@ -478,4 -469,8 -468,4 +479,8 @@@@ extern int nfvasprintf(char **str, cons
   extern void trace_printf(const char *format, ...);
   extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
   
+ +/* convert.c */
+ +extern int convert_to_git(const char *path, char **bufp, unsigned long *sizep);
+ +extern int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep);
+ +
   #endif /* CACHE_H */
diff --combined config.c
index c938aa0b15513265866b88dbb9b8217958cac376,e8ae919b5217816b9f3c9a201a16dc26753f7503,d82107124a53bca99ee65ea31dceb3871bf6796f..8b6cf1aaa55a510bffa1d0b8a143e23a6883d45c
+++ b/config.c
@@@@ -310,14 -310,12 -310,12 +310,14 @@@@ int git_default_config(const char *var
        }
   
        if (!strcmp(var, "core.packedgitwindowsize")) {
 --             int pgsz = getpagesize();
 ++             int pgsz_x2 = getpagesize() * 2;
                packed_git_window_size = git_config_int(var, value);
 --             packed_git_window_size /= pgsz;
 --             if (packed_git_window_size < 2)
 --                     packed_git_window_size = 2;
 --             packed_git_window_size *= pgsz;
 ++
 ++             /* This value must be multiple of (pagesize * 2) */
 ++             packed_git_window_size /= pgsz_x2;
 ++             if (packed_git_window_size < 1)
 ++                     packed_git_window_size = 1;
 ++             packed_git_window_size *= pgsz_x2;
                return 0;
        }
   
                return 0;
        }
   
+ +     if (!strcmp(var, "core.autocrlf")) {
+ +             if (value && !strcasecmp(value, "input")) {
+ +                     auto_crlf = -1;
+ +                     return 0;
+ +             }
+ +             auto_crlf = git_config_bool(var, value);
+ +             return 0;
+ +     }
+ +
        if (!strcmp(var, "user.name")) {
                strlcpy(git_default_name, value, sizeof(git_default_name));
                return 0;
diff --combined diff.c
index 019ecbc1fa80807fc7f59e12a931b0f77224e71f,561587cace6d94ce2d2c4653588bcba237555f37,13b9b6c5602cc1aca4a95ed4d292756d3380c543..5ecb12225560e19142ad5b2936b311a9fce2a16a
--- 1/diff.c
--- 2/diff.c
--- 3/diff.c
+++ b/diff.c
@@@@ -77,7 -77,7 -77,7 +77,7 @@@@ int git_diff_ui_config(const char *var
                        diff_detect_rename_default = DIFF_DETECT_RENAME;
                return 0;
        }
 --     if (!strncmp(var, "diff.color.", 11) || !strncmp(var, "color.diff.", 11)) {
 ++     if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                color_parse(value, var, diff_colors[slot]);
                return 0;
@@@@ -184,59 -184,42 -184,42 +184,59 @@@@ static void print_line_count(int count
        }
   }
   
 --static void copy_file(int prefix, const char *data, int size)
 ++static void copy_file(int prefix, const char *data, int size,
 ++             const char *set, const char *reset)
   {
        int ch, nl_just_seen = 1;
        while (0 < size--) {
                ch = *data++;
 --             if (nl_just_seen)
 ++             if (nl_just_seen) {
 ++                     fputs(set, stdout);
                        putchar(prefix);
 --             putchar(ch);
 --             if (ch == '\n')
 ++             }
 ++             if (ch == '\n') {
                        nl_just_seen = 1;
 --             else
 ++                     fputs(reset, stdout);
 ++             } else
                        nl_just_seen = 0;
 ++             putchar(ch);
        }
        if (!nl_just_seen)
 --             printf("\n\\ No newline at end of file\n");
 ++             printf("%s\n\\ No newline at end of file\n", reset);
   }
   
   static void emit_rewrite_diff(const char *name_a,
                              const char *name_b,
                              struct diff_filespec *one,
 --                           struct diff_filespec *two)
 ++                           struct diff_filespec *two,
 ++                           int color_diff)
   {
        int lc_a, lc_b;
 ++     const char *name_a_tab, *name_b_tab;
 ++     const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO);
 ++     const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO);
 ++     const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
 ++     const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
 ++     const char *reset = diff_get_color(color_diff, DIFF_RESET);
 ++
 ++     name_a_tab = strchr(name_a, ' ') ? "\t" : "";
 ++     name_b_tab = strchr(name_b, ' ') ? "\t" : "";
 ++
        diff_populate_filespec(one, 0);
        diff_populate_filespec(two, 0);
        lc_a = count_lines(one->data, one->size);
        lc_b = count_lines(two->data, two->size);
 --     printf("--- a/%s\n+++ b/%s\n@@ -", name_a, name_b);
 ++     printf("%s--- a/%s%s%s\n%s+++ b/%s%s%s\n%s@@ -",
 ++            metainfo, name_a, name_a_tab, reset,
 ++            metainfo, name_b, name_b_tab, reset, fraginfo);
        print_line_count(lc_a);
        printf(" +");
        print_line_count(lc_b);
 --     printf(" @@\n");
 ++     printf(" @@%s\n", reset);
        if (lc_a)
 --             copy_file('-', one->data, one->size);
 ++             copy_file('-', one->data, one->size, old, reset);
        if (lc_b)
 --             copy_file('+', two->data, two->size);
 ++             copy_file('+', two->data, two->size, new, reset);
   }
   
   static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@@@ -415,16 -398,22 -398,22 +415,16 @@@@ static void emit_line(const char *set, 
        puts(reset);
   }
   
 --static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
 ++static void emit_line_with_ws(int nparents,
 ++             const char *set, const char *reset, const char *ws,
 ++             const char *line, int len)
   {
 --     int col0 = ecbdata->nparents;
 ++     int col0 = nparents;
        int last_tab_in_indent = -1;
        int last_space_in_indent = -1;
        int i;
        int tail = len;
        int need_highlight_leading_space = 0;
 --     const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 --     const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 --
 --     if (!*ws) {
 --             emit_line(set, reset, line, len);
 --             return;
 --     }
 --
        /* The line is a newly added line.  Does it have funny leading
         * whitespaces?  In indent, SP should never precede a TAB.
         */
                emit_line(set, reset, line + i, len - i);
   }
   
 ++static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
 ++{
 ++     const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 ++     const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 ++
 ++     if (!*ws)
 ++             emit_line(set, reset, line, len);
 ++     else
 ++             emit_line_with_ws(ecbdata->nparents, set, reset, ws,
 ++                             line, len);
 ++}
 ++
   static void fn_out_consume(void *priv, char *line, unsigned long len)
   {
        int i;
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
   
        if (ecbdata->label_path[0]) {
 --             printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
 --             printf("%s+++ %s%s\n", set, ecbdata->label_path[1], reset);
 ++             const char *name_a_tab, *name_b_tab;
 ++
 ++             name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
 ++             name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 ++
 ++             printf("%s--- %s%s%s\n",
 ++                    set, ecbdata->label_path[0], reset, name_a_tab);
 ++             printf("%s+++ %s%s%s\n",
 ++                    set, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
   
@@@@ -900,44 -870,30 -870,30 +900,44 @@@@ static void show_numstat(struct diffsta
   struct checkdiff_t {
        struct xdiff_emit_state xm;
        const char *filename;
 --     int lineno;
 ++     int lineno, color_diff;
   };
   
   static void checkdiff_consume(void *priv, char *line, unsigned long len)
   {
        struct checkdiff_t *data = priv;
 ++     const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE);
 ++     const char *reset = diff_get_color(data->color_diff, DIFF_RESET);
 ++     const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW);
   
        if (line[0] == '+') {
 --             int i, spaces = 0;
 ++             int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0;
   
                /* check space before tab */
                for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
                        if (line[i] == ' ')
                                spaces++;
                if (line[i - 1] == '\t' && spaces)
 --                     printf("%s:%d: space before tab:%.*s\n",
 --                             data->filename, data->lineno, (int)len, line);
 ++                     space_before_tab = 1;
   
                /* check white space at line end */
                if (line[len - 1] == '\n')
                        len--;
                if (isspace(line[len - 1]))
 --                     printf("%s:%d: white space at end: %.*s\n",
 --                             data->filename, data->lineno, (int)len, line);
 ++                     white_space_at_end = 1;
 ++
 ++             if (space_before_tab || white_space_at_end) {
 ++                     printf("%s:%d: %s", data->filename, data->lineno, ws);
 ++                     if (space_before_tab) {
 ++                             printf("space before tab");
 ++                             if (white_space_at_end)
 ++                                     putchar(',');
 ++                     }
 ++                     if (white_space_at_end)
 ++                             printf("white space at end");
 ++                     printf(":%s ", reset);
 ++                     emit_line_with_ws(1, set, reset, ws, line, len);
 ++             }
   
                data->lineno++;
        } else if (line[0] == ' ')
@@@@ -1094,8 -1050,7 -1050,7 +1094,8 @@@@ static void builtin_diff(const char *na
                if ((one->mode ^ two->mode) & S_IFMT)
                        goto free_ab_and_return;
                if (complete_rewrite) {
 --                     emit_rewrite_diff(name_a, name_b, one, two);
 ++                     emit_rewrite_diff(name_a, name_b, one, two,
 ++                                     o->color_diff);
                        goto free_ab_and_return;
                }
        }
                xecfg.flags = XDL_EMIT_FUNCNAMES;
                if (!diffopts)
                        ;
 --             else if (!strncmp(diffopts, "--unified=", 10))
 ++             else if (!prefixcmp(diffopts, "--unified="))
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 --             else if (!strncmp(diffopts, "-u", 2))
 ++             else if (!prefixcmp(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
                ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
@@@@ -1196,7 -1151,7 -1151,7 +1196,7 @@@@ static void builtin_diffstat(const cha
   
   static void builtin_checkdiff(const char *name_a, const char *name_b,
                             struct diff_filespec *one,
 --                          struct diff_filespec *two)
 ++                          struct diff_filespec *two, struct diff_options *o)
   {
        mmfile_t mf1, mf2;
        struct checkdiff_t data;
        data.xm.consume = checkdiff_consume;
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
 ++     data.color_diff = o->color_diff;
   
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@@@ -1378,6 -1332,9 -1332,6 +1378,9 @@@@ int diff_populate_filespec(struct diff_
            reuse_worktree_file(s->path, s->sha1, 0)) {
                struct stat st;
                int fd;
+ +             char *buf;
+ +             unsigned long size;
+ +
                if (lstat(s->path, &st) < 0) {
                        if (errno == ENOENT) {
                        err_empty:
                s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
                close(fd);
                s->should_munmap = 1;
- -             /* FIXME! CRLF -> LF conversion goes here, based on "s->path" */
+ +
+ +             /*
+ +              * Convert from working tree format to canonical git format
+ +              */
+ +             buf = s->data;
+ +             size = s->size;
+ +             if (convert_to_git(s->path, &buf, &size)) {
+ +                     munmap(s->data, s->size);
+ +                     s->should_munmap = 0;
+ +                     s->data = buf;
+ +                     s->size = size;
+ +                     s->should_free = 1;
+ +             }
        }
        else {
                char type[20];
@@@@ -1819,7 -1788,7 -1773,7 +1834,7 @@@@ static void run_checkdiff(struct diff_f
        diff_fill_sha1_info(p->one);
        diff_fill_sha1_info(p->two);
   
 --     builtin_checkdiff(name, other, p->one, p->two);
 ++     builtin_checkdiff(name, other, p->one, p->two, o);
   }
   
   void diff_setup(struct diff_options *options)
@@@@ -1968,7 -1937,7 -1922,7 +1983,7 @@@@ int diff_opt_parse(struct diff_options 
        else if (!strcmp(arg, "--shortstat")) {
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        }
 --     else if (!strncmp(arg, "--stat", 6)) {
 ++     else if (!prefixcmp(arg, "--stat")) {
                char *end;
                int width = options->stat_width;
                int name_width = options->stat_name_width;
   
                switch (*arg) {
                case '-':
 --                     if (!strncmp(arg, "-width=", 7))
 ++                     if (!prefixcmp(arg, "-width="))
                                width = strtoul(arg + 7, &end, 10);
 --                     else if (!strncmp(arg, "-name-width=", 12))
 ++                     else if (!prefixcmp(arg, "-name-width="))
                                name_width = strtoul(arg + 12, &end, 10);
                        break;
                case '=':
        }
        else if (!strcmp(arg, "-z"))
                options->line_termination = 0;
 --     else if (!strncmp(arg, "-l", 2))
 ++     else if (!prefixcmp(arg, "-l"))
                options->rename_limit = strtoul(arg+2, NULL, 10);
        else if (!strcmp(arg, "--full-index"))
                options->full_index = 1;
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-R"))
                options->reverse_diff = 1;
 --     else if (!strncmp(arg, "-S", 2))
 ++     else if (!prefixcmp(arg, "-S"))
                options->pickaxe = arg + 2;
        else if (!strcmp(arg, "-s")) {
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
        }
 --     else if (!strncmp(arg, "-O", 2))
 ++     else if (!prefixcmp(arg, "-O"))
                options->orderfile = arg + 2;
 --     else if (!strncmp(arg, "--diff-filter=", 14))
 ++     else if (!prefixcmp(arg, "--diff-filter="))
                options->filter = arg + 14;
        else if (!strcmp(arg, "--pickaxe-all"))
                options->pickaxe_opts = DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts = DIFF_PICKAXE_REGEX;
 --     else if (!strncmp(arg, "-B", 2)) {
 ++     else if (!prefixcmp(arg, "-B")) {
                if ((options->break_opt =
                     diff_scoreopt_parse(arg)) == -1)
                        return -1;
        }
 --     else if (!strncmp(arg, "-M", 2)) {
 ++     else if (!prefixcmp(arg, "-M")) {
                if ((options->rename_score =
                     diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->detect_rename = DIFF_DETECT_RENAME;
        }
 --     else if (!strncmp(arg, "-C", 2)) {
 ++     else if (!prefixcmp(arg, "-C")) {
                if ((options->rename_score =
                     diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->find_copies_harder = 1;
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
 --     else if (!strncmp(arg, "--abbrev=", 9)) {
 ++     else if (!prefixcmp(arg, "--abbrev=")) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
                options->xdl_opts |= XDF_IGNORE_WHITESPACE;
        else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
 ++     else if (!strcmp(arg, "--ignore-space-at-eol"))
 ++             options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
        else if (!strcmp(arg, "--color-words"))
                options->color_diff = options->color_diff_words = 1;
        else if (!strcmp(arg, "--no-renames"))
@@@@ -2564,7 -2531,7 -2516,7 +2579,7 @@@@ static void patch_id_consume(void *priv
        int new_len;
   
        /* Ignore line numbers when computing the SHA1 of the patch */
 --     if (!strncmp(line, "@@ -", 4))
 ++     if (!prefixcmp(line, "@@ -"))
                return;
   
        new_len = remove_space(line, len);
diff --combined sha1_file.c
index 2c870314d525ba0666470d53cf7901a2bac9e3c0,6ec67b2923aafdb1e2c0da556e584db0468de9ff,8ad7fad825708b5a56a3bfd4b8b98708de0bb2dc..9a1dee051a20891698f44814824ba8aa3b83bc3a
@@@@ -407,6 -407,7 -407,7 +407,6 @@@@ static unsigned int peak_pack_open_wind
   static unsigned int pack_open_windows;
   static size_t peak_pack_mapped;
   static size_t pack_mapped;
 --static size_t page_size;
   struct packed_git *packed_git;
   
   void pack_report()
                "pack_report: getpagesize()            = %10" SZ_FMT "\n"
                "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
                "pack_report: core.packedGitLimit      = %10" SZ_FMT "\n",
 --             page_size,
 ++             (size_t) getpagesize(),
                packed_git_window_size,
                packed_git_limit);
        fprintf(stderr,
@@@@ -661,9 -662,10 -662,10 +661,9 @@@@ unsigned char* use_pack(struct packed_g
                                break;
                }
                if (!win) {
 --                     if (!page_size)
 --                             page_size = getpagesize();
 ++                     size_t window_align = packed_git_window_size / 2;
                        win = xcalloc(1, sizeof(*win));
 --                     win->offset = (offset / page_size) * page_size;
 ++                     win->offset = (offset / window_align) * window_align;
                        win->len = p->pack_size - win->offset;
                        if (win->len > packed_git_window_size)
                                win->len = packed_git_window_size;
@@@@ -1545,13 -1547,11 -1547,11 +1545,13 @@@@ int pretend_sha1_file(void *buf, unsign
        co = &cached_objects[cached_object_nr++];
        co->size = len;
        co->type = strdup(type);
 ++     co->buf = xmalloc(len);
 ++     memcpy(co->buf, buf, len);
        hashcpy(co->sha1, sha1);
        return 0;
   }
   
 --void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
 ++void *read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
   {
        unsigned long mapsize;
        void *map, *buf;
@@@@ -2082,7 -2082,7 -2082,7 +2082,7 @@@@ int index_fd(unsigned char *sha1, int f
   {
        unsigned long size = st->st_size;
        void *buf;
- -     int ret;
+ +     int ret, re_allocated = 0;
   
        buf = "";
        if (size)
   
        if (!type)
                type = blob_type;
- -     /* FIXME: CRLF -> LF conversion here for blobs! We'll need the path! */
+ +
+ +     /*
+ +      * Convert blobs to git internal format
+ +      */
+ +     if (!strcmp(type, blob_type)) {
+ +             unsigned long nsize = size;
+ +             char *nbuf = buf;
+ +             if (convert_to_git(NULL, &nbuf, &nsize)) {
+ +                     if (size)
+ +                             munmap(buf, size);
+ +                     size = nsize;
+ +                     buf = nbuf;
+ +                     re_allocated = 1;
+ +             }
+ +     }
+ +
        if (write_object)
                ret = write_sha1_file(buf, size, type, sha1);
        else
                ret = hash_sha1_file(buf, size, type, sha1);
+ +     if (re_allocated) {
+ +             free(buf);
+ +             return ret;
+ +     }
        if (size)
                munmap(buf, size);
        return ret;