Gitを使用した複数の作業ディレクトリ?


242

これがGitでサポートされているものかどうかはわかりませんが、理論的には問題なく機能するようです。

私のワークフローでは、複数のブランチのファイルを同時に編集することがよくあります。つまり、あるブランチでいくつかのファイルを開きたいときは、別のブランチの別のファイルの内容を編集しているときです。

これに対する私の典型的な解決策は、2つのチェックアウトを作成することですが、ブランチと参照をそれらの間で共有できないのは残念です。私が望むのは、同じ.gitフォルダで管理されている2つの作業ディレクトリを用意することです。

ローカルのgit cloneソリューション(デフォルトは共有オブジェクトをハードリンクすること、および--sharedオプションは元のリポジトリで代替オブジェクトストアをセットアップする)を知っていますが、これらのソリューションはディスク領域の使用量を削減するだけです、特に--sharedの場合、危険に満ちているようです。

1つの.gitフォルダーを使用する方法はありますか?それによって2つの作業ディレクトリがサポートされていますか?または、Gitは常に1つの作業ディレクトリのみをチェックアウトするようにハードコードされていますか?


1
git-new-workdirgit checkout --to=<path>Git 2.5 で置き換えられます。以下の私の回答を
VonCは

3
実際には、コマンドはgit worktree add <path> [<branch>](Git 2.5 rc2)になります。以下の私の編集済みの回答を
VonC、2015

あなたが最初に質問した後に状況が変わったので、あなたは受け入れられた回答VonCの回答を変更すべきです。
xaxxon 2017年

更新された回答をありがとう!
jtolds 2017

回答:


293

Gitの2.5は、2015年7月以来の代替提案contrib/workdir/git-new-workdirgitのworktreeを

Junio C Hamano()によるcommit 68a2e6aを参照してください。gitster

リリースノートには言及します

その代わりはcontrib/workdir/git-new-workdir、シンボリックリンクに依存せず、借り手の借り手と借り手のお互いを認識させることでオブジェクトと参照の共有をより安全にします。

コミット799767cc9(Git 2.5rc2)を参照

つまり、これでgit worktree add <path> [<branch>]

作成<path>してチェックアウト<branch>します。新しい作業ディレクトリは現在のリポジトリにリンクされ、HEAD、インデックスなどの作業ディレクトリ固有のファイルを除くすべてを共有しgit worktreeます。

gitリポジトリは複数の作業ツリーをサポートできるため、一度に複数のブランチをチェックアウトできます。
を使用するgit worktree addと、新しい作業ツリーがリポジトリに関連付けられます。

この新しい作業ツリーは、「git init」または「git clone」によって作成された「メイン作業ツリー」ではなく、「リンクされた作業ツリー」と呼ばれます。
リポジトリには、1つのメイン作業ツリー(ベアリポジトリでない場合)と0個以上のリンクされた作業ツリーがあります。

詳細:

リンクされた各作業ツリーには、リポジトリの$GIT_DIR/worktreesディレクトリにプライベートサブディレクトリがあります 。
プライベートサブディレクトリの名前は通常、リンクされた作業ツリーのパスのベース名であり、一意にするために番号が追加されている可能性があります。
たとえば$GIT_DIR=/path/main/.git、コマンドgit worktree add /path/other/test-next nextが次のものを作成する場合:

  • リンクされた作業ツリー/path/other/test-next
  • また、$GIT_DIR/worktrees/test-nextディレクトリを作成します(または既に取得されている$GIT_DIR/worktrees/test-next1場合test-next)。

リンクされた作業ツリー内:

  • $GIT_DIRこのプライベートディレクトリをポイントするように設定されています(例/path/main/.git/worktrees/test-nextでは)。
  • $GIT_COMMON_DIRメインの作業ツリー$GIT_DIR(例:)を指すように設定されています/path/main/.git

これらの設定は.git、リンクされた作業ツリーの最上位ディレクトリにあるファイルで行われます。

リンクされた作業ツリーが完了したら、単に削除できます。
リポジトリ内作業ツリーの管理ファイルは、最終的に(参照自動的に削除されますgc.pruneworktreesexpiregit config)、またはあなたが実行できるgit worktree pruneすべての古い管理ファイルをクリーンアップするために、メインまたは任意のリンク作業ツリーに。


警告:注意すべきgit worktree「バグ」セクションがまだあります。

サブモジュールのサポートは不完全です。
スーパープロジェクトを複数回チェックアウトすることはお勧めしません。


