web-dev-qa-db-ja.com

Git Stashをワークフローとして使用することはアンチパターンですか?

私は最近、私と私のチームがGitをどのように使用し、ワークフローがどのように機能するかを調べています。現在は機能しているように見える機能分岐ワークフローを使用しています。

私たちのチームの何人かの個人が git stash に基づくワークフローを使用することも確認しました。ワークフローは次のようになります。

  • メインブランチ(masterなど)で作業する
  • コミットする
  • 変更を取得したり、ブランチを切り替えたりする必要がある場合は、コミットされていない変更をスタッシュにプッシュします
  • 更新が完了したら、変更を隠しておいてください。

このワークフローは機能ブランチワークフローの代わりに使用されることについて言及する必要があります。ブランチを作ってそれに取り組むのではなく、ここの開発者は1つのブランチだけに取り組み、適切と思われるスタックにプッシュ/ポップします。

実際にはこれは素晴らしいワークフローだとは思いません。このようにgit stashを使用するよりも分岐の方が適切でしょう。 git stashの値は緊急操作として見ることができますが、日常の定期的なワークフローで使用することはできません。

Git stashを定期的に使用することはアンチパターンと見なされますか?もしそうなら、発生する可能性のある特定の問題は何ですか?そうでない場合、利点は何ですか?

25
joshin4colours

Git SCM Book から:

多くの場合、プロジェクトの一部で作業しているとき、物事は厄介な状態にあり、ブランチを切り替えて少し他の作業を行う必要があります。問題は、あとでこの時点に戻ることができるように、半完了した作業をコミットしたくないということです。この問題の答えはgit stashコマンドです。

スタッシングは、作業ディレクトリのダーティな状態(つまり、変更された追跡ファイルと段階的な変更)を取得し、いつでも再適用できる未完了の変更のスタックに保存します。

この説明があれば、これはアンチパターンだと思います。 Git Stashの過度に単純化された説明は、それがソース管理の「カットアンドペースト」であるということです。一連の変更されたファイルを取り、Gitの通常の分岐ワークフローの外にあるホールディングペンで「隠して」、それらの変更を後日別の分岐に再適用します。

少し前に戻ると、マスターへのコミットはここでのアンチパターンです。ブランチを使用します。それは彼らが設計されたものです。

それは本当にこれに要約されます:

あなたは壁にねじを打ち込むことができ、それは絵を保持しますが、ドライバーを使うことはあなたがすべきことです。ドライバーがすぐそばにあるときはハンマーを使わないでください。

「壊れた」コードのコミットについて

以下は意見ですが、経験からこの意見に至りました。

早い段階でコミットし、頻繁にコミットします。壊れたコードを必要なだけコミットします。何かをハッキングしている間、ローカルコミット履歴を「セーブポイント」として表示します。論理的な作業を終えたら、コミットを行います。確かにそれはすべてを壊すかもしれませんが、あなたがそれらのコミットをPushしない限り、それは問題ではありません。プッシュする前に、コミットをリベースしてスカッシュします。

  1. 新しいブランチを作成する
  2. ハックハックハック
  3. 壊れたコードをコミットする
  4. コードを磨いて動作させる
  5. 作業コードをコミットする
  6. リベースとスカッシュ
  7. テスト
  8. テストが合格したときにプッシュ

OPの場合、この Linuxカーネルメッセージスレッド は、OPのチームの一部のメンバーがGitを同様の方法で使用しているように思えるため、興味深いかもしれません。

@RibaldEddieは以下のコメントで言った:

まず第一に、隠し場所は内部では単なる別の分岐であるため、隠し場所は「分岐ワークフロー」の範囲外ではありません。

(多くの人々の怒りを招く恐れがある)

Linusさんのコメント:

「git stash」を使用すると、複数の異なるものを保管することもできますが、それらは互いに待ち行列に入れられることはありません。それらは、ある時点で不便だったために保管したランダムな独立したパッチです。

@RibaldEddieが言っていると私が思うのは、機能ブランチワークフローでgit stashを使用できるということです-これは本当です。問題はgit stashの使用ではありません。これは、マスターへのコミットとgit stashの使用の組み合わせです。 これはアンチパターンです。

明確化git rebase

@RibaldEddieのコメントから:

リベースはコピー貼り付けによく似ており、コミットされた履歴をさらに悪化させます。

(エンファシス鉱山)

ローカルのコミット履歴である限り、コミット履歴の変更は悪いことではありません。すでにプッシュしたコミットをリベースする場合、ブランチを使用している他のユーザーは基本的に孤立します。これは悪いです。

ここで、1日の間にいくつかのコミットを行ったとします。いくつかのコミットは良かった。一部は...あまり良くない。 git rebaseコマンドをコミットの抑制と組み合わせて使用​​すると、ローカルのコミット履歴をクリーンアップできます。チームの共有ブランチのコミット履歴をクリーンに保つため、パブリックブランチへの1つのコミットにマージするのはいいことです。リベース後、もう一度テストする必要がありますが、テストに合格した場合は、複数のダーティコミットではなく1つのクリーンコミットをプッシュできます。

