【问题标题】:git commit best practicesgit 提交最佳实践
【发布时间】:2011-09-26 11:59:24
【问题描述】:

我正在使用 git 来管理一个 C++ 项目。当我处理项目时,我发现在更改与许多地方相关的内容时很难将更改组织到提交中。

例如,我可能会更改.h 文件中的类接口,这将影响相应的.cpp 文件,以及使用它的其他文件。我不确定将所有内容放在一个大提交中是否合理。

直观地说,我认为提交应该是模块化的,每个提交都对应一个功能更新/更改,以便协作者可以相应地选择内容。但似乎有时不可避免地要包含大量文件和更改以使功能更改真正起作用。

搜索没有给我任何好的建议或提示。因此,我想知道是否有人可以在提交时给我一些最佳实践。

PS。我使用 git 已经有一段时间了,我知道如何交互式地添加/rebase/split/amend/...我要问的是哲学部分。

更新:感谢所有建议。也许这应该从实践中学习。我会将问题保持开放一段时间,看看是否有更多建议。

【问题讨论】:

  • “我认为提交应该是模块化的” -> 为此使用标签。标签不一定是版本号。根据需要经常提交,同时不要破坏构建。
  • @subtenante:为此使用标签可能不是最好的主意。您将完全忘记重要的标签
  • @knittl:不明白为什么。对“重要”标签使用命名约定,并在列出它们时使用模式。
  • @subtenante: 不幸的是 git 在标签太多的情况下不能很好地扩展
  • @knittl:好的,不知道。

标签: git


【解决方案1】:

我倾向于按照您的建议提交:提交是一个逻辑连接的更改集。我的提交可以是从单行到更改所有文件的任何内容(例如在源文件中添加/更改版权声明)。更改的原因不一定是我正在实施的完整任务,但它通常是任务中的一个里程碑。

如果我修改了与当前提交无关的内容,我也倾向于进行交互式添加以分离出不相关的更改 - 即使它是一个空白整理。

我发现简单地将工作状态转储到存储库的提交会使它们的用处大大降低:如果提交无处不在,我无法将错误修复反向移植到早期版本或在另一个分支中轻松包含实用程序功能。

这种方法的一种替代方法是在功能分支中使用大量微小的提交,一旦整个功能完成,就需要进行大量的历史重写以将提交整理成一个逻辑结构。但我觉得这种方法很浪费时间。

