选自dev.to 做者:Lydia Halliecss
机器之心编译 参与:Panda、杜伟git
原文连接:https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1程序员
git merge、git rebase、git reset、git revert、git fetch、git pull、git reflog……你知道这些 git 命令执行的到底是什么任务吗?若是你还有些分不清楚,那千万不能错过这篇文章。在本文中,熟知 JavaScript、TypeScript、GraphQL、Serverless、AWS、Docker 和 Golang 的 21 岁年轻软件顾问 Lydia Hallie 经过动图形式直观地介绍了这些经常使用 git 命令的工做过程,包你过目不忘。web

尽管 Git 是一款很是强大的工具,但若是我说 Git 用起来简直是噩梦,大多数人也会认同个人说法。我发如今使用 Git 时,在头脑里可视化地想象它会很是有用:当我执行一个特定命令时,这些分支会如何交互,又会怎样影响历史记录?为何当我在 master 上执行硬重启,force push 到原分支以及 rimraf 咱们的 .git 文件夹时,个人同事哭了?
我以为建立一些最经常使用且最有用的 Git 命令的可视化示例会是一个完美的用例!下面我将介绍的不少命令都有可选参数——你可使用这些参数来改变对应命令的行为。而个人示例只会涵盖命令的默认行为,而不会添加(或添加太多)可选配置!

拥有多个分支是很方便的,这样能够将不一样的新修改互相隔离开,并且还能确保你不会意外地向生产代码推送未经许可或破损的代码修改。但一旦这些修改获得了批准许可,咱们就须要将其部署到咱们的生产分支中!
可将一个分支的修改融入到另外一个分支的一种方式是执行 git merge。Git 可执行两种类型的合并:fast-forward 和 no-fast-forward。如今你可能分不清,但咱们立刻就来看看它们的差别所在。
在当前分支相比于咱们要合并的分支没有额外的提交(commit)时,能够执行 fast-forward 合并。Git 很懒,首先会尝试执行最简单的选项:fast-forward!这类合并不会建立新的提交,而是会将咱们正在合并的分支上的提交直接合并到当前分支。

完美!如今,咱们在 dev 分支上所作的全部改变都合并到了 master 分支上。那么 no-fast-forward 又是什么意思呢?
若是你的当前分支相比于你想要合并的分支没有任何提交,那固然很好,但很遗憾现实状况不多如此!若是咱们在当前分支上提交咱们想要合并的分支不具有的改变,那么 git 将会执行 no-fast-forward 合并。
使用 no-fast-forward 合并时,Git 会在当前活动分支上建立新的 merging commit。这个提交的父提交(parent commit)即指向这个活动分支,也指向咱们想要合并的分支!

没什么大不了的,完美的合并!如今,咱们在 dev 分支上所作的全部改变都合并到了 master 分支上。
尽管 Git 可以很好地决定如何合并分支以及如何向文件添加修改,但它并不老是能彻底本身作决定。当咱们想要合并的两个分支的同一文件中的同一行代码上有不一样的修改,或者一个分支删除了一个文件而另外一个分支修改了这个文件时,Git 就不知道如何取舍了。
在这样的状况下,Git 会询问你想要保留哪一种选择?假设在这两个分支中,咱们都编辑了 README.md 的第一行。
若是咱们想把 dev 合并到 master,就会出现一个合并冲突:你想要标题是 Hello! 仍是 Hey!?
当尝试合并这些分支时,Git 会向你展现冲突出现的位置。咱们能够手动移除咱们不想保留的修改,保存这些修改,再次添加这个已修改的文件,而后提交这些修改。

完成!尽管合并冲突每每很让人厌烦,但这是合理的:Git 不该该瞎猜咱们想要保留哪些修改。
微信
咱们刚看到可经过执行 git merge 将一个分支的修改应用到另外一个分支。另外一种可将一个分支的修改融入到另外一个分支的方式是执行 git rebase。
git rebase 会将当前分支的提交复制到指定的分支之上。

完美,如今咱们在 dev 分支上获取了 master 分支上的全部修改。
变基与合并有一个重大的区别:Git 不会尝试肯定要保留或不保留哪些文件。咱们执行 rebase 的分支老是含有咱们想要保留的最新近的修改!这样咱们不会遇到任何合并冲突,并且能够保留一个漂亮的、线性的 Git 历史记录。
上面这个例子展现了在 master 分支上的变基。可是,在更大型的项目中,你一般不须要这样的操做。git rebase 在为复制的提交建立新的 hash 时会修改项目的历史记录。
若是你在开发一个 feature 分支而且 master 分支已经更新过,那么变基就很好用。你能够在你的分支上获取全部更新,这能防止将来出现合并冲突。
交互式变基(Interactive Rebase)
在为提交执行变基以前,咱们能够修改它们!咱们可使用交互式变基来完成这一任务。交互式变基在你当前开发的分支上以及想要修改某些提交时会颇有用。
在咱们正在 rebase 的提交上,咱们能够执行如下 6 个动做:
很棒!这样咱们就能彻底控制咱们的提交了。若是你想要移除一个提交,只需 drop 便可。

