使用 Git 会经常遇到各种奇怪需求,在这里分享一下,可能会持续更新?

改动历史提交记录

前排提醒:修改历史记录会对这条记录之后的 commit 都产生影响,具体表现为哈希值被修改,所以此功能不应该被滥用。如果你修改了已经推送到云端的内容,请尽量在 push 命令后面加上 --force-with-lease

你可能会在 rebase 过程中遇到冲突问题,这是正常现象,你需要对冲突的部分进行手动调整,然后执行 git addgit rebase --continue

如果你想对分支内的某条记录进行修改的话,可以先复制这个 commit 前一条记录的哈希值,然后输入下面的命令:

1
git rebase -i <hash>

就会弹出一个窗口,展示了这个提交之后的所有 commit 列表,注意不包含他自身。至于为什么,可以从 rebase 的字面意思来理解。如果我们填入的记录就是所谓的 base 的话,这个命令要做的就是改变这个 base 后面的内容,他本身自然就不需要被包含。

如果你正在跟着做的话,你可能会看到与下图类似的结构,接下来的操作都是通过将其中的 pick 改为其他的指令,必要时更改记录的位置顺序实现的。

除了直接复制 hash 值,我们还可以使用相对引用。比如你想更改的是第五个记录,只需将前面命令的 <hash> 部分更改为 HEAD~5,HEAD 是 0-base 的,所以他的含义与第六条记录的 hash 值等效。最终的效果自然也一样。

那么上面这些基本已经覆盖了 99% 的场景了,但是如果我们想更改这个仓库的第一个,也就是最老的记录,他并没有父记录,该怎么办?可以这样:

1
git rebase -i --root

改历史注释

如果你只想修改最后一次提交的内容,那么很幸运,直接输入这行命令就能便捷的修改

1
git commit --amend -m <commit>

然而如果这个 commit 的前面存在别的提交,就要用到前面讲到的 rebase 方法,将目标提交前面的 pick 删掉,改为 reword 或简写 r,关闭窗口,然后在新弹出的窗口中进行修改操作。

改历史内容

仍然是 rebase, 将 pick 改为 edit 或 e,就可以修改内容

修改完成后输入下面的命令即可:

1
2
3
git add .
git commit --amend --no-edit
git rebase --continue

具体含义就是将修改内容合并到最后一个 commit,并继续 rebase 流程

合并提交记录

将 pick 改为 squash 或 s,就可以合并到他的前一条记录中。如果想要合并的两条记录并不相邻呢?我们可以剪切较新的那条记录,插入到目标记录的下面,然后将 pick 改为 squash 即可

squash 是强制要求我们修改注释的,如果我们不想修改注释,可以使用 fixup 指令,他可以只将内容并入目标记录,不影响注释

如果我们一开始就明确了这个记录应该被合并,可以使用以下命令:

1
2
git commit --fixup=<hash>
git rebase -i --autosquash <前一个记录的hash>

这样 git 就能帮我们自动排好顺序并标注 fixup,我们只需要点关闭就好了

删除提交记录

将 pick 改为 drop 或 d

拆分提交记录

这个需求没有现成的轮子,需要配合其他的指令。先把目标记录改成 edit,然后输入:

1
git reset HEAD~1

将代码全部恢复到工作区,然后挑选文件分批提交即可。如果需要对某个文件进行更细致的更改,可以自己手动调整

单独恢复某个文件

如果只想将单独的文件回退到某个 commit 的状态,可以使用 restore 命令,使用方法非常简单:

1
git restore --source=<hash> <file>

改动分支基点

如果你因为某些原因想要将分支基点转移到一个新的节点,rebase 同样能胜任这个工作。

步骤也很简单,首先切换到需要变基的分支,然后执行 rebase 命令即可,以转移到 master 最新节点为例:

1
2
git checkout <branch>
git rebase master

将某个记录应用到新的分支

切换到这个分支,然后使用 cherry-pick:

1
2
git checkout <branch>
git cherry-pick <hash>

回退记录

如果你在操作的时候遇到了问题,且比较难解决时,比较好的办法就是回退。我们可以输入:

1
git reflog

来查看修改记录,复制目标记录的 hash 值后输入:

1
git reset --hard <hash>

就顺利的完成了一次回退