0%

Git 系列(一) 常用命令(动图讲解)

引言

本文主要以动图的方式讲解 Git 中比较常用的命令,以加深对这些命令的理解。

Merging

我们通常用不同的分支开发不同的新功能。当对应的功能准备发布时,通过 git merge 命令将该分支内容合并到生产。然而该命令有两种不同的模式:fast-forward 以及 no-fast-forward,接下来,我们看下两者的不同!

Fast-forward (–ff)

如果 master 分支没有任何新的提交(即:落后于当前分支),那么将当前分支合并到 master 时,会采用 fast-forward 方式进行合并。该方式合并时并不会产生新的 commit,而是把当前分支的 commit 合并到 master 中。



No-fast-forward (–no-ff)

如果 master 上在我们检出开发分支后有了新的 commit(即:master 领先于当前分支),那么 git 将会使用 no-fast-forward 进行合并。此时会有一个新的 commit 被创建,该 commit 同时指向了 master 以及当前分支(也就是将两个分支的内容合并到了当前 commit 中)。



Merge Conflicts

如果不同的分支在相同的文件、相同的行上进行修改或者一个分支删除,一个分支修改同一个文件等操作时发生合并,就会产生冲突。比如在以下例子中,我们在两个分支(master、dev)分别编辑 README.md 文件:



当把 dev 合并到 master 时,就会提示合并冲突,因为同时修改了 README.md 文件的第一行。此时,git 就会展示冲突的地方,我们可以手动的选择要保留的完成合并,然后再进行 add 以及 commit,提交我们合并之后的信息。



Rebasing

git merge 可以将一个分支的内容合并到另一个分支,除此之外还有另外一个命令 git rebase 也可以做到。不同的是 git rebase 会将当前分支的提交信息追加到目标分支之后(不像 git merge 会产生一个新的 commit 信息)。



git merge 相比,当遇到冲突的时候,git 并不会检查哪些文件需要保留,哪些不需要保留。要 rebase 的分支(这里的 master)总是保留最新的更改,这会有什么问题呢?就是如果 dev 上有冲突产生,那么总会保留 master 而丢弃 dev 上的更新(所以在 rebase 之前,切记保持分支代码的更新,避免冲突出现)。该命令的好处就是可以让 git 的提交历史更线性。

📚 Tips

对于一些大型项目,rebase 并不是很推荐。本文里对 rebase 的说明过于浅显,也就造成有些陷阱的存在,但是作为命令讲解已经足够了。所以,我会单独开一篇文章详细讨论一下 rebase!

Interactive Rebase

交互式的 rebase 可以让我们在 rebase 之前修改 commit 信息。以下有六种操作:

  • reword
    修改 commit 信息

  • edit
    修正本次 commit

  • squash
    将本次提交合并到上一次提交中

  • fixup
    将本次提交合并到上一次提交中,丢弃 commit 信息

  • exec
    在我们想要 rebase 的每次 commit 上执行一个命令

  • drop
    丢弃本次提交

以上命令可以使我们完全掌握我们的提交记录。



git rebase -i HEAD~3 命令的意思是:以交互式(interactive)打开最近的 3 次提交记录。通过 vim 修改 commit 的前缀,这里是将提交记录 ec5bef2 的前缀改为 drop(即:删除当前提交)。随后的两个提交记录便会产生新的 hash 值。



以上命令则是将 e45cb9e 合并到 ec5bef2 中.

Resetting

如果我们想要丢弃已经提交的信息,那么 git reset 就派上用场了。git reset 用于将 head 指针指向我们期望的提交记录。reset 又分为 soft resethard reset

soft reset

软重置将 head 指向我们期望的 commit 记录,此 commit 之后提交的信息并不会被删除。
如下图,我们并不想保留添加了一个 style.css 文件的提交记录 9378i 以及添加了一个 index.js 文件的 035cc 记录,但是我们想保留这两个文件,那么我们就可以使用软重置。



通过 git status,我们仍然可以访问之前提交的所有信息,这样的好处就是我们可以修改这些内容以便之后再次提交。

hard reset

有时你可能并不想保留之前所做的修改,而是完完全全删除它们。那么请使用硬重置。



如上图所示,9e78i 跟 035cc 两次提交的信息就完全被丢弃了。

Reverting

撤销提交的另一种方式是执行 git revert。与 git rest 不同地方是,它会产生一个新的 commit,该 commit 中包含了我们要撤销的内容。
比如下图,我们在 ec5b3 提交中添加了一个 index.js 文件,随后我们要撤销该文件。



从图中我们可以看到,产生了一个新的提交记录 9e78i,而该记录就是将 index.js 文件进行删除,以达到撤销 ec5b3 提交的目的。

📚 Tips

这里有个地方需要特别注意,如果我们把之前 revert 过的记录再合并回来是比较困难的。因为此次被 revert 的内容以 commit 的方式保留在提交记录中。所以,如果只是暂时性的撤销,那么推荐 reset。revert 带来的好处就是保留了撤销内容的 commit 信息。

Cherry-picking

如果只是想把部分 commit 而不是整个分支合并到目标分支,我们可以使用 cherry-pick
如下图,dev 分支的 76d12 提交中添加了一个 index.js 文件,以下操作只是把 76d12 合并到了 master 分支中。



Fetching

如果远程分支已经被更新,我们只是想把它下载下来而不是合并到本地分支,可以使用 git fetch 命令。



此时我们可以查看自上次 push 以来,该分支上所做的修改。同时我们注意到 head 指针的位置并没有发生变化,也就是当前下载下来的内容并没有合并到当前分支。如果想把更新的内容合并到当前分支,则需要执行 git merge

Pulling

git pull 命令可能是我们最常用的命令,该命令可以理解为两个命令的组合:git fetchgit merge。也就是将远程的更新合并到当前分支中,同时我们注意到,下图的 head 指针已经发生了变化。



Reflog

git reflog 用于显示当前分支所做的所有操作,以便于我们进行回滚。



通过 git reflog 显示当前分支最近的操作,假如我们想回滚到 HEAD@{1} 所对应的提交。我们可以执行 git reset HEAD@{1}。然后在通过 git reflog 查看,刚刚我们执行的 reset 操作已经记录下来了。



以上只是对常用的命令进行了简单介绍,通过动图的方式让大家理解每个命令的区别以及背后的原理。后续的文章会对相关的命令进行深层的探讨,以防止大家在使用中遇到各种各样的问题。


参考