Git 是一个免费的开源分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有内容。Git 的使用要比 Svn 复杂很多,Git 命令也很多,这里做个简单记录,方便查阅。
你要相信,机会不来是因为你还没准备好。
git 基础
- 文件状态: 未跟踪(untracked)、已提交(committed)、已修改(modified)、已暂存(staged)
- 工作区域: 远程仓库、工作目录、暂存区域
- 在工作目录修改文件 –> committed ot modified
- 将文件快照放入暂存区域 –> modified to staged
- 提交更新 –> staged to commited
配置
git 配置有三个作用域,其应用优先级:local > global > system。
--system配置文件为/etc/gitconfig下,所有用户的配置--global配置文件为~/.gitconfig或~/.config/git/config下,当前用户的配置--local配置文件为proj/.git/config,当前仓库的配置
查看配置
- 查看所有配置项
git config [--system|--global|--local] --list - 查看特定配置项
git config [--system|--global|--local] user.name
如果不指定作用域,则按优先级获取,即在 local 取不到该项的值再从 global 取,还是取不到再从 system 取,如果 system 中也没配置,则返回空。
设置配置
1 | git config --global user.name xxx |
如果不指定作用域,则默认为 --local。
删除配置项
1 | git config --global --unset user.name |
如果不指定作用域,则默认为 --local。
短命令
有些命令经常使用,如果可以使用更短的命令代替会方便很多,git alias 提供了给命令设置别名的功能,打开系统配置文件 /etc/gitconfig,添加下面的配置项。
1 | [alias] |
忽略文件
在 .gitignore 中指定哪些文件不需要跟踪。空目录默认不会跟踪。
- *.[oa] 所有以
.o或.a结尾的文件 - *~ 所有以
~结尾的文件 - build/ 所有
build目录及目录下所有文件 - /build/ 根目录下的
build目录及目录下所有文件 - build 所有
build文件 - /build 根目录下的
build文件 - doc/*.txt
doc根目录下的所有文本文件 - doc/*/.txt
doc目录下的所有文本文件
查看帮助
1 | git help [verb] |
git 仓库
本地仓库
本地仓库就是本地的一个文件夹,在这个文件夹下生成一个 .git 目录,这个文件夹就变成了一个本地 git 仓库。创建一个本地仓库有两种方式,第一种是在该目录下执行 git init 即可,这种方式创建创建的本地仓库不与任何远程仓库关联。
1 | cd xxxdir |
第二种是使用 git clone 直接拉取一个远程仓库到本地,会自动根据远程仓库名在本地创建一个同名的文件夹,且文件夹下有 .git 目录,这种方式创建的本地仓库默认会与拉取的远程仓库关联。
1 | git clone git@github.com:xxx/xxx.git |
远程仓库
远程仓库是指放在远程服务器上的 respository,通常是放在使用 Git 远程版本控制的软件源代码托管平台上,比如 Github, Gitlab, Gitee 等等网站。远程仓库需要我们到托管平台上创建。
关联远程仓库
git remote add <remote-name> <url>
通过 git remote add 命令可以将本地仓库关联到远程仓库,一个本地仓库可关联多个远程仓库。remote-name 是我们给远程仓库取的名字,之后对该远程仓库的所有操作都使用这个名字,url 则是远程仓库的地址。
使用
get clone拉取远程仓库到本地,默认就会关联这个远程仓库,使用的是默认名字origin。
取消关联远程仓库
git remote remove/rm <name>
查看关联的远程仓库
git remotegit remote -v/--verbose显示 urlgit remote show <name>显示某一远程仓库的详细信息
重命名远程仓库
git remote rename <old-name> <new-name>
git 分支
分支简介
- git 分支本质上仅仅是指向提交对象的可变指针,提交对象提交时分支会自动向前移动;
git init会默认创建一个名为master的分支,这个分支和普通分支没什么区别;HEAD才是特别的分支,它指向当前所在的本地分支,可看作当前分支的别名。切换分支时HEAD会指向另一个分支,同时把本地目录的文件替换为另一个分支指向的文件。
分支创建
git branch <name>
分支切换
git checkout <name>git checkout -b <name>创建新分支并切换到该分支,等价于git branch <name>git checkout <name>
checkout 即查看的意思,如果参数带的是分支名,即表示查看这个分支的内容——切换到该分支;如果参数带的是文件名,即查看远程上该文件的内容,即检出该文件到本地。
查看分支
- 查看所有分支:
git branch - 查看所有分支最后一次提交:
git branch -v - 查看所有已合并分支:
git branch --merged - 查看所有未合并分支:
git branch --no-merged - 查看所有分支包含远程分支和已删除分支:
git branch -a
删除分支
git branch -d <name>只能删除那些已经合并的分支git branch -D <name>能删除已合并或未合并的分支git push <remote> --delete <branch>删除远程分支
重命名分支
git branch -m <old-name> <new-name>如果<new-name>分支已存在则重命名不成功git branch -M <old-name> <new-name>强制重命名,即使分支<new-name>已存在
分支合并
git merge <other>
将 other 分支合并到当前分支,合并后 other 分支的状态就是 merged,可放心删除。合并时,先切换到要合并到的分支,再 merge 要合并的分支,比如要将 hotfix 分支合并到 master 分支,则
1 | git checkout master |
合并的时候有两种情况,一是要合并到的分支 master 是要合并的分支 hotfix 的直接祖先,如下图所示,这种情况 merge 的时候会出现 Fast-forward(快进) 的提示,这种情况的合并很简单,不需要任何额外的处理,因为不会有冲突,hotfix 本身就是从 master 分化出来的,内容只会比 master 新,不存在两个分支同时修改的内容。

