一点git

Part 1 Introduction to Git

Local Git

  • Creating a repository: git init
    Create a new local Git repository in a new subdirectory named GitInPracticeRedux
1
2
cd /Users/mike/
git init GitInPracticeRedux
  • Building a new commit in the index staging area: git add
    Add an existing file GitInPractice.asciidoc to the index staging area for inclusion in the next commit.
1
2
cd /Users/mike/GitInPracticeRedux/
git add GitInPractice.asciidoc
  • Committing changes to files: git commit
    Commit the contents of an existing file GitInPractice.asciidoc, which has already been added to the index staging area.
1
git commit --message 'Initial commit of book.'
  • Viewing history: git log
    View the commit history of a repository.
1
git log
  • Viewing the differences between commits: git diff
    View the differences between the previoius commit and the latest.
1
2
3
git diff master~1 master
git diff master~1 master --GitInPractice.asciidoc ## 只比较此文件
git diff # compare any changes you've made but not yet added with git add.

Remote Git

  • Adding a remote repository: git remote add
    Add the new GitInPractice remote repository to your current repository.
1
2
git remote add origin https://github.com/username/GitInPracticeRedux.git
git remote --verbose # 验证一下
  • Pushing changes to a remote repository: git push
    Push the changes from the local GitInPracticeRedux repository to the origin remote on GitHub.
1
git push --set-upstream origin master
  • Cloning a remote/GitHub repository onto your local machine: git clone
    Remove the existing GitInPracticeRedux local repository and re-create it by cloning from GitHub.
1
2
3
cd /Users/mike/
rm -rf GitInPracticeRedux
git clone https://github.com/username/GitInPracticeRedux.git GitInPracticeRedux
  • Pulling changes from another repository: git pull
    Pull new commits into the current branch on the local GitInPracticeRedux repository from the remote repository on GitHub.
1
git pull
  • Fetching changes from a remote without modifying local branches: git fetch
    git fetch performs the fetching action of downloading the new commits but skips the merge step.
1
2
git fetch
git pull # pull after fetch
  • Creating a new local branch from the current branch: git branch
    Create a new local branch named chapter-two from the current(master) branch.
    这个命令创建一个新分支,但并不切换到这个分支上,要切换还是要用git checkout
1
git branch chapter-two master
  • Checking out a local branch: git checkout
    Change to a local branch named chapter-two from the current(master) branch.
    切换分支前,如果当前分支还有内容没提交,切换分支会失败,需要commit或用git stash。如果不想要这些内容了,可以用git checkout --force
1
git checkout chapter-two
  • Pushing a local branch remotely
    Push the changes from the local chapter-two branch to create the remote branch chapter-two on GitHub.
1
git push --set-upstream origin chapter-two
  • Merging an existing branch into the current branch: git merge
    Make a commit on the local branch named chapter-two and merge this into the master branch.
    git pull约等于git fecth && git merge.
1
2
3
4
5
cd /Users/mike/GitInPracticeRedux/
git add GitInPractice.asciidoc
git commit --message 'Start Chapter2.'
git checkout master
git merge chapter-two
  • Deleting a remote branch
    Push the current master branch and delete the branch named chapter-two on the remote origin.
1
2
3
4
cd /Users/mike/GitInPracticeRedux/
git checkout master
git push
git push --delete origin chapter-two
  • Deleting the current local branch after merging
    Delete the local branch named chapter-two.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git checkout master
git branch --delete chapter-two

Part 2 Git essentials

Filesystem interactions

  • Renaming or moving a file: git mv
    Rename a previously committed file named GitInPractice.asciidoc to 01-IntroducingGitInPractice.asciidoc and commit the newly renamed file.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git mv GitInPractice.asciidoc 01-IntroducingGitInPractice.asciidoc
git commit --message 'Rename book file to first part file.'
  • Removing a file: git rm
    Remove a previously committed file named GitInPracticeReviews.tmp in your Git working directory and commit the removed file.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git rm GitInPracticeReviews.tmp
git commit -- message 'Remove unfavourable review file.'
  • Resetting files to the last commit: git reset
    Reset the state of all the files in your working directory to their last committed state.
1
2
3
4
cd /Users/mike/GitInPracticeRedux/
echo EXTRA >> 01-IntroducingGitInPractice.asciidoc
git reset --hard # 暂存区和工作目录都重置
git reset --mixed # 只重置暂存区
  • Deleting untracked files: git clean
    Remove an untracked file named GitInPracticeIdeas.tmp from a Git working directory.
    如果想删除文件夹的话可以加一个-d选项。
