一、bug 分支
假如我们现在正在 dev2
分支上进行开发,开发到一半,突然发现 master
分支上面有 bug,需要解决。
- 在Git中,每个
bug
都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
可现在 dev2
的代码在工作区中开发了一半,还无法提交,怎么办?例如:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git branch
* dev2
master
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
a,b,c,d
I am coding...
lighthouse@VM-8-10-ubuntu:gitcode$ git status
On branch dev2
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: book
no changes added to commit (use "git add" and/or "git commit -a")
Git 提供了 git stash
命令,可以将当前的工作区信息进行储藏,被储藏的内容可以在将来某个时间恢复出来。
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git stash
Saved working directory and index state WIP on dev2: 2bd7b8b modify Readme
lighthouse@VM-8-10-ubuntu:gitcode$ git status
On branch dev2
nothing to commit, working tree clean
用 git status
查看工作区,就是干净的(除非有没有被 Git 管理的文件),因此可以放心地创建分支来修复bug。
储藏 dev2 工作区之后,由于我们要基于 master
分支修复 bug,所以需要切回 master
分支,再新建临时分支来修复 bug,示例如下:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout -b fix_bg # 新建并切换
Switched to a new branch 'fix_bg'
lighthouse@VM-8-10-ubuntu:gitcode$ vim book
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
a,b,c,d,e
lighthouse@VM-8-10-ubuntu:gitcode$ git add book
lighthouse@VM-8-10-ubuntu:gitcode$ git commit -m "fix bug"
[fix_bg c0637e0] fix bug
1 file changed, 1 insertion(+), 1 deletion(-)
修复完成后,切换到 master
分支,并完成合并,最后删除 fix_bg
分支
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout master
Switched to branch 'master'
lighthouse@VM-8-10-ubuntu:gitcode$ git merge --no-ff -m "merge fix_bug branch" fix_bg
Merge made by the 'ort' strategy.
book | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
至此,bug的修复工作已经做完了,我们还要继续回到 dev2 分支进行开发。切换回 dev2
分支:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout dev2
Switched to branch 'dev2'
lighthouse@VM-8-10-ubuntu:gitcode$ git status
On branch dev2
nothing to commit, working tree clean
工作区是干净的,刚才的工作现场存到哪去了?用 git stash list
命令看看:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git stash list
stash@{0}: WIP on dev2: 2bd7b8b modify Readme
工作现场还在,Git 把 stash 内容存在某个地方了,但是需要恢复一下,如何恢复现场呢?我们可以使用 git stash pop
命令,恢复的同时会把 stash 也删了,示例如下:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git stash pop
On branch dev2
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: book
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (e2dfd6d0312e2454d1a7a4a3eb65cf3e28f333af)
再次查看的时候,我们已经发现已经没有现场可以恢复了
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git stash list
lighthouse@VM-8-10-ubuntu:gitcode$
另外,恢复现场也可以采用 git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用 git stash drop
来删除;
- 当然也可以多次
stash
,恢复的时候,先用git stash list
查看,然后恢复指定的stash
,用命令gitstash apply stash@{o}
恢复完代码之后我们便可以继续完成开发,开发完成后便可以进行提交,例如:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
a,b,c,d
I am coding... Done
lighthouse@VM-8-10-ubuntu:gitcode$ git add .
lighthouse@VM-8-10-ubuntu:gitcode$ git commit -m "modify book"
[dev2 ccb0f97] modify book
1 file changed, 1 insertion(+)
但我们注意到了,修复 bug的内容,并没有在 dev2 上显示。此时的状态图为:

Master
分支目前最新的提交,是要领先于新建 dev2
时基于的 master
分支的提交的,所以我们在 dev2 中当然看不见修复 bug 的相关代码。
- 我们的最终目的是要让
master
合并 dev2 分支的,那么正常情况下我们切回master
分支直接合并即可,但这样其实是有一定风险的
原因:在合并分支时可能会有冲突,而代码冲突需要我们手动解决(在 master上解决)
- 我们无法保证对于冲突问题可以正确地一次性解决掉,因为在实际的项目中,代码冲突不只一两行那么简单,有可能几十上百行,甚至更多,解决的过程中难免手误出错,导致错误的代码被合并到
master
上。
此时的状态为:

