Merge branch 'jk/diff-blob' into maint
authorJunio C Hamano <gitster@pobox.com>
Sat, 24 Jun 2017 22:29:28 +0000 (15:29 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 24 Jun 2017 22:29:28 +0000 (15:29 -0700)
The result from "git diff" that compares two blobs, e.g. "git diff
$commit1:$path $commit2:$path", used to be shown with the full
object name as given on the command line, but it is more natural to
use the $path in the output and use it to look up .gitattributes.

* jk/diff-blob:
diff: use blob path for blob/file diffs
diff: use pending "path" if it is available
diff: use the word "path" instead of "name" for blobs
diff: pass whole pending entry in blobinfo
handle_revision_arg: record paths for pending objects
handle_revision_arg: record modes for "a..b" endpoints
t4063: add tests of direct blob diffs
get_sha1_with_context: dynamically allocate oc->path
get_sha1_with_context: always initialize oc->symlink_path
sha1_name: consistently refer to object_context as "oc"
handle_revision_arg: add handle_dotdot() helper
handle_revision_arg: hoist ".." check out of range parsing
handle_revision_arg: stop using "dotdot" as a generic pointer
handle_revision_arg: simplify commit reference lookups
handle_revision_arg: reset "dotdot" consistently

1  2 
builtin/cat-file.c
builtin/log.c
revision.c
t/t4202-log.sh
tree-walk.c
diff --combined builtin/cat-file.c
index 9af863e7915d21aee7af3cb4d01ebbad2efbcb59,421709517c765d83fa4a4abf00ff3b8a9dd79f64..73c81f0cb1ae34169667adc7ee7fc246468e08a4
@@@ -61,7 -61,8 +61,8 @@@ static int cat_one_file(int opt, const 
        if (unknown_type)
                flags |= LOOKUP_UNKNOWN_OBJECT;
  
-       if (get_sha1_with_context(obj_name, 0, oid.hash, &obj_context))
+       if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
+                                 oid.hash, &obj_context))
                die("Not a valid object name %s", obj_name);
  
        if (!path)
                die("git cat-file %s: bad file", obj_name);
  
        write_or_die(1, buf, size);
 +      free(buf);
+       free(obj_context.path);
        return 0;
  }
  
diff --combined builtin/log.c
index ec3258368ca96eb9120c64994b8132a6e00b5d24,1db016bcd91c9c56ee2a3b146be64e40f2d1f7ed..57ce470f5094c238ea4d5390f358b5cdd9499a56
@@@ -110,8 -110,6 +110,8 @@@ static void init_log_defaults(void
  {
        init_grep_defaults();
        init_diff_ui_defaults();
 +
 +      decoration_style = auto_decoration_style();
  }
  
  static void cmd_log_init_defaults(struct rev_info *rev)
@@@ -412,6 -410,8 +412,6 @@@ static int git_log_config(const char *v
                if (decoration_style < 0)
                        decoration_style = 0; /* maybe warn? */
                return 0;
 -      } else {
 -              decoration_style = auto_decoration_style();
        }
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
@@@ -483,16 -483,20 +483,20 @@@ static int show_blob_object(const struc
            !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
                return stream_blob_to_fd(1, oid, NULL, 0);
  
-       if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context))
+       if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
+                                 oidc.hash, &obj_context))
                die(_("Not a valid object name %s"), obj_name);
-       if (!obj_context.path[0] ||
-           !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size))
+       if (!obj_context.path ||
+           !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
+               free(obj_context.path);
                return stream_blob_to_fd(1, oid, NULL, 0);
+       }
  
        if (!buf)
                die(_("git show %s: bad file"), obj_name);
  
        write_or_die(1, buf, size);
+       free(obj_context.path);
        return 0;
  }
  
