All notes




## Generic

## Xcode
#ignore private workspace stuff added by Xcode4

# Precompiled Headers
# Compiled Dynamic libraries
# Fortran module files
# Compiled Static libraries
# Executables


Global .gitignore ignoring files.

Create the file at ~/.gitignore_global and add some rules to it. Then run the following command in your terminal:

git config --global core.excludesfile ~/.gitignore_global

Explicit repository excludes

If you don't want to create a .gitignore file to share with others, you can create rules that are not committed with the repository. You can use this technique for locally-generated files that you don't expect other users to generate, such as files created by your editor.

Use your favorite text editor to open the file called .git/info/exclude within the root of your Git repository. Any rule you add here will not be checked in, and will only ignore files for your local repository.


SYNOPSIS: .git/info/attributes.

pattern    attr1 attr2 ...

Each attribute can be in one of these states for a given path:

Precedence: When more than one pattern matches the path, a later line overrides an earlier line. This overriding is done per attribute.


git-scm: Git Hooks.

Exiting non-zero from this hook aborts the commit, although you can bypass it with git commit --no-verify.

  1. Append this file to your .git/hooks/pre-commit file (or replace it if your file is empty).
  2. chmod +x pre-commit to ensure its executable.


# Check for any debugging statements that were added in the commit
git diff --cached --name-only | \
	grep -E $FILES_PATTERN | \
	GREP_COLOR='4;5;37;41' xargs egrep -w --color --with-filename -n "$FORBIDDEN" && \
	echo 'COMMIT REJECTED: debugging statement found' && \
	exit 1

# Check for any syntax errors
set -o pipefail
git diff --cached --name-only | \
	grep -E $FILES_PATTERN | if !(xargs ruby -c); then
					echo 'COMMIT REJECTED: syntax error' && exit 1; fi;
if [[ $? -eq 1 ]]; then exit 1; fi

exit 0

Best practices

Write good commit messages How to Write a Git Commit Message. Ref.

Why write messages


A properly formed Git commit subject line should always be able to complete the following sentence "If applied, this commit will xxxx":

Good example:

If applied, this commit will refactor subsystem X for readability
If applied, this commit will update getting started documentation
If applied, this commit will remove deprecated methods
If applied, this commit will release version 1.0.0
If applied, this commit will merge pull request #123 from user/branch

Notice how this doesn’t work for the other non-imperative forms:

If applied, this commit will fixed bug with Y
If applied, this commit will changing behavior of X
If applied, this commit will more fixes for broken stuff
If applied, this commit will sweet new API methods


In most cases, you can leave out details about how a change has been made. Code is generally self-explanatory in this regard (and if the code is so complex that it needs to be explained in prose, that’s what source comments are for).
Just focus on making clear the reasons why you made the change in the first place—the way things worked before the change (and what was wrong with that), the way they work now, and why you decided to solve it the way you did.

Structure it in the format:

Short (50 chars or less) summary of changes

More detailed explanatory text, if necessary.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, preceded by a
   single space, with blank lines in between, but conventions vary here

What to do when commit first before pull

What if there is new commit when you are editing codes, and git tells you "commit first before pull"?

Stash way

See SO: git commit before merge.

Use git stash when you want to record the current state of the working directory and the index, but want to go back to a clean working directory.

git stash --all
# or git stash --include-untracked
git pull origin master
git stash pop

Rebase way

One caveat with stashing is that it doesn't create an entry in the reflog. So, if you screw up and lose your stash, you can't recover it. In some ways a commit is "safer." You can also git pull --rebase after a commit rather than stashing.

With --rebase, it runs git rebase instead of git merge.

Rebase vs Merge merging vs rebasing.

Feature & Bug Branches


Merge to master

If you're using your own dev branch, you'll need to git pull --rebase on master first, and then git rebase your branch off of master. The workflow looks something like this:

echo "Moving to master and pulling w/ rebase there...";
git checkout master;
git pull --rebase;

echo "Returning to $branch and rebasing off of master...";
git checkout $branch;
git rebase master;

# Then when you're ready to commit.
# You'll need to bring your changes over to master (using git cherry-pick, for example) and push from there.

Environment branches: staging and production

Best Practices with Environment Branches:

Pull request workflow

# Add 'upstream' repo to list of remotes
git remote add upstream

# Verify the new remote named 'upstream'
git remote -v

# Fetch from upstream remote
git fetch upstream

# View all branches, including those from upstream
git branch -va

# Checkout your master branch and merge upstream
git checkout master
git merge upstream/master


# Checkout the master branch - you want your new branch to come from master
git checkout master

# Create a new branch named newfeature (give your branch its own simple informative name)
git branch newfeature
# Switch to your new branch
git checkout newfeature
## Or
## git co -b newfeature


# Fetch upstream master and merge with your repo's master branch
git fetch upstream
git checkout master
git merge upstream/master

# If there were any new commits, rebase your development branch
git checkout newfeature

# git rebase master
git rebase -i master
# Use s(squash) or f(fixup) to merge commits.

# It is save to push by force to your own branch for PR.
git push --force

Get the diff from PR

git checkout -b mylocalbranch
git pull origin pull/921/head

To view a commit as a diff/patch file, just add .diff or .patch to the end of the URL, for example:

Use https or git? A comparison of protocols offered by GitHub.

plain Git, aka git://
HTTPS, aka
SSH, aka or ssh://

HTTPS works practically everywhere, even in places which block SSH and plain-Git protocols. In some cases, it can even be a little faster than SSH, especially over high-latency connections.

Using ssh over the https port using ssh over the https port.

Sometimes, firewalls refuse to allow SSH connections entirely.

