在大部分开发者心里,Git 应该是一款非常强大的工具。Git 包含了许多有用的命令,这也使得学习它的难度增加了不少。纯文本解释是十分枯燥的,且让人难以理解。本篇文章会以动画的形式,来直观展示这些命令的作用,帮助你更好的理解并掌握这些命令的使用。
合并 (Merging)
在多人开发过程中,创建多个分支进行开发是非常有用的,比如常见的 Develop分支,Release分支,Feature分支 等,这可以将彼此的修改分隔开,互不影响,并确保不会意外的将未批准的修改推送到生产环境中去。一旦某些修改通过批准,我们希望将这些修改合并到主分支并发布线上,这时就需要用到 Git 命令:git merge
Git 可以执行两种类型的合并:fast-forward 和 no-fast-forward , 我们来看看它们之间的差异。
Fast-forward (–ff)
当当前分支与合并的目标分支相比,没有额外的提交时,Git就会尝试执行 fast-forward 合并。这种类型的合并不会创建新的提交,而是在当前分支上合并目标分支上的提交。
No-fast-foward (–no-ff)
与上述的情况相反,当当前分支与合并的目标分支相比,当前分支存在额外的提交,这时 Git 将会执行 no-fast-forward 合并。这种合并会在当前分支上创建一个新的合并提交,这个合并提交会同时指向当前分支与目标分支。
合并冲突 (merge conflicts)
Git 虽然很强大,但不是万能的,它也有一些不能自行处理的情况:比如当我们尝试合并的两个分支,在同一文件的同一行上都有修改,或者是一个分支删除了另一个分支已修改的文件时,就会发生合并冲突。在这种情况下,Git 就会要求您来决定如何处理这些冲突。
假设在两个合并的分支中,我们都编辑了 README.md 文件的第一行:
如图所示,如果我们想将 dev 合并到 master 分支中,就会导致合并冲突,Git 会给出发生冲突的信息,我们可以手动删除不想保留的更改,保存提交即可。
变基 (Rebasing)
将更改从一个分支添加到其他分支的另一个方法就是:git rebase
git rebase 从当前分支复制 commit,并将这些复制的 commit 放到目标分支的 =commit== 顶部。
如图所示,git rebase 的本质是 变基,就是找到两个分支的公共祖先,然后进行合并。
git rebase 的优点:保持良好的线性提交记录,去掉了 merge commit
git rebase 的缺点:如果合并出现代码问题不容易定位,因为重写了提交记录
FBI Warning
never use it on public branches(不要在公共分支上使用rebase)
交互式变基 (Interactive Rebase)
我们通常会遇到这种情况,在 Feature分支 中,由于各种原因会提交许多临时的 commit,而这些多个 commit 加起来才是一个完整的任务。那么为了避免太多的 commit 造成主分支的提交记录混乱,我们可以在合并主分支前,通过 交互式变基 来避免这种情况。
这时就要用到 Git 命令:Interactive Rebase
我们可以对需要变基的 commit 执行以下 6 种操作:
- reword: 修改提交信息
- edit: 修改此提交
- squash:将提交合并到上一个提交中
- fixup: 将提交合并到上一个提交中,但不保留提交的日志消息
- exec: 在我们想要变基的每个提交上运行一个命令
- drop: 删除提交
是不是很强大,这样我们就可以完全控制我们的 commit 记录。如果我们想删除一个 commit,我们可以这样做:
或者我们想把多个 commit 记录合并在一起:
这里我们只展示两个常用操作,其他的就需要大家自行摸索了:)
复位 (Resetting)
假如我们已经提交了的一些修改,后来需求变更,用不上了想回退,该怎么办呢?这种情况下,我们可以使用 Git 命令:git reset
软复位 (soft reset)
软复位 将 HEAD 移动到指定的提交,但是仍然保留先前提交所进行的修改。 假设我们不想保留添加了 style.css 文件的提交 9e78i,我们也不想保留添加了 index.js 文件的提交 035cc。但是,我们确实希望保留新添加的 style.css 和 index.js 文件,这时就需要用到: git reset --soft
输入 git status 时,您会看到先前提交中所做的更改仍有保留,这意味着我们可以修改这些内容,然后再次提交它们。
硬复位 (hard reset)
和 软复位 不一样,有时候我们想彻底的重置到某个 commit 提交时的状态,包括工作目录和暂存文件,这时就需要用到: git reset --hard
Git 已经丢弃了在 9e78i 和 035cc 上的所有更改,并将其状态重置为提交 ec5be 时的状态。
还原 (Reverting)
撤消更改的另一种方法是执行 git revert,该命令可以还原到某个指定 commit,然后再创建一个包含还原修改的新 commit。假设 ec5be 添加了一个 index.js 文件,后来我们意识到我们不再需要这个文件了,我们可以执行 git revert ec5be
新提交 9e78i 恢复到了 ec5be 提交之前的状态,执行还原操作可能会有一些冲突,我们也可以选择性的保留。 git revert 对于撤消某个提交非常有用,且无需修改分支的历史提交记录
选择提交 (Cherry-picking)
假设在 master 分支,我们想要应用其他分支的单个 commit,该怎么办呢?这时我们可以使用 git cherry-pick,cherry-pick 只会合并其他分支的单个指定 commit,合并完成后会创建一个新的 commit。
假设 dev 分支上的提交 76d12 只创建了 index.js 文件,我们想要在 master 分支上引入这个文件,而不需要其他改动,我们可以执行 git cherry-pick 76d12
获取 (Fetching)
假如我们有一个远程分支,比如 Github 上的一个分支,这个分支上有一些新的提交,这时我们可以执行 git fetch 来获取这些新提交。fetch 操作不会影响我们的本地分支,它仅仅是下载新数据而已。
现在本地已经同步了远程分支的最新数据,我们可以修改它,并再次提交
拉取 (Pulling)
git pull 实际上是 git fetch 和 git merge 两个命令的集合,当我们从远程分支拉取数据时,我们首先像使用 git fetch 一样获取最新数据,之后新数据会自动合并到本地分支中。
参考日志 (Reflog)
git reflog 是一个非常有用的命令,它记录了你在本地的几乎所有历史操作,包括 merge、reset、revert 等。
假如我们误操作了,比如执行了 git pull origin master,后来我们意识到不能合并远程分支,这时我们可以执行 git reflog 命令,从控制台的输出,我们可以看到合并前本地仓库的状态在 HEAD@{1},现在我们可以执行 git reset HEAD@{1} 来进行误操作的回退
我们可以看到 reflog 已经记录了我们最新的操作。