Git checkout
恢复工作区
checkout 的用途之一是恢复工作区。
git checkout . 复制代码
checkout .
表示恢复工作区的所有更改,未跟踪的文件不会有变化。
恢复工作区的所有文件风险比较大,会丢失所有工作区的修改,一定要慎用
git checkout -- a.txt 复制代码
中间加上 -- 就安全多了,可以只恢复单个文件。
版本切换
git checkout master 取出 master 分支,HEAD 指向 master git checkout 907d3ba 取出最后提交为 commit id 为 907d3ba 这个版本,HEAD 转到 907d3ba,和 master 分离。 复制代码
取出分支的时候 HEAD 会指向当前分支。取出某个版本,HEAD也会跟着指过来,分支不动。这会造成 HEAD 和分支 分离。在分离 HEAD 的情况下,可以查看,提交,做各种试验,如果对结果满意,可以就地打新分支保留这些提交:
git checkout -c <new-branch-name> 复制代码
如果不满意,什么也不用做,切回当前分支既可。
git checkout master 修正 HEAD 指向 master 分支 复制代码
如果不知道哪前分支名也没关系 checkout -
同样会修正 HEAD。
git checkout - 复制代码
如果要开发新功能,直接在某个提交上打分支即可,为什么要分离 HEAD?原因是这样比较轻量。比如你现在想开发一个功能,但不知道是否可行,所以先试验一下,确认好了再打分支。如果直接打分支,觉得不合适还得删除。因为分支没有合并,还删不掉,删除还得加强制删除参数。
分离头指针的操作相当于 先上车,后补票 。上车后又下车,不用买票,只有到终点才需要补票。
强制拉分支
git checkout -B dev 复制代码
假定 dev 存在,如果没有 -B
参数,会报错,加上 -B
会覆盖原来的 dev 分支,打一个新的 dev 分支出来,并转到 dev 分支。
省得费心起名了。如果并行的只有一个任务,可以每次都用 dev 分支开发。
从某个 commit 打分支
我们打分支的时候,默认会从 HEAD 处开始,对于 master 分支来说,就是 G。
如果从 F 处打分支出来,可以用第二个参数指定
git checkout -b dev F 也可以这样写 git checkout -b dev HEAD^
孤儿分支
有这样一个参数 --orphan
, orphan 的英文原意是孤儿,如果我们要打一个设计文档分支出来这样写
git checkout --orphan design 复制代码
因为设计文档和开发的代码完全是独立的部分,不适合和开发代码放一个分支上。
之所以称为孤儿分支,是因为这个分支是完完全全独立的,和以前所有的分支没有任何关联。和其它分支是平行的,永远不会相交。
就算孤儿分支是从 master 分支打出来的,你在 master 分支 执行 git log --oneline
也找不到任何有关孤儿分支的痕迹。当然更无法 merge 一个孤儿分支,实际上,也没有这个需求。
孤儿分支刚生成的时候,没有父提交,也没有任何提交,完全是空的,暂存区和工作区一般来说会有内容,因为我们要存设计文档,原来的内容都没有用,删除
git rm -rf . 复制代码
现在我们得到了一个纯净的,独立的分支,可以添加设计文档了,并生成第一个提交。
可能你会有疑问,既然我们要一个孤儿分支,为什么还要初始化内容给我们?因为我们可能还有这样的需求:需要一个起点,而不是从一无所有开始。
试想这样的场景:项目开发半年了,市场反馈却是平平,老板觉得这样下去不是办法,需要另寻出路,但又不想放弃现在的方向。因为这次是方向性的问题,改动比较大,如果打普通分支的话,可能无法向主干合并。于是老板想出了一个办法,新建一个孤儿分支,完全独立来验证新想法,如果新方向正确,就可以代取代原来的方向。
从头来实现项目来验证新想法显然是不实际的,可以从项目中选择合适的节点,比如 F 节点,以这个为基础。
git checkout --orphan laboratory F 复制代码
新分支生成后,会把 F 节点的所有内容带到暂存区和工作区,我们全部保留,在这个基础上开发。laboratory 和原来的 master 分支的级别是完全一样的,laboratory 就相当于原来的 master 分支。master 只是提供了一个起点。laboratory 后面如何发展和 master 完全没有关系。
选择合并
git checkout master git merge dev 复制代码
merge dev
的时候发生的冲突,这时可以打开冲突文件手动修改,也可以自动修改
git checkout --ours a.txt git checkout --theirs a.txt 复制代码
下面举例说明一下如何自动修改。
首先制造一个 merge 冲突的现场。起点在 master 分支。 在 master 分支 和 dev 分支同时修改 a.txt 的第一行,
echo init >a.txt git add a.txt git commit -m 'add a.txt' git checkout -b dev echo dev >a.txt git add a.txt git commit -m 'alter a.txt' git checkout master echo master >a.txt git add a.txt git merge dev 复制代码
看下 a.txt 的内容 cat a.txt
<<<<<<< HEAD master ======= dev >>>>>>> dev 复制代码
上面的是 master 的修改,下面的是 dev 的修改。
如果现在后悔了,想取消合并,恢复到合并前的状态,
git merge --abort 复制代码
自动修改用 git checkout
命令。我们可以选择保留 master 分支的内容
git checkout --ours -- a.txt 复制代码
查看 a.txt 内容,已经恢复正常了。
master 复制代码
如果发现这不是我们要的结果,可以恢复冲突现场
git checkout -m -- a.txt 复制代码
查看 a.txt ,又恢复到冲突状态了。这次我们选择 dev 的内容。
git checkout --theirs -- a.txt 复制代码
检查内容无误后,添加到暂存区。
git add a.txt 复制代码
冲突解决完了,但 merge 还没完成。
git merge --continue 复制代码
这时弹出编辑器,可以修改提交信息,确认后会自动提交修改的内容。merge
完成。
新加的 git switch
你会发现 checkout 承载了很多分支相关的命令。为了让命令更清晰,新版 git 增加了 switch 命令。 switch 能做的事 checkout 都能做。
switch 命令的功能很纯粹,就是切换分支,如果分支不存在,顺便新建分支。
举两个常见的例子。
switch | checkout |
git switch master | git checkout master |
git switch -c dev | git checkout -c dev |
git switch --orphan | git checkout --orphan |
切分支的时候建议把工作区和暂存区的内容都提交
新加的 git restore
和增加 git switch
同样的原因,新版本增加了 git resotre
命令。
git resotre
的职责是恢复工作区和暂存区。原来 checkout
能做的,它都能做。它能做的, checkout
可能做不了。
--worktree
是 git restore
的默认参数
git restore a.txt 把暂存区 a.txt 的内容恢复到工作区 git restore . 恢复工作区的所有内容。 git restore --staged a.txt 把 HEAD 的 a.txt 恢复到暂存区 git restore --source=HEAD --staged --worktree a.txt 恢复工作区和暂存区 复制代码
--source 表示从哪里来,默认是 HEAD --staged 表示恢复到暂存区,--worktree 表示恢复到工作区。这三个参数有简写方式。
git restore -s HEAD -SW a.txt 复制代码
当 merge 发生冲突时,也可以用 restore 来解决冲突,用法同 checkout。
git branch
列出,创建,或 删除 branches。
git branch 列出 git branch dev 创建 git branch -d dev 删除 复制代码
git branch -vv 输出: feature 3446d05 [origin/feature] feat:开发登录功能 * master ad9da22 [origin/master: ahead 3, behind 2] Merge branch dev 复制代码
* 代表 master 是当前分支。
origin/master 是远程分支,master 是 origin/master 的跟踪分支。
- ahead 3: master 超前 origin/master 3 个提交
- behind 2: master 落后 origin/master 2 个提交
你可能会迷惑,怎么又超前同时又落后呢? 其实并不矛盾。没有更新到远程的提交就是超前,没有同步到本地的提交就是落后。
删除远程分支
先删除远程分支
git branch -d origin feature 复制代码
再删除本地跟踪的同名分支
git branch -d feature 复制代码
注意:必须先切到其它分支,如果当前在 feature 分支,无法删除
如果 feature 没有合并到 master ,需要用 -D 强制删除
注意:建议不要删除远程分支,已经发布的分支如果删除了,可能会给别人造成影响。
git reset
reset 是用来撤销的。我们还是通过例子来看
git status -s ?? a.txt git status -s 的显示结果 git add a.txt git status -s A a.txt git reset git status -s ?? a.txt 复制代码
git reset
相当于 git reset --mixed
。 --mixed 是默认值
--mixed
表示恢复暂存区和提交。因为还没有 commit
,所以只恢复了暂存区。
如果已经有两个提交,执行命令后只剩一个提交了。
git log --oneline ab829e6 (master) 提交二 c06e5a8 提交一 复制代码
git reset --mixed head^ 复制代码
c06e5a8 提交一 复制代码
暂存区恢复到上个版本,工作区内容不变,除 --mixed 还有两个常用参数
- --soft 仅恢复提交
- --mixed 恢复提交和暂存区
- --hard 恢复提交,暂存区和工作区
从 soft 到 head 是逐渐加强的,head 最为强悍,完全恢复到指定提交。
reset 不仅是可以回退,还可以重置到任意分支的 commit。
比如当你执行 了 reset head^
,如果知道之前 head 的 id ,还可以退回来。reset "old head commit id"
如果是修改最后一次提交
git commit --amend 复制代码
优先用这个,如果这个无法解决再用 reset。
注意,已经 push 到远程的节点不要用 resset,可以用 revert,后面讲。
git revert
revert
命令用来撤销已经发布的节点。比如我想撤销已经发布的
git revert head 复制代码
执行完成后,会发现又多了一条提交。
git log --oneline 9fb34ec (HEAD -> master) Revert "修改文案" eefd714 修改文案 复制代码
因为是已经发布的提交,所以是不能再修改的,只能再提交新的来抵销之前的提交。
git revert HEAD~3 撤销最近三条提交 git revert -n master~5..master~2 撤销 master 分支最近 5条到最近3条的提交 复制代码
-n 的意思是不要自动 commit