# wcfNote: I testes, but failed to ssh. Don't know why.
# To test if SSH over the HTTPS port is possible, run this SSH command:
ssh -T -p 443

# edit the file at ~/.ssh/config, and add this section:
# Host
#   Hostname
#   Port 443

# You can test that this works by connecting once more to GitHub:
ssh -T




HEAD is reference to the current commit, which is a commit on top of which "git commit" would make a new one.

ORIG_HEAD is previous state of HEAD, set by commands that have possibly dangerous behavior, to be easy to revert them.

Reflog: HEAD@{1} is roughly equivalent to ORIG_HEAD (HEAD@{1} is always last value of HEAD, ORIG_HEAD is last value of HEAD before dangerous operation).

Cannot lock the ref 'ORIG_HEAD'

error: unable to resolve reference ORIG_HEAD: Success
fatal: Cannot lock the ref 'ORIG_HEAD'.


I had this problem, and I solved it by removing the file .git/ORIG_HEAD then doing the pull again. The .git/ORIG_HEAD file was 0 bytes instead of the git reference it was supposed to contain, so I just got rid of it.

Revision selection


##### reflogs, record when the tips of branches and other references were updated in the local repository.
# See the fifth prior value of the HEAD of your repository
git show HEAD@{5}
# See where your master branch was yesterday, you can type
git show master@{yesterday}
# If your local history is more than 2 months ago:
git show HEAD@{2.months.ago}

# Show the parent of HEAD:
git show HEAD^
# d921970^2 means "the second parent of d921970." This syntax is only useful for merge commits, which have more than one parent.

# The following are the same:
git show HEAD~3
git show HEAD^^^

##### Double dots
# master..experiment: all commits reachable by experiment that aren’t reachable by master.
git log master..experiment
# Shows any commits in your current branch that aren’t in the master branch on your origin remote.
git log origin/master..HEAD
# The following are equivalant:
git log refA..refB
git log ^refA refB
git log refB --not refA

##### Triple Dot
# Show all the commits that are reachable by either of two references but not by both of them.
# If you want to see what is in master or experiment but not any common references, you can run
git log master...experiment



The "index" holds a snapshot of the content of the working tree, and it is this snapshot that is taken as the contents of the next commit. Thus after making any changes to the working tree, and before running the commit command, you must use the add command to add any new or modified files to the index.

Git add to ignore untracked files.


# -u, --update. stages modified and deleted, without new.
git add -u

# stages new and modified, without deleted
git add .

# stages All
# equivalent to  git add .; git add -u.
git add -A

# Commit only the modified and deleted files.
git commit -a


Apply a series of patches from a mailbox.


# To generate your patch do the following:
git format-patch --stdout first_commit^..last_commit > changes.patch
git format-patch --stdout HEAD^ > changes.patch

# Now when you are ready to apply the patches:
git am -3 < changes.patch
# the -3 will do a three-way merge if there are conflicts. At this point you can do a git mergetool if you want to go to a gui or just manually merge the files using vim (the standard <<<<<<, ||||||, >>>>>> conflict resolution).

git : empty ident name (for ..) not allowed


This diff file is a plain diff, not something generated by git format-patch. A format-patch generated diff contains information about the commit author, author date, commit message, ... and maintains this information when creating the commit(s).

Your plain diff does not have these information, so git am will complain that it is missing naturally.

You should instead have used git apply which is meant to apply simple patch files like you have in this case which will only apply the patch to the worktree (and index if you tell it to) but does not create any commit automatically.


Show what revision and author last modified each line of a file.

git blame "filename"


# -r, remote branches
# -a, all branches
git branch -av

# --contains, shows only the branches that contain the named commit (in other words, the branches whose tip commits are descendants of the named commit)
# --merged, only branches merged into the named commit (i.e. the branches whose tip commits are reachable from the named commit) will be listed.

# New branch
git chechout -b [name_of_new_branch]

# Rename current local branch
git branch -m newName
# Rename local branch "oldName"
git branch -m oldName newName

# Delete local branch
git branch -d [name]
# Delete local branch by force
git branch -D [name]
# Delete remote branch
git push origin :[name]

# As of Git 1.8.0:
git branch -u upstream/foo
# Or, if local branch foo is not the current branch:
git branch -u upstream/foo foo

# As of Git 1.7.0:
git branch --set-upstream foo upstream/foo

Squash commits on a branch


# Create a temp branch for merge squashing
git checkout -b tempBranch
git merge --squash myBranch
git commit # without -m 

# Rename
git br -m myBranch myBranch.old
git br -m tempBranch myBranch

# Merge to master
git checkout master
git merge tempBranch

“The branch 'x' is not fully merged” Error


Branch deletion with -d only considers the current HEAD in determining if the branch is "fully merged". It will complain even if the branch is merged with some other branch. The error message could definitely be clearer in this regard... You can either checkout the merged branch before deleting, or just use git branch -D.

Change HEAD on remote repo



# WcfNote: must be on the server:
git symbolic-ref HEAD refs/heads/published


# If --verbose is specified, the output is a series of lines of the form:
# <source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>
# wcfNote: we should always add -v.
git check-ignore -v someFileIgnored

git check-ignore -v
# .git/info/


The git checkout command serves three distinct functions: checking out files, checking out commits, and checking out branches.

# -t, --track: When creating a new branch, set up "upstream" configuration.
git checkout -t orig_remote/RXXX

Resolve conflict

git checkout --theirs/ours conflictedFile
# Note that during git rebase and git pull --rebase, ours and theirs may appear swapped; --ours gives the version from the branch the changes are rebased onto, while --theirs gives the version from the branch that holds your work that is being rebased.