注:git 2.7rc1(2015年11月)では、ワークツリーを一覧表示できます。
参照bb9c03bをコミットし92718b7をコミットし5193490をコミットし1ceb7f9をコミットし1ceb7f9をコミットし5193490をコミットし1ceb7f9をコミットし1ceb7f9コミット、(2015年10月8日)を92718b7コミット5193490をコミットし1ceb7f9をコミットし1ceb7f9コミット、(2015年10月8日)の5193490をコミットし1ceb7f9をコミット(2015年10月8日)、1ceb7f9をコミット(2015年10月8日)、ac6c561コミット(2015年10月2日)by Michael Rappazzo(rappazzo
(による合併Junio C浜野- gitster-a46dcfbをコミットし、2015年10月26日)

worktree: ' list'コマンドを追加

' git worktree list'はワークツリーリストを反復処理し、ワークツリーへのパス、現在チェックアウトされているリビジョンとブランチ、およびワークツリーが裸かどうかを含むワークツリーの詳細を出力します。

$ git worktree list
/path/to/bare-source            (bare)
/path/to/linked-worktree        abcd1234 [master]
/path/to/other-linked-worktree  1234abc  (detached HEAD)

磁器フォーマットのオプションも利用できます。

磁器の形式には、属性ごとに1つの線があります。

  • 属性は、1つのスペースで区切られたラベルと値とともにリストされます。
  • ブール属性( 'bare'や 'detached'など)はラベルとしてのみリストされ、値がtrueの場合にのみ存在します。
  • 空の行はワークツリーの終わりを示します

例えば:

$ git worktree list --porcelain

worktree /path/to/bare-source
bare

worktree /path/to/linked-worktree
HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
branch refs/heads/master

worktree /path/to/other-linked-worktree
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
detached

注:ワークツリーフォルダーを移動する場合は、ファイルを手動で更新する必要がありgitdirます。

参照してください618244eコミット(2016年1月22日)を、そしてd4cddd6コミット(2016年1月18日)によるグエンタイ・ゴックDuyと(pclouds
協力者:エリックサンシャイン(sunshineco
(合併によりJunio C浜野- gitster-d0a1cbcコミット、2016年2月10日)を

git 2.8(2016年3月)の新しいドキュメントには、次のものが含まれます。

リンクされた作業ツリーを移動する場合gitdirは、エントリのディレクトリにある ' 'ファイルを更新する必要があります。
たとえば、リンクされた作業ツリーがに移動され/newpath/test-next、その.gitファイルがを指している/path/main/.git/worktrees/test-next場合は、代わり/path/main/.git/worktrees/test-next/gitdirに参照/newpath/test-nextに更新 します。


ブランチを削除するときは注意してください:git 2.9(2016年6月)より前は、別の作業ツリーで使用中のブランチを削除できました。

git worktree」機能が使用されている場合、「git branch -d」は別のワークツリーでチェックアウトされているブランチの削除を許可しました。

山口一樹()によるcommit f292244(2016年3月29日)を参照してください。 協力者:エリックサンシャイン((合併によりJunio C浜野- -4fca4e3コミット、2016年4月13日)をrhenium
sunshineco
gitster

branch -d:現在チェックアウトされているブランチの削除を拒否

現在の作業ツリーによってブランチがチェックアウトされている場合、ブランチの削除は禁止されています。
ただし、ブランチが他の作業ツリーによってのみチェックアウトされている場合、削除は正しく行われません。現在の作業ツリーのHEADと比較するだけでなく、ブランチが
使用さfind_shared_symref()れているかどうかを確認するために使用します。


同様に、git 2.9(2016年6月)以前は、別のワークツリーでチェックアウトされたブランチの名前を変更しても、他のワークツリーのシンボリックヘッドは調整されませんでした。

参照してください18eb3a9コミット(2016年4月8日)に、そして70999e9をコミットし2233066コミット(2016年3月27日)のことで山口和樹(rhenium
(合併によりJunio C浜野- gitster-741a694コミット、2016年4月18日)を

branch -m:すべてのワークツリーHEADを更新します

ブランチの名前を変更すると、現在の作業ツリーのHEADのみが更新されますが、古いブランチを指すすべての作業ツリーのHEADを更新する必要があります。

これは現在の動作です。/path/to/wtのHEADは更新されません。

  % git worktree list
  /path/to     2c3c5f2 [master]
  /path/to/wt  2c3c5f2 [oldname]
  % git branch -m master master2
  % git worktree list
  /path/to     2c3c5f2 [master2]
  /path/to/wt  2c3c5f2 [oldname]
  % git branch -m oldname newname
  % git worktree list
  /path/to     2c3c5f2 [master2]
  /path/to/wt  0000000 [oldname]

このパッチは、ブランチの名前を変更するときに関連するすべてのワークツリーヘッドを更新することにより、この問題を修正します。


ロック機構はgit 2.10(2016年第3四半期)で正式にサポートされています

参照080739bコミット6d30862コミット58142c0コミット346ef53コミット346ef53コミット58142c0コミット346ef53コミット346ef53コミット(2016年6月13日に)、そして984ad9eコミット6835314コミットにより(2016年6月3日)のグエンタイ・ゴックデュイ(pclouds
提案者:エリックサンシャイン(sunshineco
(による合併Junio C浜野- gitster-2c608e0コミット、2016年7月28日)を

git worktree lock [--reason <string>] <worktree>
git worktree unlock <worktree>

リンクされた作業ツリーが、常にマウントされているわけではないポータブルデバイスまたはネットワーク共有に格納されている場合は、git worktree lockコマンドを発行して、その管理ファイルが剪定されないようにすることができます--reason

<worktree>:作業ツリーのパスの最後のパスコンポーネントが作業ツリー間で一意である場合、これを使用して作業ツリーを識別できます。
たとえば、「/abc/def/ghi」と「/abc/def/ggg」で作業ツリーのみを使用する必要がある場合、「ghi」または「def/ghi」で以前の作業ツリーを指すだけで十分です。


Gitの2.13(Q2 2017)追加lockオプション507e6e9コミットすることにより(2017年4月12日)のグエンタイ・ゴックDuyと(pclouds
提案者:デビッドテイラー(dt
協力者:ジェフキング(peff
(合併によりJunio C浜野- gitster-e311597コミット、2017年4月26日)を

作成後すぐにワークツリーをロックできるようにします。
これは、「git worktree add; git worktree lock」と「git worktree prune」の間の競合を防ぐのに役立ちます。

したがってgit worktree add' --lockgit worktree lockafter git worktree addと同等ですが、競合状態はありません。


Gitの2.17+(Q2 2018)を追加git worktree move/ git worktree removeこの回答を参照してください


Git 2.19(2018年第3四半期)に、「--quiet」オプションを追加して、「」をgit worktree add冗長にしません。

Elia Pinto()によるcommit 371979c(2018年8月15日)を参照してください。 支援者:MartinÅgren、Duy Nguyen(、およびEric Sunshine((による合併Junio C浜野- -a988ce9コミット 2018年8月27日)devzero2000
pcloudssunshineco
gitster

worktree--quietオプションを追加

他のコマンドと同様に、 ' --quiet'オプションをに追加します。 「」以外のすべてのコマンドは現在デフォルトでサイレントであるため、「」が影響を受ける唯一のコマンドです。git worktreegit
addlist


git worktree add」は「statを使用して使用可能な名前を検索してから」を使用していたことに注意してくださいmkdir
これは、Git 2.22(2019年第2四半期)でループ内で使用mkdirおよび対応することで修正されEEXISTました。

Michal Suchanek()によるcommit 7af01f2(2019年2月20日)を参照してください。(合併によりJunio C浜野- -20fe798コミット、2019年4月9日)をhramrach
gitster

worktreeworktree addレースを修正

Gitはstatループを実行して、使用可能なワークツリー名を見つけmkdir、見つかった名前に対して実行します。
それを回しmkdirworktreeアド同じ自由な名前を見つけ、最初のディレクトリを作成するための別の呼び出しを避けるために、ループ。


Git 2.22(2019年第2四半期)は、Gitリポジトリに作業ツリーがあるかどうかを通知するロジックを修正し、git branch -D現在誤ってチェックアウトされているブランチを「」が削除するのを防ぎます。
このロジックの実装は、珍しい名前のリポジトリでは壊れていましたが、最近では残念ながらこれがサブモジュールの標準です。

Jonathan Tan()によるcommit f3534c9(2019年4月19日)を参照してください。(による合併Junio C浜野- -ec2642aコミット、2019年5月8日)をjhowtan
gitster

コードプルリクエスト178インサイト

worktreeis_bareヒューリスティックの更新

" git branch -D <name>"が実行されると、Gitは通常、最初にそのブランチが現在チェックアウトされているかどうかをチェックします。
ただし、そのリポジトリのGitディレクトリが「<repo>/.git」でない場合、このチェックは実行されませんsuper/.git/modules/<repo>。たとえば、そのリポジトリが、Gitディレクトリが「」として保存されているサブモジュールである場合です。
これにより、チェックアウトされていてもブランチが削除されます。

これは、get_main_worktree()中にworktree.cセットis_bareのみworktreeのパスは「で終わらない場合はレポが裸であることをヒューリスティック使用worktree上/.git」、それ以外の場合は裸ではないが。
このis_bareコードは、ヒューリスティックに従って92718b7( " worktree:ワークツリー構造に詳細を追加"、2015-10-08、Git v2.7.0-rc0)で導入されましたpre-core.bare

このパッチは2つのことを行います:

  • ティーチget_main_worktree()を使用するis_bare_repository()代わりに、で導入7d1864c(「(is_bare_repository導入)とcore.bare設定変数」、2007年1月7日、Gitのv1.5.0デベロッパー-RC1)とで更新e90fdc3(「クリーンアップ作業ツリーの取り扱い」、2007 -08-01、Git v1.5.3-rc4)。
    これにより、git branch -D <name>上記の「」の問題が解決されます。

ただし...リポジトリにcore.bare=1" git"コマンドがそのセカンダリワークツリーの1つから実行されている場合、is_bare_repository()falseを返します(使用可能なワークツリーがあるため、問題ありません)。

そして、それが裸のときにメインワークツリーを裸でないものとして扱うことは問題を引き起こします:

たとえば、メインワークツリーがむきだしの場合でも、メインワークツリーのHEADによって参照されるセカンダリワークツリーからブランチを削除できない。

それを回避するために、core.bare設定時にも確認してくださいis_bare
の場合core.bare=1は信頼し、そうでない場合はを使用しますis_bare_repository()


1
これは彼らが作成した中で最もクールなもので、まさに私が探していたものです。それをありがとう!

作業ツリーのみを削除してブランチを維持する方法
Randeep Singh

@DotnetRocksは、作業ツリー(コンピューターのローカルフォルダー)を削除できます。ブランチには影響しません。メインの.gitリポジトリには、すべてのブランチと共に、コミットされた完全な履歴が含まれます。作業ツリーが削除されました。
VonC、2015年

ええ、でも私のシステムのフォルダに移動して作業ツリーを削除して削除すると、gitはそのブランチにチェックアウトできず、<path>(<path> worktree path)で既にチェックアウトしていると言います。しかし、私が次のようにすると、次のようになります。rm -rf <path> git worktree pruneすると、機能します。そうですか?
Randeep Singh

1
@Jayanありがとうございます。2.5と2.7がリリースされたことを明確にしました。
VonC、2016年

113

git分布が付属しています貢献したスクリプトと呼ばれますgit-new-workdir。次のように使用します。

git-new-workdir project-dir new-workdir branch

ここで、project-dirは.gitリポジトリを含むディレクトリの名前です。このスクリプトは.git、共有できないファイル(現在のブランチなど)を除いて、元のディレクトリへの多くのシンボリックリンクを持つ別のディレクトリを作成し、2つの異なるブランチで作業できるようにします。

少し壊れやすいように見えますが、それはオプションです。


3
+1私は修正された立場です、これはかなり素晴らしいです。プッシュ/プルを行わず、シンボリックリンクのみを使用して、2つの異なるチェックアウトリポジトリ間で履歴とブランチを瞬時に共有しているようです。gitがこれを処理できることはまったく知りませんでした。残念ながら、私のディストリビューションには含まれていません。
貧乳

2
msysgit(windows)を使用している場合は、この移植版のスクリプトを使用できます:github.com/joero74/git-new-workdir
amos

9
通常は問題なく動作しますが、同じブランチを別の場所で誤って編集した場合、修正を行うのは簡単ではありません。
grep

Git <2.5で立ち往生していて、サブモジュールがgit-new-workdir-recursiveある人は、のラッパーであるを試してくださいgit-new-workdir
2016年

13

私はここで見つけられなかった解決策を期待してこの質問に出くわしました。だから今、私がいることでした私が必要なものを見つける、私は他人のためにそれをここに投稿することを決めました。

注意:OP状態のように複数のブランチを同時に編集する必要がある場合、これはおそらく良い解決策ではありません。編集するつもりがないの は、複数のブランチを同時にチェックアウトするためです。(1つの.gitフォルダーに基づく複数の作業ディレクトリ。)

初めてこの質問に来てから、いくつか学んだことがありました。

  1. ベアリポジトリ」とは何ですか。これは.git、作業ツリーにあるのではなく、基本的にディレクトリの内容です。

  2. .gitコマンドラインでgitオプションを使用して、使用しているリポジトリの場所(ディレクトリの場所)を指定できるという事実--git-dir=

  3. で作業コピーの場所を指定できるという事実 --work-tree=

  4. 「ミラーレポ」とは何ですか。

この最後はかなり重要な違いです。実際にはリポジトリで作業したくありません。異なるブランチやタグのコピーを同時にチェックアウトする必要があるだけです。実際には、ブランチリモートのブランチと異なるものにならないことを保証する必要があります。鏡は私にぴったりです。

だから私のユースケースでは、私は次のようにすることで必要なものを得ました:

git clone --mirror <remoteurl> <localgitdir> # Where localgitdir doesn't exist yet
mkdir firstcopy
mkdir secondcopy
git --git-dir=<localgitdir> --work-tree=firstcopy checkout -f branch1
git --git-dir=<localgitdir> --work-tree=secondcopy checkout -f branch2

これに関する大きな注意点は、2つのコピーに個別のHEADがないことです。したがって、上記の後に実行git --git-dir=<localgitdir> --work-tree=firstcopy statusすると、ブランチ2からブランチ1へのすべての違いがコミットされていない変更として表示されます-HEADがブランチ2を指しているためです。(私が実際にローカルで変更を行うつもりはないので、-fオプションをに使用するのはそのcheckoutためです-f。このオプションを使用している限り、ワークツリーのタグまたはブランチをチェックアウトできます。)

編集必要とせずに、同じコンピューター上で複数のチェックアウトを共存させる私の使用例では、これは完全に機能します。他の回答でカバーされているようなスクリプトなしで複数の作業ツリーに複数のHEADを設定する方法があるかどうかはわかりませんが、とにかくこれが他の誰かに役立つことを願っています。


これはまさに私が探していたものですが、動作させることができません...「致命的:gitリポジトリではありません: '<localgitdir>'」を取得しています。何か案は?
Dan R

気にしないでください、私は自分のディレクトリ名に「〜」を使用していたことがわかりました、そしてgitはそれが好きではありませんでした。フルパスを使用した場合、問題なく動作しました。
Dan R

@DanR、助けてくれてうれしいです。:)あなたも使用するかもしれません$HOME。上記の方法について、後で発見したもう1つの警告があります。これは、1つまたは別のブランチに存在しないファイルに関係しています。Aをdir1にチェックアウトし、Bをdir2にチェックアウトし、Cを強制的にdir1にチェックアウトする場合、Aに存在するがBまたはCには存在しないファイルがある場合、強制チェックアウトによってもファイルはdir1から削除されません。 。そのためgit clean、そのような場合は実験する必要があるかもしれません。あるいは、私がしたことを行い、このメソッドを使用して、新しく作成されたディレクトリを作成するだけです。
ワイルドカード2016年

先端をありがとう。とりあえず、これをCLIツールの一部としてルビーでラップするつもりなので、常に最初から始めるようにします。
Dan R

@DanR、私がその時に書いていたコードを見るの興味があるかもしれません。CFEngine構文チェックを除いて、ほとんどの場合、すべてのgitステージングスクリプトに適用できます。(おそらく、Rubyの構文チェックに置き換えることもできます。):)
ワイルドカード

3

私が考えることができる唯一の解決策は、2つのディレクトリを複製し、それらを互いのリモートリポジトリとして追加することです。その後、実際に何もリモートリポジトリにプッシュせずに、変更されたものから別のものにデータをプルし続けることができます。

いくつかのブランチをリモートにプッシュしたくないので、リモートの2つのクローンではなく、2つの作業ディレクトリーが必要だと想定しています。それ以外の場合は、リモートの2つのクローンが問題なく機能します。3つすべてを同期させるには、プッシュとプルをいくつか実行する必要があります。


こんにちは。上記の人は、git worktreeコマンドというクールなソリューションを共有していました 。同じリポジトリを数回クローンするよりもうまく機能します。ぜひお試しください。この新機能が気に入るはずです。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.