Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Git

survey questions

How do you work now? Describe your current workflow!

How often do you commit? How often do you push/pull?

How often do you have 'conflicts'?

How do you make sure new code works fine?

How do you ensure you did not break anything?

What are the git commands in use?

Git commands

Basic

  • add

  • commit

  • status

  • diff

  • log

  • gitk --all

Networking

  • clone
  • push
  • pull
  • fetch
  • remote

Advanced

  • stash
  • annotate
  • branch
  • merge
  • rebase
  • bisect
  • reset

Git intro

Communication

No version control system can eliminate the need for developers to talk to each other!

Why use a version control?

  • Fearless experimentations
  • Fearless deletition
  • Easier (smoother) collaboration

Version control Systems (VCS)

  • Local Version Control Systems (LVCS)
  • Centralized Version Control Systems (CVCSs)
  • Distributed Version Control Systems (DVCS)

Local Version Control Systems (LVCS)

  • Copy files, dated directories

  • RCS, short for Revision Control System

  • Save a series of patches

  • Difficult to collaborate

  • Branching is almost impossible

Centralized Version Control Systems (CVCS)

  • CVS, or Concurrent Versions System

  • Apache Subversion (aka. SVN)

  • Perforce

  • Rational Clear-Case (IBM) (Configuration Management)

  • Central server holds everything (good/bad)

  • Easier administration, better control

  • Limited or no off-line work

  • Single point of failure.

  • Branching is easy in some cases (Subversion)

  • Merging is still a pain

Distributed Version Control Systems (DVCS)

Why Git?

It seems to be far the most popular DVCS.

git services

Git Overview

  • Snapshots of the objects (files) and not diffs. Using pointers eliminate duplications.
  • Nearly every operation is local. (Off-line work)
  • Integrity using SHA-1 hashes of the files. (40 character string of hexadecimal characters called "object name" or "SHA-1 id".)

Git Installation

On Linux you use your package manger (apt-get, yum, etc...) or install from git-scm

  • yum install git-core
  • apt-get install git-core

On Microsoft Windows install Git from git-scm.

On Mac OSX use Homebrew or git-scm.

  • brew install git

Command line

  • cmd

Linux: Any terminal or console

Windows:

  • Start/cmd - set the properties!
  • CLI: All Programs/Git/Git Bash
  • GUI: All Programs/Git/Git GUI

Why Command line?

  • Many different GUI tools.
  • Most of the tutorials are for the command line.
  • Most of the people who can provide you help understand the command line, but specific GUI tools.

Which version do you have?

  • --version

Windows: use the Git Bash

$ git --version
git version 2.17.0
git version 2.20.1
git version 2.30.2

Configure Git

  • config

There are three levels of configuration:

  • System (--system)
  • User (--global)
  • Project (--local)
$ git config ...

On Unix

  • /etc/gitconfig
  • $HOME/.gitconfig (/home/foobar/.gitconfig)
  • .git/config

On Windows

  • "c:\Program Files (x86)\Git\etc\gitconfig"
  • %HOMEPATH%.gitconfig %USERPROFILE%.gitconfig (C:\Users\Foobar.gitconfig)
  • .git/config

To access them use

--system
--global
--local

Samples:

$ git config --global --add user.name "Foo Bar"
$ git config --global --add user.email foo@bar.com

$ git config --list
$ git config --list --global
$ git config user.name      # to see specific value

Configure Git - personalize

  • config
$ git config --global --add user.name "Foo Bar"
$ git config --global --add user.email foo@bar.com

$ git config --list
$ git config --list --global
$ git config user.name      # to see specific value

Configure and Privacy

  • Your name

  • Your e-mail address

  • youruser+github@gmail.com

  • eBook

More configuration - alias

  • alias
$ git config --global --add alias.st status
$ git config --global --add alias.ci commit
$ git config --global --add alias.co checkout
$ git config --global --add alias.br branch

$ git config --global color.ui true
$ git config --global alias.lol "log --oneline --graph --decorate"
$ git config --global --add core.editor notepad
$ git config --global --add merge.tool vimdiff
$ git config --global --add alias.unstage "reset HEAD"

$ git config --global --unset core.editor

$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

Getting help

  • help
  • tutorial
$ git help             # listing the most important commands
$ git help COMMAND     # man page or local web page
$ git COMMAND --help   # the same

$ git help help        # help about the help system
$ git help --all       # list all the git commands
$ git help tutorial    # a simple git tutorial

See also the docs on git-scm, including this tutorial.

Exercises

  • Check if you already have Git installed (open command line, check the version) On MS Windows start the git-bash.
  • If you don't have it installed yet, then install git.
  • Open the command line.
  • Check which version do you have?
  • List the default configuration.
  • Add your name, email to the global configuration.
  • Look at the help page of one of the commands.

Git Basics

4 Ways to get started

Creating a local empty repository

  • init
  • status
  • st
$ mkdir app
$ cd app
$ git init
Initialized empty Git repository in /c/work/app/.git/

This will create a directory called .git and put some files there.

$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

Create first file

  • status

  • Create README.txt with one line of text.

  • Check the status of the working directory.

$ git status

On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

      README.txt
