[{"data":1,"prerenderedAt":785},["ShallowReactive",2],{"/en-us/blog/what-s-new-in-git-2-50-0":3,"navigation-en-us":35,"banner-en-us":434,"footer-en-us":444,"blog-post-authors-en-us-Justin Tobler":686,"blog-related-posts-en-us-what-s-new-in-git-2-50-0":700,"assessment-promotions-en-us":735,"next-steps-en-us":775},{"id":4,"title":5,"authorSlugs":6,"body":8,"categorySlug":9,"config":10,"content":14,"description":8,"extension":26,"isFeatured":11,"meta":27,"navigation":11,"path":28,"publishedDate":21,"seo":29,"stem":32,"tagSlugs":33,"__hash__":34},"blogPosts/en-us/blog/what-s-new-in-git-2-50-0.yml","What S New In Git 2 50 0",[7],"justin-tobler",null,"open-source",{"featured":11,"template":12,"slug":13},true,"BlogPost","what-s-new-in-git-2-50-0",{"title":15,"description":16,"authors":17,"heroImage":19,"body":20,"date":21,"category":9,"tags":22},"What’s new in Git 2.50.0?","Here are contributions from GitLab's Git team and the Git community such as the git-diff-pairs(1) command and git-update-ref(1) option to perform batched reference updates.",[18],"Justin Tobler","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663087/Blog/Hero%20Images/git3-cover.png","The Git project recently released [Git Version 2.50.0](https://lore.kernel.org/git/xmqq1prj1umb.fsf@gitster.g/T/#u). Let's look at a few notable highlights from this release, which includes contributions from the Git team at GitLab and also the wider Git community.\n## New git-diff-pairs(1) command\n\nDiffs are at the heart of every code review and show all the changes made\nbetween two revisions. GitLab shows diffs in various places, but the most\ncommon place is a merge request's [\"Changes\" tab](https://docs.gitlab.com/user/project/merge_requests/changes/).\nBehind the scenes, diff generation is powered by\n[`git-diff(1)`](https://git-scm.com/docs/git-diff). For example:\n\n```shell\n$ git diff HEAD~1 HEAD\n```\n\nThis command returns the full diff for all changed files. This might pose a scalability challenge because the number of files changed between a set of revisions could be very large and cause the command to reach self-imposed timeouts for the GitLab backend. For large change sets, it would be better if\nthere were a way to break diff computation into smaller, more digestible chunks.\n\nOne way this can be achieved is by using\n[`git-diff-tree(1)`](https://git-scm.com/docs/git-diff-tree) to retrieve info\nabout all the changed files:\n\n```shell\n$ git diff-tree -r -M --abbrev HEAD~ HEAD\n:100644 100644 c9adfed339 99acf81487 M      Documentation/RelNotes/2.50.0.adoc\n:100755 100755 1047b8d11d 208e91a17f M      GIT-VERSION-GEN\n```\n\nGit refers to this output as the [\"raw\" format](https://git-scm.com/docs/git-diff-tree#_raw_output_format).\nIn short, each line of output lists filepairs and the accompanying metadata\nabout what has changed between the start and end revisions. Compared to\ngenerating the \"patch\" output for large changes, this process is relatively\nquick and provides a summary of everything that changed. This command can optionally perform rename detection by  appending the `-M` flag to check if identified changes were due to a file rename.\n\nWith this information, we could use `git-diff(1)` to compute each of the\nfilepair diffs individually. For example, we can provide the blob IDs\ndirectly:\n\n```shell\n$ git diff 1047b8d11de767d290170979a9a20de1f5692e26 208e91a17f04558ca66bc19d73457ca64d5385f\n```\n\nWe can repeat this process for each of the filepairs, but spinning up a\nseparate Git process for each individual file diff is not very efficient.\nFurthermore, when using blob IDs, the diff loses some contextual information\nsuch as the change status, and file modes which are stored in with the parent\ntree object. What we really want is a mechanism to feed \"raw\" filepair info and\ngenerate the corresponding patch output.\n\nWith the 2.50 release, Git has a new built-in command named\n[`git-diff-pairs(1)`](https://git-scm.com/docs/git-diff-pairs). This command\naccepts \"raw\" formatted filepair info as input on stdin to determine exactly which patches to output. The following example showcases how this command could be\nused:\n\n```shell\n$ git diff-tree -r -z -M HEAD~ HEAD | git diff-pairs -z\n```\n\nWhen used in this manner, the resulting output is identical to using `git-diff(1)`.\nBy having a separate command to generate patch output, the \"raw\" output from\n`git-diff-tree(1)` can be broken up into smaller batches of filepairs and fed to separate\n`git-diff-pairs(1)` processes. This solves the previously mentioned scalability\nconcern because diffs no longer have to be computed all at once. Future GitLab\nreleases could build upon this mechanism to improve diff\ngeneration performance, especially in cases where large change sets are\nconcerned. For more information on this change, check out the corresponding\n[mailing-list thread](https://lore.kernel.org/git/20250228213346.1335224-1-jltobler@gmail.com/).\n\n_This project was led by [Justin Tobler](https://gitlab.com/justintobler)._\n\n## Batched reference updates\n\nGit provides the [`git-update-ref(1)`](https://git-scm.com/docs/git-update-ref)\ncommand to perform reference updates. When used with the `--stdin` flag,\nmultiple reference updates can be batched together in a single transaction by\nspecifying instructions for each reference update to be performed on stdin.\nBulk updating references in this manner also provides atomic behavior whereby a\nsingle reference update failure results in an aborted transaction and no\nreferences being updated. Here is an example showcasing this behavior:\n\n```shell\n# Create repository with three empty commits and branch named \"foo\"\n$ git init\n$ git commit --allow-empty -m 1\n$ git commit --allow-empty -m 2\n$ git commit --allow-empty -m 3\n$ git branch foo\n\n# Print out the commit IDs\n$ git rev-list HEAD\ncf469bdf5436ea1ded57670b5f5a0797f72f1afc\n5a74cd330f04b96ce0666af89682d4d7580c354c\n5a6b339a8ebffde8c0590553045403dbda831518\n\n# Attempt to create a new reference and update existing reference in transaction.\n# Update is expected to fail because the specified old object ID doesn’t match.\n$ git update-ref --stdin \u003C\u003CEOF\n> create refs/heads/bar cf469bdf5436ea1ded57670b5f5a0797f72f1afc\n> update refs/heads/foo 5a6b339a8ebffde8c0590553045403dbda831518 5a74cd330f04b96ce0666af89682d4d7580c354c\n> EOF\nfatal: cannot lock ref 'refs/heads/foo': is at cf469bdf5436ea1ded57670b5f5a0797f72f1afc but expected 5a74cd330f04b96ce0666af89682d4d7580c354c\n\n# The \"bar\" reference was not created.\n$ git switch bar\nfatal: invalid reference: bar\n```\n\nCompared to updating many references individually, updating in bulk is also\nmuch more efficient. While this works well, there might be certain\ncircumstances where it is okay for a subset of the requested reference updates\nto fail, but we still want to take advantage of the efficiency gains of bulk\nupdates.\n\nWith this release, `git-update-ref(1)` has the new `--batch-updates` option,\nwhich allows the updates to proceed even when one or more reference updates\nfails. In this mode, individual failures are reported in the following format:\n\n```text\nrejected SP (\u003Cold-oid> | \u003Cold-target>) SP (\u003Cnew-oid> | \u003Cnew-target>) SP \u003Crejection-reason> LF\n```\n\nThis allows successful reference updates to proceed while providing context to\nwhich updates were rejected and for what reason. Using the same example\nrepository from the previous example:\n\n```shell\n# Attempt to create a new reference and update existing reference in transaction.\n$ git update-ref --stdin --batch-updates \u003C\u003CEOF\n> create refs/heads/bar cf469bdf5436ea1ded57670b5f5a0797f72f1afc\n> update refs/heads/foo 5a6b339a8ebffde8c0590553045403dbda831518 5a74cd330f04b96ce0666af89682d4d7580c354c\n> EOF\nrejected refs/heads/foo 5a6b339a8ebffde8c0590553045403dbda831518 5a74cd330f04b96ce0666af89682d4d7580c354c incorrect old value provided\n\n# The \"bar\" reference was created even though the update to \"foo\" was rejected.\n$ git switch bar\nSwitched to branch 'bar'\n```\n\nThis time, with the `--batch-updates` option, the reference creation succeeded\neven though the update didn't work. This patch series lays the groundwork for\nfuture performance improvements in `git-fetch(1)` and `git-receive-pack(1)`\nwhen references are updated in bulk. For more information, check the\n[mailing-list thread](https://lore.kernel.org/git/20250408085120.614893-1-karthik.188@gmail.com/)\n\n_This project was led by [Karthik Nayak](https://gitlab.com/knayakgl)._\n\n## New filter option for git-cat-file(1)\n\nWith [`git-cat-file(1)`](https://git-scm.com/docs/git-cat-file), it is possible\nto print info for all objects contained in the repository via the\n`--batch–all-objects` option. For example:\n\n```shell\n# Setup simple repository.\n$ git init\n$ echo foo >foo\n$ git add foo\n$ git commit -m init\n\n# Create an unreachable object.\n$ git commit --amend --no-edit\n\n# Use git-cat-file(1) to print info about all objects including unreachable objects.\n$ git cat-file --batch-all-objects --batch-check='%(objecttype) %(objectname)'\ncommit 0b07e71d14897f218f23d9a6e39605b466454ece\ntree 205f6b799e7d5c2524468ca006a0131aa57ecce7\nblob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99\ncommit c999f781fd7214b3caab82f560ffd079ddad0115\n```\n\nIn some situations, a user might want to search through all objects in the\nrepository, but only output a subset based on some specified attribute. For\nexample, if we wanted to see only the objects that are commits, we could use\n`grep(1)`:\n\n```shell\n$ git cat-file --batch-all-objects --batch-check='%(objecttype) %(objectname)' | grep ^commit\ncommit 0b07e71d14897f218f23d9a6e39605b466454ece\ncommit c999f781fd7214b3caab82f560ffd079ddad0115\n```\n\nWhile this works, one downside with filtering the output is that\n`git-cat-file(1)` still has to traverse all the objects in the repository, even\nthe ones that the user is not interested in. This can be rather inefficient.\n\nWith this release, `git-cat-file(1)` now has the `--filter` option, which only\nshows objects matching the specified criteria. This is similar to the option of\nthe same name for `git-rev-list(1)`, but with only a subset of the filters\nsupported. The supported filters are `blob:none`, `blob:limit=`, as well as\n`object:type=`. Similar to the previous example, objects can be filtered by\ntype with Git directly:\n\n```shell\n$ git cat-file --batch-all-objects --batch-check='%(objecttype) %(objectname)' --filter='object:type=commit'\ncommit 0b07e71d14897f218f23d9a6e39605b466454ece\ncommit c999f781fd7214b3caab82f560ffd079ddad0115\n```\n\nNot only is it convenient for Git to handle the processing, for large\nrepositories with many objects, it is also potentially more efficient. If a\nrepository has bitmap indices, it becomes possible for Git to efficiently\nlookup objects of a specific type, and thus avoid scanning through the\npackfile, which leads to a significant speedup. Benchmarks conducted on the\n[Chromium repository](https://github.com/chromium/chromium.git) show\nsignificant improvements:\n\n```text\nBenchmark 1: git cat-file --batch-check --batch-all-objects --unordered --buffer --no-filter\n   Time (mean ± σ):     82.806 s ±  6.363 s    [User: 30.956 s, System: 8.264 s]\n   Range (min … max):   73.936 s … 89.690 s    10 runs\n\nBenchmark 2: git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=tag\n   Time (mean ± σ):      20.8 ms ±   1.3 ms    [User: 6.1 ms, System: 14.5 ms]\n   Range (min … max):    18.2 ms …  23.6 ms    127 runs\n\nBenchmark 3: git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=commit\n   Time (mean ± σ):      1.551 s ±  0.008 s    [User: 1.401 s, System: 0.147 s]\n   Range (min … max):    1.541 s …  1.566 s    10 runs\n\nBenchmark 4: git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=tree\n   Time (mean ± σ):     11.169 s ±  0.046 s    [User: 10.076 s, System: 1.063 s]\n   Range (min … max):   11.114 s … 11.245 s    10 runs\n\nBenchmark 5: git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=blob\n   Time (mean ± σ):     67.342 s ±  3.368 s    [User: 20.318 s, System: 7.787 s]\n   Range (min … max):   62.836 s … 73.618 s    10 runs\n\nBenchmark 6: git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=blob:none\n   Time (mean ± σ):     13.032 s ±  0.072 s    [User: 11.638 s, System: 1.368 s]\n   Range (min … max):   12.960 s … 13.199 s    10 runs\n\nSummary\n   git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=tag\n    74.75 ± 4.61 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=commit\n   538.17 ± 33.17 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=tree\n   627.98 ± 38.77 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=blob:none\n  3244.93 ± 257.23 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --filter=object:type=blob\n  3990.07 ± 392.72 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --no-filter\n\n```\n\nInterestingly, these results indicate that the computation time now scales with\nthe number of objects for a given type instead of the number of total objects\nin the packfile. The original mailing-list thread can be found\n[here](https://lore.kernel.org/git/20250221-pks-cat-file-object-type-filter-v1-0-0852530888e2@pks.im/).\n\n_This project was led by [Patrick Steinhardt](https://gitlab.com/pks-gitlab)._\n\n## Improved performance when generating bundles\n\nGit provides a means to generate an archive of a repository which contains a\nspecified set of references and accompanying reachable objects via the\n[`git-bundle(1)`](https://git-scm.com/docs/git-bundle) command. This operation\nis used by GitLab to generate repository backups and also as part of the\n[bundle-URI](https://git-scm.com/docs/bundle-uri) mechanism.\n\nFor large repositories containing millions of references, this operation can\ntake hours or even days. For example, with the main GitLab repository\n([gitlab-org/gitlab](https://gitlab.com/gitlab-org/gitlab)), backup times were\naround 48 hours. Investigation revealed there was a performance bottleneck due\nto how Git was performing a check to avoid duplicated references being included\nin the bundle. The implementation used a nested `for` loop to iterate and\ncompare all listed references, leading to O(N^2) time complexity. This scales\nvery poorly as the number of references in a repository increases.\n\nIn this release, this issue was addressed by replacing the nested loops with a\nmap data structure leading to a significant speedup. The following benchmark\nthe performance improvement for creating a bundle with a repository containing\n100,000 references:\n\n```text\nBenchmark 1: bundle (refcount = 100000, revision = master)\n  Time (mean ± σ):     14.653 s ±  0.203 s    [User: 13.940 s, System: 0.762 s]\n  Range (min … max):   14.237 s … 14.920 s    10 runs\n\nBenchmark 2: bundle (refcount = 100000, revision = HEAD)\n  Time (mean ± σ):      2.394 s ±  0.023 s    [User: 1.684 s, System: 0.798 s]\n  Range (min … max):    2.364 s …  2.425 s    10 runs\n\nSummary\n  bundle (refcount = 100000, revision = HEAD) ran\n    6.12 ± 0.10 times faster than bundle (refcount = 100000, revision = master)\n\n```\n\nTo learn more, check out our blog post\n[How we decreased GitLab repo backup times from 48 hours to 41 minutes](https://about.gitlab.com/blog/how-we-decreased-gitlab-repo-backup-times-from-48-hours-to-41-minutes/).\nYou can also find the original mailing list thread\n[here](https://lore.kernel.org/git/20250401-488-generating-bundles-with-many-references-has-non-linear-performance-v1-0-6d23b2d96557@gmail.com/).\n\n_This project was led by [Karthik Nayak](https://gitlab.com/knayakgl)._\n\n## Better bundle URI unbundling\n\nThrough the [bundle URI](https://git-scm.com/docs/bundle-uri) mechanism in Git,\nlocations to fetch bundles from can be provided to clients with the goal to\nhelp speed up clones and fetches. When a client downloads a bundle, references\nunder `refs/heads/*` are copied from the bundle into the repository along with\ntheir accompanying objects. A bundle might contain additional references\noutside of `refs/heads/*` such as `refs/tags/*`, which are simply ignored when\nusing bundle URI on clone.\n\nIn Git 2.50, this restriction is lifted, and all references\nmatching `refs/*` contained in the downloaded bundle are copied.\n[Scott Chacon](https://github.com/schacon), who contributed this functionality,\ndemonstrates the difference when cloning\n[gitlab-org/gitlab-foss](https://gitlab.com/gitlab-org/gitlab-foss):\n\n```shell\n$ git-v2.49 clone --bundle-uri=gitlab-base.bundle https://gitlab.com/gitlab-org/gitlab-foss.git gl-2.49\nCloning into 'gl2.49'...\nremote: Enumerating objects: 1092703, done.\nremote: Counting objects: 100% (973405/973405), done.\nremote: Compressing objects: 100% (385827/385827), done.\nremote: Total 959773 (delta 710976), reused 766809 (delta 554276), pack-reused 0 (from 0)\nReceiving objects: 100% (959773/959773), 366.94 MiB | 20.87 MiB/s, done.\nResolving deltas: 100% (710976/710976), completed with 9081 local objects.\nChecking objects: 100% (4194304/4194304), done.\nChecking connectivity: 959668, done.\nUpdating files: 100% (59972/59972), done.\n\n$ git-v2.50 clone --bundle-uri=gitlab-base.bundle https://gitlab.com/gitlab-org/gitlab-foss.git gl-2.50\nCloning into 'gl-2.50'...\nremote: Enumerating objects: 65538, done.\nremote: Counting objects: 100% (56054/56054), done.\nremote: Compressing objects: 100% (28950/28950), done.\nremote: Total 43877 (delta 27401), reused 25170 (delta 13546), pack-reused 0 (from 0)\nReceiving objects: 100% (43877/43877), 40.42 MiB | 22.27 MiB/s, done.\nResolving deltas: 100% (27401/27401), completed with 8564 local objects.\nUpdating files: 100% (59972/59972), done.\n```\n\nComparing these results, we see that Git 2.50 fetches 43,887 objects\n(40.42 MiB) after the bundle was extracted whereas Git 2.49 fetches a\ntotal of 959,773 objects (366.94 MiB). Git 2.50 fetches roughly 95% fewer\nobjects and 90% less data, which benefits both the client and the server. The\nserver needs to process a lot less data to the client and the client needs to\ndownload and extract less data. In the example provided by Scott this led to a\nspeedup of 25%.\n\nTo learn more, check out the corresponding\n[mailing-list thread](https://lore.kernel.org/git/pull.1897.git.git.1740489585344.gitgitgadget@gmail.com/).\n\n_This patch series was contributed by [Scott Chacon](https://github.com/schacon)._\n\n## Read more\n\nThis article highlighted just a few of the contributions made by GitLab and\nthe wider Git community for this latest release. You can learn about these from\nthe [official release announcement](https://lore.kernel.org/git/xmqq1prj1umb.fsf@gitster.g/) of the Git project. Also, check\nout our [previous Git release blog posts](https://about.gitlab.com/blog/tags/git/)\nto see other past highlights of contributions from GitLab team members.\n","2025-06-16",[23,24,25],"git","open source","community","yml",{},"/en-us/blog/what-s-new-in-git-2-50-0",{"noIndex":30,"title":15,"description":31},false,"Here are contributions from GitLab's Git team and the Git community such as the git-diff-pairs(1) command and git-rev-list(1) option to perform batched reference updates.","en-us/blog/what-s-new-in-git-2-50-0",[23,9,25],"uLKkI26VSJKE8SFuKdtEijgpuX6M9KYfrOvHessVNqo",{"data":36},{"logo":37,"freeTrial":42,"sales":47,"login":52,"items":57,"search":364,"minimal":395,"duo":414,"pricingDeployment":424},{"config":38},{"href":39,"dataGaName":40,"dataGaLocation":41},"/","gitlab logo","header",{"text":43,"config":44},"Get free trial",{"href":45,"dataGaName":46,"dataGaLocation":41},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":48,"config":49},"Talk to sales",{"href":50,"dataGaName":51,"dataGaLocation":41},"/sales/","sales",{"text":53,"config":54},"Sign in",{"href":55,"dataGaName":56,"dataGaLocation":41},"https://gitlab.com/users/sign_in/","sign in",[58,85,180,185,285,345],{"text":59,"config":60,"cards":62},"Platform",{"dataNavLevelOne":61},"platform",[63,69,77],{"title":59,"description":64,"link":65},"The intelligent orchestration platform for DevSecOps",{"text":66,"config":67},"Explore our Platform",{"href":68,"dataGaName":61,"dataGaLocation":41},"/platform/",{"title":70,"description":71,"link":72},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":73,"config":74},"Meet GitLab Duo",{"href":75,"dataGaName":76,"dataGaLocation":41},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":78,"description":79,"link":80},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":81,"config":82},"Learn more",{"href":83,"dataGaName":84,"dataGaLocation":41},"/why-gitlab/","why gitlab",{"text":86,"left":11,"config":87,"link":89,"lists":93,"footer":162},"Product",{"dataNavLevelOne":88},"solutions",{"text":90,"config":91},"View all Solutions",{"href":92,"dataGaName":88,"dataGaLocation":41},"/solutions/",[94,118,141],{"title":95,"description":96,"link":97,"items":102},"Automation","CI/CD and automation to accelerate deployment",{"config":98},{"icon":99,"href":100,"dataGaName":101,"dataGaLocation":41},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[103,107,110,114],{"text":104,"config":105},"CI/CD",{"href":106,"dataGaLocation":41,"dataGaName":104},"/solutions/continuous-integration/",{"text":70,"config":108},{"href":75,"dataGaLocation":41,"dataGaName":109},"gitlab duo agent platform - product menu",{"text":111,"config":112},"Source Code Management",{"href":113,"dataGaLocation":41,"dataGaName":111},"/solutions/source-code-management/",{"text":115,"config":116},"Automated Software Delivery",{"href":100,"dataGaLocation":41,"dataGaName":117},"Automated software delivery",{"title":119,"description":120,"link":121,"items":126},"Security","Deliver code faster without compromising security",{"config":122},{"href":123,"dataGaName":124,"dataGaLocation":41,"icon":125},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[127,131,136],{"text":128,"config":129},"Application Security Testing",{"href":123,"dataGaName":130,"dataGaLocation":41},"Application security testing",{"text":132,"config":133},"Software Supply Chain Security",{"href":134,"dataGaLocation":41,"dataGaName":135},"/solutions/supply-chain/","Software supply chain security",{"text":137,"config":138},"Software Compliance",{"href":139,"dataGaName":140,"dataGaLocation":41},"/solutions/software-compliance/","software compliance",{"title":142,"link":143,"items":148},"Measurement",{"config":144},{"icon":145,"href":146,"dataGaName":147,"dataGaLocation":41},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[149,153,157],{"text":150,"config":151},"Visibility & Measurement",{"href":146,"dataGaLocation":41,"dataGaName":152},"Visibility and Measurement",{"text":154,"config":155},"Value Stream Management",{"href":156,"dataGaLocation":41,"dataGaName":154},"/solutions/value-stream-management/",{"text":158,"config":159},"Analytics & Insights",{"href":160,"dataGaLocation":41,"dataGaName":161},"/solutions/analytics-and-insights/","Analytics and insights",{"title":163,"items":164},"GitLab for",[165,170,175],{"text":166,"config":167},"Enterprise",{"href":168,"dataGaLocation":41,"dataGaName":169},"/enterprise/","enterprise",{"text":171,"config":172},"Small Business",{"href":173,"dataGaLocation":41,"dataGaName":174},"/small-business/","small business",{"text":176,"config":177},"Public Sector",{"href":178,"dataGaLocation":41,"dataGaName":179},"/solutions/public-sector/","public sector",{"text":181,"config":182},"Pricing",{"href":183,"dataGaName":184,"dataGaLocation":41,"dataNavLevelOne":184},"/pricing/","pricing",{"text":186,"config":187,"link":189,"lists":193,"feature":272},"Resources",{"dataNavLevelOne":188},"resources",{"text":190,"config":191},"View all resources",{"href":192,"dataGaName":188,"dataGaLocation":41},"/resources/",[194,227,245],{"title":195,"items":196},"Getting started",[197,202,207,212,217,222],{"text":198,"config":199},"Install",{"href":200,"dataGaName":201,"dataGaLocation":41},"/install/","install",{"text":203,"config":204},"Quick start guides",{"href":205,"dataGaName":206,"dataGaLocation":41},"/get-started/","quick setup checklists",{"text":208,"config":209},"Learn",{"href":210,"dataGaLocation":41,"dataGaName":211},"https://university.gitlab.com/","learn",{"text":213,"config":214},"Product documentation",{"href":215,"dataGaName":216,"dataGaLocation":41},"https://docs.gitlab.com/","product documentation",{"text":218,"config":219},"Best practice videos",{"href":220,"dataGaName":221,"dataGaLocation":41},"/getting-started-videos/","best practice videos",{"text":223,"config":224},"Integrations",{"href":225,"dataGaName":226,"dataGaLocation":41},"/integrations/","integrations",{"title":228,"items":229},"Discover",[230,235,240],{"text":231,"config":232},"Customer success stories",{"href":233,"dataGaName":234,"dataGaLocation":41},"/customers/","customer success stories",{"text":236,"config":237},"Blog",{"href":238,"dataGaName":239,"dataGaLocation":41},"/blog/","blog",{"text":241,"config":242},"Remote",{"href":243,"dataGaName":244,"dataGaLocation":41},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":246,"items":247},"Connect",[248,253,257,262,267],{"text":249,"config":250},"GitLab Services",{"href":251,"dataGaName":252,"dataGaLocation":41},"/services/","services",{"text":254,"config":255},"Community",{"href":256,"dataGaName":25,"dataGaLocation":41},"/community/",{"text":258,"config":259},"Forum",{"href":260,"dataGaName":261,"dataGaLocation":41},"https://forum.gitlab.com/","forum",{"text":263,"config":264},"Events",{"href":265,"dataGaName":266,"dataGaLocation":41},"/events/","events",{"text":268,"config":269},"Partners",{"href":270,"dataGaName":271,"dataGaLocation":41},"/partners/","partners",{"backgroundColor":273,"textColor":274,"text":275,"image":276,"link":280},"#2f2a6b","#fff","Insights for the future of software development",{"altText":277,"config":278},"the source promo card",{"src":279},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":281,"config":282},"Read the latest",{"href":283,"dataGaName":284,"dataGaLocation":41},"/the-source/","the source",{"text":286,"config":287,"lists":289},"Company",{"dataNavLevelOne":288},"company",[290],{"items":291},[292,297,303,305,310,315,320,325,330,335,340],{"text":293,"config":294},"About",{"href":295,"dataGaName":296,"dataGaLocation":41},"/company/","about",{"text":298,"config":299,"footerGa":302},"Jobs",{"href":300,"dataGaName":301,"dataGaLocation":41},"/jobs/","jobs",{"dataGaName":301},{"text":263,"config":304},{"href":265,"dataGaName":266,"dataGaLocation":41},{"text":306,"config":307},"Leadership",{"href":308,"dataGaName":309,"dataGaLocation":41},"/company/team/e-group/","leadership",{"text":311,"config":312},"Team",{"href":313,"dataGaName":314,"dataGaLocation":41},"/company/team/","team",{"text":316,"config":317},"Handbook",{"href":318,"dataGaName":319,"dataGaLocation":41},"https://handbook.gitlab.com/","handbook",{"text":321,"config":322},"Investor relations",{"href":323,"dataGaName":324,"dataGaLocation":41},"https://ir.gitlab.com/","investor relations",{"text":326,"config":327},"Trust Center",{"href":328,"dataGaName":329,"dataGaLocation":41},"/security/","trust center",{"text":331,"config":332},"AI Transparency Center",{"href":333,"dataGaName":334,"dataGaLocation":41},"/ai-transparency-center/","ai transparency center",{"text":336,"config":337},"Newsletter",{"href":338,"dataGaName":339,"dataGaLocation":41},"/company/contact/#contact-forms","newsletter",{"text":341,"config":342},"Press",{"href":343,"dataGaName":344,"dataGaLocation":41},"/press/","press",{"text":346,"config":347,"lists":348},"Contact us",{"dataNavLevelOne":288},[349],{"items":350},[351,354,359],{"text":48,"config":352},{"href":50,"dataGaName":353,"dataGaLocation":41},"talk to sales",{"text":355,"config":356},"Support portal",{"href":357,"dataGaName":358,"dataGaLocation":41},"https://support.gitlab.com","support portal",{"text":360,"config":361},"Customer portal",{"href":362,"dataGaName":363,"dataGaLocation":41},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":365,"login":366,"suggestions":373},"Close",{"text":367,"link":368},"To search repositories and projects, login to",{"text":369,"config":370},"gitlab.com",{"href":55,"dataGaName":371,"dataGaLocation":372},"search login","search",{"text":374,"default":375},"Suggestions",[376,378,382,384,388,392],{"text":70,"config":377},{"href":75,"dataGaName":70,"dataGaLocation":372},{"text":379,"config":380},"Code Suggestions (AI)",{"href":381,"dataGaName":379,"dataGaLocation":372},"/solutions/code-suggestions/",{"text":104,"config":383},{"href":106,"dataGaName":104,"dataGaLocation":372},{"text":385,"config":386},"GitLab on AWS",{"href":387,"dataGaName":385,"dataGaLocation":372},"/partners/technology-partners/aws/",{"text":389,"config":390},"GitLab on Google Cloud",{"href":391,"dataGaName":389,"dataGaLocation":372},"/partners/technology-partners/google-cloud-platform/",{"text":393,"config":394},"Why GitLab?",{"href":83,"dataGaName":393,"dataGaLocation":372},{"freeTrial":396,"mobileIcon":401,"desktopIcon":406,"secondaryButton":409},{"text":397,"config":398},"Start free trial",{"href":399,"dataGaName":46,"dataGaLocation":400},"https://gitlab.com/-/trials/new/","nav",{"altText":402,"config":403},"Gitlab Icon",{"src":404,"dataGaName":405,"dataGaLocation":400},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":402,"config":407},{"src":408,"dataGaName":405,"dataGaLocation":400},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":410,"config":411},"Get Started",{"href":412,"dataGaName":413,"dataGaLocation":400},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":415,"mobileIcon":420,"desktopIcon":422},{"text":416,"config":417},"Learn more about GitLab Duo",{"href":418,"dataGaName":419,"dataGaLocation":400},"/gitlab-duo/","gitlab duo",{"altText":402,"config":421},{"src":404,"dataGaName":405,"dataGaLocation":400},{"altText":402,"config":423},{"src":408,"dataGaName":405,"dataGaLocation":400},{"freeTrial":425,"mobileIcon":430,"desktopIcon":432},{"text":426,"config":427},"Back to pricing",{"href":183,"dataGaName":428,"dataGaLocation":400,"icon":429},"back to pricing","GoBack",{"altText":402,"config":431},{"src":404,"dataGaName":405,"dataGaLocation":400},{"altText":402,"config":433},{"src":408,"dataGaName":405,"dataGaLocation":400},{"title":435,"button":436,"config":441},"See how agentic AI transforms software delivery",{"text":437,"config":438},"Watch GitLab Transcend now",{"href":439,"dataGaName":440,"dataGaLocation":41},"/events/transcend/virtual/","transcend event",{"layout":442,"icon":443},"release","AiStar",{"data":445},{"text":446,"source":447,"edit":453,"contribute":458,"config":463,"items":468,"minimal":675},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":448,"config":449},"View page source",{"href":450,"dataGaName":451,"dataGaLocation":452},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":454,"config":455},"Edit this page",{"href":456,"dataGaName":457,"dataGaLocation":452},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":459,"config":460},"Please contribute",{"href":461,"dataGaName":462,"dataGaLocation":452},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":464,"facebook":465,"youtube":466,"linkedin":467},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[469,516,570,614,641],{"title":181,"links":470,"subMenu":485},[471,475,480],{"text":472,"config":473},"View plans",{"href":183,"dataGaName":474,"dataGaLocation":452},"view plans",{"text":476,"config":477},"Why Premium?",{"href":478,"dataGaName":479,"dataGaLocation":452},"/pricing/premium/","why premium",{"text":481,"config":482},"Why Ultimate?",{"href":483,"dataGaName":484,"dataGaLocation":452},"/pricing/ultimate/","why ultimate",[486],{"title":487,"links":488},"Contact Us",[489,492,494,496,501,506,511],{"text":490,"config":491},"Contact sales",{"href":50,"dataGaName":51,"dataGaLocation":452},{"text":355,"config":493},{"href":357,"dataGaName":358,"dataGaLocation":452},{"text":360,"config":495},{"href":362,"dataGaName":363,"dataGaLocation":452},{"text":497,"config":498},"Status",{"href":499,"dataGaName":500,"dataGaLocation":452},"https://status.gitlab.com/","status",{"text":502,"config":503},"Terms of use",{"href":504,"dataGaName":505,"dataGaLocation":452},"/terms/","terms of use",{"text":507,"config":508},"Privacy statement",{"href":509,"dataGaName":510,"dataGaLocation":452},"/privacy/","privacy statement",{"text":512,"config":513},"Cookie preferences",{"dataGaName":514,"dataGaLocation":452,"id":515,"isOneTrustButton":11},"cookie preferences","ot-sdk-btn",{"title":86,"links":517,"subMenu":526},[518,522],{"text":519,"config":520},"DevSecOps platform",{"href":68,"dataGaName":521,"dataGaLocation":452},"devsecops platform",{"text":523,"config":524},"AI-Assisted Development",{"href":418,"dataGaName":525,"dataGaLocation":452},"ai-assisted development",[527],{"title":528,"links":529},"Topics",[530,535,540,545,550,555,560,565],{"text":531,"config":532},"CICD",{"href":533,"dataGaName":534,"dataGaLocation":452},"/topics/ci-cd/","cicd",{"text":536,"config":537},"GitOps",{"href":538,"dataGaName":539,"dataGaLocation":452},"/topics/gitops/","gitops",{"text":541,"config":542},"DevOps",{"href":543,"dataGaName":544,"dataGaLocation":452},"/topics/devops/","devops",{"text":546,"config":547},"Version Control",{"href":548,"dataGaName":549,"dataGaLocation":452},"/topics/version-control/","version control",{"text":551,"config":552},"DevSecOps",{"href":553,"dataGaName":554,"dataGaLocation":452},"/topics/devsecops/","devsecops",{"text":556,"config":557},"Cloud Native",{"href":558,"dataGaName":559,"dataGaLocation":452},"/topics/cloud-native/","cloud native",{"text":561,"config":562},"AI for Coding",{"href":563,"dataGaName":564,"dataGaLocation":452},"/topics/devops/ai-for-coding/","ai for coding",{"text":566,"config":567},"Agentic AI",{"href":568,"dataGaName":569,"dataGaLocation":452},"/topics/agentic-ai/","agentic ai",{"title":571,"links":572},"Solutions",[573,575,577,582,586,589,593,596,598,601,604,609],{"text":128,"config":574},{"href":123,"dataGaName":128,"dataGaLocation":452},{"text":117,"config":576},{"href":100,"dataGaName":101,"dataGaLocation":452},{"text":578,"config":579},"Agile development",{"href":580,"dataGaName":581,"dataGaLocation":452},"/solutions/agile-delivery/","agile delivery",{"text":583,"config":584},"SCM",{"href":113,"dataGaName":585,"dataGaLocation":452},"source code management",{"text":531,"config":587},{"href":106,"dataGaName":588,"dataGaLocation":452},"continuous integration & delivery",{"text":590,"config":591},"Value stream management",{"href":156,"dataGaName":592,"dataGaLocation":452},"value stream management",{"text":536,"config":594},{"href":595,"dataGaName":539,"dataGaLocation":452},"/solutions/gitops/",{"text":166,"config":597},{"href":168,"dataGaName":169,"dataGaLocation":452},{"text":599,"config":600},"Small business",{"href":173,"dataGaName":174,"dataGaLocation":452},{"text":602,"config":603},"Public sector",{"href":178,"dataGaName":179,"dataGaLocation":452},{"text":605,"config":606},"Education",{"href":607,"dataGaName":608,"dataGaLocation":452},"/solutions/education/","education",{"text":610,"config":611},"Financial services",{"href":612,"dataGaName":613,"dataGaLocation":452},"/solutions/finance/","financial services",{"title":186,"links":615},[616,618,620,622,625,627,629,631,633,635,637,639],{"text":198,"config":617},{"href":200,"dataGaName":201,"dataGaLocation":452},{"text":203,"config":619},{"href":205,"dataGaName":206,"dataGaLocation":452},{"text":208,"config":621},{"href":210,"dataGaName":211,"dataGaLocation":452},{"text":213,"config":623},{"href":215,"dataGaName":624,"dataGaLocation":452},"docs",{"text":236,"config":626},{"href":238,"dataGaName":239,"dataGaLocation":452},{"text":231,"config":628},{"href":233,"dataGaName":234,"dataGaLocation":452},{"text":241,"config":630},{"href":243,"dataGaName":244,"dataGaLocation":452},{"text":249,"config":632},{"href":251,"dataGaName":252,"dataGaLocation":452},{"text":254,"config":634},{"href":256,"dataGaName":25,"dataGaLocation":452},{"text":258,"config":636},{"href":260,"dataGaName":261,"dataGaLocation":452},{"text":263,"config":638},{"href":265,"dataGaName":266,"dataGaLocation":452},{"text":268,"config":640},{"href":270,"dataGaName":271,"dataGaLocation":452},{"title":286,"links":642},[643,645,647,649,651,653,655,659,664,666,668,670],{"text":293,"config":644},{"href":295,"dataGaName":288,"dataGaLocation":452},{"text":298,"config":646},{"href":300,"dataGaName":301,"dataGaLocation":452},{"text":306,"config":648},{"href":308,"dataGaName":309,"dataGaLocation":452},{"text":311,"config":650},{"href":313,"dataGaName":314,"dataGaLocation":452},{"text":316,"config":652},{"href":318,"dataGaName":319,"dataGaLocation":452},{"text":321,"config":654},{"href":323,"dataGaName":324,"dataGaLocation":452},{"text":656,"config":657},"Sustainability",{"href":658,"dataGaName":656,"dataGaLocation":452},"/sustainability/",{"text":660,"config":661},"Diversity, inclusion and belonging (DIB)",{"href":662,"dataGaName":663,"dataGaLocation":452},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":326,"config":665},{"href":328,"dataGaName":329,"dataGaLocation":452},{"text":336,"config":667},{"href":338,"dataGaName":339,"dataGaLocation":452},{"text":341,"config":669},{"href":343,"dataGaName":344,"dataGaLocation":452},{"text":671,"config":672},"Modern Slavery Transparency Statement",{"href":673,"dataGaName":674,"dataGaLocation":452},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":676},[677,680,683],{"text":678,"config":679},"Terms",{"href":504,"dataGaName":505,"dataGaLocation":452},{"text":681,"config":682},"Cookies",{"dataGaName":514,"dataGaLocation":452,"id":515,"isOneTrustButton":11},{"text":684,"config":685},"Privacy",{"href":509,"dataGaName":510,"dataGaLocation":452},[687],{"id":688,"title":18,"body":8,"config":689,"content":691,"description":8,"extension":26,"meta":695,"navigation":11,"path":696,"seo":697,"stem":698,"__hash__":699},"blogAuthors/en-us/blog/authors/justin-tobler.yml",{"template":690},"BlogAuthor",{"name":18,"config":692},{"headshot":693,"ctfId":694},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664737/Blog/Author%20Headshots/james_tobler_headshot.png","5pnOIbNI1Sc5IFnReNHNtv",{},"/en-us/blog/authors/justin-tobler",{},"en-us/blog/authors/justin-tobler","fZs0fJVrV3MQT0RHzrMvziLDriL7K4hf9Db4QlPsRvA",[701,711,724],{"content":702,"config":709},{"title":703,"description":704,"authors":705,"date":706,"body":707,"heroImage":19,"category":9,"tags":708},"What’s new in Git 2.53.0?","Learn about release contributions, including fixes for geometric repacking, updates to git-fast-import(1) commit signature handing options, and more.",[18],"2026-02-02","The Git project recently released [Git 2.53.0](https://lore.kernel.org/git/xmqq4inz13e3.fsf@gitster.g/T/#u). Let's look at a few notable highlights from this release, which includes\ncontributions from the Git team at GitLab.\n\n## Geometric repacking support with promisor remotes\n\nNewly written objects in a Git repository are often stored as individual loose files. To ensure good performance and optimal use of disk space, these loose objects are regularly compressed into so-called packfiles. The number of packfiles in a repository grows over time as a result of the user’s activities, like writing new commits or fetching from a remote. As the number of packfiles in a repository increases, Git has to do more work to look up individual objects. Therefore, to preserve optimal repository performance, packfiles are periodically repacked via git-repack(1) to consolidate the objects into fewer packfiles. When repacking there are two strategies: “all-into-one” and “geometric”.\n\nThe all-into-one strategy is fairly straightforward and the current default. As its name implies, all objects in the repository are packed into a single packfile. From a performance perspective this is great for the repository as Git only has to scan through a single packfile when looking up objects. The main downside of such a repacking strategy is that computing a single packfile for a repository can take a significant amount of time for large repositories.\n\nThe geometric strategy helps mitigate this concern by maintaining a geometric progression of packfiles based on their size instead of always repacking into a single packfile. To explain more plainly, when repacking Git maintains a set of packfiles ordered by size where each packfile in the sequence is expected to be at least twice the size of the preceding packfile. If a packfile in the sequence violates this property, packfiles are combined as needed until the progression is restored. This strategy has the advantage of still minimizing the number of packfiles in a repository while also minimizing the amount of work that must be done for most repacking operations.\n\nOne problem with the geometric repacking strategy was that it was not compatible with partial clones. Partial clones allow the user to clone only parts of a repository by, for example, skipping all blobs larger than 1 megabyte. This can significantly reduce the size of a repository, and Git knows how to backfill missing objects that it needs to access at a later point in time.\n\nThe result is a repository that is missing some objects, and any object that may not be fully connected is stored in a “promisor” packfile.  When repacking, this promisor property needs to be retained going forward for packfiles containing a promisor object so it is known whether a missing object is expected and can be backfilled from the promisor remote. With an all-into-one repack, Git knows how to handle promisor objects properly and stores them in a separate promisor packfile. Unfortunately, the geometric repacking strategy did not know to give special treatment to promisor packfiles and instead would merge them with normal packfiles without considering whether they reference promisor objects. Luckily, due to a bug the underlying git-pack-objects(1) dies when using geometric repacking in a partial clone repository. So this means repositories in this configuration were not able to be repacked anyways which isn’t great, but better than repository corruption.\n\nWith the release of Git 2.53, geometric repacking now works with partial clone repositories. When performing a geometric repack, promisor packfiles are handled separately in order to preserve the promisor marker and repacked following a separate geometric progression. With this fix, the geometric strategy moves closer towards becoming the default repacking strategy. For more information check out the corresponding [mailing list thread](https://lore.kernel.org/git/20260105-pks-geometric-repack-with-promisors-v1-0-c4660573437e@pks.im/).\n\nThis project was led by [Patrick Steinhardt](https://gitlab.com/pks-gitlab).\n\n## git-fast-import(1) learned to preserve only valid signatures\n\nIn our [Git 2.52 release article](https://about.gitlab.com/blog/whats-new-in-git-2-52-0/), we covered signature related improvements to git-fast-import(1) and git-fast-export(1). Be sure to check out that post for a more detailed explanation of these commands, how they are used, and the changes being made with regards to signatures.\n\nTo quickly recap, git-fast-import(1) provides a backend to efficiently import data into a repository and is used by tools such as [git-filter-repo(1)](https://github.com/newren/git-filter-repo) to help rewrite the history of a repository in bulk. In the Git 2.52 release, git-fast-import(1) learned the `--signed-commits=\u003Cmode>` option similar to the same option in git-fast-export(1). With this option, it became possible to unconditionally retain or strip signatures from commits/tags.\n\nIn situations where only part of the repository history has been rewritten, any signature for rewritten commits/tags becomes invalid. This means git-fast-import(1) is limited to either stripping all signatures or keeping all signatures even if they have become invalid. But retaining invalid signatures doesn’t make much sense, so rewriting history with git-repo-filter(1) results in all signatures being stripped, even if the underlying commit/tag is not rewritten. This is unfortunate because if the commit/tag is unchanged, its signature is still valid and thus there is no real reason to strip it. What is really needed is a means to preserve signatures for unchanged objects, but strip invalid ones.\n\nWith the release of Git 2.53, the git-fast-import(1) `--signed-commits=\u003Cmode>` option has learned a new `strip-if-invalid` mode which, when specified, only strips signatures from commits that become invalid due to being rewritten. Thus, with this option it becomes possible to preserve some commit signatures when using git-fast-import(1). This is a critical step towards providing the foundation for tools like git-repo-filter(1) to preserve valid signatures and eventually re-sign invalid signatures.\n\nThis project was led by [Christian Couder](https://gitlab.com/chriscool).\n\n## More data collected in git-repo-structure\n\nIn the Git 2.52 release, the “structure” subcommand was introduced to git-repo(1). The intent of this command was to collect information about the repository and eventually become a native replacement for tools such as [git-sizer(1)](https://github.com/github/git-sizer). At GitLab, we host some extremely large repositories, and having insight into the general structure of a repository is critical to understand its performance characteristics. In this release, the command now also collects total size information for reachable objects in a repository to help understand the overall size of the repository. In the output below, you can see the command now collects both the total inflated and disk sizes of reachable objects by object type.\n\n```shell\n$ git repo structure\n\n| Repository structure | Value      |\n| -------------------- | ---------- |\n| * References         |            |\n|   * Count            |   1.78 k   |\n|     * Branches       |      5     |\n|     * Tags           |   1.03 k   |\n|     * Remotes        |    749     |\n|     * Others         |      0     |\n|                      |            |\n| * Reachable objects  |            |\n|   * Count            | 421.37 k   |\n|     * Commits        |  88.03 k   |\n|     * Trees          | 169.95 k   |\n|     * Blobs          | 162.40 k   |\n|     * Tags           |    994     |\n|   * Inflated size    |   7.61 GiB |\n|     * Commits        |  60.95 MiB |\n|     * Trees          |   2.44 GiB |\n|     * Blobs          |   5.11 GiB |\n|     * Tags           | 731.73 KiB |\n|   * Disk size        | 301.50 MiB |\n|     * Commits        |  33.57 MiB |\n|     * Trees          |  77.92 MiB |\n|     * Blobs          | 189.44 MiB |\n|     * Tags           | 578.13 KiB |\n```\n\nThe keen-eyed among you may have also noticed that the size values in the table output are also now listed in a more human-friendly manner with units appended. In subsequent releases we hope to further expand this command's output to provide additional data points such as the largest individual objects in the repository.\n\nThis project was led by [Justin Tobler](https://gitlab.com/justintobler).\n\n## Read more\n\nThis article highlighted just a few of the contributions made by GitLab and\nthe wider Git community for this latest release. You can learn about these from\nthe [official release announcement](https://lore.kernel.org/git/xmqq4inz13e3.fsf@gitster.g/T/#u) of the Git project. Also, check\nout our [previous Git release blog posts](https://about.gitlab.com/blog/tags/git/)\nto see other past highlights of contributions from GitLab team members.",[24,23,25],{"featured":11,"template":12,"slug":710},"whats-new-in-git-2-53-0",{"content":712,"config":722},{"title":713,"description":714,"authors":715,"heroImage":19,"date":719,"body":720,"category":9,"tags":721},"What’s new in Git 2.52.0?","Learn about release contributions, including the new git-last-modified(1) command, improvements to history-rewriting tools, and a new maintenance strategy.",[716,717,718],"Christian Couder","Toon Claes","Patrick Steinhardt","2025-11-17","The Git project recently released [Git 2.52](https://lore.kernel.org/git/xmqqh5usmvsd.fsf@gitster.g/). After a relatively short 8-week [release cycle for 2.51](https://about.gitlab.com/blog/what-s-new-in-git-2-51-0/), due to summer in the Northern Hemisphere, this release is back to the usual 12-week cycle. Let’s look at some notable changes, including contributions from the GitLab Git team and the wider Git community.\n\n## New git-last-modified(1) command\n\nMany Git forges like GitLab display files in a tree view like this:\n\n\n| Name        | Last commit                                             | Last update  |\n| ------------- | --------------------------------------------------------- | -------------- |\n| README.md   | README: *.txt -> *.adoc fixes                           | 4 months ago |\n| RelNotes    | Start 2.51 cycle, the first batch                       | 4 weeks ago  |\n| SECURITY.md | SECURITY: describe how to report vulnerabilities        | 4 years      |\n| abspath.c   | abspath: move related functions to abspath              | 2 years      |\n| abspath.h   | abspath: move related functions to abspath              | 2 years      |\n| aclocal.m4  | configure: use AC_LANG_PROGRAM consistently             | 15 years ago |\n| add-patch.c | pager: stop using `the_repository`                      | 7 months ago |\n| advice.c    | advice: allow disabling default branch name advice      | 4 months ago |\n| advice.h    | advice: allow disabling default branch name advice      | 4 months ago |\n| alias.h     | rebase -m: fix serialization of strategy options        | 2 years      |\n| alloc.h     | git-compat-util: move alloc macros to git-compat-util.h | 2 years ago  |\n| apply.c     | apply: only write intents to add for new files          | 8 days ago   |\n| archive.c   | Merge branch 'ps/parse-options-integers'                | 3 months ago |\n| archive.h   | archive.h: remove unnecessary include                   | 1 year       |\n| attr.h      | fuzz: port fuzz-parse-attr-line from OSS-Fuzz           | 9 months ago |\n| banned.h    | banned.h: mark `strtok()` and `strtok_r()` as banned    | 2 years      |\n\n\n\u003Cbr>\u003C/br>\n\nNext to the files themselves, we also display which commit last modified each respective file. This information is easy to extract from Git by executing the following command:\n\n\n```shell\n\n$ git log --max-count=1 HEAD -- \u003Cfilename>\n\n```\n\nWhile nice and simple, this has a significant catch: Git does not have a way to extract this information for each of these files in a single command. So to get the last commit for all the files in the tree, we'd need to run this command for each file separately. This results in a command pipeline similar to the following:\n\n```shell\n\n$ git ls-tree HEAD --name-only | xargs --max-args=1 git log --max-count=1 HEAD --\n\n```\n\nNaturally, this isn't very efficient:\n\n\n* We need to spin up a fresh Git command for each file.\n\n\n* Git has to step through history for each file separately.\n\n\n\nAs a consequence, this whole operation is quite costly and generates significant load for GitLab.\n\n\n\nTo overcome these issues, a new Git subcommand `git-last-modified(1)` has been introduced. This command returns the commit for each file of a given commit:\n\n\n\n```shell\n\n$ git last-modified HEAD\n\n\ne56f6dcd7b4c90192018e848d0810f091d092913        add-patch.c\n373ad8917beb99dc643b6e7f5c117a294384a57e        advice.h\ne9330ae4b820147c98e723399e9438c8bee60a80        advice.c\n5e2feb5ca692c5c4d39b11e1ffa056911dd7dfd3        alloc.h\n954d33a9757fcfab723a824116902f1eb16e05f7        RelNotes\n4ce0caa7cc27d50ee1bedf1dff03f13be4c54c1f        apply.c\n5d215a7b3eb0a9a69c0cb9aa43dcae956a0aa03e        archive.c\nc50fbb2dd225e7e82abba4380423ae105089f4d7        README.md\n72686d4e5e9a7236b9716368d86fae5bf1ae6156        attr.h\nc2c4138c07ca4d5ffc41ace0bfda0f189d3e262e        archive.h\n5d1344b4973c8ea4904005f3bb51a47334ebb370        abspath.c\n5d1344b4973c8ea4904005f3bb51a47334ebb370        abspath.h\n60ff56f50372c1498718938ef504e744fe011ffb        banned.h\n4960e5c7bdd399e791353bc6c551f09298746f61        alias.h\n2e99b1e383d2da56c81d7ab7dd849e9dab5b7bf0        SECURITY.md\n1e58dba142c673c59fbb9d10aeecf62217d4fc9c        aclocal.m4\n```\n\n\n\nThe benefit of this is obviously that we only have to execute a single Git process now to derive all of that information. But even more importantly, it only requires us to walk the history once for all files together instead of having to walk it multiple times. This is achieved by:\n\n\n1. Start walking the history from the specified commit.\n\n\n2. For each commit:\n   1. If it doesn't modify any of the paths we're interested in we continue to the next commit.\n   2. If it does, we print the commit ID together with the path. Furthermore, we remove the path from the set of interesting paths.\n3. When the list of interesting paths becomes empty we stop.\n\n\n\nGitaly has already been adjusted to use the new command, but the logic is still guarded by a feature flag. Preliminary testing has shown that `git-last-modified(1)` is in most situations at least twice as fast compared to using `git log --max-count=1`.\n\n\n\n*These changes were [originally written](https://github.com/ttaylorr/git/tree/tb/blame-tree) by multiple developers from GitHub and were [upstreamed](https://lore.kernel.org/git/20250805093358.1791633-1-toon@iotcl.com/) into Git by [Toon Claes](https://gitlab.com/toon).*\n\n\n\n## git-fast-export(1) and git-fast-import(1) signature-related improvements\n\n\n\nThe `git-fast-export(1)` and `git-fast-import(1)` commands are designed to be mostly used by interoperability or history rewriting tools. The goal of interoperability tools is to make Git interact nicely with other software, usually a different version control system, that stores data in a different format than Git. For example [hg-fast-export.sh](https://github.com/frej/fast-export) is a “Mercurial to Git converter using git-fast-import.\"\n\n\n\nAlternately, history-rewriting tools let users — usually admins — make changes to the history of their repositories that are not possible or not allowed by the version control system. For example, [reposurgeon](http://www.catb.org/esr/reposurgeon/) says in its [introduction](https://gitlab.com/esr/reposurgeon/-/blob/master/repository-editing.adoc?ref_type=heads#introduction) that its purpose is “to enable risky operations that version-control systems don't want to let you do, such as (a) editing past comments and metadata, (b) excising commits, (c) coalescing and splitting commits, (d) removing files and subtrees from repo history, (e) merging or grafting two or more repos, and (f) cutting a repo in two by cutting a parent-child link, preserving the branch structure of both child repos.\"\n\n\n\nWithin GitLab, we use [git-filter-repo](https://github.com/newren/git-filter-repo) to let admins perform some risky operations on their Git repositories. Unfortunately, until Git 2.50 (released last June), both `git-fast-export(1)` and `git-fast-import(1)` didn't handle cryptographic commit signatures at all. So, although `git-fast-export(1)` had a `--signed-tags=\u003Cmode>` option that allows users to change how cryptographic tag signatures are handled, commit signatures were simply ignored.\n\n\n\nCryptographic signatures are very fragile because they are based on the exact commit or tag data that was signed. When the signed data or any of its preceding history changes, the cryptographic signature becomes invalid. This is a fragile but necessary requirement to make these signatures useful.\n\n\n\nBut in the context of rewriting history this is a problem:\n\n\n\n* We may want to keep cryptographic signatures for both commits and tags that are still valid after the rewrite (e.g. because the history leading up to them did not change).\n\n\n* We may want to create new cryptographic signatures for commits and tags where the previous signature has become invalid.\n\n\n\nNeither `git-fast-import(1)` nor `git-fast-export(1)` allow for these use cases though, which limits what tools like [git-filter-repo](https://github.com/newren/git-filter-repo) or [reposurgeon](http://www.catb.org/esr/reposurgeon/) can achieve.\n\n\n\nWe have made some significant progress:\n\n\n\n* In Git 2.50 we added a `--signed-commits=\u003Cmode>` option to `git-fast-export(1)` for exporting commit signatures, and support in `git-fast-import(1)` for importing them.\n\n\n* In Git 2.51 we improved the format used for exporting and importing commit signatures, and we made it possible for `git-fast-import(1)` to import both a signature made on the SHA-1 object ID of the commit and one made on its SHA-256 object ID.\n\n\n* In Git 2.52 we added the `--signed-commits=\u003Cmode>` and `--signed-tags=\u003Cmode>` options to `git-fast-import(1)`, so the user has control over how to handle signed data at import time.\n\n\n\nThere is still more to be done. We need to add the ability to:\n\n\n\n* Retain only those commit signatures that are still valid to `git-fast-import(1)`.\n\n\n* Re-sign data where the signature became invalid.\n\n\n\nWe have already started to work on these next steps and expect this to land in Git 2.53. Once done, tools like `git-filter-repo(1)` will finally start to handle cryptographic signatures more gracefully. We will keep you posted in our next Git release blog post.\n\n\n\n*This project was led by [Christian Couder](https://gitlab.com/chriscool).*\n\n\n\n## New and improved git-maintenance(1) strategies\n\n\n\nGit repositories require regular maintenance to ensure that they perform well. This maintenance performs a bunch of different tasks: references get optimized, objects get compressed, and stale data gets pruned.\n\n\n\nUntil Git 2.28, these maintenance tasks were performed by `git-gc(1)`. The problem with this command is that it wasn't built with customizability in mind: While certain parameters can be configured, it is not possible to control which parts of a repository should be optimized. This means that it may not be a good fit for all use cases. Even more importantly, it made it very hard to iterate on how exactly Git performs repository maintenance.\n\n\n\nTo fix this issue and allow us to iterate again, [Derrick Stolee](https://github.com/derrickstolee) introduced `git-maintenance(1)`. In contrast to `git-gc(1),` it is built with customizability in mind and allows the user to configure which tasks specifically should be running in a certain repository. This new tool was made the default for Git’s automated maintenance in Git 2.29, but, by default, it still uses `git-gc(1)` to perform the maintenance.\n\n\n\nWhile this default maintenance strategy works well in small or even medium-sized repositories, it is problematic in the context of large monorepos. The biggest limiting factor is how `git-gc(1)` repacks objects: Whenever there are more than 50 packfiles, the tool will merge all of them together into a single packfile. This operation is quite CPU-intensive and causes a lot of I/O operations, so for large monorepos this operation can easily take many minutes or even hours to complete.\n\n\n\nGit already knows how to minimize these repacks via “geometric repacking.” The idea is simple: The packfiles that exist in the repository must follow a geometric progression where every packfile must contain at least twice as many objects as the next smaller one. This allows Git to amortize the number of repacks required while still ensuring that there is only a relatively small number of packfiles overall. This mode was introduced by [Taylor Blau](https://github.com/ttaylorr) in Git 2.32, but it was not wired up as part of the automated maintenance.\n\n\n\nAll the parts exist to make repository maintenance way more scalable for large monorepos: We have the flexible `git-maintenance(1)` tool that can be extended to have a new maintenance strategy, and we have a better way to repack objects. All that needs to be done is to combine these two.\n\n\n\nAnd that's exactly what we did with Git 2.52! We have introduced a new “geometric” maintenance strategy that you can configure in your Git repositories. This strategy is intended as a full replacement for the old strategy based on `git-gc(1)`. Here is the config code you need:\n\n\n\n```shell\n\n$ git config set maintenance.strategy geometric\n\n```\n\n\n\nFrom hereon, Git will use geometric repacking to optimize your objects. This should lead to less churn while ensuring that your objects are in a better-optimized state, especially in large monorepos.\n\n\n\nIn Git 2.53, we aim to make this the default strategy. So stay tuned!\n\n\n\n*This project was led by [Patrick Steinhardt](https://gitlab.com/pks-gitlab).*\n\n\n\n## New subcommand for git-repo(1) to display repository metrics\n\n\n\nPerformance of Git operations in a repository are often dependent on certain characteristics of its underlying structure. At GitLab, we host some extremely large repositories and having insight into the general structure of a repository is critical to understand performance. While it is possible to compose various Git commands and other tools together to surface certain repository metrics, Git lacks a means to surface info about a repository's shape/structure via a single command. This has led to the development of other external tools, such as [git-sizer(1)](https://github.com/github/git-sizer), to fill this gap.\n\n\n\nWith the release of Git 2.52, a new “structure” subcommand has been added to git-repo(1) with the aim to surface information about a repository's structure. Currently, it displays info about the number of references and objects in the repository in the following form:\n\n\n\n```shell\n\n$ git repo structure\n\n\n| Repository structure | Value  |\n| -------------------- | ------ |\n| * References         |        |\n|   * Count            |   1772 |\n|     * Branches       |      3 |\n|     * Tags           |   1025 |\n|     * Remotes        |    744 |\n|     * Others         |      0 |\n|                      |        |\n| * Reachable objects  |        |\n|   * Count            | 418958 |\n|     * Commits        |  87468 |\n|     * Trees          | 168866 |\n|     * Blobs          | 161632 |\n|     * Tags           |    992 |\n\n```\n\n\n\nIn subsequent releases we hope to expand on this and provide other interesting data points like the largest objects in the repository.\n\n\n\n*This project was led by [Justin Tobler](https://gitlab.com/justintobler).*\n\n\n\n## Improvements related to the Google Summer of Code 2025\n\n\n\nWe had three successful projects with the Google Summer of Code.\n\n\n\n### Refactoring in order to reduce Git's global state\n\n\n\nGit contains several global variables used throughout the codebase. This increases the complexity of the code and reduces the maintainability. As part of this project, [Ayush Chandekar](https://ayu-ch.github.io/) worked on reducing the usage of the `the_repository` global variable via a series of patches.\n\n\n\n*The project was mentored by [Christian Couder](https://gitlab.com/chriscool) and [Ghanshyam Thakkar](https://in.linkedin.com/in/ghanshyam-thakkar).*\n\n\n\n### Machine-readable Repository Information Query Tool\n\n\n\nGit lacks a centralized way to retrieve repository information, requiring users to piece it together from various commands. While `git-rev-parse(1)` has become the de-facto tool for accessing much of this information, doing so falls outside its primary purpose.\n\n\n\nAs part of this project, [Lucas Oshiro](https://lucasoshiro.github.io/en/) introduced a new command, `git-repo(1),` which will house all repository-level information. Users can now use `git repo info` to obtain repository information:\n\n\n\n```shell\n\n$ git repo info layout.bare layout.shallow object.format references.format\n\nlayout.bare=false\nlayout.shallow=false\nobject.format=sha1\nreferences.format=reftable\n\n```\n\n\n\n*The project was mentored by [Patrick Steinhardt](https://gitlab.com/pks-gitlab) and [Karthik Nayak](https://gitlab.com/knayakgl).*\n\n\n\n### Consolidate ref-related functionality into git-refs\n\n\n\nGit offers multiple commands for managing references, namely `git-for-each-ref(1)`, `git show-ref(1)`, `git-update-ref(1)`, and `git-pack-refs(1)`. This makes them harder to discover and creates overlapping functionality. To address this, we introduced the `git-refs(1)` command to consolidate these operations under a single interface. As part of this this project, [Meet Soni](https://inosmeet.github.io/) extended the command by adding the following subcommands:\n\n\n\n* `git refs optimize` to optimize the reference backend\n\n\n* `git refs list` to list all references\n\n\n* `git refs exists` to verify the existence of a reference\n\n\n\n*The project was mentored by [Patrick Steinhardt](https://gitlab.com/pks-gitlab) and [shejialuo](https://luolibrary.com/).*\n\n\n\n## What's next?\n\n\n\nReady to experience these improvements? Update to Git 2.52.0 and start using `git last-modified`.\n\n\n\nAt GitLab, we will of course ensure that all of these improvements will eventually land in a GitLab instance near you!\n\n\n\nLearn more in the [official Git 2.52.0 release notes](https://lore.kernel.org/git/xmqqh5usmvsd.fsf@gitster.g/) and explore our [complete archive of Git development coverage](https://about.gitlab.com/blog/tags/git/).\n",[24,23,25],{"featured":11,"template":12,"slug":723},"whats-new-in-git-2-52-0",{"content":725,"config":733},{"title":726,"description":727,"authors":728,"heroImage":19,"date":730,"body":731,"category":9,"tags":732},"What’s new in Git 2.51.0?","Learn about the latest contributions from GitLab's Git team and the Git community, including performance optimizations for git-push(1) and git-fetch(1).",[729],"Karthik Nayak","2025-08-18","The Git project recently released [Git 2.51](https://lore.kernel.org/git/xmqqikikk1hr.fsf@gitster.g/T/#u). Due to summer in the Northern Hemisphere and slower progress, this release cycle was on the shorter side of 8 weeks (typically a release cycle lasts about 12 weeks). Let’s look at some notable changes in this release, including contributions from the Git team at GitLab and also the wider Git community.\n\n## Performance optimizations for `git-push(1)` and `git-fetch(1)`\n\nThe `git-push(1)` and `git-fetch(1)` commands allow users to synchronize local and remote repositories. Part of the operation involves updating references in the repository. In repositories with many references, this can take significant time, especially for users who work with large development environments, monorepos, or repositories with extensive CI/CD pipelines.\nGit reference transactions can include multiple reference updates, but they follow an all-or-nothing approach. If any single update within the transaction fails, the entire transaction fails and none of the reference updates are applied. But reference updates as part of `git-push(1)` and `git-fetch(1)` are allowed to fail, which allows repositories to synchronize a subset of references even in the case where a different subset has diverged. To facilitate this behavior, Git creates a separate transaction for each reference update, allowing some transactions to fail while the rest succeed. \nCreating a separate transaction per update incurs significant overhead, as each transaction includes an initiation and teardown phase and also checks for whether there are conflicting reference names. The “reftable” backend also performs auto-compaction at the end of a transaction, so multiple transactions would trigger multiple auto-compactions, which would drastically increase the latency of the command. \nIn Git 2.51.0, these commands now use batched updates instead of separate transactions. Batched updates allow updating multiple references under a single transaction, while still allowing some updates to fail. This removes the overhead and scales better with the number of references to be updated, since only a single transaction is used. This significantly improves the performance of the “reftable” backend, which now outperforms the “files” backend. Users can reap these performance improvements without needing to make any changes.\nFor `git-fetch(1)` we see a *22x performance improvement for the “reftable” backend* and *1.25x improvement for the “files” backend* when used in a repository with 10,000 references.\n\n```text\nBenchmark 1: fetch: many refs (refformat = reftable, refcount = 10000, revision = master)\n  Time (mean ± σ):      3.403 s ±  0.775 s    [User: 1.875 s, System: 1.417 s]\n  Range (min … max):    2.454 s …  4.529 s    10 runs\n\nBenchmark 2: fetch: many refs (refformat = reftable, refcount = 10000, revision = HEAD)\n  Time (mean ± σ):     154.3 ms ±  17.6 ms    [User: 102.5 ms, System: 56.1 ms]\n  Range (min … max):   145.2 ms … 220.5 ms    18 runs\n\nSummary\n  fetch: many refs (refformat = reftable, refcount = 10000, revision = HEAD) ran\n   22.06 ± 5.62 times faster than fetch: many refs (refformat = reftable, refcount = 10000, revision = master)\n\nBenchmark 1: fetch: many refs (refformat = files, refcount = 10000, revision = master)\n  Time (mean ± σ):     605.5 ms ±   9.4 ms    [User: 117.8 ms, System: 483.3 ms]\n  Range (min … max):   595.6 ms … 621.5 ms    10 runs\n\nBenchmark 2: fetch: many refs (refformat = files, refcount = 10000, revision = HEAD)\n  Time (mean ± σ):     485.8 ms ±   4.3 ms    [User: 91.1 ms, System: 396.7 ms]\n  Range (min … max):   477.6 ms … 494.3 ms    10 runs\n\nSummary\n  fetch: many refs (refformat = files, refcount = 10000, revision = HEAD) ran\n    1.25 ± 0.02 times faster than fetch: many refs (refformat = files, refcount = 10000, revision = master)\n\n```\n\nFor `git-push(1)` we see a *18x performance improvement for the reftable backend* and *1.21x improvement for the “files” backend* when used in a repository with 10,000 references.\n\n```text\nBenchmark 1: push: many refs (refformat = reftable, refcount = 10000, revision = master)\n  Time (mean ± σ):      4.276 s ±  0.078 s    [User: 0.796 s, System: 3.318 s]\n  Range (min … max):    4.185 s …  4.430 s    10 runs\n\nBenchmark 2: push: many refs (refformat = reftable, refcount = 10000, revision = HEAD)\n  Time (mean ± σ):     235.4 ms ±   6.9 ms    [User: 75.4 ms, System: 157.3 ms]\n  Range (min … max):   228.5 ms … 254.2 ms    11 runs\n\nSummary\n  push: many refs (refformat = reftable, refcount = 10000, revision = HEAD) ran\n   18.16 ± 0.63 times faster than push: many refs (refformat = reftable, refcount = 10000, revision = master)\n\nBenchmark 1: push: many refs (refformat = files, refcount = 10000, revision = master)\n  Time (mean ± σ):      1.121 s ±  0.021 s    [User: 0.128 s, System: 0.975 s]\n  Range (min … max):    1.097 s …  1.156 s    10 runs\n\nBenchmark 2: push: many refs (refformat = files, refcount = 10000, revision = HEAD)\n  Time (mean ± σ):     927.9 ms ±  22.6 ms    [User: 99.0 ms, System: 815.2 ms]\n  Range (min … max):   903.1 ms … 978.0 ms    10 runs\n\nSummary\n  push: many refs (refformat = files, refcount = 10000, revision = HEAD) ran\n    1.21 ± 0.04 times faster than push: many refs (refformat = files, refcount = 10000, revision = master)\n\n```\n\nThis [project](https://lore.kernel.org/git/20250514-501-update-git-fetch-1-to-use-partial-transactions-v1-0-7c65f46493d4@gmail.com/) was led by [Karthik Nayak](https://gitlab.com/knayakgl).\n\n## Planning towards Git 3.0\n\n11 years ago, Git 2.0 was released, which was the last major version release of Git. While we don’t have a specific timeline for the next major Git release, this release includes decisions made towards Git 3.0.\n\nThe Git 3.0 release planning allows us to plan for and implement breaking changes and communicate them to the extended Git community. Next to documentation, Git can also be compiled with these breaking changes for those who want to experiment with these changes. More information can be found in the [BreakingChanges document](https://gitlab.com/gitlab-org/git/-/blob/master/Documentation/BreakingChanges.adoc). \n\nThe Git 2.51.0 release makes some significant changes towards Git 3.0. \n\n### Reftable as the default reference backend\n\nIn the [Git 2.45.0](https://gitlab.com/gitlab-org/git/-/blob/master/Documentation/RelNotes/2.45.0.adoc?ref_type=heads) release, the “reftable” format was introduced as a new backend for storing references like branches or tags in Git, which fixes many of the issues with the existing \"files\" backend. Please read our [beginner's guide to how reftables work](https://about.gitlab.com/blog/a-beginners-guide-to-the-git-reftable-format/) for more insight into the “reftable” backend.\n\nThe Git 2.51.0 release marks the switch to using the \"reftable\" format as default in Git 3.0 for newly created repositories and also wires up the change behind a feature flag. The “reftable” format provides the following improvements over the traditional “files” backend:\n\n* It is impossible to store two references that only differ in casing on case-insensitive filesystems with the \"files\" format. This issue is common on Windows and macOS platforms. As the \"reftable\" backend does not use filesystem paths to encode reference names this problem goes away.\n* Similarly, macOS normalizes path names that contain unicode characters, which has the consequence that you cannot store two names with unicode characters that are encoded differently with the \"files\" backend. Again, this is not an issue with the \"reftable\" backend.\n* Deleting references with the \"files\" backend requires Git to rewrite the complete \"packed-refs\" file. In large repositories with many references this file can easily be dozens of megabytes in size; in extreme cases it may be gigabytes. The \"reftable\" backend uses tombstone markers for deleted references and thus does not have to rewrite all of its data.\n* Repository housekeeping with the \"files\" backend typically performs all-into-one repacks of references. This can be quite expensive, and consequently housekeeping is a tradeoff between the number of loose references that accumulate and slow down operations that read references, and compressing those loose references into the \"packed-refs\" file. The \"reftable\" backend uses geometric compaction after every write, which amortizes costs and ensures that the backend is always in a well-maintained state.\n* Operations that write multiple references at once are not atomic with the \"files\" backend. Consequently, Git may see in-between states when it reads references while a reference transaction is in the process of being committed to disk.\n* Writing many references at once is slow with the \"files\" backend because every reference is created as a separate file. The \"reftable\" backend significantly outperforms the \"files\" backend by multiple orders of magnitude.\n* The “reftable” backend uses a binary format with prefix compression for reference names. As a result, the format uses less space compared to the \"packed-refs\" file.\n\nThis project was led by [Patrick Steinhardt](https://gitlab.com/pks-gitlab).\n\n### SHA-256 as the default hash function\n\nThe Git version control system stores objects in a content-addressable filesystem. This means it uses the hash of an object to address content such as files, directories, and revisions, unlike traditional filesystems, which use sequential numbers. Using a hash function has the following advantages: \n\n* Easy integrity checks as a single bit flip would change the hash output completely.\n* Fast object lookup as objects can be indexed by their hash.\n* Object names can be signed and third parties can trust the hash to address the signed object and all objects it references.\n* Communication using Git protocol and out of band communication methods have a short reliable string that can be used to reliably address stored content.\n\nSince its inception, Git has used the SHA-1 hashing algorithm. However, security researchers have discovered some flaws in SHA-1, specifically the [SHAttered attack](https://shattered.io), which shows a practical SHA-1 hash collision. We moved to using a hardened SHA-1 implementation by default since Git 2.13.0. However, SHA-1 is still a weak hashing algorithm and it is only a matter of time before additional attacks will further reduce its security.\n\nSHA-256 was identified as the successor to SHA-1 in late 2018. Git 2.51.0 marks it as the default hash algorithm to be used in Git 3.0.\n\nThis project was led by [brian m. carlson](https://github.com/bk2204).\n\n### Removal of `git-whatchanged(1)`\n\nThe `git-whatchanged(1)` command shows logs with differences each commit introduces. While this is now succeeded by `git log --raw`, the command was kept around for historical reasons. \n\nGit 2.51.0 requires users of the command to explicitly use the `--i-still-use-this` flag to capture any users who still use the deprecated command, and also marks the command for removal in Git 3.0. \n\nThis project was led by [Junio C Hamano](https://simple.wikipedia.org/wiki/Junio_Hamano).\n\n## `git switch` and `git restore` are no longer experimental\n\nThe `git-checkout(1)` command can be used for multiple different use cases. It can be used for switching references:\n\n```shell\n$ git status On branch master Your branch is up to date with 'origin/master'.\nnothing to commit, working tree clean\n$ git checkout next Switched to branch 'next' Your branch is up to date with 'origin/next'.\n```\n\nOr for restoring files:\n\n```shell\n$ echo \"additional line\" >> git.c\n$ git status On branch master Your branch is up to date with 'origin/master’.\nChanges not staged for commit:\n  (use \"git add \u003Cfile>...\" to update what will be committed)\n  (use \"git restore \u003Cfile>...\" to discard changes in working directory)\n    modified:   git.c\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n$ git checkout git.c Updated 1 path from the index\n$ git status On branch master Your branch is up to date with 'origin/master’.\nnothing to commit, working tree clean\n```\n\nFor new users of Git, this can cause a lot of confusion. So in Git 2.33.0, these were split into two new commands, `git-switch(1)` and `git-restore(1)`.\nThe `git-switch(1)` command allows users to switch to a specific branch: \n\n```shell\n$ git status On branch master Your branch is up to date with 'origin/master'.\nnothing to commit, working tree clean\n$ git switch next Switched to branch 'next' Your branch is up to date with 'origin/next'.\n```\n\nAnd the `git-restore(1)` command allows users to restore working tree files: \n\n```shell\n$ echo \"additional line\" >> git.c\n$ git status On branch master Your branch is up to date with 'origin/master’.\nChanges not staged for commit:\n  (use \"git add \u003Cfile>...\" to update what will be committed)\n  (use \"git restore \u003Cfile>...\" to discard changes in working directory)\n    modified:   git.c\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n$ git restore git.c\n$ git status On branch master Your branch is up to date with 'origin/master’.\nnothing to commit, working tree clean\n```\n\nWhile the two commands have existed since 2019, they were marked as experimental. The effect is that the Git project doesn’t guarantee backwards compatibility for those commands: the behavior may change at any point in time. While the intent originally was to stabilize those commands after a couple of releases, this hasn’t happened up to this point.\nThis has led to several discussions on the Git mailing list where users are unsure whether they can start using these new commands, or whether they might eventually go away again. But given that no significant changes have ever been proposed, and that some users are already using these commands, we have decided to no longer declare them as experimental in Git 2.51.\nThis project was led by [Justin Tobler](https://gitlab.com/justintobler).\n\n## `git for-each-ref(1)` receives pagination support\n\nThe `git for-each-ref` command is used to list all references present in the repository. As it is part of the plumbing layer of Git, this command is frequently used for example by hosting forges to list references that exist in the repository in their UI. But as repositories grow, it becomes less realistic to list all references at once – after all, the largest repositories may contain millions of them! So instead, forges tend to paginate the references.\n\nThis surfaces an important gap: `git-for-each-ref` does not know to skip references from previous pages that have already been shown. Consequently, it may have to list a large number of uninteresting references before it finally starts to yield the references required for the current page. This is inefficient and leads to higher-than-necessary latency or even timeouts.\n\nGit 2.51.0 supports a new `--start-after` flag for `git for-each-ref`, which allows paginating the output. This can also be combined with the `--count` flag to iterate over a batch of references. \n\n```shell\n$ git for-each-ref --count=10 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-001 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-002 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-003 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-004 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-005 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-006 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-007 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-008 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-009 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-010\n$ git for-each-ref --count=10 --start-after=refs/heads/branch-010 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-011 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-012 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-013 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-014 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-015 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-016 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-017 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-018 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-019 9751243fba48b34d29aabfc9784803617a806e81 commit    refs/heads/branch-020\n```\n\nThis project was led by [Karthik Nayak](https://gitlab.com/knayakgl).\n\n## What's next?\n\nReady to experience these improvements? Update to Git 2.51.0 and start using `git switch` and `git restore` in your daily workflow. \n\nFor GitLab users, these performance enhancements will automatically improve your development experience once your Git version is updated.\n\nLearn more in the [official Git 2.51.0 release notes](https://lore.kernel.org/git/xmqqikikk1hr.fsf@gitster.g/T/#u) and explore our [complete archive of Git development coverage](https://about.gitlab.com/blog/tags/git/).\n",[23,24,25],{"featured":30,"template":12,"slug":734},"what-s-new-in-git-2-51-0",{"promotions":736},[737,751,763],{"id":738,"categories":739,"header":741,"text":742,"button":743,"image":748},"ai-modernization",[740],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":744,"config":745},"Get your AI maturity score",{"href":746,"dataGaName":747,"dataGaLocation":239},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":749},{"src":750},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":752,"categories":753,"header":755,"text":742,"button":756,"image":760},"devops-modernization",[754,554],"product","Are you just managing tools or shipping innovation?",{"text":757,"config":758},"Get your DevOps maturity score",{"href":759,"dataGaName":747,"dataGaLocation":239},"/assessments/devops-modernization-assessment/",{"config":761},{"src":762},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":764,"categories":765,"header":767,"text":742,"button":768,"image":772},"security-modernization",[766],"security","Are you trading speed for security?",{"text":769,"config":770},"Get your security maturity score",{"href":771,"dataGaName":747,"dataGaLocation":239},"/assessments/security-modernization-assessment/",{"config":773},{"src":774},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"header":776,"blurb":777,"button":778,"secondaryButton":783},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":779,"config":780},"Get your free trial",{"href":781,"dataGaName":46,"dataGaLocation":782},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":490,"config":784},{"href":50,"dataGaName":51,"dataGaLocation":782},1772652105874]