本机测试环境:macOS 11
14 | 怎么删除不需要的分支?#
git branch -d <BRANCH_NAME>
-d
:--delete
- 有条件地删除一个本地分支
- ❗️条件:当该分支被合并到上游分支(本地分支对应的远端分支,可设置)或者 HEAD(当前分支)时,才能成功执行删除
git branch -D <BRANCH_NAME>
-D
:--delete --force
- 强制删除一个本地分支
- 当确定删除分支没有风险时,可以使用
PS:
- 不能删除当前的分支,如需删除,可通过
git checkout <BRANCH_NAME>
先切换分支 - 删除远程分支:
git push <REMOTE> --delete <BRANCH_NAME>
- 通过
git fetch -p / --prune
同步分支列表,此时就不会显示远程已被删除的分支了
- 通过
15 | 怎么修改最新 commit 的 message?#
输入 git commit --amend
,弹出文本编辑器,修改新的 commit message,如红框所示:
输入 :wq
保存并退出后,终端显示修改结果:
再次查看 commit 信息:
修改成功!
PS:git commit --amend
- 本质上是替换上一次提交,不局限于修改 message
- 一般用于修改未 push 到远端之前的 commit 操作
16 | 怎么修改老旧 commit 的 message?#
git rebase -i
可以用来修改前几次的 commit 信息, -i
为使用交互模式
—— 具体步骤 ——
先使用 git log
查看当前的版本历史:
我们想修改顺数第 3 个 commit 的 message,那么需要基于它的父 commit 来操作。
使用git rebase -i d8796c00719323800976e6c7fcfe6b02627ec6b2
命令,末尾添加父 commit 的 hash 值(如果要对最开始的 commit 进行修改,可使用 git rebase -i --root
) ,弹出编辑界面:
commit 的顺序倒置了一下;从 Commands 部分可以看到有很多用法。
我们使用 reword / r
命令,保留某次 commit,但修改其 message,如下:
:wq
保存退出后,又弹出一个新的编辑界面:
修改 commit 的 message,再:wq
保存退出。
修改成功,❗️可以看到这里使用了分离头指针,修改完成后,还更新了 master 分支的指向,说明 commit 的 hash 值很可能发生了改变。
那么验证一下,再次通过 git log
查看版本历史:
可以发现顺数第 3 个 commit 的 message 修改成功,前 3 个 commit 的 hash 值都发生了改变,除了第 3 个 commit 的 message 都没有改变,其他信息其实也都没有改变,可自行验证。
⚠️:
- 一般基于自己的分支,用在还没有提交到集成分支(团队)之前,否则一定要慎用,其会对集成分支造成影响。
rebase
常用于变基,可关注其和merge
的相同点和不同点,参考- Git 分支 - 变基—— 官方
- GIT 使用 rebase 和 merge 的正确姿势—— 知乎
17 | 怎样把连续的多个 commit 整理成 1 个?#
同样使用 git rebase -i
—— 具体步骤 ——
先使用 git log
查看当前的版本历史:
现在想合并上面红框内的 3 个 commit,减少分支里的 commit 数。
使用 git rebase -i --root
命令,末尾添加父 commit 的 hash 值(因为要对最开始的 commit 进行修改,所以使用--root
) ,弹出编辑界面:
要合并的 3 个 commit 见上面红框内,顺序与 git log
展示的顺序相反。
使用 Commands 中的 squash / s
命令,可以将某个 commit 融合到前一个 commit 中,修改如下:
:wq
保存退出后,又弹出一个新的编辑界面:
添加第 2 行,作为融合 3 个 commit 的 message,保留下面每个 commit 的 message,再:wq
保存退出。
整合成功,❗️可以看到这里使用了分离头指针,修改完成后,还更新了 master 分支的指向。
git log
也验证了整合的结果,可以发现 2 个 commit 的 hash 值都发生了改变,因为 commit 的 message 或者父 commit 的指向发生了改变。
PS:
- 整合后会导致一些 commit 没有用了,git 会进行清理。
- 通过
gitk --all
观察此时的分支情况:
- 可以发现出现了两棵树,两个分支之间已经没有关联
- 这是因为 master 分支的根 commit 已经改变
18 | 怎样把间隔的几个 commit 整理成 1 个?#
命令: git rebase -i
通过 git log
命令查看版本历史:
想合并如红框所示的间隔的几个 commit,同样使用 git rebase -i 76e5c9ca
命令,后面添加的是父 commit 的 hash 值,弹出如下熟悉的窗口:
调整要合并的 commit 位置,并使用 s
命令,修改如下:
:wq
保存退出后,提示存在合并冲突,需要解决。
上面蓝框中提示了接下来可以进行的操作,这里我们尝试第一种方式:解决冲突。
PS:还可以借助 git status
查看当前建议的操作:
接下来查看 readme 文件,解决里面的冲突,删除红框位置的代码即可:
再运行 git add readme
添加刚才的修改记录。
然后按前面的提示运行 git rebase --continue
命令,弹出编辑框,输入 message 信息, :wq
保存退出即可(可能还需要解决冲突,重复上述步骤即可)
最后 git log
查看版本历史:
可以看出:如果被间隔的 commit 和要合并的 commit 有关联,很可能不符合预期,导致它们被合并成一个 commit,所以自己要对这几个 commit 的内容非常清楚。
PS:个人认为不太实用,容易产生冲突。
19 | 怎么比较暂存区和 HEAD 所含文件的差异?#
命令: git diff --cached
别忘了这张图:
- HEAD 指向的是当前分支,分支指向的则是一个 commit。
演示:
修改 readme.md 文件后,输入 git add readme.md
将修改添加到暂存区;
再输入git diff --cached
比较暂存区和 HEAD 的差异:
可以看到添加了两行文本,确认符合预期后就可以 commit 了。
20 | 怎么比较工作区和暂存区所含文件的差异?#
命令: git diff
还可以在命令后添加文件名,从而比较指定文件在工作区和暂存区的差异,如 git diff readme.md
,当然也可以添加多个文件名。
21 | 如何让暂存区恢复成和 HEAD 的一样?#
场景:暂存区的内容不想要了,暂存区👉HEAD。
命令: git reset HEAD
, HEAD
后面什么也不加,则将所有文件恢复成和 HEAD
一致
可以通过 git status
观察 reset
前后的变化,见下图:
reset
之后,修改的操作又放到了工作区,可以通过 git add
命令又将它们添加到暂存区。
PS:
git diff --cached
可以验证暂存区和 HEAD 内容的一致性。
git reset HEAD
和 git restore --staged .
的效果是一样的,后者是 git 2.23 + 后新增的。
22 | 如何让工作区的文件恢复为和暂存区一样?#
场景:工作区做了修改,但是不想要了,想恢复为和暂存区一样。
命令: git checkout <FILENAME>
再提醒下这张图:
过程演示:
首先通过 git add
,将修改后的 readme.md 文件,从工作区添加到暂存区:
观察操作前后 git diff --cached
的结果,即暂存区与 HEAD 差异的变化,可知 git add
成功。
然后,再来修改readme.md 文件,通过 git diff
观察工作区和暂存区的差异:
红框部分为此次的修改。
通过 git status
查看工作区和暂存区的情况:
图中上半部分是暂存区中待 commit的内容;下半部分是工作区中待添加到暂存区的内容。
可以看到两个部分都有对 readme.md 的修改,分别对应刚才的两次修改。
❗️此时,如果想把 readme.md 文件在工作区的内容恢复回暂存区状态,使用 git checkout readme.md
即可。
可以看到工作区的 readme.md 文件已经不在待添加状态了。
PS:
- 慎用
git checkout
,可能导致文件找不回。 - Git 2.23 之后,用
git switch
和git restore
来替代git checkout
功能:git switch
替换git checkout
切换分支的功能;git restore
替换对工作区文件进行恢复的功能。 git checkout <FILENAME>
和git restore <FILENAME>
的效果是一样的,后者是 git 2.23 + 后新增的。
23 | 怎样取消暂存区部分文件的更改?#
命令: git reset HEAD [FILENAME]
,类似第 21 节,在命令后面多添加了具体的文件名,也可以指定多个文件,用空格隔开。
下面示例展示了暂存区中的修改被一个一个退回到工作区中:
如果想把工作区中的修改再添加到暂存区,使用 git add
命令即可。
24 | 消除最近的几次提交#
命令: git reset --hard <HASH_OF_COMMIT>
,可以改变 HEAD 的 commit 指向,并且暂存区和工作区的内容都回到该 commit。
请看下面的例子:
观察 git reset --hard
前后的两个点:
1)通过 git log
可以看出 HEAD 的指向后退了两个 commit;
2)通过 git diff
可以看出工作区和暂存区从存在差异到没有差异。
25 | 看看不同提交的指定文件的差异#
命令: git diff <HASH_OF_COMMIT> <HASH_OF_COMMIT> [-- FILENAME]
最后添加指定的文件, 如果不添加,则会展示所有文件的差异;
<HASH_OF_COMMIT>
可以用直接换成分支名,分支名本质上也是指向一个 commit。
PS:加--
会使 Git 更明确你的意图。
26 | 正确删除文件的方法#
命令: git rm <FILENAME>
效果如下:
本质上相当于删除了文件,并把这个删除操作同步到了工作区和暂存区里,等同于:
先通过 git reset --hard HEAD
让工作区和暂存区都恢复到 HEAD 的状态;
通过 git status
,分别观察 rm
文件以及 git rm
文件后,工作区和暂存区的状态:
1)前者改变了工作区,但没有暂存;
2)后者同步了暂存区,此时可以 commit 这次修改了。
27 | 开发中临时加塞了紧急任务怎么处理?#
临时加塞了紧急任务,当前工作还只是半成品,该怎么办呢?
git stash
:将当前工作区和暂存区的内容迁移到一个额外的栈上,只对被跟踪的文件有效。
完成了紧急任务,此时要恢复刚刚的半成品,先查看 stash 了哪些内容。
git stash list
:显示 stash 的记录列表。
然后,将最近的一次 stash 内容恢复到工作区内。
git stash apply
:恢复栈顶的保存记录,且记录列表里还保留该记录。
git stash pop
:恢复栈顶的保存记录,并把记录列表里的该记录删除。
- PS:也可以指定某一个记录恢复。
过程展示:
来了紧急任务,先将暂存区里未完成的内容 stash 起来。
此时 stash 记录列表里添加了一个记录 {0},并且当前工作区和暂存区里已经干净。
忙完了,恢复 stash 里最近的一个记录。
可以看到 apply
是不会清除 stash 记录的,并且被恢复的内容放到了工作区,而之前 stash 的是暂存区的内容。
再来另一种方式的恢复,使用 pop
并且添加 --index
参数。
可以看到 pop
会清除该 stash 记录,并且被恢复的内容放到了与 stash 时一样的暂存区, --index
其实就代表还会恢复暂存区。
PS:
WIP 全称 Work in progress,表示正在工作过程中,引申含义为 “目前工作区中的代码正在编写中,这部分代码不能独立运行,是半成品”。
28 | 如何指定不需要 Git 管理的文件?#
方法:在 .gitignore
文件中声明文件类型或文件名即可(必须是 .gitignore
)
声明 doc / 和 doc 的区别演示:
1)doc/:忽略文件夹,但不会忽略同名的文件
创建 doc 文件夹,在里面添加 readhim 文件。
在 .gitignore
文件里添加 doc/
后,Git 将忽略对 doc 文件夹的跟踪。
但是,将 doc 文件夹换成 doc文件后,Git 将不会忽略跟踪 doc 文件。
2)doc:同时忽略文件夹和同名文件
而对于忽略规则 doc,不管是文件夹还是同名文件,都会让 Git 忽略对其跟踪。
⚠️:
- 常用通配符
*
忽略同类的文件 - 文件已经被跟踪后,
.gitignore
文件就不起作用了- 如果提交 commit 后,想再忽略一些已经提交的文件呢?
- 可以把想忽略的文件先添加到
.gitignore
文件中,然后通过git rm --cached FILENAME
的方式忽略掉无需跟踪的文件。
- 在 Github 上新建仓库时,可以选择添加一份合适的.gitignore 文件:(很贴心❤️)
PS:对于不同的语言或项目,常见的 .gitignore
文件可参考github/gitignore库。
29 | 如何将 Git 仓库备份到本地?#
相关命令: git clone
、 git remote
、 git push
常用传输协议:本地协议、http/https 协议、ssh 协议,后两者在工作中最常用。
传输协议又可以分为两类:哑协议和智能协议,这里以本地协议为例:
1)/Path/to/.git;2)file:///Path/to/.git
前者为哑协议,后者为智能协议。
那么两者在 Git 备份时有什么区别呢?
智能协议相比哑协议,1)传输进度可见,2)并且传输速度更快。
智能协议和哑协议的效果对比:
通过 pwd
获取到了之前演示用的仓库地址为: /Users/double/Desktop/test
现在通过两种协议,将仓库备份到另外的地方:test_remote 目录下。
哑协议
⚠️: --bare
代表创建裸仓库,即不带工作区的仓库,其方便作为服务端;备份的是.git 文件夹,所以地址要添加 /.git
;最后的 ya.git
表示仓库名。
智能协议
回到 test.remote 目录下,使用智能协议,即添加 file://
前缀。
对比可以看到直观的区别:哑协议不带进度条,而智能协议有进度条。
上面展示的是从远端克隆 (clone
)仓库到本地的过程,那如何推送 (push
) 本地仓库到远端呢?
回到原仓库(这里原仓库的角色从远端转变成了本地),新建一个名为 new
的分支。
此时远端 intel.git 仓库还只有 3 个分支:
在本地添加远端服务器:
git remote add intel file:///Users/double/Desktop/test_remote/intel.git
PS: intel
为服务器别名,后面是服务器地址,使用智能协议。
通过 git remote -v
可以查看详细的服务器信息。
接下来进行推送: git push REMOTE_NAME
命令
⚠️:直接推送会报错,需要先设置上游。
再次查看下远端 intel.git 仓库的分支情况:
可以看到分支 new
已经被推送过来了~
学到这里,有哪些是你之前不知道的呢?欢迎交流~