[{"data":1,"prerenderedAt":1421},["ShallowReactive",2],{"/blog/contributions-to-git-2-42-release":3,"navigation-en-us":642,"banner-en-us":1069,"footer-en-us":1079,"blog-post-authors-en-us-Christian Couder":1324,"blog-related-posts-en-us-contributions-to-git-2-42-release":1339,"blog-promotions-en-us":1358,"next-steps-en-us":1411},{"id":4,"title":5,"authors":6,"body":8,"category":619,"date":620,"description":621,"extension":622,"externalUrl":623,"faq":623,"featured":624,"heroImage":625,"meta":626,"navigation":627,"path":628,"seo":629,"slug":633,"stem":634,"tags":635,"template":640,"updatedDate":623,"__hash__":641},"blogPosts/en-us/blog/contributions-to-git-2-42-release.md","Git 2.42 release: Here are four of our contributions in detail",[7],"Christian Couder",{"type":9,"value":10,"toc":595},"minimark",[11,21,30,35,40,55,58,62,65,68,71,75,90,99,105,119,125,131,147,155,165,197,206,213,217,220,242,251,266,270,276,332,347,354,358,364,367,399,402,406,421,433,444,451,455,458,469,472,483,500,506,511,514,532,535,539,542,555,561,565,568,583,591],[12,13,14,20],"p",{},[15,16,19],"a",{"href":17,"rel":18},"https://gitlab.com/gitlab-org/git/-/raw/master/Documentation/RelNotes/2.42.0.txt",[],"Git 2.42","\nwas officially released on August 21, 2023, and included some\nimprovements from GitLab's Git team. Git is the foundation of\nrepository data at GitLab. GitLab's Git team works on new features, performance improvements, documentation improvements,\nand growing the Git community. Often our contributions to Git have a\nlot to do with the way we integrate Git into our services at\nGitLab.",[12,22,23,24,29],{},"We previously shared ",[15,25,28],{"href":26,"rel":27},"https://about.gitlab.com/blog/contributions-to-latest-git-release/",[],"some of our improvements that were included in the Git 2.41 release",". Here are some highlights from the Git 2.42 release, and a\nwindow into how we use Git on the server side at GitLab.",[31,32,34],"h2",{"id":33},"_1-prevent-certain-refs-from-being-packed","1. Prevent certain refs from being packed",[36,37,39],"h3",{"id":38},"write-ahead-logging","Write-ahead logging",[12,41,42,43,48,49,54],{},"In ",[15,44,47],{"href":45,"rel":46},"https://docs.gitlab.com/administration/gitaly/",[],"Gitaly",", we\nwant to use a ",[15,50,53],{"href":51,"rel":52},"https://gitlab.com/groups/gitlab-org/-/epics/8911",[],"write-ahead log","\nto replicate Git operations on different machines.",[12,56,57],{},"This means that the Git objects and references that should be changed\nby a Git operation are first kept in a log entry. Then, when all the\nmachines have agreed that the operation should proceed, the log entry\nis applied so the corresponding Git objects and references are\nactually added to the repositories on all the machines.",[36,59,61],{"id":60},"need-for-temporary-references","Need for temporary references",[12,63,64],{},"Between the time when a specific log entry is first written and when\nit is applied, other log entries could be applied which could remove\nsome objects and references. It could happen that these objects and\nreferences are needed to apply the specific log entry though.",[12,66,67],{},"So when we log an entry, we have to make sure that all the objects and\nreferences that it needs to be properly applied will not be removed\nuntil that entry is either actually applied or discarded.",[12,69,70],{},"The best way to make sure things are kept in Git is to create new Git\nreferences pointing to these things. So we decided to use temporary\nreferences for that purpose. They would be created when a log entry is\nwritten, and then deleted when that entry is either applied or\ndiscarded.",[36,72,74],{"id":73},"packed-refs-performance","Packed-refs performance",[12,76,77,78,82,83,86,87,89],{},"Git can store references in \"loose\" files, with one reference per\nfile, or in the ",[79,80,81],"code",{},"packed-refs"," file, which contains many of them. The\n",[79,84,85],{},"git pack-refs"," command is used to pack some references from \"loose\"\nfiles into the ",[79,88,81],{}," file.",[12,91,92,93,95,96,98],{},"For reading a lot of references, the ",[79,94,81],{}," file is very\nefficient, but for writing or deleting a single reference, it is not\nso efficient as rewriting the whole ",[79,97,81],{}," file is required.",[12,100,101,102,104],{},"As temporary references are to be created and then deleted soon after,\nstoring them in the ",[79,103,81],{}," file would not be efficient. It\nwould be better to store them in \"loose\" files.",[12,106,107,108,110,111,114,115,118],{},"The ",[79,109,85],{}," command had no way to be told precisely which refs\nshould be packed or not though. By default it would repack all the\ntags (which are refs in ",[79,112,113],{},"refs/tags/",") and all the refs that are\nalready packed. With the ",[79,116,117],{},"--all"," option one could tell it to repack\nall the refs except the hidden refs, broken refs, and symbolic refs,\nbut that was the only thing that could be controlled.",[36,120,122,123],{"id":121},"improving-git-pack-refs","Improving ",[79,124,85],{},[12,126,127,128,130],{},"We decided to improve ",[79,129,85],{}," by adding two new options to it:",[132,133,134,141],"ul",{},[135,136,137,140],"li",{},[79,138,139],{},"--include \u003Cpattern>"," which can be used to specify which refs should be packed",[135,142,143,146],{},[79,144,145],{},"--exclude \u003Cpattern>"," which can be used to specify which refs should not be packed",[12,148,149,154],{},[15,150,153],{"href":151,"rel":152},"https://gitlab.com/jcaigitlab",[],"John Cai",", Gitaly:Git team engineering manager, implemented these options.",[12,156,157,158,161,162,164],{},"For example, if the refs managed by the write-ahead log are in\n",[79,159,160],{},"refs/wal/",", it's now possible the exclude them from being moved into\nthe ",[79,163,81],{}," file by using:",[166,167,172],"pre",{"className":168,"code":169,"language":170,"meta":171,"style":171},"language-shell shiki shiki-themes github-light","$ git pack-refs --exclude \"refs/wal/*\"\n","shell","",[79,173,174],{"__ignoreMap":171},[175,176,179,183,187,190,194],"span",{"class":177,"line":178},"line",1,[175,180,182],{"class":181},"s7eDp","$",[175,184,186],{"class":185},"sYBdl"," git",[175,188,189],{"class":185}," pack-refs",[175,191,193],{"class":192},"sYu0t"," --exclude",[175,195,196],{"class":185}," \"refs/wal/*\"\n",[12,198,199,200,205],{},"Details of the patch series, including discussions, can be found\n",[15,201,204],{"href":202,"rel":203},"https://lore.kernel.org/git/pull.1501.git.git.1683215331910.gitgitgadget@gmail.com/",[],"here",".",[31,207,209,210],{"id":208},"_2-get-machine-readable-output-from-git-cat-file-batch","2. Get machine-readable output from ",[79,211,212],{},"git cat-file --batch",[36,214,216],{"id":215},"efficiently-retrieving-git-object-information","Efficiently retrieving Git object information",[12,218,219],{},"In GitLab, we often retrieve Git object information. For example, when a\nuser navigates into the files and directories in a repository, we need\nto get the content of the corresponding Git blobs and trees so that\nwe can show it.",[12,221,222,223,226,227,229,230,233,234,237,238,241],{},"In Gitaly, we use ",[79,224,225],{},"git cat-file"," to retrieve Git object information\nfrom a Git repository. As it's a frequent operation, it needs to be\nperformed efficiently, so we use the batch modes of ",[79,228,225],{},"\navailable through the ",[79,231,232],{},"--batch",", ",[79,235,236],{},"--batch-check"," and ",[79,239,240],{},"--batch-command","\noptions.",[12,243,244,245,247,248,250],{},"In these modes, a pointer to a Git object can be repeatedly sent to\nthe standard input, called 'stdin', of a ",[79,246,225],{}," command, while\nthe corresponding object information is read from the standard ouput,\ncalled 'stdout' of the command. This way we don't need to launch a\nnew ",[79,249,225],{}," command for each object.",[12,252,253,254,257,258,261,262,265],{},"GitLab can keep, for example, a ",[79,255,256],{},"git cat-file --batch-command"," process\nrunning in the background while feeding it commands like\n",[79,259,260],{},"info \u003Cobject>"," or ",[79,263,264],{},"contents \u003Cobject>"," through its stdin to\nget either information about an object or its content.",[36,267,269],{"id":268},"newlines-in-stdin-stdout-and-filenames","Newlines in stdin, stdout, and filenames",[12,271,272,273,275],{},"The commands or pointers to Git objects that are sent through stdin\nshould be delimited using newline characters, and in the same way ",[79,274,225],{}," will use newline characters to delimit the information from\ndifferent Git objects in its output. This is a common shell practice\nto make it easy to chain commands together. For example, one can\neasily get the size (in bytes) of the last three commits on the current\nbranch using the following:",[166,277,279],{"className":168,"code":278,"language":170,"meta":171,"style":171},"$ git log -3 --format='%H' | git cat-file --batch-check='%(objectsize)'\n285\n646\n428\n",[79,280,281,314,320,326],{"__ignoreMap":171},[175,282,283,285,287,290,293,296,299,303,305,308,311],{"class":177,"line":178},[175,284,182],{"class":181},[175,286,186],{"class":185},[175,288,289],{"class":185}," log",[175,291,292],{"class":192}," -3",[175,294,295],{"class":192}," --format=",[175,297,298],{"class":185},"'%H'",[175,300,302],{"class":301},"sD7c4"," |",[175,304,186],{"class":181},[175,306,307],{"class":185}," cat-file",[175,309,310],{"class":192}," --batch-check=",[175,312,313],{"class":185},"'%(objectsize)'\n",[175,315,317],{"class":177,"line":316},2,[175,318,319],{"class":181},"285\n",[175,321,323],{"class":177,"line":322},3,[175,324,325],{"class":181},"646\n",[175,327,329],{"class":177,"line":328},4,[175,330,331],{"class":181},"428\n",[12,333,334,335,338,339,342,343,346],{},"Sometimes, though, the pointer to a Git object can contain a filename\nor a directory name, as such a pointer is allowed to be in the form\n",[79,336,337],{},"\u003Cbranch>:\u003Cpath>",". For example ",[79,340,341],{},"HEAD:Documentation"," is a valid\npointer to the blob or the tree corresponding to the ",[79,344,345],{},"Documentation","\npath on the current branch.",[12,348,349,350,353],{},"This used to be an issue because on some systems newline characters\nare allowed in file or directory names. So the ",[79,351,352],{},"-z"," option was\nintroduced last year in Git 2.38 to allow users to change the input\ndelimiter in batch modes to the NUL character.",[36,355,357],{"id":356},"error-output","Error output",[12,359,360,361,363],{},"When the ",[79,362,352],{}," option was introduced, it wasn't considered useful to\nchange the output delimiter to be also the NUL character. This is\nbecause only tree objects can contain paths and the internal format\nof tree objects already uses NUL characters to delimit paths.",[12,365,366],{},"Unfortunately, it was overlooked that in case of an error the pointer\nto the object is displayed in the error message:",[166,368,370],{"className":168,"code":369,"language":170,"meta":171,"style":171},"$ echo 'HEAD:does-not-exist' | git cat-file --batch\nHEAD:does-not-exist missing\n",[79,371,372,391],{"__ignoreMap":171},[175,373,374,376,379,382,384,386,388],{"class":177,"line":178},[175,375,182],{"class":181},[175,377,378],{"class":185}," echo",[175,380,381],{"class":185}," 'HEAD:does-not-exist'",[175,383,302],{"class":301},[175,385,186],{"class":181},[175,387,307],{"class":185},[175,389,390],{"class":192}," --batch\n",[175,392,393,396],{"class":177,"line":316},[175,394,395],{"class":181},"HEAD:does-not-exist",[175,397,398],{"class":185}," missing\n",[12,400,401],{},"As the error messages are printed along with the regular ouput of the\ncommand on stdout, passing in an invalid pointer with a number of\nnewline characters in it could make it very difficult to parse the\noutput.",[36,403,405],{"id":404},"z-comes-to-the-rescue","-Z comes to the rescue",[12,407,408,413,414,417,418,420],{},[15,409,412],{"href":410,"rel":411},"https://gitlab.com/toon",[],"Toon Claes",", Gitaly senior engineer, initially worked on a\npatch to just quote the pointer in the error message, but it was\ndecided in the Git mailing list discussions related to the patch that\nit would be better to just create a new ",[79,415,416],{},"-Z"," option. This option would\nchange both the input and the output delimiter to the NUL character,\nwhile the old ",[79,419,352],{}," option would be deprecated over time.",[12,422,423,424,429,430,432],{},"So ",[15,425,428],{"href":426,"rel":427},"https://gitlab.com/pks-gitlab",[],"Patrick Steinhardt",", Gitaly staff engineer, implemented that new ",[79,431,416],{}," option.",[12,434,199,435,439,440,205],{},[15,436,204],{"href":437,"rel":438},"https://lore.kernel.org/git/20221209150048.2400648-1-toon@iotcl.com/",[],"\nand ",[15,441,204],{"href":442,"rel":443},"https://lore.kernel.org/git/cover.1685710884.git.ps@pks.im/",[],[31,445,447,448],{"id":446},"_3-pass-pseudo-options-to-git-rev-list-stdin","3. Pass pseudo-options to ",[79,449,450],{},"git rev-list --stdin",[36,452,454],{"id":453},"computing-sizes","Computing sizes",[12,456,457],{},"In GitLab, we need to have different ways to compute the size of Git\nrelated content. For example, we need to know:",[132,459,460,463,466],{},[135,461,462],{},"how much disk space a repository is using",[135,464,465],{},"how big a specific Git object is",[135,467,468],{},"how much additional space on a repository is required by a\nspecific set of revisions (and the objects they reference)",[12,470,471],{},"Knowing \"how much disk space a repository is using\" is useful to\nenforce repository-related quotas and is easy to get using regular\nshell and OS features.",[12,473,474,475,478,479,482],{},"Size information about a specific Git object is useful to enforce\nquotas related to maximum file size. It can be obtained using, for\nexample, ",[79,476,477],{},"git cat-file -s \u003Cobject>"," or\n",[79,480,481],{},"echo \u003Cobject> | git cat-file --batch-check='%(objectsize)'","\nas already seen above.",[12,484,485,486,491,492,495,496,499],{},"Computing the space required by a set of revisions is useful, too, as\nforks can share Git content in what we call\n\"",[15,487,490],{"href":488,"rel":489},"https://docs.gitlab.com/development/git_object_deduplication/",[],"pool repositories",",\"\nand we want to discriminate how much content belongs to each forked\nrepository. Fortunately, ",[79,493,494],{},"git rev-list"," has a ",[79,497,498],{},"--disk-usage"," option\nfor this purpose.",[36,501,503,504],{"id":502},"passing-arguments-to-git-rev-list","Passing arguments to ",[79,505,494],{},[12,507,508,510],{},[79,509,494],{}," can take a number of different arguments and has a lot\nof different options. It's a fundamental command to traverse commit\ngraphs, and it should be flexible enough to fulfill a lot of different\nuser needs.",[12,512,513],{},"When repositories grow, they often store a lot of references and a lot\nof files and directories, so there is often the need to pass a big\nnumber of references or paths as arguments to the\ncommand. References and paths can be quite long though.",[12,515,516,517,520,521,524,525,528,529,531],{},"To avoid hitting platform limits related to command line length, long\nago, a ",[79,518,519],{},"--stdin"," mode was added that allowed users to pass revisions\nand paths through stdin, instead of as command line\narguments. However, when that was implemented, it was not considered\nnecessary to allow options or pseudo-options, like ",[79,522,523],{},"--not",",\n",[79,526,527],{},"--glob=...",", or ",[79,530,117],{}," to be passed through stdin.",[12,533,534],{},"This appeared to be a problem for GitLab, as for computing sizes for\nforked repositories we needed some of the pseudo-options, and it would\nhave been intricate and possibly buggy to pass some of them and their\narguments as arguments on the command line while others were passed\nthrough stdin.",[36,536,538],{"id":537},"allowing-pseudo-options","Allowing pseudo-options",[12,540,541],{},"To fix this issue, Patrick Steinhardt implemented a small patch series to\nallow pseudo-options through stdin.",[12,543,544,545,524,547,528,549,551,552,554],{},"With it, in Git 2.42, one can now pass pseudo-options, like ",[79,546,523],{},[79,548,527],{},[79,550,117],{}," through stdin when the ",[79,553,519],{}," mode is used.",[12,556,199,557,205],{},[15,558,204],{"href":559,"rel":560},"https://lore.kernel.org/git/cover.1686744685.git.ps@pks.im/",[],[31,562,564],{"id":563},"_4-code-and-test-improvements","4. Code and test improvements",[12,566,567],{},"While looking at some Git code, we are often tempted to modify nearby\ncode, either to change only its style when the code is ancient and it\nwould look better using Git's current code style, or to refactor it to\nmake it cleaner. This is why we sometimes send small patch series that\ndon't have a real GitLab related purpose.",[12,569,570,571,576,577,582],{},"In Git 2.42, examples of style code improvements we made are the\n",[15,572,575],{"href":573,"rel":574},"https://lore.kernel.org/git/pull.1513.git.git.1684440205.gitgitgadget@gmail.com/",[],"part1","\nand\n",[15,578,581],{"href":579,"rel":580},"https://lore.kernel.org/git/pull.1514.git.git.1684599239.gitgitgadget@gmail.com/",[],"part2","\ntest code modernization patches from John Cai.",[12,584,585,586,590],{},"And ",[15,587,204],{"href":588,"rel":589},"https://lore.kernel.org/git/cover.1684324059.git.ps@pks.im/",[]," is\nan example of a refactoring to cleanup some code by Patrick Steinhardt.",[592,593,594],"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 .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);}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}",{"title":171,"searchDepth":316,"depth":316,"links":596},[597,604,611,618],{"id":33,"depth":316,"text":34,"children":598},[599,600,601,602],{"id":38,"depth":322,"text":39},{"id":60,"depth":322,"text":61},{"id":73,"depth":322,"text":74},{"id":121,"depth":322,"text":603},"Improving git pack-refs",{"id":208,"depth":316,"text":605,"children":606},"2. Get machine-readable output from git cat-file --batch",[607,608,609,610],{"id":215,"depth":322,"text":216},{"id":268,"depth":322,"text":269},{"id":356,"depth":322,"text":357},{"id":404,"depth":322,"text":405},{"id":446,"depth":316,"text":612,"children":613},"3. Pass pseudo-options to git rev-list --stdin",[614,615,617],{"id":453,"depth":322,"text":454},{"id":502,"depth":322,"text":616},"Passing arguments to git rev-list",{"id":537,"depth":322,"text":538},{"id":563,"depth":316,"text":564},"product","2023-10-12","Find out how GitLab's Git team helped improve Git 2.42.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667792/Blog/Hero%20Images/git-241.jpg",{},true,"/en-us/blog/contributions-to-git-2-42-release",{"title":5,"description":621,"ogTitle":5,"ogDescription":621,"noIndex":624,"ogImage":625,"ogUrl":630,"ogSiteName":631,"ogType":632,"canonicalUrls":630},"https://about.gitlab.com/blog/contributions-to-git-2-42-release","https://about.gitlab.com","article","contributions-to-git-2-42-release","en-us/blog/contributions-to-git-2-42-release",[636,637,638,639],"git","news","open source","community","BlogPost","lTgtiz5zlg4NLSTxu-nrMJD48jXK6MKaAaobXQk9kU8",{"logo":643,"freeTrial":648,"sales":653,"login":658,"items":663,"search":989,"minimal":1020,"duo":1039,"switchNav":1048,"pricingDeployment":1059},{"config":644},{"href":645,"dataGaName":646,"dataGaLocation":647},"/","gitlab logo","header",{"text":649,"config":650},"Get free trial",{"href":651,"dataGaName":652,"dataGaLocation":647},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":654,"config":655},"Request a demo",{"href":656,"dataGaName":657,"dataGaLocation":647},"/sales/?contact-topic=request-demo","sales",{"text":659,"config":660},"Sign in",{"href":661,"dataGaName":662,"dataGaLocation":647},"https://gitlab.com/users/sign_in/","sign in",[664,693,793,798,911,967],{"text":665,"config":666,"menu":668},"Platform",{"dataNavLevelOne":667},"platform",{"type":669,"columns":670},"cards",[671,677,685],{"title":665,"description":672,"link":673},"The intelligent orchestration platform for DevSecOps",{"text":674,"config":675},"Explore our Platform",{"href":676,"dataGaName":667,"dataGaLocation":647},"/platform/",{"title":678,"description":679,"link":680},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":681,"config":682},"Meet GitLab Duo",{"href":683,"dataGaName":684,"dataGaLocation":647},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":686,"description":687,"link":688},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":689,"config":690},"Learn more",{"href":691,"dataGaName":692,"dataGaLocation":647},"/why-gitlab/","why gitlab",{"text":694,"left":627,"config":695,"menu":697},"Product",{"dataNavLevelOne":696},"solutions",{"type":698,"link":699,"columns":703,"feature":772},"lists",{"text":700,"config":701},"View all Solutions",{"href":702,"dataGaName":696,"dataGaLocation":647},"/solutions/",[704,728,751],{"title":705,"description":706,"link":707,"items":712},"Automation","CI/CD and automation to accelerate deployment",{"config":708},{"icon":709,"href":710,"dataGaName":711,"dataGaLocation":647},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[713,717,720,724],{"text":714,"config":715},"CI/CD",{"href":716,"dataGaLocation":647,"dataGaName":714},"/solutions/continuous-integration/",{"text":678,"config":718},{"href":683,"dataGaLocation":647,"dataGaName":719},"gitlab duo agent platform - product menu",{"text":721,"config":722},"Source Code Management",{"href":723,"dataGaLocation":647,"dataGaName":721},"/solutions/source-code-management/",{"text":725,"config":726},"Automated Software Delivery",{"href":710,"dataGaLocation":647,"dataGaName":727},"Automated software delivery",{"title":729,"description":730,"link":731,"items":736},"Security","Deliver code faster without compromising security",{"config":732},{"href":733,"dataGaName":734,"dataGaLocation":647,"icon":735},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[737,741,746],{"text":738,"config":739},"Application Security Testing",{"href":733,"dataGaName":740,"dataGaLocation":647},"Application security testing",{"text":742,"config":743},"Software Supply Chain Security",{"href":744,"dataGaLocation":647,"dataGaName":745},"/solutions/supply-chain/","Software supply chain security",{"text":747,"config":748},"Software Compliance",{"href":749,"dataGaName":750,"dataGaLocation":647},"/solutions/software-compliance/","software compliance",{"title":752,"link":753,"items":758},"Measurement",{"config":754},{"icon":755,"href":756,"dataGaName":757,"dataGaLocation":647},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[759,763,767],{"text":760,"config":761},"Visibility & Measurement",{"href":756,"dataGaLocation":647,"dataGaName":762},"Visibility and Measurement",{"text":764,"config":765},"Value Stream Management",{"href":766,"dataGaLocation":647,"dataGaName":764},"/solutions/value-stream-management/",{"text":768,"config":769},"Analytics & Insights",{"href":770,"dataGaLocation":647,"dataGaName":771},"/solutions/analytics-and-insights/","Analytics and insights",{"title":773,"type":698,"items":774},"GitLab for",[775,781,787],{"text":776,"config":777},"Enterprise",{"icon":778,"href":779,"dataGaLocation":647,"dataGaName":780},"Building","/enterprise/","enterprise",{"text":782,"config":783},"Small Business",{"icon":784,"href":785,"dataGaLocation":647,"dataGaName":786},"Work","/small-business/","small business",{"text":788,"config":789},"Public Sector",{"icon":790,"href":791,"dataGaLocation":647,"dataGaName":792},"Organization","/solutions/public-sector/","public sector",{"text":794,"config":795},"Pricing",{"href":796,"dataGaName":797,"dataGaLocation":647,"dataNavLevelOne":797},"/pricing/","pricing",{"text":799,"config":800,"menu":802},"Resources",{"dataNavLevelOne":801},"resources",{"type":698,"link":803,"columns":807,"feature":900},{"text":804,"config":805},"View all resources",{"href":806,"dataGaName":801,"dataGaLocation":647},"/resources/",[808,841,868],{"title":809,"items":810},"Getting started",[811,816,821,826,831,836],{"text":812,"config":813},"Install",{"href":814,"dataGaName":815,"dataGaLocation":647},"/install/","install",{"text":817,"config":818},"Quick start guides",{"href":819,"dataGaName":820,"dataGaLocation":647},"/get-started/","quick setup checklists",{"text":822,"config":823},"Learn",{"href":824,"dataGaLocation":647,"dataGaName":825},"https://university.gitlab.com/","learn",{"text":827,"config":828},"Product documentation",{"href":829,"dataGaName":830,"dataGaLocation":647},"https://docs.gitlab.com/","product documentation",{"text":832,"config":833},"Best practice videos",{"href":834,"dataGaName":835,"dataGaLocation":647},"/getting-started-videos/","best practice videos",{"text":837,"config":838},"Integrations",{"href":839,"dataGaName":840,"dataGaLocation":647},"/integrations/","integrations",{"title":842,"items":843},"Discover",[844,849,854,859,863],{"text":845,"config":846},"Customer success stories",{"href":847,"dataGaName":848,"dataGaLocation":647},"/customers/","customer success stories",{"text":850,"config":851},"Blog",{"href":852,"dataGaName":853,"dataGaLocation":647},"/blog/","blog",{"text":855,"config":856},"Demo Hub",{"href":857,"dataGaName":858,"dataGaLocation":647},"/demo-hub/","demo hub",{"text":860,"config":861},"The Source",{"href":862,"dataGaName":853,"dataGaLocation":647},"/the-source/",{"text":864,"config":865},"Remote",{"href":866,"dataGaName":867,"dataGaLocation":647},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":869,"items":870},"Connect",[871,876,881,885,890,895],{"text":872,"config":873},"GitLab Services",{"href":874,"dataGaName":875,"dataGaLocation":647},"/services/","services",{"text":877,"config":878},"Contribute",{"href":879,"dataGaName":880,"dataGaLocation":647},"https://contributors.gitlab.com","contribute",{"text":882,"config":883},"Community",{"href":884,"dataGaName":639,"dataGaLocation":647},"/community/",{"text":886,"config":887},"Forum",{"href":888,"dataGaName":889,"dataGaLocation":647},"https://forum.gitlab.com/","forum",{"text":891,"config":892},"Events",{"href":893,"dataGaName":894,"dataGaLocation":647},"/events/","events",{"text":896,"config":897},"Partners",{"href":898,"dataGaName":899,"dataGaLocation":647},"/partners/","partners",{"config":901,"title":904,"text":905,"link":906},{"background":902,"textColor":903},"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":907,"config":908},"Read the latest",{"href":909,"dataGaName":910,"dataGaLocation":647},"/whats-new/","whats new",{"text":912,"config":913,"menu":915},"Company",{"dataNavLevelOne":914},"company",{"type":698,"columns":916},[917],{"items":918},[919,924,930,932,937,942,947,952,957,962],{"text":920,"config":921},"About",{"href":922,"dataGaName":923,"dataGaLocation":647},"/company/","about",{"text":925,"config":926,"footerGa":929},"Jobs",{"href":927,"dataGaName":928,"dataGaLocation":647},"/jobs/","jobs",{"dataGaName":928},{"text":891,"config":931},{"href":893,"dataGaName":894,"dataGaLocation":647},{"text":933,"config":934},"Leadership",{"href":935,"dataGaName":936,"dataGaLocation":647},"/company/team/e-group/","leadership",{"text":938,"config":939},"Handbook",{"href":940,"dataGaName":941,"dataGaLocation":647},"https://handbook.gitlab.com/","handbook",{"text":943,"config":944},"Investor relations",{"href":945,"dataGaName":946,"dataGaLocation":647},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":948,"config":949},"Trust Center",{"href":950,"dataGaName":951,"dataGaLocation":647},"/security/","trust center",{"text":953,"config":954},"AI Transparency Center",{"href":955,"dataGaName":956,"dataGaLocation":647},"/ai-transparency-center/","ai transparency center",{"text":958,"config":959},"Newsletter",{"href":960,"dataGaName":961,"dataGaLocation":647},"/company/contact/#contact-forms","newsletter",{"text":963,"config":964},"Press",{"href":965,"dataGaName":966,"dataGaLocation":647},"/press/","press",{"text":968,"config":969,"menu":970},"Contact us",{"dataNavLevelOne":914},{"type":698,"columns":971},[972],{"items":973},[974,979,984],{"text":975,"config":976},"Talk to sales",{"href":977,"dataGaName":978,"dataGaLocation":647},"/sales/","talk to sales",{"text":980,"config":981},"Support portal",{"href":982,"dataGaName":983,"dataGaLocation":647},"https://support.gitlab.com/hc/en-us","support portal",{"text":985,"config":986},"Customer portal",{"href":987,"dataGaName":988,"dataGaLocation":647},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":990,"login":991,"suggestions":998},"Close",{"text":992,"link":993},"To search repositories and projects, login to",{"text":994,"config":995},"gitlab.com",{"href":661,"dataGaName":996,"dataGaLocation":997},"search login","search",{"text":999,"default":1000},"Suggestions",[1001,1003,1007,1009,1013,1017],{"text":678,"config":1002},{"href":683,"dataGaName":678,"dataGaLocation":997},{"text":1004,"config":1005},"Code Suggestions (AI)",{"href":1006,"dataGaName":1004,"dataGaLocation":997},"/solutions/code-suggestions/",{"text":714,"config":1008},{"href":716,"dataGaName":714,"dataGaLocation":997},{"text":1010,"config":1011},"GitLab on AWS",{"href":1012,"dataGaName":1010,"dataGaLocation":997},"/partners/technology-partners/aws/",{"text":1014,"config":1015},"GitLab on Google Cloud",{"href":1016,"dataGaName":1014,"dataGaLocation":997},"/partners/technology-partners/google-cloud-platform/",{"text":1018,"config":1019},"Why GitLab?",{"href":691,"dataGaName":1018,"dataGaLocation":997},{"freeTrial":1021,"mobileIcon":1026,"desktopIcon":1031,"secondaryButton":1034},{"text":1022,"config":1023},"Start free trial",{"href":1024,"dataGaName":652,"dataGaLocation":1025},"https://gitlab.com/-/trials/new/","nav",{"altText":1027,"config":1028},"Gitlab Icon",{"src":1029,"dataGaName":1030,"dataGaLocation":1025},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1027,"config":1032},{"src":1033,"dataGaName":1030,"dataGaLocation":1025},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1035,"config":1036},"Get Started",{"href":1037,"dataGaName":1038,"dataGaLocation":1025},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1040,"mobileIcon":1044,"desktopIcon":1046},{"text":1041,"config":1042},"Learn more about GitLab Duo",{"href":683,"dataGaName":1043,"dataGaLocation":1025},"gitlab duo",{"altText":1027,"config":1045},{"src":1029,"dataGaName":1030,"dataGaLocation":1025},{"altText":1027,"config":1047},{"src":1033,"dataGaName":1030,"dataGaLocation":1025},{"button":1049,"mobileIcon":1054,"desktopIcon":1056},{"text":1050,"config":1051},"/switch",{"href":1052,"dataGaName":1053,"dataGaLocation":1025},"#contact","switch",{"altText":1027,"config":1055},{"src":1029,"dataGaName":1030,"dataGaLocation":1025},{"altText":1027,"config":1057},{"src":1058,"dataGaName":1030,"dataGaLocation":1025},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1060,"mobileIcon":1065,"desktopIcon":1067},{"text":1061,"config":1062},"Back to pricing",{"href":796,"dataGaName":1063,"dataGaLocation":1025,"icon":1064},"back to pricing","GoBack",{"altText":1027,"config":1066},{"src":1029,"dataGaName":1030,"dataGaLocation":1025},{"altText":1027,"config":1068},{"src":1033,"dataGaName":1030,"dataGaLocation":1025},{"title":1070,"titleMobile":1071,"button":1072,"config":1077},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":689,"config":1073},{"href":1074,"dataGaName":1075,"dataGaLocation":1076},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1078,"disabled":624},"release",{"data":1080},{"text":1081,"source":1082,"edit":1088,"contribute":1093,"config":1098,"items":1103,"minimal":1313},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1083,"config":1084},"View page source",{"href":1085,"dataGaName":1086,"dataGaLocation":1087},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1089,"config":1090},"Edit this page",{"href":1091,"dataGaName":1092,"dataGaLocation":1087},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1094,"config":1095},"Please contribute",{"href":1096,"dataGaName":1097,"dataGaLocation":1087},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1099,"facebook":1100,"youtube":1101,"linkedin":1102},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1104,1151,1205,1249,1281],{"title":794,"links":1105,"subMenu":1120},[1106,1110,1115],{"text":1107,"config":1108},"View plans",{"href":796,"dataGaName":1109,"dataGaLocation":1087},"view plans",{"text":1111,"config":1112},"Why Premium?",{"href":1113,"dataGaName":1114,"dataGaLocation":1087},"/pricing/premium/","why premium",{"text":1116,"config":1117},"Why Ultimate?",{"href":1118,"dataGaName":1119,"dataGaLocation":1087},"/pricing/ultimate/","why ultimate",[1121],{"title":1122,"links":1123},"Contact Us",[1124,1127,1129,1131,1136,1141,1146],{"text":1125,"config":1126},"Contact sales",{"href":977,"dataGaName":657,"dataGaLocation":1087},{"text":980,"config":1128},{"href":982,"dataGaName":983,"dataGaLocation":1087},{"text":985,"config":1130},{"href":987,"dataGaName":988,"dataGaLocation":1087},{"text":1132,"config":1133},"Status",{"href":1134,"dataGaName":1135,"dataGaLocation":1087},"https://status.gitlab.com/","status",{"text":1137,"config":1138},"Terms of use",{"href":1139,"dataGaName":1140,"dataGaLocation":1087},"/terms/","terms of use",{"text":1142,"config":1143},"Privacy statement",{"href":1144,"dataGaName":1145,"dataGaLocation":1087},"/privacy/","privacy statement",{"text":1147,"config":1148},"Cookie preferences",{"dataGaName":1149,"dataGaLocation":1087,"id":1150,"isOneTrustButton":627},"cookie preferences","ot-sdk-btn",{"title":694,"links":1152,"subMenu":1161},[1153,1157],{"text":1154,"config":1155},"DevSecOps platform",{"href":676,"dataGaName":1156,"dataGaLocation":1087},"devsecops platform",{"text":1158,"config":1159},"AI-Assisted Development",{"href":683,"dataGaName":1160,"dataGaLocation":1087},"ai-assisted development",[1162],{"title":1163,"links":1164},"Topics",[1165,1170,1175,1180,1185,1190,1195,1200],{"text":1166,"config":1167},"CICD",{"href":1168,"dataGaName":1169,"dataGaLocation":1087},"/topics/ci-cd/","cicd",{"text":1171,"config":1172},"GitOps",{"href":1173,"dataGaName":1174,"dataGaLocation":1087},"/topics/gitops/","gitops",{"text":1176,"config":1177},"DevOps",{"href":1178,"dataGaName":1179,"dataGaLocation":1087},"/topics/devops/","devops",{"text":1181,"config":1182},"Version Control",{"href":1183,"dataGaName":1184,"dataGaLocation":1087},"/topics/version-control/","version control",{"text":1186,"config":1187},"DevSecOps",{"href":1188,"dataGaName":1189,"dataGaLocation":1087},"/topics/devsecops/","devsecops",{"text":1191,"config":1192},"Cloud Native",{"href":1193,"dataGaName":1194,"dataGaLocation":1087},"/topics/cloud-native/","cloud native",{"text":1196,"config":1197},"AI for Coding",{"href":1198,"dataGaName":1199,"dataGaLocation":1087},"/topics/devops/ai-for-coding/","ai for coding",{"text":1201,"config":1202},"Agentic AI",{"href":1203,"dataGaName":1204,"dataGaLocation":1087},"/topics/agentic-ai/","agentic ai",{"title":1206,"links":1207},"Solutions",[1208,1210,1212,1217,1221,1224,1228,1231,1233,1236,1239,1244],{"text":738,"config":1209},{"href":733,"dataGaName":738,"dataGaLocation":1087},{"text":727,"config":1211},{"href":710,"dataGaName":711,"dataGaLocation":1087},{"text":1213,"config":1214},"Agile development",{"href":1215,"dataGaName":1216,"dataGaLocation":1087},"/solutions/agile-delivery/","agile delivery",{"text":1218,"config":1219},"SCM",{"href":723,"dataGaName":1220,"dataGaLocation":1087},"source code management",{"text":1166,"config":1222},{"href":716,"dataGaName":1223,"dataGaLocation":1087},"continuous integration & delivery",{"text":1225,"config":1226},"Value stream management",{"href":766,"dataGaName":1227,"dataGaLocation":1087},"value stream management",{"text":1171,"config":1229},{"href":1230,"dataGaName":1174,"dataGaLocation":1087},"/solutions/gitops/",{"text":776,"config":1232},{"href":779,"dataGaName":780,"dataGaLocation":1087},{"text":1234,"config":1235},"Small business",{"href":785,"dataGaName":786,"dataGaLocation":1087},{"text":1237,"config":1238},"Public sector",{"href":791,"dataGaName":792,"dataGaLocation":1087},{"text":1240,"config":1241},"Education",{"href":1242,"dataGaName":1243,"dataGaLocation":1087},"/solutions/education/","education",{"text":1245,"config":1246},"Financial services",{"href":1247,"dataGaName":1248,"dataGaLocation":1087},"/solutions/finance/","financial services",{"title":799,"links":1250},[1251,1253,1255,1257,1260,1262,1265,1267,1269,1271,1273,1275,1277,1279],{"text":812,"config":1252},{"href":814,"dataGaName":815,"dataGaLocation":1087},{"text":817,"config":1254},{"href":819,"dataGaName":820,"dataGaLocation":1087},{"text":822,"config":1256},{"href":824,"dataGaName":825,"dataGaLocation":1087},{"text":827,"config":1258},{"href":829,"dataGaName":1259,"dataGaLocation":1087},"docs",{"text":850,"config":1261},{"href":852,"dataGaName":853,"dataGaLocation":1087},{"text":1263,"config":1264},"What's new",{"href":909,"dataGaName":910,"dataGaLocation":1087},{"text":845,"config":1266},{"href":847,"dataGaName":848,"dataGaLocation":1087},{"text":864,"config":1268},{"href":866,"dataGaName":867,"dataGaLocation":1087},{"text":872,"config":1270},{"href":874,"dataGaName":875,"dataGaLocation":1087},{"text":877,"config":1272},{"href":879,"dataGaName":880,"dataGaLocation":1087},{"text":882,"config":1274},{"href":884,"dataGaName":639,"dataGaLocation":1087},{"text":886,"config":1276},{"href":888,"dataGaName":889,"dataGaLocation":1087},{"text":891,"config":1278},{"href":893,"dataGaName":894,"dataGaLocation":1087},{"text":896,"config":1280},{"href":898,"dataGaName":899,"dataGaLocation":1087},{"title":912,"links":1282},[1283,1285,1287,1289,1291,1293,1297,1302,1304,1306,1308],{"text":920,"config":1284},{"href":922,"dataGaName":914,"dataGaLocation":1087},{"text":925,"config":1286},{"href":927,"dataGaName":928,"dataGaLocation":1087},{"text":933,"config":1288},{"href":935,"dataGaName":936,"dataGaLocation":1087},{"text":938,"config":1290},{"href":940,"dataGaName":941,"dataGaLocation":1087},{"text":943,"config":1292},{"href":945,"dataGaName":946,"dataGaLocation":1087},{"text":1294,"config":1295},"Sustainability",{"href":1296,"dataGaName":1294,"dataGaLocation":1087},"/sustainability/",{"text":1298,"config":1299},"Diversity, inclusion and belonging (DIB)",{"href":1300,"dataGaName":1301,"dataGaLocation":1087},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":948,"config":1303},{"href":950,"dataGaName":951,"dataGaLocation":1087},{"text":958,"config":1305},{"href":960,"dataGaName":961,"dataGaLocation":1087},{"text":963,"config":1307},{"href":965,"dataGaName":966,"dataGaLocation":1087},{"text":1309,"config":1310},"Modern Slavery Transparency Statement",{"href":1311,"dataGaName":1312,"dataGaLocation":1087},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1314},[1315,1318,1321],{"text":1316,"config":1317},"Terms",{"href":1139,"dataGaName":1140,"dataGaLocation":1087},{"text":1319,"config":1320},"Cookies",{"dataGaName":1149,"dataGaLocation":1087,"id":1150,"isOneTrustButton":627},{"text":1322,"config":1323},"Privacy",{"href":1144,"dataGaName":1145,"dataGaLocation":1087},[1325],{"id":1326,"title":7,"body":623,"config":1327,"content":1329,"description":623,"extension":1333,"meta":1334,"navigation":627,"path":1335,"seo":1336,"stem":1337,"__hash__":1338},"blogAuthors/en-us/blog/authors/christian-couder.yml",{"template":1328},"BlogAuthor",{"name":7,"config":1330},{"headshot":1331,"ctfId":1332},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663687/Blog/Author%20Headshots/chriscool-headshot.jpg","chriscool","yml",{},"/en-us/blog/authors/christian-couder",{},"en-us/blog/authors/christian-couder","6VsKjKNdm1XKAmFMZHj53ROyylDlxaSJhcARRkw6EQQ",[1340,1346,1350],{"title":1341,"description":1342,"heroImage":1343,"category":619,"date":1344,"authors":623,"slug":623,"externalUrl":1345},"GitLab Patch Release: 19.2.2, 19.1.4, 19.0.6","Learn about this latest patch release for Community Edition and Enterprise Edition.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749661926/Blog/Hero%20Images/security-patch-blog-image-r2-0506-700x400-fy25_2x.jpg","2026-08-12","https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-2-2-released/",{"title":1347,"description":1342,"heroImage":1343,"category":619,"date":1348,"authors":623,"slug":623,"externalUrl":1349}," GitLab Patch Release: 19.2.1, 19.1.3, 19.0.5","2026-07-29","https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-2-1-released/",{"title":1351,"description":1352,"heroImage":1353,"category":619,"date":1354,"authors":1355,"slug":1357,"externalUrl":623},"Automate work item assignment with a \"Work item created\" trigger","Our step-by-step guide walks through this AI capability, which enables instant, scalable triage without manual intervention.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-07-20",[1356],"Cesar Saavedra","how-to-use-a-work-item-created-trigger",{"promotions":1359},[1360,1374,1385,1397],{"id":1361,"categories":1362,"header":1364,"text":1365,"button":1366,"image":1371},"ai-modernization",[1363],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1367,"config":1368},"Get your AI maturity score",{"href":1369,"dataGaName":1370,"dataGaLocation":853},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1372},{"src":1373},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1375,"categories":1376,"header":1377,"text":1365,"button":1378,"image":1382},"devops-modernization",[619,1189],"Are you just managing tools or shipping innovation?",{"text":1379,"config":1380},"Get your DevOps maturity score",{"href":1381,"dataGaName":1370,"dataGaLocation":853},"/assessments/devops-modernization-assessment/",{"config":1383},{"src":1384},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1386,"categories":1387,"header":1389,"text":1365,"button":1390,"image":1394},"security-modernization",[1388],"security","Are you trading speed for security?",{"text":1391,"config":1392},"Get your security maturity score",{"href":1393,"dataGaName":1370,"dataGaLocation":853},"/assessments/security-modernization-assessment/",{"config":1395},{"src":1396},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1398,"paths":1399,"header":1402,"text":1403,"button":1404,"image":1409},"github-azure-migration",[1400,1401],"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":1405,"config":1406},"See how GitLab compares to GitHub",{"href":1407,"dataGaName":1408,"dataGaLocation":853},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1410},{"src":1384},{"header":1412,"blurb":1413,"button":1414,"secondaryButton":1419},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1415,"config":1416},"Get your free trial",{"href":1417,"dataGaName":652,"dataGaLocation":1418},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1125,"config":1420},{"href":977,"dataGaName":657,"dataGaLocation":1418},1786803731592]