HN 日本語サマリー

← 一覧へ戻る
プログラミング

git history コマンドはもっと注目されるべき

The Git history command deserves more attention (lalitm.com)

408 pointsby turbocon287 コメント

要約

Gitの履歴操作をより安全かつ効率的に行うための実験的なコマンド「git history」について解説しています。このコマンドは、jjのような外部ツールに切り替えることなく、既存のGitワークフロー内でコミットの修正や分割をアトミックに行う機能を提供します。fixup、reword、splitの3つのサブコマンドを通じて、開発者は履歴の整合性を保ちながら、より洗練されたコミット管理が可能になります。

全文翻訳

Gitで多くの変更を並行して扱うのは苦痛な場合があります。ブランチやコミットを駆使し、少しのミスでツリーが半壊状態になりかねない恐ろしいrebase -iコマンドを実行することになります。 最近よく話題になるjjというGitの代替ツール(1, 2, 3, 4)は、しばしば解決策として提示されます。jjが解決しようとしている問題点には非常に共感できますが、その解決方法が私にはまだしっくりきていません。過去1年半、3ヶ月ごとに数日間試していますが、どうしてもワークフローに組み込むことができず、結局Gitに戻ってしまいます。 そこで登場するのがgit historyです。これは、2つのリリース(2.54(4月、サブコマンドのリワードと分割)と2.55(6月、fixupサブコマンド))で登場した実験的なコマンドです。各リリース日には多くの注目を集めましたが、私の知る限り、その後コミュニティでの議論はあまり行われていません。これは残念なことです。なぜなら、私の意見では、これはワークフロー全体を切り替えることなく、jjが謳う多くのメリットをすでに提供しているからです。そしてクールなのは、これがコアGitディストリビューションの一部であるため、何もインストールせずに試すことができることです。 3つのサブコマンドがあります:fixup、reword、splitです。 fixup git history fixupは、何か問題のある古いコミットを修正し、すべてのブランチを自動的にリベースしてそれに合わせます。 git addで通常通り修正をステージングし、次にgit history fixup <commit>を実行して、ステージングされた変更をターゲットコミットに折り込みます。これはgit commit --fixupにオートスクワッシュリベースを追加したようなものですが、そのコミットを含んでいた他のすべてのブランチも更新するという追加の魔法があります。 この最後の部分は、git rebase --update-refsよりも進んでいます。git rebase --update-refsは、アクティブにリベースしている範囲内の参照しか移動しません。git historyは代わりに、そのコミットから派生したすべてのローカルブランチを見つけて書き換えます(現在のブランチのみに限定するオプションもあります)。一方で、マージコミットが存在する場合には機能しないため、Gitのいくつかの用途では決定的な欠点となるでしょう。 以下は実際の動作例です: Bに修正をステージングする前: B* git history fixup Bの後: B*は修正が折り込まれたBです。コミットを書き換えると新しいハッシュが生成されるため、CとDは自動的にC*とD*としてその上に再作成され、feat-1とfeat-2ブランチの先端もそれに追随します。 最も重要な特性は、3つのコマンドすべてに共通するアトミック性です。ツリーを半壊した状態のまま放置することはありません。これは、競合を引き起こす可能性のある操作を拒否することでこれを管理します。 明確にしておくと、これはjjよりも厳密に機能が劣ります。jjは競合をファーストクラスとして扱い、リベース中に競合状態を維持して後で解決できるようにします。git historyはまだこれを実行しませんが、ドキュメントは扉を開いたままにしています:「この制限は意図的なものであり、履歴の書き換えは状態を保持する操作を意図していないためです。Gitがファーストクラスの競合をサポートするようになれば(もしそうなれば)、この制限は解除される可能性があります。」 つまり、この制限は将来変更される可能性があるということです。そうなったらエキサイティングです! reword git history rewordは、古いコミットのコミットメッセージを更新し、その上のすべてを自動的にリベースします。これは、イテレーション中に設計が変更された際にコミットメッセージを修正するために非常に役立ちます。 git history reword <commit>は、そのコミットの既存のメッセージでエディタを開きます。編集して保存すると、残りのスタックがその上に再構築され、ブランチもそれに追随します。これは、ツリーの内容ではなくコミットメッセージに対するもの以外は、fixupと全く同じです。 メッセージのみを変更するため、reword(後述のsplitも同様)はインデックスやワーキングツリーに一切触れません。コミットグラフのみを操作します。そのため、どちらのコマンドも、チェックアウトしていないブランチ上のコミットを、現在作業中の内容を邪魔することなく書き換えることができます。 前: B git history reword Bの後: B* Bのメッセージのみが変更されますが、それでも新しいハッシュが生成されるため、Cはその上にC*として再構築され、feat-1がそれに追随します。 split git history splitは、1つのコミットを2つに分割し、それぞれについてインタラクティブに何が必要かを選択します。これはgit add -pの同等物ですが、git rebaseでの体操は不要です。私はこれが3つの中で最も特殊なコマンドだと感じましたが、必要な時には非常に役立ちます。 具体的には、git history split <commit>は、そのコミットの差分に対して、ハンクごとのプロンプトを表示します。保持するハンクが最初のコミットになり、残りは2番目のコミットになります。 Bが2つの無関係な変更をバンドルしていた前: B git history split Bの後: B* BはB1とB2になり、Cはそのペアの上にC*として再構築されます。 結論 jjを使用している人の数から判断すると、まだ私が理解できていない重要な考え方のシフトがあると思います。そして明確にしておくと、git historyは完全なギャップを埋めるものではありません。jjは簡単なアンドゥのための操作ログを提供し、ワーキングコピーをコミットとしてモデル化し、リベース中に競合を処理できますが、これらはgit historyがやろうとしていることではありません。 しかし、現時点では、git historyは人々をjjに惹きつける多くの要素を取り入れる上で大きな一歩であり、すでに私が毎日使用しているツールに含まれています。そして、ドキュメントの書き方は、今後のリリースでさらなる改善が期待できることを示唆しています! この投稿を楽しんだなら、私のニュースレターを購読するか、RSSでフォローすることを検討してください。Hacker NewsやLobstersで共有することもできます。 # 19:40 / #git 興味があれば、この経験について投稿を書くのは喜んで行います。↩︎