若是你想把多个提交融合到一块儿以便获得清晰的提交历史,那也没有问题!

交互式变基能为你在 rebase 时提供大量控制,甚至能够控制当前的活动分支。
当咱们不想要以前提交的修改时,就会用到这个命令。也许这是一个 WIP 提交或者多是引入了 bug 的提交,这时候就要执行 git reset。
git reset 能让咱们再也不使用当前台面上的文件,让咱们能够控制 HEAD 应该指向的位置。
软重置
软重置会将 HEAD 移至指定的提交(或与 HEAD 相比的提交的索引),而不会移除该提交以后加入的修改!
假设咱们不想保留添加了一个 style.css 文件的提交 9e78i,并且咱们也不想保留添加了一个 index.js 文件的提交 035cc。可是,咱们确实又想要保留新添加的 style.css 和 index.js 文件!这是软重置的一个完美用例。

输入 git status 后,你会看到咱们仍然能够访问在以前的提交上作过的全部修改。这很好,这意味着咱们能够修复这些文件的内容,以后再从新提交它们!
硬重置
有时候咱们并不想保留特定提交引入的修改。不一样于软重置,咱们应该再也无需访问它们。Git 应该直接将总体状态直接重置到特定提交以前的状态:这甚至包括你在工做目录中和暂存文件上的修改。

Git 丢弃了 9e78i 和 035cc 引入的修改,并将状态重置到了 ec5be 的状态。
另外一种撤销修改的方法是执行 git revert。经过对特定的提交执行还原操做,咱们会建立一个包含已还原修改的新提交。
假设 ec5be 添加了一个 index.js 文件。但以后咱们发现其实咱们不再须要由这个提交引入的修改了。那就还原 ec5be 提交吧!

完美!提交 9e78i 还原了由提交 ec5be 引入的修改。在撤销特定的提交时,git revert 很是有用,同时也不会修改分支的历史。
当一个特定分支包含咱们的活动分支须要的某个提交时,咱们对那个提交执行 cherry-pick!对一个提交执行 cherry-pick 时,咱们会在活动分支上建立一个新的提交,其中包含由拣选出来的提交所引入的修改。
假设 dev 分支上的提交 76d12 为 index.js 文件添加了一项修改,而咱们但愿将其整合到 master 分支中。咱们并不想要整个 dev 分支,而只须要这个提交!

如今 master 分支包含 76d12 引入的修改了。
若是你有一个远程 Git 分支,好比在 GitHub 上的分支,当远程分支上包含当前分支没有的提交时,可使用取回。好比当合并了另外一个分支或你的同事推送了一个快速修复时。
经过在这个远程分支上执行 git fetch,咱们就可在本地获取这些修改。这不会以任何方式影响你的本地分支:fetch 只是单纯地下载新的数据而已。

如今咱们能够看到自上次推送以来的全部修改了。这些新数据也已经在本地了,咱们能够决定用这些新数据作什么了。
尽管 git fetch 可用于获取某个分支的远程信息,但咱们也能够执行 git pull。git pull 其实是两个命令合成了一个:git fetch 和 git merge。当咱们历来源拉取修改时,咱们首先是像 git fetch 那样取回全部数据,而后最新的修改会自动合并到本地分支中。

很好,咱们如今与远程分支完美同步了,而且也有了全部最新的修改!
每一个人都会犯错,但犯错其实没啥!有时候你可能感受你把 git repo 彻底搞坏了,让你想彻底删了了事。
git reflog 是一个很是有用的命令,能够展现已经执行过的全部动做的日志。包括合并、重置、还原,基本上包含你对你的分支所作的任何修改。

若是你犯了错,你能够根据 reflog 提供的信息经过重置 HEAD 来轻松地重作!
假设咱们实际上并不须要合并原有分支。当咱们执行 git reflog 命令时,咱们能够看到这个 repo 的状态在合并前位于 HEAD@{1}。那咱们就执行一次 git reset,将 HEAD 从新指向在 HEAD@{1} 的位置。