# When resloving during rebase merge:
git checkout --ours conflictedFile
git add conflictedFile
git rebase --continue

Undoing changes undoig changes.


Given one or more existing commits, apply the change each one introduces, recording a new commit for each.

The CHERRY_PICK_HEAD ref is set to point at the commit that introduced the change that is difficult to apply. cherry picking explained.

git cherry-pick topic^             (1)
git diff                           (2)
git reset --merge ORIG_HEAD        (3)
git cherry-pick -Xpatience topic^  (4)

1. apply the change that would be shown by git show topic^. In this example, the patch does not apply cleanly, so information about the conflict is written to the index and
working tree and no new commit results.
2. summarize changes to be reconciled
3. cancel the cherry-pick. In other words, return to the pre-cherry-pick state, preserving any local modifications you had in the working tree.
4. try to apply the change introduced by topic^ again, spending extra time to avoid mistakes based on incorrectly matching context lines.


Removes untracked files from your working directory.

# -n: Dry run
# -f: force. Will not remove untracked folders or files specified by .gitignore.

# First see what will removed with -n, if it is OK then remove them with -f.
git clean -n
git clean -f


Shallow copy

Ref1, Ref2. Use git clone --depth to do a shallow copy. Git 1.9/2.0 (Q1 2014) has removed that limitation that you cannot clone or fetch from it, nor push from nor into it.

Clone a specific tag

git clone ...
# Checkout and create a branch (otherwise you will be on a branch named after the revision number of tag):
git checkout tags/tag_name -b branch_name


# --amend: Combine the staged changes with the previous commit and replace the previous commit with the resulting snapshot.
# Suppose there is no staged change, and you just wanna rewrite commit message:
git commit --amend

# --no-edit: do not edit the commit message. Use it when you forgot one/more files in last commit.
git add missingFile
git commit --amend --no-edit

Rewriting history rewritting history.


git config -l

# --edit
git config -e
git config --global -e

git config --get
git config --get

git config --global --get http.proxy
git config --global --unset http.proxy


# Search commit message:
git log --grep=pattern

git log --author

# Show branch merging trees
git log --pretty=format:'%h %s' --graph

# See reflog information formatted like the git-log output
git log -g

# Limits the number of commits to show to 3.
git log -3
# --oneline: Show only the title sentence.
git log --oneline -5 --author cbeams --before "Fri Mar 26 2009"

# --abbrev-commit: the output will use shorter values but keep them unique; it defaults to using seven characters
git log --abbrev-commit --pretty=oneline
# ca82a6d changed the version number
# 085bb3b removed unnecessary test code

git log v2.6.12.. include/scsi drivers/scsi
    Show all commits since version v2.6.12 that changed any file in the include/scsi or drivers/scsi subdirectories

git log --since="2 weeks ago" -- gitk
    Show the changes during the last two weeks to the file gitk. The “--” is necessary to avoid confusion with the branch named gitk

git log --name-status release..test
    Show the commits that are in the "test" branch but not yet in the "release" branch, along with the list of paths each commit modifies.

git log --follow builtin/rev-list.c
    Shows the commits that changed builtin/rev-list.c, including those commits that occurred before the file was given its present name.

git log --branches --not --remotes=origin
    Shows all commits that are in any of local branches but not in any of remote-tracking branches for origin (what you have that origin doesn’t).

git log master --not --remotes=*/master
    Shows all commits that are in local master but not in any remote repository master branches.

git log -p -m --first-parent
    Shows the history including change diffs, but only from the “main branch” perspective, skipping commits that come from merged branches, and showing full diffs of changes introduced by the merges. This makes sense only when following a strict policy of merging all topic branches when staying on a single integration branch.

git log -L '/int main/',/^}/:main.c
    Shows how the function main() in the file main.c evolved over time.


Summarizes git log output in a format suitable for inclusion in release announcements. Each commit will be grouped by author and title.

git log --pretty=short
# Or
git shortlog

push pushing to a remote.

git push remotename branchname
# pushes the localbranchname to your remotename, but it is renamed to remotebranchname.
git push remotename localbranchname:remotebranchname

# Push branch to a remote and track
git push -u origin branch

git push remotename tagname 
# to push all your tags, you can type the command:
git push remotename --tags

# [--all | --mirror | --tags]
# --all:    Push all branches (i.e. refs under refs/heads/).

########## Remove remote branches/tags

# You just need to push an 'empty' reference to the remote tag name:
git push origin :tagname
# Or, more expressively, use the --delete option:
git push --delete origin tagname
# You also need to delete the local tag, use:
git tag --delete tagname

# [-d | --delete]: All listed refs are deleted from the remote repository. This is the same as prefixing all refs with a colon.

# --prune:  Remove remote branches that don’t have a local counterpart.


atlassian: merging vs rebasing.

# merge the master branch into the feature branch:
git checkout feature
git merge master
# Or, you can condense this to a one-liner:
git merge master feature


Rebasing is the process of moving a branch to a new base commit.

Instead of using a merge commit, rebasing re-writes the project history by creating brand new commits for each commit in the original branch.

Interactive rebase

Interactive rebasing gives you the opportunity to alter commits as they are moved to the new branch. This is even more powerful than an automated rebase, since it offers complete control over the branch’s commit history. Typically, this is used to clean up a messy history before merging a feature branch into master.

To begin an interactive rebasing session, pass the i option to the git rebase command:

git checkout feature
git rebase -i master

# By specifying HEAD~3 as the new base, you’re not actually moving the branch—you’re just interactively re-writing the 3 commits that follow it. Note that this will not incorporate upstream changes into the feature branch.
git checkout feature
git rebase -i HEAD~3
# Original
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