1
2
3
cd /Users/mike/GitInPracticeRedux/
git clean --dry-run # 可以先预览一下会移除哪些文件
git clean --force
  • Ignoring files: .gitignore
    Ignore all files with the extension .tmp in a Git repository.
    git help gitignore看帮助。GitHub提供了一些有用的gitignore模板
1
2
3
4
cd ?Users/mike/GitInPracticeRedux/
echo \*.tmp > .gitignore
git add .gitignore
git commit --message='Ignore .tmp files.'
  • Deleting ignored files
    Delete all ignored files from a Git working directory.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git clean --force -X # 只删除ignore的文件
git clean --force -x # 删除ignore的文件和untrack的文件。
1
2
3
# reset all files to their last-committed state and remove all uncommitted files.
git reset --hard
git clean -xdf
  • Temporarily stashing some changes: git stash
    Stash all your uncommitted changes for later retrieval.
    Create a temporary commit with a prepopulated commit message and then returns your current branch to the state before the temporary commit was made.
1
2
3
4
cd /Users/mike/GitInPracticeRedux/
echo EXTRA >> 01-IntroducingGitInPractice.asciidoc
git stash save
git stash list # 可以看所有stash
  • Reapplying stashed changes: git stash pop
    Pop the changes from the last git stash save into the current working directory.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git stash pop # 会弹出最顶上的stash
git stash apply # 不会弹出最顶上的stash,只是应用,于是可以应用多次
  • Clearing stashed changes: git stash clear
    Clear all previously stashed changes.
1
git stash clear
  • Assuming files are unchanged
    Let Git assume there have been no changes made to 01-IntroducingGitInPractice.asciidoc.
1
git update-index --assume-unchanged 01-IntroducingGitInPractice.asciidoc
  • Listing assumed-unchanged files
    Let Git list all the files that it has been told to assume haven’t changed.
1
git ls-files -v

输出结果中H开头的是committed file,h开头的是Assumed-unchanged file。

1
git ls-files -v | grep '^[hsmrck?]' | cut -c 3-

这样就只输出Assumed-unchanged file的文件名。

  • Stopping assuming files are unchanged
    Let Git stop assuming there have been no changes made to 01-IntroducingGitInPractice.asciidoc.
1
2
git update-index --no-assume-unchanged 01-IntroducingGitInPractice.asciidoc
git ls-files -v | grep 01-IntroducingGitInPractice.asciidoc # 验证一下。结果应该是H打头的

History visualization

  • Listing only certain commits
    List the commits authord by Mike McQuaid after November 10,2013, with the string “file.” in their message.
1
git log --author "Mike McQuaid" --after "Nov 10 2013" --grep 'file\.'

还有一些参数,如–max-count限制只输出几条commit。–reverse让输出从老到新排序。–before是在什么时间之前。–merges只显示merge commit们。

  • Listing commits with different formatting
    List the last two commits in an email format with the oldest displayed first.
1
git log --format=email --reverse --max-count 2

也可以自定义格式。参看git log --help

1
git log --format="%ar %an did: %s"	# 6 weeks ago Mike McQuaid did: Ignore .tmp files.

常用于open source software-release的格式。

1
git shortlog HEAD~6..HEAD

作者推荐的一个格式。

1
git log --oneline --graph --decorate
  • Showing who last changed each line of a file: git blame
    Show the commit, person, and date on which each line of GitInPractice.asciidoc was changed.
1
git blame --date=short 01-IntroducingGitInPractice.asciidoc
  • Finding which commit caused a particular bug: git bisect
    Locate the commit that renamed GitInPractice.asciidoc to 01-IntroducingGitInPractice.asciidoc
    在使用之前要commit或stash当前内容,因为bisect操作将当前目录checkout到该相关commit。
1
2
3
4
5
cd /Users/mike/GitInPracticeRedux/
git bisect start
git bisect bad
git bisect good 6576b6
git bisect reset

还可用git bisect log查看历史。git bisect start src/gui只检查此目录下的更改。git bisect skip在不知该版本good还是bad时跳过此版本。
git bisect run可以通过process的返回值(0是success,1是failure)自动判断是good还是bad。

