DETACHED HEADとMASTER/ORIGINをどのように調和させればよいのでしょうか?
私はGitのブランチの複雑さに慣れていません。私はいつも1つのブランチで作業し、変更をコミットしてから定期的にリモートのオリジンにプッシュします。
最近、いくつかのファイルをリセットしてコミットステージングから外し、後で rebase -i
を実行して最近のローカルコミットをいくつか削除しました。今、私はよくわからない状態になっています。
私の作業領域では、git log
は私が期待した通りのものを示しています。私は正しい方向に進んでいて、私が望んでいないコミットはなくなり、新しいコミットはそこにある、といった具合です。
しかし、リモートリポジトリにプッシュしたところ、そこにあるものは違っていました。リベースで消したいくつかのコミットがプッシュされ、ローカルにコミットされた新しいコミットはそこにありません。
私は、"master/origin"がHEADから切り離されていると考えていますが、それが何を意味するのか、コマンドラインツールでそれをどのように可視化するのか、そしてそれをどのように修正するのか、100%明確ではありません。
1497
3
まず、HEADとは何かと[デタッチの意味]を明確にしましょう。
HEADは、現在チェックアウトされているコミットのシンボリックな名前です。HEADがデタッチされていない場合("通常"1の状態:ブランチがチェックアウトされている状態)、HEADは実際にブランチの "ref "を指し、ブランチはコミットを指します。このように、HEADはブランチに「接続」されています。新しいコミットをすると、HEADが指しているブランチは、新しいコミットを指すように更新されます。HEADはそのブランチを指しているだけなので、自動的にそれに従います。
git symbolic-ref HEAD
はrefs/heads/master
になります。 master "という名前のブランチがチェックアウトされます。git rev-parse refs/heads/master
yield17a02998078923f2d62811326d130de991d1a95a
このコミットがmasterブランチの現在のチップ、つまり「head」になります。git rev-parse HEAD
でも17a02998078923f2d62811326d130de991d1a95a
が得られます。 これが「シンボリックなref」ということです。他の参照を通してオブジェクトを指しています。(シンボリックレフはもともとシンボリックリンクとして実装されていましたが、後にシンボリックリンクを持たないプラットフォームでも使用できるように、特別な解釈を加えたプレーンファイルに変更されました)。
HEAD
→
refs/heads/master→
17a02998078923f2d62811326d130de991d1a95a` となります。HEADがデタッチされると、ブランチを経由して間接的にコミットを指すのではなく、直接コミットを指すようになります。デタッチされたHEADは、名前のないブランチにあると考えることができます。
git symbolic-ref HEAD
はfatal: ref HEAD is not a symbolic ref
で失敗します。git rev-parse HEAD
は17a02998078923f2d62811326d130de991d1a95a
となります。 シンボリックな参照ではないので、コミット自体を直接指している必要があります。HEAD
→
17a02998078923f2d62811326d130de991d1a95a` となります。切り離された HEAD で覚えておくべき重要なことは、その HEAD が指すコミットが参照されていない (他の参照が到達できない) 場合、他のコミットをチェックアウトしたときにその HEAD が「ぶら下がった」状態になるということです。最終的には、そのようなダングリングコミットはガベージコレクションのプロセスで刈り取られます (デフォルトでは、少なくとも2週間は保存されますが、HEADのreflogで参照されることにより、より長く保存されることもあります)。
1 切り離されたHEADで「通常の」作業をすることは全く問題ありません。ただ、自分が何をしているのかを追跡して、ドロップした履歴をreflogから漁る必要がないようにしなければなりません。
インタラクティブなリベースの中間ステップは、(部分的にはアクティブブランチのreflogを汚さないようにするために)HEADをデタッチして行われます。完全なリベース操作を終えると、元のブランチにリベース操作の累積結果が更新され、元のブランチにHEADが再接続されます。私の推測では、リベース処理が完全には完了しなかったのではないかと思います。この場合、切り離された HEAD は、リベース処理が行われた直近のコミットを指すことになります。
この状態から回復するには、切り離された HEAD が指すコミットを指すブランチを作成する必要があります。
(この2つのコマンドは、
git checkout -b temp
と略すことができます)。これにより、あなたのHEADは新しい
temp
ブランチに再接続されます。次に、現在のコミット(とその履歴)を、あなたが作業する予定だった通常のブランチと比較する必要があります。
(ログオプションを試してみたくなるかもしれません。
-p
を追加したり、--pretty=...
を外してログメッセージ全体を見たり、といった具合です)新しい
temp
ブランチがよさそうなら、(たとえば)master
を更新してそのブランチを指すようにしたいと思うかもしれません。(この2つのコマンドは
git checkout -B master temp
と略すことができます)。その後、一時的なブランチを削除することができます。
最後に、再構築した履歴をプッシュすることになるでしょう。
リモートブランチを新しいコミットに「早送り」できない場合 (つまり、既存のコミットを削除したり書き換えたり、あるいは履歴を書き換えたりした場合) は、このコマンドの最後に
--force
を追加してプッシュする必要があるかもしれません。もしリベース作業の途中であったなら、おそらくそれを片付けるべきでしょう。リベースが進行中だったかどうかは、
.git/rebase-merge/
というディレクトリを探せばわかります。進行中のリベースを手動でクリーンアップするには、そのディレクトリを削除すればいいのです (例えば、進行中のリベース操作の目的や文脈をもはや覚えていない場合などです)。通常はgit rebase --abort
を使いますが、これは避けたいリセットを行うことになります (HEAD を元のブランチに戻し、元のコミットにリセットするので、上で行った作業の一部が取り消されてしまいます)。ヘッドの基本的な説明はこちらをご覧ください。
http://git-scm.com/docs/git-checkout
コマンドラインで可視化しています。
または
と入力すると、以下のような出力が得られます。
git checkout somecommit`などを行うことでこの状態になり、以下のように警告されていたでしょう。
**さて、これらのブランチを master に追加します。
git reflog
あるいは単に
git logを実行して、コミット内容を記録します。次に、
git checkout masterと
git merge` でコミットをマージします。編集します。
付け加えると、不要なコミットの削除/抹殺だけでなく、編集にも
git rebase -i
を使います。コミットリストで "edit "と入力すれば、コミットを修正してからgit rebase --continue
を発行して進めることができます。これで、切り離されたHEADに入ることはありませんでした。デタッチしたコミットを別のブランチに移す
単に
git checkout -b mynewbranch
を実行するだけです。次に
git log
を実行すると、コミットが新しいブランチのHEAD
になっていることがわかります。