# Condense #1 and #2 into one by 'fixup'. Eliminating insignificant commits.
pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

Forgot "git rebase --continue" and did "git commit"


Just do git reset --soft HEAD^. It moves the HEAD pointer to its parent but keeps the work tree and adds the merge change to the index. So you can continue rebasing with git rebase --continue as before.



Reference logs, or "reflogs", record when the tips of branches and other references were updated in the local repository. Reflogs are useful in various Git commands, to specify the old value of a reference.

git reflog
# 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated
# d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive.

git reflog show
# is an alias for
git log -g --abbrev-commit --pretty=oneline

The "exists" subcommand checks whether a ref has a reflog. It exits with zero status if the reflog exists, and non-zero status if it does not.


It undoes a committed snapshot, appends a new commit with the resulting content, without affecting history.

revert vs reset

Reverting has two important advantages over resetting. First, it doesn’t change the project history, which makes it a “safe” operation for commits that have already been published to a shared repository.

Second, git-revert is able to target an individual commit at an arbitrary point in the history, whereas git-reset can only work backwards from the current commit.

reset resetting.

##### File-level

# Unstage Actually, it fetches the file from repo and stage it. See the next example.
git reset
# Fetch the version of in the 2nd-to-last commit and stage it for the next commit:
git reset HEAD~2
# git-checkout updates the working directory instead of the stage:
# Makes in the working directory match the one from the 2nd-to-last commit:
git checkout HEAD~2

# How to unstage?
git reset .

##### Commit level.

# Removing Local Commits
# CAUTION: dont reset on pushed commits!
git reset --hard HEAD~2

# --soft – The staged snapshot and working directory are not altered in any way.
# --mixed – The staged snapshot is updated to match the specified commit, but the working directory is not affected. This is the default option.
# --hard – The staged snapshot and the working directory are both updated to match the specified commit.


git stash list

# View the content of the most recent stash
git stash show -p

# View the content of an arbitrary stash
git stash show -p stash@{1}


SO: how to diff a commit with its previous one.

# Show the diff between a commit with its previous one, and the log msg.
git show commitId


Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate.

### Add submodules.

# Add a new file (.gitmodules) and a directory (src/main/webapp/ui-libs)
# submodule's .git is just a file reference to the main repo's .git.
git submodule add git:// src/main/webapp/ui-libs
# To specify a branch to follow:
git submodule add -b branch repository [path]

# Usually specify the read-only URL in ".gitmodules". For your own use:
git config submodule.repository.url PRIVATE_URL

# List all submodules
git submodule

### Initialize submodules.

# Initialize the submodules, i.e. register each submodule name and url found in .gitmodules into .git/config.
git submodule init
# clone missing submodules and checkout the commit specified in the index of the containing repository.
git submodule update

# Pass --recursive to the git clone command, it will automatically initialize and update each submodule in the repository.
git clone --recursive

# Git will go into your submodules and fetch and update for you.
git submodule update --remote DbConnector

# Have the DbConnector submodule track that repository’s “stable” branch instead of "master":
# If you leave off the -f .gitmodules it will only make the change for you
git config -f .gitmodules submodule.DbConnector.branch stable

### Edit

git diff --submodule
git config --global diff.submodule log
# Make git show you a short summary of changes to your submodules:
git config status.submodulesummary 1
git log -p --submodule

# Synchronizes submodules' remote URL configuration setting to the value specified in .gitmodules. This is useful when submodule URLs change upstream and you need to update your local repositories accordingly.
git submodule sync

Notice the "160000" mode for the newly added submodule entry. It is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file.

git commit -am 'added DbConnector module'
# [master fb9093c] added DbConnector module
#  2 files changed, 4 insertions(+)
#  create mode 100644 .gitmodules
#  create mode 160000 DbConnector

Wcf use case

Suppose my project wcfProj (on company GitLab) depends on a third party lib aGoodLib on GitHub.

Here are what I will do:

  1. Git clone aGoodLib from GitHub to local. Add a GitLab local repo and sync them. Name the GitHub repo "vendor", and GitLab repo "origin".
  2. Make the branch "master" in aGoodLib point to "origin".
  3. wcfProj has a submodule pointing to aGoodLib with company branch.
  4. We may customize codes on company branch, and merge updates from remote/GitHub.
  5. We may also send a pull request to GitHub.


# 刚开始加入子项目时:
git subtree add --prefix=Vendor/AFNetworking --squash master
# 切分出相关的提交:
git subtree split --prefix=Vendor/AFNetworking/ --branch AFNetworking
# 提交到对应的分支:
git push AFNetworking:critical-bug-fix
# 拉取下最新的代码:
git subtree pull --prefix=Vendor/AFNetworking --squash master


git symbolic-ref HEAD
# refs/heads/master

git symbolic-ref -q HEAD
# refs/heads/master

git symbolic-ref --short HEAD
# master


# List all tags.
git tag
# List tags with pattern.
git tag -l "v1.*"

# Make a lightweight tag.
git tag v1.4
# Make an unsigned, annotated tag.
git tag -a v1.5 -m "Message"
# Make a GPG-signed tag, using the default e-mail address's key.
git tag -s v1.6 -m "Message"
# Make a GPG-signed tag, using the given key.
git tag -u keyID v1.7 -m "Message"
# Take the tag message from the given file. Use - to read the message from the standard input. -a is implied.
git tag -F file v1.8

# Show the tag status.
git show v1.5

# Delete tag.
git tag -d v1.4

# Verify GPG signature.
git tag -v v1.6

# Replace an existing tag.
git tag -f/--force v1.7

# Remember to share your tags.
git push origin --tags

