首页 版本控制工具——git
文章
取消

版本控制工具——git

前言

本篇最早写于 Heabot,点此处查看 Heabot 版本。

版本控制工具

如果你和四个同事一起开发一个软件(或游戏),要如何保证每个人都能正确地获得另外四个人修改过的代码?如果一段代码出了问题,如何找到是谁最后修改了这段代码?如果软件(或游戏)突然有一天出现了严重的崩溃,要如何回退到上一个正常的版本?

而这就是为什么我们需要版本控制工具。它管理每个人对代码的修改权限,处理不同人代码的合并,跟踪每一行代码的修改历史与修改者,对于合作开发而言是必须的工具。

有人会说:那我从来都是单打独斗,就不需要版本控制工具了吧?并非如此。即使单从代码回退这一个功能上来看,任何项目都应当使用版本控制工具。

中央式版本控制及分布式版本控制

最初,版本控制工具都是中央式系统,即工具只维护一个远程仓库,所有修改记录都储存在服务器上,任何人想要向仓库提交代码,必须先从远程仓库拉取所有修改,任何人想要回退代码,或查看修改记录,都必须保持在能够连接到服务器的环境下才能工作。从 RCS,CVS 到 Subversion 都采取了这种工作方式。

进入21世纪,一些新生版本控制系统,比如 BitKeeper 以及我们的主角 git,选择了另一种方式:分布式版本控制。分布式版本控制的特点是,他维护本地仓库,所有的修改历史都储存在项目目录下一个名为 .git 的文件夹里,这样即使在无网络情况下,程序员仍然能正常使用版本控制工具。

源代码托管服务平台

首先需要明确的是,GitHub 与 git 是两个不同的概念。GitHub 是一个源代码托管服务平台,允许将代码仓库托管在它的服务器上,以方便不同程序员维护同一个仓库,除它之外还有 GitLab,BitBucket,Gitee 等等平台。通常来说,在同一局域网内的项目可以直接使用局域网操作 git,如果没有局域网条件,大部分闭源项目也会选择 GitLab 之类的来托管代码,可以说,GitHub 现在的定位更像是开源代码分享平台。

关于git

说起git就不能不吹一手林纳斯·托瓦兹(Linus Torvalds),他是 Linux 内核最早的开发者,为了管理 Linux 内核的源代码,他发起了 git 这一项目。git 可以在其官网下载到,Windows 端建议手动将它的目录加入到环境变量的 PATH 中,Linux 和 MacOS 端则建议使用包管理器来安装 git 而不是去官网下载。

初次使用 git,需要设置用户名和邮箱:

1
2
git config --global user.name 用户名
git config --global user.email 邮箱

这并非是在注册账号,事实上你可以随便填写,这些信息是显示在代码的修改历史中的。

基础操作

查看 git 帮助文档:

1
git --help

更详细地,查看某个特定命令的帮助:

1
git 命令 --help

想将一个现有项目加入到 git 的控制下很简单,在 cmd/PowerShell 或 Linux 终端里,使用 cd 命令进入项目目录,然后键入:

1
git init

要区分暂存(stash)和提交(commit)的区别,暂存是将修改加入到索引中,提交是将已经暂存的内容记录到历史中。提交一般而言都是实现了一个完整的功能或模块,提交必须要写本次提交的信息(message),版本回退时就根据这些信息决定回退到哪个提交。暂存一般是还没写完整个功能,但是确定这一块已经没啥问题了,就暂时保存一下,如果之后代码出现了错误,通过 git 的功能可以看到哪些代码是已经暂存的,哪些是没有暂存的,方便寻找出问题的代码。

要暂存某个特定的文件/文件夹,使用:

1
git add 文件路径

想暂存所有修改,将文件路径换成点即可:

1
git add .

注意,提交只会提交暂存过的代码,没有暂存的代码是不会被提交的!通常提交使用的命令是:

1
git commit -m "消息"

如果你不喜欢暂存,想提交的时候就全部提交了,可以添加a参数,相当于在此之前执行一次git add .

1
git commit -a -m "消息"

要查看当前代码和上一次提交相比哪些文件发生了暂存/修改/添加/删除,使用:

1
git status

如果你想把某个文件/文件夹恢复到上次暂存时的状态,使用:

1
git checkout -- 文件路径

注意两个杠两边有空格。

如果想把所有文件/文件夹都恢复到上次暂存的状态,使用:

1
git checkout .

如果想把某个文件/文件夹恢复到上次提交时的状态,使用:

1
git reset HEAD 文件路径

同样,想恢复所有文件/文件夹到上次提交的状态,使用:

