-
A1. 附录 A: 在其他环境中使用 Git
- A1.1 图形界面
- A1.2 在 Visual Studio 中使用 Git
- A1.3 在 Visual Studio Code 中使用 Git
- A1.4 在 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中使用 Git
- A1.5 在 Sublime Text 中使用 Git
- A1.6 在 Bash 中使用 Git
- A1.7 在 Zsh 中使用 Git
- A1.8 在 PowerShell 中使用 Git
- A1.9 总结
-
A2. 附录 B: 在你的应用程序中嵌入 Git
-
A3. 附录 C: Git 命令
5.2 分布式 Git - 参与项目
参与项目
描述如何参与一个项目的主要困难在于参与方法的多样性。因为 Git 非常灵活,人们可以而且确实以多种方式一起工作,所以很难描述你*应该*如何参与——每个项目都有些不同。一些相关的变量是活跃贡献者数量、选择的工作流程、你的提交权限,以及可能的外部贡献方法。
第一个变量是活跃贡献者数量——有多少用户正在积极地向这个项目贡献代码,频率如何?在许多情况下,你会看到两到三个开发者每天提交几个 commits,或者对于一些不活跃的项目来说可能更少。对于较大的公司或项目,开发者的数量可能达到数千,每天都有数百或数千个 commits 提交。这很重要,因为随着越来越多的开发者,你会遇到更多的问题,例如确保你的代码可以干净地应用或者可以很容易地合并。你提交的更改可能会因为在你工作期间或者你的更改等待批准或应用期间合并进来的工作而变得过时或严重损坏。你如何保持你的代码始终是最新的,以及你的 commits 是有效的?
下一个变量是项目中使用的工作流程。是中心化的,每个开发者都拥有对主代码库的同等写入权限吗?项目是否有维护者或集成管理者来检查所有补丁?所有补丁是否经过同行评审和批准?你是否参与到这个过程中?是否有 lieutenant 系统,你是否必须先将你的工作提交给他们?
下一个变量是你的提交权限。如果你拥有对项目的写入权限,那么参与项目所需的工作流程与你没有写入权限时大不相同。如果你没有写入权限,项目更喜欢接受什么样的贡献工作?它甚至有政策吗?你一次贡献多少工作?你多久贡献一次?
所有这些问题都会影响你如何有效地参与一个项目以及哪些工作流程是首选或可供你使用的。我们将在一系列用例中涵盖这些方面的每一个,从简单到更复杂;你应该能够从这些示例中构建你在实践中需要的特定工作流程。
Commit 指南
在我们开始研究具体的用例之前,这里先简单介绍一下提交信息。拥有一套良好的提交规范并坚持执行,可以让你在使用 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 可能想测试这个新代码,以确保 Jessica 的工作没有影响他的任何工作,并且只要一切似乎都很好,他最终可以将新的合并工作推送到服务器
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
最后,John 的提交历史记录将如下所示

origin
服务器后的历史记录与此同时,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 <jsmith@example.com>
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 现在通过合并位于 origin/master
分支中的 John 之前获取的工作来完成本地合并过程
$ 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 的历史记录现在如下所示

现在可以从 Jessica 的 master
分支访问 origin/master
,因此她应该能够成功推送(假设 John 在此期间没有推送更多更改)
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
每个开发人员都提交了几次,并成功地合并了彼此的工作。

这是最简单的工作流程之一。你工作一段时间(通常在主题分支中),并在准备好集成时将该工作合并到你的 master
分支中。当你想共享该工作时,如果 origin/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(-)
现在,杰西卡想把所有合并的“featureB”工作推送到服务器,但她不想简单地推送自己的 featureB
分支。相反,由于乔西已经启动了一个上游 featureBee
分支,杰西卡想推送到那个分支,她通过以下命令实现:
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee
这被称为 *refspec*。有关 Git refspec 的更详细讨论以及您可以使用它们执行的不同操作,请参阅 Refspec。另请注意 -u
标志;它是 --set-upstream
的缩写,用于配置分支以便稍后更轻松地推送和拉取。
突然,杰西卡收到约翰的电子邮件,告诉她他已经将一些更改推送到他们正在协作的 featureA
分支,并要求杰西卡查看它们。 杰西卡再次运行一个简单的 git fetch
命令来获取服务器上的所有新内容,包括(当然)约翰的最新工作。
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA
杰西卡可以通过比较新获取的 featureA
分支的内容与她本地副本的相同分支来显示约翰新工作的日志。
$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date: Fri May 29 19:57:33 2009 -0700
Increase log output to 30 from 25
如果杰西卡喜欢她所看到的,她可以使用以下命令将约翰的新工作合并到她本地的 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(-)
最后,杰西卡可能想对所有合并的内容进行一些小的更改,因此她可以随意进行这些更改,将它们提交到她本地的 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
杰西卡现在的提交历史记录看起来像这样:

在某个时候,杰西卡、乔西和约翰通知集成人员,服务器上的 featureA
和 featureBee
分支已准备好集成到主线中。 集成人员将这些分支合并到主线后,一次 fetch 将拉取新的合并提交,使历史记录看起来像这样:

许多团队切换到 Git 是因为它可以让多个团队并行工作,并在流程后期合并不同的工作线。 团队中较小的子组通过远程分支进行协作,而无需涉及或阻碍整个团队的能力是 Git 的一个巨大优势。 您在此处看到的workflow流程如下所示:

