repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

git-pr / tmpl
Eric Bower  ·  2025-03-28

index.html

  1{{template "base" .}}
  2
  3{{define "title"}}git-pr{{end}}
  4
  5{{define "meta"}}
  6<link rel="alternate" type="application/atom+xml"
  7      title="RSS feed for git collaboration server"
  8      href="/rss" />
  9{{end}}
 10
 11{{define "body"}}
 12<header class="group">
 13  <h1 class="text-2xl">git-pr</h1>
 14  <div>
 15    <span>A pastebin supercharged for git collaboration</span> &middot;
 16    <a href="https://github.com/picosh/git-pr">github</a> &middot;
 17    <a href="https://youtu.be/d28Dih-BBUw">demo video</a>
 18  </div>
 19
 20  {{if .MetaData.Desc}}
 21  <div class="box-sm">
 22    <div>{{.MetaData.Desc}}</div>
 23  </div>
 24  {{end}}
 25
 26  <details>
 27    <summary>Intro</summary>
 28
 29    <div>
 30      <p>
 31        We are trying to build the simplest git collaboration tool. The goal is to make
 32        self-hosting as simple as running an SSH server -- all without
 33        sacrificing external collaborators time and energy.
 34      </p>
 35
 36      <blockquote>
 37        <code>git format-patch</code> isn't the problem and pull requests aren't the solution.
 38      </blockquote>
 39
 40      <p>
 41        We are combining mailing list and pull request workflows. In order to build the
 42        simplest collaboration tool, we needed something as simple as generating patches
 43        but the ease-of-use of pull requests.
 44      </p>
 45
 46      <p>
 47        The goal is not to create another code forge, the goal is to create a very
 48        simple self-hosted git solution with the ability to collaborate with external
 49        contributors. All the code owner needs to setup a running git server:
 50      </p>
 51
 52      <ul><li>A single golang binary</li></ul>
 53
 54      <div>
 55        All an external contributor needs is:
 56      </div>
 57
 58      <ul>
 59        <li>An SSH keypair</li>
 60        <li>An SSH client</li>
 61      </ul>
 62
 63      <p>Then everyone subscribes to our RSS feeds to receive updates to patch requests.</p>
 64
 65      <h2 class="text-xl">the problem</h2>
 66
 67      <p>
 68        Email is great as a decentralized system to send and receive changes (patchsets)
 69        to a git repo. However, onboarding a new user to a mailing list, properly
 70        setting up their email client, and then finally submitting the code contribution
 71        is enough to make many developers give up. Further, because we are leveraging
 72        the email protocol for collaboration, we are limited by its feature-set. For
 73        example, it is not possible to make edits to emails, everyone has a different
 74        client, those clients have different limitations around plain text email and
 75        downloading patches from it.
 76      </p>
 77
 78      <p>
 79        Github pull requests are easy to use, easy to edit, and easy to manage. The
 80        downside is it forces the user to be inside their website to perform reviews.
 81        For quick changes, this is great, but when you start reading code within a web
 82        browser, there are quite a few downsides. At a certain point, it makes more
 83        sense to review code inside your local development environment, IDE, etc. There
 84        are tools and plugins that allow users to review PRs inside their IDE, but it
 85        requires a herculean effort to make it usable.
 86      </p>
 87
 88      <p>
 89        Further, self-hosted solutions that mimic a pull request require a lot of
 90        infrastructure in order to manage it. A database, a web site connected to git,
 91        admin management, and services to manage it all. Another big point of friction:
 92        before an external user submits a code change, they first need to create an
 93        account and then login. This adds quite a bit of friction for a self-hosted
 94        solution, not only for an external contributor, but also for the code owner who
 95        has to provision the infra. Often times they also have to fork the repo within
 96        the code forge before submitting a PR. Then they never make a contribution ever
 97        again and keep a forked repo around forever. That seems silly.
 98      </p>
 99
