Merge branch 'lt/date-human'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:24 +0000 (22:05 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:24 +0000 (22:05 -0800)
A new date format "--date=human" that morphs its output depending
on how far the time is from the current time has been introduced.
"--date=auto" can be used to use this new format when the output is
going to the pager or to the terminal and otherwise the default
format.

* lt/date-human:
Add `human` date format tests.
Add `human` format to test-tool
Add 'human' date format documentation
Replace the proposed 'auto' mode with 'auto:'
Add 'human' date format

1  2 
Documentation/rev-list-options.txt
builtin/blame.c
cache.h
date.c
t/helper/test-date.c
t/t0006-date.sh
index 8a4867998e0bf2c76953ca483773971b005a1479,891aef96b4e1477db32074bf22adfa08f586c9d1..cad711ce0ac060d9356e49599ffc13d130fc593c
@@@ -13,6 -13,8 +13,6 @@@ has a line that matches `<pattern>`), u
  Note that these are applied before commit
  ordering and formatting options, such as `--reverse`.
  
 ---
 -
  -<number>::
  -n <number>::
  --max-count=<number>::
@@@ -270,13 -272,13 +270,13 @@@ depending on a few rules
  +
  --
  1. If the starting point is specified as `ref@{Nth}`, show the index
 -format.
 +   format.
  +
  2. If the starting point was specified as `ref@{now}`, show the
 -timestamp format.
 +   timestamp format.
  +
  3. If neither was used, but `--date` was given on the command line, show
 -the timestamp in the format requested by `--date`.
 +   the timestamp in the format requested by `--date`.
  +
  4. Otherwise, show the index format.
  --
@@@ -306,6 -308,8 +306,6 @@@ ifdef::git-rev-list[
        `<header>` text will be printed with each progress update.
  endif::git-rev-list[]
  
 ---
 -
  History Simplification
  ~~~~~~~~~~~~~~~~~~~~~~
  
@@@ -727,16 -731,6 +727,16 @@@ the requested refs
  +
  The form '--filter=sparse:path=<path>' similarly uses a sparse-checkout
  specification contained in <path>.
 ++
 +The form '--filter=tree:<depth>' omits all blobs and trees whose depth
 +from the root tree is >= <depth> (minimum depth if an object is located
 +at multiple depths in the commits traversed). <depth>=0 will not include
 +any trees or blobs unless included explicitly in the command-line (or
 +standard input when --stdin is used). <depth>=1 will include only the
 +tree and blobs which are referenced directly by a commit reachable from
 +<commit> or an explicitly-given object. <depth>=2 is like <depth>=1
 +while also including trees and blobs one more level removed from an
 +explicitly-given commit or tree.
  
  --no-filter::
        Turn off any previous `--filter=` argument.
@@@ -762,6 -756,7 +762,6 @@@ Unexpected missing objects will raise a
  +
  The form '--missing=print' is like 'allow-any', but will also print a
  list of the missing objects.  Object IDs are prefixed with a ``?'' character.
 -endif::git-rev-list[]
  
  --exclude-promisor-objects::
        (For internal use only.)  Prefilter object traversal at
        stronger than `--missing=allow-promisor` because it limits the
        traversal, rather than just silencing errors about missing
        objects.
 +endif::git-rev-list[]
  
  --no-walk[=(sorted|unsorted)]::
        Only show the given commits, but do not traverse their ancestors.
@@@ -836,6 -830,13 +836,13 @@@ Note that the `-local` option does not 
  value (which is always measured in UTC), but does switch the accompanying
  timezone value.
  +
+ `--date=human` shows the timezone if the timezone does not match the
+ current time-zone, and doesn't print the whole date if that matches
+ (ie skip printing year for dates that are "this year", but also skip
+ the whole date itself if it's in the last few days and we can just say
+ what weekday it was).  For older dates the hour and minute is also
+ omitted.
+ +
  `--date=unix` shows the date as a Unix epoch timestamp (seconds since
  1970).  As with `--raw`, this is always in UTC and therefore `-local`
  has no effect.
diff --combined builtin/blame.c
index 0074ed311c8b6c26770f62a668f51abda316ec46,7b6235321c3bdb5496cb351af8504c3d60da9bba..581de0d8322681ef11026b3e36a81c7baf0fda38
@@@ -9,7 -9,6 +9,7 @@@
  #include "config.h"
  #include "color.h"
  #include "builtin.h"
 +#include "repository.h"
  #include "commit.h"
  #include "diff.h"
  #include "revision.h"
@@@ -24,7 -23,6 +24,7 @@@
  #include "line-log.h"
  #include "dir.h"
  #include "progress.h"
 +#include "object-store.h"
  #include "blame.h"
  #include "string-list.h"
  
@@@ -410,7 -408,7 +410,7 @@@ static void parse_color_fields(const ch
        }
  
        if (next == EXPECT_COLOR)
 -              die (_("must end with a color"));
 +              die(_("must end with a color"));
  
        colorfield[colorfield_nr].hop = TIME_MAX;
        string_list_clear(&l, 0);
@@@ -578,7 -576,7 +578,7 @@@ static int read_ancestry(const char *gr
                /* The format is just "Commit Parent1 Parent2 ...\n" */
                struct commit_graft *graft = read_graft_line(&buf);
                if (graft)
 -                      register_commit_graft(graft, 0);
 +                      register_commit_graft(the_repository, graft, 0);
        }
        fclose(fp);
        strbuf_release(&buf);
@@@ -732,8 -730,6 +732,8 @@@ static int blame_copy_callback(const st
  {
        int *opt = option->value;
  
 +      BUG_ON_OPT_NEG(unset);
 +
        /*
         * -C enables copy from removed files;
         * -C -C enables copy from existing files, but only
@@@ -756,8 -752,6 +756,8 @@@ static int blame_move_callback(const st
  {
        int *opt = option->value;
  
 +      BUG_ON_OPT_NEG(unset);
 +
        *opt |= PICKAXE_BLAME_MOVE;
  
        if (arg)
@@@ -834,7 -828,7 +834,7 @@@ int cmd_blame(int argc, const char **ar
  
        setup_default_color_by_age();
        git_config(git_blame_config, &output_option);
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        revs.date_mode = blame_date_mode;
        revs.diffopt.flags.allow_textconv = 1;
        revs.diffopt.flags.follow_renames = 1;
                case PARSE_OPT_HELP:
                case PARSE_OPT_ERROR:
                        exit(129);
 +              case PARSE_OPT_COMPLETE:
 +                      exit(0);
                case PARSE_OPT_DONE:
                        if (ctx.argv[0])
                                dashdash_pos = ctx.cpidx;
@@@ -925,6 -917,10 +925,10 @@@ parse_done
                 */
                blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
                break;
+       case DATE_HUMAN:
+               /* If the year is shown, no time is shown */
+               blame_date_width = sizeof("Thu Oct 19 16:00");
+               break;
        case DATE_NORMAL:
                blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
                break;
        sb.revs = &revs;
        sb.contents_from = contents_from;
        sb.reverse = reverse;
 +      sb.repo = the_repository;
        setup_scoreboard(&sb, path, &o);
        lno = sb.num_lines;
  
                long bottom, top;
                if (parse_range_arg(range_list.items[range_i].string,
                                    nth_line_cb, &sb, lno, anchor,
 -                                  &bottom, &top, sb.path))
 +                                  &bottom, &top, sb.path,
 +                                  the_repository->index))
                        usage(blame_usage);
 -              if (lno < top || ((lno || bottom) && lno < bottom))
 +              if ((!lno && (top || bottom)) || lno < bottom)
                        die(Q_("file %s has only %lu line",
                               "file %s has only %lu lines",
                               lno), path, lno);
                if (bottom < 1)
                        bottom = 1;
 -              if (top < 1)
 +              if (top < 1 || lno < top)
                        top = lno;
                bottom--;
                range_set_append_unsafe(&ranges, bottom, top);
                find_alignment(&sb, &output_option);
                if (!*repeated_meta_color &&
                    (output_option & OUTPUT_COLOR_LINE))
 -                      strcpy(repeated_meta_color, GIT_COLOR_CYAN);
 +                      xsnprintf(repeated_meta_color,
 +                                sizeof(repeated_meta_color),
 +                                "%s", GIT_COLOR_CYAN);
        }
        if (output_option & OUTPUT_ANNOTATE_COMPAT)
                output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR);