nothing added to commit but untracked files present (use "git add" to track)
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       README.txt
nothing added to commit but untracked files present (use "git add" to track)

File status

Each file can be either

  • untracked
  • tracked

Tracked files can be

  • unmodified
  • modified
  • staged

Add first file

  • index

  • staging area

  • cache

  • add

  • $ git add README.txt

  • $ git status

Add the files to the index (or staging area, or cache).

$ git add README.txt
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   README.txt
#

Commit first file

  • commit

  • $ git commit -m "Add README"

  • $ git status

$ git commit -m "Add README"
[master (root-commit) 1cd95a6] Add README
 1 file changed, 1 insertion(+)
 create mode 100644 README.txt
$ git status
# On branch master
nothing to commit, working directory clean

Making some changes

  • diff

  • --cached|diff

  • edit the README.txt file again, adding a new row.

  • $ git status

  • $ git diff

  • $ git add README.txt

  • $ git status

  • $ git diff

  • $ git$ git diff --cached (or --staged)

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

What has changed?

$ git diff
WARNING: terminal is not fully functional
diff --git a/README.txt b/README.txt
index e51ca0d..a697828 100644
--- a/README.txt
+++ b/README.txt
@@ -1 +1,2 @@
-Hello Git
+Hello Git
+Second line
$ git add README.txt
$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   README.txt
#

What did we change?

$ git diff
$ git diff --cached    (or --staged)
WARNING: terminal is not fully functional
diff --git a/README.txt b/README.txt
index e51ca0d..62567d0 100644
--- a/README.txt
+++ b/README.txt
@@ -1 +1,2 @@
-Hello Git
\ No newline at end of file
+Hello Git
+Second line
$ git commit -m "update README"
[master 1251a45] update README
 1 file changed, 2 insertions(+), 1 deletion(-)

Untracked and Modified

Create another file called setup.pl with a single line and also change the README file.

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       setup.pl
no changes added to commit (use "git add" and/or "git commit -a")

Untracked/Modified/Staged

Create another file called config.pl

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       config.pl
#       setup.pl
no changes added to commit (use "git add" and/or "git commit -a")

Stage it

$ git add config.pl
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   config.pl
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       setup.pl

Commit the file(s)

Only the staged file is committed.

$ git commit -m "first version of config.pl"
[master cdd2c3b] first version of config.pl
 1 file changed, 1 insertion(+)
 create mode 100644 config.pl
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       setup.pl
no changes added to commit (use "git add" and/or "git commit -a")

See the changes

Make some changes to the config.pl file and stage it.

$ notepad config.pl
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#       modified:   config.pl
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       setup.pl
no changes added to commit (use "git add" and/or "git commit -a")
$ git add config.pl
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   config.pl
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       setup.pl

So what was changed?

$ git diff
diff --git a/README.txt b/README.txt
index 62567d0..fb89137 100644
--- a/README.txt
+++ b/README.txt
@@ -1,2 +1,4 @@
 Hello Git
 Second line
+Third line

Only the changes to the not staged files are shown

$ git diff --cached   ( or --staged)
diff --git a/config.pl b/config.pl
index f9d55cd..e2b7f47 100644
--- a/config.pl
+++ b/config.pl
@@ -1 +1,2 @@
 # this is the config.pl file
+# second line

Only the changed to the staged files are shown

$ git diff HEAD
diff --git a/README.txt b/README.txt
index 62567d0..fb89137 100644
--- a/README.txt
+++ b/README.txt
@@ -1,2 +1,4 @@
 Hello Git
 Second line
+Third line
+
diff --git a/config.pl b/config.pl
index f9d55cd..e2b7f47 100644
--- a/config.pl
+++ b/config.pl
@@ -1 +1,2 @@
 # this is the config.pl file
+# second line

changes between working copy and HEAD

Stage and HEAD

  • diff
  • HEAD
working copy  ->  (git add) index -> (git commit) -> HEAD
$ git diff
   # (changes between working copy and staged copy (index, cache))
$ git diff --staged
   # (changes between staged copy and HEAD)
$ git diff HEAD
   # (changes between working copy and HEAD)

Remove from stage (unstage)

  • unstage
  • reset HEAD
$ git reset HEAD config.pl
Unstaged changes after reset:
M       README.txt
M       config.pl
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   README.txt
#       modified:   config.pl
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       setup.pl
no changes added to commit (use "git add" and/or "git commit -a")

$ git reset HEAD will reset all the files currently staged.

See also the unstage alias we created earlier.

Drop local changes (restore to HEAD or to index)

  • restore
  • checkout
$ git restore config.pl
$ git checkout config.pl    # old school :-)
$ git status

git checkout FILENAME will replace FILENAME in the work tree with the one committed (or if there is a version already staged then to that version). You loose your local work.

git checkout . will do it for all the files in the tree.

# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   README.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   setup.pl
no changes added to commit (use "git add" and/or "git commit -a")

Add all the files

$ git add .
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   README.txt
#   new file:   setup.pl
#
$ git commit -m"start writing the setup script"
[master 887d712] start writing the setup script
 2 files changed, 2 insertions(+)
 create mode 100644 setup.pl

Git ignore

  • .gitignore