100      <h2 class="text-xl">introducing patch requests (PR)</h2>
101
102      <p>
103        Instead, we want to create a self-hosted git "server" that can handle sending
104        and receiving patches without the cumbersome nature of setting up email or the
105        limitations imposed by the email protocol. Further, we want the primary workflow
106        to surround the local development environment. Github is bringing the IDE to the
107        browser in order to support their workflow, we want to flip that idea on its
108        head by making code reviews a first-class citizen inside your local development
109        environment.
110      </p>
111
112      <p>
113        We see this as a hybrid between the github workflow of a pull request and
114        sending and receiving patches over email.
115      </p>
116
117      <p>
118        The basic idea is to leverage an SSH app to handle most of the interaction
119        between contributor and owner of a project. Everything can be done completely
120        within the terminal, in a way that is ergonomic and fully featured.
121      </p>
122
123      <p>
124        Notifications would happen with RSS and all state mutations would result in the
125        generation of static web assets so it can all be hosted using a simple file web
126        server.
127      </p>
128
129      <h3 class="text-lg">format-patch workflow</h3>
130
131      <p>
132        The fundamental collaboration tool here is <code>format-patch</code>. Whether you a
133        submitting code changes or you are reviewing code changes, it all happens in
134        code. Both contributor and owner are simply creating new commits and generating
135        patches on top of each other. This obviates the need to have a web viewer where
136        the reviewing can "comment" on a line of code block. There's no need, apply the
137        contributor's patches, write comments or code changes, generate a new patch,
138        send the patch to the git server as a "review." This flow also works the exact
139        same if two users are collaborating on a set of changes.
140      </p>
141
142      <p>
143        This also solves the problem of sending multiple patchsets for the same code
144        change. There's a single, central Patch Request where all changes and
145        collaboration happens.
146      </p>
147
148      <p>
149        We could figure out a way to leverage <code>git notes</code> for reviews / comments, but
150        honestly, that solution feels brutal and outside the comfort level of most git
151        users. Just send reviews as code and write comments in the programming language
152        you are using. It's the job of the contributor to "address" those comments and
153        then remove them in subsequent patches. This is the forcing function to address
154        all comments: the patch won't be merged if there are comment unaddressed in
155        code; they cannot be ignored or else they will be upstreamed erroneously.
156      </p>
157    </div>
158  </details>
159
160  <details>
161    <summary>How do Patch Requests work?</summary>
162      <div>
163        Patch requests (PR) are the simplest way to submit, review, and accept changes to your git repository.
164        Here's how it works:
165      </div>
166
167      <ol>
168        <li>External contributor clones repo (<code>git-clone</code>)</li>
169        <li>External contributor makes a code change (<code>git-add</code> & <code>git-commit</code>)</li>
170        <li>External contributor generates patches (<code>git-format-patch</code>)</li>
171        <li>External contributor submits a PR to SSH server</li>
172        <li>Owner receives RSS notification that there's a new PR</li>
173        <li>Owner applies patches locally (<code>git-am</code>) from SSH server</li>
174        <li>Owner makes suggestions in code! (<code>git-add</code> & <code>git-commit</code>)</li>
175        <li>Owner submits review by piping patch to SSH server (<code>git-format-patch</code>)</li>
176        <li>External contributor receives RSS notification of the PR review</li>
177        <li>External contributor re-applies patches (<code>git-am</code>)</li>
178        <li>External contributor reviews and removes comments in code!</li>
179        <li>External contributor submits another patch (<code>git-format-patch</code>)</li>
180        <li>Owner applies patches locally (<code>git-am</code>)</li>
181        <li>Owner marks PR as accepted and pushes code to main (<code>git-push</code>)</li>
182      </ol>
183
184      <div>Example commands</div>
185
186      <pre># Owner hosts repo `test.git` using github
187
188# Contributor clones repo
189git clone git@github.com:picosh/test.git
190
191# Contributor wants to make a change
192# Contributor makes changes via commits
193git add -A && git commit -m "fix: some bugs"
194
195# Contributor runs:
196git format-patch origin/main --stdout | ssh {{.MetaData.URL}} pr create test
197# > Patch Request has been created (ID: 1)
198
199# Owner can checkout patch:
200ssh {{.MetaData.URL}} pr print 1 | git am -3
201
202# Owner can comment (IN CODE), commit, then send another format-patch
203# on top of the PR:
204git format-patch origin/main --stdout | ssh {{.MetaData.URL}} pr add --review 1
205# UI clearly marks patch as a review
206
207# Contributor can checkout reviews
208ssh {{.MetaData.URL}} print pr-1 | git am -3
209
210# Owner can reject a pr:
211ssh {{.MetaData.URL}} pr close 1
212
213# Owner can accept a pr:
214ssh {{.MetaData.URL}} pr accept 1
215
216# Owner can prep PR for upstream:
217git rebase -i origin/main
218
219# Then push to upstream
220git push origin main
221
222# Done!
223</pre>
224  </details>
225
226  <details>
227    <summary>First time user?</summary>
228
229    <div>
230      Using this service for the first time?  Creating a patch request is simple:
231    </div>
232
233    <pre>git format-patch main --stdout | ssh {{.MetaData.URL}} pr create {repo}</pre>
234
235    <div>When running that command we will automatically create a user and a repo if one doesn't exist.</div>
236
237    <div>Want to submit a v2 of the patch request?</div>
238
239    <pre>git format-patch main --stdout | ssh {{.MetaData.URL}} pr add {prID}</pre>
240  </details>
241</header>
242
243<main>
244  <div>
245    filter
246    <a href="/">open</a> <code>{{.NumOpen}}</code>
247    &middot;
248    <a href="/?status=accepted">accepted</a> <code>{{.NumAccepted}}</code>
249    &middot;
250    <a href="/?status=closed">closed</a> <code>{{.NumClosed}}</code>
251  </div>
252  {{template "pr-table" .Prs}}
253</main>
254
255<footer class="mt">
256  <a href="/rss">rss</a>
257</footer>
258{{end}}