Drupal uses a patch based workflow. Changes to core and contrib projects are suggested in the form of patches attached to each project's issue queue. This is nothing new.
But this is not the norm for projects that use git for version control. Normally they use a traditional git branch/fork process - issues are worked on in branches or forks, when ready a pull request is sent, when approved the code is merged.
The git workflow has a number of advantages over the patch workflow, the most notable being there is no need to re-roll patches - you just merge HEAD instead.
So given Drupal uses the patch workflow, this article shares what I've found to be a successful recipe for maximizing the features of git in the patch workflow constraints.
This recipe has several advantages, including ease of creating interdiffs.
The guts of it
The guts of the recipe is quite simple - 1 branch per issue.
As evidenced by this git branch command - I have a lot of branches in my main (I have three) Drupal 8 code base.
rowlands@local [~/drupal/drupal]: git branch |wc -l 115
The detail
So my workflow goes like this.
- Find an issue that interests me on Drupal.org.
- Assign the issue to myself so no-one else works on it.
- Head to my main Drupal code-base in ~/drupal/drupal. I have a couple of code-bases dedicated to particular issues where re-installing would be required with every branch change. But for most issues - I can get away with not needing a full reinstall - in those cases I use my main code base.
cd ~/drupal/drupal
- Make sure my branch is clean from the previous issue
git status
- If it's not clean
- Ensure you are on a local branch (ie not 8.x or 7.x), then commit and add any modified/added files from previous issue
git add /path/to/some/file /path/to/some/other/file
git commit -m "Patch 17"
You'll note I use the comment number as my commit messages - these are local branches that aren't going to be pushed anywhere - so need for descriptive commit messages.
- Ensure you are on a local branch (ie not 8.x or 7.x), then commit and add any modified/added files from previous issue
- Switch back to the main branch
git checkout 8.x
- Switch to a new branch for the issue
git checkout -b some-cool-issue-123456
I use some descriptor text and the issue number (nid) for the branch name. I use the description first because I have git tab-completion configured and I'm more likely to remember the name than the nid. Also I end up using this branch name for my patch file names too so something reasonable for others who download the patch is helpful - If someone else has worked on the issue
- Fetch their patch first using wget (Mac users might like to use curl instead).
wget http://drupal.org/files/some-patch-1234.patch
- Apply their patch
patch -p1 < some-patch-12345.patch
I use patch instead of git apply, I find it is more robust - and able to apply dirty patches (those that don't apply cleanly) more regularly - your mileage may vary - Add and commit their patch
git add /path/to/some/file /path/to/some/other/file
git commit -m "Patch 12"
Again I use the issue comment number for the commit message.
- Fetch their patch first using wget (Mac users might like to use curl instead).
- Make my changes.....
- Add and commit my changes
git add /path/to/some/file /path/to/some/other/file
git commit -m "Patch 14"
- Then its a straight forward matter of chasing down the commit shas to make my interdiff. The commit sha is the hash you see for each commit. Thanks to Boris (@boztek) I have a nice git alias to give me a pretty git output. Add this to your ~/.bashrc file:
alias gitl='git log --graph --abbrev-commit --pretty=oneline'
. Then I can just typegitl
to get a nice succinct git log output. Which outputs something likerowlands@local [~/drupal/drupal]: gitl * b104002 Patch 14 * a7c8418 Patch 12 * fc7c38b Issue #1764474 by Berdir, chx, alexpott, pounard, msonnabaum: Make Cache interface and backends use the DIC. * 59d72a2 Issue #1919178 by vijaycs85, YesCT, sandipmkhairnar: Create configuration schemas for language module.
So to get my interdiff I use the followinggit show b104002 > ~/patches/some-cool-issue.123456.14.interdiff.txt
- So all that remains is to get the patch, because I'm on a new branch - I make sure I have the latest upstream changes so my patch applies cleanly.
git fetch origin && git merge origin/8.x
- Then make the patch
git diff origin/8.x > ~/patches/some-cool-issue.123456.14.patch
- Then upload the interdiff and patch to the issue queue and unassign myself (so others know that I'm no longer working on it).
Re-rolls
So the project (core/contrib) has moved on and you need to re-roll your patch?
Well that is easy with this recipe
- Make sure you're branch is clean (see points above) and then switch to the issue branch
git checkout some-cool-issue-123456
- Fetch the remote repo
git fetch origin
- Merge the upstream branch
git merge origin/8.x
and resolve and commit any conflicts - Get your new patch
git diff origin/8.x > ~/patches/some-cool-issue.123456.15.patch
Other cool features
One other advantage here is that 'blocking issues' can be worked around (if they have a patch). Use the approach above to create a branch for the blocking issue and commit the latest patch - then branch your dependent issue off that branch (not off 8.x)
- Create a branch for the blocking issue
git checkout 8.x && git pull && git checkout -b some-issue-that-blocks-my-issue-54321
- Apply the patch for the fix
wget http://drupal.org/files/fix-for-blocking-issue.patch && patch -p1 < fix-for-blocking-issue.patch
- Add and commit the fix
git add some/file && git commit -m "Patch 11"
- Create a new branch for the dependent issue
git checkout -b my-issue-that-is-blocked-12453
- Make my changes and interdiff/patches as above
- To provide a patch that excludes the patch from the blocking issue
git diff some-issue-that-blocks-my-issue-54321 > ~/patches/my-issue-that-is-blocked.12453.minus54321.do-not-test.patch
- When the blocking issue is committed you can rebase your branch off 8.x or you can create a new branch, cherry pick the commits and delete the old branch
Summary
So what do you think? The workflow above gives you the piece of mind that you're not inadvertently including changes from one issue in another's patch and makes it easier to do re-rolls
In my opinion it makes the patch workflow more sane and gives us some of the advantages that a git branch/fork workflow would provide