版本控制¶
Git 是每个程序开发者都应该学习的技术,无论他们的研究方向是哪个领域。
版本控制是对文档、文件或任何其他类型数据的更改的管理。在软件开发中,它对于管理和跟踪代码库的更改、确保代码质量、减少错误以及改善团队成员之间的协作至关重要。
如果没有版本控制,管理和跟踪代码更改将是一项困难且容易出错的任务。Git 等版本控制工具提供了一种管理代码更改、跟踪版本以及与团队成员协作的方法。这使它成为现代软件开发的重要组成部分,几乎所有软件开发团队都在使用它。
前提条件¶
为了完成本节,你需要具备以下条件:
- 一个命令行环境(Windows Terminal/Terminal.app/...);
- 一个你选择的文本编辑器(我将使用 VS Code);
- 一个GitHub账户。
什么是Git?¶
Git 是一种流行的版本控制系统,开发者使用它来管理代码更改。它允许开发者跟踪对其代码库所做的更改,与团队成员协作,并在需要时恢复到以前的版本。
Git 由于其灵活性、速度和轻松处理大型代码库的能力而被广泛用于软件开发。它还提供了一系列用于管理和组织代码的功能和工具,例如分支和合并。通过 Git,你可以恢复到文件的不同状态(revert),也可以创建一个文件的副本(fork),对该副本进行修改,然后将这些修改合并到原来的文件(upstream)中。
Git 的使用并不局限于源代码文件,你也可以用它来跟踪文本文件甚至图像。这就是说,Git 不仅仅是为开发者服务的,任何人都可以获得它的帮助。
安装¶
Git¶
GitHub Desktop¶
https://github.com/desktop/desktop
SourceTree¶
https://www.sourcetreeapp.com/
使用方法 - Git¶
提示
由于 Git 的操作和其余图形化界面原理相同,将不讲解图形化界面的操作。
建立一个应用了 Git 的仓库¶
创建目录¶
设置新 Git 存储库的第一步是在您的计算机上创建一个新目录。该目录将作为新存储库的根目录。
使用命令行,输入 mkdir
,或通过文件资源管理器创建目录;通过 cd <dir_name>
进入目录;在新储存库的根目录下输入 git init
以初始化一个有 Git 的仓库。
修改文件¶
对仓库中的文件做出你想要的修改。一般将工作目录使用 VS Code 等编辑器打开。
暂存修改¶
你需要在修改后将做出修改的文件迁移至暂存区(staging area),以通过后续的步骤将修改提交。
在命令行中输入 git add <filename>
或 git add --all
来将更改放置在暂存区。
将文件添加到暂存区后,就可以将其提交到存储库。重要的是要注意,将文件添加到暂存区并不会真正提交它们——它只是为提交做好准备。你可以在提交之前根据需要继续添加和修改文件。
提交修改¶
在将你想要提交的更改均放置到暂存区后,你就可以提交修改(commit)。
在命令行输入 git commit -m "commit message"
,将 "commit message" 替换为一段关于你提交修改的内容的摘要。提交后,更改将保存到存储库的历史记录中,并且可以根据需要进行跟踪、还原或与其他分支合并。
要与其他开发人员共享您的更改或在项目上进行协作,你可以使用 Git 将本地存储库连接到远程存储库。
远程存储库是托管在服务器(例如 GitHub、GitLab 或 BitBucket)上的存储库副本,并允许多个贡献者在同一代码库上工作。
要连接到远程存储库,请使用 git remote add
命令,后跟远程存储库的 URL。
在你接下来的推送(push)中,你只需要 git push
即可。
git pull
命令从远程存储库中获取其他贡献者所做的最新更改,并自动将它们合并到当前分支中。
如:你可以输入 git remote add origin <repo_url>
来连接,在你的第一次提交时,使用 git push -u <default branch>
来建立连接。当他人对远程存储库做出修改后,你可以通过 git pull origin main
来拉取更改(fetch)并合并到本地的分支当中。
克隆别人的仓库¶
克隆存储库是使用 Git 时的一项常见任务。克隆(clone)创建远程存储库的本地副本,包括项目的所有文件和历史记录。
在 Git 中克隆存储库非常简单,您可以通过多种方式完成,例如使用命令行或图形用户界面。这使您能够访问存储库的代码、提交历史记录和分支。
-
复制存储库 URL:首先获取要克隆的远程存储库的 URL。 您可以在存储库的托管平台上找到此 URL,例如 GitHub 或 GitLab。
-
打开终端或命令提示符。 打开您喜欢的命令行界面。 这可能是 macOS 和 Linux 上的终端或 Windows 上的命令提示符。
-
导航到所需位置:使用 cd 命令导航到要克隆存储库的目录。 例如,如果您想将它克隆到桌面上的“Projects”目录中,您可以运行
cd ~/Desktop/Projects
。 -
开始克隆。输入
git clone <https://github.com/username/repository.git> .
回滚之前的更改¶
Git 将你的更改历史用哈希值表示出来,这意味着你可以通过一次更改的哈希值将储存库返回到当时的状态。
在更改的历史记录中找到你想要回到的提交的哈希值,输入 git reset <hash>
,然后通过 git push -f origin main
或 git push --force origin main
回滚更改(revert)。若你无法强制推送回滚操作,你可能需要通过一次提交进行回滚,但这样不会清除历史记录中之前的提交记录。
冲突解决¶
当你在本地进行代码修改时,与此同时,远程仓库也有了新的提交,这时你再进行 git push
时,就会出现冲突,这时你需要先 git pull
将远程分支当中的变更在本地分支上进行更新,然后再进行冲突解决。
Git 使用的注意点¶
Pull Request 与分支的使用¶
正常情况下,如果你在使用 Github 等主流 Git 仓库托管平台托管代码,你应该在仓库下有多个分支,如:main
、dev
、feature
、hotfix
等,其中 main
分支是主分支,dev
分支是开发分支,feature
分支是功能分支,hotfix
分支是热修复分支。main
分支将会开启分支保护,使得他人不得直接将代码 push
置 main
分支,而是需要通过 Pull Request
的方式(以下简称 PR)将代码合并到 main
分支。
这样的好处是,在经过审核之前或经过单元测试之前,你的代码不会被合并到 main
分支,从而保证了 main
分支的稳定性。
同时也可以使用 Github 等平台提供的 fork
功能进行开发,如每个人都拥有一个 fork
仓库。每个人在此仓库进行修改后再合并进主仓库的 feature
分支。
多人协同下的合并冲突解决¶
与单分支的情况相同,多分支也会出现冲突的情况,如程序员 A
的 feature-a
分支通过了 PR 并被合并进了 main
分支中。但与此同时 B
也在 feature-b
分支中同步开发。当他开发完毕时,他的代码也需要合并进 main
分支中,但此时 main
分支已经发生了变更,这时就会出现冲突。很明显,feature-b
当中并没有应用 feature-a
当中的更新。因此我们要引入 rebase
与 merge
的概念。
Rebase¶
rebase
是将一个分支的变更应用到另一个分支上,这样就可以保证 feature-b
分支中的代码是最新的。但这样做的缺点是,feature-b
分支的提交历史将会被改写,这样会使得 feature-b
分支的提交历史变得混乱,不利于代码的追溯。
rebase
一般用于将远端 main
分支当中的更新应用到本地分支上,这样可以保证本地分支的代码是最新的。
使用如下:
那么现在feature-b
分支的代码就是最新的了。 Merge¶
merge
是将一个分支的变更合并到另一个分支上,这样做的好处是,不会改写提交历史,但是会产生一个新的提交,这样会使得提交历史变得混乱,不利于代码的追溯。
merge
一般用于将其他分支合并进 main
分支的情况。可以使得回滚变得方便,因为不会抹除分支当中的提交历史。
Github 当中提供了相关的功能。因此 merge
操作可以不用在本地进行,直接在 Github 上进行即可。
Git Log¶
git log
可以查看提交历史,但是默认情况下,git log
只会显示当前分支的提交历史。如果想要查看其他分支的提交历史,可以使用 git log <branch-name>
来查看。
如果按照正确的方式,即上一节所说的步骤进行冲突处理,那么 git log
产生的结果应该如下:
* e2e6451 (HEAD -> master) feture-c finished
|\
| * 516fc18 C.2
| * 09112f5 C.1
|/
* c6667ab feture-a finished
|\
| * e64c4b6 A.2
| * 6058323 A.1
|/
* 2b24281 feture-b finished
|\
| * c354401 B.4
| * 4bfefb8 B.3
| * eb13f72 B.2
| * c2c62b9 B.1
|/
* bbbba82 init
rebase
则会发现分支是平的,而 merge
则会发现分支是树状的,十分混乱。 只有使用 rebase
与 merge
结合的方式才能使得分支的提交历史变得清晰。