Merge branch 'tb/add-renormalize'
authorJunio C Hamano <gitster@pobox.com>
Mon, 27 Nov 2017 02:06:37 +0000 (11:06 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Nov 2017 02:06:37 +0000 (11:06 +0900)
"git add --renormalize ." is a new and safer way to record the fact
that you are correcting the end-of-line convention and other
"convert_to_git()" glitches in the in-repository data.

* tb/add-renormalize:
add: introduce "--renormalize"

1  2 
builtin/add.c
cache.h
read-cache.c
sha1_file.c
diff --combined builtin/add.c
index 8d08e99e92c1fa12e1dcd4b079097b2b63d69089,c42b50f8576ae86d60362a702ad75202e3ff5b83..bf01d89e28eaef0f8113d42c6b9d4142b8e912e4
@@@ -26,6 -26,7 +26,7 @@@ static const char * const builtin_add_u
  };
  static int patch_interactive, add_interactive, edit_interactive;
  static int take_worktree_changes;
+ static int add_renormalize;
  
  struct update_callback_data {
        int flags;
@@@ -116,13 -117,32 +117,32 @@@ int add_files_to_cache(const char *pref
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        rev.diffopt.format_callback_data = &data;
 -      rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
 +      rev.diffopt.flags.override_submodule_config = 1;
        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
        clear_pathspec(&rev.prune_data);
        return !!data.add_errors;
  }
  
+ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
+ {
+       int i, retval = 0;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce))
+                       continue; /* do not touch unmerged paths */
+               if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
+                       continue; /* do not touch non blobs */
+               if (pathspec && !ce_path_match(ce, pathspec, NULL))
+                       continue;
+               retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE);
+       }
+       return retval;
+ }
  static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
  {
        char *seen;
@@@ -218,7 -238,7 +238,7 @@@ static int edit_patch(int argc, const c
        argc = setup_revisions(argc, argv, &rev, NULL);
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev.diffopt.use_color = 0;
 -      DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
 +      rev.diffopt.flags.ignore_dirty_submodules = 1;
        out = open(file, O_CREAT | O_WRONLY, 0666);
        if (out < 0)
                die(_("Could not open '%s' for writing."), file);
@@@ -276,6 -296,7 +296,7 @@@ static struct option builtin_add_option
        OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
        OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
+       OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")),
        OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
        OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
        { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
@@@ -406,7 -427,7 +427,7 @@@ int cmd_add(int argc, const char **argv
                          chmod_arg[1] != 'x' || chmod_arg[2]))
                die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
  
-       add_new_files = !take_worktree_changes && !refresh_only;
+       add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
        require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
  
        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        plug_bulk_checkin();
  
-       exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+       if (add_renormalize)
+               exit_status |= renormalize_tracked_files(&pathspec, flags);
+       else
+               exit_status |= add_files_to_cache(prefix, &pathspec, flags);
  
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
diff --combined cache.h
index f684c7933ba38041961f210425f31d2976190054,3e63a741139dcd621b54de5056a729ec2fa73785..2e143450514c88ec30b447c8a94e280be9105aa0
+++ b/cache.h
@@@ -204,7 -204,6 +204,7 @@@ struct cache_entry 
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 +#define CE_FSMONITOR_VALID   (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -328,7 -327,6 +328,7 @@@ static inline unsigned int canon_mode(u
  #define CACHE_TREE_CHANGED    (1 << 5)
  #define SPLIT_INDEX_ORDERED   (1 << 6)
  #define UNTRACKED_CHANGED     (1 << 7)
 +#define FSMONITOR_CHANGED     (1 << 8)
  
  struct split_index;
  struct untracked_cache;
@@@ -347,8 -345,6 +347,8 @@@ struct index_state 
        struct hashmap dir_hash;
        unsigned char sha1[20];
        struct untracked_cache *untracked;
 +      uint64_t fsmonitor_last_update;
 +      struct ewah_bitmap *fsmonitor_dirty;
  };
  
  extern struct index_state the_index;
@@@ -606,28 -602,9 +606,28 @@@ extern int do_read_index(struct index_s
  extern int read_index_from(struct index_state *, const char *path);
  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)
 -#define CLOSE_LOCK            (1 << 1)
 +
 +/*
 + * Write the index while holding an already-taken lock. Close the lock,
 + * and if `COMMIT_LOCK` is given, commit it.
 + *
 + * Unless a split index is in use, write the index into the lockfile.
 + *
 + * With a split index, write the shared index to a temporary file,
 + * adjust its permissions and rename it into place, then write the
 + * split index to the lockfile. If the temporary file for the shared
 + * index cannot be created, fall back to the behavior described in
 + * the previous paragraph.
 + *
 + * With `COMMIT_LOCK`, the lock is always committed or rolled back.
 + * Without it, the lock is closed, but neither committed nor rolled
 + * back.
 + */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 +
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
@@@ -704,13 -681,12 +704,14 @@@ extern void *read_blob_data_from_index(
  #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);
 +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 +#define CE_MATCH_IGNORE_FSMONITOR 0X20
 +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);
  
@@@ -741,17 -717,12 +742,17 @@@ extern void fill_stat_cache_info(struc
  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);
  
 +/*
 + * 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;
 +extern int verify_ce_order;
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -805,7 -776,6 +806,7 @@@ extern int core_apply_sparse_checkout
  extern int precomposed_unicode;
  extern int protect_hfs;
  extern int protect_ntfs;
 +extern const char *core_fsmonitor;
  
  /*
   * Include broken refs in all ref iterations, which will
@@@ -1348,13 -1318,6 +1349,13 @@@ extern int set_disambiguate_hint_config
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +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,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
diff --combined read-cache.c
index fbb4967c81976825e7d2c52da91391c6574c4b71,8acd3fcb936f82b4cceae7e71490da1cd2940588..2eb81a66b941325dc339d61a0ab804a60cbb8be2
@@@ -19,7 -19,6 +19,7 @@@
  #include "varint.h"
  #include "split-index.h"
  #include "utf8.h"
 +#include "fsmonitor.h"
  
  /* Mask for the name length in ce_flags in the on-disk index */
  
  #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
  #define CACHE_EXT_LINK 0x6c696e6b       /* "link" */
  #define CACHE_EXT_UNTRACKED 0x554E5452          /* "UNTR" */
 +#define CACHE_EXT_FSMONITOR 0x46534D4E          /* "FSMN" */
  
  /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
  #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
                 CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
 -               SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
 +               SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED)
  
  struct index_state the_index;
  static const char *alternate_index_output;
