"Git is a free & open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency."
First initialize the git repository:
git init
Then add files
git add -A # or git add . # or git add myfile.txt git add *.rb git add directory/
And commit the files
git commit
Git will ask you for a commit message.
Committing modified files. Stage the files (more on that later):
git add file.txt
And commit:
git commit
Or skip the staging step and immediately commit:
git commit -a
Removing a file
git rm file.txt
And commit
git commit
A.K.A. The Staging Area
The index contains the changes that will be added to your next commit. Your commit will not contain the changes in your working directory.
Only the changes that were added to the index will be in the commit
If you add a file, or a part of a file, to the index, a copy is made of that file. When you commit, it is that copy which ends up in the commit.
Important: if you make changes to the file after adding it to the index, those changes will not end up in your commit, unless you add the file again to the index!
If you modify files, you need to 'stage' them again, by running git add.
You can also add only parts of a modified file:
git add -p myfile.txt
Tip: use a tool for this
Checking the state of the working copy
git status
Seeing what has changed
`git diff` # shows the diff of your working copy `git diff --cached` # show the diff of your index
Tip: use a tool for this
Checking the log of your current branch
git log
git log has many options. For example:
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
results in:
Tip: add this in your global gitconfig as an alias:
[alias] l = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
See the log of all the branches:
git log --all
git checkout -- file.txt # the -- are optional, to tell git it's about a file git checkout directory/
But not
git checkout branch-name
Note: This gets back the version from the index. If the file is not in the index, it will get the file from the repository.
To reset the file after it was added to the index:
git reset file.txt
The working copy is now the version from the index and the file is not in the index anymore. Then you can use
git checkout file.txt
To completely reset it to the version in the repository.
git clean -f # to remove untracked files git clean -f -d # to remove untracked directories
git reset --hard # optionally followed by git clean -f
Use with care!
After a commit was already pushed out to other people.
git revert
This will create a commit to undo everything introduced by
Knowing this gives you a better understanding of Git
A Git repository is a collection of objects
Git stores the contents of a file in a 'blob'.
some examples:
$ git hash-object hello.txt ce6c1fd146f65c899e6b10e46c89097c644e3229 $ git hash-object say-hi.rb a8784b043f12b4b0c9114c55ebf33f5c9b44ce8f
A tree is like a directory
If you think about Git, think about commits!
Did I mention that you should think in terms of commits, when working with Git?
If you understand commits, you basically understand Git.
Because a commit hash is very difficult to remember and not really useful to work with, Git uses references to point to specific commits.
One such reference is HEAD
Another important reference is master
references to other commits
First create the branch
git branch mywork
Then start working on that branch:
git checkout mywork
Or combine the two steps
git checkout -b mywork
Deleting a local branch is also possible
git branch -d mywork
When you have been working on a (feature) branch for a while you will probably want to merge those branches back together.
# to merge the origin branch back into your mywork branch (to bring it up to date) # checkout the target branch git checkout mywork # merge the branch into the current branch git merge origin
Now your repository looks like this: (Notice that commit C7 has two parents)
When merging two or more branches there are two possibilities:
Git automatically proposes a commit message:
Merge branch 'mywork' into master
The commit has two or more parents
Merge the 'mywork' branch into origin
The origin branch is simply fast-forwarded
Sometimes you want a merge commit, even when a simple Fast Forward is possible.
git merge --no-ff
git show 2a2ea8d git show HEAD^^
Gives you a diff of what exactly that commit changed + metadata for that commit
You can checkout a commit to see the repository as it was back then
git checkout <commit-hash>
Note: Git will tell you that you are in a detached head state, because your HEAD reference doesn't point to any branch.
You can reset your branch to an earlier commit, to throw away the newer commit(s).
git reset HEAD^^
This will change the reference for the branch to that commit
Note: only do this if you have not yet pushed these newer commits
The reflog is a log of everything which happened in your repository
git reflog
Useful when you make a mistake during rebasing, merging or resetting.
Your old commits are not 'lost'. They are simply not used anymore and will be removed by git gc after a while.
Instead of merging (with merge commits) you can also rebase (so you can then fast forward)
Some people will tell you that this is very harmful, it can break your repository and destroy the universe. This is NOT TRUE. (At least if you know what you are doing)
By rebasing your commits you can actually rewrite your history:
Each commit which is rebased will get a new, different, hash. People (and Git) which pull this new hash will get confused.
If you do rebase a commit which was already pushed, Git will refuse the new commit, unless you use the --force option.
After modifying your index again:
git commit --amend
This is an alternative approach to merging, with a merge commit.
Let's reuse the example:
Now you want to merge 'mywork' into 'origin' without creating a merge commit
What we did before:
git checkout origin git merge mywork
What we will do now:
# on the mywork branch git rebase origin # fix any merge conflicts git checkout origin git merge mywork
# on the mywork branch git rebase origin
With interactive rebasing you can really rewrite history the way you want it to be. ...And break your repository.
Rebase the commits since the specified commit-hash
git rebase --interactive <commit-hash>
Suppose we have the following commits
To rebase the most recent 3 commits:
git rebase --interactive 4efd195
To share work with other people, you can add one or more remotes
Easiest method to set this up is by cloning an existing repository instead of initializing your repo.
git clone http://git.drupal.org/project/drupal.git
This will set up everything for you
Sometimes you will want to add a remote
This is done with the git remote command
git remote add github git@github.com:teranex/git-talk.git
This will add my Github repository for this presentation as a remote with the name github
You can add shortcuts in your git config for often used url's
git config --global url.git@github.com.insteadOf gh git config --global url.ssh://myuser@mydomain.net/path/to/repos/.insteadOf r
Then you can use short url's to clone
git clone gh:teranex/dotvim git clone r:myrepo.git
When you have cloned the repository:
git pull
This will pull in the changes from the current branch on the origin
git push
Will push your changes to the origin
However: This will only work for 'tracking' branches.
It is important to think about remote branches as just branches
By default, Git does not know, nor care, about relationships between branches!
Git just sees two branches
git pull github master git push github master
By default, no local branches are created for remote branches
You can get a good overview of all your local and remote branches and how they are tracking with: git branch -avv
To create a local branch based on a remote branch:
git checkout --track -b mywork github/mywork
To link an existing local branch to a remote branch:
git branch --setup-stream github/mywork
You can verify this in the git config file (.git/config) in your repository:
To better understand pulling, let's see what actually happens. Instead of using git pull, you also do (while on the master branch):
# pull in the new objects git fetch github # merge the remote branch with the local branch git merge github/master
This works exactly the same as merging two local branches!
Merging a remote branch in your local branch can create a useless merge commit:
You can avoid this by rebasing instead of merging:
git pull --rebase
or, if you want to do it manually:
git fetch github git rebase github/master
Sometimes you want to set aside your changes
to stash everything
git stash
Or you can give a description
git stash save "something fancy I was working on"
to see the stashes
git stash list
to get changes back from the stash
git stash pop
I often use the stash when I want to git pull --rebase, while I have uncommitted changes (git will refuse to do it in that case):
git stash git pull --rebase git stash pop
Include $(__git_ps1 ' %s ') in your $PS1
Git has plugins available to migrate from and/or integrate into other versioning systems as well. One such plugin is git-svn.
With git-svn you can:
To locally use Git and push to a central Subversion server:
First 'clone' the Subversion repository into a local Git repo
git svn clone -s http://svn.example.com/myproject # the -s means the subversion repo has a standard layout (trunk/ etc)
Now you can work as usual with your Git repository. Except... instead of running git pull to get the changes from other people, you now do:
git svn rebase
And you don't do git push, but:
git svn dcommit
To use the 'Git Flow' branching strategy a Git plugin is available: git-flow. This plugin makes it really easy to follow Git Flow:
First initialize it to configure the names. (I recommend to use the defaults)
git flow init
To start a feature branch:
git flow feature start my-exiting-feature
To publish the feature (push it to the remote)
git flow feature publish my-exiting-feature
To finalize the branch:
git flow feature finish my-exiting-feature
In a Git repository you can 'link' other repositories to subdirectories. This can be useful, for example when using external libraries or when building your VIM configuration, to pull in all the plugins.
Similar to svn:externals, but not quite the same:
# from the root of the repository git submodule add git://path/to/repo path/in/repo
This will modify the .gitmodules file, which is part of the parent repository, and record the exact commit which is checked out in the submodule. Now you can commit, push, etc.
When another repository pulls in the change the following steps are required:
git submodule init
This will update the .git/config file for that repository and register the submodule in the repository
git submodule update
This will check-out all the submodules to the correct commit
# or combine both steps git submodule update --init
To update you can checkout another commit in the submodule (for example by pulling) and commit the reference to this new commit in the parent repository.
To update all the modules, you can use something like:
git submodule foreach git pull origin master
Helping you to find which commit introduced the bug
Sometimes somebody will introduce bugs into your software or brake previously working features. Who knows, maybe even you! While trying to find the source of the problem, it can be useful to know which commit exactly introduced the troubles in paradise. That is exactly what Git bisect is for.
Let's say you know the feature was already broken in the previous commit and 10 commits ago the feature still worked.
Start Git Bisect and inform Git about this:
git bisect start git bisect bad HEAD^ git bisect good HEAD~10
Git will checkout the commit in the middle, so you can test:
Bisecting: 5 revisions left to test after this (roughly 3 steps)
After testing you inform Git about the result
git bisect good # when the feature worked git bisect bad # when the feature is b0rken
After you have found the bad commit, reset your repository:
git bisect reset
If you write a script which can verify each commit, you can let Git run it for every commit!