1. ホーム
  2. git

[解決済み] detached HEADとmaster/originの連携はどうすればいいですか?

2022-03-18 07:07:39

質問

Gitの複雑なブランチングについて、私は初心者です。私はいつもひとつのブランチで作業し、変更をコミットして、定期的にリモートのオリジンにプッシュしています。

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

私のワーキングエリアでは git log はまさに私が期待するものを示しています -- 消えてほしくないコミットや新しいコミットなど、正しい列車に乗っているのです。

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

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

解決方法は?

まず、明確にしましょう。 HEADとは と、それが切り離されたときの意味。

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

  • git symbolic-ref HEAD 収穫 refs/heads/master
    masterというブランチがチェックアウトされています。
  • git rev-parse refs/heads/master 降伏 17a02998078923f2d62811326d130de991d1a95a
    そのコミットは、master ブランチの現在の先端、つまり「頭」です。
  • git rev-parse HEAD も生成されます。 17a02998078923f2d62811326d130de991d1a95a
    これが「シンボリックレフ」であることの意味です。他の参照を通じて、あるオブジェクトを指し示すものです。
    (シンボリックレフはもともとシンボリックリンクとして実装されていましたが、シンボリックリンクを持たないプラットフォームでも使用できるように、後に特別な解釈を加えたプレーンなファイルに変更されました)。

私たちは HEADrefs/heads/master17a02998078923f2d62811326d130de991d1a95a

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

  • git symbolic-ref HEAD で失敗します。 fatal: ref HEAD is not a symbolic ref
  • git rev-parse HEAD 収量 17a02998078923f2d62811326d130de991d1a95a
    シンボリックリファレンスではないので、コミットそのものを直接指す必要があります。

私たちは HEAD17a02998078923f2d62811326d130de991d1a95a

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

1 切り離されたHEADで「普通の」作業をするのは全く問題ありません。ただ、reflogから落とした履歴を漁る必要がないように、何をしているのか記録しておく必要があります。


対話型リベースの中間段階は、切り離された HEAD で行われます (アクティブブランチの reflog を汚染しないようにするためでもあります)。完全なリベース操作を終えると、リベース操作の累積結果で元のブランチを更新し、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 を元のブランチに戻して元のコミットに戻すので、上で行った作業のいくつかが台無しになってしまいます)。