[{"data":1,"prerenderedAt":760},["ShallowReactive",2],{"/en-us/blog/gitlabs-contributions-to-git-2-44-0":3,"navigation-en-us":36,"banner-en-us":464,"footer-en-us":481,"Patrick Steinhardt":726,"next-steps-en-us":739,"footer-source-/en-us/blog/gitlabs-contributions-to-git-2-44-0/":754},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":26,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},"/en-us/blog/gitlabs-contributions-to-git-2-44-0","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"GitLab's contributions to Git 2.44.0","Find out the topics that GitLab’s Git team – as well as the wider community – contributed to the latest Git release, including fast scripted rebases via git-replay.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749666069/Blog/Hero%20Images/AdobeStock_639935439.jpg","https://about.gitlab.com/blog/gitlabs-contributions-to-git-2-44-0","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"GitLab's contributions to Git 2.44.0\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Patrick Steinhardt\"}],\n        \"datePublished\": \"2024-02-26\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Patrick Steinhardt","2024-02-26","The Git project recently released [Git 2.44.0](https://git-scm.com/downloads). In this blog post, we will highlight the contributions made by GitLab's Git team, as well as those from the wider Git community.\n\n## Fast scripted rebases via `git-replay`\n\nThe `git-rebase` command can be used to reapply a set of commits onto a different base commit. This can be quite useful when you have a feature branch where the main branch it was originally created from has advanced since creating the feature branch.\n\nIn this case, `git-rebase` can be used to reapply all commits of the feature branch onto the new commits of the main branch.\n\nSuppose you have the following commit history with the main development branch `main` and your feature branch `feature`:\n\n![main and feature branch](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749678099/Blog/Content%20Images/Screenshot_2024-02-20_at_2.15.37_PM.png)\n\nYou have originally created your feature branch from `m-2`, but since then the `main` branch has gained two additional commits. Now `git-rebase` can be used to reapply your commits `f-1` and `f-2` on top of the newest commit `m-4`:\n\n![applying git-rebase](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749678099/Blog/Content%20Images/Screenshot_2024-02-20_at_2.16.28_PM.png)\n\nYou can see this functionality in GitLab when you create a merge request. When you want to reapply the commits of your merge request onto new commits in the target branch, all you have to do is [to create a comment that contains the `/rebase` command](https://docs.gitlab.com/ee/topics/git/git_rebase.html#rebase-from-the-ui). The magic then happens behind the scenes.\n\nThere is one problem though: `git-rebase` only works on repositories that have a worktree (a directory where a branch, tag or commit has been checked out). The repositories we host at GitLab are “bare” repositories, which don’t have a worktree. This means that the files and directories tracked by your commits are only tracked as Git objects in the `.git` directory of the repository. This is mostly done to save precious disk space and speed up operations.\n\nIn the past, we used [libgit2](https://libgit2.org/) to implement rebases. But for various reasons, we decided to remove this dependency in favor of only using Git commands to access Git repositories. But this created a problem for\nus because we could neither use libgit2 nor `git-rebase` to perform rebases. While we could create an ad-hoc worktree to use `git-rebase`, this would have been prohibitively expensive in large monorepos.\n\nLuckily, [Elijah Newren](https://www.linkedin.com/in/elijah-newren-0a41665/) has upstreamed a new merge algorithm called `merge-ort` in Git 2.33. Despite being significantly faster than the old `recursive` merge strategy in almost all cases, it also has the added benefit that it can perform merges in-memory. In practice, this also allows us to perform such rebases in-memory.\n\nEnter `git-replay`, which is a new command that does essentially the same thing as `git-rebase` but in-memory, thus not requiring a worktree anymore. This is an\nimportant building block to allow us to develop faster rebasing of merge requests in the future.\n\nYou may ask: Why a new command instead of updating `git-rebase`? The problem here was that `git-rebase` is essentially a user-focused command (also called a\n\"porcelain\" command in Git). Thus it performs several actions that are not required by a script at all, like, for example, executing hooks or checking out files into the worktree. The new `git-replay` command is a script-focused\ncommand (also called a \"plumbing\" command in Git) and has a different set of advantages and drawbacks. Furthermore, besides doing rebases, we plan to use it to do cherry-picks and reverts in the future, too.\n\nThis topic was a joint effort by [Elijah Newren](https://www.linkedin.com/in/elijah-newren-0a41665/) and\n[Christian Couder](https://www.gitlab.com/chriscool).\n\n## Commit-graph object existence checks\n\nYou may know that each commit can have an arbitrary number of parents:\n\n- The first commit in your repository has no parents. This is the \"root\" commit.\n- Normal commits have a single parent.\n- Merge commits have at least two, but sometimes even more than two parents.\n\nThis parent relationship is part of what forms the basis of Git's object model and establishes the object graph. If you want to traverse this object graph, Git must look up an entry point commit and from there walk the parent chain of commits.\n\nTo fully traverse history from the newest to the oldest commit, you must look up and parse all commit objects in between. Because repositories can consist of hundreds of thousands or even millions of such commits, this can be\nquite an expensive operation. But users of such repositories still want to be able to, for example, search for a specific commit that changes a specific file\nwithout waiting several minutes for the search to complete.\n\nThe Git project introduced a commit-graph data structure a long time ago that essentially caches a lot of the parsed information in a more accessible data structure. This commit-graph encodes the parent-child relation and some additional information, like, for example, a [bloom filter](https://en.wikipedia.org/wiki/Bloom_filter) of changed\nfiles.\n\nThis commit-graph is usually updated automatically during repository housekeeping. Because housekeeping only runs every so often, the commit-graph can be missing entries for recently added commits. This is perfectly fine and expected to happen, and Git knows to instead look up and parse the commit object in such a case.\n\nNow, the reverse case also theoretically exists: The commit-graph contains cached information of an object that does not exist anymore because it has been deleted without regenerating the commit-graph. The consequence would\nbe that lookups of this commit succeed even though they really shouldn't. To avoid this, in Git 2.43.0, we upstreamed a change into Git that detects commits\nthat exist in the commit-graph but no longer in the object database.\n\nThis change requires us to do an existence check for every commit that we parse via the commit-graph. Naturally, this change leads to a performance regression, which was measured to be about 30% in the worst case. This was\ndeemed acceptable though, because it is better to return the correct result slowly than to return the wrong result quickly. Furthermore, the commit-graph still results in a significant performance improvement compared to not using the commit-graph at all. To give users an escape hatch in case they do not want this performance regression, we also introduced a `GIT_COMMIT_GRAPH_PARANOIA` environment variable that can be used to disable this check.\n\nAfter this change was merged and released though, we heard of cases where the impact was even worse than 30%: counting the number of commits via `git rev-list --count` in the Linux repository regressed by about 100%. After some\ndiscussion upstream, we changed the default so that we no longer verify commit existence for the commit-graph to speed up such queries again. Because repository housekeeping should ensure that commit-graphs are consistent, this change should stop us from needlessly pessimizing this uncommon case.\n\nThis change was implemented by\n[Patrick Steinhardt](https://gitlab.com/pks-gitlab).\n\n## Making Git ready for a new ref backend\n\nA common theme among our customers is that large monorepos with many refs create significant performance problems with many workloads. The range of problems here are manyfold, but the more refs a repository has, the more pronounced the problems become.\n\nMany of the issues are inherent limitations of the way Git stores refs. The so-called `files` ref backend uses a combination of two mechanisms:\n- \"Loose refs\" are simple files that contain the object ID they point to.\n- \"Packed refs\" are a single file that contains a collection of refs.\n\nWhenever you update or create a ref, Git creates them as a loose ref. Every once in a while, repository housekeeping then compresses all loose refs into the `packed-refs` file and deletes the corresponding loose refs. A typical repo looks as follows:\n\n```shell\n $ git init --ref-format=files repo\nInitialized empty Git repository in /tmp/repo/.git/\n $ cd repo/\n $ git commit --allow-empty --message \"initial commit\"\n $ tree .git/\n.git/\n├── config\n├── HEAD\n├── index\n└── refs\n\t├── heads\n\t│   └── main\n\t└── tags\n $ cat .git/HEAD\nref: refs/heads/main\n $ cat .git/refs/heads/main\nbf1814060ed3a88bd457ac4dca055d000ffe4482\n\n $ git pack-refs --all\n $ cat .git/packed-refs\n# pack-refs with: peeled fully-peeled sorted\nbf1814060ed3a88bd457ac4dca055d000ffe4482 refs/heads/main\n```\n\nWhile this model has served the Git project quite well, relying on a filesystem like this has several limitations:\n- Deleting a single ref requires you to rewrite the `packed-refs` file, which can be gigabytes in size.\n- It is impossible to do atomic reads because you cannot atomically scan multiple files at once when a concurrent writer may modify some refs.\n- It is impossible to do atomic writes because creating or updating several refs requires you to write to several files.\n- Housekeeping via `git-pack-refs` does not scale well because of its all-into-one repacking nature.\n- The storage format of both loose and packed refs is inefficient and wastes disk space.\n- Filesystem-specific behavior can be weird and may restrict which refs can be created. For example, Case-insensitivity on filesystems like FAT32 can cause issues, when trying to create two refs with the same name that only differ in their case.\n\nSeveral years ago, [Shawn Pearce](https://sfconservancy.org/blog/2018/jan/30/shawn-pearce/) had proposed the \"reftable\" format as an alternative new format to store refs in a repository. This new format was supposed to help with most or all of the above issues and is essentially a\nbinary format specifically catered towards storing references in Git.\n\nThis new \"reftable\" format has already been implemented by\n[JGit](https://www.eclipse.org/jgit/) and is used extensively by the [Gerrit project](https://www.gerritcodereview.com/). And, in 2021, [Han-Wen Nienhuys](https://www.linkedin.com/pub/dir/han-wen/nienhuys) upstreamed a library to read and write reftables into the Git project. What is still missing though is the backend that ties together the reftable library and\nGit, and unfortunately progress has stalled here. As we experience much of the pain that the reftable format is supposed to address, we decided to take over the work from Han-Wen and continue the upstreaming process.\n\nBefore we can upstream the reftable backend itself though, we first had to prepare several parts of Git for such a new backend. While the Git project already has a concept of different ref backends, the boundaries were very blurry because until now there only exists a single \"files\" backend.\n\nThe biggest contribution by GitLab in this release was thus a joint effort to prepare all the parts of Git for the new backend that were crossing boundaries:\n- Some commands used to read or write refs directly via the filesystem without going through the ref backend.\n- The ref databases of worktrees created via `git-worktree` were initialized ad-hoc instead of going through the ref backend.\n- Cloning a repository created the ref database with the wrong object format when using SHA256. This did not matter with the \"files\" backend because the format was not stored anywhere by the ref backend itself. But because the reftable backend encodes the format into its binary format, this was a problem.\n- Many tests read or write refs via the filesystem directly.\n- We invested quite some time already into bug fixing and performance optimizations for the reftable library itself.\n- We introduced a new `refStorage` extension that tells Git in which format the repository stores its refs. This can be changed when creating a new repository by specifying `--ref-format` flag in `git-init` or `git-clone`. For now, only the “files” format is supported.\n\nThe overarching goal was to get the work-in-progress reftable backend into a state where it passes the complete test suite. And even though the reftable backend is not yet part of Git 2.44.0, I am happy to report that we have\nsucceeded in this goal: Overall, we have contributed more than 150 patches to realize it. Given the current state, we expect that the new reftable backend will become available with Git v2.45.0.\n\nWe will not cover the new reftable format in this post because it is out of scope, but stay tuned for more details soon!\n\nThis project was a joint effort by\n[John Cai](https://gitlab.com/jcaigitlab),\n[Justin Tobler](https://gitlab.com/justintobler),\n[Karthik Nayak](https://gitlab.com/knayakgl),\n[Stan Hu](https://gitlab.com/stanhu),\n[Toon Claes](https://gitlab.com/toon),\nand [Patrick Steinhardt](https://gitlab.com/pks-gitlab), who has led the effort. Credit also goes to\n[Shawn Pearce](https://sfconservancy.org/blog/2018/jan/30/shawn-pearce/) as original inventor of the format and [Han-Wen Nienhuys](https://www.linkedin.com/pub/dir/han-wen/nienhuys) as the\nauthor of the reftable library.\n\n## Support for GitLab CI\n\nAs all the preparations for the new `reftable` backend demonstrate, we have significantly increased our investments into the long-term vision and health of\nthe Git project. And because a very important part of our product depends on the Git project to remain healthy, we want to continue investing into the Git project like this.\n\nFor us, this means that it was high time to improve our own workflows in the context of the Git project. Naturally, we were already using GitLab CI as part of the process instead of the GitHub Workflows support that existed in\nthe Git project. But we were using a [`.gitlab-ci.yml` definition](https://docs.gitlab.com/ee/ci/yaml/) that was not part of the upstream repository and instead maintained outside the Git project.\n\nWhile this worked reasonably well, there were two significant downsides:\n- Test coverage was significantly lower than that of the GitHub Workflows definition. Notably, we did not test on macOS, had no static analysis, and didn't test with non-default settings. This often led to failures in the GitHub Workflows pipeline that we could have detected earlier if we had better CI integration.\n- Other potential contributors to Git who may already be using GitLab on a daily basis didn't have easy access to a GitLab CI pipeline.\n\nTherefore, we decided to upstream a new GitLab CI definition that integrates with the preexisting CI infrastructure that the Git project already had. Because we reuse a lot of pre-existing infrastructure, this ensures that both GitLab CI and GitHub Workflows run tests mostly in the same way.\n\nAnother benefit of GitLab CI support is that, for the first time, we now also exercise an architecture other than `x86_64` or `i686`: the [macOS runners we provide at GitLab.com](https://docs.gitlab.com/ee/ci/runners/saas/macos_saas_runner.html) use an Apple M1, which is based on the `arm64` architecture.\n\nThis change was contributed by [Patrick Steinhardt](https://gitlab.com/pks-gitlab).\n\n## More to come\n\nThis blog post gives just a glimpse into what has happened in the Git project, which lies at the heart of [source code management](https://about.gitlab.com/solutions/source-code-management/) at GitLab. Stay tuned for more insights into future contributions and the reftable backend in particular!","open-source",[23,24,25],"git","open source","CI",{"slug":27,"featured":6,"template":28},"gitlabs-contributions-to-git-2-44-0","BlogPost","content:en-us:blog:gitlabs-contributions-to-git-2-44-0.yml","yaml","Gitlabs Contributions To Git 2 44 0","content","en-us/blog/gitlabs-contributions-to-git-2-44-0.yml","en-us/blog/gitlabs-contributions-to-git-2-44-0","yml",{"_path":37,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":39,"_id":460,"_type":30,"title":461,"_source":32,"_file":462,"_stem":463,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":391,"minimal":422,"duo":441,"pricingDeployment":450},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/","gitlab logo","header",{"text":46,"config":47},"Get free trial",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Talk to sales",{"href":53,"dataGaName":54,"dataGaLocation":44},"/sales/","sales",{"text":56,"config":57},"Sign in",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,105,202,207,312,372],{"text":62,"config":63,"cards":65,"footer":88},"Platform",{"dataNavLevelOne":64},"platform",[66,72,80],{"title":62,"description":67,"link":68},"The most comprehensive AI-powered DevSecOps Platform",{"text":69,"config":70},"Explore our Platform",{"href":71,"dataGaName":64,"dataGaLocation":44},"/platform/",{"title":73,"description":74,"link":75},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":76,"config":77},"Meet GitLab Duo",{"href":78,"dataGaName":79,"dataGaLocation":44},"/gitlab-duo/","gitlab duo ai",{"title":81,"description":82,"link":83},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":84,"config":85},"Learn more",{"href":86,"dataGaName":87,"dataGaLocation":44},"/why-gitlab/","why gitlab",{"title":89,"items":90},"Get started with",[91,96,101],{"text":92,"config":93},"Platform Engineering",{"href":94,"dataGaName":95,"dataGaLocation":44},"/solutions/platform-engineering/","platform engineering",{"text":97,"config":98},"Developer Experience",{"href":99,"dataGaName":100,"dataGaLocation":44},"/developer-experience/","Developer experience",{"text":102,"config":103},"MLOps",{"href":104,"dataGaName":102,"dataGaLocation":44},"/topics/devops/the-role-of-ai-in-devops/",{"text":106,"left":107,"config":108,"link":110,"lists":114,"footer":184},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,140,163],{"title":116,"description":117,"link":118,"items":123},"Automation","CI/CD and automation to accelerate deployment",{"config":119},{"icon":120,"href":121,"dataGaName":122,"dataGaLocation":44},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[124,128,132,136],{"text":125,"config":126},"CI/CD",{"href":127,"dataGaLocation":44,"dataGaName":125},"/solutions/continuous-integration/",{"text":129,"config":130},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":131},"AI assisted development",{"text":133,"config":134},"Source Code Management",{"href":135,"dataGaLocation":44,"dataGaName":133},"/solutions/source-code-management/",{"text":137,"config":138},"Automated Software Delivery",{"href":121,"dataGaLocation":44,"dataGaName":139},"Automated software delivery",{"title":141,"description":142,"link":143,"items":148},"Security","Deliver code faster without compromising security",{"config":144},{"href":145,"dataGaName":146,"dataGaLocation":44,"icon":147},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[149,153,158],{"text":150,"config":151},"Application Security Testing",{"href":145,"dataGaName":152,"dataGaLocation":44},"Application security testing",{"text":154,"config":155},"Software Supply Chain Security",{"href":156,"dataGaLocation":44,"dataGaName":157},"/solutions/supply-chain/","Software supply chain security",{"text":159,"config":160},"Software Compliance",{"href":161,"dataGaName":162,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":164,"link":165,"items":170},"Measurement",{"config":166},{"icon":167,"href":168,"dataGaName":169,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[171,175,179],{"text":172,"config":173},"Visibility & Measurement",{"href":168,"dataGaLocation":44,"dataGaName":174},"Visibility and Measurement",{"text":176,"config":177},"Value Stream Management",{"href":178,"dataGaLocation":44,"dataGaName":176},"/solutions/value-stream-management/",{"text":180,"config":181},"Analytics & Insights",{"href":182,"dataGaLocation":44,"dataGaName":183},"/solutions/analytics-and-insights/","Analytics and insights",{"title":185,"items":186},"GitLab for",[187,192,197],{"text":188,"config":189},"Enterprise",{"href":190,"dataGaLocation":44,"dataGaName":191},"/enterprise/","enterprise",{"text":193,"config":194},"Small Business",{"href":195,"dataGaLocation":44,"dataGaName":196},"/small-business/","small business",{"text":198,"config":199},"Public Sector",{"href":200,"dataGaLocation":44,"dataGaName":201},"/solutions/public-sector/","public sector",{"text":203,"config":204},"Pricing",{"href":205,"dataGaName":206,"dataGaLocation":44,"dataNavLevelOne":206},"/pricing/","pricing",{"text":208,"config":209,"link":211,"lists":215,"feature":299},"Resources",{"dataNavLevelOne":210},"resources",{"text":212,"config":213},"View all resources",{"href":214,"dataGaName":210,"dataGaLocation":44},"/resources/",[216,249,271],{"title":217,"items":218},"Getting started",[219,224,229,234,239,244],{"text":220,"config":221},"Install",{"href":222,"dataGaName":223,"dataGaLocation":44},"/install/","install",{"text":225,"config":226},"Quick start guides",{"href":227,"dataGaName":228,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":230,"config":231},"Learn",{"href":232,"dataGaLocation":44,"dataGaName":233},"https://university.gitlab.com/","learn",{"text":235,"config":236},"Product documentation",{"href":237,"dataGaName":238,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":240,"config":241},"Best practice videos",{"href":242,"dataGaName":243,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":245,"config":246},"Integrations",{"href":247,"dataGaName":248,"dataGaLocation":44},"/integrations/","integrations",{"title":250,"items":251},"Discover",[252,257,261,266],{"text":253,"config":254},"Customer success stories",{"href":255,"dataGaName":256,"dataGaLocation":44},"/customers/","customer success stories",{"text":258,"config":259},"Blog",{"href":260,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":262,"config":263},"Remote",{"href":264,"dataGaName":265,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":267,"config":268},"TeamOps",{"href":269,"dataGaName":270,"dataGaLocation":44},"/teamops/","teamops",{"title":272,"items":273},"Connect",[274,279,284,289,294],{"text":275,"config":276},"GitLab Services",{"href":277,"dataGaName":278,"dataGaLocation":44},"/services/","services",{"text":280,"config":281},"Community",{"href":282,"dataGaName":283,"dataGaLocation":44},"/community/","community",{"text":285,"config":286},"Forum",{"href":287,"dataGaName":288,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":290,"config":291},"Events",{"href":292,"dataGaName":293,"dataGaLocation":44},"/events/","events",{"text":295,"config":296},"Partners",{"href":297,"dataGaName":298,"dataGaLocation":44},"/partners/","partners",{"backgroundColor":300,"textColor":301,"text":302,"image":303,"link":307},"#2f2a6b","#fff","Insights for the future of software development",{"altText":304,"config":305},"the source promo card",{"src":306},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":308,"config":309},"Read the latest",{"href":310,"dataGaName":311,"dataGaLocation":44},"/the-source/","the source",{"text":313,"config":314,"lists":316},"Company",{"dataNavLevelOne":315},"company",[317],{"items":318},[319,324,330,332,337,342,347,352,357,362,367],{"text":320,"config":321},"About",{"href":322,"dataGaName":323,"dataGaLocation":44},"/company/","about",{"text":325,"config":326,"footerGa":329},"Jobs",{"href":327,"dataGaName":328,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":328},{"text":290,"config":331},{"href":292,"dataGaName":293,"dataGaLocation":44},{"text":333,"config":334},"Leadership",{"href":335,"dataGaName":336,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":338,"config":339},"Team",{"href":340,"dataGaName":341,"dataGaLocation":44},"/company/team/","team",{"text":343,"config":344},"Handbook",{"href":345,"dataGaName":346,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":348,"config":349},"Investor relations",{"href":350,"dataGaName":351,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":353,"config":354},"Trust Center",{"href":355,"dataGaName":356,"dataGaLocation":44},"/security/","trust center",{"text":358,"config":359},"AI Transparency Center",{"href":360,"dataGaName":361,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":363,"config":364},"Newsletter",{"href":365,"dataGaName":366,"dataGaLocation":44},"/company/contact/","newsletter",{"text":368,"config":369},"Press",{"href":370,"dataGaName":371,"dataGaLocation":44},"/press/","press",{"text":373,"config":374,"lists":375},"Contact us",{"dataNavLevelOne":315},[376],{"items":377},[378,381,386],{"text":51,"config":379},{"href":53,"dataGaName":380,"dataGaLocation":44},"talk to sales",{"text":382,"config":383},"Support portal",{"href":384,"dataGaName":385,"dataGaLocation":44},"https://support.gitlab.com","support portal",{"text":387,"config":388},"Customer portal",{"href":389,"dataGaName":390,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":392,"login":393,"suggestions":400},"Close",{"text":394,"link":395},"To search repositories and projects, login to",{"text":396,"config":397},"gitlab.com",{"href":58,"dataGaName":398,"dataGaLocation":399},"search login","search",{"text":401,"default":402},"Suggestions",[403,405,409,411,415,419],{"text":73,"config":404},{"href":78,"dataGaName":73,"dataGaLocation":399},{"text":406,"config":407},"Code Suggestions (AI)",{"href":408,"dataGaName":406,"dataGaLocation":399},"/solutions/code-suggestions/",{"text":125,"config":410},{"href":127,"dataGaName":125,"dataGaLocation":399},{"text":412,"config":413},"GitLab on AWS",{"href":414,"dataGaName":412,"dataGaLocation":399},"/partners/technology-partners/aws/",{"text":416,"config":417},"GitLab on Google Cloud",{"href":418,"dataGaName":416,"dataGaLocation":399},"/partners/technology-partners/google-cloud-platform/",{"text":420,"config":421},"Why GitLab?",{"href":86,"dataGaName":420,"dataGaLocation":399},{"freeTrial":423,"mobileIcon":428,"desktopIcon":433,"secondaryButton":436},{"text":424,"config":425},"Start free trial",{"href":426,"dataGaName":49,"dataGaLocation":427},"https://gitlab.com/-/trials/new/","nav",{"altText":429,"config":430},"Gitlab Icon",{"src":431,"dataGaName":432,"dataGaLocation":427},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":429,"config":434},{"src":435,"dataGaName":432,"dataGaLocation":427},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":437,"config":438},"Get Started",{"href":439,"dataGaName":440,"dataGaLocation":427},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":442,"mobileIcon":446,"desktopIcon":448},{"text":443,"config":444},"Learn more about GitLab Duo",{"href":78,"dataGaName":445,"dataGaLocation":427},"gitlab duo",{"altText":429,"config":447},{"src":431,"dataGaName":432,"dataGaLocation":427},{"altText":429,"config":449},{"src":435,"dataGaName":432,"dataGaLocation":427},{"freeTrial":451,"mobileIcon":456,"desktopIcon":458},{"text":452,"config":453},"Back to pricing",{"href":205,"dataGaName":454,"dataGaLocation":427,"icon":455},"back to pricing","GoBack",{"altText":429,"config":457},{"src":431,"dataGaName":432,"dataGaLocation":427},{"altText":429,"config":459},{"src":435,"dataGaName":432,"dataGaLocation":427},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":465,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":466,"button":467,"image":472,"config":476,"_id":478,"_type":30,"_source":32,"_file":479,"_stem":480,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":468,"config":469},"Try the Beta",{"href":470,"dataGaName":471,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"altText":473,"config":474},"GitLab Duo Agent Platform",{"src":475},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":477},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":482,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":483,"_id":722,"_type":30,"title":723,"_source":32,"_file":724,"_stem":725,"_extension":35},"/shared/en-us/main-footer",{"text":484,"source":485,"edit":491,"contribute":496,"config":501,"items":506,"minimal":714},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":486,"config":487},"View page source",{"href":488,"dataGaName":489,"dataGaLocation":490},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":492,"config":493},"Edit this page",{"href":494,"dataGaName":495,"dataGaLocation":490},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":497,"config":498},"Please contribute",{"href":499,"dataGaName":500,"dataGaLocation":490},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":502,"facebook":503,"youtube":504,"linkedin":505},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[507,554,607,651,680],{"title":203,"links":508,"subMenu":523},[509,513,518],{"text":510,"config":511},"View plans",{"href":205,"dataGaName":512,"dataGaLocation":490},"view plans",{"text":514,"config":515},"Why Premium?",{"href":516,"dataGaName":517,"dataGaLocation":490},"/pricing/premium/","why premium",{"text":519,"config":520},"Why Ultimate?",{"href":521,"dataGaName":522,"dataGaLocation":490},"/pricing/ultimate/","why ultimate",[524],{"title":525,"links":526},"Contact Us",[527,530,532,534,539,544,549],{"text":528,"config":529},"Contact sales",{"href":53,"dataGaName":54,"dataGaLocation":490},{"text":382,"config":531},{"href":384,"dataGaName":385,"dataGaLocation":490},{"text":387,"config":533},{"href":389,"dataGaName":390,"dataGaLocation":490},{"text":535,"config":536},"Status",{"href":537,"dataGaName":538,"dataGaLocation":490},"https://status.gitlab.com/","status",{"text":540,"config":541},"Terms of use",{"href":542,"dataGaName":543,"dataGaLocation":490},"/terms/","terms of use",{"text":545,"config":546},"Privacy statement",{"href":547,"dataGaName":548,"dataGaLocation":490},"/privacy/","privacy statement",{"text":550,"config":551},"Cookie preferences",{"dataGaName":552,"dataGaLocation":490,"id":553,"isOneTrustButton":107},"cookie preferences","ot-sdk-btn",{"title":106,"links":555,"subMenu":563},[556,560],{"text":557,"config":558},"DevSecOps platform",{"href":71,"dataGaName":559,"dataGaLocation":490},"devsecops platform",{"text":129,"config":561},{"href":78,"dataGaName":562,"dataGaLocation":490},"ai-assisted development",[564],{"title":565,"links":566},"Topics",[567,572,577,582,587,592,597,602],{"text":568,"config":569},"CICD",{"href":570,"dataGaName":571,"dataGaLocation":490},"/topics/ci-cd/","cicd",{"text":573,"config":574},"GitOps",{"href":575,"dataGaName":576,"dataGaLocation":490},"/topics/gitops/","gitops",{"text":578,"config":579},"DevOps",{"href":580,"dataGaName":581,"dataGaLocation":490},"/topics/devops/","devops",{"text":583,"config":584},"Version Control",{"href":585,"dataGaName":586,"dataGaLocation":490},"/topics/version-control/","version control",{"text":588,"config":589},"DevSecOps",{"href":590,"dataGaName":591,"dataGaLocation":490},"/topics/devsecops/","devsecops",{"text":593,"config":594},"Cloud Native",{"href":595,"dataGaName":596,"dataGaLocation":490},"/topics/cloud-native/","cloud native",{"text":598,"config":599},"AI for Coding",{"href":600,"dataGaName":601,"dataGaLocation":490},"/topics/devops/ai-for-coding/","ai for coding",{"text":603,"config":604},"Agentic AI",{"href":605,"dataGaName":606,"dataGaLocation":490},"/topics/agentic-ai/","agentic ai",{"title":608,"links":609},"Solutions",[610,612,614,619,623,626,630,633,635,638,641,646],{"text":150,"config":611},{"href":145,"dataGaName":150,"dataGaLocation":490},{"text":139,"config":613},{"href":121,"dataGaName":122,"dataGaLocation":490},{"text":615,"config":616},"Agile development",{"href":617,"dataGaName":618,"dataGaLocation":490},"/solutions/agile-delivery/","agile delivery",{"text":620,"config":621},"SCM",{"href":135,"dataGaName":622,"dataGaLocation":490},"source code management",{"text":568,"config":624},{"href":127,"dataGaName":625,"dataGaLocation":490},"continuous integration & delivery",{"text":627,"config":628},"Value stream management",{"href":178,"dataGaName":629,"dataGaLocation":490},"value stream management",{"text":573,"config":631},{"href":632,"dataGaName":576,"dataGaLocation":490},"/solutions/gitops/",{"text":188,"config":634},{"href":190,"dataGaName":191,"dataGaLocation":490},{"text":636,"config":637},"Small business",{"href":195,"dataGaName":196,"dataGaLocation":490},{"text":639,"config":640},"Public sector",{"href":200,"dataGaName":201,"dataGaLocation":490},{"text":642,"config":643},"Education",{"href":644,"dataGaName":645,"dataGaLocation":490},"/solutions/education/","education",{"text":647,"config":648},"Financial services",{"href":649,"dataGaName":650,"dataGaLocation":490},"/solutions/finance/","financial services",{"title":208,"links":652},[653,655,657,659,662,664,666,668,670,672,674,676,678],{"text":220,"config":654},{"href":222,"dataGaName":223,"dataGaLocation":490},{"text":225,"config":656},{"href":227,"dataGaName":228,"dataGaLocation":490},{"text":230,"config":658},{"href":232,"dataGaName":233,"dataGaLocation":490},{"text":235,"config":660},{"href":237,"dataGaName":661,"dataGaLocation":490},"docs",{"text":258,"config":663},{"href":260,"dataGaName":5,"dataGaLocation":490},{"text":253,"config":665},{"href":255,"dataGaName":256,"dataGaLocation":490},{"text":262,"config":667},{"href":264,"dataGaName":265,"dataGaLocation":490},{"text":275,"config":669},{"href":277,"dataGaName":278,"dataGaLocation":490},{"text":267,"config":671},{"href":269,"dataGaName":270,"dataGaLocation":490},{"text":280,"config":673},{"href":282,"dataGaName":283,"dataGaLocation":490},{"text":285,"config":675},{"href":287,"dataGaName":288,"dataGaLocation":490},{"text":290,"config":677},{"href":292,"dataGaName":293,"dataGaLocation":490},{"text":295,"config":679},{"href":297,"dataGaName":298,"dataGaLocation":490},{"title":313,"links":681},[682,684,686,688,690,692,694,698,703,705,707,709],{"text":320,"config":683},{"href":322,"dataGaName":315,"dataGaLocation":490},{"text":325,"config":685},{"href":327,"dataGaName":328,"dataGaLocation":490},{"text":333,"config":687},{"href":335,"dataGaName":336,"dataGaLocation":490},{"text":338,"config":689},{"href":340,"dataGaName":341,"dataGaLocation":490},{"text":343,"config":691},{"href":345,"dataGaName":346,"dataGaLocation":490},{"text":348,"config":693},{"href":350,"dataGaName":351,"dataGaLocation":490},{"text":695,"config":696},"Sustainability",{"href":697,"dataGaName":695,"dataGaLocation":490},"/sustainability/",{"text":699,"config":700},"Diversity, inclusion and belonging (DIB)",{"href":701,"dataGaName":702,"dataGaLocation":490},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":353,"config":704},{"href":355,"dataGaName":356,"dataGaLocation":490},{"text":363,"config":706},{"href":365,"dataGaName":366,"dataGaLocation":490},{"text":368,"config":708},{"href":370,"dataGaName":371,"dataGaLocation":490},{"text":710,"config":711},"Modern Slavery Transparency Statement",{"href":712,"dataGaName":713,"dataGaLocation":490},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":715},[716,718,720],{"text":540,"config":717},{"href":542,"dataGaName":543,"dataGaLocation":490},{"text":545,"config":719},{"href":547,"dataGaName":548,"dataGaLocation":490},{"text":550,"config":721},{"dataGaName":552,"dataGaLocation":490,"id":553,"isOneTrustButton":107},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[727],{"_path":728,"_dir":729,"_draft":6,"_partial":6,"_locale":7,"content":730,"config":734,"_id":736,"_type":30,"title":18,"_source":32,"_file":737,"_stem":738,"_extension":35},"/en-us/blog/authors/patrick-steinhardt","authors",{"name":18,"config":731},{"headshot":732,"ctfId":733},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749661952/Blog/Author%20Headshots/pks-gitlab-headshot.png","pksgitlab",{"template":735},"BlogAuthor","content:en-us:blog:authors:patrick-steinhardt.yml","en-us/blog/authors/patrick-steinhardt.yml","en-us/blog/authors/patrick-steinhardt",{"_path":740,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":741,"eyebrow":742,"blurb":743,"button":744,"secondaryButton":748,"_id":750,"_type":30,"title":751,"_source":32,"_file":752,"_stem":753,"_extension":35},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":46,"config":745},{"href":746,"dataGaName":49,"dataGaLocation":747},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":749},{"href":53,"dataGaName":54,"dataGaLocation":747},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":755,"content":756,"config":759,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},{"title":9,"description":10,"authors":757,"heroImage":11,"date":19,"body":20,"category":21,"tags":758},[18],[23,24,25],{"slug":27,"featured":6,"template":28},1761814428288]