@@ -477,13 +477,29 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t
477477
478478static int append_to_pack (git_indexer * idx , const void * data , size_t size )
479479{
480+ git_off_t new_size ;
481+ size_t mmap_alignment ;
482+ size_t page_offset ;
483+ git_off_t page_start ;
480484 git_off_t current_size = idx -> pack -> mwf .size ;
481485 int fd = idx -> pack -> mwf .fd ;
486+ int error ;
482487
483488 if (!size )
484489 return 0 ;
485490
486- if (p_lseek (fd , current_size + size - 1 , SEEK_SET ) < 0 ||
491+ if ((error = git__mmap_alignment (& mmap_alignment )) < 0 )
492+ return error ;
493+
494+ /* Write a single byte to force the file system to allocate space now or
495+ * report an error, since we can't report errors when writing using mmap.
496+ * Round the size up to the nearest page so that we only need to perform file
497+ * I/O when we add a page, instead of whenever we write even a single byte. */
498+ new_size = current_size + size ;
499+ page_offset = new_size % mmap_alignment ;
500+ page_start = new_size - page_offset ;
501+
502+ if (p_lseek (fd , page_start + mmap_alignment - 1 , SEEK_SET ) < 0 ||
487503 p_write (idx -> pack -> mwf .fd , data , 1 ) < 0 ) {
488504 giterr_set (GITERR_OS , "cannot extend packfile '%s'" , idx -> pack -> pack_name );
489505 return -1 ;
@@ -1041,6 +1057,13 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
10411057 goto on_error ;
10421058
10431059 git_mwindow_free_all (& idx -> pack -> mwf );
1060+
1061+ /* Truncate file to undo rounding up to next page_size in append_to_pack */
1062+ if (p_ftruncate (idx -> pack -> mwf .fd , idx -> pack -> mwf .size ) < 0 ) {
1063+ giterr_set (GITERR_OS , "failed to truncate pack file '%s'" , idx -> pack -> pack_name );
1064+ return -1 ;
1065+ }
1066+
10441067 /* We need to close the descriptor here so Windows doesn't choke on commit_at */
10451068 if (p_close (idx -> pack -> mwf .fd ) < 0 ) {
10461069 giterr_set (GITERR_OS , "failed to close packfile" );
0 commit comments