<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Never Commit to Master: An Introduction to Git Flow | DrupalCamp London 2014</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link rel="stylesheet" href="css/reveal.min.css"> <link rel="stylesheet" href="css/theme/night.css" id="theme"> <link rel="stylesheet" href="lib/css/zenburn.css"> <link rel="stylesheet" href="lib/css/pdf.css"> <script> document.write( '<link rel="stylesheet" href="css/print/' + ( window.location.search.match( /print-pdf/gi ) ? 'pdf' : 'paper' ) + '.css" type="text/css" media="print">' ); </script> <!--[if lt IE 9]> <script src="lib/js/html5shiv.js"></script> <![endif]--> </head> <body> <div class="reveal"> <div class="slides"> <section> <h1>Never Commit to Master</h1> <h3>An Introduction to Git Flow</h3> <p> <small>By <a href="http://www.oliverdavies.co.uk">Oliver Davies</a> / <a href="http://twitter.com/opdavies">@opdavies</a></small> </p> <aside class="notes"> Drupal for 8 years, Git since for 4 years, Git Flow for a year </aside> </section> <section> <h2>Me</h2> <ul> <li class="fragment">Oliver Davies</li> <li class="fragment">Developer and Systems Administrator</li> <li class="fragment"><a href="http://twitter.com/opdavies">@opdavies</a> (Twitter, D.O., IRC)</li> <li class="fragment"><a href="http://dgo.to/@opdavies">http://dgo.to/@opdavies</a></li> <li class="fragment">PHPer and Drupalista since 2007 (full time since 2010)</li> <li class="fragment">Git user since 2010</li> <li class="fragment">Git Flow user since 2013</li> </ul> </section> <section> <img src="images/precedent-white.png" style="background: none; border: none;"> </section> <section> <h2>Git Flow is:</h2> <blockquote>"A collection of Git extensions to provide high-level repository operations for <a href="http://nvie.com/git-model">Vincent Driessen's branching model</a>."</blockquote> <p><small>From <a href="https://github.com/nvie/gitflow">https://github.com/nvie/gitflow</a></small></p> <aside class="notes"> * Read quote from slide * Provides a wrapper around standard Git functionality Everything in Git Flow can be done manually </aside> </section> <section> <h2>The Branching Model</h2> <div class="image"> <img src="images/nvie.png" style="border: none;"> </div> <p><small>From <a href=""></a></small></p> <aside class="notes"> Separates production code from development code<br> Encourages feature driven development<br> Allows for releasing and tagging of code<br> Multiple opportunities for QA and review </aside> </section> <section> <h2>Branches</h2> <ul> <li class="fragment"><code>master</code>: production code</li> <li class="fragment"><code>develop</code>: development code</li> <li class="fragment"><code>feature/</code>: a specific task or ticket (multiple)</li> <li class="fragment"><code>release/</code>: temporary release branch for testing (single)</li> <li class="fragment"><code>hotfix/</code>: temporary branch for emergency fixes</li> <li class="fragment"><code>support/</code>: experimental</li> </ul> <aside class="notes"> master: stable production code, tagged at the end of each release (stable)<br> develop: stores tested and verified development code, awaiting deployment (fairly stable)<br> feature: unstable code relating to a specific task, bug or user story<br> release: <br> hotfix: Used to apply urgent fixes to production code </aside> </section> <section> <h2>Why use Git Flow?</h2> <ul> <li class="fragment">Separation of production and development code</li> <li class="fragment">Flexibility</li> <li class="fragment">Better code quality</li> <li class="fragment">Encourages collaboration</li> <li class="fragment">Encourages peer code reviews</li> </ul> <aside class="notes"> Separates code by using different branches<br> Being able to easily switch between features<br> Continuous testing of feature and release branches<br> Collaboration through sharing of standard branch names<br> Code reviews </aside> </section> <section> <h2>My rules of Git Flow</h2> <ol> <li class="fragment">Never, ever commit code directly to <code>master</code></li> <li class="fragment">Only commit stable code to <code>develop</code></li> <li class="fragment">Try not to commit directly to <code>develop</code></li> <li class="fragment">One feature branch per user story/bug</li> <li class="fragment">Commit early, commit often, push often</li> </ol> </section> <section> <h2>How do I use it?</h2> <ul> <li class="fragment">CLI <ul> <li class="fragment"><code>~ $ brew install git-flow</code></li> <li class="fragment"><code>~ $ apt-get install git-flow</code></li> <li class="fragment"><code>~ $ yum install gitflow</code></li> </ul> </li> <li class="fragment"><a href="http://www.sourcetreeapp.com/">SourceTree</a> (free, cross-platform GUI)</li> </ul> <aside class="notes"> You can install and use Git Flow on the command line.<br> There are packages for Ubuntu (apt), Red Hat and CentOS (yum) and OS X (Homebrew).<br> You can also download SourceTree from Atlassian which is cross-platform </aside> </section> <section> <h2>Need Help?</h2> <pre><code data-trim> ~ $ git flow help # Shows the standard help menu ~ $ git flow {subcommand} help # Shows the help menu for a specific subcommand </code></pre> </section> <section> <h2>Initialise Git Flow</h2> <pre><code data-trim> ~ $ git flow init </code></pre> <aside class="notes"> This can be done at any time, with existing commits </aside> </section> <section> <h2>Create your default branches</h2> <pre><code data-trim> ~ $ git flow init No branches exist yet. Base branches must be created now. Branch name for production releases: [master] Branch name for "next release" development: [develop] </code></pre> </section> <section> <h2>Configure branch prefixes</h2> <pre><code data-trim> ~ $ git flow init No branches exist yet. Base branches must be created now. Branch name for production releases: [master] Branch name for "next release" development: [develop] How to name your supporting branch prefixes? Feature branches? [feature/] Release branches? [release/] Hotfix branches? [hotfix/] Support branches? [support/] Version tag prefix? [] </code></pre> </section> <section> <h2>Tip: Automatically accept the <br>default branch names</h2> <pre><code data-trim> ~ $ git flow init -d # Accepts the default branch names. </code></pre> <aside class="notes"> <p>Adding -d accepts the default branch names.</p> </aside> </section> <section> <h2>Features</h2> <p><code>$ git flow feature</code></p> <ul> <li class="fragment"><code>list</code>: lists all features</li> <li class="fragment"><code>checkout</code>: checks out an existing feature</li> <li class="fragment"><code>start</code>: starts a new feature</li> <li class="fragment"><code>finish</code>: finishes a feature</li> <li class="fragment"><code>publish</code>: pushes a feature into a remote repo</li> <li class="fragment"><code>pull</code>: pulls a feature from a remote repo</li> </ul> <aside class="notes"> Checkout can be a full feature name or a prefix </aside> </section> <section> <h2>Start a new feature branch</h2> <pre><code data-trim> ~ $ git flow feature start {name} </code></pre> <div class="fragment"> <pre><code data-trim> ~ $ git flow feature start foo Switched to a new branch 'feature/foo' Summary of actions: - A new branch 'feature/foo' was created, based on 'develop' - You are now on branch 'feature/foo' Now, start committing on your feature. When done, use: git flow feature finish foo </code></pre> </div> <aside class="notes"> * Branches from develop </aside> </section> <section> <h2>Add and commit changes</h2> <pre><code data-trim> ~ $ drush dl admin_menu ~ $ git add sites/all/modules/contrib/admin_menu ~ $ git commit -m "Added admin_menu" </code></pre> </section> <!-- <section> <h2>Features</h2> <pre><code data-trim> * 267dd3f - (HEAD, feature/foo, develop) Added admin_menu (8 minutes ago) [Oliver Davies] * f3108db - Initial commit (9 minutes ago) [Oliver Davies] * 2581201 - (master) Initial commit (9 minutes ago) [Oliver Davies] </code></pre> </section> --> <section> <h2>Recommended: Rebase Your Feature</h2> <p>Ensure that your feature is up-to-date</p> <pre><code data-trim> ~ $ git flow feature rebase Will try to rebase 'foo'... First, rewinding head to replay your work on top of it... Applying: Added admin_menu </code></pre> <aside class="notes"> Rewinds the feature, updates from develop and re-applies the feature changes<br> Ensures that the feature code is up to date and will merge correctly. </aside> </section> <section> <h2>Finish a feature</h2> <pre><code data-trim> ~ $ git flow feature finish {name} </code></pre> <div class="fragment"> <pre><code data-trim> ~ $ git flow feature finish foo Switched to branch 'develop' Updating 5c04d5a..6487134 Fast-forward ... 31 files changed, 5051 insertions(+) Deleted branch feature/foo (was 6487134). Summary of actions: - The feature branch 'feature/foo' was merged into 'develop' - Feature branch 'feature/foo' has been removed - You are now on branch 'develop' </code></pre> </div> <p class="fragment">And repeat...</p> </section> <section> <h2>Releases</h2> <p><code>$ git flow release</code></p> <ul> <li class="fragment"><code>list</code>: lists existing releases</li> <li class="fragment"><code>start</code>: starts a new release</li> <li class="fragment"><code>finish</code>: finishes a release</li> </ul> <aside class="notes"> Creates a new version of the prod code<br> I tend to do this at the end of each sprint </aside> </section> <section> <h2>Start a new release</h2> <pre><code data-trim> ~ $ git flow release start {version} </code></pre> <div class="fragment"> <pre><code data-trim> ~ $ git flow release start 2014-03-02.0 Switched to a new branch 'release/2014-03-02.0' Summary of actions: - A new branch 'release/2014-03-02.0' was created, based on 'develop' - You are now on branch 'release/2014-03-02.0' Follow-up actions: - Bump the version number now! - Start committing last-minute fixes in preparing your release - When done, run: git flow release finish '2014-03-02.0' </code></pre> </div> <aside class="notes"> Final commits or bug fixes<br> Database export </aside> </section> <section> <h2>Finish a release</h2> <pre><code data-trim> ~ $ git flow release finish {version} </code></pre> <div class="fragment"> <pre><code data-trim> ~ $ git flow release finish 2014-03-02.0 ... Deleted branch release/2014-03-02.0 (was f2aee7d). Summary of actions: - Latest objects have been fetched from 'origin' - Release branch has been merged into 'master' - The release was tagged '2014-03-02.0' - Release branch has been back-merged into 'develop' - Release branch 'release/2014-03-02.0' has been deleted </code></pre> </div> <aside class="notes"> Merges develop into master<br> Enter a tag message<br> Tags the release with the specified tag<br> Advanced options for release tag singing with GPG </aside> </section> <section> <h2>Pushing changes remotely</h2> <pre><code data-trim> ~ $ git push --all # Push the changes to the remote branches. ~ $ git push --tags # Push the tags. </code></pre> </section> <section> <h2>Tip: Finish a release in one command</h2> <pre><code data-trim> ~ $ git flow release finish -pm {message} {version} # Specify a commit message and automatically push the changes. </code></pre> <div class="fragment"> <pre><code data-trim> ~ $ git flow release finish -pm 2014-03-02.0 2014-03-02.0 </code></pre> </div> <aside class="notes"> $ git flow release finish -pm "tag message" {branch} </aside> </section> <section> <h2>finish-sprint.sh</h2> <p><code>~ $ ./finish-sprint.sh 2014-03-02.1</code></p> <pre><code data-trim> #!/bin/bash DRUPAL_DIR="/path/to/drupal/docroot" TAG=$1 if [ -z $TAG ]; then # A tag must be specified. echo 'You must specify a tag.' fi # Go into the Drupal directory cd $DRUPAL_DIR # Start a new Git Flow release. git flow release start $TAG -F # Flush the cache. drush cc all # Export the database drush sql-dump --gzip --result-file=../db/$TAG.sql git add ../db/$TAG.sql git commit -m "Exported database for $TAG" # Finish and push the release git flow release finish -pm $TAG $TAG </code></pre> </section> <section> <h2>Hotfixes</h2> <p><code>$ git flow hotfix</code></p> <ul> <li class="fragment"><code>list</code>: list all hotfixes</li> <li class="fragment"><code>start</code>: start a hotfix</li> <li class="fragment"><code>finish</code>: finish a hotfix</li> </ul> <aside class="notes"> Applies an emergency fix to your production code<br> Branches from master (by default)<br> Merges back into master and develop </aside> </section> <section> <h2>Create a new hot fix</h2> <pre class="fragment"><code data-trim> ~ $ git flow hotfix start {version} </code></pre> <pre class="fragment"><code data-trim> ~ $ git flow hotfix start 2014-03-02.2 Switched to a new branch 'hotfix/2014-03-02.2' Summary of actions: - A new branch 'hotfix/2014-03-02.2' was created, based on 'master' - You are now on branch 'hotfix/2014-03-02.2' Follow-up actions: - Bump the version number now! - Start committing your hot fixes - When done, run: git flow hotfix finish '2014-03-02.2' </code></pre> </section> <section> <h2>Commit your fixes</h2> <pre><code data-trim> ~ $ git ci -am 'Updated .htaccess' [hotfix/2014-03-02.2 6d04738] Updated .htaccess 1 file changed, 4 insertions(+), 4 deletions(-) </code></pre> </section> <section> <h2>Finish the hot fix</h2> <pre><code data-trim> ~ $ git flow hotfix finish 2014-03-02.2 Switched to branch 'master' Merge made by the 'recursive' strategy. .htaccess | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) Switched to branch 'develop' Merge made by the 'recursive' strategy. .htaccess | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) Deleted branch hotfix/2014-03-02.2 (was 6d04738). Summary of actions: - Latest objects have been fetched from 'origin' - Hotfix branch has been merged into 'master' - The hotfix was tagged '2014-03-02.2' - Hotfix branch has been back-merged into 'develop' - Hotfix branch 'hotfix/2014-03-02.2' has been deleted </code></pre> </section> <section> <h2>Resources</h2> <ul> <li><a href="http://nvie.com/posts/a-successful-git-branching-model/">http://nvie.com/posts/a-successful-git-branching-model/</a></li> <li><a href="http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/">http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/</a></li> <li><a href="http://danielkummer.github.io/git-flow-cheatsheet/">http://danielkummer.github.io/git-flow-cheatsheet/</a></li> <li><a href="https://github.com/nvie/gitflow">https://github.com/nvie/gitflow</a></li> <li><a href="https://github.com/nvie/gitflow/wiki">https://github.com/nvie/gitflow/wiki</a></li> </ul> </section> <section> <h2>Demo</h2> </section> <section> <h2>Questions?</h2> </section> <section> <h2>Thanks</h2> <p>Feedback appreciated!</p> <ul> <li>Slides: <a href="http://www.oliverdavies.co.uk/git-flow">http://www.oliverdavies.co.uk/git-flow</a></li> <li>Session evaluation: <a href="http://2014.drupalcamplondon.co.uk/node/add/session-evaluation?nid=86">http://2014.drupalcamplondon.co.uk/node/add/session-evaluation?nid=86</a></li> </ul> </section> </div> <!-- /.slides --> </div> <!-- /.reveal --> <script src="lib/js/head.min.js"></script> <script src="js/reveal.min.js"></script> <script> // Full list of configuration options available here: // https://github.com/hakimel/reveal.js#configuration Reveal.initialize({ controls: true, progress: true, history: true, center: true, theme: Reveal.getQueryHash().theme, // available themes are in /css/theme transition: Reveal.getQueryHash().transition || 'fade', // default/cube/page/concave/zoom/linear/fade/none // Parallax scrolling // parallaxBackgroundImage: 'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg', // parallaxBackgroundSize: '2100px 900px', // Optional libraries used to extend on reveal.js dependencies: [ { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, { src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, { src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }, { src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } } ] }); </script> </body> </html>