内容纲要

查看【Git】专题可浏览更多内容

如果我们直接对一个第三方的 Github 项目进行推送将会看到这样的提示:

ERROR: Permission to github/repo.git denied to Example.
致命错误:无法读取远程仓库。

请确认您有正确的访问权限并且仓库存在。

解决这个权限问题的办法有两种:

作为 Collaborator

如果这个 Github 项目实际上是团队或公司的一个项目,那么可以让项目的所有人邀请你,给予你权限。

在项目的「Settings」的「Access」下的「Collaborators」中,可以通过「Add People」添加 Github 用户:

在添加后拥有了这个仓库的权限,就可以正常推送了。

除了这个方法还有一种常见的场景是,你遇到了一个开源在 Github 上的项目,但它有点小问题而你恰好知道解决办法,那么除了提 Issue 也可以自己改进好然后申请提交给项目主,这就是第二种方法

使用 Fork 及 Pull Request

Fork

首先,我们先进行 Fork

点击要参与贡献的项目右上角的「Fork」,然后可选修改 Fork 到你 Github 帐户上的仓库名

所谓「Fork」仓库类似于复制仓库,用于更改项目而不影响原始仓库。 之后可以通过拉取请求从原始仓库提取更新,或者提交更改到原始仓库。

注意左上角,可以看到现在这个项目来源于哪个帐户下的什么仓库

然后就可以根据你的方式选择 HTTPS 或 SSH 将仓库克隆到本地计算机上,如果你没有做过这方面的验证可以查看 使用 SSH 或 Token 向 GitHub 进行身份验证 一文

创建特性/开发分支

在克隆自己帐户上 Fork 而来的远程仓库到本地后,第一步应该是创建一个特性分支用于改动,而不是直接在主分支 (mastermain) 上改动

# 创建并切换到名为 feature 的分支
git switch -c feature

然后就可以做改动并推送 feature:

git add .
git commit -m '改动标题' -m '改动内容描述'

# 推送 feature 分支
git push -u origin feature

到这里,你的 Github 帐户的仓库就有了 feature 分支

原仓库有了更新

在我们进行本地修改的时候,很可能原仓库有了别的更新,这时候要如何同步原仓库的改动?

你可以选择 Github 页面上的「Sync fork」功能点点鼠标:

之后你要从 feature 分支切换回主分支,然后 git pull origin master 拉取下来

或者通过命令行实现:

首先我们要在本地计算机的仓库上添加原仓库的远程信息:

# 添加原仓库的远程信息,将原仓库命名为 upstream
git remote add upstream https://github.com/github/repo.git
# 查看下远程仓库信息
git remote -v
origin  https://github.com/Nobody/repo.git (fetch)
origin  https://github.com/Nobody/repo.git (push)
upstream    https://github.com/github/repo.git (fetch)
upstream    https://github.com/github/repo.git (push)

origin 表示我们 Github 帐户上 Fork 而来的仓库,upstream 表示所 Fork 的原仓库

接着获取原仓库数据:

git fetch upstream

然后要合并原仓库的更新到本地主分支上了,当前是不是还在feature 分支上开发呢?

# 切换到主分支
git switch master

并不是所有的项目的主分支都叫 master,因为「政治正确」现在许多新项目改叫 main 了,根据你的实际情况来吧

然后将改动合并到本地当前分支:

git merge upstream/master

同样的,这里如果原项目中的主分支为 main 就要修改为 upstream/main

现在你本地仓库的 master 分支已经和原仓库的更新同步了,但你 Github 上 Fork 而来的仓库还没有,所以如果有必要就进行推送 master

git push origin master

推送特性/开发分支

同步了原仓库的改动后,现在回到 feature 分支,假设现在改动也改动好了可以推送了,那么:

# 回到 feature 分支
git switch feature

# 先把原仓库同步到本地 master 分支的最新修改放进来,再放上自己的修改
git rebase master
# 使用以上命令后可以使用 git log 看看历史记录的变化

# 然后推送 feature 到 github
git push -f origin feature

为什么最后一步加了 -f 选项,不是都说最好不用该选项吗?

