Merge branch 'jk/diff-blob'
authorJunio C Hamano <gitster@pobox.com>
Fri, 2 Jun 2017 06:06:05 +0000 (15:06 +0900)
committerJunio C Hamano <gitster@pobox.com>
Fri, 2 Jun 2017 06:06:05 +0000 (15:06 +0900)
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/diff.c
builtin/grep.c
builtin/log.c
cache.h
revision.c
sha1_name.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/diff.c
index 8c03ddaf589bbdce723b00a9d911673b064bd13e,5e7c6428c91203fa1f3fb0c7790364987a623fc0..0c8f86e40da6537d57f5ed6ca0ecdd005e29c8f5
  #define DIFF_NO_INDEX_EXPLICIT 1
  #define DIFF_NO_INDEX_IMPLICIT 2
  
- struct blobinfo {
-       struct object_id oid;
-       const char *name;
-       unsigned mode;
- };
  static const char builtin_diff_usage[] =
  "git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
  
+ static const char *blob_path(struct object_array_entry *entry)
+ {
+       return entry->path ? entry->path : entry->name;
+ }
  static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const struct object_id *old_oid,
                         const struct object_id *new_oid,
                         int old_oid_valid,
                         int new_oid_valid,
-                        const char *old_name,
-                        const char *new_name)
+                        const char *old_path,
+                        const char *new_path)
  {
        struct diff_filespec *one, *two;
  
        if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
-               SWAP(old_name, new_name);
+               SWAP(old_path, new_path);
        }
  
        if (opt->prefix &&
-           (strncmp(old_name, opt->prefix, opt->prefix_length) ||
-            strncmp(new_name, opt->prefix, opt->prefix_length)))
+           (strncmp(old_path, opt->prefix, opt->prefix_length) ||
+            strncmp(new_path, opt->prefix, opt->prefix_length)))
                return;
  
-       one = alloc_filespec(old_name);
-       two = alloc_filespec(new_name);
+       one = alloc_filespec(old_path);
+       two = alloc_filespec(new_path);
        fill_filespec(one, old_oid->hash, old_oid_valid, old_mode);
        fill_filespec(two, new_oid->hash, new_oid_valid, new_mode);
  
@@@ -65,7 -64,7 +64,7 @@@
  
  static int builtin_diff_b_f(struct rev_info *revs,
                            int argc, const char **argv,
-                           struct blobinfo *blob)
+                           struct object_array_entry **blob)
  {
        /* Blob vs file in the working tree*/
        struct stat st;
  
        diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
  
-       if (blob[0].mode == S_IFINVALID)
-               blob[0].mode = canon_mode(st.st_mode);
+       if (blob[0]->mode == S_IFINVALID)
+               blob[0]->mode = canon_mode(st.st_mode);
  
        stuff_change(&revs->diffopt,
-                    blob[0].mode, canon_mode(st.st_mode),
-                    &blob[0].oid, &null_oid,
+                    blob[0]->mode, canon_mode(st.st_mode),
+                    &blob[0]->item->oid, &null_oid,
                     1, 0,
-                    path, path);
+                    blob[0]->path ? blob[0]->path : path,
+                    path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
  
  static int builtin_diff_blobs(struct rev_info *revs,
                              int argc, const char **argv,
-                             struct blobinfo *blob)
+                             struct object_array_entry **blob)
  {
        unsigned mode = canon_mode(S_IFREG | 0644);
  
        if (argc > 1)
                usage(builtin_diff_usage);
  
-       if (blob[0].mode == S_IFINVALID)
-               blob[0].mode = mode;
+       if (blob[0]->mode == S_IFINVALID)
+               blob[0]->mode = mode;
  
-       if (blob[1].mode == S_IFINVALID)
-               blob[1].mode = mode;
+       if (blob[1]->mode == S_IFINVALID)
+               blob[1]->mode = mode;
  
        stuff_change(&revs->diffopt,
-                    blob[0].mode, blob[1].mode,
-                    &blob[0].oid, &blob[1].oid,
+                    blob[0]->mode, blob[1]->mode,
+                    &blob[0]->item->oid, &blob[1]->item->oid,
                     1, 1,
-                    blob[0].name, blob[1].name);
+                    blob_path(blob[0]), blob_path(blob[1]));
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@@ -259,7 -259,7 +259,7 @@@ int cmd_diff(int argc, const char **arg
        struct rev_info rev;
        struct object_array ent = OBJECT_ARRAY_INIT;
        int blobs = 0, paths = 0;
-       struct blobinfo blob[2];
+       struct object_array_entry *blob[2];
        int nongit = 0, no_index = 0;
        int result = 0;
  
                                add_head_to_pending(&rev);
                                if (!rev.pending.nr) {
                                        struct tree *tree;
 -                                      tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
 +                                      tree = lookup_tree(&empty_tree_oid);
                                        add_pending_object(&rev, &tree->object, "HEAD");
                                }
                                break;
                const char *name = entry->name;
                int flags = (obj->flags & UNINTERESTING);
                if (!obj->parsed)
 -                      obj = parse_object(obj->oid.hash);
 +                      obj = parse_object(&obj->oid);
                obj = deref_tag(obj, NULL, 0);
                if (!obj)
                        die(_("invalid object '%s' given."), name);
                } else if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die(_("more than two blobs given: '%s'"), name);