# Switched to a new branch 'version2', with content of tag v2.0.0.
git checkout -b version2 v2.0.0

附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。

To tag previous commit:

git log --pretty=oneline
# Shows: 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile

# Tag the commit with SHA starting with 9fceb02.
git tag -a v1.2 9fceb02


# Shows you the URLs that Git has stored for the shortname to be used when reading and writing to that remote
git remote -v

git remote rename old new

Exchange remote origin

git remote rename origin origin2
git remote rename candidate origin
git remote rename origin2 legacyName
git remote get-url origin

# Change the tracking branch:
git br -u origin/master
# Check the current tracking branch:
git br -vv
# * master be43c1d [origin/master] sewerBlockage

Reset, checkout, revert


Commit-level Operation

File-level Operations

### Commit-level

# Suppose we are on branch hotfix now
git reset HEAD~2
# The two commits on the end of hotfix branch are dangling now.

# Unstaging all changes, but leaves them in the working directory:
git reset --mixed HEAD
# Completely throw away all your uncommitted changes:
git reset --hard HEAD

### File-level

# Fetch the version of in the 2nd-to-last commit and stage it for the next commit:
git reset HEAD~2
# Unstage The changes it contains will still be present in the working directory.
git reset HEAD

# Discard unstaged changes to
git checkout HEAD

gc, garbage collection

StackOverflow. one of the housekeeping tasks that git-gc performs is packing and repacking of loose objects. Even if you never have any dangling objects in your bare repository, you will -- over time -- accumulate lots of loose objects. These loose objects should periodically get packed, for efficiency. Similarly, if a large number of packs accumulate, they should periodically get repacked into larger (fewer) packs.

# Count unpacked number of objects and their disk consumption, to help you decide when it is a good time to repack.
# -H, --human-readable
git count-objects -H
# 4315 objects, 11483 kilobytes

git count-objects -v
# count: 4315
# size: 11483
# in-pack: 9778
# packs: 20
# size-pack: 15726
# prune-packable: 1395
# garbage: 0
# count: the number of loose objects
# size: disk space consumed by loose objects, in KiB (unless -H is specified)
# in-pack: the number of in-pack objects
# size-pack: disk space consumed by the packs, in KiB (unless -H is specified)
# prune-packable: the number of loose objects that are also present in the packs. These objects could be pruned using git prune-packed.
# garbage: the number of files in object database that are neither valid loose objects nor valid packs
# size-garbage: disk space consumed by garbage files, in KiB (unless -H is specified)

# --aggressive: this option only needs to be used occasionally.
git gc --aggressive
# Counting objects: 8548, done.
# Delta compression using up to 4 threads.
# Compressing objects: 100% (8468/8468), done.
# Writing objects: 100% (8548/8548), done.
# Total 8548 (delta 7007), reused 0 (delta 0)
# Removing duplicate objects: 100% (256/256), done.

git count-objects -v
# count: 0
# size: 0
# in-pack: 8548
# packs: 1
# size-pack: 8937
# prune-packable: 0
# garbage: 0

git count-objects
# 0 objects, 0 kilobytes

# Setting the value of to 0 disables automatic packing of loose objects.
git config --global 0


Bare repo?

git gc should be called automatically called during "normal" use of a bare repository.


Exclude/Ignore files from commit


#---------- --skip-worktree

# It is the flag which means the files should change locally.
# Because the command is to prevent local changes from being managed by Git, we will use the command in most cases.
# Skip-worktree bit can be defined in one (long) sentence: When reading an entry, if it is marked as skip-worktree, then Git pretends its working directory version is up to date and read the index version instead.
# Writing is not affected by this bit, content safety is still first priority. Note that Git can update working directory file, that is marked skip-worktree, if it is safe to do so (i.e. working directory version matches index version).
# Skip-worktree also takes precedence over assume-unchanged bit when both are set.

git update-index --skip-worktree path/to/file

# Print to confirm:
# -v check the file being ignored.
# --skip-worktree is displayed with 'S'.
git ls-files -v | grep ^S

git update-index --no-skip-worktree path/to/file

#---------- --assume-unchanged

# --assume-unchanged is the flag which means the files should not change locally.
# it is used when ignore files that you do not need to change locally (or should not change).
# It is used when you want to speed up Git's behavior by ignoring unnecessary files.
# When the "assume unchanged" bit is on, the user promises not to change the file and allows Git to assume that the working tree file matches what is recorded in the index.
# Git will fail (gracefully) in case it needs to modify this file in the index e.g. when merging in a commit; thus, in case the assumed-untracked file is changed upstream, you will need to handle the situation manually.

git update-index --assume-unchanged path/to/file
# To confirm:
git ls-files -v | grep ^h
git update-index --no-assume-unchanged path/to/file


gitignore gitignore.

In order of precedence, from highest to lowest (within one level of precedence, the last matching pattern decides the outcome):

  1. Patterns read from the command line for those commands that support them.
  2. Patterns read from a .gitignore file in the same directory as the path, or in any parent directory, with patterns in the higher level files (up to the toplevel of the work tree) being overridden by those in lower level files down to the directory containing the file. These patterns match relative to the location of the .gitignore file.
  3. Patterns read from $GIT_DIR/info/exclude.
  4. Patterns read from the file specified by the configuration variable core.excludesFile.

Which file to place a pattern in depends on how the pattern is meant to be used.

Pattern format

Git treats the pattern as a shell glob: "*" matches anything except "/", "?" matches any one character except "/", and "[]" matches one character in a selected range.

A trailing "/**" matches everything inside. For example, "abc/**" matches all files inside directory "abc", relative to the location of the .gitignore file, with infinite depth. A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.