首先这个操作只是在自己的项目上,其次如果在 git rebase 操作之前如上示例,在拉取原仓库最新修改前有过一次推送,那么当前本地 feature 和你 Github 上的 feature 是有所不同的。

你本地 feature 的历史中多了原仓库的更新,和之前推送不一样了。

创建 Pull Request

前往你的 Github 项目,点击「Pull requests」

点击右侧的「New pull request」按钮创建一个 Pull Request

如上图,主要是三个部分:

  1. 请求的项目为 github/repo,从我的 Github 项目 Nobody/repofeature 分支合并修改
  2. 然后是填写请求的标题及内容,为什么要做这些、大概做了什么修改进行描述一下;
  3. 在页面底部还有修改前后的修改对比部分,不过这部分不是本文重点就不截图了;

在确定后就可以点击「Create pull request」了

管理 Pull Request

现在转换身份,如果你是一个项目主,该如何处理 Pull Request 呢?

你可以在这个界面预览修改、改动了什么文件、有哪些提交,也可以使用右侧列表的功能,比如加上标签等等

你还可以在 Commit 中的具体一行通过点击蓝色加号按钮添加评论,例如说你认为某段代码可以进一步改进等等

最后如果你觉得这次请求并不合理可以选择「Close pull request」,或点击绿色按钮进行 Merge

Pull Request 的几种合并有什么不同?

在 Merge 按钮的右侧有个下拉按钮,你可以看到有几种不同的合并方式,那么它们有什么不同呢?

「Create a merge commit」:

*   commit 686785618ac8795a7eaa412049e7f859869ed8f3 (HEAD -> master, origin/master, origin/HEAD)
|\  Merge: 9ef96c8 94fd1f1
| | Author: Github <119550803+github@users.noreply.github.com>
| |
| |     Merge pull request #1 from Nobody/repo
| |
| * commit 94fd1f1f76b3c6a0a296ea64f0983aafc141230d
| | Author: Nobody <nobody@example.com>
| |
| |     docs: Fix content
| |
| * commit 8f832d016c82b0b5d1f8dad27aba0b507f468052
|/  Author: Nobody <nobody@example.com>
|
|       docs: Fix title
|
* commit f6112e6c5721a95d204f215227c48c9f7d33dee1
| Author: Github <example@github.com>
|
|     docs: add README.md
|
* commit b36602c3bf6c3be2a989d48e35834a14757b2550
  Author: Github <119550803+github@users.noreply.github.com>

      Initial commit

可以看到,使用「Create a merge commit」除了贡献者自己的提交,还会多一个合并提交,即「Merge pull request #1 from Nobody/repo」

并且可以看到支线有分开在合并的记录

「Squash and merge」:

* commit b27cfa1afdafea6d740f01668d7ace5de0cb4eab (HEAD -> master, origin/master, origin/HEAD)
| Author: Nobody <nobody@example.com>
|
|     docs: Fix format error
|
* commit f6112e6c5721a95d204f215227c48c9f7d33dee1
| Author: Github <i@github.com>
|
|     docs: add README.md
|
* commit b36602c3bf6c3be2a989d48e35834a14757b2550
  Author: Github <119550803+github@users.noreply.github.com>

      Initial commit

而使用「Squash and merge」会将贡献者的提交合并成一条提交

「Rebase and merge」

* commit 94fd1f1f76b3c6a0a296ea64f0983aafc141230d (HEAD -> master, origin/master, origin/HEAD)
| Author: Nobody <nobody@example.com>
|
|     docs: Fix content
|
* commit 8f832d016c82b0b5d1f8dad27aba0b507f468052
| Author: Nobody <nobody@example.com>
|
|     docs: Fix title
|
* commit f6112e6c5721a95d204f215227c48c9f7d33dee1
| Author: Github <i@github.com>
|
|      docs: add README.md
|
* commit b36602c3bf6c3be2a989d48e35834a14757b2550
  Author: Github <119550803+github@users.noreply.github.com>

      Initial commit

使用「Rebase and merge」会将贡献者的提交记录直接合并入分支,而且可以看到分支是一条直线