DUICUO

ブランチを切り替えずに複数の Git ブランチを同時に操作するにはどうすればよいですか?

[[436465]]

背景

前回の記事「Gitコミット履歴をクリーンに保つ:3つのヒントで十分」で、読者の方から「とても便利な機能だ」というコメントをいただきました。このコメントを見て、仕事で使っているもう一つのGit機能を思い出しました。これも非常に便利なので、ぜひ全文をシェアしたいと思います。

プログラマーなら誰でも、プロジェクトに参加すると、開発から本番環境へのデプロイ、ホットフィックス、そしてリリース後のメンテナンスまで、ほぼすべての作業に関わらなければならないという感覚を経験したことがあるでしょう。ある機能の開発に取り組んでいる最中に、上司から突然本番環境のホットフィックスの作業を依頼されることはよくあることです。このような状況に直面したGitユーザーは通常、2つの解決策を思いつきます。

  • 急いで不完全な機能を送信し、ブランチをホットフィックスに切り替えます。
  • `git stash | git stash pop` は、ホットフィックスに切り替える前に作業をスタッシュします。

2 番目の方法は最初の方法よりもはるかに優れていますが、次のシナリオでは stash はまだ適切なソリューションではありません。

私たちが直面しているシナリオ

  1. メイン ブランチで長時間実行テストが実行されている場合、ホットフィックス ブランチまたは機能ブランチに切り替えるとテストが停止します。
  2. プロジェクトは非常に大規模であり、頻繁なインデックス切り替えには多大なコストがかかります。
  3. 数年前にリリースされた設定が異なる古いバージョンがあり、それらを再構築して IDE 用に適応させると、大きなオーバーヘッドが発生する可能性があります。
  4. ブランチを切り替えるには、dev/qa/prod などの対応する環境変数をリセットする必要があります。
  5. 問題のデバッグと再現を支援するために、同僚のコードに切り替える必要があります。

学生の中には、「`git clone` を使って複数のリポジトリをクローンすればいいのでは?」と考える人もいるかもしれません。これは問題を解決する 1 つの方法ですが、他の多くの問題も隠れてしまいます。

  • 複数のリポジトリの状態を同期するのは困難です。例えば、素早くチェリーピックする方法はありません。あるリポジトリでブランチをチェックアウトすると、他のリポジトリでも再度チェックアウトが必要になります。
  • `git history/log` は反復的であり、プロジェクトの履歴が非常に長い場合、`.git` フォルダーの内容が大量のディスク領域を消費する可能性があります。
  • 同じプロジェクトの複数のリポジトリを管理するのは困難です。

では、前述の問題に遭遇することなく、これらの特殊なシナリオのニーズを満たすにはどうすればよいでしょうか?

git ワークツリー

実は、これはGitが2015年からサポートしている機能なのですが、あまり知られていません。git-worktreeを使うのはとても便利です。ターミナルで次のコマンドを入力するだけです。

  1. git ワークツリー--help  

ヘルプドキュメントをすぐに表示できます。

簡単に言えば、git-worktree の目的は次のとおりです。

1 つのリポジトリを管理するだけで済みますが、相互に干渉することなく複数のブランチで同時に作業できます。

上記では赤で強調表示されているコマンドが多数ありますが、一般的に使用されるのは次の 4 つだけです。

  1. git ワークツリーを追加[-f] [ --detach] [--checkout] [--lock] [-b ] []  
  2.  
  3. git ワークツリーリスト [ --porcelain]  
  4.  
  5. git ワークツリー削除 [-f]
  6.  
  7. git ワークツリーのプルーニング [-n] [-v] [ --expire ]  

詳細に入る前に、見落としているかもしれない 2 つの Git の概念について説明する必要があります。

デフォルトでは、`git init` または `git clone` によって初期化されたリポジトリには、メイン ワークツリーと呼ばれる 1 つのワークツリーのみがあります。

ディレクトリ内でGitコマンドを使用する場合、現在のディレクトリには.gitフォルダまたは.gitファイルのいずれかが含まれている必要があります。.gitファイルのみが存在する場合は、その内容が.gitフォルダを指している必要があります。

2 番目の文は少し複雑ですが、次の例を見ると理解しやすくなります。

git ワークツリーの追加

現在のプロジェクト ディレクトリ構造は次のとおりです (amend-crash-demo はリポジトリのルートです)。

  1. └── 修正クラッシュデモ
  2.  
  3. 1 ディレクトリ