1
git reset HEAD .

要查看提交历史,使用:

1
git log

通常一个提交的格式如下:

1
2
3
4
5
commit 7007c77594a8e37bd26c52a6929f00015ccf8aa5
Author: Nichts Hsu <NichtsVonChaos@gmail.com>
Date:   Thu Nov 14 14:40:11 2019 +0800

    init

第一行是 commit 句头,紧接着是本次提交的哈希值。第二行是提交者的名字和邮箱,第三行是提交日期,第四行为空,第五行是提交时填写的消息。

想要把代码回退到某个提交,可以先使用 git log 找到想回退的提交,然后将其哈希值复制下来填入到:

1
git reset 提交哈希值

注意它不会把你还未提交的修改删除,如果想要删除未提交的修改,则可以加入 hard 参数:

1
git reset --hard 提交哈希值

分支

对于多人合作而言,每个人使用一个分支几乎是必须的事情,否则很容易干扰到别人。即使是一个人工作,有时候可能也需要为了测试什么东西,或者固定一个版本而新建分支。这时可以使用:

1
git branch 分支名

查看已有分支去掉分支名参数即可:

1
git branch

切换工作分支:

1
git checkout 分支名

对于多人合作而言,你可能会想把别人的分支的修改合并到自己的分支里,则可以使用:

1
git merge 合并来源分支

注意:合并可能会出现冲突,建议使用专门的软件(如 GitHub Desktop,Gitkraken,VSCode 等)来处理冲突。你也可以找到每个冲突的文件自己一个个解决冲突,然后再提交。

冲突的地方 git 会做以下标记:

1
2
3
4
5
<<<<<<< HEAD
当前分支的冲突代码
=======
合并来源分支的冲突代码
>>>>>>> 合并来源的分支名

通过全局搜索 <<<<< 可以快速找到冲突位置。

删除一个不需要的分支:

1
git branch -d 分支名

重命名分支:

1
git branch -m 旧分支名 新分支名

远程仓库

此处我们以 GitHub 为例。如何在 GitHub 申请账号、创建仓库与 git 并无关系,因此不做详细讲解。当你创建好远程仓库后,或者你想把别人的仓库下载到本地,则可以:

1
git clone 远程仓库地址

如果你在本地已经有一个项目了,想要关联到远程仓库,则可以用:

1
git remote add origin 远程仓库地址

注意,origin 只是代表默认服务器主机,你可以为一个项目添加许多服务器主机,不过一般很少用到。

查看本地仓库关联的远程仓库:

1
git remote -v

想要修改本地仓库所关联的远程仓库,可以使用:

1
git remote set-url origin 新的远程仓库地址

想要把本地修改推送到远程仓库,使用:

1
git push origin 分支名

推送所有分支的修改,或者说你没有修改过其他分支,则可以简略为:

1
git push

推送分支前需要先拉取远程仓库的修改:

1
git pull origin 分支名

同样拉取所有分支简略为:

1
git pull

拉取远程仓库会将修改合并到本地,如果你只想看云端有什么新的提交记录,而不想合并修改到本地,则可以使用:

1
git fetch

也就是说,pull 相当于 fetch + merge。

在合并分支时,如果合并来源是远程仓库,则分支名需要使用 origin/分支名的格式。

删除云端的分支使用:

1
git push origin --delete 分支名

由于 git 在 push 之前必须 pull,但是有时候你想覆盖掉远程仓库的修改,只保留本地的修改并推送到远程仓库,则可以使用:

1
git push --force

使用此命令前请务必和你的同事沟通好!否则你很可能让同事的心血毁于一旦。

上游

什么是上游?一个开源项目,他并不属于你,你也没有写入的权限,这时候,你就可以通过 GitHub 上的 fork 功能,将这个项目拷贝到你的账号下,这样你就可以随便修改这个副本了。对于你这份副本而言,原始项目就是上游。同样,对于原始项目而言,你这份副本是下游。

为项目设置上游很简单,就是将主机参数 origin 换成了 upstream:

1
git remote add upstream 上游远程仓库地址

其余操作也是如此,只需要把 origin 换成 upstream 就行。

但是要注意的是,如果你没有上游仓库的写入权限,git push upstream 是必定失败的。

如果你觉得自己写的代码好,想让原作者也接收这份代码,则可以先push到自己 fork 的副本中,然后在 GitHub 里发起一个 Pull Request 给原作者,原作者看到并检查之后,觉得确实好,那么他会接受你的 Pull Request,这时候你可以在上游仓库的 contributors 里看到自己的头像和 id。

本文由作者按照 CC BY 4.0 进行授权