Merge branch 'jc/prune-all'
authorJunio C Hamano <gitster@pobox.com>
Wed, 29 May 2013 21:23:03 +0000 (14:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 May 2013 21:23:04 +0000 (14:23 -0700)
We used the approxidate() parser for "--expire=<timestamp>" options
of various commands, but it is better to treat --expire=all and
--expire=now a bit more specially than using the current timestamp.
Update "git gc" and "git reflog" with a new parsing function for
expiry dates.

* jc/prune-all:
prune: introduce OPT_EXPIRY_DATE() and use it
api-parse-options.txt: document "no-" for non-boolean options
git-gc.txt, git-reflog.txt: document new expiry options
date.c: add parse_expiry_date()

1  2 
Documentation/git-reflog.txt
Documentation/technical/api-parse-options.txt
builtin/reflog.c
cache.h
date.c
parse-options.h
index fb8697ea4c390154b63dd88612ddf1effcb57ffe,141e8a5e4338499d8a6ea48a04a31a005591669b..70791b9fd88b4c64260523a5fcadcb46ce504f8f
@@@ -38,7 -38,7 +38,7 @@@ The reflog will cover all recent action
  as well).  It is an alias for `git log -g --abbrev-commit --pretty=oneline`;
  see linkgit:git-log[1].
  
 -The reflog is useful in various git commands, to specify the old value
 +The reflog is useful in various Git commands, to specify the old value
  of a reference. For example, `HEAD@{2}` means "where HEAD used to be
  two moves ago", `master@{one.week.ago}` means "where master used to
  point to one week ago", and so on. See linkgit:gitrevisions[7] for
@@@ -67,14 -67,19 +67,19 @@@ them
  --expire=<time>::
        Entries older than this time are pruned.  Without the
        option it is taken from configuration `gc.reflogExpire`,
-       which in turn defaults to 90 days.
+       which in turn defaults to 90 days.  --expire=all prunes
+       entries regardless of their age; --expire=never turns off
+       pruning of reachable entries (but see --expire-unreachable).
  
  --expire-unreachable=<time>::
        Entries older than this time and not reachable from
        the current tip of the branch are pruned.  Without the
        option it is taken from configuration
        `gc.reflogExpireUnreachable`, which in turn defaults to
-       30 days.
+       30 days.  --expire-unreachable=all prunes unreachable
+       entries regardless of their age; --expire-unreachable=never
+       turns off early pruning of unreachable entries (but see
+       --expire).
  
  --all::
        Instead of listing <refs> explicitly, prune all refs.
index 32ddc1cf13741bea4b809e98508df3b5e71fc828,a8bae69e6e40b96fca90f2c4d4e651c2ddcd260f..1317db4d6ca798c0ffa5150a7373c37b5599ecd9
@@@ -1,7 -1,7 +1,7 @@@
  parse-options API
  =================
  
 -The parse-options API is used to parse and massage options in git
 +The parse-options API is used to parse and massage options in Git
  and to provide a usage help with consistent look.
  
  Basics
@@@ -41,6 -41,8 +41,8 @@@ The parse-options API allows
  * Boolean long options can be 'negated' (or 'unset') by prepending
    `no-`, e.g. `--no-abbrev` instead of `--abbrev`. Conversely,
    options that begin with `no-` can be 'negated' by removing it.
+   Other long options can be unset (e.g., set string to NULL, set
+   integer to 0) by prepending `no-`.
  
  * Options and non-option arguments can clearly be separated using the `--`
    option, e.g. `-a -b --option -- --this-is-a-file` indicates that
@@@ -174,6 -176,10 +176,10 @@@ There are some macros to easily define 
        Introduce an option with date argument, see `approxidate()`.
        The timestamp is put into `int_var`.
  
+ `OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+       Introduce an option with expiry date argument, see `parse_expiry_date()`.
+       The timestamp is put into `int_var`.
  `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
        Introduce an option with argument.
        The argument will be fed into the function given by `func_ptr`