-                       oidcpy(&blob[blobs].oid, &obj->oid);
-                       blob[blobs].name = name;
-                       blob[blobs].mode = entry->mode;
+                       blob[blobs] = entry;
                        blobs++;
  
                } else {
diff --combined builtin/grep.c
index c6c26e9b9e383df482858f92500fd1334f3b062b,254c1c7849152884d9abd0b603f1c75f87e085b5..72a19699a829646eb5261d934ceebff16b5d4f4d
@@@ -866,7 -866,7 +866,7 @@@ static int grep_directory(struct grep_o
        if (exc_std)
                setup_standard_excludes(&dir);
  
 -      fill_directory(&dir, pathspec);
 +      fill_directory(&dir, &the_index, pathspec);
        for (i = 0; i < dir.nr; i++) {
                if (!dir_path_match(dir.entries[i], pathspec, 0, NULL))
                        continue;
@@@ -1190,16 -1190,18 +1190,18 @@@ int cmd_grep(int argc, const char **arg
                        break;
                }
  
-               if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
+               if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH,
+                                         oid.hash, &oc)) {
                        if (seen_dashdash)
                                die(_("unable to resolve revision: %s"), arg);
                        break;
                }
  
 -              object = parse_object_or_die(oid.hash, arg);
 +              object = parse_object_or_die(&oid, arg);
                if (!seen_dashdash)
                        verify_non_filename(prefix, arg);
                add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
+               free(oc.path);
        }
  
        /*
diff --combined builtin/log.c
index a440601efe826aadabdffbfd0d461f83047b8e99,1db016bcd91c9c56ee2a3b146be64e40f2d1f7ed..e89ec941ce2c97de7f8233a5e7e6f9010fabdb64
@@@ -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;
  }
  
@@@ -596,7 -600,7 +600,7 @@@ int cmd_show(int argc, const char **arg
                        rev.shown_one = 1;
                        if (ret)
                                break;
 -                      o = parse_object(t->tagged->oid.hash);
 +                      o = parse_object(&t->tagged->oid);
                        if (!o)
                                ret = error(_("Could not read object %s"),
                                            oid_to_hex(&t->tagged->oid));
@@@ -878,8 -882,8 +882,8 @@@ static void get_patch_ids(struct rev_in
        o2 = rev->pending.objects[1].item;
        flags1 = o1->flags;
        flags2 = o2->flags;
 -      c1 = lookup_commit_reference(o1->oid.hash);
 -      c2 = lookup_commit_reference(o2->oid.hash);
 +      c1 = lookup_commit_reference(&o1->oid);
 +      c2 = lookup_commit_reference(&o2->oid);
  
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
                die(_("Not a range."));
  static void gen_message_id(struct rev_info *info, char *base)
  {
        struct strbuf buf = STRBUF_INIT;
 -      strbuf_addf(&buf, "%s.%lu.git.%s", base,
 -                  (unsigned long) time(NULL),
 +      strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 +                  (timestamp_t) time(NULL),
                    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
        info->message_id = strbuf_detach(&buf, NULL);
  }
@@@ -1263,7 -1267,7 +1267,7 @@@ static struct commit *get_base_commit(c
  
                        if (get_oid(upstream, &oid))
                                die(_("Failed to resolve '%s' as a valid ref."), upstream);
 -                      commit = lookup_commit_or_die(oid.hash, "upstream base");
 +                      commit = lookup_commit_or_die(&oid, "upstream base");
                        base_list = get_merge_bases_many(commit, total, list);
                        /* There should be one and only one merge base. */
                        if (!base_list || base_list->next)
