blame,cat-file --textconv: Don't assume mode is ``S_IFREF | 0664''
authorKirill Smelkov <kirr@landau.phys.spbu.ru>
Wed, 29 Sep 2010 11:35:24 +0000 (15:35 +0400)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 Sep 2010 23:53:25 +0000 (16:53 -0700)
We need to get the correct mode when blame reads the source from the
working tree, the index, or trees. This allows us to omit running
textconv filters on symbolic links.

Signed-off-by: Kirill Smelkov <kirr@landau.phys.spbu.ru>
Reviewed-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin.h
builtin/blame.c
builtin/cat-file.c
sha1_name.c
t/t8006-blame-textconv.sh
t/t8007-cat-file-textconv.sh
index ed6ee26933430e1db4e29e62badfaf0b217935ad..98b1e85e8b98a2e8f09d95d88f843e9f842701f4 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -37,7 +37,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
 
 extern int check_pager_config(const char *cmd);
 
-extern int textconv_object(const char *path, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
index 28e3be2eadadc71723da96334b8ff1dd22e73205..173f286b193111c85f692cba13fd4c8c632d46b0 100644 (file)
@@ -83,6 +83,7 @@ struct origin {
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
+       unsigned mode;
        char path[FLEX_ARRAY];
 };
 
@@ -92,6 +93,7 @@ struct origin {
  * Return 1 if the conversion succeeds, 0 otherwise.
  */
 int textconv_object(const char *path,
+                   unsigned mode,
                    const unsigned char *sha1,
                    char **buf,
                    unsigned long *buf_size)
@@ -100,7 +102,7 @@ int textconv_object(const char *path,
        struct userdiff_driver *textconv;
 
        df = alloc_filespec(path);
-       fill_filespec(df, sha1, S_IFREG | 0664);
+       fill_filespec(df, sha1, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@ -125,7 +127,7 @@ static void fill_origin_blob(struct diff_options *opt,
 
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                   textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
+                   textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@ -313,21 +315,23 @@ static struct origin *get_origin(struct scoreboard *sb,
  * for an origin is also used to pass the blame for the entire file to
  * the parent to detect the case where a child's blob is identical to
  * that of its parent's.
+ *
+ * This also fills origin->mode for corresponding tree path.
  */
-static int fill_blob_sha1(struct origin *origin)
+static int fill_blob_sha1_and_mode(struct origin *origin)
 {
-       unsigned mode;
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
        if (get_tree_entry(origin->commit->object.sha1,
                           origin->path,
-                          origin->blob_sha1, &mode))
+                          origin->blob_sha1, &origin->mode))
                goto error_out;
        if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
                goto error_out;
        return 0;
  error_out:
        hashclr(origin->blob_sha1);
+       origin->mode = S_IFINVALID;
        return -1;
 }
 
@@ -360,12 +364,14 @@ static struct origin *find_origin(struct scoreboard *sb,
                        /*
                         * If the origin was newly created (i.e. get_origin
                         * would call make_origin if none is found in the
-                        * scoreboard), it does not know the blob_sha1,
+                        * scoreboard), it does not know the blob_sha1/mode,
                         * so copy it.  Otherwise porigin was in the
-                        * scoreboard and already knows blob_sha1.
+                        * scoreboard and already knows blob_sha1/mode.
                         */
-                       if (porigin->refcnt == 1)
+                       if (porigin->refcnt == 1) {
                                hashcpy(porigin->blob_sha1, cached->blob_sha1);
+                               porigin->mode = cached->mode;
+                       }
                        return porigin;
                }
                /* otherwise it was not very useful; free it */
@@ -400,6 +406,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                /* The path is the same as parent */
                porigin = get_origin(sb, parent, origin->path);
                hashcpy(porigin->blob_sha1, origin->blob_sha1);
+               porigin->mode = origin->mode;
        } else {
                /*
                 * Since origin->path is a pathspec, if the parent
@@ -425,6 +432,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                case 'M':
                        porigin = get_origin(sb, parent, origin->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
+                       porigin->mode = p->one->mode;
                        break;
                case 'A':
                case 'T':
@@ -444,6 +452,7 @@ static struct origin *find_origin(struct scoreboard *sb,
 
                cached = make_origin(porigin->commit, porigin->path);
                hashcpy(cached->blob_sha1, porigin->blob_sha1);
+               cached->mode = porigin->mode;
                parent->util = cached;
        }
        return porigin;
@@ -486,6 +495,7 @@ static struct origin *find_rename(struct scoreboard *sb,
                    !strcmp(p->two->path, origin->path)) {
                        porigin = get_origin(sb, parent, p->one->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
+                       porigin->mode = p->one->mode;
                        break;
                }
        }
@@ -1099,6 +1109,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
 
                        norigin = get_origin(sb, parent, p->one->path);
                        hashcpy(norigin->blob_sha1, p->one->sha1);
+                       norigin->mode = p->one->mode;
                        fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
                                continue;
@@ -2083,7 +2094,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                           textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
+                           textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
                                buf.len = buf_len;
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@ -2463,11 +2474,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        }
        else {
                o = get_origin(&sb, sb.final, path);
-               if (fill_blob_sha1(o))
+               if (fill_blob_sha1_and_mode(o))
                        die("no such path %s in %s", path, final_commit_name);
 
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
-                   textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
+                   textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
index 76ec3fec9279f38520fa4b5b525ce2c03cc9cbb0..94632dbdb400f9a2986d10f06dd16119ce1b4e54 100644 (file)
@@ -143,7 +143,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
-               if (!textconv_object(obj_context.path, sha1, &buf, &size))
+               if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
                        die("git cat-file --textconv: unable to run textconv on %s",
                            obj_name);
                break;
index 4af94fa59806c570c177a68139b54d46772d68a5..36c9cbf1acd94b4614f1a66e73664910309c279a 100644 (file)
@@ -1068,6 +1068,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                struct cache_entry *ce;
                int pos;
                if (namelen > 2 && name[1] == '/')
+                       /* don't need mode for commit */
                        return get_sha1_oneline(name + 2, sha1);
                if (namelen < 3 ||
                    name[2] != ':' ||
@@ -1095,6 +1096,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                                break;
                        if (ce_stage(ce) == stage) {
                                hashcpy(sha1, ce->sha1);
+                               oc->mode = ce->ce_mode;
                                return 0;
                        }
                        pos++;
index 7c35959b44c320e45587b71036a95f6ed782ac78..dbf623bce5598860800fa463c26ec8145d6e2400 100755 (executable)
@@ -94,8 +94,7 @@ test_expect_success SYMLINKS 'blame with --no-textconv (on symlink)' '
        test_cmp expected result
 '
 
-# fails with '...symlink.bin is not "binary" file'
-test_expect_failure SYMLINKS 'blame --textconv (on symlink)' '
+test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
        git blame --textconv symlink.bin >blame &&
        find_blame <blame >result &&
        test_cmp expected result
@@ -114,8 +113,7 @@ EOF
        GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
 '
 
-# fails with '...symlink.bin is not "binary" file'
-test_expect_failure SYMLINKS 'blame on last commit (-C -C, symlink)' '
+test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
        git blame -C -C three.bin >blame &&
        find_blame <blame >result &&
        cat >expected <<\EOF &&
index 98a3e1f571a5c8a73e9c6ae38e5eae85899419cc..78a0085e648b8fa6773b47e86959853cf29ccdf9 100755 (executable)
@@ -79,8 +79,7 @@ test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
 '
 
 
-# fails because cat-file tries to run converter on symlink.bin
-test_expect_failure SYMLINKS 'cat-file --textconv on index (symlink)' '
+test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
        ! git cat-file --textconv :symlink.bin 2>result &&
        cat >expected <<\EOF &&
 fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
@@ -88,8 +87,7 @@ EOF
        test_cmp expected result
 '
 
-# fails because cat-file tries to run converter on symlink.bin
-test_expect_failure SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
+test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
        ! git cat-file --textconv HEAD:symlink.bin 2>result &&
        cat >expected <<EOF &&
 fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin