[{"data":1,"prerenderedAt":1557},["ShallowReactive",2],{"/blog/gitlabs-contributions-to-git-2-44-0":3,"navigation-en-us":774,"banner-en-us":1202,"footer-en-us":1212,"blog-post-authors-en-us-Patrick Steinhardt":1456,"blog-related-posts-en-us-gitlabs-contributions-to-git-2-44-0":1471,"blog-promotions-en-us":1494,"next-steps-en-us":1548},{"id":4,"title":5,"authors":6,"body":8,"category":753,"date":754,"description":755,"extension":756,"externalUrl":757,"faq":757,"featured":758,"heroImage":759,"meta":760,"navigation":477,"path":761,"seo":762,"slug":766,"stem":767,"tags":768,"template":772,"updatedDate":757,"__hash__":773},"blogPosts/en-us/blog/gitlabs-contributions-to-git-2-44-0.md","GitLab's contributions to Git 2.44.0",[7],"Patrick Steinhardt",{"type":9,"value":10,"toc":745},"minimark",[11,22,31,38,44,55,62,86,92,105,115,130,147,156,168,181,185,188,201,204,207,216,219,222,229,236,243,247,250,257,265,272,517,520,547,556,577,580,583,623,626,629,671,675,682,694,697,705,708,722,728,732,741],[12,13,14,15,21],"p",{},"The Git project recently released ",[16,17,20],"a",{"href":18,"rel":19},"https://git-scm.com/downloads",[],"Git 2.44.0",". In this blog post, we will highlight the contributions made by GitLab's Git team, as well as those from the wider Git community.",[23,24,26,27],"h2",{"id":25},"fast-scripted-rebases-via-git-replay","Fast scripted rebases via ",[28,29,30],"code",{},"git-replay",[12,32,33,34,37],{},"The ",[28,35,36],{},"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.",[12,39,40,41,43],{},"In this case, ",[28,42,36],{}," can be used to reapply all commits of the feature branch onto the new commits of the main branch.",[12,45,46,47,50,51,54],{},"Suppose you have the following commit history with the main development branch ",[28,48,49],{},"main"," and your feature branch ",[28,52,53],{},"feature",":",[12,56,57],{},[58,59],"img",{"alt":60,"src":61},"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",[12,63,64,65,68,69,71,72,74,75,78,79,82,83,54],{},"You have originally created your feature branch from ",[28,66,67],{},"m-2",", but since then the ",[28,70,49],{}," branch has gained two additional commits. Now ",[28,73,36],{}," can be used to reapply your commits ",[28,76,77],{},"f-1"," and ",[28,80,81],{},"f-2"," on top of the newest commit ",[28,84,85],{},"m-4",[12,87,88],{},[58,89],{"alt":90,"src":91},"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",[12,93,94,95,104],{},"You 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 ",[16,96,99,100,103],{"href":97,"rel":98},"https://docs.gitlab.com/topics/git/git_rebase/#rebase-from-the-ui",[],"to create a comment that contains the ",[28,101,102],{},"/rebase"," command",". The magic then happens behind the scenes.",[12,106,107,108,110,111,114],{},"There is one problem though: ",[28,109,36],{}," 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 ",[28,112,113],{},".git"," directory of the repository. This is mostly done to save precious disk space and speed up operations.",[12,116,117,118,123,124,126,127,129],{},"In the past, we used ",[16,119,122],{"href":120,"rel":121},"https://libgit2.org/",[],"libgit2"," 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 ",[28,125,36],{}," to perform rebases. While we could create an ad-hoc worktree to use ",[28,128,36],{},", this would have been prohibitively expensive in large monorepos.",[12,131,132,133,138,139,142,143,146],{},"Luckily, ",[16,134,137],{"href":135,"rel":136},"https://www.linkedin.com/in/elijah-newren-0a41665/",[],"Elijah Newren"," has upstreamed a new merge algorithm called ",[28,140,141],{},"merge-ort"," in Git 2.33. Despite being significantly faster than the old ",[28,144,145],{},"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.",[12,148,149,150,152,153,155],{},"Enter ",[28,151,30],{},", which is a new command that does essentially the same thing as ",[28,154,36],{}," 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.",[12,157,158,159,161,162,164,165,167],{},"You may ask: Why a new command instead of updating ",[28,160,36],{},"? The problem here was that ",[28,163,36],{}," 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 ",[28,166,30],{}," 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.",[12,169,170,171,174,175,180],{},"This topic was a joint effort by ",[16,172,137],{"href":135,"rel":173},[]," and\n",[16,176,179],{"href":177,"rel":178},"https://gitlab.com/chriscool",[],"Christian Couder",".",[23,182,184],{"id":183},"commit-graph-object-existence-checks","Commit-graph object existence checks",[12,186,187],{},"You may know that each commit can have an arbitrary number of parents:",[189,190,191,195,198],"ul",{},[192,193,194],"li",{},"The first commit in your repository has no parents. This is the \"root\" commit.",[192,196,197],{},"Normal commits have a single parent.",[192,199,200],{},"Merge commits have at least two, but sometimes even more than two parents.",[12,202,203],{},"This 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.",[12,205,206],{},"To 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.",[12,208,209,210,215],{},"The 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 ",[16,211,214],{"href":212,"rel":213},"https://en.wikipedia.org/wiki/Bloom_filter",[],"bloom filter"," of changed\nfiles.",[12,217,218],{},"This 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.",[12,220,221],{},"Now, 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.",[12,223,224,225,228],{},"This 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 ",[28,226,227],{},"GIT_COMMIT_GRAPH_PARANOIA"," environment variable that can be used to disable this check.",[12,230,231,232,235],{},"After 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 ",[28,233,234],{},"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.",[12,237,238,239,180],{},"This change was implemented by\n",[16,240,7],{"href":241,"rel":242},"https://gitlab.com/pks-gitlab",[],[23,244,246],{"id":245},"making-git-ready-for-a-new-ref-backend","Making Git ready for a new ref backend",[12,248,249],{},"A 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.",[12,251,252,253,256],{},"Many of the issues are inherent limitations of the way Git stores refs. The so-called ",[28,254,255],{},"files"," ref backend uses a combination of two mechanisms:",[189,258,259,262],{},[192,260,261],{},"\"Loose refs\" are simple files that contain the object ID they point to.",[192,263,264],{},"\"Packed refs\" are a single file that contains a collection of refs.",[12,266,267,268,271],{},"Whenever 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 ",[28,269,270],{},"packed-refs"," file and deletes the corresponding loose refs. A typical repo looks as follows:",[273,274,279],"pre",{"className":275,"code":276,"language":277,"meta":278,"style":278},"language-shell shiki shiki-themes github-light"," $ 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    ├── heads\n    │   └── main\n    └── 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","shell","",[28,280,281,304,325,336,355,366,372,381,389,397,406,415,427,436,447,456,466,472,479,492,502,509],{"__ignoreMap":278},[282,283,286,290,294,297,301],"span",{"class":284,"line":285},"line",1,[282,287,289],{"class":288},"s7eDp"," $",[282,291,293],{"class":292},"sYBdl"," git",[282,295,296],{"class":292}," init",[282,298,300],{"class":299},"sYu0t"," --ref-format=files",[282,302,303],{"class":292}," repo\n",[282,305,307,310,313,316,319,322],{"class":284,"line":306},2,[282,308,309],{"class":288},"Initialized",[282,311,312],{"class":292}," empty",[282,314,315],{"class":292}," Git",[282,317,318],{"class":292}," repository",[282,320,321],{"class":292}," in",[282,323,324],{"class":292}," /tmp/repo/.git/\n",[282,326,328,330,333],{"class":284,"line":327},3,[282,329,289],{"class":288},[282,331,332],{"class":292}," cd",[282,334,335],{"class":292}," repo/\n",[282,337,339,341,343,346,349,352],{"class":284,"line":338},4,[282,340,289],{"class":288},[282,342,293],{"class":292},[282,344,345],{"class":292}," commit",[282,347,348],{"class":299}," --allow-empty",[282,350,351],{"class":299}," --message",[282,353,354],{"class":292}," \"initial commit\"\n",[282,356,358,360,363],{"class":284,"line":357},5,[282,359,289],{"class":288},[282,361,362],{"class":292}," tree",[282,364,365],{"class":292}," .git/\n",[282,367,369],{"class":284,"line":368},6,[282,370,371],{"class":288},".git/\n",[282,373,375,378],{"class":284,"line":374},7,[282,376,377],{"class":288},"├──",[282,379,380],{"class":292}," config\n",[282,382,384,386],{"class":284,"line":383},8,[282,385,377],{"class":288},[282,387,388],{"class":292}," HEAD\n",[282,390,392,394],{"class":284,"line":391},9,[282,393,377],{"class":288},[282,395,396],{"class":292}," index\n",[282,398,400,403],{"class":284,"line":399},10,[282,401,402],{"class":288},"└──",[282,404,405],{"class":292}," refs\n",[282,407,409,412],{"class":284,"line":408},11,[282,410,411],{"class":288},"    ├──",[282,413,414],{"class":292}," heads\n",[282,416,418,421,424],{"class":284,"line":417},12,[282,419,420],{"class":288},"    │",[282,422,423],{"class":292},"   └──",[282,425,426],{"class":292}," main\n",[282,428,430,433],{"class":284,"line":429},13,[282,431,432],{"class":288},"    └──",[282,434,435],{"class":292}," tags\n",[282,437,439,441,444],{"class":284,"line":438},14,[282,440,289],{"class":288},[282,442,443],{"class":292}," cat",[282,445,446],{"class":292}," .git/HEAD\n",[282,448,450,453],{"class":284,"line":449},15,[282,451,452],{"class":288},"ref:",[282,454,455],{"class":292}," refs/heads/main\n",[282,457,459,461,463],{"class":284,"line":458},16,[282,460,289],{"class":288},[282,462,443],{"class":292},[282,464,465],{"class":292}," .git/refs/heads/main\n",[282,467,469],{"class":284,"line":468},17,[282,470,471],{"class":288},"bf1814060ed3a88bd457ac4dca055d000ffe4482\n",[282,473,475],{"class":284,"line":474},18,[282,476,478],{"emptyLinePlaceholder":477},true,"\n",[282,480,482,484,486,489],{"class":284,"line":481},19,[282,483,289],{"class":288},[282,485,293],{"class":292},[282,487,488],{"class":292}," pack-refs",[282,490,491],{"class":299}," --all\n",[282,493,495,497,499],{"class":284,"line":494},20,[282,496,289],{"class":288},[282,498,443],{"class":292},[282,500,501],{"class":292}," .git/packed-refs\n",[282,503,505],{"class":284,"line":504},21,[282,506,508],{"class":507},"sAwPA","# pack-refs with: peeled fully-peeled sorted\n",[282,510,512,515],{"class":284,"line":511},22,[282,513,514],{"class":288},"bf1814060ed3a88bd457ac4dca055d000ffe4482",[282,516,455],{"class":292},[12,518,519],{},"While this model has served the Git project quite well, relying on a filesystem like this has several limitations:",[189,521,522,528,531,534,541,544],{},[192,523,524,525,527],{},"Deleting a single ref requires you to rewrite the ",[28,526,270],{}," file, which can be gigabytes in size.",[192,529,530],{},"It is impossible to do atomic reads because you cannot atomically scan multiple files at once when a concurrent writer may modify some refs.",[192,532,533],{},"It is impossible to do atomic writes because creating or updating several refs requires you to write to several files.",[192,535,536,537,540],{},"Housekeeping via ",[28,538,539],{},"git-pack-refs"," does not scale well because of its all-into-one repacking nature.",[192,542,543],{},"The storage format of both loose and packed refs is inefficient and wastes disk space.",[192,545,546],{},"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.",[12,548,549,550,555],{},"Several years ago, ",[16,551,554],{"href":552,"rel":553},"https://sfconservancy.org/blog/2018/jan/30/shawn-pearce/",[],"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.",[12,557,558,559,564,565,570,571,576],{},"This new \"reftable\" format has already been implemented by\n",[16,560,563],{"href":561,"rel":562},"https://www.eclipse.org/jgit/",[],"JGit"," and is used extensively by the ",[16,566,569],{"href":567,"rel":568},"https://www.gerritcodereview.com/",[],"Gerrit project",". And, in 2021, ",[16,572,575],{"href":573,"rel":574},"https://www.linkedin.com/pub/dir/han-wen/nienhuys",[],"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.",[12,578,579],{},"Before 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.",[12,581,582],{},"The 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:",[189,584,585,588,595,598,601,604],{},[192,586,587],{},"Some commands used to read or write refs directly via the filesystem without going through the ref backend.",[192,589,590,591,594],{},"The ref databases of worktrees created via ",[28,592,593],{},"git-worktree"," were initialized ad-hoc instead of going through the ref backend.",[192,596,597],{},"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.",[192,599,600],{},"Many tests read or write refs via the filesystem directly.",[192,602,603],{},"We invested quite some time already into bug fixing and performance optimizations for the reftable library itself.",[192,605,606,607,610,611,614,615,618,619,622],{},"We introduced a new ",[28,608,609],{},"refStorage"," extension that tells Git in which format the repository stores its refs. This can be changed when creating a new repository by specifying ",[28,612,613],{},"--ref-format"," flag in ",[28,616,617],{},"git-init"," or ",[28,620,621],{},"git-clone",". For now, only the “files” format is supported.",[12,624,625],{},"The 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.",[12,627,628],{},"We will not cover the new reftable format in this post because it is out of scope, but stay tuned for more details soon!",[12,630,631,632,637,638,637,643,637,648,637,653,658,659,662,663,666,667,670],{},"This project was a joint effort by\n",[16,633,636],{"href":634,"rel":635},"https://gitlab.com/jcaigitlab",[],"John Cai",",\n",[16,639,642],{"href":640,"rel":641},"https://gitlab.com/justintobler",[],"Justin Tobler",[16,644,647],{"href":645,"rel":646},"https://gitlab.com/knayakgl",[],"Karthik Nayak",[16,649,652],{"href":650,"rel":651},"https://gitlab.com/stanhu",[],"Stan Hu",[16,654,657],{"href":655,"rel":656},"https://gitlab.com/toon",[],"Toon Claes",",\nand ",[16,660,7],{"href":241,"rel":661},[],", who has led the effort. Credit also goes to\n",[16,664,554],{"href":552,"rel":665},[]," as original inventor of the format and ",[16,668,575],{"href":573,"rel":669},[]," as the\nauthor of the reftable library.",[23,672,674],{"id":673},"support-for-gitlab-ci","Support for GitLab CI",[12,676,677,678,681],{},"As all the preparations for the new ",[28,679,680],{},"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.",[12,683,684,685,693],{},"For 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 ",[16,686,689,692],{"href":687,"rel":688},"https://docs.gitlab.com/ci/yaml/",[],[28,690,691],{},".gitlab-ci.yml"," definition"," that was not part of the upstream repository and instead maintained outside the Git project.",[12,695,696],{},"While this worked reasonably well, there were two significant downsides:",[189,698,699,702],{},[192,700,701],{},"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.",[192,703,704],{},"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.",[12,706,707],{},"Therefore, 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.",[12,709,710,711,618,714,717,718,721],{},"Another benefit of GitLab CI support is that, for the first time, we now also exercise an architecture other than ",[28,712,713],{},"x86_64",[28,715,716],{},"i686",": the macOS runners we provide at GitLab.com use an Apple M1, which is based on the ",[28,719,720],{},"arm64"," architecture.",[12,723,724,725,180],{},"This change was contributed by ",[16,726,7],{"href":241,"rel":727},[],[23,729,731],{"id":730},"more-to-come","More to come",[12,733,734,735,740],{},"This blog post gives just a glimpse into what has happened in the Git project, which lies at the heart of ",[16,736,739],{"href":737,"rel":738},"https://about.gitlab.com/solutions/source-code-management/",[],"source code management"," at GitLab. Stay tuned for more insights into future contributions and the reftable backend in particular!",[742,743,744],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":278,"searchDepth":306,"depth":306,"links":746},[747,749,750,751,752],{"id":25,"depth":306,"text":748},"Fast scripted rebases via git-replay",{"id":183,"depth":306,"text":184},{"id":245,"depth":306,"text":246},{"id":673,"depth":306,"text":674},{"id":730,"depth":306,"text":731},"open-source","2024-02-26","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.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749666069/Blog/Hero%20Images/AdobeStock_639935439.jpg",{},"/en-us/blog/gitlabs-contributions-to-git-2-44-0",{"title":5,"description":755,"ogTitle":5,"ogDescription":755,"noIndex":758,"ogImage":759,"ogUrl":763,"ogSiteName":764,"ogType":765,"canonicalUrls":763},"https://about.gitlab.com/blog/gitlabs-contributions-to-git-2-44-0","https://about.gitlab.com","article","gitlabs-contributions-to-git-2-44-0","en-us/blog/gitlabs-contributions-to-git-2-44-0",[769,770,771],"git","open source","CI","BlogPost","lpp1fSjMyuUW_es_urMA1Ebl75h8JrMN0DXpoM_LDAg",{"logo":775,"freeTrial":780,"sales":785,"login":790,"items":795,"search":1122,"minimal":1153,"duo":1172,"switchNav":1181,"pricingDeployment":1192},{"config":776},{"href":777,"dataGaName":778,"dataGaLocation":779},"/","gitlab logo","header",{"text":781,"config":782},"Get free trial",{"href":783,"dataGaName":784,"dataGaLocation":779},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":786,"config":787},"Request a demo",{"href":788,"dataGaName":789,"dataGaLocation":779},"/sales/?contact-topic=request-demo","sales",{"text":791,"config":792},"Sign in",{"href":793,"dataGaName":794,"dataGaLocation":779},"https://gitlab.com/users/sign_in/","sign in",[796,825,925,930,1044,1100],{"text":797,"config":798,"menu":800},"Platform",{"dataNavLevelOne":799},"platform",{"type":801,"columns":802},"cards",[803,809,817],{"title":797,"description":804,"link":805},"The intelligent orchestration platform for DevSecOps",{"text":806,"config":807},"Explore our Platform",{"href":808,"dataGaName":799,"dataGaLocation":779},"/platform/",{"title":810,"description":811,"link":812},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":813,"config":814},"Meet GitLab Duo",{"href":815,"dataGaName":816,"dataGaLocation":779},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":818,"description":819,"link":820},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":821,"config":822},"Learn more",{"href":823,"dataGaName":824,"dataGaLocation":779},"/why-gitlab/","why gitlab",{"text":826,"left":477,"config":827,"menu":829},"Product",{"dataNavLevelOne":828},"solutions",{"type":830,"link":831,"columns":835,"feature":904},"lists",{"text":832,"config":833},"View all Solutions",{"href":834,"dataGaName":828,"dataGaLocation":779},"/solutions/",[836,860,883],{"title":837,"description":838,"link":839,"items":844},"Automation","CI/CD and automation to accelerate deployment",{"config":840},{"icon":841,"href":842,"dataGaName":843,"dataGaLocation":779},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[845,849,852,856],{"text":846,"config":847},"CI/CD",{"href":848,"dataGaLocation":779,"dataGaName":846},"/solutions/continuous-integration/",{"text":810,"config":850},{"href":815,"dataGaLocation":779,"dataGaName":851},"gitlab duo agent platform - product menu",{"text":853,"config":854},"Source Code Management",{"href":855,"dataGaLocation":779,"dataGaName":853},"/solutions/source-code-management/",{"text":857,"config":858},"Automated Software Delivery",{"href":842,"dataGaLocation":779,"dataGaName":859},"Automated software delivery",{"title":861,"description":862,"link":863,"items":868},"Security","Deliver code faster without compromising security",{"config":864},{"href":865,"dataGaName":866,"dataGaLocation":779,"icon":867},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[869,873,878],{"text":870,"config":871},"Application Security Testing",{"href":865,"dataGaName":872,"dataGaLocation":779},"Application security testing",{"text":874,"config":875},"Software Supply Chain Security",{"href":876,"dataGaLocation":779,"dataGaName":877},"/solutions/supply-chain/","Software supply chain security",{"text":879,"config":880},"Software Compliance",{"href":881,"dataGaName":882,"dataGaLocation":779},"/solutions/software-compliance/","software compliance",{"title":884,"link":885,"items":890},"Measurement",{"config":886},{"icon":887,"href":888,"dataGaName":889,"dataGaLocation":779},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[891,895,899],{"text":892,"config":893},"Visibility & Measurement",{"href":888,"dataGaLocation":779,"dataGaName":894},"Visibility and Measurement",{"text":896,"config":897},"Value Stream Management",{"href":898,"dataGaLocation":779,"dataGaName":896},"/solutions/value-stream-management/",{"text":900,"config":901},"Analytics & Insights",{"href":902,"dataGaLocation":779,"dataGaName":903},"/solutions/analytics-and-insights/","Analytics and insights",{"title":905,"type":830,"items":906},"GitLab for",[907,913,919],{"text":908,"config":909},"Enterprise",{"icon":910,"href":911,"dataGaLocation":779,"dataGaName":912},"Building","/enterprise/","enterprise",{"text":914,"config":915},"Small Business",{"icon":916,"href":917,"dataGaLocation":779,"dataGaName":918},"Work","/small-business/","small business",{"text":920,"config":921},"Public Sector",{"icon":922,"href":923,"dataGaLocation":779,"dataGaName":924},"Organization","/solutions/public-sector/","public sector",{"text":926,"config":927},"Pricing",{"href":928,"dataGaName":929,"dataGaLocation":779,"dataNavLevelOne":929},"/pricing/","pricing",{"text":931,"config":932,"menu":934},"Resources",{"dataNavLevelOne":933},"resources",{"type":830,"link":935,"columns":939,"feature":1033},{"text":936,"config":937},"View all resources",{"href":938,"dataGaName":933,"dataGaLocation":779},"/resources/",[940,973,1000],{"title":941,"items":942},"Getting started",[943,948,953,958,963,968],{"text":944,"config":945},"Install",{"href":946,"dataGaName":947,"dataGaLocation":779},"/install/","install",{"text":949,"config":950},"Quick start guides",{"href":951,"dataGaName":952,"dataGaLocation":779},"/get-started/","quick setup checklists",{"text":954,"config":955},"Learn",{"href":956,"dataGaLocation":779,"dataGaName":957},"https://university.gitlab.com/","learn",{"text":959,"config":960},"Product documentation",{"href":961,"dataGaName":962,"dataGaLocation":779},"https://docs.gitlab.com/","product documentation",{"text":964,"config":965},"Best practice videos",{"href":966,"dataGaName":967,"dataGaLocation":779},"/getting-started-videos/","best practice videos",{"text":969,"config":970},"Integrations",{"href":971,"dataGaName":972,"dataGaLocation":779},"/integrations/","integrations",{"title":974,"items":975},"Discover",[976,981,986,991,995],{"text":977,"config":978},"Customer success stories",{"href":979,"dataGaName":980,"dataGaLocation":779},"/customers/","customer success stories",{"text":982,"config":983},"Blog",{"href":984,"dataGaName":985,"dataGaLocation":779},"/blog/","blog",{"text":987,"config":988},"Demo Hub",{"href":989,"dataGaName":990,"dataGaLocation":779},"/demo-hub/","demo hub",{"text":992,"config":993},"The Source",{"href":994,"dataGaName":985,"dataGaLocation":779},"/the-source/",{"text":996,"config":997},"Remote",{"href":998,"dataGaName":999,"dataGaLocation":779},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1001,"items":1002},"Connect",[1003,1008,1013,1018,1023,1028],{"text":1004,"config":1005},"GitLab Services",{"href":1006,"dataGaName":1007,"dataGaLocation":779},"/services/","services",{"text":1009,"config":1010},"Contribute",{"href":1011,"dataGaName":1012,"dataGaLocation":779},"https://contributors.gitlab.com","contribute",{"text":1014,"config":1015},"Community",{"href":1016,"dataGaName":1017,"dataGaLocation":779},"/community/","community",{"text":1019,"config":1020},"Forum",{"href":1021,"dataGaName":1022,"dataGaLocation":779},"https://forum.gitlab.com/","forum",{"text":1024,"config":1025},"Events",{"href":1026,"dataGaName":1027,"dataGaLocation":779},"/events/","events",{"text":1029,"config":1030},"Partners",{"href":1031,"dataGaName":1032,"dataGaLocation":779},"/partners/","partners",{"config":1034,"title":1037,"text":1038,"link":1039},{"background":1035,"textColor":1036},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":1040,"config":1041},"Read the latest",{"href":1042,"dataGaName":1043,"dataGaLocation":779},"/whats-new/","whats new",{"text":1045,"config":1046,"menu":1048},"Company",{"dataNavLevelOne":1047},"company",{"type":830,"columns":1049},[1050],{"items":1051},[1052,1057,1063,1065,1070,1075,1080,1085,1090,1095],{"text":1053,"config":1054},"About",{"href":1055,"dataGaName":1056,"dataGaLocation":779},"/company/","about",{"text":1058,"config":1059,"footerGa":1062},"Jobs",{"href":1060,"dataGaName":1061,"dataGaLocation":779},"/jobs/","jobs",{"dataGaName":1061},{"text":1024,"config":1064},{"href":1026,"dataGaName":1027,"dataGaLocation":779},{"text":1066,"config":1067},"Leadership",{"href":1068,"dataGaName":1069,"dataGaLocation":779},"/company/team/e-group/","leadership",{"text":1071,"config":1072},"Handbook",{"href":1073,"dataGaName":1074,"dataGaLocation":779},"https://handbook.gitlab.com/","handbook",{"text":1076,"config":1077},"Investor relations",{"href":1078,"dataGaName":1079,"dataGaLocation":779},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1081,"config":1082},"Trust Center",{"href":1083,"dataGaName":1084,"dataGaLocation":779},"/security/","trust center",{"text":1086,"config":1087},"AI Transparency Center",{"href":1088,"dataGaName":1089,"dataGaLocation":779},"/ai-transparency-center/","ai transparency center",{"text":1091,"config":1092},"Newsletter",{"href":1093,"dataGaName":1094,"dataGaLocation":779},"/company/contact/#contact-forms","newsletter",{"text":1096,"config":1097},"Press",{"href":1098,"dataGaName":1099,"dataGaLocation":779},"/press/","press",{"text":1101,"config":1102,"menu":1103},"Contact us",{"dataNavLevelOne":1047},{"type":830,"columns":1104},[1105],{"items":1106},[1107,1112,1117],{"text":1108,"config":1109},"Talk to sales",{"href":1110,"dataGaName":1111,"dataGaLocation":779},"/sales/","talk to sales",{"text":1113,"config":1114},"Support portal",{"href":1115,"dataGaName":1116,"dataGaLocation":779},"https://support.gitlab.com/hc/en-us","support portal",{"text":1118,"config":1119},"Customer portal",{"href":1120,"dataGaName":1121,"dataGaLocation":779},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1123,"login":1124,"suggestions":1131},"Close",{"text":1125,"link":1126},"To search repositories and projects, login to",{"text":1127,"config":1128},"gitlab.com",{"href":793,"dataGaName":1129,"dataGaLocation":1130},"search login","search",{"text":1132,"default":1133},"Suggestions",[1134,1136,1140,1142,1146,1150],{"text":810,"config":1135},{"href":815,"dataGaName":810,"dataGaLocation":1130},{"text":1137,"config":1138},"Code Suggestions (AI)",{"href":1139,"dataGaName":1137,"dataGaLocation":1130},"/solutions/code-suggestions/",{"text":846,"config":1141},{"href":848,"dataGaName":846,"dataGaLocation":1130},{"text":1143,"config":1144},"GitLab on AWS",{"href":1145,"dataGaName":1143,"dataGaLocation":1130},"/partners/technology-partners/aws/",{"text":1147,"config":1148},"GitLab on Google Cloud",{"href":1149,"dataGaName":1147,"dataGaLocation":1130},"/partners/technology-partners/google-cloud-platform/",{"text":1151,"config":1152},"Why GitLab?",{"href":823,"dataGaName":1151,"dataGaLocation":1130},{"freeTrial":1154,"mobileIcon":1159,"desktopIcon":1164,"secondaryButton":1167},{"text":1155,"config":1156},"Start free trial",{"href":1157,"dataGaName":784,"dataGaLocation":1158},"https://gitlab.com/-/trials/new/","nav",{"altText":1160,"config":1161},"Gitlab Icon",{"src":1162,"dataGaName":1163,"dataGaLocation":1158},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1160,"config":1165},{"src":1166,"dataGaName":1163,"dataGaLocation":1158},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1168,"config":1169},"Get Started",{"href":1170,"dataGaName":1171,"dataGaLocation":1158},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1173,"mobileIcon":1177,"desktopIcon":1179},{"text":1174,"config":1175},"Learn more about GitLab Duo",{"href":815,"dataGaName":1176,"dataGaLocation":1158},"gitlab duo",{"altText":1160,"config":1178},{"src":1162,"dataGaName":1163,"dataGaLocation":1158},{"altText":1160,"config":1180},{"src":1166,"dataGaName":1163,"dataGaLocation":1158},{"button":1182,"mobileIcon":1187,"desktopIcon":1189},{"text":1183,"config":1184},"/switch",{"href":1185,"dataGaName":1186,"dataGaLocation":1158},"#contact","switch",{"altText":1160,"config":1188},{"src":1162,"dataGaName":1163,"dataGaLocation":1158},{"altText":1160,"config":1190},{"src":1191,"dataGaName":1163,"dataGaLocation":1158},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1193,"mobileIcon":1198,"desktopIcon":1200},{"text":1194,"config":1195},"Back to pricing",{"href":928,"dataGaName":1196,"dataGaLocation":1158,"icon":1197},"back to pricing","GoBack",{"altText":1160,"config":1199},{"src":1162,"dataGaName":1163,"dataGaLocation":1158},{"altText":1160,"config":1201},{"src":1166,"dataGaName":1163,"dataGaLocation":1158},{"title":1203,"titleMobile":1204,"button":1205,"config":1210},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":821,"config":1206},{"href":1207,"dataGaName":1208,"dataGaLocation":1209},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1211,"disabled":758},"release",{"data":1213},{"text":1214,"source":1215,"edit":1221,"contribute":1226,"config":1231,"items":1236,"minimal":1445},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1216,"config":1217},"View page source",{"href":1218,"dataGaName":1219,"dataGaLocation":1220},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1222,"config":1223},"Edit this page",{"href":1224,"dataGaName":1225,"dataGaLocation":1220},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1227,"config":1228},"Please contribute",{"href":1229,"dataGaName":1230,"dataGaLocation":1220},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1232,"facebook":1233,"youtube":1234,"linkedin":1235},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1237,1284,1338,1381,1413],{"title":926,"links":1238,"subMenu":1253},[1239,1243,1248],{"text":1240,"config":1241},"View plans",{"href":928,"dataGaName":1242,"dataGaLocation":1220},"view plans",{"text":1244,"config":1245},"Why Premium?",{"href":1246,"dataGaName":1247,"dataGaLocation":1220},"/pricing/premium/","why premium",{"text":1249,"config":1250},"Why Ultimate?",{"href":1251,"dataGaName":1252,"dataGaLocation":1220},"/pricing/ultimate/","why ultimate",[1254],{"title":1255,"links":1256},"Contact Us",[1257,1260,1262,1264,1269,1274,1279],{"text":1258,"config":1259},"Contact sales",{"href":1110,"dataGaName":789,"dataGaLocation":1220},{"text":1113,"config":1261},{"href":1115,"dataGaName":1116,"dataGaLocation":1220},{"text":1118,"config":1263},{"href":1120,"dataGaName":1121,"dataGaLocation":1220},{"text":1265,"config":1266},"Status",{"href":1267,"dataGaName":1268,"dataGaLocation":1220},"https://status.gitlab.com/","status",{"text":1270,"config":1271},"Terms of use",{"href":1272,"dataGaName":1273,"dataGaLocation":1220},"/terms/","terms of use",{"text":1275,"config":1276},"Privacy statement",{"href":1277,"dataGaName":1278,"dataGaLocation":1220},"/privacy/","privacy statement",{"text":1280,"config":1281},"Cookie preferences",{"dataGaName":1282,"dataGaLocation":1220,"id":1283,"isOneTrustButton":477},"cookie preferences","ot-sdk-btn",{"title":826,"links":1285,"subMenu":1294},[1286,1290],{"text":1287,"config":1288},"DevSecOps platform",{"href":808,"dataGaName":1289,"dataGaLocation":1220},"devsecops platform",{"text":1291,"config":1292},"AI-Assisted Development",{"href":815,"dataGaName":1293,"dataGaLocation":1220},"ai-assisted development",[1295],{"title":1296,"links":1297},"Topics",[1298,1303,1308,1313,1318,1323,1328,1333],{"text":1299,"config":1300},"CICD",{"href":1301,"dataGaName":1302,"dataGaLocation":1220},"/topics/ci-cd/","cicd",{"text":1304,"config":1305},"GitOps",{"href":1306,"dataGaName":1307,"dataGaLocation":1220},"/topics/gitops/","gitops",{"text":1309,"config":1310},"DevOps",{"href":1311,"dataGaName":1312,"dataGaLocation":1220},"/topics/devops/","devops",{"text":1314,"config":1315},"Version Control",{"href":1316,"dataGaName":1317,"dataGaLocation":1220},"/topics/version-control/","version control",{"text":1319,"config":1320},"DevSecOps",{"href":1321,"dataGaName":1322,"dataGaLocation":1220},"/topics/devsecops/","devsecops",{"text":1324,"config":1325},"Cloud Native",{"href":1326,"dataGaName":1327,"dataGaLocation":1220},"/topics/cloud-native/","cloud native",{"text":1329,"config":1330},"AI for Coding",{"href":1331,"dataGaName":1332,"dataGaLocation":1220},"/topics/devops/ai-for-coding/","ai for coding",{"text":1334,"config":1335},"Agentic AI",{"href":1336,"dataGaName":1337,"dataGaLocation":1220},"/topics/agentic-ai/","agentic ai",{"title":1339,"links":1340},"Solutions",[1341,1343,1345,1350,1353,1356,1360,1363,1365,1368,1371,1376],{"text":870,"config":1342},{"href":865,"dataGaName":870,"dataGaLocation":1220},{"text":859,"config":1344},{"href":842,"dataGaName":843,"dataGaLocation":1220},{"text":1346,"config":1347},"Agile development",{"href":1348,"dataGaName":1349,"dataGaLocation":1220},"/solutions/agile-delivery/","agile delivery",{"text":1351,"config":1352},"SCM",{"href":855,"dataGaName":739,"dataGaLocation":1220},{"text":1299,"config":1354},{"href":848,"dataGaName":1355,"dataGaLocation":1220},"continuous integration & delivery",{"text":1357,"config":1358},"Value stream management",{"href":898,"dataGaName":1359,"dataGaLocation":1220},"value stream management",{"text":1304,"config":1361},{"href":1362,"dataGaName":1307,"dataGaLocation":1220},"/solutions/gitops/",{"text":908,"config":1364},{"href":911,"dataGaName":912,"dataGaLocation":1220},{"text":1366,"config":1367},"Small business",{"href":917,"dataGaName":918,"dataGaLocation":1220},{"text":1369,"config":1370},"Public sector",{"href":923,"dataGaName":924,"dataGaLocation":1220},{"text":1372,"config":1373},"Education",{"href":1374,"dataGaName":1375,"dataGaLocation":1220},"/solutions/education/","education",{"text":1377,"config":1378},"Financial services",{"href":1379,"dataGaName":1380,"dataGaLocation":1220},"/solutions/finance/","financial services",{"title":931,"links":1382},[1383,1385,1387,1389,1392,1394,1397,1399,1401,1403,1405,1407,1409,1411],{"text":944,"config":1384},{"href":946,"dataGaName":947,"dataGaLocation":1220},{"text":949,"config":1386},{"href":951,"dataGaName":952,"dataGaLocation":1220},{"text":954,"config":1388},{"href":956,"dataGaName":957,"dataGaLocation":1220},{"text":959,"config":1390},{"href":961,"dataGaName":1391,"dataGaLocation":1220},"docs",{"text":982,"config":1393},{"href":984,"dataGaName":985,"dataGaLocation":1220},{"text":1395,"config":1396},"What's new",{"href":1042,"dataGaName":1043,"dataGaLocation":1220},{"text":977,"config":1398},{"href":979,"dataGaName":980,"dataGaLocation":1220},{"text":996,"config":1400},{"href":998,"dataGaName":999,"dataGaLocation":1220},{"text":1004,"config":1402},{"href":1006,"dataGaName":1007,"dataGaLocation":1220},{"text":1009,"config":1404},{"href":1011,"dataGaName":1012,"dataGaLocation":1220},{"text":1014,"config":1406},{"href":1016,"dataGaName":1017,"dataGaLocation":1220},{"text":1019,"config":1408},{"href":1021,"dataGaName":1022,"dataGaLocation":1220},{"text":1024,"config":1410},{"href":1026,"dataGaName":1027,"dataGaLocation":1220},{"text":1029,"config":1412},{"href":1031,"dataGaName":1032,"dataGaLocation":1220},{"title":1045,"links":1414},[1415,1417,1419,1421,1423,1425,1429,1434,1436,1438,1440],{"text":1053,"config":1416},{"href":1055,"dataGaName":1047,"dataGaLocation":1220},{"text":1058,"config":1418},{"href":1060,"dataGaName":1061,"dataGaLocation":1220},{"text":1066,"config":1420},{"href":1068,"dataGaName":1069,"dataGaLocation":1220},{"text":1071,"config":1422},{"href":1073,"dataGaName":1074,"dataGaLocation":1220},{"text":1076,"config":1424},{"href":1078,"dataGaName":1079,"dataGaLocation":1220},{"text":1426,"config":1427},"Sustainability",{"href":1428,"dataGaName":1426,"dataGaLocation":1220},"/sustainability/",{"text":1430,"config":1431},"Diversity, inclusion and belonging (DIB)",{"href":1432,"dataGaName":1433,"dataGaLocation":1220},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1081,"config":1435},{"href":1083,"dataGaName":1084,"dataGaLocation":1220},{"text":1091,"config":1437},{"href":1093,"dataGaName":1094,"dataGaLocation":1220},{"text":1096,"config":1439},{"href":1098,"dataGaName":1099,"dataGaLocation":1220},{"text":1441,"config":1442},"Modern Slavery Transparency Statement",{"href":1443,"dataGaName":1444,"dataGaLocation":1220},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1446},[1447,1450,1453],{"text":1448,"config":1449},"Terms",{"href":1272,"dataGaName":1273,"dataGaLocation":1220},{"text":1451,"config":1452},"Cookies",{"dataGaName":1282,"dataGaLocation":1220,"id":1283,"isOneTrustButton":477},{"text":1454,"config":1455},"Privacy",{"href":1277,"dataGaName":1278,"dataGaLocation":1220},[1457],{"id":1458,"title":7,"body":757,"config":1459,"content":1461,"description":757,"extension":1465,"meta":1466,"navigation":477,"path":1467,"seo":1468,"stem":1469,"__hash__":1470},"blogAuthors/en-us/blog/authors/patrick-steinhardt.yml",{"template":1460},"BlogAuthor",{"name":7,"config":1462},{"headshot":1463,"ctfId":1464},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749661952/Blog/Author%20Headshots/pks-gitlab-headshot.png","pksgitlab","yml",{},"/en-us/blog/authors/patrick-steinhardt",{},"en-us/blog/authors/patrick-steinhardt","SV9Yd_vW69UbvntDP-SEOV9NKT_VwUAj5nfftf2ElSw",[1472,1479,1487],{"title":1473,"description":1474,"heroImage":1475,"category":753,"date":1476,"authors":1477,"slug":1478,"externalUrl":757},"What's new in Git 2.55.0?","Learn about the new features and changes in Git 2.55, including a new git-history(1) fixup command, an fsmonitor daemon for Linux, pushing to remote groups, and more.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782732487/ztwgrvlolpifo01vj8qc.png","2026-06-29",[657],"whats-new-in-git-2-55-0",{"title":1480,"description":1481,"heroImage":1482,"category":753,"date":1483,"authors":1484,"slug":1486,"externalUrl":757},"GitLab AI Hackathon 2026: Meet the winners","Nearly 7,000 developers built 600+ AI agents and flows on GitLab Duo Agent Platform. Find out who won and what they created.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1776457632/llddiylsgwuze0u1rjks.png","2026-04-22",[1485],"Nick Veenhof","gitlab-ai-hackathon-2026-meet-the-winners",{"title":1488,"description":1489,"heroImage":1490,"category":753,"date":1491,"authors":1492,"slug":1493,"externalUrl":757},"What’s new in Git 2.54.0?","Learn about release contributions, including new repository maintenance, a new command to edit commit history, a replacement for git-sizer(1), and more.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782734716/ztwgrvlolpifo01vj8qc.png","2026-04-20",[7],"whats-new-in-git-2-54-0",{"promotions":1495},[1496,1510,1522,1534],{"id":1497,"categories":1498,"header":1500,"text":1501,"button":1502,"image":1507},"ai-modernization",[1499],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1503,"config":1504},"Get your AI maturity score",{"href":1505,"dataGaName":1506,"dataGaLocation":985},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1508},{"src":1509},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1511,"categories":1512,"header":1514,"text":1501,"button":1515,"image":1519},"devops-modernization",[1513,1322],"product","Are you just managing tools or shipping innovation?",{"text":1516,"config":1517},"Get your DevOps maturity score",{"href":1518,"dataGaName":1506,"dataGaLocation":985},"/assessments/devops-modernization-assessment/",{"config":1520},{"src":1521},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1523,"categories":1524,"header":1526,"text":1501,"button":1527,"image":1531},"security-modernization",[1525],"security","Are you trading speed for security?",{"text":1528,"config":1529},"Get your security maturity score",{"href":1530,"dataGaName":1506,"dataGaLocation":985},"/assessments/security-modernization-assessment/",{"config":1532},{"src":1533},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1535,"paths":1536,"header":1539,"text":1540,"button":1541,"image":1546},"github-azure-migration",[1537,1538],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1542,"config":1543},"See how GitLab compares to GitHub",{"href":1544,"dataGaName":1545,"dataGaLocation":985},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1547},{"src":1521},{"header":1549,"blurb":1550,"button":1551,"secondaryButton":1555},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1552,"config":1553},"Get your free trial",{"href":1554,"dataGaName":784,"dataGaLocation":53},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/",{"text":1258,"config":1556},{"href":1110,"dataGaName":789,"dataGaLocation":53},1786803761425]