-
附录 A: Git 在其他环境中的使用
- A1.1 图形界面
- A1.2 Git 在 Visual Studio 中
- A1.3 Git 在 Visual Studio Code 中
- A1.4 Git 在 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中
- A1.5 Git 在 Sublime Text 中
- A1.6 Git 在 Bash 中
- A1.7 Git 在 Zsh 中
- A1.8 Git 在 PowerShell 中
- A1.9 总结
-
附录 B: 在应用程序中嵌入 Git
-
附录 C: Git 命令
5.2 分布式 Git - 为项目贡献代码
为项目贡献代码
描述如何为项目贡献代码的主要困难在于,贡献方式存在多种变体。由于 Git 非常灵活,人们可以(并且确实会)以多种方式协同工作,因此很难描述应该如何贡献代码——每个项目都略有不同。涉及的一些变量包括活跃贡献者数量、选择的工作流程、你的提交权限,以及可能存在的外部贡献方法。
第一个变量是活跃贡献者数量——有多少用户积极地为该项目贡献代码,以及贡献频率?在许多情况下,你会看到两到三位开发者,每天提交几次,或者对于一些处于休眠状态的项目,频率可能更低。对于大型公司或项目来说,开发人员的数量可能达到数千人,每天有数百或数千个提交。这一点很重要,因为随着开发人员数量的增加,你将遇到更多问题,例如确保你的代码能够干净地应用或能够轻松合并。在你工作期间或你的更改等待批准或应用期间,合并到主代码库中的工作可能会使你提交的更改过时或严重损坏。如何才能始终保持你的代码更新,并保证你的提交有效?
下一个变量是项目使用的工作流程。它是集中式的,每个开发者都拥有对主代码库的相同写入权限吗?项目是否有维护者或集成经理负责检查所有补丁?所有补丁是否都经过同行评审并获得批准?你是否参与了这个过程?是否设置了中尉系统,你需要先将你的工作提交给他们吗?
下一个变量是你的提交权限。如果你有权写入项目,那么为项目贡献代码所需的工作流程与你没有写入权限的情况截然不同。如果你没有写入权限,项目首选如何接受贡献的工作?项目是否有相应的策略?你一次贡献多少工作?你贡献代码的频率如何?
所有这些问题都会影响你如何有效地为项目贡献代码,以及哪些工作流程适合你或对你有用。我们将通过一系列用例来讲解每个方面,从简单到复杂;你应该能够从这些示例中构建你在实践中需要的特定工作流程。
提交指南
在我们开始查看特定用例之前,这里简要说明一下提交信息。制定一个良好的提交信息创建指南并严格遵守它,能够让使用 Git 和与他人协作变得更加轻松。Git 项目提供了一份文档,其中列出了许多创建提交信息的良好技巧,用于提交补丁——你可以在 Git 源代码的Documentation/SubmittingPatches
文件中找到它。
首先,你的提交不应包含任何空白字符错误。Git 提供了一个简单的方法来检查这一点——在你提交之前,运行git diff --check
,它会识别潜在的空白字符错误并列出它们。
git diff --check
的输出如果你在提交之前运行该命令,就可以判断你是否即将提交可能会让其他开发者感到困扰的空白字符问题。
接下来,尝试使每个提交成为一个逻辑上独立的变更集。如果可以,尝试使你的更改易于消化——不要在周末花整个周末处理五个不同的问题,然后在周一将它们全部提交为一个巨大的提交。即使你在周末没有提交,也要在周一使用暂存区将你的工作拆分为每个问题至少一个提交,并为每个提交添加有用的信息。如果一些更改修改了同一个文件,尝试使用git add --patch
来部分暂存文件(在交互式暂存中详细介绍)。分支顶端的项目快照无论你进行一次提交还是五次提交都是一样的,只要所有更改在某个时间点都已添加,所以尝试在其他开发人员必须审查你的更改时,使他们更容易处理。
这种方法也使以后更容易提取或回滚其中一个变更集。重写历史描述了许多有用的 Git 技巧,用于重写历史并交互式暂存文件——使用这些工具帮助你创建一个干净且易于理解的历史,然后再将工作发送给其他人。
最后要记住的是提交信息。养成创建高质量提交信息的习惯会使使用和协作 Git 变得容易得多。一般来说,你的信息应该以一行不超过大约 50 个字符且简要描述变更集的单行开头,后面跟着一个空行,然后是一个更详细的解释。Git 项目要求更详细的解释包括你更改的动机以及将其实现与之前的行为进行对比——这是一个很好的遵循指南。以祈使句编写你的提交信息:“修复错误”,而不是“已修复错误”或“修复错误”。以下是一个你可以遵循的模板,我们对其进行了轻微调整,它最初由 Tim Pope 编写
Capitalized, short (50 chars or less) summary
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase will confuse you if you run the
two together.
Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
or "Fixes bug." This convention matches up with commit messages generated
by commands like git merge and git revert.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, followed by a
single space, with blank lines in between, but conventions vary here
- Use a hanging indent
如果所有提交信息都遵循此模型,那么对你和你合作的开发人员来说,事情会变得容易得多。Git 项目拥有格式良好的提交信息——尝试在那里运行git log --no-merges
看看格式良好的项目提交历史是什么样子的。
注意
|
照我们说的做,不要照我们做的做。
为了简洁起见,本书中的许多示例都没有像这样格式良好的提交信息;相反,我们只是简单地使用 简而言之,照我们说的做,不要照我们做的做。 |
私有小型团队
你可能遇到的最简单的设置是与一两个其他开发人员合作的私有项目。“私有”在此上下文中的意思是封闭源代码——对外部世界不可访问。你和其他的开发人员都拥有对存储库的推送访问权限。
在这种环境中,你可以遵循类似于使用 Subversion 或其他集中式系统时可能进行的工作流程。你仍然可以获得离线提交以及更简单的分支和合并等优点,但工作流程可能非常相似;主要区别在于合并发生在客户端,而不是在提交时发生在服务器上。让我们看看当两个开发人员开始使用共享存储库协作时,它可能是什么样子。第一个开发人员 John 克隆了存储库,进行了一些更改,并在本地提交了更改。在这些示例中,协议消息已替换为…
,以简化它们。
# John's Machine
$ git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'Remove invalid default value'
[master 738ee87] Remove invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)
第二个开发人员 Jessica 做了同样的事情——克隆了存储库并提交了更改
# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'Add reset task'
[master fbff5bc] Add reset task
1 files changed, 1 insertions(+), 0 deletions(-)
现在,Jessica 将她的工作推送到服务器,这工作正常
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
上面的输出的最后一行显示了推送操作的有用返回消息。基本格式为<oldref>..<newref> fromref → toref
,其中oldref
表示旧引用,newref
表示新引用,fromref
是被推送的本地引用的名称,toref
是正在更新的远程引用的名称。你将在下面的讨论中看到类似的输出,因此了解基本含义将有助于理解存储库的各种状态。有关更多详细信息,请参阅git-push的文档。
继续此示例,不久之后,John 进行了一些更改,将它们提交到他的本地存储库,并尝试将它们推送到同一个服务器
# John's Machine
$ git push origin master
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
在这种情况下,John 的推送失败,因为 Jessica 之前推送了她的更改。如果你习惯了 Subversion,这尤其重要,因为你会注意到两个开发人员没有编辑同一个文件。尽管 Subversion 会在服务器上自动执行此类合并(如果编辑了不同的文件),但使用 Git,你必须首先在本地合并提交。换句话说,John 必须首先获取 Jessica 的上游更改,并将它们合并到他的本地存储库中,然后他才能被允许推送。
作为第一步,John 获取 Jessica 的工作(这只会获取 Jessica 的上游工作,它尚未将其合并到 John 的工作中)
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
此时,John 的本地存储库看起来像这样
现在 John 可以将获取的 Jessica 的工作合并到他自己的本地工作中
$ git merge origin/master
Merge made by the 'recursive' strategy.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
只要本地合并顺利进行,John 的更新后的历史将看起来像这样
origin/master
后的 John 的存储库此时,John 可能希望测试此新代码以确保 Jessica 的工作不会影响他的任何工作,只要一切看起来都很好,他就可以最终将合并后的新工作推送到服务器
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
最后,John 的提交历史将看起来像这样
origin
服务器后的 John 的历史同时,Jessica 创建了一个名为issue54
的新主题分支,并为此分支进行了三次提交。她还没有获取 John 的更改,因此她的提交历史看起来像这样
突然,Jessica 了解到 John 已将一些新工作推送到服务器,她想看看它,因此她可以使用以下命令从服务器获取她尚未拥有的所有新内容
# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master
这会拉取 John 同时推送的工作。Jessica 的历史现在看起来像这样
Jessica 认为她的主题分支已准备就绪,但她想知道 John 的获取工作中哪部分需要合并到她的工作中,以便她能够推送。她运行git log
来找出
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <[email protected]>
Date: Fri May 29 16:01:27 2009 -0700
Remove invalid default value
issue54..origin/master
语法是一个日志过滤器,它要求 Git 仅显示位于后者分支(在本例中为origin/master
)且不在前者分支(在本例中为issue54
)上的提交。我们将在提交范围中详细介绍此语法。
从上面的输出中,我们可以看到 John 进行了一次提交,Jessica 尚未将其合并到她的本地工作中。如果她合并了origin/master
,那就是唯一会修改她本地工作的提交。
现在,Jessica 可以将她的主题工作合并到她的master
分支中,将 John 的工作(origin/master
)合并到她的master
分支中,然后再次推送到服务器。
首先(在她提交了issue54
主题分支上的所有工作之后),Jessica 切换回她的master
分支,准备整合所有这些工作
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
Jessica 可以先合并origin/master
或issue54
——它们都是上游,因此顺序无关紧要。无论她选择哪个顺序,最终的快照都应该相同;只有历史记录会不同。她选择先合并issue54
分支
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)
没有出现任何问题;如你所见,这是一个简单的快进合并。Jessica 现在通过合并 John 的先前获取工作(位于origin/master
分支中)来完成本地合并过程
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
一切都合并得干净利落,Jessica 的历史现在看起来像这样
现在origin/master
可以从 Jessica 的master
分支访问,因此她应该能够成功推送(假设 John 同时没有推送更多更改)
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
每个开发人员都提交了几次,并成功地合并了彼此的工作。
这是最简单的工作流程之一。你工作一段时间(通常在主题分支中),并在准备好集成时将该工作合并到你的master
分支中。当你想要共享该工作时,如果origin/master
有更改,请获取并合并你的master
,最后推送到服务器上的master
分支。一般顺序类似于以下
私有托管团队
在下一个场景中,你将查看大型私有组中的贡献者角色。你将学习如何在小型团队协作开发功能的环境中工作,之后这些基于团队的贡献将由另一方整合。
假设 John 和 Jessica 正在一起开发一个功能(称为“featureA”),而 Jessica 和第三位开发人员 Josie 正在开发第二个功能(比如,“featureB”)。在这种情况下,公司正在使用一种集成管理器工作流程,其中个人组的工作仅由某些工程师整合,并且主仓库的master
分支只能由这些工程师更新。在这种情况下,所有工作都在基于团队的分支中完成,并在稍后由集成人员整合在一起。
让我们跟踪 Jessica 在她的两个功能上的工作流程,她在这个环境中与两个不同的开发人员并行协作。假设她已经克隆了她的存储库,她决定先开发featureA
。她为该功能创建一个新分支,并在那里进行一些工作
# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'Add limit to log function'
[featureA 3300904] Add limit to log function
1 files changed, 1 insertions(+), 1 deletions(-)
此时,她需要与 John 分享她的工作,因此她将 featureA
分支的提交推送到服务器。Jessica 没有 master
分支的推送权限 - 只有集成人员有 - 所以她必须推送到另一个分支才能与 John 协作。
$ git push -u origin featureA
...
To jessica@githost:simplegit.git
* [new branch] featureA -> featureA
Jessica 给 John 发送电子邮件,告诉他她已将一些工作推送到名为 featureA
的分支,他现在可以查看。在等待 John 的反馈时,Jessica 决定与 Josie 一起开始开发 featureB
。首先,她从服务器的 master
分支创建一个新的功能分支。
# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'
现在,Jessica 在 featureB
分支上进行了一些提交。
$ vim lib/simplegit.rb
$ git commit -am 'Make ls-tree function recursive'
[featureB e5b0fdc] Make ls-tree function recursive
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'Add ls-files'
[featureB 8512791] Add ls-files
1 files changed, 5 insertions(+), 0 deletions(-)
Jessica 的仓库现在看起来像这样。
她准备推送她的工作,但收到了 Josie 的电子邮件,说一个包含一些初始“featureB”工作的分支已经被推送到服务器,作为 featureBee
分支。Jessica 需要将这些更改与她自己的更改合并,才能将她的工作推送到服务器。Jessica 首先使用 git fetch
获取 Josie 的更改。
$ git fetch origin
...
From jessica@githost:simplegit
* [new branch] featureBee -> origin/featureBee
假设 Jessica 仍然在已签出的 featureB
分支上,她现在可以使用 git merge
将 Josie 的工作合并到该分支中。
$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
此时,Jessica 想将所有这些合并的“featureB”工作推回服务器,但她不想仅仅推送她自己的 featureB
分支。相反,由于 Josie 已经启动了一个上游 featureBee
分支,Jessica 想推送到该分支,她使用以下命令进行操作:
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee
这被称为refspec。有关 Git refspec 和不同操作方法的更详细讨论,请参阅 Refspec。还要注意 -u
标志;它是 --set-upstream
的缩写,它为分支配置了更方便的推送和拉取操作。
突然,Jessica 收到了 John 的电子邮件,他告诉她,他已将一些更改推送到他们正在协作的 featureA
分支,并要求 Jessica 查看这些更改。Jessica 再次运行一个简单的 git fetch
来获取服务器上的所有新内容,包括(当然)John 的最新工作。
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA
Jessica 可以通过比较新获取的 featureA
分支的内容和她本地副本的相同分支的内容来显示 John 的新工作的日志。
$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <[email protected]>
Date: Fri May 29 19:57:33 2009 -0700
Increase log output to 30 from 25
如果 Jessica 喜欢她看到的内容,她可以将 John 的新工作合并到她的本地 featureA
分支中,使用:
$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
最后,Jessica 可能想对所有这些合并的内容进行一些小的更改,因此她可以自由地进行这些更改,将它们提交到她的本地 featureA
分支,并将最终结果推回服务器。
$ git commit -am 'Add small tweak to merged content'
[featureA 774b3ed] Add small tweak to merged content
1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
3300904..774b3ed featureA -> featureA
Jessica 的提交历史现在看起来像这样。
在某个时候,Jessica、Josie 和 John 通知集成人员,服务器上的 featureA
和 featureBee
分支已准备好集成到主线上。在集成人员将这些分支合并到主线后,获取操作将下载新的合并提交,使历史记录看起来像这样。
许多团队转向 Git 是因为这种能力,即允许多个团队并行工作,并在流程后期合并不同的工作线。团队中较小的子团队能够通过远程分支进行协作,而无需一定涉及或阻碍整个团队,这是 Git 的一大优势。您在此处看到的流程的顺序类似于以下内容:
分叉的公共项目
对公共项目的贡献有点不同。因为您没有直接更新项目分支的权限,所以您必须通过其他方式将工作提交给维护者。此第一个示例描述了通过在支持轻松分叉的 Git 主机上分叉进行贡献。许多托管网站支持这一点(包括 GitHub、BitBucket、repo.or.cz 等),许多项目维护者也希望这种贡献方式。下一节将介绍更喜欢通过电子邮件接受贡献补丁的项目。
首先,您可能需要克隆主仓库,为要贡献的补丁或补丁系列创建一个主题分支,并在那里进行您的工作。顺序基本如下:
$ git clone <url>
$ cd project
$ git checkout -b featureA
... work ...
$ git commit
... work ...
$ git commit
注意
|
您可能希望使用 |
当您的分支工作完成,并且您准备好将它贡献回维护者时,请转到原始项目页面,单击“分叉”按钮,创建您自己的可写项目分叉。然后,您需要将此仓库 URL 添加为本地仓库的新远程仓库;在此示例中,我们将其称为 myfork
。
$ git remote add myfork <url>
然后,您需要将新工作推送到此仓库。将您正在处理的主题分支推送到分叉的仓库,而不是将该工作合并到您的 master
分支并推送到那里,这更容易。原因是,如果您的工作没有被接受或被 cherry-pick,您就不必倒回您的 master
分支(Git 的 cherry-pick
操作将在 变基和 Cherry-Pick 工作流程 中更详细地介绍)。如果维护者 merge
、rebase
或 cherry-pick
您的工作,您最终将通过从他们的仓库拉取来获得它。
在任何情况下,您都可以使用以下命令推送您的工作:
$ git push -u myfork featureA
将您的工作推送到您分叉的仓库后,您需要通知原始项目的维护者,您有工作要他们合并。这通常称为pull request,通常您可以通过网站生成这样的请求 - GitHub 有自己的“Pull Request”机制,我们将在 GitHub 中介绍 - 或者您可以运行 git request-pull
命令,并将后续输出手动发送给项目维护者。
git request-pull
命令将您要将主题分支拉取到的基分支和要从其拉取的 Git 仓库 URL 作为参数,并生成您要求拉取的所有更改的摘要。例如,如果 Jessica 想要向 John 发送一个 pull request,并且她在刚刚推送到主题分支上做了两个提交,她可以运行以下命令:
$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jessica Smith (1):
Create new function
are available in the git repository at:
https://githost/simplegit.git featureA
Jessica Smith (2):
Add limit to log function
Increase log output to 30 from 25
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
此输出可以发送给维护者 - 它告诉他们工作是从哪里分支出来的,总结了提交,并标识了要从哪里拉取新工作。
在您不是维护者的项目中,通常更容易让一个分支(如 master
)始终跟踪 origin/master
,并在主题分支中进行您的工作,这样您可以在拒绝的情况下轻松放弃它们。将工作主题隔离到主题分支中,也使您更容易重新整理您的工作,如果主仓库的顶端已移动,并且您的提交不再干净地应用。例如,如果您想向项目提交第二个主题工作,不要继续处理您刚刚推送到主题分支 - 从主仓库的 master
分支重新开始。
$ git checkout -b featureB origin/master
... work ...
$ git commit
$ git push myfork featureB
$ git request-pull origin/master myfork
... email generated request pull to maintainer ...
$ git fetch origin
现在,您的每个主题都包含在一个筒仓中 - 类似于补丁队列 - 您可以重写、变基和修改它们,而不会让主题互相干扰或相互依赖,如下所示:
featureB
工作的初始提交历史记录假设项目维护者已拉取了许多其他补丁,并尝试了您的第一个分支,但它不再干净地合并。在这种情况下,您可以尝试将该分支变基到 origin/master
的顶部,解决维护者的冲突,然后重新提交您的更改。
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
这将重写您的历史记录,使其现在看起来像 featureA
工作后的提交历史记录。
featureA
工作后的提交历史记录因为您对分支进行了变基,所以您必须在推送命令中指定 -f
,以便能够用不是其后代的提交替换服务器上的 featureA
分支。另一种选择是将此新工作推送到服务器上的另一个分支(可能称为 featureAv2
)。
让我们看一下另一种可能的情况:维护者查看了您第二个分支中的工作,并喜欢这个概念,但希望您更改实现细节。您也将利用此机会将工作移至基于项目的当前 master
分支。您从当前 origin/master
分支开始一个新的分支,将 featureB
更改压缩到那里,解决任何冲突,进行实现更改,然后将其推送到一个新的分支。
$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
... change implementation ...
$ git commit
$ git push myfork featureBv2
--squash
选项将合并分支上的所有工作压缩成一个更改集,生成与实际合并发生时一样的仓库状态,而不会实际创建合并提交。这意味着您将来的提交将只有一个父节点,并允许您引入来自另一个分支的所有更改,然后在记录新提交之前进行更多更改。此外,--no-commit
选项在默认合并过程的情况下可以用来延迟合并提交。
此时,您可以通知维护者您已进行了请求的更改,并且他们可以在您的 featureBv2
分支中找到这些更改。
featureBv2
工作后的提交历史记录通过电子邮件进行的公共项目
许多项目已经建立了接受补丁的流程 - 您需要查看每个项目的具体规则,因为它们会有所不同。由于有一些较旧、较大的项目通过开发者邮件列表接受补丁,因此我们将介绍一个关于此的示例。
流程类似于前面的用例 - 您为要处理的每个补丁系列创建主题分支。不同之处在于您如何将它们提交给项目。您不是分叉项目并推送到您自己的可写版本,而是生成每个提交系列的电子邮件版本,并将它们发送到开发者邮件列表。
$ git checkout -b topicA
... work ...
$ git commit
... work ...
$ git commit
现在您有两个要发送到邮件列表的提交。您使用 git format-patch
生成 mbox 格式的文件,您可以将其发送到列表 - 它将每个提交变成一个电子邮件消息,其中包含提交消息的第一行为主题,其余消息以及提交引入的补丁作为正文。这样做的优点是,应用从使用 format-patch
生成的电子邮件中生成的补丁,可以正确地保留所有提交信息。
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
format-patch
命令打印出它创建的补丁文件的名称。-M
开关告诉 Git 搜索重命名。这些文件最终看起来像这样:
$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <[email protected]>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] Add limit to log function
Limit log functionality to the first 20
---
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
end
def log(treeish = 'master')
- command("git log #{treeish}")
+ command("git log -n 20 #{treeish}")
end
def ls_tree(treeish = 'master')
--
2.1.0
您还可以编辑这些补丁文件,为邮件列表添加更多信息,您不想在提交消息中显示。如果您在 ---
行和补丁开始之间添加文本(diff --git
行),开发人员可以阅读它,但该内容会被修补过程忽略。
要将此邮件发送到邮件列表,您可以将文件粘贴到您的电子邮件程序中,或者通过命令行程序发送。粘贴文本通常会导致格式问题,尤其是对于“更智能”的客户端,它们不会适当地保留换行符和其他空白符。幸运的是,Git 提供了一个工具来帮助您通过 IMAP 发送格式正确的补丁,这可能对您来说更容易。我们将演示如何通过 Gmail 发送补丁,恰好是我们最了解的电子邮件代理;您可以在上述 `Documentation/SubmittingPatches` 文件的 Git 源代码末尾阅读有关许多邮件程序的详细说明。
首先,您需要在您的 `~/.gitconfig` 文件中设置 imap 部分。您可以使用一系列 `git config` 命令分别设置每个值,或者您可以手动添加它们,但最终您的配置文件应该看起来像这样
[imap]
folder = "[Gmail]/Drafts"
host = imaps://imap.gmail.com
user = [email protected]
pass = YX]8g76G_2^sFbd
port = 993
sslverify = false
如果您的 IMAP 服务器没有使用 SSL,那么最后两行可能不是必需的,主机值将是 `imap://` 而不是 `imaps://`。设置好后,您可以使用 `git imap-send` 将补丁系列放置到指定 IMAP 服务器的草稿文件夹中
$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done
此时,您应该能够进入您的草稿文件夹,将“收件人”字段更改为您要发送补丁的邮件列表,可能还要抄送维护者或负责该部分的人员,然后发送出去。
您也可以通过 SMTP 服务器发送补丁。与之前一样,您可以使用一系列 `git config` 命令分别设置每个值,或者您可以在您的 `~/.gitconfig` 文件中的 sendemail 部分手动添加它们
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = [email protected]
smtpserverport = 587
完成此操作后,您可以使用 `git send-email` 发送您的补丁
$ git send-email *.patch
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <[email protected]>]
Emails will be sent from: Jessica Smith <[email protected]>
Who should the emails be sent to? [email protected]
Message-ID to be used as In-Reply-To for the first email? y
然后,Git 会为您发送的每个补丁吐出一堆日志信息,看起来像这样
(mbox) Adding cc: Jessica Smith <[email protected]> from
\line 'From: Jessica Smith <[email protected]>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i [email protected]
From: Jessica Smith <[email protected]>
To: [email protected]
Subject: [PATCH 1/2] Add limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <[email protected]>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>
Result: OK
提示
|
有关配置您的系统和电子邮件、更多提示和技巧以及通过电子邮件发送试用补丁的沙盒的帮助,请访问 git-send-email.io. |
总结
在本节中,我们介绍了多种工作流程,并讨论了在封闭源代码项目的 小型团队中工作与为大型公共项目做出贡献之间的区别。您知道在提交之前要检查空白错误,并且可以写一个很棒的提交消息。您学习了如何格式化补丁,并将其通过电子邮件发送到开发人员邮件列表。处理合并也涵盖在不同工作流程的背景下。您现在已经为在任何项目上进行协作做好了充分准备。
接下来,您将看到如何处理事物的另一面:维护 Git 项目。您将学习如何成为仁慈的独裁者或集成经理。