Forked 公共项目
为公共项目做贡献有点不同。因为您没有直接更新项目上的分支的权限,所以您必须以其他方式将工作传递给维护者。第一个例子描述了通过在支持轻松 fork 的 Git 主机上进行 fork 来进行贡献。许多托管站点支持此功能(包括 GitHub、BitBucket、repo.or.cz 等),并且许多项目维护者都期望这种贡献方式。下一节将介绍更喜欢通过电子邮件接受贡献补丁的项目。
首先,您可能需要克隆主存储库,为您计划贡献的补丁或补丁系列创建一个主题分支,并在那里完成您的工作。 流程基本如下:
$ git clone <url>
$ cd project
$ git checkout -b featureA
... work ...
$ git commit
... work ...
$ git commit
注意
|
您可能需要使用 |
完成分支工作并准备好将其贡献回给维护者后,请转到原始项目页面并单击“Fork”按钮,创建您自己的可写项目 fork。 然后,您需要将此存储库 URL 添加为本地存储库的新远程; 在本例中,我们将其称为 myfork
$ git remote add myfork <url>
然后,您需要将您的新工作推送到此存储库。 最简单的方法是将您正在处理的主题分支推送到您的 fork 存储库,而不是将该工作合并到您的 master
分支并推送该分支。 原因是如果您的工作未被接受或被 cherry-pick,您不必回滚您的 master
分支(Git cherry-pick
操作在 Rebase 和 Cherry-Picking 工作流程 中有更详细的介绍)。 如果维护者 merge
、rebase
或 cherry-pick
您的工作,您最终将通过从他们的存储库中拉取来获得它。
无论如何,您可以使用以下命令推送您的工作:
$ git push -u myfork featureA
将您的工作推送到您的存储库 fork 后,您需要通知原始项目的维护者您有希望他们合并的工作。 这通常被称为 *pull request*,您通常通过网站生成此类请求 - GitHub 有其自己的“Pull Request”机制,我们将在 GitHub 中介绍 - 或者您可以运行 git request-pull
命令并将后续输出手动通过电子邮件发送给项目维护者。
git request-pull
命令采用您希望将主题分支拉入的基础分支和您希望他们从中拉取的 Git 存储库 URL,并生成您要求拉取的所有更改的摘要。 例如,如果杰西卡想向约翰发送一个 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
,并在您可以轻松丢弃的主题分支中完成您的工作,如果它们被拒绝。 将工作主题隔离到主题分支中也使您更容易在主存储库的顶端已移动且您的提交不再干净地应用时 rebase 您的工作。 例如,如果您想向项目提交第二个主题的工作,请不要继续处理您刚刚推送的主题分支 - 从主存储库的 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
现在,您的每个主题都包含在一个 silo 中 - 类似于一个补丁队列 - 您可以重写、rebase 和修改,而无需主题相互干扰或相互依赖,如下所示:

featureB
工作的初始提交历史假设项目维护者已经拉入了一堆其他补丁并尝试了您的第一个分支,但它不再干净地合并。 在这种情况下,您可以尝试将该分支 rebase 到 origin/master
之上,解决维护者的冲突,然后重新提交您的更改。
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
这将重写您的历史记录,现在看起来像 在 featureA
工作后的提交历史记录。

featureA
工作后的提交历史因为您 rebase 了该分支,您必须在您的推送命令中指定 -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
工作后的提交历史通过电子邮件进行的公共项目
许多项目都建立了接受补丁的程序 - 您需要检查每个项目的具体规则,因为它们会有所不同。 由于有几个较旧的、较大的项目通过开发人员邮件列表接受补丁,因此我们现在将介绍一个示例。
工作流程与之前的用例类似 - 您为每个您正在处理的补丁系列创建主题分支。 不同之处在于您如何将它们提交给项目。 您不是 fork 项目并推送到您自己的可写版本,而是生成每个提交系列的电子邮件版本,并通过电子邮件将其发送给开发人员邮件列表。
$ 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 <jessica@example.com>
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 发送补丁,这恰好是我们最熟悉的电子邮件代理; 您可以在上述 Git 源代码中的 Documentation/SubmittingPatches
文件末尾阅读有关许多邮件程序的详细说明。
首先,您需要在您的 ~/.gitconfig
文件中设置 imap 部分。 您可以使用一系列 git config
命令分别设置每个值,也可以手动添加它们,但最终您的配置文件应该看起来像这样:
[imap]
folder = "[Gmail]/Drafts"
host = imaps://imap.gmail.com
user = user@gmail.com
pass = YX]8g76G_2^sFbd
port = 993
sslverify = false
如果你的 IMAP 服务器没有使用 SSL,那么最后两行可能是不必要的,并且 host 值将是 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
此时,你应该能够进入你的草稿文件夹,将 To 字段更改为你发送补丁的邮件列表,可以抄送维护者或负责该部分的人,然后将其发送出去。
你也可以通过 SMTP 服务器发送补丁。和之前一样,你可以使用一系列 git config
命令分别设置每个值,或者你可以手动将它们添加到 ~/.gitconfig
文件中的 sendemail 部分。
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = user@gmail.com
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 <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y
然后,Git 会为你要发送的每个补丁输出大量类似如下的日志信息。
(mbox) Adding cc: Jessica Smith <jessica@example.com> from
\line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] Add limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
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 项目。你将学习如何成为一位仁慈的独裁者或集成管理者。