Github 食用指南

要了解 Github,需要先了解什么是 Git,Github 和 Git 的区别又是什么?

  1. Git:Git 是一种 VCS(Version Control System)即版本控制系统,Git 是一种分布式版本控制系统。相对的,肯定还有集中式版本控制系统,这种 VCS 的代表就是 SVN。在 2005 年,Linux 的创始人 Linus Torvalds 为了解决 Linux 内核开发中的版本控制问题,花了一周时间从头开始编写了一个革命性的新系统,并将其命名为 Git。

  2. Github:Github 是一个基于 Git 的在线版本控制和协作平台,在 2008 年被创建,于十年之后的 2018 年被微软收购。GitHub 的目标是简化 Git 的使用和协作,使得开发者可以轻松地共享代码、跟踪问题和合并代码。GitHub 提供了一个 Web 界面,使得用户可以在浏览器中查看和管理 Git 仓库。此外,GitHub 还提供了一些额外的功能,如代码审查、项目管理工具、社区功能等。

初窥 Github

在浏览和创建开源项目之前,我们最好先要有一个 Github 账号,当然了,Github 并不会像国内网站那样不登陆就完全无法使用,即使是游客,也可以浏览和下载开源项目(不过无法参与协作)。注册账号的过程并不复杂,由于我们是个人使用所以只需要注册个人账户即可,进入官网,按照提示进行注册即可。

Tips:在国外网站中,一般 Sign up 代表注册账号,Sign in 代表登陆已有账号。

登陆账号后我们就进入了 Github 主页面(Dashboard),如果是新账号,可能是一片空白,因为你还没有什么内容可以展示。

Dashboard

在 GitHub 上,有一些基本的概念需要了解:

  1. 仓库(Repository):仓库是 GitHub 用来存储项目代码的地方。一个仓库包含所有的项目文件,包括代码、文档、资源等,以及每个文件的修订历史。

  2. 分支(Branch):在 GitHub 中,开发者可以在主分支(通常是 master 或 main)之外创建其他分支进行开发。这样可以在不影响主分支的情况下进行开发和测试。

  3. 提交(Commit):提交是在 GitHub 上保存更改的方式。每次提交都会在当前分支上创建一个新的版本,记录下所有文件的当前状态。

  4. 推送(Push):推送是将本地代码更改上传到 GitHub 仓库的过程。开发者可以使用命令行或者 Git 客户端工具将本地代码推送到远程仓库,使得其他人可以访问和使用这些更改。

  5. 拉取请求(Pull Request):当开发者在分支上完成开发并准备将更改合并到主分支时,他们可以创建一个拉取请求。这是一种通知项目维护者的方式,让他们知道你已经准备好将更改合并到主分支。

  6. 合并(Merge):当拉取请求被接受后,更改会被合并到主分支。这是将分支上的更改应用到主分支的过程。

  7. 克隆(Clone):克隆是将 GitHub 上的仓库复制到本地计算机的过程。这样开发者就可以在本地进行开发和测试,然后将更改推送(Push)回 GitHub。

  8. Fork:Fork 是在 GitHub 上复制别人的仓库到你的 GitHub 账户下的过程。这样你就可以在这个副本上自由地进行更改,而不会影响原始仓库。

  9. Star:如果你喜欢某个项目,你可以给它 “Star”。这是一种表达喜爱和支持的方式,同时也可以帮助你追踪你喜欢的项目。

  10. Issue:如果你在项目中发现了问题,或者有新的想法,你可以创建一个 Issue。这是一种与项目维护者和其他用户讨论的方式。

  11. GitHub Actions:GitHub Actions 是 GitHub 的持续集成/持续部署(CI/CD)工具,可以自动化软件工作流程。

现在对这些概念不熟悉没关系,后面我们会经常碰到这些概念,见的多了,慢慢就能理解了。

