Do Git Compression and HTTP Buffer Settings Really Speed Up Clones?


Two settings intended to speed up CI were compared using public repositories on GitHub and GitLab.

core.compression = 0
http.postBuffer = 1073741824

Each setting was tested separately and in combination using shallow clones over HTTPS with a full checkout.

Neither setting showed a consistent speedup. On both services, the received packfiles had identical sizes and SHA-256 hashes across configurations, as did the HTTP request counts and POST body sizes.

This article examines the Git documentation and the benchmark results.

Background

I came across articles recommending two ways to speed up CI checkouts: disabling compression and increasing the HTTP POST buffer. Their arguments were:

  • Disabling compression reduces CPU usage and allows more data to be transferred over the network.
  • Increasing the HTTP POST buffer reduces the number of round trips when transferring large repositories.

With settings like these, it is easy to confuse where they apply—the client or the server—and the direction of data transfer.

How Git Transfers Data

Objects, Loose Objects, and Packfiles

Git stores file contents, directory structures, and commits as objects. An object containing file contents is a blob.

Git primarily uses two storage formats:

Storage formatDescription
Loose objectStores each object in a separate file
PackfileCombines multiple objects into a single packfile

Packfiles can use delta encoding to exploit similarities between objects, as well as zlib compression. core.compression=0 controls the zlib compression level; it does not disable delta encoding.

When preparing a packfile, Git may also reuse existing compressed data. It does not necessarily decompress and recompress every object.

How Clone and Fetch Work

In a typical Git transfer over HTTPS, the client requests the objects it needs, and the server prepares a packfile for transmission.

Client                               Server
    │                                  │
    ├── Fetch request ────────────────>│
    │                            Prepare pack
    │<──────────────────── Send pack ──┤
Receive, verify, store                 │
    │                                  │

git clone creates a new local repository. By default, it also checks out the selected commit after downloading the objects, writing files into the working directory.

git fetch downloads objects and references into an existing local repository. Fetching alone does not update files in the working directory.

Checkout also involves reading compressed objects and writing files. Therefore, the total execution time of git clone includes more than network transfer.

How Push Works

With git push, the client sends the necessary objects to the server.

Client                               Server
    │                                  │
Prepare pack                           │
    ├── Send pack ────────────────────>│
    │                         Receive, verify,
    │                         and store
    │<─────────────────────── Response ─┤

Clone and push differ in which side prepares the packfile.

git config Changes Settings on the Machine Where It Runs

Running the following command on a CI runner changes the Git configuration for that runner’s user:

git config --global core.compression 0

The --global option applies to the current user’s Git configuration. It does not send configuration changes to GitHub or GitLab servers.

For a self-hosted Git server, changing the configuration read by the Git processes running on that server can affect server-side behavior. The relevant configuration files and user accounts depend on how the server is deployed.

Users of GitHub.com and GitLab.com cannot change the hosting service’s compression settings through their local git config.

For temporary experiments, git -c allows you to specify a setting without modifying any configuration files.

git -c core.compression=0 clone https://example.com/group/repo.git

In this example, the configuration is passed to the Git process and the Git subprocesses it launches.

What core.compression=0 Actually Changes

Default Compression Level

According to the official Git documentation, core.compression specifies the default compression level: 0 disables compression, -1 uses zlib’s default, and 1–9 offer different trade-offs between speed and size.

This value serves as the default for two other settings:

  • core.looseCompression: Compression level used when writing loose objects.
  • pack.compression: Compression level used when writing objects into packfiles.

Explicit values for these settings take precedence. With all compression settings unset, loose objects default to level 1 and packfiles to zlib’s default.

How Compression Settings Affect the Client

On the client, compression settings can affect:

  • Writing new loose objects with git add.
  • Creating packfiles with git gc or git repack.
  • Preparing packfiles for push.
  • Unpacking received packfiles and rewriting their contents as loose objects.

