Git笔记(五)– 协作

我们已学习了Git的基本知识,本文学习协作。我们将了解协作的工作流程,了解拉取、推送等知识,并将学习如何将工作成果推送到Github,了解如何对开源项目作贡献。


工作流程

我们介绍过,有两种版本控制系统:集中式和分布式。Git属于分布式,彼此可以直接同步工作成果。在一个团队里,往往有多人一起协作,如果每个人彼此经常进行同步,就太复杂且容易出错。我们可以使用一种集中式的工作流程。

每个人都有本地的存储库,同时也有一个中心存储库。但这与集中式版本控制系统相比有什么不同呢?

它没有单点故障!集中式版本控制系统如果中心节点故障,所有人都将无法工作,而Git的集中式工作流程每个人都有存储库,可以各自离线进行工作,在必要的时候也才与中心存储库进行同步。

有些开发团队创建私有服务器存储中心存储库,而有的团队则使用Github或gitlab等公开服务作为中心存储库,只要设置只允许团队内部人员访问即可。

我们来看一下。

JOHN可以将工作成果推送(PUSH)到中心存储库,而AMY可以从中心存储库拉取(PULL),如果碰到冲突,AMY进行冲突处理。AMY也可以将她的工作成果推送到中心存储库,JOHN可以进行拉取。这种方式一般用于团队的闭源开发模式。

还有一种开源开发的(集成管理)模式。它有维护者(Maintainer)和贡献者(Contributor)共同组成。

官方存储库是公开可读的,但只允许维护者写入。贡献者将官方存储库复制过来进行开发,当完成开发后,通知维护者。维护者获取开发者开发的版本并进行检查,如果没有问题,就可以将贡献者的成果推送到官方存储库。


创建 Github 仓库

我们选择Github,登录并创建Mars存储库,可以添加描述,设置私有private或公用public。这里我们选择public。可以选择生成Readme文件,.gitignore文件以及某个许可协议。

创建之后,该存储库的URL为 https://github.com/用户名/Mars,我们能看到目前只有一个分支,一个默认由Github生成的提交,以及README.md文件。


添加协作者

github的存储库即便设为public,其他人也是不能向其提交推送的。需要添加到团队成员里,才有推送权限。

被添加的成员将会收到电子邮件的邀请通知,一旦接受邀请,他将被允许向该存储库进行推送。


克隆存储库

任务团队成员都可以克隆存储库,复制存储库的URL链接,然后到本机执行克隆命令。

git clone https://github.com/kelemi001/Mars.git

存储库名称可以一样,也可以改名,比如MarsProject。

git clone https://github.com/kelemi001/Mars.git MarsProject

键入命令查看历史快照。

git log –oneline –all –graph

我们能看到有个主分支,以及两个红色的分支,origin/main和origin/HEAD,origin表示远程分支。

我们不能切换到远程分支比如 git switch origin/main 将会提示出错。

使用git branch查看,将只会看到一个本地分支。

需要熟悉的一个命令是:git remote。

git remote

git remote -v


获取(Fetching)

本地的存储库与远程存储库是不连接的,远程添加了快照提交,本地是不知道的。我们得使用 git fetch 命令下载新提交并进行合并。

origin/main是一个远程跟踪分支,它告诉我们远程的main分支在哪里。目前本地有两个分支,git可不管该分支是远程的还是本地的。

如果远程分支没有分叉,可以使用快速合并;相反如果有分叉,我们必须像之前的章节介绍过的那样进行解决。

实际操作:

登录GitHub,编辑README.md,并进行提交。

查看本地历史提交,没有刚才提交的快照的。通过执行

git fetch 

这个命令省略了origin main,默认从origin main获取。

再检查历史记录,发现远程的提交已同步过来了。

git log –oneline –all — graph

查看本地与远端跟踪的分支的差异,发现是不一致的。

git branch -vv

我们用之前讲过的命令进行合并

git merge origin/main

然后查看发现本地和远程已一致了。

git log –oneline –all –graph

git branch -vv


拉取(pulling)

绝大多数情况下,我们先从远程获取更新,然后再进行合并。有一个命令可以将这两个操作合二为一,就是拉取pull

pull= fetch+merge

