]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ns/core-fsyncmethod'
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 Mar 2022 23:38:24 +0000 (16:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 25 Mar 2022 23:38:24 +0000 (16:38 -0700)
Replace core.fsyncObjectFiles with two new configuration variables,
core.fsync and core.fsyncMethod.

* ns/core-fsyncmethod:
  core.fsync: documentation and user-friendly aggregate options
  core.fsync: new option to harden the index
  core.fsync: add configuration parsing
  core.fsync: introduce granular fsync control infrastructure
  core.fsyncmethod: add writeout-only mode
  wrapper: make inclusion of Windows csprng header tightly scoped

13 files changed:
1  2 
Makefile
builtin/fast-import.c
builtin/index-pack.c
builtin/pack-objects.c
bulk-checkin.c
cache.h
commit-graph.c
config.c
config.mak.uname
environment.c
git-compat-util.h
object-file.c
read-cache.c

diff --combined Makefile
index 70f0a004e75614a1afc1ccae07c5564cced4f758,17fd9b023a48eb7fc1012ddafea709dba395970b..e8aba291d7f591eeec42855b06ed79549b8d0841
+++ b/Makefile
@@@ -1,9 -1,6 +1,9 @@@
  # The default target of this Makefile is...
  all::
  
 +# Import tree-wide shared Makefile behavior and libraries
 +include shared.mak
 +
  # Define V=1 to have a more verbose compile.
  #
  # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
  #
  # Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC.
  #
+ # Define HAVE_SYNC_FILE_RANGE if your platform has sync_file_range.
+ #
  # Define NEEDS_LIBRT if your platform requires linking with librt (glibc version
  # before 2.17) for clock_gettime and CLOCK_MONOTONIC.
  #
@@@ -833,33 -832,12 +835,33 @@@ GENERATED_H += hook-list.
  .PHONY: generated-hdrs
  generated-hdrs: $(GENERATED_H)
  
 -LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
 +## Exhaustive lists of our source files, either dynamically generated,
 +## or hardcoded.
 +SOURCES_CMD = ( \
 +      git ls-files \
 +              '*.[hcS]' \
 +              '*.sh' \
 +              ':!*[tp][0-9][0-9][0-9][0-9]*' \
 +              ':!contrib' \
 +              2>/dev/null || \
        $(FIND) . \
 -      -name .git -prune -o \
 -      -name t -prune -o \
 -      -name Documentation -prune -o \
 -      -name '*.h' -print)))
 +              \( -name .git -type d -prune \) \
 +              -o \( -name '[tp][0-9][0-9][0-9][0-9]*' -prune \) \
 +              -o \( -name contrib -type d -prune \) \
 +              -o \( -name build -type d -prune \) \
 +              -o \( -name 'trash*' -type d -prune \) \
 +              -o \( -name '*.[hcS]' -type f -print \) \
 +              -o \( -name '*.sh' -type f -print \) \
 +              | sed -e 's|^\./||' \
 +      )
 +FOUND_SOURCE_FILES := $(shell $(SOURCES_CMD))
 +
 +FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
 +FOUND_H_SOURCES = $(filter %.h,$(FOUND_SOURCE_FILES))
 +
 +COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
 +
 +LIB_H = $(FOUND_H_SOURCES)
  
  LIB_OBJS += abspath.o
  LIB_OBJS += add-interactive.o
@@@ -1013,7 -991,6 +1015,7 @@@ LIB_OBJS += rebase-interactive.
  LIB_OBJS += rebase.o
  LIB_OBJS += ref-filter.o
  LIB_OBJS += reflog-walk.o
 +LIB_OBJS += reflog.o
  LIB_OBJS += refs.o
  LIB_OBJS += refs/debug.o
  LIB_OBJS += refs/files-backend.o
@@@ -1290,6 -1267,10 +1292,6 @@@ endi
  ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
  ALL_LDFLAGS = $(LDFLAGS)
  
 -comma := ,
 -empty :=
 -space := $(empty) $(empty)
 -
  ifdef SANITIZE
  SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))
  BASIC_CFLAGS += -fsanitize=$(SANITIZE) -fno-sanitize-recover=$(SANITIZE)
@@@ -1918,6 -1899,10 +1920,10 @@@ ifdef HAVE_CLOCK_MONOTONI
        BASIC_CFLAGS += -DHAVE_CLOCK_MONOTONIC
  endif
  
+ ifdef HAVE_SYNC_FILE_RANGE
+       BASIC_CFLAGS += -DHAVE_SYNC_FILE_RANGE
+ endif
  ifdef NEEDS_LIBRT
        EXTLIBS += -lrt
  endif
@@@ -2002,6 -1987,39 +2008,6 @@@ ifndef PAGER_EN
  PAGER_ENV = LESS=FRX LV=-c
  endif
  
 -QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 -QUIET_SUBDIR1  =
 -
 -ifneq ($(findstring w,$(MAKEFLAGS)),w)
 -PRINT_DIR = --no-print-directory
 -else # "make -w"
 -NO_SUBDIR = :
 -endif
 -
 -ifneq ($(findstring s,$(MAKEFLAGS)),s)
 -ifndef V
 -      QUIET_CC       = @echo '   ' CC $@;
 -      QUIET_AR       = @echo '   ' AR $@;
 -      QUIET_LINK     = @echo '   ' LINK $@;
 -      QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
 -      QUIET_GEN      = @echo '   ' GEN $@;
 -      QUIET_LNCP     = @echo '   ' LN/CP $@;
 -      QUIET_XGETTEXT = @echo '   ' XGETTEXT $@;
 -      QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 -      QUIET_GCOV     = @echo '   ' GCOV $@;
 -      QUIET_SP       = @echo '   ' SP $<;
 -      QUIET_HDR      = @echo '   ' HDR $(<:hcc=h);
 -      QUIET_RC       = @echo '   ' RC $@;
 -      QUIET_SPATCH   = @echo '   ' SPATCH $<;
 -      QUIET_SUBDIR0  = +@subdir=
 -      QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 -                       $(MAKE) $(PRINT_DIR) -C $$subdir
 -      export V
 -      export QUIET_GEN
 -      export QUIET_BUILT_IN
 -endif
 -endif
 -
  ifdef NO_INSTALL_HARDLINKS
        export NO_INSTALL_HARDLINKS
  endif
@@@ -2182,6 -2200,16 +2188,6 @@@ shell_compatibility_test: please_set_SH
  strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $^
  
 -### Flags affecting all rules
 -
 -# A GNU make extension since gmake 3.72 (released in late 1994) to
 -# remove the target of rules if commands in those rules fail. The
 -# default is to only do that if make itself receives a signal. Affects
 -# all targets, see:
 -#
 -#    info make --index-search=.DELETE_ON_ERROR
 -.DELETE_ON_ERROR:
 -
  ### Target-specific flags and dependencies
  
  # The generic compilation pattern rule and automatically