需要注意的是,我们在了解的时候,不能只记住概念的中文词条,也要把英文词条记住,因为在你使用 Github 的时候,并不会看到中文概念词条,所以需要我们做到将英文词条直接和概念本身对应起来。在很多时候,你可能会听到开发人员说诸如:“把这个 commit push 一下”、“切一下 branch”、“提一个 PR”这种中英文混合的语句,这并不是装逼,而是很多时候用英文本身更加贴近概念本身,因为这些概念本身就是从英文世界中出来的,像这种基础概念可能还好一些,有与之对应的中文词条。而很多时候甚至都没有合适的中文词条来对应原有的概念,这就非常麻烦,所以我一直主张尽量用原本的英文词条,除非某个概念的中文词条已经深入人心,例如:“使能”。即便如此,在学习的过程中还是要尽量知道中文词条对应的英文词条是什么,这样会在后续的外网资料或者英文文档中的查阅学习更加容易一些。

从实践开始

首先,我们创建一个 Repo,也就是仓库,一般我们所谓的项目都是以 Repo 的形式存放于 Github 中的。在 Github 主页面的右上角我们可以看到一个加号,点击它之后就能看到 New repository,点击这个就进入新建仓库的页面了。

新建仓库

然后填入信息即可,必填信息只有 Repository name(仓库名),其他信息选填即可。

仓库信息

Tips:默认新建的仓库为 Public 公有仓库,即公开访问权限,可以手动改为 Private 私有仓库,不公开仓库访问权限,也就是说 Private 仓库只有自己或者本组织的用户才能访问。

剩下信息可以选填,比如 .gitignore 是 Git 的配置文件,它告诉 Git 哪些文件或目录不应该被添加到 Git 仓库中。license 是开源许可证,它定义了其他人如何使用、分发、修改或贡献你的项目。在 GitHub 中创建新的仓库时,你可以选择为你的项目添加一个开源许可证。

如果你对这些不熟悉的话,可以选择不填写。

其他信息

新建仓库完成之后,我们可以看到有很多的提示命令。

仓库信息

我们先来关注最重要的部分,也就是这个 HTTPSSSH,在这里先不看 SSH,因为使用它作为仓库链接还需要配置 SSH 密钥对才能连接,这个我们后面再配置。

Tips:

HTTPS 和 SSH 是两种不同的协议,用于在你的本地机器和 GitHub 服务器之间传输数据。

  1. HTTPS(Hyper Text Transfer Protocol Secure)是一种安全的 HTTP,它在 HTTP 和 TCP 之间添加了一个安全层(SSL 或 TLS)。这意味着所有传输的数据都会被加密,从而防止被第三方窃取。在 GitHub 中,当你使用 HTTPS 克隆仓库时,你需要每次推送时都输入你的 GitHub 用户名和密码(或使用凭据助手存储它们)。

  2. SSH(Secure Shell)是一种网络协议,用于安全地在不安全的网络上执行网络服务,如远程登录或远程命令执行。在 GitHub 中,当你使用 SSH 克隆仓库时,你需要创建一个 SSH 密钥对,并将公钥添加到你的 GitHub 账户。这样,你就可以在推送时使用你的私钥进行身份验证,而无需输入用户名和密码。

在 GitHub 中,你可以选择使用 HTTPS 或 SSH 克隆仓库,这取决于你的具体需求和偏好。

仓库链接

在本地电脑创建一个文件夹,最好和你的仓库同名,当然不同名也可以。然后在该文件路径下打开 Terminal 终端执行 git init 即可初始化该文件夹为 Git 仓库。

初始化仓库

如果你细心的话就会发现这个文件夹下多了个名为 .git 的隐藏文件夹,这里面存放的就是 Git 仓库的各种信息,没有 .git 下的各种文件,那这个文件夹就不能称之为 Git 仓库。

.git

接着我们来新建一个文件,一般来说每个仓库都会有一个名为 README.md 的 Markdown 格式的文件。这个文件主要描述了这个项目的用途、如何配置使用以及其他信息,是使用者需要阅读的第一手文档。

这里我用命令向 README.md 文件中写入了 # demo 的文字内容。这是一种 Markdown 格式,不熟悉的读者可以自行查阅学习相关语法。当然你不使用 Markdown 语法,单纯的使用 TXT 文本也是可以的,只不过在程序员群体中,Markdown 具有很高的通用性。

README

注意了,接下来要展示的是 Git 最最最常用的三个命令,也就是 addcommitpush,掌握这三个命令的使用,基本可以应对个人开发(注意是个人开发,不涉及到团队开发)的大部分日常需求了。

提交推送