假设,远程中心存储库有了更新,本地存储也有了更新,这时进行拉取操作的话,我们就面临选择。

一种是三方合并。如图,上一次同步在A,远程更新了在C,而本地更新了在B通,过三方合并,创建新提交M。显然,这种合并有点破坏简洁,变得非线性了。

另一种是变基。首先获取远程的C并将基准点指向C,然后加上本地的修改B。这种方式比较简洁保持线性。

哪一种更好?这个争论已有多年了,具体就要看个人喜好及应用场景了。

实际操作下。

登录GitHub,编辑README.md,并进行提交。

再回到本地,创建一个新文件file1.txt: echo hello>file1.txt。提交。

查询历史提交记录,git log –oneline –all –graph

此时本地存储库并不知道远端的更新。

我们进行拉取:

git pull

未加rebase参数,表示进行三方合并。拉取之后,可以用git log 查看确认创建了一个新提交进行了三方合并。

如果加rebase参数,

git pull –rebase

则是一个变基合并。


推送(pushing)

目前本地比远端新,我们想把新提交推送到远端中央存储库。比如本地端有A、B、C三个提交,而远端只有A、B二个提交,执行推送时,远端存储库同步C并将分支指向到C,然后将本地的origin/mail也指向C。

命令是

git push origin main

由于本地和远端的分支都是main可以省略,默认的远端存储库的名字是origin,也是可以省略的。

git push

当推送时,远端已有新的提交,就会被拒绝。比如上次同步是A、B,现在本地增加了C有A、B、C三个提交,远端更新了D是A、B、D,则会被拒绝。有一个命令可以强制同步:

git push -f

但除非很确定,否则不要执行该命令,它将丢掉远端的更新,强制与本地一致。

一般的做法是我们先获取远端的提交。

然后进行合并。

再推送到远端。


存储凭证

推送时,会弹出要求输入用户凭证的界面,有点繁琐。我们可以将凭证存储减少这种干扰。

git config –global credential.helper cache

这种方法将会凭证存储到缓存,15分钟内不会要求输入凭证。

我们也可以将任证永久存储,不同操作系统有所不同。

MAC系统,使用钥匙串:

git credential-osxkeychain

git config –global credential.helper osxkeychain

Windows系统,需要安装Credential Manager for Windows工具。

具体可以访问以下网址获取。

github.com/Microsoft/Git-Credential-Manager-for-Windows


共享标签

默认情况下,Git不会将标签tags传到远端存储库,如果有此需求,就要明确指出。

我们实际操作下。

创建标签v1.0:

git tag v1.0

然后明确推送它:

git push origin v1.0

查看远端存储库,可以看到该标签。生成标签后,它允许用户下载打包的源码。

如果要删除远端的tag,可以显式指明。

git push origin –delete v1.0

本地仍然存在这个tag的,如果也想删除的话,可以执行:

git tag -d v1.0


发布

Github 中与标签密切相关的功能之一是发布管理。我们可以创建一个发布版本,将我们的软件与源代码二进制文件和发布说明一起打包。

登录访问Github的Mars存储库,创建一个发布,命名为v1.0。该操作将会自动创建一个v1.0的标签。我们提供发布的标题,以及发布的说明等信息。我们也可以添加二进制包,比如编译的版本等。如果该发布不是一个稳定版,可以选中“Set as a Pre-release”。

如果发现有错误,也可以很方便地删除发布。

另外要知道,发布不是Git的功能,它只是Github的功能。


共享分支

到目前为止,我们只有main,现在我们来看一个分支。

分支类似于标签(tags),默认情况下是私有。如果要与团队成员在分支上进行协作,推送时需要明确指定,就像之前的tags一样。

实践下。

先创建分支feature/change-password

git switch -C feature/change-password

不带参数的推送。

git push

我们得到一个错误,大致的意思是当前分支没有上游对应的分支。

查看分支链接。

git branch -vv

我们看到,feature/change-password 没有链接到远端的分支。

如果我们想查看远端都有哪些分支,可以用这个。

git branch -r

我们将本地的feature/change-password分支明确地推送到远端

git push -u origin feature/change-password

-u 相当于 –Upstream,这个命令执行成功之后,我们的分支也被推送到远端了。

我们可以登录Github验证下。也可以通过以下命令查看。