@@@ -2544,6 -2572,8 +2550,6 @@@ ASM_SRC := $(wildcard $(OBJECTS:o=S)
  ASM_OBJ := $(ASM_SRC:S=o)
  C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS))
  
 -.SUFFIXES:
 -
  $(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
        $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
  $(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
@@@ -2746,8 -2776,7 +2752,8 @@@ all:: $(MOFILES
  endif
  
  po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
 -      $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
 +      $(call mkdir_p_parent_template)
 +      $(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
  
  LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
  LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
@@@ -2763,16 -2792,35 +2769,16 @@@ NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','
  endif
  
  perl/build/lib/%.pm: perl/%.pm GIT-PERL-DEFINES
 -      $(QUIET_GEN)mkdir -p $(dir $@) && \
 +      $(call mkdir_p_parent_template)
 +      $(QUIET_GEN) \
        sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \
            -e 's|@@NO_GETTEXT@@|$(NO_GETTEXT_SQ)|g' \
            -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
        < $< > $@
  
  perl/build/man/man3/Git.3pm: perl/Git.pm
 -      $(QUIET_GEN)mkdir -p $(dir $@) && \
 -      pod2man $< $@
 -
 -FIND_SOURCE_FILES = ( \
 -      git ls-files \
 -              '*.[hcS]' \
 -              '*.sh' \
 -              ':!*[tp][0-9][0-9][0-9][0-9]*' \
 -              ':!contrib' \
 -              2>/dev/null || \
 -      $(FIND) . \
 -              \( -name .git -type d -prune \) \
 -              -o \( -name '[tp][0-9][0-9][0-9][0-9]*' -prune \) \
 -              -o \( -name contrib -type d -prune \) \
 -              -o \( -name build -type d -prune \) \
 -              -o \( -name 'trash*' -type d -prune \) \
 -              -o \( -name '*.[hcS]' -type f -print \) \
 -              -o \( -name '*.sh' -type f -print \) \
 -              | sed -e 's|^\./||' \
 -      )
 -
 -FOUND_SOURCE_FILES = $(shell $(FIND_SOURCE_FILES))
 +      $(call mkdir_p_parent_template)
 +      $(QUIET_GEN)pod2man $< $@
  
  $(ETAGS_TARGET): $(FOUND_SOURCE_FILES)
        $(QUIET_GEN)$(RM) $@+ && \
@@@ -2906,7 -2954,7 +2912,7 @@@ test_bindir_programs := $(patsubst %,bi
  all:: $(TEST_PROGRAMS) $(test_bindir_programs)
  
  bin-wrappers/%: wrap-for-bin.sh
 -      @mkdir -p bin-wrappers
 +      $(call mkdir_p_parent_template)
        $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
             -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
             -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \
@@@ -2983,6 -3031,9 +2989,6 @@@ check: $(GENERATED_H
                exit 1; \
        fi
  
 -FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
 -COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
 -
  %.cocci.patch: %.cocci $(COCCI_SOURCES)
        $(QUIET_SPATCH) \
        if test $(SPATCH_BATCH_SIZE) = 0; then \
diff --combined builtin/fast-import.c
index ad045c7c2d8b9510f50dc3a9a94d859b40940e43,f2c036a89556e876b564097edd23ac97fedeab56..28d3193c380326e890463c759e3de06cb2073037
@@@ -865,7 -865,7 +865,7 @@@ static void end_packfile(void
                struct tag *t;
  
                close_pack_windows(pack_data);
-               finalize_hashfile(pack_file, cur_pack_oid.hash, 0);
+               finalize_hashfile(pack_file, cur_pack_oid.hash, FSYNC_COMPONENT_PACK, 0);
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->hash,
                                         pack_data->pack_name, object_count,
                                         cur_pack_oid.hash, pack_size);
@@@ -944,8 -944,8 +944,8 @@@ static int store_object
        git_hash_ctx c;
        git_zstream s;
  
 -      hdrlen = xsnprintf((char *)hdr, sizeof(hdr), "%s %lu",
 -                         type_name(type), (unsigned long)dat->len) + 1;
 +      hdrlen = format_object_header((char *)hdr, sizeof(hdr), type,
 +                                    dat->len);
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, hdr, hdrlen);
        the_hash_algo->update_fn(&c, dat->buf, dat->len);
@@@ -1098,7 -1098,7 +1098,7 @@@ static void stream_blob(uintmax_t len, 
        hashfile_checkpoint(pack_file, &checkpoint);
        offset = checkpoint.offset;
  
 -      hdrlen = xsnprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
 +      hdrlen = format_object_header((char *)out_buf, out_sz, OBJ_BLOB, len);
  
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, out_buf, hdrlen);
@@@ -2490,7 -2490,7 +2490,7 @@@ static void note_change_n(const char *p
                unsigned long size;
                char *buf = read_object_with_reference(the_repository,
                                                       &commit_oid,
 -                                                     commit_type, &size,
 +                                                     OBJ_COMMIT, &size,
                                                       &commit_oid);
                if (!buf || size < the_hash_algo->hexsz + 6)
                        die("Not a valid commit: %s", p);
@@@ -2562,7 -2562,7 +2562,7 @@@ static void parse_from_existing(struct 
                char *buf;
  
                buf = read_object_with_reference(the_repository,
 -                                               &b->oid, commit_type, &size,
 +                                               &b->oid, OBJ_COMMIT, &size,
                                                 &b->oid);
                parse_from_commit(b, buf, size);
                free(buf);
@@@ -2658,7 -2658,7 +2658,7 @@@ static struct hash_list *parse_merge(un
                        unsigned long size;
                        char *buf = read_object_with_reference(the_repository,
                                                               &n->oid,
 -                                                             commit_type,
 +                                                             OBJ_COMMIT,
                                                               &size, &n->oid);
                        if (!buf || size < the_hash_algo->hexsz + 6)
                                die("Not a valid commit: %s", from);
diff --combined builtin/index-pack.c
index 8a5a6447442fe3ad1c2d16db8d7305ae877525a0,c5f12f14df5d76055bb21a4994eacdd3c2ff1dff..680b66b0636522be7bf831e69be3adf2115ff94a
@@@ -453,7 -453,8 +453,7 @@@ static void *unpack_entry_data(off_t of
        int hdrlen;
  
        if (!is_delta_type(type)) {
 -              hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX,
 -                                 type_name(type),(uintmax_t)size) + 1;
 +              hdrlen = format_object_header(hdr, sizeof(hdr), type, size);
                the_hash_algo->init_fn(&c);
                the_hash_algo->update_fn(&c, hdr, hdrlen);
        } else
@@@ -582,7 -583,7 +582,7 @@@ static void *unpack_data(struct object_
                if (!n)
                        die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
                               "premature end of pack file, %"PRIuMAX" bytes missing",
 -                             (unsigned int)len),
 +                             len),
                            (uintmax_t)len);
                from += n;
                len -= n;
@@@ -974,7 -975,7 +974,7 @@@ static struct base_data *resolve_delta(
        if (!result_data)
                bad_object(delta_obj->idx.offset, _("failed to apply delta"));
        hash_object_file(the_hash_algo, result_data, result_size,
 -                       type_name(delta_obj->real_type), &delta_obj->idx.oid);
 +                       delta_obj->real_type, &delta_obj->idx.oid);
        sha1_object(result_data, NULL, result_size, delta_obj->real_type,
                    &delta_obj->idx.oid);
  
@@@ -1112,7 -1113,6 +1112,7 @@@ static void *threaded_second_pass(void 
                        list_add(&child->list, &work_head);
                        base_cache_used += child->size;
                        prune_base_data(NULL);
 +                      free_base_data(child);
                } else {
                        /*
                         * This child does not have its own children. It may be
  
                                p = next_p;
                        }
 +                      FREE_AND_NULL(child);
                }
                work_unlock();
        }
@@@ -1291,7 -1290,7 +1291,7 @@@ static void conclude_pack(int fix_thin_
                            nr_objects - nr_objects_initial);
                stop_progress_msg(&progress, msg.buf);
                strbuf_release(&msg);
-               finalize_hashfile(f, tail_hash, 0);
+               finalize_hashfile(f, tail_hash, FSYNC_COMPONENT_PACK, 0);
                hashcpy(read_hash, pack_hash);
                fixup_pack_header_footer(output_fd, pack_hash,
                                         curr_pack, nr_objects,
@@@ -1418,8 -1417,9 +1418,8 @@@ static void fix_unresolved_deltas(struc
                if (!data)
                        continue;
  
 -              if (check_object_signature(the_repository, &d->oid,
 -                                         data, size,
 -                                         type_name(type), NULL))
 +              if (check_object_signature(the_repository, &d->oid, data, size,
 +                                         type) < 0)
                        die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
  
                /*
                 * object).
                 */
                append_obj_to_pack(f, d->oid.hash, data, size, type);
 +              free(data);
                threaded_second_pass(NULL);
  
                display_progress(progress, nr_resolved_deltas);
@@@ -1513,7 -1512,7 +1513,7 @@@ static void final(const char *final_pac
        if (!from_stdin) {
                close(input_fd);
        } else {
-               fsync_or_die(output_fd, curr_pack_name);
+               fsync_component_or_die(FSYNC_COMPONENT_PACK, output_fd, curr_pack_name);
                err = close(output_fd);
                if (err)
                        die_errno(_("error while closing pack file"));
@@@ -1708,7 -1707,6 +1708,7 @@@ static void show_pack_info(int stat_onl
                          i + 1,
                          chain_histogram[i]);
        }
 +      free(chain_histogram);
  }
  
  int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (do_fsck_object && fsck_finish(&fsck_options))
                die(_("fsck error in pack objects"));
  
 +      free(opts.anomaly);
        free(objects);
        strbuf_release(&index_name_buf);
        strbuf_release(&rev_index_name_buf);
diff --combined builtin/pack-objects.c
index 829ca359cf9c4553c8658229ac85ba087e8c9b12,c14fee8e99f56b9512264b62beedbcf208929f8d..c23b788ac882ccf33ce49f5446333cd602980b95
@@@ -1199,16 -1199,26 +1199,26 @@@ static void write_pack_file(void
                        display_progress(progress_state, written);
                }
  
-               /*
-                * Did we write the wrong # entries in the header?
-                * If so, rewrite it like in fast-import
-                */
                if (pack_to_stdout) {
-                       finalize_hashfile(f, hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE);
+                       /*
+                        * We never fsync when writing to stdout since we may
+                        * not be writing to an actual pack file. For instance,
+                        * the upload-pack code passes a pipe here. Calling
+                        * fsync on a pipe results in unnecessary
+                        * synchronization with the reader on some platforms.
+                        */
+                       finalize_hashfile(f, hash, FSYNC_COMPONENT_NONE,
+                                         CSUM_HASH_IN_STREAM | CSUM_CLOSE);
                } else if (nr_written == nr_remaining) {
-                       finalize_hashfile(f, hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
+                       finalize_hashfile(f, hash, FSYNC_COMPONENT_PACK,
+                                         CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
                } else {
-                       int fd = finalize_hashfile(f, hash, 0);
+                       /*
+                        * If we wrote the wrong number of entries in the
+                        * header, rewrite it like in fast-import.
+                        */
+                       int fd = finalize_hashfile(f, hash, FSYNC_COMPONENT_PACK, 0);
                        fixup_pack_header_footer(fd, hash, pack_tmp_name,
                                                 nr_written, hash, offset);
                        close(fd);
@@@ -1802,7 -1812,7 +1812,7 @@@ static void add_preferred_base(struct o
                return;
  
        data = read_object_with_reference(the_repository, oid,
 -                                        tree_type, &size, &tree_oid);
 +                                        OBJ_TREE, &size, &tree_oid);
        if (!data)
                return;
  
@@@ -3651,7 -3661,7 +3661,7 @@@ static int pack_options_allow_reuse(voi
  
  static int get_object_list_from_bitmap(struct rev_info *revs)
  {
 -      if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options, 0)))
 +      if (!(bitmap_git = prepare_bitmap_walk(revs, 0)))
                return -1;
  
        if (pack_options_allow_reuse() &&
@@@ -3727,7 -3737,6 +3737,7 @@@ static void get_object_list(int ac, con
        repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
        setup_revisions(ac, av, &revs, &s_r_opt);
 +      list_objects_filter_copy(&revs.filter, &filter_options);
  
        /* make sure shallows are read */
        is_repository_shallow(the_repository);
  
        if (!fn_show_object)
                fn_show_object = show_object;
 -      traverse_commit_list_filtered(&filter_options, &revs,
 -                                    show_commit, fn_show_object, NULL,
 -                                    NULL);
 +      traverse_commit_list(&revs,
 +                           show_commit, fn_show_object,
 +                           NULL);
  
        if (unpack_unreachable_expiration) {
                revs.ignore_missing_links = 1;
diff --combined bulk-checkin.c
index 85b3ebaf971087f2863b7d3bc6da5ee7be3317cb,a2cf9dcbc8d4a20289fa237a613c10c1226b9786..6d6c37171c9578b45cc41587f9cdfecb75ebc826
@@@ -53,9 -53,10 +53,10 @@@ static void finish_bulk_checkin(struct 
                unlink(state->pack_tmp_name);
                goto clear_exit;
        } else if (state->nr_written == 1) {
-               finalize_hashfile(state->f, hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
+               finalize_hashfile(state->f, hash, FSYNC_COMPONENT_PACK,
+                                 CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
        } else {
-               int fd = finalize_hashfile(state->f, hash, 0);
+               int fd = finalize_hashfile(state->f, hash, FSYNC_COMPONENT_PACK, 0);
                fixup_pack_header_footer(fd, hash, state->pack_tmp_name,
                                         state->nr_written, hash,
                                         state->offset);
@@@ -220,8 -221,8 +221,8 @@@ static int deflate_to_pack(struct bulk_
        if (seekback == (off_t) -1)
                return error("cannot find the current offset");
  
 -      header_len = xsnprintf((char *)obuf, sizeof(obuf), "%s %" PRIuMAX,
 -                             type_name(type), (uintmax_t)size) + 1;
 +      header_len = format_object_header((char *)obuf, sizeof(obuf),
 +                                        type, size);
        the_hash_algo->init_fn(&ctx);
        the_hash_algo->update_fn(&ctx, obuf, header_len);
  
diff --combined cache.h
index 0bc0a37cecbfdcec436dbd26546d2d78190e0173,86680f144ecf1a455ce2f6a851264d9dc83f9dbb..c643268a93a4352b9d33d378002cb613188c9c34
+++ b/cache.h
@@@ -993,8 -993,54 +993,54 @@@ void reset_shared_repository(void)
  extern int read_replace_refs;
  extern char *git_replace_ref_base;
  
+ /*
+  * These values are used to help identify parts of a repository to fsync.
+  * FSYNC_COMPONENT_NONE identifies data that will not be a persistent part of the
+  * repository and so shouldn't be fsynced.
+  */
+ enum fsync_component {
+       FSYNC_COMPONENT_NONE,
+       FSYNC_COMPONENT_LOOSE_OBJECT            = 1 << 0,
+       FSYNC_COMPONENT_PACK                    = 1 << 1,
+       FSYNC_COMPONENT_PACK_METADATA           = 1 << 2,
+       FSYNC_COMPONENT_COMMIT_GRAPH            = 1 << 3,
+       FSYNC_COMPONENT_INDEX                   = 1 << 4,
+ };
+ #define FSYNC_COMPONENTS_OBJECTS (FSYNC_COMPONENT_LOOSE_OBJECT | \
+                                 FSYNC_COMPONENT_PACK)
+ #define FSYNC_COMPONENTS_DERIVED_METADATA (FSYNC_COMPONENT_PACK_METADATA | \
+                                          FSYNC_COMPONENT_COMMIT_GRAPH)
+ #define FSYNC_COMPONENTS_DEFAULT (FSYNC_COMPONENTS_OBJECTS | \
+                                 FSYNC_COMPONENTS_DERIVED_METADATA | \
+                                 ~FSYNC_COMPONENT_LOOSE_OBJECT)
+ #define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS)
+ #define FSYNC_COMPONENTS_ADDED (FSYNC_COMPONENTS_COMMITTED | \
+                               FSYNC_COMPONENT_INDEX)
+ #define FSYNC_COMPONENTS_ALL (FSYNC_COMPONENT_LOOSE_OBJECT | \
+                             FSYNC_COMPONENT_PACK | \
+                             FSYNC_COMPONENT_PACK_METADATA | \
+                             FSYNC_COMPONENT_COMMIT_GRAPH | \
+                             FSYNC_COMPONENT_INDEX)
+ /*
+  * A bitmask indicating which components of the repo should be fsynced.
+  */
+ extern enum fsync_component fsync_components;
  extern int fsync_object_files;
  extern int use_fsync;
+ enum fsync_method {
+       FSYNC_METHOD_FSYNC,
+       FSYNC_METHOD_WRITEOUT_ONLY
+ };
+ extern enum fsync_method fsync_method;
  extern int core_preload_index;
  extern int precomposed_unicode;
  extern int protect_hfs;
@@@ -1003,7 -1049,6 +1049,7 @@@ extern const char *core_fsmonitor
  
  extern int core_apply_sparse_checkout;
  extern int core_sparse_checkout_cone;
 +extern int sparse_expect_files_outside_of_patterns;
  
  /*
   * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
@@@ -1320,23 -1365,9 +1366,23 @@@ enum unpack_loose_header_result unpack_
  struct object_info;
  int parse_loose_header(const char *hdr, struct object_info *oi);
  
 +/**
 + * With in-core object data in "buf", rehash it to make sure the
 + * object name actually matches "oid" to detect object corruption.
 + *
 + * A negative value indicates an error, usually that the OID is not
 + * what we expected, but it might also indicate another error.
 + */
  int check_object_signature(struct repository *r, const struct object_id *oid,
 -                         void *buf, unsigned long size, const char *type,
 -                         struct object_id *real_oidp);
 +                         void *map, unsigned long size,
 +                         enum object_type type);
 +
 +/**
 + * A streaming version of check_object_signature().
 + * Try reading the object named with "oid" using
 + * the streaming interface and rehash it to do the same.
 + */
 +int stream_object_signature(struct repository *r, const struct object_id *oid);
  
  int finalize_object_file(const char *tmpfile, const char *filename);
  
@@@ -1563,7 -1594,7 +1609,7 @@@ int cache_name_stage_compare(const cha
  
  void *read_object_with_reference(struct repository *r,
                                 const struct object_id *oid,
 -                               const char *required_type,
 +                               enum object_type required_type,
                                 unsigned long *size,
                                 struct object_id *oid_ret);
  
@@@ -1715,6 -1746,8 +1761,8 @@@ int copy_file_with_time(const char *dst
  
  void write_or_die(int fd, const void *buf, size_t count);
  void fsync_or_die(int fd, const char *);
+ int fsync_component(enum fsync_component component, int fd);
+ void fsync_component_or_die(enum fsync_component component, int fd, const char *msg);
  
  ssize_t read_in_full(int fd, void *buf, size_t count);
  ssize_t write_in_full(int fd, const void *buf, size_t count);
diff --combined commit-graph.c
index adffd020dd4bfa8229228a22da2485ae576ee8da,64897f57d9f2ebc9503994c34eb3ced714352fda..441b36016ba796c13665603b4c523c9baa52c12d
@@@ -39,8 -39,8 +39,8 @@@ void git_test_write_commit_graph_or_die
  #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
  #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
  #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
 -#define GRAPH_CHUNKID_GENERATION_DATA 0x47444154 /* "GDAT" */
 -#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f56 /* "GDOV" */
 +#define GRAPH_CHUNKID_GENERATION_DATA 0x47444132 /* "GDA2" */
 +#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f32 /* "GDO2" */
  #define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
  #define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
  #define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
@@@ -407,9 -407,6 +407,9 @@@ struct commit_graph *parse_commit_graph
                        &graph->chunk_generation_data);
                pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
                        &graph->chunk_generation_data_overflow);
 +
 +              if (graph->chunk_generation_data)
 +                      graph->read_generation_data = 1;
        }
  
        if (r->settings.commit_graph_read_changed_paths) {
@@@ -806,7 -803,7 +806,7 @@@ static void fill_commit_graph_info(stru
                                die(_("commit-graph requires overflow generation data but has none"));
  
                        offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
 -                      graph_data->generation = get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
 +                      graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
                } else
                        graph_data->generation = item->date + offset;
        } else
@@@ -1559,16 -1556,12 +1559,16 @@@ static void compute_generation_numbers(
                                if (current->date && current->date > max_corrected_commit_date)
                                        max_corrected_commit_date = current->date - 1;
                                commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
 -
 -                              if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
 -                                      ctx->num_generation_data_overflows++;
                        }
                }
        }
 +
 +      for (i = 0; i < ctx->commits.nr; i++) {
 +              struct commit *c = ctx->commits.list[i];
 +              timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
 +              if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
 +                      ctx->num_generation_data_overflows++;
 +      }
        stop_progress(&ctx->progress);
  }
  
@@@ -1686,22 -1679,21 +1686,22 @@@ int write_commit_graph_reachable(struc
  }
  
  static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
 -                              struct string_list *pack_indexes)
 +                              const struct string_list *pack_indexes)
  {
        uint32_t i;
        struct strbuf progress_title = STRBUF_INIT;
        struct strbuf packname = STRBUF_INIT;
        int dirlen;
 +      int ret = 0;
  
        strbuf_addf(&packname, "%s/pack/", ctx->odb->path);
        dirlen = packname.len;
        if (ctx->report_progress) {
                strbuf_addf(&progress_title,
 -                          Q_("Finding commits for commit graph in %d pack",
 -                             "Finding commits for commit graph in %d packs",
 +                          Q_("Finding commits for commit graph in %"PRIuMAX" pack",
 +                             "Finding commits for commit graph in %"PRIuMAX" packs",
                               pack_indexes->nr),
 -                          pack_indexes->nr);
 +                          (uintmax_t)pack_indexes->nr);
                ctx->progress = start_delayed_progress(progress_title.buf, 0);
                ctx->progress_done = 0;
        }
                strbuf_addstr(&packname, pack_indexes->items[i].string);
                p = add_packed_git(packname.buf, packname.len, 1);
                if (!p) {
 -                      error(_("error adding pack %s"), packname.buf);
 -                      return -1;
 +                      ret = error(_("error adding pack %s"), packname.buf);
 +                      goto cleanup;
                }
                if (open_pack_index(p)) {
 -                      error(_("error opening index for %s"), packname.buf);
 -                      return -1;
 +                      ret = error(_("error opening index for %s"), packname.buf);
 +                      goto cleanup;
                }
                for_each_object_in_pack(p, add_packed_commits, ctx,
                                        FOR_EACH_OBJECT_PACK_ORDER);
                free(p);
        }
  
 +cleanup:
        stop_progress(&ctx->progress);
        strbuf_release(&progress_title);
        strbuf_release(&packname);
  
 -      return 0;
 +      return ret;
  }
  
  static int fill_oids_from_commits(struct write_commit_graph_context *ctx,
@@@ -1861,7 -1852,6 +1861,7 @@@ static int write_commit_graph_file(stru
  
                hold_lock_file_for_update_mode(&lk, lock_name,
                                               LOCK_DIE_ON_ERROR, 0444);
 +              free(lock_name);
  
                fd = git_mkstemp_mode(ctx->graph_name, 0444);
                if (fd < 0) {
        }
  
        close_commit_graph(ctx->r->objects);
-       finalize_hashfile(f, file_hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
+       finalize_hashfile(f, file_hash, FSYNC_COMPONENT_COMMIT_GRAPH,
+                         CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        free_chunkfile(cf);
  
        if (ctx->split) {
                } else {
                        char *graph_name = get_commit_graph_filename(ctx->odb);
                        unlink(graph_name);
 +                      free(graph_name);
                }
  
                ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash));
@@@ -2270,7 -2260,7 +2271,7 @@@ out
  }
  
  int write_commit_graph(struct object_directory *odb,
 -                     struct string_list *pack_indexes,
 +                     const struct string_list *const pack_indexes,
                       struct oidset *commits,
                       enum commit_graph_write_flags flags,
                       const struct commit_graph_opts *opts)
diff --combined config.c
index e78397725c955e3976fab4016c37cc3870f26ea7,fd8e16593120c1e96ee76a01d2d32286db274139..eb75f653382b16d3dc45ec7241bae67be3bd91eb
+++ b/config.c
@@@ -1323,6 -1323,79 +1323,79 @@@ static int git_parse_maybe_bool_text(co
        return -1;
  }
  
+ static const struct fsync_component_name {
+       const char *name;
+       enum fsync_component component_bits;
+ } fsync_component_names[] = {
+       { "loose-object", FSYNC_COMPONENT_LOOSE_OBJECT },
+       { "pack", FSYNC_COMPONENT_PACK },
+       { "pack-metadata", FSYNC_COMPONENT_PACK_METADATA },
+       { "commit-graph", FSYNC_COMPONENT_COMMIT_GRAPH },
+       { "index", FSYNC_COMPONENT_INDEX },
+       { "objects", FSYNC_COMPONENTS_OBJECTS },
+       { "derived-metadata", FSYNC_COMPONENTS_DERIVED_METADATA },
+       { "committed", FSYNC_COMPONENTS_COMMITTED },
+       { "added", FSYNC_COMPONENTS_ADDED },
+       { "all", FSYNC_COMPONENTS_ALL },
+ };
+ static enum fsync_component parse_fsync_components(const char *var, const char *string)
+ {
+       enum fsync_component current = FSYNC_COMPONENTS_DEFAULT;
+       enum fsync_component positive = 0, negative = 0;
+       while (string) {
+               int i;
+               size_t len;
+               const char *ep;
+               int negated = 0;
+               int found = 0;
+               string = string + strspn(string, ", \t\n\r");
+               ep = strchrnul(string, ',');
+               len = ep - string;
+               if (!strcmp(string, "none")) {
+                       current = FSYNC_COMPONENT_NONE;
+                       goto next_name;
+               }
+               if (*string == '-') {
+                       negated = 1;
+                       string++;
+                       len--;
+                       if (!len)
+                               warning(_("invalid value for variable %s"), var);
+               }
+               if (!len)
+                       break;
+               for (i = 0; i < ARRAY_SIZE(fsync_component_names); ++i) {
+                       const struct fsync_component_name *n = &fsync_component_names[i];
+                       if (strncmp(n->name, string, len))
+                               continue;
+                       found = 1;
+                       if (negated)
+                               negative |= n->component_bits;
+                       else
+                               positive |= n->component_bits;
+               }
+               if (!found) {
+                       char *component = xstrndup(string, len);
+                       warning(_("ignoring unknown core.fsync component '%s'"), component);
+                       free(component);
+               }
+ next_name:
+               string = ep;
+       }
+       return (current & ~negative) | positive;
+ }
  int git_parse_maybe_bool(const char *value)
  {
        int v = git_parse_maybe_bool_text(value);
@@@ -1600,7 -1673,28 +1673,28 @@@ static int git_default_core_config(cons
                return 0;
        }
  
+       if (!strcmp(var, "core.fsync")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               fsync_components = parse_fsync_components(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "core.fsyncmethod")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (!strcmp(value, "fsync"))
+                       fsync_method = FSYNC_METHOD_FSYNC;
+               else if (!strcmp(value, "writeout-only"))
+                       fsync_method = FSYNC_METHOD_WRITEOUT_ONLY;
+               else
+                       warning(_("ignoring unknown core.fsyncMethod value '%s'"), value);
+       }
        if (!strcmp(var, "core.fsyncobjectfiles")) {
+               if (fsync_object_files < 0)
+                       warning(_("core.fsyncobjectfiles is deprecated; use core.fsync instead"));
                fsync_object_files = git_config_bool(var, value);
                return 0;
        }
        return platform_core_config(var, value, cb);
  }
  
 +static int git_default_sparse_config(const char *var, const char *value)
 +{
 +      if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) {
 +              sparse_expect_files_outside_of_patterns = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      /* Add other config variables here and to Documentation/config/sparse.txt. */
 +      return 0;
 +}
 +
  static int git_default_i18n_config(const char *var, const char *value)
  {
        if (!strcmp(var, "i18n.commitencoding"))
@@@ -1796,9 -1879,6 +1890,9 @@@ int git_default_config(const char *var
                return 0;
        }
  
 +      if (starts_with(var, "sparse."))
 +              return git_default_sparse_config(var, value);
 +
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
diff --combined config.mak.uname
index 7727b707b743a0a467331e5068004e70ad5a5a76,404fff5dd048b9f764162c1b763d03379ba476c8..f6ac966c084ffb28984a27220720955239944151
@@@ -57,6 -57,7 +57,7 @@@ ifeq ($(uname_S),Linux
        HAVE_CLOCK_MONOTONIC = YesPlease
        # -lrt is needed for clock_gettime on glibc <= 2.16
        NEEDS_LIBRT = YesPlease
+       HAVE_SYNC_FILE_RANGE = YesPlease
        HAVE_GETDELIM = YesPlease
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
        BASIC_CFLAGS += -DHAVE_SYSINFO
@@@ -463,6 -464,7 +464,7 @@@ endi
        CFLAGS =
        BASIC_CFLAGS = -nologo -I. -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
        COMPAT_OBJS = compat/msvc.o compat/winansi.o \
+               compat/win32/flush.o \
                compat/win32/path-utils.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/trace2_win32_process_info.o \
@@@ -640,6 -642,7 +642,7 @@@ ifeq ($(uname_S),MINGW
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
                compat/win32/trace2_win32_process_info.o \
+               compat/win32/flush.o \
                compat/win32/path-utils.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
@@@ -727,6 -730,7 +730,6 @@@ vcxproj
        git diff-index --cached --quiet HEAD --
  
        # Make .vcxproj files and add them
 -      unset QUIET_GEN QUIET_BUILT_IN; \
        perl contrib/buildsystems/generate -g Vcxproj
        git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj
  
diff --combined environment.c
index fb55bf61290641f260448a5d7bd78c7a59aee4d4,698f03a2f47d306c49620edd68f08d28134ee5b5..f27e235548baa638038f33556c20f4c9fb706151
@@@ -42,8 -42,10 +42,10 @@@ const char *git_attributes_file
  const char *git_hooks_path;
  int zlib_compression_level = Z_BEST_SPEED;
  int pack_compression_level = Z_DEFAULT_COMPRESSION;
- int fsync_object_files;
+ int fsync_object_files = -1;
  int use_fsync = -1;
+ enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT;
+ enum fsync_component fsync_components = FSYNC_COMPONENTS_DEFAULT;
  size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
  size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
  size_t delta_base_cache_limit = 96 * 1024 * 1024;
@@@ -70,7 -72,6 +72,7 @@@ char *notes_ref_name
  int grafts_replace_parents = 1;
  int core_apply_sparse_checkout;
  int core_sparse_checkout_cone;
 +int sparse_expect_files_outside_of_patterns;
  int merge_log_config = -1;
  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
  unsigned long pack_size_limit_cfg;
diff --combined git-compat-util.h
index e50e2fafaec9d7cf378495bbf80af95cbb3d1d54,00356476a9d1dc5ea49f1e3c8ca59015311f080a..0892e209a2fb23f797030dd04c62cd534e3d1775
  #endif
  #include <windows.h>
  #define GIT_WINDOWS_NATIVE
- #ifdef HAVE_RTLGENRANDOM
- /* This is required to get access to RtlGenRandom. */
- #define SystemFunction036 NTAPI SystemFunction036
- #include <NTSecAPI.h>
- #undef SystemFunction036
- #endif
  #endif
  
  #include <unistd.h>
@@@ -534,7 -528,9 +528,7 @@@ void warning_errno(const char *err, ...
  /*
   * Let callers be aware of the constant return value; this can help
   * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,
 - * because some compilers may not support variadic macros. Since we're only
 - * trying to help gcc, anyway, it's OK; other compilers will fall back to
 - * using the function as usual.
 + * because other compilers may be confused by this.
   */
  #if defined(__GNUC__)
  static inline int const_error(void)
@@@ -1256,13 -1252,49 +1250,37 @@@ static inline int regexec_buf(const reg
  #endif
  #endif
  
 -/*
 - * This is always defined as a first step towards making the use of variadic
 - * macros unconditional. If it causes compilation problems on your platform,
 - * please report it to the Git mailing list at git@vger.kernel.org.
 - */
 -#define HAVE_VARIADIC_MACROS 1
 -
  /* usage.c: only to be used for testing BUG() implementation (see test-tool) */
  extern int BUG_exit_code;
  
 -#ifdef HAVE_VARIADIC_MACROS
  __attribute__((format (printf, 3, 4))) NORETURN
  void BUG_fl(const char *file, int line, const char *fmt, ...);
  #define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
 -#else
 -__attribute__((format (printf, 1, 2))) NORETURN
 -void BUG(const char *fmt, ...);
 -#endif
  
+ #ifdef __APPLE__
+ #define FSYNC_METHOD_DEFAULT FSYNC_METHOD_WRITEOUT_ONLY
+ #else
+ #define FSYNC_METHOD_DEFAULT FSYNC_METHOD_FSYNC
+ #endif
+ enum fsync_action {
+       FSYNC_WRITEOUT_ONLY,
+       FSYNC_HARDWARE_FLUSH
+ };
+ /*
+  * Issues an fsync against the specified file according to the specified mode.
+  *
+  * FSYNC_WRITEOUT_ONLY attempts to use interfaces available on some operating
+  * systems to flush the OS cache without issuing a flush command to the storage
+  * controller. If those interfaces are unavailable, the function fails with
+  * ENOSYS.
+  *
+  * FSYNC_HARDWARE_FLUSH does an OS writeout and hardware flush to ensure that
+  * changes are durable. It is not expected to fail.
+  */
+ int git_fsync(int fd, enum fsync_action action);
  /*
   * Preserves errno, prints a message, but gives no warning for ENOENT.
   * Returns 0 on success, which includes trying to unlink an object that does
diff --combined object-file.c
index bdc5cbdd3868612fc7e0fb43d6268aaa1420b3f6,e3f0bf27ff13e3fadc7b859b859c259f8c3c9231..62ebe236c90a85641b8c806f2cdbebe18fa6307e
@@@ -1049,50 -1049,35 +1049,50 @@@ void *xmmap(void *start, size_t length
        return ret;
  }
  
 -/*
 - * With an in-core object data in "map", rehash it to make sure the
 - * object name actually matches "oid" to detect object corruption.
 - * With "map" == NULL, try reading the object named with "oid" using
 - * the streaming interface and rehash it to do the same.
 - */
 +static int format_object_header_literally(char *str, size_t size,
 +                                        const char *type, size_t objsize)
 +{
 +      return xsnprintf(str, size, "%s %"PRIuMAX, type, (uintmax_t)objsize) + 1;
 +}
 +
 +int format_object_header(char *str, size_t size, enum object_type type,
 +                       size_t objsize)
 +{
 +      const char *name = type_name(type);
 +
 +      if (!name)
 +              BUG("could not get a type name for 'enum object_type' value %d", type);
 +
 +      return format_object_header_literally(str, size, name, objsize);
 +}
 +
  int check_object_signature(struct repository *r, const struct object_id *oid,
 -                         void *map, unsigned long size, const char *type,
 -                         struct object_id *real_oidp)
 +                         void *buf, unsigned long size,
 +                         enum object_type type)
  {
 -      struct object_id tmp;
 -      struct object_id *real_oid = real_oidp ? real_oidp : &tmp;
 +      struct object_id real_oid;
 +
 +      hash_object_file(r->hash_algo, buf, size, type, &real_oid);
 +
 +      return !oideq(oid, &real_oid) ? -1 : 0;
 +}
 +
 +int stream_object_signature(struct repository *r, const struct object_id *oid)
 +{
 +      struct object_id real_oid;
 +      unsigned long size;
        enum object_type obj_type;
        struct git_istream *st;
        git_hash_ctx c;
        char hdr[MAX_HEADER_LEN];
        int hdrlen;
  
 -      if (map) {
 -              hash_object_file(r->hash_algo, map, size, type, real_oid);
 -              return !oideq(oid, real_oid) ? -1 : 0;
 -      }
 -
        st = open_istream(r, oid, &obj_type, &size, NULL);
        if (!st)
                return -1;
  
        /* Generate the header */
 -      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(obj_type), (uintmax_t)size) + 1;
 +      hdrlen = format_object_header(hdr, sizeof(hdr), obj_type, size);
  
        /* Sha1.. */
        r->hash_algo->init_fn(&c);
                        break;
                r->hash_algo->update_fn(&c, buf, readlen);
        }
 -      r->hash_algo->final_oid_fn(real_oid, &c);
 +      r->hash_algo->final_oid_fn(&real_oid, &c);
        close_istream(st);
 -      return !oideq(oid, real_oid) ? -1 : 0;
 +      return !oideq(oid, &real_oid) ? -1 : 0;
  }
  
  int git_open_cloexec(const char *name, int flags)
@@@ -1677,7 -1662,7 +1677,7 @@@ int pretend_object_file(void *buf, unsi
  {
        struct cached_object *co;
  
 -      hash_object_file(the_hash_algo, buf, len, type_name(type), oid);
 +      hash_object_file(the_hash_algo, buf, len, type, oid);
        if (has_object_file_with_flags(oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
            find_cached_object(oid))
                return 0;
@@@ -1737,15 -1722,16 +1737,15 @@@ void *read_object_file_extended(struct 
  
  void *read_object_with_reference(struct repository *r,
                                 const struct object_id *oid,
 -                               const char *required_type_name,
 +                               enum object_type required_type,
                                 unsigned long *size,
                                 struct object_id *actual_oid_return)
  {
 -      enum object_type type, required_type;
 +      enum object_type type;
        void *buffer;
        unsigned long isize;
        struct object_id actual_oid;
  
 -      required_type = type_from_string(required_type_name);
        oidcpy(&actual_oid, oid);
        while (1) {
                int ref_length = -1;
        }
  }
  
 +static void hash_object_body(const struct git_hash_algo *algo, git_hash_ctx *c,
 +                           const void *buf, unsigned long len,
 +                           struct object_id *oid,
 +                           char *hdr, int *hdrlen)
 +{
 +      algo->init_fn(c);
 +      algo->update_fn(c, hdr, *hdrlen);
 +      algo->update_fn(c, buf, len);
 +      algo->final_oid_fn(oid, c);
 +}
 +
  static void write_object_file_prepare(const struct git_hash_algo *algo,
                                      const void *buf, unsigned long len,
 -                                    const char *type, struct object_id *oid,
 +                                    enum object_type type, struct object_id *oid,
                                      char *hdr, int *hdrlen)
  {
        git_hash_ctx c;
  
        /* Generate the header */
 -      *hdrlen = xsnprintf(hdr, *hdrlen, "%s %"PRIuMAX , type, (uintmax_t)len)+1;
 +      *hdrlen = format_object_header(hdr, *hdrlen, type, len);
  
        /* Sha1.. */
 -      algo->init_fn(&c);
 -      algo->update_fn(&c, hdr, *hdrlen);
 -      algo->update_fn(&c, buf, len);
 -      algo->final_oid_fn(oid, &c);
 +      hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
 +}
 +
 +static void write_object_file_prepare_literally(const struct git_hash_algo *algo,
 +                                    const void *buf, unsigned long len,
 +                                    const char *type, struct object_id *oid,
 +                                    char *hdr, int *hdrlen)
 +{
 +      git_hash_ctx c;
 +
 +      *hdrlen = format_object_header_literally(hdr, *hdrlen, type, len);
 +      hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
  }
  
  /*
@@@ -1869,31 -1836,29 +1869,36 @@@ static int write_buffer(int fd, const v
        return 0;
  }
  
 -int hash_object_file(const struct git_hash_algo *algo, const void *buf,
 -                   unsigned long len, const char *type,
 -                   struct object_id *oid)
 +static void hash_object_file_literally(const struct git_hash_algo *algo,
 +                                     const void *buf, unsigned long len,
 +                                     const char *type, struct object_id *oid)
  {
        char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
 -      write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
 -      return 0;
 +
 +      write_object_file_prepare_literally(algo, buf, len, type, oid, hdr, &hdrlen);
 +}
 +
 +void hash_object_file(const struct git_hash_algo *algo, const void *buf,
 +                    unsigned long len, enum object_type type,
 +                    struct object_id *oid)
 +{
 +      hash_object_file_literally(algo, buf, len, type_name(type), oid);
  }
  
  /* Finalize a file on disk, and close it. */
  static void close_loose_object(int fd)
  {
-       if (!the_repository->objects->odb->will_destroy) {
-               if (fsync_object_files)
-                       fsync_or_die(fd, "loose object file");
-       }
+       if (the_repository->objects->odb->will_destroy)
+               goto out;
  
+       if (fsync_object_files > 0)
+               fsync_or_die(fd, "loose object file");
+       else
+               fsync_component_or_die(FSYNC_COMPONENT_LOOSE_OBJECT, fd,
+                                      "loose object file");
+ out:
        if (close(fd) != 0)
                die_errno(_("error when closing loose object file"));
  }
@@@ -2038,7 -2003,7 +2043,7 @@@ static int freshen_packed_object(const 
  }
  
  int write_object_file_flags(const void *buf, unsigned long len,
 -                          const char *type, struct object_id *oid,
 +                          enum object_type type, struct object_id *oid,
                            unsigned flags)
  {
        char hdr[MAX_HEADER_LEN];
        return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
  }
  
 -int hash_object_file_literally(const void *buf, unsigned long len,
 -                             const char *type, struct object_id *oid,
 -                             unsigned flags)
 +int write_object_file_literally(const void *buf, unsigned long len,
 +                              const char *type, struct object_id *oid,
 +                              unsigned flags)
  {
        char *header;
        int hdrlen, status = 0;
        /* type string, SP, %lu of the length plus NUL must fit this */
        hdrlen = strlen(type) + MAX_HEADER_LEN;
        header = xmalloc(hdrlen);
 -      write_object_file_prepare(the_hash_algo, buf, len, type, oid, header,
 -                                &hdrlen);
 +      write_object_file_prepare_literally(the_hash_algo, buf, len, type,
 +                                          oid, header, &hdrlen);
  
        if (!(flags & HASH_WRITE_OBJECT))
                goto cleanup;
@@@ -2092,7 -2057,7 +2097,7 @@@ int force_object_loose(const struct obj
        buf = read_object(the_repository, oid, &type, &len);
        if (!buf)
                return error(_("cannot read object for %s"), oid_to_hex(oid));
 -      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
 +      hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
        free(buf);
  
@@@ -2158,8 -2123,7 +2163,8 @@@ static int index_mem(struct index_stat
                     enum object_type type,
                     const char *path, unsigned flags)
  {
 -      int ret, re_allocated = 0;
 +      int ret = 0;
 +      int re_allocated = 0;
        int write_object = flags & HASH_WRITE_OBJECT;
  
        if (!type)
        }
  
        if (write_object)
 -              ret = write_object_file(buf, size, type_name(type), oid);
 +              ret = write_object_file(buf, size, type, oid);
        else
 -              ret = hash_object_file(the_hash_algo, buf, size,
 -                                     type_name(type), oid);
 +              hash_object_file(the_hash_algo, buf, size, type, oid);
        if (re_allocated)
                free(buf);
        return ret;
@@@ -2200,7 -2165,7 +2205,7 @@@ static int index_stream_convert_blob(st
                                     const char *path,
                                     unsigned flags)
  {
 -      int ret;
 +      int ret = 0;
        const int write_object = flags & HASH_WRITE_OBJECT;
        struct strbuf sbuf = STRBUF_INIT;
  
                                 get_conv_flags(flags));
  
        if (write_object)
 -              ret = write_object_file(sbuf.buf, sbuf.len, type_name(OBJ_BLOB),
 +              ret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB,
                                        oid);
        else
 -              ret = hash_object_file(the_hash_algo, sbuf.buf, sbuf.len,
 -                                     type_name(OBJ_BLOB), oid);
 +              hash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB,
 +                               oid);
        strbuf_release(&sbuf);
        return ret;
  }
@@@ -2334,8 -2299,8 +2339,8 @@@ int index_path(struct index_state *ista
                        return error_errno("readlink(\"%s\")", path);
                if (!(flags & HASH_WRITE_OBJECT))
                        hash_object_file(the_hash_algo, sb.buf, sb.len,
 -                                       blob_type, oid);
 -              else if (write_object_file(sb.buf, sb.len, blob_type, oid))
 +                                       OBJ_BLOB, oid);
 +              else if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))
                        rc = error(_("%s: failed to insert into database"), path);
                strbuf_release(&sb);
                break;
@@@ -2639,10 -2604,9 +2644,10 @@@ int read_loose_object(const char *path
                        git_inflate_end(&stream);
                        goto out;
                }
 -              if (check_object_signature(the_repository, expected_oid,
 +              hash_object_file_literally(the_repository->hash_algo,
                                           *contents, *size,
 -                                         oi->type_name->buf, real_oid))
 +                                         oi->type_name->buf, real_oid);
 +              if (!oideq(expected_oid, real_oid))
                        goto out;
        }
  
diff --combined read-cache.c
index 1ad56d02e1d2494ed1852f260a6e8ad8ba6192ee,7683b6792583a7cc64f5d7ab6fc22c53526ab1c5..3e0e7d418375ebbb916ab66735b5345342e8402b
@@@ -736,7 -736,7 +736,7 @@@ static struct cache_entry *create_alias
  void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
  {
        struct object_id oid;
 -      if (write_object_file("", 0, blob_type, &oid))
 +      if (write_object_file("", 0, OBJ_BLOB, &oid))
                die(_("cannot create an empty blob in the object database"));
        oidcpy(&ce->oid, &oid);
  }
@@@ -2842,7 -2842,7 +2842,7 @@@ static int record_ieot(void
   * rely on it.
   */
  static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
-                         int strip_extensions)
+                         int strip_extensions, unsigned flags)
  {
        uint64_t start = getnanotime();
        struct hashfile *f;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = istate->drop_cache_tree;
        off_t offset;
+       int csum_fsync_flag;
        int ieot_entries = 1;
        struct index_entry_offset_table *ieot = NULL;
        int nr, nr_threads;
                        return -1;
        }
  
-       finalize_hashfile(f, istate->oid.hash, CSUM_HASH_IN_STREAM);
+       csum_fsync_flag = 0;
+       if (!alternate_index_output && (flags & COMMIT_LOCK))
+               csum_fsync_flag = CSUM_FSYNC;
+       finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX,
+                         CSUM_HASH_IN_STREAM | csum_fsync_flag);
        if (close_tempfile_gently(tempfile)) {
                error(_("could not close '%s'"), get_tempfile_path(tempfile));
                return -1;
@@@ -3144,7 -3151,7 +3151,7 @@@ static int do_write_locked_index(struc
         */
        trace2_region_enter_printf("index", "do_write_index", the_repository,
                                   "%s", get_lock_file_path(lock));
-       ret = do_write_index(istate, lock->tempfile, 0);
+       ret = do_write_index(istate, lock->tempfile, 0, flags);
        trace2_region_leave_printf("index", "do_write_index", the_repository,
                                   "%s", get_lock_file_path(lock));
  
@@@ -3238,7 -3245,7 +3245,7 @@@ static int clean_shared_index_files(con
  }
  
  static int write_shared_index(struct index_state *istate,
-                             struct tempfile **temp)
+                             struct tempfile **temp, unsigned flags)
  {
        struct split_index *si = istate->split_index;
        int ret, was_full = !istate->sparse_index;
  
        trace2_region_enter_printf("index", "shared/do_write_index",
                                   the_repository, "%s", get_tempfile_path(*temp));
-       ret = do_write_index(si->base, *temp, 1);
+       ret = do_write_index(si->base, *temp, 1, flags);
        trace2_region_leave_printf("index", "shared/do_write_index",
                                   the_repository, "%s", get_tempfile_path(*temp));
  
@@@ -3357,7 -3364,7 +3364,7 @@@ int write_locked_index(struct index_sta
                        ret = do_write_locked_index(istate, lock, flags);
                        goto out;
                }
-               ret = write_shared_index(istate, &temp);
+               ret = write_shared_index(istate, &temp, flags);
  
                saved_errno = errno;
                if (is_tempfile_active(temp))