第二种情况是要合并到的分支不是要合并的分支的直接祖先,如下图一所示,把分支 iss53 合并到分支 master,这两个分支在某个时间点出现了分叉,无直接关系,只有共同的祖先 C2 快照。这种情况 Git 会自动拿 C2, C4, C5 三个快照进行合并生成一个新的快照 C6,然后让 master 分支指向 C6 快照,如下图二所示。


merge 操作是 Git 自动完成的,通过快照对比修改本地文件,如果没冲突则自动将修改过的文件加入到暂存区,文件状态变为 staged;有冲突的文件则会为 conflict,需要我们手动处理冲突,处理完成后标记为 modified,然后手动添加到暂存区变为 staged。merge 之后只是我们本地的文件做了修改,所以还需要执行 git commit 将修改提交到远程。
关联远程分支
本地分支在两种情况下会自动关联远程分支,一是使用 git checkout <branch> 从远程检出分支到本地,检出的本地分支自动与远程分支关联,二是在本地新创建的分支,默认是没有与远程分支关联的,当有 push 新的 commit 到远程的时候,就会将本地分支关联到远程的同名分支,git push <remote> <branch> 会将 <branch> 关联到 <remote>/<branch>,如果远程分支不存在,则会创建一个。
有时候需要修改本地分支关联的远程分支,比如修改了远程仓库的地址,从 remote1 变为了 remote2,原来的 master 分支关联的是 remote1/master,需要将其变更为 remote2/master 才能正常进行 push 或 pull 操作。
手动关联远程分支:git branch --set-upstream-to=origin/<branch> <branch>
1 | git remote remove origin |
同步远程分支
git remote show origin 可以查看远程的所有分支以及本地分支和远程的关联,如下图所示。

