Next Level git

Follow along: http://eosrei.github.io/talk-git2

Source: https://github.com/eosrei/talk-git2

Press spacebar to continue.

About Us

Brad Erickson (eosrei) & Mark Ferree (mrf)

git History

A distributed version-control system created in 2005 by Linus Torvalds for Linux kernel development when no existing options provided the required features.

When asked why he called the new software, "git", British slang meaning "a rotten person", he said. "I'm an egotistical bastard, so I name all my projects after myself. First Linux, now git."

Prerequisites

Git Commands

Working knowledge of Git from the command line. You must know the following commands and their usage:

  • clone
  • checkout
  • add
  • commit
  • push
  • pull

Glossary

  • VCS - Version-Control System
  • commit
  • repo / repository
  • tag
  • branch

Useful commands: Tagging commits

Like most VCSs, Git has the ability to tag specific points in history as being important.

Tags are generally used to denote a version release of the codebase or repository.

user@server ~/example $ git tag
user@server ~/example $ git tag v1.0
user@server ~/example $ git tag
v1.0
user@server ~/example $ git show v1.0
commit 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2
Author: Example <user@example.com>
Date:   Tue Jan 19 03:14:08 2038 0000
    Initial git repository build.

Think of tags as “Commit Names”

Useful commands: Comparing differences

git diff shows the differences between commits, the stage, or your current working directory.

Often used to see what’s been changed since the last commit.

diff --git a/daemon.go b/daemon.go
index d3d57ae..f1ef55c 100644
--- a/daemon.go
+++ b/daemon.go
@@ -179,12 +180,19 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {

  logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})