可能有人会问:“你这不是还有 git branchgit remote 命令吗?”,不用担心这些命令基本只有第一次提交到远程仓库或者更换分支和更换远程仓库链接时才会用到,而且后续我们用 VSCode 初始化和提交仓库时就更方便了,几乎不需要使用命令。

提交完之后再去浏览器刷新看一下,就能看到我们提交的文件了,因为 Github 会在每个文件夹的首页默认显示 README 文件,所以我们就能直接看到 README.md 的内容而无需点击进去。

远程仓库

后续再添加、提交和推送文件就还是老三样就行了:

1
2
3
4
5
6
git add 你要提交的文件
# git add . 可以提交所有修改过的文件(注意 add 后面是个英文句点)
git commit -m "你要写的提交信息"
# 必须要写提交信息,即使你没什么可写的,否则无法提交(当然可以使用特殊方法跳过,但是没那个必要)
git push
# 第一次使用 -u 指定上游的远程分支之后,后续不修改分支的情况下无需加额外参数指定

至此,基础的 Git 命令行使用就基本学习完毕了。我们要学习的不只是 Git 的命令行使用,还有它的设计思想,比如 push 之前会有一个 commit 的行为,这是一般我们使用其他软件时很少见到的,用命令行能够更加清晰的展示这些设计思想。

Github organization

GitHub organization 是一种管理多个用户和仓库的方式。它可以用于团队合作,也可以用于大型开源项目。

  1. 用户管理:组织可以有多个成员,并且可以将成员分配到不同的团队。每个团队可以有不同的访问权限,例如,某些团队可能只能读取仓库,而其他团队可能可以提交更改。

  2. 仓库管理:组织可以拥有多个仓库。这些仓库可以被组织的所有成员访问,也可以被特定的团队访问。

  3. 权限管理:组织管理员可以控制成员的访问权限。例如,他们可以决定谁可以创建新的仓库,谁可以添加新的成员,等等。

  4. 统一的身份:组织可以有自己的名称、描述和头像,这可以帮助创建一个统一的品牌身份。

  5. 高级功能:对于付费用户,GitHub 组织还提供了一些高级功能,例如细粒度的权限控制、审核日志、高级安全功能等。

要创建一个新的 GitHub 组织,可以在 GitHub 网站的用户菜单中选择 “New organization”。创建组织后,你可以开始添加成员、创建仓库,以及设置团队和权限。

organization

上图中便是设置组织的信息页面,按照要求填写即可,需要注意的是创建者可以选择组织归属权,可以设置为归属于你自己的 Github 账号,也可以选择归属于一个企业或者机构。

创建完组织之后我们就可以进入组织主页面了,下图是我以前创建过的一个组织,所以会有一些信息展示出来。

organization

再次看到上方的功能栏就会发现稍有不同,Teams 和 People,顾名思义,在这里我们可以创建团队和添加人员到组织中。

Teams

具体的创建和添加方法按照提示来操作非常简单,这里就不多赘述了。

在组织中,最重要的事情莫过于权限管理,组织所有者需要谨慎设置组织角色的权限以避免造成不必要的麻烦。

组织所有者(Owner)可以手动修改组织成员的角色以保持不同的权限等级。具体的权限划分可以在 “Settings” 中修改。

权限

团队协作

平常我们除了开发个人项目,可能还会需要团队多人协作去开发项目,这个时候 Github 的 Issues 和 Pull Requests 功能就派上用场了。

在 Github 仓库页面的上方,可以看到有很多功能栏,我们可以点击进入对应的功能。在这里我们暂时只用关注 Issues 和 Pull Requests,其他的留给读者自己去探索。

功能栏

Issues

Issues 直译过来就是『问题』,顾名思义,这里就是给项目提出各种问题的地方。一般来说,项目的使用者或者开发者会在这里提出项目的 Bug(漏洞)、Feature(特性)和建议等等,然后项目的开发者就会来解答这些 Issues。所以一般我们在使用一些开源项目、开源库遇到问题的时候,最好先来对应的仓库看一下 Issues 有没有人提过类似的问题并且有无人解答,如果有答案那最好,如果没有,那你也 + 1。

