branch指针 + fast-forward/3-way merge + rebase vs merge + 冲突解决 · SVG可视化
<<<<<<< 是Git在说:"这里有两份不同的修改,你来定!"
git fetch + git rebase origin/main;2. 小步提交,别攒一大堆;3. 不同人改不同文件/模块;4. 沟通!改动公共文件前先告知。
分支就是一个指向commit的指针,存储在 .git/refs/heads/ 下的文本文件中,内容只有40个字符(commit的SHA-1)。创建分支=新建一个文件,O(1)操作。删除分支=删文件。切换分支=修改HEAD指向。
Fast-forward:当前分支(如main)没有新commit,合并只是把main指针移动到feature的位置。不产生新commit,历史是线性的。
三方合并:两个分支都有新commit。Git找共同祖先,做3-way diff,自动合并不同改动,产生merge commit(有2个parent)。
merge:保留完整历史(含分叉),不改commit SHA,产生merge commit。适合已push的共享分支。
rebase:改写历史变线性,不产生merge commit,但commit SHA会变。适合本地未push的分支。
核心原则:不要rebase已push的commit(会让别人的历史混乱)。
因为commit的SHA-1计算包含了parent字段。rebase把C3"移植"到C4之后,C3的parent从C2变成了C4。parent变了→SHA-1就变了→这是一个"新"commit。虽然代码改动一样,但Git认为它是不同的对象。
普通commit只有1个parent。merge commit有2个parent(分别指向两个被合并的分支的最新commit)。用 git cat-file -p <merge-commit> 可以看到两行parent。git log --graph 会显示分叉和合并线。
两个分支修改了同一个文件的同一行,且改动内容不同,Git无法自动决定用哪个版本。
解决步骤:1.打开冲突文件 2.选择保留哪个版本(或合并两者) 3.删除 <<<<<<</=======/>>>>>>> 标记 4.git add 5.git commit
想放弃合并:git merge --abort
cherry-pick = 从别的分支"摘"一个commit过来,应用到当前分支。
和merge的区别:merge把整个分支合过来,cherry-pick只选一个commit。
和rebase的区别:rebase把整条分支"移植"过来,cherry-pick只挑一个。
典型场景:hotfix修了一个bug,需要同时应用到main和release分支。
HEAD直接指向一个commit而不是分支名,就是detached HEAD状态。常见于 git checkout <SHA> 或 git checkout v1.0。
在这个状态下做的commit,切走后没有分支指着它,可能被GC清理掉。
处理:在当前位置创建新分支 git switch -c new-branch,或者如果只是查看代码,看完切回分支即可。
Git三原则:① 提交信息写清楚为什么改而不是改了什么 → ② 永远不在共享分支force push → ③ 合并前先rebase保持线性历史