Skip to content

Git 核心概念与操作总结

1. 本地 Commit 本质

  1. IDEA 代码自动保存:仅保存文件内容到本地文件夹,无版本记录、无法回退、无修改历史。
  2. Git Commit:对当前代码状态拍摄版本快照,存入本地 .git 文件夹;仅本地可见,不上传远程仓库。
  3. Commit 作用:生成版本记录、可查看历史、支持回退,是 Push 的前置步骤。

2. Commit 与 Push 区别

  • Commit:提交到本地 Git 仓库,仅自己可见。
  • Push:将本地已 Commit 的版本上传到远程仓库(GitHub/Gitee 等),他人可拉取。

3. 查看历史更改操作

  1. 全局提交记录:IDEA 按 Alt+9 打开 Git 面板 → Log 标签,查看所有 Commit、修改文件及差异。
  2. 单个文件历史:右键文件 → Git → Show History,仅查看当前文件修改记录。

4. 代码回退操作

  1. 未 Commit 撤销:右键文件 → Git → Revert,放弃未提交修改。
  2. 版本重置 Reset:右键历史记录 → Reset Current Branch to Here
    • Soft:回退到旧版本,保留未提交代码。
    • Hard:彻底回退,清空未提交改动(谨慎使用)。

5. 签出修订(Checkout)

  1. 含义:选中历史 Commit,将项目代码切换到该时间点的旧版本。
  2. 特点:临时切换查看/排查 Bug,不修改分支历史;处于游离状态,可随时切回原分支。
  3. 与 Reset 区别:签出仅临时查看;重置抹除后续提交,回退版本。

6. 分支核心概念

  1. 分支类比:main/master 为主干,分支为独立树枝。
  2. 特性:从某分支新建分支,初始代码与原分支一致;分支可无限嵌套,可在已有分支上建子分支。
  3. 用途:独立开发新功能/修复 Bug,不影响主分支;完成后合并回主分支。

7. 多人分支开发冲突规则

  1. 冲突触发:两人从 main 建分支,修改同一文件同一行;一人先合并到 main,另一人合并时触发冲突。
  2. 冲突机制:Git 不自动覆盖,暂停合并,等待手动处理。
  3. 处理方式:保留自身代码、保留他人代码、手动合并兼容双方代码。
  4. 无冲突场景:功能类似但修改不同文件/行,Git 自动合并,两份代码均保留。
  5. 规避方式:提前划分功能、定期拉取 main 最新代码、避免多人改同一核心文件。

GitHub分支的签出,合并与变基

一、签出(Checkout)

签出 = 切换分支,是 Git 中最基础的操作。

  • 作用:让你的本地工作区切换到指定分支,切换后可以正常查看、修改、提交代码,并非 “只读状态”。

  • 关键:切换后,你的所有修改都会保存在当前分支上,和其他分支互不影响。

二、合并(Merge)

合并 = 将一个分支的代码,完整合并到另一个分支,是最常用的分支整合方式。

  • 适用场景:将功能分支的开发成果合并到主分支,或合并两个独立的分支历史。

  • 示例(合并 master 到 main,处理无关历史):

  • 特点:

    • 会保留两个分支的提交历史,提交线会出现分叉,形成清晰的 “合并节点”。
    • 遇到文件冲突时,需要手动解决冲突后再提交。

三、变基

变基(Rebase)= 把你当前分支的提交,“搬家”到目标分支的最新位置。

不是把两个分支变成一样,而是把你的提交线接在别人的尾巴上,让历史变成一条直线


假设:

  • main 是主干(别人/远程最新代码)
  • master 是你写的功能分支(你的提交)

不变基(直接合并)

历史会变成:

main ---A---B
            \
master ------C---D

两条线,有分叉。

变基之后

你的 C、D 提交会被复制、搬家,接到 main 最后面:

main ---A---B---C---D

变成一条干净直线!


那变基的作用

变基 = 重新摆放你的提交,让分支历史变成一条直线,更干净。

它不会把两个分支变成一样,而是:

  • 保留你写的代码

  • 保留你的提交记录

  • 只是把提交线接得更整齐记忆口诀


总结

  1. 签出 = 切换分支
  2. 合并 = 把代码合进来
  3. 变基 = 把提交排成一条直线