diff --combined cache.h
index 400bc0ab25cd5dd12a3b1a71aab0168259df6e19,34e54dee4da903c0943f27634dbf8a12b159fda7..ef9f3c4eaf2956c416b3b698ce965acb9c58ca98
+++ b/cache.h
@@@ -15,7 -15,6 +15,7 @@@
  #include "path.h"
  #include "sha1-array.h"
  #include "repository.h"
 +#include "mem-pool.h"
  
  #include <zlib.h>
  typedef struct git_zstream {
@@@ -45,20 -44,10 +45,20 @@@ unsigned long git_deflate_bound(git_zst
  /* The length in bytes and in hex digits of an object name (SHA-1 value). */
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 +/* The block size of SHA-1. */
 +#define GIT_SHA1_BLKSZ 64
 +
 +/* The length in bytes and in hex digits of an object name (SHA-256 value). */
 +#define GIT_SHA256_RAWSZ 32
 +#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
 +/* The block size of SHA-256. */
 +#define GIT_SHA256_BLKSZ 64
  
  /* The length in byte and in hex digits of the largest possible hash value. */
 -#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
 -#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
 +#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
 +#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
 +/* The largest possible block size for any supported hash. */
 +#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
  
  struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@@ -167,7 -156,6 +167,7 @@@ struct cache_entry 
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
 +      unsigned int mem_pool_allocated;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
        struct object_id oid;
  /* Forward structure decls */
  struct pathspec;
  struct child_process;
 +struct tree;
  
  /*
   * Copy the sha1 and stat state of a cache entry from one to
@@@ -240,7 -227,6 +240,7 @@@ static inline void copy_cache_entry(str
                                    const struct cache_entry *src)
  {
        unsigned int state = dst->ce_flags & CE_HASHED;
 +      int mem_pool_allocated = dst->mem_pool_allocated;
  
        /* Don't copy hash chain and name */
        memcpy(&dst->ce_stat_data, &src->ce_stat_data,
  
        /* Restore the hash state */
        dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
 +
 +      /* Restore the mem_pool_allocated flag */
 +      dst->mem_pool_allocated = mem_pool_allocated;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -345,9 -328,10 +345,9 @@@ struct index_state 
        struct untracked_cache *untracked;
        uint64_t fsmonitor_last_update;
        struct ewah_bitmap *fsmonitor_dirty;
 +      struct mem_pool *ce_mem_pool;
  };
  
 -extern struct index_state the_index;
 -
  /* Name hashing */
  extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
  extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
@@@ -355,74 -339,18 +355,74 @@@ extern void remove_name_hash(struct ind
  extern void free_name_hash(struct index_state *istate);
  
  
 -#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
 +/* Cache entry creation and cleanup */
 +
 +/*
 + * Create cache_entry intended for use in the specified index. Caller
 + * is responsible for discarding the cache_entry with
 + * `discard_cache_entry`.
 + */
 +struct cache_entry *make_cache_entry(struct index_state *istate,
 +                                   unsigned int mode,
 +                                   const struct object_id *oid,
 +                                   const char *path,
 +                                   int stage,
 +                                   unsigned int refresh_options);
 +
 +struct cache_entry *make_empty_cache_entry(struct index_state *istate,
 +                                         size_t name_len);
 +
 +/*
 + * Create a cache_entry that is not intended to be added to an index.
 + * Caller is responsible for discarding the cache_entry
 + * with `discard_cache_entry`.
 + */
 +struct cache_entry *make_transient_cache_entry(unsigned int mode,
 +                                             const struct object_id *oid,
 +                                             const char *path,
 +                                             int stage);
 +
 +struct cache_entry *make_empty_transient_cache_entry(size_t name_len);
 +
 +/*
 + * Discard cache entry.
 + */
 +void discard_cache_entry(struct cache_entry *ce);
 +
 +/*
 + * Check configuration if we should perform extra validation on cache
 + * entries.
 + */
 +int should_validate_cache_entries(void);
 +
 +/*
 + * Duplicate a cache_entry. Allocate memory for the new entry from a
 + * memory_pool. Takes into account cache_entry fields that are meant
 + * for managing the underlying memory allocation of the cache_entry.
 + */
 +struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
 +
 +/*
 + * Validate the cache entries in the index.  This is an internal
 + * consistency check that the cache_entry structs are allocated from
 + * the expected memory pool.
 + */
 +void validate_cache_entries(const struct index_state *istate);
 +
 +#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
 +extern struct index_state the_index;
 +
  #define active_cache (the_index.cache)
  #define active_nr (the_index.cache_nr)
  #define active_alloc (the_index.cache_alloc)
  #define active_cache_changed (the_index.cache_changed)
  #define active_cache_tree (the_index.cache_tree)
  
 -#define read_cache() read_index(&the_index)
 +#define read_cache() repo_read_index(the_repository)
  #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
 -#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 +#define read_cache_preload(pathspec) repo_read_index_preload(the_repository, (pathspec), 0)
  #define is_cache_unborn() is_index_unborn(&the_index)
 -#define read_cache_unmerged() read_index_unmerged(&the_index)
 +#define read_cache_unmerged() repo_read_index_unmerged(the_repository)
  #define discard_cache() discard_index(&the_index)
  #define unmerged_cache() unmerged_index(&the_index)
  #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
  #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
  #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #define read_blob_data_from_cache(path, sz) read_blob_data_from_index(&the_index, (path), (sz))
 +#define hold_locked_index(lock_file, flags) repo_hold_locked_index(the_repository, (lock_file), (flags))
  #endif
  
  #define TYPE_BITS 3
@@@ -497,8 -424,6 +497,8 @@@ static inline enum object_type object_t
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
  #define GITMODULES_FILE ".gitmodules"
 +#define GITMODULES_INDEX ":.gitmodules"
 +#define GITMODULES_HEAD "HEAD:.gitmodules"
  #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
  #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
@@@ -559,7 -484,7 +559,7 @@@ extern const char *get_git_dir(void)
  extern const char *get_git_common_dir(void);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
 -extern char *get_graft_file(void);
 +extern char *get_graft_file(struct repository *r);
  extern void set_git_dir(const char *path);
  extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
@@@ -671,14 -596,14 +671,14 @@@ extern int daemonize(void)
  
  /* Initialize and use the cache information */
  struct lock_file;
 -extern int read_index(struct index_state *);
 -extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 +extern void preload_index(struct index_state *index,
 +                        const struct pathspec *pathspec,
 +                        unsigned int refresh_flags);
  extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path,
                           const char *gitdir);
  extern int is_index_unborn(struct index_state *);
 -extern int read_index_unmerged(struct index_state *);
  
  /* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
@@@ -710,15 -635,12 +710,15 @@@ extern void move_index_extensions(struc
  extern int unmerged_index(const struct index_state *);
  
  /**
 - * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
 - * branch, returns 1 if there are entries in the index, 0 otherwise. If an
 - * strbuf is provided, the space-separated list of files that differ will be
 - * appended to it.
 + * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
 + * compares istate to HEAD.  If tree is NULL and on an unborn branch,
 + * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
 + * provided, the space-separated list of files that differ will be appended
 + * to it.
   */
 -extern int index_has_changes(struct strbuf *sb);
 +extern int repo_index_has_changes(struct repository *repo,
 +                                struct tree *tree,
 +                                struct strbuf *sb);
  
  extern int verify_path(const char *path, unsigned mode);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@@ -751,7 -673,6 +751,7 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  #define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
 +#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  
@@@ -777,6 -698,7 +777,6 @@@ extern int remove_file_from_index(struc
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  
 -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@@ -795,16 -717,14 +795,16 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_REFRESH              0x10
  /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
  #define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int is_racy_timestamp(const struct index_state *istate,
 +                           const struct cache_entry *ce);
  extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  #define HASH_RENORMALIZE  4
 -extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 -extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 +extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 +extern int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
  /*
   * Record to sd the data from st that we use to check whether a file
@@@ -830,10 -750,16 +830,10 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 +#define REFRESH_PROGRESS      0x0040  /* show progress bar if stderr is tty */
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 -extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
 +extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
  
 -/*
 - * Opportunistically update the index but do not complain if we can't.
 - * The lockfile is always committed or rolled back.
 - */
 -extern void update_index_if_able(struct index_state *, struct lock_file *);
 -
 -extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
@@@ -878,13 -804,16 +878,13 @@@ void reset_shared_repository(void)
   * Do replace refs need to be checked this run?  This variable is
   * initialized to true unless --no-replace-object is used or
   * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
 - * commands that do not want replace references to be active.  As an
 - * optimization it is also set to false if replace references have
 - * been sought but there were none.
 + * commands that do not want replace references to be active.
   */
 -extern int check_replace_refs;
 +extern int read_replace_refs;
  extern char *git_replace_ref_base;
  
  extern int fsync_object_files;
  extern int core_preload_index;
 -extern int core_commit_graph;
  extern int core_apply_sparse_checkout;
  extern int precomposed_unicode;
  extern int protect_hfs;
@@@ -911,6 -840,14 +911,6 @@@ int use_optional_locks(void)
  extern char comment_line_char;
  extern int auto_comment_line_char;
  
 -/* Windows only */
 -enum hide_dotfiles_type {
 -      HIDE_DOTFILES_FALSE = 0,
 -      HIDE_DOTFILES_TRUE,
 -      HIDE_DOTFILES_DOTGITONLY
 -};
 -extern enum hide_dotfiles_type hide_dotfiles;
 -
  enum log_refs_config {
        LOG_REFS_UNSET = -1,
        LOG_REFS_NONE = 0,
  };
  extern enum log_refs_config log_all_ref_updates;
  
 -enum branch_track {
 -      BRANCH_TRACK_UNSPECIFIED = -1,
 -      BRANCH_TRACK_NEVER = 0,
 -      BRANCH_TRACK_REMOTE,
 -      BRANCH_TRACK_ALWAYS,
 -      BRANCH_TRACK_EXPLICIT,
 -      BRANCH_TRACK_OVERRIDE
 -};
 -
  enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
@@@ -935,6 -881,7 +935,6 @@@ enum push_default_type 
        PUSH_DEFAULT_UNSPECIFIED
  };
  
 -extern enum branch_track git_branch_track;
  extern enum rebase_setup_type autorebase;
  extern enum push_default_type push_default;
  
@@@ -959,13 -906,11 +959,13 @@@ extern int grafts_replace_parents
  extern int repository_format_precious_objects;
  extern char *repository_format_partial_clone;
  extern const char *core_partial_clone_filter_default;
 +extern int repository_format_worktree_config;
  
  struct repository_format {
        int version;
        int precious_objects;
        char *partial_clone; /* value of extensions.partialclone */
 +      int worktree_config;
        int is_bare;
        int hash_algo;
        char *work_tree;
@@@ -1027,12 -972,6 +1027,12 @@@ extern const struct object_id null_oid
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
 +      /*
 +       * Teach the compiler that there are only two possibilities of hash size
 +       * here, so that it can optimize for this case as much as possible.
 +       */
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
        return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
@@@ -1041,40 -980,24 +1041,40 @@@ static inline int oidcmp(const struct o
        return hashcmp(oid1->hash, oid2->hash);
  }
  
 +static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 +{
 +      /*
 +       * We write this here instead of deferring to hashcmp so that the
 +       * compiler can properly inline it and avoid calling memcmp.
 +       */
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 +}
 +
 +static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
 +{
 +      return hasheq(oid1->hash, oid2->hash);
 +}
 +
  static inline int is_null_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, null_sha1);
 +      return hasheq(sha1, null_sha1);
  }
  
  static inline int is_null_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, null_sha1);
 +      return hasheq(oid->hash, null_sha1);
  }
  
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
  {
 -      memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
 +      memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
  }
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
  {
 -      hashcpy(dst->hash, src->hash);
 +      memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
  }
  
  static inline struct object_id *oiddup(const struct object_id *src)
  
  static inline void hashclr(unsigned char *hash)
  {
 -      memset(hash, 0, GIT_SHA1_RAWSZ);
 +      memset(hash, 0, the_hash_algo->rawsz);
  }
  
  static inline void oidclr(struct object_id *oid)