Git Objects


# -w: write into the object database.
# --stdin: read the content from stdin instead of a file.
# The output is a 40-character SHA-1 hash.
echo 'test content' | git hash-object -w --stdin
# d670460b4b4aece5915caf5c68d12f560a9fe3e4

git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
# test content

echo 'version 1' > test.txt
git hash-object -w test.txt
# 83baae61804e65cc73a7201a7252750c76066a30

git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
cat test.txt
# version 1

git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
# blob

# master^{tree} specifies the tree object that is pointed to by the last commit on your master branch.
git cat-file -p master^{tree}
# 100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README
# 100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile
# 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib

Git normally creates a tree by taking the state of your staging area or index and writing a series of tree objects from it.

# --add: add a specified file into the index. Default behaviour is to ignore new files.
echo 'new file' > new.txt
git update-index test.txt
git update-index --add new.txt

# --cacheinfo mode,object,path, --cacheinfo mode object path: Directly insert the specified info into the index.
# 100644: normal file.
# 100755: executable.
# 120000: symbolic link.
git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt

# Automatically creates a tree object from the state of the index if that tree doesn’t yet exist.
git write-tree
# d8329fc1cc938780ffdd9f94e0d364e0ea74f579
git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
# 100644 blob 83baae61804e65cc73a7201a7252750c76066a30      test.txt
git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
# tree


Trees specify the different snapshots of your project

echo 'first commit' | git commit-tree treeSHA
# commit SHA: fdf4fc3344e67ab068f836878b6c4951e3b15f3d

git cat-file -p fdf4fc3
# tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
# author Scott Chacon <> 1243040974 -0700
# committer Scott Chacon <> 1243040974 -0700
# first commit

Object Storage

Git annex



sudo yum -y install haskell-platform

# Arch.
sudo pacman -S cabal-install ghc
cabal update
cabal install git-annex --bind=$HOME/bin

Good references


# Creating a repo.
mkdir ~/annex
cd ~/annex
git init
git annex init "my laptop"

# Adding a remote on a USB drive.
sudo mount /media/usb
cd /media/usb
git clone ~/annex
cd annex
git annex init "portable USB drive"
git remote add laptop ~/annex
cd ~/annex
git remote add usbdrive /media/usb/annex

# Add files
cd ~/annex
cp /tmp/big_file .
cp /tmp/debian.iso .
git annex add .
	# add big_file (checksum...) ok
	# add debian.iso (checksum...) ok
git commit -a -m added

# renaming files
cd ~/annex
git mv big_file my_cool_big_file
mkdir iso
git mv debian.iso iso/
git commit -m moved

# Getting file content.
cd /media/usb/annex
# let git-annex know what has changed in the other repositories like the laptop.
git annex sync laptop
git annex get .
	# get my_cool_big_file (from laptop...) ok
	# get iso/debian.iso (from laptop...) ok

Direct mode


git-annex direct
git-annex indirect

# Undo
git-annex undo file

# Workflow.
git annex add file
git annex sync
git annex copy file --to otherrepo

Removing files


SSH remote

Fix broken

Branchable: move file. Branchable: removing files.

# To fix up broken symlinks, you can either run "git annex fix", or just commit the move. The pre-commit hook fixes up links automatically.
git-annex fix fileName

# Remove the content of a file from your local repository to save space.
git-annex drop iso/debian.iso

Special remotes

export AWS_ACCESS_KEY_ID="somethingotherthanthis"
export AWS_SECRET_ACCESS_KEY="s3kr1t"
git annex initremote mys3 type=S3 chunk=1MiB encryption=shared
	# initremote mys3 (shared encryption) (checking bucket) (creating bucket in US) ok
# Now you can store files on the newly initialized special remote.
git annex copy my_cool_big_file --to mys3
	# copy my_cool_big_file (to mys3...) ok

# First get git-annex in sync (so it knows about the special remote that was added in the other repository)
git annex sync
export AWS_ACCESS_KEY_ID="somethingotherthanthis"
export AWS_SECRET_ACCESS_KEY="s3kr1t"
git annex enableremote mys3
	# enableremote mys3 (checking bucket) ok
# And now you can download files from the special remote:
git annex get my_cool_big_file --from mys3
	# get my_cool_big_file (from mys3...) ok

Moving file content between repositories

Unused data

git annex unused
	# unused . (checking for unused data...) 
	#   Some annexed data is no longer used by any files in the repository.
	#     NUMBER  KEY
	#     1       SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e
	#     2       SHA1-s14--f1358ec1873d57350e3dc62054dc232bc93c2bd1
	#   (To see where data was previously used, try: git log --stat -S'KEY')
	#   (To remove unwanted data: git-annex dropunused NUMBER)
	# ok

git annex dropunused 1
	# dropunused 1 ok
git annex dropunused 1-1000

#Rather than removing the data, you can instead send it to other repositories:
git annex copy --unused --to backup
git annex move --unused --to archive


git annex fsck
git annex fsck my_cool_big_file


git-annex can be configured to require more than one copy of a file exists, as a simple backup for your data. This is controlled by the numcopies setting, which defaults to 1 copy. Let's change that to require 2 copies, and send a copy of every file to a USB drive.

git annex numcopies 2
git annex copy . --to usbdrive

cd /media/usbdrive
git annex whereis
	# whereis my_cool_big_file (1 copy)
	#     0c443de8-e644-11df-acbf-f7cd7ca6210d  -- laptop
	# whereis other_file (3 copies)
	#     0c443de8-e644-11df-acbf-f7cd7ca6210d  -- laptop
	#     62b39bbe-4149-11e0-af01-bb89245a1e61  -- usb drive [here]
	#     7570b02e-15e9-11e0-adf0-9f3f94cb2eaa  -- backup drive