The .gitignore file in the root of the repository can describe sets of files that don't need tracking. This will make sure you don't add the files by mistake (e.g. while using git add .) and they won't show up in the output of the git status command.

config.ini
*.html
*.[oa]
*~
*.swp

Add this file to the repository and commit it. This will ensure that no one in the project will have the extra files problem.

.git/info/exclude

  • exclude

You can have a file called .git/info/exclude with similar format to the .gitignore file where you would list the files that should be ignored only on your computer. I don't think it is a good practice to use this.

  • Others might have the same files that need to be excluded.
  • You might move to a different computer and need to set that up manually.

.gitkeep

  • Git won't track an empty directory, but sometimes you would like to have one in your repository so your code can assume the directory is there (e.g. for temporary files or for caching).
  • Not a real feature but a convention is to add an emty file called .gitkeep to the directory you'd like to keep and add that file to git. This will make git track the directory.
  • If you also want to ignore all the content of that directory you can use .gitignore. In that case you might need to use the force to add the file to git: git add --force some/directory/.gitkeep

add and commit in one step

git add and commit at once of the modified files, but not the new files

$ git commit -a -m "some message"

Move a file

  • mv
  • move
  • rename
$ mv config.pl app.pl
$ git status
$ git add app.pl
$ git rm config.pl
($ git add -u)
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    config.pl -> app.pl
#
$ git commit -m "rename"

$ git mv old.pl new.pl

Try also moving a file and changing its content a bit.

Remove a file

  • rm
$ git rm setup.pl
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   deleted:    setup.pl
#
$ git commit -m "remove"

Frequency of commits

Adding files and committing changes to Git is cheap. What happens if you made some great work during the day and, at 5 pm when you were tired you made some bad changes. How can you go back to the state that was 5 minutes ago?

  • Commit after adding a new function.
  • Commit after writing a new test case.
  • Commit after making any small change.
  • Commits are cheap and fast.

log

  • log
  • gitk
$ git log
$ git log -p
$ git log --stat --summary
$ git log --graph

gitk

$ gitk --all

blame

  • git blame [filename]

Exercise

  • Create a directory and inside create a new local repository.
  • Create a directory and in that directory create a file. (You can use Visual Studio or Eclipse or your IDE to start a new project.)
  • Add the directories and files to the repository.
  • Are there any files that should not be tracked?
  • Make sure git will ignore them in this project.
  • Make some changes. Check what are the changes. Commit some of them.
  • Go over the previous chapter and execute all the commands we went through.
  • If there is any problem. Ask for help!

Git Branching

Create a branch

  • branch

  • You might work on several features and bug-fixes at the same time.

  • Sometimes you will need to stop working and implement some other changes.

  • There might be several people working on different features at the same time.

  • Git allows and encourages frequent commits.

Create a branch

$ git branch
* master

$ git branch featurex
$ git branch
  featurex
* master

$ git checkout featurex
Switched to branch 'featurex'
$ git branch
* featurex
  master

make some changes to app.pl, and commit them to the repository

  • Alternative way that creates a branch and checks it out:
git checkout -b featurex
  • Make some changes to file, and commit it to the repository.
  • Use gitk --all to see the branch.

Switch between branches

git checkout master
  • This will switch to the master branch.

  • See that the changes have "disappeared".

  • Make some other change on the master to README.txt.

  • See that the two have diverged (use gitk --all).

Exercises Session 4

  • While still on the master branch create 2 new files A.txt and B.txt with the content "a file" and "b file" respevtively.

  • Commit the changes.

  • As you make the changes, keep using gitk --all to observe the changes.

  • Create three new branches featureA, featureB, and featureC. (or any other names)

  • On master make a commit to README. (Add a line 'this is master')

  • On featureA make a few commits to a file called A.txt.

  • On featureB make a few commits to a file called B.txt.

  • On featureC make a commit to README changing (Add a line 'this is feaure C').

Simple automatic merge

$ git checkout master
  • see that the changes have "disappeared"
  • make some other change on the master to README.txt
  • see that the two have diverged (use gitk)
  • merge the feature into master
$ git merge featurex
Merge made by the 'recursive' strategy.
 app.pl |    1 +
 1 file changed, 1 insertion(+)

$ gitk --all

Merge with conflict

$ git branch featurey
$ git co featurey
# edit the app.pl file , add a line, commit the change

$ git co master
# edit the app.pl file, add a line, commit the change

$ git merge featurey
Auto-merging app.pl
CONFLICT (content): Merge conflict in app.pl
Automatic merge failed; fix conflicts and then commit the result.
use 5.010;
say "config";
# some change
# adding featurx step 1
<<<<<<< HEAD
# add fix on master
=======
# featurey 1
>>>>>>> featurey

Edit the app.pl file and resolved the conflict, removing the marks and writing the correct code.

$ git add app.pl
$ git ci -m "featurey merged"

Repeated merge

$ git co featurey
# edit app.pl add another line
$ git add app.pl
$ git ci -m "another line"

$ git co master
$ git merge featurey

This time the merge was automatic, and it only included the changes since the previous merge.

Delete branch

  • $ git branch -d featurex

Force delete branch

If you started to work on a feature but arrived to a dead-end. You can get rid of the whole branch without merging it.

git branch -D feature

Delete remote branch

git push origin :barnchname

