Contents
配置
Git 配置文件 .gitconfig
,系统配置位于/etc/gitconfig
(Windows 该文件在Git安装目录下),全局配置位于用户目录下,项目配置位于仓库目录下。
1 | git config --list # 显示当前的Git配置 |
配置别名
1 | git config --global alias.ci "commit" |
如果想要执行外部命令,而不是一个 Git 子命令,可以在命令前面加入 !
符号。
忽略文件
如果是版本库级别的忽略用.gitignore
;如果仅仅是个人的忽略用.git/info/exclude
,不会将此设置传播出去
文件 .gitignore 的格式规范如下:
- 所有空行或者以 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配。
- 匹配模式可以以(
/
)开头防止递归。 - 匹配模式可以以(
/
)结尾指定目录。 - 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(
!
)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号(*
)匹配零个或多个任意字符;[abc]
匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?
)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9]
表示匹配所有 0 到 9 的数字)。 使用两个星号(*
) 表示匹配任意中间目录,比如a/**/z
可以匹配 a/z
, a/b/z
或 a/b/c/z
等。
1 | # 此为注释 – 将被 Git 忽略 |
添加文件到暂存区
- 基本命令
git add
; - 交互模式
git add -i
; - 补丁模式
git add -p
交互模式下如果出现两个>
时,什么都不选,直接敲回车可以回到上一级。
1 | $ git add -i |
移除文件
从暂存区移除,并从工作目录中删除
1 | git rm <filename> |
从暂存区移除,保留工作目录中的文件,文件仍在磁盘中,但是git不再跟踪文件
1 | git rm --cached <filename> |
移动文件
1 | git mv <oldfile> <newfile> |
提交修改
先暂存,后提交
1 | git add files |
跳过暂存直接提交 git commit -m ".." -a
; 或者 git commit -m ".." files
提交时显示diff信息git commit -v
- 多个-m可以换行
- 提交留言至少应该体现出进行本次修改的原因
- 增补提交
对于提交后发现一些小问题的情况,可以使用–amend。只能针对最后一个提交。例如:编辑contact.html添加一个链接git commit -m "add link to google" -a
。之后修改链接地址git commit -C HEAD -a --amend
。-C
使复用指定提交(HEAD)的提交留言。-c
会打开默认编辑器。增补提交是修正上次提交,git log
会发现只有增补提交的提交名称,而没有上次的。
保存进度
Git 不允许在当前分支处于修改状态时,切换到其他分支。此时可以用git stash
命令来保存当前分支修改进度。
1 | git stash # 保存进度,会分别对暂存区和工作区的状态进行保存。 |
切换分支,做一些提交,然后切回来,然后执行下面命令可以恢复之前进度
1 | # 查看保存的进度 |
git stash list
执行后可能得到类似下面的结果
1 | stash@{0}: WIP on dev: 8297914 m head.htm |
之后可以可以通过指定恢复到哪次进度git stash pop stash@{0}
其它命令:git stash apply [–index] [<stash>]
除了不删除恢复的进度之外,其余和git stash pop
命令一样。 git stash drop [<stash>]
删除一个存储的进度。缺省删除最新的进度。git stash clear
删除所有存储的进度。git stash branch <branchname> <stash>
基于进度创建分支。
stash 引用位于.git/refs/stash
,它指向一个提交对象。git log --graph --pretty=raw stash@{0} -3
可以查看提交记录。最新的提交说明中有WIP字样(是Work In Progess的简称),代表了工作区进度。而最新提交的第二个父提交有index on master字样,这个提交代表着暂存区的进度。git diff stash@{0}^2 stash@{0}
可以比较原暂存区和原工作区的差异。
查看工作目录状态
1 | git status |
查看文件改动
- 比较工作目录和暂存区
git diff
; - 比较暂存区和版本库
git diff --cached
高版本1.6.1+还可以使用git diff --staged
; - 比较工作目录和版本库
git diff HEAD
查看提交信息
常用命令 git log
,快速查看历史记录 git log --pretty=oneline
。记录比较多时,可以使用jk等vi的命令向下向上查看。还有个命令可以查看提交信息git whatchanged
。
git log
的常用选项
选项 | 说明 |
---|---|
-p |
按补丁格式显示每个更新之间的代码差异。审查代码查看每次提交带来的变化时很有用 |
--stat |
显示每次更新的文件修改统计信息,如果想了解提交时哪些文件被改变了很有用。显示每个提交哪些文件被改了,-p会显示具体哪些代码发生了变化 |
--shortstat |
只显示 –stat 中最后的行数修改添加移除统计。 |
--name-only |
仅在提交信息后显示已修改的文件清单。 |
--name-status |
显示新增、修改、删除的文件清单。 |
--abbrev-commit |
仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。 |
--relative-date |
使用较短的相对时间显示(比如,“2 weeks ago”)。 |
--graph |
显示 ASCII 图形表示的分支合并历史。 |
--pretty |
使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。 |
--oneline |
在每行显示一个提交的hash和message |
限制 git log
输出的选项
选项 | 说明 |
---|---|
-(n) |
仅显示最近的 n 条提交 git log -2 查看近两次提交 |
--since , --after |
仅显示指定时间之后的提交。 |
--until , --before |
仅显示指定时间之前的提交。 |
--author=<name> |
仅显示指定作者相关的提交。 |
--committer=<name> |
仅显示指定提交者相关的提交。作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。eg. stackoverflow-1 stackoverflow-2 |
--grep |
仅显示含指定关键字的提交 |
-S |
仅显示添加或移除了某个关键字的提交 git log -S"some code" 显示提交的文件中添加或删除了关键字”some code”的提交 |
如果要得到同时满足这两个选项搜索条件的提交,就必须用 --all-match
选项
按范围查看提交信息
1 | git log --since="5 hours" |
可以接受的的时间格式:3 hours, 5 minutes, 1 minute, 2013-4-13, 2013.4.13, 2013-4.13
1 | git log *.xml # 显示项目路径下的所有以.xml结尾的文件的提交 |
git log --pretty=format
常用的选项
选项 | 说明 |
---|---|
%H | 提交对象(commit)的完整哈希字串 |
%h | 提交对象的简短哈希字串 |
%T | 树对象(tree)的完整哈希字串 |
%t | 树对象的简短哈希字串 |
%P | 父对象(parent)的完整哈希字串 |
%p | 父对象的简短哈希字串 |
%an | 作者(author)的名字 |
%ae | 作者的电子邮件地址 |
%ad | 作者修订日期(可以用 –date= 选项定制格式) |
%ar | 作者修订日期,按多久以前的方式显示 |
%cn | 提交者(committer)的名字 |
%ce | 提交者的电子邮件地址 |
%cd | 提交日期 |
%cr | 提交日期,按多久以前的方式显示 |
%s | 提交说明 |
^
:一个脱字符作用相遇回溯一个版本 8d0a22b^是指8d0a22b之前的那个版本~n
:波浪线加数字是指回溯n个版本。8d0a22b~1是指8d0a22b的父节点
以下命令执行结果相同
1 | git log -1 HEAD^^^ |
查看版本之间的差异
1 | git diff 8d0a22b # 默认第二个参数不写的话就是HEAD |
查看代码块的历史信息
1 | git blame index.html |
输出:
1 | ^f6bb33f (Feng Hao 2016-05-06 15:46:05 +0800 1) <html> |
有脱字符的表示版本库中的第一个提交
已经提交了但是还是出现00000000 (Not Committed Yet ....)
这个问题可以参考Git blame showing no history一种办法是指明分支git blame <branch> <filename>
或者最好git blame HEAD <filename>
;还有中办法是指明-w
参数git blame -w <filename>
。
可以限定查看范围
1 | git blame -L 12,13 index.html |
跟踪内容
创建文件original.txt写入三行,并再复制这三行
1 | git blame original.txt |
后者显示的所有提交名称相同,因为git检测到了重复的内容
复制文件original.txt并重命名为copy.txt
1 | git blame -C -C copy.txt |
显示出事提交名称和初始文件名original.txt
1 | git log -C -C -1 -p |
撤销操作
通过git reset HEAD <filename>
命令可以撤销已暂存的文件
git checkout -- <filename>
命令可以撤销已修改的文件,这是很危险的,意味着对文件做的任何修改都会丢失。--
是为了避免文件和引用或提交ID发生冲突。
在 Git 中任何已提交的东西几乎总是可以恢复的。然而,任何未提交的东西丢失后很可能再也找不到了。
reset 命令会以特定的顺序重写三棵树(版本库HEAD、暂存区index、工作区Workdir),在你指定以下选项时停止:
- 移动 HEAD 指向的分支引用 (若指定了 –soft,则到此停止)
- 使索引看起来像 HEAD (若指定了 –mixed,默认不加参数就是mixed,则到此停止)
- 使工作目录看起来像索引 (若指定了 –hard,则到此停止)
下面的速查表列出了命令对树的影响。 “HEAD” 一列中的 “REF” 表示该命令移动了 HEAD 指向的分支引用(如HEAD 当前指向master,则改变master的引用的提交对象),而“HEAD” 则表示只移动了 HEAD 自身(改变HEAD指向的分支)。 特别注意 WD Safe? 一列 - 如果它标记为 NO,那么运行该命令之前请考虑一下。
Command | HEAD | Index | Workdir | WD Safe? |
---|---|---|---|---|
Commit Level | ||||
reset –soft [commit] | REF | NO | NO | YES |
reset [commit] | REF | YES | NO | YES |
reset –hard [commit] | REF | YES | YES | NO |
checkout [commit] | HEAD | YES | YES | YES |
File Level | ||||
reset (commit) [file] | NO | YES | NO | YES |
checkout (commit) [file] | NO | YES | YES | NO |
反转提交
1 | git revert -n HEAD # -n选项git 会暂存所有变更,而不立即提交,--no-edit不打开编辑器 |
复位
1 | git reset --hard HEAD^ # 复位到HEAD的父节点 |
revert vs. reset –hard
- revert相对安全,是对某一次提交undo,并且保留提交历史。
- reset相对比较危险,要对某一次提交undo则其后的所有提交都会undo,不保留历史,永久性的。
克隆远程版本库
区别于 Subversion 之类的工具,这里是clone
而不是checkout
。克隆出的是版本库的几乎所有数据。
1 | git clone git://github.com/tswicegood/mysite-chp6.git |
克隆后本地只有主分支,可以通过git branch -a
查看所有分支,其它分支是隐藏的,可以通过git checkout -b experimental origin/experimental
创建并切换分支。
版本库同步
版本库同步,Git从远程的分支获取最新的版本到本地有这样2个命令:
1 | git fetch |
fetch vs pull
git fetch:相当于是从远程获取最新版本到本地,不会自动merge
1
2
3git fetch origin master
git log -p master..origin/master
git merge origin/master以上命令的含义:首先从远程的origin的master主分支下载最新的版本到origin/master分支上;然后比较本地的master分支和origin/master分支的差别;最后进行合并
上述过程其实可以用以下更清晰的方式来进行:
1
2
3git fetch origin master:tmp
git diff tmp
git merge tmp从远程获取最新的版本到本地的tmp分支上;之后再进行比较合并
git pull:相当于是从远程获取最新版本并merge到本地
1
git pull origin master
上述命令其实相当于git fetch 和 git merge。在实际使用中,git fetch更安全一些,因为在merge前,我们可以查看更新情况,然后再决定是否合并。如果加上
--rebase
选项相当于git fetch
和git rebase
。
给远程版本库起别名
1 | git remote add origin git://github.com/tswicegood/mysite.git |
推入远程版本库
1 | git push origin master |
查看本地创建的全部远程版本库别名
1 | git remote |
查看某个远程版本库 的详细信息
1 | git remote show <版本库别名> |
重命名远程仓库简写名
1 | git remote rename <old> <new> |
删除版本库别名
1 | git remote rm <版本库别名> |
删除远程分支
1 | git push <remote> :<branch> |
分支
Git 中分支是对提交对象的引用,是一个包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件。本地分支在.git/refs/heads
目录下,远程分支在.git/refs/remotes
目录下。而 HEAD (位于.git/HEAD
)指针指向了当前分支。
远程分支是远程仓库分支状态的引用。它们是你不能移动的本地引用,当你做任何网络通信操作时(比如fetch、push等),它们会自动移动。它们以 (remote)/(branch) 形式命名。
上次跟远程仓库通信后a
分支又有了新的提交,本地分支将领先远程分支一个提交。只要不与 origin
服务器连接,origin/a
指针就不会移动。
1 | +--------+ +--------+ |
注意上面的数据是最后一次从服务器上抓取的数据,可能其它人向远程仓库提交了新的内容,git fetch origin
(这个命令将远程仓库数据同步到本地,将origin/a
指针指向远程仓库分支a
最新的一次提交,并不会改变本地分支a
,也不会修改工作目录中的内容)后,将会发现从C2处远程分支和本地分支分叉了,需要手动将origin/a
合并到a
分支
1 | +--------+ |
从一个远程追踪分支检出一个本地分支会自动创建一个叫做 “追踪分支”(有时候也叫做 “上游分支”)。 追踪分支是与远程分支有直接关系的本地分支。
如果本地分支与远程分支存在追踪关系,git pull
和git push
就可以省略远程分支名。
1 | git pull origin |
如果本地分支只有一个追踪分支,那么远程仓库名也可以省略。
1 | git pull |
在某些场合,Git会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)。比如,在git clone的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master分支自动”追踪”origin/master分支。下面几种情况能建立追踪关系。
1 | git clone <url> |
当设置好跟踪分支后,可以通过 @{upstream}
或 @{u}
快捷方式来引用它。
1 | git merge origin/a # 和下面等效 |
查看追踪关系
1 | git branch -vv |
列出的信息中还包括本地分支领先(ahead)或者落后(behind)远程分支几个提交
创建分支
显示分支
1 | git branch -r # 列出所有远程分支 |
新建分支
1 | git branch RB_1.0 # RB_1.0新分支名,默认指向当前分支的提交对象 |
以下情况创建分支
- 试验性修改
- 增加新功能
- bug修复
切换分支
1 | git checkout RB_1.0 |
创建分支并检出该分支
1 | git chechout -b new master # new是新分支名,master是告诉git,不是从当前分支,而是从主分支上创建新分支,即基于主分支末梢创建新分支 |
重命名分支
1 | git branch -m master mymaster # master是就分支名,mymaster是新分支名 |
新分支名和已有的分支名相同时,重命名不会成功。
1 | git branch -M master mymaster |
可以强制重命名,并覆盖已有分支名称,需小心使用
合并分支
直接合并
1 | git chechout master |
压合合并
1 | git chechout master |
拣选合并
拣选一次提交进行合并
1 | git chechout master |
拣选多个提交
1 | git checkout master |
冲突处理
如果在不同分支上对同一文件的同一文本块进行不同的修改,合并这两个不同分支时会有冲突
1 | git mergetool |
选择工具来人工处理冲突
1 | git commit # 不需要-m参数 |
把RB_1.0分支上所做的修改合并到主分支上,通过rebase合并,提交历史会更加简洁
1 | git chechout master |
删除分支
1 | git branch -d RB_1.0 # 仅仅是删除了分知名,该分支的内容并没有被删除 |
如果分支还没有合并回当前分支,删除不会成功
1 | git branch -D RB_1.0 |
可以强制删除分支
一般来说运行git branch --merged
结果中前面没有*
的分支都是可以被删除的分支,因为它已经被合并到当前的分支中了。
变基
改写历史记录
重新排序
1 | git rebase -i HEAD~3 # 之后在打开的编辑器中改变顺序 |
将多个提交压合成一个提交
1 | git rebase -i 0bb3dfb^ # 将要修改要合并的另一个记录移动到0bb3dfb后,并将其前面的pick改为squash |
将一个提交分解成多个提交
1 | git rebase -i HEAD~4 # 在打开的编辑器中将要分解的记录前的pick改为edit,保存退出,git reset HEAD^,git diff,对文件修改并分别提交,git rebase --continue |
打标签
查看所有标签
1 | git tag |
Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。
一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。
然而,附注标签是存储在 Git 数据库中的一个完整对象。
轻量标签
1 | git tag 1.0 # 指向当前提交 |
附注标签
1 | git tag -a 1.0 -m 'version 1.0' |
通常建议创建附注标签,这样你可以拥有更多标签信息
后期打标签
1 | git tag -a 1.0 9fceb02 |
推送标签
1 | git push origin 1.0 |
删除标签
1 | git tag -d 1.0 |
检出标签
1 | git checkout -b version1 1.0 |
创建归档文件
1 | git archive --format=tar --prefix=mysite-1.0/ 1.0 | gzip > mysite-1.0.tar.gz |
文件重命名和移动
1 | git mv index.html hello.html |
合并其他remote上面的更新
在 Github 上面 Fork 别人的项目时,我们常常会遇到主项目有了更新,这个时候怎么把主项目的更新合并到自己 Fork 的版本里面来呢?
1 | # 先把 tualatrix Fork 的版本获取到本地 |
Git的资源:免费的编程中文书籍索引–版本控制
Git 工作流