@@@ -1819,7 -1823,7 +1823,7 @@@ static int add_pending_commit(const cha
  {
        struct object_id oid;
        if (get_oid(arg, &oid) == 0) {
 -              struct commit *commit = lookup_commit_reference(oid.hash);
 +              struct commit *commit = lookup_commit_reference(&oid);
                if (commit) {
                        commit->object.flags |= flags;
                        add_pending_object(revs, &commit->object, arg);
diff --combined cache.h
index ae4c45d379d5b91941616c967a619ae643fe6921,52b91f5b64146e1b6c7c93aa5054a7aca6303b42..4d92aae0e818015c1ec7a6d6bc99cb1391e0d824
+++ b/cache.h
@@@ -597,7 -597,6 +597,7 @@@ extern int read_index_unmerged(struct i
  #define CLOSE_LOCK            (1 << 1)
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  extern int discard_index(struct index_state *);
 +extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@@ -1334,13 -1333,18 +1334,18 @@@ static inline int hex2chr(const char *s
  
  struct object_context {
        unsigned char tree[20];
-       char path[PATH_MAX];
        unsigned mode;
        /*
         * symlink_path is only used by get_tree_entry_follow_symlinks,
         * and only for symlinks that point outside the repository.
         */
        struct strbuf symlink_path;
+       /*
+        * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
+        * found when resolving the name. The caller is responsible for
+        * releasing the memory.
+        */
+       char *path;
  };
  
  #define GET_SHA1_QUIETLY           01
  #define GET_SHA1_TREEISH          020
  #define GET_SHA1_BLOB             040
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
+ #define GET_SHA1_RECORD_PATH     0200
  #define GET_SHA1_ONLY_TO_DIE    04000
  
  #define GET_SHA1_DISAMBIGUATORS \
@@@ -1364,7 -1369,7 +1370,7 @@@ extern int get_sha1_tree(const char *st
  extern int get_sha1_treeish(const char *str, unsigned char *sha1);
  extern int get_sha1_blob(const char *str, unsigned char *sha1);
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
- extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
+ extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
  
  extern int get_oid(const char *str, struct object_id *oid);
  
@@@ -1480,18 -1485,18 +1486,18 @@@ struct date_mode 
  #define DATE_MODE(t) date_mode_from_type(DATE_##t)
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
 -const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
 -void show_date_relative(unsigned long time, int tz, const struct timeval *now,
 +const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
 +void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
 -int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
 -int parse_expiry_date(const char *date, unsigned long *timestamp);
 +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
 +int parse_expiry_date(const char *date, timestamp_t *timestamp);
  void datestamp(struct strbuf *out);
  #define approxidate(s) approxidate_careful((s), NULL)
 -unsigned long approxidate_careful(const char *, int *);
 -unsigned long approxidate_relative(const char *date, const struct timeval *now);
 +timestamp_t approxidate_careful(const char *, int *);
 +timestamp_t approxidate_relative(const char *date, const struct timeval *now);
  void parse_date_format(const char *format, struct date_mode *mode);
 -int date_overflows(unsigned long date);
 +int date_overflows(timestamp_t date);
  
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
@@@ -2199,8 -2204,8 +2205,8 @@@ struct commit_list
  int try_merge_command(const char *strategy, size_t xopts_nr,
                const char **xopts, struct commit_list *common,
                const char *head_arg, struct commit_list *remotes);
 -int checkout_fast_forward(const unsigned char *from,
 -                        const unsigned char *to,
 +int checkout_fast_forward(const struct object_id *from,
 +                        const struct object_id *to,
                          int overwrite_ignore);
  
  
diff --combined revision.c
index b02394530926ebe24233de5e6653e7127755675a,a927d263a53f62cc7bcb0b9db0f25f250b759bc6..3680a43a6e35d9850414b42f663acdc184d4d366
@@@ -59,10 -59,10 +59,10 @@@ static void mark_tree_contents_unintere
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
 -                      mark_tree_uninteresting(lookup_tree(entry.oid->hash));
 +                      mark_tree_uninteresting(lookup_tree(entry.oid));
                        break;
                case OBJ_BLOB:
 -                      mark_blob_uninteresting(lookup_blob(entry.oid->hash));
 +                      mark_blob_uninteresting(lookup_blob(entry.oid));
                        break;
                default:
                        /* Subproject commit - not in this repository */
@@@ -177,23 -177,23 +177,23 @@@ void add_pending_object(struct rev_inf
  
  void add_head_to_pending(struct rev_info *revs)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct object *obj;
 -      if (get_sha1("HEAD", sha1))
 +      if (get_oid("HEAD", &oid))
                return;
 -      obj = parse_object(sha1);
 +      obj = parse_object(&oid);
        if (!obj)
                return;
        add_pending_object(revs, obj, "HEAD");
  }
  
  static struct object *get_reference(struct rev_info *revs, const char *name,
 -                                  const unsigned char *sha1,
 +                                  const struct object_id *oid,
                                    unsigned int flags)
  {
        struct object *object;
  
 -      object = parse_object(sha1);
 +      object = parse_object(oid);
        if (!object) {
                if (revs->ignore_missing)
                        return object;
        return object;
  }
  
 -void add_pending_sha1(struct rev_info *revs, const char *name,
 -                    const unsigned char *sha1, unsigned int flags)
 +void add_pending_oid(struct rev_info *revs, const char *name,
 +                    const struct object_id *oid, unsigned int flags)
  {
 -      struct object *object = get_reference(revs, name, sha1, flags);
 +      struct object *object = get_reference(revs, name, oid, flags);
        add_pending_object(revs, object, name);
  }
  
@@@ -228,9 -228,9 +228,9 @@@ static struct commit *handle_commit(str
                        add_pending_object(revs, object, tag->tag);
                if (!tag->tagged)
                        die("bad tag");
 -              object = parse_object(tag->tagged->oid.hash);
 +              object = parse_object(&tag->tagged->oid);
                if (!object) {
 -                      if (flags & UNINTERESTING)
 +                      if (revs->ignore_missing_links || (flags & UNINTERESTING))
                                return NULL;
                        die("bad object %s", oid_to_hex(&tag->tagged->oid));
                }
@@@ -884,7 -884,7 +884,7 @@@ static void cherry_pick_list(struct com
  /* How many extra uninteresting commits we want to see.. */
  #define SLOP 5
  
 -static int still_interesting(struct commit_list *src, unsigned long date, int slop,
 +static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
                             struct commit **interesting_cache)
  {
        /*
@@@ -1018,7 -1018,7 +1018,7 @@@ static void limit_left_right(struct com
  static int limit_list(struct rev_info *revs)
  {
        int slop = SLOP;
 -      unsigned long date = ~0ul;
 +      timestamp_t date = TIME_MAX;
        struct commit_list *list = revs->commits;
        struct commit_list *newlist = NULL;
        struct commit_list **p = &newlist;
@@@ -1157,9 -1157,9 +1157,9 @@@ static int handle_one_ref(const char *p
        if (ref_excluded(cb->all_revs->ref_excludes, path))
            return 0;
  
 -      object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags);
 +      object = get_reference(cb->all_revs, path, oid, cb->all_flags);
        add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
 -      add_pending_sha1(cb->all_revs, path, oid->hash, cb->all_flags);
 +      add_pending_oid(cb->all_revs, path, oid, cb->all_flags);
        return 0;
  }
  
@@@ -1200,7 -1200,7 +1200,7 @@@ static void handle_one_reflog_commit(st
  {
        struct all_refs_cb *cb = cb_data;
        if (!is_null_oid(oid)) {
 -              struct object *o = parse_object(oid->hash);
 +              struct object *o = parse_object(oid);
                if (o) {
                        o->flags |= cb->all_flags;
                        /* ??? CMDLINEFLAGS ??? */
  }
  
  static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
 -              const char *email, unsigned long timestamp, int tz,
 +              const char *email, timestamp_t timestamp, int tz,
                const char *message, void *cb_data)
  {
        handle_one_reflog_commit(ooid, cb_data);
@@@ -1249,7 -1249,7 +1249,7 @@@ static void add_cache_tree(struct cache
        int i;
  
        if (it->entry_count >= 0) {
 -              struct tree *tree = lookup_tree(it->sha1);
 +              struct tree *tree = lookup_tree(&it->oid);
                add_pending_object_with_path(revs, &tree->object, "",
                                             040000, path->buf);
        }
@@@ -1275,7 -1275,7 +1275,7 @@@ void add_index_objects_to_pending(struc
                if (S_ISGITLINK(ce->ce_mode))
                        continue;
  
 -              blob = lookup_blob(ce->oid.hash);
 +              blob = lookup_blob(&ce->oid);
                if (!blob)
                        die("unable to add index blob to traversal");
                add_pending_object_with_path(revs, &blob->object, "",
  static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
                            int exclude_parent)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct object *it;
        struct commit *commit;
        struct commit_list *parents;
                flags ^= UNINTERESTING | BOTTOM;
                arg++;
        }
 -      if (get_sha1_committish(arg, sha1))
 +      if (get_sha1_committish(arg, oid.hash))
                return 0;
        while (1) {
 -              it = get_reference(revs, arg, sha1, 0);
 +              it = get_reference(revs, arg, &oid, 0);
                if (!it && revs->ignore_missing)
                        return 0;
                if (it->type != OBJ_TAG)
                        break;
                if (!((struct tag*)it)->tagged)
                        return 0;
 -              hashcpy(sha1, ((struct tag*)it)->tagged->oid.hash);
 +              oidcpy(&oid, &((struct tag*)it)->tagged->oid);
        }
        if (it->type != OBJ_COMMIT)
                return 0;
@@@ -1389,16 -1389,16 +1389,16 @@@ static void prepare_show_merge(struct r
  {
        struct commit_list *bases;
        struct commit *head, *other;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        const char **prune = NULL;
        int i, prune_num = 1; /* counting terminating NULL */
  
 -      if (get_sha1("HEAD", sha1))
 +      if (get_oid("HEAD", &oid))
                die("--merge without HEAD?");
 -      head = lookup_commit_or_die(sha1, "HEAD");
 -      if (get_sha1("MERGE_HEAD", sha1))
 +      head = lookup_commit_or_die(&oid, "HEAD");
 +      if (get_oid("MERGE_HEAD", &oid))
                die("--merge without MERGE_HEAD?");
 -      other = lookup_commit_or_die(sha1, "MERGE_HEAD");
 +      other = lookup_commit_or_die(&oid, "MERGE_HEAD");
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
        bases = get_merge_bases(head, other);
        revs->limited = 1;
  }
  
 -      a_obj = parse_object(a_oid.hash);
 -      b_obj = parse_object(b_oid.hash);
+ 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 = lookup_commit_reference(a_obj->oid.hash);
 -              b = lookup_commit_reference(b_obj->oid.hash);
++      a_obj = parse_object(&a_oid);
++      b_obj = parse_object(&b_oid);
+       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);
++              b = lookup_commit_reference(&b_obj->oid);
+               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];
 +      struct object_id oid;
        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) {
-               struct object_id from_oid;
-               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_oid.hash) &&
-                   !get_sha1_committish(next, oid.hash)) {
-                       struct object *a_obj, *b_obj;
-                       if (!cant_be_filename) {
-                               *dotdot = '.';
-                               verify_non_filename(revs->prefix, arg);
-                       }
-                       a_obj = parse_object(&from_oid);
-                       b_obj = parse_object(&oid);
-                       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));
-                               b = (b_obj->type == OBJ_COMMIT
-                                    ? (struct commit *)b_obj
-                                    : lookup_commit_reference(&b_obj->oid));
-                               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))
 +      if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc))
                return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
 -      object = get_reference(revs, arg, sha1, flags ^ local_flags);
 +      object = get_reference(revs, arg, &oid, 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;
  }
  
@@@ -2287,12 -2322,12 +2322,12 @@@ int setup_revisions(int argc, const cha
        if (revs->show_merge)
                prepare_show_merge(revs);
        if (revs->def && !revs->pending.nr && !got_rev_arg) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                struct object *object;
                struct object_context oc;
 -              if (get_sha1_with_context(revs->def, 0, sha1, &oc))
 +              if (get_sha1_with_context(revs->def, 0, oid.hash, &oc))
                        diagnose_missing_default(revs->def);
 -              object = get_reference(revs, revs->def, sha1, 0);
 +              object = get_reference(revs, revs->def, &oid, 0);
                add_pending_object_with_mode(revs, object, revs->def, oc.mode);
        }
  
diff --combined sha1_name.c
index 389276e9d34cb6681cf97ed975ff0b46e249f023,5e2ec37b6557f59970ed2b2dd69d4e0b151d1fd0..e9ffe685d58366dc3802986519e3e2fd22c151a4
@@@ -241,7 -241,7 +241,7 @@@ static int disambiguate_committish_only
                return 0;
  
        /* We need to do this the hard way... */
 -      obj = deref_tag(parse_object(oid->hash), NULL, 0);
 +      obj = deref_tag(parse_object(oid), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
@@@ -265,7 -265,7 +265,7 @@@ static int disambiguate_treeish_only(co
                return 0;
  
        /* We need to do this the hard way... */
 -      obj = deref_tag(parse_object(oid->hash), NULL, 0);
 +      obj = deref_tag(parse_object(oid), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
@@@ -354,14 -354,14 +354,14 @@@ static int show_ambiguous_object(const 
  
        type = sha1_object_info(oid->hash, NULL);
        if (type == OBJ_COMMIT) {
 -              struct commit *commit = lookup_commit(oid->hash);
 +              struct commit *commit = lookup_commit(oid);
                if (commit) {
                        struct pretty_print_context pp = {0};
                        pp.date_mode.type = DATE_SHORT;
                        format_commit_message(commit, " %ad - %s", &desc, &pp);
                }
        } else if (type == OBJ_TAG) {
 -              struct tag *tag = lookup_tag(oid->hash);
 +              struct tag *tag = lookup_tag(oid);
                if (!parse_tag(tag) && tag->tag)
                        strbuf_addf(&desc, " %s", tag->tag);
        }
@@@ -660,8 -660,8 +660,8 @@@ static int get_sha1_basic(const char *s
  
        if (reflog_len) {
                int nth, i;
 -              unsigned long at_time;
 -              unsigned long co_time;
 +              timestamp_t at_time;
 +              timestamp_t co_time;
                int co_tz, co_cnt;
  
                /* Is it asking for N-th entry, or approxidate? */
  static int get_parent(const char *name, int len,
                      unsigned char *result, int idx)
  {
 -      unsigned char sha1[20];
 -      int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
 +      struct object_id oid;
 +      int ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
  
        if (ret)
                return ret;
 -      commit = lookup_commit_reference(sha1);
 +      commit = lookup_commit_reference(&oid);
        if (parse_commit(commit))
                return -1;
        if (!idx) {
  static int get_nth_ancestor(const char *name, int len,
                            unsigned char *result, int generation)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct commit *commit;
        int ret;
  
 -      ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
 +      ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
        if (ret)
                return ret;
 -      commit = lookup_commit_reference(sha1);
 +      commit = lookup_commit_reference(&oid);
        if (!commit)
                return -1;
  
@@@ -776,7 -776,7 +776,7 @@@ struct object *peel_to_type(const char 
        if (name && !namelen)
                namelen = strlen(name);
        while (1) {
 -              if (!o || (!o->parsed && !parse_object(o->oid.hash)))
 +              if (!o || (!o->parsed && !parse_object(&o->oid)))
                        return NULL;
                if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
  static int peel_onion(const char *name, int len, unsigned char *sha1,
                      unsigned lookup_flags)
  {
 -      unsigned char outer[20];
 +      struct object_id outer;
        const char *sp;
        unsigned int expected_type = 0;
        struct object *o;
        else if (expected_type == OBJ_TREE)
                lookup_flags |= GET_SHA1_TREEISH;
  
 -      if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
 +      if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags))
                return -1;
  
 -      o = parse_object(outer);
 +      o = parse_object(&outer);
        if (!o)
                return -1;
        if (!expected_type) {
                o = deref_tag(o, name, sp - name - 2);
 -              if (!o || (!o->parsed && !parse_object(o->oid.hash)))
 +              if (!o || (!o->parsed && !parse_object(&o->oid)))
                        return -1;
                hashcpy(sha1, o->oid.hash);
                return 0;
@@@ -981,7 -981,7 +981,7 @@@ static int handle_one_ref(const char *p
                          int flag, void *cb_data)
  {
        struct commit_list **list = cb_data;
 -      struct object *object = parse_object(oid->hash);
 +      struct object *object = parse_object(oid);
        if (!object)
                return 0;
        if (object->type == OBJ_TAG) {
@@@ -1027,7 -1027,7 +1027,7 @@@ static int get_sha1_oneline(const char 
                int matches;
  
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
 -              if (!parse_object(commit->object.oid.hash))
 +              if (!parse_object(&commit->object.oid))
                        continue;
                buf = get_commit_buffer(commit, NULL);
                p = strstr(buf, "\n\n");
@@@ -1054,7 -1054,7 +1054,7 @@@ struct grab_nth_branch_switch_cbdata 
  };
  
  static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
 -                                const char *email, unsigned long timestamp, int tz,
 +                                const char *email, timestamp_t timestamp, int tz,
                                  const char *message, void *cb_data)
  {
        struct grab_nth_branch_switch_cbdata *cb = cb_data;
@@@ -1136,13 -1136,13 +1136,13 @@@ int get_oid_mb(const char *name, struc
        }
        if (st)
                return st;
 -      one = lookup_commit_reference_gently(oid_tmp.hash, 0);
 +      one = lookup_commit_reference_gently(&oid_tmp, 0);
        if (!one)
                return -1;
  
        if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", oid_tmp.hash))
                return -1;
 -      two = lookup_commit_reference_gently(oid_tmp.hash, 0);
 +      two = lookup_commit_reference_gently(&oid_tmp, 0);
        if (!two)
                return -1;
        mbs = get_merge_bases(one, two);
@@@ -1511,6 -1511,7 +1511,7 @@@ static int get_sha1_with_context_1(cons
  
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
+       strbuf_init(&oc->symlink_path, 0);
        ret = get_sha1_1(name, namelen, sha1, flags);
        if (!ret)
                return ret;
                        namelen = strlen(cp);
                }
  
-               strlcpy(oc->path, cp, sizeof(oc->path));
+               if (flags & GET_SHA1_RECORD_PATH)
+                       oc->path = xstrdup(cp);
  
                if (!active_cache)
                        read_cache();
                                }
                        }
                        hashcpy(oc->tree, tree_sha1);
-                       strlcpy(oc->path, filename, sizeof(oc->path));
+                       if (flags & GET_SHA1_RECORD_PATH)
+                               oc->path = xstrdup(filename);
  
                        free(new_filename);
                        return ret;
@@@ -1638,9 -1641,9 +1641,9 @@@ void maybe_die_on_misspelt_object_name(
        get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
  }
  
- int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+ int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc)
  {
        if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
                die("BUG: incompatible flags for get_sha1_with_context");
-       return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+       return get_sha1_with_context_1(str, flags, NULL, sha1, oc);
  }
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;