From: Junio C Hamano Date: Thu, 7 Feb 2019 06:05:24 +0000 (-0800) Subject: Merge branch 'lt/date-human' X-Git-Tag: v2.21.0-rc0~20 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/ecbe1beb8e41664ac00581234a449c4487600e1d?ds=inline;hp=-c Merge branch 'lt/date-human' 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 --- ecbe1beb8e41664ac00581234a449c4487600e1d diff --combined Documentation/rev-list-options.txt index 8a4867998e,891aef96b4..cad711ce0a --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@@ -13,6 -13,8 +13,6 @@@ has a line that matches ``), u Note that these are applied before commit ordering and formatting options, such as `--reverse`. --- - -:: -n :: --max-count=:: @@@ -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[ `
` 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=' similarly uses a sparse-checkout specification contained in . ++ +The form '--filter=tree:' omits all blobs and trees whose depth +from the root tree is >= (minimum depth if an object is located +at multiple depths in the commits traversed). =0 will not include +any trees or blobs unless included explicitly in the command-line (or +standard input when --stdin is used). =1 will include only the +tree and blobs which are referenced directly by a commit reachable from + or an explicitly-given object. =2 is like =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 @@@ -769,7 -764,6 +769,7 @@@ 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 0074ed311c,7b6235321c..581de0d832 --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -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; @@@ -850,8 -844,6 +850,8 @@@ 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; @@@ -994,7 -990,6 +998,7 @@@ sb.revs = &revs; sb.contents_from = contents_from; sb.reverse = reverse; + sb.repo = the_repository; setup_scoreboard(&sb, path, &o); lno = sb.num_lines; @@@ -1007,16 -1002,15 +1011,16 @@@ 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); @@@ -1079,9 -1073,7 +1083,9 @@@ 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 400bc0ab25,34e54dee4d..ef9f3c4eaf --- a/cache.h +++ b/cache.h @@@ -15,7 -15,6 +15,7 @@@ #include "path.h" #include "sha1-array.h" #include "repository.h" +#include "mem-pool.h" #include 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; @@@ -230,7 -218,6 +230,7 @@@ /* 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, @@@ -249,9 -235,6 +249,9 @@@ /* 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)) @@@ -443,7 -371,6 +443,7 @@@ #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, @@@ -919,6 -856,15 +919,6 @@@ }; 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) @@@ -1086,7 -1009,7 +1086,7 @@@ 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 @@@ -1396,13 -1363,10 +1396,13 @@@ * * 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; }; @@@ -1488,8 -1451,10 +1489,10 @@@ 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 61449f8b2e,7b9e8fa2ba..9c5870e102 --- a/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) { @@@ -107,9 -101,36 +101,36 @@@ return offset * eastwest; } + /* + * 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, int tz, - const struct timeval *now, - struct strbuf *timebuf) +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; } + 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, tz, &now, buf); ++ 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) { @@@ -202,6 -294,15 +294,15 @@@ 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); @@@ -215,8 -316,8 +316,8 @@@ 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; } @@@ -258,14 -359,7 +359,7 @@@ 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; @@@ -937,19 -1012,16 +1041,19 @@@ 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 aac4d542c2,9b2ac772ee..a47bfa3003 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@@ -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: [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; @@@ -100,6 -109,8 +109,8 @@@ 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 ffb2975e48,1376491b5b..d9fcc829a9 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@@ -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