【问题标题】:Git Squash sequential commits by an author to compress history作者的 Git Squash 顺序提交以压缩历史记录
【发布时间】:2016-03-11 16:52:27
【问题描述】:

我的团队正在开发一个长期运行的功能分支,现在有数百个提交,现在我需要将它合并到 master 以进行生产发布。

我不想在那个分支中有那么多提交,因为许多提交是为了修复错误而完成的,并且每次提交只更改几行。

在 Github 的 PR 创建页面上,它限制了显示为 250 的提交。

来自 Github -“这个比较很大!我们只显示最近的 250 次提交”

因此,我决定压缩历史记录,将作者的一组连续提交压缩为单个提交。

例如,假设我们有来自作者 A、B、C 和 D 的提交,例如:A - A - A - B - B - A - C - D - D - B- B -A,那么生成的提交日志将有 A(3) - B(2) - A - C - D(2) - B(2) - A其中 X`(N) 是作者 X 的 N 次提交的压缩提交。

编辑:我知道这需要一个脚本,我正在寻找相同的脚本。我不想通过交互式变基来做同样的事情。

【问题讨论】:

    标签: git github rebase git-squash


    【解决方案1】:

    控制台:

    git log --format="p|%h|%an|%s" --no-merges -n 190 | tail -r  > commits.txt
    

    PHP:

    $fp = fopen("./commits.txt", "r");
    
    $commits = array();
    $previous_author = '';
    while (($line = fgets($fp)) !== FALSE) {
      list($mode, $sha, $current_author, $subject) = explode('|', $line);
      if ($previous_author == '' || $previous_author !== $current_author) {
        $previous_author = $current_author;
      }
      else {
        $mode = 'f';
      }
      $output[] = implode(' ', array($mode, $sha, $subject));
    }
    
    fclose($fp);
    
    print implode(' ', $output);
    

    控制台:

    git rebase -i HEAD~190
    

    在交互式编辑器中,删除所有内容并粘贴上面 php 脚本的输出。我可以达到预期的效果。

    【讨论】:

      【解决方案2】:

      没有内置的东西可以做到这一点;您必须编写自己的脚本。

      为此,请从 git rev-listgit log 开始(两者本质上是相同的,只是命令行选项有所不同)来遍历您要扫描的所有提交。您的目标是将提交复制但压缩到一个新的临时分支上。例如,假设所有提交都在分支 feature 上进行并且要合并到分支 target,您可以获取要检查的提交列表:

      git rev-list --reverse --topo-order target..feature > /tmp/list
      

      此处的输出是提交 SHA-1 ID 的列表。对于每个提交,您都需要找到作者,可能还有提交消息:

      while read sha1; do
          author_name=$(git log -n 1 --format=%an $sha1)
          ...
      done < /tmp/list
      

      如果当前作者名和之前的作者名相同,你只是想累积这个提交,但如果不同,你想发出提交 ID。由于 $author_name 最初是未设置的,它将是空字符串,所以第一次提交不会匹配以前的作者,但你必须特别处理这个(连同最终提交 ID),因为你总是想累积第一次提交,并在最后一次提交或作者更改后采取行动。因此... 部分有点复杂。我们还需要一些设置工作来创建并进入一个临时分支,它的第一次提交指向分支target 的尖端。我们将在这里使用匿名分支,而不是使用命名的临时分支。

      最后,进行压缩提交的方法特别棘手:最简单的方法是使用管道命令git commit-tree,然后推进临时分支。

      将所有这些放在一起,我们得到以下完全未经测试的代码:

      # add new squash-style commit using commit $1
      make_squash_commit() {
          local sha1=$1 tree new_sha1
      
          tree=$(git rev-parse $sha1^{tree})
          new_sha1=$(git commit-tree $tree -p HEAD)
          git update-ref -m "squash $sha1" HEAD $new_sha1
      }
      
      set -e
      git rev-list --reverse --topo-order target..feature > /tmp/list
      git checkout --detach target
      : > /tmp/accum_log
      prev_sha1=""
      while read sha1; do
          author_name=$(git log -n 1 --format=%an $sha1)
          if [ "$author_name" != "$prev_name" -a -n "$prev_sha1" ]; then
              make_squash_commit $prev_sha1 < /tmp/accum_log
              : > /tmp/accum_log
          fi
          prev_name="$author_name"
          prev_sha1=$sha1
          git log -n 1 --format="%B" $sha1 >> /tmp/accum_log
      done < /tmp/list
      if [ -z "$prev_sha1" ]; then
          echo "Warning: no commits found to squash!"
          sha1=$(git rev-parse target)
      fi
      # squash final commit, then give anonymous branch a name
      make_squash_commit $prev_sha1 < /tmp/accum_log
      git checkout -b temp_branch
      rm /tmp/list /tmp/accum_log
      

      这个脚本有一个故意的缺陷:它使用当前日期和时间(这是适当的,因为你是把多个作者的提交弄得一团糟,不管他们是否同意,都要把他们压扁)。您可以通过在git commit-tree 步骤中设置所有适当的环境变量来“修复”此问题(请参阅其文档)。

      【讨论】:

      • 谢谢@torek。我使用了 unix 和 php 脚本的组合来执行此操作。将在答案中发布。
      猜你喜欢
      • 2011-01-19
      • 2015-04-20
      • 1970-01-01
      • 2021-04-25
      • 2022-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-04
      相关资源
      最近更新 更多