Skip to main content

A recipe for a sane git process in Drupal's patch based workflow

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.

by Lee Rowlands /

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.

  1. Find an issue that interests me on Drupal.org.
  2. Assign the issue to myself so no-one else works on it.
  3. 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
  4. Make sure my branch is clean from the previous issue git status
  5. 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.
  6. Switch back to the main branch git checkout 8.x
  7. 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
  8. 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.
  9. Make my changes.....
  10. Add and commit my changes git add /path/to/some/file /path/to/some/other/file git commit -m "Patch 14"
  11. 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 type gitl to get a nice succinct git log output. Which outputs something like rowlands@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 following git show b104002 > ~/patches/some-cool-issue.123456.14.interdiff.txt
  12. 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
  13. Then make the patch git diff origin/8.x > ~/patches/some-cool-issue.123456.14.patch
  14. 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

  1. Make sure you're branch is clean (see points above) and then switch to the issue branch git checkout some-cool-issue-123456
  2. Fetch the remote repo git fetch origin
  3. Merge the upstream branch git merge origin/8.x and resolve and commit any conflicts
  4. 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)

  1. Create a branch for the blocking issue git checkout 8.x && git pull && git checkout -b some-issue-that-blocks-my-issue-54321
  2. Apply the patch for the fix wget http://drupal.org/files/fix-for-blocking-issue.patch && patch -p1 < fix-for-blocking-issue.patch
  3. Add and commit the fix git add some/file && git commit -m "Patch 11"
  4. Create a new branch for the dependent issue git checkout -b my-issue-that-is-blocked-12453
  5. Make my changes and interdiff/patches as above
  6. 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
  7. 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

Posted by Lee Rowlands
Senior Drupal Developer

Dated

Comments

Comment by greg.1.anderson

Dated

You might also be interested in checking out the Drush Issue Queue Commands. Many, although not all of the workflows described above can be done with the various Drush commands available in this project. You can even use the iq-submit command to submit a patch with an interdiff back to drupal.org, all in one quick operation.

Project page: http://drupal.org/project/drush_iq
Readme (with workflows): http://drupalcode.org/project/drush_iq.git/blob_plain/refs/heads/7.x-1…

Comment by Joachim

Dated

This looks broadly similar to my workflow for working on issue queue patches: one branch per issue.

One difference though: I rebase my issue branch rather than merge the mainline in, as it means that I retain a clear history on the issue branch of the successive changes.

Also, I start my branch name with the issue number and have the description after, as I just pull that from the URL and use git's autocomplete for the description bit.

One useful trick I've found is for updating your branch with someone else's patch: the situation is that the tip of my branch looks like my patch, and another contributor has uploaded patch #2 that builds on my patch #1. So ideally, I'd like one new commit on my branch with a commit message like 'Other user's patch'.

The way to do this is to make git check out the mainline's files (say, 7.x-1.x), while still keeping the branch pointer at my issue branch. Then, the patch #2 can be applied normally. Once that is done, the change git perceives is in fact an interdiff. It works like this:

// This checks out the mainline, but specifying files (in this case, the whole tree) means the branch pointer is not moved. So you are still on issue-12345.
$ git co 7.x-1.x -- .
$ patch -p1 < yadayada.patch
$ git add .
$ git commit -m 'Other user's patch'

And you now have a new commit on the tip of issue-12345, with the changes the other contributor has made.

Pagination

Add new comment

Restricted HTML

  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h2> <h3> <h4> <h5> <h6>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Not sure where to start? Try typing "hello" or "help" if you get stuck. Be nice, I'm only BETA :)