Exercises Session 6

  • Check out featureC, make some changes, commit

  • Merge fetureC into master again - it should be without conflict

  • Check out featureA, make some changes, commit

  • Check out master and run gitk --all. featureC should be fully merged and featureA should have commit on it.

  • Remove (delete) both featureA and featureC branches.

rebase

  • Have straight line of history.
  • Make it easier for the maintainer of the application to merge changes.
  • Resolve conflicts before the merge.

Create a branch and make some changes

git checkout -b feature
...
git add .
git commit -m "some changes"

Make some progress on the master branch:

git checkout master
...
git add .
git commit -m "progress"

Observe the situation using gitk --all &

$  git checkout feature
Switched to branch 'feature'

$  git rebase master
First, rewinding head to replay your work on top of it...
Applying: feature

Observe the situation again using gitk --all &

Exercises Session 7

  • featureC has some changes that started a while ago. Since then master made some progress.
  • Rebase featureC onto master.
  • Observe the state before and after.

Various ways to list changes

  • gitk --all
  • git log

log between commits

$ git log SHA1..SHA2

log show filenames

$ git log --name-only

Show history of renamed file

$ git lot --follow --name-only FILENAME

Commits that were not merged yet

$ git log fetureX --not master

Git tag

  • tag

  • You release a new version of your software.

  • What if later you'll need to come back to the same commit and make some changes?

  • How to remember which SHA-1 was this release?

$ git tag v1.10
$ git tag -a v1.10 -m "commit message"
  • A tag marks a specific commit. The former is a "light weight tag", the latter is an "annotated tag".
  • The light weight tag is just like a branch that does not move. A pointer to a commit.
  • An annotated tag is a full object with owner and message (annotation).
  • git push --follow-tags only pushes annotated tags

Remove tags

Locally:

git tag -d TAGNAME

Remotely:

git push --delete origin TAGNAME

Exercise

  • Create two branches A and B
  • On A make 2 changes
  • On B make one change
  • Merge A to B
  • Create a new branch called C from A
  • Make a change on C
  • Make 2 more changes on A
  • Merge A to B again
  • Delete branch C
  • Delete branch A
  • Merge B to master
  • delete B

Exercises Session 3

  • Create a tag on the current commit using git tag -a v1 -m 'this is v1'
  • Use gitk --all to see it.
  • Use git log to see it.

Git Local

Create server repository

$ mkdir git-demo/
$ cd git-demo
$ git init

Initialized empty Git repository in /Users/gabor/work/git/git-demo/

Configure user

$ vim .git/config

add:

[user]
	name = Code Maven
	email = me@code-maven.com

Add file

$ vim README
$ git add .
$ git commit -m "first"
[master (root-commit) ef28fcf] first
 1 file changed, 1 insertion(+)
 create mode 100644 README

Update file

$  vim README
$  git add .
$  git commit -m "second"
[master 398a810] second
 1 file changed, 1 insertion(+)

$  vim README
$  git add .
$  git commit -m "third"
[master 33bb6a4] third
 1 file changed, 1 insertion(+)

First 3 commits

Create branch

$ git branch add-poll

Switch to branch

$ git checkout add-poll

Make changes on the branch

$  vim POLL
$  git add .
$  git commit -m "question 1"
[add-poll dc940b9] question 1
 1 file changed, 1 insertion(+)
 create mode 100644 POLL

$  vim POLL
$  git add .
$  git commit -m "question 2"
[add-poll 869018d] question 2
 1 file changed, 1 insertion(+)

$  vim POLL
$  git add .
$  git commit -m "question 3"
[add-poll e0dbe09] question 3
 1 file changed, 1 insertion(+)

Switch to master

$ git checkout master

The file POLL has disappeared from the working directory.

Now the word master is bold.

Add changes to master

$  vim MASTER
$  git add .
$  git commit -m m1
[master 8f78730] m1
 1 file changed, 1 insertion(+)
 create mode 100644 MASTER

$  vim MASTER
$  git add .
$  git commit -m m2
[master 591a601] m2
 1 file changed, 1 insertion(+)

Switch to branch

$ git checkout add-poll

The file MASTER disappers. The file POLL reappears.

Now the word add-poll is bold.

Another change on the branch

$  vim POLL
$  git add .
$  git commit -m "question 4"
[add-poll d00e2a8] question 4
 1 file changed, 1 insertion(+)

The add-poll branch is now displayed higher because it had the most recent commit.

Merge branch to master

$ git checkout master

$  git merge add-poll
Merge made by the 'recursive' strategy.
 POLL | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 POLL
Merge branch 'add-poll'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

Shared feature branch

A: $ git checkout -b feature
A: $ git push -u origin feature

B: $ git chechkout master
B: $ git pull
B: $ git branch feature origin/feature
  • Every developer works on the "feature" branch.
  • Every developer create a small branch from the "feautre", commits there. pushes it out. Send PR to the feature.

Git Remote

Setup remote repository

On the "remote machine"

$ mkdir git/repo
$ cd git/repo
$ git init --bare

Clone a repository

  • clone
$ cd ~/work
$ git clone /path/to/git/repo

List remote repositories

$ git remote
$ git remote -v

push

Do some work, make some changes, commit several times

