{"article":{"slug":"how-big-is-a-git-commit","title":"How big is a Git commit?","subtitle":null,"summary":"Dave Gauer runs small experiments to measure how much space Git loose objects take per commit, from an empty repo and tiny one-line changes to a 580 KB binary and a 16 MB source file, and explains which commit, tree and blob objects each change adds.","content_type":"blog_post","language":"en","canonical_url":"https://ratfactor.com/cards/git-commit-size","author":{"name":"Dave Gauer","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"ratfactor","url":"https://ratfactor.com/","listing_slug":null,"listing":null},"topics":[{"name":"Git","slug":"git","url":"https://listedarticles.com/topics/git"},{"name":"Software Engineering","slug":"software-engineering","url":"https://listedarticles.com/topics/software-engineering"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1034,"reading_minutes":4,"published_at":"2026-10-11T08:12:41.188Z","added_at":"2026-10-11T08:12:41.188Z","updated_at":"2026-10-11T08:12:41.188Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/how-big-is-a-git-commit","markdown_url":"https://listedarticles.com/articles/how-big-is-a-git-commit.md","example":false,"citation":"Dave Gauer, ratfactor. \"How big is a Git commit?.\" 11 Oct 2026. https://ratfactor.com/cards/git-commit-size (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://ratfactor.com/cards/git-commit-size"},"body_markdown":"back to [Git](https://ratfactor.com/cards/git).\n\nShort answer: It depends. Git stores \"objects\" as zlib compressed data. So the exact size of a commit will depend on the compressibility of your files (which is basically a function of the amount of repetition in them).\n\n<https://git-scm.com/docs/gitformat-loose>\n\nThe other factor is that Git eventually turns object files into even more efficient packfiles, saving additional space by getting rid of redundancy across objects.\n\n<https://git-scm.com/book/en/v2/Git-Internals-Packfiles>\n\nFor fun, I did some little experiments (all \"loose objects\" with no packfiles created). Here’s the results:\n\n* **64,828** bytes - git init\n* **47,640** bytes - committing 250 tiny files\n* **17,145** bytes - committing 3 bytes of change to a file in a directory of 50 files\n* **8,709** bytes - committing 3 bytes of change to a single-file repo\n* **297,873** bytes - committing a 583,840 byte binary file\n* **1,622,188** bytes - committing a huge 16,415,223 byte source file\n\nSo we see kilobytes of overhead for very tiny commits - exactly how much will depend on the size of the file/directory tree involved with the changes.\n\nFor larger commits with small numbers of files, the stored data in `.git` will be anywhere from 50% to 10% of the uncompressed committed files.\n\nIn other words, it’s extremely efficient.\n\nBelow is the running log of the experiments resulting in the numbers above.\n\n## Init, 5 directories, 250 files\n\nFirst, how big is a brand new empty Git directory (`.git`)?\n\n```\n$ git init\nInitialized empty Git repository in /home/dave/tmp/foo/.git/\n\n$ du -sb .git\n64828\t.git\n```\n\nAbout **64 Kb**.\n\n(The `du` options are: `s` to summarize and `b` to display bytes.)\n\nNow I’m going to make a bunch of tiny files in directories with a pair of nested shell loops:\n\n```\n$ for d in {1..5};do\n    mkdir $d;\n    for f in {1..50};do\n        echo foo > $d/$f;\n    done;\ndone\n```\n\nNow I have 250 files in 5 directories containing the string \"foo\":\n\n```\n$ ls\n1  2  3  4  5\n$ cat 2/45\nfoo\n```\n\nI’ll commit them:\n\n```\n$ git add .\n$ git commit -m 'initial commit'\n```\n\nLet’s see how big `.git` is now:\n\n```\n$du -sb .git\n112468\t.git\n```\n\nThe commit has added about **47 Kb** to the initial blank repo.\n\n**Aside:** I calculated this in the geekiest way I could think of, using the standard Unix command `dc`, the arbitrary precicision reverse-polish notation desk calculator (note that 'p' is the print command so we can see the result!):\n\n```\n$ dc -e '112468 64828 - p'\n47640\n```\n\nNow I’ll make one tiny change to one file and commit it:\n\n```\n$ echo bar > 1/1\n$ git commit -am 'changed 1/1'\n\n$ du -sb .git\n129613\n$ dc -e '129613 112468 - p'\n17145\n```\n\nSo that’s **17 Kb** to store about 3 bytes of changes to my repo.\n\nWhere is that weight coming from?\n\nI’m gonna do another little commit. Here’s what it looks like before:\n\n```\n$ du -b .git\n4096\t.git/objects/info\n4096\t.git/objects/pack\n4115\t.git/objects/25\n4155\t.git/objects/7b\n4225\t.git/objects/af\n4115\t.git/objects/57\n4271\t.git/objects/07\n4177\t.git/objects/61\n4255\t.git/objects/bb\n4299\t.git/objects/a8\n45900\t.git/objects\n4336\t.git/info\n4096\t.git/refs/tags\n4137\t.git/refs/heads\n12329\t.git/refs\n4096\t.git/branches\n4411\t.git/logs/refs/heads\n8507\t.git/logs/refs\n12918\t.git/logs\n27538\t.git/hooks\n129613\t.git\n```\n\nThen I modify file 1/1 again to contain the string 'baz' and commit that.\n\nAfter:\n\n```\n4096\t.git/objects/info\n4096\t.git/objects/pack\n4115\t.git/objects/25\n4253\t.git/objects/81 <-- new 4 Kb\n4155\t.git/objects/7b\n4225\t.git/objects/af\n4115\t.git/objects/57\n4271\t.git/objects/07\n4177\t.git/objects/61\n4302\t.git/objects/fc <-- new 4 Kb\n4255\t.git/objects/bb\n4115\t.git/objects/76 <-- new 4 Kb\n4299\t.git/objects/a8\n4177\t.git/objects/92 <-- new 4 Kb\n62747\t.git/objects\n4336\t.git/info\n4096\t.git/refs/tags\n4137\t.git/refs/heads\n12329\t.git/refs\n4096\t.git/branches\n4561\t.git/logs/refs/heads <-- added 0.1 Kb\n8657\t.git/logs/refs\n13218\t.git/logs\n27538\t.git/hooks\n146759\t.git\n```\n\nThat’s 4 new objects for a single-file commit.\n\nThanks to Julia \"b0rk\" Evans’s awesome work on the\n[new Git data model documentation](https://jvns.ca/blog/2026/01/08/a-data-model-for-git/) (jvns.ca)\n, I can figure out what these new objects are.\n\nAlthough these files are all zlib compressed data, I figured out that `git show` will show the value of **any** object by hash identifier, not just commits!\n\nSo to view these objects, I did:\n\n```\n$ ls .git/objects/81\nb1a9d27494b40679d612b96bb6acaac83afa9c\n$ git show 81b1a9d27494b40679d612b96bb6acaac83afa9c\n```\n\nAnd the results are:\n\n* 81…​ is my actual commit\n* fc…​ is the tree file for the changed directory `1`., listing all of the files in that directory (1 through 50).\n* 76…​ is the actual contents of file 1/1 (which is now the string 'baz')\n* 92…​ is another tree for the root directory of the repo\n\nThat makes sense. There’s a commit, two trees, and one file.\n\n## Tiny repo\n\nHow about a repo with just a single little file. How much space does a commit require then?\n\n```\n$ git init\n$ echo foo > foo\n$ git add foo\n$ git commit -m 'initial'\n$ du -sb .git\n94208\n$ echo bar > foo\n$ git -am 'change1'\n$ du -sb .git\n102917\n$ dc -e '102917 94208 - p'\n8709\n```\n\nSo that’s **8 Kb** per commit, about *half* the size of the repo with a larger tree of files. I suspect a lot of the size of both of these is overhead and larger commits will see substantial savings.\n\n## \"Big\" files?\n\nLet’s add a \"big\" (580 Kb) binary file:\n\n```\n$ cp /usr/bin/librewolf .\n$ du -b librewolf\n583840\n$ git add .\n$ git commit -m 'added librewolf'\n$ du -sb .git\n400790\n$ dc -e '400790 102917 -  p'\n297873\n```\n\nThe binary compresses quite a bit, so the commit is smaller than the added file (**300 Kb** for a 580 Kb file).\n\nEven better, a **huge** (16.2 Mb) text source file:\n\n```\n$ cp /usr/src/linux...mask.h .\n$ du -b nbio_7_2_0_sh_mask.h\n16415223\n$ git add .\n$ git commit -m 'huge source'\n$ du -sb .git\n2022978\n$ dc -e '2022978 400790 - p'\n1622188\n```\n\nSo that’s **1.6 Mb** to commit a 16.4 Mb source file! I expected the text to compress nicely, but that’s *really* impressive.","body_html":"<p>back to <a href=\"https://ratfactor.com/cards/git\" rel=\"nofollow ugc noopener\">Git</a>.</p>\n<p>Short answer: It depends. Git stores &quot;objects&quot; as zlib compressed data. So the exact size of a commit will depend on the compressibility of your files (which is basically a function of the amount of repetition in them).</p>\n<p><a href=\"https://git-scm.com/docs/gitformat-loose\" rel=\"nofollow ugc noopener\">https://git-scm.com/docs/gitformat-loose</a></p>\n<p>The other factor is that Git eventually turns object files into even more efficient packfiles, saving additional space by getting rid of redundancy across objects.</p>\n<p><a href=\"https://git-scm.com/book/en/v2/Git-Internals-Packfiles\" rel=\"nofollow ugc noopener\">https://git-scm.com/book/en/v2/Git-Internals-Packfiles</a></p>\n<p>For fun, I did some little experiments (all &quot;loose objects&quot; with no packfiles created). Here’s the results:</p>\n<ul><li><strong>64,828</strong> bytes - git init</li><li><strong>47,640</strong> bytes - committing 250 tiny files</li><li><strong>17,145</strong> bytes - committing 3 bytes of change to a file in a directory of 50 files</li><li><strong>8,709</strong> bytes - committing 3 bytes of change to a single-file repo</li><li><strong>297,873</strong> bytes - committing a 583,840 byte binary file</li><li><strong>1,622,188</strong> bytes - committing a huge 16,415,223 byte source file</li></ul>\n<p>So we see kilobytes of overhead for very tiny commits - exactly how much will depend on the size of the file/directory tree involved with the changes.</p>\n<p>For larger commits with small numbers of files, the stored data in <code>.git</code> will be anywhere from 50% to 10% of the uncompressed committed files.</p>\n<p>In other words, it’s extremely efficient.</p>\n<p>Below is the running log of the experiments resulting in the numbers above.</p>\n<h2 id=\"init-5-directories-250-files\">Init, 5 directories, 250 files</h2>\n<p>First, how big is a brand new empty Git directory (<code>.git</code>)?</p>\n<pre><code>$ git init\nInitialized empty Git repository in /home/dave/tmp/foo/.git/\n\n$ du -sb .git\n64828    .git</code></pre>\n<p>About <strong>64 Kb</strong>.</p>\n<p>(The <code>du</code> options are: <code>s</code> to summarize and <code>b</code> to display bytes.)</p>\n<p>Now I’m going to make a bunch of tiny files in directories with a pair of nested shell loops:</p>\n<pre><code>$ for d in {1..5};do\n    mkdir $d;\n    for f in {1..50};do\n        echo foo &gt; $d/$f;\n    done;\ndone</code></pre>\n<p>Now I have 250 files in 5 directories containing the string &quot;foo&quot;:</p>\n<pre><code>$ ls\n1  2  3  4  5\n$ cat 2/45\nfoo</code></pre>\n<p>I’ll commit them:</p>\n<pre><code>$ git add .\n$ git commit -m &#39;initial commit&#39;</code></pre>\n<p>Let’s see how big <code>.git</code> is now:</p>\n<pre><code>$du -sb .git\n112468    .git</code></pre>\n<p>The commit has added about <strong>47 Kb</strong> to the initial blank repo.</p>\n<p><strong>Aside:</strong> I calculated this in the geekiest way I could think of, using the standard Unix command <code>dc</code>, the arbitrary precicision reverse-polish notation desk calculator (note that &#39;p&#39; is the print command so we can see the result!):</p>\n<pre><code>$ dc -e &#39;112468 64828 - p&#39;\n47640</code></pre>\n<p>Now I’ll make one tiny change to one file and commit it:</p>\n<pre><code>$ echo bar &gt; 1/1\n$ git commit -am &#39;changed 1/1&#39;\n\n$ du -sb .git\n129613\n$ dc -e &#39;129613 112468 - p&#39;\n17145</code></pre>\n<p>So that’s <strong>17 Kb</strong> to store about 3 bytes of changes to my repo.</p>\n<p>Where is that weight coming from?</p>\n<p>I’m gonna do another little commit. Here’s what it looks like before:</p>\n<pre><code>$ du -b .git\n4096    .git/objects/info\n4096    .git/objects/pack\n4115    .git/objects/25\n4155    .git/objects/7b\n4225    .git/objects/af\n4115    .git/objects/57\n4271    .git/objects/07\n4177    .git/objects/61\n4255    .git/objects/bb\n4299    .git/objects/a8\n45900    .git/objects\n4336    .git/info\n4096    .git/refs/tags\n4137    .git/refs/heads\n12329    .git/refs\n4096    .git/branches\n4411    .git/logs/refs/heads\n8507    .git/logs/refs\n12918    .git/logs\n27538    .git/hooks\n129613    .git</code></pre>\n<p>Then I modify file 1/1 again to contain the string &#39;baz&#39; and commit that.</p>\n<p>After:</p>\n<pre><code>4096    .git/objects/info\n4096    .git/objects/pack\n4115    .git/objects/25\n4253    .git/objects/81 &lt;-- new 4 Kb\n4155    .git/objects/7b\n4225    .git/objects/af\n4115    .git/objects/57\n4271    .git/objects/07\n4177    .git/objects/61\n4302    .git/objects/fc &lt;-- new 4 Kb\n4255    .git/objects/bb\n4115    .git/objects/76 &lt;-- new 4 Kb\n4299    .git/objects/a8\n4177    .git/objects/92 &lt;-- new 4 Kb\n62747    .git/objects\n4336    .git/info\n4096    .git/refs/tags\n4137    .git/refs/heads\n12329    .git/refs\n4096    .git/branches\n4561    .git/logs/refs/heads &lt;-- added 0.1 Kb\n8657    .git/logs/refs\n13218    .git/logs\n27538    .git/hooks\n146759    .git</code></pre>\n<p>That’s 4 new objects for a single-file commit.</p>\n<p>Thanks to Julia &quot;b0rk&quot; Evans’s awesome work on the\n<a href=\"https://jvns.ca/blog/2026/01/08/a-data-model-for-git/\" rel=\"nofollow ugc noopener\">new Git data model documentation</a> (jvns.ca)\n, I can figure out what these new objects are.</p>\n<p>Although these files are all zlib compressed data, I figured out that <code>git show</code> will show the value of <strong>any</strong> object by hash identifier, not just commits!</p>\n<p>So to view these objects, I did:</p>\n<pre><code>$ ls .git/objects/81\nb1a9d27494b40679d612b96bb6acaac83afa9c\n$ git show 81b1a9d27494b40679d612b96bb6acaac83afa9c</code></pre>\n<p>And the results are:</p>\n<ul><li>81…​ is my actual commit</li><li>fc…​ is the tree file for the changed directory <code>1</code>., listing all of the files in that directory (1 through 50).</li><li>76…​ is the actual contents of file 1/1 (which is now the string &#39;baz&#39;)</li><li>92…​ is another tree for the root directory of the repo</li></ul>\n<p>That makes sense. There’s a commit, two trees, and one file.</p>\n<h2 id=\"tiny-repo\">Tiny repo</h2>\n<p>How about a repo with just a single little file. How much space does a commit require then?</p>\n<pre><code>$ git init\n$ echo foo &gt; foo\n$ git add foo\n$ git commit -m &#39;initial&#39;\n$ du -sb .git\n94208\n$ echo bar &gt; foo\n$ git -am &#39;change1&#39;\n$ du -sb .git\n102917\n$ dc -e &#39;102917 94208 - p&#39;\n8709</code></pre>\n<p>So that’s <strong>8 Kb</strong> per commit, about <em>half</em> the size of the repo with a larger tree of files. I suspect a lot of the size of both of these is overhead and larger commits will see substantial savings.</p>\n<h2 id=\"big-files\">&quot;Big&quot; files?</h2>\n<p>Let’s add a &quot;big&quot; (580 Kb) binary file:</p>\n<pre><code>$ cp /usr/bin/librewolf .\n$ du -b librewolf\n583840\n$ git add .\n$ git commit -m &#39;added librewolf&#39;\n$ du -sb .git\n400790\n$ dc -e &#39;400790 102917 -  p&#39;\n297873</code></pre>\n<p>The binary compresses quite a bit, so the commit is smaller than the added file (<strong>300 Kb</strong> for a 580 Kb file).</p>\n<p>Even better, a <strong>huge</strong> (16.2 Mb) text source file:</p>\n<pre><code>$ cp /usr/src/linux...mask.h .\n$ du -b nbio_7_2_0_sh_mask.h\n16415223\n$ git add .\n$ git commit -m &#39;huge source&#39;\n$ du -sb .git\n2022978\n$ dc -e &#39;2022978 400790 - p&#39;\n1622188</code></pre>\n<p>So that’s <strong>1.6 Mb</strong> to commit a 16.4 Mb source file! I expected the text to compress nicely, but that’s <em>really</em> impressive.</p>","headings":[{"level":2,"text":"Init, 5 directories, 250 files","id":"init-5-directories-250-files"},{"level":2,"text":"Tiny repo","id":"tiny-repo"},{"level":2,"text":"\"Big\" files?","id":"big-files"}]}}