Merge与Rebase的区别

所以变基是让合并历史保持直线,merge是出现分叉的合并,但是两者不是都要解决冲突问题吗,所以使用哪个都差不多把,我都要对冲突的地方进行选择,是保留我自己的还是保留其他人的

无论是变基(rebase)还是合并(merge),当两个分支修改了同一个文件的同一区域时,都必须手动解决冲突。从“解决冲突”这个动作来说,两者确实都要做选择(保留自己的还是对方的,或者混合)。

但它们在冲突出现的次数、解决流程、最终历史形态上有明显区别,并不是完全“差不多”。下面帮你对比一下,方便你根据实际情况选择。

1. 冲突解决的“次数”不同

  • merge:只在最后合并的那一刻解决一次冲突。
    你执行 git merge recover-route,如果两个分支有冲突,Git 会直接停下来,让你一次性处理好所有冲突文件,然后提交一个合并提交。一个冲突只处理一次

  • rebase:会将 recover-route 上的每个提交依次重放到 main 上
    如果第一个提交有冲突,你解决 → 继续;第二个提交可能又出现冲突(因为上下文变了),再解决 → 继续……如果有 N 个提交都涉及冲突区域,你就要解决 N 次冲突。这很烦人,尤其当你不熟悉每个提交的具体改动时。

举例:你在 recover-route 上有 5 个提交,每个都修改了 MqConstants.java 的不同行。rebase 时会逐个应用这 5 个提交,每个都可能冲突,你要重复解决 5 次。merge 只会在最终合并时比较两边的最终版本,冲突可能只出现一次(或者更少)。

2. 解决冲突时的“基准”不同

  • merge:你看到的是“当前 main 的最终内容” 与 “recover-route 的最终内容” 之间的差异。解决时只需要关心最终代码应该长什么样

  • rebase:你看到的是“当前 main 的最新内容” 与 “recover-route 上某一个历史提交” 之间的差异。你可能需要回忆或理解那个历史提交的意图,解决起来更烧脑。

3. 历史形状不同(这是你关心的)

  • merge:产生一个分叉点 + 一个合并提交(Merge branch 'recover-route' into main),历史呈分叉状(非直线)。
    优点:真实保留了分支的开发过程,明确知道合并点。
    缺点:历史看起来有点“乱”。

  • rebase:将 recover-route 的提交“剪下来贴到 main 的末尾”,得到一条直线。
    优点:历史干净,像串行开发。
    缺点:改变了提交的哈希值,如果已经推送到远程,需要强制推送,可能影响他人。

4. 所以到底用哪个?

对于你的具体目标:把 recover-route 的所有提交历史并入 main,然后删除 recover-route

  • 推荐用 merge

    • 一次解决冲突,简单直接。
    • 即使历史有分叉,对你个人项目来说完全没问题。
    • 不会重写提交,安全省心。
  • 不推荐用 rebase(除非你特别偏爱直线历史):

    • 可能需要反复解决冲突,浪费时间。
    • 既然你已经在 rebase 过程中遇到冲突且感到困惑,说明对 rebase 流程还不够熟悉,贸然继续容易出错(比如误解决导致代码丢失)。

一个折中做法(既直线又不反复冲突):

如果你想获得直线历史,但不想多次解冲突,可以这样做:

bash
# 1. 切换到 main
git checkout main

# 2. 使用 squash merge(把整个分支压成一个提交再合并)
git merge --squash recover-route
# 此时所有改动都在暂存区,但不会自动提交,也没有分支历史

# 3. 手动解决一次冲突(如果有的话)

# 4. 提交成一个新的提交
git commit -m "Add recover-route features"

这样 main 上只多了一个提交,包含了 recover-route 的所有改动,历史是直线的。但缺点是你丢失了 recover-route 上的多个小提交记录(它们被压成一个了)。如果你不介意,这也很干净。

总结

两者都要解决冲突,但 merge 通常更简单(一次冲突),rebase 可能更麻烦(多次冲突)。
除非你非常想要一条直线历史并且愿意接受多次解决冲突的代价,否则对于“合并分支然后删除”的场景,直接用 merge 就好

你目前已经在 rebase 冲突中,可以用 git rebase --abort 退出,然后改用 git merge recover-route
如果 merge 时也遇到冲突,解决一次,完成合并,然后删除分支。这样最符合你的需求。

