{"thread":{"id":"66453","subject":"[ANNOUNCE] git-carrier 0.1.0: park a stopped merge, land it later as a real merge","startedAt":"2026-10-03T01:37:51Z","lastAt":"2026-10-03T01:37:51Z","messageCount":1,"participants":["Oscar Mira"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"554039","messageId":"DKZjdpQb-rCGMXf5m_vAwXJGqsNM5jacEb_nzx_qS46udko31UhnYZvppt-AmHdOasUzzGfs6xm69ziK4nstKM-ddNNm8HIBj1SLuEVPNMw=@molly.im","threadId":"66453","inReplyTo":null,"subject":"[ANNOUNCE] git-carrier 0.1.0: park a stopped merge, land it later as a real merge","fromName":"Oscar Mira","fromEmail":"valldrac@molly.im","sentAt":"2026-10-03T01:37:44Z","receivedAt":"2026-10-03T01:37:51Z","isPatch":false,"body":"Hello,\n\nI've just published a set of git extensions that park a merge that\nstopped at conflicts as an ordinary commit, and land it later as a\nreal merge.\n\nAn idea like this has been proposed on this list several times, as a\nsolution to the problem of working with large merges. I believe ours\nis the first tool to implement it in plain git.\n\nI'm one of the maintainers of Molly, a Signal client fork of Signal\nfor Android. Upstream updates arrive in our tree as large merges\nthat generate many conflicts. I wrote this tool because we need to\ndistribute the resolution work across the team, so the conflict state\nhas to travel across clones.\n\ngit-carrier is one bash script that implements three git commands,\nwith no dependencies beyond git and standard Unix utilities:\n\n  git park --branch carrier/v1.2.3\n      Moves the stopped merge onto a carrier branch, without\n      recreating the merge, and stores the conflicted paths under\n      a .hangar directory in ordinary commits.\n\n  git unpark -- <path>\n      Takes a parked path back out of the hangar. If the file is\n      unchanged, it reopens as the conflict git left, so\n      `git mergetool` works as usual. If you changed or deleted it,\n      the working file is the resolution.\n\n  git land <dst>\n      Stages the finished work onto the destination without the\n      .hangar directory, and writes MERGE_HEAD. Then it stops, and\n      you run `git commit` to finish the two-parent merge.\n\nEverything in between is ordinary git. The carrier branch is pushed,\nreviewed and cloned like any branch. The work left is the\n.hangar/stages/<path>/ directories at HEAD, and a path is released\nby deleting its directory, which shows in any diff. No new ref\nnamespaces, no server-side changes.\n\nThe tool was designed to meet our needs. We read the list's discussions\nof the same problem only afterward, and it was a happy convergence.\nIn June 2020 this list discussed how to pass a partially resolved merge\non to the next person [1]. Junio's answer was that the important and\nuseful part is the data format used for that hand-off [2]. Chris Torek\nsketched what the format holds [3]; his sketch reads like a description\nof the hangar:\n\n    resolved entries          the carrier's own commits\n    stages 1, 2, 3            .hangar/stages/<path>/{1,2,3}\n    the working file          .hangar/stages/<path>/w\n    the merge's own context   .hangar/manifest and .hangar/message\n\nAnd the 2025 contributor summit expected external tooling to cover this\nuntil first-class conflicts exist [6].\n\nI want to share the design, because the format is the useful part for\nthis list. The spec is on its own page, CC0, and git-carrier is just\none possible implementation. That said, the spec was written after the\nfirst implementation and can have gaps; the script does more than it\nexplains.\n\nNotes on the decisions, since the list has positions on them:\n\n  * Out of tree. The thread's advice was to implement it outside\n    git [4], and to help distros bundle good tools rather than\n    integrate them [5]. We are not proposing contrib/ or core\n    inclusion. But we would love to list the tool on the git wiki's\n    tools page.\n\n  * If everyone in the loop runs jj, it is a good alternative: jj\n    makes conflicts first-class committable state. This tool is for\n    loops where some resolvers, CI, or agents are plain git.\n\n  * The carrier branch carries conflict markers by design. It is a\n    handoff, never merged directly, and the landed tree is the\n    carrier's tree minus .hangar.\n\n  * Merge conflicts only, for now: no rebase, cherry-pick, or revert\n    parking, one merge source, no octopus, no submodule gitlinks.\n\nA note on confidence: the tool is brand new. The bash code was mostly\nauthored through several LLM iterations, and the commits carry\n\"Assisted-by: LLM\" trailers for that reason. We reviewed and tested it\nextensively, but it could still be wrong somewhere. The bash file grew\nbigger than expected, and I know we pay for that in maintenance.\n\nI built it with care, and I hope it is useful.\n\n  Repo:  https://github.com/git-carrier/git-carrier\n  Spec:  docs/hangar-format.txt in the repository, CC0.\n  Tool:  one bash file, MIT, with a bats test suite. It needs bash\n         3.2 or newer and git 2.34 or newer.\n\nThanks for reading. Comments welcome, especially on the format.\nBug reports even more.\n\nOscar Mira\n\n[1] https://lore.kernel.org/git/BY5PR19MB3400EB9AD87DFE612AFD5CC390810@BY5PR19MB3400.namprd19.prod.outlook.com/\n[2] https://lore.kernel.org/git/xmqq1rmgxo67.fsf@gitster.c.googlers.com/\n[3] https://lore.kernel.org/git/CAPx1GvdT6sZRtu8q1R9=fA-mE9pi1Ag-gKEzQfwbGap+KqSoSg@mail.gmail.com/\n[4] https://lore.kernel.org/git/874kr92xyz.fsf@osv.gnss.ru/\n[5] https://lore.kernel.org/git/xmqqa716zs7w.fsf@gitster.c.googlers.com/\n[6] https://lore.kernel.org/git/aOQV%2Fja9Ltw%2FbTP3@nand.local/\n"}]}