クリーンコミット履歴 には、別の興味深いLinuxカーネルスレッドがあります。

再び、Linusから:

私はきれいな歴史を望みますが、それは本当に(a)きれいで(b)歴史を意味します。

人々はprivateツリー(彼ら自身の仕事)をリベースすることができます(おそらくそうすべきです)。これはcleanupです。しかし、他の人々のコードは決してありません。それは「破壊の歴史」です

したがって、履歴部分はかなり簡単です。主要なルールが1つと、明確化が1つだけあります。

  • あなたは決して他の人々の歴史を破壊してはいけません。他の人がやったコミットをリベースしてはいけません。基本的に、それがあなたのサインオフを持っていない場合、それは立ち入り禁止です:それはあなたのものではないので、それをリベースすることはできません。

    これは実際には他の人々historyに関するものであり、他の人々codeに関するものではないことに注意してください。彼らがメールでパッチとしてあなたに物を送って、あなたが "git am -s"でそれを適用したなら、それは彼らのコードですが、それはyourの履歴です。

    そのため、コミット自体がプライベートのものである限り、コードを記述していなくても、「git rebase」のことを思いのままに行うことができます。

  • ルールの明確化:ある公開サイトで履歴を公開すると、他の人がそれを使用している可能性があるため、private履歴ではなくなります。

    したがって、マイナーな説明としては、「コミット」だけでなく、ツリーに対してプライベートであること、そしてまだプッシュして発表していないことです。

...

最初のルールは明白で簡単ですが、「クリーン」な部分はもう少し微妙です。

  • 自分の履歴を読みやすくする

    一部の人々は、最初に物事を頭の中で練習し、間違いをしないことでこれを行います。しかし、それは非常にまれであり、私たちの残りの部分では、問題に取り組む間、「git rebase」などを使用します。

    「git rebase」は間違いありません。しかし、それはあなた自身の非常にプライベートなgitツリーである場合にのみ正しいです。

  • あなたのがらくたを公開しないでください。

    つまり、まだ「git rebase」フェーズにいる場合は、プッシュしないでください。準備が整っていない場合は、パッチを送信するか、一般に知られていないプライベートgitツリーを(「パッチシリーズの置き換え」のように)使用します。

(強調鉱山)

結論

結局、OPにはこれを行う開発者がいます。

git checkout master
(edit files)
git commit -am "..."
(edit files)
git stash
git pull
git stash (pop|apply)

ここには2つの問題があります。

  1. 開発者は習得に専念しています。これをすぐにロックします。本当に、これは最大の問題です。
  2. 開発者は、機能ブランチを使用する必要があるときに、マスターでgit stashおよびgit pullを常に使用しています。

git stashを使用することに特に問題はありません-特にプルの前に-しかし、この方法でgit stashを使用することは、Gitに優れたワークフローがある場合の反パターンです。

彼らがgit stash赤ニシンを使用している。それは問題ではありません。マスターすることにコミットすることが問題です。

32
Greg Burghardt

私は個人的にstashを使用するのは、予期しない中断が発生した場合のみです。たとえば、誰かが別のブランチに変更する必要がある質問をするような場合です。私は以前に隠し場所を忘れていたのでこれをします、そしてそれらはきれいに適用されません。機能ブランチでの定期的なコミットは、忘れることがはるかに難しく、マージが容易であるため、今は壊れたコミットを作成してからgit reset HEAD~1または後で残したくない場合はリベース。

ただし、分散バージョン管理の優れている点は、共有リポジトリが標準を満たしている限り、自分のリポジトリで好みのワークフローを使用できることです。十分なトレーニングや代替案の認識がないため、stashワークフローを使用しているだけではないことを確認しますが、それでも彼らがワークフローを選択した場合、最適とは言えないままにしてしまいます。

7
Karl Bielefeldt

あなたの質問のアンチパターンである部分は、単一の共有masterブランチの使用だと思います。ただし、masterブランチに加えてdevelopブランチを含め、スタッシュを使用して、developブランチで独自のコンテキストスイッチを処理する場合は、これはアンチパターンではなく、EtsyやFacebookなどの組織が説明するワークフローの一部を非常に厳密に反映しています。

そうは言っても、上記の@Greg Burghardtの答えは、いわゆるgit-flowまたはfeature-branchのワークフローには少なすぎます。以前は同様の戦略を提唱していましたが、それが不必要な複雑さを追加し、誤った安心感を生み出すことに気づいた後、私はもうやっていません。また、Subversionのような非分散型バージョン管理システムの時代からの持ち越しでもあります。

まず、GitはSubversionとは異なり、分散型のバージョン管理システムであるため、開発者のローカルリポジトリは、本質的にコード自体の巨大なブランチです。個々の開発者がローカルで行うことは、壊れた、またはバグのあるコードが共有リポジトリの共有ブランチにプッシュされない限り、他のチームメンバーに影響を与えません。

