vlambda博客
学习文章列表

[开发工具] 关于 Git 掌握这些就够了

1 版本控制

Git 是一个版本控制工具,功能上类似于 SVN。Git 提供了非常多强大的指令来帮助你进行版本管理,此外你还可以使用 SourceTree 提供的界面来管理。不过,Git 的命令行已经足够使用了。关于 Git 的安装和初始化配置……此处略去一万字。

1.1 提交文件

在一个新的文件夹下可以使用 git init 将一个文件夹添加到 Git 中进行管理。

说明:也就是说,如果你想用 Git 管理这个文件夹下面的内容,你得先使用 git init 初始化一下。

然后,可以使用 git add file_1 [file_2] ... 指令将文件添加到 Git 仓库中。如果要提交全部文件,可以使用 git add .。添加了文件之后就可以提交文件了,这使用 git commit -m "提交内容备注" 指令。

说明:其实使用 git add 只不过将文件加入到了 Git 的一“缓存”区域里面。使用 git commit 提交之后才将其作为你的文件的一个“版本”。这里的 -m 用来为你的这次提交添加一些备注信息,比如这次主要改动了些什么之类的,以便于自己回头查看。

如果需要查看 Git 当前到状态,则可以使用 git status 指令。这个指令会返回仓库当前的状态,比如哪些文件被做了修改,但是不会显示出具体修改了哪些内容。除了查看修改了哪些文件,当我们使用 git add 将文件添加到 Git 到之后也可以再次执行这个命令,它将告诉我们有哪些文件将要被提交。总之,它能够显示出我们 Git 当前到状态。如果想要查看文件被修改了哪些内容,那么我们可以使用 git diff 文件名 命令。

这是一个好习惯:一般我们随时使用 git status 查看 Git 当前到状态,以免提交了不应该提交的代码,如果发生了改变,就使用 git diff 查看具体做了哪些修改。

1.2 版本回退

我们可以使用 Git 进行版本控制。在对版本进行回退之前,我们需要知道每个提交版本对版本号。这可以通过 git log 指令得到。

然后,就是回退到指定到版本,可以通过下面到指令 git reset --hard commit_id。这里的 commit_id 是某次提交的版本号。通常,我们可以使用 HEAD 表示当前到版本号。用 HEAD^ 表示上一个版本号,HEAD^^ 表示上两个版本号,HEAD-100 表示上第 100 个版本号。在 Git 当中,HEAD 是一个指向当前版本的指针。进行版本回退的时候,本质上也就是指针的位置发生了变化。

假如我们从版本 A 回退到了上一版本 B 之后又希望回到 A 的时候只要使用 commit id 执行上述指令就可以了。但如果我们已经回退到了 B,再使用 git log 是不会显示 A 的提交记录的。此时,可以使用 git reflog。它将显示所有的提交记录,可以在该命令到输出中找到 A 的 commit id

1.3 工作区与暂存区

当使用 git init 指令初始化了 Git 仓库的时候吗,Git 会为我们创建一个隐藏的 .git 文件夹。它叫做 Git 的版本库。而当前目录中除了这个隐藏的文件夹之外的其他文件夹,也就是我们文件相关的文件夹叫做工作区。Git 的版本库又分成暂存区和分支。默认地,当我们初始化代码仓库的时候会默认创建一个 master 分支。

我们项目当中所有的文件默认都是放在工作区的。当一个文件被修改,并且使用 git add 之后,它就被添加到了暂存区。然后当我们最终使用 git commit 提交之后,它才被添加到了分支上面。

所以,当使用 git commit 进行提交的时候,提交的是暂存区的修改。如果工作区的内容没有使用 git add 添加到暂存区,那么它是无法被提交的。

这部分了解即可,使用的时候的重点还是各个指令。不过这种分区的设计理念值得我们学习和借鉴。

1.4 撤销修改

版本回退包括两个部分:工作区版本回退和暂存区版本回退。

如果文件只在工作区做了修改,也就是修改了,但是没有使用 git add 添加到暂存区,那么我们可以使用 git checkout -- <file> ... 命令来撤销工作区的修改。这里的 -- 很重要,不然就成了切换分支的操作。这里的根据有些类似于 SVN 中的 revert 操作。

也就是,使用了 git add 的,还没有使用 git commit 提交的文件,可以使用这种方式进行回退。