@@@ -64,7 -62,6 +64,7 @@@ static void replace_index_entry(struct 
        free(old);
        set_index_entry(istate, nr, ce);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
 +      mark_fsmonitor_invalid(istate, ce);
        istate->cache_changed |= CE_ENTRY_CHANGED;
  }
  
@@@ -153,10 -150,8 +153,10 @@@ void fill_stat_cache_info(struct cache_
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
  
 -      if (S_ISREG(st->st_mode))
 +      if (S_ISREG(st->st_mode)) {
                ce_mark_uptodate(ce);
 +              mark_fsmonitor_valid(ce);
 +      }
  }
  
  static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
@@@ -196,7 -191,7 +196,7 @@@ static int ce_compare_link(const struc
  
  static int ce_compare_gitlink(const struct cache_entry *ce)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        /*
         * We don't actually require that the .git directory
         *
         * If so, we consider it always to match.
         */
 -      if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
 +      if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
                return 0;
 -      return hashcmp(sha1, ce->oid.hash);
 +      return oidcmp(&oid, &ce->oid);
  }
  
  static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
@@@ -306,7 -301,7 +306,7 @@@ int match_stat_data_racy(const struct i
        return match_stat_data(sd, st);
  }
  
 -int ie_match_stat(const struct index_state *istate,
 +int ie_match_stat(struct index_state *istate,
                  const struct cache_entry *ce, struct stat *st,
                  unsigned int options)
  {
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
 +      int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
  
 +      if (!ignore_fsmonitor)
 +              refresh_fsmonitor(istate);
        /*
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
                return 0;
        if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
 +      if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID))
 +              return 0;
  
        /*
         * Intent-to-add entries have not been added, so the index entry
        return changed;
  }
  
 -int ie_modified(const struct index_state *istate,
 +int ie_modified(struct index_state *istate,
                const struct cache_entry *ce,
                struct stat *st, unsigned int options)
  {
@@@ -641,13 -631,17 +641,17 @@@ int add_to_index(struct index_state *is
  {
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
-       struct cache_entry *ce, *alias;
+       struct cache_entry *ce, *alias = NULL;
        unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
        int intent_only = flags & ADD_CACHE_INTENT;
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
+       int newflags = HASH_WRITE_OBJECT;
+       if (flags & HASH_RENORMALIZE)
+               newflags |= HASH_RENORMALIZE;
  
        if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
                return error("%s: can only add regular files, symbolic links or git-directories", path);
        if (ignore_case) {
                adjust_dirname_case(istate, ce->name);
        }
+       if (!(flags & HASH_RENORMALIZE)) {
+               alias = index_file_exists(istate, ce->name,
+                                         ce_namelen(ce), ignore_case);
+               if (alias &&
+                   !ce_stage(alias) &&
+                   !ie_match_stat(istate, alias, st, ce_option)) {
+                       /* Nothing changed, really */
+                       if (!S_ISGITLINK(alias->ce_mode))
+                               ce_mark_uptodate(alias);
+                       alias->ce_flags |= CE_ADDED;
  
-       alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
-       if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
-               /* Nothing changed, really */
-               if (!S_ISGITLINK(alias->ce_mode))
-                       ce_mark_uptodate(alias);
-               alias->ce_flags |= CE_ADDED;
-               free(ce);
-               return 0;
+                       free(ce);
+                       return 0;
+               }
        }
        if (!intent_only) {
-               if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
+               if (index_path(&ce->oid, path, st, newflags)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
@@@ -788,7 -786,6 +796,7 @@@ int chmod_index_entry(struct index_stat
        }
        cache_tree_invalidate_path(istate, ce->name);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
 +      mark_fsmonitor_invalid(istate, ce);
        istate->cache_changed |= CE_ENTRY_CHANGED;
  
        return 0;
@@@ -1240,13 -1237,10 +1248,13 @@@ static struct cache_entry *refresh_cach
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
 +      int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
  
        if (!refresh || ce_uptodate(ce))
                return ce;
  
 +      if (!ignore_fsmonitor)
 +              refresh_fsmonitor(istate);
        /*
         * CE_VALID or CE_SKIP_WORKTREE means the user promised us
         * that the change to the work tree does not matter and told
                ce_mark_uptodate(ce);
                return ce;
        }
 +      if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) {
 +              ce_mark_uptodate(ce);
 +              return ce;
 +      }
  
        if (has_symlink_leading_path(ce->name, ce_namelen(ce))) {
                if (ignore_missing)
                         * because CE_UPTODATE flag is in-core only;
                         * we are not going to write this change out.
                         */
 -                      if (!S_ISGITLINK(ce->ce_mode))
 +                      if (!S_ISGITLINK(ce->ce_mode)) {
                                ce_mark_uptodate(ce);
 +                              mark_fsmonitor_valid(ce);
 +                      }
                        return ce;
                }
        }