Issues 的写法还是 Markdown 语法,不同的是,在 Github 中的 Markdown,你可以 @ 别人,引用别人的回复以及其他扩展功能。更重要的是,在 Github 中,你可以直接粘贴图片,Github 会自动帮你上传到云端,然后给你一个可以访问的链接,这样就能很方便的使用 Markdown 中的图片了。

Issues

Markdown 图片

不知道你有没有注意到 Issues 页面的上方有很多按钮?其中比较显眼的是上方的搜索框,在这里我们通过关键词或者特定语句过滤我们需要的结果,其中搜索框里灰色的文字 is:issue is:open 就属于特殊语句,是 GitHub 的搜索过滤器。点击左侧的 Filters(过滤器) 就能用更多的过滤器来精准搜索。例如,is:issue is:open label:bug 将会显示所有标记为 bug 的开放 Issues。

对于我们日常使用来说,最常用的就是 openclosed 这两个过滤器,于是 Github 就把它们做成了按钮,直接点击就可以切换。所谓 open 就是指该问题还没有被解决,所以处于开放状态,等待能人异士来解决,而 closed 就是已经被解决的问题,所以处于关闭状态。因此如果我们想要解决问题的话,往往在 closed 里能找到类似的问题答案。

Issues 页面

如果你是该项目的开发者或维护者,你可以给不同的 Issues 打上 Labels(标签)以区别对待。Github 贴心的为你准备了初始的几个标签,如果不够或者没有你想要的,你也可以自己添加或者删除。

Labels

此外我们还可以将 Issues 分配给某个开发者,让他来跟踪和解决该问题。如果问题被顺利解决或者时间太过久远没有人回答,可能会被 bot(机器人)(如果这个仓库有机器人的话)给自动关闭,同时我们也可以点击 Close issue 手动关闭。

Issues 页面

当然你可以根据实际情况选择关闭该 Issues 的原因,如果问题被解决了,就选择 Close as completed,如果没有解决,就选择 Close as not planned。如果问题又『复活』了,你也可以手动再 Reopen issue 重新打开这个 Issues。

Close issue

请注意,上面所描述的所有规则和使用方法,均为官方建议或者约定俗成的用法,并不是强制性的。例如明明这个问题没有被解决,你以 completed 的形式关闭了这个 Issues,这当然没有问题,只不过会给其他使用者和开发者带来困扰,使用约定俗成的用法能更好的提高开发和使用效率。

至此,基础的 Issues 功能已经足够日常团队使用了,当然个人开发项目也可以用 Issues。使用好 Issues 这个功能,能给使用者带来极大的便利,并且使用者也能给开发者反馈信息和需求,开发者和维护者可以实时的跟进需求、改进性能、修复漏洞和增加功能。

Pull Requests

Pull Requests 通常被简称为 PR,这个功能与 Issues 不同,大多数情况下只有多人协作开发时才会用到。它的使用场景是这样的:假设别人 fork 了你的仓库,别人对仓库代码做了改动,无论是删除内容、修复漏洞还是添加功能,只要和你的仓库代码不一样,他就能在 Github 里提 PR 给你的仓库。我们知道 pull 在 Git 中是拉取代码的意思,request 是请求的意思,那么 Pull Requests 就是对原仓库的开发者说:“请求拉取我的代码”,然后你就可以去审查他提交的代码,如果觉得不错,你就可以将这个 PR 手动合并到原仓库中,如果不满意,你们可以讨论问题所在直到 PR 通过你的审查或者你也可以选择不将其合并到原仓库中。这个审查代码的过程也被称为 Code Review。

那么为什么不让别人直接把代码推送到原仓库呢?

  1. 别人没有原仓库的权限,所以无法推送代码;
  2. 原仓库开发者应当保证所有改进的稳定性和代码质量。

总而言之,不要让别人在你的项目仓库乱拉 shit。

此外,PR 不仅适用于仓库与仓库之间,还适用于分支与分支之间的更改。实际上,PR 理论上就是用于提交分支间的更改的,只不过还可以在跨仓库的分支中提交更改罢了。

在下图中,我创建了一个名为 feature 的分支,并修改了其中的 README.md 文件,提交到新的远程分支之后回到浏览器就可以看到仓库页面上方出现了黄色提示栏,点击 Compare & pull request,便可以创建一个 PR。

PR

创建 PR 的页面乍一看好像和 Issues 页面差不多,都是一个 title 和 description。