这里不应该是只能选择接受我的还是其他人的更改吗,为什么还能选择合并,不是不能合并吗

你观察得很细致!这里的“合并(M)”选项并不是说 Git 会自动帮你合并两边的冲突(那是不可能的),而是指允许你手动编辑冲突区域,灵活地保留双方的部分修改,而不是只能“全要你的”或“全要他的”。

具体解释一下:

1. “合并”选项的真实含义

在冲突界面中:

  • “接受您的更改(Y)”:完全使用 recover-route 分支上的代码(你的)。
  • “接受他们的(T)”:完全使用 main 分支上的代码(他人的)。
  • “合并(M)”手动编辑冲突文件,你可以逐行、逐块地选择保留哪些内容。比如保留你新增的几行,同时也保留对方修改的几行,最终生成一个混合版本

所以不是不能合并,而是你可以“自定义合并”。对于简单的冲突(比如两边改了同一行),你只能二选一;但对于复杂的冲突(比如两边在不同位置新增了代码,或功能互补),你完全可以把两边的有用部分都留下来。

2. 为什么你感觉“不能合并”?

你可能从 Git 命令行学习时,通常解决冲突就是打开文件,删除 <<<<<<<, =======, >>>>>>> 标记,然后手动编辑成想要的代码。这个手动编辑的过程,本质上就是**“合并”**。图形界面里的“合并(M)”按钮,就是帮你自动打开编辑器(或内置合并工具),让你去做这个手动操作。

3. 什么时候该用“合并”?

  • 两边的修改并不直接冲突的行,而是互补的(比如你在 A 方法加了代码,对方在 B 方法加了代码),你需要同时保留。
  • 两边的修改逻辑上都需要,但需要手动调整顺序或语法。
  • 你只想从对方那里取一小部分代码,而不是整个文件。

例如:

java
<<<<<<< HEAD
// 你的代码:增加了优惠券有效期校验
if (coupon.getExpireTime().before(now)) { ... }
=======
// 对方的代码:增加了用户黑名单检查
if (user.isBlacklisted()) { ... }
>>>>>>> main

此时你既需要有效期校验,又需要黑名单检查,那么“合并”就能让你把两段代码都保留下来(适当调整顺序)。

4. 总结

  • “接受您的/他们的”:简单粗暴,全盘采用一方。
  • “合并”:精细控制,手动混合双方代码(或者甚至都删掉重写)。

你现在看到这个界面,说明冲突文件较多(12个文件)。如果每个文件的冲突都不大且明确该用谁的,直接选“接受您的”或“接受他们的”更快。如果有些文件需要两边都保留一部分,那就选“合并”手动处理。

关键: 无论你选哪个选项,最终目标都是生成一个可编译、逻辑正确的代码版本。如果你不确定,先选“合并”,查看冲突区域,再做出合理选择。

还原提交(Revert Commit)

  • 作用:创建一个新的提交,该提交的内容刚好是选中提交的反向修改(即撤销那个提交的所有改动)。
  • 用途:安全地“撤销”一个已经推送到远程的提交,因为不重写历史,不影响其他人。常用于线上修复

这里撤销了提交那就没有修改的代码了,如果已经推送到远程仓库,其他人也已经基于这个代码进行开发了,这时候还原不是就会报错吗

你提出的这个问题非常好,它触及了 Git 协作中的一个关键区别:“撤销历史” vs “反向提交”

1. 还原(Revert)不会删除历史,而是新增一个“反向”提交

假设远程仓库有一条提交历史:
A → B → C(其中 C 是你想撤销的提交)

执行 git revert C 后,Git 会自动计算 C 相对于 B 的所有改动,然后生成一个完全相反的改动,并作为一个新的提交 C' 附加在历史末尾。
结果:A → B → C → C'

  • C' 的内容刚好是撤销 C 的改动。所以从最终代码看C' 让代码回到了 B 的状态。
  • C 这个提交依然存在,历史没有被重写。

2. 其他人基于包含 C 的代码开发,会发生什么?

假设你的同事小张在推送 C 之后,克隆了仓库(或拉取了 C),然后基于 C 继续开发了新的提交 D
他的本地历史:A → B → C → D

