[{"data":1,"prerenderedAt":720},["ShallowReactive",2],{"/en-us/blog/contributions-to-latest-git-release/":3,"navigation-en-us":37,"banner-en-us":465,"footer-en-us":482,"John Cai":692,"next-steps-en-us":705},{"_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","[Git\n2.41](https://gitlab.com/gitlab-org/git/-/raw/master/Documentation/RelNotes/2.41.0.txt)\n\nwas officially released on June 1, 2023, and included some improvements from\nGitLab's Git team. Git is the foundation of\n\nrepository data at GitLab. GitLab's Git team works on everything from new\n\nfeatures, performance improvements, documentation improvements, and growing\nthe Git\n\ncommunity. Often our contributions to Git have a lot to do with the way we\nintegrate Git into\n\nour services at GitLab. Here are some highlights from this latest Git\nrelease,\n\nand a window into how we use Git on the server side at GitLab.\n\n\n## 1. Machine-parseable fetch output\n\nWhen `git-fetch` is run, the output is a familiar for users of Git and looks\n\nsomething like this:\n\n\n```bash\n\n> git fetch\n\nremote: Enumerating objects: 296, done.\n\nremote: Counting objects: 100% (189/189), done.\n\nremote: Compressing objects: 100% (103/103), done.\n\nremote: Total 296 (delta 132), reused 84 (delta 84), pack-reused 107\n\nReceiving objects: 100% (296/296), 184.46 KiB | 11.53 MiB/s, done.\n\nResolving deltas: 100% (173/173), completed with 42 local objects.\n\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\n\nThe problem with this output is that it's not meant for machines to parse.\n\n\nBut why would it be useful to make this output parseable by machines? To\nunderstand\n\nthis, we need to back up a little bit and talk about Gitaly Cluster. [Gitaly\nCluster](https://docs.gitlab.com/ee/administration/gitaly/#gitaly-cluster)\n\nis a service at GitLab that provides high availability of Git repositories\nby\n\nreplicating repository writes to replica nodes. Each time a write comes in\nwhich\n\nchanges a Git repository (for example, a push that updates a reference) the\nwrite goes to\n\nthe primary node, and to all replica nodes before the write can succeed. A\n\nvoting mechanism takes place where the nodes vote on what its updated\n\nvalue for the reference would be. This vote succeeds when a quorum of\nreplica\n\nnodes have successfully written the ref, and the write succeeds.\n\n\nOne of our remote procedure calls (RPCs) in Gitaly runs `git-fetch(1)` for\nrepository mirroring. By\n\ndefault, when `git-fetch(1)` is run, it will update any references that are\nable\n\nto be fast-forwarded and fail on any reference that has since diverged will\nnot\n\nbe updated.\n\n\nAs mentioned above, whenever there is an operation that modifies a\nrepository, there\n\nis a voting mechanism that ensures the same modification is made to all\nreplica nodes.\n\nTo dive in even a little deeper, our voting mechanism leverages Git's\nreference transaction hook,\n\nwhich runs an executable once per reference transaction. `git-fetch(1)` by\ndefault will\n\nstart a reference transaction per reference it updates. A fetch that updates\nhundreds or\n\neven thousand of references would thus vote once per reference that gets\nupdated.\n\n\nIn the following sequence diagram, we are only showing one Gitaly node, but\nfor a Gitaly Cluster\n\nwith, let's say, three nodes, what happens with the Gitaly primary also\nhappens in\n\nthe replicas.\n\n\n```mermaid\n\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\n\nThis is inefficient. Ideally we would want to vote once per batch of\nreferences\n\nupdated from one `git-fetch(1)` call. There is an option `--atomic` in\n\n`git-fetch(1)` that will open one reference transaction for all references\n\nupdated by `git-fetch(1)`. However, when `--atomic` is used, a `git-fetch`\ncall will fail if any references have since diverged. This is not how we\nwant repository mirroring to work. We actually want `git-fetch` to update\nwhichever refs it can.\n\n\nSo, that means we cannot use the `--atomic` flag and are thus stuck voting\nper reference we update.\n\n\n### Solution: Handle the reference update ourselves\n\nThe way we are solving this inefficiency is to handle the reference update\n\nourselves. Instead of relying on `git-fetch(1)` to both fetch the objects\nand\n\nupdate all the references, we can use the `--dry-run` option of\n`git-fetch(1)`\n\nto first fetch the objects into a quarantine directory. Then if we can know\n\nwhich references *would* be updated, we can start a reference transaction\n\nourselves with `git-update-ref(1)` and update all the refs in one\ntransaction,\n\nhence triggering a single vote only.\n\n\n```mermaid\n\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\n\nA requirement for this however, is that we would be able to parse the output\nof\n\n`git-fetch(1)` to tell which refs will be updated and to what values.\nCurrently\n\nin `--dry-run`, `git-fetch(1)`'s output cannot be parsed by a machine.\n\n\n[Patrick Steinhardt](https://gitlab.com/pks-gitlab), Staff Backend Engineer,\nGitaly, added a  `--porcelain` [option to\ngit-fetch](https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt---porcelain)\n\nthat causes `git-fetch(1)` to gives its output in a machine-parseable\nformat.\n\n\n```\n\n> git fetch --porcelain --dry-run --quiet\n\n* cd7ec0e2505463855d04f0a685d53af604079bdf\n023a4cca58ac713090df15015a2efeadc73be522 refs/remotes/origin/master\n\n* 0000000000000000000000000000000000000000\nb4a007671bd331f1c6f5857aa9a6ab95d500b412\nrefs/remotes/origin/alejguer-improve-readabiliy-geo\n  2314938437eb962dadd6a88f45d463f8ed2c7cec 3d3e36fa40e9b87b90ef31f80c63c767d0ef3638 refs/remotes/origin/ali/document-keyless-container-signing\n+ c8107330f8d5a938f6349743310db030ca5159e6\ne155670196e4974659304c79e670b238192bce08\nrefs/remotes/origin/fc-add-failed-jobs-in-mr-part-2\n\n+ 9ec873de405b3c5078ad1c073711a222e7734337\neb7947e37d05460a94c988bf1f408f96228dd50d\nrefs/remotes/origin/fc-mvc-details-page\n\n* 0000000000000000000000000000000000000000\n36d214774f39d3c3d0569df8befd2b46d22ea94b\nrefs/remotes/origin/group-runner-docs\n\n+ b357bfdec53b96e76582ac5dd64deb2d35dbe697\n7b85d775b1a46ea94e0b241aa0b6aa37ae2e0b69\nrefs/remotes/origin/jwanjohi-add-abuse-training-data-table\n\n+ c9beb0b9c0b933903c12393acaa2c4447bb9035f\nfd13eda262c67a48495a0695659fea10b32e7e02\nrefs/remotes/origin/jy-permissions-blueprint\n\n+ 9ecf5a7fb7ca39a6a4296e569af0ddff1058a830\n3341369e650c931c46d9880f3b781dc1e21c9f75\nrefs/remotes/origin/kassio/spike-pages-review-apps\n\n```\n\n\nThis change allows us to be much more efficient when mirroring repositories.\n\n\nDetails of the patch series, including discussions can be found\n[here](https://lore.kernel.org/git/cover.1683721293.git.ps@pks.im/).\n\n\n## 2. A new way to read Git attribute files\n\n[Git attribute](https://docs.gitlab.com/ee/user/project/git_attributes.html)\nis\n\na way to define attributes in a Git repository such as syntax highlighting.\nUntil now, Git only read `.gitattribute` files in the wokrtree or the\n\n`.git/info/attributes` files. On Gitaly servers, we store repositories on\ndisk\n\nas [bare\n\nrepositories](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---bare).\n\nThis means that on the server we don't keep worktrees around. To\n\nsupport gitattributes on GitLab then, we use a workaround whereby when the\nuser\n\nchanges attributes on the default branch, we copy the contents of the blob\n\n`HEAD:.gitattribute` to the `info/attributes` file.\n\n\n\u003Cpre class=\"mermaid\">\n\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\n### Solution: New git option to read attribute files directly\n\nTo get rid of this extra step of copying a blob to `info/attributes`,\n\nI added a new git\n\n[option](https://git-scm.com/docs/git#Documentation/git.txt---attr-sourcelttree-ishgt)\n\n`--attr-source=\u003Ctree>` whereby a caller can pass in a tree from which Git\nwill\n\nread the attributes file directly. This way Git can read the attributes blob\ndirectly\n\nwithout a worktree and without having to copy the contents to\n`info/attributes` each time it changes.\n\n\n\u003Cpre class=\"mermaid\">\n\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\n\nHaving this feature in Git allows us to simplify this process a lot. We no\nlonger\n\nhave to manually copy over the contents to a separate file. Internally, this\n\nallows us to delete two RPCs, reducing complexity and improving performance.\n\n\nDetails of this patch series, including discussions can be found\n[here](https://lore.kernel.org/git/pull.1470.v6.git.git.1683346530487.gitgitgadget@gmail.com/).\n\n\n## 3. Bug fix in commit-graph generation numbers\n\nA regression for truncated commit-graph generation numbers is a bug that we\nhave been hitting for\n\nspecific repositories, corrupting the commit-graph. The [commit\n\ngraph](https://git-scm.com/docs/commit-graph) is an important Git\noptimization\n\nthat speeds up commit graph walks. Commit graph walks happen whenever Git\nhas to\n\nwalk through commit history. Any time we display commit history in the UI,\nfor\n\ninstance, it  will trigger a commit graph walk. Keeping these fast is\ncrucial to a\n\nsnappy browsing experience.\n\n\n### Solution: A patch series to fix the bug\n\nPatrick submitted a patch series to fix the regression for truncated\ncommit-graph generation numbers bug \n\nDetails of this patch series, including discussions can be found\n[here](https://lore.kernel.org/git/f8a0a869e8b0882f05cac49d78f49ba3553d3c44.1679904401.git.ps@pks.im/).\n\n\n## 4. Fix for stale lockfiles in `git-receive-pack`\n\n`git-receive-pack(1)` is a Git command that handles the server-side of\npushes. When `git push` is run\n\nagainst a GitLab server, Gitaly will handle the `ssh` or `http` request and\n\nspawn a `git-receive-pack(1)` process behind the scenes to handle the push.\n\n\n`git-receive-pack(1)` will write a lockfile when processing packfiles in\norder\n\nto prevent a race condition where a concurrent garbage-collecting process\ntries\n\nto delete the new packfile that is not yet being referenced by anything.\n\n\nWhen the `git-receive-pack(1)` process dies prematurely for whatever reason,\nthis\n\nlockfile was being left around instead of being cleaned up. Busy\nrepositories\n\nthat received many pushes a day could grow in size quickly due to the\n\naccumulation of these lockfiles.\n\n\n### Solution: A patch series to clean up unused lockfiles\n\nPatrick fixed this by submitting a patch series that allows\n`git-receive-pack(1)` to clean up its unused lockfiles. This allows GitLab\nto save space on its servers from having to keep useless lockfiles around.\n\n\nDetails of this patch series, including discussions can be found\n[here](https://lore.kernel.org/git/e1ee1d8026a361bc58d16bc741e2b347ada7a53e.1678431076.git.ps@pks.im/).\n\n\n## 5. Fixed geometric repacking with alternate object databases\n\n[Geometric\nrepacking](https://git-scm.com/docs/git-repack#Documentation/git-repack.txt---geometricltfactorgt)\n\nis a repacking strategy where instead of packing everything into on giant\npack\n\neach time, several packs are kept around according to a geometric\nprogression\n\nbased on object size.\n\n\nThis is useful for large and very busy repositories so that housekeeping\ndoesn't\n\nhave to pack all of its objects into a giant pack each time.\n\n\nUnfortunately, geometric repacking had various corner case bugs when an\n\nalternate object database was involved. At GitLab, we leverage the Git\n\nalternates mechanism to save space in the case of forks. A fork of a\nrepository\n\nshares most files. Instead of keeping a second copy of all the data, when we\n\ncreate a fork, we can deduplicate this data by having both the source\n\nrepository, as well as the fork repository share objects by pointing to a\nthird\n\nrepository. This means that only one copy of a blob needs to be kept around\n\nrather than two.\n\n\nGeometric repacking bugs prevented it from working in an object database\nthat\n\nwas connected to an alternate object database.\n\n\n### Solution: A patch series\n\nThese bugs have been fixed via a patch series from Patrick. This\n\nhelps us as we improve our implementation of object pools in Gitaly.\n\n\nDetails of this patch series, including discussions can be found\n[here](https://lore.kernel.org/git/cover.1681452028.git.ps@pks.im/).","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":461,"_type":31,"title":462,"_source":33,"_file":463,"_stem":464,"_extension":36},"/shared/en-us/main-navigation","en-us",{"logo":41,"freeTrial":46,"sales":51,"login":56,"items":61,"search":392,"minimal":423,"duo":442,"pricingDeployment":451},{"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,204,209,313,373],{"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":186},"Product",true,{"dataNavLevelOne":110},"solutions",{"text":112,"config":113},"View all Solutions",{"href":114,"dataGaName":110,"dataGaLocation":45},"/solutions/",[116,141,165],{"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,155,160],{"text":151,"config":152},"Application Security Testing",{"href":153,"dataGaName":154,"dataGaLocation":45},"/solutions/application-security-testing/","Application security testing",{"text":156,"config":157},"Software Supply Chain Security",{"href":158,"dataGaLocation":45,"dataGaName":159},"/solutions/supply-chain/","Software supply chain security",{"text":161,"config":162},"Software Compliance",{"href":163,"dataGaName":164,"dataGaLocation":45},"/solutions/software-compliance/","software compliance",{"title":166,"link":167,"items":172},"Measurement",{"config":168},{"icon":169,"href":170,"dataGaName":171,"dataGaLocation":45},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[173,177,181],{"text":174,"config":175},"Visibility & Measurement",{"href":170,"dataGaLocation":45,"dataGaName":176},"Visibility and Measurement",{"text":178,"config":179},"Value Stream Management",{"href":180,"dataGaLocation":45,"dataGaName":178},"/solutions/value-stream-management/",{"text":182,"config":183},"Analytics & Insights",{"href":184,"dataGaLocation":45,"dataGaName":185},"/solutions/analytics-and-insights/","Analytics and insights",{"title":187,"items":188},"GitLab for",[189,194,199],{"text":190,"config":191},"Enterprise",{"href":192,"dataGaLocation":45,"dataGaName":193},"/enterprise/","enterprise",{"text":195,"config":196},"Small Business",{"href":197,"dataGaLocation":45,"dataGaName":198},"/small-business/","small business",{"text":200,"config":201},"Public Sector",{"href":202,"dataGaLocation":45,"dataGaName":203},"/solutions/public-sector/","public sector",{"text":205,"config":206},"Pricing",{"href":207,"dataGaName":208,"dataGaLocation":45,"dataNavLevelOne":208},"/pricing/","pricing",{"text":210,"config":211,"link":213,"lists":217,"feature":300},"Resources",{"dataNavLevelOne":212},"resources",{"text":214,"config":215},"View all resources",{"href":216,"dataGaName":212,"dataGaLocation":45},"/resources/",[218,251,273],{"title":219,"items":220},"Getting started",[221,226,231,236,241,246],{"text":222,"config":223},"Install",{"href":224,"dataGaName":225,"dataGaLocation":45},"/install/","install",{"text":227,"config":228},"Quick start guides",{"href":229,"dataGaName":230,"dataGaLocation":45},"/get-started/","quick setup checklists",{"text":232,"config":233},"Learn",{"href":234,"dataGaLocation":45,"dataGaName":235},"https://university.gitlab.com/","learn",{"text":237,"config":238},"Product documentation",{"href":239,"dataGaName":240,"dataGaLocation":45},"https://docs.gitlab.com/","product documentation",{"text":242,"config":243},"Best practice videos",{"href":244,"dataGaName":245,"dataGaLocation":45},"/getting-started-videos/","best practice videos",{"text":247,"config":248},"Integrations",{"href":249,"dataGaName":250,"dataGaLocation":45},"/integrations/","integrations",{"title":252,"items":253},"Discover",[254,259,263,268],{"text":255,"config":256},"Customer success stories",{"href":257,"dataGaName":258,"dataGaLocation":45},"/customers/","customer success stories",{"text":260,"config":261},"Blog",{"href":262,"dataGaName":5,"dataGaLocation":45},"/blog/",{"text":264,"config":265},"Remote",{"href":266,"dataGaName":267,"dataGaLocation":45},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":269,"config":270},"TeamOps",{"href":271,"dataGaName":272,"dataGaLocation":45},"/teamops/","teamops",{"title":274,"items":275},"Connect",[276,281,285,290,295],{"text":277,"config":278},"GitLab Services",{"href":279,"dataGaName":280,"dataGaLocation":45},"/services/","services",{"text":282,"config":283},"Community",{"href":284,"dataGaName":26,"dataGaLocation":45},"/community/",{"text":286,"config":287},"Forum",{"href":288,"dataGaName":289,"dataGaLocation":45},"https://forum.gitlab.com/","forum",{"text":291,"config":292},"Events",{"href":293,"dataGaName":294,"dataGaLocation":45},"/events/","events",{"text":296,"config":297},"Partners",{"href":298,"dataGaName":299,"dataGaLocation":45},"/partners/","partners",{"backgroundColor":301,"textColor":302,"text":303,"image":304,"link":308},"#2f2a6b","#fff","Insights for the future of software development",{"altText":305,"config":306},"the source promo card",{"src":307},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":309,"config":310},"Read the latest",{"href":311,"dataGaName":312,"dataGaLocation":45},"/the-source/","the source",{"text":314,"config":315,"lists":317},"Company",{"dataNavLevelOne":316},"company",[318],{"items":319},[320,325,331,333,338,343,348,353,358,363,368],{"text":321,"config":322},"About",{"href":323,"dataGaName":324,"dataGaLocation":45},"/company/","about",{"text":326,"config":327,"footerGa":330},"Jobs",{"href":328,"dataGaName":329,"dataGaLocation":45},"/jobs/","jobs",{"dataGaName":329},{"text":291,"config":332},{"href":293,"dataGaName":294,"dataGaLocation":45},{"text":334,"config":335},"Leadership",{"href":336,"dataGaName":337,"dataGaLocation":45},"/company/team/e-group/","leadership",{"text":339,"config":340},"Team",{"href":341,"dataGaName":342,"dataGaLocation":45},"/company/team/","team",{"text":344,"config":345},"Handbook",{"href":346,"dataGaName":347,"dataGaLocation":45},"https://handbook.gitlab.com/","handbook",{"text":349,"config":350},"Investor relations",{"href":351,"dataGaName":352,"dataGaLocation":45},"https://ir.gitlab.com/","investor relations",{"text":354,"config":355},"Trust Center",{"href":356,"dataGaName":357,"dataGaLocation":45},"/security/","trust center",{"text":359,"config":360},"AI Transparency Center",{"href":361,"dataGaName":362,"dataGaLocation":45},"/ai-transparency-center/","ai transparency center",{"text":364,"config":365},"Newsletter",{"href":366,"dataGaName":367,"dataGaLocation":45},"/company/contact/","newsletter",{"text":369,"config":370},"Press",{"href":371,"dataGaName":372,"dataGaLocation":45},"/press/","press",{"text":374,"config":375,"lists":376},"Contact us",{"dataNavLevelOne":316},[377],{"items":378},[379,382,387],{"text":52,"config":380},{"href":54,"dataGaName":381,"dataGaLocation":45},"talk to sales",{"text":383,"config":384},"Get help",{"href":385,"dataGaName":386,"dataGaLocation":45},"/support/","get help",{"text":388,"config":389},"Customer portal",{"href":390,"dataGaName":391,"dataGaLocation":45},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":393,"login":394,"suggestions":401},"Close",{"text":395,"link":396},"To search repositories and projects, login to",{"text":397,"config":398},"gitlab.com",{"href":59,"dataGaName":399,"dataGaLocation":400},"search login","search",{"text":402,"default":403},"Suggestions",[404,406,410,412,416,420],{"text":74,"config":405},{"href":79,"dataGaName":74,"dataGaLocation":400},{"text":407,"config":408},"Code Suggestions (AI)",{"href":409,"dataGaName":407,"dataGaLocation":400},"/solutions/code-suggestions/",{"text":126,"config":411},{"href":128,"dataGaName":126,"dataGaLocation":400},{"text":413,"config":414},"GitLab on AWS",{"href":415,"dataGaName":413,"dataGaLocation":400},"/partners/technology-partners/aws/",{"text":417,"config":418},"GitLab on Google Cloud",{"href":419,"dataGaName":417,"dataGaLocation":400},"/partners/technology-partners/google-cloud-platform/",{"text":421,"config":422},"Why GitLab?",{"href":87,"dataGaName":421,"dataGaLocation":400},{"freeTrial":424,"mobileIcon":429,"desktopIcon":434,"secondaryButton":437},{"text":425,"config":426},"Start free trial",{"href":427,"dataGaName":50,"dataGaLocation":428},"https://gitlab.com/-/trials/new/","nav",{"altText":430,"config":431},"Gitlab Icon",{"src":432,"dataGaName":433,"dataGaLocation":428},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":430,"config":435},{"src":436,"dataGaName":433,"dataGaLocation":428},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":438,"config":439},"Get Started",{"href":440,"dataGaName":441,"dataGaLocation":428},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":443,"mobileIcon":447,"desktopIcon":449},{"text":444,"config":445},"Learn more about GitLab Duo",{"href":79,"dataGaName":446,"dataGaLocation":428},"gitlab duo",{"altText":430,"config":448},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":450},{"src":436,"dataGaName":433,"dataGaLocation":428},{"freeTrial":452,"mobileIcon":457,"desktopIcon":459},{"text":453,"config":454},"Back to pricing",{"href":207,"dataGaName":455,"dataGaLocation":428,"icon":456},"back to pricing","GoBack",{"altText":430,"config":458},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":460},{"src":436,"dataGaName":433,"dataGaLocation":428},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":466,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"title":467,"button":468,"image":473,"config":477,"_id":479,"_type":31,"_source":33,"_file":480,"_stem":481,"_extension":36},"/shared/en-us/banner","is now in public beta!",{"text":469,"config":470},"Try the Beta",{"href":471,"dataGaName":472,"dataGaLocation":45},"/gitlab-duo/agent-platform/","duo banner",{"altText":474,"config":475},"GitLab Duo Agent Platform",{"src":476},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":478},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":483,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":484,"_id":688,"_type":31,"title":689,"_source":33,"_file":690,"_stem":691,"_extension":36},"/shared/en-us/main-footer",{"text":485,"source":486,"edit":492,"contribute":497,"config":502,"items":507,"minimal":680},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":487,"config":488},"View page source",{"href":489,"dataGaName":490,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":493,"config":494},"Edit this page",{"href":495,"dataGaName":496,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":498,"config":499},"Please contribute",{"href":500,"dataGaName":501,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":503,"facebook":504,"youtube":505,"linkedin":506},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[508,531,587,616,650],{"title":63,"links":509,"subMenu":514},[510],{"text":511,"config":512},"DevSecOps platform",{"href":72,"dataGaName":513,"dataGaLocation":491},"devsecops platform",[515],{"title":205,"links":516},[517,521,526],{"text":518,"config":519},"View plans",{"href":207,"dataGaName":520,"dataGaLocation":491},"view plans",{"text":522,"config":523},"Why Premium?",{"href":524,"dataGaName":525,"dataGaLocation":491},"/pricing/premium/","why premium",{"text":527,"config":528},"Why Ultimate?",{"href":529,"dataGaName":530,"dataGaLocation":491},"/pricing/ultimate/","why ultimate",{"title":532,"links":533},"Solutions",[534,539,541,543,548,553,557,560,564,569,571,574,577,582],{"text":535,"config":536},"Digital transformation",{"href":537,"dataGaName":538,"dataGaLocation":491},"/topics/digital-transformation/","digital transformation",{"text":151,"config":540},{"href":153,"dataGaName":151,"dataGaLocation":491},{"text":140,"config":542},{"href":122,"dataGaName":123,"dataGaLocation":491},{"text":544,"config":545},"Agile development",{"href":546,"dataGaName":547,"dataGaLocation":491},"/solutions/agile-delivery/","agile delivery",{"text":549,"config":550},"Cloud transformation",{"href":551,"dataGaName":552,"dataGaLocation":491},"/topics/cloud-native/","cloud transformation",{"text":554,"config":555},"SCM",{"href":136,"dataGaName":556,"dataGaLocation":491},"source code management",{"text":126,"config":558},{"href":128,"dataGaName":559,"dataGaLocation":491},"continuous integration & delivery",{"text":561,"config":562},"Value stream management",{"href":180,"dataGaName":563,"dataGaLocation":491},"value stream management",{"text":565,"config":566},"GitOps",{"href":567,"dataGaName":568,"dataGaLocation":491},"/solutions/gitops/","gitops",{"text":190,"config":570},{"href":192,"dataGaName":193,"dataGaLocation":491},{"text":572,"config":573},"Small business",{"href":197,"dataGaName":198,"dataGaLocation":491},{"text":575,"config":576},"Public sector",{"href":202,"dataGaName":203,"dataGaLocation":491},{"text":578,"config":579},"Education",{"href":580,"dataGaName":581,"dataGaLocation":491},"/solutions/education/","education",{"text":583,"config":584},"Financial services",{"href":585,"dataGaName":586,"dataGaLocation":491},"/solutions/finance/","financial services",{"title":210,"links":588},[589,591,593,595,598,600,602,604,606,608,610,612,614],{"text":222,"config":590},{"href":224,"dataGaName":225,"dataGaLocation":491},{"text":227,"config":592},{"href":229,"dataGaName":230,"dataGaLocation":491},{"text":232,"config":594},{"href":234,"dataGaName":235,"dataGaLocation":491},{"text":237,"config":596},{"href":239,"dataGaName":597,"dataGaLocation":491},"docs",{"text":260,"config":599},{"href":262,"dataGaName":5,"dataGaLocation":491},{"text":255,"config":601},{"href":257,"dataGaName":258,"dataGaLocation":491},{"text":264,"config":603},{"href":266,"dataGaName":267,"dataGaLocation":491},{"text":277,"config":605},{"href":279,"dataGaName":280,"dataGaLocation":491},{"text":269,"config":607},{"href":271,"dataGaName":272,"dataGaLocation":491},{"text":282,"config":609},{"href":284,"dataGaName":26,"dataGaLocation":491},{"text":286,"config":611},{"href":288,"dataGaName":289,"dataGaLocation":491},{"text":291,"config":613},{"href":293,"dataGaName":294,"dataGaLocation":491},{"text":296,"config":615},{"href":298,"dataGaName":299,"dataGaLocation":491},{"title":314,"links":617},[618,620,622,624,626,628,630,634,639,641,643,645],{"text":321,"config":619},{"href":323,"dataGaName":316,"dataGaLocation":491},{"text":326,"config":621},{"href":328,"dataGaName":329,"dataGaLocation":491},{"text":334,"config":623},{"href":336,"dataGaName":337,"dataGaLocation":491},{"text":339,"config":625},{"href":341,"dataGaName":342,"dataGaLocation":491},{"text":344,"config":627},{"href":346,"dataGaName":347,"dataGaLocation":491},{"text":349,"config":629},{"href":351,"dataGaName":352,"dataGaLocation":491},{"text":631,"config":632},"Sustainability",{"href":633,"dataGaName":631,"dataGaLocation":491},"/sustainability/",{"text":635,"config":636},"Diversity, inclusion and belonging (DIB)",{"href":637,"dataGaName":638,"dataGaLocation":491},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":354,"config":640},{"href":356,"dataGaName":357,"dataGaLocation":491},{"text":364,"config":642},{"href":366,"dataGaName":367,"dataGaLocation":491},{"text":369,"config":644},{"href":371,"dataGaName":372,"dataGaLocation":491},{"text":646,"config":647},"Modern Slavery Transparency Statement",{"href":648,"dataGaName":649,"dataGaLocation":491},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":651,"links":652},"Contact Us",[653,656,658,660,665,670,675],{"text":654,"config":655},"Contact an expert",{"href":54,"dataGaName":55,"dataGaLocation":491},{"text":383,"config":657},{"href":385,"dataGaName":386,"dataGaLocation":491},{"text":388,"config":659},{"href":390,"dataGaName":391,"dataGaLocation":491},{"text":661,"config":662},"Status",{"href":663,"dataGaName":664,"dataGaLocation":491},"https://status.gitlab.com/","status",{"text":666,"config":667},"Terms of use",{"href":668,"dataGaName":669,"dataGaLocation":491},"/terms/","terms of use",{"text":671,"config":672},"Privacy statement",{"href":673,"dataGaName":674,"dataGaLocation":491},"/privacy/","privacy statement",{"text":676,"config":677},"Cookie preferences",{"dataGaName":678,"dataGaLocation":491,"id":679,"isOneTrustButton":108},"cookie preferences","ot-sdk-btn",{"items":681},[682,684,686],{"text":666,"config":683},{"href":668,"dataGaName":669,"dataGaLocation":491},{"text":671,"config":685},{"href":673,"dataGaName":674,"dataGaLocation":491},{"text":676,"config":687},{"dataGaName":678,"dataGaLocation":491,"id":679,"isOneTrustButton":108},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[693],{"_path":694,"_dir":695,"_draft":6,"_partial":6,"_locale":7,"content":696,"config":700,"_id":702,"_type":31,"title":18,"_source":33,"_file":703,"_stem":704,"_extension":36},"/en-us/blog/authors/john-cai","authors",{"name":18,"config":697},{"headshot":698,"ctfId":699},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667386/Blog/Author%20Headshots/jcaigitlab-headshot.jpg","jcaigitlab",{"template":701},"BlogAuthor","content:en-us:blog:authors:john-cai.yml","en-us/blog/authors/john-cai.yml","en-us/blog/authors/john-cai",{"_path":706,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"header":707,"eyebrow":708,"blurb":709,"button":710,"secondaryButton":714,"_id":716,"_type":31,"title":717,"_source":33,"_file":718,"_stem":719,"_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":711},{"href":712,"dataGaName":50,"dataGaLocation":713},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":52,"config":715},{"href":54,"dataGaName":55,"dataGaLocation":713},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326230023]