diff --combined builtin/reflog.c
index 72a0af70c3dcd7d108887cd289bc7b3c606226aa,44700f92432adf9545d160e698fd57aa3cc07d29..54184b3d133bb66db272fd21ad3cad53a4a9a5cb
@@@ -414,7 -414,7 +414,7 @@@ static int expire_reflog(const char *re
                if (cb.unreachable_expire_kind == UE_HEAD) {
                        struct commit_list *elem;
                        for (elem = tips; elem; elem = elem->next)
 -                              clear_commit_marks(tip_commit, REACHABLE);
 +                              clear_commit_marks(elem->item, REACHABLE);
                        free_commit_list(tips);
                } else {
                        clear_commit_marks(tip_commit, REACHABLE);
@@@ -496,11 -496,9 +496,9 @@@ static int parse_expire_cfg_value(cons
  {
        if (!value)
                return config_error_nonbool(var);
-       if (!strcmp(value, "never") || !strcmp(value, "false")) {
-               *expire = 0;
-               return 0;
-       }
-       *expire = approxidate(value);
+       if (parse_expiry_date(value, expire))
+               return error(_("%s' for '%s' is not a valid timestamp"),
+                            value, var);
        return 0;
  }
  
  
  static int reflog_expire_config(const char *var, const char *value, void *cb)
  {
 -      const char *lastdot = strrchr(var, '.');
 +      const char *pattern, *key;
 +      int pattern_len;
        unsigned long expire;
        int slot;
        struct reflog_expire_cfg *ent;
  
 -      if (!lastdot || prefixcmp(var, "gc."))
 +      if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
                return git_default_config(var, value, cb);
  
 -      if (!strcmp(lastdot, ".reflogexpire")) {
 +      if (!strcmp(key, "reflogexpire")) {
                slot = EXPIRE_TOTAL;
                if (parse_expire_cfg_value(var, value, &expire))
                        return -1;
 -      } else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
 +      } else if (!strcmp(key, "reflogexpireunreachable")) {
                slot = EXPIRE_UNREACH;
                if (parse_expire_cfg_value(var, value, &expire))
                        return -1;
        } else
                return git_default_config(var, value, cb);
  
 -      if (lastdot == var + 2) {
 +      if (!pattern) {
                switch (slot) {
                case EXPIRE_TOTAL:
                        default_reflog_expire = expire;
                return 0;
        }
  
 -      ent = find_cfg_ent(var + 3, lastdot - (var+3));
 +      ent = find_cfg_ent(pattern, pattern_len);
        if (!ent)
                return -1;
        switch (slot) {
@@@ -614,11 -611,13 +612,13 @@@ static int cmd_reflog_expire(int argc, 
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
                        cb.dry_run = 1;
                else if (!prefixcmp(arg, "--expire=")) {
-                       cb.expire_total = approxidate(arg + 9);
+                       if (parse_expiry_date(arg + 9, &cb.expire_total))
+                               die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_TOTAL;
                }
                else if (!prefixcmp(arg, "--expire-unreachable=")) {
-                       cb.expire_unreachable = approxidate(arg + 21);
+                       if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
+                               die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_UNREACH;
                }
                else if (!strcmp(arg, "--stale-fix"))
diff --combined cache.h
index 94ca1acf704bd2dfdc561b6b2d3d64740b975f61,f43f6d94e37803322ea65ef9d7713db18b6f4324..7ce90611373ba9f0b3d7ab12eef53639458d241e
+++ b/cache.h
@@@ -34,7 -34,6 +34,7 @@@ int git_inflate(git_zstream *, int flus
  
  void git_deflate_init(git_zstream *, int level);
  void git_deflate_init_gzip(git_zstream *, int level);
 +void git_deflate_init_raw(git_zstream *, int level);
  void git_deflate_end(git_zstream *);
  int git_deflate_abort(git_zstream *);
  int git_deflate_end_gently(git_zstream *);
@@@ -162,9 -161,6 +162,9 @@@ struct cache_entry 
  #define CE_UNPACKED          (1 << 24)
  #define CE_NEW_SKIP_WORKTREE (1 << 25)
  
 +/* used to temporarily mark paths matched by pathspecs */
 +#define CE_MATCHED           (1 << 26)
 +
  /*
   * Extended on-disk flags
   */
@@@ -311,7 -307,6 +311,7 @@@ extern void free_name_hash(struct index
  #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
  #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))
  #endif
  
  enum object_type {
@@@ -335,11 -330,9 +335,11 @@@ static inline enum object_type object_t
                OBJ_BLOB;
  }
  
 +/* Double-check local_repo_env below if you add to this list. */
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
  #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 +#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
  #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
  #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
 +#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
  
  /*
 - * Repository-local GIT_* environment variables
 - * The array is NULL-terminated to simplify its usage in contexts such
 - * environment creation or simple walk of the list.
 - * The number of non-NULL entries is available as a macro.
 + * This environment variable is expected to contain a boolean indicating
 + * whether we should or should not treat:
 + *
 + *   GIT_DIR=foo.git git ...
 + *
 + * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
 + * of this, but we use it internally to communicate to sub-processes that we
 + * are in a bare repo. If not set, defaults to true.
   */
 -#define LOCAL_REPO_ENV_SIZE 9
 -extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
 +#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
 +
 +/*
 + * Repository-local GIT_* environment variables; these will be cleared
 + * when git spawns a sub-process that runs inside another repository.
 + * The array is NULL-terminated, which makes it easy to pass in the "env"
 + * parameter of a run-command invocation, or to do a simple walk.
 + */
 +extern const char * const local_repo_env[];
  
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
@@@ -466,13 -447,11 +466,13 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 +#define ADD_CACHE_IMPLICIT_DOT 32     /* internal to "git add -u/-A" */
  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, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
  extern int index_name_is_other(const struct index_state *, const char *, int);
 +extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 +#define PATHSPEC_ONESTAR 1    /* the pathspec pattern sastisfies GFNM_ONESTAR */
 +
  struct pathspec {
        const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
        int nr;
        struct pathspec_item {
                const char *match;
                int len;
 -              unsigned int use_wildcard:1;
 +              int nowildcard_len;
 +              int flags;
        } *items;
  };
  
@@@ -503,8 -479,6 +503,8 @@@ extern int init_pathspec(struct pathspe
  extern void free_pathspec(struct pathspec *);
  extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
  
 +extern int limit_pathspec_to_literal(void);
 +
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@@ -545,7 -519,6 +545,7 @@@ extern int delete_ref(const char *, con
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
  extern int trust_ctime;
 +extern int check_stat;
  extern int quote_path_fully;
  extern int has_symlinks;
  extern int minimum_abbrev, default_abbrev;
@@@ -572,12 -545,6 +572,12 @@@ extern int core_preload_index
  extern int core_apply_sparse_checkout;
  extern int precomposed_unicode;
  
 +/*
 + * The character that begins a commented line in user-editable file
 + * that is subject to stripspace.
 + */
 +extern char comment_line_char;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -722,7 -689,8 +722,7 @@@ enum sharedrepo 
        PERM_EVERYBODY      = 0664
  };
  int git_config_perm(const char *var, const char *value);
 -int set_shared_perm(const char *path, int mode);
 -#define adjust_shared_perm(path) set_shared_perm((path), 0)
 +int adjust_shared_perm(const char *path);
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
  int mkdir_in_gitdir(const char *path);
@@@ -910,6 -878,7 +910,7 @@@ void show_date_relative(unsigned long t
                        struct strbuf *timebuf);
  int parse_date(const char *date, char *buf, int bufsize);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
+ int parse_expiry_date(const char *date, unsigned long *timestamp);
  void datestamp(char *buf, int bufsize);
  #define approxidate(s) approxidate_careful((s), NULL)
  unsigned long approxidate_careful(const char *, int *);
@@@ -1021,20 -990,15 +1022,20 @@@ struct ref 
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        char *symref;
 -      unsigned int force:1,
 +      unsigned int
 +              force:1,
 +              forced_update:1,
                merge:1,
 -              nonfastforward:1,
 -              deletion:1;
 +              deletion:1,
 +              matched:1;
        enum {
                REF_STATUS_NONE = 0,
                REF_STATUS_OK,
                REF_STATUS_REJECT_NONFASTFORWARD,
 +              REF_STATUS_REJECT_ALREADY_EXISTS,
                REF_STATUS_REJECT_NODELETE,
 +              REF_STATUS_REJECT_FETCH_FIRST,
 +              REF_STATUS_REJECT_NEEDS_FORCE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
                REF_STATUS_EXPECTING_REPORT
@@@ -1058,9 -1022,7 +1059,9 @@@ struct extra_have_objects 
        int nr, alloc;
        unsigned char (*array)[20];
  };
 -extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
 +extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 +                                   struct ref **list, unsigned int flags,
 +                                   struct extra_have_objects *);
  extern int server_supports(const char *feature);
  extern int parse_feature_request(const char *features, const char *feature);
  extern const char *server_feature_value(const char *feature, int *len_ret);
@@@ -1068,9 -1030,6 +1069,9 @@@ extern const char *parse_feature_value(
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
 +/* A hook for count-objects to report invalid files in pack directory */
 +extern void (*report_garbage)(const char *desc, const char *path);
 +
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
  extern void install_packed_git(struct packed_git *pack);
@@@ -1168,9 -1127,6 +1169,9 @@@ extern int check_repository_format_vers
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int config_error_nonbool(const char *);
 +#if defined(__GNUC__) && ! defined(__clang__)
 +#define config_error_nonbool(s) (config_error_nonbool(s), -1)
 +#endif
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
  
@@@ -1184,28 -1140,12 +1185,28 @@@ struct config_include_data 
  #define CONFIG_INCLUDE_INIT { 0 }
  extern int git_config_include(const char *name, const char *value, void *data);
  
 +/*
 + * Match and parse a config key of the form:
 + *
 + *   section.(subsection.)?key
 + *
 + * (i.e., what gets handed to a config_fn_t). The caller provides the section;
 + * we return -1 if it does not match, 0 otherwise. The subsection and key
 + * out-parameters are filled by the function (and subsection is NULL if it is
 + * missing).
 + */
 +extern int parse_config_key(const char *var,
 +                          const char *section,
 +                          const char **subsection, int *subsection_len,
 +                          const char **key);
 +
  extern int committer_ident_sufficiently_given(void);
  extern int author_ident_sufficiently_given(void);
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
  extern const char *git_mailmap_file;
 +extern const char *git_mailmap_blob;
  
  /* IO helper functions */
  extern void maybe_flush_or_die(FILE *, const char *);
diff --combined date.c
index df20d0ba1d9c5d9fa03316968c1b7038659add05,876d679b1eed24296249e3fd9d9afbdafa22f0ba..29f15404feab0d98f377b312a3fcf5d8888fdb0a
--- 1/date.c
--- 2/date.c
+++ b/date.c
@@@ -383,7 -383,7 +383,7 @@@ static int is_date(int year, int month
                 * sense to specify timestamp way into the future.  Make
                 * sure it is not later than ten days from now...
                 */
 -              if (now + 10*24*3600 < specified)
 +              if ((specified != -1) && (now + 10*24*3600 < specified))
                        return 0;
                tm->tm_mon = r->tm_mon;
                tm->tm_mday = r->tm_mday;
@@@ -694,14 -694,8 +694,14 @@@ int parse_date_basic(const char *date, 
  
        /* mktime uses local timezone */
        *timestamp = tm_to_time_t(&tm);
 -      if (*offset == -1)
 -              *offset = ((time_t)*timestamp - mktime(&tm)) / 60;
 +      if (*offset == -1) {
 +              time_t temp_time = mktime(&tm);
 +              if ((time_t)*timestamp > temp_time) {
 +                      *offset = ((time_t)*timestamp - temp_time) / 60;
 +              } else {
 +                      *offset = -(int)((temp_time - (time_t)*timestamp) / 60);
 +              }
 +      }
  
        if (*timestamp == -1)
                return -1;
        return 0; /* success */
  }
  
+ int parse_expiry_date(const char *date, unsigned long *timestamp)
+ {
+       int errors = 0;
+       if (!strcmp(date, "never") || !strcmp(date, "false"))
+               *timestamp = 0;
+       else if (!strcmp(date, "all") || !strcmp(date, "now"))
+               /*
+                * We take over "now" here, which usually translates
+                * to the current timestamp.  This is because the user
+                * really means to expire everything she has done in
+                * the past, and by definition reflogs are the record
+                * of the past, and there is nothing from the future
+                * to be kept.
+                */
+               *timestamp = ULONG_MAX;
+       else
+               *timestamp = approxidate_careful(date, &errors);
+       return errors;
+ }
  int parse_date(const char *date, char *result, int maxlen)
  {
        unsigned long timestamp;
diff --combined parse-options.h
index 1c8bd8d5a0894d9deda1e849165ff38e8d594a4d,854181113731070fee38c86c95b42822d03e1e26..c378b75b13317d27f05232c7a035ddad2374a87e
@@@ -140,6 -140,9 +140,9 @@@ struct option 
  #define OPT_DATE(s, l, v, h) \
        { OPTION_CALLBACK, (s), (l), (v), N_("time"),(h), 0,    \
          parse_opt_approxidate_cb }
+ #define OPT_EXPIRY_DATE(s, l, v, h) \
+       { OPTION_CALLBACK, (s), (l), (v), N_("expiry date"),(h), 0,     \
+         parse_opt_expiry_date_cb }
  #define OPT_CALLBACK(s, l, v, a, h, f) \
        { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
  #define OPT_NUMBER_CALLBACK(v, h, f) \
@@@ -177,10 -180,6 +180,10 @@@ extern NORETURN void usage_msg_opt(cons
  
  extern int optbug(const struct option *opt, const char *reason);
  extern int opterror(const struct option *opt, const char *reason, int flags);
 +#if defined(__GNUC__) && ! defined(clang)
 +#define opterror(o,r,f) (opterror((o),(r),(f)), -1)
 +#endif
 +
  /*----- incremental advanced APIs -----*/
  
  enum {
@@@ -219,6 -218,7 +222,7 @@@ extern int parse_options_concat(struct 
  /*----- some often used options -----*/
  extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
  extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+ extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);
  extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
  extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
  extern int parse_opt_with_commit(const struct option *, const char *, int);