Advanced branching

  • Merging branches and always creating a merge commit
    Merge the chapter-spacing branch into the master branch and create a merge commit, not perform a fast-forward merge.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git checkout master
git merge --no-ff chapter-spacing
  • Resolving a merge conflict
    Merge the separate-files branch into the master branch and resolve the resulting merge conflict.
1
2
3
4
5
6
7
8
cd /Users/mike/GitInPracticeRedux/
git checkout master
git merge separate-files
# 然后编辑冲突文件解决冲突
cd /Users/mike/GitInPracticeRedux/
git add 01-IntroducingGitInPractice.asciidoc
git commit
git branch --delete separate-files # 此时可以删除分支separate-files

可以用一个graphical merge tool。git mergetool

  • Resolving each merge conflict only once: git rerere
    rerere代表Reuse Recorded Resolution。
    Set up git rerere to integrate with the merge workflow so you don’t need to repeatedly resolve the same merges.
1
git config --global --add rerere.enabled 1

如果想要忘记某次错误的resolve,可以用git rerere forget 01-IntroducingGitInPractice.asciidoc

  • Creating a tag: git tag
    A tag is another ref(or pointer) for a single commit. Tags differ from branches in that they’re (usually) permanent.
    Tag the current state of the GitInPracticeRedux master branch as version v0.1。
1
2
3
4
5
cd /Users/mike/GitInPracticeRedux/
git checkout master
git tag v0.1
git tag # 显示当前repository的所有tag
git push -tags

可用–delete删除某tag。

  • Generating a version number based on previous tags: git describe
    Generate a version number for a software project based on existing tags in the repository.
1
2
cd /Users/mike/GitInPracticeRedux/
git describe --tags
  • Adding a single commit to the current branch: git cherry-pick
    cherry-pick a commit from the v0.1-release branch to the master branch.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git checkout master
git cherry-pick v0.1-release # 或 git cherry-pick df32377

添加额外的commit信息用–edit。从远程分支cherry-pick一个到另一远程分支最好加上-x,会指明从哪个commit来的。–signoff会附上执行cherry-pick的人的信息。cherry-pick时如果遇到冲突,改正之后用git cherry-pick --continue提交,如果此时想放弃,用git cherry-pick --abort

  • Reverting a previous commit: git revert
    Revert a commit to reverse its changes. 尤其是已经push到远程的情况下。
1
2
3
cd /Users/mike/GitInPracticeRedux/
git checkout master
git revert c18c9ef
  • Listing what branches contain a commit: git cherry
    See what commits remain unmerged to the master branch from the v0.1-release branch.
1
2
git checkout v0.1-release
git cherry --verbose master

Rewriting history and disaster recovery

  • Listing all changes including history rewrites: git reflog
    View the state of the reflog for the HEAD pointer.
1
2
git checkout master
git reflog

更建议使用commit然后之后修改,而不是使用stash,因为stash不会出现在reflog上。reflog只在本地仓库,不会被push到远程。
防止数据丢失的原则就是commit early and commit often。

  • Resetting a branch to a previous commit: git reset
    还没被push的commit用reset处理更合适。
    Undo the last commit on the master branch.
1
git reset HEAD^

如果想要restore。注意超过90天且不是别人parent的commit的commit会从reflog中删去。

1
2
git reflog HEAD
git reset 4455fa9

修改上一次的commit。

1
git commit --amend
  • Rebasing commits on top of another branch: git rebase
    Rebase the inspiration branch on top of the v0.1-release branch.
1
2
git checkout inspiration
git rebase v0.1-release
  • Rebasing commits interactively: git rebase --interactive
    Interactively rebase the history of a branch. 应该常用。
1
2
3
4
5
6
git checkout inspiration
git rebase --interactive v0.1 # 会弹出一个editor
输入
pick 6d4ad83 Add Chapter 1 inspiration.
p df32377 Advanced practice technique.
f a8200e1 Add release preface.
  • Pulling a branch and rebasing commits: git pull --rebase
    避免出现merge commit,常用。
    Pull commits from origin/master and rebase your current commits in master on top of the upstream changes.
1
git pull --rebase	# 等价于git fetch && git rebase
  • Rewriting history on a remote branch: git push --force
    Rewrite the history on the remote origin/inspiration branch based on the contents of the local inspiration branch.