ただし、リベースコマンドは、再生されたコミットの1つにマージの競合がある場合、ブランチの履歴を損傷する可能性があります。 http://ryantablada.com/post/the-dangers-of-rebasing-a-branch から

リベースの残りの部分はスムーズに進み、テストはすべて成功したようです。 PRが行われます。

そして、commentsForAllPostsプロパティに依存するコードがさらに記述され、すべてが壊れています。しかし、私たちは誰に行って助けを求めますか? git blameは、コード行がサーバー側のエンジニアによってのみ作成されており、彼が手を投げていることを示しています。

これで、フロントエンドエンジニアが休暇中、病気休暇中、または知り合いです。そのコードがどのように見えるべきか誰も理解できません!

子ブランチでのマージの競合が解消され、元のコードが永久に失われるため、リベースにより、何が問題であるかを見つけるために履歴を確認するチームの機能が終了しました。

この同じマージ競合が発生し、マージが使用された場合、非難は、マージプロセス、親ブランチでのコミット、および子ブランチでのコミットでそのコード行が変更されたことを示します。 3つの順列をいじる人もいれば、元の意図をコードベースに戻し、指で頭をひっかき回すことなく作業することができます。そしてあなたが本当に持っていたすべては別のコミットでした

さらに、複数の分岐モデルでは、2つの分岐が相互に依存するコードの変更を含むことができないと想定しています。それが必然的に発生する場合、開発者は効率的に機能するために、さらに多くのブランチを操作する必要があります。

私が目にする基本的なアンチパターンはブランチとスタッシュに関連しているのではなく、かなり賢い人たちがしばらくの間話していた種類の問題についての詳細です:あなたはあなたのコードに自信がありますか?単体テストと優れたアーキテクチャ?開発者が変更について簡単に推論し、変更によって何が行われるかを理解できるように、コードを段階的に変更できますか?開発者は、新しいコードを一度実行して、実際に機能するかどうかを確認していますか? (はい、私はこれを以前に見たことがあります)。

これらの質問に対する答えが「いいえ」の場合、ブランチの数は実際には問題ではありません。開発者は、実際には準備ができていなくても、コードは準備が整っており、運用に適していると言い、ブランチの数はありません。とにかく、そのコードが本番環境に移行するときに役立ちます。

1
RibaldEddie

git stashはツールです。それ自体はパターンでもアンチパターンでもありません。ハンマーが道具であるように、それは道具です。ハンマーを使用して釘を打ち込むことはパターンであり、ハンマーを使用してねじを打ち込むことは反パターンです。同様に、git stashが適切なツールであるワークフローと環境と、それが間違っているワークフローと環境があります。

「全員がコミットしてメインラインにプッシュする」ワークフローは、リスクの高い変更がない場合にかなり合理的に機能するワークフローです。これは、コードを持つ信頼できる中央サーバーが1つあるsvn環境でよく使用されます。

ただし、Gitは1つの中央サーバーを廃止することにつながります。すべての開発者がcommitpull(または、それが好きな場合はrebase)を行っていると、Pushは常に大きな混乱を引き起こす可能性があります。

最大の問題は、壊れている進行中の問題があり、優先バグに取り組む必要がある場合に発生します。つまり、その作業を少し脇に置いて、最新のものを入手し、その作業を行う必要がありますなし前の作業が進行中の場合、実行しようとしているビルドで問題が発生します。

このため、git stashが適切なツールになります。

ただし、このワークフローの中心には大きな問題が潜んでいます。つまり、allバージョン管理ブランチの役割は単一のブランチにあります。メインライン、開発、保守、蓄積、パッケージングは​​すべてマスターにあります。 Thisは問題です。 (ブランチへのこのアプローチの詳細については、 高度なSCMブランチ戦略 を参照してください)

そして、はい、あなたはそれが素晴らしいワークフローではなく、それに問題があることを指摘しました。ただし、問題はツールgit stashではありません。問題は、ロールまたは 互換性のないポリシー の明確な分岐がないことです。

git stashは、私が少し構築した状況で、適切な状態であるかどうかわからなかった粘着性のある状態になったときに使用したものです...だから私は自分の変更を隠しておき、問題を解決する別のアプローチを模索しました。それがうまくいった場合-すばらしい、隠し場所にあるものを破棄して続行します。他の探査に問題が生じた場合は、前の変更にリセットし、スタッシュを再適用して作業します。代わりの方法は、コミット、チェックアウト、分岐してから、この新しい分岐を続行するか、戻ってそれをリセットすることです。問題は、それをちょっと探究したいときに、それを歴史に入れる価値があるのか​​ということです。

git stashはアンチパターンではありません。全員がマスターにコミットしているときの分岐の代わりにgit stashを使用するのは反パターンですが、git stashのためではありません。

まだヒットしていない場合は、 ビルドの問題 になるまで待ってください。誰かが大量のファイルにアーキテクチャ上の重要な変更を加える必要がある場合(およびマージの競合)、またはテストされていない進行中のコードがいくつかあります。このアンチパターンがあなたに追いつくために、それは本番に漏れます。

1
user40980