If compression accounts for a significant portion of execution time, lowering the compression level may reduce CPU usage. However, it may also increase storage requirements or the amount of data transmitted.

Changing the setting does not automatically recompress stored objects, as the Git documentation notes.

Client-Side Compression Settings Do Not Control Received Packfiles

The server prepares the packfile for a clone. The client’s core.compression=0 does not set the server’s compression level.

If the client stores the received packfile directly, the compression of the received objects remains unchanged. Decompression is still necessary when reading those objects.

If the client unpacks a received packfile into loose objects, it may recompress them locally. git unpack-objects extracts objects from a packfile and stores them as loose objects; local compression settings may affect this process.

The distinction is:

OperationEffect of client-side compression settings
Server prepares a packfile for cloneClient settings do not determine the compression level
Client writes new objects or packfilesLocal compression settings may affect the process

Disabling compression on the client does not make clone transfers uncompressed.

What Happens If Compression Is Disabled on the Server?

Changing server-side compression settings can affect newly compressed data. Its impact should be evaluated in terms of CPU time, transfer size, and client-side decompression time.

Reusing existing packfiles or compressed objects also affects the results. Even if compression is disabled on the server, not every transferred object will necessarily be uncompressed.

The GitHub and GitLab server configurations were not changed and were not accessible for modification.

What http.postBuffer Actually Changes

HTTP POST Buffering

http.postBuffer sets the buffer threshold for POST data sent over Git smart HTTP. The default is 1 MiB (1,048,576 bytes).

The tested value, 1,073,741,824 bytes, is 1 GiB.

git -c http.postBuffer=1073741824 clone https://example.com/group/repo.git

Clones also use POST requests to fetch objects, and the responses may contain large packfiles. But the two data sizes differ:

Client → Server: POST request body containing the fetch request
Server → Client: Packfile data returned in the response

http.postBuffer applies to client-to-server data, not to a download buffer holding the received packfile.

Data Larger Than the Buffer Can Still Be Sent

HTTP/1.1 supports chunked transfer encoding, which sends a body in chunks without knowing its total size in advance. The chunks belong to a single HTTP request.

Git documents using HTTP/1.1 chunked transfer encoding when POST data exceeds the buffer. HTTP/2 uses DATA frames instead. Neither requires a separate HTTP request for each chunk or frame.

When Should the Buffer Be Increased?

The Git FAQ suggests increasing the buffer for servers or proxies that mishandle chunked transfers. Normally, the default should be kept.

A large push may exceed 1 MiB, but transfers can proceed beyond the buffer threshold.

When Clone Requests Become Large

Clone and fetch POST bodies can grow large when they include many object IDs. Receiving a packfile of 100 MiB or several GiB does not mean the POST body is similarly large.

In the GitLab measurements, POST bodies were 375 and 201 bytes, while the received packfile was about 137 MiB.

A Large Repository Is Not the Same as a Large Push

Push size depends on the packfile needed to transfer objects missing from the destination, not just the repository’s total size.

OperationFactors affecting transfer size
Changing a few lines in a large repositoryThe push may be small if relatively few new objects are needed
Modifying every fileDepends on the number and size of objects and the effectiveness of delta compression
Replacing a large binary fileTransfer size may increase significantly if delta compression is ineffective
Initial push to an empty remoteMay require sending many objects, including repository history
Pushing many commits at onceDepends on the amount of history and object data missing from the server

Only clones were measured, not pushes, initial pushes, or pushes involving large changes.

Which Settings Affect Which Operations?

Operationcore.compression=0Increasing http.postBuffer
Client-side cloneMay affect local recompression, but not server-side packfile compressionGenerally no effect
Client-side fetchSame as cloneMay matter if requests contain many object IDs or other data
Client-side pushCan affect compression when creating the outgoing packfile, although existing compressed data may be reusedMay matter for large POST bodies, but transfers normally work with the default
Local add, gc, repackAffects compression of newly written objects or packfilesNo effect without HTTP transfers
Server handling clone or fetchServer-side compression settings may matter; client settings are not appliedDoes not configure the server’s buffer for receiving client POST requests
Server receiving a pushMay affect compression when the server rewrites objectsDoes not specify the maximum accepted POST body size

