Replies: 1 comment 1 reply
-
Maintaining Git Branches to Represent a PatchsetThe goal is to maintain a set of branches where each branch represents a cohesive feature or patch. This allows us to:
Workflow Overview
Git Workflow Steps1. Initial Setup
2. Ongoing DevelopmentWhen adding a new patch ( 3. Periodic Upstream Updates (Expanded Example)You will periodically want to rebase your patchset branches on the latest
4. Merging Back to Preserve Review HistoryAfter rebasing the patches, you’ll want to merge the rebased branches back into the original patch branches (
By merging instead of rebasing directly on the patch branches, you avoid rewriting history, which keeps the pull request history intact and GitHub’s review system functional. Visualization with MermaidJSHere’s how the branch structure looks, including the steps for gitGraph
branch upstream/master
commit id: "upstream/master"
branch patch0
commit id: "patch0"
branch patch1
commit id: "patch1"
branch patch2
commit id: "patch2"
branch patchN
commit id: "patchN"
checkout upstream/master
commit id: "upstream/master (new)" tag: "new upstream commit"
branch rebased-patch0
commit id: "rebased-patch0"
branch rebased-patch1
commit id: "rebased-patch1"
branch rebased-patch2
commit id: "rebased-patch2"
branch rebased-patchN
commit id: "rebased-patchN"
checkout patch0
merge rebased-patch0
checkout patch1
merge rebased-patch1
checkout patch2
merge rebased-patch2
checkout patchN
merge rebased-patchN
ConclusionThis approach allows you to maintain your patchset efficiently while periodically updating it from upstream. By rebasing onto intermediate branches and merging back into the original branches, you:
Merge + Patch branches AlternativegitGraph
branch upstream/master
commit id: "upstream/master"
branch patch0
commit id: "patch0"
branch patch1
commit id: "patch1"
branch patch2
commit id: "patch2"
branch patchN
commit id: "patchN"
checkout upstream/master
commit id: "upstream/master (new)" tag: "new upstream commit"
checkout patch0
merge upstream/master
checkout patch1
merge patch0
checkout patch2
merge patch1
checkout patchN
merge patch2
Note both strategies refer to the patchset preserving merge/rebase workflows, not the previous comment
|
Beta Was this translation helpful? Give feedback.
-
Tip
TL;DR A merge strategy is my recommended approach because it (a) preserves history in full; while (b) encoding merge-conflict resolution in the tree such that it can be reviewed with a single command. A rebase strategy loses history and makes it difficult to review conflict resolution because it (a) isn't encoded in the tree; and (b) requires multiple steps.
Background
This document opens a discussion around possible git strategies for updating
libevm
to incorporate the latest upstreamgeth
commits. It analyses a merge strategy and a rebase strategy.Requirements
a.
libevm
andgeth
commits in their original state; andb. Modifications due to resolution of merge conflicts.
Branches and tags
master
tracks theethereum/go-ethereum
equivalent branch;libevm
is our default branch.libevm-base
is a floating tag that we update to point to the latest geth version incorporated inlibevm
;libevm-base
and in the future it will mean all commits up to the last one included in an update.sync
refers to some intermediate branch being used to bring commits frommaster
intolibevm
via a PR for review.Caution
When performing a sync of
master
, triple check the branch. GitHub displays the option for ourlibevm
branch as well and this would be a destructive action that loses all history!Current state
We branched off
geth
at some historical version, tagged aslibevm-base
. Both we and they have since committed to our respective branches and their latest release isv1.14.11
(the specific value is irrelevant). We wish to incorporate this release intolibevm
.Merge strategy
A rough outline of the (untested)
git
recipe:$ git checkout libevm $ git checkout -b sync $ git merge master # Resolve conflicts $ git push --set-upstream origin sync
Then open a PR to merge
sync
intolibevm
.Note
All "PR" tags should be interpreted as labels for easy reference, not as literal tags.
Reviewing a merge strategy
M
carries the merge-conflict resolutions.a. These can be inspected with
git show
, which usesgit diff-tree -cc
under the hood for merge commits.b. A UI for inspection +/- code review is an open question.
Post review
Incorporating commit
M
intolibevm
in a non-destructive fashion is important. A squash commit MUST NOT be used.Merge + fast-forward (preferred)
Note that since development is paused on
libevm
, the branch can be fast-forwarded toM
.Whether GitHub's PR merging via merge commit will perform a fast-forward is an open question.If this were to be performed locally, the recipe would be as follows, whichapparentlyresults in automatic PR merging (GitHub documentation) while respecting branch-protection rules:That would result in
M
simply being the next commit in thelibevm
branch before further development recommences withLn+1
:Although this doesn't result in a linear history, that is actually the true history of concurrent development on the
master
andlibevm
branches.MergeM
+ merge-commitMC
This is now redundant as merge + fast-forward has been demonstrated to work.
If pushing after local fast-forward doesn't work as expected, a regular PR merge can be performed, but it would result in an effectively emptyMC
commit being added. This is ugly but still preserves all history.Reverting a merge strategy
If the incorporated changes need to be reverted, either the
M
orMC
commits are simple targets forgit revert
, assuming no further development on the branch.Rebase strategy
A completely different approach is to rebase all of the
L*
commits to instead branch off theGm
commit that would have otherwise been merged. Each of theLi
commits now has a respectiveRi
commit that introduces the same functionality/fix but with conflicts resolved on a per-commit basis.Opening a PR to merge
sync
(Rn
) intolibevm
:Reviewing a rebase strategy
a.
n
comparisons viagit diff Li Ri
would have to be performed.i. This requires external knowledge of the PR pairings, not captured in the commit tree.
b. A UI for inspection +/- code review is an open question.
Post review
Rebase + merge is ambiguous
Even if there were no conflicts when merging
sync
intolibevm
, future commits would have an ambiguous history. If there were conflicts and they had to be resolved, this would indicate a broken process because resolution was already performed during the rebase.Rebase + force push loses history
The
libevm
branch could be forced to point toRn
.There are a lot of resources online explaining the problems with force pushes. Using the commit tree alone, history regarding the original development of the
L*
branches will be lost, which doesn't meet our requirements.Reverting a rebase strategy
Reverting a merged rebase can be done with
git revert M
. Reverting a force-pushed rebase requires knowledge of the oldlibevm
branch, which would itself be force-pushed.Beta Was this translation helpful? Give feedback.
All reactions