What is a Pull Request
A pull request is an information layer above git--it is a signal that a branch is ready to be merged into another branch. It comes with nice affordances:
- comments and feedback - captured as meta on the changes or the changeset
- a visible review of changes made
- a trigger for evaluative pipelines and workflows; git hooks for 'ready for merge'
- a check to determine if the two branches contain conflicts
I recently had a need to run some of these workflows locally--outside of the purview of a cloud provider that provided this metadata. Not all of this can be cleanly replicated, but, for instance, I don't need comments and feedback today.
I mostly focused on points 2 and 4; what are the git mechanics of a merge request.
A Visible Review of Changes Made #
To get the changes made, you first need to find the point of divergence. In git, this is called a merge base. Luckily, there's a command for just such a thing: git merge-base target feature. This will give you the last shared ancestor of the two branches.
From there, a diff introduces the list of changes made on the branch: git diff $(git merge-base target feature) feature. A log will show the commits made: git log $(git merge-base target feature)..feature.[1]
It's worth noting that the above command can be collapsed for diffs: git diff target...feature is synonymous with git diff $(git merge-base target feature) feature.
Conflict Management #
The above reveals changes made only on the feature branch, which necessarily hides potential conflicts. That means that a review and merge could lead to a failure due to simultaneous changes. In order to resolve this, GitHub provides a secondary check.
This check simulates a merge: git checkout target && git merge feature. This merge command merges feature into target.
There are lots of options for this command, as there are lots of ways to merge. A great deal of pixels have been spent on them. Here's a fun trick instead: git merge --no-ff --no-commit feature. This will disable fast forwards, but also disable commits. So the end result is one of two things:
- a successful but incomplete merge
- an unsuccessful incomplete merge
We can determine if it is successful by checking the result of the command: echo $?. Then we can throw away the merge: git merge --abort.
This whole thing could be a git hook.
Finally, A Merge #
Okay, so now the changes have been reviewed, and it has been validated that the branch is conflict free. It's time to merge. Merging is interesting enough as a topic that, even though it's not directly related to the pull request process itself, it deserves the tiniest attention. If for no other reason than to use the merge base reference to highlight that a merge is a three-way merge: both base and target are merged on top of the merge-base.
What Isn't Done #
This exercise exists primarily to enable some small experiments with autonomous coding agents. This approach doesn't resolve the need or ability to provide feedback, nor does it provide a clean mechanism for the agent to signal that it is ready for a review. Finally, it doesn't facilitate an agent forking a repo to have its own workspace. Perhaps I'll follow up when I've resolved those other problems.
This always trips me up;
git diff A..Bis functionally the same asgit diff A B, butgit log A Bshows all commits in A and B, wheregit log A..Bshows all commits in B not A. ↩︎
- Previous: Learning with LLMs
- Next: Vibe Coding Strategies