Benchmark Setup: GitHub and GitLab

Repositories

ItemGitHubGitLab
Repositorypytorch/pytorchgitlab-org/gitlab
Fixed tagv2.9.0v18.0.0-ee
Checked-out commit0fabc3ba448504fd9e5236e
Entries counted using git ls-files -z19,41787,073
Stored packfile size58,668,712 bytes (approximately 55.95 MiB)143,907,468 bytes (approximately 137.24 MiB)

git ls-files counts tracked entries, potentially including submodules. It does not count files inside submodules or the data a recursive clone would fetch.

Packfile sizes were measured from .git/objects/pack/*.pack after cloning. They exclude TLS, HTTP, and Git protocol overhead, so they are not total network transfer sizes. They also differ from the sizes of the full repository history and the working directory.

Environment and Measurement Method

  • OS: macOS
  • Git: git version 2.50.1 (Apple Git-155)
  • Transport: HTTPS to public repositories; HTTP/2 confirmed through tracing
  • Clone options: --depth=1 --single-branch --no-tags, with a fixed tag
  • Checkout: Full working-tree checkout using the default clone behavior; no partial clone or sparse checkout
  • Submodules: No recursive submodule fetching
  • Configuration isolation: Disabled system-level configuration and redirected the global configuration file to /dev/null
  • Test configurations: Passed through git -c without modifying configuration files
  • Measurement interval: From the start to the completion of the clone command, including network transfer, post-receive processing, and checkout
  • Timing: Python’s time.perf_counter()
  • HTTP tracing: GIT_TRACE_CURL and GIT_TRACE_CURL_NO_DATA=1
  • Iterations: Four configurations tested over three rounds on GitHub and five rounds on GitLab, for 15 and 25 runs respectively. Each run used a fresh directory.

Configurations Tested

ConfigurationSettings
DefaultNeither setting explicitly specified
Compression disabledcore.compression=0
Increased POST bufferhttp.postBuffer=1073741824
CombinedBoth settings enabled

GitHub Results

Times are in seconds; all measurements are shown.

ConfigurationRound 1Round 2Round 3Median
Default8.0198.3548.2218.221
Compression disabled8.05536.7608.0578.057
Increased POST buffer7.9017.9257.7977.901
Combined8.1738.2538.2538.253

No consistent improvement was observed. One run with compression disabled took 36.760 seconds; the cause was not investigated.

The following were identical across configurations:

  • GET requests: 1
  • POST requests: 2
  • POST request body Content-Length: 351 bytes and 201 bytes
  • Stored packfile size: 58,668,712 bytes
  • Packfile SHA-256: Identical across all 15 runs

GitLab Results

All timings are in seconds.

ConfigurationRound 1Round 2Round 3Round 4Round 5Median
Default14.96813.92614.19214.92814.86214.862
Compression disabled14.04914.53314.36016.38114.81914.533
Increased POST buffer14.43213.68113.95515.09413.45513.955
Combined14.70415.42016.58715.63415.14715.420

The GitLab results also showed no clear improvement.

The following were identical across configurations:

  • GET requests: 1
  • POST requests: 2
  • POST request body Content-Length: 375 bytes and 201 bytes
  • Stored packfile size: 143,907,468 bytes
  • Packfile SHA-256: Identical across all 25 runs

Even with a received packfile of about 137 MiB, the POST bodies were below the default 1 MiB threshold.

Conclusion

Client-side settings differ from server-side settings. The same issue often comes up with relational databases.

ci-perf-lint incorporates findings from investigations like this to help speed up CI with a single command.