Gitでの設計
ローカルリポジトリとリモートリポジトリはどちらも対等な存在として扱っている。複数の完全なリポジトリ同士が参照(ref)を互いに交換しあって分散管理するシステムとなっています。
やり取りされるものの事項としては
- オブジェクト(blob,tree,comit,tag)
- リファレンス(branch,tag)
を含むすべてである。
したがって、どちらが正本ということとはみなさない。
実際にはリモートがGitHubの場合だと、ローカル側への制御ができなかったり、ローカルがPCだとリモート側からの制御ができなかったりするので実質的には非対称なことが多い。
Gitの用語について
Remote Branch(リモートブランチ)
リモートリポジトリに存在するブランチのことを指す。
Remote-Tracking Branch(リモート追跡ブランチ)
ローカルリポジトリに存在する、リモートを取得した際に(fetch)、リモートリポジトリに存在するブランチの位置に対応するブランチのことを指す。リモートのブランチをトラッキングしているものと思ってもらったほうがわかりやすい。
したがって、取得から時間がたつと、本当にリモートと一致しているのかは不明になってくる。fetchをすることにより一致するようになる。
必ずしも対応するローカルリポジトリのブランチと一致させる必要はないが、随時追随させる方針のほうが、コンフリクトが発生しずらいので運用上望ましい。
Tracking Branch(追跡ブランチ)
ブランチと名がついているがブランチを指しているわけではなく。ローカルリポジトリのブランチがそれに対応するリモート追跡ブランチを参照している状態になっていること。あるいはその参照を指している。ローカルブランチとリモート追跡ブランチの対応関係といってもよい。参照されるブランチは必ずしもリモート追跡ブランチではなくてよい。(必要性があるは別として)
Upstream Branch(上流ブランチ)
追跡ブランチが存在している場合の参照されているブランチを指す。ローカルブランチとリモート追跡ブランチの対応関係がある場合のリモート追跡ブランチがそれにあたる。
fetchの設計思想について
相手の情報を取り込むだけに特化している。相手のリファレンスはダウンロードされるが、自身のオブジェクトやリファレンスについては何も変化をさせない。自分で明示的に同じにさせるのかどうかを決める。mergeやrebase等で行う。
pushの設計思想について
実際にリモート側の状態を変化させるため、参照とは異なるが、Fast-Forwardな状態のみを受け付けるという制約を加えることで実質的に参照に近づけている。もし何かあってもブランチを移動させるだけで済むような形にしているということになる。そのため、一つ一つのブランチに対して実行し制御しやすくするようにしている。Fast-Forward状態ではない場合はローカルでFast-Forwardな状態にしてからpushをするといったローカル側での作業が必要になる。
git-remoteについて
リモートリポジトリに関する設定を行うコマンド
リモートリポジトリを登録する。
git remote add <shortname> <URL>
リモートリポジトリを表示する。
git remote -v
git-fetchについて
コミット履歴、タグ、オブジェクトをすべてダウンロードし、リモート追跡ブランチを更新する。
ただ単純にリモートの状況を取得するということに特化すると考えてよい。
ローカル側のブランチには一切変更がなされないので、git-mergeやgit-rebaseを行うことで、追跡ブランチにあるものに対して追従を行う必要がある。
使い方
git fetch <shorname> <refspec>
リモートリポジトリのshortnameがoriginに対して、ローカルリポジトリのブランチmainに対応するリモートリポジトリのブランチmainをfetchする場合。
# git fetch <shortname> <src>:<dist>
git fetch origin main:main
ローカルリポジトリのブランチ名とリモートリポジトリのブランチ名は基本一致していることが多いので<dist>は省略できる。
git fetch origin main
<refspec>を省略した場合は、.git/configにあるremote項目に従い取得を行う。
git fetch origin
[remote "origin"]
url = https://github.com/AAAA/BBBB.git
fetch = +refs/heads/*:refs/remotes/origin/*
この場合だと、リモートリポジトリのブランチすべてを対象となる。git-remote-addやgit-cloneでデフォルトで追加される設定である。
さらに<shortname>を省略した場合は、remotes.defaultを設定していればそれで、なければoriginを指定することになる。remotes.defaultを設定する方針はまれなので(かつ内部の仕組みを想起しながら実行するのは面倒なので)、複数リモートリポジトリがあれば具体的に<shortname>を指定して明示的にfetchし、1つのリモートリポジトリなら、省略してよいという方針のほうがよいかと思われる。
git fetch
git-mergeについて
二つのブランチにおいて、現在のブランチにもう一つのブランチを結合させる。
リモートリポジトリとの関連においては、現在のローカルリポジトリのブランチを上流ブランチに結合させることを指す。
mainブランチにいる状態でmainブランチの上流ブランチorigin/mainをmergeする
git merge origin/main
mainブランチとorigin/mainブランチの位置関係に応じてFast-Forwardmergeか3-waymergeのどちらかになる。
git-rebaseについて
他のブランチに枝分かれした自身のブランチのコミットを加えて、新たに自身のブランチとして生成する。
git rebase <他のブランチ>
カレントブランチmainに対して上流ブランチorigin/mainをリベースしたい場合このように実行する
git rebase origin/main
git-cloneについて
ローカルにフォルダを作成して、すべてのコミット履歴、タグ、オブジェクトをすべてダウンロードし、リモート追跡ブランチを追加する。加えてリモートリポジトリの現在ブランチに対してローカルブランチを作成して、それをチェックアウトする。(GitHubの場合はデフォルトブランチを設定しているのでそれが対応する。リモート側のシステムにより異なる。)
shortnameをoriginに設定し、ローカルディレクトリ~/testに格納する場合
git clone -o origin <URL> ~/test
※デフォルトではshortnameはoriginでディレクトリの名前もリポジトリの名前で格納されるため省略は可能
git clone <URL>
git-switchについて
基本的にはすでに作成されたブランチに対してブランチを移動するコマンドではあるが、リモート追跡ブランチがある場合は、それに対応するローカルブランチ名を指定してあげるとまだ存在しない場合でも推測し、作成と追跡ブランチの設定を自動で行い、そのローカルブランチに移動する。
例えばorigin/testというリモート追跡ブランチがあり、かつローカルブランチtestが存在しない場合は、以下のコマンドでローカルブランチtestを生成し、追跡ブランチを設定、その後、ローカルブランチtestに移動する。
git switch test
git-branchについて
ブランチの作成、削除、listを表示する。
リモート追跡ブランチを含めてすべてを表示する
git branch --all
上流ブランチの存在を含めてローカルブランチを表示する
git branch -vv
カレントブランチに対して上流ブランチを設定する
例えばカレントブランチmain対してリモート追跡ブランチorigin/mainを上流ブランチ設定する
git branch --set-upstream-to=origin/main
git-pullについて
git-fetch後、カレントブランチに対してFast-Forwardならそれでmergeする。(これができなければエラーになる)そうでない場合はmergeかrebaseを選んで実行する。
Fast-Forwardな状態の場合でpullをする
git pull
rebaseを選択する
git pull --rebase
mergeを選択する
git pull --no-rebase
※オプションの表記から基本rebaseで行ってくれといったニュアンスが感じられる。
git-pushについて
git push <shorname> <refspec>
git-fetchと異なり、デフォルトは特定のブランチに対して実施される。
Fast-Forwardであるという前提の下で、特定のブランチに対してローカルリポジトリとリモートリポジトリを同期させる。Fast-Forwardでない場合は、rebaseやmergeを駆使してFast-Forwardにする必要がある。
リモートリポジトリoriginに対して、ローカルブランチmainからリモートのブランチotherに同期させる。
# git push <shortname> <src>:<dist>
git push origin main:other
リモートリポジトリoriginに対して、ローカルブランチmainと対応するリモートのブランチ名は基本同じなので以下のように省略できる。この場合、対応するリモートのブランチが存在しない場合でも(つまりリモート追跡ブランチが存在しなくても)自動的に同名のリモート追跡ブランチを付与してくれる。ただし、追跡ブランチは付与されない。
git push origin main
# 追跡ブランチを付与したい場合は以下を加える。
git push --set-upstream origin main
上流ブランチが設定されている場合において、リモートリポジトリoriginに対して、カレントブランチに対応したリモートブランチを同期させる。
git push origin
上流ブランチが設定されている場合において、カレントブランチに対応したリモートブランチを同期させる。(実質的に上記と同じ)
git push