diff --combined revision.c
index 67ebc6fc4b89358f1d28dc223935d66bfb00135b,a927d263a53f62cc7bcb0b9db0f25f250b759bc6..d2a6bfcc9e7cb58ad2c8f78ccdc2baeb0c64575e
@@@ -230,7 -230,7 +230,7 @@@ static struct commit *handle_commit(str
                        die("bad tag");
                object = parse_object(tag->tagged->oid.hash);
                if (!object) {
 -                      if (flags & UNINTERESTING)
 +                      if (revs->ignore_missing_links || (flags & UNINTERESTING))
                                return NULL;
                        die("bad object %s", oid_to_hex(&tag->tagged->oid));
                }
@@@ -1429,134 -1429,168 +1429,168 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
+ static int dotdot_missing(const char *arg, char *dotdot,
+                         struct rev_info *revs, int symmetric)
+ {
+       if (revs->ignore_missing)
+               return 0;
+       /* de-munge so we report the full argument */
+       *dotdot = '.';
+       die(symmetric
+           ? "Invalid symmetric difference expression %s"
+           : "Invalid revision range %s", arg);
+ }
+ static int handle_dotdot_1(const char *arg, char *dotdot,
+                          struct rev_info *revs, int flags,
+                          int cant_be_filename,
+                          struct object_context *a_oc,
+                          struct object_context *b_oc)
+ {
+       const char *a_name, *b_name;
+       struct object_id a_oid, b_oid;
+       struct object *a_obj, *b_obj;
+       unsigned int a_flags, b_flags;
+       int symmetric = 0;
+       unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
+       unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
+       a_name = arg;
+       if (!*a_name)
+               a_name = "HEAD";
+       b_name = dotdot + 2;
+       if (*b_name == '.') {
+               symmetric = 1;
+               b_name++;
+       }
+       if (!*b_name)
+               b_name = "HEAD";
+       if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
+           get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
+               return -1;
+       if (!cant_be_filename) {
+               *dotdot = '.';
+               verify_non_filename(revs->prefix, arg);
+               *dotdot = '\0';
+       }
+       a_obj = parse_object(a_oid.hash);
+       b_obj = parse_object(b_oid.hash);
+       if (!a_obj || !b_obj)
+               return dotdot_missing(arg, dotdot, revs, symmetric);
+       if (!symmetric) {
+               /* just A..B */
+               b_flags = flags;
+               a_flags = flags_exclude;
+       } else {
+               /* A...B -- find merge bases between the two */
+               struct commit *a, *b;
+               struct commit_list *exclude;
+               a = lookup_commit_reference(a_obj->oid.hash);
+               b = lookup_commit_reference(b_obj->oid.hash);
+               if (!a || !b)
+                       return dotdot_missing(arg, dotdot, revs, symmetric);
+               exclude = get_merge_bases(a, b);
+               add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
+                                    flags_exclude);
+               add_pending_commit_list(revs, exclude, flags_exclude);
+               free_commit_list(exclude);
+               b_flags = flags;
+               a_flags = flags | SYMMETRIC_LEFT;
+       }
+       a_obj->flags |= a_flags;
+       b_obj->flags |= b_flags;
+       add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags);
+       add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags);
+       add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path);
+       add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path);
+       return 0;
+ }
+ static int handle_dotdot(const char *arg,
+                        struct rev_info *revs, int flags,
+                        int cant_be_filename)
+ {
+       struct object_context a_oc, b_oc;
+       char *dotdot = strstr(arg, "..");
+       int ret;
+       if (!dotdot)
+               return -1;
+       memset(&a_oc, 0, sizeof(a_oc));
+       memset(&b_oc, 0, sizeof(b_oc));
+       *dotdot = '\0';
+       ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
+                             &a_oc, &b_oc);
+       *dotdot = '.';
+       free(a_oc.path);
+       free(b_oc.path);
+       return ret;
+ }
  int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
  {
        struct object_context oc;
-       char *dotdot;
+       char *mark;
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
        const char *arg = arg_;
        int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
-       unsigned get_sha1_flags = 0;
+       unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
  
        flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
  
-       dotdot = strstr(arg, "..");
-       if (dotdot) {
-               unsigned char from_sha1[20];
-               const char *next = dotdot + 2;
-               const char *this = arg;
-               int symmetric = *next == '.';
-               unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
-               static const char head_by_default[] = "HEAD";
-               unsigned int a_flags;
-               *dotdot = 0;
-               next += symmetric;
-               if (!*next)
-                       next = head_by_default;
-               if (dotdot == arg)
-                       this = head_by_default;
-               if (this == head_by_default && next == head_by_default &&
-                   !symmetric) {
-                       /*
-                        * Just ".."?  That is not a range but the
-                        * pathspec for the parent directory.
-                        */
-                       if (!cant_be_filename) {
-                               *dotdot = '.';
-                               return -1;
-                       }
-               }
-               if (!get_sha1_committish(this, from_sha1) &&
-                   !get_sha1_committish(next, sha1)) {
-                       struct object *a_obj, *b_obj;
-                       if (!cant_be_filename) {
-                               *dotdot = '.';
-                               verify_non_filename(revs->prefix, arg);
-                       }
-                       a_obj = parse_object(from_sha1);
-                       b_obj = parse_object(sha1);
-                       if (!a_obj || !b_obj) {
-                       missing:
-                               if (revs->ignore_missing)
-                                       return 0;
-                               die(symmetric
-                                   ? "Invalid symmetric difference expression %s"
-                                   : "Invalid revision range %s", arg);
-                       }
-                       if (!symmetric) {
-                               /* just A..B */
-                               a_flags = flags_exclude;
-                       } else {
-                               /* A...B -- find merge bases between the two */
-                               struct commit *a, *b;
-                               struct commit_list *exclude;
-                               a = (a_obj->type == OBJ_COMMIT
-                                    ? (struct commit *)a_obj
-                                    : lookup_commit_reference(a_obj->oid.hash));
-                               b = (b_obj->type == OBJ_COMMIT
-                                    ? (struct commit *)b_obj
-                                    : lookup_commit_reference(b_obj->oid.hash));
-                               if (!a || !b)
-                                       goto missing;
-                               exclude = get_merge_bases(a, b);
-                               add_rev_cmdline_list(revs, exclude,
-                                                    REV_CMD_MERGE_BASE,
-                                                    flags_exclude);
-                               add_pending_commit_list(revs, exclude,
-                                                       flags_exclude);
-                               free_commit_list(exclude);
-                               a_flags = flags | SYMMETRIC_LEFT;
-                       }
-                       a_obj->flags |= a_flags;
-                       b_obj->flags |= flags;
-                       add_rev_cmdline(revs, a_obj, this,
-                                       REV_CMD_LEFT, a_flags);
-                       add_rev_cmdline(revs, b_obj, next,
-                                       REV_CMD_RIGHT, flags);
-                       add_pending_object(revs, a_obj, this);
-                       add_pending_object(revs, b_obj, next);
-                       return 0;
-               }
-               *dotdot = '.';
+       if (!cant_be_filename && !strcmp(arg, "..")) {
+               /*
+                * Just ".."?  That is not a range but the
+                * pathspec for the parent directory.
+                */
+               return -1;
        }
  
-       dotdot = strstr(arg, "^@");
-       if (dotdot && !dotdot[2]) {
-               *dotdot = 0;
+       if (!handle_dotdot(arg, revs, flags, revarg_opt))
+               return 0;
+       mark = strstr(arg, "^@");
+       if (mark && !mark[2]) {
+               *mark = 0;
                if (add_parents_only(revs, arg, flags, 0))
                        return 0;
-               *dotdot = '^';
+               *mark = '^';
        }
-       dotdot = strstr(arg, "^!");
-       if (dotdot && !dotdot[2]) {
-               *dotdot = 0;
+       mark = strstr(arg, "^!");
+       if (mark && !mark[2]) {
+               *mark = 0;
                if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
-                       *dotdot = '^';
+                       *mark = '^';
        }
-       dotdot = strstr(arg, "^-");
-       if (dotdot) {
+       mark = strstr(arg, "^-");
+       if (mark) {
                int exclude_parent = 1;
  
-               if (dotdot[2]) {
+               if (mark[2]) {
                        char *end;
-                       exclude_parent = strtoul(dotdot + 2, &end, 10);
+                       exclude_parent = strtoul(mark + 2, &end, 10);
                        if (*end != '\0' || !exclude_parent)
                                return -1;
                }
  
-               *dotdot = 0;
+               *mark = 0;
                if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
-                       *dotdot = '^';
+                       *mark = '^';
        }
  
        local_flags = 0;
        }
  
        if (revarg_opt & REVARG_COMMITTISH)
-               get_sha1_flags = GET_SHA1_COMMITTISH;
+               get_sha1_flags |= GET_SHA1_COMMITTISH;
  
        if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
                return revs->ignore_missing ? 0 : -1;
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
-       add_pending_object_with_mode(revs, object, arg, oc.mode);
+       add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
+       free(oc.path);
        return 0;
  }
  
diff --combined t/t4202-log.sh
index 6499cdf6ff06b6da3185107f76902cc88767c15d,6da1bbe9122e71a79658495c9ace61441071080c..e4441957e2b406c16121578f6d1344488509cafe
@@@ -399,7 -399,7 +399,7 @@@ cat > expect <<\EO
  | |
  | |     Merge branch 'side'
  | |
 -| * commit side
 +| * commit tags/side-2
  | | Author: A U Thor <author@example.com>
  | |
  | |     side-2
@@@ -577,18 -577,6 +577,18 @@@ test_expect_success 'log.decorate confi
  
  '
  
 +test_expect_success 'log.decorate config parsing' '
 +      git log --oneline --decorate=full >expect.full &&
 +      git log --oneline --decorate=short >expect.short &&
 +
 +      test_config log.decorate full &&
 +      test_config log.mailmap true &&
 +      git log --oneline >actual &&
 +      test_cmp expect.full actual &&
 +      git log --oneline --decorate=short >actual &&
 +      test_cmp expect.short actual
 +'
 +
  test_expect_success TTY 'log output on a TTY' '
        git log --oneline --decorate >expect.short &&
  
@@@ -1392,4 -1380,13 +1392,13 @@@ test_expect_success 'log --source paint
        test_cmp expect actual
  '
  
+ test_expect_success 'log --source paints symmetric ranges' '
+       cat >expect <<-\EOF &&
+       09e12a9 source-b three
+       8e393e1 source-a two
+       EOF
+       git log --oneline --source source-a...source-b >actual &&
+       test_cmp expect actual
+ '
  test_done
diff --combined tree-walk.c
index f25a08fddfab443b1eff1f13f33939f1b21f4b23,c7ecfc8560aca64d21466aa666c2cbfe12ec5ad3..6a42e402b00a3da90a008adf6e4c68dc8c45e516
@@@ -589,7 -589,6 +589,6 @@@ enum follow_symlinks_result get_tree_en
        int i;
  
        init_tree_desc(&t, NULL, 0UL);
-       strbuf_init(result_path, 0);
        strbuf_addstr(&namebuf, name);
        hashcpy(current_tree_sha1, tree_sha1);
  
@@@ -1075,7 -1074,7 +1074,7 @@@ match_wildcards
                 * later on.
                 * max_depth is ignored but we may consider support it
                 * in future, see
 -               * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840
 +               * https://public-inbox.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/
                 */
                if (ps->recursive && S_ISDIR(entry->mode))
                        return entry_interesting;