可以看到远程的分支现在有两种,一种是 tracked(已跟踪),即有用的分支,另一种是 stale(陈旧),即远程已删除的分支,但本地还保留着,可使用 prune(精剪) 命令来删除。
git remote prune origin 在本地删除陈旧的远程分支。
分支变基
分支采摘
git merge 可以把一个分支的所有最新改动合并到另一个分支,而如果想只采摘一个分支的几个提交到另一个分支,则可以使用 git cherry-pick。
git cherry-pick <commit> 把某个 commit 应用到当前分支,产生一个新的 commit;git cherry-pick <branch> 把某个 branch 的最新一次 commit 应用到当前分支,产生一个新的 commit;git cherry-pick <commit1> <commit2> 依次采摘两个 commit;git cherry-pick <commit1>..<commit2> 采摘 (commit1, commit2] 之间的所有 commit,不包括 commit1;git cherry-pick <commit1>^..<commit2> 采摘 [commit1, commit2] 之间的所有 commit,包括 commit1;
查看分支树
使用 log
使用 git log --graph 可以通过查看 log 的方式来查看分支的关系树。
git log –graph –decorate –oneline –simplify-by-decoration –all
-–oneline每条 commit 一行显示,一般这是必须的,否则整个树会十分庞大,不方便查看-–decorate显示每条 commit 的引用,如分支、tag 等-–simplify-by-decoration只显示被分支或 tag 引用的 commit-–all显示所有的分支
使用 gitk
gitk –-simplify-by-decoration –-all 也可以使用 gitk 工具来查看 log 信息,可以更直观的看分支的关系树。gitk 默认在 Git bash 安装的时候就会安装,在 git cmd 输入 gitk 即可打开 gitk 工具。
常用命令
检查文件状态
git statusgit status -s/--short
-s 只列出所有差异文件及其状态,状态以符号的形式显示在文件前面,?? 表示未加入暂存区的新文件,A 表示已加入暂存区的新文件,M 表示已修改,D 表示已删除。符号为红色表示未添加到暂存区,绿色表示已添加到暂存区。其中 ?? 只会显示为红色,A 只会显示为绿色。
跟踪文件
git add <file> 添加文件到暂存区
- 跟踪新文件,从
untracked到staged,??toA(红变绿); - 跟踪修改的旧文件,从
modified到staged,MtoM(红变绿); - 跟踪删除的旧文件,从
modified到staged,DtoD(红变绿); - 当然也可以跟踪未修改的旧文件,只是没意义而已,跟踪之后也不会有什么变化;
- 每次文件修改后,提交更新前都要重新跟踪,因为提交更新时是暂存区的版本,而不是本地目录的版本。
取消跟踪
git reset HEAD <file> 将文件从暂存区移除,保留文件状态,如果是新文件则从已跟踪重新变成未跟踪,旧文件已修改仍是已修改,已删除仍是已删除。
HEAD可以省略,但如果是是取消跟踪已删除文件则不能省略,因为在本地找不到该文件,也可以用--来代替HEAD。
删除文件
删除文件 git rm 及下面介绍的移动文件 git mv 是对 Linux 命令的“扩展”,如果文件未跟踪,即在版本中不存在,是本地新增的文件,则用 Linux 命令即可,而且只能用 Linux 命令,用 git 命令会报找不到文件的错误。
1 | $ touch test |
git rm <file> 先使用 Linux 命令删除本地文件,再跟踪删除的文件,即添加到暂存区,相当于下面几条命令。
1 | $ rm package.json |
- 文件未跟踪
rm <file> - 文件未修改未删除
git rm <file> - 文件已修改
git rm -f <file>强制删除,无论文件原来是否在暂存区,都从本地删除并重新加入暂存区
- 文件已修改
git rm --cached <file>从版本中移除,保留在本地目录成为未跟踪文件
- 文件已删除,未加入暂存区
git rm <file>加入暂存区,相当于只执行
git rm的第二步操作 - 文件已删除,已加入暂存区,无法再进行任何删除操作
移动文件
git mv <old-name> <new-name> 文件重命名并将修改加入暂存区,其实就是将旧文件删除,然后建一个新文件,再将两个文件添加到暂存区,等价于下面几条命令。
1 | $ mv <oldfile> <newfile> |
同 git rm 一样,如果是版本中不存在的新文件,不能使用 git mv 来重命名(也没意义),直接使用 Linux 命令 mv 即可。
推送到远程仓库
git push <remote> <branch>
当前 <remote>-<branch> 没人抢先推送时才能推送成功,否则必须先拉取远程分支内容合并后再推送。
从远程仓库拉取
git fetch <remote>拉取远程仓库的所有分支的引用,不会进行合并,必须手动合并分支;git pull <remote> <branch>拉取某个远程分支的内容并合并到本地分支。
查看 diff
- 查看未暂存文件的修改信息
git diff - 查看已暂存文件的修改信息
git diff -cachedorgit diff -staged - 查看距离某次 commit 的修改信息
git diff <commit> - 查看单个文件的修改信息
git diff -- <file> - 查看单个距离某次 commit 的修改信息
git diff <commit> -- <file>
- 如果要查看某个文件的修改信息,直接带上文件名即可
git diff <file>,不带文件名则查看所有有更改文件的 diff; - 只列出有更改的行及上下文,删除的行在最前面加上
-并显示为红色,新增的行在最前面加上+并显示为绿色;
提交更新
- 在编辑器中编辑提交信息
git commit - 直接输入提交信息
git commit -m <statement> - 跳过暂存区域直接提交
git commit -a
查看提交日志
git log
-p显示每次提交的内容差异-3只显示最新的 3 次提交--stat显示简略的统计信息--pretty指定显示的格式--author=[xxx]按提交作者筛选
默认查看所有提交日志,如果要查看某个文件的提交日志,带上文件名即可 git log -- <file>。
git shortlog列出所有提交记录,只显示 commit 说明,按作者分类;git shortlog --author=[xxx]只列出某位作者或某几位作者的日志,这个命令比较常用,比如查看自己的提交日志。
储藏文件
有时候在本地做了一些修改后,想切换到其它分支或者从远程拉取最新代码,而本地的修改暂时又不想提交,则可以将本地修改储藏起来,储藏之后本地又恢复成干净状态(即无修改状态),这时候 pull 代码或 checkout 分支都不会有冲突出现。之后需要的时候再从储藏中将文件恢复到本地,恢复的时候如果修改的文件有更新,则可能会出现冲突,所以一般是想更新远程较新文件且这些文件在本地没修改,本地修改的是远程无更新文件,才会使用储藏操作。
- 储藏本地修改文件
git stash - 查看储藏列表
git stash list - 应用储藏
git stash apply如果要恢复指定的储藏,带上名字即可,比如
git stash aaply stash@{1},默认从最近一次的储藏进行恢复,等价于git stash apply stash@{0}。 - 取消储藏
git stash show -p | git apply -Rgit 没有提供
stash unapply的命令,但是可以通过取消该储藏的补丁达到同样的效果,如果要取消指定的储藏,带上名字即可,git stash show -p stash@{1} | git apply -R。 - 移除储藏
git stash dropstash apply只是应用储藏,应用之后储藏的内容还在栈中,需要手动移除,drop默认移除最近一次的储藏,git stash drop stash@{1}移除上上次的储藏。 - 应用并移除储藏
git stash popstash pop应用储藏的同时将其从栈中移除,等价于stash apply+stash drop,git stash pop stash@{1}可应用并移除上上次的储藏。 - 移除所有储藏
git stash clear
撤销操作
撤销暂存
git reset HEAD <file>
撤销修改
git checkout -- <file>
将文件修改为上次提交的样子或者刚放入工作目录时的样子,直接拿版本快照中的文件来覆盖本地,因此很危险。
撤销提交
git reset [--soft/--mixed/--hard] HEAD^
恢复到当前版本状态,--soft 只撤销 commit,文件仍在暂存区中,--mixed 撤销 commit 和 add,文件恢复到工作目录,--hard 最粗暴,不仅撤销 commit 和 add,还将本地修改直接撤销了,即完全恢复到上次提交的状态,要慎用。默认参数是 --mixed,也是最常用的参数。
重新提交
git commit --amend
第二次提交会覆盖上一次的提交,如果第二次提交没有新的修改,就会使用第一次的快照,只覆盖提交信息。
数据恢复
有时候会丢失一些 commit 信息,比如强制删除了一个没 merge 的分支,则这个分支上的 commit 就丢失了,或者 hard-reset 到某个版本,则这个版本之后的 commit 就丢失了。这些丢失的数据是可以找回的,因为这些操作并不是真正的删除,只是从 git 仓库中移除了,数据还保留在 .git 目录下;git 会不定时的执行 git gc 命令,将不被引用且存在数月的垃圾删除,所以如果是误操作丢失了数据,不要慌,只要不是太久都可以找回的。
找回 commit 其实很简单,比方在这个 commit 上创建一个新分支就把它恢复过来了,关键是得知道我们弄丢的是哪个 commit,git log 只会显示当前的所有 commit,不包含已删除的,这时候可以用 git reflog,可以查看所有分支的所有操作记录,包括已删除的 commit 和 reset 的操作,reflog 只显示简单的 commit 信息,这时可以再执行下 git log -g 来输出 reflog 的正常日志,这样就能看到我们要恢复的是哪个 commit。
1 | git reflog |
reflog 数据保存在 .git/logs 目录下,如果这个目录被删了,则 git reflog 就帮不了我们了,这时候可以使用 git fsck 命令,该命令会检查仓库的数据完整性和一致性,如果指定选项 --full,该命令显示所有未被其它对象引用的所有对象。通过 git fsck --full 也能找到被删除的 commit 信息。
有时候会误删 stash,比如想应用并移除储藏的时候,脑子一热将 git stash pop 打成了 git stash drop,这时储藏没应用,却已经从栈中移除了。但别慌,只是从栈中移除了,stash 的数据还是在的,只是无法在 git stash list 中列出来,即没有 stash 名字而已,但我们仍可以通过 commit 信息来恢复。其实在移除储藏的时候,从移除的结果就可以看到这条储藏的 commit 信息,如果马上意识到自己误操作,这时想恢复十分简单,直接使用这条 commit 信息进行恢复即可。
1 | $ git stash list |
这是在刚移除 stash 的时候恢复,这时我们知道 commit 信息,如果隔了一段时间不知道 commit 信息了,也可以通过 git fsck 来列出所有 commit,但列出来的 commit 是无序的,只能通过 git show [commit] 来一条条查看 commit 的 diff,找到需要的 commit 之后再进行恢复,相对会麻烦挺多。
规范
commit 规范
标题:#[type] message
一般使用 #[type] 来表示这次提交内容的类型,比如 #feature 代码功能开发,#bugfix 代表 bug 修复,或者在一个博客系统中, #newpost 代表新文章,#newpage 代表新页面,#newdraft 代表新草稿,#amend 代表文章修改,#feature 代表其它修改。
默认
#为 commit 信息的注释符号,通过配置git config core.commentchar *将其设置为*或其它符号。
单一职责,一次 commit 只处理一件事,方便查看和回退,多提交几次 commit 并没有不好的。
分支命名规范
master主干分支,一般不在这个分支上直接改东西;feature/xxx功能开发分支;bugfix/xxxbug 修复分支;release/xxx发行分支。