`amend-crash-demo` ディレクトリに移動し、コマンド `git worktree add ../feature/feature2` を実行します。

  1. ?amend-crash-demo git:(main) git ワークツリーに../feature/feature2を追加します
  2.  
  3. ワークツリーを準備しています(新しいブランチ「feature2」
  4.  
  5. HEAD現在82b8711あります。メインファイルを追加してください。

ディレクトリ構造を再検討する

  1. ├── 修正クラッシュデモ
  2. └── 特集
  3. └── 特集2
  4.  
  5. 3つのディレクトリ

このコマンドは、HEAD が存在するコミット(または git ログ内の任意のコミット)に基づいて、feature2 という名前のブランチを作成します。ブランチのディスク上の位置は上記の構造に示されています。

../feature/feature2/ に移動すると、このブランチには .git フォルダはありませんが、 .git ファイルがあることがわかります。ファイルを開くと、内容は次のとおりです。

  1. gitdir: /Users/rgyb/Documents/projects/amend-crash-demo/.git/worktrees/feature2

さて、上記のポイント 2 をもう一度見てみると、もっと明確になりませんか?

次に、メインのワークツリーに干渉することなく、feature2 ブランチで必要な操作 (追加/コミット/プル/プッシュ) をすべて実行できます。

一般的に、プロジェクトチームには、feature/JIRAID-Title、hotfix/JIRAID-Title といった特定のブランチ命名規則があります。上記のコマンドを使用して新しいワークツリーを作成すると、ブランチ名の「/」はファイルディレクトリとして扱われます。

  1. git ワークツリー../hotfix/hotfix/JIRA234-fix-namingを追加します

コマンドを実行すると、ファイル ディレクトリ構造は次のようになります。

  1. ├── 修正クラッシュデモ
  2. ├── 特集
  3. │ └── 特集2
  4. └── ホットフィックス
  5. └── ホットフィックス
  6. └── JIRA234-命名修正
  7.  
  8. 6つのディレクトリ

これは明らかに私たちが望んでいることではありません。そこで、`git checkout -b` と同様に、`-b` パラメータが役に立ちます。

コマンドを実行:

  1. git ワークツリーを追加-b "hotfix/JIRA234-fix-naming" ../hotfix/JIRA234-fix-naming

ディレクトリ構造を見てみましょう。

  1. ├── 修正クラッシュデモ
  2. ├── 特集
  3. │ └── 特集2
  4. └── ホットフィックス
  5. ├── JIRA234-命名修正
  6. └── ホットフィックス
  7. └── JIRA234-命名修正
  8.  
  9. 7つのディレクトリ

JIRA234-fix-naming ディレクトリに移動します。デフォルトでは、これは hotfix/JIRA234-fix-naming ブランチです。

ワークツリーは簡単に作成できますが、適切な管理を行わないとプロジェクトのディレクトリ構造が必然的に混乱してしまいます。これは避けたいものです。そのため、特定のリポジトリに対してどのワークツリーが作成されているかを明確に把握する必要があります。

git ワークツリーリスト

すべてのワークツリーは単一のリポジトリを共有するため、任意のワークツリー ディレクトリで次のコマンドを実行して、ワークツリーのリストを表示できます。

  1. git ワークツリーリスト

コマンドを実行すると、上記で作成したすべてのワークツリー情報が表示され、メインのワークツリーもここに表示されます。

  1. /Users/rgyb/Documents/projects/amend-crash-demo 82b8711 [メイン]
  2.  
  3. /Users/rgyb/Documents/projects/chore/chore 8782898 (分離されたHEAD)
  4.  
  5. /Users/rgyb/Documents/projects/feature/feature2 82b8711 [feature2]
  6.  
  7. /Users/rgyb/Documents/projects/hotfix/hotfix/JIRA234-fix-naming 82b8711 [JIRA234-fix-naming]
  8.  
  9. /Users/rgyb/Documents/projects/hotfix/JIRA234-fix-naming 82b8711 [hotfix/JIRA234-fix-naming]

ワークツリーが作業を終えたら、すぐに削除する必要があります。そうしないと、大量のディスク領域が無駄になってしまいます。

git ワークツリーの削除

このコマンドは非常に簡単です。ワークツリーの名前を使用して「remove」と言うだけです。

  1. git worktree で hotfix/hotfix/JIRA234-fix-naming を削除します

この時点で、誤ったブランチ名のホットフィックスは削除されました。

  1. /Users/rgyb/Documents/projects/amend-crash-demo 82b8711 [メイン]
  2. /Users/rgyb/Documents/projects/chore/chore 8782898 (分離されたHEAD)
  3. /Users/rgyb/Documents/projects/feature/feature2 82b8711 [feature2]
  4. /Users/rgyb/Documents/projects/hotfix/JIRA234-fix-naming 82b8711 [hotfix/JIRA234-fix-naming]

ワークツリーを作成し、変更を加えた後、突然そのワークツリーが不要になったとします。上記のコマンドでは削除できません。このような場合に -f パラメータが役立ちます。

  1. git ワークツリー削除 -f ホットフィックス/JIRA234-命名修正

ワークツリーを削除した後でも、Gitには不要な管理ファイルが多数残っています。システムをクリーンな状態に保つには、さらなるクリーンアップが必要です。

git ワークツリーのプルーニング

このコマンドは、作業領域が常に整頓された状態を保つためのフォールバック クリーニング手順です。

要約

ここまでで、git-worktree の使用フロー全体が次の 4 つのコマンドで構成されていることがおわかりになったと思います。

  1. git ワークツリーの追加 
  2.  
  3. git ワークツリーリスト
  4.  
  5. git ワークツリーの削除
  6.  
  7. git ワークツリーのプルーニング

これで、`git worktree` と `git clone multiple repos` の違いが理解できたかと思います。リポジトリを 1 つだけ維持し、複数のワークツリーを作成することで、シームレスな操作が可能になります。

私の実践:私は通常、git ワークツリーを使用しています。一貫したディレクトリ構造を維持しています。例えば、`feature` ディレクトリにはすべての機能のワークツリーを保存し、`hotfix` ディレクトリにはすべてのホットフィックスのワークツリーを保存します。これにより、複数のワークツリーを作成することでディスク全体のディレクトリ構造が混乱するのを防いでいます。

ディスク管理に関しては、私は少々完璧主義者です。理想的には、特定のリポジトリのワークツリーはリポジトリのファイルディレクトリに配置するべきです。しかし、そうするとGitが新しく作成されたワークツリー以下のすべてのファイルを追跡することになります。Gitがワークツリーの内容を追跡しないようにするために、.gitignoreファイルを繰り返し変更するのは絶対に適切ではありません。次のセクションでは、より良い方法を紹介します。

魂を探求する質問

メインワークツリーは削除できますか?なぜですか?

ワークツリーを繰り返し作成および削除することによって `repo/.git/wortree` ディレクトリに生じた変更を理解できますか?