Git Workflows

Slides by Matthew Gray


https://matthew.nz
https://github.com/heymatthew

Special thanks to


Jef Vratney: Opportunity setting and guidance
Section 6: Compatibility advice and feedback

## Rough Timeline 9:30 - 12 - Setup and Overview - Personal Workflows 12:30 - 5 - Team Workflows - Release workflows Tomorrow Onsite for Help
## Why Git? * Flexability * Tooling * Speed * Momentum
## SVN Comparison * Decentralised, every clone is an entire copy * Branches contain entire history * Git tracks content * SVN tracks deltas
Git thinks in Snapshots, Not Differences > The major difference between Git and any other VCS (Subversion and friends included) > is the way Git thinks about its data. src: git-scm.com, ch 1.3 Getting Started: Git Basics

SVN commits

svn branching strategy

Git commits

git history
## Setup Git https://git-scm.com/download/win Tortose Git https://tortoisegit.org/ Git pad https://github.com/github/GitPad Yes! "...use gitpad as your default editor for git"
## Configure ```bash git config --global user.name "Matthew B. Gray" git config --global user.email "code@matthew.nz" git config --list git config --list --show-origin ``` --global stores your global settings Default puts settings into your repo
## Docs For help on git config, git has a help page ```bash git help $command git help config ```
## Exercise - Using `git help`, pull up pages on `git config` - Use help to find out how to unset `user.email` - List configurations with `--list --show-origin` - Open your .gitconfig file, how are fields saved?
## Personal Workflows 1 - Immediate productivity: Build and commit to repos - Anatomy of a commit: Fields and behaviours - Staging files: Steps leading to a commit - All about Branches: Create and delete branches
## Immediate productivity Create a git repo ```bash mkdir test cd test git init git status ```
## Immediate productivity Adding and resetting files in the staging area ```bash git status git add myfile.txt git reset myfile.txt git status ``` Whole directories too ```bash git add . git add myfolder git reset myfolder ```
## Immediate productivity Commit your staged changes ```bash git commit ``` Save with an empty file and git will abort
## Immediate productivity Delete a git repo ```bash rmdir /S .git git status ```
## Exercise time! Create a repo and commit to it ``` mkdir test_repo cd test_repo git init git add git commit ``` HOT TIP Set verbose on commit and commit another file ``` git config --global commit.verbose true git add git commit ``` What shows up in the commit message?
## Exercise time 2! Lookup help pages on git commit ``` git help ``` Based on help hints, remove a file and commit
## Anatomy of a commit Commits are - Immutable - SHA1 Hashed - Built on - File contents - Timestamp - Author - Parent or parents

One more time in context

git history
## Anatomy of a commit Commits can be seen with ``` git show git log ``` And referenced by - Hash - Branch - HEAD - Relative to other commits
## Anatomy of a commit Looking at a commit ``` git show ``` ``` commit d5401a6399234ddfe7bc986a0fe120359505aad5 (HEAD -> master) Author: Matthew B. Gray <code@matthew.nz> Date: Mon Oct 13 13:01:15 2017 -0800 All of the things diff --git a/bar.txt b/bar.txt new file mode 100644 index 0000000..7bcc722 --- /dev/null +++ b/bar.txt @@ -0,0 +1 @@ +the bar has a bar tender \ No newline at end of file diff --git a/fu.txt b/fu.txt new file mode 100644 index 0000000..f2e93e3 --- /dev/null +++ b/fu.txt @@ -0,0 +1 @@ +a fu enters the bar \ No newline at end of file ```
## Anatomy of a commit Raw commit ``` git show --raw ``` ``` commit d5401a6399234ddfe7bc986a0fe120359505aad5 Author: Matthew B. Gray <code@matthew.nz> Date: Mon Oct 13 13:01:15 2017 -0800 All of the things :000000 100644 0000000... 7bcc722... A bar.txt :000000 100644 0000000... f2e93e3... A fu.txt :000000 100644 0000000... bc979a6... A lib/flubber.txt :000000 100644 0000000... e69de29... A lib/flubber2.txt ```
## Anatomy of a commit Lets talk about immutability! * Nodes represent diffs with respect to each other * Nodes represent entire file tree on their own Point to nodes with... * **branch** - a name to track your work * **tag** - a label, it doesn't move * **commit hash** - complete history and set of files * **HEAD** - your current checkout * **$ref~** - above are $refs, chain ^ or ~ to go up a branch
## Exercise time! What is the difference between ``` git show git show HEAD git show master ``` You can get at relative commits by appending ~, try it ``` git show HEAD~ git show master~ ``` Find the parent commit with `git log` Make log show all but the last commit with `HEAD~` What does `git log --stat` do?
## Exercise time 2! Take the hash from ``` git show ``` Pass it to `git log` and `git show`
## Staging files ![staged, unstages, commits, and stashes](images/staging-area.png) credit: http://ndpsoftware.com/git-cheatsheet.html