$ git push

fetch

$ git fetch
$ git co master
$ git merge origin/master

pull

$ git pull

pull is the same as fetch+merge, but it is probably better to do it in two step. First fetch and then try to merge.

Simple Centralized workflow

One central git repository where everyone has read/write access. You can work off-line without caring what the others do making changes, commiting to your repository. Before you try to push you will first have to fetch and merge to integrate all the changes others did. Then you can push your own changes.

incoming, outgoing

git incoming - After a fetch it will show what is on the remote master barnch that I have not merged into my own master.

$ git log origin/master ^master

git outgoing - What is on my master that has not been pushed to the remote master

$ git log master ^origin/master

Remote branch

$ git branch
$ git branch --remote
$ git branch --all

$ git config --global push.default  simple

$ git co -b feature

$ git push -u origin feature

$ git fetch
$ git co feature
$ git merge origin/feature
$ git push

$ git push origin --delete feature

# older version:
$ git push origin :feature

Remote branch created by others

$ git clone URL
$ git branch --remote      # listing the remote branches

$ git checkout origin/feature
(detached HEAD)
$ git co master

$ git co -b feature origin/feature
$ git push     # will push the the remote feature branch

$ git fetch     # syncs the remote repository to the local one
$ git co feature
$ git merge origin/feature

Exercise

  • Create a directory and in it create a --bare repository, or if you have a remote sandbox repository use that.

  • Clone it to 'local' directory (joe)

  • Add 2 files

  • Push them to the main repository.

  • Clone the repository to another location on the hard-disk (mary)

  • Make some changes: create a file your_git_user.txt

  • push the changes to the server

  • Clone a reposiotry

  • Create local develop branch tracking the remote develop branch

  • Create local branch feature/USERNAME

  • Make some changes

  • Push the changes to the remote feature/USERNAME branch

  • Fetch the remote to see if other have been doing anything

Some commands you might need

$ git clone URL
$ git checkout -b local_name
$ git checkout -b local_name origin/remote_name
$ git fetch
$ git push
$ git merge origin/remote_name

Git Workspace

tags

A light weight tag can be created using git tag NAME.

An annotated tag can be created using git tag -a NAME.

$ git tag v3.0
$ git tag v3.0 SHA1

$ git tag -a v3.0

$ git push --tags

Stash

You are in the middle of a change (some files have changed in your working directory) when you suddenly think about some refactoring to be done. How to save the local changes easily?

# change some files locally
$ git stash        # saves all the changes and leaves the directory clean
Saved working directory and index state WIP on master: 6217360 last-commit
HEAD is now at 6217360 last-commit

$ git stash list
stash@{0}: WIP on master: 6217360 last-commit

# make some other changes, add and commit them

$ git stash pop     # will merge the stashed changes

Stash untracked files as well

$ stash --include-untracked
$ stash -u

Stash selected files

git stash push filename

How to stash only part of the files

Let's say we have made changes to two files (X and Y) and we would like to stash one of them (X)

$ git add Y
$ git stash --keep-index
$ git reset HEAD Y

Clear stash

Remove everything from stash

$ git stash clear

Exercises Session 8

  • Make some local changes

  • Create a new file

  • Stash them away using git stash -u

  • Observe that the working directory is clean and back to the previous state

  • Observe the content of the stash.

  • Make some other changes

  • Commit them.

  • git stash pop

  • See the previous partial changes are in the working directory. Including the new file.

  • Commit the changes.

Undo last commit

  • undo
$ git reset --soft HEAD~


