# 贡献于 Kudu
原文链接 : [http://kudu.apache.org/docs/contributing.html](http://kudu.apache.org/docs/contributing.html)
译文链接 : [http://cwiki.apachecn.org/pages/viewpage.action?pageId=10813653](http://cwiki.apachecn.org/pages/viewpage.action?pageId=10813653)
贡献者 : [小瑶](/display/~chenyao) [ApacheCN](/display/~apachecn) [Apache中文网](/display/~apachechina)
## Contributing Patches Using Gerrit ( 使用 Gerrit 贡献补丁 )
**Kudu** 团队使用 **Gerrit** 进行代码审查,而不是 **Github pull requests**。通常,您从 **Github pull** ,但是 **push** 到 **Gerrit** , **Gerrit** 用于查看代码并将其合并到 **Github** 中。
有关使用 **Gerrit** 进行代码审查的概述,请参阅 [**Gerrit** 教程](https://www.mediawiki.org/wiki/Gerrit/Tutorial)。
### Gerrit 的初始设置
1. 使用您的 **Github** 用户名登录 [**Gerrit**](http://gerrit.cloudera.org:8080/) 。
2. 前往 **[Setting](http://gerrit.cloudera.org:8080/#/settings/) **。在 **Contact Information** 页面上更新您的姓名和电子邮件地址,并上传 **SSH** 公钥。如果您不更新您的姓名,它将在 **Gerrit** 评论中显示为 **“Anonymous Coward”** 。
3. 如果还没有这样做,请 **clone the main Kudu repository** 。默认情况下,**main remote** 称为 **origin** 。当你 **fetch **或 **pull** ,你会从 **origin** 这样做。
```
git clone https://github.com/apache/kudu
```
4. 切换到新的 **kudu** 目录。
5. 添加 **gerrit remote** 。在以下命令中,用您的 **Github** 用户名替换 **<username>** 。
```
git remote add gerrit ssh://<username>@gerrit.cloudera.org:29418/kudu
```
6. 运行以下命令来安装 **Gerrit commit-msg hook** 。使用以下命令,用您的 **Github** 用户名替换 **<username>** 。
```
gitdir=$(git rev-parse --git-dir); scp -p -P 29418 <username>@gerrit.cloudera.org:hooks/commit-msg ${gitdir}/hooks/
```
7. 默认情况下,您已经设置了 **Kudu** 存储库使用 **pull -rebase** 。您可以使用以下两个命令,假设您至今已经检查过主机:
```
git config branch.autosetuprebase always
git config branch.master.rebase true
```
如果由于某种原因,您已经 **checked out branches** 而不是 **master** ,请在上面的第二个命令中替换 **master **以获取 **other branch names**。
### Submitting Patches ( 提交补丁 )
要提交修补程序,首先提交您的更改(如果可能,使用描述性多行提交消息),然后将请求 **push** 到 **gerrit remote**。例如,要将更改推送到 **master** 分支:
```
git push gerrit HEAD:refs/for/master --no-thin
```
或者将更改 **push** 到 **gh-pages** 分支 ( 更新网页 ):
```
git push gerrit HEAD:refs/for/gh-pages --no-thin
```
注意
在准备一个修补程序进行审查时,最好[遵循通用 **git** 提交准则和良好做法](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines)。
注意
**--no-thin** 参数是防止 **Gerrit** 中的错误的解决方法。请参阅
注意
考虑为上述命令创建 **Git** 别名。 **Gerrit** 还包括一个名为 **git-review** 的命令行工具,您可能会发现有用。
**Gerrit** 会在您的提交消息中添加更改 **ID** ,并创建一个 **Gerrit review** ,其 **URL** 将作为推送回复的一部分发布。如果需要,您可以向 **kudu-dev** 邮件列表发送消息,解释补丁并请求 **review** 。
获得反馈后,您可以更改或修改您的提交(例如,使用像 **git commit --amend** 这样的命令),同时保留更改 **ID** 。将您的更改再次 **push** 给 **Gerrit** ,这将在 **Gerrit** 中创建一个新的修补程序,并通知所有审阅者有关更改。
当您的代码经过审查并准备合并到 **Kudu** 代码库后, **Kudu** 提交者将使用 **Gerrit** 进行合并。你可以丢弃你的 **local branch** 。
### Abandoning a Review ( 放弃 review )
如果您的补丁不被接受或您决定从考虑中提取补丁,则可以使用 **Gerrit UI** 放弃补丁。它仍将在 **Gerrit** 的历史上展示,但不会被列为待审查。
### Reviewing Patches In Gerrit ( 审查 Gerrit 中的补丁 )
您可以使用 **Web UI** 查看 **Gerrit** 中的统一或并行差异更改。要发表评论,请单击相关行号或突出显示该行的相关部分,然后键入 **“c”** 以显示注释框。要提交您的 **review** 和/或 您的 **review status** ,请转到评论的顶层,然后单击 **Reply**。您可以在此处添加其他顶级注释,然后提交。
要查看 **Gerrit review** 中的代码,请单击下载并将相关的 **Git** 命令粘贴到 **Git** 客户端。然后,您可以更新提交并推送 **Gerrit** 向审阅提交补丁,即使您不是原始审阅者。
**Gerrit** 允许您对 **review** 进行投票。在修补程序可以合并之前,需要至少提交一个提交者(除了提交者)之外的一个 **+2** 的投票。
## Code Style ( 代码风格 )
熟悉这些准则,以便您的贡献能够快速轻松地进行审查和整合。
一般来说,**Kudu** 遵循 **[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)** ,但有以下例外:
### Notes on C++ 11 ( 关于 C++ 11 的注释 )
**Kudu** 使用 **C ++ 11** 。查看 **C ++ 11** 移动语义和 **rvalue** 引用的方便指南:[https://www.chromium.org/rvalue-references](https://www.chromium.org/rvalue-references) 。
我们的目标是遵循大多数相同的指导原则,例如在可能的情况下迁移远离 **foo.Pass()** ,有利于 **std :: move(foo)**。
### Limitations on boost Use ( boost 使用限制 )
在 **kudu** 代码库中不存在合适的替换的情况下,可以使用仅来自标头库的 **boost** 类。然而:
* 不要对标准 **C ++** 库或 **src/kudu/gutil/** 中存在等效功能的 **boost** 类引入依赖关系。例如,喜欢来自 **gutil** 的 **strings :: Split()** ,而不是 **boost :: split** 。
* 喜欢使用 **boost** 的功能而不是重新实现相同的功能,除非使用 **boost** 功能需要过度使用我们的风格指南不允许的 **C ++** 功能。例如,**boost :: spirit** 非常基于模板元编程,不应该使用。
* 不要在 **Kudu C ++** 客户端的任何公用头文件中使用 **boost** ,因为 **boost** 通常会破坏向后兼容性,并且在两个升级版本之间传递数据(一个由用户由 **Kudu** 导出)会导致严重的问题。
如果有任何提升功能引入新的依赖关系,最好发送电子邮件至 dev@kudu.apache.org 开始讨论。
### Line length ( 线长 )
**Kudu** 团队允许每行 **100** 个字符的行长度,而不是 **Google** 的 **80** 标准。尽可能保持在 **80** 以下,但如果需要,您可以溢出到 **100** 个左右。
### Pointers ( 指针 )
**Smart Pointers and Singly-Owned Pointers ( 智能指针和单独指针 )**
通常,大多数对象应该有明确的 **“single-owner”** 语义。大多数时候, **singly-owned** 的对象可以包装在 **unique_ptr <>** 中,确保在范围退出时删除,并防止意外复制。
如果对象是 **singly owned** 的,但是从多个位置引用,例如当已知指向对象至少与指针本身一样长时,将注释与将原始指针存储并存储的构造函数相关联,如在下面的例子中。
```
// 'blah' must remain valid for the lifetime of this class
MyClass(const Blah* blah) :
blah_(blah) {
}
```
注意
**Kudu** 代码库的较旧部分使用 **gscoped_ptr** 而不是 **unique_ptr** 。这些都是在 **Kudu** 采用 **C ++ 11** 之前进行的。新代码不应该使用 **gscoped_ptr** ,除非需要与现有代码进行接口。或者,考虑在您遇到这些问题时更新用法。
注意
严格禁止使用 **std :: auto_ptr** ,因为它的难度大且易出错的语义。此外, **std :: auto_ptr** 自 **C ++ 11** 被声明为不推荐使用。
**Smart Pointers for Multiply-Owned Pointers ( 多指针指针的智能指针 ):**
虽然 **single ownership** 是理想的,但有时候是不可能的,特别是当多个线程正在运行时,指针的生命周期没有明确定义。在这些情况下,您可以使用 **std :: shared_ptr** 或 **Kudu** 自己的 **scoped_refptr** 从 **gutil/ref_counted.hpp** 。这些机制中的每一个依赖于引用计数,以便在没有更多指针保留时自动删除指示。这两种类型的指针之间的关键区别是 **scoped_refptr** 要求对象扩展一个 **RefCounted** 基类,并将其引用计数存储在对象存储本身内,而 **shared_ptr** 在堆上维护单独的引用计数。
利弊是:
**shared_ptr**
* 可以与任何类型的对象一起使用,而不需要从特殊的基类派生对象
* 标准库的一部分,大多数 **C ++** 开发人员熟悉
* 支持 **weak_ptr** 的用例:
* 当对象仅在存在的情况下需要被访问时才是临时所有权
* 打破 **shared_ptr** 的循环引用,如果由于聚合存在任何存在
* 您可以将 **shared_ptr** 转换为 **weak_ptr** 并返回
* 如果使用 **std :: make_shared <>()** 创建一个实例,则只能进行一次分配(因为 **C ++ 11; Standard** 中的非绑定的要求)
* 如果使用 **shared_ptr <T> p(new T)** 创建新对象需要两个分配(一个用于创建引用计数,另一个用于创建对象)
* 引用计数可能不在堆上的对象附近,因此在访问时可能会发生额外的高速缓存未命中
* **shared_ptr** 实例本身需要 **16** 个字节(指向 **ref** 计数的指针和指向对象的指针)
**scoped_refptr**
* 只需要一个分配,并且 **ref** 计数与对象在同一个高速缓存行上
* 指针只需要 **8** 个字节(因为引用计数在对象内)
* 当需要更多控制时,您可以手动增加或减少参考计数
* 您可以将原始指针转换回 **scoped_refptr** ,而不必担心双重释放
* 由于我们控制实现,我们可以实现功能,例如调试构建,捕获每个对象的堆栈跟踪以帮助调试泄漏。
* 引用对象必须从 **RefCounted** 继承
* 不支持 **weak_ptr** 的用例
由于 **scoped_refptr** 通常越来越小,所以尝试在新代码中使用而不是 **shared_ptr** 。现有代码在许多地方使用 **shared_ptr** 。当与该代码连接时,可以继续使用 shared_ptr 。
### Function Binding and Callbacks ( 函数绑定和回调 )
现有代码使用 **boost :: bind** 和 **boost :: function** 来进行函数绑定和回调。 对于新代码,请使用 **gutil** 中的回调和绑定类。 虽然功能较少(绑定不支持参数占位符,包装函数指针或函数对象),但它们通过参数生命周期管理的方式提供更多选项。 例如,当 **Callback** 超出范围时,绑定参数的类扩展 **RefCounted** 将在绑定期间递增,并减少。
有关详细信息,请参阅 **gutil/callback.h** 中的大文件注释,**util/callback_bind-test.cc** 作为示例。
## CMake Style Guide ( CMake 样式指南 )
**CMake** 允许以较低,上限或混合大小写的命令。 要保持 **CMake** 文件一致,请使用以下准则:
* 小写的 **built-in commands**
```
add_subdirectory(some/path)
```
* 大写的 **built-in arguments**
```
message(STATUS "message goes here")
```
* 大写的 **custom commands or macros**
```
ADD_KUDU_TEST(some-test)
```
## GFlags
**Kudu** 使用 **gflags** 进行命令行和基于文件的配置。使用这些准则添加新的 **gflag** 。所有新 **gflags** 必须符合这些准则。现有的不符合要求的产品将及时符合规定。
### Name ( 名称 )
**gflag** 的名字传达了很多信息,所以选择一个好名字。该名称将传播到其他系统,如 [配置参考](/pages/viewpage.action?pageId=10813644)。
* 多字名称的不同部分应以下划线分隔。例如, **fs_data_dirs** 。
* 该名称应以其影响的上下文为前缀。例如, **webserver_num_worker_threads** 和 **cfile_default_block_size** 。上下文可能很难定义,所以请记住,这个前缀将用于将类似的 **gflags** 组合在一起。如果 **gflag** 影响整个过程,那么它不应该是前缀。
* 如果 **gflag** 是一个数量,该名称应该后缀单位。例如, **tablet_copy_idle_timeout_ms** 。
* 如有可能,请使用短名称。这将为手动输入命令行选项的人节省时间。
* 这个名称是 **Kudu** 兼容性合同的一部分,不应该没有很好的理由来改变。
### Default value ( 默认值 )
选择默认值通常很简单,但像名称一样,它传播到其他系统中。
* 默认值是 **Kudu** 的 **compatibility contract** ( 兼容性合同 ) 的一部分,如果没有很好的理由,不应该改变。
### Description ( 描述 )
**gflag** 的描述应该补充名称并提供其他上下文和信息。与名称一样,说明传播到其他系统。
* 描述可以包括多个句子。每个都应该以一个大写字母开头,以一个句点结尾,并在之前的一个空格开始。
* 描述不应包含 **gflag** 的类型或默认值;它们是带外提供的。
* 描述应该在第三人称。不要使用像你这样的话。
* **gflag** 描述可以自由更改; **Kudu** 的发行预计不会保持不变。
### Tags ( 标志 )
**Kudu** 的 **gflag** 标记机制为每个 **gflag** 添加了机器可读上下文,用于消耗系统,如文档或管理工具。请参阅 **flag_tags.h** 中的大块注释,以获取准则。
### Miscellaneous ( 杂 )
* 避免为同一个逻辑参数创建多个 **gflags** 。例如,许多 **Kudu** 二进制文件需要配置一个 **WAL** 目录。而不是创建 **foo_wal_dir** 和 **bar_wal_dir gflags** ,最好使用一个单一的 **kudu_wal_dir gflag** 来普遍使用。
## Testing ( 测试 )
### All new code should have tests. ( 所有新的代码都应该有测试。 )
在现有文件中添加新的测试,或根据需要创建新的测试文件。
### All bug fixes should have tests. ( 所有错误修复都应该有测试。 )
如果由现有测试用例触发,则可以修复错误而不添加新测试。例如,如果在 **20** 分钟左右之后运行多线程系统测试时出现了一个 **race **,那么值得尝试更有针对性的测试用例来触发该错误。但是如果这很难做,现有的系统测试就够了。
### Tests should run quickly (< 1s). ( 测试应该很快运行(<1s)。 )
如果要编写时间密集的测试,请使运行时依赖于通过 **KUDU_ALLOW_SLOW_TESTS** 环境变量启用的 **KuduTest#AllowSlowTests** ,并由 **Jenkins** 测试执行使用。
### Tests which run a number of iterations of some task should use a `gflags` command-line argument for the number of iterations. ( 运行一些任务的迭代的测试应该使用 gflags 命令行参数来执行迭代次数。 )
这对于编写快速压力测试或性能测试非常方便。
### Commits which may affect performance should include before/after `perf-stat(1)` output. ( 可能影响性能的提交应包括在 perf-stat(1) 输出 之前/之后。 )
这将显示性能提升或 **non-regression** ( 不回归 )。**Performance-sensitive** ( 性能敏感 ) 代码应包括一些可用作目标基准测试用例。