Typical git workflow

personal workflow
## Staging files Lets talk about Test Driven Development 1. write test, RED 2. git add 3. write code, GREEN 4. git add, git commit 5. REFACTOR 6. git commit
## Staging files Audit your work interactively with your staging area ``` git add -p ``` Unstaging files can be done in parts too ``` git reset -p ```
## Staging files If you want to reset to a GREEN state ``` git checkout . git checkout myfile.txt git status ``` This doesn't affect added files ``` git add -p git checkout . git status ```
## Exercise time! 1. Make changes to 2 files already committed 2. Change 1 of them 3. Checkout everything ``` git checkout . ``` What changed? How could you get rid of your staged changes?
## All about Branches Master is the default branch in git Create branches with ``` git branch mybranch ``` Checkout branches with ``` git checkout mybranch ```
## All about branches ![](images/branches-typical.png) ![](images/branches-multi.png) Typical branching, images from http://git-scm.com
## All about branches ![](images/branches-perspective.png) How branches can be used for workflow
## All about Branches List branches with ``` git branch ``` ...list all branches with -a ``` git branch -a ```
## All about Branches Delete branches with ``` git branch -d mybranch ``` It complains if this is the only copy, such as - When another branch isn't referencing it - When it's not shared / pushed ...use -D to go ahead deleting the only copy
## Exercise time! Create and checkout a branch ``` git branch git checkout git status ``` Commit to the branch, copy the hash down Delete the branch ``` git branch -d ``` Can you `show` the hash from the deleted branch?
## Exercise time 2! Run ``` git gc ``` Can you still see the hash? Why? ``` git help gc ```
## Personal Workflows 2 - Searching and Testing: How to find missing stuff - Reset the world: Moving branches around arbitrarily - Diffs and Commits: Tips for review - Merge stuff, break stuff: Integrate your work
## Searching and Testing Understanding why something is in it's current form 1. Avoids regressing your codebase 2. Lets you know if code is under warantee 3. Lets you seek out a human when stuck
## Searching and Testing * Log - Show commit logs * Pickaxe - Search commit diffs for changes * Blame - revision and author for lines of a file * Headless Checkout - Options for work without tracking
## Searching and Testing Regular log ``` git log ``` Show me contents of patches ``` git log -p ``` Show me a summary of patches ``` git log --stat ``` Find changes associated with text in a commit message: ``` git log --grep '$search_term' git log --grep 'bug#1234' ```
## Searching and Testing ``` git log -S 'some distinct line that changed' git log --stat -S '$racebook_uri_result' ``` * Find all the changes to a line in your entire history * The pickaxe can help find code that’s been deleted or moved * Typically helps you with questions like - What happened to myFabuliousFunc()? - When did $mySillyVariable get referenced everywhere? * Works with log, diff, and format-patch
## Searching and Testing ``` git blame $filename git show $hash ``` Ignore whitespace ``` git blame -w $filename ``` Copy paste detection in a file, and across the repo ``` git blame -C $filename ```
## Headless Checkout Options for work without tracking If you're not on a branch, git puts you in a headless state ``` git checkout $tag git checkout $commit git checkout $hash ```
## Headless Checkout Trying something like this... ``` git checkout HEAD~ ``` Results in something like this... ``` You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at f4b37d8... CSS: Return values should be numbers ``` Which is git being melodramatic
## Exercise time! 1. Clone jquery repo 2. Use git log to find fixes to issue #14756 3. Use pickaxe to find commits that added or removed 'parseFloat' 4. Try git blame on the README.md file 5. With this info, when was the Contribution Guides section last modified? ``` git clone https://github.com/jquery/jquery git log --grep 'asdf' git log -S 'asdf' git blame $file git show $hash ```
## Reset the world Moving branches around arbitrarily ``` git checkout $branch git reset $ref ``` Same command for repo and staging area ``` git reset $ref git reset myfile.txt git reset myfolder ```
## Reset the world Backup if you're not sure with a branch ``` git branch backup git reset $ref ``` And if everything goes pear shaped ``` git reflog ```
``` # Save my ass! What have I had 'checked out' recently? git reflog git reset REF ```
## Exercise time! In jquery, from master, create a backup branch Run ``` git reset HEAD~ ``` What happened? What happens when you use `--hard`? Reset to your backup branch Run ```git reflog``` How else could you put master back?
## Diffs and Commits Review modifications to a file with ``` git diff ``` Review modifications after git add with ``` git diff --staged ```
## Diffs and Commits You can specify ranges with dots to both log and diff .. (two dots) * differences between two revisions * optionally limiting comparison to files/folders ... (three dots) * differences between the *last common ancestor* * also optionally limiting comparison to files/folders
## Diffs and Commits Differences between your branch and master ``` git diff master..HEAD ``` Differences added since you branched from master ``` git diff master...HEAD ```
## Diffs and Commits Similarly, differences between your branch and master ``` git log master..HEAD ``` Differences added since you branched from master ``` git log master...HEAD ```
## Exercise time! In your jquery checkout, diff between 1.1 and 1.2 ``` git diff 1.1...1.2 ``` What happens when you pass `--stat`? Try with 3 and 2 dots, are they different? Why? What happens when you run ``` git diff 1.1... ``` Repeat with `git log`
## Merge stuff, break stuff Check out the branch you want your stuff in, and merge with git merge ``` git checkout master git merge mybranch ``` Or pull other people's stuff into your branch ``` git checkout mybranch git merge master git merge anotherbranch ```
## Merge stuff, break stuff Source control is about social coding and interactions. A conflict should be a conversation. When you see ``` CONFLICT (content): Merge conflict in jabberwocky.html Automatic merge failed; fix conflicts and then commit the result. ``` Get info with ``` git status git diff git blame $file ```
## Merge stuff, break stuff Markers are usually HEAD and a $ref, where 1. HEAD is work of yours 2. $ref is the thing you're in conflict with Lets try simulate a merge conflict and get here... ``` git status git diff git blame $file ```
## Exercise time! In your jquery repo, checkout master and merge in `origin/killphp` ``` git merge origin/killphp git status git diff git blame $file ``` What files are in conflict? What authors need to talk? How many commits is origin/killphp from master? ``` git log ref1...ref2 ```
## Team Workflows 1 - Remotes: Lets talk about branches on origin - Share your work: Team Foundation Server - Rebase vs Merge: An argument for clean history - Git ignore: Don't bother tracking the superfluous
## Remotes Git just tracks content. All history is stored in .git, and sharing is just syncing this directory. Location can be... 1. ssh://[user@]host.xz[:port]/path/to/repo.git/ 1. git://host.xz[:port]/path/to/repo.git/ 1. http[s]://host.xz[:port]/path/to/repo.git/ 1. ftp[s]://host.xz[:port]/path/to/repo.git/ 1. rsync://host.xz/path/to/repo.git/
## Remotes Clone coppies a remote ``` git clone $location ``` ...creates a remote called "origin" To modify remotes... ``` git remote add $alias $location git remote rm $alias $location ```
## Exercise Time! Share your terrible poetry with your neighbour. This is for unauthenticated read-only access. ``` git config --global alias.serve ^ "daemon --verbose --export-all " ^ "--base-path=.git --reuseaddr --strict-paths .git/" ``` Run it in the base of any git checkout ``` git clone https://github.com/heymatthew/jabberwocky cd jabberwocky git serve ```
## Exercise Time 2! When this is setup, exchange details and try clone... ``` ipconfig git clone git://$host_ip/ jabberwocky-fred cd jabberwocky-fred ``` or add a remote... ``` git remote add git://$host_ip/ fred git fetch --all git log fred/master git diff fred/master master ```
lion king, throwing simba as a metaphor for sharing
## Share your work Lets try again with Team Foundation Server 1. Lets get everyone on this project https://fregroup.visualstudio.com/_git/AO%20Training 2. Clone it 3. Create a branch 4. Make a change, commit and push 5. Create a merge request 6. Leave a comment on the merge request
## Rebase vs Merge An argument for clean history `pull` can create merges implicitly
## Rebase vs Merge `pull --rebase` will use a rebase strategy to get your work copied over
## Rebase vs Merge And you can rewrite your history with `--interactive`
## Rebase vs Merge Set up git pull to rebase by default ``` git config --global branch.rebase true git config --global branch.autosetuprebase always ``` If you're not ready, you can pull the plug ``` git rebase --abort git merge --abort ```
## Rebase vs Merge Demo with `exec` on `heymatthew/status-gem.git`
## Exercise time! 1. From the jabberwocky repo 3. Delete script tag on line 28 4. Commit, call it something like FIXUP 5. Interactive rebase onto the tag v1 6. Squash your fix with 'Oops' and give it a message. ``` cd jabberwocky git add jabberwocky.html git commit git rebase v1 --interactive git log -p ``` ...it's as if the error was never there.
## Exercise 2 ``` git reset origin/master --hard ``` When would you choose to rebase?
## Git ignore Sometimes your IDE creates cache files Or your project has compiled resources on disk You can ignore these by creating `.gitignore` and checking it in
## Git ignore Lets open up the jquery `.gitignore` and talk over it
## Team Workflows 2 - Git SVN bridge: Local git, remote svn - Backups and Mirrors: Keep a copy - Changelogs: Share what happened - Databases: Logic vs State, State vs Data
## Git SVN bridge Local git, remote svn ``` git svn clone SVN_REPO_ROOT_URL [DEST_FOLDER_PATH] \ -T TRUNK_REPO_PATH \ -t TAGS_REPO_PATH \ -b BRANCHES_REPO_PATH ``` If you use standard svn layouts ``` svn clone -s SVN_REPO_ROOT_URL [DEST_FOLDER_PATH] ```
## Git SVN bridge ``` git svn clone -s https://github.com/heymatthew/jabberwocky jabberwocky-svn ``` ``` Initialized empty Git repository in /Users/mbgray/code/training/freightways/FileZilla3/.git/ W: +empty_dir: brances W: +empty_dir: tags W: +empty_dir: trunk r1 = 1cecc4b6b4a907327130da10ca56f049b7e98101 (refs/remotes/git-svn) A trunk/src/interface/Mainfrm.h A trunk/src/interface/LocalTreeView.cpp A trunk/src/interface/RemoteTreeView.cpp A trunk/src/interface/LocalTreeView.h ... ```
## Backups and Mirrors There are lots of ways to keep backups 1. Copy paste a directory 2. Clone locally 3. Clone a bare repository 4. Have a mirror repository sync periodically
## Backups and Mirrors Copy paste a directory Exact copy including checkout and permissions, can push and pull
## Backups and Mirrors Clone locally ``` git clone source destination ``` * Creates a new directory called `destination` * Sets `origin` to your repository
## Backups and Mirrors Clone a bare repository ``` git clone --bare source ``` Creates source.git
## Backups and Mirrors Have a mirror repository sync periodically ``` git clone --bare --mirror source ``` * Creates source.git * Copies remotes (upstream, origin) * Doesn't track their locations
## Exercise Make a mirror of the AO Training project https://fregroup.visualstudio.com/_git/AO%20Training ``` git clone --mirror --bare $repo $alias ``` View the directory, where are the files? You can run `git log`, what's a command you can't run?
## Changelogs So you can get a log between points ``` git log v1..v2 ``` ``` git log --oneline v1..v2 ```
## Changelogs Or you can go completely custom with --pretty=format:"CUSTOM" ``` git log v1..v2 \ --pretty=format:"<li> <a href=\"https://github.com/heymatthew/jabberwocky/commit/%H\"> view commit </a> </li>" ```
## Exercise Bring up the help pages on git log ``` git help log ``` And skip to the section titled "PRETTY FORMATS" Can you construct a changelog which prints the commit message and author?
## Databases Logic vs State, State vs Data Git doesn't care about non-filesystem entities Database patching is important to get right ---------------------------------------- You can either use convetion, or process to manage this 1. Numeric patch - set 1+$current in your filename 2. Automatic numeric patch - use a merge script on release
## Release Workflows 1 - Working with Upstream: .Net core - CI/CD Tricks: Branch convention and Hooks - Sign commits: Using a trust model for git - Bisect: Find when a big was introduced
## Working with Upstream .Net core is on github To track bugs, raise issues On github, everyone forks To push patches upstream, create pull requests
## Working with Upstream What's the difference between 1. a Merge Request, 2. and a Pull Request?
## Working with Upstream Lets do this together! 1. Sign up for an account with github 2. Go to https://github.com/heymatthew/core 3. "Fork" my rep 4. Clone your fork using the "Clone or Download" button 5. Change README.md 6. Commit and Push Changes 7. Click "Create pull request"
## CI/CD: Branch and Tag conventions Rule of thumb if you want it automated, pick something that'll be picked up by a regular expression. For branches, you could merge request master into a branch named production or staging
## CI/CD: Branch and Tag conventions For tags, semver is great http://semver.org/ Format: MAJOR.MINOR.PATCH, e.g. 1.0.0 * MAJOR version when you make incompatible API changes, * MINOR version when you add functionality in a backwards-compatible manner, and * PATCH version when you make backwards-compatible bug fixes.
Lets discuss use cases
## CI/CD Tricks: Hooks ``` git help githooks ``` Checkout the templates ``` ls .git/hooks ``` Exit with non-zero to abort the operation
## CI/CD Tricks: Hooks You can nick other people's scripts, e.g. linting https://github.com/okonet/lint-staged Or you could use http push to trigger a build
## Sign commits A trust model for git We're going to need to install one more thing...
## Exercise From an admin console... ``` choco install gpg4win ``` Open Kleopatra, File > New Key Pair... Choose "make a backup of your new key pair" Put it on your desktop "backup.gpg"
fingerprint of generated key
## Exercise 2 From Kleopatera, copy key fingerprint to clipboard Configure git ``` git config --global ... user.signingkey F211ED820E36E40355572F883041C906DC7C27F0 commit.pgpsign true gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe" ``` Commit and sign ``` git add myfile.txt git commit -S ``` Verify ``` git log --show-signature ```
Same thing but with tags ``` # Create and sign new tag git tag -s v1.5.0 -m "Release 1.5.0 reviewed by the team" # Verify git tag -v v1.5.0 ```
## Bisect Find when a big was introduced. To start bisecting... ``` git bisect start $bad_commit $good_commit ``` 'Unwanted' behaviour? ``` git bisect bad ``` 'Wanted' behaviour? ``` git bisect good ``` Tidy up ``` git bisect reset ```
## Bisect Sometimes it's guess work ``` git bisect skip ``` Sometimes it's complicated to get back to your guess spot ``` git bisect log > bisect.log git bisect reply bisect.log ```
## Exercise 1. Clone the jabberwocky repo, pull tags 2. Checkout the changes made with git log 3. Use bisect with v1 and v2 tags as good/bad commits 4. Find the commit that created the alert ``` git clone https://github.com/heymatthew/jabberwocky git fetch --tags git log -p git log --stat git bisect start $bad $good ``` Mark wanted or unwanted behaviour with ``` git bisect good git bisect bad ```
## Exercise 2 Try the following command ``` git checkout origin/master db ``` What was it's effect? When would you choose to do this? How would you use this to get at patches from where you jumped? ``` git bisect log ```
## Tidyup ``` git bisect reset ```
## Release Workflows 2 - Revert: Winding back a change - Linear Releases: Simple branching - Merge Forward Releases: Release branches - Microkernel architecture: Break up a Monolyth
## Revert Winding back a change Basic form ``` git revert $ref ``` Combine several together ``` git revert $ref1 -n git revert $ref2 -n git commit ```
## Revert Reverting a merge commit requires parent ``` git show $merge_commit commit f830ef9b372222287b0df82dd92a41feccd7eaf7 (HEAD -> master) Merge: 482a1c3 345969e Author: Matthew B. Gray <code@matthew.nz> ... ``` ``` git revert -m $parent_number REF git revert -m 1 f830ef9b372222287b0df82dd92a41feccd7eaf7 ``` "Reverting a merge commit declares that you will never want the tree changes brought in by the merge."
## Revert Given the heat is off, to work again on your change you must 1. Branch from the merge revert... 2. Revert that revert 3. Fix the issue 4. Merge back when ready
## Revert Caviet Lets talk about fast forward merges Expicitly avoid them with --no-ff If your team wants to work like this every time... ``` git config branch.master.mergeoptions "--no-ff" ``` Shows up in config as ``` [branch "master"] mergeoptions = --no-ff ```
## Revert Knowing what you know now * When is it appropriate to revert? * When is it appropriate to rebase squash/drop?
## Linear Releases Simple branching - Work on master - Branch at release time - Cherry pick or merge to master on hotfixes
![Hotfix](images/basic-branching-1.png) ![Hotfix merge forward](images/basic-branching-2.png)
## Linear Releases with Feature Branches Remixed SVN-esk workflow - Branch from production - Work till test complete - Merge to master - Branch at release time - Merge to release branch for hotfixes - Merge release branch into master on hotfix release
## Merge Forward Releases * Branch per release * Commit to your release * Tag when ready to push to an env * Hotfixes end up on the relelease branch * Merge forward * Can work on upstream while downstream is still going
![](images/release-branches.png)
## Microkernel architecture Breaking up a Monolyth * Coordination of components moved from branches to package management * Libraries can be worked on simultaniously * Release scripts become a bit trickier * Can have different policies per repo * Good for scale
![](images/basic-branching-2.png) ![](images/release-branches.png)
## Microkernel Architecture Migration steps - Either break your repository up and use package dependencies to manage code ``` git mv $source $destination ``` - Or break your repository up using folders - Manage your dependencies with docker tags ``` git tag git filter-branch OR git mv $file $folder ```
# Each has it's costs! Choose the one that works best for your usecase

Further Reading

Attribution

## Course Information Slides can be found on [github.com/heymatthew/git-workflow-slides]( https://github.com/heymatthew/git-workflow-slides ) ![qr for quick access](images/github-qr-link.png)
Feedback? Corrections? Questions? Please leave an issue against the course github page. Hope you enjoyed the course! ![Boom](images/big-end.gif)