PR

但是如果把视线移到上面就会发现这里可以选择两个分支来进行 compare(比较),如果这两分支之间有『差异』,也就是说有更改,那么就可以将这些更改或者说差异添加到左边的 main 分支中。

compare

在页面最下方还可以看到具体的更改差异,一般来说,绿色代表新增的部分,红色代表删减的部分。

diff

检查没问题之后点击 Create pull request 即可创建 PR。

后面就是原仓库开发者的事情了,Github 会自动检查有无冲突,然后开发者需要审查提交的 PR,通过审查后将其合并到对应的分支,倘若后续后悔了,甚至还可以撤回通过的 PR(当然撤回 PR 还是以自动新建一个 PR 的方式回滚)。

Tips:现在 Github 有很多 App 和 Action 可以帮助开发者快速的辅助自动化的代码审查。

merge

merge 方式也有好几种,这里简单介绍一下:

  1. Create a merge commit:这个选项会创建一个新的合并提交,这个提交有两个父提交(一个是你的分支的最新提交,一个是你要合并到的分支的最新提交)。这个选项会保留完整的提交历史和分支信息,但可能会使得提交历史变得复杂。

  2. Squash and merge:这个选项会将你的所有提交压缩(squash)成一个单独的新提交,然后将这个新提交合并到你要合并到的分支。这个选项会使得提交历史更加简洁,但你会失去原始分支的提交历史。

  3. Rebase and merge:这个选项会首先将你的分支(branch)上的所有提交(commit)应用到你要合并到的分支的最新提交上,然后再进行合并。这就好像你的更改是在最新的提交上完成的一样。这个选项可以创建一个线性的提交历史,使得历史更容易理解,但它会改变你的提交历史,可能会使得追踪更改更加困难。

Tips:方式一中所说的两个父提交并不是指两个提交(commit),如果你的一次 PR 里有 nn 个 commit,那么会产生 n+1n+1 个 commit 记录,也就是 nn 个你的分支的 commit 记录加上 11 个你要合并到的分支的最新 commit 记录。

下图依次展示了三种方式 merge 之后(在演示中每种 merge 方式都只包含一个 commit),main 分支所产生的 Commits 记录。可以看到第一种 merge 方式确实产生了两个 commit 记录;第二种方式它会压缩所有 commit 所以最后都只有一个 commit 记录;第三个比较复杂了,虽然也只有一个 commit 记录,但是它在 main 分支产生的 commit 记录就好像直接在 main 分支进行 commit 然后 push 一样,非常强大,这个 rebase 的概念读者可以后续自行了解,这里不作介绍。

Create a merge commit

Squash and merge

Rebase and merge

对于新手来说一般使用第一和第二种方式均可,第三种方式请谨慎使用,即使它很强大,但在你没有驾驭它的能力之前尽量不要使用。

本节结束后,读者应基本了解团队协作开发所需要使用到的 Github 基本功能,足以应对初级的团队项目开发了。

百川东到海 — Branch

上文中介绍了 PR,可能有读者深深困扰于各种 branch(分支)之间的转换以及关系,在本节中将对 branch 这一 Git 中的重要概念做一个较为深入浅出的介绍。

在 Git 中,分支可以被看作是你项目历史的一个快照或者说是一个工作流。你可以在分支上进行开发,而不会影响其他分支,这使得试验新想法变得非常容易。

当你创建一个新的分支时,Git 会创建一个指向你最新提交的指针。你可以把它想象成一个可移动的标签,表示你当前的工作进度。当你在这个分支上进行新的提交时,这个指针就会向前移动,指向新的提交。

Git 的一个主要特点是它的分支模型。Git 允许你在同一时间存在多个分支,并且可以轻松地在它们之间切换。这使得你可以在一个分支上进行新的开发,同时保持主分支的稳定性。

当你完成了在一个分支上的开发后,你可以将这个分支合并到其他分支,比如你的主分支。在合并过程中,Git 会尝试自动地将你的更改应用到目标分支。如果在这个过程中存在任何冲突,Git 会让你手动解决。

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了 50% 的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

其他版本控制系统如 SVN 等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。

但 Git 的分支是与众不同的,无论创建、切换和删除分支,Git 在 1 秒钟之内就能完成,无论你的版本库是 1 个文件还是 1 万个文件。

