Introduction to Git
Git Intro
Updated slides
These slides are not maintained any more. Check out our other Git slides.
Self intro
-
Help teams improve their development practices
Why use a version control?
- Fearless experimentations
- Fearless deletition
- Easier (smoother) collaboration
- History
- git, mercurial, subversion, cvs, rcs, ...
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 macOS (was Mac OSX) use Homebrew or git-scm.
-
brew install git
-
You could also just type
git
in the Terminal and you'll be offered to installgit
.
Which version do you have?
Windows: use the Git Cmd
$ git --version
git version 2.20.1
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 and Privacy
-
Your name
-
Your e-mail address
-
youruser+github@gmail.com
Getting help
- help
$ 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
Exercises Session 1
- 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.
4 Ways to get started
- Create empty local repository
- Create repository in local directory where we already have files of the project
- Clone remote repository
- Fork and then clone remote repository of someone else
Creating a local empty repository
- status
$ mkdir app
$ cd app
$ git init
Initialized empty Git repository in /c/work/app/.git/
$ git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
This will create a directory called .git and put some files there.
Create first file
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)
Add first file
-
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
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")
File status
Each file can be either
- untracked
- tracked
Tracked files can be
- unmodified
- modified
- staged
Drop local changes (restore to HEAD or to index)
- restore
- checkout
$ git restore config.pl
$ git checkout config.pl
$ 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")
Remove from stage (unstage) (restore to modified)
$ git restore --staged config.pl
$ 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.
So what was changed?
- git diff
- git diff --cached
- git diff HEAD
$ 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
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
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)
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
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
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
- git mv old.txt new.txt
Remove a file
- 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 and
- git log
- git log -p
- git log --stat --summary
- git log --graph
gitk
- gitk --all
blame
- git blame [filename]
Exercises Session 2
- 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!
Alias
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
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"
Git 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
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.
Branching
Branching in Git
- 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
- Alternative 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 toREADME
. (Add a line 'this is master') -
On
featureA
make a few commits to a file calledA.txt
. -
On
featureB
make a few commits to a file calledB.txt
. -
On
featureC
make a commit toREADME
changing (Add a line 'this is feaure C').
Simple automatic merge
$ git checkout master
- Merge the feature into master.
$ git merge featurex
Merge made by the 'recursive' strategy.
A.txt | 1 +
1 file changed, 1 insertion(+)
$ gitk --all
Merge with conflict
$ git branch featurey
$ git checkout featurey
edit the README file, add a line, commit the change.
$ git checkout master
edit the README file, add a line, commit the change.
$ git merge featurey
Auto-merging README
CONFLICT (content): Merge conflict in README
Automatic merge failed; fix conflicts and then commit the result.
Line before changes
<<<<<<< HEAD
# add fix on master
=======
# line added in featurey
>>>>>>> featurey
Edit the README file and resolved the conflict, removing the marks and writing the correct code.
$ git add README
$ git commit -m "featurey merged"
Exercises Session 5
-
Merge featureA into the master branch.
-
Observe the results with
gitk --all
. -
Then merge featureC into the master branch
Repeated merge
$ git checkout featurey
edit README add another line
$ git add README
$ git commit -m "another line"
$ git checkout 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
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 log --follow --name-only FILENAME
Commits that were not merged yet
- commits on one branch but not on the other branch
$ git log fetureX --not master
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
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.
bisect - find broken commit
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:
#!/bin/bash
expr $1 - $2
-
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.
Working with remote repository
-
fork repository (service of the host)
-
git clone
-
git push
-
git pull (fetch and merge or fetch and rebase)
-
Send Pull-Request (service of the host)
-
git remote add upstream ..
-
git pull upstream master
Fork repository
- Visit particpants
- fork
Clone repository
cd ~/work
$ git clone https://github.com/cm-demo/participants.git
cd participants
git remote -v
origin https://github.com/cm-demo/participants (fetch)
origin https://github.com/cm-demo/participants (p.sh" %}
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
- Visit particpants
Make more changes and update the pull-request
git add .
git commit
git push
- Visit particpants
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
Resources
Thank you
- Gabor Szabo
- Help teams improve their development practices