Merge branch 'ds/commit-graph'
authorJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:20 +0000 (15:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:20 +0000 (15:59 +0900)
Precompute and store information necessary for ancestry traversal
in a separate file to optimize graph walking.

* ds/commit-graph:
  commit-graph: implement "--append" option
  commit-graph: build graph from starting commits
  commit-graph: read only from specific pack-indexes
  commit: integrate commit graph with commit parsing
  commit-graph: close under reachability
  commit-graph: add core.commitGraph setting
  commit-graph: implement git commit-graph read
  commit-graph: implement git-commit-graph write
  commit-graph: implement write_commit_graph()
  commit-graph: create git-commit-graph builtin
  graph: add commit graph design document
  commit-graph: add format document
  csum-file: refactor finalize_hashfile() method
  csum-file: rename hashclose() to finalize_hashfile()

18 files changed:
1  2 
.gitignore
Documentation/config.txt
Makefile
builtin.h
builtin/index-pack.c
builtin/pack-objects.c
bulk-checkin.c
cache.h
commit-graph.c
commit.c
config.c
contrib/completion/git-completion.bash
environment.c
fast-import.c
git.c
pack-bitmap-write.c
packfile.c
packfile.h

diff --combined .gitignore
@@@ -3,7 -3,6 +3,7 @@@
  /GIT-LDFLAGS
  /GIT-PREFIX
  /GIT-PERL-DEFINES
 +/GIT-PERL-HEADER
  /GIT-PYTHON-VARS
  /GIT-SCRIPT-DEFINES
  /GIT-USER-AGENT
@@@ -35,6 -34,7 +35,7 @@@
  /git-clone
  /git-column
  /git-commit
+ /git-commit-graph
  /git-commit-tree
  /git-config
  /git-count-objects
  /git-rm
  /git-send-email
  /git-send-pack
 +/git-serve
  /git-sh-i18n
  /git-sh-i18n--envsubst
  /git-sh-setup
diff --combined Documentation/config.txt
@@@ -898,6 -898,10 +898,10 @@@ core.notesRef:
  This setting defaults to "refs/notes/commits", and it can be overridden by
  the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].
  
+ core.commitGraph::
+       Enable git commit graph feature. Allows reading from the
+       commit-graph file.
  core.sparseCheckout::
        Enable "sparse checkout" feature. See section "Sparse checkout" in
        linkgit:git-read-tree[1] for more information.
@@@ -1398,16 -1402,7 +1402,16 @@@ fetch.unpackLimit:
  
  fetch.prune::
        If true, fetch will automatically behave as if the `--prune`
 -      option was given on the command line.  See also `remote.<name>.prune`.
 +      option was given on the command line.  See also `remote.<name>.prune`
 +      and the PRUNING section of linkgit:git-fetch[1].
 +
 +fetch.pruneTags::
 +      If true, fetch will automatically behave as if the
 +      `refs/tags/*:refs/tags/*` refspec was provided when pruning,
 +      if not set already. This allows for setting both this option
 +      and `fetch.prune` to maintain a 1=1 mapping to upstream
 +      refs. See also `remote.<name>.pruneTags` and the PRUNING
 +      section of linkgit:git-fetch[1].
  
  fetch.output::
        Control how ref update status is printed. Valid values are
@@@ -1957,7 -1952,6 +1961,7 @@@ http.sslVersion:
        - tlsv1.0
        - tlsv1.1
        - tlsv1.2
 +      - tlsv1.3
  
  +
  Can be overridden by the `GIT_SSL_VERSION` environment variable.
@@@ -2955,15 -2949,6 +2959,15 @@@ remote.<name>.prune:
        remote (as if the `--prune` option was given on the command line).
        Overrides `fetch.prune` settings, if any.
  
 +remote.<name>.pruneTags::
 +      When set to true, fetching from this remote by default will also
 +      remove any local tags that no longer exist on the remote if pruning
 +      is activated in general via `remote.<name>.prune`, `fetch.prune` or
 +      `--prune`. Overrides `fetch.pruneTags` settings, if any.
 ++
 +See also `remote.<name>.prune` and the PRUNING section of
 +linkgit:git-fetch[1].
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
@@@ -3229,8 -3214,7 +3233,8 @@@ submodule.active:
  
  submodule.recurse::
        Specifies if commands recurse into submodules by default. This
 -      applies to all commands that have a `--recurse-submodules` option.
 +      applies to all commands that have a `--recurse-submodules` option,
 +      except `clone`.
        Defaults to false.
  
  submodule.fetchJobs::
@@@ -3365,7 -3349,7 +3369,7 @@@ uploadpack.packObjectsHook:
        stdout.
  
  uploadpack.allowFilter::
 -      If this option is set, `upload-pack` will advertise partial
 +      If this option is set, `upload-pack` will support partial
        clone and partial fetch object filtering.
  +
  Note that this configuration variable is ignored if it is seen in the
diff --combined Makefile
+++ b/Makefile
@@@ -29,10 -29,10 +29,10 @@@ all:
  # Perl-compatible regular expressions instead of standard or extended
  # POSIX regular expressions.
  #
 -# Currently USE_LIBPCRE is a synonym for USE_LIBPCRE1, define
 -# USE_LIBPCRE2 instead if you'd like to use version 2 of the PCRE
 -# library. The USE_LIBPCRE flag will likely be changed to mean v2 by
 -# default in future releases.
 +# USE_LIBPCRE is a synonym for USE_LIBPCRE2, define USE_LIBPCRE1
 +# instead if you'd like to use the legacy version 1 of the PCRE
 +# library. Support for version 1 will likely be removed in some future
 +# release of Git, as upstream has all but abandoned it.
  #
  # When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if the PCRE v1
  # library is compiled without --enable-jit. We will auto-detect
  #
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
 +# Define NO_PERL_CPAN_FALLBACKS if you do not want to install bundled
 +# copies of CPAN modules that serve as a fallback in case the modules
 +# are not available on the system. This option is intended for
 +# distributions that want to use their packaged versions of Perl
 +# modules, instead of the fallbacks shipped with Git.
 +#
  # Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python
  # but /usr/bin/python2.7 on some platforms).
  #
  # when hardlinking a file to another name and unlinking the original file right
  # away (some NTFS drivers seem to zero the contents in that scenario).
  #
 +# Define INSTALL_SYMLINKS if you prefer to have everything that can be
 +# symlinked between bin/ and libexec/ to use relative symlinks between
 +# the two. This option overrides NO_CROSS_DIRECTORY_HARDLINKS and
 +# NO_INSTALL_HARDLINKS which will also use symlinking by indirection
 +# within the same directory in some cases, INSTALL_SYMLINKS will
 +# always symlink to the final target directly.
 +#
  # Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
  # programs as a tar, where bin/ and libexec/ might be on different file systems.
  #
  #
  # When cross-compiling, define HOST_CPU as the canonical name of the CPU on
  # which the built Git will run (for instance "x86_64").
 +#
 +# Define RUNTIME_PREFIX to configure Git to resolve its ancillary tooling and
 +# support files relative to the location of the runtime binary, rather than
 +# hard-coding them into the binary. Git installations built with RUNTIME_PREFIX
 +# can be moved to arbitrary filesystem locations. RUNTIME_PREFIX also causes
 +# Perl scripts to use a modified entry point header allowing them to resolve
 +# support files at runtime.
 +#
 +# When using RUNTIME_PREFIX, define HAVE_BSD_KERN_PROC_SYSCTL if your platform
 +# supports the KERN_PROC BSD sysctl function.
 +#
 +# When using RUNTIME_PREFIX, define PROCFS_EXECUTABLE_PATH if your platform
 +# mounts a "procfs" filesystem capable of resolving the path of the current
 +# executable. If defined, this must be the canonical path for the "procfs"
 +# current executable path.
 +#
 +# When using RUNTIME_PREFIX, define HAVE_NS_GET_EXECUTABLE_PATH if your platform
 +# supports calling _NSGetExecutablePath to retrieve the path of the running
 +# executable.
 +#
 +# When using RUNTIME_PREFIX, define HAVE_WPGMPTR if your platform offers
 +# the global variable _wpgmptr containing the absolute path of the current
 +# executable (this is the case on Windows).
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -501,12 -465,11 +501,12 @@@ ARFLAGS = rc
  #   mandir
  #   infodir
  #   htmldir
 +#   localedir
 +#   perllibdir
  # This can help installing the suite in a relocatable way.
  
  prefix = $(HOME)
 -bindir_relative = bin
 -bindir = $(prefix)/$(bindir_relative)
 +bindir = $(prefix)/bin
  mandir = $(prefix)/share/man
  infodir = $(prefix)/share/info
  gitexecdir = libexec/git-core
@@@ -523,13 -486,9 +523,13 @@@ lib = li
  # DESTDIR =
  pathsep = :
  
 +bindir_relative = $(patsubst $(prefix)/%,%,$(bindir))
  mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
  infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
 +gitexecdir_relative = $(patsubst $(prefix)/%,%,$(gitexecdir))
 +localedir_relative = $(patsubst $(prefix)/%,%,$(localedir))
  htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
 +perllibdir_relative = $(patsubst $(prefix)/%,%,$(perllibdir))
  
  export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
  
@@@ -581,7 -540,6 +581,7 @@@ SCRIPT_PERL 
  SCRIPT_PYTHON =
  SCRIPT_SH =
  SCRIPT_LIB =
 +TEST_BUILTINS_OBJS =
  TEST_PROGRAMS_NEED_X =
  
  # Having this variable in your environment would break pipelines because
@@@ -679,6 -637,7 +679,6 @@@ PROGRAM_OBJS += imap-send.
  PROGRAM_OBJS += sh-i18n--envsubst.o
  PROGRAM_OBJS += shell.o
  PROGRAM_OBJS += show-index.o
 -PROGRAM_OBJS += upload-pack.o
  PROGRAM_OBJS += remote-testsvn.o
  
  # Binary suffix, set to .exe for Windows builds
@@@ -686,50 -645,47 +686,50 @@@ X 
  
  PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
  
 -TEST_PROGRAMS_NEED_X += test-chmtime
 -TEST_PROGRAMS_NEED_X += test-ctype
 -TEST_PROGRAMS_NEED_X += test-config
 -TEST_PROGRAMS_NEED_X += test-date
 -TEST_PROGRAMS_NEED_X += test-delta
 -TEST_PROGRAMS_NEED_X += test-drop-caches
 -TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 +TEST_BUILTINS_OBJS += test-chmtime.o
 +TEST_BUILTINS_OBJS += test-config.o
 +TEST_BUILTINS_OBJS += test-ctype.o
 +TEST_BUILTINS_OBJS += test-date.o
 +TEST_BUILTINS_OBJS += test-delta.o
 +TEST_BUILTINS_OBJS += test-drop-caches.o
 +TEST_BUILTINS_OBJS += test-dump-cache-tree.o
 +TEST_BUILTINS_OBJS += test-dump-split-index.o
 +TEST_BUILTINS_OBJS += test-example-decorate.o
 +TEST_BUILTINS_OBJS += test-genrandom.o
 +TEST_BUILTINS_OBJS += test-hashmap.o
 +TEST_BUILTINS_OBJS += test-index-version.o
 +TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
 +TEST_BUILTINS_OBJS += test-match-trees.o
 +TEST_BUILTINS_OBJS += test-mergesort.o
 +TEST_BUILTINS_OBJS += test-mktemp.o
 +TEST_BUILTINS_OBJS += test-online-cpus.o
 +TEST_BUILTINS_OBJS += test-path-utils.o
 +TEST_BUILTINS_OBJS += test-prio-queue.o
 +TEST_BUILTINS_OBJS += test-read-cache.o
 +TEST_BUILTINS_OBJS += test-ref-store.o
 +TEST_BUILTINS_OBJS += test-regex.o
 +TEST_BUILTINS_OBJS += test-revision-walking.o
 +TEST_BUILTINS_OBJS += test-run-command.o
 +TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 +TEST_BUILTINS_OBJS += test-sha1-array.o
 +TEST_BUILTINS_OBJS += test-sha1.o
 +TEST_BUILTINS_OBJS += test-sigchain.o
 +TEST_BUILTINS_OBJS += test-strcmp-offset.o
 +TEST_BUILTINS_OBJS += test-string-list.o
 +TEST_BUILTINS_OBJS += test-submodule-config.o
 +TEST_BUILTINS_OBJS += test-subprocess.o
 +TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
 +TEST_BUILTINS_OBJS += test-wildmatch.o
 +TEST_BUILTINS_OBJS += test-write-cache.o
 +
  TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
 -TEST_PROGRAMS_NEED_X += test-dump-split-index
  TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 -TEST_PROGRAMS_NEED_X += test-example-decorate
  TEST_PROGRAMS_NEED_X += test-fake-ssh
 -TEST_PROGRAMS_NEED_X += test-genrandom
 -TEST_PROGRAMS_NEED_X += test-hashmap
 -TEST_PROGRAMS_NEED_X += test-index-version
 -TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash
  TEST_PROGRAMS_NEED_X += test-line-buffer
 -TEST_PROGRAMS_NEED_X += test-match-trees
 -TEST_PROGRAMS_NEED_X += test-mergesort
 -TEST_PROGRAMS_NEED_X += test-mktemp
 -TEST_PROGRAMS_NEED_X += test-online-cpus
  TEST_PROGRAMS_NEED_X += test-parse-options
 -TEST_PROGRAMS_NEED_X += test-path-utils
 -TEST_PROGRAMS_NEED_X += test-prio-queue
 -TEST_PROGRAMS_NEED_X += test-read-cache
 -TEST_PROGRAMS_NEED_X += test-write-cache
 -TEST_PROGRAMS_NEED_X += test-ref-store
 -TEST_PROGRAMS_NEED_X += test-regex
 -TEST_PROGRAMS_NEED_X += test-revision-walking
 -TEST_PROGRAMS_NEED_X += test-run-command
 -TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 -TEST_PROGRAMS_NEED_X += test-sha1
 -TEST_PROGRAMS_NEED_X += test-sha1-array
 -TEST_PROGRAMS_NEED_X += test-sigchain
 -TEST_PROGRAMS_NEED_X += test-strcmp-offset
 -TEST_PROGRAMS_NEED_X += test-string-list
 -TEST_PROGRAMS_NEED_X += test-submodule-config
 -TEST_PROGRAMS_NEED_X += test-subprocess
 +TEST_PROGRAMS_NEED_X += test-pkt-line
  TEST_PROGRAMS_NEED_X += test-svn-fe
 -TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
 -TEST_PROGRAMS_NEED_X += test-wildmatch
 +TEST_PROGRAMS_NEED_X += test-tool
  
  TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))
  
@@@ -810,12 -766,12 +810,13 @@@ LIB_OBJS += branch.
  LIB_OBJS += bulk-checkin.o
  LIB_OBJS += bundle.o
  LIB_OBJS += cache-tree.o
 +LIB_OBJS += chdir-notify.o
  LIB_OBJS += checkout.o
  LIB_OBJS += color.o
  LIB_OBJS += column.o
  LIB_OBJS += combine-diff.o
  LIB_OBJS += commit.o
+ LIB_OBJS += commit-graph.o
  LIB_OBJS += compat/obstack.o
  LIB_OBJS += compat/terminal.o
  LIB_OBJS += config.o
@@@ -846,7 -802,7 +847,7 @@@ LIB_OBJS += ewah/bitmap.
  LIB_OBJS += ewah/ewah_bitmap.o
  LIB_OBJS += ewah/ewah_io.o
  LIB_OBJS += ewah/ewah_rlw.o
 -LIB_OBJS += exec_cmd.o
 +LIB_OBJS += exec-cmd.o
  LIB_OBJS += fetch-object.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
@@@ -869,11 -825,9 +870,11 @@@ LIB_OBJS += list-objects-filter-options
  LIB_OBJS += ll-merge.o
  LIB_OBJS += lockfile.o
  LIB_OBJS += log-tree.o
 +LIB_OBJS += ls-refs.o
  LIB_OBJS += mailinfo.o
  LIB_OBJS += mailmap.o
  LIB_OBJS += match-trees.o
 +LIB_OBJS += mem-pool.o
  LIB_OBJS += merge.o
  LIB_OBJS += merge-blobs.o
  LIB_OBJS += merge-recursive.o
@@@ -918,7 -872,7 +919,7 @@@ LIB_OBJS += refs/packed-backend.
  LIB_OBJS += refs/ref-cache.o
  LIB_OBJS += ref-filter.o
  LIB_OBJS += remote.o
 -LIB_OBJS += replace_object.o
 +LIB_OBJS += replace-object.o
  LIB_OBJS += repository.o
  LIB_OBJS += rerere.o
  LIB_OBJS += resolve-undo.o
@@@ -926,13 -880,12 +927,13 @@@ LIB_OBJS += revision.
  LIB_OBJS += run-command.o
  LIB_OBJS += send-pack.o
  LIB_OBJS += sequencer.o
 +LIB_OBJS += serve.o
  LIB_OBJS += server-info.o
  LIB_OBJS += setup.o
  LIB_OBJS += sha1-array.o
  LIB_OBJS += sha1-lookup.o
 -LIB_OBJS += sha1_file.o
 -LIB_OBJS += sha1_name.o
 +LIB_OBJS += sha1-file.o
 +LIB_OBJS += sha1-name.o
  LIB_OBJS += shallow.o
  LIB_OBJS += sideband.o
  LIB_OBJS += sigchain.o
@@@ -955,7 -908,6 +956,7 @@@ LIB_OBJS += tree-diff.
  LIB_OBJS += tree.o
  LIB_OBJS += tree-walk.o
  LIB_OBJS += unpack-trees.o
 +LIB_OBJS += upload-pack.o
  LIB_OBJS += url.o
  LIB_OBJS += urlmatch.o
  LIB_OBJS += usage.o
@@@ -968,7 -920,7 +969,7 @@@ LIB_OBJS += walker.
  LIB_OBJS += wildmatch.o
  LIB_OBJS += worktree.o
  LIB_OBJS += wrapper.o
 -LIB_OBJS += write_or_die.o
 +LIB_OBJS += write-or-die.o
  LIB_OBJS += ws.o
  LIB_OBJS += wt-status.o
  LIB_OBJS += xdiff-interface.o
@@@ -995,6 -947,7 +996,7 @@@ BUILTIN_OBJS += builtin/clone.
  BUILTIN_OBJS += builtin/column.o
  BUILTIN_OBJS += builtin/commit-tree.o
  BUILTIN_OBJS += builtin/commit.o
+ BUILTIN_OBJS += builtin/commit-graph.o
  BUILTIN_OBJS += builtin/config.o
  BUILTIN_OBJS += builtin/count-objects.o
  BUILTIN_OBJS += builtin/credential.o
@@@ -1060,7 -1013,6 +1062,7 @@@ BUILTIN_OBJS += builtin/rev-parse.
  BUILTIN_OBJS += builtin/revert.o
  BUILTIN_OBJS += builtin/rm.o
  BUILTIN_OBJS += builtin/send-pack.o
 +BUILTIN_OBJS += builtin/serve.o
  BUILTIN_OBJS += builtin/shortlog.o
  BUILTIN_OBJS += builtin/show-branch.o
  BUILTIN_OBJS += builtin/show-ref.o
@@@ -1074,7 -1026,6 +1076,7 @@@ BUILTIN_OBJS += builtin/update-index.
  BUILTIN_OBJS += builtin/update-ref.o
  BUILTIN_OBJS += builtin/update-server-info.o
  BUILTIN_OBJS += builtin/upload-archive.o
 +BUILTIN_OBJS += builtin/upload-pack.o
  BUILTIN_OBJS += builtin/var.o
  BUILTIN_OBJS += builtin/verify-commit.o
  BUILTIN_OBJS += builtin/verify-pack.o
@@@ -1215,18 -1166,13 +1217,18 @@@ ifdef NO_LIBGEN_
        COMPAT_OBJS += compat/basename.o
  endif
  
 -USE_LIBPCRE1 ?= $(USE_LIBPCRE)
 +USE_LIBPCRE2 ?= $(USE_LIBPCRE)
  
 -ifneq (,$(USE_LIBPCRE1))
 -      ifdef USE_LIBPCRE2
 -$(error Only set USE_LIBPCRE1 (or its alias USE_LIBPCRE) or USE_LIBPCRE2, not both!)
 +ifneq (,$(USE_LIBPCRE2))
 +      ifdef USE_LIBPCRE1
 +$(error Only set USE_LIBPCRE2 (or its alias USE_LIBPCRE) or USE_LIBPCRE1, not both!)
        endif
  
 +      BASIC_CFLAGS += -DUSE_LIBPCRE2
 +      EXTLIBS += -lpcre2-8
 +endif
 +
 +ifdef USE_LIBPCRE1
        BASIC_CFLAGS += -DUSE_LIBPCRE1
        EXTLIBS += -lpcre
  
@@@ -1235,6 -1181,11 +1237,6 @@@ ifdef NO_LIBPCRE1_JI
  endif
  endif
  
 -ifdef USE_LIBPCRE2
 -      BASIC_CFLAGS += -DUSE_LIBPCRE2
 -      EXTLIBS += -lpcre2-8
 -endif
 -
  ifdef LIBPCREDIR
        BASIC_CFLAGS += -I$(LIBPCREDIR)/include
        EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
@@@ -1697,27 -1648,10 +1699,27 @@@ ifdef HAVE_BSD_SYSCT
        BASIC_CFLAGS += -DHAVE_BSD_SYSCTL
  endif
  
 +ifdef HAVE_BSD_KERN_PROC_SYSCTL
 +      BASIC_CFLAGS += -DHAVE_BSD_KERN_PROC_SYSCTL
 +endif
 +
  ifdef HAVE_GETDELIM
        BASIC_CFLAGS += -DHAVE_GETDELIM
  endif
  
 +ifneq ($(PROCFS_EXECUTABLE_PATH),)
 +      procfs_executable_path_SQ = $(subst ','\'',$(PROCFS_EXECUTABLE_PATH))
 +      BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"'
 +endif
 +
 +ifdef HAVE_NS_GET_EXECUTABLE_PATH
 +      BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH
 +endif
 +
 +ifdef HAVE_WPGMPTR
 +      BASIC_CFLAGS += -DHAVE_WPGMPTR
 +endif
 +
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
@@@ -1802,13 -1736,10 +1804,13 @@@ mandir_relative_SQ = $(subst ','\'',$(m
  infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
  perllibdir_SQ = $(subst ','\'',$(perllibdir))
  localedir_SQ = $(subst ','\'',$(localedir))
 +localedir_relative_SQ = $(subst ','\'',$(localedir_relative))
  gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 +gitexecdir_relative_SQ = $(subst ','\'',$(gitexecdir_relative))
  template_dir_SQ = $(subst ','\'',$(template_dir))
  htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative))
  prefix_SQ = $(subst ','\'',$(prefix))
 +perllibdir_relative_SQ = $(subst ','\'',$(perllibdir_relative))
  gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
@@@ -1819,31 -1750,6 +1821,31 @@@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_
  DIFF_SQ = $(subst ','\'',$(DIFF))
  PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA))
  
 +# RUNTIME_PREFIX's resolution logic requires resource paths to be expressed
 +# relative to each other and share an installation path.
 +#
 +# This is a dependency in:
 +# - Git's binary RUNTIME_PREFIX logic in (see "exec_cmd.c").
 +# - The runtime prefix Perl header (see
 +#   "perl/header_templates/runtime_prefix.template.pl").
 +ifdef RUNTIME_PREFIX
 +
 +ifneq ($(filter /%,$(firstword $(gitexecdir_relative))),)
 +$(error RUNTIME_PREFIX requires a relative gitexecdir, not: $(gitexecdir))
 +endif
 +
 +ifneq ($(filter /%,$(firstword $(localedir_relative))),)
 +$(error RUNTIME_PREFIX requires a relative localedir, not: $(localedir))
 +endif
 +
 +ifndef NO_PERL
 +ifneq ($(filter /%,$(firstword $(perllibdir_relative))),)
 +$(error RUNTIME_PREFIX requires a relative perllibdir, not: $(perllibdir))
 +endif
 +endif
 +
 +endif
 +
  # We must filter out any object files from $(GITLIBS),
  # as it is typically used like:
  #
