- What is Git?
- A Short History of Git
- Git Config
- Use https instead of git to push / clone
- Create and Initialize Repository
- First commit => HEAD
- Show the working tree status
- Branching
- Push to GitHub
- Manage set of tracked repositories
- Pull in upstream changes
- Pull VS Fetch & Merge
- Push tags
- Rebase
- Stashing
- Replace local changes
- Git Tips
- Git Resources
What is Git?
Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.
A Short History of Git
As with many great things in life, Git began with a bit of creative destruction and fiery controversy. The Linux kernel is an open source software project of fairly large scope. For most of the lifetime of the Linux kernel maintenance (1991–2002), changes to the software were passed around as patches and archived files. In 2002, the Linux kernel project began using a proprietary DVCS system called BitKeeper.
In 2005, the relationship between the community that developed the Linux kernel and the commercial company that developed BitKeeper broke down, and the tool’s free-of-charge status was revoked. This prompted the Linux development community (and in particular Linus Torvalds, the creator of Linux) to develop their own tool based on some of the lessons they learned while using BitKeeper. Some of the goals of the new system were as follows:
- Speed
- Simple design
- Strong support for non-linear development (thousands of parallel branches)
- Fully distributed
- Able to handle large projects like the Linux kernel efficiently (speed and data size)
Since its birth in 2005, Git has evolved and matured to be easy to use and yet retain these initial qualities. It’s incredibly fast, it’s very efficient with large projects, and it has an incredible branching system for non-linear development.
Git Config
git config - Get and set repository or global options
Git comes with a tool called git config that lets you get and set configuration variables that control all aspects of how Git looks and operates. These variables can be stored in three different places:
- /etc/gitconfig file: Contains values for every user on the system and all their repositories. If you pass the option--system to git config, it reads and writes from this file specifically.
- ~/.gitconfig file: Specific to your user. You can make Git read and write to this file specifically by passing the --global option.
- config file in the git directory (that is, .git/config) of whatever repository you’re currently using: Specific to that single repository. Each level overrides values in the previous level, so values in .git/config trump those in /etc/gitconfig.
Example
git config --global user.name "Terry Wang" git config --global user.email 'terry.wang@linux.ninja' git config --global core.editor vim git config --global diff.tool vimdiff git config --global difftool.prompt false git config --global merge.tool vimdiff git config --global alias.d difftool
Check config
git config --list git config -l
Set git push policy
push.default
Defines the action git push should take if no refspec is given on the command line, no refspec is configured in the remote, and no refspec is implied by any of the options given on the command line. Possible values are:
nothing - do not push anything.
- matching - push all branches having the same name in both ends. This is for those who prepare all the branches into a publishable shape and then push them out with a single command. It is not appropriate for pushing into a repository shared by multiple users, since locally stalled branches will attempt a non-fast forward push if other users updated the branch. + This is currently the default, but Git 2.0 will change the default to simple.
- upstream - push the current branch to its upstream branch (tracking is a deprecated synonym for this). With this, git push will update the same remote ref as the one which is merged by git pull, making push and pull symmetrical. See "branch.<name>.merge" for how to configure the upstream branch.
- simple - like upstream, but refuses to push if the upstream branch's name is different from the local one. This is the safest option and is well-suited for beginners. It will become the default in Git 2.0.
- current - push the current branch to a branch of the same name.
The simple, current and upstream modes are for those who want to push out a single branch after finishing work, even when the other branches are not yet ready to be pushed out. If you are working with other people to push into the same shared repository, you would want to use one of these.
NOTE: per repository git config settings are stored in .git/config
Use https instead of git to push / clone
It's easier to clone/pull/fetch and push via HTTPS if you are behind proxy.
git config --global url."https://".insteadOf git://
This adds the following in ~/.gitconfig
[url "https://"] insteadOf = git://
Refer to => http://git-scm.com/docs/git-config
url.<base>.insteadOf
Any URL that starts with this value will be rewritten to start, instead, with <base>. In cases where some site serves a large number of repositories, and serves them with multiple access methods, and some users need to use different access methods, this feature allows people to specify any of the equivalent URLs and have Git automatically rewrite the URL to the best alternative for the particular user, even for a never-before-seen repository on the site. When more than one insteadOf strings match a given URL, the longest match is used.
url.<base>.pushInsteadOf
Any URL that starts with this value will not be pushed to; instead, it will be rewritten to start with <base>, and the resulting URL will be pushed to. In cases where some site serves a large number of repositories, and serves them with multiple access methods, some of which do not allow push, this feature allows people to specify a pull-only URL and have Git automatically use an appropriate URL to push, even for a never-before-seen repository on the site. When more than one pushInsteadOf strings match a given URL, the longest match is used. If a remote has an explicit pushurl, Git will ignore this setting for that remote.
Create and Initialize Repository
git init - Create an empty git repository or reinitialize an existing one
Create directory
mkdir -p /bea/git/ipsearch cd /btrfs/git/ipsearch terry@ubuntu:/bea/git/ipsearch$ git status # On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .gitignore # ip/ # scripts/ nothing added to commit but untracked files present (use "git add" to track)
Create an empty git repository or reinitialize an existing one
git init
git add - Add file contents to the index (staging area)
It is common to recursively add all files in a new project by specifying the current working directory like below:
Add all file contents to the index (staging area)
git add .
Since Git will recursively add all files under a directory specified, if you give it the current working directory, it will simply start tracking every file there. In this case, a git add . would have done the same thing as a "git add *", but that's only because we don't have subdirectories which the * would not recurse into.
First commit => HEAD
HEAD => points to the last commit you have made
git commit - Record changes to the repository (records snapshots of an index / staging area)
First commit
git commit -m 'initial commit'
Example
terry@ubuntu:/bea/git/ipsearch$ git commit -m 'initial commit' [master (root-commit) 53f6462] initial commit 47 files changed, 3330 insertions(+) create mode 100644 .gitignore create mode 100755 ip/AboutBox.java create mode 100755 ip/I18n.java create mode 100755 ip/IPEntry.java create mode 100755 ip/IPSearch.java create mode 100755 ip/IPSearchUI.java create mode 100755 ip/IPSeeker.java create mode 100755 ip/QQ.java create mode 100755 ip/QQWry.dat create mode 100755 ip/UI.java create mode 100755 ip/Utils.java create mode 100755 ip/about.png create mode 100755 ip/icons/add_att.gif create mode 100755 ip/icons/clear_co.gif create mode 100755 ip/icons/console_view.gif create mode 100755 ip/icons/debug_exc.gif create mode 100755 ip/icons/delete_obj.gif create mode 100755 ip/icons/discovery.gif create mode 100755 ip/icons/eclipse_launcher.gif create mode 100755 ip/icons/expression_obj.gif create mode 100755 ip/icons/find_obj.gif create mode 100755 ip/icons/ftr_jar_obj.gif create mode 100755 ip/icons/help.gif create mode 100755 ip/icons/insp_sbook.gif create mode 100755 ip/icons/occ_read.gif create mode 100755 ip/icons/output_folder_attrib.gif create mode 100755 ip/icons/quickassist_obj.gif create mode 100755 ip/icons/rem_co.gif create mode 100755 ip/icons/remove_exc.gif create mode 100755 ip/icons/rep_editors_view.gif create mode 100755 ip/icons/run_exc.gif create mode 100755 ip/icons/save_edit.gif create mode 100755 ip/icons/synced.gif create mode 100755 ip/icons/synch_participants.gif create mode 100755 ip/icons/types.gif create mode 100755 ip/icons/updates_obj.gif create mode 100755 ip/ipsearch.conf create mode 100755 ip/locale/IPSearch.properties create mode 100755 ip/locale/IPSearch_en.properties create mode 100755 ip/locale/IPSearch_zh.properties create mode 100755 ip/locale/IPSearch_zh_CN_gbk.properties create mode 100755 scripts/IPSearch.bat create mode 100755 scripts/IPSearch.sh create mode 100755 scripts/QQWry.dat create mode 100755 scripts/Read.txt create mode 100755 scripts/ipsearch.conf create mode 100755 scripts/setenv.bat Pull before Push (github repository has a README.md generated when creating the repository, it is the initial commit, need to merge!!!) terry@ubuntu:/bea/git/ipsearch$ git pull git@github.com:terrywang/ipsearch.gitwarning: no common commits remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From github.com:terrywang/ipsearch * branch HEAD -> FETCH_HEAD Merge made by the 'recursive' strategy. README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md
Removing files
git rm - Remove files from the working tree AND from the index/staging area
NOTE: By default, a git rm file will remove the file from the staging area entirely and also off your disk (the working directory). To leave the file in the working directory, you can use git rm --cached .
git rm ip/QQWry.dat git rm scripts/Read.txt git status git commit -m "Removing 2 useless files" git push -u origin master
git rm --cache => Remove files from the index ONLY - leave the file in the working directory
--cached Use this option to unstage and remove paths only from the index. Working tree files, whether modified or not, will be left alone. git rm --cached file
Show the working tree status
git status - show the working tree status
view the status of your files in the working directory and staging area
git status
Example
terry@ubuntu:/bea/git/ipsearch$ git status # On branch master nothing to commit (working directory clean)
Branching
Creating branch
# create new branch git branch new_feature_x # switch to new branch git checkout new_feature_x # switch back to master git checkout master
OR
git checkout -b new_feature_x master
This is equivalent to
git branch new_feature_x git checkout new_feature_x
Deleting branch
Delete branch
git branch -d new_feature_x # regardless of merge status git branch -D new_feature_x
NOTE:
-d, --delete
Delete a branch. The branch must be fully merged in its upstream branch, or in HEAD if no upstream was set with --track or --set-upstream.
-D
Delete a branch irrespective of its merged status.
List branches
List branches
git branch -v git branch -av
Push To Remote branch
git push origin new_feature_x
Someone else needs to get updated repo (commits)
git fetch origin
Tracking Remote Branches
Create a local branch to track origin new branch
$ git checkout -b new_feature_x origin/new_feature_x Branch serverfix set up to track remote branch refs/remotes/origin/new_feature_x. Switched to a new branch "new_feature_x"
Same branch name
$ git checkout --track origin/serverfix Branch serverfix set up to track remote branch refs/remotes/origin/serverfix. Switched to a new branch "serverfix"
To set up a local branch with a different name than the remote branch, you can easily use the first version with a different local branch name
$ git checkout -b sf origin/serverfix Branch sf set up to track remote branch refs/remotes/origin/serverfix. Switched to a new branch "sf"
Now, your local branch sf will automatically push to and pull from origin/serverfix.
Deleting Remote branches
git push origin --delete branch_to_delete # preferred - easier to remember git push origin :branch_to_delete
Push to GitHub
git push - push your new branches and data to a remote repository
Push to github
git remote add origin git@github.com:terrywang/ipsearch.git git push -u origin master
NOTE: Use -u for the first push.
-u, --set-upstream
For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git-pull and other commands. For more information, see branch.<name>.merge in git-config.
Example
terry@ubuntu:/bea/git/ipsearch$ git push -u origin master Counting objects: 59, done. Delta compression using up to 4 threads. Compressing objects: 100% (55/55), done. Writing objects: 100% (57/57), 2.39 MiB | 498 KiB/s, done. Total 57 (delta 3), reused 0 (delta 0) To git@github.com:terrywang/ipsearch.git 51e9756..208f12f master -> master Branch master set up to track remote branch master from origin.
More git push examples
Find a ref that matches master in the source repository (most likely, it would find refs/heads/master), and update the same ref (e.g. refs/heads/master) in origin repository with it. If master did not exist remotely, it would be created.
git push origin master
A handy way to push the current branch to the same name on the remote.
git push origin HEAD
Push the current branch to the remote ref matching master in the origin repository. This form is convenient to push the current branch without thinking about its local name.
git push origin HEAD:master
Manage set of tracked repositories
git remote - list, add and delete remote repository aliases
Example
$ git remote -v origin git@github.com:terrywang/ipsearch.git (fetch) origin git@github.com:terrywang/ipsearch.git (push)
Octopress use case, need to update Octopress (to the latest commits) - git remote add
If current working directory is cloned from terrywang.github.com repository, initially the .git/config looks like below
$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = https://github.com/terrywang/terrywang.github.com.git [branch "master"] remote = origin merge = refs/heads/master [branch "source"] remote = origin merge = refs/heads/source
Add the remote octopress repository using git add
git add remote octopress git@github.com:terrywang/terrywang.github.com.git
Check what's been added
$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = https://github.com/terrywang/terrywang.github.com.git [branch "master"] remote = origin merge = refs/heads/master [branch "source"] remote = origin merge = refs/heads/source [remote "octopress"] url = git@github.com:terrywang/terrywang.github.com.git fetch = +refs/heads/*:refs/remotes/octopress/*
git pull - fetch from a remote repo and try to merge into the current branch
then
git pull octopress master
Merge
To merge another branch into your active branch (e.g. master), use
git merge branch
in both cases git tries to auto-merge changes. Unfortunately, this is not always possible and results in conflicts. You are responsible to merge those conflicts manually by editing the files shown by git.
After changing, you need to mark them as merged with
git add file
before merging changes, you can also preview them by using
git diff source_branch target_branch
Push new octopress version to remote source branch
git push origin source
Octopress update done.
Pull in upstream changes
Configure remotes
When a repository is cloned, it has a default remote called origin that points to your fork on GitHub, not the original repository it was forked from. To keep track of the original repository, you need to add another remote named upstream
# Changes the active directory in the prompt to the newly cloned "Spoon-Knife" directory cd Spoon-Knife # Assigns the original repository to a remote called "upstream" git remote add upstream https://github.com/octocat/Spoon-Knife.git # Pulls in changes not present in your local repository, without modifying your files git fetch upstream
NOTE: A remote is a repository stored on another computer, in this case on GitHub's server. It is standard practice (and also the default when you clone a repository) to give the name origin to the remote that points to your main offsite repository (for example, your GitHub repository).
Git supports multiple remotes. This is commonly used when forking a repository.
Pull in changes from upstream
If the original repository forked (your project from) gets updated, the changes can be added the fork by running the following
# Fetches any new changes from the original repository git fetch upstream # Merges any changes fetched into your working files git merge upstream/master
Pull VS Fetch & Merge
There are two ways to get commits from a remote repository or branch: git fetch and git pull. While they might seem similar at first, there are distinct differences you should consider.
Pull
# Pulls commits from 'upstream' and stores them in the local repository git pull upstream master
IMPORTANT: When you use git pull, git tries to automatically do your work for you. It is context sensitive, so git will merge any pulled commits into the branch you are currently working in. One thing to keep in mind is that git pull automatically merges the commits without letting you review them first. If you don't closely manage your branches you may run into frequent conflicts.
NOTE: If the project has tags that have NOT been merged to master, you should do: git fetch upstream --tags
Fetch & Merge
# Fetches any new commits from the original repository git fetch upstream # Merges any fetched commits into your working files git merge upstream/master
NOTE: When you git fetch, git retrieves any commits from the target remote that you do not have and stores them in your local repository. However, it does not merge them with your current branch. This is particularly useful if you need to keep your repository up to date but are working on something that might break if you update your files. To integrate the commits into your local branch, you use git merge. This combines the specified branches and prompts you if there are any conflicts.
Use case - keep forked repositories up-to-date with upstream
git remote add upstream REPO git fetch upstream # Switch to a different branch git branch -D master git checkout -b master upstream/master
NOTE: This is the recommended way to keep the master branch in the forked repository up-to-date with upstream.
Push tags
Get latest commits and new tags from upstream
git fetch upstream
Push tags to the forked project repository
git push origin --tags
NOTE: git push --mirror will push all branches and tags to remote repository.
Rebase
Use case - use git rebase to squash multiple commits into 1 singe commit (especially useful for GitHub Pull Requests).
Commits to be squashed
git log commit c7820790e44a50d058d3bf4378eb682f87e782e6 Author: Terry Wang <i@terry.im> Date: Fri May 23 21:53:58 2014 +1000 kpatch: make kpatch load & unload work on more distros with less code commit c795b5d1535aeab7a9a83b3a223cc68a65760c04 Author: Terry Wang <i@terry.im> Date: Fri May 23 21:26:04 2014 +1000 kpatch: simple fix to make rmmod path distro independent commit ee7d5b5b4d12be557b27349434ea9ebfeb002a25 Author: Terry Wang <i@terry.im> Date: Fri May 23 21:22:40 2014 +1000 kpatch: simple fix to make insmod path distro independent
Use interactive rebase to squash the last 3 commits
git rebase -i HEAD~3
A new editor screen, Vim in this case will be opened, see below
pick ee7d5b5 kpatch: simple fix to make insmod path distro independent pick c795b5d kpatch: simple fix to make rmmod path distro independent pick c782079 kpatch: make kpatch load & unload work on more distros with less code # Rebase 24fba4f..c782079 onto 24fba4f # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Change to
pick ee7d5b5 kpatch: simple fix to make insmod path distro independent squash c795b5d kpatch: simple fix to make rmmod path distro independent squash c782079 kpatch: make kpatch load & unload work on more distros with less code
Save and quit by using :wq 2 and 3 will be merged into 1.
NOTE: squash can only be backwards, squash latest commits to the one before them.
After rebasing, another Vim screen will show up (for editing commit message!)
# This is a combination of 3 commits. # The first commit's message is: kpatch: simple fix to make insmod path distro independent # This is the 2nd commit message: kpatch: simple fix to make rmmod path distro independent # This is the 3rd commit message: kpatch: make kpatch load & unload work on more distros with less code # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # rebase in progress; onto 24fba4f # You are currently editing a commit while rebasing branch 'ubuntu' on '24fba4f'. # # Changes to be committed: # modified: kpatch/kpatch #
Comment or delete what is NOT wanted. Save and it’s done!
git rebase -i HEAD~3 [detached HEAD 2b8d6ab] kpatch: make kpatch load & unload work on more distros with less code 1 file changed, 3 insertions(+), 3 deletions(-) Successfully rebased and updated refs/heads/ubuntu.
NOTE: man git-rebase for more.
Stashing
Use git stash to quickly save some changes that you are NOT ready to be committed or saved, but want to come back later.
git-stash - Stash the changes in a dirty working directory away
Add the current changes to the stack (revert to HEAD)
git stash save
After fetching and merging with remote repository (or just pull)
git pull
Apply the changes
git stash apply # when no <stash> is given, stash@{0} is assumed, otherwise <stash> must be a reference of the form stash@{<revision>}. git stash apply stash@{0}
NOTE: If you want to remove the item from the stack when applying, use git stash pop instead.
More
git stash list git stash drop
An example from manpage:
Pulling into a dirty tree
When you are in the middle of something, you learn that there are upstream changes that are possibly relevant to what you are doing. When your local changes do not conflict with the changes in the upstream, a simple git pull will let you move forward.
However, there are cases in which your local changes do conflict with the upstream changes, and git pull refuses to overwrite your changes. In such a case, you can stash your changes away, perform a pull, and then unstash, like this:
$ git pull ... file foobar not up to date, cannot merge. $ git stash $ git pull $ git stash pop
Replace local changes
In case you did something wrong (which for sure never happens ;) you can replace local changes using the command
git checkout -- file
this replaces the changes in your working tree with the last content in HEAD. Changes already added to the index, as well as new files, will be kept.
If you instead want to drop all your local changes and commits, fetch the latest history from the server and point your local master branch at it like this
git fetch origin git reset --hard origin/master
Git Tips
Every time files are modified, updated, run
- git status
- git log
- git reflog
then add them by using git add or git rm (git rm --cached) before commit. Finally run git push to github.
MUST know
git add -p -p, --patch Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the index. git add -i -i, --interactive Add modified contents in the working tree interactively to the index. Optional path arguments may be supplied to limit operation to a subset of the working tree. See "Interactive mode" for details. git rebase -i -i, --interactive Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits (see SPLITTING COMMITS below). git cherry-pick Apply the changes introduced by some existing commits
Life Saver
git reflog - Manage reflog information
git cherry-pick - Apply the changes introduced by some existing commits
NOTE: it is extremely helpful when accidentally git reset --hard, this command can help you find the commit hash, use git cherry-pick to back to the future!
Proxy
Set HTTP Proxy
git config --global http.proxy www-proxy.au.oracle.com:80
Disable SSL certification verification
git config --global http.sslVerify false
OR specify the certification path
git config --global http.sslCAinfo /opt/goagent/CA.crt
tig - text-mode interface for git
gitk - Git repository browser
Push to multiple git repos (GitHub + Heroku)
Modify .git/config file in working directory
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "heroku"] url = git@heroku.com:maxburstein.git fetch = +refs/heads/*:refs/remotes/heroku/* [remote "github"] url = git@github.com:mburst/burstolio.git fetch = +refs/heads/*:refs/remotes/github/* [remote "origin"] url = git@heroku.com:maxburstein.git url = git@github.com:mburst/burstolio.git
The "heroku" and "github" remotes are generated by git and are created in the setup instructional steps of each service respectively. I went ahead and manually added the remote "origin" and just copied the url variable from the other remotes. You'll now be able to push to both by calling "git push origin" and then you can fetch from each one as individually needed.
Git Resources
- Git Documentation
- Git Reference
- GitHub Help https://help.github.com/
- git - the simple guide - 1 page
- A Visual Git Reference http://marklodato.github.io/visual-git-guide/
- Cheat sheets http://teach.github.com/articles/git-cheatsheets/
- slides http://teach.github.com/articles/course-slides/
- Presentations http://training.github.com/resources/presentations/
- Git Resources - github:training
- Pro Git http://git-scm.com/book
- The Git Community Book http://alx.github.com/gitbook/
- Git Tutorial and Training by Atlassian
- Slides by @ihower
Yet Another Introduction to Git http://www.slideshare.net/ihower/git-coscup2013
Git Tutorial http://www.slideshare.net/ihower/git-tutorial-13695342
Git and GitHub http://www.slideshare.net/ihower/git-and-github-7306407 - Git by ihower http://ihower.tw/git/
- 5 Useful Git Tips http://adit.io/posts/2013-08-16-five-useful-git-tips.html