5

私のチームは、現在何百ものコミットがある長期実行機能ブランチに取り組んでいましたが、今ではそれを本番リリース用のマスターにマージする必要があります。

多くのコミットはバグ修正を行うために行われ、コミットごとに数行しか変更されていないため、そのブランチにそれほど多くのコミットをしたくありません。

Github の PR 作成ページでは、表示されるコミットを 250 に制限しています。

Github から - 「この比較は大きいです!最新の 250 件のコミットのみを表示しています」

したがって、著者からの一連の連続したコミットが 1 つのコミットに押しつぶされるように、履歴を圧縮することにしました。

たとえば、次のようなコミットがあるとします: A - A - A - B - B - A - C - D - D - B- B -Afrom author A, B, C and D の場合、結果のコミット ログには A (3) - B(2) - A - C - D (2) - B(2) - A が含まれます。ここで、X`(N) は N コミットの squashed commit です。作者Xより。

編集:これにはスクリプトが必要であることを理解しており、同じものを探しています。同じことをするためにインタラクティブなリベースを行いたくありません。

4

2 に答える 2

1

これを行うための組み込み機能はありません。独自のスクリプトを作成する必要があります。

これを行うには、git rev-listorから始めてgit log(どちらも基本的に同じことを行いますが、コマンドライン オプションが多少異なります)、スキャンするすべてのコミットを繰り返し処理します。ここでの目標は、コミットを新しい一時ブランチにコピーしてスカッシュすることです。たとえば、コミットがすべて branch で行われ、 branchfeatureにマージされると仮定する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 と共に)。最後のコミット後または作成者の変更時。したがって、...セクションは少し複雑です。また、最初のコミットが branch の先端を指す一時的なブランチを作成して取得するためのセットアップ作業も必要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(ドキュメントを参照)。

于 2015-12-07T09:23:09.087 に答える
0

コンソール:

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 スクリプトの出力を貼り付けました。意図した結果を得ることができました。

于 2015-12-07T10:01:03.827 に答える