All notes
Gi

Templates

.gitconfig

Command git config --global could be replaced by manually editing the global config file for Git: ~/.gitconfig.

[http]
    sslVerify = false
    sslCAinfo = /bin/curl-ca-bundle.crt
[user]
	name = myName
	email = [email protected]
[push]
	default = simple
[alias]
	st = status
	co = checkout
	ci = commit -am
	cis = commit -am "Small modi." && git pori
	pori = push origin master --tags
	fori = fetch origin
	mori = merge origin/master
	br = branch
	hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
	type = cat-file -t
	dump = cat-file -p
  diffu = diff --no-prefix -U10000 --no-color
[credential]
	helper = store

.gitignore

Example


## Generic
*~
*.lock
*.DS_Store
*.swp
*.out

## Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
*.perspective
*~.nib
#ignore private workspace stuff added by Xcode4
xcuserdata
project.xcworkspace

# CPP
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

# CMAKE
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt

.gitattributes

SYNOPSIS: .git/info/attributes.

pattern    attr1 attr2 ...

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

Precedence:

pre-commit

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.

#!/bin/bash
FILES_PATTERN='\.rb$|\.rake$'
FORBIDDEN='binding.pry|byebug|pry'

# 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

chris.beams.io: How to Write a Git Commit Message. Ref.

Why write messages

Title

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

Body

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

atlassian.com: merging vs rebasing.

Feature & Bug Branches

BeansTalkApp.

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

gitst.github.com.


# Add 'upstream' repo to list of remotes
git remote add upstream https://github.com/UPSTREAM-USER/ORIGINAL-PROJECT.git

# 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.
# http://stackoverflow.com/questions/8939977/git-push-rejected-after-feature-branch-rebase
git push --force

Use https or git?

gist.github.com: A comparison of protocols offered by GitHub.

plain Git, aka git://github.com/
HTTPS, aka https://github.com/
SSH, aka [email protected]: or ssh://[email protected]/

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

help.github.com: 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 [email protected]

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

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

Topics

ORIG_HEAD

SO: HEAD and ORIG_HEAD.

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: [email protected]{1} is roughly equivalent to ORIG_HEAD ([email protected]{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'.

Stackoverflow.

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

git-scm.


##### 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 [email protected]{5}
# See where your master branch was yesterday, you can type
git show [email protected]{yesterday}
# If your local history is more than 2 months ago:
git show [email protected]{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

Commands

add

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.


# http://stackoverflow.com/questions/572549/difference-between-git-add-a-and-git-add

# -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

blame

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


git blame "filename"

branch

Examples


# -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

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

SO.

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

StackOverflow.

StackOverflow.


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

checkout

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.

Undoing changes

atlassian.com: undoig changes.

cherry-pick

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.

think-like-a-git.net: 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.

clean

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

clone

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.

commit


# --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

atlassian.com: rewritting history.

config


git config -l

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

git config --get user.name
git config --get user.email

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

diff

Stackoverflow.



git diff --no-prefix -U10000 --no-color
git diff --staged
# Compare the most recent version of the project to your committed changes.
git diff HEAD^ HEAD

# Compare your current pom.xml to the one from master 20 revisions ago through the first parent.
git diff master~20:pom.xml pom.xml

# git diff revision path
git diff b0d14a4 foobar.txt

# To see what was changed in a file in the last commi:
git diff HEAD~1 path/to/file.

# Diff between two branches.
git diff master..test
# 找出master, test的共有父分支和test分支之间的差异,用3个 '.' 来取代前面的两个'.'。
git diff master...test

# Compare two commits and apply the patch to files.
git diff 0da94be 59ff30c > my.patch
git apply my.patch

# 找当前工作目录和上次提交的本地索引间的差异。
git diff
# 显示你当前的索引和上次提交间的差异,这些内容在不带"-a"参数运行 "git commit"命令时就会被提交。
git diff --cached
# 这条命令会显示你工作目录与上次提交时之间的所有差别,这条命令所显示的 内容都会在执行"git commit -a"命令时被提交。
git diff HEAD

log


# Search commit message:
# http://stackoverflow.com/questions/3826748/how-to-search-in-commit-messages-using-command-line
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.

shortlog

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


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

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

# 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

# Push branch to a remote and track
# http://stackoverflow.com/questions/2765421/how-do-i-push-a-new-local-branch-to-a-remote-git-repository-and-track-it-too
git push -u origin branch

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

merge

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

rebase

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

reflog

git-scm.

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 [email protected]{0}: commit: fixed refs handling, added gc auto, updated
# d921970 [email protected]{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.

revert

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

atlassian.com: resetting.


##### File-level

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

##### 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.

stash


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 [email protected]{1}

show

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

submodule

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://github.com/soberh/ui-libs.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 https://github.com/someone/MainProject

# 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.

subtree


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

Symbolic-ref


git symbolic-ref HEAD
# refs/heads/master

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

git symbolic-ref --short HEAD
# master

tag

http://git-scm.com/book/en/v2/Git-Basics-Tagging.


# 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

Remote


# 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

Reset, checkout, revert

Atlassian.

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 foo.py in the 2nd-to-last commit and stage it for the next commit:
git reset HEAD~2 foo.py
# Unstage foo.py. The changes it contains will still be present in the working directory.
git reset HEAD foo.py

# Discard unstaged changes to foo.py.
git checkout HEAD foo.py

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 gc.auto to 0 disables automatic packing of loose objects.
git config --global gc.auto 0

FAQ

Bare repo?

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

Internals

Basics

Git-SCM.

Git Objects

Git-SCM.


# -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

Commits

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 <[email protected]> 1243040974 -0700
# committer Scott Chacon <[email protected]> 1243040974 -0700
# 
# first commit

Object Storage

Git annex

Installation

Branchable.


sudo yum -y install haskell-platform

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

Good references

Walkthrough


# 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

Branchable.


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

Modification

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

fsck


git annex fsck
git annex fsck my_cool_big_file

Numcopies

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

Branchable.

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.

Git FAQ

Checkout github wiki

SO: how do I clone a github wiki.

git clone [email protected]:myusername/foobar.git would be the path to clone your repository, and git clone [email protected]:myusername/foobar.wiki.git would be the path to clone its wiki.

Discard all local change

StackOverflow.


# 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

Exclude/Ignore files from commit

SO.


# This will tell git you want to start ignoring the changes to the file
git update-index --assume-unchanged path/to/file

# When you want to start keeping track again
git update-index --no-assume-unchanged path/to/file

Provide username and password when git clone


git clone https://username:[email protected]/username/repository.git

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.

Solution:


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

Revert to a commit

StackOverflow.


## 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

GitForums.


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

StackOverflow.


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?

StackOverflow.

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.

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 .

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

Stackoverflow.


# 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

Ref.

  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

Ref.


# 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 [email protected]:~/gitRepo/myproj.git.

Ref. git clone ssh://[email protected]/project.git
或者不指明某个协议 — 这时 Git 会默认使用 SSH :
git clone [email protected]: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

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("[email protected]") do |i|
    i.admin = true
    i.save
end

One thing. It is said [email protected] is the default administrator, but I found nil by using User.find_by_email('[email protected]'). 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: [email protected].

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。

每一个开发人员在远程有一个属于自己的分支,在完成各自的功能模块的过程中,都是从自己的远程分支pull下来,然后push到自己的远程分支,当一个功能完成之后,负责人做一个代码审核,审核通过之后由负责人与远程的master分支进行merge。