Missing Semester 计算机教育中缺失的一课 Lecture 05 Version Control and Git
前言
acmer 听这种底层概念真的毫无压力!
基本命令
创建
❯ git init
在 /home/Bluuue/Desktop/git-demo/.git/ 初始化空的 Git 仓库
❯ ls -la
总计 4
drwxr-xr-x 1 Bluuue Bluuue 24 9月11日 00:14 .
drwxr-xr-x 1 Bluuue Bluuue 1458 9月10日 18:49 ..
drwxr-xr-x 1 Bluuue Bluuue 128 9月11日 00:14 .git
-rw-r--r-- 1 Bluuue Bluuue 12 9月11日 00:11 test.txt
首先,git help commit 可以查询某个命令的用法。之后,对于当前工作目录,首先用 git init 创建一个空的 git 仓库。此时可以发现目录中多了一个名为 .git 的隐藏文件夹,这个就是用来存放仓库数据的。
❯ git switch test
切换到分支 'test'
❯ echo "v3" >> test.txt
❯ git status
位于分支 test
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
❯ git add test.txt
❯ git status
位于分支 test
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: test.txt
❯ echo "v4" >> test.txt
❯ git status
位于分支 test
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: test.txt
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
❯ git diff
diff --git a/test.txt b/test.txt
index 4b1d4d4..ed7ce12 100644
--- a/test.txt
+++ b/test.txt
@@ -1,3 +1,4 @@
v1
v2
v3
+v4
❯ git diff --staged
diff --git a/test.txt b/test.txt
index 2139d8b..4b1d4d4 100644
--- a/test.txt
+++ b/test.txt
@@ -1,2 +1,3 @@
v1
v2
+v3
对于每一次修改,可以先 git add test.txt 将文件添加到暂存区。注意,这里是加入暂存区而非直接 commit,相当于将真正想提交的放进去,等到都处理好之后再 commit 创建新版本。对应的,git status 可以查看工作区的哪些修改已经放入暂存区、哪些修改还没放入暂存区。类似的,git diff 可以查看工作区和暂存区的变化,加上 --staged 参数可以查看当前暂存区和上一次 commit 的变化。
❯ git commit -m "add v3"
[test d671dea] add v3
1 file changed, 1 insertion(+)
❯ git status
位于分支 test
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
❯ git log
commit d671dea1ee4b9eca423d3918501be21435e9190a (HEAD -> test)
Author: Bluuue <xxx@qq.com>
Date: Fri Sep 11 00:15:50 2026 +0800
add v3
commit da9d3e62f69dd0fad281bf057a6565d0bab9f4b6
Author: Bluuue <xxx@qq.com>
Date: Thu Sep 10 00:22:32 2026 +0800
v2
commit e89f78f894b4e2b79ad951c2476245a02ce9dd93 (master)
Author: Bluuue <xxx@qq.com>
Date: Thu Sep 10 00:21:53 2026 +0800
v1
commit 时,可以使用 -m 参数添加更改说明。在 commit 多次之后,可以通过 git log 查看历史提交记录。此外,--graph 参数可以图形化查看提交记录的关系,-p 参数可以查看每次提交发生的改变。
分支
❯ git switch -c myTest
切换到一个新分支 'myTest'
❯ git branch dev
❯ git switch dev
M test.txt
切换到分支 'dev'
❯ git status
位于分支 dev
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
如果想要创建一个新的分支,branch xxx 或 switch -c xxx 都是可选项。此时有几个概念,首先,分支并不是完全复制一份代码,可以理解为一个指向某个 commit 的指针。之后,HEAD 是一个会指向当前所处分支的指针。此时在 commit 的时候,分支指针就会指向当前分支最新的一次 commit。
❯ git switch myTest
切换到分支 'myTest'
❯ vim test.txt
❯ cat test.txt
v1
v3
v5
❯ git switch dev
错误:您对下列文件的本地修改将被检出操作覆盖:
test.txt
请在切换分支前提交或储藏您的修改。
正在中止
❯ git add test.txt
❯ git commit -m "erase v2 and add v5"
[myTest 0bcd884] erase v2 and add v5
1 file changed, 1 insertion(+), 1 deletion(-)
❯ git switch dev
切换到分支 'dev'
❯ cat test.txt
v1
v2
v3
v4
❯ git log --graph --all
* commit 0bcd884050f9009aa8802e42799d85677be3ea51 (myTest)
| Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:41:45 2026 +0800
|
| erase v2 and add v5
|
| * commit 08865f73efd372d88b552fbcfd555d33858b7437 (HEAD -> dev)
|/ Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:37:53 2026 +0800
|
| add v4
|
* commit d671dea1ee4b9eca423d3918501be21435e9190a (test)
| Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:15:50 2026 +0800
|
| add v3
|
* commit da9d3e62f69dd0fad281bf057a6565d0bab9f4b6
| Author: Bluuue <3531918730@qq.com>
| Date: Thu Sep 10 00:22:32 2026 +0800
|
| v2
|
* commit e89f78f894b4e2b79ad951c2476245a02ce9dd93 (master)
Author: Bluuue <3531918730@qq.com>
Date: Thu Sep 10 00:21:53 2026 +0800
v1
注意,当在当前版本修改文件后,如果立刻切换到别的分支,此时由于会将该修改覆盖掉,所以发生报错,需要 commit 之后再切换。
❯ echo "unfinished optimization" >> test.txt
❯ git status
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
❯ git diff
diff --git a/test.txt b/test.txt
index 11fea7d..11f3a00 100644
--- a/test.txt
+++ b/test.txt
@@ -4,3 +4,4 @@ v4
v5
v6
+unfinished optimization
❯ git switch myTest
错误:您对下列文件的本地修改将被检出操作覆盖:
test.txt
请在切换分支前提交或储藏您的修改。
正在中止
❯ git stash
保存工作目录和索引状态 WIP on dev: bc4b307 add peer change
❯ git status
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
无文件要提交,工作区干净
❯ cat test.txt
v1
v3
v4
v5
v6
❯ git stash list
stash@{0}: WIP on dev: bc4b307 add peer change
❯ git switch myTest
切换到分支 'myTest'
❯ git switch dev
切换到分支 'dev'
您的分支与上游分支 'origin/dev' 一致。
❯ git stash pop
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
丢弃了 refs/stash@{0}(dcae8a93f4c8650a11912c3673cfeeddff610cf1)
❯ cat test.txt
v1
v3
v4
v5
v6
unfinished optimization
❯ git stash list
如果我在代码写了一半的时候想切出去,就可以使用 git stash 临时把修改收起来,先让工作区恢复干净,等到切换回来的时候从 list 里面 git stash pop 即可。注意,pop 后这条记录会消失,如果想保留可以 git stash apply。实际中,还可以 git stash push -m "xxx" 像 commit 一样添加 message,这样更方便识别。
合并
❯ git merge myTest
自动合并 test.txt
冲突(内容):合并冲突于 test.txt
自动合并失败,修正冲突然后提交修正的结果。
❯ cat test.txt
v1
v3
<<<<<<< HEAD
v4
=======
v5
>>>>>>> myTest
❯ vim test.txt
❯ cat test.txt
v1
v3
v4
v5
❯ git status
位于分支 dev
您有尚未合并的路径。
(解决冲突并运行 "git commit")
(使用 "git merge --abort" 中止合并)
未合并的路径:
(使用 "git add <文件>..." 标记解决方案)
双方修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
❯ git diff
diff --cc test.txt
index ed7ce12,05c4b7c..0000000
--- a/test.txt
+++ b/test.txt
@@@ -1,4 -1,3 +1,5 @@@
v1
- v2
v3
+v4
+ v5
++
❯ git add test.txt
❯ git status
位于分支 dev
所有冲突已解决但您仍处于合并中。
(使用 "git commit" 结束合并)
要提交的变更:
修改: test.txt
❯ git commit -m "merge myTest and dev"
[dev b6af65f] merge myTest and dev
❯ git log --graph --all
* commit b6af65f6327e2318df2497e531b259c22dfa7b86 (HEAD -> dev)
|\ Merge: 08865f7 0bcd884
| | Author: Bluuue <3531918730@qq.com>
| | Date: Fri Sep 11 00:50:36 2026 +0800
| |
| | merge myTest and dev
| |
| * commit 0bcd884050f9009aa8802e42799d85677be3ea51 (myTest)
| | Author: Bluuue <3531918730@qq.com>
| | Date: Fri Sep 11 00:41:45 2026 +0800
| |
| | erase v2 and add v5
| |
* | commit 08865f73efd372d88b552fbcfd555d33858b7437
|/ Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:37:53 2026 +0800
|
| add v4
|
* commit d671dea1ee4b9eca423d3918501be21435e9190a (test)
| Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:15:50 2026 +0800
|
| add v3
|
* commit da9d3e62f69dd0fad281bf057a6565d0bab9f4b6
| Author: Bluuue <3531918730@qq.com>
| Date: Thu Sep 10 00:22:32 2026 +0800
|
| v2
|
* commit e89f78f894b4e2b79ad951c2476245a02ce9dd93 (master)
Author: Bluuue <3531918730@qq.com>
Date: Thu Sep 10 00:21:53 2026 +0800
如果想要合并两个版本,就可以使用 git merge。注意,此时由于 dev 版本里最后是 v4,myTest 版本里最后是 v5,所以 git 无法自动判断如何 merge。此时可以看到 test.txt 里发生了变化,HEAD 下方表示是当前版本的内容,myTest 上方是要合并的版本的内容。此时就可以手动删除后手动 add 然后 commit 来完成 merge。
❯ git branch
* dev
master
myTest
test
❯ git branch -d test
已删除分支 test(原为 d671dea)。
❯ git branch
* dev
master
myTest
❯ git log --graph --all
* commit b6af65f6327e2318df2497e531b259c22dfa7b86 (HEAD -> dev)
|\ Merge: 08865f7 0bcd884
| | Author: Bluuue <3531918730@qq.com>
| | Date: Fri Sep 11 00:50:36 2026 +0800
| |
| | merge myTest and dev
| |
| * commit 0bcd884050f9009aa8802e42799d85677be3ea51 (myTest)
| | Author: Bluuue <3531918730@qq.com>
| | Date: Fri Sep 11 00:41:45 2026 +0800
| |
| | erase v2 and add v5
| |
* | commit 08865f73efd372d88b552fbcfd555d33858b7437
|/ Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:37:53 2026 +0800
|
| add v4
|
* commit d671dea1ee4b9eca423d3918501be21435e9190a
| Author: Bluuue <3531918730@qq.com>
| Date: Fri Sep 11 00:15:50 2026 +0800
|
| add v3
|
* commit da9d3e62f69dd0fad281bf057a6565d0bab9f4b6
| Author: Bluuue <3531918730@qq.com>
| Date: Thu Sep 10 00:22:32 2026 +0800
|
| v2
|
* commit e89f78f894b4e2b79ad951c2476245a02ce9dd93 (master)
Author: Bluuue <3531918730@qq.com>
Date: Thu Sep 10 00:21:53 2026 +0800
当然,可以直接 branch 查看当前所有分支,-d 参数来删除某个分支。注意,此时不会改变历史版本记录,能改变的只有 branch,所有的 commit 都是不可改变的!
高级命令
远程仓库
❯ git init --bare git-demo-remote.git
在 /home/Bluuue/Desktop/git-demo-remote.git/ 初始化空的 Git 仓库
❯ ls git-demo-remote.git
hooks info objects refs config description HEAD
❯ git remote add origin ../git-demo-remote.git
❯ git remote -v
origin ../git-demo-remote.git (fetch)
origin ../git-demo-remote.git (push)
❯ git push -u origin dev
枚举对象中: 18, 完成.
对象计数中: 100% (18/18), 完成.
使用 32 个线程进行压缩
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (18/18), 1.21 KiB | 1.21 MiB/s, 完成.
总共 18(差异 1),复用 0(差异 0),包复用 0(来自 0 个包)
To ../git-demo-remote.git
* [new branch] dev -> dev
分支 'dev' 设置为跟踪 'origin/dev'。
❯ git branch -a
* dev
master
myTest
remotes/origin/dev
❯ git branch -vv
* dev b6af65f [origin/dev] merge myTest and dev
master e89f78f v1
myTest 0bcd884 erase v2 and add v5
remote 可以理解为给另一个 git 仓库保存一个名字和地址,然后管理当前仓库关联的其他仓库,在这里名字就是 origin。在创建一个新仓库并添加之后,此时就可以将当前分支 push 到远程仓库。此时可以看到分支中多了一个 remotes/origin/dev,这个表示上一次和这个远程仓库联系时的 dev 状态。
对于 git push -u origin dev 命令,含义就是更新远程仓库的状态,将缺少的 commit 发过去并更新 branch 的指向。其中,-u 参数是将本地的 dev 的 upstream 设置为 origin/dev,默认以后两者对应,于是之后在 dev 分支时直接 git push 就可以更新远程的这个分支了。此时可以使用 git branch -vv 查看分支的 upstream。
❯ echo "v6" >> test.txt
❯ git add test.txt
❯ git commit -m "add v6"
[dev 808cb24] add v6
1 file changed, 1 insertion(+)
❯ git status
位于分支 dev
您的分支领先 'origin/dev' 共 1 个提交。
(使用 "git push" 来发布您的本地提交)
无文件要提交,工作区干净
❯ git push
枚举对象中: 5, 完成.
对象计数中: 100% (5/5), 完成.
写入对象中: 100% (3/3), 248 字节 | 248.00 KiB/s, 完成.
总共 3(差异 0),复用 0(差异 0),包复用 0(来自 0 个包)
To ../git-demo-remote.git
b6af65f..808cb24 dev -> dev
❯ git status
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
无文件要提交,工作区干净
❯ git log --oneline --graph --decorate --all
* 808cb24 (HEAD -> dev, origin/dev) add v6
* b6af65f merge myTest and dev
|\
| * 0bcd884 (myTest) erase v2 and add v5
* | 08865f7 add v4
|/
* d671dea add v3
* da9d3e6 v2
* e89f78f (master) v1
在进行了一次 commit 之后,此时就存在一个 “领先 ahead” 的概念,意思就是本地的 dev 比上次联系时远程仓库的 dev 多了一个 commit。 此时再 push 可以发现 commit 同步过去了,提示也说明了这一点。
❯ git fetch origin
remote: 枚举对象中: 4, 完成.
remote: 对象计数中: 100% (4/4), 完成.
remote: 压缩对象中: 100% (2/2), 完成.
remote: 总共 3(差异 0),复用 0(差异 0),包复用 0(来自 0 个包)
展开对象中: 100% (3/3), 269 字节 | 269.00 KiB/s, 完成.
来自 ../git-demo-remote
808cb24..bc4b307 dev -> origin/dev
❯ git log --oneline --graph --decorate --all
* bc4b307 (origin/dev) add peer change
* 808cb24 (HEAD -> dev) add v6
* b6af65f merge myTest and dev
|\
| * 0bcd884 (myTest) erase v2 and add v5
* | 08865f7 add v4
|/
* d671dea add v3
* da9d3e6 v2
* e89f78f (master) v1
❯ git merge origin/dev
更新 808cb24..bc4b307
Fast-forward
peer.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 peer.txt
❯ ls
peer.txt test.txt
当有别的远程开发者更新了仓库中的分支,此时本地的分支仍然指向之前的 commit。此时就可以通过 git fetch origin 来将仓库中的内容同步到本地,注意此时不会改变本地 dev 的指向,只会更新其对应的 origin/dev 状态。之后就可以使用 git log 和 git diff 查看发生了什么变化,如果想要将修改放进 dev 的话,merge 一下即可。
当然,如果觉得先 fetch 再 merge 太麻烦,可以直接 git pull。而当本地压根没有这个仓库时,就是 git clone xxx 将远程仓库直接搬下来。
撤销
❯ echo "temporary change" >> test.txt
❯ git diff
diff --git a/test.txt b/test.txt
index 11fea7d..51b434a 100644
--- a/test.txt
+++ b/test.txt
@@ -4,3 +4,4 @@ v4
v5
v6
+temporary change
❯ cat test.txt
v1
v3
v4
v5
v6
temporary change
❯ git restore test.txt
❯ git diff
在修改当前工作区的内容后,如果不想要了,可以 git restore xxx 进行撤销。具体原理是,将当前暂存区的该文件拿来覆盖掉工作区的文件,也就是丢弃尚未暂存的文件。
❯ echo "temporary change" >> test.txt
❯ git add test.txt
❯ git status
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: test.txt
❯ git restore --staged test.txt
❯ git status
位于分支 dev
您的分支与上游分支 'origin/dev' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
而如果想丢弃暂存区的内容,可以使用 --staged 参数来取消暂存,就是 git add 的反向操作。
❯ git switch -c amend-demo
切换到一个新分支 'amend-demo'
❯ echo "amend demo" > amend.txt
❯ git add amend.txt
❯ git commit -m "add amend deom"
[amend-demo f030ac8] add amend deom
1 file changed, 1 insertion(+)
create mode 100644 amend.txt
❯ git log -1
commit f030ac810b951ecbe843cf4a164fc9366bfa78d9 (HEAD -> amend-demo)
Author: Bluuue <3531918730@qq.com>
Date: Sun Sep 13 19:27:05 2026 +0800
add amend deom
❯ git commit --amend -m "add amend demo"
[amend-demo a41cbdc] add amend demo
Date: Sun Sep 13 19:27:05 2026 +0800
1 file changed, 1 insertion(+)
create mode 100644 amend.txt
❯ git log -1
commit a41cbdc1c97d5f453d1f747cf92240ad63edaac7 (HEAD -> amend-demo)
Author: Bluuue <3531918730@qq.com>
Date: Sun Sep 13 19:27:05 2026 +0800
add amend demo
❯ echo "forget file" > forget.txt
❯ git add forget.txt
❯ git commit --amend --no-edit
[amend-demo e3f857b] add amend demo
Date: Sun Sep 13 19:27:05 2026 +0800
2 files changed, 2 insertions(+)
create mode 100644 amend.txt
create mode 100644 forget.txt
❯ git show HEAD
commit e3f857bc45a3b26f2b73c1200de95bf3bc46d73a (HEAD -> amend-demo)
Author: Bluuue <3531918730@qq.com>
Date: Sun Sep 13 19:27:05 2026 +0800
add amend demo
diff --git a/amend.txt b/amend.txt
new file mode 100644
index 0000000..bf94862
--- /dev/null
+++ b/amend.txt
@@ -0,0 +1 @@
+amend demo
diff --git a/forget.txt b/forget.txt
new file mode 100644
index 0000000..4adede8
--- /dev/null
+++ b/forget.txt
@@ -0,0 +1 @@
+forget file
而当 commit 完才发现 commit message 写错了,此时使用 --amend 可以修改 message。不仅如此,如果发现漏了一个文件没提交,此时可以将这个文件 add 到暂存区,然后使用 --amend 参数重新 commit。注意看,两个 commit 的哈希值不同,说明并不是在之前的基础上修改,而是相当于换了一个新的。这里的 --no-edit 表示不修改原来的 message,而 git show 可以查看某个 commit 的内容和带来的修改。
❯ git log -2
commit 7becc5ab17afbac3ab52638b8a06569eb4376923 (HEAD -> reset-demo)
Author: Bluuue <3531918730@qq.com>
Date: Sun Sep 13 19:59:41 2026 +0800
add r2
commit 61be045f28bd86cf36d060277b91132b0e5b4d9c
Author: Bluuue <3531918730@qq.com>
Date: Sun Sep 13 19:53:16 2026 +0800
add r1
❯ git reset --soft HEAD~1
❯ git status
位于分支 reset-demo
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: reset.txt
❯ git diff --staged
diff --git a/reset.txt b/reset.txt
index f7d55cf..e573969 100644
--- a/reset.txt
+++ b/reset.txt
@@ -1 +1,2 @@
r1
+r2
❯ git reset HEAD~1
重置后取消暂存的变更:
M reset.txt
❯ git status
位于分支 reset-demo
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: reset.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
❯ git diff
diff --git a/reset.txt b/reset.txt
index f7d55cf..e573969 100644
--- a/reset.txt
+++ b/reset.txt
@@ -1 +1,2 @@
r1
+r2
❯ cat reset.txt
r1
r2
❯ git reset --hard HEAD~1
HEAD 现在位于 61be045 add r1
❯ cat reset.txt
r1
这个就有点复杂了。首先,git reset 可以把当前 branch 的指针移动到指定 commit。默认模式是 --mixed,即撤销 commit 和暂存区的内容,但仍然保留工作区的内容。而 --soft 是只撤销 commit 而不撤销暂存区的内容,--hard 是连带工作区的内容也不要了,是比较危险的行为。
❯ echo "normal code" > revert.txt
❯ git add revert.txt
❯ git commit -m "add normal code"
[revert-demo 915b4b0] add normal code
1 file changed, 1 insertion(+)
create mode 100644 revert.txt
❯ echo "BAD CHANGE" >> revert.txt
❯ git add revert.txt
❯ git commit -m "add bad code"
[revert-demo df4832b] add bad code
1 file changed, 1 insertion(+)
❯ cat revert.txt
normal code
BAD CHANGE
❯ git commit --amend -m "add bad change"
[revert-demo 2c84a8f] add bad change
Date: Sun Sep 13 20:16:58 2026 +0800
1 file changed, 1 insertion(+)
❯ git revert HEAD --no-edit
[revert-demo 31991db] Revert "add bad change"
Date: Sun Sep 13 20:17:39 2026 +0800
1 file changed, 1 deletion(-)
❯ cat revert.txt
normal code
❯ git log --graph -4
* commit 31991db60cb4b13a579de09df99efef905c07c72 (HEAD -> revert-demo)
| Author: Bluuue <3531918730@qq.com>
| Date: Sun Sep 13 20:17:39 2026 +0800
|
| Revert "add bad change"
|
| This reverts commit 2c84a8fe31912f8e0d5ea8c830039b1511999ab8.
|
* commit 2c84a8fe31912f8e0d5ea8c830039b1511999ab8
| Author: Bluuue <3531918730@qq.com>
| Date: Sun Sep 13 20:16:58 2026 +0800
|
| add bad change
|
* commit 915b4b083721f8b4e91bd44f6304d6844c800d22
| Author: Bluuue <3531918730@qq.com>
| Date: Sun Sep 13 20:16:42 2026 +0800
|
| add normal code
|
* commit 61be045f28bd86cf36d060277b91132b0e5b4d9c (reset-demo)
| Author: Bluuue <3531918730@qq.com>
| Date: Sun Sep 13 19:53:16 2026 +0800
|
| add r1
这个就比较牛了。和 reset 不同,git revert 不会删除一个 commit,而是在之前的基础上尝试撤销某个历史 commit 的影响。对于已经 push 的 commit,直接 reset 会导致协作历史很混乱,而 revert 可以不改变协作历史,只是加入一个新的 commit。当然,既然是撤销历史影响,那么肯定会像 merge 一样发生冲突,那么就需要手动操作了。
重建
❯ echo "main change" > rebase-main.txt
❯ git add rebase-main.txt
❯ git commit -m "add main change"
[rebase-main 758c963] add main change
1 file changed, 1 insertion(+)
create mode 100644 rebase-main.txt
❯ git switch -c rebase-feature dev
切换到一个新分支 'rebase-feature'
❯ echo "feature v1" > rebase-feature.txt
❯ git add rebase-feature.txt
❯ git commit -m "add feature v1"
[rebase-feature b1ce59d] add feature v1
1 file changed, 1 insertion(+)
create mode 100644 rebase-feature.txt
❯ echo "feature v2" >> rebase-feature.txt
❯ git add rebase-feature.txt
❯ git commit -m "add feature v1"
[rebase-feature f5e7ce2] add feature v1
1 file changed, 1 insertion(+)
❯ git branch rebase-feature-old
❯ git rebase rebase-main
成功变基并更新 refs/heads/rebase-feature。
❯ git log --oneline --graph --decorate --all
* 24bbf60 (HEAD -> rebase-feature) add feature v1
* 4535277 add feature v1
* 758c963 (rebase-main) add main change
* 31991db (revert-demo) Revert "add bad change"
* 2c84a8f add bad change
* 915b4b0 add normal code
* 61be045 (reset-demo) add r1
* e3f857b (amend-demo) add amend demo
| * f5e7ce2 (rebase-feature-old) add feature v1
| * b1ce59d add feature v1
|/
* bc4b307 (origin/dev, dev) add peer change
* 808cb24 add v6
* b6af65f merge myTest and dev
|\
| * 0bcd884 (myTest) erase v2 and add v5
* | 08865f7 add v4
|/
* d671dea add v3
* da9d3e6 v2
* e89f78f (master) v1
当从某个 commit 开始分出去一个 branch,分别开发 branch 和 main 两条线。此时如果想让我的 branch 看起来像从最新 main 开始开发一样,就可以 git rebase main。这样会从 main 所指的 commit 开始,将当前 branch 的 commit 创建重新应用一遍,表现为在 main 的基础上创建多个 commit。
除此之外,如果只想把某个特定的 commit 搬过来,可以使用 git cherry-pick xxx。
.gitignore
这个文件的用处就是,某些本来没有被跟踪的文件,不要老是在 git status 里提醒我,也不要误加进仓库。比如要提交的只有 main.cpp,其的生成文件 main 是不需要提交的,就可以在 .gitignore 里写上 main,或者直接 *.log。
二分
❯ echo "OK" > state.txt
❯ git add state.txt
❯ git commit -m "bisect: state 1"
[bisect-demo 6a94d1d] bisect: state 1
1 file changed, 1 insertion(+)
create mode 100644 state.txt
❯ echo "version 2" >> state.txt
❯ git add state.txt
❯ git commit -m "bisect: state 2"
[bisect-demo 1fe77c5] bisect: state 2
1 file changed, 1 insertion(+)
❯ echo "version 3" >> state.txt
❯ git add state.txt
❯ git commit -m "bisect: state 3"
[bisect-demo 4e67559] bisect: state 3
1 file changed, 1 insertion(+)
❯ echo "BROKEN" >| state.txt
❯ echo "version 4" >> state.txt
❯ git add state.txt
❯ git commit -m "bisect: introduce bug"
[bisect-demo 12b6c15] bisect: introduce bug
1 file changed, 2 insertions(+), 3 deletions(-)
❯ echo "version 5" >> state.txt
❯ git add state.txt
❯ git commit -m "bisect: state 5"
[bisect-demo 63251d0] bisect: state 5
1 file changed, 1 insertion(+), 1 deletion(-)
❯ echo "version 6" >> state.txt
❯ git add state.txt
❯ git commit -m "bisect: state 6"
[bisect-demo a4af14c] bisect: state 6
1 file changed, 1 insertion(+)
❯ git log --oneline --graph -6
* a4af14c (HEAD -> bisect-demo) bisect: state 6
* 63251d0 bisect: state 5
* 12b6c15 bisect: introduce bug
* 98af541 bisect: state 3
* 5220b18 bisect: state 2
* 6a94d1d bisect: state 1
❯ git bisect start
状态:正在等待 'good' 和 'bad' 提交
❯ git bisect bad
状态:正在等待 'good' 提交,已知 'bad' 提交
❯ git bisect good 6a94d1d
二分查找中:在此之后,还剩 2 个版本待测试 (大概 1 步)
[98af541a3b6c39a564684bfcc2475ac1bf110f91] bisect: state 3
❯ git bisect reset
之前的 HEAD 位置是 98af541 bisect: state 3
切换到分支 'bisect-demo'
当发现原来程序还是好的而现在程序坏了,但不知道是在之前哪次修改后坏的,此时就可以使用 git bisect 二分查找出现问题的 commit。除此之外,还可以检查在哪次 commit 后性能出现了严重问题。
使用时,只需要 start 之后说明 good 和 bad 的 commit,之后就会自动返回分界 commit。注意,最后要 reset 返回开始前的位置。除此之外,还可以使用 git bisect run xxx.sh,写一个脚本编译并运行程序,性能达到阈值就返回 0 表示 good 来测试性能。
恢复
❯ git switch -c reflog-demo
切换到一个新分支 'reflog-demo'
❯ echo "very important code" > recover.txt
❯ git add recover.txt
❯ git commit -m "add important code"
[reflog-demo 8a2ad13] add important code
1 file changed, 1 insertion(+)
create mode 100644 recover.txt
❯ git log -2 --oneline
8a2ad13 (HEAD -> reflog-demo) add important code
bc4b307 (origin/dev, dev) add peer change
❯ git reset --hard HEAD~1
HEAD 现在位于 bc4b307 add peer change
❯ git log -2 --oneline
bc4b307 (HEAD -> reflog-demo, origin/dev, dev) add peer change
808cb24 add v6
❯ git reflog
bc4b307 (HEAD -> reflog-demo, origin/dev, dev) HEAD@{0}: reset: moving to HEAD~1
8a2ad13 HEAD@{1}: commit: add important code
...
❯ git branch recovered HEAD@{1}
如果 reset 后发现操作错了,此时可以通过 git reflog 查看 HEAD 之前的位置并恢复过去。
总结
这个功能也太强了!

Lecture 05 Version Control and Git
浙公网安备 33010602011771号