@@@ -2064,44 -1970,27 +2066,44 @@@ git.res: git.rc GIT-VERSION-FIL
  # This makes sure we depend on the NO_PERL setting itself.
  $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
  
 -ifndef NO_PERL
 -$(SCRIPT_PERL_GEN):
 +# Used for substitution in Perl modules. Disabled when using RUNTIME_PREFIX
 +# since the locale directory is injected.
 +perl_localedir_SQ = $(localedir_SQ)
  
 +ifndef NO_PERL
 +PERL_HEADER_TEMPLATE = perl/header_templates/fixed_prefix.template.pl
  PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ)
 -$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-VERSION-FILE
 +
 +PERL_DEFINES := $(PERL_PATH_SQ) $(PERLLIB_EXTRA_SQ) $(perllibdir_SQ)
 +PERL_DEFINES += $(RUNTIME_PREFIX)
 +
 +# Support Perl runtime prefix. In this mode, a different header is installed
 +# into Perl scripts.
 +ifdef RUNTIME_PREFIX
 +
 +PERL_HEADER_TEMPLATE = perl/header_templates/runtime_prefix.template.pl
 +
 +# Don't export a fixed $(localedir) path; it will be resolved by the Perl header
 +# at runtime.
 +perl_localedir_SQ =
 +
 +endif
 +
 +PERL_DEFINES += $(gitexecdir) $(perllibdir) $(localedir)
 +
 +$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
 -      INSTLIBDIR='$(perllibdir_SQ)' && \
 -      INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
 -      INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
 -          -e '        h' \
 -          -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
 -          -e '        H' \
 -          -e '        x' \
 +          -e '        rGIT-PERL-HEADER' \
 +          -e '        G' \
            -e '}' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
  
 +PERL_DEFINES := $(subst $(space),:,$(PERL_DEFINES))
  GIT-PERL-DEFINES: FORCE
        @FLAGS='$(PERL_DEFINES)'; \
            if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
                echo "$$FLAGS" >$@; \
            fi
  
 +GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
 +      $(QUIET_GEN)$(RM) $@ && \
 +      INSTLIBDIR='$(perllibdir_SQ)' && \
 +      INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
 +      INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
 +      sed -e 's=@@PATHSEP@@=$(pathsep)=g' \
 +          -e "s=@@INSTLIBDIR@@=$$INSTLIBDIR=g" \
 +          -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \
 +          -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \
 +          -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \
 +          $< >$@+ && \
 +      mv $@+ $@
 +
 +.PHONY: perllibdir
 +perllibdir:
 +      @echo '$(perllibdir_SQ)'
  
  .PHONY: gitweb
  gitweb:
@@@ -2206,7 -2079,7 +2208,7 @@@ VCSSVN_OBJS += vcs-svn/fast_export.
  VCSSVN_OBJS += vcs-svn/svndiff.o
  VCSSVN_OBJS += vcs-svn/svndump.o
  
 -TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS))
 +TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
  OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
  $(OBJECTS): $(LIB_H)
  endif
  
 -exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX
 -exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
 +exec-cmd.sp exec-cmd.s exec-cmd.o: GIT-PREFIX
 +exec-cmd.sp exec-cmd.s exec-cmd.o: EXTRA_CPPFLAGS = \
        '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
 +      '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \
        '-DBINDIR="$(bindir_relative_SQ)"' \
        '-DPREFIX="$(prefix_SQ)"'
  
@@@ -2287,13 -2159,11 +2289,13 @@@ attr.sp attr.s attr.o: EXTRA_CPPFLAGS 
  
  gettext.sp gettext.s gettext.o: GIT-PREFIX
  gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
 -      -DGIT_LOCALE_PATH='"$(localedir_SQ)"'
 +      -DGIT_LOCALE_PATH='"$(localedir_relative_SQ)"'
  
  http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
  
 +pack-revindex.sp: SPARSE_FLAGS += -Wno-memcpy-max-count
 +
  ifdef NO_EXPAT
  http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
  endif
@@@ -2348,15 -2218,13 +2350,15 @@@ $(VCSSVN_LIB): $(VCSSVN_OBJS
  
  export DEFAULT_EDITOR DEFAULT_PAGER
  
 -.PHONY: doc man html info pdf
 -doc:
 +.PHONY: doc man man-perl html info pdf
 +doc: man-perl
        $(MAKE) -C Documentation all
  
 -man:
 +man: man-perl
        $(MAKE) -C Documentation man
  
 +man-perl: perl/build/man/man3/Git.3pm
 +
  html:
        $(MAKE) -C Documentation html
  
@@@ -2434,22 -2302,14 +2436,22 @@@ po/build/locale/%/LC_MESSAGES/git.mo: p
  
  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))
 +LIB_CPAN := $(wildcard perl/FromCPAN/*.pm perl/FromCPAN/*/*.pm)
 +LIB_CPAN_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_CPAN))
  
  ifndef NO_PERL
  all:: $(LIB_PERL_GEN)
 +ifndef NO_PERL_CPAN_FALLBACKS
 +all:: $(LIB_CPAN_GEN)
 +endif
 +NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS))
  endif
  
  perl/build/lib/%.pm: perl/%.pm
        $(QUIET_GEN)mkdir -p $(dir $@) && \
 -      sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' < $< > $@
 +      sed -e 's|@@LOCALEDIR@@|$(perl_localedir_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 $@) && \
@@@ -2618,12 -2478,10 +2620,12 @@@ t/helper/test-svn-fe$X: $(VCSSVN_LIB
  
  .PRECIOUS: $(TEST_OBJS)
  
 +t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
 +
  t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
  
 -check-sha1:: t/helper/test-sha1$X
 +check-sha1:: t/helper/test-tool$X
        t/helper/test-sha1.sh
  
  SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
@@@ -2732,48 -2590,39 +2734,48 @@@ endi
  
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
 +      destdir_from_execdir_SQ=$$(echo '$(gitexecdir_relative_SQ)' | sed -e 's|[^/][^/]*|..|g') && \
        { test "$$bindir/" = "$$execdir/" || \
          for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
                $(RM) "$$execdir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
 -              ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
 -              cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "$$destdir_from_execdir_SQ/$(bindir_relative_SQ)/$$p" "$$execdir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
 +                ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
 +                cp "$$bindir/$$p" "$$execdir/$$p" || exit; } \
          done; \
        } && \
        for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
                $(RM) "$$bindir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)" && \
 -              ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
 -              ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
 -              cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "git$X" "$$bindir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)" && \
 +                ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
 +                ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
 +                cp "$$bindir/git$X" "$$bindir/$$p" || exit; } \
        done && \
        for p in $(BUILT_INS); do \
                $(RM) "$$execdir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)" && \
 -              ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
 -              ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 -              cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "$$destdir_from_execdir_SQ/$(bindir_relative_SQ)/git$X" "$$execdir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)" && \
 +                ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
 +                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 +                cp "$$execdir/git$X" "$$execdir/$$p" || exit; } \
        done && \
        remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
        for p in $$remote_curl_aliases; do \
                $(RM) "$$execdir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)" && \
 -              ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 -              ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 -              cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "git-remote-http$X" "$$execdir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)" && \
 +                ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 +                ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 +                cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; } \
        done && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
  
 -.PHONY: install-gitweb install-doc install-man install-html install-info install-pdf
 +.PHONY: install-gitweb install-doc install-man install-man-perl install-html install-info install-pdf
  .PHONY: quick-install-doc quick-install-man quick-install-html
  install-gitweb:
        $(MAKE) -C gitweb install
@@@ -2784,7 -2633,7 +2786,7 @@@ install-doc: install-man-per
  install-man: install-man-perl
        $(MAKE) -C Documentation install-man
  
 -install-man-perl: perl/build/man/man3/Git.3pm
 +install-man-perl: man-perl
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
        (cd perl/build/man/man3 && $(TAR) cf - .) | \
        (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
@@@ -2887,7 -2736,7 +2889,7 @@@ clean: profile-clean coverage-clea
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
 -      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
 +      $(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
@@@ -2905,7 -2754,7 +2907,7 @@@ ifndef NO_TCLT
  endif
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS
        $(RM) GIT-USER-AGENT GIT-PREFIX
 -      $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PYTHON-VARS
 +      $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
  
  .PHONY: all install profile-clean clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --combined builtin.h
+++ b/builtin.h
@@@ -149,6 -149,7 +149,7 @@@ extern int cmd_clone(int argc, const ch
  extern int cmd_clean(int argc, const char **argv, const char *prefix);
  extern int cmd_column(int argc, const char **argv, const char *prefix);
  extern int cmd_commit(int argc, const char **argv, const char *prefix);
+ extern int cmd_commit_graph(int argc, const char **argv, const char *prefix);
  extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_config(int argc, const char **argv, const char *prefix);
  extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
@@@ -215,7 -216,6 +216,7 @@@ extern int cmd_rev_parse(int argc, cons
  extern int cmd_revert(int argc, const char **argv, const char *prefix);
  extern int cmd_rm(int argc, const char **argv, const char *prefix);
  extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
 +extern int cmd_serve(int argc, const char **argv, const char *prefix);
  extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
  extern int cmd_show(int argc, const char **argv, const char *prefix);
  extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
@@@ -232,7 -232,6 +233,7 @@@ extern int cmd_update_ref(int argc, con
  extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
  extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
  extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
 +extern int cmd_upload_pack(int argc, const char **argv, const char *prefix);
  extern int cmd_var(int argc, const char **argv, const char *prefix);
  extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
  extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
diff --combined builtin/index-pack.c
@@@ -9,11 -9,10 +9,11 @@@
  #include "tree.h"
  #include "progress.h"
  #include "fsck.h"
 -#include "exec_cmd.h"
 +#include "exec-cmd.h"
  #include "streaming.h"
  #include "thread-utils.h"
  #include "packfile.h"
 +#include "object-store.h"
  
  static const char index_pack_usage[] =
  "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@@ -50,7 -49,6 +50,7 @@@ struct thread_local 
        int pack_fd;
  };
  
 +/* Remember to update object flag allocation in object.h */
  #define FLAG_LINK (1u<<20)
  #define FLAG_CHECKED (1u<<21)
  
@@@ -60,7 -58,7 +60,7 @@@ struct ofs_delta_entry 
  };
  
  struct ref_delta_entry {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int obj_no;
  };
  
@@@ -223,14 -221,14 +223,14 @@@ static unsigned check_object(struct obj
  
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
 -              int type = sha1_object_info(obj->oid.hash, &size);
 +              int type = oid_object_info(&obj->oid, &size);
                if (type <= 0)
                        die(_("did not receive expected object %s"),
                              oid_to_hex(&obj->oid));
                if (type != obj->type)
                        die(_("object %s: expected type %s, found %s"),
                            oid_to_hex(&obj->oid),
 -                          typename(obj->type), typename(type));
 +                          type_name(obj->type), type_name(type));
                obj->flags |= FLAG_CHECKED;
                return 1;
        }
@@@ -450,7 -448,7 +450,7 @@@ static void *unpack_entry_data(off_t of
        int hdrlen;
  
        if (!is_delta_type(type)) {
 -              hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1;
 +              hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), size) + 1;
                the_hash_algo->init_fn(&c);
                the_hash_algo->update_fn(&c, hdr, hdrlen);
        } else
@@@ -673,18 -671,18 +673,18 @@@ static void find_ofs_delta_children(off
        *last_index = last;
  }
  
 -static int compare_ref_delta_bases(const unsigned char *sha1,
 -                                 const unsigned char *sha2,
 +static int compare_ref_delta_bases(const struct object_id *oid1,
 +                                 const struct object_id *oid2,
                                   enum object_type type1,
                                   enum object_type type2)
  {
        int cmp = type1 - type2;
        if (cmp)
                return cmp;
 -      return hashcmp(sha1, sha2);
 +      return oidcmp(oid1, oid2);
  }
  
 -static int find_ref_delta(const unsigned char *sha1, enum object_type type)
 +static int find_ref_delta(const struct object_id *oid, enum object_type type)
  {
        int first = 0, last = nr_ref_deltas;
  
                struct ref_delta_entry *delta = &ref_deltas[next];
                int cmp;
  
 -              cmp = compare_ref_delta_bases(sha1, delta->sha1,
 +              cmp = compare_ref_delta_bases(oid, &delta->oid,
                                              type, objects[delta->obj_no].type);
                if (!cmp)
                        return next;
        return -first-1;
  }
  
 -static void find_ref_delta_children(const unsigned char *sha1,
 +static void find_ref_delta_children(const struct object_id *oid,
                                    int *first_index, int *last_index,
                                    enum object_type type)
  {
 -      int first = find_ref_delta(sha1, type);
 +      int first = find_ref_delta(oid, type);
        int last = first;
        int end = nr_ref_deltas - 1;
  
                *last_index = -1;
                return;
        }
 -      while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1))
 +      while (first > 0 && !oidcmp(&ref_deltas[first - 1].oid, oid))
                --first;
 -      while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1))
 +      while (last < end && !oidcmp(&ref_deltas[last + 1].oid, oid))
                ++last;
        *first_index = first;
        *last_index = last;
@@@ -773,7 -771,7 +773,7 @@@ static int check_collison(struct object
  
        memset(&data, 0, sizeof(data));
        data.entry = entry;
 -      data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL);
 +      data.st = open_istream(&entry->idx.oid, &type, &size, NULL);
        if (!data.st)
                return -1;
        if (size != entry->size || type != entry->type)