如果已经把文件添加到了暂存区,那么可以使用 git reset HEAD <file> ... 将暂存区的修改撤销掉。

也就是使用了 git commit 提交了的文件。

以上撤销操作都是在本地修改还没有被提交到远程仓库中的前提下进行的。

1.5 删除文件

可以使用 rm <file> 指令从文件系统中删除一个文件,然后使用 git status 指令查看当前的状态,将会显示出存在一个文件被删除。

如果要从 Git 仓库中删除一个文件则可以使用 git rm <file> ... 命令。这种删除操作作用在暂存区。使用 git commit -m "message" 命令之后这个文件就被从代码仓库移除了。

至于恢复操作则可以使用上面的撤销操作。

2 分支管理

2.1 分支的基本操作

  1. 列出当前所有的分支:git branch

  2. 切换到指定的分支:git checkout

  3. 创建分支:git branch

  4. 创建并切换到指定的分支:git checkout -b

  5. 将指定的分支合并到当前到分支:git merge -m "message"

  6. 删除指定到分支:git branch -d

  7. 查看 Git 提交路线到图表:git log --graph

2.2 合并冲突

一个暂存区对应多个分支,如果一个文件被在分支 A 上面添加到了缓存区,在 B 上面被提交,那么这个文件最终将会被添加到分支 B 上面。

如果两个文件在合并到时候发生了冲突,那么在合并到时候会提示 “自动合并失败”。此时到解决办法是通过 git status 查看冲突到文件。然后打开冲突到文件,通过文本编辑到形式对文本进行修改得到期望到结果。然后通过 git add 等操作将合并到结果添加到仓库并提交。

相比于使用文本编辑修改冲突,更推荐使用 VS Code 来解决冲突,因为它有高亮提示。

2.3 --no-ff

在合并分支到时候使用 git merge --no-ff -m "message" <branch_name> 的形式。也就是禁用 Fast Forward 的意思。它的好处是,以下面的提交记录为例:

| Author: WngShhng <[email protected]>
| Date:   Sun Jun 9 18:44:37 2019 +0800

|     change rm2
|   
*   commit 4ef755bb364b75b8b132f58be4b3ab6b7aa71356
|\  Merge: b8af794 d5b488d
| | Author: WngShhng <[email protected]>
| | Date:   Sun Jun 9 18:39:35 2019 +0800
| | 
| |     merge from new
| | 
| * commit d5b488d25752f06c97309e6891da601b222cc8bc
|/  Author: WngShhng <[email protected]>
|   Date:   Sun Jun 9 18:35:13 2019 +0800
|   
|       change rm2.md

这里我们进行了两次合并,从下到上,第一次使用的是 --no-ff 模式,第二次没有使用,并且合并过来的分支都只做了一次提交,并且它们在合并之后都被删除了。那么,第一次的时候,这里存在一个 merge 的 commit,而第二次的时候没有提交记录。这将会导致我们无法判断那一次的修改记录是因为分支合并还是因为在 master 上面做了修改之后提交的。

2.4 保存工作现场

假如我们在 dev 分支开发,而突然需要修复某个 bug,此时原来的工作仍然有部分未完成。我们希望保存当前的工作状态,然后创建一个新的分支,修复 bug 之后在恢复到之前到工作现场。此时,我们可以通过 git stash 保存当前到工作现场。注意这里所谓到工作现场是指工作区的现场。如果是 dev 分支上面已经被提交到修改是不会被存储到工作现场里面到。

当我们修复完了问题并且 merge 到主分支上面之后再切换回 dev 分支。然后,我们可以使用 git dash list 查看有哪些可用到工作区。然后可以使用 $ git stash apply stash@{0} 指令来恢复到指定到工作区。这种恢复方式不会同时删除这个工作区,你需要再次调用 git stash drop 来删除。一般我们可以使用 git stash pop 来弹出工作区,这样可以恢复到指定到工作区的同时将其删除。

git status 应用的场景是保存当前文件的状态,整个目录恢复到上一次 commit 之前的状态。此时就可以切换到任意其他的分支,或者从远程拉取代码了。如果想要将 stash 了的记录应用到拉取之后的分支上面,使用 git stash pop 即可。

3 远程仓库

3.1 添加远程库