@@@ -1101,22 -1024,22 +1101,22 @@@ static inline void oidread(struct objec
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
 +      return hasheq(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !oidcmp(oid, the_hash_algo->empty_blob);
 +      return oideq(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
 +      return hasheq(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !oidcmp(oid, the_hash_algo->empty_tree);
 +      return oideq(oid, the_hash_algo->empty_tree);
  }
  
  const char *empty_tree_oid_hex(void);
@@@ -1269,6 -1192,32 +1269,6 @@@ extern char *xdg_config_home(const cha
   */
  extern char *xdg_cache_home(const char *filename);
  
 -extern void *read_object_file_extended(const struct object_id *oid,
 -                                     enum object_type *type,
 -                                     unsigned long *size, int lookup_replace);
 -static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
 -{
 -      return read_object_file_extended(oid, type, size, 1);
 -}
 -
 -/* Read and unpack an object file into memory, write memory to an object file */
 -int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
 -
 -extern int hash_object_file(const void *buf, unsigned long len,
 -                          const char *type, struct object_id *oid);
 -
 -extern int write_object_file(const void *buf, unsigned long len,
 -                           const char *type, struct object_id *oid);
 -
 -extern int hash_object_file_literally(const void *buf, unsigned long len,
 -                                    const char *type, struct object_id *oid,
 -                                    unsigned flags);
 -
 -extern int pretend_object_file(void *, unsigned long, enum object_type,
 -                             struct object_id *oid);
 -
 -extern int force_object_loose(const struct object_id *oid, time_t mtime);
 -
  extern int git_open_cloexec(const char *name, int flags);
  #define git_open(name) git_open_cloexec(name, O_RDONLY)
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
@@@ -1278,6 -1227,43 +1278,6 @@@ extern int check_object_signature(cons
  
  extern int finalize_object_file(const char *tmpfile, const char *filename);
  
 -/*
 - * Open the loose object at path, check its hash, and return the contents,
 - * type, and size. If the object is a blob, then "contents" may return NULL,
 - * to allow streaming of large blobs.
 - *
 - * Returns 0 on success, negative on error (details may be written to stderr).
 - */
 -int read_loose_object(const char *path,
 -                    const struct object_id *expected_oid,
 -                    enum object_type *type,
 -                    unsigned long *size,
 -                    void **contents);
 -
 -/*
 - * Convenience for sha1_object_info_extended() with a NULL struct
 - * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass
 - * nonzero flags to also set other flags.
 - */
 -extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags);
 -static inline int has_sha1_file(const unsigned char *sha1)
 -{
 -      return has_sha1_file_with_flags(sha1, 0);
 -}
 -
 -/* Same as the above, except for struct object_id. */
 -extern int has_object_file(const struct object_id *oid);
 -extern int has_object_file_with_flags(const struct object_id *oid, int flags);
 -
 -/*
 - * Return true iff an alternate object database has a loose object
 - * with the specified name.  This function does not respect replace
 - * references.
 - */
 -extern int has_loose_object_nonlocal(const struct object_id *oid);
 -
 -extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
 -
  /* Helper to check and "touch" a file */
  extern int check_and_freshen_file(const char *fn, int freshen);
  
@@@ -1334,24 -1320,6 +1334,24 @@@ struct object_context 
        GET_OID_TREE | GET_OID_TREEISH | \
        GET_OID_BLOB)
  
 +enum get_oid_result {
 +      FOUND = 0,
 +      MISSING_OBJECT = -1, /* The requested object is missing */
 +      SHORT_NAME_AMBIGUOUS = -2,
 +      /* The following only apply when symlinks are followed */
 +      DANGLING_SYMLINK = -4, /*
 +                              * The initial symlink is there, but
 +                              * (transitively) points to a missing
 +                              * in-tree file
 +                              */
 +      SYMLINK_LOOP = -5,
 +      NOT_DIR = -6, /*
 +                     * Somewhere along the symlink chain, a path is
 +                     * requested which contains a file as a
 +                     * non-final element.
 +                     */
 +};
 +
  extern int get_oid(const char *str, struct object_id *oid);
  extern int get_oid_commit(const char *str, struct object_id *oid);
  extern int get_oid_committish(const char *str, struct object_id *oid);
@@@ -1359,9 -1327,8 +1359,9 @@@ extern int get_oid_tree(const char *str
  extern int get_oid_treeish(const char *str, struct object_id *oid);
  extern int get_oid_blob(const char *str, struct object_id *oid);
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 -extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 -
 +extern enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
 +                              unsigned flags, struct object_id *oid,
 +                              struct object_context *oc);
  
  typedef int each_abbrev_fn(const struct object_id *oid, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@@ -1386,9 -1353,9 +1386,9 @@@ extern int get_oid_hex(const char *hex
  extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  
  /*
 - * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
 + * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
 - * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
 + * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
   * convenience.
   *
   * The non-`_r` variant returns a static buffer, but uses a ring of 4
   *
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
 -extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 -extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 -extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
 +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
 +char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +char *oid_to_hex_r(char *out, const struct object_id *oid);
 +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);     /* static buffer result! */
 +char *sha1_to_hex(const unsigned char *sha1);                                         /* same static buffer */
 +char *hash_to_hex(const unsigned char *hash);                                         /* same static buffer */
 +char *oid_to_hex(const struct object_id *oid);                                                /* same static buffer */
  
  /*
   * Parse a 40-character hexadecimal object ID starting from hex, updating the
@@@ -1461,20 -1425,19 +1461,21 @@@ extern void *read_object_with_reference
  extern struct object *peel_to_type(const char *name, int namelen,
                                   struct object *o, enum object_type);
  
 +enum date_mode_type {
 +      DATE_NORMAL = 0,
++      DATE_HUMAN,
 +      DATE_RELATIVE,
 +      DATE_SHORT,
 +      DATE_ISO8601,
 +      DATE_ISO8601_STRICT,
 +      DATE_RFC2822,
 +      DATE_STRFTIME,
 +      DATE_RAW,
 +      DATE_UNIX
 +};
 +
  struct date_mode {
 -      enum date_mode_type {
 -              DATE_NORMAL = 0,
 -              DATE_HUMAN,
 -              DATE_RELATIVE,
 -              DATE_SHORT,
 -              DATE_ISO8601,
 -              DATE_ISO8601_STRICT,
 -              DATE_RFC2822,
 -              DATE_STRFTIME,
 -              DATE_RAW,
 -              DATE_UNIX
 -      } type;
 +      enum date_mode_type type;
        const char *strftime_fmt;
        int local;
  };
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
  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,
 +void show_date_relative(timestamp_t time, const struct timeval *now,
                        struct strbuf *timebuf);
+ void show_date_human(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, timestamp_t *timestamp, int *offset);
  int parse_expiry_date(const char *date, timestamp_t *timestamp);
@@@ -1510,7 -1475,6 +1513,7 @@@ extern const char *fmt_name(const char 
  extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
 +extern const char *git_sequence_editor(void);
  extern const char *git_pager(int stdout_is_tty);
  extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
@@@ -1557,15 -1521,14 +1560,15 @@@ struct checkout 
        unsigned force:1,
                 quiet:1,
                 not_new:1,
 +               clone:1,
                 refresh_cache:1;
  };
  #define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
 -extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 +extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
  extern void enable_delayed_checkout(struct checkout *state);
 -extern int finish_delayed_checkout(struct checkout *state);
 +extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
  
  struct cache_def {
        struct strbuf path;
@@@ -1615,6 -1578,116 +1618,6 @@@ extern int odb_mkstemp(struct strbuf *t
   */
  extern int odb_pack_keep(const char *name);
  
 -/*
 - * Iterate over the files in the loose-object parts of the object
 - * directory "path", triggering the following callbacks:
 - *
 - *  - loose_object is called for each loose object we find.
 - *
 - *  - loose_cruft is called for any files that do not appear to be
 - *    loose objects. Note that we only look in the loose object
 - *    directories "objects/[0-9a-f]{2}/", so we will not report
 - *    "objects/foobar" as cruft.
 - *
 - *  - loose_subdir is called for each top-level hashed subdirectory
 - *    of the object directory (e.g., "$OBJDIR/f0"). It is called
 - *    after the objects in the directory are processed.
 - *
 - * Any callback that is NULL will be ignored. Callbacks returning non-zero
 - * will end the iteration.
 - *
 - * In the "buf" variant, "path" is a strbuf which will also be used as a
 - * scratch buffer, but restored to its original contents before
 - * the function returns.
 - */
 -typedef int each_loose_object_fn(const struct object_id *oid,
 -                               const char *path,
 -                               void *data);
 -typedef int each_loose_cruft_fn(const char *basename,
 -                              const char *path,
 -                              void *data);
 -typedef int each_loose_subdir_fn(unsigned int nr,
 -                               const char *path,
 -                               void *data);
 -int for_each_file_in_obj_subdir(unsigned int subdir_nr,
 -                              struct strbuf *path,
 -                              each_loose_object_fn obj_cb,
 -                              each_loose_cruft_fn cruft_cb,
 -                              each_loose_subdir_fn subdir_cb,
 -                              void *data);
 -int for_each_loose_file_in_objdir(const char *path,
 -                                each_loose_object_fn obj_cb,
 -                                each_loose_cruft_fn cruft_cb,
 -                                each_loose_subdir_fn subdir_cb,
 -                                void *data);
 -int for_each_loose_file_in_objdir_buf(struct strbuf *path,
 -                                    each_loose_object_fn obj_cb,
 -                                    each_loose_cruft_fn cruft_cb,
 -                                    each_loose_subdir_fn subdir_cb,
 -                                    void *data);
 -
 -/*
 - * Iterate over loose objects in both the local
 - * repository and any alternates repositories (unless the
 - * LOCAL_ONLY flag is set).
 - */
 -#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 -
 -struct object_info {
 -      /* Request */
 -      enum object_type *typep;
 -      unsigned long *sizep;
 -      off_t *disk_sizep;
 -      unsigned char *delta_base_sha1;
 -      struct strbuf *type_name;
 -      void **contentp;
 -
 -      /* Response */
 -      enum {
 -              OI_CACHED,
 -              OI_LOOSE,
 -              OI_PACKED,
 -              OI_DBCACHED
 -      } whence;
 -      union {
 -              /*
 -               * struct {
 -               *      ... Nothing to expose in this case
 -               * } cached;
 -               * struct {
 -               *      ... Nothing to expose in this case
 -               * } loose;
 -               */
 -              struct {
 -                      struct packed_git *pack;
 -                      off_t offset;
 -                      unsigned int is_delta;
 -              } packed;
 -      } u;
 -};
 -
 -/*
 - * Initializer for a "struct object_info" that wants no items. You may
 - * also memset() the memory to all-zeroes.
 - */
 -#define OBJECT_INFO_INIT {NULL}
 -
 -/* Invoke lookup_replace_object() on the given hash */
 -#define OBJECT_INFO_LOOKUP_REPLACE 1
 -/* Allow reading from a loose object file of unknown/bogus type */
 -#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
 -/* Do not check cached storage */
 -#define OBJECT_INFO_SKIP_CACHED 4
 -/* Do not retry packed storage after checking packed and loose storage */
 -#define OBJECT_INFO_QUICK 8
 -/* Do not check loose object */
 -#define OBJECT_INFO_IGNORE_LOOSE 16
 -
 -int oid_object_info_extended(struct repository *r,
 -                           const struct object_id *,
 -                           struct object_info *, unsigned flags);
 -
  /*
   * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
   * blobs. This has a difference only if extensions.partialClone is set.
@@@ -1734,7 -1807,7 +1737,7 @@@ void shift_tree_by(const struct object_
  /* All WS_* -- when extended, adapt diff.c emit_symbol */
  #define WS_RULE_MASK           07777
  extern unsigned whitespace_rule_cfg;
 -extern unsigned whitespace_rule(const char *);
 +extern unsigned whitespace_rule(struct index_state *, const char *);
  extern unsigned parse_whitespace_rule(const char *);
  extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
  extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
@@@ -1756,12 -1829,10 +1759,12 @@@ extern struct startup_info *startup_inf
  
  /* merge.c */
  struct commit_list;
 -int try_merge_command(const char *strategy, size_t xopts_nr,
 +int try_merge_command(struct repository *r,
 +              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 struct object_id *from,
 +int checkout_fast_forward(struct repository *r,
 +                        const struct object_id *from,
                          const struct object_id *to,
                          int overwrite_ignore);
  
@@@ -1812,7 -1883,4 +1815,7 @@@ void safe_create_dir(const char *dir, i
   */
  extern int print_sha1_ellipsis(void);
  
 +/* Return 1 if the file is empty or does not exists, 0 otherwise. */
 +extern int is_empty_or_missing_file(const char *filename);
 +
  #endif /* CACHE_H */
diff --combined date.c
index 61449f8b2e51507f148617120d455eefda19a406,7b9e8fa2ba5afe5bcdd51be3dfc3c56d5bf8dedf..9c5870e102951ed25f5def2103dab15bf8ed5c99
--- 1/date.c
--- 2/date.c
+++ b/date.c
@@@ -77,22 -77,16 +77,16 @@@ static struct tm *time_to_tm_local(time
  }
  
  /*
-  * What value of "tz" was in effect back then at "time" in the
-  * local timezone?
+  * Fill in the localtime 'struct tm' for the supplied time,
+  * and return the local tz.
   */
- static int local_tzoffset(timestamp_t time)
+ static int local_time_tzoffset(time_t t, struct tm *tm)
  {
-       time_t t, t_local;
-       struct tm tm;
+       time_t t_local;
        int offset, eastwest;
  
-       if (date_overflows(time))
-               die("Timestamp too large for this system: %"PRItime, time);
-       t = (time_t)time;
-       localtime_r(&t, &tm);
-       t_local = tm_to_time_t(&tm);
+       localtime_r(&t, tm);
+       t_local = tm_to_time_t(tm);
        if (t_local == -1)
                return 0; /* error; just use +0000 */
        if (t_local < t) {
        return offset * eastwest;
  }
  
 -void show_date_relative(timestamp_t time, int tz,
 -                             const struct timeval *now,
 -                             struct strbuf *timebuf)
+ /*
+  * What value of "tz" was in effect back then at "time" in the
+  * local timezone?
+  */
+ static int local_tzoffset(timestamp_t time)
+ {
+       struct tm tm;
+       if (date_overflows(time))
+               die("Timestamp too large for this system: %"PRItime, time);
+       return local_time_tzoffset((time_t)time, &tm);
+ }
+ static void get_time(struct timeval *now)
+ {
+       const char *x;
+       x = getenv("GIT_TEST_DATE_NOW");
+       if (x) {
+               now->tv_sec = atoi(x);
+               now->tv_usec = 0;
+       }
+       else
+               gettimeofday(now, NULL);
+ }
 +void show_date_relative(timestamp_t time,
 +                      const struct timeval *now,
 +                      struct strbuf *timebuf)
  {
        timestamp_t diff;
        if (now->tv_sec < time) {
@@@ -191,9 -212,80 +212,80 @@@ struct date_mode *date_mode_from_type(e
        return &mode;
  }
  
 -              show_date_relative(time, tz, &now, buf);
+ static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm, int tz, struct tm *human_tm, int human_tz, int local)
+ {
+       struct {
+               unsigned int    year:1,
+                               date:1,
+                               wday:1,
+                               time:1,
+                               seconds:1,
+                               tz:1;
+       } hide = { 0 };
+       hide.tz = local || tz == human_tz;
+       hide.year = tm->tm_year == human_tm->tm_year;
+       if (hide.year) {
+               if (tm->tm_mon == human_tm->tm_mon) {
+                       if (tm->tm_mday > human_tm->tm_mday) {
+                               /* Future date: think timezones */
+                       } else if (tm->tm_mday == human_tm->tm_mday) {
+                               hide.date = hide.wday = 1;
+                       } else if (tm->tm_mday + 5 > human_tm->tm_mday) {
+                               /* Leave just weekday if it was a few days ago */
+                               hide.date = 1;
+                       }
+               }
+       }
+       /* Show "today" times as just relative times */
+       if (hide.wday) {
+               struct timeval now;
+               get_time(&now);
++              show_date_relative(time, &now, buf);
+               return;
+       }
+       /*
+        * Always hide seconds for human-readable.
+        * Hide timezone if showing date.
+        * Hide weekday and time if showing year.
+        *
+        * The logic here is two-fold:
+        *  (a) only show details when recent enough to matter
+        *  (b) keep the maximum length "similar", and in check
+        */
+       if (human_tm->tm_year) {
+               hide.seconds = 1;
+               hide.tz |= !hide.date;
+               hide.wday = hide.time = !hide.year;
+       }
+       if (!hide.wday)
+               strbuf_addf(buf, "%.3s ", weekday_names[tm->tm_wday]);
+       if (!hide.date)
+               strbuf_addf(buf, "%.3s %d ", month_names[tm->tm_mon], tm->tm_mday);
+       /* Do we want AM/PM depending on locale? */
+       if (!hide.time) {
+               strbuf_addf(buf, "%02d:%02d", tm->tm_hour, tm->tm_min);
+               if (!hide.seconds)
+                       strbuf_addf(buf, ":%02d", tm->tm_sec);
+       } else
+               strbuf_rtrim(buf);
+       if (!hide.year)
+               strbuf_addf(buf, " %d", tm->tm_year + 1900);
+       if (!hide.tz)
+               strbuf_addf(buf, " %+05d", tz);
+ }
  const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
  {
        struct tm *tm;
+       struct tm human_tm = { 0 };
+       int human_tz = -1;
        static struct strbuf timebuf = STRBUF_INIT;
  
        if (mode->type == DATE_UNIX) {
                return timebuf.buf;
        }
  
+       if (mode->type == DATE_HUMAN) {
+               struct timeval now;
+               get_time(&now);
+               /* Fill in the data for "current time" in human_tz and human_tm */
+               human_tz = local_time_tzoffset(now.tv_sec, &human_tm);
+       }
        if (mode->local)
                tz = local_tzoffset(time);
  
                struct timeval now;
  
                strbuf_reset(&timebuf);
-               gettimeofday(&now, NULL);
+               get_time(&now);
 -              show_date_relative(time, tz, &now, &timebuf);
 +              show_date_relative(time, &now, &timebuf);
                return timebuf.buf;
        }
  
                strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz,
                                !mode->local);
        else
-               strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
-                               weekday_names[tm->tm_wday],
-                               month_names[tm->tm_mon],
-                               tm->tm_mday,
-                               tm->tm_hour, tm->tm_min, tm->tm_sec,
-                               tm->tm_year + 1900,
-                               mode->local ? 0 : ' ',
-                               tz);
+               show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode->local);
        return timebuf.buf;
  }
  
@@@ -819,6 -913,8 +913,8 @@@ static enum date_mode_type parse_date_t
                return DATE_SHORT;
        if (skip_prefix(format, "default", end))
                return DATE_NORMAL;
+       if (skip_prefix(format, "human", end))
+               return DATE_HUMAN;
        if (skip_prefix(format, "raw", end))
                return DATE_RAW;
        if (skip_prefix(format, "unix", end))
@@@ -833,6 -929,14 +929,14 @@@ void parse_date_format(const char *form
  {
        const char *p;
  
+       /* "auto:foo" is "if tty/pager, then foo, otherwise normal" */
+       if (skip_prefix(format, "auto:", &p)) {
+               if (isatty(1) || pager_in_use())
+                       format = p;
+               else
+                       format = "default";
+       }
        /* historical alias */
        if (!strcmp(format, "local"))
                format = "default-local";
@@@ -887,49 -991,20 +991,49 @@@ static time_t update_tm(struct tm *tm, 
        return n;
  }
  
 +/*
 + * Do we have a pending number at the end, or when
 + * we see a new one? Let's assume it's a month day,
 + * as in "Dec 6, 1992"
 + */
 +static void pending_number(struct tm *tm, int *num)
 +{
 +      int number = *num;
 +
 +      if (number) {
 +              *num = 0;
 +              if (tm->tm_mday < 0 && number < 32)
 +                      tm->tm_mday = number;
 +              else if (tm->tm_mon < 0 && number < 13)
 +                      tm->tm_mon = number-1;
 +              else if (tm->tm_year < 0) {
 +                      if (number > 1969 && number < 2100)
 +                              tm->tm_year = number - 1900;
 +                      else if (number > 69 && number < 100)
 +                              tm->tm_year = number;
 +                      else if (number < 38)
 +                              tm->tm_year = 100 + number;
 +                      /* We screw up for number = 00 ? */
 +              }
 +      }
 +}
 +
  static void date_now(struct tm *tm, struct tm *now, int *num)
  {
 +      *num = 0;
        update_tm(tm, now, 0);
  }
  
  static void date_yesterday(struct tm *tm, struct tm *now, int *num)
  {
 +      *num = 0;
        update_tm(tm, now, 24*60*60);
  }
  
  static void date_time(struct tm *tm, struct tm *now, int hour)
  {
        if (tm->tm_hour < hour)
 -              date_yesterday(tm, now, NULL);
 +              update_tm(tm, now, 24*60*60);
        tm->tm_hour = hour;
        tm->tm_min = 0;
        tm->tm_sec = 0;
  
  static void date_midnight(struct tm *tm, struct tm *now, int *num)
  {
 +      pending_number(tm, num);
        date_time(tm, now, 0);
  }
  
  static void date_noon(struct tm *tm, struct tm *now, int *num)
  {
 +      pending_number(tm, num);
        date_time(tm, now, 12);
  }
  
  static void date_tea(struct tm *tm, struct tm *now, int *num)
  {
 +      pending_number(tm, num);
        date_time(tm, now, 17);
  }
  
@@@ -985,7 -1057,6 +1089,7 @@@ static void date_never(struct tm *tm, s
  {
        time_t n = 0;
        localtime_r(&n, tm);
 +      *num = 0;
  }
  
  static const struct special {
@@@ -1143,6 -1214,33 +1247,6 @@@ static const char *approxidate_digit(co
        return end;
  }
  
 -/*
 - * Do we have a pending number at the end, or when
 - * we see a new one? Let's assume it's a month day,
 - * as in "Dec 6, 1992"
 - */
 -static void pending_number(struct tm *tm, int *num)
 -{
 -      int number = *num;
 -
 -      if (number) {
 -              *num = 0;
 -              if (tm->tm_mday < 0 && number < 32)
 -                      tm->tm_mday = number;
 -              else if (tm->tm_mon < 0 && number < 13)
 -                      tm->tm_mon = number-1;
 -              else if (tm->tm_year < 0) {
 -                      if (number > 1969 && number < 2100)
 -                              tm->tm_year = number - 1900;
 -                      else if (number > 69 && number < 100)
 -                              tm->tm_year = number;
 -                      else if (number < 38)
 -                              tm->tm_year = 100 + number;
 -                      /* We screw up for number = 00 ? */
 -              }
 -      }
 -}
 -
  static timestamp_t approxidate_str(const char *date,
                                   const struct timeval *tv,
                                   int *error_ret)
@@@ -1205,7 -1303,7 +1309,7 @@@ timestamp_t approxidate_careful(const c
                return timestamp;
        }
  
-       gettimeofday(&tv, NULL);
+       get_time(&tv);
        return approxidate_str(date, &tv, error_ret);
  }
  
diff --combined t/helper/test-date.c
index aac4d542c2936bf90f5ef9c0f27ff347aaf8b660,9b2ac772eeac5397289c8a26e2d447a6aa4e8679..a47bfa3003ba72d9ccac56981bf2ed89302af802
@@@ -3,6 -3,7 +3,7 @@@
  
  static const char *usage_msg = "\n"
  "  test-tool date relative [time_t]...\n"
+ "  test-tool date human [time_t]...\n"
  "  test-tool date show:<format> [time_t]...\n"
  "  test-tool date parse [date]...\n"
  "  test-tool date approxidate [date]...\n"
@@@ -16,12 -17,20 +17,20 @@@ static void show_relative_dates(const c
  
        for (; *argv; argv++) {
                time_t t = atoi(*argv);
 -              show_date_relative(t, 0, now, &buf);
 +              show_date_relative(t, now, &buf);
                printf("%s -> %s\n", *argv, buf.buf);
        }
        strbuf_release(&buf);
  }
  
+ static void show_human_dates(const char **argv)
+ {
+       for (; *argv; argv++) {
+               time_t t = atoi(*argv);
+               printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(HUMAN)));
+       }
+ }
  static void show_dates(const char **argv, const char *format)
  {
        struct date_mode mode;
@@@ -87,7 -96,7 +96,7 @@@ int cmd__date(int argc, const char **ar
        struct timeval now;
        const char *x;
  
-       x = getenv("TEST_DATE_NOW");
+       x = getenv("GIT_TEST_DATE_NOW");
        if (x) {
                now.tv_sec = atoi(x);
                now.tv_usec = 0;
                usage(usage_msg);
        if (!strcmp(*argv, "relative"))
                show_relative_dates(argv+1, &now);
+       else if (!strcmp(*argv, "human"))
+               show_human_dates(argv+1);
        else if (skip_prefix(*argv, "show:", &x))
                show_dates(argv+1, x);
        else if (!strcmp(*argv, "parse"))
diff --combined t/t0006-date.sh
index ffb2975e4875287bd510503a3becfd90fd7d325b,1376491b5be6ea698061a94aa0a73a9a895f010d..d9fcc829a9e6cb10088e6503841928cf85d1d697
@@@ -4,10 -4,10 +4,10 @@@ test_description='test date parsing an
  . ./test-lib.sh
  
  # arbitrary reference time: 2009-08-30 19:20:00
TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
  
  check_relative() {
-       t=$(($TEST_DATE_NOW - $1))
+       t=$(($GIT_TEST_DATE_NOW - $1))
        echo "$t -> $2" >expect
        test_expect_${3:-success} "relative date ($2)" "
        test-tool date relative $t >actual &&
@@@ -113,8 -113,6 +113,8 @@@ check_approxidate '3:00' '2009-08-30 03
  check_approxidate '15:00' '2009-08-30 15:00:00'
  check_approxidate 'noon today' '2009-08-30 12:00:00'
  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
 +check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
 +check_approxidate '10am noon' '2009-08-29 12:00:00'
  
  check_approxidate 'last tuesday' '2009-08-25 19:20:00'
  check_approxidate 'July 5th' '2009-07-05 19:20:00'
@@@ -128,4 -126,22 +128,22 @@@ check_approxidate '6AM, June 7, 2009' '
  check_approxidate '2008-12-01' '2008-12-01 19:20:00'
  check_approxidate '2009-12-01' '2009-12-01 19:20:00'
  
+ check_date_format_human() {
+       t=$(($GIT_TEST_DATE_NOW - $1))
+       echo "$t -> $2" >expect
+       test_expect_success "human date $t" '
+               test-tool date human $t >actual &&
+               test_i18ncmp expect actual
+ '
+ }
+ check_date_format_human 18000 "5 hours ago" # 5 hours ago
+ check_date_format_human 432000 "Tue Aug 25 19:20" # 5 days ago
+ check_date_format_human 1728000 "Mon Aug 10 19:20" # 3 weeks ago
+ check_date_format_human 13000000 "Thu Apr 2 08:13" # 5 months ago
+ check_date_format_human 31449600 "Aug 31 2008" # 12 months ago
+ check_date_format_human 37500000 "Jun 22 2008" # 1 year, 2 months ago
+ check_date_format_human 55188000 "Dec 1 2007" # 1 year, 9 months ago
+ check_date_format_human 630000000 "Sep 13 1989" # 20 years ago
  test_done