# Automatically reconfigure.
git annex get --auto --numcopies=2
	# get my_cool_big_file (from laptop...) ok
git annex drop --auto --numcopies=2
	# drop other_file ok

Git annex FAQ

Git-annex add hangs


I guess this is a mismatch between the version of git that git-annex was built with and the version it's using. In particular, git check-attr behavior varies between git older than 1.7.7 and newer, and git-annex picks the version at build time.


Checkout github wiki

SO: how do I clone a github wiki.

git clone would be the path to clone your repository, and git clone would be the path to clone its wiki.

Discard all local change


# For a specific file use:
git checkout path/to/file/to/revert

# For all unstaged files use:
git checkout -- .

# Another method
git stash save --keep-index
git stash drop

Provide username and password when git clone

git clone

Proxy unset

SO: how to remove proxy in git.

wcf: even when I unset all http_proxy in shell, the git client still uses the proxy settings before, which is annoying.


git config --unset http.proxy
git config --unset https.proxy

Revert to a commit


## Temporarily switch to a different commit.

# This will detach your HEAD, that is, leave you with no branch checked out:
git checkout 0d1d7fc32
# Or if you want to make commits while you're there, go ahead and make a new branch while you're at it:
git checkout -b old-state 0d1d7fc32

## Hard delete unpublished commits

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.

## Undo published commits with new commits

# This will create three separate revert commits:
git revert a867b4af 25eee4ca 0766c053
# It also takes ranges. This will revert the last two commits:
git revert HEAD~2..HEAD
# Reverting a merge commit
git revert -m 1 <merge_commit_sha>
# To get just one, you could use `rebase -i` to squash them afterwards
# Or, you could do it manually (be sure to do this at top level of the repo)
# get your index and work tree into the desired state, without changing HEAD:
git checkout 0d1d7fc32 .
# Then commit. Be sure and write a good message describing what you just did
git commit

"object file is empty" and "loose object is corrupt" errors


git status
#  error: object file .git/objects/9b/b43162ab8a41594735a628e3c5cacdaa675123 is empty
#  fatal: loose object 9bb43162ab8a41594735a628e3c5cacdaa675123 (stored in .git/objects/9b/b43162ab8a41594735a628e3c5cacdaa675123) is corrupt

The easiest way to do this is to create a second clone and hope that the object exists in the clone.

# Check to see what type it is.
git cat-file -t 9bb43162ab8a41594735a628e3c5cacdaa675123
# See the content according to the type
git cat-file commit commitSHA
git cat-file blob blobSHA
# Also premit: ask for a "tree" with object being a commit object that contains it, or to ask for a "blob" with object being a tag object that points at it.

# The new clone almost certainly has its objects packed. So to get the missing file:
git cat-file 9bb43162ab8a41594735a628e3c5cacdaa675123 > b43162ab8a41594735a628e3c5cacdaa675123
cp b43162ab8a41594735a628e3c5cacdaa675123 /path/to/broken/clone/.git/objects/9b/.

git ls-tree -r HEAD | grep 9bb43162ab8a41594735a628e3c5cacdaa675123

If the new repo is shallowly cloned, there will be a "shallow" file under .git/.

git clone --depth=1 repoURL new
cd new
cd .git/objects && ls
# info pack
cd pack

# Make a temporary repo, and unpack all ojbects here.
mkdir test && cd test
git init .
git unpack-objects < ../pack-768126ea0e7b26300a081d229f3845532216f3e6.pack
cd .git/objects/ && ls
# OK, we have all objects here.
# Start copying the missing ones!

If that operation fails then the new clone does not have the file in question and you're back to trying to figure out which file that was created by work in the original clone is missing and how to recover its contents (exactly) so that the SHA matches.

# Use "git log" to find the SHA of the last commit on your branch.
git cat-file -p SHA | egrep ' tree |9bb43162ab8a41594735a628e3c5cacdaa675123'
# where "SHA" is the 40 character hex string from the output of "git log".

How to make a git repo bare


mv repo/.git repo.git
git --git-dir=repo.git config core.bare true
mv repo repo.bak

How to make a bare repo un-bare?


git pull fails "unable to resolve reference" "unable to update local ref"

Stackoverflow. Happened to me as well. In my case, the bad ref was master, and I did the following:

rm .git/refs/remotes/origin/master
git fetch

This made git restore the ref file. After that everything worked as expected again.

error: RPC failed; curl 18 transfer closed with outstanding read data remaining

SO. The error happens when the repo is too large.

# The idea is to do a shallow clone first and then update the repository with its history.
git clone --depth 1
cd large-repository
git fetch --unshallow

# Or increment buffer size:
git config --global http.postBuffer 524288000

Fatal: index file smaller than expected

'git status' failed with code 128:'fatal: index file smaller than expected'. Atlassian.

rm .git/index
git reset HEAD .

fatal: cannot resume: .git/rebase-apply/final-commit does not exist


# Abort and then try again, and again.
git rebase --abort
git rebase master

Fatal: Unable to create '.git/index.lock': File exists


rm ./.git/index.lock

Remember password

# Store your credentials in clear text in project's .git-credentials.
git config credential.helper store
# Store in ~/.git-credentials
git config --global credential.helper store

# keep your password cached in memory for (by default) 15 minutes.
git config --global credential.helper cache
# Keep 3600s.
git config --global credential.helper "cache --timeout=3600"

# Don't remember password.
git config --unset credential.helper

Reset, revert changes