【讨论】:

    【解决方案2】:

    这正是在 git 中引入了暂存区 index 的用例。

    您可以随意进行尽可能多的彼此无关的更改。 然后您选择所有相关的内容,然后一次性进行多个原子提交。

    我一直这样做。如果您使用git-gui 或任何其他GUI 客户端,您不仅可以选择要提交的文件,还可以选择hunks within the files,因此您的提交尽可能具有原子性。

    【讨论】:

    • 您也可以使用git add -p 从命令行选择性地提交数据块,而无需使用 GUI 客户端。
    • 这是真的。我通常使用gitx 来完成交互式登台工作。
    • git add -i 比 -p 更强大。为您提供基于菜单的 cli 系统,您可以在其中更新整个文件、添加补丁等。
    • 如果这些提交已经创建(git rebase --interactive HEAD~x 其中 x 是返回的提交次数),您还可以交互地变基以添加或删除主题分支中过去提交的内容。 Here is why 应该始终创建原子提交(以及它们到底是什么)。
    【解决方案3】:

    我尝试按顺序遵循这些做法...

    1. 提交不能使构建失败。最重要的!

    2. 它应该由一个变化的逻辑单元组成——无论是单行/字符还是整个文件/类,在代码的其他部分有相应的变化,仍然遵循#1。

      什么是变化的逻辑单元?就git 而言,如果您可以在一句话中以最少的字符数指定提交消息中的更改(当然没有AND),并且您不能将该描述进一步分解为更小的单元,我称之为一个单位。

    3. 提交信息应明确说明提交的本质。

    4. 提交消息应该很小,通常不超过 80 个字符。任何更多的详细说明都应该是description 的一部分。

    【讨论】:

    • 我发现第四点令人困惑。提交消息是编辑器中的整个文本,显然应该只要需要解释提交的内容即可。它应该被构造为最多 50 的 1 行“主题”(比如说 git 自己的编码指南)字符,然后是空行,然后是需要的任何详细解释。
    • 你提到的“主题”是他们术语的“消息”,其余的属于“描述”。
    • 是的,我也遵循这些策略。但是,即使我遵循这些做法,即使是单个功能更改也可能会引入胖提交
    • 如果您需要一个胖提交来更改单个函数,我不一定会责怪 Git。这与构建代码、分离关注点和封装数据的方式一样重要。
    【解决方案4】:

    非常有助于我弄清楚我正在提交什么以及为什么将我们的存储库组织转移到“功能分支”模型,正如 Git Flow extension 所推广的那样。

    通过分支描述正在处理的每个特性(或更新、错误修复等),提交变得更少关于特性,而更多地关于你将如何实现该特性。例如,我最近在它自己的 bugfix 分支(例如 bugfixes/gh-87)中修复了一个时区 bug,提交被拆分为已完成的操作或服务器端和前端,以及测试中。因为所有这些都发生在一个专门解决该错误的分支 (with a GitHub issue number too, for clarity and auto closing) 上,所以我的提交被视为解决该问题的增量步骤,因此不需要解释我为什么这样做。

    【讨论】:

    • 感谢您的信息。我知道 git 流。但是,这与我的要求不太相关。
    【解决方案5】:

    有时当您进行大型重构时,您不可避免地会在一次提交中更改许多文件。当你改变一个类的接口时,你必须在一次提交中改变头部、实现和所有使用接口的地方,因为没有中间状态会起作用。

    但是,建议的做法是更改界面而不首先实际引入任何新功能,测试您没有破坏现有功能并提交它。比实现需要更新接口的实际功能并单独提交。您可能最终会在使用交互式 rebase 压缩到第一次提交的过程中对重构进行一些调整。

    这样会有一个很大的提交,但它不会做任何困难的事情,只是将代码打乱,所以即使它很大并且比第二次提交(或更多,如果功能很大)也应该很容易理解这不是太大。

    【讨论】:

      【解决方案6】:

      免责声明:我也在努力弄清楚提交应该是什么,以及最终历史应该如何结束。不过,我想分享一些我在自己的研究中遇到的资源。

      首先,Linux Kernel 项目在Merge Strategies 上有一个很棒的页面,用于将您的代码合并到上游。他们谈论做出一口大小的提交;在您想要的实际添加之前进行一个或多个重构提交(重构当然应该使您的功能更清洁;)和其他事情。

      我最喜欢的另一个页面是 Seth Robertson 的 Git Best Practices。这不仅是关于使用 git 的许多最佳实践的页面,而且还是一个巨大的资源,包含有关各种 git 主题的足够信息,使谷歌搜索更深入的信息变得轻而易举。

      【讨论】:

      • 支持 Seth Robertson 链接,谢谢。我现在熟悉与 Git 相关的“香肠制作”一词,我可以安详地死去。
      【解决方案7】:

      我要问的是哲学部分。

      我想我可以回答这个问题,因为我最近参与了一些个人研究。

      应该专注于创建atomic commit。这意味着有必要在提交时特别注意一些事情:

      • 如果只做一部分应该没有任何价值
      • 它不应该破坏构建
      • 它应该包含一个良好的消息和正文以进行可追溯性(尽可能提供票证参考)
      • 它不应该包含很多差异噪音(空白和样式更改,除非提交是特定的)

      提交应集中在one change, and one change only。除此之外的任何事情都会产生不良的副作用。

      有些人可能会争辩说这太过分了,不切实际。但是,即使对于小公司来说,支持它的最佳论据是构建原子提交将迫使您的设计更加解耦和一致,因为实现完全最佳原子提交的一个要求是拥有一个健康的代码库,而不是一团糟。

      如果您始终坚持良好的提交实践,您将能够推动工程文化和代码本身达到更好的状态。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2022-11-22
        • 1970-01-01
        • 1970-01-01
        • 2012-03-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多