序論
ここでの観察は、作業を開始した後branch1
(branch2
最初に別のブランチに切り替えるのが良いことを忘れているか、または気づいていない)、次のように実行することです。
git checkout branch2
時々、Gitは「OK、あなたはブランチ2にいます!」と言います。時々、Gitは「それはできない、あなたの変更の一部を失うだろう」と言います。
Gitで許可されない場合は、変更をコミットして永続的な場所に保存する必要があります。 あなたはgit stash
それらを保存するのに使いたいかもしれません。これは、その目的の1つです。 注ことgit stash save
やgit stash push
、実際の手段は、「すべての変更をコミットしますが、まったくの枝の上に、その後、私は今の私どこからそれらを削除します。」これにより、切り替えが可能になります。進行中の変更はありません。その後git stash apply
、切り替え後にそれらを使用できます。
サイドバー:git stash save
は古い構文です。git stash push
は、Gitバージョン2.13で導入されました。これは、への引数に関するいくつかの問題を修正しgit stash
、新しいオプションを可能にするためです。基本的な方法で使用すると、どちらも同じことを行います。
必要に応じて、ここで読むのをやめることができます。
Gitがいる場合ではないだろう使用を:あなたは切り替えることができ、あなたはすでに救済策を持っていますgit stash
かgit commit
。または、変更が簡単に再作成できる場合は、を使用git checkout -f
して強制的に変更します。この答えは、変更を加え始めたにもかかわらず、 Gitがいつ許可するかについてのすべてgit checkout branch2
です。なぜそれが時々機能し、他の場合では機能しないのですか?
ここでのルールは、ある意味では単純で、別の意味では複雑で説明が難しいものです。
上記の切り替えで変更を破棄する必要がない場合に限り、作業ツリーでコミットされていない変更を使用してブランチを切り替えることができます。
つまり、これはまだ簡略化されていることに注意してください。ステージングされたgit add
sやgit rm
sなどの非常に難しいコーナーケースがいくつかありますbranch1
。A git checkout branch2
はこれを行う必要があります。
- すべてのファイルのためであるに
branch1
していないでbranch2
、1つのファイル削除。
- すべてのファイルのためにあるに
branch2
していないでbranch1
、(適切な内容で)そのファイルを作成します。
- 両方のブランチにあるすべてのファイルについて、バージョン
branch2
が異なる場合は、作業ツリーのバージョンを更新します。
これらの各ステップでは、作業ツリーに何かが含まれる可能性があります。
- 作業ツリーのバージョンがのコミットされたバージョンと同じ場合、ファイルの削除は「安全」です
branch1
。変更を加えた場合は「安全」ではありません。
- ファイルが
branch2
存在しない場合でも、ファイルの表示方法は「安全」です。2 現在は存在するが「内容が間違っている」場合、「危険」です。
- そしてもちろん、作業ツリーバージョンが既ににコミットされて
branch1
いる場合、ファイルの作業ツリーバージョンを別のバージョンに置き換えることは「安全」です。
新しいブランチ(git checkout -b newbranch
)の作成は常に「安全」と見なされます。このプロセスの一部として作業ツリーでファイルが追加、削除、または変更されることはなく、インデックス/ステージング領域も変更されません。(注意:新しいブランチの開始点を変更せずに新しいブランチを作成する場合は安全です。ただし、たとえばに別の引数を追加するとgit checkout -b newbranch different-start-point
、変更が必要にdifferent-start-point
なる場合があります。Gitは通常どおりチェックアウトの安全規則を適用します。 。)
1これは、ファイルが順番に単語の定義が必要です支店、であるためにそれが何を意味するのか定義する必要が枝を適切に。(も参照してください正確に、我々は「ブランチ」でどういう?)ここで、私は本当に平均で何コミットたへの分岐名解決します。そのパスのファイルであるであれば、ハッシュを生成します。代わりにエラーメッセージが表示される場合、そのファイルは存在しません。インデックスまたは作業ツリーにパスが存在するかどうかは、この特定の質問に答えるときには関係ありません。したがって、ここでの秘密は、それぞれの結果を調べることですP
branch1
git rev-parse branch1:P
branch1
P
git rev-parse
branch-name:path
。これは、ファイルが最大で1つのブランチに「ある」ために失敗するか、2つのハッシュIDを提供します。2つのハッシュIDが同じ場合、ファイルは両方のブランチで同じです。変更は必要ありません。ハッシュIDが異なる場合、ファイルは2つのブランチで異なり、ブランチを切り替えるために変更する必要があります。
ここで重要な概念は、コミット中のファイルは永久に凍結されるということです。編集するファイルは明らかにフリーズされていません。少なくとも最初は、2つの凍結されたコミット間の不一致のみを見ています。 残念ながら、我々 -またはGitは、また、ファイルに対処する必要がありませんあなたから離れて切り替えしようとしているコミットしているあなたにスイッチしようとしているコミットに。これにより、ファイルがインデックスや作業ツリーに存在する可能性があるため、残りの複雑化につながります。これらの作業している2つの特定の凍結されたコミットを存在させる必要はありません。
2 Gitが結局それを作成する必要がないように、「正しい内容」ですでに存在する場合、「安全な種類」と見なされる可能性があります。これを許可するGitの少なくともいくつかのバージョンを思い出しますが、テストしたところ、Git 1.8.5.4では「安全でない」と見なされていることがわかりました。同じことが、たまたま切り替え先ブランチに一致するように変更された変更済みファイルにも当てはまります。ここでも、1.8.5.4は「上書きされる」とだけ言っています。テクニカルノートの最後もご覧ください。バージョン1.5.somethingで最初にGitを使い始めてから、読み取りツリールールが変更されていないと思いますので、メモリに問題がある可能性があります。
変更がステージングされているか、ステージングされていないかは重要ですか?
はい、ある意味で。特に、変更をステージングしてから、作業ツリーファイルを「変更解除」できます。ここで違う二つの分岐内のファイル、だbranch1
とはbranch2
:
$ git show branch1:inboth
this file is in both branches
$ git show branch2:inboth
this file is in both branches
but it has more stuff in branch2 now
$ git checkout branch1
Switched to branch 'branch1'
$ echo 'but it has more stuff in branch2 now' >> inboth
この時点で、作業ツリーファイルはinboth
1つに一致するbranch2
私たちがしているにもかかわらず、branch1
。この変更は、コミットの段階ではありません。これは、git status --short
ここに示されているものです。
$ git status --short
M inboth
space-then-Mは、「変更されたがステージングされていない」ことを意味します(より正確には、作業ツリーのコピーはステージングされた/インデックスのコピーとは異なります)。
$ git checkout branch2
error: Your local changes ...
では、作業ツリーのコピーをステージングしてみましょう。これも、のコピーと一致していますbranch2
。
$ git add inboth
$ git status --short
M inboth
$ git checkout branch2
Switched to branch 'branch2'
ここでは、ステージングされた作業用コピーはどちらもの内容と一致したbranch2
ため、チェックアウトが許可されました。
別のステップを試してみましょう:
$ git checkout branch1
Switched to branch 'branch1'
$ cat inboth
this file is in both branches
ここで行った変更は、ステージング領域から失われます(チェックアウトがステージング領域を介して書き込むため)。これはちょっとしたケースです。変更はなくなっていませんが、私がそれを上演したという事実はなくなっています。
ブランチコピーとは異なる、ファイルの3番目のバリアントをステージングしてから、作業コピーを現在のブランチバージョンと一致するように設定します。
$ echo 'staged version different from all' > inboth
$ git add inboth
$ git show branch1:inboth > inboth
$ git status --short
MM inboth
2 M
秒ここ平均:からファイル異なり上演HEAD
ファイル、および、段階的なファイルからファイル異なりツリー取り組んでいます。作業ツリーバージョンはbranch1
(別名HEAD
)バージョンと一致します。
$ git diff HEAD
$
しかしgit checkout
、チェックアウトを許可しません:
$ git checkout branch2
error: Your local changes ...
branch2
バージョンを作業バージョンとして設定しましょう:
$ git show branch2:inboth > inboth
$ git status --short
MM inboth
$ git diff HEAD
diff --git a/inboth b/inboth
index ecb07f7..aee20fb 100644
--- a/inboth
+++ b/inboth
@@ -1 +1,2 @@
this file is in both branches
+but it has more stuff in branch2 now
$ git diff branch2 -- inboth
$ git checkout branch2
error: Your local changes ...
現在の作業コピーはのコピーと一致しbranch2
ますが、ステージングされたファイルは一致しないため、git checkout
はそのコピーを失い、git checkout
は拒否されます。
テクニカルノート—めちゃくちゃ好奇心旺盛な人のためだけ:-)
これらすべての基礎となる実装メカニズムは、Gitのインデックスです。「ステージング領域」とも呼ばれるインデックスは、次のコミットを構築する場所です。現在のコミット(つまり、現在チェックアウトしているもの)と一致し始めgit add
、ファイルを作成するたびに、インデックスバージョンを置き換えます作業ツリーにあるすべてのものを使用します。
覚えておいて、作業ツリーは、あなたのファイルで作業場所です。ここでは、コミットやインデックスで行うような、Git専用の特別な形式ではなく、通常の形式をとっています。したがって、コミットからファイルを抽出し、インデックスを介して、作業ツリーに移動します。変更後、あなたはgit add
それをインデックスに追加します。したがって、実際には各ファイルに3つの場所があります。現在のコミット、インデックス、作業ツリーです。
を実行するとgit checkout branch2
、Gitがカバーの下で行うのは、現在のコミットとインデックスの両方にあるもののチップコミットを比較することですbranch2
。現在のファイルと一致するファイルであれば、Gitはそのままにしておくことができます。それはすべてそのままです。両方のcommitで同じファイルであれば、Gitをそのままにしておくこともできます。これらは、ブランチを切り替えるためのファイルです。
このインデックスがあるため、コミット切り替えを含むGitの多くは比較的高速です。実際にインデックスにあるのは、各ファイル自体ではなく、各ファイルのハッシュです。ファイル自体のコピーは、Gitがblobオブジェクトと呼ぶものとしてリポジトリに保存されます。これは、ファイルがコミットに保存される方法にも似ています。コミットには実際にはファイルが含まれず、Gitが各ファイルのハッシュIDにつながるだけです。そのため、GitはハッシュID(現在は160ビット長の文字列)を比較して、コミットXとYに同じファイルがあるかどうかを判断できます。次に、それらのハッシュIDをインデックス内のハッシュIDと比較することもできます。
これは、上記のすべての奇妙なコーナーケースにつながるものです。両方ともファイルを持つコミットXとYがpath/to/name.txt
あり、のインデックスエントリがありpath/to/name.txt
ます。たぶん、3つのハッシュすべてが一致します。多分それらの2つは一致し、1つは一致しません。たぶん、3つすべてが異なるでしょう。そして、我々はまた、持っているかもしれないanother/file.txt
ことが唯一だXのみでYとであるか、今のインデックスではありません。XからYに切り替えるには、Git がファイルをコミットからインデックスにコピーするか、インデックスから削除する必要があるか?もしそうなら、それもする必要がありますファイルを作業ツリーにコピーするか、作業ツリーから削除します。場合とそれが「ケースをね、インデックスと作業ツリーのバージョンでは、より良い試合コミットさバージョンの少なくとも一つを持っていました。それ以外の場合、Gitは一部のデータを破壊します。
(これらすべての完全なルールは、git checkout
予想されるgit read-tree
ドキュメントではなく、「2つのツリーのマージ」というタイトルのセクションのドキュメントに記載されています。)
git checkout -m
、ワークツリーとインデックスの変更を新しいチェックアウトにマージします。