@@@ -812,12 -810,12 +812,12 @@@ static void sha1_object(const void *dat
                enum object_type has_type;
                unsigned long has_size;
                read_lock();
 -              has_type = sha1_object_info(oid->hash, &has_size);
 +              has_type = oid_object_info(oid, &has_size);
                if (has_type < 0)
                        die(_("cannot read existing object info %s"), oid_to_hex(oid));
                if (has_type != type || has_size != size)
                        die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
 -              has_data = read_sha1_file(oid->hash, &has_type, &has_size);
 +              has_data = read_object_file(oid, &has_type, &has_size);
                read_unlock();
                if (!data)
                        data = new_data = get_data_from_pack(obj_entry);
                free(has_data);
        }
  
 -      if (strict) {
 +      if (strict || do_fsck_object) {
                read_lock();
                if (type == OBJ_BLOB) {
                        struct blob *blob = lookup_blob(oid);
                        obj = parse_object_buffer(oid, type, size, buf,
                                                  &eaten);
                        if (!obj)
 -                              die(_("invalid %s"), typename(type));
 +                              die(_("invalid %s"), type_name(type));
                        if (do_fsck_object &&
                            fsck_object(obj, buf, size, &fsck_options))
                                die(_("Error in object"));
 -                      if (fsck_walk(obj, NULL, &fsck_options))
 +                      if (strict && fsck_walk(obj, NULL, &fsck_options))
                                die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
  
                        if (obj->type == OBJ_TREE) {
@@@ -961,7 -959,7 +961,7 @@@ static void resolve_delta(struct object
        if (!result->data)
                bad_object(delta_obj->idx.offset, _("failed to apply delta"));
        hash_object_file(result->data, result->size,
 -                       typename(delta_obj->real_type), &delta_obj->idx.oid);
 +                       type_name(delta_obj->real_type), &delta_obj->idx.oid);
        sha1_object(result->data, NULL, result->size, delta_obj->real_type,
                    &delta_obj->idx.oid);
        counter_lock();
@@@ -993,7 -991,7 +993,7 @@@ static struct base_data *find_unresolve
                                                  struct base_data *prev_base)
  {
        if (base->ref_last == -1 && base->ofs_last == -1) {
 -              find_ref_delta_children(base->obj->idx.oid.hash,
 +              find_ref_delta_children(&base->obj->idx.oid,
                                        &base->ref_first, &base->ref_last,
                                        OBJ_REF_DELTA);
  
@@@ -1077,7 -1075,7 +1077,7 @@@ static int compare_ref_delta_entry(cons
        const struct ref_delta_entry *delta_a = a;
        const struct ref_delta_entry *delta_b = b;
  
 -      return hashcmp(delta_a->sha1, delta_b->sha1);
 +      return oidcmp(&delta_a->oid, &delta_b->oid);
  }
  
  static void resolve_base(struct object_entry *obj)
@@@ -1143,7 -1141,7 +1143,7 @@@ static void parse_pack_objects(unsigne
                        ofs_delta++;
                } else if (obj->type == OBJ_REF_DELTA) {
                        ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
 -                      hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_oid.hash);
 +                      oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid);
                        ref_deltas[nr_ref_deltas].obj_no = i;
                        nr_ref_deltas++;
                } else if (!data) {
@@@ -1271,7 -1269,7 +1271,7 @@@ static void conclude_pack(int fix_thin_
                            nr_objects - nr_objects_initial);
                stop_progress_msg(&progress, msg.buf);
                strbuf_release(&msg);
-               hashclose(f, tail_hash, 0);
+               finalize_hashfile(f, tail_hash, 0);
                hashcpy(read_hash, pack_hash);
                fixup_pack_header_footer(output_fd, pack_hash,
                                         curr_pack, nr_objects,
@@@ -1375,15 -1373,14 +1375,15 @@@ static void fix_unresolved_deltas(struc
  
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
                        continue;
 -              base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size);
 +              base_obj->data = read_object_file(&d->oid, &type,
 +                                                &base_obj->size);
                if (!base_obj->data)
                        continue;
  
 -              if (check_sha1_signature(d->sha1, base_obj->data,
 -                              base_obj->size, typename(type)))
 -                      die(_("local object %s is corrupt"), sha1_to_hex(d->sha1));
 -              base_obj->obj = append_obj_to_pack(f, d->sha1,
 +              if (check_object_signature(&d->oid, base_obj->data,
 +                              base_obj->size, type_name(type)))
 +                      die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
 +              base_obj->obj = append_obj_to_pack(f, d->oid.hash,
                                        base_obj->data, base_obj->size, type);
                find_unresolved_deltas(base_obj);
                display_progress(progress, nr_resolved_deltas);
@@@ -1593,7 -1590,7 +1593,7 @@@ static void read_idx_option(struct pack
        /*
         * Get rid of the idx file as we do not need it anymore.
         * NEEDSWORK: extract this bit from free_pack_by_name() in
 -       * sha1_file.c, perhaps?  It shouldn't matter very much as we
 +       * sha1-file.c, perhaps?  It shouldn't matter very much as we
         * know we haven't installed this pack (hence we never have
         * read anything from it).
         */
@@@ -1618,7 -1615,7 +1618,7 @@@ static void show_pack_info(int stat_onl
                        continue;
                printf("%s %-6s %lu %lu %"PRIuMAX,
                       oid_to_hex(&obj->idx.oid),
 -                     typename(obj->real_type), obj->size,
 +                     type_name(obj->real_type), obj->size,
                       (unsigned long)(obj[1].idx.offset - obj->idx.offset),
                       (uintmax_t)obj->idx.offset);
                if (is_delta_type(obj->type)) {
@@@ -1691,8 -1688,6 +1691,8 @@@ int cmd_index_pack(int argc, const cha
                        } else if (!strcmp(arg, "--check-self-contained-and-connected")) {
                                strict = 1;
                                check_self_contained_and_connected = 1;
 +                      } else if (!strcmp(arg, "--fsck-objects")) {
 +                              do_fsck_object = 1;
                        } else if (!strcmp(arg, "--verify")) {
                                verify = 1;
                        } else if (!strcmp(arg, "--verify-stat")) {
diff --combined builtin/pack-objects.c
@@@ -1,6 -1,5 +1,6 @@@
  #include "builtin.h"
  #include "cache.h"
 +#include "repository.h"
  #include "config.h"
  #include "attr.h"
  #include "object.h"
@@@ -29,7 -28,6 +29,7 @@@
  #include "argv-array.h"
  #include "list.h"
  #include "packfile.h"
 +#include "object-store.h"
  
  static const char *pack_usage[] = {
        N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
@@@ -124,10 -122,11 +124,10 @@@ static void *get_delta(struct object_en
        void *buf, *base_buf, *delta_buf;
        enum object_type type;
  
 -      buf = read_sha1_file(entry->idx.oid.hash, &type, &size);
 +      buf = read_object_file(&entry->idx.oid, &type, &size);
        if (!buf)
                die("unable to read %s", oid_to_hex(&entry->idx.oid));
 -      base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type,
 -                                &base_size);
 +      base_buf = read_object_file(&entry->delta->idx.oid, &type, &base_size);
        if (!base_buf)
                die("unable to read %s",
                    oid_to_hex(&entry->delta->idx.oid));
@@@ -268,10 -267,11 +268,10 @@@ static unsigned long write_no_reuse_obj
        if (!usable_delta) {
                if (entry->type == OBJ_BLOB &&
                    entry->size > big_file_threshold &&
 -                  (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL)
 +                  (st = open_istream(&entry->idx.oid, &type, &size, NULL)) != NULL)
                        buf = NULL;
                else {
 -                      buf = read_sha1_file(entry->idx.oid.hash, &type,
 -                                           &size);
 +                      buf = read_object_file(&entry->idx.oid, &type, &size);
                        if (!buf)
                                die(_("unable to read %s"),
                                    oid_to_hex(&entry->idx.oid));
@@@ -837,11 -837,11 +837,11 @@@ static void write_pack_file(void
                 * If so, rewrite it like in fast-import
                 */
                if (pack_to_stdout) {
-                       hashclose(f, oid.hash, CSUM_CLOSE);
+                       finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE);
                } else if (nr_written == nr_remaining) {
-                       hashclose(f, oid.hash, CSUM_FSYNC);
+                       finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
                } else {
-                       int fd = hashclose(f, oid.hash, 0);
+                       int fd = finalize_hashfile(f, oid.hash, 0);
                        fixup_pack_header_footer(fd, oid.hash, pack_tmp_name,
                                                 nr_written, oid.hash, offset);
                        close(fd);
@@@ -1025,7 -1025,8 +1025,7 @@@ static int want_object_in_pack(const st
                if (want != -1)
                        return want;
        }
 -
 -      list_for_each(pos, &packed_git_mru) {
 +      list_for_each(pos, get_packed_git_mru(the_repository)) {
                struct packed_git *p = list_entry(pos, struct packed_git, mru);
                off_t offset;
  
                        }
                        want = want_found_object(exclude, p);
                        if (!exclude && want > 0)
 -                              list_move(&p->mru, &packed_git_mru);
 +                              list_move(&p->mru,
 +                                        get_packed_git_mru(the_repository));
                        if (want != -1)
                                return want;
                }
@@@ -1190,7 -1190,7 +1190,7 @@@ static struct pbase_tree_cache *pbase_t
        /* Did not find one.  Either we got a bogus request or
         * we need to read and perhaps cache.
         */
 -      data = read_sha1_file(oid->hash, &type, &size);
 +      data = read_object_file(oid, &type, &size);
        if (!data)
                return NULL;
        if (type != OBJ_TREE) {
@@@ -1351,7 -1351,7 +1351,7 @@@ static void add_preferred_base(struct o
        if (window <= num_preferred_base++)
                return;
  
 -      data = read_object_with_reference(oid->hash, tree_type, &size, tree_oid.hash);
 +      data = read_object_with_reference(oid, tree_type, &size, &tree_oid);
        if (!data)
                return;
  
@@@ -1379,10 -1379,10 +1379,10 @@@ static void cleanup_preferred_base(void
        it = pbase_tree;
        pbase_tree = NULL;
        while (it) {
 -              struct pbase_tree *this = it;
 -              it = this->next;
 -              free(this->pcache.tree_data);
 -              free(this);
 +              struct pbase_tree *tmp = it;
 +              it = tmp->next;
 +              free(tmp->pcache.tree_data);
 +              free(tmp);
        }
  
        for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
@@@ -1516,7 -1516,7 +1516,7 @@@ static void check_object(struct object_
                unuse_pack(&w_curs);
        }
  
 -      entry->type = sha1_object_info(entry->idx.oid.hash, &entry->size);
 +      entry->type = oid_object_info(&entry->idx.oid, &entry->size);
        /*
         * The error condition is checked in prepare_pack().  This is
         * to permit a missing preferred base object to be ignored
@@@ -1578,7 -1578,8 +1578,7 @@@ static void drop_reused_delta(struct ob
                 * And if that fails, the error will be recorded in entry->type
                 * and dealt with in prepare_pack().
                 */
 -              entry->type = sha1_object_info(entry->idx.oid.hash,
 -                                             &entry->size);
 +              entry->type = oid_object_info(&entry->idx.oid, &entry->size);
        }
  }
  
@@@ -1870,7 -1871,8 +1870,7 @@@ static int try_delta(struct unpacked *t
        /* Load data if not already done */
        if (!trg->data) {
                read_lock();
 -              trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type,
 -                                         &sz);
 +              trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
                read_unlock();
                if (!trg->data)
                        die("object %s cannot be read",
        }
        if (!src->data) {
                read_lock();
 -              src->data = read_sha1_file(src_entry->idx.oid.hash, &type,
 -                                         &sz);
 +              src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
                read_unlock();
                if (!src->data) {
                        if (src_entry->preferred_base) {
@@@ -2546,7 -2549,6 +2546,7 @@@ static void read_object_list_from_stdin
        }
  }
  
 +/* Remember to update object flag allocation in object.h */
  #define OBJECT_ADDED (1u<<20)
  
  static void show_commit(struct commit *commit, void *data)
@@@ -2671,7 -2673,7 +2671,7 @@@ static void add_objects_in_unpacked_pac
  
        memset(&in_pack, 0, sizeof(in_pack));
  
 -      for (p = packed_git; p; p = p->next) {
 +      for (p = get_packed_git(the_repository); p; p = p->next) {
                struct object_id oid;
                struct object *o;
  
  static int add_loose_object(const struct object_id *oid, const char *path,
                            void *data)
  {
 -      enum object_type type = sha1_object_info(oid->hash, NULL);
 +      enum object_type type = oid_object_info(oid, NULL);
  
        if (type < 0) {
                warning("loose object at %s could not be examined", path);
@@@ -2734,8 -2736,7 +2734,8 @@@ static int has_sha1_pack_kept_or_nonloc
        static struct packed_git *last_found = (void *)1;
        struct packed_git *p;
  
 -      p = (last_found != (void *)1) ? last_found : packed_git;
 +      p = (last_found != (void *)1) ? last_found :
 +                                      get_packed_git(the_repository);
  
        while (p) {
                if ((!p->pack_local || p->pack_keep) &&
                        return 1;
                }
                if (p == last_found)
 -                      p = packed_git;
 +                      p = get_packed_git(the_repository);
                else
                        p = p->next;
                if (p == last_found)
@@@ -2780,7 -2781,7 +2780,7 @@@ static void loosen_unused_packed_object
        uint32_t i;
        struct object_id oid;
  
 -      for (p = packed_git; p; p = p->next) {
 +      for (p = get_packed_git(the_repository); p; p = p->next) {
                if (!p->pack_local || p->pack_keep)
                        continue;
  
@@@ -3148,9 -3149,10 +3148,9 @@@ int cmd_pack_objects(int argc, const ch
        if (progress && all_progress_implied)
                progress = 2;
  
 -      prepare_packed_git();
        if (ignore_packed_keep) {
                struct packed_git *p;
 -              for (p = packed_git; p; p = p->next)
 +              for (p = get_packed_git(the_repository); p; p = p->next)
                        if (p->pack_local && p->pack_keep)
                                break;
                if (!p) /* no keep-able packs found */
                 * also covers non-local objects
                 */
                struct packed_git *p;
 -              for (p = packed_git; p; p = p->next) {
 +              for (p = get_packed_git(the_repository); p; p = p->next) {
                        if (!p->pack_local) {
                                have_non_local_packs = 1;
                                break;
diff --combined bulk-checkin.c
@@@ -3,7 -3,6 +3,7 @@@
   */
  #include "cache.h"
  #include "bulk-checkin.h"
 +#include "repository.h"
  #include "csum-file.h"
  #include "pack.h"
  #include "strbuf.h"
@@@ -36,9 -35,9 +36,9 @@@ static void finish_bulk_checkin(struct 
                unlink(state->pack_tmp_name);
                goto clear_exit;
        } else if (state->nr_written == 1) {
-               hashclose(state->f, oid.hash, CSUM_FSYNC);
+               finalize_hashfile(state->f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
        } else {
-               int fd = hashclose(state->f, oid.hash, 0);
+               int fd = finalize_hashfile(state->f, oid.hash, 0);
                fixup_pack_header_footer(fd, oid.hash, state->pack_tmp_name,
                                         state->nr_written, oid.hash,
                                         state->offset);
@@@ -58,20 -57,20 +58,20 @@@ clear_exit
  
        strbuf_release(&packname);
        /* Make objects we just wrote available to ourselves */
 -      reprepare_packed_git();
 +      reprepare_packed_git(the_repository);
  }
  
 -static int already_written(struct bulk_checkin_state *state, unsigned char sha1[])
 +static int already_written(struct bulk_checkin_state *state, struct object_id *oid)
  {
        int i;
  
        /* The object may already exist in the repository */
 -      if (has_sha1_file(sha1))
 +      if (has_sha1_file(oid->hash))
                return 1;
  
        /* Might want to keep the list sorted */
        for (i = 0; i < state->nr_written; i++)
 -              if (!hashcmp(state->written[i]->oid.hash, sha1))
 +              if (!oidcmp(&state->written[i]->oid, oid))
                        return 1;
  
        /* This is a new object we need to keep */
@@@ -187,7 -186,7 +187,7 @@@ static void prepare_to_stream(struct bu
  }
  
  static int deflate_to_pack(struct bulk_checkin_state *state,
 -                         unsigned char result_sha1[],
 +                         struct object_id *result_oid,
                           int fd, size_t size,
                           enum object_type type, const char *path,
                           unsigned flags)
                return error("cannot find the current offset");
  
        header_len = xsnprintf((char *)obuf, sizeof(obuf), "%s %" PRIuMAX,
 -                             typename(type), (uintmax_t)size) + 1;
 +                             type_name(type), (uintmax_t)size) + 1;
        the_hash_algo->init_fn(&ctx);
        the_hash_algo->update_fn(&ctx, obuf, header_len);
  
                if (lseek(fd, seekback, SEEK_SET) == (off_t) -1)
                        return error("cannot seek back");
        }
 -      the_hash_algo->final_fn(result_sha1, &ctx);
 +      the_hash_algo->final_fn(result_oid->hash, &ctx);
        if (!idx)
                return 0;
  
        idx->crc32 = crc32_end(state->f);
 -      if (already_written(state, result_sha1)) {
 +      if (already_written(state, result_oid)) {
                hashfile_truncate(state->f, &checkpoint);
                state->offset = checkpoint.offset;
                free(idx);
        } else {
 -              hashcpy(idx->oid.hash, result_sha1);
 +              oidcpy(&idx->oid, result_oid);
                ALLOC_GROW(state->written,
                           state->nr_written + 1,
                           state->alloc_written);
        return 0;
  }
  
 -int index_bulk_checkin(unsigned char *sha1,
 +int index_bulk_checkin(struct object_id *oid,
                       int fd, size_t size, enum object_type type,
                       const char *path, unsigned flags)
  {
 -      int status = deflate_to_pack(&state, sha1, fd, size, type,
 +      int status = deflate_to_pack(&state, oid, fd, size, type,
                                     path, flags);
        if (!state.plugged)
                finish_bulk_checkin(&state);
diff --combined cache.h
+++ b/cache.h
@@@ -428,7 -428,6 +428,7 @@@ static inline enum object_type object_t
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
 +#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
  
  /*
   * Environment variable used in handshaking the wire protocol.
   */
  extern const char * const local_repo_env[];
  
 -extern void setup_git_env(void);
 +extern void setup_git_env(const char *git_dir);
  
  /*
   * Returns true iff we have a configured git repository (either via
@@@ -478,7 -477,7 +478,7 @@@ extern const char *get_git_common_dir(v
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
 -extern int set_git_dir(const char *path);
 +extern void set_git_dir(const char *path);
  extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
  extern const char *get_git_namespace(void);
@@@ -600,7 -599,6 +600,7 @@@ extern int read_index_unmerged(struct i
  
  /* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 +#define SKIP_IF_UNCHANGED     (1 << 1)
  
  /*
   * Write the index while holding an already-taken lock. Close the lock,
   * With `COMMIT_LOCK`, the lock is always committed or rolled back.
   * Without it, the lock is closed, but neither committed nor rolled
   * back.
 + *
 + * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
 + * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
   */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  
@@@ -806,6 -801,7 +806,7 @@@ extern char *git_replace_ref_base
  
  extern int fsync_object_files;
  extern int core_preload_index;
+ extern int core_commit_graph;
  extern int core_apply_sparse_checkout;
  extern int precomposed_unicode;
  extern int protect_hfs;
@@@ -941,6 -937,12 +942,6 @@@ extern void check_repository_format(voi
  #define DATA_CHANGED    0x0020
  #define TYPE_CHANGED    0x0040
  
 -/*
 - * Put in `buf` the name of the file in the local object database that
 - * would be used to store a loose object with the specified sha1.
 - */
 -extern void sha1_file_name(struct strbuf *buf, const unsigned char *sha1);
 -
  /*
   * Return an abbreviated sha1 unique within this repository's object database.
   * The result will be at least `len` characters long, and will be NUL
   * more calls to find_unique_abbrev are made.
   *
   * The `_r` variant writes to a buffer supplied by the caller, which must be at
 - * least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
 + * least `GIT_MAX_HEXSZ + 1` bytes. The return value is the number of bytes
   * written (excluding the NUL terminator).
   *
   * Note that while this version avoids the static buffer, it is not fully
   * reentrant, as it calls into other non-reentrant git code.
   */
 -extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
 -extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
 +extern const char *find_unique_abbrev(const struct object_id *oid, int len);
 +extern int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len);
  
  extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
  extern const struct object_id null_oid;
@@@ -1184,19 -1186,19 +1185,19 @@@ extern char *xdg_config_home(const cha
   */
  extern char *xdg_cache_home(const char *filename);
  
 -extern void *read_sha1_file_extended(const unsigned char *sha1,
 -                                   enum object_type *type,
 -                                   unsigned long *size, int lookup_replace);
 -static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 +extern void *read_object_file_extended(const struct object_id *oid,
 +                                     enum object_type *type,
 +                                     unsigned long *size, int lookup_replace);
 +static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
  {
 -      return read_sha1_file_extended(sha1, type, size, 1);
 +      return read_object_file_extended(oid, type, size, 1);
  }
  
  /*
   * This internal function is only declared here for the benefit of
   * lookup_replace_object().  Please do not call it directly.
   */
 -extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 +extern const struct object_id *do_lookup_replace_object(const struct object_id *oid);
  
  /*
   * If object sha1 should be replaced, return the replacement object's
   * either sha1 or a pointer to a permanently-allocated value.  When
   * object replacement is suppressed, always return sha1.
   */
 -static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
 +static inline const struct object_id *lookup_replace_object(const struct object_id *oid)
  {
        if (!check_replace_refs)
 -              return sha1;
 -      return do_lookup_replace_object(sha1);
 +              return oid;
 +      return do_lookup_replace_object(oid);
  }
  
 -/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 -extern int sha1_object_info(const unsigned char *, unsigned long *);
 +/* Read and unpack an object file into memory, write memory to an object file */
 +extern int oid_object_info(const struct object_id *, unsigned long *);
  
  extern int hash_object_file(const void *buf, unsigned long len,
                            const char *type, struct object_id *oid);
@@@ -1231,22 -1233,23 +1232,22 @@@ extern int force_object_loose(const str
  
  extern int git_open_cloexec(const char *name, int flags);
  #define git_open(name) git_open_cloexec(name, O_RDONLY)
 -extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
 -extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 +extern int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
  
  extern int finalize_object_file(const char *tmpfile, const char *filename);
  
  /*
 - * Open the loose object at path, check its sha1, and return the contents,
 + * Open the loose object at path, check its hash, and return the contents,
   * type, and size. If the object is a blob, then "contents" may return NULL,
   * to allow streaming of large blobs.
   *
   * Returns 0 on success, negative on error (details may be written to stderr).
   */
  int read_loose_object(const char *path,
 -                    const unsigned char *expected_sha1,
 +                    const struct object_id *expected_oid,
                      enum object_type *type,
                      unsigned long *size,
                      void **contents);
@@@ -1273,7 -1276,7 +1274,7 @@@ extern int has_object_file_with_flags(c
   */
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
 +extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
  
  /* Helper to check and "touch" a file */
  extern int check_and_freshen_file(const char *fn, int freshen);
@@@ -1429,10 -1432,10 +1430,10 @@@ extern int df_name_compare(const char *
  extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
  extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
  
 -extern void *read_object_with_reference(const unsigned char *sha1,
 +extern void *read_object_with_reference(const struct object_id *oid,
                                        const char *required_type,
                                        unsigned long *size,
 -                                      unsigned char *sha1_ret);
 +                                      struct object_id *oid_ret);
  
  extern struct object *peel_to_type(const char *name, int namelen,
                                   struct object *o, enum object_type);
@@@ -1558,6 -1561,57 +1559,6 @@@ extern int has_dirs_only_path(const cha
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
 -extern struct alternate_object_database {
 -      struct alternate_object_database *next;
 -
 -      /* see alt_scratch_buf() */
 -      struct strbuf scratch;
 -      size_t base_len;
 -
 -      /*
 -       * Used to store the results of readdir(3) calls when searching
 -       * for unique abbreviated hashes.  This cache is never
 -       * invalidated, thus it's racy and not necessarily accurate.
 -       * That's fine for its purpose; don't use it for tasks requiring
 -       * greater accuracy!
 -       */
 -      char loose_objects_subdir_seen[256];
 -      struct oid_array loose_objects_cache;
 -
 -      char path[FLEX_ARRAY];
 -} *alt_odb_list;
 -extern void prepare_alt_odb(void);
 -extern char *compute_alternate_path(const char *path, struct strbuf *err);
 -typedef int alt_odb_fn(struct alternate_object_database *, void *);
 -extern int foreach_alt_odb(alt_odb_fn, void*);
 -
 -/*
 - * Allocate a "struct alternate_object_database" but do _not_ actually
 - * add it to the list of alternates.
 - */
 -struct alternate_object_database *alloc_alt_odb(const char *dir);
 -
 -/*
 - * Add the directory to the on-disk alternates file; the new entry will also
 - * take effect in the current process.
 - */
 -extern void add_to_alternates_file(const char *dir);
 -
 -/*
 - * Add the directory to the in-memory list of alternates (along with any
 - * recursive alternates it points to), but do not modify the on-disk alternates
 - * file.
 - */
 -extern void add_to_alternates_memory(const char *dir);
 -
 -/*
 - * Returns a scratch strbuf pre-filled with the alternate object directory,
 - * including a trailing slash, which can be used to access paths in the
 - * alternate. Always use this over direct access to alt->scratch, as it
 - * cleans up any previous use of the scratch buffer.
 - */
 -extern struct strbuf *alt_scratch_buf(struct alternate_object_database *alt);
 -
  struct pack_window {
        struct pack_window *next;
        unsigned char *base;
        unsigned int inuse_cnt;
  };
  
 -extern struct packed_git {
 -      struct packed_git *next;
 -      struct list_head mru;
 -      struct pack_window *windows;
 -      off_t pack_size;
 -      const void *index_data;
 -      size_t index_size;
 -      uint32_t num_objects;
 -      uint32_t num_bad_objects;
 -      unsigned char *bad_object_sha1;
 -      int index_version;
 -      time_t mtime;
 -      int pack_fd;
 -      unsigned pack_local:1,
 -               pack_keep:1,
 -               freshened:1,
 -               do_not_close:1,
 -               pack_promisor:1;
 -      unsigned char sha1[20];
 -      struct revindex_entry *revindex;
 -      /* something like ".git/objects/pack/xxxxx.pack" */
 -      char pack_name[FLEX_ARRAY]; /* more */
 -} *packed_git;
 -
 -/*
 - * A most-recently-used ordered version of the packed_git list.
 - */
 -extern struct list_head packed_git_mru;
 -
  struct pack_entry {
        off_t offset;
        unsigned char sha1[20];
   * usual "XXXXXX" trailer, and the resulting filename is written into the
   * "template" buffer. Returns the open descriptor.
   */
 -extern int odb_mkstemp(struct strbuf *template, const char *pattern);
 +extern int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
  
  /*
   * Create a pack .keep file named "name" (which should generally be the output
@@@ -1650,7 -1733,7 +1651,7 @@@ struct object_info 
        unsigned long *sizep;
        off_t *disk_sizep;
        unsigned char *delta_base_sha1;
 -      struct strbuf *typename;
 +      struct strbuf *type_name;
        void **contentp;
  
        /* Response */
  #define OBJECT_INFO_SKIP_CACHED 4
  /* Do not retry packed storage after checking packed and loose storage */
  #define OBJECT_INFO_QUICK 8
 -extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +/* Do not check loose object */
 +#define OBJECT_INFO_IGNORE_LOOSE 16
 +extern int oid_object_info_extended(const struct object_id *, struct object_info *, unsigned flags);
  
  /*
   * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
diff --combined commit-graph.c
index 0000000,3ff8c84..3fc1e0d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,738 +1,741 @@@
 -      prepare_alt_odb();
 -      for (alt = alt_odb_list; !commit_graph && alt; alt = alt->next)
+ #include "cache.h"
+ #include "config.h"
+ #include "git-compat-util.h"
+ #include "lockfile.h"
+ #include "pack.h"
+ #include "packfile.h"
+ #include "commit.h"
+ #include "object.h"
+ #include "revision.h"
+ #include "sha1-lookup.h"
+ #include "commit-graph.h"
++#include "object-store.h"
+ #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
+ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
+ #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
+ #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
+ #define GRAPH_CHUNKID_LARGEEDGES 0x45444745 /* "EDGE" */
+ #define GRAPH_DATA_WIDTH 36
+ #define GRAPH_VERSION_1 0x1
+ #define GRAPH_VERSION GRAPH_VERSION_1
+ #define GRAPH_OID_VERSION_SHA1 1
+ #define GRAPH_OID_LEN_SHA1 GIT_SHA1_RAWSZ
+ #define GRAPH_OID_VERSION GRAPH_OID_VERSION_SHA1
+ #define GRAPH_OID_LEN GRAPH_OID_LEN_SHA1
+ #define GRAPH_OCTOPUS_EDGES_NEEDED 0x80000000
+ #define GRAPH_PARENT_MISSING 0x7fffffff
+ #define GRAPH_EDGE_LAST_MASK 0x7fffffff
+ #define GRAPH_PARENT_NONE 0x70000000
+ #define GRAPH_LAST_EDGE 0x80000000
+ #define GRAPH_FANOUT_SIZE (4 * 256)
+ #define GRAPH_CHUNKLOOKUP_WIDTH 12
+ #define GRAPH_MIN_SIZE (5 * GRAPH_CHUNKLOOKUP_WIDTH + GRAPH_FANOUT_SIZE + \
+                       GRAPH_OID_LEN + 8)
+ char *get_commit_graph_filename(const char *obj_dir)
+ {
+       return xstrfmt("%s/info/commit-graph", obj_dir);
+ }
+ static struct commit_graph *alloc_commit_graph(void)
+ {
+       struct commit_graph *g = xcalloc(1, sizeof(*g));
+       g->graph_fd = -1;
+       return g;
+ }
+ struct commit_graph *load_commit_graph_one(const char *graph_file)
+ {
+       void *graph_map;
+       const unsigned char *data, *chunk_lookup;
+       size_t graph_size;
+       struct stat st;
+       uint32_t i;
+       struct commit_graph *graph;
+       int fd = git_open(graph_file);
+       uint64_t last_chunk_offset;
+       uint32_t last_chunk_id;
+       uint32_t graph_signature;
+       unsigned char graph_version, hash_version;
+       if (fd < 0)
+               return NULL;
+       if (fstat(fd, &st)) {
+               close(fd);
+               return NULL;
+       }
+       graph_size = xsize_t(st.st_size);
+       if (graph_size < GRAPH_MIN_SIZE) {
+               close(fd);
+               die("graph file %s is too small", graph_file);
+       }
+       graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       data = (const unsigned char *)graph_map;
+       graph_signature = get_be32(data);
+       if (graph_signature != GRAPH_SIGNATURE) {
+               error("graph signature %X does not match signature %X",
+                     graph_signature, GRAPH_SIGNATURE);
+               goto cleanup_fail;
+       }
+       graph_version = *(unsigned char*)(data + 4);
+       if (graph_version != GRAPH_VERSION) {
+               error("graph version %X does not match version %X",
+                     graph_version, GRAPH_VERSION);
+               goto cleanup_fail;
+       }
+       hash_version = *(unsigned char*)(data + 5);
+       if (hash_version != GRAPH_OID_VERSION) {
+               error("hash version %X does not match version %X",
+                     hash_version, GRAPH_OID_VERSION);
+               goto cleanup_fail;
+       }
+       graph = alloc_commit_graph();
+       graph->hash_len = GRAPH_OID_LEN;
+       graph->num_chunks = *(unsigned char*)(data + 6);
+       graph->graph_fd = fd;
+       graph->data = graph_map;
+       graph->data_len = graph_size;
+       last_chunk_id = 0;
+       last_chunk_offset = 8;
+       chunk_lookup = data + 8;
+       for (i = 0; i < graph->num_chunks; i++) {
+               uint32_t chunk_id = get_be32(chunk_lookup + 0);
+               uint64_t chunk_offset = get_be64(chunk_lookup + 4);
+               int chunk_repeated = 0;
+               chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
+               if (chunk_offset > graph_size - GIT_MAX_RAWSZ) {
+                       error("improper chunk offset %08x%08x", (uint32_t)(chunk_offset >> 32),
+                             (uint32_t)chunk_offset);
+                       goto cleanup_fail;
+               }
+               switch (chunk_id) {
+               case GRAPH_CHUNKID_OIDFANOUT:
+                       if (graph->chunk_oid_fanout)
+                               chunk_repeated = 1;
+                       else
+                               graph->chunk_oid_fanout = (uint32_t*)(data + chunk_offset);
+                       break;
+               case GRAPH_CHUNKID_OIDLOOKUP:
+                       if (graph->chunk_oid_lookup)
+                               chunk_repeated = 1;
+                       else
+                               graph->chunk_oid_lookup = data + chunk_offset;
+                       break;
+               case GRAPH_CHUNKID_DATA:
+                       if (graph->chunk_commit_data)
+                               chunk_repeated = 1;
+                       else
+                               graph->chunk_commit_data = data + chunk_offset;
+                       break;
+               case GRAPH_CHUNKID_LARGEEDGES:
+                       if (graph->chunk_large_edges)
+                               chunk_repeated = 1;
+                       else
+                               graph->chunk_large_edges = data + chunk_offset;
+                       break;
+               }
+               if (chunk_repeated) {
+                       error("chunk id %08x appears multiple times", chunk_id);
+                       goto cleanup_fail;
+               }
+               if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
+               {
+                       graph->num_commits = (chunk_offset - last_chunk_offset)
+                                            / graph->hash_len;
+               }
+               last_chunk_id = chunk_id;
+               last_chunk_offset = chunk_offset;
+       }
+       return graph;
+ cleanup_fail:
+       munmap(graph_map, graph_size);
+       close(fd);
+       exit(1);
+ }
+ /* global storage */
+ static struct commit_graph *commit_graph = NULL;
+ static void prepare_commit_graph_one(const char *obj_dir)
+ {
+       char *graph_name;
+       if (commit_graph)
+               return;
+       graph_name = get_commit_graph_filename(obj_dir);
+       commit_graph = load_commit_graph_one(graph_name);
+       FREE_AND_NULL(graph_name);
+ }
+ static int prepare_commit_graph_run_once = 0;
+ static void prepare_commit_graph(void)
+ {
+       struct alternate_object_database *alt;
+       char *obj_dir;
+       if (prepare_commit_graph_run_once)
+               return;
+       prepare_commit_graph_run_once = 1;
+       obj_dir = get_object_directory();
+       prepare_commit_graph_one(obj_dir);
++      prepare_alt_odb(the_repository);
++      for (alt = the_repository->objects->alt_odb_list;
++           !commit_graph && alt;
++           alt = alt->next)
+               prepare_commit_graph_one(alt->path);
+ }
+ static void close_commit_graph(void)
+ {
+       if (!commit_graph)
+               return;
+       if (commit_graph->graph_fd >= 0) {
+               munmap((void *)commit_graph->data, commit_graph->data_len);
+               commit_graph->data = NULL;
+               close(commit_graph->graph_fd);
+       }
+       FREE_AND_NULL(commit_graph);
+ }
+ static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
+ {
+       return bsearch_hash(oid->hash, g->chunk_oid_fanout,
+                           g->chunk_oid_lookup, g->hash_len, pos);
+ }
+ static struct commit_list **insert_parent_or_die(struct commit_graph *g,
+                                                uint64_t pos,
+                                                struct commit_list **pptr)
+ {
+       struct commit *c;
+       struct object_id oid;
+       hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos);
+       c = lookup_commit(&oid);
+       if (!c)
+               die("could not find commit %s", oid_to_hex(&oid));
+       c->graph_pos = pos;
+       return &commit_list_insert(c, pptr)->next;
+ }
+ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t pos)
+ {
+       struct object_id oid;
+       uint32_t edge_value;
+       uint32_t *parent_data_ptr;
+       uint64_t date_low, date_high;
+       struct commit_list **pptr;
+       const unsigned char *commit_data = g->chunk_commit_data + (g->hash_len + 16) * pos;
+       item->object.parsed = 1;
+       item->graph_pos = pos;
+       hashcpy(oid.hash, commit_data);
+       item->tree = lookup_tree(&oid);
+       date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
+       date_low = get_be32(commit_data + g->hash_len + 12);
+       item->date = (timestamp_t)((date_high << 32) | date_low);
+       pptr = &item->parents;
+       edge_value = get_be32(commit_data + g->hash_len);
+       if (edge_value == GRAPH_PARENT_NONE)
+               return 1;
+       pptr = insert_parent_or_die(g, edge_value, pptr);
+       edge_value = get_be32(commit_data + g->hash_len + 4);
+       if (edge_value == GRAPH_PARENT_NONE)
+               return 1;
+       if (!(edge_value & GRAPH_OCTOPUS_EDGES_NEEDED)) {
+               pptr = insert_parent_or_die(g, edge_value, pptr);
+               return 1;
+       }
+       parent_data_ptr = (uint32_t*)(g->chunk_large_edges +
+                         4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK));
+       do {
+               edge_value = get_be32(parent_data_ptr);
+               pptr = insert_parent_or_die(g,
+                                           edge_value & GRAPH_EDGE_LAST_MASK,
+                                           pptr);
+               parent_data_ptr++;
+       } while (!(edge_value & GRAPH_LAST_EDGE));
+       return 1;
+ }
+ int parse_commit_in_graph(struct commit *item)
+ {
+       if (!core_commit_graph)
+               return 0;
+       if (item->object.parsed)
+               return 1;
+       prepare_commit_graph();
+       if (commit_graph) {
+               uint32_t pos;
+               int found;
+               if (item->graph_pos != COMMIT_NOT_FROM_GRAPH) {
+                       pos = item->graph_pos;
+                       found = 1;
+               } else {
+                       found = bsearch_graph(commit_graph, &(item->object.oid), &pos);
+               }
+               if (found)
+                       return fill_commit_in_graph(item, commit_graph, pos);
+       }
+       return 0;
+ }
+ static void write_graph_chunk_fanout(struct hashfile *f,
+                                    struct commit **commits,
+                                    int nr_commits)
+ {
+       int i, count = 0;
+       struct commit **list = commits;
+       /*
+        * Write the first-level table (the list is sorted,
+        * but we use a 256-entry lookup to be able to avoid
+        * having to do eight extra binary search iterations).
+        */
+       for (i = 0; i < 256; i++) {
+               while (count < nr_commits) {
+                       if ((*list)->object.oid.hash[0] != i)
+                               break;
+                       count++;
+                       list++;
+               }
+               hashwrite_be32(f, count);
+       }
+ }
+ static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
+                                  struct commit **commits, int nr_commits)
+ {
+       struct commit **list = commits;
+       int count;
+       for (count = 0; count < nr_commits; count++, list++)
+               hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
+ }
+ static const unsigned char *commit_to_sha1(size_t index, void *table)
+ {
+       struct commit **commits = table;
+       return commits[index]->object.oid.hash;
+ }
+ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
+                                  struct commit **commits, int nr_commits)
+ {
+       struct commit **list = commits;
+       struct commit **last = commits + nr_commits;
+       uint32_t num_extra_edges = 0;
+       while (list < last) {
+               struct commit_list *parent;
+               int edge_value;
+               uint32_t packedDate[2];
+               parse_commit(*list);
+               hashwrite(f, (*list)->tree->object.oid.hash, hash_len);
+               parent = (*list)->parents;
+               if (!parent)
+                       edge_value = GRAPH_PARENT_NONE;
+               else {
+                       edge_value = sha1_pos(parent->item->object.oid.hash,
+                                             commits,
+                                             nr_commits,
+                                             commit_to_sha1);
+                       if (edge_value < 0)
+                               edge_value = GRAPH_PARENT_MISSING;
+               }
+               hashwrite_be32(f, edge_value);
+               if (parent)
+                       parent = parent->next;
+               if (!parent)
+                       edge_value = GRAPH_PARENT_NONE;
+               else if (parent->next)
+                       edge_value = GRAPH_OCTOPUS_EDGES_NEEDED | num_extra_edges;
+               else {
+                       edge_value = sha1_pos(parent->item->object.oid.hash,
+                                             commits,
+                                             nr_commits,
+                                             commit_to_sha1);
+                       if (edge_value < 0)
+                               edge_value = GRAPH_PARENT_MISSING;
+               }
+               hashwrite_be32(f, edge_value);
+               if (edge_value & GRAPH_OCTOPUS_EDGES_NEEDED) {
+                       do {
+                               num_extra_edges++;
+                               parent = parent->next;
+                       } while (parent);
+               }
+               if (sizeof((*list)->date) > 4)
+                       packedDate[0] = htonl(((*list)->date >> 32) & 0x3);
+               else
+                       packedDate[0] = 0;
+               packedDate[1] = htonl((*list)->date);
+               hashwrite(f, packedDate, 8);
+               list++;
+       }
+ }
+ static void write_graph_chunk_large_edges(struct hashfile *f,
+                                         struct commit **commits,
+                                         int nr_commits)
+ {
+       struct commit **list = commits;
+       struct commit **last = commits + nr_commits;
+       struct commit_list *parent;
+       while (list < last) {
+               int num_parents = 0;
+               for (parent = (*list)->parents; num_parents < 3 && parent;
+                    parent = parent->next)
+                       num_parents++;
+               if (num_parents <= 2) {
+                       list++;
+                       continue;
+               }
+               /* Since num_parents > 2, this initializer is safe. */
+               for (parent = (*list)->parents->next; parent; parent = parent->next) {
+                       int edge_value = sha1_pos(parent->item->object.oid.hash,
+                                                 commits,
+                                                 nr_commits,
+                                                 commit_to_sha1);
+                       if (edge_value < 0)
+                               edge_value = GRAPH_PARENT_MISSING;
+                       else if (!parent->next)
+                               edge_value |= GRAPH_LAST_EDGE;
+                       hashwrite_be32(f, edge_value);
+               }
+               list++;
+       }
+ }
+ static int commit_compare(const void *_a, const void *_b)
+ {
+       const struct object_id *a = (const struct object_id *)_a;
+       const struct object_id *b = (const struct object_id *)_b;
+       return oidcmp(a, b);
+ }
+ struct packed_commit_list {
+       struct commit **list;
+       int nr;
+       int alloc;
+ };
+ struct packed_oid_list {
+       struct object_id *list;
+       int nr;
+       int alloc;
+ };
+ static int add_packed_commits(const struct object_id *oid,
+                             struct packed_git *pack,
+                             uint32_t pos,
+                             void *data)
+ {
+       struct packed_oid_list *list = (struct packed_oid_list*)data;
+       enum object_type type;
+       off_t offset = nth_packed_object_offset(pack, pos);
+       struct object_info oi = OBJECT_INFO_INIT;
+       oi.typep = &type;
+       if (packed_object_info(pack, offset, &oi) < 0)
+               die("unable to get type of object %s", oid_to_hex(oid));
+       if (type != OBJ_COMMIT)
+               return 0;
+       ALLOC_GROW(list->list, list->nr + 1, list->alloc);
+       oidcpy(&(list->list[list->nr]), oid);
+       list->nr++;
+       return 0;
+ }
+ static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+ {
+       struct commit_list *parent;
+       for (parent = commit->parents; parent; parent = parent->next) {
+               if (!(parent->item->object.flags & UNINTERESTING)) {
+                       ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
+                       oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
+                       oids->nr++;
+                       parent->item->object.flags |= UNINTERESTING;
+               }
+       }
+ }
+ static void close_reachable(struct packed_oid_list *oids)
+ {
+       int i;
+       struct commit *commit;
+       for (i = 0; i < oids->nr; i++) {
+               commit = lookup_commit(&oids->list[i]);
+               if (commit)
+                       commit->object.flags |= UNINTERESTING;
+       }
+       /*
+        * As this loop runs, oids->nr may grow, but not more
+        * than the number of missing commits in the reachable
+        * closure.
+        */
+       for (i = 0; i < oids->nr; i++) {
+               commit = lookup_commit(&oids->list[i]);
+               if (commit && !parse_commit(commit))
+                       add_missing_parents(oids, commit);
+       }
+       for (i = 0; i < oids->nr; i++) {
+               commit = lookup_commit(&oids->list[i]);
+               if (commit)
+                       commit->object.flags &= ~UNINTERESTING;
+       }
+ }
+ void write_commit_graph(const char *obj_dir,
+                       const char **pack_indexes,
+                       int nr_packs,
+                       const char **commit_hex,
+                       int nr_commits,
+                       int append)
+ {
+       struct packed_oid_list oids;
+       struct packed_commit_list commits;
+       struct hashfile *f;
+       uint32_t i, count_distinct = 0;
+       char *graph_name;
+       int fd;
+       struct lock_file lk = LOCK_INIT;
+       uint32_t chunk_ids[5];
+       uint64_t chunk_offsets[5];
+       int num_chunks;
+       int num_extra_edges;
+       struct commit_list *parent;
+       oids.nr = 0;
+       oids.alloc = approximate_object_count() / 4;
+       if (append) {
+               prepare_commit_graph_one(obj_dir);
+               if (commit_graph)
+                       oids.alloc += commit_graph->num_commits;
+       }
+       if (oids.alloc < 1024)
+               oids.alloc = 1024;
+       ALLOC_ARRAY(oids.list, oids.alloc);
+       if (append && commit_graph) {
+               for (i = 0; i < commit_graph->num_commits; i++) {
+                       const unsigned char *hash = commit_graph->chunk_oid_lookup +
+                               commit_graph->hash_len * i;
+                       hashcpy(oids.list[oids.nr++].hash, hash);
+               }
+       }
+       if (pack_indexes) {
+               struct strbuf packname = STRBUF_INIT;
+               int dirlen;
+               strbuf_addf(&packname, "%s/pack/", obj_dir);
+               dirlen = packname.len;
+               for (i = 0; i < nr_packs; i++) {
+                       struct packed_git *p;
+                       strbuf_setlen(&packname, dirlen);
+                       strbuf_addstr(&packname, pack_indexes[i]);
+                       p = add_packed_git(packname.buf, packname.len, 1);
+                       if (!p)
+                               die("error adding pack %s", packname.buf);
+                       if (open_pack_index(p))
+                               die("error opening index for %s", packname.buf);
+                       for_each_object_in_pack(p, add_packed_commits, &oids);
+                       close_pack(p);
+               }
+               strbuf_release(&packname);
+       }
+       if (commit_hex) {
+               for (i = 0; i < nr_commits; i++) {
+                       const char *end;
+                       struct object_id oid;
+                       struct commit *result;
+                       if (commit_hex[i] && parse_oid_hex(commit_hex[i], &oid, &end))
+                               continue;
+                       result = lookup_commit_reference_gently(&oid, 1);
+                       if (result) {
+                               ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
+                               oidcpy(&oids.list[oids.nr], &(result->object.oid));
+                               oids.nr++;
+                       }
+               }
+       }
+       if (!pack_indexes && !commit_hex)
+               for_each_packed_object(add_packed_commits, &oids, 0);
+       close_reachable(&oids);
+       QSORT(oids.list, oids.nr, commit_compare);
+       count_distinct = 1;
+       for (i = 1; i < oids.nr; i++) {
+               if (oidcmp(&oids.list[i-1], &oids.list[i]))
+                       count_distinct++;
+       }
+       if (count_distinct >= GRAPH_PARENT_MISSING)
+               die(_("the commit graph format cannot write %d commits"), count_distinct);
+       commits.nr = 0;
+       commits.alloc = count_distinct;
+       ALLOC_ARRAY(commits.list, commits.alloc);
+       num_extra_edges = 0;
+       for (i = 0; i < oids.nr; i++) {
+               int num_parents = 0;
+               if (i > 0 && !oidcmp(&oids.list[i-1], &oids.list[i]))
+                       continue;
+               commits.list[commits.nr] = lookup_commit(&oids.list[i]);
+               parse_commit(commits.list[commits.nr]);
+               for (parent = commits.list[commits.nr]->parents;
+                    parent; parent = parent->next)
+                       num_parents++;
+               if (num_parents > 2)
+                       num_extra_edges += num_parents - 1;
+               commits.nr++;
+       }
+       num_chunks = num_extra_edges ? 4 : 3;
+       if (commits.nr >= GRAPH_PARENT_MISSING)
+               die(_("too many commits to write graph"));
+       graph_name = get_commit_graph_filename(obj_dir);
+       fd = hold_lock_file_for_update(&lk, graph_name, 0);
+       if (fd < 0) {
+               struct strbuf folder = STRBUF_INIT;
+               strbuf_addstr(&folder, graph_name);
+               strbuf_setlen(&folder, strrchr(folder.buf, '/') - folder.buf);
+               if (mkdir(folder.buf, 0777) < 0)
+                       die_errno(_("cannot mkdir %s"), folder.buf);
+               strbuf_release(&folder);
+               fd = hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+               if (fd < 0)
+                       die_errno("unable to create '%s'", graph_name);
+       }
+       f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
+       hashwrite_be32(f, GRAPH_SIGNATURE);
+       hashwrite_u8(f, GRAPH_VERSION);
+       hashwrite_u8(f, GRAPH_OID_VERSION);
+       hashwrite_u8(f, num_chunks);
+       hashwrite_u8(f, 0); /* unused padding byte */
+       chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
+       chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
+       chunk_ids[2] = GRAPH_CHUNKID_DATA;
+       if (num_extra_edges)
+               chunk_ids[3] = GRAPH_CHUNKID_LARGEEDGES;
+       else
+               chunk_ids[3] = 0;
+       chunk_ids[4] = 0;
+       chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
+       chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
+       chunk_offsets[2] = chunk_offsets[1] + GRAPH_OID_LEN * commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (GRAPH_OID_LEN + 16) * commits.nr;
+       chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+       for (i = 0; i <= num_chunks; i++) {
+               uint32_t chunk_write[3];
+               chunk_write[0] = htonl(chunk_ids[i]);
+               chunk_write[1] = htonl(chunk_offsets[i] >> 32);
+               chunk_write[2] = htonl(chunk_offsets[i] & 0xffffffff);
+               hashwrite(f, chunk_write, 12);
+       }
+       write_graph_chunk_fanout(f, commits.list, commits.nr);
+       write_graph_chunk_oids(f, GRAPH_OID_LEN, commits.list, commits.nr);
+       write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
+       write_graph_chunk_large_edges(f, commits.list, commits.nr);
+       close_commit_graph();
+       finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
+       commit_lock_file(&lk);
+       free(oids.list);
+       oids.alloc = 0;
+       oids.nr = 0;
+ }
diff --combined commit.c
+++ b/commit.c
@@@ -1,6 -1,7 +1,7 @@@
  #include "cache.h"
  #include "tag.h"
  #include "commit.h"
+ #include "commit-graph.h"
  #include "pkt-line.h"
  #include "utf8.h"
  #include "diff.h"
@@@ -266,13 -267,13 +267,13 @@@ const void *get_commit_buffer(const str
        if (!ret) {
                enum object_type type;
                unsigned long size;
 -              ret = read_sha1_file(commit->object.oid.hash, &type, &size);
 +              ret = read_object_file(&commit->object.oid, &type, &size);
                if (!ret)
                        die("cannot read commit object %s",
                            oid_to_hex(&commit->object.oid));
                if (type != OBJ_COMMIT)
                        die("expected commit for %s, got %s",
 -                          oid_to_hex(&commit->object.oid), typename(type));
 +                          oid_to_hex(&commit->object.oid), type_name(type));
                if (sizep)
                        *sizep = size;
        }
@@@ -383,7 -384,9 +384,9 @@@ int parse_commit_gently(struct commit *
                return -1;
        if (item->object.parsed)
                return 0;
 -      buffer = read_sha1_file(item->object.oid.hash, &type, &size);
+       if (parse_commit_in_graph(item))
+               return 0;
 +      buffer = read_object_file(&item->object.oid, &type, &size);
        if (!buffer)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
@@@ -859,19 -862,19 +862,19 @@@ struct commit_list *get_octopus_merge_b
        commit_list_insert(in->item, &ret);
  
        for (i = in->next; i; i = i->next) {
 -              struct commit_list *new = NULL, *end = NULL;
 +              struct commit_list *new_commits = NULL, *end = NULL;
  
                for (j = ret; j; j = j->next) {
                        struct commit_list *bases;
                        bases = get_merge_bases(i->item, j->item);
 -                      if (!new)
 -                              new = bases;
 +                      if (!new_commits)
 +                              new_commits = bases;
                        else
                                end->next = bases;
                        for (k = bases; k; k = k->next)
                                end = k;
                }
 -              ret = new;
 +              ret = new_commits;
        }
        return ret;
  }
@@@ -1206,7 -1209,7 +1209,7 @@@ static void handle_signed_tag(struct co
        desc = merge_remote_util(parent);
        if (!desc || !desc->obj)
                return;
 -      buf = read_sha1_file(desc->obj->oid.hash, &type, &size);
 +      buf = read_object_file(&desc->obj->oid, &type, &size);
        if (!buf || type != OBJ_TAG)
                goto free_return;
        len = parse_signature(buf, size);
@@@ -1517,7 -1520,7 +1520,7 @@@ int commit_tree_extended(const char *ms
        int encoding_is_utf8;
        struct strbuf buffer;
  
 -      assert_sha1_type(tree->hash, OBJ_TREE);
 +      assert_oid_type(tree, OBJ_TREE);
  
        if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
@@@ -1614,11 -1617,11 +1617,11 @@@ struct commit *get_merge_parent(const c
  struct commit_list **commit_list_append(struct commit *commit,
                                        struct commit_list **next)
  {
 -      struct commit_list *new = xmalloc(sizeof(struct commit_list));
 -      new->item = commit;
 -      *next = new;
 -      new->next = NULL;
 -      return &new->next;
 +      struct commit_list *new_commit = xmalloc(sizeof(struct commit_list));
 +      new_commit->item = commit;
 +      *next = new_commit;
 +      new_commit->next = NULL;
 +      return &new_commit->next;
  }
  
  const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
diff --combined config.c
+++ b/config.c
@@@ -9,7 -9,7 +9,7 @@@
  #include "config.h"
  #include "repository.h"
  #include "lockfile.h"
 -#include "exec_cmd.h"
 +#include "exec-cmd.h"
  #include "strbuf.h"
  #include "quote.h"
  #include "hashmap.h"
@@@ -653,45 -653,7 +653,45 @@@ static int get_base_var(struct strbuf *
        }
  }
  
 -static int git_parse_source(config_fn_t fn, void *data)
 +struct parse_event_data {
 +      enum config_event_t previous_type;
 +      size_t previous_offset;
 +      const struct config_options *opts;
 +};
 +
 +static int do_event(enum config_event_t type, struct parse_event_data *data)
 +{
 +      size_t offset;
 +
 +      if (!data->opts || !data->opts->event_fn)
 +              return 0;
 +
 +      if (type == CONFIG_EVENT_WHITESPACE &&
 +          data->previous_type == type)
 +              return 0;
 +
 +      offset = cf->do_ftell(cf);
 +      /*
 +       * At EOF, the parser always "inserts" an extra '\n', therefore
 +       * the end offset of the event is the current file position, otherwise
 +       * we will already have advanced to the next event.
 +       */
 +      if (type != CONFIG_EVENT_EOF)
 +              offset--;
 +
 +      if (data->previous_type != CONFIG_EVENT_EOF &&
 +          data->opts->event_fn(data->previous_type, data->previous_offset,
 +                               offset, data->opts->event_fn_data) < 0)
 +              return -1;
 +
 +      data->previous_type = type;
 +      data->previous_offset = offset;
 +
 +      return 0;
 +}
 +
 +static int git_parse_source(config_fn_t fn, void *data,
 +                          const struct config_options *opts)
  {
        int comment = 0;
        int baselen = 0;
        /* U+FEFF Byte Order Mark in UTF8 */
        const char *bomptr = utf8_bom;
  
 +      /* For the parser event callback */
 +      struct parse_event_data event_data = {
 +              CONFIG_EVENT_EOF, 0, opts
 +      };
 +
        for (;;) {
 -              int c = get_next_char();
 +              int c;
 +
 +              c = get_next_char();
                if (bomptr && *bomptr) {
                        /* We are at the file beginning; skip UTF8-encoded BOM
                         * if present. Sane editors won't put this in on their
                        }
                }
                if (c == '\n') {
 -                      if (cf->eof)
 +                      if (cf->eof) {
 +                              if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
 +                                      return -1;
                                return 0;
 +                      }
 +                      if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
 +                              return -1;
                        comment = 0;
                        continue;
                }
 -              if (comment || isspace(c))
 +              if (comment)
                        continue;
 +              if (isspace(c)) {
 +                      if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
 +                                      return -1;
 +                      continue;
 +              }
                if (c == '#' || c == ';') {
 +                      if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
 +                                      return -1;
                        comment = 1;
                        continue;
                }
                if (c == '[') {
 +                      if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
 +                                      return -1;
 +
                        /* Reset prior to determining a new stem */
                        strbuf_reset(var);
                        if (get_base_var(var) < 0 || var->len < 1)
                }
                if (!isalpha(c))
                        break;
 +
 +              if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
 +                      return -1;
 +
                /*
                 * Truncate the var name back to the section header
                 * stem prior to grabbing the suffix part of the name
                        break;
        }
  
 +      if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
 +              return -1;
 +
        switch (cf->origin_type) {
        case CONFIG_ORIGIN_BLOB:
                error_msg = xstrfmt(_("bad config line %d in blob %s"),
@@@ -1293,6 -1226,11 +1293,11 @@@ static int git_default_core_config(cons
                return 0;
        }
  
+       if (!strcmp(var, "core.commitgraph")) {
+               core_commit_graph = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "core.sparsecheckout")) {
                core_apply_sparse_checkout = git_config_bool(var, value);
                return 0;
@@@ -1465,8 -1403,7 +1470,8 @@@ int git_default_config(const char *var
   * fgetc, ungetc, ftell of top need to be initialized before calling
   * this function.
   */
 -static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
 +static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
 +                        const struct config_options *opts)
  {
        int ret;
  
        strbuf_init(&top->var, 1024);
        cf = top;
  
 -      ret = git_parse_source(fn, data);
 +      ret = git_parse_source(fn, data, opts);
  
        /* pop config-file parsing state stack */
        strbuf_release(&top->value);
  static int do_config_from_file(config_fn_t fn,
                const enum config_origin_type origin_type,
                const char *name, const char *path, FILE *f,
 -              void *data)
 +              void *data, const struct config_options *opts)
  {
        struct config_source top;
 +      int ret;
  
        top.u.file = f;
        top.origin_type = origin_type;
        top.do_ungetc = config_file_ungetc;
        top.do_ftell = config_file_ftell;
  
 -      return do_config_from(&top, fn, data);
 +      flockfile(f);
 +      ret = do_config_from(&top, fn, data, opts);
 +      funlockfile(f);
 +      return ret;
  }
  
  static int git_config_from_stdin(config_fn_t fn, void *data)
  {
 -      return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
 +      return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
 +                                 data, NULL);
  }
  
 -int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 +int git_config_from_file_with_options(config_fn_t fn, const char *filename,
 +                                    void *data,
 +                                    const struct config_options *opts)
  {
        int ret = -1;
        FILE *f;
  
        f = fopen_or_warn(filename, "r");
        if (f) {
 -              flockfile(f);
 -              ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
 -              funlockfile(f);
 +              ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
 +                                        filename, f, data, opts);
                fclose(f);
        }
        return ret;
  }
  
 +int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 +{
 +      return git_config_from_file_with_options(fn, filename, data, NULL);
 +}
 +
  int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
                        const char *name, const char *buf, size_t len, void *data)
  {
        top.do_ungetc = config_buf_ungetc;
        top.do_ftell = config_buf_ftell;
  
 -      return do_config_from(&top, fn, data);
 +      return do_config_from(&top, fn, data, NULL);
  }
  
  int git_config_from_blob_oid(config_fn_t fn,
        unsigned long size;
        int ret;
  
 -      buf = read_sha1_file(oid->hash, &type, &size);
 +      buf = read_object_file(oid, &type, &size);
        if (!buf)
                return error("unable to load config blob object '%s'", name);
        if (type != OBJ_BLOB) {
@@@ -2298,98 -2224,96 +2303,98 @@@ void git_die_config(const char *key, co
   * Find all the stuff for git_config_set() below.
   */
  
 -static struct {
 +struct config_store_data {
        int baselen;
        char *key;
        int do_not_match;
        regex_t *value_regex;
        int multi_replace;
 -      size_t *offset;
 -      unsigned int offset_alloc;
 -      enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
 -      unsigned int seen;
 -} store;
 +      struct {
 +              size_t begin, end;
 +              enum config_event_t type;
 +              int is_keys_section;
 +      } *parsed;
 +      unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
 +      unsigned int key_seen:1, section_seen:1, is_keys_section:1;
 +};
  
 -static int matches(const char *key, const char *value)
 +static int matches(const char *key, const char *value,
 +                 const struct config_store_data *store)
  {
 -      if (strcmp(key, store.key))
 +      if (strcmp(key, store->key))
                return 0; /* not ours */
 -      if (!store.value_regex)
 +      if (!store->value_regex)
                return 1; /* always matches */
 -      if (store.value_regex == CONFIG_REGEX_NONE)
 +      if (store->value_regex == CONFIG_REGEX_NONE)
                return 0; /* never matches */
  
 -      return store.do_not_match ^
 -              (value && !regexec(store.value_regex, value, 0, NULL, 0));
 +      return store->do_not_match ^
 +              (value && !regexec(store->value_regex, value, 0, NULL, 0));
 +}
 +
 +static int store_aux_event(enum config_event_t type,
 +                         size_t begin, size_t end, void *data)
 +{
 +      struct config_store_data *store = data;
 +
 +      ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
 +      store->parsed[store->parsed_nr].begin = begin;
 +      store->parsed[store->parsed_nr].end = end;
 +      store->parsed[store->parsed_nr].type = type;
 +
 +      if (type == CONFIG_EVENT_SECTION) {
 +              if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
 +                      BUG("Invalid section name '%s'", cf->var.buf);
 +
 +              /* Is this the section we were looking for? */
 +              store->is_keys_section =
 +                      store->parsed[store->parsed_nr].is_keys_section =
 +                      cf->var.len - 1 == store->baselen &&
 +                      !strncasecmp(cf->var.buf, store->key, store->baselen);
 +              if (store->is_keys_section) {
 +                      store->section_seen = 1;
 +                      ALLOC_GROW(store->seen, store->seen_nr + 1,
 +                                 store->seen_alloc);
 +                      store->seen[store->seen_nr] = store->parsed_nr;
 +              }
 +      }
 +
 +      store->parsed_nr++;
 +
 +      return 0;
  }
  
  static int store_aux(const char *key, const char *value, void *cb)
  {
 -      const char *ep;
 -      size_t section_len;
 +      struct config_store_data *store = cb;
  
 -      switch (store.state) {
 -      case KEY_SEEN:
 -              if (matches(key, value)) {
 -                      if (store.seen == 1 && store.multi_replace == 0) {
 +      if (store->key_seen) {
 +              if (matches(key, value, store)) {
 +                      if (store->seen_nr == 1 && store->multi_replace == 0) {
                                warning(_("%s has multiple values"), key);
                        }
  
 -                      ALLOC_GROW(store.offset, store.seen + 1,
 -                                 store.offset_alloc);
 +                      ALLOC_GROW(store->seen, store->seen_nr + 1,
 +                                 store->seen_alloc);
  
 -                      store.offset[store.seen] = cf->do_ftell(cf);
 -                      store.seen++;
 +                      store->seen[store->seen_nr] = store->parsed_nr;
 +                      store->seen_nr++;
                }
 -              break;
 -      case SECTION_SEEN:
 +      } else if (store->is_keys_section) {
                /*
 -               * What we are looking for is in store.key (both
 -               * section and var), and its section part is baselen
 -               * long.  We found key (again, both section and var).
 -               * We would want to know if this key is in the same
 -               * section as what we are looking for.  We already
 -               * know we are in the same section as what should
 -               * hold store.key.
 +               * Do not increment matches yet: this may not be a match, but we
 +               * are in the desired section.
                 */
 -              ep = strrchr(key, '.');
 -              section_len = ep - key;
 +              ALLOC_GROW(store->seen, store->seen_nr + 1, store->seen_alloc);
 +              store->seen[store->seen_nr] = store->parsed_nr;
 +              store->section_seen = 1;
  
 -              if ((section_len != store.baselen) ||
 -                  memcmp(key, store.key, section_len+1)) {
 -                      store.state = SECTION_END_SEEN;
 -                      break;
 -              }
 -
 -              /*
 -               * Do not increment matches: this is no match, but we
 -               * just made sure we are in the desired section.
 -               */
 -              ALLOC_GROW(store.offset, store.seen + 1,
 -                         store.offset_alloc);
 -              store.offset[store.seen] = cf->do_ftell(cf);
 -              /* fallthru */
 -      case SECTION_END_SEEN:
 -      case START:
 -              if (matches(key, value)) {
 -                      ALLOC_GROW(store.offset, store.seen + 1,
 -                                 store.offset_alloc);
 -                      store.offset[store.seen] = cf->do_ftell(cf);
 -                      store.state = KEY_SEEN;
 -                      store.seen++;
 -              } else {
 -                      if (strrchr(key, '.') - key == store.baselen &&
 -                            !strncmp(key, store.key, store.baselen)) {
 -                                      store.state = SECTION_SEEN;
 -                                      ALLOC_GROW(store.offset,
 -                                                 store.seen + 1,
 -                                                 store.offset_alloc);
 -                                      store.offset[store.seen] = cf->do_ftell(cf);
 -                      }
 +              if (matches(key, value, store)) {
 +                      store->seen_nr++;
 +                      store->key_seen = 1;
                }
        }
 +
        return 0;
  }
  
@@@ -2401,33 -2325,31 +2406,33 @@@ static int write_error(const char *file
        return 4;
  }
  
 -static struct strbuf store_create_section(const char *key)
 +static struct strbuf store_create_section(const char *key,
 +                                        const struct config_store_data *store)
  {
        const char *dot;
        int i;
        struct strbuf sb = STRBUF_INIT;
  
 -      dot = memchr(key, '.', store.baselen);
 +      dot = memchr(key, '.', store->baselen);
        if (dot) {
                strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
 -              for (i = dot - key + 1; i < store.baselen; i++) {
 +              for (i = dot - key + 1; i < store->baselen; i++) {
                        if (key[i] == '"' || key[i] == '\\')
                                strbuf_addch(&sb, '\\');
                        strbuf_addch(&sb, key[i]);
                }
                strbuf_addstr(&sb, "\"]\n");
        } else {
 -              strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
 +              strbuf_addf(&sb, "[%.*s]\n", store->baselen, key);
        }
  
        return sb;
  }
  
 -static ssize_t write_section(int fd, const char *key)
 +static ssize_t write_section(int fd, const char *key,
 +                           const struct config_store_data *store)
  {
 -      struct strbuf sb = store_create_section(key);
 +      struct strbuf sb = store_create_section(key, store);
        ssize_t ret;
  
        ret = write_in_full(fd, sb.buf, sb.len);
        return ret;
  }
  
 -static ssize_t write_pair(int fd, const char *key, const char *value)
 +static ssize_t write_pair(int fd, const char *key, const char *value,
 +                        const struct config_store_data *store)
  {
        int i;
        ssize_t ret;
 -      int length = strlen(key + store.baselen + 1);
 +      int length = strlen(key + store->baselen + 1);
        const char *quote = "";
        struct strbuf sb = STRBUF_INIT;
  
                quote = "\"";
  
        strbuf_addf(&sb, "\t%.*s = %s",
 -                  length, key + store.baselen + 1, quote);
 +                  length, key + store->baselen + 1, quote);
  
        for (i = 0; value[i]; i++)
                switch (value[i]) {
        return ret;
  }
  
 -static ssize_t find_beginning_of_line(const char *contents, size_t size,
 -      size_t offset_, int *found_bracket)
 +/*
 + * If we are about to unset the last key(s) in a section, and if there are
 + * no comments surrounding (or included in) the section, we will want to
 + * extend begin/end to remove the entire section.
 + *
 + * Note: the parameter `seen_ptr` points to the index into the store.seen
 + * array.  * This index may be incremented if a section has more than one
 + * entry (which all are to be removed).
 + */
 +static void maybe_remove_section(struct config_store_data *store,
 +                               const char *contents,
 +                               size_t *begin_offset, size_t *end_offset,
 +                               int *seen_ptr)
  {
 -      size_t equal_offset = size, bracket_offset = size;
 -      ssize_t offset;
 +      size_t begin;
 +      int i, seen, section_seen = 0;
  
 -contline:
 -      for (offset = offset_-2; offset > 0
 -                      && contents[offset] != '\n'; offset--)
 -              switch (contents[offset]) {
 -                      case '=': equal_offset = offset; break;
 -                      case ']': bracket_offset = offset; break;
 +      /*
 +       * First, ensure that this is the first key, and that there are no
 +       * comments before the entry nor before the section header.
 +       */
 +      seen = *seen_ptr;
 +      for (i = store->seen[seen]; i > 0; i--) {
 +              enum config_event_t type = store->parsed[i - 1].type;
 +
 +              if (type == CONFIG_EVENT_COMMENT)
 +                      /* There is a comment before this entry or section */
 +                      return;
 +              if (type == CONFIG_EVENT_ENTRY) {
 +                      if (!section_seen)
 +                              /* This is not the section's first entry. */
 +                              return;
 +                      /* We encountered no comment before the section. */
 +                      break;
 +              }
 +              if (type == CONFIG_EVENT_SECTION) {
 +                      if (!store->parsed[i - 1].is_keys_section)
 +                              break;
 +                      section_seen = 1;
                }
 -      if (offset > 0 && contents[offset-1] == '\\') {
 -              offset_ = offset;
 -              goto contline;
        }
 -      if (bracket_offset < equal_offset) {
 -              *found_bracket = 1;
 -              offset = bracket_offset+1;
 -      } else
 -              offset++;
 +      begin = store->parsed[i].begin;
  
 -      return offset;
 +      /*
 +       * Next, make sure that we are removing he last key(s) in the section,
 +       * and that there are no comments that are possibly about the current
 +       * section.
 +       */
 +      for (i = store->seen[seen] + 1; i < store->parsed_nr; i++) {
 +              enum config_event_t type = store->parsed[i].type;
 +
 +              if (type == CONFIG_EVENT_COMMENT)
 +                      return;
 +              if (type == CONFIG_EVENT_SECTION) {
 +                      if (store->parsed[i].is_keys_section)
 +                              continue;
 +                      break;
 +              }
 +              if (type == CONFIG_EVENT_ENTRY) {
 +                      if (++seen < store->seen_nr &&
 +                          i == store->seen[seen])
 +                              /* We want to remove this entry, too */
 +                              continue;
 +                      /* There is another entry in this section. */
 +                      return;
 +              }
 +      }
 +
 +      /*
 +       * We are really removing the last entry/entries from this section, and
 +       * there are no enclosed or surrounding comments. Remove the entire,
 +       * now-empty section.
 +       */
 +      *seen_ptr = seen;
 +      *begin_offset = begin;
 +      if (i < store->parsed_nr)
 +              *end_offset = store->parsed[i].begin;
 +      else
 +              *end_offset = store->parsed[store->parsed_nr - 1].end;
  }
  
  int git_config_set_in_file_gently(const char *config_filename,
@@@ -2626,9 -2492,6 +2631,9 @@@ int git_config_set_multivar_in_file_gen
        char *filename_buf = NULL;
        char *contents = NULL;
        size_t contents_sz;
 +      struct config_store_data store;
 +
 +      memset(&store, 0, sizeof(store));
  
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
                }
  
                store.key = (char *)key;
 -              if (write_section(fd, key) < 0 ||
 -                  write_pair(fd, key, value) < 0)
 +              if (write_section(fd, key, &store) < 0 ||
 +                  write_pair(fd, key, value, &store) < 0)
                        goto write_err_out;
        } else {
                struct stat st;
                size_t copy_begin, copy_end;
                int i, new_line = 0;
 +              struct config_options opts;
  
                if (value_regex == NULL)
                        store.value_regex = NULL;
                        }
                }
  
 -              ALLOC_GROW(store.offset, 1, store.offset_alloc);
 -              store.offset[0] = 0;
 -              store.state = START;
 -              store.seen = 0;
 +              ALLOC_GROW(store.parsed, 1, store.parsed_alloc);
 +              store.parsed[0].end = 0;
 +
 +              memset(&opts, 0, sizeof(opts));
 +              opts.event_fn = store_aux_event;
 +              opts.event_fn_data = &store;
  
                /*
 -               * After this, store.offset will contain the *end* offset
 -               * of the last match, or remain at 0 if no match was found.
 +               * After this, store.parsed will contain offsets of all the
 +               * parsed elements, and store.seen will contain a list of
 +               * matches, as indices into store.parsed.
 +               *
                 * As a side effect, we make sure to transform only a valid
                 * existing config file.
                 */
 -              if (git_config_from_file(store_aux, config_filename, NULL)) {
 +              if (git_config_from_file_with_options(store_aux,
 +                                                    config_filename,
 +                                                    &store, &opts)) {
                        error("invalid config file %s", config_filename);
                        free(store.key);
                        if (store.value_regex != NULL &&
                }
  
                /* if nothing to unset, or too many matches, error out */
 -              if ((store.seen == 0 && value == NULL) ||
 -                              (store.seen > 1 && multi_replace == 0)) {
 +              if ((store.seen_nr == 0 && value == NULL) ||
 +                  (store.seen_nr > 1 && multi_replace == 0)) {
                        ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
                        goto out_free;
                }
  
 -              if (store.seen == 0)
 -                      store.seen = 1;
 +              if (store.seen_nr == 0) {
 +                      if (!store.seen_alloc) {
 +                              /* Did not see key nor section */
 +                              ALLOC_GROW(store.seen, 1, store.seen_alloc);
 +                              store.seen[0] = store.parsed_nr
 +                                      - !!store.parsed_nr;
 +                      }
 +                      store.seen_nr = 1;
 +              }
  
 -              for (i = 0, copy_begin = 0; i < store.seen; i++) {
 -                      if (store.offset[i] == 0) {
 -                              store.offset[i] = copy_end = contents_sz;
 -                      } else if (store.state != KEY_SEEN) {
 -                              copy_end = store.offset[i];
 -                      } else
 -                              copy_end = find_beginning_of_line(
 -                                      contents, contents_sz,
 -                                      store.offset[i]-2, &new_line);
 +              for (i = 0, copy_begin = 0; i < store.seen_nr; i++) {
 +                      size_t replace_end;
 +                      int j = store.seen[i];
 +
 +                      new_line = 0;
 +                      if (!store.key_seen) {
 +                              copy_end = store.parsed[j].end;
 +                              /* include '\n' when copying section header */
 +                              if (copy_end > 0 && copy_end < contents_sz &&
 +                                  contents[copy_end - 1] != '\n' &&
 +                                  contents[copy_end] == '\n')
 +                                      copy_end++;
 +                              replace_end = copy_end;
 +                      } else {
 +                              replace_end = store.parsed[j].end;
 +                              copy_end = store.parsed[j].begin;
 +                              if (!value)
 +                                      maybe_remove_section(&store, contents,
 +                                                           &copy_end,
 +                                                           &replace_end, &i);
 +                              /*
 +                               * Swallow preceding white-space on the same
 +                               * line.
 +                               */
 +                              while (copy_end > 0 ) {
 +                                      char c = contents[copy_end - 1];
 +
 +                                      if (isspace(c) && c != '\n')
 +                                              copy_end--;
 +                                      else
 +                                              break;
 +                              }
 +                      }
  
                        if (copy_end > 0 && contents[copy_end-1] != '\n')
                                new_line = 1;
                                    write_str_in_full(fd, "\n") < 0)
                                        goto write_err_out;
                        }
 -                      copy_begin = store.offset[i];
 +                      copy_begin = replace_end;
                }
  
                /* write the pair (value == NULL means unset) */
                if (value != NULL) {
 -                      if (store.state == START) {
 -                              if (write_section(fd, key) < 0)
 +                      if (!store.section_seen) {
 +                              if (write_section(fd, key, &store) < 0)
                                        goto write_err_out;
                        }
 -                      if (write_pair(fd, key, value) < 0)
 +                      if (write_pair(fd, key, value, &store) < 0)
                                goto write_err_out;
                }
  
@@@ -2959,8 -2784,7 +2964,8 @@@ static int section_name_is_ok(const cha
  
  /* if new_name == NULL, the section is removed instead */
  static int git_config_copy_or_rename_section_in_file(const char *config_filename,
 -                                    const char *old_name, const char *new_name, int copy)
 +                                    const char *old_name,
 +                                    const char *new_name, int copy)
  {
        int ret = 0, remove = 0;
        char *filename_buf = NULL;
        FILE *config_file = NULL;
        struct stat st;
        struct strbuf copystr = STRBUF_INIT;
 +      struct config_store_data store;
 +
 +      memset(&store, 0, sizeof(store));
  
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
                                }
                                store.baselen = strlen(new_name);
                                if (!copy) {
 -                                      if (write_section(out_fd, new_name) < 0) {
 +                                      if (write_section(out_fd, new_name, &store) < 0) {
                                                ret = write_error(get_lock_file_path(&lock));
                                                goto out;
                                        }
                                                output[0] = '\t';
                                        }
                                } else {
 -                                      copystr = store_create_section(new_name);
 +                                      copystr = store_create_section(new_name, &store);
                                }
                        }
                        remove = 0;
@@@ -29,8 -29,6 +29,8 @@@
  # tell the completion to use commit completion.  This also works with aliases
  # of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
  #
 +# Compatible with bash 3.2.57.
 +#
  # You can set the following environment variables to influence the behavior of
  # the completion routines:
  #
@@@ -282,43 -280,6 +282,43 @@@ __gitcomp (
        esac
  }
  
 +# Clear the variables caching builtins' options when (re-)sourcing
 +# the completion script.
 +unset $(set |sed -ne 's/^\(__gitcomp_builtin_[a-zA-Z0-9_][a-zA-Z0-9_]*\)=.*/\1/p') 2>/dev/null
 +
 +# This function is equivalent to
 +#
 +#    __gitcomp "$(git xxx --git-completion-helper) ..."
 +#
 +# except that the output is cached. Accept 1-3 arguments:
 +# 1: the git command to execute, this is also the cache key
 +# 2: extra options to be added on top (e.g. negative forms)
 +# 3: options to be excluded
 +__gitcomp_builtin ()
 +{
 +      # spaces must be replaced with underscore for multi-word
 +      # commands, e.g. "git remote add" becomes remote_add.
 +      local cmd="$1"
 +      local incl="$2"
 +      local excl="$3"
 +
 +      local var=__gitcomp_builtin_"${cmd/-/_}"
 +      local options
 +      eval "options=\$$var"
 +
 +      if [ -z "$options" ]; then
 +              # leading and trailing spaces are significant to make
 +              # option removal work correctly.
 +              options=" $(__git ${cmd/_/ } --git-completion-helper) $incl "
 +              for i in $excl; do
 +                      options="${options/ $i / }"
 +              done
 +              eval "$var=\"$options\""
 +      fi
 +
 +      __gitcomp "$options"
 +}
 +
  # Variation of __gitcomp_nl () that appends to the existing list of
  # completion candidates, COMPREPLY.
  __gitcomp_nl_append ()
@@@ -390,7 -351,12 +390,7 @@@ __git_index_files (
        local root="${2-.}" file
  
        __git_ls_files_helper "$root" "$1" |
 -      while read -r file; do
 -              case "$file" in
 -              ?*/*) echo "${file%%/*}" ;;
 -              *) echo "$file" ;;
 -              esac
 -      done | sort | uniq
 +      cut -f1 -d/ | sort | uniq
  }
  
  # Lists branches from the local repository.
@@@ -473,7 -439,7 +473,7 @@@ __git_refs (
                        track=""
                        ;;
                *)
 -                      for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
 +                      for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do
                                case "$i" in
                                $match*)
                                        if [ -e "$dir/$i" ]; then
@@@ -875,6 -841,7 +875,7 @@@ __git_list_porcelain_commands (
                check-ref-format) : plumbing;;
                checkout-index)   : plumbing;;
                column)           : internal helper;;
+               commit-graph)     : plumbing;;
                commit-tree)      : plumbing;;
                count-objects)    : infrequent;;
                credential)       : credentials;;
@@@ -1106,13 -1073,12 +1107,13 @@@ __git_count_arguments (
  }
  
  __git_whitespacelist="nowarn warn error error-all fix"
 +__git_am_inprogress_options="--skip --continue --resolved --abort --quit --show-current-patch"
  
  _git_am ()
  {
        __git_find_repo_path
        if [ -d "$__git_repo_path"/rebase-apply ]; then
 -              __gitcomp "--skip --continue --resolved --abort"
 +              __gitcomp "$__git_am_inprogress_options"
                return
        fi
        case "$cur" in
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --3way --committer-date-is-author-date --ignore-date
 -                      --ignore-whitespace --ignore-space-change
 -                      --interactive --keep --no-utf8 --signoff --utf8
 -                      --whitespace= --scissors
 -                      "
 +              __gitcomp_builtin am "--no-utf8" \
 +                      "$__git_am_inprogress_options"
                return
        esac
  }
@@@ -1135,7 -1105,14 +1136,7 @@@ _git_apply (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --stat --numstat --summary --check --index
 -                      --cached --index-info --reverse --reject --unidiff-zero
 -                      --apply --no-add --exclude=
 -                      --ignore-whitespace --ignore-space-change
 -                      --whitespace= --inaccurate-eof --verbose
 -                      --recount --directory=
 -                      "
 +              __gitcomp_builtin apply
                return
        esac
  }
@@@ -1144,7 -1121,10 +1145,7 @@@ _git_add (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --interactive --refresh --patch --update --dry-run
 -                      --ignore-errors --intent-to-add --force --edit --chmod=
 -                      "
 +              __gitcomp_builtin add
                return
        esac
  
@@@ -1221,8 -1201,12 +1222,8 @@@ _git_branch (
                __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
 -              __gitcomp "
 -                      --color --no-color --verbose --abbrev= --no-abbrev
 -                      --track --no-track --contains --no-contains --merged --no-merged
 -                      --set-upstream-to= --edit-description --list
 -                      --unset-upstream --delete --move --copy --remotes
 -                      --column --no-column --sort= --points-at
 +              __gitcomp_builtin branch "--no-color --no-abbrev
 +                      --no-track --no-column
                        "
                ;;
        *)
@@@ -1264,7 -1248,11 +1265,7 @@@ _git_checkout (
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
                ;;
        --*)
 -              __gitcomp "
 -                      --quiet --ours --theirs --track --no-track --merge
 -                      --conflict= --orphan --patch --detach --ignore-skip-worktree-bits
 -                      --recurse-submodules --no-recurse-submodules
 -                      "
 +              __gitcomp_builtin checkout "--no-track --no-recurse-submodules"
                ;;
        *)
                # check if --track, --no-track, or --no-guess was specified
  
  _git_cherry ()
  {
 +      case "$cur" in
 +      --*)
 +              __gitcomp_builtin cherry
 +              return
 +      esac
 +
        __git_complete_refs
  }
  
 +__git_cherry_pick_inprogress_options="--continue --quit --abort"
 +
  _git_cherry_pick ()
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
 -              __gitcomp "--continue --quit --abort"
 +              __gitcomp "$__git_cherry_pick_inprogress_options"
                return
        fi
        case "$cur" in
        --*)
 -              __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
 +              __gitcomp_builtin cherry-pick "" \
 +                      "$__git_cherry_pick_inprogress_options"
                ;;
        *)
                __git_complete_refs
@@@ -1314,7 -1293,7 +1315,7 @@@ _git_clean (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--dry-run --quiet"
 +              __gitcomp_builtin clean
                return
                ;;
        esac
@@@ -1327,7 -1306,26 +1328,7 @@@ _git_clone (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --local
 -                      --no-hardlinks
 -                      --shared
 -                      --reference
 -                      --quiet
 -                      --no-checkout
 -                      --bare
 -                      --mirror
 -                      --origin
 -                      --upload-pack
 -                      --template=
 -                      --depth
 -                      --single-branch
 -                      --no-tags
 -                      --branch
 -                      --recurse-submodules
 -                      --no-single-branch
 -                      --shallow-submodules
 -                      "
 +              __gitcomp_builtin clone "--no-single-branch"
                return
                ;;
        esac
@@@ -1360,7 -1358,16 +1361,7 @@@ _git_commit (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --all --author= --signoff --verify --no-verify
 -                      --edit --no-edit
 -                      --amend --include --only --interactive
 -                      --dry-run --reuse-message= --reedit-message=
 -                      --reset-author --file= --message= --template=
 -                      --cleanup= --untracked-files --untracked-files=
 -                      --verbose --quiet --fixup= --squash=
 -                      --patch --short --date --allow-empty
 -                      "
 +              __gitcomp_builtin commit "--no-edit --verify"
                return
        esac
  
@@@ -1376,7 -1383,11 +1377,7 @@@ _git_describe (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --all --tags --contains --abbrev= --candidates=
 -                      --exact-match --debug --long --match --always --first-parent
 -                      --exclude --dirty --broken
 -                      "
 +              __gitcomp_builtin describe
                return
        esac
        __git_complete_refs
@@@ -1401,7 -1412,7 +1402,7 @@@ __git_diff_common_options="--stat --num
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
                        --diff-algorithm=
 -                      --submodule --submodule=
 +                      --submodule --submodule= --ignore-submodules
  "
  
  _git_diff ()
@@@ -1442,11 -1453,11 +1443,11 @@@ _git_difftool (
                return
                ;;
        --*)
 -              __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
 -                      --base --ours --theirs
 -                      --no-renames --diff-filter= --find-copies-harder
 -                      --relative --ignore-submodules
 -                      --tool="
 +              __gitcomp_builtin difftool "$__git_diff_common_options
 +                                      --base --cached --ours --theirs
 +                                      --pickaxe-all --pickaxe-regex
 +                                      --relative --staged
 +                                      "
                return
                ;;
        esac
  
  __git_fetch_recurse_submodules="yes on-demand no"
  
 -__git_fetch_options="
 -      --quiet --verbose --append --upload-pack --force --keep --depth=
 -      --tags --no-tags --all --prune --dry-run --recurse-submodules=
 -      --unshallow --update-shallow
 -"
 -
  _git_fetch ()
  {
        case "$cur" in
                return
                ;;
        --*)
 -              __gitcomp "$__git_fetch_options"
 +              __gitcomp_builtin fetch "--no-tags"
                return
                ;;
        esac
@@@ -1500,7 -1517,20 +1501,7 @@@ _git_fsck (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --tags --root --unreachable --cache --no-reflogs --full
 -                      --strict --verbose --lost-found --name-objects
 -                      "
 -              return
 -              ;;
 -      esac
 -}
 -
 -_git_gc ()
 -{
 -      case "$cur" in
 -      --*)
 -              __gitcomp "--prune --aggressive"
 +              __gitcomp_builtin fsck "--no-reflogs"
                return
                ;;
        esac
@@@ -1556,7 -1586,21 +1557,7 @@@ _git_grep (
  
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --cached
 -                      --text --ignore-case --word-regexp --invert-match
 -                      --full-name --line-number
 -                      --extended-regexp --basic-regexp --fixed-strings
 -                      --perl-regexp
 -                      --threads
 -                      --files-with-matches --name-only
 -                      --files-without-match
 -                      --max-depth
 -                      --count
 -                      --and --or --not --all-match
 -                      --break --heading --show-function --function-context
 -                      --untracked --no-index
 -                      "
 +              __gitcomp_builtin grep
                return
                ;;
        esac
@@@ -1574,7 -1618,7 +1575,7 @@@ _git_help (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--all --guides --info --man --web"
 +              __gitcomp_builtin help
                return
                ;;
        esac
@@@ -1597,7 -1641,7 +1598,7 @@@ _git_init (
                return
                ;;
        --*)
 -              __gitcomp "--quiet --bare --template= --shared --shared="
 +              __gitcomp_builtin init
                return
                ;;
        esac
@@@ -1607,7 -1651,13 +1608,7 @@@ _git_ls_files (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--cached --deleted --modified --others --ignored
 -                      --stage --directory --no-empty-directory --unmerged
 -                      --killed --exclude= --exclude-from=
 -                      --exclude-per-directory= --exclude-standard
 -                      --error-unmatch --with-tree= --full-name
 -                      --abbrev --ignored --exclude-per-directory
 -                      "
 +              __gitcomp_builtin ls-files "--no-empty-directory"
                return
                ;;
        esac
@@@ -1621,7 -1671,7 +1622,7 @@@ _git_ls_remote (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--heads --tags --refs --get-url --symref"
 +              __gitcomp_builtin ls-remote
                return
                ;;
        esac
  
  _git_ls_tree ()
  {
 +      case "$cur" in
 +      --*)
 +              __gitcomp_builtin ls-tree
 +              return
 +              ;;
 +      esac
 +
        __git_complete_file
  }
  
@@@ -1752,18 -1795,22 +1753,18 @@@ _git_log (
        __git_complete_revlist
  }
  
 -# Common merge options shared by git-merge(1) and git-pull(1).
 -__git_merge_options="
 -      --no-commit --no-stat --log --no-log --squash --strategy
 -      --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
 -      --verify-signatures --no-verify-signatures --gpg-sign
 -      --quiet --verbose --progress --no-progress
 -"
 -
  _git_merge ()
  {
        __git_complete_strategy && return
  
        case "$cur" in
        --*)
 -              __gitcomp "$__git_merge_options
 -                      --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
 +              __gitcomp_builtin merge "--no-rerere-autoupdate
 +                              --no-commit --no-edit --no-ff
 +                              --no-log --no-progress
 +                              --no-squash --no-stat
 +                              --no-verify-signatures
 +                              "
                return
        esac
        __git_complete_refs
@@@ -1787,7 -1834,7 +1788,7 @@@ _git_merge_base (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--octopus --independent --is-ancestor --fork-point"
 +              __gitcomp_builtin merge-base
                return
                ;;
        esac
@@@ -1798,7 -1845,7 +1799,7 @@@ _git_mv (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--dry-run"
 +              __gitcomp_builtin mv
                return
                ;;
        esac
        fi
  }
  
 -_git_name_rev ()
 -{
 -      __gitcomp "--tags --all --stdin"
 -}
 -
  _git_notes ()
  {
 -      local subcommands='add append copy edit list prune remove show'
 +      local subcommands='add append copy edit get-ref list merge prune remove show'
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
  
        case "$subcommand,$cur" in
        ,--*)
 -              __gitcomp '--ref'
 +              __gitcomp_builtin notes
                ;;
        ,*)
                case "$prev" in
                        ;;
                esac
                ;;
 -      add,--reuse-message=*|append,--reuse-message=*|\
 -      add,--reedit-message=*|append,--reedit-message=*)
 +      *,--reuse-message=*|*,--reedit-message=*)
                __git_complete_refs --cur="${cur#*=}"
                ;;
 -      add,--*|append,--*)
 -              __gitcomp '--file= --message= --reedit-message=
 -                              --reuse-message='
 -              ;;
 -      copy,--*)
 -              __gitcomp '--stdin'
 -              ;;
 -      prune,--*)
 -              __gitcomp '--dry-run --verbose'
 +      *,--*)
 +              __gitcomp_builtin notes_$subcommand
                ;;
 -      prune,*)
 +      prune,*|get-ref,*)
 +              # this command does not take a ref, do not complete it
                ;;
        *)
                case "$prev" in
@@@ -1862,11 -1921,12 +1863,11 @@@ _git_pull (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --rebase --no-rebase
 -                      --autostash --no-autostash
 -                      $__git_merge_options
 -                      $__git_fetch_options
 -              "
 +              __gitcomp_builtin pull "--no-autostash --no-commit --no-edit
 +                                      --no-ff --no-log --no-progress --no-rebase
 +                                      --no-squash --no-stat --no-tags
 +                                      --no-verify-signatures"
 +
                return
                ;;
        esac
@@@ -1917,7 -1977,12 +1918,7 @@@ _git_push (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --all --mirror --tags --dry-run --force --verbose
 -                      --quiet --prune --delete --follow-tags
 -                      --receive-pack= --repo= --set-upstream
 -                      --force-with-lease --force-with-lease= --recurse-submodules=
 -              "
 +              __gitcomp_builtin push
                return
                ;;
        esac
@@@ -1928,11 -1993,11 +1929,11 @@@ _git_rebase (
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
 -              __gitcomp "--continue --skip --abort --quit --edit-todo"
 +              __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch"
                return
        elif [ -d "$__git_repo_path"/rebase-apply ] || \
             [ -d "$__git_repo_path"/rebase-merge ]; then
 -              __gitcomp "--continue --skip --abort --quit"
 +              __gitcomp "--continue --skip --abort --quit --show-current-patch"
                return
        fi
        __git_complete_strategy && return
                        --autostash --no-autostash
                        --verify --no-verify
                        --keep-empty --root --force-rebase --no-ff
 +                      --rerere-autoupdate
                        --exec
                        "
  
@@@ -2018,7 -2082,7 +2019,7 @@@ _git_send_email (
                        --compose --confirm= --dry-run --envelope-sender
                        --from --identity
                        --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
 -                      --no-suppress-from --no-thread --quiet
 +                      --no-suppress-from --no-thread --quiet --reply-to
                        --signed-off-by-cc --smtp-pass --smtp-server
                        --smtp-server-port --smtp-encryption= --smtp-user
                        --subject --suppress-cc= --suppress-from --thread --to
@@@ -2056,7 -2120,11 +2057,7 @@@ _git_status (
                return
                ;;
        --*)
 -              __gitcomp "
 -                      --short --branch --porcelain --long --verbose
 -                      --untracked-files= --ignore-submodules= --ignored
 -                      --column= --no-column
 -                      "
 +              __gitcomp_builtin status "--no-column"
                return
                ;;
        esac
@@@ -2198,7 -2266,14 +2199,7 @@@ _git_config (
        esac
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --system --global --local --file=
 -                      --list --replace-all
 -                      --get --get-all --get-regexp
 -                      --add --unset --unset-all
 -                      --remove-section --rename-section
 -                      --name-only
 -                      "
 +              __gitcomp_builtin config
                return
                ;;
        branch.*.*)
                core.bigFileThreshold
                core.checkStat
                core.commentChar
+               core.commitGraph
                core.compression
                core.createObject
                core.deltaBaseCacheLimit
@@@ -2598,7 -2674,7 +2600,7 @@@ _git_remote (
        if [ -z "$subcommand" ]; then
                case "$cur" in
                --*)
 -                      __gitcomp "--verbose"
 +                      __gitcomp_builtin remote
                        ;;
                *)
                        __gitcomp "$subcommands"
  
        case "$subcommand,$cur" in
        add,--*)
 -              __gitcomp "--track --master --fetch --tags --no-tags --mirror="
 +              __gitcomp_builtin remote_add "--no-tags"
                ;;
        add,*)
                ;;
        set-head,--*)
 -              __gitcomp "--auto --delete"
 +              __gitcomp_builtin remote_set-head
                ;;
        set-branches,--*)
 -              __gitcomp "--add"
 +              __gitcomp_builtin remote_set-branches
                ;;
        set-head,*|set-branches,*)
                __git_complete_remote_or_refspec
                ;;
        update,--*)
 -              __gitcomp "--prune"
 +              __gitcomp_builtin remote_update
                ;;
        update,*)
                __gitcomp "$(__git_get_config_variables "remotes")"
                ;;
        set-url,--*)
 -              __gitcomp "--push --add --delete"
 +              __gitcomp_builtin remote_set-url
                ;;
        get-url,--*)
 -              __gitcomp "--push --all"
 +              __gitcomp_builtin remote_get-url
                ;;
        prune,--*)
 -              __gitcomp "--dry-run"
 +              __gitcomp_builtin remote_prune
                ;;
        *)
                __gitcomp_nl "$(__git_remotes)"
@@@ -2647,7 -2723,7 +2649,7 @@@ _git_replace (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--edit --graft --format= --list --delete"
 +              __gitcomp_builtin replace
                return
                ;;
        esac
@@@ -2671,26 -2747,26 +2673,26 @@@ _git_reset (
  
        case "$cur" in
        --*)
 -              __gitcomp "--merge --mixed --hard --soft --patch --keep"
 +              __gitcomp_builtin reset
                return
                ;;
        esac
        __git_complete_refs
  }
  
 +__git_revert_inprogress_options="--continue --quit --abort"
 +
  _git_revert ()
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
 -              __gitcomp "--continue --quit --abort"
 +              __gitcomp "$__git_revert_inprogress_options"
                return
        fi
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --edit --mainline --no-edit --no-commit --signoff
 -                      --strategy= --strategy-option=
 -                      "
 +              __gitcomp_builtin revert "--no-edit" \
 +                      "$__git_revert_inprogress_options"
                return
                ;;
        esac
@@@ -2701,7 -2777,7 +2703,7 @@@ _git_rm (
  {
        case "$cur" in
        --*)
 -              __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
 +              __gitcomp_builtin rm
                return
                ;;
        esac
@@@ -2759,7 -2835,12 +2761,7 @@@ _git_show_branch (
  {
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --all --remotes --topo-order --date-order --current --more=
 -                      --list --independent --merge-base --no-name
 -                      --color --no-color
 -                      --sha1-name --sparse --topics --reflog
 -                      "
 +              __gitcomp_builtin show-branch "--no-color"
                return
                ;;
        esac
@@@ -2966,7 -3047,7 +2968,7 @@@ _git_tag (
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
 -              -d|-v)
 +              -d|--delete|-v|--verify)
                        __gitcomp_direct "$(__git_tags "" "$cur" " ")"
                        return
                        ;;
  
        case "$cur" in
        --*)
 -              __gitcomp "
 -                      --list --delete --verify --annotate --message --file
 -                      --sign --cleanup --local-user --force --column --sort=
 -                      --contains --no-contains --points-at --merged --no-merged --create-reflog
 -                      "
 +              __gitcomp_builtin tag
                ;;
        esac
  }
@@@ -3004,26 -3089,23 +3006,26 @@@ _git_whatchanged (
  
  _git_worktree ()
  {
 -      local subcommands="add list lock prune unlock"
 +      local subcommands="add list lock move prune remove unlock"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
                case "$subcommand,$cur" in
                add,--*)
 -                      __gitcomp "--detach"
 +                      __gitcomp_builtin worktree_add
                        ;;
                list,--*)
 -                      __gitcomp "--porcelain"
 +                      __gitcomp_builtin worktree_list
                        ;;
                lock,--*)
 -                      __gitcomp "--reason"
 +                      __gitcomp_builtin worktree_lock
                        ;;
                prune,--*)
 -                      __gitcomp "--dry-run --expire --verbose"
 +                      __gitcomp_builtin worktree_prune
 +                      ;;
 +              remove,--*)
 +                      __gitcomp "--force"
                        ;;
                *)
                        ;;
        fi
  }
  
 +__git_complete_common () {
 +      local command="$1"
 +
 +      case "$cur" in
 +      --*)
 +              __gitcomp_builtin "$command"
 +              ;;
 +      esac
 +}
 +
 +__git_cmds_with_parseopt_helper=
 +__git_support_parseopt_helper () {
 +      test -n "$__git_cmds_with_parseopt_helper" ||
 +              __git_cmds_with_parseopt_helper="$(__git --list-parseopt-builtins)"
 +
 +      case " $__git_cmds_with_parseopt_helper " in
 +      *" $1 "*)
 +              return 0
 +              ;;
 +      *)
 +              return 1
 +              ;;
 +      esac
 +}
 +
 +__git_complete_command () {
 +      local command="$1"
 +      local completion_func="_git_${command//-/_}"
 +      if declare -f $completion_func >/dev/null 2>/dev/null; then
 +              $completion_func
 +              return 0
 +      elif __git_support_parseopt_helper "$command"; then
 +              __git_complete_common "$command"
 +              return 0
 +      else
 +              return 1
 +      fi
 +}
 +
  __git_main ()
  {
        local i c=1 command __git_dir __git_repo_path
                return
        fi
  
 -      local completion_func="_git_${command//-/_}"
 -      declare -f $completion_func >/dev/null 2>/dev/null && $completion_func && return
 +      __git_complete_command "$command" && return
  
        local expansion=$(__git_aliased_command "$command")
        if [ -n "$expansion" ]; then
                words[1]=$expansion
 -              completion_func="_git_${expansion//-/_}"
 -              declare -f $completion_func >/dev/null 2>/dev/null && $completion_func
 +              __git_complete_command "$expansion"
        fi
  }
  
diff --combined environment.c
@@@ -13,9 -13,6 +13,9 @@@
  #include "refs.h"
  #include "fmt-merge-msg.h"
  #include "commit.h"
 +#include "argv-array.h"
 +#include "object-store.h"
 +#include "chdir-notify.h"
  
  int trust_executable_bit = 1;
  int trust_ctime = 1;
@@@ -65,6 -62,7 +65,7 @@@ enum push_default_type push_default = P
  enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
  char *notes_ref_name;
  int grafts_replace_parents = 1;
+ int core_commit_graph;
  int core_apply_sparse_checkout;
  int merge_log_config = -1;
  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
@@@ -103,7 -101,7 +104,7 @@@ int ignore_untracked_cache_config
  /* This is set by setup_git_dir_gently() and/or git_default_config() */
  char *git_work_tree_cfg;
  
 -static char *namespace;
 +static char *git_namespace;
  
  static const char *super_prefix;
  
@@@ -150,35 -148,10 +151,35 @@@ static char *expand_namespace(const cha
        return strbuf_detach(&buf, NULL);
  }
  
 -void setup_git_env(void)
 +/*
 + * Wrapper of getenv() that returns a strdup value. This value is kept
 + * in argv to be freed later.
 + */
 +static const char *getenv_safe(struct argv_array *argv, const char *name)
 +{
 +      const char *value = getenv(name);
 +
 +      if (!value)
 +              return NULL;
 +
 +      argv_array_push(argv, value);
 +      return argv->argv[argv->argc - 1];
 +}
 +
 +void setup_git_env(const char *git_dir)
  {
        const char *shallow_file;
        const char *replace_ref_base;
 +      struct set_gitdir_args args = { NULL };
 +      struct argv_array to_free = ARGV_ARRAY_INIT;
 +
 +      args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
 +      args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
 +      args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
 +      args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
 +      args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
 +      repo_set_gitdir(the_repository, git_dir, &args);
 +      argv_array_clear(&to_free);
  
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                check_replace_refs = 0;
        free(git_replace_ref_base);
        git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
                                                          : "refs/replace/");
 -      free(namespace);
 -      namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
 +      free(git_namespace);
 +      git_namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
        shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
        if (shallow_file)
                set_alternate_shallow_file(shallow_file, 0);
@@@ -221,9 -194,9 +222,9 @@@ const char *get_git_common_dir(void
  
  const char *get_git_namespace(void)
  {
 -      if (!namespace)
 +      if (!git_namespace)
                BUG("git environment hasn't been setup");
 -      return namespace;
 +      return git_namespace;
  }
  
  const char *strip_namespace(const char *namespaced_ref)
@@@ -272,12 -245,12 +273,12 @@@ const char *get_git_work_tree(void
  
  char *get_object_directory(void)
  {
 -      if (!the_repository->objectdir)
 +      if (!the_repository->objects->objectdir)
                BUG("git environment hasn't been setup");
 -      return the_repository->objectdir;
 +      return the_repository->objects->objectdir;
  }
  
 -int odb_mkstemp(struct strbuf *template, const char *pattern)
 +int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
  {
        int fd;
        /*
         * restrictive except to remove write permission.
         */
        int mode = 0444;
 -      git_path_buf(template, "objects/%s", pattern);
 -      fd = git_mkstemp_mode(template->buf, mode);
 +      git_path_buf(temp_filename, "objects/%s", pattern);
 +      fd = git_mkstemp_mode(temp_filename->buf, mode);
        if (0 <= fd)
                return fd;
  
        /* slow path */
 -      /* some mkstemp implementations erase template on failure */
 -      git_path_buf(template, "objects/%s", pattern);
 -      safe_create_leading_directories(template->buf);
 -      return xmkstemp_mode(template->buf, mode);
 +      /* some mkstemp implementations erase temp_filename on failure */
 +      git_path_buf(temp_filename, "objects/%s", pattern);
 +      safe_create_leading_directories(temp_filename->buf);
 +      return xmkstemp_mode(temp_filename->buf, mode);
  }
  
  int odb_pack_keep(const char *name)
@@@ -324,31 -297,13 +325,31 @@@ char *get_graft_file(void
        return the_repository->graft_file;
  }
  
 -int set_git_dir(const char *path)
 +static void set_git_dir_1(const char *path)
  {
        if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
 -              return error("Could not set GIT_DIR to '%s'", path);
 -      repo_set_gitdir(the_repository, path);
 -      setup_git_env();
 -      return 0;
 +              die("could not set GIT_DIR to '%s'", path);
 +      setup_git_env(path);
 +}
 +
 +static void update_relative_gitdir(const char *name,
 +                                 const char *old_cwd,
 +                                 const char *new_cwd,
 +                                 void *data)
 +{
 +      char *path = reparent_relative_path(old_cwd, new_cwd, get_git_dir());
 +      trace_printf_key(&trace_setup_key,
 +                       "setup: move $GIT_DIR to '%s'",
 +                       path);
 +      set_git_dir_1(path);
 +      free(path);
 +}
 +
 +void set_git_dir(const char *path)
 +{
 +      set_git_dir_1(path);
 +      if (!is_absolute_path(path))
 +              chdir_notify_register(NULL, update_relative_gitdir, NULL);
  }
  
  const char *get_log_output_encoding(void)
diff --combined fast-import.c
@@@ -154,7 -154,6 +154,7 @@@ Format of STDIN stream
  
  #include "builtin.h"
  #include "cache.h"
 +#include "repository.h"
  #include "config.h"
  #include "lockfile.h"
  #include "object.h"
  #include "dir.h"
  #include "run-command.h"
  #include "packfile.h"
 +#include "object-store.h"
 +#include "mem-pool.h"
  
  #define PACK_ID_BITS 16
  #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@@ -212,6 -209,13 +212,6 @@@ struct last_object 
        unsigned no_swap : 1;
  };
  
 -struct mem_pool {
 -      struct mem_pool *next_pool;
 -      char *next_free;
 -      char *end;
 -      uintmax_t space[FLEX_ARRAY]; /* more */
 -};
 -
  struct atom_str {
        struct atom_str *next_atom;
        unsigned short str_len;
@@@ -300,8 -304,9 +300,8 @@@ static int global_argc
  static const char **global_argv;
  
  /* Memory pools */
 -static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
 -static size_t total_allocd;
 -static struct mem_pool *mem_pool;
 +static struct mem_pool fi_mem_pool =  {NULL, 2*1024*1024 -
 +                                     sizeof(struct mp_block), 0 };
  
  /* Atom management */
  static unsigned int atom_table_sz = 4451;
@@@ -336,7 -341,6 +336,7 @@@ static unsigned int tree_entry_alloc = 
  static void *avail_tree_entry;
  static unsigned int avail_tree_table_sz = 100;
  static struct avail_tree_content **avail_tree_table;
 +static size_t tree_entry_allocd;
  static struct strbuf old_tree = STRBUF_INIT;
  static struct strbuf new_tree = STRBUF_INIT;
  
@@@ -630,10 -634,49 +630,10 @@@ static unsigned int hc_str(const char *
        return r;
  }
  
 -static void *pool_alloc(size_t len)
 -{
 -      struct mem_pool *p;
 -      void *r;
 -
 -      /* round up to a 'uintmax_t' alignment */
 -      if (len & (sizeof(uintmax_t) - 1))
 -              len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
 -
 -      for (p = mem_pool; p; p = p->next_pool)
 -              if ((p->end - p->next_free >= len))
 -                      break;
 -
 -      if (!p) {
 -              if (len >= (mem_pool_alloc/2)) {
 -                      total_allocd += len;
 -                      return xmalloc(len);
 -              }
 -              total_allocd += sizeof(struct mem_pool) + mem_pool_alloc;
 -              p = xmalloc(st_add(sizeof(struct mem_pool), mem_pool_alloc));
 -              p->next_pool = mem_pool;
 -              p->next_free = (char *) p->space;
 -              p->end = p->next_free + mem_pool_alloc;
 -              mem_pool = p;
 -      }
 -
 -      r = p->next_free;
 -      p->next_free += len;
 -      return r;
 -}
 -
 -static void *pool_calloc(size_t count, size_t size)
 -{
 -      size_t len = count * size;
 -      void *r = pool_alloc(len);
 -      memset(r, 0, len);
 -      return r;
 -}
 -
  static char *pool_strdup(const char *s)
  {
        size_t len = strlen(s) + 1;
 -      char *r = pool_alloc(len);
 +      char *r = mem_pool_alloc(&fi_mem_pool, len);
        memcpy(r, s, len);
        return r;
  }
@@@ -642,7 -685,7 +642,7 @@@ static void insert_mark(uintmax_t idnum
  {
        struct mark_set *s = marks;
        while ((idnum >> s->shift) >= 1024) {
 -              s = pool_calloc(1, sizeof(struct mark_set));
 +              s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
                s->shift = marks->shift + 10;
                s->data.sets[0] = marks;
                marks = s;
                uintmax_t i = idnum >> s->shift;
                idnum -= i << s->shift;
                if (!s->data.sets[i]) {
 -                      s->data.sets[i] = pool_calloc(1, sizeof(struct mark_set));
 +                      s->data.sets[i] = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
                        s->data.sets[i]->shift = s->shift - 10;
                }
                s = s->data.sets[i];
@@@ -689,7 -732,7 +689,7 @@@ static struct atom_str *to_atom(const c
                if (c->str_len == len && !strncmp(s, c->str_dat, len))
                        return c;
  
 -      c = pool_alloc(sizeof(struct atom_str) + len + 1);
 +      c = mem_pool_alloc(&fi_mem_pool, sizeof(struct atom_str) + len + 1);
        c->str_len = len;
        memcpy(c->str_dat, s, len);
        c->str_dat[len] = 0;
@@@ -720,7 -763,7 +720,7 @@@ static struct branch *new_branch(const 
        if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL))
                die("Branch name doesn't conform to GIT standards: %s", name);
  
 -      b = pool_calloc(1, sizeof(struct branch));
 +      b = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct branch));
        b->name = pool_strdup(name);
        b->table_next_branch = branch_table[hc];
        b->branch_tree.versions[0].mode = S_IFDIR;
@@@ -756,7 -799,7 +756,7 @@@ static struct tree_content *new_tree_co
                        avail_tree_table[hc] = f->next_avail;
        } else {
                cnt = cnt & 7 ? ((cnt / 8) + 1) * 8 : cnt;
 -              f = pool_alloc(sizeof(*t) + sizeof(t->entries[0]) * cnt);
 +              f = mem_pool_alloc(&fi_mem_pool, sizeof(*t) + sizeof(t->entries[0]) * cnt);
                f->entry_capacity = cnt;
        }
  
@@@ -801,7 -844,7 +801,7 @@@ static struct tree_entry *new_tree_entr
  
        if (!avail_tree_entry) {
                unsigned int n = tree_entry_alloc;
 -              total_allocd += n * sizeof(struct tree_entry);
 +              tree_entry_allocd += n * sizeof(struct tree_entry);
                ALLOC_ARRAY(e, n);
                avail_tree_entry = e;
                while (n-- > 1) {
@@@ -973,7 -1016,7 +973,7 @@@ static void end_packfile(void
                struct tag *t;
  
                close_pack_windows(pack_data);
-               hashclose(pack_file, cur_pack_oid.hash, 0);
+               finalize_hashfile(pack_file, cur_pack_oid.hash, 0);
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
                                    cur_pack_oid.hash, pack_size);
                if (!new_p)
                        die("core git rejected index %s", idx_name);
                all_packs[pack_id] = new_p;
 -              install_packed_git(new_p);
 +              install_packed_git(the_repository, new_p);
                free(idx_name);
  
                /* Print the boundary */
@@@ -1053,7 -1096,7 +1053,7 @@@ static int store_object
        git_zstream s;
  
        hdrlen = xsnprintf((char *)hdr, sizeof(hdr), "%s %lu",
 -                         typename(type), (unsigned long)dat->len) + 1;
 +                         type_name(type), (unsigned long)dat->len) + 1;
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, hdr, hdrlen);
        the_hash_algo->update_fn(&c, dat->buf, dat->len);
        if (e->idx.offset) {
                duplicate_count_by_type[type]++;
                return 1;
 -      } else if (find_sha1_pack(oid.hash, packed_git)) {
 +      } else if (find_sha1_pack(oid.hash,
 +                                get_packed_git(the_repository))) {
                e->type = type;
                e->pack_id = MAX_PACK_ID;
                e->idx.offset = 1; /* just not zero! */
@@@ -1265,8 -1307,7 +1265,8 @@@ static void stream_blob(uintmax_t len, 
                duplicate_count_by_type[OBJ_BLOB]++;
                truncate_pack(&checkpoint);
  
 -      } else if (find_sha1_pack(oid.hash, packed_git)) {
 +      } else if (find_sha1_pack(oid.hash,
 +                                get_packed_git(the_repository))) {
                e->type = OBJ_BLOB;
                e->pack_id = MAX_PACK_ID;
                e->idx.offset = 1; /* just not zero! */
@@@ -1371,7 -1412,7 +1371,7 @@@ static void load_tree(struct tree_entr
                        die("Can't load tree %s", oid_to_hex(oid));
        } else {
                enum object_type type;
 -              buf = read_sha1_file(oid->hash, &type, &size);
 +              buf = read_object_file(oid, &type, &size);
                if (!buf || type != OBJ_TREE)
                        die("Can't load tree %s", oid_to_hex(oid));
        }
@@@ -1872,7 -1913,7 +1872,7 @@@ static void read_marks(void
                        die("corrupt mark line: %s", line);
                e = find_object(&oid);
                if (!e) {
 -                      enum object_type type = sha1_object_info(oid.hash, NULL);
 +                      enum object_type type = oid_object_info(&oid, NULL);
                        if (type < 0)
                                die("object not found: %s", oid_to_hex(&oid));
                        e = insert_object(&oid);
@@@ -2382,7 -2423,7 +2382,7 @@@ static void file_change_m(const char *p
                else if (oe) {
                        if (oe->type != OBJ_COMMIT)
                                die("Not a commit (actually a %s): %s",
 -                                      typename(oe->type), command_buf.buf);
 +                                      type_name(oe->type), command_buf.buf);
                }
                /*
                 * Accept the sha1 without checking; it expected to be in
                enum object_type expected = S_ISDIR(mode) ?
                                                OBJ_TREE: OBJ_BLOB;
                enum object_type type = oe ? oe->type :
 -                                      sha1_object_info(oid.hash, NULL);
 +                                      oid_object_info(&oid, NULL);
                if (type < 0)
                        die("%s not found: %s",
                                        S_ISDIR(mode) ?  "Tree" : "Blob",
                                        command_buf.buf);
                if (type != expected)
                        die("Not a %s (actually a %s): %s",
 -                              typename(expected), typename(type),
 +                              type_name(expected), type_name(type),
                                command_buf.buf);
        }
  
@@@ -2542,9 -2583,8 +2542,9 @@@ static void note_change_n(const char *p
                oidcpy(&commit_oid, &commit_oe->idx.oid);
        } else if (!get_oid(p, &commit_oid)) {
                unsigned long size;
 -              char *buf = read_object_with_reference(commit_oid.hash,
 -                      commit_type, &size, commit_oid.hash);
 +              char *buf = read_object_with_reference(&commit_oid,
 +                                                     commit_type, &size,
 +                                                     &commit_oid);
                if (!buf || size < 46)
                        die("Not a valid commit: %s", p);
                free(buf);
        } else if (oe) {
                if (oe->type != OBJ_BLOB)
                        die("Not a blob (actually a %s): %s",
 -                              typename(oe->type), command_buf.buf);
 +                              type_name(oe->type), command_buf.buf);
        } else if (!is_null_oid(&oid)) {
 -              enum object_type type = sha1_object_info(oid.hash, NULL);
 +              enum object_type type = oid_object_info(&oid, NULL);
                if (type < 0)
                        die("Blob not found: %s", command_buf.buf);
                if (type != OBJ_BLOB)
                        die("Not a blob (actually a %s): %s",
 -                          typename(type), command_buf.buf);
 +                          type_name(type), command_buf.buf);
        }
  
        construct_path_with_fanout(oid_to_hex(&commit_oid), *old_fanout, path);
@@@ -2613,8 -2653,9 +2613,8 @@@ static void parse_from_existing(struct 
                unsigned long size;
                char *buf;
  
 -              buf = read_object_with_reference(b->oid.hash,
 -                                               commit_type, &size,
 -                                               b->oid.hash);
 +              buf = read_object_with_reference(&b->oid, commit_type, &size,
 +                                               &b->oid);
                parse_from_commit(b, buf, size);
                free(buf);
        }
@@@ -2691,9 -2732,8 +2691,9 @@@ static struct hash_list *parse_merge(un
                        oidcpy(&n->oid, &oe->idx.oid);
                } else if (!get_oid(from, &n->oid)) {
                        unsigned long size;
 -                      char *buf = read_object_with_reference(n->oid.hash,
 -                              commit_type, &size, n->oid.hash);
 +                      char *buf = read_object_with_reference(&n->oid,
 +                                                             commit_type,
 +                                                             &size, &n->oid);
                        if (!buf || size < 46)
                                die("Not a valid commit: %s", from);
                        free(buf);
@@@ -2822,7 -2862,7 +2822,7 @@@ static void parse_new_tag(const char *a
        enum object_type type;
        const char *v;
  
 -      t = pool_alloc(sizeof(struct tag));
 +      t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag));
        memset(t, 0, sizeof(struct tag));
        t->name = pool_strdup(arg);
        if (last_tag)
        } else if (!get_oid(from, &oid)) {
                struct object_entry *oe = find_object(&oid);
                if (!oe) {
 -                      type = sha1_object_info(oid.hash, NULL);
 +                      type = oid_object_info(&oid, NULL);
                        if (type < 0)
                                die("Not a valid object: %s", from);
                } else
                    "object %s\n"
                    "type %s\n"
                    "tag %s\n",
 -                  oid_to_hex(&oid), typename(type), t->name);
 +                  oid_to_hex(&oid), type_name(type), t->name);
        if (tagger)
                strbuf_addf(&new_data,
                            "tagger %s\n", tagger);
@@@ -2926,7 -2966,7 +2926,7 @@@ static void cat_blob(struct object_entr
        char *buf;
  
        if (!oe || oe->pack_id == MAX_PACK_ID) {
 -              buf = read_sha1_file(oid->hash, &type, &size);
 +              buf = read_object_file(oid, &type, &size);
        } else {
                type = oe->type;
                buf = gfi_unpack_entry(oe, &size);
                die("Can't read object %s", oid_to_hex(oid));
        if (type != OBJ_BLOB)
                die("Object %s is a %s but a blob was expected.",
 -                  oid_to_hex(oid), typename(type));
 +                  oid_to_hex(oid), type_name(type));
        strbuf_reset(&line);
        strbuf_addf(&line, "%s %s %lu\n", oid_to_hex(oid),
 -                                              typename(type), size);
 +                                              type_name(type), size);
        cat_blob_write(line.buf, line.len);
        strbuf_release(&line);
        cat_blob_write(buf, size);
  
  static void parse_get_mark(const char *p)
  {
 -      struct object_entry *oe = oe;
 +      struct object_entry *oe;
        char output[GIT_MAX_HEXSZ + 2];
  
        /* get-mark SP <object> LF */
  
  static void parse_cat_blob(const char *p)
  {
 -      struct object_entry *oe = oe;
 +      struct object_entry *oe;
        struct object_id oid;
  
        /* cat-blob SP <object> LF */
@@@ -3008,7 -3048,7 +3008,7 @@@ static struct object_entry *dereference
        unsigned long size;
        char *buf = NULL;
        if (!oe) {
 -              enum object_type type = sha1_object_info(oid->hash, NULL);
 +              enum object_type type = oid_object_info(oid, NULL);
                if (type < 0)
                        die("object not found: %s", oid_to_hex(oid));
                /* cache it! */
                buf = gfi_unpack_entry(oe, &size);
        } else {
                enum object_type unused;
 -              buf = read_sha1_file(oid->hash, &unused, &size);
 +              buf = read_object_file(oid, &unused, &size);
        }
        if (!buf)
                die("Can't load object %s", oid_to_hex(oid));
@@@ -3421,16 -3461,17 +3421,16 @@@ int cmd_main(int argc, const char **arg
        atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
        branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
 -      marks = pool_calloc(1, sizeof(struct mark_set));
 +      marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
  
        global_argc = argc;
        global_argv = argv;
  
 -      rc_free = pool_alloc(cmd_save * sizeof(*rc_free));
 +      rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free));
        for (i = 0; i < (cmd_save - 1); i++)
                rc_free[i].next = &rc_free[i + 1];
        rc_free[cmd_save - 1].next = NULL;
  
 -      prepare_packed_git();
        start_packfile();
        set_die_routine(die_nicely);
        set_checkpoint_signal();
                fprintf(stderr, "Total branches:  %10lu (%10lu loads     )\n", branch_count, branch_load_count);
                fprintf(stderr, "      marks:     %10" PRIuMAX " (%10" PRIuMAX " unique    )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count);
                fprintf(stderr, "      atoms:     %10u\n", atom_cnt);
 -              fprintf(stderr, "Memory total:    %10" PRIuMAX " KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024);
 -              fprintf(stderr, "       pools:    %10lu KiB\n", (unsigned long)(total_allocd/1024));
 +              fprintf(stderr, "Memory total:    %10" PRIuMAX " KiB\n", (tree_entry_allocd + fi_mem_pool.pool_alloc + alloc_count*sizeof(struct object_entry))/1024);
 +              fprintf(stderr, "       pools:    %10lu KiB\n", (unsigned long)((tree_entry_allocd + fi_mem_pool.pool_alloc) /1024));
                fprintf(stderr, "     objects:    %10" PRIuMAX " KiB\n", (alloc_count*sizeof(struct object_entry))/1024);
                fprintf(stderr, "---------------------------------------------------------------------\n");
                pack_report();
diff --combined git.c
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -1,33 -1,15 +1,33 @@@
  #include "builtin.h"
  #include "config.h"
 -#include "exec_cmd.h"
 +#include "exec-cmd.h"
  #include "help.h"
  #include "run-command.h"
  
 +#define RUN_SETUP             (1<<0)
 +#define RUN_SETUP_GENTLY      (1<<1)
 +#define USE_PAGER             (1<<2)
 +/*
 + * require working tree to be present -- anything uses this needs
 + * RUN_SETUP for reading from the configuration file.
 + */
 +#define NEED_WORK_TREE                (1<<3)
 +#define SUPPORT_SUPER_PREFIX  (1<<4)
 +#define DELAY_PAGER_CONFIG    (1<<5)
 +#define NO_PARSEOPT           (1<<6) /* parse-options is not used */
 +
 +struct cmd_struct {
 +      const char *cmd;
 +      int (*fn)(int, const char **, const char *);
 +      unsigned int option;
 +};
 +
  const char git_usage_string[] =
 -      "git [--version] [--help] [-C <path>] [-c name=value]\n"
 -      "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 -      "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
 -      "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
 -      "           <command> [<args>]";
 +      N_("git [--version] [--help] [-C <path>] [-c <name>=<value>]\n"
 +         "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 +         "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
 +         "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
 +         "           <command> [<args>]");
  
  const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@@ -36,7 -18,7 +36,7 @@@
  
  static int use_pager = -1;
  
 -static void list_builtins(void);
 +static void list_builtins(unsigned int exclude_option, char sep);
  
  static void commit_pager_choice(void) {
        switch (use_pager) {
@@@ -83,7 -65,7 +83,7 @@@ static int handle_options(const char **
                 */
                if (skip_prefix(cmd, "--exec-path", &cmd)) {
                        if (*cmd == '=')
 -                              git_set_argv_exec_path(cmd + 1);
 +                              git_set_exec_path(cmd + 1);
                        else {
                                puts(git_exec_path());
                                exit(0);
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
                        if (*argc < 2) {
 -                              fprintf(stderr, "No directory given for --git-dir.\n" );
 +                              fprintf(stderr, _("no directory given for --git-dir\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
                        if (*argc < 2) {
 -                              fprintf(stderr, "No namespace given for --namespace.\n" );
 +                              fprintf(stderr, _("no namespace given for --namespace\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
 -                              fprintf(stderr, "No directory given for --work-tree.\n" );
 +                              fprintf(stderr, _("no directory given for --work-tree\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--super-prefix")) {
                        if (*argc < 2) {
 -                              fprintf(stderr, "No prefix given for --super-prefix.\n" );
 +                              fprintf(stderr, _("no prefix given for --super-prefix\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-c")) {
                        if (*argc < 2) {
 -                              fprintf(stderr, "-c expects a configuration string\n" );
 +                              fprintf(stderr, _("-c expects a configuration string\n" ));
                                usage(git_usage_string);
                        }
                        git_config_push_parameter((*argv)[1]);
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
                        if (*argc < 2) {
 -                              fprintf(stderr, "No directory given for -C.\n" );
 +                              fprintf(stderr, _("no directory given for -C\n" ));
                                usage(git_usage_string);
                        }
                        if ((*argv)[1][0]) {
                                if (chdir((*argv)[1]))
 -                                      die_errno("Cannot change to '%s'", (*argv)[1]);
 +                                      die_errno("cannot change to '%s'", (*argv)[1]);
                                if (envchanged)
                                        *envchanged = 1;
                        }
                        (*argv)++;
                        (*argc)--;
                } else if (!strcmp(cmd, "--list-builtins")) {
 -                      list_builtins();
 +                      list_builtins(0, '\n');
 +                      exit(0);
 +              } else if (!strcmp(cmd, "--list-parseopt-builtins")) {
 +                      list_builtins(NO_PARSEOPT, ' ');
                        exit(0);
                } else {
 -                      fprintf(stderr, "Unknown option: %s\n", cmd);
 +                      fprintf(stderr, _("unknown option: %s\n"), cmd);
                        usage(git_usage_string);
                }
  
@@@ -268,7 -247,7 +268,7 @@@ static int handle_alias(int *argcp, con
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
  
 -                      die_errno("While expanding alias '%s': '%s'",
 +                      die_errno("while expanding alias '%s': '%s'",
                            alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
                            split_cmdline_strerror(count));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
 -                      die("alias '%s' changes environment variables\n"
 -                               "You can use '!git' in the alias to do this.",
 +                      die("alias '%s' changes environment variables.\n"
 +                               "You can use '!git' in the alias to do this",
                                 alias_command);
                memmove(new_argv - option_count, new_argv,
                                count * sizeof(char *));
        return ret;
  }
  
 -#define RUN_SETUP             (1<<0)
 -#define RUN_SETUP_GENTLY      (1<<1)
 -#define USE_PAGER             (1<<2)
 -/*
 - * require working tree to be present -- anything uses this needs
 - * RUN_SETUP for reading from the configuration file.
 - */
 -#define NEED_WORK_TREE                (1<<3)
 -#define SUPPORT_SUPER_PREFIX  (1<<4)
 -#define DELAY_PAGER_CONFIG    (1<<5)
 -
 -struct cmd_struct {
 -      const char *cmd;
 -      int (*fn)(int, const char **, const char *);
 -      int option;
 -};
 -
  static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
  {
        int status, help;
  static struct cmd_struct commands[] = {
        { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
        { "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
 -      { "annotate", cmd_annotate, RUN_SETUP },
 +      { "annotate", cmd_annotate, RUN_SETUP | NO_PARSEOPT },
        { "apply", cmd_apply, RUN_SETUP_GENTLY },
        { "archive", cmd_archive, RUN_SETUP_GENTLY },
        { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
        { "blame", cmd_blame, RUN_SETUP },
        { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
 -      { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
 +      { "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
        { "cat-file", cmd_cat_file, RUN_SETUP },
        { "check-attr", cmd_check_attr, RUN_SETUP },
        { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
        { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
 -      { "check-ref-format", cmd_check_ref_format },
 +      { "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
        { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
        { "checkout-index", cmd_checkout_index,
                RUN_SETUP | NEED_WORK_TREE},
        { "clone", cmd_clone },
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
 -      { "commit-tree", cmd_commit_tree, RUN_SETUP },
 -      { "config", cmd_config, RUN_SETUP_GENTLY },
+       { "commit-graph", cmd_commit_graph, RUN_SETUP },
 +      { "commit-tree", cmd_commit_tree, RUN_SETUP | NO_PARSEOPT },
 +      { "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
        { "count-objects", cmd_count_objects, RUN_SETUP },
 -      { "credential", cmd_credential, RUN_SETUP_GENTLY },
 +      { "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
        { "describe", cmd_describe, RUN_SETUP },
 -      { "diff", cmd_diff },
 -      { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
 -      { "diff-index", cmd_diff_index, RUN_SETUP },
 -      { "diff-tree", cmd_diff_tree, RUN_SETUP },
 +      { "diff", cmd_diff, NO_PARSEOPT },
 +      { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
 +      { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
 +      { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
        { "difftool", cmd_difftool, RUN_SETUP | NEED_WORK_TREE },
        { "fast-export", cmd_fast_export, RUN_SETUP },
        { "fetch", cmd_fetch, RUN_SETUP },
 -      { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
 +      { "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
        { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
        { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
        { "format-patch", cmd_format_patch, RUN_SETUP },
        { "fsck", cmd_fsck, RUN_SETUP },
        { "fsck-objects", cmd_fsck, RUN_SETUP },
        { "gc", cmd_gc, RUN_SETUP },
 -      { "get-tar-commit-id", cmd_get_tar_commit_id },
 +      { "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
        { "grep", cmd_grep, RUN_SETUP_GENTLY },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
 -      { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
 +      { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
        { "init", cmd_init_db },
        { "init-db", cmd_init_db },
        { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
        { "ls-files", cmd_ls_files, RUN_SETUP },
        { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
        { "ls-tree", cmd_ls_tree, RUN_SETUP },
 -      { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
 -      { "mailsplit", cmd_mailsplit },
 +      { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
 +      { "mailsplit", cmd_mailsplit, NO_PARSEOPT },
        { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
        { "merge-base", cmd_merge_base, RUN_SETUP },
        { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
 -      { "merge-index", cmd_merge_index, RUN_SETUP },
 -      { "merge-ours", cmd_merge_ours, RUN_SETUP },
 -      { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 -      { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 -      { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 -      { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 -      { "merge-tree", cmd_merge_tree, RUN_SETUP },
 -      { "mktag", cmd_mktag, RUN_SETUP },
 +      { "merge-index", cmd_merge_index, RUN_SETUP | NO_PARSEOPT },
 +      { "merge-ours", cmd_merge_ours, RUN_SETUP | NO_PARSEOPT },
 +      { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
 +      { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
 +      { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
 +      { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
 +      { "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
 +      { "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
        { "mktree", cmd_mktree, RUN_SETUP },
        { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
        { "name-rev", cmd_name_rev, RUN_SETUP },
        { "notes", cmd_notes, RUN_SETUP },
        { "pack-objects", cmd_pack_objects, RUN_SETUP },
 -      { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
 +      { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
        { "pack-refs", cmd_pack_refs, RUN_SETUP },
 -      { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY },
 +      { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
        { "pickaxe", cmd_blame, RUN_SETUP },
        { "prune", cmd_prune, RUN_SETUP },
        { "prune-packed", cmd_prune_packed, RUN_SETUP },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
        { "remote", cmd_remote, RUN_SETUP },
 -      { "remote-ext", cmd_remote_ext },
 -      { "remote-fd", cmd_remote_fd },
 +      { "remote-ext", cmd_remote_ext, NO_PARSEOPT },
 +      { "remote-fd", cmd_remote_fd, NO_PARSEOPT },
        { "repack", cmd_repack, RUN_SETUP },
        { "replace", cmd_replace, RUN_SETUP },
        { "rerere", cmd_rerere, RUN_SETUP },
        { "reset", cmd_reset, RUN_SETUP },
 -      { "rev-list", cmd_rev_list, RUN_SETUP },
 -      { "rev-parse", cmd_rev_parse },
 +      { "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
 +      { "rev-parse", cmd_rev_parse, NO_PARSEOPT },
        { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
        { "rm", cmd_rm, RUN_SETUP },
        { "send-pack", cmd_send_pack, RUN_SETUP },
 +      { "serve", cmd_serve, RUN_SETUP },
        { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
        { "show", cmd_show, RUN_SETUP },
        { "show-branch", cmd_show_branch, RUN_SETUP },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
 -      { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
 +      { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
        { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 -      { "unpack-file", cmd_unpack_file, RUN_SETUP },
 -      { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
 +      { "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
 +      { "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
        { "update-index", cmd_update_index, RUN_SETUP },
        { "update-ref", cmd_update_ref, RUN_SETUP },
        { "update-server-info", cmd_update_server_info, RUN_SETUP },
 -      { "upload-archive", cmd_upload_archive },
 -      { "upload-archive--writer", cmd_upload_archive_writer },
 -      { "var", cmd_var, RUN_SETUP_GENTLY },
 +      { "upload-archive", cmd_upload_archive, NO_PARSEOPT },
 +      { "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
 +      { "upload-pack", cmd_upload_pack },
 +      { "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
        { "verify-commit", cmd_verify_commit, RUN_SETUP },
        { "verify-pack", cmd_verify_pack },
        { "verify-tag", cmd_verify_tag, RUN_SETUP },
        { "version", cmd_version },
        { "whatchanged", cmd_whatchanged, RUN_SETUP },
 -      { "worktree", cmd_worktree, RUN_SETUP },
 +      { "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT },
        { "write-tree", cmd_write_tree, RUN_SETUP },
  };
  
@@@ -510,15 -505,11 +511,15 @@@ int is_builtin(const char *s
        return !!get_builtin(s);
  }
  
 -static void list_builtins(void)
 +static void list_builtins(unsigned int exclude_option, char sep)
  {
        int i;
 -      for (i = 0; i < ARRAY_SIZE(commands); i++)
 -              printf("%s\n", commands[i].cmd);
 +      for (i = 0; i < ARRAY_SIZE(commands); i++) {
 +              if (exclude_option &&
 +                  (commands[i].option & exclude_option))
 +                      continue;
 +              printf("%s%c", commands[i].cmd, sep);
 +      }
  }
  
  #ifdef STRIP_EXTENSION
@@@ -694,8 -685,8 +695,8 @@@ int cmd_main(int argc, const char **arg
                if (errno != ENOENT)
                        break;
                if (was_alias) {
 -                      fprintf(stderr, "Expansion of alias '%s' failed; "
 -                              "'%s' is not a git command\n",
 +                      fprintf(stderr, _("expansion of alias '%s' failed; "
 +                                        "'%s' is not a git command\n"),
                                cmd, argv[0]);
                        exit(1);
                }
                        break;
        }
  
 -      fprintf(stderr, "Failed to run command '%s': %s\n",
 +      fprintf(stderr, _("failed to run command '%s': %s\n"),
                cmd, strerror(errno));
  
        return 1;
diff --combined pack-bitmap-write.c
@@@ -73,7 -73,8 +73,7 @@@ void bitmap_writer_build_type_index(str
                        break;
  
                default:
 -                      real_type = sha1_object_info(entry->idx.oid.hash,
 -                                                   NULL);
 +                      real_type = oid_object_info(&entry->idx.oid, NULL);
                        break;
                }
  
@@@ -534,7 -535,7 +534,7 @@@ void bitmap_writer_finish(struct pack_i
        if (options & BITMAP_OPT_HASH_CACHE)
                write_hash_cache(f, index, index_nr);
  
-       hashclose(f, NULL, CSUM_FSYNC);
+       finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
  
        if (adjust_shared_perm(tmp_file.buf))
                die_errno("unable to make temporary bitmap file readable");
diff --combined packfile.c
@@@ -1,7 -1,6 +1,7 @@@
  #include "cache.h"
  #include "list.h"
  #include "pack.h"
 +#include "repository.h"
  #include "dir.h"
  #include "mergesort.h"
  #include "packfile.h"
@@@ -14,7 -13,6 +14,7 @@@
  #include "tag.h"
  #include "tree-walk.h"
  #include "tree.h"
 +#include "object-store.h"
  
  char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@@ -46,6 -44,8 +46,6 @@@ static unsigned int pack_open_fds
  static unsigned int pack_max_fds;
  static size_t peak_pack_mapped;
  static size_t pack_mapped;
 -struct packed_git *packed_git;
 -LIST_HEAD(packed_git_mru);
  
  #define SZ_FMT PRIuMAX
  static inline uintmax_t sz_fmt(size_t s) { return s; }
@@@ -245,7 -245,7 +245,7 @@@ static int unuse_one_window(struct pack
  
        if (current)
                scan_windows(current, &lru_p, &lru_w, &lru_l);
 -      for (p = packed_git; p; p = p->next)
 +      for (p = the_repository->objects->packed_git; p; p = p->next)
                scan_windows(p, &lru_p, &lru_w, &lru_l);
        if (lru_p) {
                munmap(lru_w->base, lru_w->len);
@@@ -304,18 -304,18 +304,18 @@@ void close_pack_index(struct packed_gi
        }
  }
  
static void close_pack(struct packed_git *p)
+ void close_pack(struct packed_git *p)
  {
        close_pack_windows(p);
        close_pack_fd(p);
        close_pack_index(p);
  }
  
 -void close_all_packs(void)
 +void close_all_packs(struct raw_object_store *o)
  {
        struct packed_git *p;
  
 -      for (p = packed_git; p; p = p->next)
 +      for (p = o->packed_git; p; p = p->next)
                if (p->do_not_close)
                        die("BUG: want to close pack marked 'do-not-close'");
                else
@@@ -383,7 -383,7 +383,7 @@@ static int close_one_pack(void
        struct pack_window *mru_w = NULL;
        int accept_windows_inuse = 1;
  
 -      for (p = packed_git; p; p = p->next) {
 +      for (p = the_repository->objects->packed_git; p; p = p->next) {
                if (p->pack_fd == -1)
                        continue;
                find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
@@@ -680,13 -680,13 +680,13 @@@ struct packed_git *add_packed_git(cons
        return p;
  }
  
 -void install_packed_git(struct packed_git *pack)
 +void install_packed_git(struct repository *r, struct packed_git *pack)
  {
        if (pack->pack_fd != -1)
                pack_open_fds++;
  
 -      pack->next = packed_git;
 -      packed_git = pack;
 +      pack->next = r->objects->packed_git;
 +      r->objects->packed_git = pack;
  }
  
  void (*report_garbage)(unsigned seen_bits, const char *path);
@@@ -735,7 -735,7 +735,7 @@@ static void report_pack_garbage(struct 
        report_helper(list, seen_bits, first, list->nr);
  }
  
 -static void prepare_packed_git_one(char *objdir, int local)
 +static void prepare_packed_git_one(struct repository *r, char *objdir, int local)
  {
        struct strbuf path = STRBUF_INIT;
        size_t dirnamelen;
                base_len = path.len;
                if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
                        /* Don't reopen a pack we already have. */
 -                      for (p = packed_git; p; p = p->next) {
 +                      for (p = r->objects->packed_git; p;
 +                           p = p->next) {
                                size_t len;
                                if (strip_suffix(p->pack_name, ".pack", &len) &&
                                    len == base_len &&
                             * corresponding .pack file that we can map.
                             */
                            (p = add_packed_git(path.buf, path.len, local)) != NULL)
 -                              install_packed_git(p);
 +                              install_packed_git(r, p);
                }
  
                if (!report_garbage)
        strbuf_release(&path);
  }
  
 -static int approximate_object_count_valid;
 -
 +static void prepare_packed_git(struct repository *r);
  /*
   * Give a fast, rough count of the number of objects in the repository. This
   * ignores loose objects completely. If you have a lot of them, then either
   */
  unsigned long approximate_object_count(void)
  {
 -      static unsigned long count;
 -      if (!approximate_object_count_valid) {
 +      if (!the_repository->objects->approximate_object_count_valid) {
 +              unsigned long count;
                struct packed_git *p;
  
 -              prepare_packed_git();
 +              prepare_packed_git(the_repository);
                count = 0;
 -              for (p = packed_git; p; p = p->next) {
 +              for (p = the_repository->objects->packed_git; p; p = p->next) {
                        if (open_pack_index(p))
                                continue;
                        count += p->num_objects;
                }
 +              the_repository->objects->approximate_object_count = count;
        }
 -      return count;
 +      return the_repository->objects->approximate_object_count;
  }
  
  static void *get_next_packed_git(const void *p)
@@@ -867,55 -866,43 +867,55 @@@ static int sort_pack(const void *a_, co
        return -1;
  }
  
 -static void rearrange_packed_git(void)
 +static void rearrange_packed_git(struct repository *r)
  {
 -      packed_git = llist_mergesort(packed_git, get_next_packed_git,
 -                                   set_next_packed_git, sort_pack);
 +      r->objects->packed_git = llist_mergesort(
 +              r->objects->packed_git, get_next_packed_git,
 +              set_next_packed_git, sort_pack);
  }
  
 -static void prepare_packed_git_mru(void)
 +static void prepare_packed_git_mru(struct repository *r)
  {
        struct packed_git *p;
  
 -      INIT_LIST_HEAD(&packed_git_mru);
 +      INIT_LIST_HEAD(&r->objects->packed_git_mru);
  
 -      for (p = packed_git; p; p = p->next)
 -              list_add_tail(&p->mru, &packed_git_mru);
 +      for (p = r->objects->packed_git; p; p = p->next)
 +              list_add_tail(&p->mru, &r->objects->packed_git_mru);
  }
  
 -static int prepare_packed_git_run_once = 0;
 -void prepare_packed_git(void)
 +static void prepare_packed_git(struct repository *r)
  {
        struct alternate_object_database *alt;
  
 -      if (prepare_packed_git_run_once)
 +      if (r->objects->packed_git_initialized)
                return;
 -      prepare_packed_git_one(get_object_directory(), 1);
 -      prepare_alt_odb();
 -      for (alt = alt_odb_list; alt; alt = alt->next)
 -              prepare_packed_git_one(alt->path, 0);
 -      rearrange_packed_git();
 -      prepare_packed_git_mru();
 -      prepare_packed_git_run_once = 1;
 +      prepare_packed_git_one(r, r->objects->objectdir, 1);
 +      prepare_alt_odb(r);
 +      for (alt = r->objects->alt_odb_list; alt; alt = alt->next)
 +              prepare_packed_git_one(r, alt->path, 0);
 +      rearrange_packed_git(r);
 +      prepare_packed_git_mru(r);
 +      r->objects->packed_git_initialized = 1;
 +}
 +
 +void reprepare_packed_git(struct repository *r)
 +{
 +      r->objects->approximate_object_count_valid = 0;
 +      r->objects->packed_git_initialized = 0;
 +      prepare_packed_git(r);
 +}
 +
 +struct packed_git *get_packed_git(struct repository *r)
 +{
 +      prepare_packed_git(r);
 +      return r->objects->packed_git;
  }
  
 -void reprepare_packed_git(void)
 +struct list_head *get_packed_git_mru(struct repository *r)
  {
 -      approximate_object_count_valid = 0;
 -      prepare_packed_git_run_once = 0;
 -      prepare_packed_git();
 +      prepare_packed_git(r);
 +      return &r->objects->packed_git_mru;
  }
  
  unsigned long unpack_object_header_buffer(const unsigned char *buf,
@@@ -1026,7 -1013,7 +1026,7 @@@ const struct packed_git *has_packed_and
        struct packed_git *p;
        unsigned i;
  
 -      for (p = packed_git; p; p = p->next)
 +      for (p = the_repository->objects->packed_git; p; p = p->next)
                for (i = 0; i < p->num_bad_objects; i++)
                        if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
                                return p;
@@@ -1108,13 -1095,13 +1108,13 @@@ static int retry_bad_packed_offset(stru
  {
        int type;
        struct revindex_entry *revidx;
 -      const unsigned char *sha1;
 +      struct object_id oid;
        revidx = find_pack_revindex(p, obj_offset);
        if (!revidx)
                return OBJ_BAD;
 -      sha1 = nth_packed_object_sha1(p, revidx->nr);
 -      mark_bad_packed_object(p, sha1);
 -      type = sha1_object_info(sha1, NULL);
 +      nth_packed_object_oid(&oid, p, revidx->nr);
 +      mark_bad_packed_object(p, oid.hash);
 +      type = oid_object_info(&oid, NULL);
        if (type <= OBJ_NONE)
                return OBJ_BAD;
        return type;
@@@ -1374,16 -1361,16 +1374,16 @@@ int packed_object_info(struct packed_gi
                *oi->disk_sizep = revidx[1].offset - obj_offset;
        }
  
 -      if (oi->typep || oi->typename) {
 +      if (oi->typep || oi->type_name) {
                enum object_type ptot;
                ptot = packed_to_object_type(p, obj_offset, type, &w_curs,
                                             curpos);
                if (oi->typep)
                        *oi->typep = ptot;
 -              if (oi->typename) {
 -                      const char *tn = typename(ptot);
 +              if (oi->type_name) {
 +                      const char *tn = type_name(ptot);
                        if (tn)
 -                              strbuf_addstr(oi->typename, tn);
 +                              strbuf_addstr(oi->type_name, tn);
                }
                if (ptot < 0) {
                        type = OBJ_BAD;
@@@ -1465,7 -1452,7 +1465,7 @@@ struct unpack_entry_stack_ent 
        unsigned long size;
  };
  
 -static void *read_object(const unsigned char *sha1, enum object_type *type,
 +static void *read_object(const struct object_id *oid, enum object_type *type,
                         unsigned long *size)
  {
        struct object_info oi = OBJECT_INFO_INIT;
        oi.sizep = size;
        oi.contentp = &content;
  
 -      if (sha1_object_info_extended(sha1, &oi, 0) < 0)
 +      if (oid_object_info_extended(oid, &oi, 0) < 0)
                return NULL;
        return content;
  }
@@@ -1514,11 -1501,11 +1514,11 @@@ void *unpack_entry(struct packed_git *p
                        struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
                        off_t len = revidx[1].offset - obj_offset;
                        if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
 -                              const unsigned char *sha1 =
 -                                      nth_packed_object_sha1(p, revidx->nr);
 +                              struct object_id oid;
 +                              nth_packed_object_oid(&oid, p, revidx->nr);
                                error("bad packed object CRC for %s",
 -                                    sha1_to_hex(sha1));
 -                              mark_bad_packed_object(p, sha1);
 +                                    oid_to_hex(&oid));
 +                              mark_bad_packed_object(p, oid.hash);
                                data = NULL;
                                goto out;
                        }
                         * of a corrupted pack, and is better than failing outright.
                         */
                        struct revindex_entry *revidx;
 -                      const unsigned char *base_sha1;
 +                      struct object_id base_oid;
                        revidx = find_pack_revindex(p, obj_offset);
                        if (revidx) {
 -                              base_sha1 = nth_packed_object_sha1(p, revidx->nr);
 +                              nth_packed_object_oid(&base_oid, p, revidx->nr);
                                error("failed to read delta base object %s"
                                      " at offset %"PRIuMAX" from %s",
 -                                    sha1_to_hex(base_sha1), (uintmax_t)obj_offset,
 +                                    oid_to_hex(&base_oid), (uintmax_t)obj_offset,
                                      p->pack_name);
 -                              mark_bad_packed_object(p, base_sha1);
 -                              base = read_object(base_sha1, &type, &base_size);
 +                              mark_bad_packed_object(p, base_oid.hash);
 +                              base = read_object(&base_oid, &type, &base_size);
                                external_base = base;
                        }
                }
        return data;
  }
  
 +int bsearch_pack(const struct object_id *oid, const struct packed_git *p, uint32_t *result)
 +{
 +      const unsigned char *index_fanout = p->index_data;
 +      const unsigned char *index_lookup;
 +      int index_lookup_width;
 +
 +      if (!index_fanout)
 +              BUG("bsearch_pack called without a valid pack-index");
 +
 +      index_lookup = index_fanout + 4 * 256;
 +      if (p->index_version == 1) {
 +              index_lookup_width = 24;
 +              index_lookup += 4;
 +      } else {
 +              index_lookup_width = 20;
 +              index_fanout += 8;
 +              index_lookup += 8;
 +      }
 +
 +      return bsearch_hash(oid->hash, (const uint32_t*)index_fanout,
 +                          index_lookup, index_lookup_width, result);
 +}
 +
  const unsigned char *nth_packed_object_sha1(struct packed_git *p,
                                            uint32_t n)
  {
@@@ -1756,17 -1720,30 +1756,17 @@@ off_t nth_packed_object_offset(const st
  off_t find_pack_entry_one(const unsigned char *sha1,
                                  struct packed_git *p)
  {
 -      const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
 -      unsigned stride;
 +      struct object_id oid;
        uint32_t result;
  
        if (!index) {
                if (open_pack_index(p))
                        return 0;
 -              level1_ofs = p->index_data;
 -              index = p->index_data;
 -      }
 -      if (p->index_version > 1) {
 -              level1_ofs += 2;
 -              index += 8;
 -      }
 -      index += 4 * 256;
 -      if (p->index_version > 1) {
 -              stride = 20;
 -      } else {
 -              stride = 24;
 -              index += 4;
        }
  
 -      if (bsearch_hash(sha1, level1_ofs, index, stride, &result))
 +      hashcpy(oid.hash, sha1);
 +      if (bsearch_pack(&oid, p, &result))
                return nth_packed_object_offset(p, result);
        return 0;
  }
@@@ -1837,18 -1814,22 +1837,18 @@@ static int fill_pack_entry(const unsign
        return 1;
  }
  
 -/*
 - * Iff a pack file contains the object named by sha1, return true and
 - * store its location to e.
 - */
 -int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 +int find_pack_entry(struct repository *r, const unsigned char *sha1, struct pack_entry *e)
  {
        struct list_head *pos;
  
 -      prepare_packed_git();
 -      if (!packed_git)
 +      prepare_packed_git(r);
 +      if (!r->objects->packed_git)
                return 0;
  
 -      list_for_each(pos, &packed_git_mru) {
 +      list_for_each(pos, &r->objects->packed_git_mru) {
                struct packed_git *p = list_entry(pos, struct packed_git, mru);
                if (fill_pack_entry(sha1, e, p)) {
 -                      list_move(&p->mru, &packed_git_mru);
 +                      list_move(&p->mru, &r->objects->packed_git_mru);
                        return 1;
                }
        }
  int has_sha1_pack(const unsigned char *sha1)
  {
        struct pack_entry e;
 -      return find_pack_entry(sha1, &e);
 +      return find_pack_entry(the_repository, sha1, &e);
  }
  
  int has_pack_index(const unsigned char *sha1)
        return 1;
  }
  
static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
+ int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
  {
        uint32_t i;
        int r = 0;
@@@ -1894,8 -1875,8 +1894,8 @@@ int for_each_packed_object(each_packed_
        int r = 0;
        int pack_errors = 0;
  
 -      prepare_packed_git();
 -      for (p = packed_git; p; p = p->next) {
 +      prepare_packed_git(the_repository);
 +      for (p = the_repository->objects->packed_git; p; p = p->next) {
                if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
                        continue;
                if ((flags & FOR_EACH_OBJECT_PROMISOR_ONLY) &&
@@@ -1926,7 -1907,7 +1926,7 @@@ static int add_promisor_object(const st
  
        /*
         * If this is a tree, commit, or tag, the objects it refers
 -       * to are also promisor objects. (Blobs refer to no objects.)
 +       * to are also promisor objects. (Blobs refer to no objects->)
         */
        if (obj->type == OBJ_TREE) {
                struct tree *tree = (struct tree *)obj;
diff --combined packfile.h
@@@ -34,11 -34,9 +34,11 @@@ extern struct packed_git *parse_pack_in
  #define PACKDIR_FILE_GARBAGE 4
  extern void (*report_garbage)(unsigned seen_bits, const char *path);
  
 -extern void prepare_packed_git(void);
 -extern void reprepare_packed_git(void);
 -extern void install_packed_git(struct packed_git *pack);
 +extern void reprepare_packed_git(struct repository *r);
 +extern void install_packed_git(struct repository *r, struct packed_git *pack);
 +
 +struct packed_git *get_packed_git(struct repository *r);
 +struct list_head *get_packed_git_mru(struct repository *r);
  
  /*
   * Give a rough count of objects in the repository. This sacrifices accuracy
@@@ -65,7 -63,8 +65,8 @@@ extern void close_pack_index(struct pac
  
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
 -extern void close_all_packs(void);
+ extern void close_pack(struct packed_git *);
 +extern void close_all_packs(struct raw_object_store *o);
  extern void unuse_pack(struct pack_window **);
  extern void clear_delta_base_cache(void);
  extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
   */
  extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
  
 +/*
 + * Perform binary search on a pack-index for a given oid. Packfile is expected to
 + * have a valid pack-index.
 + *
 + * See 'bsearch_hash' for more information.
 + */
 +int bsearch_pack(const struct object_id *oid, const struct packed_git *p, uint32_t *result);
 +
  /*
   * Return the SHA-1 of the nth object within the specified packfile.
   * Open the index if it is not already open.  The return value points
@@@ -130,11 -121,7 +131,11 @@@ extern int packed_object_info(struct pa
  extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
  extern const struct packed_git *has_packed_and_bad(const unsigned char *sha1);
  
 -extern int find_pack_entry(const unsigned char *sha1, struct pack_entry *e);
 +/*
 + * Iff a pack file in the given repository contains the object named by sha1,
 + * return true and store its location to e.
 + */
 +extern int find_pack_entry(struct repository *r, const unsigned char *sha1, struct pack_entry *e);
  
  extern int has_sha1_pack(const unsigned char *sha1);
  
@@@ -154,6 -141,7 +155,7 @@@ typedef int each_packed_object_fn(cons
                                  struct packed_git *pack,
                                  uint32_t pos,
                                  void *data);
+ extern int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn, void *data);
  extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
  
  /*