git branch -vv

git branch -r

现在远端也已跟踪了feature/change-password,我们可以像在main分支一样操作feature/change-password了,我们可以在该分支做些新的提交并同步推送到远端存储库。

如果想删除远端分支,可以用以下命令。

git push -d origin feature/change-password

-d 参数表示删除。检查远端分支跟踪情况:

git branch -r

git branch -vv

我们看到提示远端分支已丢失,但本地分支是存在的,如果想删除,则使用之前章节介绍过的方法删除。

git branch

git switch main

git branch -d feature/change-password


协作过程

我们模拟一个协作实例。假设Amy和我协作开发一个新功能。

首先创建一个分支,这次我们在Github上创建,分支名称为feature/change-password。

回到本地控制台,检查

git branch

自然是没有这个新分支的,获取下

git fetch

但当我们再用git branch检查发现,仍然只有main,用git branch -r 检查是能看到远端的feature/change-password分支的。

所以,当我们用运行 git fetch时,git 只是获取了远端的跟踪。我们必须手动创建本地私有分支并与远端进行关联。

git switch -C feature/change-password origin/feature/change-password

现在我们到Amy的客户端:

首先是复制Git存储库。

git clone https://github.com/kelemi001/Mars.git

检查分支,只有主分支main。

git branch

同样我们需要手动创建分支feature/change-password,并与远端的分支关联。

git switch -C feature/change-password origin/feature/change-password

然后,Amy创建新提交,并推送到远端。

echo password > file1.txt

git commit -am “Update file1”

git push

可以到Github上进行验证新提交。

回到自己控制台,进行拉取,并将feature/change-password分支的提交合并到main

git pull

git switch main

git merge feature/change-password

检查,本地main与本地及远端feature/change-password分支指向同一点了。

git log –oneline –all –graph

然后,我们再将主分支推送到GitHub。检查本地远端的main和feature/change-password都指向同一点了。

git push

git log –oneline –all –graph

当分支功能开发完成后,我们需要将分支关闭,避免污染主分支的线性简洁

先将远端的跟踪分支删除。

git push -d origin feature/change-password

再将本地的分支删除。

git branch -d feature/change-password

检查确认远端的分支已删除了。

git branch -r

那Amy客户端如何操作

首先获取

git pull

检查分支仍然是存在的

git branch 

删除本地分支。

git branch -d feature/change-password

但用 git branch -r 检查发现,远端跟踪分支仍然存在。

可以使用下面命令清除。

git remote prune origin

以上就是两个或多个人协作完成分支功能的方式。


拉取请求

我们希望其他团队成员对我们的代码提供反馈,这时我们会使用拉取请求。拉取请求的作用是,在合并并分支到主分支之前,团队可以先进行讨论。

假设 Amy 要实现一个新功能,创建新分支并将该分支推送到Github。

git switch -C feature/login

echo hello > file3.txt

git add .

git commit -m “Write hello to file3”

git push -u origin feature/login

现在到Github:

找到 Pull Request 标签,并创建新Pull Request。

选择分支,这里是main和feature/login,并写明title和description。以及选择参与讨论者。

参与讨论者会收到email,登录GitHub时会收有拉取请求。

 添加评论意见,并提交为注释comment。

回到Amy客户端,再添加一个新提交:

echo Hello > file3.txt

git commit -am “Capitalize Hello.”

git push

Amy做了提新交,需要我们再次发表意见。可以选择“Viewed”,并评论类似”Everything looks perfect. Let’s go ahead with merging!”

并选择 “Approve”。

现在我们能够进行合并了,谁来合并?

有两种不同的观点:评论者或发起请求者。

但各有各的道理,具体要看项目的实际情况。

合并可以在Github上创建一个合并,然后点击删除分支。

再到Amy的控制台,拉取刚才在远端的操作,因为已经不再需要feature/login分支了,清除远端的分支跟踪,并删除本地的该分支。

git switch main

git pull

git remote prune origin

检查确认已清除

git branch -r

然后删除本地分支

git branch -d feature/login


解决冲突

在关闭拉取请求并进行合并时,可能会出现冲突,我们看实例。

在Amy的本地创建分支feature/logout,修改file1.txt文件提交一个快照,并将分支推送到Github。

