翻译自 Git Out Of Trouble。
搭建练习环境
- 在 Github.com 上创建仓库并 clone 到本地;
- 创建一个新分支,叫作 test;
- 创建一系列 commit,可使用如下脚本: - Bash - for d in {1..6}; do touch file$d.md; git add file$d.md; git commit -m "adding file $d"; done
- PowerShell - for ($d=1; $d -le 6;$d++) { touch file$d.md; git add file$d.md; git commit -m "adding file$d.md"; }
 
创建了太多细碎的 Commit
在开发自己的项目的过程中,可能很多细小的改变也创建了 commit,这使得 commit 历史很繁琐。
还没有 push
如果还没有 push, 可以修改创建的 commit 而无需担心在和协作者开发时出现问题。
- 确保当前分支正确,并输入命令 git log --oneline;
- 在本例中,我们将 adding files 4, 5, and 6 的 commit 合并。所以找到 adding file 3 的 commit 的 SHA-1 值;
- 输入命令 git reset --soft SHA-1,其中 SHA-1 为 adding file 3 的 SHA-1;
- 输入 git status,将会看到 files 4, 5, 6;
- 输入 git commit -m "Adding files 4, 5, and 6";
- 输入 git log --oneline,此时 commit 历史已经修改完成。
已经 push 了
此时再通过 Git 修改 commit 将可能会引起协作上的问题,所以建议使用 Squash and Merge 来修改。
翔一样的 Commit Message
比如单词写错了,写了不该写的东西之类的情况。
还没有 push
首先问自己一个问题,是要修改上一个 commit 还是要修改之前的几个 commits?
修改上一个 commit
- 确认在正确的分支;
- 输入 git log --oneline来确定你想要修改的 commit 在列表的最上面;
- 输入 git commit --amend;
- 输入你想要修改的 message。
修改之前的 commit
- 确认分支并输入 git log --oneline;
- 找到你想要修改的 commit 的前一个 SHA-1,在本例中,使用 add file 4;
- 输入 git reset --mixed SHA-1;
- 现在可以重新 add 和 commit 对 file 5 和 6 的修改了。
已经 push 了
此时需要使用 git push --force-with-lease 命令,该命令可能会对协作者造成严重的问题,如果 message 不是那么烂,可以考虑就那样放着。
首先问自己一个问题,是要修改上一个 commit 还是要修改之前的几个 commits?
修改上一个 commit
- 确认在正确的分支;
- 输入 git log --oneline来确定你想要修改的 commit 在列表的最上面;
- 输入 git commit --amend;
- 输入你想要修改的 message;
- 输入 git push --force-with-lease强行向 remote 同步更改。
修改之前的 commit
- 确认分支并输入 git log --oneline;
- 找到你想要修改的 commit 的前一个 SHA-1,在本例中,使用 add file 4;
- 输入 git rebase -i SHA-1,-i是使用交互模式,会打开编辑器,让你更改rebase脚本;
- 找到你想要修改的 commit,将 pick替换为e或是edit,当更改完成后关闭编辑器,回到终端;
- rebase 操作会停在第一个被编辑的 commit 处,输入 git commit --amend修改该 commit message;
- 关闭编辑器,输入 git rebase --continue;
- 重复前两步操作;
- 当全部 commit 修改完,输入 git push --force-with-lease。
提交到了错误的分支
突然发现提交到了 master 分支而不是用来开发的分支。
还没有 push
- 确认现在处于提交错误 commit 的分支,在本例中,已经提交了几个 commits 在 test分支中;
- 输入 git log --oneline,找到错误 commit 之前的一个 SHA-1 值,本例中,假定 file 5 本来应该提交到另一个分支上;
- 输入 git reset --mixed SHA-1,其中 SHA-1 为 adding file 4 的 SHA-1;
- 输入 git status,此时 file 5 和 6 都在工作目录中;
- 输入 git checkout -b correct,创建一个名为correct的新分支并检出;
- 输入 git status,此时 file 5 和 6 应该还在工作目录中;
- 添加 file 5 和 6,git add file*;
- 输入 git status,现在 file 5 和 6 已被成功添加,等待 commit;
- 输入 `git commit -m "Adding file 5 and 6"。
已经 push 了
首要任务:将 master 分支修好
- 在 master分支中输入git log --oneline,找到需要移除的 commits,在本例中,定为 adding file 3;
- 输入 git revert SHA-1,可以 revert 多个 commits,用空格分开;
- 修改 revert commit message,或者可以直接关闭编辑器;
- 输入 git push。
重建分支
现在 master 分支已经修好,创建一个新分支来整理错误的 commits。
- 创建分支,git checkout -b new-branch,或者检出到已创建好的分支;
- 输入 git reflog,找到需要重建的 commits 的 SHA-1;
- 输入 git cherry-pick SHA-1,可以重建多个 SHA-1 值,用空格分开;
- 输入 git push -u origin new-branch。
意外 commit
还没有 push
- 确认在正确的分支,并输入 git log --oneline;
- 在本例中,假定我们并不想提交 file 6,所以找到 adding file 5 的 SHA-1;
- 输入 git reset --mixed SHA-1;
- 输入 git status,现在 file 6 将出现在工作目录中。
已经 push 了
首先需要 revert commits。
- 确认在正确的分支,并输入 git log --oneline;
- 找到错误的 commit 的 SHA-1,本例中,使用 adding file 4;
- 输入 git revert SHA-1;
- 输入 revert message,或保留默认直接关闭编辑器;
- 输入 git push。
恢复工作。
- 输入 git log --oneline,找到 revert commit,如果是默认的,应该为Revert "adding file 4";
- 输入 git revert SHA-1,SHA-1值为 revert commit;
- 完成当前需要继续的工作;
- 输入 git add;
- 输入 git commit --amend,将现在的修改合并到之前的 commit 中;
- 输入正确的 commit message;
- 输入 git push同步工作。
推倒重来
还没有 push
首先问自己一个问题:要彻底重来还是有一部分可以凑合留着?
彻底重来
- 检出到 master 分支,git checkout master;
- 删除旧分支,git branch -D BRANCH-NAME。
有一部分可以留着
如果你想看到一些炫酷的操作,请在文件浏览器中打开仓库(Finder 等),然后放在能看到的地方。
- 确认在正确的分支,并输入 git log --oneline;
- 找到你想要保留的最后一次 commit 的 SHA-1 值。在本例中,假定 file 1 和 2 是要保留的,其他部分不要,所以找到 adding file 2 的 SHA-1 值;
- 输入 git reset --hard SHA-1,如果你打开了文件浏览器,你会发现已经出现了一些炫酷的操作!
- 输入 git status和ls,注意到现在只有 file 1 和 2 仍保留着。
- 输入 git log --oneline,adding file2.md 之后的 commits 都消失了。
等等,我好像干了不该干的事!
一时冲动,你「意外」地删除了一些文件。
找回一个文件
- 输入 git reflog;
- 找到 adding file 6 commit 的 SHA-1 值;
- 输入 git cherry-pick SHA-1;
- 输入 git log --oneline和ls,file 6 已经回来了。
全部找回
- 输入 git reflog;
- 找到 adding file 6 commit 的 SHA-1 值;
- 输入 git reset --hard SHA-1;
- 输入 git log --oneline,所有文件都出现了。
已经 push 了
首先问自己一个问题:要彻底重来还是有一部分可以凑合留着?
彻底重来
- 检出到 master 分支,git checkout master;
- 输入 git push origin :BRANCH-NAME或者git push --delete BRANCH-NAME来删除远程的分支;
- 输入 git fetch --prune来删除远程跟踪分支;
- 输入 git branch -D BRANCH-NAME删除分支的本地拷贝。
有一部分可以留着
- 确认在正确的分支,并输入 git log --oneline;
- 找到你想要保留的最后一次 commit 的 SHA-1 值。在本例中,假定 file 1 和 2 是要保留的,其他部分不要,所以找到 adding file 2 的 SHA-1 值;
- 输入 git reset --hard SHA-1;
- 输入 git status和ls,注意到现在只有 file 1 和 2 仍保留着;
- 输入 git push --force。