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)
-
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)
-
Full copy of repository
-
No central control (good/bad)
-
No single point of failure
-
Easy branching
-
Easy merging
-
Fast
-
Allow off-line development
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
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
- 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
- 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 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
- 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
- Feature Branch
- Gitflow
- Forking Workflow
- Atlassian 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 <conflicted_files>", 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 <a@code-maven.com>
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 <file>..." to unstage)
(use "git add <file>..." 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
- Visit particpants
- fork
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
- 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
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
- SourceTree a free Git and Mercurical client for Windows and Mac
- The home of Git
- Pro Git book
- Git Guys
- Tutorial of Ralf Ebert
- Git reference site
- Leran Git branching interactive tutorial.
- Why aren't you using git-flow?
- git-flow, a successful Git branching model
- What git branching models actually work?
- How to use a scalable Git branching model called Gitflow (video)
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