1
2
3
git checkout inspiration
git pull --rebase
git push origin +inspiration
  • Rewriting the entire history of a branch: git filter-branch
    Remove all references to the file 00-Preface.asciidoc on the master branch.
1
git filter-branch --prune-empty --index-filter "git rm --cached --ignore-unmatch 00-Preface.asciidoc" master

Part 3 Advanced Git

Personalizing Git

  • Setting the configuration for all repositories
    Set your Git username in your global Git configuration.
1
2
3
git config --global user.name "Mike McQuaid"	# 会写到/Users/mike/.gitconfig里
git config --global user.name # 查询该项
git config --global --unset rerere.enabled # 取消某项设置
  • Setting the configuration for a single repository
    Set your Git user email in your repository Git configuration.
1
2
3
cd /Users/mike/GitInPracticeRedux/
git config user.email mike.mcquaid@github.com # 会写到.git/config
git config --local user.email # 查询该项

在浏览器里看git help。git help --help --web。或者直接去看http://git-scm.com/docs

  • Aliasing commands
1
git config --global alias.ultimate-log "log --graph --oneline --decorate"

在GitHub上建一个dotfiles repository记录自己的gitconfig文件。作者的在https://github.com/mikemcquaid/dotfiles。运行install-dotfiles.sh即可。
GitHub上还有一些共享的dotfiles库,http://dotfiles.github.io

Vendoring dependencies as submodules

Learn how to maintain dependencies on other Git-based software projects within your own using Git submodules.
把别人用git管理的开源代码库作为自己代码的submodule。
Git’s submodules store the reference to a specific SHA-1 reference in another remote Git repository and store these in subdirectories of the current repository.

  • Adding a git submodule: git submodule add
    Add a the GitInPracticeReduxSubmodule repository as a submodule of the GitInPracticeRedux repository in the master branch.
1
2
3
4
cd /Users/mike/GitInPracticeRedux/
git checkout master
git submodule add https://github.com/username/GitInPracticeReduxSubmodule.git submodule
git commit --message "Add submodule"
  • Showing the status of submodules: git submodule status
    Show the current states of all submodules of a repository.
1
2
cd /Users/mike/GitInPracticeRedux/
git submodule status
  • Updating and initializing all submodules: git submodule update --init
    Initialize all submodules in your repository and update them to the latest revision.
1
2
cd /Users/mike/GitInPracticeRedux/
git submodule update --init
  • Running a command in every submodule: git submodule foreach
    Output some status information for every submodule in the GitInPracticeRedux repository.
1
git submodule foreach 'echo $name: $toplevel/$path [$sha1]'

Working with Subversion

  • Importing an SVN repository into a Git repository
    Import a Subversion repository into a local Git repository.
1
git svn clone --prefix=origin/ --stdlayout http://google-enterprise-connector-notes.googlecode.com/svn/ google-notes
  • Committing and pushing to an SVN repository from a Git repository
    Commit and push changes to a Subversion repository.
1
2
3
cd /Users/mike/GitSVN/
git commit --message "README.txt: improve grammar." README.txt
git svn dcommit
  • Accessing a GitHub repository with Subversion
    Check out the GitInPracticeRedux repository from earlier chapters using Subversion.
1
2
cd /Users/mike/GitInPracticeReduxSVN/
svn co http://github.com/username/GitInPracticeRedux GitInPracticeReduxSVN

Git best practices

  • Writing a good commit message
    A Git commit message guide by Tim Pope.
  • Building a commit from parts of files: git add --patch
    同一个文件里的改动想分为多次提交。会有一个交互性界面来选择文件中每个hunk是否要stage。
1
git add --patch
  • Avoiding whitespace issues: git diff --check
    Good Git practice:
    • No lines in files end with whitespace (trailing tab or space characters).
    • No lines in files start with one or more space characters followed immediately by one or more tab characters.
    • All files end with one or more newline character(s): a line-feed character on Unix or a carriage-return and a line-feed character on Windows.
1
git diff --check

Useful Figures

  • Git add/commit/checkout workflow
    Git add/commit/checkout workflow
  • A typical commit broken down into its parts
    A typical commit broken down into its part
  • Commit, blob, and tree objects
    Commit, blob, and tree objects
  • Parent commit pointers
    Parent commit pointers
  • Git add/commit/push/pull/checkout cycle
    Git add/commit/push/pull/checkout cycle

Ref: 《Git in Practice》