[解決済み] 現在のブランチにコミットされていない変更がある場合、別のブランチをチェックアウトする
質問
既存の別のブランチをチェックアウトしようとすると、現在のブランチに未コミットの変更がある場合は、ほとんどの場合 Git がそれを許可してくれません。だから、まずその変更をコミットするか隠しておかなければなりません。
しかし、時折 Git はコミットや変更を保存せずに別のブランチをチェックアウトすることを許可し、私がチェックアウトしたブランチに変更を反映させることがあります。
ここでのルールは何でしょうか?変更がステージングされているか、ステージングされていないかは重要ですか?変更を別のブランチに運ぶというのは、私には何の意味もありません。なぜgitは時々それを許すのでしょうか? つまり、ある状況下では役に立つのでしょうか?
解決方法は?
予備知識
この回答は、以下の説明を試みるものです。
なぜ
Gitはそのような振る舞いをします。 これは、特定のワークフローに従事することを推奨するものではありません。 (私自身の好みは、とにかくコミットすることです。
git stash
と、あまり小難しいことは考えず、他の方法を好む人もいます)。
ここでの観察は、あなたが仕事を始めた後に
branch1
(別のブランチに切り替えるのが良いことを忘れたり、気づかなかったりする)
branch2
を先に実行します。
git checkout branch2
時には、Git が "OK, you're on branch2 now!" と言うこともあれば、Git が "I can't do that, I would lose some of your changes." と言うこともあります。
もしGitが
はしません。
をさせると、変更をコミットして永久にどこかに保存する必要があります。
を使用するとよいでしょう。
git stash
を保存するために設計されたものです。
なお
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
などにあります。
branch1
. A
git checkout branch2
は、こうしなければならないだろう。
-
すべてのファイルに対して
は
で
branch1
と ではなく でbranch2
, 1 そのファイルを削除します。 -
すべてのファイルについて
は
で
branch2
と ではなく でbranch1
のように、そのファイルを(適切な内容で)作成します。 -
両方のブランチにあるすべてのファイルについて、もし
branch2
が異なる場合、作業木のバージョンを更新します。
これらの各ステップは、ワークツリー内の何かを破壊する可能性があります。
-
ファイルを削除することは、ワークツリー内のバージョンが
branch1
変更を加えた場合は "unsafe"となります。 -
に表示される方法でファイルを作成する
branch2
は、現在存在しない場合は "safe"です。 2 現在存在していても、その内容が "unsafe" であれば、それは "wrong" です。 -
そしてもちろん、あるファイルのワークツリー版を別のバージョンに置き換えることは、そのワークツリー版がすでに
branch1
.
新しいブランチの作成 (
git checkout -b newbranch
) は
常に
この処理では、ワークツリー内でファイルが追加、削除、変更されることはなく、インデックス/ステージング領域も変更されることはありません。 (注意: 新しいブランチの開始点を変更せずに新しいブランチを作成する場合は安全ですが、別の引数を追加した場合、たとえば
git checkout -b newbranch different-start-point
に移動するように変更しなければならないかもしれません。
different-start-point
. その後、Git は通常通りチェックアウトの安全ルールを適用します)。
1
そのためには、ファイルがブランチに含まれることの意味を定義する必要があります。
ブランチ
を適切に設定する必要があります。 (参照
ブランチとはどういう意味ですか?
) ここで、私が本当に言いたいことは
branch-nameの解決先となるコミット。
をパスとするファイル
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 もし、既に正しい内容で存在していれば、quot;sort-of-safe"と見なされ、Gitは結局それを作成する必要がありません。 Gitのいくつかのバージョンでは、これを許可していたように記憶しています。しかし、たった今テストしたところ、Git 1.8.5.4では、これは"unsafe"と見なされることがわかりました。 同じ議論は、変更されたファイルがたまたま to-be-switch-to ブランチにマッチするように変更された場合にも当てはまります。 ここでも、1.8.5.4では単に"would be overwritten"とだけ書かれていますね。 テクニカルノートの最後もご覧ください。私が初めてGitを使い始めたバージョン1.5.sからリードツリーのルールは変わっていないと思うので、私の記憶が間違っているのかもしれません。
変更がステージングされているか、ステージングされていないかは重要ですか?
はい、いくつかの点ではそうです。 特に、変更をステージングしてから、ワークツリー・ファイルを "de-modify"することができます。 次の図は、2つのブランチに分かれているファイルです。
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
のものと一致します。
branch2
にいるにもかかわらず
branch1
. この変更はコミット用にステージングされておらず、そのため
git status --short
がここに表示されています。
$ git status --short
M inboth
スペースからMは、quot;modified but not staged"(正確には、ワーキングツリーのコピーとステージング/インデックスのコピーが異なる)を意味します。
$ git checkout branch2
error: Your local changes ...
OK、では作業ツリーのコピーをステージングしてみましょう。
branch2
.
$ git add inboth
$ git status --short
M inboth
$ git checkout branch2
Switched to branch 'branch2'
ここでは、ステージングコピーとワーキングコピーの両方が
branch2
ということで、チェックアウトが許可されました。
もう1ステップやってみましょう。
$ 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 はそのままにしておくことができます。 すべて手つかずです。 の両方で同じであるファイルは
コミット
は、Git は
また
これらはブランチの切り替えを可能にするものです。
コミット切り替えを含むGitの多くは、比較的高速です。 なぜなら このインデックスが 実際にインデックスに入っているのは、各ファイルそのものではなく、各ファイルの ハッシュ . ファイルのコピー自体は、Git が呼ぶところの ブロブオブジェクト というように、リポジトリにあります。 これは、コミットにもファイルが格納されているのと同様です。コミットには、実際には ファイル それは、各ファイルのハッシュIDをGitに導くだけなのです。 そのため、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 checkout
のドキュメントではなく
その
git read-tree
のドキュメントで、「"Two Tree Merge"」と題されたセクションの下にあります。
.)
関連
-
[解決済み] Git のステージされていない変更から "old mode 100755 new mode 100644" というファイルを削除するにはどうすればよいですか?
-
[解決済み] Git で現在のブランチ名を取得するには?
-
[解決済み] Gitで落としたスタッシュを復元する方法とは?
-
[解決済み] Git のローカルコミットを破棄する
-
[解決済み] 現在のGitブランチをmasterブランチにする
-
[解決済み] git のコミットを削除しても、変更は維持できますか?
-
[解決済み] Git master の未段階/未コミットの変更からブランチを作成する
-
[解決済み] リモートレポにプッシュされたGitの特定のコミットを元に戻す
-
[解決済み] ローカルのGitの変更を削除するさまざまな方法
-
[解決済み] 別のブランチからのすべての変更を単一のコミットとしてマージ(squash付き)する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 現在のブランチのトラッキング情報がありません。
-
[解決済み】Git:コミットに何も追加されていないが、未追跡のファイルが存在する
-
[解決済み】なぜ「git commit」は私の変更を保存しないのですか?
-
[解決済み】ローカルに変更があるにもかかわらず、git pushが「すべて最新」と言う
-
[解決済み】GitHubで空のブランチを作成する
-
git push reports an error ! [リモート拒否] master -> master (受信前のフックが拒否されました)
-
[解決済み] 現在の支店にはトラッキング情報がありません
-
[解決済み] Git エラー : 'upstream' は git リポジトリでないようです。
-
[解決済み] "fatal: This operation must be run in work tree." というメッセージが表示されるのはなぜですか?
-
[解決済み] リモートブランチにプッシュできない、ブランチに解決できない