此外分支还可以解决质量问题:

比如:一个任务,两个人做。

  1. 开两个分支,谁的分支先上来,我合并谁的;

  2. 两个分支,如果其中一个人的分支有后期 bug,那么我可以切换到另一个人的分支上;

  3. 同一个人开两个分支,就是两个方案。

在下图的右侧可以看到我的博客仓库的 commit 记录,其中有绿色和蓝色两种线条,其实这就表示这是两个不同的 branch,它们交替展示就代表我在两个 branch 之间切换并提交 commit。

branch

Tips:这是使用 VSCode 中的 Git lens 插件的可视化功能。

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master 分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在 dev 分支上,也就是说,dev 分支是不稳定的,到某个时候,比如 1.0 版本发布时,再把 dev 分支合并到 master 上,在 master 分支发布 1.0 版本;

你和你的小伙伴们每个人都在 dev 分支上干活,每个人都有自己的分支,时不时地往 dev 分支上合并就可以了。

团队合作的分支看起来就像这样:

团队合作

当然我们自己个人开发就没那么多讲究了,怎么舒服怎么来。

本地与远程

分支有本地和远程的区别,也就是所谓的本地分支(local branch)和远程分支(remote branch):

  1. 本地分支是你在自己的机器上进行操作的分支。你可以在本地分支上进行开发,包括创建新的提交、修改文件等。当你满意你的更改并准备将它们分享给其他人时,你可以将你的本地分支推送到远程仓库。

  2. 远程分支是存储在远程仓库(如 GitHub)上的分支,它是多人协作的基础。当你克隆一个远程仓库时,Git 会自动创建一些远程分支来匹配远程仓库中的分支。你可以获取(fetch)远程分支来查看其他人的更改,也可以推送(push)你的本地分支来分享你的更改。

在大多数情况下,一个本地分支会有一个与之关联的远程分支,这被称为跟踪(tracking)。这使得 Git 可以知道当你执行如 git pushgit pull 等命令时应该与哪个远程分支进行交互。

1
2
3
4
5
6
7
8
# 查看本地所有分支
git branch

# 查看远程所有分支
git branch -r

# 查看本地和远程所有分支
git branch -a

Tips:

在 Git 中,fetch 是一个命令,用于从远程仓库获取最新的更改,但不会自动合并或修改你当前的工作。这意味着你可以随时查看远程仓库的更新,而不会影响你的本地工作。

当你执行 git fetch 命令时,Git 会连接到远程仓库,然后下载所有你还没有的数据(包括新的分支、更新的分支、新的提交等)。这些新的更改会被存储在你的本地仓库,但不会影响你的工作目录。

如果你想将这些新的更改应用到你的工作目录,你可以使用 git merge 命令来合并这些更改,或者使用 git checkout 命令来切换到一个新的分支。

当你使用 git branch -a 后,可能会疑惑:“为什么我明明有一个 feature 本地分支,但是这里没有列出来?”

实际上如果你是刚 clone 下来的仓库,本地只会有一个默认的分支(哪个分支是默认的取决于原仓库的设定),当然你可以在 clone 时手动加参数 -b 指定要克隆的远程分支,但是该仓库的其他远程分支并不会一并被创建为本地分支。

也就是说当你 clone 一个远程仓库或者执行 git fetch 命令时,Git 会获取远程仓库的所有分支,但这些远程分支并不会自动转化为本地分支。

但当你使用 git checkout feature 切换到 feature 分支的时候,如果本地不存在 feature 分支,但远程存在,Git 会自动创建一个新的本地 feature 分支,并将其设置为跟踪远程的 feature 分支。这就是为什么你需要先 checkout 到 feature 分支,然后才能在 git branch 的结果中看到它。

branch

Tips:这里执行 git checkout feature 后返回信息:branch ‘feature’ set up to track ‘origin/feature’. Switched to a new branch ‘feature’,可以看出 Git 自动创建了一个 feature 分支并将其设置为跟踪远程的 origin/feature 分支。

remotes/origin/HEAD -> origin/main 这行信息表示的是远程仓库 origin 的默认分支(或称为 HEAD 分支)是 main 分支。

