[{"data":1,"prerenderedAt":707},["ShallowReactive",2],{"/en-us/blog/contributions-to-latest-git-release/":3,"navigation-en-us":37,"banner-en-us":453,"footer-en-us":468,"John Cai":679,"next-steps-en-us":692},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":27,"_id":30,"_type":31,"title":32,"_source":33,"_file":34,"_stem":35,"_extension":36},"/en-us/blog/contributions-to-latest-git-release","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Git 2.41 release - Here are five of our contributions in detail","Find out how GitLab's Git team helped improve the latest version of Git.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667792/Blog/Hero%20Images/git-241.jpg","https://about.gitlab.com/blog/contributions-to-latest-git-release","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Git 2.41 release - Here are five of our contributions in detail\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"John Cai\"}],\n        \"datePublished\": \"2023-06-20\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"John Cai","2023-06-20","\n[Git 2.41](https://gitlab.com/gitlab-org/git/-/raw/master/Documentation/RelNotes/2.41.0.txt)\nwas officially released on June 1, 2023, and included some improvements from GitLab's Git team. Git is the foundation of\nrepository data at GitLab. GitLab's Git team works on everything from new\nfeatures, performance improvements, documentation improvements, and growing the Git\ncommunity. Often our contributions to Git have a lot to do with the way we integrate Git into\nour services at GitLab. Here are some highlights from this latest Git release,\nand a window into how we use Git on the server side at GitLab.\n\n## 1. Machine-parseable fetch output\nWhen `git-fetch` is run, the output is a familiar for users of Git and looks\nsomething like this:\n\n```bash\n> git fetch\nremote: Enumerating objects: 296, done.\nremote: Counting objects: 100% (189/189), done.\nremote: Compressing objects: 100% (103/103), done.\nremote: Total 296 (delta 132), reused 84 (delta 84), pack-reused 107\nReceiving objects: 100% (296/296), 184.46 KiB | 11.53 MiB/s, done.\nResolving deltas: 100% (173/173), completed with 42 local objects.\nFrom https://gitlab.com/gitlab-org/gitaly\n   cfd146b4d..a69cf20ce  master                                                                             -> origin/master\n   3a877b8f3..854f25045  15-11-stable                                                                       -> origin/15-11-stable\n * [new branch]          5316-check-metrics-and-decide-if-need-to-context-cancel-the-running-git-process-in -> origin/5316-check-metrics-and-decide-if-need-to-context-cancel-the-running-git-process-in\n + bdd3c05a2...0bcf6f9d4 blanet_default_branch_opt                                                          -> origin/blanet_default_branch_opt  (forced update)\n * [new branch]          jt-object-pool-disconnect-refactor                                                 -> origin/jt-object-pool-disconnect-refactor\n + f2447981c...34e06e106 jt-replicate-repository-alternates                                                 -> origin/jt-replicate-repository-alternates  (forced update)\n * [new branch]          kn-logrus-update                                                                   -> origin/kn-logrus-update\n + 05cea76f3...258543674 kn-smarthttp-docs                                                                  -> origin/kn-smarthttp-docs  (forced update)\n * [new branch]          pks-git-pseudorevision-validation                                                  -> origin/pks-git-pseudorevision-validation\n + 2e8d0ccd5...bf4ed8a52 pks-storage-repository                                                             -> origin/pks-storage-repository  (forced update)\n * [new branch]          qmnguyen0711/expose-another-port-for-pack-rpcs                                     -> origin/qmnguyen0711/expose-another-port-for-pack-rpcs\n + 82473046f...8e23e474c use_head_reference\n```\n\nThe problem with this output is that it's not meant for machines to parse.\n\nBut why would it be useful to make this output parseable by machines? To understand\nthis, we need to back up a little bit and talk about Gitaly Cluster. [Gitaly Cluster](https://docs.gitlab.com/ee/administration/gitaly/#gitaly-cluster)\nis a service at GitLab that provides high availability of Git repositories by\nreplicating repository writes to replica nodes. Each time a write comes in which\nchanges a Git repository (for example, a push that updates a reference) the write goes to\nthe primary node, and to all replica nodes before the write can succeed. A\nvoting mechanism takes place where the nodes vote on what its updated\nvalue for the reference would be. This vote succeeds when a quorum of replica\nnodes have successfully written the ref, and the write succeeds.\n\nOne of our remote procedure calls (RPCs) in Gitaly runs `git-fetch(1)` for repository mirroring. By\ndefault, when `git-fetch(1)` is run, it will update any references that are able\nto be fast-forwarded and fail on any reference that has since diverged will not\nbe updated.\n\nAs mentioned above, whenever there is an operation that modifies a repository, there\nis a voting mechanism that ensures the same modification is made to all replica nodes.\nTo dive in even a little deeper, our voting mechanism leverages Git's reference transaction hook,\nwhich runs an executable once per reference transaction. `git-fetch(1)` by default will\nstart a reference transaction per reference it updates. A fetch that updates hundreds or\neven thousand of references would thus vote once per reference that gets updated.\n\nIn the following sequence diagram, we are only showing one Gitaly node, but for a Gitaly Cluster\nwith, let's say, three nodes, what happens with the Gitaly primary also happens in\nthe replicas.\n\n```mermaid\nsequenceDiagram\n    participant user\n    participant GitlabUI as Gitlab UI\n    participant p as Praefect\n    participant g0 as Gitaly (primary)\n    participant git as Git\n    user->>GitlabUI: mirror my repository\n    GitlabUI->>p: FetchRemote\n    activate p\n    p->>g0: FetchRemote\n    activate g0\n    g0->>git: fetch-remote\n    activate git\n    git->>g0: vote on refs/heads/branch1 update\n    g0->>p: vote on refs/heads/branch1 update\n    git->>g0: vote on refs/heads/branch2 update\n    g0->>p: vote on refs/heads/branch2 update\n    git->>g0: vote on refs/heads/branch3 update\n    g0->>p: vote on refs/heads/branch3 update\n    deactivate git\n    note over p: vote succeeds\n    p->>GitlabUI: success\n    deactivate g0\n    deactivate p\n\n```\n\nThis is inefficient. Ideally we would want to vote once per batch of references\nupdated from one `git-fetch(1)` call. There is an option `--atomic` in\n`git-fetch(1)` that will open one reference transaction for all references\nupdated by `git-fetch(1)`. However, when `--atomic` is used, a `git-fetch` call will fail if any references have since diverged. This is not how we want repository mirroring to work. We actually want `git-fetch` to update whichever refs it can.\n\nSo, that means we cannot use the `--atomic` flag and are thus stuck voting per reference we update.\n\n### Solution: Handle the reference update ourselves\nThe way we are solving this inefficiency is to handle the reference update\nourselves. Instead of relying on `git-fetch(1)` to both fetch the objects and\nupdate all the references, we can use the `--dry-run` option of `git-fetch(1)`\nto first fetch the objects into a quarantine directory. Then if we can know\nwhich references *would* be updated, we can start a reference transaction\nourselves with `git-update-ref(1)` and update all the refs in one transaction,\nhence triggering a single vote only.\n\n```mermaid\n\nsequenceDiagram\n    participant user\n    participant Gitlab UI\n    participant p as Praefect\n    participant g0 as Gitaly (primary)\n    participant git as Git\n    user->>Gitlab UI: mirror my repository\n    Gitlab UI->>p: FetchRemote\n    activate p\n    p->>g0: FetchRemote\n    g0->>git: fetch-remote --dry-run --porcelain\n    activate git\n    note over git: objects are fetched into a quarantine directory\n    git->>g0: branch1, branch2, branch3 will be updated\n    deactivate git\n    g0->>git: update-ref\n    activate git\n    note over git: update branch1, branch2, branch3 in a single transaction\n    git->>g0: reference transaction hook\n    deactivate git\n    g0->>p: vote on ref updates\n    note over p: vote succeeds\n    p->>Gitlab UI: success\n    deactivate p\n\n```\n\nA requirement for this however, is that we would be able to parse the output of\n`git-fetch(1)` to tell which refs will be updated and to what values. Currently\nin `--dry-run`, `git-fetch(1)`'s output cannot be parsed by a machine.\n\n[Patrick Steinhardt](https://gitlab.com/pks-gitlab), Staff Backend Engineer, Gitaly, added a  `--porcelain` [option to git-fetch](https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt---porcelain)\nthat causes `git-fetch(1)` to gives its output in a machine-parseable format.\n\n```\n> git fetch --porcelain --dry-run --quiet\n* cd7ec0e2505463855d04f0a685d53af604079bdf 023a4cca58ac713090df15015a2efeadc73be522 refs/remotes/origin/master\n* 0000000000000000000000000000000000000000 b4a007671bd331f1c6f5857aa9a6ab95d500b412 refs/remotes/origin/alejguer-improve-readabiliy-geo\n  2314938437eb962dadd6a88f45d463f8ed2c7cec 3d3e36fa40e9b87b90ef31f80c63c767d0ef3638 refs/remotes/origin/ali/document-keyless-container-signing\n+ c8107330f8d5a938f6349743310db030ca5159e6 e155670196e4974659304c79e670b238192bce08 refs/remotes/origin/fc-add-failed-jobs-in-mr-part-2\n+ 9ec873de405b3c5078ad1c073711a222e7734337 eb7947e37d05460a94c988bf1f408f96228dd50d refs/remotes/origin/fc-mvc-details-page\n* 0000000000000000000000000000000000000000 36d214774f39d3c3d0569df8befd2b46d22ea94b refs/remotes/origin/group-runner-docs\n+ b357bfdec53b96e76582ac5dd64deb2d35dbe697 7b85d775b1a46ea94e0b241aa0b6aa37ae2e0b69 refs/remotes/origin/jwanjohi-add-abuse-training-data-table\n+ c9beb0b9c0b933903c12393acaa2c4447bb9035f fd13eda262c67a48495a0695659fea10b32e7e02 refs/remotes/origin/jy-permissions-blueprint\n+ 9ecf5a7fb7ca39a6a4296e569af0ddff1058a830 3341369e650c931c46d9880f3b781dc1e21c9f75 refs/remotes/origin/kassio/spike-pages-review-apps\n```\n\nThis change allows us to be much more efficient when mirroring repositories.\n\nDetails of the patch series, including discussions can be found [here](https://lore.kernel.org/git/cover.1683721293.git.ps@pks.im/).\n\n## 2. A new way to read Git attribute files\n[Git attribute](https://docs.gitlab.com/ee/user/project/git_attributes.html) is\na way to define attributes in a Git repository such as syntax highlighting. Until now, Git only read `.gitattribute` files in the wokrtree or the\n`.git/info/attributes` files. On Gitaly servers, we store repositories on disk\nas [bare\nrepositories](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---bare).\nThis means that on the server we don't keep worktrees around. To\nsupport gitattributes on GitLab then, we use a workaround whereby when the user\nchanges attributes on the default branch, we copy the contents of the blob\n`HEAD:.gitattribute` to the `info/attributes` file.\n\n\u003Cpre class=\"mermaid\">\nflowchart TD\n  A[User A] -->|edit HEAD:.gitattributes\u003Cbr/>git push| B[Gitaly]\n  B --> |copy HEAD:.gitattributes\u003Cbr/>to info/attributes| C[info/attributes file]\n  D[GitLab UI] --> |Display code with syntax highlighting| B\n  B -.->|how should I do syntax highlighting?\u003Cbr/>Read info/attributes file| C   \n\u003C/pre>\n\n### Solution: New git option to read attribute files directly\nTo get rid of this extra step of copying a blob to `info/attributes`,\nI added a new git\n[option](https://git-scm.com/docs/git#Documentation/git.txt---attr-sourcelttree-ishgt)\n`--attr-source=\u003Ctree>` whereby a caller can pass in a tree from which Git will\nread the attributes file directly. This way Git can read the attributes blob directly\nwithout a worktree and without having to copy the contents to `info/attributes` each time it changes.\n\n\u003Cpre class=\"mermaid\">\nflowchart TD\n    A[User A] -->|edit HEAD:.gitattributes\u003Cbr/>git push| B[Gitaly]\n    D[GitLab UI] --> |Display code with syntax highlighting|B\n    B --> |Directly read the HEAD:.gitattributes blob|B\n\u003C/pre>\n\nHaving this feature in Git allows us to simplify this process a lot. We no longer\nhave to manually copy over the contents to a separate file. Internally, this\nallows us to delete two RPCs, reducing complexity and improving performance.\n\nDetails of this patch series, including discussions can be found [here](https://lore.kernel.org/git/pull.1470.v6.git.git.1683346530487.gitgitgadget@gmail.com/).\n\n## 3. Bug fix in commit-graph generation numbers\nA regression for truncated commit-graph generation numbers is a bug that we have been hitting for\nspecific repositories, corrupting the commit-graph. The [commit\ngraph](https://git-scm.com/docs/commit-graph) is an important Git optimization\nthat speeds up commit graph walks. Commit graph walks happen whenever Git has to\nwalk through commit history. Any time we display commit history in the UI, for\ninstance, it  will trigger a commit graph walk. Keeping these fast is crucial to a\nsnappy browsing experience.\n\n### Solution: A patch series to fix the bug\nPatrick submitted a patch series to fix the regression for truncated commit-graph generation numbers bug \nDetails of this patch series, including discussions can be found [here](https://lore.kernel.org/git/f8a0a869e8b0882f05cac49d78f49ba3553d3c44.1679904401.git.ps@pks.im/).\n\n## 4. Fix for stale lockfiles in `git-receive-pack`\n`git-receive-pack(1)` is a Git command that handles the server-side of pushes. When `git push` is run\nagainst a GitLab server, Gitaly will handle the `ssh` or `http` request and\nspawn a `git-receive-pack(1)` process behind the scenes to handle the push.\n\n`git-receive-pack(1)` will write a lockfile when processing packfiles in order\nto prevent a race condition where a concurrent garbage-collecting process tries\nto delete the new packfile that is not yet being referenced by anything.\n\nWhen the `git-receive-pack(1)` process dies prematurely for whatever reason, this\nlockfile was being left around instead of being cleaned up. Busy repositories\nthat received many pushes a day could grow in size quickly due to the\naccumulation of these lockfiles.\n\n### Solution: A patch series to clean up unused lockfiles\nPatrick fixed this by submitting a patch series that allows `git-receive-pack(1)` to clean up its unused lockfiles. This allows GitLab to save space on its servers from having to keep useless lockfiles around.\n\nDetails of this patch series, including discussions can be found [here](https://lore.kernel.org/git/e1ee1d8026a361bc58d16bc741e2b347ada7a53e.1678431076.git.ps@pks.im/).\n\n## 5. Fixed geometric repacking with alternate object databases\n[Geometric repacking](https://git-scm.com/docs/git-repack#Documentation/git-repack.txt---geometricltfactorgt)\nis a repacking strategy where instead of packing everything into on giant pack\neach time, several packs are kept around according to a geometric progression\nbased on object size.\n\nThis is useful for large and very busy repositories so that housekeeping doesn't\nhave to pack all of its objects into a giant pack each time.\n\nUnfortunately, geometric repacking had various corner case bugs when an\nalternate object database was involved. At GitLab, we leverage the Git\nalternates mechanism to save space in the case of forks. A fork of a repository\nshares most files. Instead of keeping a second copy of all the data, when we\ncreate a fork, we can deduplicate this data by having both the source\nrepository, as well as the fork repository share objects by pointing to a third\nrepository. This means that only one copy of a blob needs to be kept around\nrather than two.\n\nGeometric repacking bugs prevented it from working in an object database that\nwas connected to an alternate object database.\n\n### Solution: A patch series\nThese bugs have been fixed via a patch series from Patrick. This\nhelps us as we improve our implementation of object pools in Gitaly.\n\nDetails of this patch series, including discussions can be found [here](https://lore.kernel.org/git/cover.1681452028.git.ps@pks.im/).\n","devsecops",[23,24,25,26],"git","news","open source","community",{"slug":28,"featured":6,"template":29},"contributions-to-latest-git-release","BlogPost","content:en-us:blog:contributions-to-latest-git-release.yml","yaml","Contributions To Latest Git Release","content","en-us/blog/contributions-to-latest-git-release.yml","en-us/blog/contributions-to-latest-git-release","yml",{"_path":38,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":40,"_id":449,"_type":31,"title":450,"_source":33,"_file":451,"_stem":452,"_extension":36},"/shared/en-us/main-navigation","en-us",{"logo":41,"freeTrial":46,"sales":51,"login":56,"items":61,"search":390,"minimal":421,"duo":440},{"config":42},{"href":43,"dataGaName":44,"dataGaLocation":45},"/","gitlab logo","header",{"text":47,"config":48},"Get free trial",{"href":49,"dataGaName":50,"dataGaLocation":45},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":52,"config":53},"Talk to sales",{"href":54,"dataGaName":55,"dataGaLocation":45},"/sales/","sales",{"text":57,"config":58},"Sign in",{"href":59,"dataGaName":60,"dataGaLocation":45},"https://gitlab.com/users/sign_in/","sign in",[62,106,202,207,311,371],{"text":63,"config":64,"cards":66,"footer":89},"Platform",{"dataNavLevelOne":65},"platform",[67,73,81],{"title":63,"description":68,"link":69},"The most comprehensive AI-powered DevSecOps Platform",{"text":70,"config":71},"Explore our Platform",{"href":72,"dataGaName":65,"dataGaLocation":45},"/platform/",{"title":74,"description":75,"link":76},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":77,"config":78},"Meet GitLab Duo",{"href":79,"dataGaName":80,"dataGaLocation":45},"/gitlab-duo/","gitlab duo ai",{"title":82,"description":83,"link":84},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":85,"config":86},"Learn more",{"href":87,"dataGaName":88,"dataGaLocation":45},"/why-gitlab/","why gitlab",{"title":90,"items":91},"Get started with",[92,97,102],{"text":93,"config":94},"Platform Engineering",{"href":95,"dataGaName":96,"dataGaLocation":45},"/solutions/platform-engineering/","platform engineering",{"text":98,"config":99},"Developer Experience",{"href":100,"dataGaName":101,"dataGaLocation":45},"/developer-experience/","Developer experience",{"text":103,"config":104},"MLOps",{"href":105,"dataGaName":103,"dataGaLocation":45},"/topics/devops/the-role-of-ai-in-devops/",{"text":107,"left":108,"config":109,"link":111,"lists":115,"footer":184},"Product",true,{"dataNavLevelOne":110},"solutions",{"text":112,"config":113},"View all Solutions",{"href":114,"dataGaName":110,"dataGaLocation":45},"/solutions/",[116,141,163],{"title":117,"description":118,"link":119,"items":124},"Automation","CI/CD and automation to accelerate deployment",{"config":120},{"icon":121,"href":122,"dataGaName":123,"dataGaLocation":45},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[125,129,133,137],{"text":126,"config":127},"CI/CD",{"href":128,"dataGaLocation":45,"dataGaName":126},"/solutions/continuous-integration/",{"text":130,"config":131},"AI-Assisted Development",{"href":79,"dataGaLocation":45,"dataGaName":132},"AI assisted development",{"text":134,"config":135},"Source Code Management",{"href":136,"dataGaLocation":45,"dataGaName":134},"/solutions/source-code-management/",{"text":138,"config":139},"Automated Software Delivery",{"href":122,"dataGaLocation":45,"dataGaName":140},"Automated software delivery",{"title":142,"description":143,"link":144,"items":149},"Security","Deliver code faster without compromising security",{"config":145},{"href":146,"dataGaName":147,"dataGaLocation":45,"icon":148},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[150,153,158],{"text":151,"config":152},"Security & Compliance",{"href":146,"dataGaLocation":45,"dataGaName":151},{"text":154,"config":155},"Software Supply Chain Security",{"href":156,"dataGaLocation":45,"dataGaName":157},"/solutions/supply-chain/","Software supply chain security",{"text":159,"config":160},"Compliance & Governance",{"href":161,"dataGaLocation":45,"dataGaName":162},"/solutions/continuous-software-compliance/","Compliance and governance",{"title":164,"link":165,"items":170},"Measurement",{"config":166},{"icon":167,"href":168,"dataGaName":169,"dataGaLocation":45},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[171,175,179],{"text":172,"config":173},"Visibility & Measurement",{"href":168,"dataGaLocation":45,"dataGaName":174},"Visibility and Measurement",{"text":176,"config":177},"Value Stream Management",{"href":178,"dataGaLocation":45,"dataGaName":176},"/solutions/value-stream-management/",{"text":180,"config":181},"Analytics & Insights",{"href":182,"dataGaLocation":45,"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":45,"dataGaName":191},"/enterprise/","enterprise",{"text":193,"config":194},"Small Business",{"href":195,"dataGaLocation":45,"dataGaName":196},"/small-business/","small business",{"text":198,"config":199},"Public Sector",{"href":200,"dataGaLocation":45,"dataGaName":201},"/solutions/public-sector/","public sector",{"text":203,"config":204},"Pricing",{"href":205,"dataGaName":206,"dataGaLocation":45,"dataNavLevelOne":206},"/pricing/","pricing",{"text":208,"config":209,"link":211,"lists":215,"feature":298},"Resources",{"dataNavLevelOne":210},"resources",{"text":212,"config":213},"View all resources",{"href":214,"dataGaName":210,"dataGaLocation":45},"/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":45},"/install/","install",{"text":225,"config":226},"Quick start guides",{"href":227,"dataGaName":228,"dataGaLocation":45},"/get-started/","quick setup checklists",{"text":230,"config":231},"Learn",{"href":232,"dataGaLocation":45,"dataGaName":233},"https://university.gitlab.com/","learn",{"text":235,"config":236},"Product documentation",{"href":237,"dataGaName":238,"dataGaLocation":45},"https://docs.gitlab.com/","product documentation",{"text":240,"config":241},"Best practice videos",{"href":242,"dataGaName":243,"dataGaLocation":45},"/getting-started-videos/","best practice videos",{"text":245,"config":246},"Integrations",{"href":247,"dataGaName":248,"dataGaLocation":45},"/integrations/","integrations",{"title":250,"items":251},"Discover",[252,257,261,266],{"text":253,"config":254},"Customer success stories",{"href":255,"dataGaName":256,"dataGaLocation":45},"/customers/","customer success stories",{"text":258,"config":259},"Blog",{"href":260,"dataGaName":5,"dataGaLocation":45},"/blog/",{"text":262,"config":263},"Remote",{"href":264,"dataGaName":265,"dataGaLocation":45},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":267,"config":268},"TeamOps",{"href":269,"dataGaName":270,"dataGaLocation":45},"/teamops/","teamops",{"title":272,"items":273},"Connect",[274,279,283,288,293],{"text":275,"config":276},"GitLab Services",{"href":277,"dataGaName":278,"dataGaLocation":45},"/services/","services",{"text":280,"config":281},"Community",{"href":282,"dataGaName":26,"dataGaLocation":45},"/community/",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":45},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":45},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":45},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"/images/navigation/the-source-promo-card.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":45},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":45},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":45},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":45},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":45},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":45},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":45},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":45},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":45},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":45},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":45},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":45},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":52,"config":378},{"href":54,"dataGaName":379,"dataGaLocation":45},"talk to sales",{"text":381,"config":382},"Get help",{"href":383,"dataGaName":384,"dataGaLocation":45},"/support/","get help",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":45},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":59,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":74,"config":403},{"href":79,"dataGaName":74,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":126,"config":409},{"href":128,"dataGaName":126,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":87,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":50,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"/images/brand/gitlab-logo-type.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":79,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":454,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"title":455,"button":456,"image":460,"config":463,"_id":465,"_type":31,"_source":33,"_file":466,"_stem":467,"_extension":36},"/shared/en-us/banner","is now in public beta!",{"text":85,"config":457},{"href":458,"dataGaName":459,"dataGaLocation":45},"/gitlab-duo/agent-platform/","duo banner",{"config":461},{"src":462},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":464},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":469,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":470,"_id":675,"_type":31,"title":676,"_source":33,"_file":677,"_stem":678,"_extension":36},"/shared/en-us/main-footer",{"text":471,"source":472,"edit":478,"contribute":483,"config":488,"items":493,"minimal":667},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":473,"config":474},"View page source",{"href":475,"dataGaName":476,"dataGaLocation":477},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":479,"config":480},"Edit this page",{"href":481,"dataGaName":482,"dataGaLocation":477},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":484,"config":485},"Please contribute",{"href":486,"dataGaName":487,"dataGaLocation":477},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":489,"facebook":490,"youtube":491,"linkedin":492},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[494,517,574,603,637],{"title":63,"links":495,"subMenu":500},[496],{"text":497,"config":498},"DevSecOps platform",{"href":72,"dataGaName":499,"dataGaLocation":477},"devsecops platform",[501],{"title":203,"links":502},[503,507,512],{"text":504,"config":505},"View plans",{"href":205,"dataGaName":506,"dataGaLocation":477},"view plans",{"text":508,"config":509},"Why Premium?",{"href":510,"dataGaName":511,"dataGaLocation":477},"/pricing/premium/","why premium",{"text":513,"config":514},"Why Ultimate?",{"href":515,"dataGaName":516,"dataGaLocation":477},"/pricing/ultimate/","why ultimate",{"title":518,"links":519},"Solutions",[520,525,528,530,535,540,544,547,551,556,558,561,564,569],{"text":521,"config":522},"Digital transformation",{"href":523,"dataGaName":524,"dataGaLocation":477},"/topics/digital-transformation/","digital transformation",{"text":151,"config":526},{"href":146,"dataGaName":527,"dataGaLocation":477},"security & compliance",{"text":140,"config":529},{"href":122,"dataGaName":123,"dataGaLocation":477},{"text":531,"config":532},"Agile development",{"href":533,"dataGaName":534,"dataGaLocation":477},"/solutions/agile-delivery/","agile delivery",{"text":536,"config":537},"Cloud transformation",{"href":538,"dataGaName":539,"dataGaLocation":477},"/topics/cloud-native/","cloud transformation",{"text":541,"config":542},"SCM",{"href":136,"dataGaName":543,"dataGaLocation":477},"source code management",{"text":126,"config":545},{"href":128,"dataGaName":546,"dataGaLocation":477},"continuous integration & delivery",{"text":548,"config":549},"Value stream management",{"href":178,"dataGaName":550,"dataGaLocation":477},"value stream management",{"text":552,"config":553},"GitOps",{"href":554,"dataGaName":555,"dataGaLocation":477},"/solutions/gitops/","gitops",{"text":188,"config":557},{"href":190,"dataGaName":191,"dataGaLocation":477},{"text":559,"config":560},"Small business",{"href":195,"dataGaName":196,"dataGaLocation":477},{"text":562,"config":563},"Public sector",{"href":200,"dataGaName":201,"dataGaLocation":477},{"text":565,"config":566},"Education",{"href":567,"dataGaName":568,"dataGaLocation":477},"/solutions/education/","education",{"text":570,"config":571},"Financial services",{"href":572,"dataGaName":573,"dataGaLocation":477},"/solutions/finance/","financial services",{"title":208,"links":575},[576,578,580,582,585,587,589,591,593,595,597,599,601],{"text":220,"config":577},{"href":222,"dataGaName":223,"dataGaLocation":477},{"text":225,"config":579},{"href":227,"dataGaName":228,"dataGaLocation":477},{"text":230,"config":581},{"href":232,"dataGaName":233,"dataGaLocation":477},{"text":235,"config":583},{"href":237,"dataGaName":584,"dataGaLocation":477},"docs",{"text":258,"config":586},{"href":260,"dataGaName":5,"dataGaLocation":477},{"text":253,"config":588},{"href":255,"dataGaName":256,"dataGaLocation":477},{"text":262,"config":590},{"href":264,"dataGaName":265,"dataGaLocation":477},{"text":275,"config":592},{"href":277,"dataGaName":278,"dataGaLocation":477},{"text":267,"config":594},{"href":269,"dataGaName":270,"dataGaLocation":477},{"text":280,"config":596},{"href":282,"dataGaName":26,"dataGaLocation":477},{"text":284,"config":598},{"href":286,"dataGaName":287,"dataGaLocation":477},{"text":289,"config":600},{"href":291,"dataGaName":292,"dataGaLocation":477},{"text":294,"config":602},{"href":296,"dataGaName":297,"dataGaLocation":477},{"title":312,"links":604},[605,607,609,611,613,615,617,621,626,628,630,632],{"text":319,"config":606},{"href":321,"dataGaName":314,"dataGaLocation":477},{"text":324,"config":608},{"href":326,"dataGaName":327,"dataGaLocation":477},{"text":332,"config":610},{"href":334,"dataGaName":335,"dataGaLocation":477},{"text":337,"config":612},{"href":339,"dataGaName":340,"dataGaLocation":477},{"text":342,"config":614},{"href":344,"dataGaName":345,"dataGaLocation":477},{"text":347,"config":616},{"href":349,"dataGaName":350,"dataGaLocation":477},{"text":618,"config":619},"Sustainability",{"href":620,"dataGaName":618,"dataGaLocation":477},"/sustainability/",{"text":622,"config":623},"Diversity, inclusion and belonging (DIB)",{"href":624,"dataGaName":625,"dataGaLocation":477},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":627},{"href":354,"dataGaName":355,"dataGaLocation":477},{"text":362,"config":629},{"href":364,"dataGaName":365,"dataGaLocation":477},{"text":367,"config":631},{"href":369,"dataGaName":370,"dataGaLocation":477},{"text":633,"config":634},"Modern Slavery Transparency Statement",{"href":635,"dataGaName":636,"dataGaLocation":477},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":638,"links":639},"Contact Us",[640,643,645,647,652,657,662],{"text":641,"config":642},"Contact an expert",{"href":54,"dataGaName":55,"dataGaLocation":477},{"text":381,"config":644},{"href":383,"dataGaName":384,"dataGaLocation":477},{"text":386,"config":646},{"href":388,"dataGaName":389,"dataGaLocation":477},{"text":648,"config":649},"Status",{"href":650,"dataGaName":651,"dataGaLocation":477},"https://status.gitlab.com/","status",{"text":653,"config":654},"Terms of use",{"href":655,"dataGaName":656,"dataGaLocation":477},"/terms/","terms of use",{"text":658,"config":659},"Privacy statement",{"href":660,"dataGaName":661,"dataGaLocation":477},"/privacy/","privacy statement",{"text":663,"config":664},"Cookie preferences",{"dataGaName":665,"dataGaLocation":477,"id":666,"isOneTrustButton":108},"cookie preferences","ot-sdk-btn",{"items":668},[669,671,673],{"text":653,"config":670},{"href":655,"dataGaName":656,"dataGaLocation":477},{"text":658,"config":672},{"href":660,"dataGaName":661,"dataGaLocation":477},{"text":663,"config":674},{"dataGaName":665,"dataGaLocation":477,"id":666,"isOneTrustButton":108},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[680],{"_path":681,"_dir":682,"_draft":6,"_partial":6,"_locale":7,"content":683,"config":687,"_id":689,"_type":31,"title":18,"_source":33,"_file":690,"_stem":691,"_extension":36},"/en-us/blog/authors/john-cai","authors",{"name":18,"config":684},{"headshot":685,"ctfId":686},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667386/Blog/Author%20Headshots/jcaigitlab-headshot.jpg","jcaigitlab",{"template":688},"BlogAuthor","content:en-us:blog:authors:john-cai.yml","en-us/blog/authors/john-cai.yml","en-us/blog/authors/john-cai",{"_path":693,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"header":694,"eyebrow":695,"blurb":696,"button":697,"secondaryButton":701,"_id":703,"_type":31,"title":704,"_source":33,"_file":705,"_stem":706,"_extension":36},"/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":47,"config":698},{"href":699,"dataGaName":50,"dataGaLocation":700},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":52,"config":702},{"href":54,"dataGaName":55,"dataGaLocation":700},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1753981634979]