@@@ -1412,7 -1400,6 +1420,7 @@@ int refresh_index(struct index_state *i
                                 */
                                ce->ce_flags &= ~CE_VALID;
                                ce->ce_flags |= CE_UPDATE_IN_BASE;
 +                              mark_fsmonitor_invalid(istate, ce);
                                istate->cache_changed |= CE_ENTRY_CHANGED;
                        }
                        if (quiet)
@@@ -1532,9 -1519,6 +1540,9 @@@ struct ondisk_cache_entry_extended 
  /* Allow fsck to force verification of the index checksum. */
  int verify_index_checksum;
  
 +/* Allow fsck to force verification of the cache entry order. */
 +int verify_ce_order;
 +
  static int verify_hdr(struct cache_header *hdr, unsigned long size)
  {
        git_SHA_CTX c;
@@@ -1575,9 -1559,6 +1583,9 @@@ static int read_index_extension(struct 
        case CACHE_EXT_UNTRACKED:
                istate->untracked = read_untracked_extension(data, sz);
                break;
 +      case CACHE_EXT_FSMONITOR:
 +              read_fsmonitor_extension(istate, data, sz);
 +              break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@@ -1695,9 -1676,6 +1703,9 @@@ static void check_ce_order(struct index
  {
        unsigned int i;
  
 +      if (!verify_ce_order)
 +              return;
 +
        for (i = 1; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i - 1];
                struct cache_entry *next_ce = istate->cache[i];
@@@ -1753,7 -1731,6 +1761,7 @@@ static void post_read_index_from(struc
        check_ce_order(istate);
        tweak_untracked_cache(istate);
        tweak_split_index(istate);
 +      tweak_fsmonitor(istate);
  }
  
  /* remember to discard_cache() before reading a different cache! */
@@@ -2207,22 -2184,17 +2215,22 @@@ static int has_racy_timestamp(struct in
        return 0;
  }
  
 -/*
 - * Opportunistically update the index but do not complain if we can't
 - */
  void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
  {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
 -          verify_index(istate) &&
 -          write_locked_index(istate, lockfile, COMMIT_LOCK))
 +          verify_index(istate))
 +              write_locked_index(istate, lockfile, COMMIT_LOCK);
 +      else
                rollback_lock_file(lockfile);
  }
  
 +/*
 + * On success, `tempfile` is closed. If it is the temporary file
 + * of a `struct lock_file`, we will therefore effectively perform
 + * a 'close_lock_file_gently()`. Since that is an implementation
 + * detail of lockfiles, callers of `do_write_index()` should not
 + * rely on it.
 + */
  static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                          int strip_extensions)
  {
                if (err)
                        return -1;
        }
 +      if (!strip_extensions && istate->fsmonitor_last_update) {
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              write_fsmonitor_extension(&sb, istate);
 +              err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
 +                      || ce_write(&c, newfd, sb.buf, sb.len) < 0;
 +              strbuf_release(&sb);
 +              if (err)
 +                      return -1;
 +      }
  
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
        if (close_tempfile_gently(tempfile)) {
                error(_("could not close '%s'"), tempfile->filename.buf);
 -              delete_tempfile(&tempfile);
                return -1;
        }
        if (stat(tempfile->filename.buf, &st))
@@@ -2388,9 -2351,14 +2396,9 @@@ static int do_write_locked_index(struc
        int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
 -      assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
 -             (COMMIT_LOCK | CLOSE_LOCK));
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
 -      else if (flags & CLOSE_LOCK)
 -              return close_lock_file_gently(lock);
 -      else
 -              return ret;
 +      return close_lock_file_gently(lock);
  }
  
  static int write_split_index(struct index_state *istate,
@@@ -2535,15 -2503,11 +2543,15 @@@ int write_locked_index(struct index_sta
        int new_shared_index, ret;
        struct split_index *si = istate->split_index;
  
 +      if (istate->fsmonitor_last_update)
 +              fill_fsmonitor_bitmap(istate);
 +
        if (!si || alternate_index_output ||
            (istate->cache_changed & ~EXTMASK)) {
                if (si)
                        hashclr(si->base_sha1);
 -              return do_write_locked_index(istate, lock, flags);
 +              ret = do_write_locked_index(istate, lock, flags);
 +              goto out;
        }
  
        if (getenv("GIT_TEST_SPLIT_INDEX")) {
        if (new_shared_index) {
                ret = write_shared_index(istate, lock, flags);
                if (ret)
 -                      return ret;
 +                      goto out;
        }
  
        ret = write_split_index(istate, lock, flags);
        if (!ret && !new_shared_index)
                freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
  
 +out:
 +      if (flags & COMMIT_LOCK)
 +              rollback_lock_file(lock);
        return ret;
  }
  
diff --combined sha1_file.c
index 8a7c6b7eba8ab2f829c4504b9db4578c0daac25a,15abb184c2db83b3b070ca1619beb749b2cbee56..8ae6cb6285a86fe3117200cb789bbf57a2063923
@@@ -74,6 -74,18 +74,18 @@@ static struct cached_object *find_cache
        return NULL;
  }
  
+ static enum safe_crlf get_safe_crlf(unsigned flags)
+ {
+       if (flags & HASH_RENORMALIZE)
+               return SAFE_CRLF_RENORMALIZE;
+       else if (flags & HASH_WRITE_OBJECT)
+               return safe_crlf;
+       else
+               return SAFE_CRLF_FALSE;
+ }
  int mkdir_in_gitdir(const char *path)
  {
        if (mkdir(path, 0777)) {
@@@ -404,9 -416,6 +416,9 @@@ static void link_alt_odb_entries(const 
        struct strbuf objdirbuf = STRBUF_INIT;
        struct strbuf entry = STRBUF_INIT;
  
 +      if (!alt || !*alt)
 +              return;
 +
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                                relative_base);
@@@ -459,19 -468,19 +471,19 @@@ struct alternate_object_database *alloc
  
  void add_to_alternates_file(const char *reference)
  {
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock = LOCK_INIT;
        char *alts = git_pathdup("objects/info/alternates");
        FILE *in, *out;
 +      int found = 0;
  
 -      hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
 -      out = fdopen_lock_file(lock, "w");
 +      hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
 +      out = fdopen_lock_file(&lock, "w");
        if (!out)
                die_errno("unable to fdopen alternates lockfile");
  
        in = fopen(alts, "r");
        if (in) {
                struct strbuf line = STRBUF_INIT;
 -              int found = 0;
  
                while (strbuf_getline(&line, in) != EOF) {
                        if (!strcmp(reference, line.buf)) {
  
                strbuf_release(&line);
                fclose(in);
 -
 -              if (found) {
 -                      rollback_lock_file(lock);
 -                      lock = NULL;
 -              }
        }
        else if (errno != ENOENT)
                die_errno("unable to read alternates file");
  
 -      if (lock) {
 +      if (found) {
 +              rollback_lock_file(&lock);
 +      } else {
                fprintf_or_die(out, "%s\n", reference);
 -              if (commit_lock_file(lock))
 +              if (commit_lock_file(&lock))
                        die_errno("unable to move new alternates file into place");
                if (alt_odb_tail)
                        link_alt_odb_entries(reference, '\n', NULL, 0);
@@@ -607,6 -619,7 +619,6 @@@ void prepare_alt_odb(void
                return;
  
        alt = getenv(ALTERNATE_DB_ENVIRONMENT);
 -      if (!alt) alt = "";
  
        alt_odb_tail = &alt_odb_list;
        link_alt_odb_entries(alt, PATH_SEP, NULL, 0);
@@@ -1663,7 -1676,7 +1675,7 @@@ static void check_tag(const void *buf, 
                die("corrupt tag");
  }
  
 -static int index_mem(unsigned char *sha1, void *buf, size_t size,
 +static int index_mem(struct object_id *oid, void *buf, size_t size,
                     enum object_type type,
                     const char *path, unsigned flags)
  {
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
                if (convert_to_git(&the_index, path, buf, size, &nbuf,
-                                  write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
+                                  get_safe_crlf(flags))) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
        }
  
        if (write_object)
 -              ret = write_sha1_file(buf, size, typename(type), sha1);
 +              ret = write_sha1_file(buf, size, typename(type), oid->hash);
        else
 -              ret = hash_sha1_file(buf, size, typename(type), sha1);
 +              ret = hash_sha1_file(buf, size, typename(type), oid->hash);
        if (re_allocated)
                free(buf);
        return ret;
  }
  
 -static int index_stream_convert_blob(unsigned char *sha1, int fd,
 +static int index_stream_convert_blob(struct object_id *oid, int fd,
                                     const char *path, unsigned flags)
  {
        int ret;
        assert(would_convert_to_git_filter_fd(path));
  
        convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
-                                write_object ? safe_crlf : SAFE_CRLF_FALSE);
+                                get_safe_crlf(flags));
  
        if (write_object)
                ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 -                                    sha1);
 +                                    oid->hash);
        else
                ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 -                                   sha1);
 +                                   oid->hash);
        strbuf_release(&sbuf);
        return ret;
  }
  
 -static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
 +static int index_pipe(struct object_id *oid, int fd, enum object_type type,
                      const char *path, unsigned flags)
  {
        struct strbuf sbuf = STRBUF_INIT;
        int ret;
  
        if (strbuf_read(&sbuf, fd, 4096) >= 0)
 -              ret = index_mem(sha1, sbuf.buf, sbuf.len, type, path, flags);
 +              ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
        else
                ret = -1;
        strbuf_release(&sbuf);
  
  #define SMALL_FILE_SIZE (32*1024)
  
 -static int index_core(unsigned char *sha1, int fd, size_t size,
 +static int index_core(struct object_id *oid, int fd, size_t size,
                      enum object_type type, const char *path,
                      unsigned flags)
  {
        int ret;
  
        if (!size) {
 -              ret = index_mem(sha1, "", size, type, path, flags);
 +              ret = index_mem(oid, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                ssize_t read_result = read_in_full(fd, buf, size);
                        ret = error("short read while indexing %s",
                                    path ? path : "<unknown>");
                else
 -                      ret = index_mem(sha1, buf, size, type, path, flags);
 +                      ret = index_mem(oid, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -              ret = index_mem(sha1, buf, size, type, path, flags);
 +              ret = index_mem(oid, buf, size, type, path, flags);
                munmap(buf, size);
        }
        return ret;
@@@ -1801,12 -1814,12 +1813,12 @@@ int index_fd(struct object_id *oid, in
         * die() for large files.
         */
        if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
 -              ret = index_stream_convert_blob(oid->hash, fd, path, flags);
 +              ret = index_stream_convert_blob(oid, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
 -              ret = index_pipe(oid->hash, fd, type, path, flags);
 +              ret = index_pipe(oid, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
                 (path && would_convert_to_git(&the_index, path)))
 -              ret = index_core(oid->hash, fd, xsize_t(st->st_size), type, path,
 +              ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
                                 flags);
        else
                ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
@@@ -1840,7 -1853,7 +1852,7 @@@ int index_path(struct object_id *oid, c
                strbuf_release(&sb);
                break;
        case S_IFDIR:
 -              return resolve_gitlink_ref(path, "HEAD", oid->hash);
 +              return resolve_gitlink_ref(path, "HEAD", oid);
        default:
                return error("%s: unsupported file type", path);
        }
@@@ -1883,7 -1896,6 +1895,7 @@@ int for_each_file_in_obj_subdir(unsigne
        DIR *dir;
        struct dirent *de;
        int r = 0;
 +      struct object_id oid;
  
        if (subdir_nr > 0xff)
                BUG("invalid loose object subdirectory: %x", subdir_nr);
                return r;
        }
  
 +      oid.hash[0] = subdir_nr;
 +
        while ((de = readdir(dir))) {
                if (is_dot_or_dotdot(de->d_name))
                        continue;
                strbuf_setlen(path, baselen);
                strbuf_addf(path, "/%s", de->d_name);
  
 -              if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
 -                      char hex[GIT_MAX_HEXSZ+1];
 -                      struct object_id oid;
 -
 -                      xsnprintf(hex, sizeof(hex), "%02x%s",
 -                                subdir_nr, de->d_name);
 -                      if (!get_oid_hex(hex, &oid)) {
 -                              if (obj_cb) {
 -                                      r = obj_cb(&oid, path->buf, data);
 -                                      if (r)
 -                                              break;
 -                              }
 -                              continue;
 +              if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2 &&
 +                  !hex_to_bytes(oid.hash + 1, de->d_name,
 +                                GIT_SHA1_RAWSZ - 1)) {
 +                      if (obj_cb) {
 +                              r = obj_cb(&oid, path->buf, data);
 +                              if (r)
 +                                      break;
                        }
 +                      continue;
                }
  
                if (cruft_cb) {