只需要在本地执行 git remote add origin <url> 即可。这里的 origin 是远程库的名字。然后使用 git push -u origin <master_name> 就可以将本地名为 master_name 的分支的内容提交到远程 origin 仓库。使用 git pull 将远程仓库中的代码拉到本地。

然后,可以使用 git remote 查看远程仓库的状态,如果需要更多的信息可以使用 git remote -v 查看更详细的信息。

添加或者更换远程分支的操作比较多,你可以使用 git remote set-url/get-url 等一系列命令来了解如何增删改查远程分支。这些命令不需要刻意去记,按照上面这种输入 git 命令行会自动给出提示。另外,如果想查询本地分支与远程分支的对应关系,使用 git branch -vv 即可。

3.2 与远程仓库交互

如果已经存在一个远程仓库,那么我们可以使用 git clone <url> 来把远程到仓库克隆到本地。

上面这种克隆的方式只是将远程仓库中的 master 分支克隆了下来。如果我们需要克隆远程仓库中的某个非 master 分支呢?假如我们需要使用远程仓库的 dev 分支,那么我们可以使用 git checkout -b dev origin/dev 来完成。显然这里在本地创建了一个名为 dev 的分支,并且其对应的远程仓库是 origin 中的 dev 分支。

在做完了我们的修改之后可以使用 git push origin dev 来将本地当前分支的修改提交到远程的 dev 分支。这里需要注意的一个问题是,如果远程没有叫做 dev 的分支,那么上述推送指令将会在远程创建一个名为 dev 的分支。

4 Tag

4.1 Git 版本管理的两种常见方式

一般使用 Git 来进行版本管理的时候有两种方式来记录版本。

一种是每新增一个版本的时候从 develop 分支上面创建一个分支,每结束一个版本的时候就将其合并到 develop 上面,也就是保证 develop 版本的代码是最新的,然后各个版本的分支保留。这种方式的缺点是随着不断迭代开发,版本分支越来越多。

另一种方式开发新版本的时候从 master 分支进行创建,开发完之后合并到 master 分支。每次一个版本开发完毕之后就在用 tag 做一个版本的标记。比如,Github 上面就会从 tag 里面读取你的 release 信息。

两种方式各有利弊,如果业务存在多条相互独立的主线的话,使用打 tag 的方式就会比较乱。应该结合自己的实际情况来进行选择。

4.2 Tag 操作

这里只例举常规的操作,如果存在非常规的需求,参考文档或者帮助即可。

当你发布了一个版本之后在提交了代码之后,使用 git tag -a 标签名称 -m 备注 (推荐这么写)即创建一个指向当前 commit 的 tag 标签。然后,使用 git push origin --tags 即可将本地创建的标签提交到远程仓库。这样一次打标的操作就完成了。

最后

1 获取 Git 帮助

如果对哪个指令的含义不是很清楚,可以随时使用 git -h 或者 git --help 来查看把帮助,使用 git command -h 或者 git command --help 来查看某条指令的帮助。

2 Git 常用的操作流程

可以按照这个流程来操作完成一次代码的从拉取到提交的整个流程:

  1. 首先使用 git branch -vv 来查看本地分支及其对应的远程分支的信息;

  2. 如果要切换到某个远程分支进行开发,你可以先 git pull 一下来更新本地的远程分支信息;

  3. 如果在步骤 2 中出现了代码冲突(本地有修改的情况),可以先使用 git stash 将本地分支暂存起来,然后再 git pull 进行拉取;

  4. 如果想要切换到的分支是存在的话,使用 git checkout 分支名 即可;

  5. 如果想要切换到的分支是不存在的,使用 git checkout -b 分支名 origin/分支名 创建一个本地分支并将其关联到远程分支;

  6. 然后使用 git addgit commit 添加修改;

  7. 修改完成之后,使用 git push 将代码提交到(之前关联的)远程分支即可;

  8. 如果使用 git push 的过程发现远程别人提交了修改,此时命令行会提示你使用 git pull 更新代码;

  9. 如果 git pull 的过程出现了冲突 CONFLICT 你就需要先使用 git stash 将本地修改暂存起来,然就再 git pull 拉取远程修改,拉取完之后使用 git pop 将本地的修改弹出,解决掉冲突之后再把修改的文件提交即可。(这就是一次冲突的解决过程)