前言
该手册(以下简称它)不是Git入门教程,如果您想入门Git推荐廖雪峰的Git教程
这只是一篇个人学习Git时顺带记录的命令手册以及对Git的一些理解心得,如有不正确的地方欢迎大家留言指正。
它包含了Git几乎所有的高级命令和部分底层命令,每个命令都有详细的解释和示例告诉您如何使用,以及使用时需要注意的地方;内容按模块划分,比如和add
相关的命令就都会集中在一个模块,大家可以选择自己感兴趣的模块自由阅读,由于某些命令的选项非常多但是并不常用,所以该手册可能没有记录,如果想查看某个命令的所有选项请查阅Git命令参考。
有些比较简单的命令这里可能没有记录,文章中的()表示的是全称,例如{u(upstream)},表示upstream是u的全称,实际使用的时候用@{u}或者@{upstream}都可以。
Git本身是一个内容寻址文件系统,Git的核心部分是一个简单的键值对数据库,你可以向Git仓库中插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回该内容。
Tips:
Git里一些同名的选项在相似的地方也可以使用。例如
--abort
选项,和git merge --abort
搭配可以撤销合并操作,和git rebase --abort
搭配可以撤销rebase操作。Git里的大部分选项都是可以搭配一起使用的,例如
git log --author='internetwei' --after="2020-01-01" --before="2020-06-01" --no-merges -- WXReader/Book/BookRack/BookReader
这行命令搭配了多达5个选项(指定作者,指定开始时间,指定结束时间,非合并提交,指定路径), 它的意思是:打印internetwei
作者在2020年1月1号到2020年6月1号之间对WXReader/Book/BookRack/BookReader
这个文件夹(包括文件夹下所有文件)进行修改的所有提交记录但不包括合并提交。Git里需要使用哈希值的地方不需要复制整个哈希字符串,通常只需要复制前6~8个字符就够了,如果项目比较大可以扩大到前8~10个,即使是像
Linux
这样的项目也只需要前10~12个字符即可保证唯一性。
Git的特点
Git和SVN(其他版本控制工具)对待文件的区别?
Git保存的不是文件的差异或变化,而是文件快照(快照可以简单的理解为这个文件的副本),如果一个文件在提交的时候没有任何变化那么Git只会保留一个链接指向之前存储的内容;而SVN保存的是这个文件提交时的差异变化。
Git如何保证完整性?
Git所有的数据在存储前都会计算校验和,计算校验和的机制叫做SHA-1散列(hash、哈希),这是一个由40个16进制字符(0
9,af)组成的字符串,基于文件的内容和目录结构计算出来的(由于相同的文件计算出来的哈希值是一样的,所以当你存储一个文件到Git仓库时,如果Git发现已经有了就只会创建一个链接指向之前存储的那个对象),计算出来的哈希值就像这样:a6b6695a3594cc79b3c3fa9ef5772df036ec8d8e
,校验和的前两个字符用于命名子目录,剩下的38个字符则用作文件名,Git数据库中保存的信息都是以哈希值来索引。Git暂存和提交时分别做了什么?
当你进行暂存操作时,Git会对每个文件计算校验和,然后将这些校验和加入到暂存区等待提交。
当你进行提交操作时,Git会计算每个子目录的校验和以及对所有的文件创建快照并保存快照的索引,然后Git会在仓库中把这些校验和保存为一个树对象,然后Git会创建一个提交对象,它包含一个指向最上层树对象的指针和一个指向父对象的指针(如果是合并提交会有2个父对象,如图1),还有作者的名称、邮箱地址、提交说明。
图1
Git暂存区的作用
大部分的版本管理(SVN)都没有暂存区的概念,Git的add命令和其他类似工具的add命令也不太一样,其他工具的add命令是将文件加入到版本管理,而Git的add命令有3种作用:1. 将未跟踪的文件加入到版本管理;2. 将已修改的文件内容加入到暂存区;3. 将冲突文件标记为冲突已解决状态。由于Git暂存区的存在,你可以在完成提交前审查你的提交内容,还可以精确的控制每一行的提交内容。例如一个文件有20行是修改BUG,另外有100行是添加新功能,现在我只想提交这个文件的修改BUG的那20行代码,那么只需要把这20行代码加入到暂存区就行了。
Merge的逻辑
如果Git发现可以快进合并的话那么Git会直接把当前分支指向合并分支的最新提交(快进合并有一个缺点就是当你删除合并分支后会无法知道这个分支以前是从哪个分支合并过来的,如果想禁用快进合并可以使用
--no-ff
选项,这样Git会创建一个合并提交),如果不可以快进合并的话Git会使用这两个分支的末端提交对象以及这两个分支的首个公共祖先提交,做一个简单的三方合并。GPG签名
GPG可以签名你的提交或标签,它能够有效的提高Git仓库的安全性,因为它可以证明这个提交或标签是通过你信任的电脑提交的而不是某个冒用你名号的黑客,这相当于赋予了提交对象一种不可更改性,即使黑客通过某种办法拿到了Git仓库的读写权,他也没有办法添加或使用–force修改任何带有你GPG签名的提交或标签。
由于Git的作者名称和邮箱地址是可以随意填写的,如果有人将他的作者名称和邮箱地址改成和你的一样,然后通过某种方式将代码推送到了你的仓库中……这可能会导致你或其他人遭受损失,使用GPG签名可以尽量避免这种情况的发生。使用GPG签名过的提交在Github上显示如图2,你可以点击标签查看具体的签名信息。
图2HEAD是什么?
HEAD本质就是一个指针,也可以叫做符号指针,因为通常情况下它指向一个分支(符号),通过它可以获取你仓库当前的状态,可以使用
git symbolic-ref HEAD
查看当前HEAD的状态。当HEAD指向一个具体的提交对象而不是分支引用时,Git会提示您处于“游离状态”,“游离状态”表示你当前不处于任何分支,所以你不能进行提交,只能查看提交快照的内容,也可以进行修改运行,如果你想要进行提交则需要先创建一个分支,“游离状态”通常用于调试或回滚某次提交。
HEAD^和HEAD~的区别?
当后面不跟数字的时候^和~表示的意思相同,都表示第1个父提交对象;如果后面跟上数字就有差别了,^后面只能跟数字1或者数字2,^1表示当前对象的第1父提交,^2表示当前对象的第2父提交,只有合并提交对象才有第2父提交,第1父提交就是合并时接受合并的分支,第2父提交就是合并时被合并的那个分支;~后面理论上可以跟任意大于0的数字,~1表示当前提交对象的第1个父提交对象,~2表示当前提交对象的第2个父提交对象,以此类推,~2和~~表达同一个意思,不过当你想表示第100个父提交时,显然用~100比写100个~更现实,~和^可以组合使用,例如
git show HEAD~2^2
这行命令表示查看当前提交的第2个父提交的第2父提交内容。Git中危险的命令
在Git中任何已提交的内容几乎都是可以恢复的(那些被覆盖的提交也可以恢复),但是未提交的内容丢失后可能再也找不回了,任何可能会导致工作区内容丢失的命令都是危险的命令,在Git中危险的命令总共可以分为3类:
- 所有带
-f
选项的命令,-f
的全称是--force
,它表示强制的意思,例如git push -f
表示强行将本地仓库推送到远程仓库,git switch -f dev
表示强行切换到dev分支,总之使用-f
选项时请小心。 git reset
命令总共有3个选项,--soft
、--mixed
、--hard
,其中只有--hard
选项是危险的命令,其他两个都是安全的,当你使用--hard
选项时Git会使用仓库中指定的快照内容覆盖工作区。git checkout
命令的本质是操纵HEAD指针,它的后面可以是分支名表示切换分支,也可以是文件路径,如果后面是文件路径,那么它就是一个危险的命令,Git会使用仓库中指定的快照内容覆盖工作区的指定文件(建议使用switch命令来切换分支,防止误操作导致丢失文件内容)。
- 所有带
checkout和reset的区别
很多人容易把
checkout
和reset
弄混,因为它们都可以用来回滚历史提交,虽然最终效果是一样的,但是实现过程确不一样;简单的说checkout命令会直接修改HEAD指针的指向,而reset命令修改的是HEAD指针指向的对象。假设master分支有一个提交对象(ab4adf),你想回滚到这次提交,Git提供了2种方式:
git checkout ab4adf
和git reset --hard ab4adf
都可以达到你的目的,但它们的实现机制略微不一样;git checkout ab4adf
会把HEAD指针直接指向ab4adf这个提交对象,然后再使用ab4adf这个提交对象的快照内容覆盖工作区的内容来达到目的(如果你细心观察的话会发现Git会提示你当前进入了HEAD游离状态),如图3所示;git reset --hard ab4adf
的实现就稍微复杂一些,它会操纵HEAD指针指向的对象(这里是master分支)指向ab4adf这次提交,然后再使用ab4adf这次提交中的快照内容覆盖暂存区和工作区的内容,如图4所示。
图3
图4Git协议的区别
HTTP协议
HTTP协议有2个版本,一种是哑协议(只能通过GET方式读取仓库内容),另一种是智能协议,哑协议由于自身问题已经基本退出舞台了;相比SSH协议,HTTP协议可以使用用户名、密码授权是一个优势,就算不知道用户名和密码也可以获取项目,这很适合开源项目的推广,另一个优势就是HTTPS协议被广泛使用,一般的企业防火墙都会允许这些端口的数据通过;唯一的缺点就是在一些服务器上,架设HTTPS协议的服务端稍微棘手一些
在使用HTTPS协议时,可能要重复输入用户名和密码,这时可以执行
git config --global credential.helper cache
命令来将用户名和密码临时缓存到内存中,可以使用--timeout <seconds>
选项控制保留时长,默认是900秒(15分钟)SSH协议
架设SSH协议相对简单,SSH守护进程很常见,多数管理员都会使用,多数操作系统都包含它及相关的管理工具,相对于HTTPS协议它不用输入用户名和密码也更加方便;它的缺点是不支持匿名访问Git仓库,即使只是读取数据,使用者也必须通过SSH访问你的主机,所以SSH不利于开源项目
Git协议
是所有协议里传输速度最快的,因为没有加密和授权的开销;它的缺点是由于没有加密和授权,要么谁都可以克隆这个仓库,要么谁也不能,它也是最难架设的协议,还要求防火墙开放9418端口,但是企业防火墙一般都不会开放这个非标准端口。Git协议一般和其他协议(例如HTTPS)搭配使用,使用Git协议进行拉取,使用HTTPS协议进行推送。
Local协议
一般不会用到,所有没有去了解这方面的知识,有兴趣的可以自行搜索
Git 常用命令
git help <verb>
获取指定Git命令的帮助文档示例:
git help add
获取add命令的帮助文档-a
列出所有的Git命令-c(--config)
列出所有可用的配置变量git add -h
简易版的帮助文档等价命令:
git <verb> –help
man git-<verb>
git rm <path>
删除指定文件--cached(--staged)
从暂存区删除指定文件如果不小心把要忽略的文件添加到了Git仓库中,后续即使在忽略文件中声明也还是会在每次提交时提示你该文件未暂存;这时可以使用该命令从暂存区删除指定文件,该操作不会影响工作区。
-n(--try-run)
列出需要删除的文件git describe <commit>
根据指定提交对象生成一个字符串构建号。它由提交对象最近的标签名、自该标签之后的提交数目和你所描述的提交的部分哈希值构成(可以为提交附上一个可读的名称)。git reflog
显示所有的引用日志reflog的本质只是记录HEAD指针发生变化的记录,它只存在于本地仓库,一般情况下Git只会保留最近几个月(默认是90天)的记录
git reflog --date=local --all | grep <分支名>
获取指定分支的HEAD变更记录可以用来查看某个分支是基于哪个分支创建的,由于reflog所以只能在创建分支的那台电脑上才能生效并且时间太长也会失效。
git clean
移除那些没有被Git跟踪的文件,该命令需要搭配以下选项执行-d
递归删除子目录下的文件/文件夹-f
如果clean.requireForce
没有被设置为false,则必须要使用-f
告诉Git需要删除那些文件-n(--dry-run)
只显示那些会被移除的文件-x
默认情况下不会删除被忽略的文件,-x表示将移除那些被忽略的文件-X
仅删除被忽略的未跟踪文件git gc
手动对Git仓库进行打包优化--aggressive
加上此选项Git将会花费更多时间优化仓库git fsck
验证数据库中对象的有效性--unreachable
打印那些存在但无法从任何节点访问的对象git restore
恢复工作区的文件--staged <path>
将指定文件从暂存区移除-s(--source) <tree_id> <path>
使用指定树中的文件恢复工作区的指定文件git clone -d <分支名> <path> --depth=<number>
克隆指定仓库下指定分支的number条提交历史示例:
git clone -b master <path> --depth=1
只克隆远程仓库的master分支的最近一条提交
Git Config
git config --list
查看Git配置信息,配置信息可能会重复,如果存在重复Git会以最后一个值为准。--show-origin
显示该配置的来源(路径)git config <范围> <选项> <值>
在指定范围内设置指定配置信息。示例:
git config --global user.name 'author'
在全局范围内设置提交者的名称。
Git Config总共有3个范围,分别是system、global、local(默认值),权重依次递增,system的影响范围是最大的,针对当前电脑下的所有Git仓库都生效;global针对当前用户下的所有Git仓库生效;local只对当前Git仓库生效。git config --global merge.conflictstyle diff3
修改合并冲突样式为diff3,这种样式下会额外显示base的内容,如图5。图5
git config --global help.autocorrect 50
设置该选项后,当Git匹配了相似的命令时会在倒计时结束后自动执行该命令git config --global core.autocrlf true
启用该功能的话当你提交时Git会自动把回车和换行转换成一个换行符,而在检出代码时把一个换行符转换成回车和换行。在Windows的某些编辑器中,换行符是由回车符和换行符2个命令组成的,而在macOS和Linux系统中换行符只有一个符号。如果同一个项目在Windows和其他平台上开发,可能会遇到空格冲突。
input
设置为input告诉Git在提交时把回车和换行转换成换行,检出时不转换core.whitespace
空格处理方案,Git提供了6种处理多余空白字符的主要选项,3项默认开启,3项默认关闭默认开启:blank-at-eol(查找行尾的空格),blank-at-eof(盯住文件底部的空行),space-before-tab(警惕行头tab前面的空格)
默认关闭:indent-with-non-tab(揪出以空格而非tab开头的行,你可以用tabwidth选项控制它),tab-in-indent(监视在行头表示缩进的tab),cr-at-eol(告诉Git忽略行尾的回车)
如果想关闭某个选项,可以在输入设置选项时不指定它或在它前面加个 -
git config <范围> alias.<别名> '<全名>'
在指定范围内设置一个Git别名。示例:
git config --global alias.st 'status'
在全局范围内给status设置一个别名st。别名通常用来简化命令,如果想给非Git命令起别名,例如gitk,可以这样设置
git config --global alias.gitk '!gitk'
,当你调用”git gitk”时会执行”gitk”命令。一些常用的别名:
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.s 'status -s'
git config --global alias.last 'log -1'
git config <范围> --unset alias.<name>
删除指定别名
Git Diff
git diff
显示工作区和暂存区之间的差异--cached(--staged)
显示暂存区和最新提交之前的差异--check
打印所有可能的空格错误--ours
冲突时查看合并引入了什么--theirs -b
冲突时查看合并的结果与另一边有什么不同,-b表示去除空格--base
冲突时查看文件在两边是如何改动的
Git Log
git log
显示当前分支下的提交历史-n
显示指定数量的提交记录,例如git log -2
显示最近2条提交记录--show-signature
显示GPG签名信息--stat
显示提交时的简略信息--abbrev-commit
显示简短的哈希值--relative-date
显示一个相对时间(例如 9 hours ago)--graph
在日志旁以ASCII图形显示分支和合并历史--oneline
–pretty=oneline和–abbrev-commit的缩写--pretty=
使用其他格式显示提交历史oneline
将每条提交放在一行显示short
不显示提交时间full
不显示提交时间但额外显示提交者信息fuller
显示提交者信息以及提交时间format
自定义显示风格(自定义风格可以不受Git版本影响)示例:
git log --pretty=format:"%Cred%h%Creset - %C(yellow)%an%Creset, %C(green)%ar%Creset : %s"
一行显示简写的提交哈希值 - 作者名称, 提交相对时间 : 提交说明。format可接受的选项如下:
%H
提交的完整哈希值%h
提交的简写哈希值%T
树的完整哈希值%t
树的简写哈希值%P
父提交的完整哈希值%p
父提交的简写哈希值%an
作者的名称%ae
作者的电子邮件地址%ad
作者的修订日期%ar
作者的修订日期,按多久以前的方式显示,例如20 hours ago%cn
提交者的名称%ce
提交者的电子邮件地址%cd
提交日期%cr
提交日期,按多久以前的方式显示%s
提交说明Git支持的颜色选项:
normal、black、red、green、yellow、blue、magenta、cyan、white
Git支持的字体属性
bold、dim、ul、blink、reverse
--author
显示和指定作者的提交记录--committer
显示和指定提交者的提交记录--grep
显示提交说明中包含指定字符串的提交记录-S
显示对指定字符串进行了修改的提交记录(常用来查找某个函数的修改提交记录)-G <正则表达式>
使用正则查找相关的提交记录-- <path>
显示指定路径下的修改记录(常用来和其他选项搭配查找某个文件下某个函数的修改提交记录,一般写在最后)-L
显示指定文件中指定函数的相关修改提交记录示例:
git log -L :firstApplication:WXReader/AppDelegate.m
显示AppDelegate.m文件下与firstApplication相关的修改提交--no-merges
不显示合并提交--after(--since)
显示指定时间之后的提交记录--before(--until)
显示指定时间之前的提交记录--after
和--before
可以搭配指定的时间值,例如”2008-08-08”,也可以是”2 years 1 day 3 minutes ago”、”2.weeaks”这样的相对日期示例:
git log --author='internetwei' --after="2020-01-01" --before="2020-06-01" --no-merges -- WXReader/Book/BookRack/BookReader
显示作者internetwei在2020年1月1号到2020年6月1号之间对WXReader/Book/BookRack/BookReader
这个文件夹(包括文件夹下所有的文件)有关的所有修改提交但不包括合并提交<分支1>..<分支2>
显示分支2有但分支1没有的提交记录(常用来检查分支哪些提交未推送)和..语法等价的命令:
git log ^<分支1> <分支2>
git log <分支2> --not <分支1>
上面两种命令是..语法的扩展,除了查询2个引用,还支持查询超过2个引用,例如
git log refA refB ^refC
表示显示所有被refA
或refB
包含但不被refC
包含的提交记录<分支1>...<分支2>
显示分支1和分支2不共有的提交--left-right
和…等类似语法搭配使用,可以用<、>表示左右分支git shortlog
显示当前分支的修改日志文档示例:
git shortlog --no-merges master --not v1.0
显示master分支从v1.0标签后的所有非合并提交提交日志
Git Rebase
Merge和Rebase的区别: |
图6
图7
git rebase <分支>
将当前分支上的提交依次变基到目标分支上git rebase <分支1> <分支2>
将分支2上的提交依次变基到分支1上示例:
git rebase master experiment
将experiment分支上的提交内容依次变基到master分支上,如图8图8
切换到master分支再合并experiment分支就不会有分叉的提交历史了,如图9
图9
rebase的原理:
首先找到这2个分支的最近共同祖先C2
然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件
然后将当前分支指向目标基底C3
最后以此将之前保存的临时文件的修改依次应用
git rebase --onto <分支1> <分支2> <分支3>
找到分支3在分支2分歧后的提交,然后把这些提交在分支1上依次应用一遍示例:
git rebase --onto master server client
找到client分支,找出它从server分支分歧之后的提交,然后把这些提交在master分支上重放一遍,如图10所示图10
git rebase -i <区间>
对指定区间内的提交进行交互式变基示例:
git rebase -i HEAD~2
对最近3次提交进行交互式变基,交互式变基的功能非常强大,可以实现“将任意提交压缩成一个提交”、“将一个提交拆成多个提交”、“修改任意提交的提交说明”、“重新排序”、“移除提交”等功能git commit --amend
修改最新的提交信息虽然它是commit开头的命令,不过它的本质就是rebase操作,所以把它放在rebase里
该命令有2个作用:
修改最新的提交说明;确保当前暂存区是干净的,然后运行此命令,它会将你带到提交说明编辑框,重新编辑提交信息然后保存退出即可。
补充提交文件;例如上次提交时忘记添加一个文件,或者上次提交的文件又有修改了,而这次修改应该和上次提交是一起的;首先将需要补充提交的文件添加到暂存区,确保暂存区的文件是你要补充提交的,然后运行此命令,编辑提交信息保存退出即可。
git cherry-pick <commit>
将指定提交的修改在当前分支的基础上重放一遍并提交
Git Remote
git remote
显示所有远程仓库的名称-v
显示远程仓库的具体信息show <仓库名>
显示指定远程仓库的详细信息ls-remote <仓库名>
显示指定远程仓库的完整引用列表prune <仓库名>
删除本地镜像仓库存在但远程已删除的分支信息git remote set-url <仓库名> <url>
修改指定仓库的地址git fetch <仓库名>
拉取指定仓库的信息并更新本地的仓库信息(这只会更新本地的远程镜像仓库,不会修改本地分支和工作区内容)git pull
拉取上游分支的新提交并合并,它是fetch
和merge
2个命令的缩写pull 命令需要该分支拥有上游分支,如果没有上游分支会提示错误信息,它会运行
git fetch
将远程仓库的信息拉取下来,然后运行git merge
合并远程的提交git branch -u(--set-upstream-to) <远程分支引用>
设置/修改当前分支的上游分支引用git branch -u origin/dev
设置/修改当前分支的上游分支为origin仓库的dev分支当某个分支拥有上游分支后,可以使用{u(upstream)}代替上游分支
git push
将当前分支的新提交推送到上游分支git push origin refs/head/dev:refs/head/dev
这是推送命令的全称,将本地dev分支推送到远程dev分支上git push origin v1.0
推送指定标签到远程仓库(默认推送并不会推送标签)git push origin --tags
推送所有标签到远程仓库git push origin --delete dev
删除远程仓库的dev分支git push origin --delete v1.0
删除远程仓库的v1.0分支git push origin --delete
命令实际是git push origin :refs/head/dev
,推送一个空对象覆盖远程仓库的dev分支(这么做只是从服务器上移除一个指针,Git通常会将数据保存一段时间直到垃圾回收机制运行。
Git Tag
Tag和Branch的区别: |
git tag <标签名>
给当前提交创建一个轻量Tag-a
创建一个附注Tag<hash>
尾随一个哈希值,可以给指定提交创建Tag-s
使用GPG对标签进行签名-l(--list) "v1.*"
显示所有以”v1.”开头的标签,支持正则匹配-v
使用GPG验证签名(前提是签名者的公钥需要在你的钥匙链中)
Git Branch
git branch
显示所有本地分支-v
显示分支对应的最新提交-vv
显示分支与上游分支的落后/领先情况-merged
显示与当前分支已合并的分支--no-merged
显示与当前分支未合并的分支
Git Merge
git merge <分支名>
合并指定分支--squash
将合并产生的多个提交压缩成一个提交--verify-signature
拒绝合并那些GPG签名验证不通过的提交-Xignore-space-change
将一个空白符与多个连续的空白字符视作等价-Xignore-all-space
合并时完全忽略空白修改-Xours
遇到冲突时保留当前分支的修改内容-Xtheirs
遇到冲突时保留目标分支的修改内容git merge-base <分支1> <分支2>
获取2个分支最近的公共提交git merge --verity-signature -S <分支名>
生成一个签名的合并提交git merge -s ours <分支名>
做一次假的合并,记录一个以两边分支作为父结点的新合并提交,但是它甚至根本不关注你正合并入的分支,它只会简单地把当前分支的代码当作合并结果记录下来假如你有一个分叉的release分支并且在上面做了一些你想要在未来某个时候合并回master的工作,与此同时master分支上的某些bugfix需要向后移植回release分支,你可以合并bugfix分支进入release分支同时也merge -s ours 合并进入你的master分支(即使那个修复已经在那里了)这样当你之后再次合并release分支时,就不会有来自bugfix的冲突。
Git 邮箱工作
git format-patch -M <分支名>
生成当前分支和指定分支之间不共有的提交补丁文件到根目录下(有几个提交就会生成几个补丁文件)示例:
git format-patch -M origin/master
生成当前分支和origin/master分支之间不共有的提交补丁文件到根目录下(这会生成一份可以邮寄的mbox格式的文件,它将每一个提交转换为一封电子邮件)。-M
表示允许Git检查是否有对文件重命名的提交git apply --check <path>
检查指定路径下的补丁内容是否可以被应用git apply <path>
应用指定路径下的补丁内容git am <path>
和git apply
功能类似,不过比它更智能--resolved
继续应用下一个补丁-3
表示当应用补丁发生冲突时Git会尝试进行三方合并,该选项默认是关闭的am
的含义:应用(Apply)一系列来自邮箱(Mailbox)的补丁git apply
用于应用git diff
或Unix diff
命令创建的补丁,它和patch -p1
命令几乎是等效的,但是git apply
更加严格,相对于patch
来说它能够接受的模糊匹配更少,git apply
采用了一种”要么全部应用,要么就全部撤销”的模型,即补丁只有全部内容都被应用和全部不被应用两种状态,如果补丁是用git format-patch
来创建的,那么建议使用git am
来应用补丁,只有对老式的补丁,你才必须使用git apply
Git Rerere
Rerere是“重用已记录的冲突解决方案”的意思,它是一种简化冲突解决的方法,当启动rerere时,Git会维护一些成功合并之前和之后的镜像,当Git发现之前已经修复过类似的冲突时,便会使用之前的修复方案而不需要你的干预 |
git config --global rerere.enabled true
启用Rerere功能对于已经创建的仓库如果想启动Rerere功能,需要手动在.git文件夹下创建一个rr-cache文件夹
开启Rerere后会增加本地仓库的体积,rr-cache文件夹只存在于本地仓库,不会推送到远程
git checkout --conflict=merge <path>
将指定文件恢复到Rerere执行前的状态
Git 归档
git archive <commit> --prefix='<解压后的文件名>/' | gzip > <压缩包的名称>.tar.gz
基于指定提交对象创建一个当前所有快照内容的压缩文件示例:
git archive master --prefix='project/' > gzip > master.tar.gz
基于master最新提交的所有快照内容创建一个tar.gz压缩文件git archive master –prefix=’project/‘ –format=zip > `git describe master`.zip 基于master最新提交的所有快照内容创建一个zip压缩文件
git bundle
对分支进行打包示例:
git bundle create repo.bundle HEAD master
对master分支的所有提交历史进行打包git bundle verify <path>
检查指定bundle包是否合法git bundle list-heads <path>
列出指定bundle包的引用
Git Stash
git stash
贮藏所有已跟踪的未提交文件,并还原暂存区和工作区的修改变化-u(--include-untracked)
贮藏所有已跟踪和未跟踪的文件(不包括忽略文件)-a(--all)
贮藏所有文件(包括忽略文件)--keep-index
保存到贮藏的同时不清空暂存区的内容list
查看所有贮藏文件列表apply
恢复最新的贮藏文件但不删除,可以指定某个贮藏文件pop
和apply命令一样但是会自动删除贮藏文件(仅在恢复成功时自动删除)--index
如果在pop
和apply
时加上此选项会把贮藏前在暂存区的内容重新加到暂存区中,默认全部恢复在工作区git stash <贮藏文件> <分支名>
使用指定贮藏文件创建一个分支
Git Add
git add
将文件加入到暂存区中-i(--interactive)
使用交互式终端模式进行暂存-p(--patch)
自定义暂存补丁文件(该命令只是-i的一个子命令)
Git Commit
git commit
将暂存区的文件提交到Git仓库-S
使用GPG签名此次提交-m "注释"
使用指定注释作为提交内容-a
将已跟踪的所有文件加入到暂存区(未跟踪的文件不会加入到暂存区)-am
-a和-m这2个命令的缩写
Git 搜索
git grep <字符串>
在工作区中搜索指定字符串出现的所有位置示例:
git grep --break --heading -n -e '#define' --and \( -e kEncryptionSecret -e kEncryptionKey \) v4.6.0
查看在v4.6.0标签的Git代码库中定义了常量名包含”kEncryptionSecret”或”kEncryptionKey”这两个字符串的位置-n(--line-number)
显示在文件中的行数-c(--count)
显示在文件中匹配的数量-p(--show-function)
显示该字符串上下文内容
Git Subtree
这里没有提到 git submodule(子模块),因为使用子模块相比Subtree更麻烦,例如在有子模块的仓库中切换分支需要重新初始化一下子模块,git subtree可以实现git submodule的功能,并且比git submodule更简单。 |
git subtree add --prefix=<path> <link> <分支名>
添加指定仓库下的指定分支内容到指定路径下示例:
git subtree add --prefix=sub/libpng <link> master
拉取指定仓库下master分支到sub/libpng文件夹下git subtree pull --prefix=<path> <link> <分支名>
拉取子树的修改提交git subtree push --prefix=<path> <link> <分支名>
推送子树的修改提交
Git Filter
git filter-branch
重写提交历史示例:
git filter-branch --tree-filter 'rm -f password.txt' HEAD
从Git仓库中删除password.txt,–tree-filter表示检出项目的每一个提交后运行指定的命令然后重新提交结果,如果要让上述命令在所有分支上运行,可以加上–allgit filter-branch --subdirectory-filter trunk HEAD
将trunk子目录设置为项目的根目录,Git会自动移除所有不影响子目录的提交git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "internetwei@foxmail.com" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
遍历所有提交,如果作者邮件地址是internetwei@foxmail.com则修改作者名称和邮件地址(即使某个提交不用修改但是它的哈希值也会更改)git filter-repo
重写提交历史Git官方建议使用git filter-repo而不是git filter-branch,因为git filter-branch在重写提交历史时充满大量的陷阱,性能也很低,它的设计架构在接口的每一层都存在泄漏,这使得它几乎不可能在不向后兼容的情况下更改设计的任何内容。相比较而言git filter-repo的功能更多,而且性能也高出很多(有资料说用git filter-repo几个小时完成的任务用git filter-branch需要等待3个月)。
git filter-repo --path-rename <old_name:new_name>
遍历提交历史,将作者名从old_name修改为new_name--tag-rename <old_tag:new_tag>
遍历提交历史,重命名以old开头的标签并以new开头,例如--tag-rename foo:bar
会将foo-1.2改为bar-1.2想学习git filter-repo请查看git filter-repo命令手册
Git Show
git show <分支名>@{yesterday}
查看指定分支在昨天的最后一次提交对象yesterday可以是其他任意Git可以识别的时间
git show <commit>:<path>
查看指定提交下指定文件的内容
Git Reset
git reset --soft <commit>
将当前分支移动到指定提交上(这样做会导致Git仓库和暂存区与工作区的内容不一致,而暂存区和工作区的内容一致,所以Git会提示你进行commit操作和Git仓库保持一致),如图11。图11
git reset --mixed <commit>
默认行为,将当前分支移动到指定提交上并且更新暂存区的内容(这样做会导致Git仓库和暂存区的内容一致,但是和工作区的内容不一致,这时Git会提交你进行add操作将需要提交的文件添加到暂存区),如图12图12
git reset --hard <commit>
将当前分支移动到指定提交上并且更新暂存区和工作区的内容,这也是reset唯一危险的命令(这样做Git会使用版本库中的内容更新(覆盖)暂存区和工作区的内容),如图13图13
git reset <path>
将指定文件从暂存区中删除它本质上调用的是
git reset --mixed HEAD <path>
命令,使用版本库中的指定文件覆盖暂存区的文件,所以它可以将文件从暂存区中移除
Git Checkout
git checkout -b <分支名>
创建并切换到指定分支该命令其实是
git branch <分支名>
和git checkout <分支名>
的缩写切换分支建议使用
git switch <分支名>
,一是因为switch比checkout更容易理解,二是因为switch命令比checkout命令更安全(例如本地有一个文件名和某一个分支同名,当你使用git checkout <文件/分支名> 时可能会不小心覆盖工作区的内容)git checkout -- <path>
撤销工作区指定文件的修改该命令会使用当前版本库中的指定文件覆盖工作区的指定文件,如果工作区的文件有任何修改将会丢失
Git Revert
git revert <commit>
还原指定提交的内容该命令用于将某次提交的内容重置并生成一次新提交
Git Blame
git blame -L <range> <path>
查看指定文件中指定范围内的修改提交记录示例:
git blame -L 68,100 WXYZ_BookReaderViewController.m
查看WXYZ_BookReaderViewController.m文件有关第68到100行的修改提交记录-C
该选项表示Git会分析你正在标注的文件,并且尝试找出文件中代码片段的原始出处
Git Bisect
git bisect
使用二分搜索查找引入错误的提交使用流程:
git bisect start
启动二分查找git bisect bad
告诉Git当前提交是有问题的git bisect good <commit>
告诉Git没有问题的提交是哪一次git bisect good
告诉Git当前提交没有问题git bisect reset
结束二分查找git biesect start <bad_id> <good_id>
快速执行二分查找,第一个参数是不正常的提交,第二个参数是正常的提交git bisect run <path>
执行脚本进行二分查找
Git Replace
git replace
替换指定对象git replace <commit1> <commit2>
将commit1替换为commit2
Git 底层命令
git ls-files
显示有关索引和工作树中文件的信息-u(--unmerged)
显示冲突对象的哈希值-s(--stage)
显示暂存区当前状态git rev-list
按时间倒序列出提交对象--count <分支名>
查看指定分支的提交数量git cat-file
查看指定对象的内容或类型和大小信息-p
获取指定对象的内容或类型信息-t
获取指定对象的类型-s
获取指定对象的大小git ls-tree
列出树对象的内容-r
递归子树对象下的内容git update-ref
更新存储在ref中的对象名称示例:
git update-ref refs/tags/v1.0 c7cfb9
给指定提交对象添加一个轻量标签git count-objects -v
查看仓库的详细占用空间git for-each-ref
显示所有的引用