$ git commit ...
$ git reset --soft HEAD^
$ edit
$ git add ....
$ git commit -c ORIG_HEAD     (

Undo git reset

$ git reset HEAD~

$ git reset HEAD@{1}

$ git reflog    # to list the history of HEAD

git bisect - finding bugs

  • Notice a bug that you recall was working earlier. (but apparently there were no automated tests checking it)

  • Task: find the change that broke it

  • Find an old commit where it was still working.

  • Binary search the commit since then to locate the breaking change.

Preferably write an automated test that can verify the feature. Put it in a separate test file in the workspace.

git bisect start

# test fails
git bisect bad

git checkout old-sha
# test passes
git bisect good

# test passes / fails
git bisect good / bad

Exercises Session 9

  • Create a file called 'add.sh' with the following content:

  • Make it executable.

  • Test it: ./add 23 19 should print 42

  • Commit it.

  • Create a file called NUMBER and put a 1 in it.

  • Commit it.

  • Then create 5 more commit changing the file to some other number. (This might help)

echo 7 > NUMBER
git commit -am "7"
  • Then change the add.sh file replacing the + by a -.

  • Create another 5 commits chaning the NUMBER file.

  • No check if the add.sh script works

  • ./add.sh 23 19 will now prin 4 instead of 42.

  • Using bisect find the commit that broke it.

git rebase

Git Workflows

A collection of workflows and issues

  • Individual developer, local changes only

Change, commit, push - not very exciting.

  • Individual developer, local changes only, using stash for small interruptions

This time the git stash pop might need to handle merge conflicts.

  • Individual developer, both local and remote changes (e.g. a pull-request was accepted using the web site of GitHub)

Need to pull before push. One might need to merge or rebase and one might need to handle conflicts.

  • Individual developer, working in branches

  • A single main developer + multiple helpers

The latter forking the repository and sending pull-requests

  • Multiple developers on the same repo - single main branch

  • Multiple developers on the same repo - development branches and Pull-requests

Workflow for individuals

  • main only
  • stash for small interruptions
  • feature branches (merge or rebase?)

Git workflows

Centralized Workflow

  • Single remote repository.
  • Single master branch.

Repo start

  • A: $ git clone ssh://user@host/path/to/repo.git
  • B: $ git clone ssh://user@host/path/to/repo.git

Developer A

  • A: make changes, commits $ git push

Developer B

B: make changes, commits $ git push fails with

error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

git pull (fetch+merge)

B: $ git pull

Merge branch 'master' of /Users/gabor/work/git/git-server

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

Forgetting to rebase

  • If you executed a git pull and forgot to add --rebase it can be still fixed.
  • If you have not pushed it out yet: $ git reset HEAD~1 --hard
  • Then you can $ git pull --rebase

git pull --rebase (fetch+rebase)

  • B: $ git pull --rebase

  • B: $ git push

Resolve conflicts

  • A makes changes to the same area where B is making changes.
  • B: git push rejected
  • B: git pull --rebase

remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Users/gabor/work/git/git-server
   3f6fad3..126d1f8  master     -> origin/master
First, rewinding head to replay your work on top of it...
Applying: add another line by A
Applying: A at the bottom
Using index info to reconstruct a base tree...
M	README
Falling back to patching base and 3-way merge...
Auto-merging README
CONFLICT (content): Merge conflict in README
error: Failed to merge in the changes.
Patch failed at 0002 A at the bottom
Use 'git am --show-current-patch' to see the failed patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm &lt;conflicted_files&gt;", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

Conflicting patch

$ git am --show-current-patch

commit 6297d04c97c29937bcf540cf22312ec3900ae33e (master)
Author: A &lt;a@code-maven.com&gt;
Date:   Wed Jun 27 15:53:23 2018 +0300

    A at the bottom

diff --git a/README b/README
index 31d0540..a8a7095 100644
--- a/README
+++ b/README
@@ -2,3 +2,4 @@ Add line by a
 Another line by A
 Readme file
 2nd line
+line added by A

Conflicting file

Resolve conflicting file

  • git status
rebase in progress; onto 126d1f8
You are currently rebasing branch 'master' on '126d1f8'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD &lt;file&gt;..." to unstage)
  (use "git add &lt;file&gt;..." to mark resolution)

	both modified:   README

no changes added to commit (use "git add" and/or "git commit -a")

Continue rebase

$ git add .
        no need to commit that changes!!!
$ git rebase --continue

Git Flow

About Git Flow

Best for web applications or if you only need to handle single version.

Git Flow branches

  • flow

  • master (or deployment)

  • hotfixes (several small branches)

  • release (to prepare for release)

  • develop (or integration)

  • feature (several branches)

Git flow chart

Try Git Flow

Joe creates a branch called feature/a and pushes it to the central repository Mary also wants to work on the same feature, so she pulls it down.

Joe:

$ git co master
$ git co -b develop
$ git co -b feature/A
# create file A.txt
$ git add A.txt
$ git ci -m "start A"
$ git push -u origin feature/A

Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 327 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/gabor/work/test_remote/
 * [new branch]      feature/A -> feature/A
Branch feature/A set up to track remote branch feature/A from origin.

Mary

$ git fetch origin

remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/gabor/work/test_remote
$ git checkout --track origin/feature/A

Branch feature/A set up to track remote branch feature/A from origin.
Switched to a new branch 'feature/A'

Merge to develop

$ git co develop
$ git merge feature/A
$ git push

Delete remote branch

  • delete

Mary

$ git co develop
$ git branch -d feature/A

$ git push origin --delete feature/A

# older version:
$ git push origin :feature/A

Joe

$ git pull
$ git branch -d feature/A
$ git remote prune origin

Gitflow with Fast Forward merges

Set up the develop branch on the central server. Needs to be done only once.

git clone  URL
git checkout -b develop
git push -u origin develop

Developer sets up a local version of the develop branch. Each developer (in each clone) needs to do once.

git clone  URL
git checkout -b develop origin/develop

When a developer starts to work on feature X, she needs to create a local branch and push ot to the server.

git checkout -b feature/X
git push -u origin feature/X

Other developer who also wants to work on feature X, needs to create local branch for it tracking the respecive remote branch.

git fetch
# Using git branch --remote  make sure origin/feature/X  already exists
git checkout feature/X origin/feature/X

Developers do they work on the feature/x branch and push it out to remote. Sometimes they fetch from remote and integrate the changes the other developer(s) of feature/x have done.

git add ....
git commit ...
git fetch
git merger origin/feature/X
git push

When the feature is finished one of the developers needs to integrate the changes in develop since the start of featoure/X and feature/X itself. In order to keep feature/X intact we do this integration in a separate branch:

# first update develop
git fetch
git checkout develop
git merge --ff-only origin/develop    # fast-forward

# create the integration branch
git checkout feature/X
git checkout -b integrate/X
git merge develop                     # there might be conflicts!

The branch integrate/X can be tested. If something is broken, the developers can fix it on feature/X and merge it again into integrate/X. Once the integration is done and the integrate/X was pused to the central server,

git checkout integrate/X
git push -u origin integrate/X

At this point the branch integrate/X contains all the changes in develop and all the changes in feature/X and the developers have tested it. Now we can allow a tester to test this on her own machine:

git fetch
git checkout -b integrate/X   origin/integrate/X
# Testing, if it failed, complain to the developers and then later:

git fetch
git checkout integrate/X
git merge --ff-only origin/integrate/X   # fast-forward

Once the Tester accepts the version, the Chief Integrator (CI) - who is not necessarily a developer

  • can now integrate this branch into develop using fast forward:
git fetch
git checkout develop

# Check if there are any changes on develop that are not yet in feature/X
# the next log should be empty
git log develop --not origin/integrate/X

git merge --ff-only origin/integrate/X  # only if it is fast-forward

There is a separate command set for release and for hotfixes:

git checkout develop
git checkout -b release/v1.00
git push -u origin release/v1.00
# do some last minute work on the relaase branch. Once it is done
git checkout master
git merge --ff-only release/v1.00    # fast forward
git tag v1.00                        # tag is placed on the master
git checkout release/v1.00
git merge develop                    # resolve conflicts if necessary
# Test
git checkout develop
git merge --ff-only release/v1.00    # fast forward

hotfix is the same as release except it stars from 'master' and not from 'develop' and the branch is called hotfix/v1.01.

Github

Fork repository

Forking workflow

  • Joe has a local and a remote repository. https://github.com/joe/demo
  • Mary "fork" the repository of Joe creating a remote copy of her own: https://github.com/mary/demo
  • Mary clones her remote repository to the local disk
  • Mary creates a local branch called 'feature', makes some changes, pushes the changes out to the 'feature' branch in her own remote repo.
  • Mary sends a "pull request" to Joe asking Joe to integrate the changes to the master branch of his own repository.
  • If Joe has comments, he makes them. Mary can make further changes in the 'feature' branch, and push them out.
  • Once Joe is satisfied with the pull-request he accepts it merging the changes to his own "master" branch.
  • Mary sets up the remote repository of Joe to be a remote repository of her own. (She only needs to do this once per repo.) git remote add upstream https://github.com/joe/demo.git
  • Mary changes to "master" locally, and pulls from Joe: git checkout master git pull upstream master
  • Mary can then delete the local and remote 'feature' branch.
  • If Mary already has other feature branchese she might want to rebase them onto "master" so they will be based on the most recent changes.

Clone a repository from Github

  • clone

Let's see GithuGithubb

$ git clone git://github.com/eilara/camel-defense.git
git remote -v

origin	https://github.com/cm-demo/participants (fetch)
origin	https://github.com/cm-demo/participants (push)

Github fork

Forking on Github will create another copy of the public repository, but in your own user-space. You then clone from that place. Make changes locally, commit them and push them to your own public repository.

Integration: You can send a pull request to the author via the Github web site. He can then either merge your changes on the Github site or can do it on his local machine.

git remote add

  • remote
$ git remote
origin

$ git remote add upstream /path/to/git/repo2
$ git remote
origin
upstream


$ git pull

$ git fetch upstream
$ git merge upstream/master

Public key

Generating SSH Keys and add them to a Github account

Exercise

  • Clone this repository from Github and check its history

  • Create an account on Github

  • Fork a repository and clone it.

  • Make some changes: create a file your_git_user.txt

  • push the changes to Github

  • Observer the changes have arrived

  • Send a pull request

  • Find other people in the team who have forked the project.

  • Set up the repository of one of them as remote.

  • Fetch and merge their changes.

Make some local changes

git checkout -b feature
   edit participants.json file
git add participants.json
git commit -m "some change"

push out local changes to branch

git push

fatal: The current branch feature has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin feature
$  git push -u origin feature

Total 0 (delta 0), reused 0 (delta 0)
To github.com:collab-dev/participants/
 * [new branch]      feature -> feature
Branch 'feature' set up to track remote branch 'feature' from 'origin'.

Send Pull-Request

Make more changes and update the pull-request

git add .
git commit
git push

Follow the changes in the original repository

git remote add upstream https://github.com/collab-dev/participants.git
git checkout master
git pull upstream master
git push

Remove local branch

git checkout maste
git branch -d feature

Remove remote branch

$  git push origin :feature

To github.com:collab-dev/participants/
 - [deleted]         feature

Git Internals

Plumbing and Porcelan

  • branch

Porcelan refers to the regular commands people use.

Plumbing refers to the low-level system commands of Git

The .git directory

  • .git

git init

  • branches/

  • index

  • config

  • description

  • HEAD

  • hooks/

  • info/

  • objects/

  • refs/

  • config holds your project specific configuration.

  • objects/ holds the objects (blob, tree, commit)

  • refs/ holds references to objects

  • hooks/ the local and remote hooks.

Git Objects (Files)

  • .git
git init
ls -l .git
find .git/objects
find .git/objects -type f

$ git hash-object -w test.txt
$ git cat-file -p  SHA1
$ git cat-file -t SHA1
blob

the type of these object is called blob

See Objects

$ mkdir test
$ cd test
$ git init
$ ls -al
$ ls -al .git
$ find .git
$ echo "first version" > README
$ git add README
$ find

# observe the creation of .git/objects/22/de8d69c9026be2a49f540fda12f3e755a33e6c
$ git cat-file -t 22de8d69c9026be2a49f540fda12f3e755a33e6c
blob
$ git cat-file -p 22de8d69c9026be2a49f540fda12f3e755a33e6c
first version

$ git ci -m "commit first ver"

$ .git/COMMIT_MESSAGE   (always the last commit message)

Two more objects appeared:
  a tree object and a commit object

.git/refs/heads/master  was created with the SHA1 of commit object in it

$ echo "second version" >> README
$ git add README

   new blob object created
   .git/index file updated

tags

A light weight tag that was created using git tag NAME is a simple file saved as .git/refs/tags/NAME that contains the SHA1 of the current commit when the tag was created.

An annotated tag created using git tag -a NAME is a simple file saved as .git/refs/tags/NAME holding the SHA1 of a newly created object of type "tag" that was saved in .git/objects.

$ git cat-file -p SHA1

$ git push --tags

Appendix

Setup remote repository

On the "remote machine"

$ mkdir git/repo
$ cd git/repo
$ git init --bare
$ git remote add origin /home/gabor/work/test_remote/
git push -u origin master

Revert local changes

Changes that were made to the working copy:

$ git checkout file-name

Changes that were already staged using git add:

$ git reset HEAD file-name

Last change that was already committed:

$ git reset --hard HEAD~1

Do NOT do this if the change was already made public.

Undelete a file

After running git rm file, but before committing the change, you suddenly notice you don't need to remove the file.

$ git rm file

$ git reset HEAD file
$ git checkout file

Edit config files

Open the respective configuration files for editing

  • git config --edit --local
  • git config --edit --global
  • git config --edit --system

Common Git commands

$ git init
$ git status

$ git add FILENAMEs
$ git add .
$ git commit -m "Text"

$ git diff
$ git diff --cached       (aka. --staged)
$ git diff HEAD

$ git log
$ git checkout
$ git checkout branch_n ame


$ git branch
$ git merge  name_of_branch_to_merge_from

$ git tag

$ git clone

$ git remote
$ git remote add

$ git push
$ git push REMOTE  BRANCH_NAME
$ git push REMOTE  LOCAL_BRANCH_NAME:REMOTE_BRANCH_NAME
$ git push REMOTE  HEAD:REMOTE_BRANCH_NAME
$ git push -u REMOTE  branchname    # set up tracking as well

$ git fetch
$ git pull

$ git stash
$ git stash list
$ git stash pop

Tools

In the Git-Bash you can run Notepad++ like the first command. You can set a Bash alias to it (second line) and then use that alias to launch Notepad++.

$ /c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe
$ alias np="/c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe"
$ np

To make this permanent, cd to switch the to home directory and there create a file called .bashrc containing the following:

alias np="/c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe"
alias ll="ls -al"

(On my system this file is located in c:\Users\Gabor, so if you are not familiar with linux, it is probably easier to create the file while using Windows.)

External difftool WinMerge

git config --replace --global diff.tool winmerge
git config --replace --global difftool.winmerge.cmd
   "winmerge.sh \"\$LOCAL\" \"\$REMOTE\""
git config --replace --global difftool.prompt false

Save the following in c:\Users\Gabor\bin\winmerge.sh

#!/bin/sh
echo Launching WinMergeU.exe: $1 $2
"$PROGRAMFILES/WinMerge/WinMergeU.exe" -e -ub -dl "Base" -dr "Mine" "$1" "$2"
$ git difftool

Resources

Extra

Stage hunk-by-hunk

  • hunk
$ git add -p

Edit last commit message

  • amend
$ git commit --amend -m "New commit message"

Push to remote branch

Mary:
git push -u origin local_branch_name_of_mary

Joe:
$ git fetch
$ git co -b local_for_joe
$ git merge origin/local_branch_name_of_mary
# make changed
$ git push origin HEAD:local_branch_name_of_mary
$ git push -u origin HEAD:local_branch_name_of_mary

Detached HEAD

$ git co master

Change last commit message

$ git commit --amend -m "new message"
$ git commit --amend

Only do this if the specific commit has not been shared by the public. (Has not been pushed to a remote). Otherwise it will break the history and merging for everyone.

How to see the content of the staged version of a file?

  • show
$ git show :FILE

How to see an older version of a file?

  • show
$ git show HEAD:FILE
$ git show HEAD~2:FILE
$ git show 8931:FILE    (The SHA of the relevant commit)

short SHA1 (at least 4 but usually not more than 7)

Setup git subtree

  • subtree
$ cd parent-repo
$ git remote add some-library URL-TO-LIBRARY-REPO
$ git subtree add --prefix lib/ some-library master
$ git log

git subtree change parent

$ echo "add to parent" >> README.md
$ git add README.md
$ git commit -m "added to parent"
$ git push
$ git log

git subtree change in subtre

$ echo "add to library" >> lib/README.md
$ git add lib/README.md
$ git commit -m "added to library"
$ git push

Pushes out to parent