在 Git 中,HEAD 是一个指向你当前正在工作的最新提交的指针。在你切换分支时,HEAD 会改变来指向新分支的最新提交。

在远程仓库中,HEAD 通常指向默认分支。在 GitHub 中,新创建的仓库的默认分支名是 main,所以 origin/HEAD 通常会指向 origin/main。

当你克隆一个远程仓库时,本地仓库的 HEAD 会自动设置为远程仓库的 HEAD 所指向的分支。所以,如果远程仓库的 HEAD 指向 origin/main,那么你克隆后的本地仓库的默认分支也会是 main 分支。

Tips:

在 Git 中,HEAD 是一个指针,指向当前检出的分支的最新提交。你可以把它看作是一个“你当前在哪里”的标记。

当你切换分支或创建新的提交时,HEAD 会自动更新以指向正确的提交。例如,如果你当前在 main 分支,并且刚刚创建了一个新的提交,HEAD 就会指向这个新的提交。如果你切换到 feature 分支,HEAD 就会更新为指向 feature 分支的最新提交。

HEAD 的一个重要用途是确定当前的工作状态。例如,git status 命令会显示你相对于 HEAD(也就是你的当前工作)有哪些更改。同样,git diff HEAD 命令会显示你的工作目录和 HEAD 之间的差异。

此外,HEAD 也常常用在其他的 Git 命令中,例如 git reset HEAD(将当前的工作状态重置为 HEAD 的状态)和 git checkout HEAD(检出 HEAD 指向的提交)。

可能有读者会困扰于 main 和 origin/main 以及 feature 和 origin/feature,名字前面带 origin 的分支与不带的有何不同呢?

实际上,feature 和 origin/feature 通常分别代表本地的 feature 分支和远程的 feature 分支。

  1. feature:这是你在本地创建或者从远程仓库检出(checkout)的 feature 分支。你可以在这个分支上进行提交、合并等操作,这些操作只会影响你的本地仓库。

  2. origin/feature:这是远程仓库 origin 的 feature 分支。当你执行 git fetchgit pull 命令时,Git 会更新这个分支以反映远程仓库的最新状态。你不能直接在这个分支上进行提交或合并操作,但你可以使用 git merge origin/featuregit rebase origin/feature 命令来将这个分支的更改合并到你的本地分支。

另外需要注意的是 git fetchgit pull 都是用来从远程获取最新的版本。然而,这两个命令的工作方式有所不同:

  1. git fetch:这个命令会从远程获取最新的版本到本地,但它不会合并或修改你当前的工作。fetch 只是将远程的数据拉取到你的本地仓库,你需要手动合并。

  2. git pull:这个命令实际上是 git fetchgit merge 的组合。它不仅会获取远程的最新版本,还会自动尝试合并到你当前的工作分支。如果存在冲突,你需要手动解决冲突。

在某些情况下,你可能只想查看远程的更改,但并不想立即合并这些更改到你的工作分支,这时你可以使用 git fetch。如果你确定要获取远程的更改并立即合并,你可以使用 git pull

Tips:

在 Git 中,origin 是默认的远程仓库的名称。当你使用 git clone 命令克隆一个仓库时,Git 会自动创建一个名为 origin 的远程仓库别名,指向你克隆的仓库的 URL。

这只是一个约定俗成的命名规则,你完全可以使用其他的名称。例如,你可以使用 git remote add myrepo url 命令来添加一个名为 myrepo 的远程仓库。

然而,由于 origin 是默认的远程仓库名称,所以很多 Git 命令和教程都会使用它。如果你使用其他的名称,你可能需要在执行 Git 命令时指定远程仓库的名称,例如 git push myrepo master。

本节结束后我们就可以明白 Git 为什么是分布式的版本控制系统了,在 Git 的分布式模型中,每个开发者的机器上都有一个完整的仓库,包含了所有的文件和历史版本。这意味着即使远程仓库丢失,你仍然可以从任何一个开发者的机器上恢复所有的数据。

此外,因为每个开发者都有一个完整的仓库,所以大部分操作(如提交、查看历史、切换分支等)都可以在本地完成,无需连接到远程服务器,这使得 Git 的操作非常快速。

Git 可以在没有 GitHub 的情况下正常工作。实际上,Git 是一个独立的版本控制系统,它并不依赖于任何特定的在线服务。

