参阅: http://mindlee.net/2012/04/27/learning-git/ 【注意文章中所有的-均应为--】
1. git blame 熟悉之后,可以试着用git gui blame, 不过命令行参数有些不一样
E.g $git gui blame --line=44 src\Test.java
git ls-files
打印所有版控的文件列表
git ls-tree branch
打印内容类似
$ git ls-tree HEAD 100644 blob 8dead1626e5854f476b5c6eaa2507150c11605e0 a.txt 100644 blob 3e757656cf36eca53338e520d134963a44f793f8 b.txt
git show [参考http://*.com/questions/610208/how-to-retrieve-a-single-file-from-specific-revision-in-git]
git show $REV:$FILE 显示$REV这个commit的$FILE文件的内容,注意$FILE必须使用full path
git cat-file 用于显示object的内容,object通常包含blob, tree和commit
[参考:http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/ch08.html]
-p 漂亮的打印object内容,可以为blob(文件内容), tree(文件属性,名称大小,修改时间等)
--batch 从标准输入获取object,例如 echo e7eeb5f6fc8d1ee46002ed83e073f2a028d0a7e1 |git cat-file --batch
2. git reset 熟悉之后,学些使用
git reset 之soft, hard
git reset --hard HEAD~1 为完全恢复,即抹掉这条指定commit之前所有的commit记录
git reset --soft HEAD~1 只改变版本库,不改变暂存区和工作区; 【为取消指定这条commit之前的那条commit,还原到将修改保存到stage的状态(绿色),用处不大,完全可以用commit --amend来替换】
git reset --mixed HEAD~1 改变版本库和暂存区,不改变工作区; 【为取消commit, 还原到未将修改保存到stage的状态(红色),即修改内容依旧存在】
git reset . 将git add 命令更新到暂存区的内容撤出暂存区,++【注意不要和git checkout .弄混!!!】
即用版本库的HEAD重置暂存区;
git reset filename 将暂存区中filename撤出暂存区,git clean: 用于清除未加入(或者不打算加入)版本控制的文件和目录。参数-f用于文件,-d用于目录+++
参考:http://www.dotblogs.com.tw/larrynung/archive/2012/12/20/85834.aspx
git reflog 用于查看历史操作
git reset --hard HEAD@{1} 恢复到该操作,即reset之后的状态和该HEAD@{1}的操作结果是一致的++【可用于从merge冲突中脱身】【可用于在reset之后找回源码】
git reflog -5 查看最近五次的操作记录,默认是HEAD
git reflog show master –5 查看master最近五次的操作记录
git diff 比较分支或commit差异
git diff HEAD~1 HEAD~2 -- a.txt 比较不同commit之间相同文件的内容+++++
git diff brach1 branch2 -- a.txt 比较不同分支之间相同文件的内容+++++
git checkout
git checkout . 会用暂存区内容刷新工作区,相当于取消本地所有修改;++++
git checkout branch -- filename 用branch中的文件替换暂存区 + 工作区中相应的文件;++++
git checkout HEAD~1 -- welcome.txt 从历史中恢复文件;++++++
与git diff配合使用,选择正确版本的文件内容git tag
git tag -a v1.2 9fceb02 -m "Message here" 给某个旧的commit打标签
git push origin --tags 推送所有标签到remote repo,必须要加'--tags',pull的时候同样要加
3. git format-patch 熟悉之后,学习
git cherry-pick 详解:http://yiyingloveart.blogspot.com/2013/04/git-cherry-pick.html
分支之间的commit不需要指定分支名称,只需要提供commit的SHA1值
git cherry-pick certain_commit 从众多的提交中挑选出一个提交应用到当前的工作分支中【理解为将certain_commit重新应用到当前分支】
举例:
alias.hist=log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short
$ git hist * edc561a 2013-08-16 | hoho [Bing Wei] * 7b6abea 2013-08-16 | commit 4 from b2 [Bing Wei] * 4282054 2013-08-16 | commit 2 from b2 [Bing Wei] * 56a7fdd 2013-08-16 | commit 3 from b1 (testbb, branch1) [Bing Wei]
$ git cherry-pick 4282054 [master e5b4d82] commit 2 from b2 1 file changed, 1 insertion(+)
$ git hist * e5b4d82 2013-08-16 | commit 2 from b2 (HEAD, master) [Bing Wei] * edc561a 2013-08-16 | hoho [Bing Wei] * 7b6abea 2013-08-16 | commit 4 from b2 [Bing Wei] * 4282054 2013-08-16 | commit 2 from b2 [Bing Wei] * 56a7fdd 2013-08-16 | commit 3 from b1 (testbb, branch1) [Bing Wei]
同样可以从reflog的Sha1中恢复某commit.
参阅:http://blog.csdn.net/cn_chenfeng/article/details/7244615
4. git rebase的高级用法 参阅【http://blog.yorkxin.org/2011/07/29/git-rebase】
git show 参阅http://www.open-open.com/lib/view/1328070367499
5. 很难用到的git bisect,感觉就是丫就是为了证明什么,这命令真的太少用到了。。。
Refer to: http://heikezhi.com/yuanyi/git-201-slightly-more-advanced
6. 对象原理
http://git-scm.com/book/zh/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1
对象存储 之前我提到当存储数据内容时,同时会有一个文件头被存储起来。我们花些时间来看看 Git 是如何存储对象的。你将看来如何通过 Ruby 脚本语言存储一个 blob 对象 (这里以字符串 "what is up, doc?" 为例) 。使用 irb 命令进入 Ruby 交互式模式: $ irb >> content = "what is up, doc?" => "what is up, doc?" Git 以对象类型为起始内容构造一个文件头,本例中是一个 blob。然后添加一个空格,接着是数据内容的长度,最后是一个空字节 (null byte): >> header = "blob #{content.length}\0" => "blob 16\000" Git 将文件头与原始数据内容拼接起来,并计算拼接后的新内容的 SHA-1 校验和。可以在 Ruby 中使用 require 语句导入 SHA1 digest 库,然后调用 Digest::SHA1.hexdigest() 方法计算字符串的 SHA-1 值: >> store = header + content => "blob 16\000what is up, doc?" >> require 'digest/sha1' => true >> sha1 = Digest::SHA1.hexdigest(store) => "bd9dbf5aae1a3862dd1526723246b20206e5fc37" Git 用 zlib 对数据内容进行压缩,在 Ruby 中可以用 zlib 库来实现。首先需要导入该库,然后用 Zlib::Deflate.deflate() 对数据进行压缩: >> require 'zlib' => true >> zlib_content = Zlib::Deflate.deflate(store) => "x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235" 最后将用 zlib 压缩后的内容写入磁盘。需要指定保存对象的路径 (SHA-1 值的头两个字符作为子目录名称,剩余 38 个字符作为文件名保存至该子目录中)。在 Ruby 中,如果子目录不存在可以用 FileUtils.mkdir_p() 函数创建它。接着用 File.open 方法打开文件,并用 write() 方法将之前压缩的内容写入该文件: >> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38] => ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37" >> require 'fileutils' => true >> FileUtils.mkdir_p(File.dirname(path)) => ".git/objects/bd" >> File.open(path, 'w') { |f| f.write zlib_content } => 32 这就行了 ── 你已经创建了一个正确的 blob 对象。所有的 Git 对象都以这种方式存储,惟一的区别是类型不同 ── 除了字符串 blob,文件头起始内容还可以是 commit 或 tree 。不过虽然 blob 几乎可以是任意内容,commit 和 tree 的数据却是有固定格式的。
7. 查找删除文件的commit
http://www.vogella.com/articles/Git/article.html#retrievefiles_finddeletedfile
git log -1 -- [file path]
8. git submodule
引用一段《Git权威指南》的话: 项目的版本库在某些情况虾需要引用其他版本库中的文件,例如公司积累了一套常用的函数库,被多个项目调用,显然这个函数库的代码不能直接放到某个项目的代码中,而是要独立为一个代码库,那么其他项目要调用公共函数库该如何处理呢?分别把公共函数库的文件拷贝到各自的项目中会造成冗余,丢弃了公共函数库的维护历史,这显然不是好的方法。
Best practise:
http://www.kafeitu.me/git/2012/03/27/git-submodule.html
http://josephjiang.com/entry.php?id=342
一些常见的问题和处理方法:
1. 在依赖的子项目中使用特定的分支或者TAG
ref: http://*.com/questions/1777854/git-submodules-specify-a-branch-tag
操作:
1.1 进入子项目的目录
1.2 切换到签出你需要使用的分支
1.3 返回父项目/主项目的根目录
1.4 使用git add 和 git commit 保存修改
2. 撤销对子项目分支的切换
ref: http://*.com/questions/12192095/how-to-discard-change-to-git-submodule
操作:
2.1 进入父项目的根目录,执行git submodule update
将签出默认分支,从而撤销掉对其的修改
PS: 如果想撤销代码的修改,则直接进入子项目,使用git checkout -- 进行撤销操作
9. Git中的AutoCRLF与SafeCRLF换行符问题
CR回车 LF换行Windows/Dos CRLF \r\n Linux/Unix LF \n MacOS CR \r
#提交时转换为LF,检出时转换为CRLF git config --global core.autocrlf true
http://www.cnblogs.com/flying_bat/p/3324769.html
10. Git push的默认行为
http://*.com/questions/948354/git-push-default-behavior
http://longair.net/blog/2011/02/27/an-asymmetry-between-git-pull-and-git-push/
设置push的默认行为
git config --global push.default current
其他选项
nothing : 不进行push操作(除非指定repo) - 最安全
matching : Push所有branch名称匹配的分支,意味着如果当前你有n条分支,并且如果他们都能在repo上被找到的话,会全部被推送 - 默认选项,但是不推荐使用,因为非常容易造成将其他branch上未完成的commits推送到remote repo上
tracking : Push当前分支,不管它在多少个repo上能被找到(derpecated)
current : Push当前分支(待测试和tracking的区别)
simple: (Git 1.7.11新增) 优先推送到upstream,但是如果upstream上的分支名和当前的不一致,则拒绝推送,为git安装后默认使用的选项 - 推荐使用
PS: 我们可以使用git branch --set-upstream-to=[remote repo]/branch 来将我们本地的branch和远程指定仓库的分支的进行绑定,这样的话,当使用push.default = simple选项时, 使用git push就会第一时间到我们设定的remote repo上去寻找是否存在同名本地branch,然后进行推送
PS2: 万恶的upstream,一直以为只是用来追踪upstream的repo的,其实跟那个完全没关系好么!
11. 设置git pull --rebase 为默认操作
默认git pull操作是 git fetch + git merge
但是对于需要经常提交pull request的我们来说,需要的是git pull --rebase, 所以下面的config可以帮助我们将rebase作为默认操作,减少重复操作
参考: https://coderwall.com/p/yf5-0w
$git config branch.autosetuprebase always
这样所有新创建的branch就会使用rebase当你使用git pull的时候
针对已经存在的branch,则需要以下设置
$git config branch.YOUR_BRANCH_NAME.rebase true
最后,如果你需要在git pull的时候使用merge,则使用
$git pull --no-rebase
12. 找到好东西了:) - 教你如何抹掉历史记录
先来看下git rebase --help的说明
We can get this using the following command:
git rebase --onto master next topic
Another example of --onto option is to rebase part of a branch. If we have the following situation:
H---I---J topicB
/
E---F---G topicA
/
A---B---C---D master
then the command
git rebase --onto master topicA topicB
would result in:
H'--I'--J' topicB
/
| E---F---G topicA
|/
A---B---C---D master
This is useful when topicB does not depend on topicA.
这里如果topicB不依赖topicA,那么我们可以用上面的命令将topicB这个commit直接接到topicA上面
现在用commit的实例来演示一下
设想你有如此的记录
$git status
last-commit
to-be-removed-commit
old-commit
...
你需要将to-be-removed-commit从当前分支中抹掉,那么你可以用如下命令来实现
git rebase --onto HEAD^^ HEAD^ HEAD
这里面HEAD^^意味着倒数第三个commit, 即 old-commit, 别的以此类推
那么git rebase --onto是做什么的呢?
基于上面的演示,我们可以知道--onto可以将最后提供的commit或者分支直接接到第一个提供的commit或者分支上
当执行完这个命令之后,你会跳到一个新的临时分支上,这时候就需要你手动checkout出来。
OK, 当你理解这些之后,那么下面的例子你应该同样可以理解了
A range of commits could also be removed with rebase. If we have the following situation:
E---F---G---H---I---J topicA
then the command
git rebase --onto topicA~5 topicA~3 topicA
would result in the removal of commits F and G:
E---H'---I'---J' topicA
不过一定要注意:topicB不能依赖topicA,否则无法成功。
待研究 http://www.worldhello.net/git-quiz/exam02.html
rev-list??
git log ..maint
git show :0:./file > file-2
git describe --tags --always --dirty
git name-rev
git log origin/master..
git diff-tree origin/master..
git request-pull origin/master URL-of-your-repo
git diff --stat origin/master
13. 查看修改的内容
参考: http://*.com/questions/1587846/how-do-i-show-the-changes-which-have-been-staged
git diff
显示那些本地修改的,但尚未加入到缓存区(staged)的内容
git diff --cached
显示那些本地修改的,并且加入到缓存区(staged)的内容
git diff HEAD
显示所有的修改内容,包括加入或者未加入到缓存区(staged)的内容