Terry : Git

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

NOTEA 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

http://adit.io/posts/2013-08-16-five-useful-git-tips.html