Git入門:バージョン管理のきほん
ブランチを作って移動する(switch)
このレッスンで分かること
git switch -cで新しいブランチを作り、その場で移動する手順- ブランチを移動したときに作業ツリーのファイルに何が起きるのか
- コミットしていない変更を抱えたまま移動しようとして止められたときの逃げ道
ブランチは移動して初めて意味を持つ
前回のレッスンで、ブランチは「コミットにつけた名札」だと確かめました。名札を増やしただけでは何も起きません。その名札の上に立って、新しいコミットを積んで初めてブランチは枝になります。
いま自分がどの名札の上に立っているかを覚えているのが HEAD です。HEAD は「現在地」を指す特別な印で、ふつうはブランチ名を経由してコミットを指しています。ブランチを移動するとは、この HEAD の指し先を別のブランチに付け替えることです。
ターミナル
$ git branch
* main* が付いている行が現在地です。いまは main の上に立っています。
もう少し正確に言うと、HEAD が指しているのはコミットそのものではなく main というブランチ名です。そして main が最新のコミットを指しています。この二段構えのおかげで、main の上でコミットすると main の指し先が自動で新しいコミットに移り、HEAD はそのまま main を指し続けます。ブランチの上に立っていれば、コミットするたびに名札が一緒に前へ進むというのが Git の基本の動きです。
中身を直接見ることもできます。
ターミナル
$ cat .git/HEAD
ref: refs/heads/main.git/HEAD にはブランチ名が 1 行書かれているだけです。ブランチの移動が一瞬で終わるのは、実体としてはこの 1 行が書き換わり、それに合わせて作業ディレクトリのファイルが並べ直されているからです。
git switch -c で作ってすぐ移動する
新しい作業を始めるときの基本形は 1 行です。
ターミナル
$ git switch -c feature-login
Switched to a new branch 'feature-login'-c は create の頭文字で、「作って、そこへ移動する」をまとめて行います。作られた feature-login は、実行した瞬間に main が指していたのと同じコミットを指します。つまりスタート地点は完全に同じで、ここから積むコミットだけが枝分かれしていきます。
作るだけで移動したくない場合は git branch を使います。
ターミナル
$ git branch hotfix
$ git branch
feature-login
hotfix
* main既にあるブランチへ移動するときは -c を外します。
ターミナル
$ git switch main
Switched to branch 'main'直前にいたブランチへ戻りたいときは - が使えます。ターミナルの cd - と同じ感覚です。
ターミナル
$ git switch -
Switched to branch 'feature-login'昔の教材や記事では
git checkout -b feature-loginと書かれています。checkoutはブランチ移動とファイルの復元という 2 つの別々の仕事を兼ねていて分かりにくかったため、移動がswitch、復元がrestoreに分けられました。checkoutは今も問題なく動くので、古い記事のコマンドを打っても失敗はしません。新しく覚えるならswitchを選んでください。
使うコマンドの一覧
| コマンド | 何をするか |
|---|---|
git switch -c 名前 | ブランチを作って、そこへ移動する |
git switch 名前 | 既にあるブランチへ移動する |
git switch - | 直前にいたブランチへ戻る |
git branch 名前 | ブランチを作るだけ。移動はしない |
git branch | ローカルのブランチ一覧。* が現在地 |
git branch -v | 一覧に、各ブランチの先頭コミットも並べて表示する |
git branch -m 新名前 | いま居るブランチの名前を変える |
git branch -d 名前 | ブランチを消す。マージ済みでないと拒否される |
git branch -v は現在地の把握に効きます。
ターミナル
$ git branch -v
* feature-login 8f3a91c ログインフォームの雛形を追加
main 4c2b70e READMEに開発手順を追記各行の右側に、そのブランチが指しているコミットの短いハッシュとメッセージが並びます。feature-login と main が同じハッシュを指していれば、まだ 1 つもコミットしていない切りたての状態だと分かります。
git status の 1 行目にも現在地は出ます。ブランチを扱っている間は、迷ったらこれを打つのがいちばん早い確認方法です。
ターミナル
$ git status
On branch feature-login
nothing to commit, working tree cleanなお、ターミナルのプロンプトに現在のブランチ名を出す設定を入れておくと、確認そのものが要らなくなります。Mac の zsh でも Windows の Git Bash でも設定できますが、まずは git status を打つ癖で十分です。
移動するとファイルの中身が入れ替わる
ここが最初につまずくところです。ブランチを移動すると、作業ディレクトリのファイルが、移動先のブランチのコミットの中身に丸ごと書き換わります。エディタで開いていたファイルの中身が突然変わって驚くことがありますが、壊れたわけではありません。
実際に見てみます。feature-login で 1 つコミットしてから main に戻ります。
ターミナル
$ git switch -c feature-login
Switched to a new branch 'feature-login'
$ git add login.html
$ git commit -m "ログインフォームの雛形を追加"
[feature-login 8f3a91c] ログインフォームの雛形を追加
1 file changed, 24 insertions(+)
create mode 100644 login.html
$ ls
README.md index.html login.html
$ git switch main
Switched to branch 'main'
$ ls
README.md index.htmllogin.html が消えました。消えたのではなく、main にはまだそのファイルが存在しないので、作業ディレクトリから外されただけです。feature-login に戻せば元通りです。
ターミナル
$ git switch feature-login
Switched to branch 'feature-login'
$ ls
README.md index.html login.htmlファイルが消えたと思って作り直さないでください。ブランチを移動しただけなら、コミット済みの内容は必ずリポジトリの中に残っています。焦る前に
git branch -vで自分がどこに立っているかを確かめてください。
作業を持ったまま移動しようとして止められる
よくある失敗はこれです。ファイルを編集したまま、コミットせずにブランチを移動しようとしました。
ターミナル
$ git switch main
error: Your local changes to the following files would be overwritten by checkout:
index.html
Please commit your changes or stash them before you switch branches.
AbortingGit は「その編集内容は移動先で上書きされて消える。だから移動をやめた」と言っています。勝手に消さずに止めてくれているので、これは親切なエラーです。逃げ道は 3 つあります。
区切りが良いならコミットする
いちばん素直な方法です。まだ途中でも、作業ブランチの上ならコミットして構いません。あとで git commit --amend でまとめ直せます。
ターミナル
$ git add index.html
$ git commit -m "作業途中 ヘッダーのリンクを調整"まだコミットしたくないなら stash に退避する
git stash は、コミットしていない変更を一時的な棚に片付けて、作業ツリーを直前のコミットの状態に戻すコマンドです。
ターミナル
$ git stash
Saved working directory and index state WIP on feature-login: 8f3a91c ログインフォームの雛形を追加
$ git switch main
Switched to branch 'main'戻したくなったら、その変更を適用したいブランチへ移動してから取り出します。
ターミナル
$ git switch feature-login
Switched to branch 'feature-login'
$ git stash pop
On branch feature-login
Changes not staged for commit:
modified: index.html
Dropped refs/stash@{0} (2b8e4d1f9a3c0e7b5d2f8a1c6e4b9d3f7a0c2e5b)詳しくは 作業を一時退避する(stash) で扱っています。
編集そのものを捨てる
いらない編集なら git restore で捨ててから移動します。この操作は取り消せないので、本当にいらないときだけにしてください。
ターミナル
$ git restore index.html
$ git switch main
Switched to branch 'main'なお、移動先にそのファイルが存在しない、あるいは中身が同じ場合は、編集を持ったままでも移動できてしまいます。編集はそのまま連れて行かれるので、意図せず別ブランチで編集を続けてしまう事故が起きます。移動の前に git status を打つ癖をつけてください。
ブランチ名の付け方と消し方
名前に決まりはありませんが、チームでは慣習に合わせます。よく見るのは種類を頭に付ける形です。
プレーンテキスト
feature/login-form
fix/header-overflow
docs/setup-guideスラッシュは階層ではなく、ただの文字です。feature/ で始まる名前をまとめて眺めやすくなるだけの効果ですが、数が増えたときに効いてきます。日本語やスペースは使えますが、環境によって扱いが揺れるので半角英数字とハイフンに寄せてください。
名前を間違えたら、そのブランチに立った状態で付け替えます。
ターミナル
$ git branch -m feature/login-form役目を終えたブランチは消します。
ターミナル
$ git branch -d feature-login
Deleted branch feature-login (was 8f3a91c).まだマージしていないブランチを -d で消そうとすると拒否されます。
ターミナル
$ git branch -d feature-login
error: the branch 'feature-login' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature-login'-D は確認なしで消します。名札が消えるだけでコミット自体は少しの間残るため、直後なら reflog から拾い直せます。ただし当てにせず、-D は内容を確認してから打ってください。
なお、いま立っているブランチは削除できません。
ターミナル
$ git branch -d feature-login
error: Cannot delete branch 'feature-login' checked out at '/Users/you/work/app'削除したいブランチとは別のブランチへ移動してから消します。
detached HEAD に迷い込んだとき
git switch にはブランチ名ではなくコミットのハッシュも渡せます。過去の状態を確かめたいときに便利ですが、--detach が要ります。
ターミナル
$ git switch --detach 4c2b70e
HEAD is now at 4c2b70e READMEに開発手順を追記このとき HEAD はブランチを経由せず、コミットを直接指しています。この状態を detached HEAD と呼びます。名札の上ではなく、コミットの上に直接立っている状態です。
ターミナル
$ git status
HEAD detached at 4c2b70e
nothing to commit, working tree cleanファイルを読んだり動かして確かめたりするぶんには問題ありません。危ないのは、ここでコミットしてしまったときです。新しいコミットはどのブランチからも指されないので、他のブランチへ移動した瞬間に行き場を失います。
ターミナル
$ git switch main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
a71f4c9 detachedのままコミットしてしまったこの警告が出たらハッシュを控えてください。救い出すには、そのハッシュを指すブランチを作ります。
ターミナル
$ git branch rescue a71f4c9控え忘れても git reflog から探せます。detached HEAD は事故ではなく単なる状態なので、慌てず git switch main で本流に戻れば元通りです。過去の状態を見たあとにそこから作業を始めたくなったら、その場でブランチを作ってしまうのが安全です。
ターミナル
$ git switch -c experiment
Switched to a new branch 'experiment'- ブランチを作るだけなら何も起きない。
git switchで移動して初めて作業の枝になる - 移動すると作業ディレクトリのファイルが移動先の中身に入れ替わる。消えたわけではない
- 「would be overwritten」で止められたら、コミットするか
git stashで退避してから移動する - 移動の前に
git statusとgit branch -vを打てば、迷子になる事故はほぼ防げる