Merge branch 'nd/daemonize-gc'
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Mar 2014 23:06:38 +0000 (15:06 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Mar 2014 23:06:39 +0000 (15:06 -0800)
Allow running "gc --auto" in the background.

* nd/daemonize-gc:
gc: config option for running --auto in background
daemon: move daemonize() to libgit.a

1  2 
Documentation/config.txt
builtin/gc.c
cache.h
setup.c
diff --combined Documentation/config.txt
index 040197b10ea5a46e6ba643b28de17d2100cb129b,4781773e6a80fbbd62a5eb562b959e1a7d296cb2..02776e51c8c1abfc151d245259f61546e4ff598c
@@@ -992,14 -992,6 +992,14 @@@ commit.cleanup:
        have to remove the help lines that begin with `#` in the commit log
        template yourself, if you do this).
  
 +commit.gpgsign::
 +
 +      A boolean to specify whether all commits should be GPG signed.
 +      Use of this option when doing operations such as rebase can
 +      result in a large number of commits being signed. It may be
 +      convenient to use an agent to avoid typing your GPG passphrase
 +      several times.
 +
  commit.status::
        A boolean to enable/disable inclusion of status information in the
        commit message template when using an editor to prepare the commit
@@@ -1175,6 -1167,10 +1175,10 @@@ gc.autopacklimit:
        --auto` consolidates them into one larger pack.  The
        default value is 50.  Setting this to 0 disables it.
  
+ gc.autodetach::
+       Make `git gc --auto` return immediately andrun in background
+       if the system supports it. Default is true.
  gc.packrefs::
        Running `git pack-refs` in a repository renders it
        unclonable by Git versions prior to 1.5.1.2 over dumb
@@@ -1870,31 -1866,6 +1874,31 @@@ pack.packSizeLimit:
        Common unit suffixes of 'k', 'm', or 'g' are
        supported.
  
 +pack.useBitmaps::
 +      When true, git will use pack bitmaps (if available) when packing
 +      to stdout (e.g., during the server side of a fetch). Defaults to
 +      true. You should not generally need to turn this off unless
 +      you are debugging pack bitmaps.
 +
 +pack.writebitmaps::
 +      When true, git will write a bitmap index when packing all
 +      objects to disk (e.g., when `git repack -a` is run).  This
 +      index can speed up the "counting objects" phase of subsequent
 +      packs created for clones and fetches, at the cost of some disk
 +      space and extra time spent on the initial repack.  Defaults to
 +      false.
 +
 +pack.writeBitmapHashCache::
 +      When true, git will include a "hash cache" section in the bitmap
 +      index (if one is written). This cache can be used to feed git's
 +      delta heuristics, potentially leading to better deltas between
 +      bitmapped and non-bitmapped objects (e.g., when serving a fetch
 +      between an older, bitmapped pack and objects that have been
 +      pushed since the last gc). The downside is that it consumes 4
 +      bytes per object of disk space, and that JGit's bitmap
 +      implementation does not understand it, causing it to complain if
 +      Git and JGit are used on the same repository. Defaults to false.
 +
  pager.<cmd>::
        If the value is boolean, turns on or off pagination of the
        output of a particular Git subcommand when writing to a tty.
@@@ -1914,16 -1885,6 +1918,16 @@@ pretty.<name>:
        Note that an alias with the same name as a built-in format
        will be silently ignored.
  
 +pull.ff::
 +      By default, Git does not create an extra merge commit when merging
 +      a commit that is a descendant of the current commit. Instead, the
 +      tip of the current branch is fast-forwarded. When set to `false`,
 +      this variable tells Git to create an extra merge commit in such
 +      a case (equivalent to giving the `--no-ff` option from the command
 +      line). When set to `only`, only such fast-forward merges are
 +      allowed (equivalent to giving the `--ff-only` option from the
 +      command line).
 +
  pull.rebase::
        When true, rebase branches on top of the fetched branch, instead
        of merging the default branch from the default remote when "git
diff --combined builtin/gc.c
index 5bbb5e3cc6e3c75ad41a0a92594006a659459634,ed5cc3c0318f87abd8674f7b98936a2265fff577..63d400bcb2092b1751427994b35057f72b04de17
@@@ -29,6 -29,7 +29,7 @@@ static int pack_refs = 1
  static int aggressive_window = 250;
  static int gc_auto_threshold = 6700;
  static int gc_auto_pack_limit = 50;
+ static int detach_auto = 1;
  static const char *prune_expire = "2.weeks.ago";
  
  static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
@@@ -73,6 -74,10 +74,10 @@@ static int gc_config(const char *var, c
                gc_auto_pack_limit = git_config_int(var, value);
                return 0;
        }
+       if (!strcmp(var, "gc.autodetach")) {
+               detach_auto = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "gc.pruneexpire")) {
                if (value && strcmp(value, "now")) {
                        unsigned long now = approxidate("now");
@@@ -188,12 -193,13 +193,12 @@@ static int need_to_gc(void
  static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
  {
        static struct lock_file lock;
 -      static char locking_host[128];
        char my_host[128];
        struct strbuf sb = STRBUF_INIT;
        struct stat st;
        uintmax_t pid;
        FILE *fp;
 -      int fd, should_exit;
 +      int fd;
  
        if (pidfile)
                /* already locked */
        fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
 +              static char locking_host[128];
 +              int should_exit;
                fp = fopen(git_path("gc.pid"), "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
@@@ -302,11 -306,19 +307,19 @@@ int cmd_gc(int argc, const char **argv
                 */
                if (!need_to_gc())
                        return 0;
-               if (!quiet)
-                       fprintf(stderr,
-                                       _("Auto packing the repository for optimum performance. You may also\n"
-                                       "run \"git gc\" manually. See "
-                                       "\"git help gc\" for more information.\n"));
+               if (!quiet) {
+                       if (detach_auto)
+                               fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
+                       else
+                               fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
+                       fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
+               }
+               if (detach_auto)
+                       /*
+                        * failure to daemonize is ok, we'll continue
+                        * in foreground
+                        */
+                       daemonize();
        } else
                add_repack_all_option();
  
diff --combined cache.h
index db223e8048a50d9f37d523226dfdda05a4f5dd8e,521262f8b494a46ff310d79c38cbd839aa080615..b7d82e5d52bb09aed15e43d34e89871e3735df5c
+++ b/cache.h
@@@ -3,7 -3,7 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
 -#include "hash.h"
 +#include "hashmap.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -130,12 -130,12 +130,12 @@@ struct stat_data 
  };
  
  struct cache_entry {
 +      struct hashmap_entry ent;
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
 -      struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 -#define CE_UNHASHED          (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -194,18 -195,17 +194,18 @@@ struct pathspec
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
   */
 -#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
  static inline void copy_cache_entry(struct cache_entry *dst,
                                    const struct cache_entry *src)
  {
 -      unsigned int state = dst->ce_flags & CE_STATE_MASK;
 +      unsigned int state = dst->ce_flags & CE_HASHED;
  
        /* Don't copy hash chain and name */
 -      memcpy(dst, src, offsetof(struct cache_entry, next));
 +      memcpy(&dst->ce_stat_data, &src->ce_stat_data,
 +                      offsetof(struct cache_entry, name) -
 +                      offsetof(struct cache_entry, ce_stat_data));
  
        /* Restore the hash state */
 -      dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 +      dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -277,8 -277,8 +277,8 @@@ struct index_state 
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
 -      struct hash_table name_hash;
 -      struct hash_table dir_hash;
 +      struct hashmap name_hash;
 +      struct hashmap dir_hash;
  };
  
  extern struct index_state the_index;
@@@ -316,6 -316,7 +316,6 @@@ extern void free_name_hash(struct index
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
  #define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
 -#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
  #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
@@@ -433,6 -434,7 +433,7 @@@ extern int set_git_dir_init(const char 
  extern int init_db(const char *template_dir, unsigned int flags);
  
  extern void sanitize_stdfds(void);
+ extern int daemonize(void);
  
  #define alloc_nr(x) (((x)+16)*3/2)
  
@@@ -466,6 -468,7 +467,6 @@@ extern int unmerged_index(const struct 
  extern int verify_path(const char *path);
  extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 -extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -485,9 -488,8 +486,9 @@@ extern int remove_file_from_index(struc
  #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 struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  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);
  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 *);
  
  #define CE_MATCH_RACY_IS_DIRTY                02
  /* do stat comparison even if CE_SKIP_WORKTREE is true */
  #define CE_MATCH_IGNORE_SKIP_WORKTREE 04
 +/* ignore non-existent files during stat update  */
 +#define CE_MATCH_IGNORE_MISSING               0x08
 +/* enable stat refresh */
 +#define CE_MATCH_REFRESH              0x10
  extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
 -extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 -
  #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);
@@@ -738,29 -738,8 +739,29 @@@ enum sharedrepo 
  };
  int git_config_perm(const char *var, const char *value);
  int adjust_shared_perm(const char *path);
 -int safe_create_leading_directories(char *path);
 -int safe_create_leading_directories_const(const char *path);
 +
 +/*
 + * Create the directory containing the named path, using care to be
 + * somewhat safe against races.  Return one of the scld_error values
 + * to indicate success/failure.
 + *
 + * SCLD_VANISHED indicates that one of the ancestor directories of the
 + * path existed at one point during the function call and then
 + * suddenly vanished, probably because another process pruned the
 + * directory while we were working.  To be robust against this kind of
 + * race, callers might want to try invoking the function again when it
 + * returns SCLD_VANISHED.
 + */
 +enum scld_error {
 +      SCLD_OK = 0,
 +      SCLD_FAILED = -1,
 +      SCLD_PERMS = -2,
 +      SCLD_EXISTS = -3,
 +      SCLD_VANISHED = -4
 +};
 +enum scld_error safe_create_leading_directories(char *path);
 +enum scld_error safe_create_leading_directories_const(const char *path);
 +
  int mkdir_in_gitdir(const char *path);
  extern void home_config_paths(char **global, char **xdg, char *file);
  extern char *expand_user_path(const char *path);
@@@ -809,7 -788,6 +810,7 @@@ extern int hash_sha1_file(const void *b
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +extern int git_open_noatime(const char *name);
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -917,12 -895,9 +918,12 @@@ extern int dwim_log(const char *str, in
  extern int interpret_branch_name(const char *str, int len, struct strbuf *);
  extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
 -extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 -extern const char *ref_rev_parse_rules[];
 -#define ref_fetch_rules ref_rev_parse_rules
 +/*
 + * Return true iff abbrev_name is a possible abbreviation for
 + * full_name according to the rules defined by ref_rev_parse_rules in
 + * refs.c.
 + */
 +extern int refname_match(const char *abbrev_name, const char *full_name);
  
  extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  extern int validate_headref(const char *ref);
diff --combined setup.c
index cffb6d605e7893a3f9ff83f427732f6b7f3443ff,713043ad508dd0083e359547ed477fc9ee5e8406..613e3b3c13b3f09bce07c54dc440d03c0b5beb0c
+++ b/setup.c
@@@ -5,70 -5,6 +5,70 @@@
  static int inside_git_dir = -1;
  static int inside_work_tree = -1;
  
 +/*
 + * The input parameter must contain an absolute path, and it must already be
 + * normalized.
 + *
 + * Find the part of an absolute path that lies inside the work tree by
 + * dereferencing symlinks outside the work tree, for example:
 + * /dir1/repo/dir2/file   (work tree is /dir1/repo)      -> dir2/file
 + * /dir/file              (work tree is /)               -> dir/file
 + * /dir/symlink1/symlink2 (symlink1 points to work tree) -> symlink2
 + * /dir/repolink/file     (repolink points to /dir/repo) -> file
 + * /dir/repo              (exactly equal to work tree)   -> (empty string)
 + */
 +static int abspath_part_inside_repo(char *path)
 +{
 +      size_t len;
 +      size_t wtlen;
 +      char *path0;
 +      int off;
 +      const char *work_tree = get_git_work_tree();
 +
 +      if (!work_tree)
 +              return -1;
 +      wtlen = strlen(work_tree);
 +      len = strlen(path);
 +      off = 0;
 +
 +      /* check if work tree is already the prefix */
 +      if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
 +              if (path[wtlen] == '/') {
 +                      memmove(path, path + wtlen + 1, len - wtlen);
 +                      return 0;
 +              } else if (path[wtlen - 1] == '/' || path[wtlen] == '\0') {
 +                      /* work tree is the root, or the whole path */
 +                      memmove(path, path + wtlen, len - wtlen + 1);
 +                      return 0;
 +              }
 +              /* work tree might match beginning of a symlink to work tree */
 +              off = wtlen;
 +      }
 +      path0 = path;
 +      path += offset_1st_component(path) + off;
 +
 +      /* check each '/'-terminated level */
 +      while (*path) {
 +              path++;
 +              if (*path == '/') {
 +                      *path = '\0';
 +                      if (strcmp(real_path(path0), work_tree) == 0) {
 +                              memmove(path0, path + 1, len - (path - path0));
 +                              return 0;
 +                      }
 +                      *path = '/';
 +              }
 +      }
 +
 +      /* check whole path */
 +      if (strcmp(real_path(path0), work_tree) == 0) {
 +              *path0 = '\0';
 +              return 0;
 +      }
 +
 +      return -1;
 +}
 +
  /*
   * Normalize "path", prepending the "prefix" for relative paths. If
   * remaining_prefix is not NULL, return the actual prefix still
@@@ -86,17 -22,11 +86,17 @@@ char *prefix_path_gently(const char *pr
        const char *orig = path;
        char *sanitized;
        if (is_absolute_path(orig)) {
 -              const char *temp = real_path(path);
 -              sanitized = xmalloc(len + strlen(temp) + 1);
 -              strcpy(sanitized, temp);
 +              sanitized = xmalloc(strlen(path) + 1);
                if (remaining_prefix)
                        *remaining_prefix = 0;
 +              if (normalize_path_copy_len(sanitized, path, remaining_prefix)) {
 +                      free(sanitized);
 +                      return NULL;
 +              }
 +              if (abspath_part_inside_repo(sanitized)) {
 +                      free(sanitized);
 +                      return NULL;
 +              }
        } else {
                sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
                strcpy(sanitized + len, path);
                if (remaining_prefix)
                        *remaining_prefix = len;
 -      }
 -      if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
 -              goto error_out;
 -      if (is_absolute_path(orig)) {
 -              size_t root_len, len, total;
 -              const char *work_tree = get_git_work_tree();
 -              if (!work_tree)
 -                      goto error_out;
 -              len = strlen(work_tree);
 -              root_len = offset_1st_component(work_tree);
 -              total = strlen(sanitized) + 1;
 -              if (strncmp(sanitized, work_tree, len) ||
 -                  (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
 -              error_out:
 +              if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
                        free(sanitized);
                        return NULL;
                }
 -              if (sanitized[len] == '/')
 -                      len++;
 -              memmove(sanitized, sanitized + len, total - len);
        }
        return sanitized;
  }
@@@ -841,3 -787,27 +841,27 @@@ void sanitize_stdfds(void
        if (fd > 2)
                close(fd);
  }
+ int daemonize(void)
+ {
+ #ifdef NO_POSIX_GOODIES
+       errno = ENOSYS;
+       return -1;
+ #else
+       switch (fork()) {
+               case 0:
+                       break;
+               case -1:
+                       die_errno("fork failed");
+               default:
+                       exit(0);
+       }
+       if (setsid() == -1)
+               die_errno("setsid failed");
+       close(0);
+       close(1);
+       close(2);
+       sanitize_stdfds();
+       return 0;
+ #endif
+ }