GitHub 是一个提供 Git 仓库托管服务的平台,它提供了一些额外的功能,如 Pull Request、Issue Tracking 等,这些功能在纯粹的 Git 中并不存在。但是,你完全可以在本地使用 Git,或者使用其他的 Git 仓库托管服务,如 GitLab、Bitbucket 等。

你可以在本地创建 Git 仓库,进行提交、创建分支、合并分支等操作,这些都不需要 GitHub。如果你想与他人共享你的代码,你可以将你的 Git 仓库复制到任何可以访问的服务器上,或者使用其他的 Git 仓库托管服务。

加密通信 - SSH

如果有读者用过 ssh 工具,可能对这个比较熟悉,SSH(Secure Shell)是一个协议,用于在不安全的网络上安全地执行远程命令。ssh 命令是一个客户端程序,用于连接到运行 SSH 服务器的远程机器。

SSH 的使用前提必须要有 SSH 密钥对,你需要在本地生成一个 SSH 密钥对,包括一个私钥和一个公钥。私钥保存在你的本地机器上,公钥需要添加到你的 GitHub 账户。当你尝试连接到 GitHub 时,GitHub 会使用你的公钥来验证你的身份。

Tips:这里的 Github 可以更换为其他任何远程终端,包括但不限于各种云服务器以及局域网中的计算机。

一般的 Linux 和 Windows 系统都会自带 SSH 客户端(Windows 不自带 SSH 服务端,需要手动安装),连接 Github 的过程大致可以分为三步:

  1. 创建密钥对:执行 ssh-keygen -t ed25519 -C "your_email@example.com" 命令,然后一路回车即可(引号内可以填写你的 Github 邮箱地址);
  2. 复制 ~/.ssh/id_ed25519.pub 文件中的所有内容,打开浏览器,进入 “Settings” 页面,点击 “SSH and GPG keys” 选项,然后点击 “New SSH key” 即可,按照要求填入即可;
  3. 在终端执行 ssh -T git@github.com,如果弹出警告,键入 “yes” 即可。

SSH and GPG keys

如果没有出现错误信息,那基本就没问题了。后续就可以使用仓库中的 SSH 链接 clone 仓库了,并且不会碰到要求输入密码的问题,建议读者在本机采用 SSH 方式进行开发。

我的工作流

上文介绍了较为详细深入的内容,但在日常使用中我们并不需要经常这样『原始』的开发项目,我们完全可以采用 VSCode + Github CLI/Github Desktop 的方式简化开发。下面就简单介绍一下本人的日常开发流程。

首先,我们还是需要创建一个文件夹作为仓库来存放代码,然后使用 VSCode 打开这个文件夹,随便新建一个 README 文件。

日常开发

进入 VSCode 的源代码管理功能,点击 “Publish to Github”,这里可以填写发布到 Github 的仓库名和该仓库的权限为公有还是私有。然后勾选要 push 的文件点击 “OK” 即可,打开浏览器便可看到仓库已被推送到 Github。

源代码管理

后续的 add、commit 和 push 操作都在 VSCode 的可视化界面操作即可,没有什么难度。

点击修改过的文件的右侧的加号就相当于 add 操作,输入信息点击 commit 便是 commit 操作,点击 “Sync Changes” 就相当于 push,非常符合人类的操作直觉。还可以在 VSCode 中直观的看到每次的 commit 远程仓库以及分支等等信息。

而 Github CLI 可以通过命令行的方式管理 Github 账号、仓库等等事务。

Github CLI

例如可以通过 gh 命令添加 SSH 公钥到 Github,这在 gh 中只需要一条命令,当然方便与否见仁见智,有人喜欢用命令行,有人喜欢图形化。

如果你更倾向于 GUI 图形化的操作,你可以使用 Github Desktop,这是 Github 官方推出的桌面软件,也可实现 Github CLI 的功能,不过我没有用过这个桌面应用。

至此,本文主要内容已撰写完毕。如果读者还没有习得 Git 和 Github 的使用方法,可以前去 B 站观看更多大佬制作的视频讲解;如果想了解更多细节,可以在 Github Docs 查阅文档,我推荐每一位读者遇到问题时应第一时间查阅官方文档,这是成为一名合格开发人员的必备技能。