解决这个问题的一个好的建议就是:最好在自己的分支上合并下 master
,再让 master
去合并dev ,这样做的目的是有冲突可以在本地分支解决并进行测试,而不影响 master
此时的状态为:


对应的实操演示如下,要说明的是,以下演示的merge操作,没有使用 --no-ff
,但上述的图示是禁用 Fast forward
了模式后得出的,主要是为了方便解释问题。
bash
# dev 合并 master
lighthouse@VM-8-10-ubuntu:gitcode$ git branch
* dev2
master
lighthouse@VM-8-10-ubuntu:gitcode$ git merge master
Auto-merging book
CONFLICT (content): Merge conflict in book
Automatic merge failed; fix conflicts and then commit the result.
# 发送冲突
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
<<<<<<< HEAD
a,b,c,d
I am coding... Done
=======
a,b,c,d,e
>>>>>>> master
# 解决冲突并重新提交
lighthouse@VM-8-10-ubuntu:gitcode$ vim book
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
a,b,c,d,e
I am coding... Done
lighthouse@VM-8-10-ubuntu:gitcode$ git add .
lighthouse@VM-8-10-ubuntu:gitcode$ git commit -m "merge master"
[dev2 9e77002] merge master
lighthouse@VM-8-10-ubuntu:gitcode$ git status
On branch dev2
nothing to commit, working tree clean
# 切回 master
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout master
Switched to branch 'master'
# master 合并 dev2 -- 无需解决冲突
lighthouse@VM-8-10-ubuntu:gitcode$ git merge dev2
Updating adbb267..9e77002
Fast-forward
book | 1 +
1 file changed, 1 insertion(+)
lighthouse@VM-8-10-ubuntu:gitcode$ git status
On branch master
nothing to commit, working tree clean
# 删除 dev2 分支
lighthouse@VM-8-10-ubuntu:gitcode$ git branch -d dev2
Deleted branch dev2 (was 9e77002).
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
a,b,c,d,e
I am coding... Done
二、强制删除分支
软件开发中,总有无穷无尽的新的功能要不断添加进来
- 添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个分支,我们可以将其称之为
feature
分支,在上面开发,完成后,合并,最后,删除该feature
分支。 - 可是,如果我们今天正在某个
feature
分支上开发了一半,被产品经理突然叫停,说是要停止新功能的开发。虽然白干了,但是这个feature
分支还是必须就地销毁,留着无用了。 - 这时使用传统的
git branch -d
命令删除分支的方法是不行的。
演示如下:
bash
# 新增并切换到 dev3 分支
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout -b dev3
Switched to a new branch 'dev3'
lighthouse@VM-8-10-ubuntu:gitcode$ clear
# 开始开发新功能并提交
lighthouse@VM-8-10-ubuntu:gitcode$ vim book
lighthouse@VM-8-10-ubuntu:gitcode$ cat book
Hello Island1314
Hello World
hello version1
hello version2
hello version3
write bbb for new branch
a,b,c,d,e
I am coding... Done
I am writing new features ...
lighthouse@VM-8-10-ubuntu:gitcode$ git add .
lighthouse@VM-8-10-ubuntu:gitcode$ git commit -m "modify book for new features"
[dev3 fb6a737] modify book for new features
1 file changed, 1 insertion(+)
# 此时新功能叫停, 切回 master 准备删除 dev3
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout master
Switched to branch 'master'
对 dev3 进行删除,如下:
bash
# 常规删除 dev3 分支时失败
lighthouse@VM-8-10-ubuntu:gitcode$ git branch -d dev3
error: The branch 'dev3' is not fully merged.
If you are sure you want to delete it, run 'git branch -D dev3'.
# 按照提示进行删除
lighthouse@VM-8-10-ubuntu:gitcode$ git branch -D dev3
Deleted branch dev3 (was fb6a737).
三、分支的作用
🌊 分支在实际中有什么用呢?
- 假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。
- 如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
- 现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
而且 Git 无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件
四、细节补充
1. 未提交修改时(未commit
)
当我们在 dev 分支下修改文件之后,master 还是可以看到的(没有 add 的情况下),如下:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout dev
Switched to branch 'dev'
lighthouse@VM-8-10-ubuntu:gitcode$ vim file
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout master
Switched to branch 'master'
lighthouse@VM-8-10-ubuntu:gitcode$ cat file
i miss you # master 分支
I Miss You # dev 分支
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout dev
Switched to branch 'dev'
lighthouse@VM-8-10-ubuntu:gitcode$ git add file
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout master
A file
Switched to branch 'master'
lighthouse@VM-8-10-ubuntu:gitcode$ cat file
i miss you # master 分支
I Miss You # dev 分支
- 工作目录是共享的 :所有未提交的改动(包括未
add
的修改和已add
但未commit
的暂存)都存在于工作目录 中,不会被 Git 自动隔离到特定分支 - 切换分支时的行为 :
- 如果目标分支(如
master
)和当前分支(如dev
)对同一文件的修改不冲突,Git 允许直接切换,并保留工作目录的改动。 - 因此,在未提交的情况下,无论切换到哪个分支,看到的都是工作目录中的当前内容(即未提交的修改)
- 如果目标分支(如
2. 已提交修改后
而当我们在 切换到 dev
分支进行 commit 之后,然后再切换到 master 分支查看,然后 如下:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout dev
A file
Switched to branch 'dev'
lighthouse@VM-8-10-ubuntu:gitcode$ git commit file
Aborting commit due to empty commit message.
lighthouse@VM-8-10-ubuntu:gitcode$ git commit -m "改变" file
[dev 1a1993f] 改变
1 file changed, 2 insertions(+)
create mode 100644 file
- 提交 (
commit
) 的作用 :将暂存区的内容永久记录到当前分支的历史中,此时修改正式归属于dev
分支。 - 切换分支时的行为 :
- Git 会严格根据目标分支(如
master
)的最新提交重建工作目录。 - 如果
master
分支从未包含file
文件(即该文件只在dev
分支提交过,下面等下会细说这个问题 ),切换到master
时,Git 会删除工作目录中的file
文件,因为master
分支的历史中不存在它
- Git 会严格根据目标分支(如
总结
- 提交后,数据已保存在
dev
分支的历史中,不会丢失。 master
分支的工作区被重建为它自己的最新提交状态,因此看不到dev
分支的改动。
这里要说一下,由于我们在 master 分支下没有 commit
过文件,因此当我们在 dev
下首次 commit
,那么 master
分支下的 file 文件就会消失,如下:
bash
lighthouse@VM-8-10-ubuntu:gitcode$ cat file
i miss you # master 分支
I Miss You # dev 分支
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout master
Switched to branch 'master'
lighthouse@VM-8-10-ubuntu:gitcode$ cat file
cat: file: No such file or directory
lighthouse@VM-8-10-ubuntu:gitcode$ ll
total 20
drwxrwxr-x 4 lighthouse lighthouse 4096 Apr 18 19:52 ./
drwxrwxr-x 7 lighthouse lighthouse 4096 Apr 15 21:47 ../
-rw-rw-r-- 1 lighthouse lighthouse 129 Apr 8 23:05 book
drwxrwxr-x 8 lighthouse lighthouse 4096 Apr 18 19:52 .git/
drwxrwxr-x 3 lighthouse lighthouse 4096 Apr 10 22:29 git_learning/
初始状态:
master
分支未提交过file
文件(或该文件未被跟踪)。- 你在
dev
分支修改并提交了file
,此时file
成为dev
分支的一部分。
切换回 master
:
master
分支没有file
的记录,Git 会清理工作目录,删除file
文件以匹配master
分支的状态
因此分支交换的逻辑,可以这样理解,如下:
- 未提交时 :
- 如果修改未提交,Git 在切换分支时会尽量保留工作区内容(除非目标分支和当前修改冲突)。
- 保留的原因 :并非"怕数据丢失",而是为了用户能自由切换分支并继续工作。例如,你在
dev
修改了文件,想临时切到master
修复问题,Git 允许携带未提交的修改切换(只要不冲突)。
- 提交后 :
- 提交的修改会写入
dev
分支的历史记录,此时dev
分支的最新提交包含该文件,而master
分支的历史记录中没有该文件。 - 切换回
master
时,Git 会根据master
的最新提交重建工作区,因此文件会消失(因为master
从未记录过它)。
- 提交的修改会写入
3. 如何避免问题
- 提交前检查分支状态:确保在正确的分支提交改动。
- 使用
git stash
:临时保存未提交的修改,切换分支后再恢复- 核心功能:临时保存工作区和暂存区的未提交修改,清空工作区,使其"干净"(与当前分支的最新提交一致)
- 效果:切换分支时,未提交的修改不会被带到其他分支(避免意外覆盖或冲突)
- 理解文件跟踪 :新增文件需通过
git add
和commit
明确添加到分支历史中。
- 未提交的修改:属于工作目录,切换分支时可能保留(除非冲突)。
- 已提交的修改:属于特定分支的历史,切换分支时 Git 会严格重建工作目录以匹配目标分支
这就是为什么在提交后切换到 master
时文件"消失"的原因------Git 严格遵循分支的历史记录管理文件
4. 重新理解 工作区、暂存区、版本库
-
工作区(Working Directory)
- 所有分支共享同一个物理工作目录,未提交的修改(无论是否
add
)都存在于工作区中,不直接绑定到任何分支 - 工作区的内容会根据当前分支的最新提交动态变化
- 所有分支共享同一个物理工作目录,未提交的修改(无论是否
-
暂存区(Staging Area) :同样全局共享,
git add
后的内容属于暂存区,与分支无关。 -
版本库(Repository)
- 提交(
commit
)后,修改会绑定到当前分支的历史记录中,不同分支的提交历史是独立的 - 提交后的内容属于分支的历史记录,与工作区无关
- 提交(
但是看到这里,也许大家也会有点问题,就是当 commit 之后,那么产生的文件在两个分支下不就内容不一样了吗,此时它两也能算得上全局共享嘛???
解释如下:
假设有一个文件 file.txt
,初始状态如下:
步骤 1:在 master
分支创建文件并提交
bash
git checkout master
echo "Hello from master" > file.txt
git add file.txt
git commit -m "Add file in master"
此时:
master
分支的提交历史包含file.txt
。- 工作区中的
file.txt
内容是Hello from master
。
步骤 2:创建 dev
分支并修改文件
bash
git checkout -b dev
echo "Hello from dev" > file.txt
git add file.txt
git commit -m "Update file in dev"
此时:
dev
分支的提交历史包含修改后的file.txt
(内容是Hello from dev
)。master
分支的提交历史仍为旧版本(内容是Hello from master
)。
步骤 3:切换回 master
分支查看文件
bash
git checkout master
cat file.txt
Hello from master
关键现象解释
- 为什么切换分支后文件内容变了?
- Git 会根据目标分支的最新提交重建工作区。
master
分支的最新提交是原始版本,而dev
分支的最新提交是修改后的版本。
- 工作区是"共享"的吗?
- 物理上共享:所有分支操作同一块磁盘区域。
- 逻辑上隔离:切换分支时,Git 会覆盖工作区内容,使其匹配目标分支的提交。
- 你可以理解为:工作区是"共享的舞台",但每次切换分支时,Git 会更换舞台上的"布景"。
- 提交后的文件到底属于谁?
- 提交后的内容属于分支的历史记录,不直接绑定到工作区。
dev
分支的提交历史中有一个新版本的file.txt
,而master
分支的提交历史中仍是旧版本。
全局共享 vs 分支独立
区域 | 是否全局共享? | 是否受分支切换影响? |
---|---|---|
工作区 | ✅ 是(所有分支共享同一物理目录) | ✅ 是(切换分支时内容会被覆盖) |
暂存区 | ✅ 是 | ✅ 是(切换分支时可能被覆盖) |
版本库(提交) | ❌ 否(每个分支有独立提交历史) | ❌ 否(提交历史永久绑定到分支) |
疑问解决
-
你的问题:
"commit 之后,产生的文件在两个分支下内容不一样了,此时它们还能算全局共享吗?"
-
答案:
- 工作区本身是共享的,但它的内容会根据当前分支的提交历史动态变化。
- 提交后的差异是分支历史隔离的结果,而不是工作区本身的隔离。
- 你可以想象工作区是一个"画布",而不同分支是"不同的画作版本"。每次切换分支时,Git 会擦掉当前画布上的内容,重新绘制目标分支对应的画作。
总结
- 工作区共享:所有分支操作同一块磁盘区域,但内容由当前分支的提交历史决定。
- 提交隔离:不同分支的提交历史独立,切换分支时 Git 会严格按目标分支的历史重建工作区。
- 看似矛盾的现象 :工作区是共享的,但提交后的文件在不同分支下内容不同,这正是 Git 分支机制的核心设计------通过动态覆盖工作区内容,实现高效的多分支协作。