# Remove file from source control, but still keep it on disk.
git rm --cached file
# Check out file1 and overwrite uncommitted changes.
git checkout pathto/file1
# Remove uncommitted change for file1 only.
git reset HEAD file1
# Remove uncommitted changes
git reset --hard

# Remove the untracked files recursively from the working tree.
git clean # Use in caution!
git clean -nfd # -f, remove files. -d, remove dirs. -n, just try.

Ref: "git reset HEAD" to undo the "git add".

fatal: Unable to find remote helper for 'https'

Git uses curl to fetch http packages. So be sure to install curl libs before compiling git from sources:

sudo yum install -y curl-devel
./configure --prefix=/path
make -j8
make install

Reset or revert a specific file to a specific revision

If you messed up in "abbcdf" and want the version right before "abbcdf", you can do:

git checkout "abbcdf~1" path/to/file

Delete branches


# As of Git v1.7.0, you can delete a remote branch using
git push origin --delete <branchName>
# From Git v1.5.0, before v1.7.0:
git push origin :<branchName>

# Remove local branch with
git branch -d your branchname

Push to checked out branch

You can't push to the currently checked out branch of a repository. Why? If allowed, a push on the checked-out branch would change the HEAD to be inconsistent with the index and working tree on the remote repository. This would make it very easy to accidentally commit a change that undoes all of the pushed changes and also makes it very difficult to distinguish between any local changes that have not been committed and differences between the new HEAD, the index and the working tree that have been caused by push moving HEAD.

# Do your work on this branch and commit.
git checkout -b myBranch
# Then push your branch without having to destroy anything.
git push origin master:myBranch

# On server
git merge myBranch

After that, your branch in the remote repo can be merged or rebased into master.

Change tracking upstream


  1. Use git remote rename to backup "origin" and set new "origin". Note: this will also rename the remote name in upstream setting, so we need the next step.
  2. Set branch merge with git branch branchName -u newRemote/branchName. You can Check this in .git/config file.

Make an existing branch track a remote branch


# As of Git 1.8.0:
git branch -u upstream/foo foo
git branch --set-upstream-to=upstream/foo foo

# As of Git 1.7.0:
git branch --set-upstream foo upstream/foo

Set git server using ssh

Ref. The idea is easy: init git repo on your server, and using ssh to fetch the repo.

For example, I set ~/gitRepo on my server, and on my client, I could visit myproj repo by git clone me@serverIP:~/gitRepo/myproj.git.

Ref. git clone ssh://user@server/project.git
或者不指明某个协议 — 这时 Git 会默认使用 SSH :
git clone user@server:project.git

Also do make the user "git" has no normal shell login:

# See where the git-shell is.
which git-shell
# Add git-shell here.
sudo emacs /etc/shells
# Change git's shell to git-shell.
sudo chsh git
# For more information on customizing the shell:
git help shell


GitLab has its config file under /etc/gitlab/gitlab.rb.

# Configure and start GitLab:
# Also run the following every time you wanna settings to take effect
sudo gitlab-ctl reconfigure
# or this does the same
sudo service gitlab reload

# You must be root to run lokkit.
# -s: Open the firewall for a service.
sudo lokkit -s http -s ssh

Add admin

Open console

Ref for openning gitlab console.

# Change to gitlab directory. Normally, /home/git/gitlab.
cd /home/git/gitlab
sudo -u git -H bundle exec rails console production

# Or, if you are running omnibus-gitlab you should run the following:
# Open-source gitlab could also use this:
sudo gitlab-rails console

OK, here is the explanation about Omnibus package:"Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable Omnibus package installation (deb/rpm)."

Set admin

Ref for setting admin.

User.find_by_email("") do |i|
    i.admin = true

One thing. It is said is the default administrator, but I found nil by using User.find_by_email(''). Maybe it is because my gitlab uses LDAP. Then after I login with my own LDAP account (which is admin now), I found the default admin is actually:

Enabling the admin group feature

You can have an LDAP group with the common name (CN) 'GitLab administrators' containing the users that should be GitLab administrators. For omnibus-gitlab, add the following to /etc/gitlab/gitlab.rb and run gitlab-ctl reconfigure.

gitlab_rails['ldap_admin_group'] = 'GitLab administrators'

NOTE: It is recommended that you keep a non-LDAP GitLab administrator user around on your GitLab instance in case you accidentally remove the admin status from your own LDAP-enabled GitLab user.

Recipe Compile Error: External URL must include a FQDN

On CentOS 6.5, after sudo gitlab-ctl reconfigure, it complained the error as the title says. This provided a good solution for me. The file /etc/gitlab/gitlab.rb has ill-formed FQDN - missing a '=' sign. After adding this, it compiled successfully.

Gitlab FAQ

Where is gitlab.yml?

Under /var/opt/gitlab/gitlab-rails/etc/. However, it says:

This file is managed by gitlab-ctl. Manual changes will be erased! To change the contents below, edit /etc/gitlab/gitlab.rb and run `sudo gitlab-ctl reconfigure`.

However, if I want to use IP addr instead of FQDN, I have to manually change it in gitlab.yml under "host" key, and then "sudo gitlab-ctl restart" (not reconfigure, otherwise it will be erased!). About how to change the URL for gitlab, see Ref.

LDAP failed

In /var/log/gitlab/gitlab-rails/production.log, it says "ArgumentError (uid or filter MUST be provided)". So I changed the "uid=sAMAccount" to "uid=cn", problem solved.

Developer can't push?

关于git developer角色无法push的问题, see Ref. gitlab为了安全考虑默认保护主干,只有master角色才能push到主干。可以考虑使用git推荐的策略:Keep stable branches secure and force developers to use Merge Requests。即开发人员在本地整合好以后,发出merge request。