+  if len(cli.LogConfig.Config) > 0 {
+    if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
+      logrus.Fatalf("Failed to set log opts: %v", err)
+    }
+  }
+
  var pfile *pidfile.PIDFile
  if cli.Pidfile != "" {
    pf, err := pidfile.New(cli.Pidfile)

Useful commands: Viewing changes in commit history

user@server $ git log -p
commit a1f3028a87741bc5218373bc9af8a8aaa562b87e (HEAD, origin/master, origin/HEAD, master)
Author: Brad Erickson <user@example.com>
Date:   Thu Oct 22 15:34:34 2015 -0700

    Add custom.css and adjust Makefile to include

diff --git a/Makefile b/Makefile
index e902fc1..76f5a99 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ index.html: index.md
        --no-highlight --variable hlss=zenburn \
+       --css=css/custom.css \
        index.md -o index.html

Useful commands: Shorter history

user@server $ git log --oneline
0c869ad Adjust pandoc template and Makefile to use highlightjs
0e92680 Add 'make publish' to update the rendered Github pages branch
413d635 Move revealjs download into Makefile
31f2cb0 Updating index.md with current gdocs

Useful commands: Whatchanged

user@server $ git whatchanged
commit a1f3028a87741bc5218373bc9af8a8aaa562b87e (HEAD, origin/master, origin/HEAD, master)
Author: Brad Erickson <user@example.com>
Date:   Thu Oct 22 15:34:34 2015 -0700

    Add custom.css and adjust Makefile to include

:100644 100644 e902fc1... 76f5a99... M  Makefile
:000000 100644 0000000... 5de960e... A  css/custom.css

Useful commands: Git Blame

Shows who wrote (or at least last edited) each line.

user@server $ git blame filename.txt
accd5f0c (Dries Buytaert      2001-03-10 11:07:52 +0000  1) <?php
^008612a (Dries Buytaert      2000-05-18 19:51:59 +0000  2)
94e30bf7 (Dries Buytaert      2004-08-21 06:42:38 +0000  3) /**
94e30bf7 (Dries Buytaert      2004-08-21 06:42:38 +0000  4)  * @file
94e30bf7 (Dries Buytaert      2004-08-21 06:42:38 +0000  5)  * The PHP page that serves all page requests on a Drupal installation.
94e30bf7 (Dries Buytaert      2004-08-21 06:42:38 +0000  6)  *
362cade1 (Dries Buytaert      2007-12-26 08:46:48 +0000  7)  * All Drupal code is released under the GNU General Public License.
f434037c (Nathan Haug         2011-10-30 21:05:57 -0700  8)  * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
94e30bf7 (Dries Buytaert      2004-08-21 06:42:38 +0000  9)  */
94e30bf7 (Dries Buytaert      2004-08-21 06:42:38 +0000 10)
5e58da00 (Nathaniel Catchpole 2014-06-26 11:47:01 +0100 11) use Drupal\Core\DrupalKernel;
5e58da00 (Nathaniel Catchpole 2014-06-26 11:47:01 +0100 12) use Symfony\Component\HttpFoundation\Request;
8a567823 (Alex Pott           2014-04-25 20:13:44 +0100 13)
95fe74d5 (catch               2015-03-11 08:31:22 +0000 14) $autoloader = require_once 'autoload.php';
5e58da00 (Nathaniel Catchpole 2014-06-26 11:47:01 +0100 15)

Useful commands: Git Stash

Store your changes and remove them from the working directory. A stash can re-applied later as needed.

user@server ~/example $ git stash
user@server ~/example $ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
user@server ~/example $ git stash apply

Useful commands: Amend

user@server ~/example $ git commit --amend

Replace the current commit. Often used for minor updates.

Careful! Don't amend published commits, anything someone else may have pulled.

Useful commands: Force push

user@server ~/example $ git push --force

Overwrites the remote branch history with your local branch history.

Careful! You can delete your remote branch history with this command or even the entire repository.

Useful commands: Bisect

Bisect: Divide into two parts

user@server ~/example $ git bisect start
user@server ~/example $ git bisect bad 90d6
user@server ~/example $ git bisect good 362c

Run through the commit history to find where a change was introduced. An optimized binary search is more efficient than checking out each commit in order:

user@server ~/example $ git checkout 90d6
user@server ~/example $ git checkout 4j3h
user@server ~/example $ git checkout 362c
user@server ~/example $ git checkout 53j2
user@server ~/example $ git checkout 5484

Reference: http://webchick.net/node/99

User Story Time

Using git IRL

Incorrect git can work

Two users working on one branch.

Cross your fingers and hope.

Failed to push some refs

The error we’ve all seen working like this.

To https://server.com/user/example.git
! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@server.com/user/example.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again. See the
'Note about fast-forwards' section of 'git push --help' for details.

Git won't be able to push your changes if another user has pushed to the same branch as you.

What do you do?

Common solution

user@server ~/example $ git pull # Equal to fetch and merge.
user@server ~/example $ git push

Reasons to avoid

  • Creates an extra git merge commit
  • Introduces greater risk of merge conflicts
  • A messy git history makes problems more difficult to track down.

A Better Solution

All shared work goes into a development branch.

Controlled releases happen off of this branch when it is merged into the Master branch.

Master is preserved for hotfixes

The Best Solution

Two users work in their own branches, the second branch is rebased, then merged to master when features are complete. This is the Github/Bitbucket Pull Request workflow.

Creating branches

Git branches allow you to separate work on a project into discrete groups of commits.

Ideally name branches descriptively, such as:

issue#-short_description

user@server ~/example $ git branch #List local branches
* master

user@server ~/example $ git checkout -b 1-readme-details #Create a new branch
Switched to a new branch '1-readme-details'

user@server ~/example $ git branch #List local branches
  master
* 1-readme-details

Deleting branches

Branches can be used for new features, temporary tests or quick backups.

user@server ~/example $ git commit -am "Updating readme"
[1-readme-details 5849d9b] Updating readme
 1 file changed, 1 insertion(+), 1 deletion(-)

user@server ~/example $ git checkout master
Switched to branch 'master'

user@server ~/example $ git branch -d 1-readme-details
error: The branch '1-readme-details' is not fully merged.
If you are sure you want to delete it, run 'git branch -D 1-readme-details'.
user@server ~/example $ git branch -D 1-readme-details
Deleted branch 1-readme-details (was 5849d9b).

Delete local and remote branches when you are done with them.

Merging branches

A merge creates a new commit to incorporate changes from other branches. The two branches commit histories are combined into a single history with each commit holding its place in time.

user@server ~/example $ git checkout master
user@server ~/example $ git merge develop

Finding branches to clean up

Git keeps track of what is merged and what isn't.

user@server ~/example $ git branch -r --merged
  origin/HEAD -> origin/master
  origin/master

user@server ~/example $ git branch -r --no-merged
  origin/20-news-section
  origin/23-user-login

user@server ~/example $ git branch --merged
* master

user@server ~/example $ git branch --no-merged

Merge conflicts: Creating

If you want them?

user@server ~/example $ git merge macos
Updating c1f5cc2..fc56b58
Fast-forward
 src/ProjectLauncher/LaunchForm.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
user@server ~/example $ git merge linux
Auto-merging src/ProjectLauncher/LaunchForm.cs
CONFLICT (content): Merge conflict in src/ProjectLauncher/LaunchForm.cs
Automatic merge failed; fix conflicts and then commit the result.

Merge conflicts: Resolving

user@server ~/example $ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   LaunchForm.cs
using System.Drawing;
using System.Text;
<<<<<<< HEAD
using System.Macos.Forms;
=======
using System.Linux.Forms;
>>>>>>> linux

using EnvDTE;
user@server ~/example $ git add LaunchForm.cs

Remotes and forks: Origin Remote

The source repository is called your origin remote.

user@server ~ $ git clone https://github.com/user/example.git
user@server ~ $ cd example
user@server ~/example $ git remote
origin
user@server ~/example $ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Remotes and forks: Two users, one remote

Two users can use the same repository as their origin remote.

Remotes and forks: Two users, two remotes.

User2 forks the project repository creating their own fork.

Remotes and forks: Origin and Upstream

The world according to user2

  • Pull changes from upstream into local/master
  • Pushes changes to origin
  • Merges to upstream or create a Pull Request.

Remotes and forks: Update your fork

There are changes to the upstream master. How do you bring those changes into your fork’s master branch?

git remote
git remote add upstream git@github.com:user/project.git
git remote update --prune # Update remote repos copies, remove deleted branches
git checkout master
git pull upstream master
git push # aka git push origin master

Now make your new feature branch or rebase your existing branches.

Pull requests

A pull request is a request to pull your changes.

Not a feature of Git, but of UI tools such as Github, Bitbucket, and Gitlab.

Create a pull request when you have new commits for a project in a fork and/or branch which should be pulled into the original project.

”I’ve made some changes! Will you accept them?”

Rebase

"REset your git branch BASE commit"

A merge creates a single commit with two parents, creating a non-linear history.

A rebase “replays” the commits from one branch onto another, creating a linear history. Commits for each feature stay together.

The goal: Create a clean history without resolved merge conflicts or dozens of tiny commits.

Rebasing a feature branch - 1

Remember this?

Rebasing a feature branch - 2

Rebase to master, then merge to master.

git checkout FeatureB
git rebase master
git push -f # Force update your remote branch
git checkout master
git merge master # Or create Pull Request

Squashing commits - 1

You push early and often, but that results in a cluttered history.

user@server ~/example $ git log --oneline
bf7d984 Minor readme edit
7fc1195 Readme details
53ddfc4 Minor edit
63df92b Minor edit
7714f34 New feature
286e2e4 Initial

Rebase allows you to “squash” those extra commits together.

Squashing commits - 2

user@server ~/example $ git rebase -i 286e2e4

Which opens your default text editor

pick 7714f34 New feature
pick 63df92b Minor edit
pick 53ddfc4 Minor edit
pick 7fc1195 Readme details
pick bf7d984 Minor readme edit
# Rebase 286e2e4..bf7d984 onto 286e2e4
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# Note that empty commits are commented out

Squashing commits - 3

Change pick to squash

pick 7714f34 New feature
squash 63df92b Minor edit
squash 53ddfc4 Minor edit
pick 7fc1195 Readme details
squash bf7d984 Minor readme edit

Save and exit.

Squashing commits - 4

git opens the text editor again allowing you amend the commit message of the now combined commits.

# This is a combination of 3 commits.
# The first commit's message is:
New feature
# This is the 2nd commit message:
Minor edit
# This is the 3rd commit message:
Minor edit
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 286e2e4
# You are currently editing a commit while rebasing branch 'master' on '286e2e4$
#
# Changes to be committed:
#       modified:   site.css
#

Squashing commits - 5

The result: a clean git history ready to merge or used in a pull request.

user@server ~/example $ git log --oneline
bf7d984 Minor readme edit
7fc1195 Readme details
53ddfc4 Minor edit
63df92b Minor edit
7714f34 New feature
286e2e4 Initial
user@server ~/example $ git rebase -i 286e2e4444px
[detached HEAD 859d12b] New feature
 1 file changed, 4 insertions(+)
[detached HEAD d714f67] Readme details
 1 file changed, 3 insertions(+)
Successfully rebased and updated refs/heads/master.
user@server ~/example $ git log --oneline
d714f67 Readme details
859d12b New feature
286e2e4 Initial

Editing commits with rebase - 1

Editing commits with rebase is functions like amending a commit, but allows you to work with the entire history.

user@server ~/example $ git rebase -i 286e2e4

Your text editor:

edit 859d12b New feature
pick d714f67 Readme details

# Rebase 286e2e4..d714f67 onto 286e2e4
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell

Editing commits with rebase - 2

The result of a rebase to simply edit a commit message

user@server ~/example $ git log --oneline
d714f67 Readme details
859d12b New feature
286e2e4 Initial
user@server ~/example $ git rebase -i 286e2e4
Stopped at 859d12bdcfc54fafcb5fbca3bb9ab0da57f4c92a... New feature
You can amend the commit now, with
    git commit --amend
Once you are satisfied with your changes, run
    git rebase --continue
user@server ~/example $ git commit --amend
[detached HEAD 0a9c2fe] New feature, more details to message
 1 file changed, 4 insertions(+)
user@server ~/example $ git rebase --continue
Successfully rebased and updated refs/heads/master.
user@server ~/example $ git log --oneline
7d328c7 Readme details
0a9c2fe New feature, more details to message
286e2e4 Initial

Useful commit messages

Not this

git commit -m “css fix”

It is not descriptive or useful.

Yes, this.

The git history explains how the project evolved and why decisions were made. Git commit messages, just like code comments, should succinctly explain “what”, but explain why in detail.

tag: Short explanation of the commit

Longer explanation explaining exactly what's changed and why, whether any
external or private interfaces changed, what bugs were fixed (with bug
tracker reference if applicable) and so forth. Be concise but not too brief.

Reference: https://wiki.gnome.org/Git/CommitMessages

Summary

New git commands: tag, diff, log, whatchanged, blame, remote, stash, rebase, bisect

Plus: Pull Requests, Useful commit messages

Further Study

  • history rewriting for removing passwords and extraneous binary files.
  • repository hooks
  • Pull Requests are coming to Drupal.org: https://www.youtube.com/watch?v=37zyV2mqDjU
  • https://www.atlassian.com/git/tutorials/advanced-overview/

EOF

Brad Erickson - eosrei.net - github.com/eosrei

Mark Ferree - chapterthree.com - github.com/mrf