DETACHED HEADとMASTER/ORIGINをどのように調和させればよいのでしょうか?

私はGitのブランチの複雑さに慣れていません。私はいつも1つのブランチで作業し、変更をコミットしてから定期的にリモートのオリジンにプッシュします。

最近、いくつかのファイルをリセットしてコミットステージングから外し、後で rebase -i を実行して最近のローカルコミットをいくつか削除しました。今、私はよくわからない状態になっています。

私の作業領域では、git logは私が期待した通りのものを示しています。私は正しい方向に進んでいて、私が望んでいないコミットはなくなり、新しいコミットはそこにある、といった具合です。

しかし、リモートリポジトリにプッシュしたところ、そこにあるものは違っていました。リベースで消したいくつかのコミットがプッシュされ、ローカルにコミットされた新しいコミットはそこにありません。

私は、"master/origin"がHEADから切り離されていると考えていますが、それが何を意味するのか、コマンドラインツールでそれをどのように可視化するのか、そしてそれをどのように修正するのか、100%明確ではありません。

ソリューション

まず、HEADとは何かと[デタッチの意味]を明確にしましょう。

HEADは、現在チェックアウトされているコミットのシンボリックな名前です。HEADがデタッチされていない場合("通常"1の状態:ブランチがチェックアウトされている状態)、HEADは実際にブランチの "ref "を指し、ブランチはコミットを指します。このように、HEADはブランチに「接続」されています。新しいコミットをすると、HEADが指しているブランチは、新しいコミットを指すように更新されます。HEADはそのブランチを指しているだけなので、自動的にそれに従います。

  • git symbolic-ref HEADrefs/heads/master になります。 master "という名前のブランチがチェックアウトされます。
  • git rev-parse refs/heads/master yield 17a02998078923f2d62811326d130de991d1a95a このコミットがmasterブランチの現在のチップ、つまり「head」になります。
  • git rev-parse HEAD でも 17a02998078923f2d62811326d130de991d1a95a が得られます。 これが「シンボリックなref」ということです。他の参照を通してオブジェクトを指しています。
    (シンボリックレフはもともとシンボリックリンクとして実装されていましたが、後にシンボリックリンクを持たないプラットフォームでも使用できるように、特別な解釈を加えたプレーンファイルに変更されました)。

HEADrefs/heads/master17a02998078923f2d62811326d130de991d1a95a` となります。

HEADがデタッチされると、ブランチを経由して間接的にコミットを指すのではなく、直接コミットを指すようになります。デタッチされたHEADは、名前のないブランチにあると考えることができます。

  • git symbolic-ref HEADfatal: ref HEAD is not a symbolic ref で失敗します。
  • git rev-parse HEAD17a02998078923f2d62811326d130de991d1a95a となります。 シンボリックな参照ではないので、コミット自体を直接指している必要があります。

HEAD17a02998078923f2d62811326d130de991d1a95a` となります。

切り離された HEAD で覚えておくべき重要なことは、その HEAD が指すコミットが参照されていない (他の参照が到達できない) 場合、他のコミットをチェックアウトしたときにその HEAD が「ぶら下がった」状態になるということです。最終的には、そのようなダングリングコミットはガベージコレクションのプロセスで刈り取られます (デフォルトでは、少なくとも2週間は保存されますが、HEADのreflogで参照されることにより、より長く保存されることもあります)。

1 切り離されたHEADで「通常の」作業をすることは全く問題ありません。ただ、自分が何をしているのかを追跡して、ドロップした履歴をreflogから漁る必要がないようにしなければなりません。


インタラクティブなリベースの中間ステップは、(部分的にはアクティブブランチのreflogを汚さないようにするために)HEADをデタッチして行われます。完全なリベース操作を終えると、元のブランチにリベース操作の累積結果が更新され、元のブランチにHEADが再接続されます。私の推測では、リベース処理が完全には完了しなかったのではないかと思います。この場合、切り離された HEAD は、リベース処理が行われた直近のコミットを指すことになります。

この状態から回復するには、切り離された HEAD が指すコミットを指すブランチを作成する必要があります。

git branch temp
git checkout temp

(この2つのコマンドは、git checkout -b tempと略すことができます)。

これにより、あなたのHEADは新しい temp ブランチに再接続されます。

次に、現在のコミット(とその履歴)を、あなたが作業する予定だった通常のブランチと比較する必要があります。

git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp

(ログオプションを試してみたくなるかもしれません。-p を追加したり、--pretty=... を外してログメッセージ全体を見たり、といった具合です)

新しい temp ブランチがよさそうなら、(たとえば) master を更新してそのブランチを指すようにしたいと思うかもしれません。

git branch -f master temp
git checkout master

(この2つのコマンドは git checkout -B master temp と略すことができます)。

その後、一時的なブランチを削除することができます。

git branch -d temp

最後に、再構築した履歴をプッシュすることになるでしょう。

git push origin master

リモートブランチを新しいコミットに「早送り」できない場合 (つまり、既存のコミットを削除したり書き換えたり、あるいは履歴を書き換えたりした場合) は、このコマンドの最後に --force を追加してプッシュする必要があるかもしれません。

もしリベース作業の途中であったなら、おそらくそれを片付けるべきでしょう。リベースが進行中だったかどうかは、.git/rebase-merge/ というディレクトリを探せばわかります。進行中のリベースを手動でクリーンアップするには、そのディレクトリを削除すればいいのです (例えば、進行中のリベース操作の目的や文脈をもはや覚えていない場合などです)。通常は git rebase --abort を使いますが、これは避けたいリセットを行うことになります (HEAD を元のブランチに戻し、元のコミットにリセットするので、上で行った作業の一部が取り消されてしまいます)。

解説 (22)

ヘッドの基本的な説明はこちらをご覧ください。

http://git-scm.com/docs/git-checkout

コマンドラインで可視化しています。

git branch

または

git branch -a

と入力すると、以下のような出力が得られます。

* (no branch)
master
branch1
  • (no branch)` は、あなたが切り離された頭の中にいることを示しています。

git checkout somecommit`などを行うことでこの状態になり、以下のように警告されていたでしょう。

You are in 'detached HEAD' state.あなたは 周りを見回したり、実験的な変更を行ったり 実験的な変更を行い、それをコミットすることができます。 この状態で行ったコミットは、どのブランチにも影響を与えることなく、 > 廃棄することができます。 ブランチに影響を与えることなく、この状態で行ったコミットを もう一回チェックアウトを行うことで

自分が作成したコミットを保持するために新しいブランチを作成したい場合は 作成したコミットを保持するために、新しいブランチを作成したい場合は チェックアウトコマンドで-bを使うことで、(今でも後でも)そのようにできます。 checkoutコマンドで-bを使用することで、(現在または後に)行うことができます。例

git checkout -b new_branch_name

**さて、これらのブランチを master に追加します。

git reflogあるいは単にgit logを実行して、コミット内容を記録します。次に、git checkout mastergit merge` でコミットをマージします。

git merge HEAD@{1}

編集します。

付け加えると、不要なコミットの削除/抹殺だけでなく、編集にも git rebase -i を使います。コミットリストで "edit "と入力すれば、コミットを修正してから git rebase --continue を発行して進めることができます。これで、切り離されたHEADに入ることはありませんでした。

解説 (2)

デタッチしたコミットを別のブランチに移す

単に git checkout -b mynewbranch を実行するだけです。

次に git log を実行すると、コミットが新しいブランチの HEAD になっていることがわかります。

解説 (2)