git switch -C feature/logout

echo hello > file1.txt

git commit -am “Write hello to file1”

git push -u origin feature/logout

登录Github能看到这个feature/logout分支,我们人为给它创造一些冲突。回到本地控制台,切换到main分支,修改同一个文件file1.txt文件并推送到Github。

git switch main

echo world > file1.txt

git commit -am “Write world to file1”

git push

现在,假设Amy已完成分支feature/logout的工作,想创建一个拉取请求pull request。Github会提示不能自动进行合并,但仍然可以创建拉取请求,我们设置该拉取请求标题为Feature: Logout,系统提示有冲突必须要解决。

处理冲突一般有两种选择:命令行 或者 使用Github界面。命令行的方式前面章节有讲过,通过拉取最新的代码,然后解决冲突,并新提交一个快照并推送到Github。这里我们就用Github界面来处理。

通过编辑冲突文件file1.txt,完成后点“Mark as resolved”,再进行合并就可以了。


Github Issues

Github上进行问题跟踪,跟拉取请求一样重要,可以跟踪Bugs、新功能等问题。我们实际来操作下。

在Githubr存储库Mars上创建一个Issue,设置标题、描述,也可以附件文件。并赋给某个人参与讨论,可与标签(Label,下节讨论)关联。也可以与拉取请求关联,当拉取请求关闭时,该问题也自动关闭。还可以与里程碑(MileStone)等进行关联。


标签(Label)

在每个Github存储库中,默认已存在一些标签,我们也可以自定义标签。可以设置标签的标题、描述以及颜色等。并可以将问题(Issue)赋于某个标签。


里程碑(Milestones)

Github的里程碑用于跟踪多个问题(Issues),我们可以将一系列Issues加到某个里程碑,然后观察问题处理的进度。

如图,我们创建里程碑,名称为2.0.0,截止日期为2025/11/10,并将问题赋给该里程碑。

然后我们可以查看该里程碑,到问题全部处置完成后,里程碑进度将为100%。


为开源项目做贡献

直接看实例,比如我们想对存储库 chenyongping001/game-hub 做贡献,显然我们不能将修改提交上去的,因为该存储库不属于我们。

我们可以在Github上进行 fork ,将该存储库复制过来。

克隆到本地工作目录进行操作。操作完成推送到GitHub。

git clone https://github.com/kelemi001/game-hub.git

cd game-hub

git switch -C bugfix

echo hello > README.md

git commit -am “Update README”

git push -u origin bugfix

然后再到Github上,启动拉取请求。其中基准分支可以选择我们要贡献的Fork的存储库。

开源存储库的维护者将收到电子邮件,登录后可以维护,如果没有问题,可以进行合并。


保持 Fork 仓库更新

有个问题:从何保持Fork仓库更新呢?Fork仓库如图所示。

我们可以给本地库添加与原始存储库的引用,然后从原始存储库拉取,上传则向Fork存储库。这样就能保持Fork为最新。

实际操作下。

我们知道, git remote 命令用于查看远程存储库。加 -v 参数可以查看更详细的信息。

git remote -v 

我们我看有两个通道:fetch 用于获取新对象,push 用于推送对象。这两个通道都映射到URL。 

我们添加原始存储库。

git remote add upstream https://github.com/chenyongping001/game-hub.git

其中upstream 是命名,可以顺便取。

再用 git remote -v 查看,能看到新的远端存储库。

git remote -v

我们可以重命名该远程存储库,比如改成 base。

git remote rename upstream base

我们也可以用下面命令删除。这里我们暂不删除。

git remote rm base

然后我们修改原始存储库,比如修改README.md,并进行提交。

再回到Fork库。执行命令。

git fetch base

如果不指明 base,默认是获取 origin的。

查询下当前的的提交并进行合并。

 git log –oneline –all –graph

git merge base/main

现在本地存储库已与原base存储库一致了。我们需要将更改推送到Fork存储库。

git push

在Github检查Fork存储库。发现已做了更新。

将主分支合并到当前的分支总是一个不错的实践,可以减少未来可能的冲突。

git switch bugfix

git merge main


小结

本文介绍协作的工作流程,了解拉取、推送等知识,并介绍如何将工作成果推送到Github,了解如何对开源项目作贡献。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注