现在你执行了 git revert C 并推送到远程,远程历史变成:A → B → C → C'

小张执行 git pull 时,Git 会尝试合并两边的历史:

  • 本地:A-B-C-D
  • 远程:A-B-C-C'

合并时会创建一个合并提交 M,包含 DC' 的改动。
如果 DC' 修改了相同的文件/代码行,就会产生冲突,需要小张手动解决。

不是“报错”,而是正常的合并冲突,就像你把两个分支合并时一样。冲突解决后,小张的代码就能正常推送到远程。

3. 为什么还说 revert 是“安全”的?

  • 不破坏历史:已经推送的 C 仍然存在,其他人的本地提交(如 D)不需要变基或强制推送,可以直接 pull 并解决冲突。
  • 可追溯:所有人都能看到“有人撤销了 C”这个操作记录(C')。
  • 可恢复:如果将来又想恢复 C,可以 revert C'(撤销撤销),非常灵活。

相比之下,如果用 git reset 删除 C 然后强制推送,其他人再拉取就会遇到“历史被重写”的错误(git pull 会提示分叉或拒绝),必须用 --force 覆盖本地历史,容易导致他人丢失工作——那才是真正的“报错”和危险。

4. 什么时候适合用 revert?

  • 已经推送到公共分支(如 maindevelop),且可能有其他人基于该分支开发。
  • 需要保留完整的操作审计记录(企业/开源项目)。
  • 你只是觉得某次提交的功能不再需要,但不想完全抹掉它的存在

总结

  • git revert 不会导致其他人报错,只会可能引入合并冲突(如果他人修改了相同代码)。
  • 冲突是 Git 日常协作的一部分,解决即可。
  • revert 的最大价值:在不伤害协作流程的前提下,安全地撤销已推送的提交

如果你希望彻底删除某个提交(且确信没有其他人依赖它),可以用 git reset + push --force,但那只适用于未推送的本地分支你个人独占的分支

Git图形界面中对于提交的分支操作菜单

这个菜单是在 Git 图形界面(如 IntelliJ IDEA、GitKraken、SourceTree 等)中,右键点击某个提交(commit)时弹出的操作菜单。下面逐项解释每个功能的作用和典型使用场景。


1. 复制修订号

  • 快捷键Ctrl+Alt+Shift+C
  • 作用:将该提交的哈希值(40 位十六进制字符串,或短哈希)复制到剪贴板。
  • 用途:方便在终端、聊天工具或文档中引用这个提交,比如执行 git show <hash>git cherry-pick <hash>

2. 创建补丁...

  • 作用:基于选中的提交生成一个补丁文件(.patch)。
  • 用途:把这次提交的改动打包成一个文件,可以通过邮件或其他方式发送给别人,对方用 git applygit am 就能应用你的修改。常用于不共享仓库的协作或代码审查。

3. 优选(Cherry-pick)

  • 作用:将选中的提交复制到当前分支上,生成一个新的提交(内容相同,但哈希值不同)。
  • 用途:只想从其他分支引入某一个或某几个提交(而不是整个分支)。例如,你发现 feature 分支上的一个 bug 修复很有用,但不想合并整个分支,就用 cherry-pick 把它拿过来。

4. 签出修订(Checkout revision)

  • 作用:让当前工作目录切换到该提交的状态,进入 detached HEAD(分离头指针)状态。
  • 用途:临时查看项目在某个历史提交时刻的样子,用来调试或回顾。注意:此时不能直接提交新代码(除非新建分支)。

5. 在修订版中显示仓库

  • 作用:在版本库浏览器(或文件树)中以该提交为根节点展示文件结构。
  • 用途:浏览当时的代码快照,看看哪些文件存在、内容如何。

6. 与本地比较

  • 作用:将选中的提交与当前工作目录(或当前分支最新代码)进行差异对比。
  • 用途:查看自那次提交以来,代码发生了哪些变化。

7. 将当前分支重置到此处...

  • 作用:把当前分支的指针移动到这个提交,并选择重置模式(soft / mixed / hard)。
  • 用途
    • 撤销多个提交:想回到过去某个干净的状态。
    • 丢弃之后的提交:用 --hard 会永久删除该提交之后的所有改动。
    • 保留改动为未提交:用 --soft--mixed 可以重新整理提交。

8. 还原提交(Revert Commit)

  • 作用:创建一个新的提交,该提交的内容刚好是选中提交的反向修改(即撤销那个提交的所有改动)。
  • 用途:安全地“撤销”一个已经推送到远程的提交,因为不重写历史,不影响其他人。常用于线上修复。

9. 撤消提交...

  • 作用:相当于 git reset 的快捷方式,将当前分支回退到该提交之前,并丢弃或暂存后续改动。
  • 用途:后悔最近几个提交,想重新修改后再提交。注意:如果已经推送过,不要使用(除非是个人分支)。

10. 编辑提交消息...

  • 作用:修改选中提交的提交说明文字。
  • 用途:发现自己写错了提交消息(比如错别字),需要纠正。这通常会改变提交的哈希值,因此只适用于尚未推送的本地提交。

11. Fixup...

  • 作用:将当前选中的提交与前一个提交合并,并丢弃这个提交的消息(只保留前一个的消息)。这是交互式变基(rebase -i)的 fixup 命令的图形化版本。
  • 用途:把一个小修小补的提交“吸收”到它前面的主要提交中,保持历史干净。例如:你提交了“添加登录功能”,然后又提交了“修复登录功能的拼写错误”,就可以用 Fixup 把后者合并到前者里。

12. 压缩到...(Squash into...)

  • 作用:将当前选中的提交与前一个提交合并,并合并两者的提交消息(让你编辑新的消息)。对应交互式变基的 squash
  • 用途:把多个相关的提交压缩成一个有意义的提交。比如开发一个功能时做了 5 个小提交,最终想合并为 1 个清晰的提交。

13. 删除提交

  • 作用:完全移除选中的提交(及其改动)。一般只能删除最新的几个提交,或者通过变基删除更早的提交。
  • 警告:会丢失代码,且会改变历史。只用于本地未推送的分支。

14. 从这里进行交互式变更...(Interactive Rebase from Here...)

  • 作用:从选中的提交开始,弹出交互式变基的编辑器,让你可以对后续一系列提交执行重排、压缩、编辑、删除等操作。
  • 用途:整理本地分支的历史,比如把多个小提交合并、修改提交顺序、拆分提交等。

15. 推送此前所有提交...

  • 作用:把从该提交及之前的所有提交(即该提交到当前分支顶端的所有提交)推送到远程仓库。
  • 用途:当你想推送某个提交及其之后的工作,但不想推送更早的(很少用),或者配合 git push --force 修改历史后推送。

16. 新建分支...

  • 作用:基于选中的提交创建一个新的分支。
  • 用途:从历史中的某个时间点分支出一个新的开发线,例如发现一个 bug 需要从旧版本开始修复。

17. 新建标记...(New Tag...)

  • 作用:为选中的提交打上一个标签(轻量标签或附注标签)。
  • 用途:标记重要的版本(如 v1.0.0release-2026),便于日后快速引用。

18. 转到子提交 / 向左箭头 / 转到父提交 / 向右箭头

  • 解释:这些是导航功能,用于在提交图中移动。
    • 转到子提交:跳到当前提交的孩子提交(即更晚的提交,如果当前分支是线性的,通常只有一个子节点)。
    • 转到父提交:跳到当前提交的提交(即更早的提交)。
    • 箭头方向:左箭头可能对应“父提交”,右箭头对应“子提交”(具体依赖界面布局)。有的工具用 <> 表示。
  • 用途:快速在提交历史中浏览,不用手动滚动列表。

19. 在浏览器中查看

  • 作用:如果该提交关联了一个远程仓库(如 GitHub、GitLab),会在默认浏览器中打开该提交的页面。
  • 用途:查看完整的 diff、评论、CI 状态等在线信息。

总结:常用操作速查表

操作典型场景
复制修订号需要手动输入哈希时
优选从其他分支单独拿一个提交
签出修订临时查看旧版本代码
重置到此处撤销本地的多个提交(回到过去)
还原提交安全地撤销已推送的提交
编辑提交消息 / Fixup / 压缩整理本地提交历史(保持干净)
交互式变基复杂的提交历史重写(合并、删除、编辑)
新建分支 / 新建标记从某个历史点开始新工作,或标记版本

理解这些功能后,你就能更灵活地管理 Git 历史了。如果你在使用中遇到不确定的,建议先在测试仓库中尝试,避免丢失代码。

这些选项是 Git 图形界面中针对分支 main 的常见操作。下面分别解释:


1. 从 'main' 新建分支...

  • 作用:基于当前 main 分支的最新提交,创建一个新的本地分支(例如 feature-xxx)。新分支与 main 指向同一个提交,之后你可以在新分支上开发,不影响 main
  • 典型场景:开始开发一个新功能或修复 bug,需要从稳定的主分支分叉出去。

2. 显示与工作树的差异

  • “工作树” 是指你当前尚未提交的文件改动(包括已修改、已暂存或新增但未 add 的文件)。
  • 作用:对比 main 分支最新提交的内容当前工作目录中未提交的内容 之间的差异。
    注意: 这不是与“主分支”的差异(主分支就是 main 自己),而是与你本地未提交的改动的差异。
  • 通俗理解:看看你在 main 分支上改了哪些东西但还没提交。

3. 更新

  • 作用:从远程仓库(这里远程仓库是 SmartCourse-Al-Platform)拉取 main 分支的最新提交,并合并到你的本地 main 分支。
    本质上相当于依次执行:
    bash
    git fetch SmartCourse-Al-Platform main   # 下载远程最新提交到本地远程跟踪分支
    git merge SmartCourse-Al-Platform/main   # 合并到当前本地 main
  • 如果本地有未推送的提交,更新可能会产生合并提交或冲突。如果只想下载而不合并,应该用 fetch,但这里的“更新”通常是 pull(fetch + merge)。

4. 推送...

  • 作用:将你本地 main 分支上的所有本地提交上传到远程仓库 SmartCourse-Al-Platformmain 分支。
  • 注意:如果远程分支有本地没有的新提交(即远程领先于本地),推送会被拒绝,需要先执行“更新”拉取并合并/解决冲突。

5. 跟踪分支 'SmartCourse-Al-Platform/main'

  • 作用:设置本地 main 分支跟踪远程分支 SmartCourse-Al-Platform/main
    跟踪关系意味着:

    • 当你在 main 分支上执行 git pull(无参数)时,会自动从该远程分支拉取并合并。
    • 执行 git push(无参数)时,会自动推送到该远程分支。
  • 什么时候需要手动设置:如果你用 git branch 创建了一个新分支,但没有指定 --track,或者本地分支名称与远程分支名称不同,就需要手动建立跟踪。通常 git clone 时,本地的 main 会自动跟踪远程的 main

  • 跟踪分支(tracking branch)并不会让本地分支自动拉取远程的变动。它只是建立了关联关系,让你在执行某些 Git 命令时可以省略参数。

具体来说:

  • 当你设置了跟踪关系后(例如本地 main 跟踪远程 origin/main):

    • 直接执行 git pull(不带参数):Git 就知道要从 origin/main 拉取并合并到当前分支。
    • 直接执行 git push(不带参数):Git 就知道要推送到 origin/main
    • 执行 git status 时,会提示你本地分支领先或落后远程多少提交。
  • 但远程发生变动时,本地并不会自动拉取。 你仍然需要手动执行 git fetchgit pull 来获取远程的新提交。Git 没有内置的“自动同步”功能(除非使用额外的钩子或工具)。

所以“跟踪”是一种便捷配置,不是自动化同步。


6. 重命名...

  • 作用:将当前本地分支 main 重命名为另一个名字(例如 mastertrunk)。
  • 注意:重命名后,如果已经推送过原来的 main 到远程,远程分支不会自动改名,你需要删除旧的远程分支并推送新的分支,或者重新设置跟踪关系。

总结对照表

菜单项核心操作改变历史?影响远程?
从 'main' 新建分支...git branch <new>
显示与工作树的差异git diff HEAD
更新git pull(fetch+merge)可能产生合并提交只拉取
推送...git push上传本地提交
跟踪分支 '...'git branch --set-upstream-to仅设置关联
重命名...git branch -m <new>是(本地分支名)不影响远程内容

希望这能帮你理解这些选项的作用。如果还有疑问,欢迎继续追问。

基于 VitePress 构建