【備忘録】Git操作
git checkoutで特定のファイルの内容を取得して上書きする方法
実務で教えてもらって便利そうだったので備忘録として残しておく。
使う場面としては、「自分の作業ブランチ内で作成したファイル」が他の人の作業でも使いたいファイルだったときに、そのファイルの分だけ切り出して先にPRを作るみたいな場合。つまり、別ブランチから一部のファイルのみを取得したいようなケース。共通で使うモデルクラスとかでよく起きそうなイメージ。
やり方
# 特定のコミットID内の特定のファイルを今いるブランチのファイルに上書きしてステージング(git add)された状態にする(コミットはされていない状態)
git checkout {コミットID} {ファイル名}
git checkout {コミットID} -- {ファイル名} # 参考記事では -- のハイフンが区切り文字として付いていた
git checkout {コミットID} {ディレクトリ名} # ディレクトリごと取り込んで上書きすることもできる
# コミットIDの箇所はブランチ名を指定することもできる。別ブランチ内の特定のファイルを今いるブランチのファイルに上書きしてステージング(git add)された状態にする
git checkout {ブランチ名} {ファイル名}
git checkout {ブランチ名} -- {ファイル名}
git checkout {ブランチ名} {ディレクトリ名}
実際の例
前提:Aさんが開発しているfeature/a
ブランチで作成したHogeModel.php
を、Bさんが開発しているfeature/b
ブランチの方でも使う必要が出てきた。
対応策としては、今回のgit checkout
で取り込む方法以外にも、Bさん側が🍒git cherry-pick
で取り込む方法が考えられるが、cherry-pickは取り込む対象のコミットにHogeModel.php
以外の変更も含まれている時には少し使いづらい。
こういった場合においては、特定の対象ファイルだけを取り込むことのできるgit checkout
の方が有用でありそう。
# 共通で使うブランチへ取り込みたいため、まずはdevelopブランチに移動しておく(ブランチ移動のcheckout)
git checkout develop
# feature/aから取り込みたい対象ファイルを指定して、今いるブランチに取り込む(ファイル取り込みのcheckout)
git checkout feature/a HogeModel.php
# 取り込んだファイルはステージング状態(git add)なのでコミットする
git commit -m "HogeModelの作成"
# プッシュする(developブランチ内にHogeModel作成の変更が入る)
git push origin HEAD
# feature/bの作業をしているBさんが最新のdevelopを取り込んだら作業終了
余談
最近ではこのgit checkout
の役割と同等なものとしてgit restore
というコマンドが新設されているので、こちらを使うのも可。
ローカルブランチをリモートブランチの状態に強制的に合わせる方法
やり方
# リモート追跡ブランチの更新
git fetch --prune
# ローカルの内容をリモートブランチの内容に強制的に合わせる
git reset --hard origin/hoge
補足
git reset --hard
は、現在のブランチの状態を強制的に対象に合わせるコマンド。
git reset --hard {コミットへの参照}
今回やっていることは、現在いるブランチの内容を、強制的にリモート追跡ブランチの内容に書き換えたということ。リモート追跡ブランチ(=リモートブランチ(ほぼ))なので、リモートブランチの内容に強制的に合わせるということが実現できる。
CLI上でローカルブランチとリモートブランチ間の差分を表示する
以下の記事参照。
よく使いそうなのは、リモートのdevelopブランチとローカルの作業ブランチの差分を表示させる以下の書き方(GitHubのPRの差分で出る内容と同じ)。
git fetch --prune # リモートの最新状態のフェッチ
git diff origin/develop {ローカル作業ブランチ} --stat # 差分の表示
実行結果。
git stashコマンドで任意のファイルを指定して退避させる方法
# 「--」はファイル指定との区切りを明確にするためのものだが、なくても可
# 「-u」オプションは、一度もコミットに含めたことのないファイルまで含めて退避する
git stash push -u -m '退避メッセージ' -- filepath/path/... filepath2/path/...
ちなみに、ファイルを指定せずに全て退避する場合は普通に以下のようにする
git stash push -u -m '退避メッセージ'
同じブランチに対して、別の人がコミットしてプッシュしていて、自分が後からプッシュしようとしたときに、エラーになってしまうのをいい感じに処理する方法
前提
-
feature/example
というブランチがある(A
というコミットが最新) - 自分が
A
コミットに続けて、B
というコミットを作る - 他人が
A
コミットに続けて、C
というコミットを作る。そしてそのままプッシュする- この時点では、
origin/feature/example
は「A
←C
」でコミットが続いている
- この時点では、
- 自分が「
A
←B
」となっている状態のローカルブランチをリモートにプッシュする - 以下のエラーが発生する
❯ git push origin HEAD
To https://github.com/example.git
! [rejected] HEAD -> feature/example (non-fast-forward)
error: failed to push some refs to 'https://github.com/example.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
いい感じに処理する方法
上記状態をいい感じに解消する方法。
よりよい方法があれば、求む。
# 最新のリモート状態を取得する
git fetch --prune
# 現在のブランチ名を適当なブランチ名に変更する(ここでは仮に「feature/example-OLD」に改名)
git branch -M feature/example-OLD
# 現在の最新のリモートブランチを元にローカルブランチを作成し直す
git checkout -b feature/example origin/feature/example
# 元のブランチでプッシュが叶わなかったコミットたちを cherry-pick で取得する(ここでコンフリクトした場合はわからない)
git cherry-pick {コミットID}
# リモートにプッシュする
git push origin HEAD
これで多分いい感じ🐨
一時的に過去のコミットの状態に戻る方法
あくまで一時的に戻る方法。
# 指定したコミットIDの場所に戻る(切り替える)
git checkout {コミットID}
# 元の最新状態に戻る場合
git checkout {ブランチ名}
checkout
の代替コマンドであるswitch
コマンドで同じことをしたい場合は以下の記事参照。
# 指定したコミットIDの場所に戻る(切り替える)
# ※ -d オプションは移動後の HEAD を「detached HEAD」という使い捨ての HEAD にするという意味
git switch -d {コミットID}
# 元の最新状態に戻る場合
git switch -