以下は
『筆者が現在参画している、GitHubを用いて開発を行っているプロジェクトへの新規参画者向け用語集』
です。
想定読者は「初めてGitに触れる」「当然GitHubを用いて開発を行うのも初めて」という人ですので、それに該当する学生さんなんかにも役に立つかと思い、記事として公開することにしました。
「gitは手順書に従って使ったことはあるけど、イマイチよくわかってない」という人にとっても参考になるかも知れません。
くどいくらいに文中リンクを用いていますが、その用語の意味や概念を理解していることを前提として説明していますので、「えーと、これってどういう意味だっけ…?」と思った瞬間にリンク先にジャンプして復習しながら読み進める作業を繰り返してください。
いずれも、筆者が参画しているプロジェクトでは必須となる知識です。
gitやGitHubといったツールの習得には時間が掛かるのは事実ですが、このような便利ツールの使い方がわからず手が止まるのは「電車に乗って会社に行って仕事しなきゃいけないけど、切符の買い方がわからなくて電車に乗れない」みたいな状態だと思います。
切符の買い方なんてのは本来やらなきゃいけない仕事以前の話であり、そんなものは自分で調べてどうにかすべきですので、この用語集やネット上の記事を読んで自分でリポジトリ作って実験して…どうにかして下さい(笑)
Git
バージョン管理システム(版管理システム)の一種。
WebサービスであるGitHubとは別物。
Gitに関する基本用語を以下に列挙する。
git
Gitを制御するコマンド。Windows機の場合はgit.exe、Un*xであればgitという実行ファイルとほぼ同義。
当記事内では、これを指す場合は小文字でgitと表記し、Gitと区別して扱う。
Gitクライアント
各種gitコマンドのwrapper。GUIのものが多い。
代表的なものは
辺りだが、近年はVSCodeやVisualStudioをGitクライアントとして利用するケースも多い模様。(筆者が現在参画しているプロジェクトも、基本的にVSCodeをGitクライアントとして利用する方針)
経験上、GUIクライアントのみに頼るとGitの理解が乏しくなり、トラブルシュートに対応しにくくなる印象がある。(もちろん、人や環境によるが。)
かと言って、gitのみならずCUIすらも初めてという『IT初心者』や、GUI開発環境のみで生きてきた『ベテランWindowsエンジニア』にCUIでのGit操作を強要するのも酷なので、
- 基本はGUIクライアント経由で操作しつつ、各操作の裏で発行されているgitコマンドを積極的に調べるよう努力する
という取り組み方が良いかと思う。
VSCodeを用いるのであれば、
辺りの拡張機能をインストールしておくとほぼGUIで完結できるはず。
ちなみに筆者自身はEmacs上で動作するMagitを愛用していたが、現在はVSCode上でedamagit等の拡張機能を組み合わせて利用しつつ、足らずをコマンド直打ちで補っている。
(同僚の@masaminは現在もMagitのためだけにSpacemacsを使っている模様…)
作業ツリー
作業対象となるディレクトリとファイル群。入れ子のツリー構造となっている。
以下も同義。
- ワーキングツリー(working tree)
- 作業ディレクトリ、ワーキングディレクトリ(working directory)
インデックス
変更したファイル群のスナップショットをcommit前に一時的に保存しておく領域。
commit対象となるのは、この領域に登録された内容。
ステージ(stage)と呼ぶことも多く、addでインデックスに追加することをステージングと表現する。
「スナップショット(ある瞬間の切り抜き)」や「ステージ(撮影台)」は写真用語なので、変更履歴を保存する作業を写真撮影に喩えている模様。
リポジトリ
ファイルの格納庫(repository)。Gitに於いては作業ツリーの最上位(ルート)ディレクトリを指す。
ここに「『コミット』という単位で作業ツリーのスナップショットが変更履歴として格納されている」という理解で問題無い。
存在する場所によって、以下の2つに大別される。
リモートリポジトリ
ネットワーク上に存在し、複数の開発者で共有するリポジトリ。
当記事内では、インターネット上にありGitHubが管理するリポジトリを指す。
各種クラウドストレージと同様のイメージで捉えておけば良い。
ローカルリポジトリ
各開発者のコンピュータ内に存在するリポジトリ。
Gitは、作業ツリーのルートに「.git」というディレクトリを作成し、その配下に各種管理ファイルを配置することでそのディレクトリをリポジトリとして扱うため、「ルートに.gitディレクトリがあるローカルPC内のディレクトリ」と脳内変換しても差し支え無い。
当然ながら、ここでの作業内容は他の開発者には見えない。
他の開発者(自身の別PC含む)と共有するためには、pushして変更内容をリモートリポジトリに反映する必要がある。
Gitオブジェクト
Gitは、管理対象のファイルやコミットを4種類の「オブジェクト」として.git/objects
ディレクトリ内に保存している。
いずれもzlibライブラリで可逆圧縮されたものであり、コンテンツ(ファイル)の内容にヘッダーを加えたデータから生成した40文字のsha-1ハッシュ値で管理されている。
オブジェクトは、ハッシュ値の先頭2文字のディレクトリ内に残りの38文字のファイル名で保存される。
オブジェクトの内容はcat-fileコマンドで表示することができる。
これらGitオブジェクトのうち、tagオブジェクト以外の3種類について説明する。
5334912124033467blobオブジェクト
リポジトリ内の各ファイルの中身そのもの。ファイル名は含まれない。
変更したファイルがインデックスに登録される度に新規に生成される。
treeオブジェクト
リポジトリ内の各ディレクトリの情報を保持している。
オブジェクトの中身は以下の通り。
- ディレクトリ直下に存在するファイル及びディレクトリの名前
- それらに対応するblobオブジェクト及びtreeオブジェクトのハッシュ値
生成されるタイミングについてはtreeオブジェクトが保持している内容を考えれば自明なので蛇足ではあるが、念のため記載しておく。
以下の内容が変化する度に新規に生成される。
- ディレクトリ直下のファイルの内容が変更されたことにより、そのファイルに対応するblobオブジェクトが更新された
- ディレクトリ直下に存在するファイルやディレクトリに対し、追加や削除(移動・リネーム含む)が行われた
- ディレクトリ直下に存在するディレクトリに対応するtreeオブジェクトが更新された
commitオブジェクト
所謂コミット。これを作成するcommitコマンドとは別物だがどちらも「コミット」と呼ばれるので、どちらを指しているのかを文脈に応じ判断する必要がある。
オブジェクトの中身は以下の通り。
- 作成時点のリポジトリルートディレクトリのtreeオブジェクト
- commitオブジェクトの作成日時
- commitオブジェクトの作成者情報
- コミットメッセージ
- 親コミット(1つ前のコミット)のハッシュ
- リポジトリ内の初回コミット(initial commit)には親コミットは存在しない
- マージコミットは親が2つある
コミット
変更履歴のこと。実体はcommitオブジェクト。commitやrebase、cherry-pick等のコマンドによって作成される。
以下の情報を持っていることは頭に叩き込んでおくこと。
- コミットが作成された時点でのリポジトリのスナップショット
- 40文字のsha-1ハッシュ(コミットのID)
- 一つ前のコミット(親コミット)のハッシュ
- コミットの作成日時
- コミットの作成者情報
- コミットメッセージ
HEAD
カレントブランチの最新のコミットに対するポインタ。
ブランチ名同様、「特定のコミットに貼る付箋」のようなイメージ。
resetでこれを別のコミットに貼り替えることで、結果としてブランチを巻き戻したり逆に進めたりすることができる。
実体は.git/HEAD
というファイルで、内容は
ref: refs/heads/main
のように「ブランチの最新コミットを保持しているファイル名」となっており、Gitはこれを基にカレントブランチを判断する。(detached headという状態も存在するが、ここでは言及しない)
switch等の操作によりカレントブランチを切り替えた際は、HEADの内容がそれに合わせて書き換わる。
詳細は下図参照。
要は「『commitオブジェクトへの参照であるブランチ』への参照」である。
C/C++風に言うとダブルポインタ。
コミットメッセージ
コミットの内容を説明するメッセージ。commit時に作成する。
書き方について、Gitの制約や規定は特に無いが、以下のような形式にするのが一般的である。
コミット内容の概要を一行で書く
[空白行]
コミット内容の詳細を書く
必要であれば複数行に亘って書いても良い
上記のような書き方をしておくと、GitHub上や各種Gitクライアントツールでも下図のようにうまく表示される。
ブランチ
変更履歴を枝分かれさせて管理するために用いられる機能。
実際のところ、「ブランチ」は「コミットに対するポインタ」でしかない。
具体的には.git/refs/heads/
ディレクトリに保存されている各ブランチ名のファイル群。各ファイルの内容は、各ブランチの最新のcommitオブジェクトのハッシュ値。
「ブランチ名は、特定のコミットに貼られた付箋のようなもの」ということを理解することが極めて重要。
その付箋は、基本的にそのブランチの先頭(最新)のコミットに貼られている。
「resetで別のブランチのコミットに移動したりブランチを巻き戻したりした場合も、実態としてはブランチ名が書かれた付箋を元のコミットからreset先のコミットに貼り替えているだけである」というイメージを持てるようになれば、各種gitコマンドへの恐怖感がグッと減る。
存在場所による分類
ブランチの存在場所という観点から、以下の3つに大別される。
リモートブランチ
リモートリポジトリ内に存在するブランチ(群)。
GitHubの画面からブランチに対し何かしらの操作を行った場合、変更されるのはあくまでもリモートブランチであるため、fetch等のコマンドを用いその変更内容をローカルリポジトリに取り込む必要がある。
リモート追跡ブランチ
ローカルリポジトリ内に存在し、リモートブランチの写像となるブランチ。
fetch等のコマンドでリモートブランチでの変更内容を反映する以外は、基本的にこれらを直接変更することは無い。
ローカルブランチ
ローカルリポジトリ内に存在し、各開発者が直接変更を行うブランチ。
ここに対して変更をcommitすることでコミットを作成し、そのコミットをpushすることでリモート追跡ブランチ及びリモートブランチに変更を反映する。
なお、GitHubにとってのローカルブランチは開発者にとってのリモートブランチである。
利用目的による分類
筆者が参画しているプロジェクトでの利用目的によって分類すると、以下の2つに大別される。
デフォルトブランチ
当記事が想定する開発スタイルでは、レビューを通過した正式版が格納されているブランチのことを指す。
昔は「master」という名称にするのが一般的だったが、master⇔slaveという表現がよろしくないとのことで2020年頃からは「main」という名称にする風潮となった。
参考:The default branch for newly-created repositories is now main
featureブランチ
各機能の開発やバグの修正に用いられる、作業用の使い捨てブランチ。「機能ブランチ」や「トピックブランチ」と呼ばれることもある。
当記事が(略)では、各開発者は変更内容をmainブランチにcommitするのではなく、基本的に下記の流れで作業を行う。
- Issueを起票
- そのIssueに関する作業を行うfeatureブランチを作成
- 作成したfeatureブランチに変更をcommitし、push
- そのfeatureブランチをmainブランチにmergeするためのPull Rquestを作成し、レビューを受ける
- レビュー通過後、Pull Requestの機能を用いreviewerがmainにmergeしリモートリポジトリ内のfeatureブランチを削除
- 各開発者は
git fetch --prune
にてリモート追跡ブランチを削除し、ローカルリポジトリ内のfeatureブランチも削除
カレントブランチ
現在、作業対象として選択されているローカルブランチのこと。
複数のPRの作業を並行する場合は、これを切り替えて対象となるブランチを都度変更しながら作業を進める。
コミットグラフ
リポジトリ内のコミット群をツリー状に並べて表示したもの。樹形図とも呼ぶ。
随時これを更新し確認しながら作業を進めるべきだが、以下の理由により初学者は無視しがちである。
-
複雑な樹形図
Git初心者であるほどGitクライアントに促されるままpullやmergeを多用するため、ブランチ間でのmergeが大量に発生し、それに伴うマージコミットも作成される。
その結果、線が入り乱れた複雑なグラフになる。
これを追いかけるのは熟練者でも疲弊する作業なので、初心者が「わけわからん」と感じグラフを見なくなるのは至極当然である。 -
ブランチを区別しづらい
大抵のGitクライアントでは、リポジトリの変更履歴を表示した際に- リモート追跡ブランチとローカルブランチのどちらにも存在するコミットは一つのものとして表示される
- 片方にしか存在しないものは共通部分から枝分かれして表示される
という挙動となるが、初心者がこれを見たときに
- リモート追跡ブランチとローカルブランチは別物であること
- リモートブランチとリモート追跡ブランチは別物であること(リモートブランチの内容が必ずしもリモート追跡ブランチに反映されているとは限らない)
を整理して読み取ることは困難である。
1.についてはrebase等を駆使しコミットが一直線に並ぶよう心掛けることで、2.についてはブランチの分類についてしっかり理解することで、それぞれ軽減できる。
いずれも、その辺りの理解が乏しい初心者がブランチを利用し複数名で開発した際に生じる問題なので、Gitクライアントの項で述べた通り、クライアントツールの操作方法の『丸暗記』と仕組みの『理解』を並行して行う必要がある。
時間と相談にはなるが、GitHub上に個人リポジトリを作成し実験する等の作業も積極的に行うべきである。
gitコマンド
代表的なgitのサブコマンドを以下に列挙する。
なお、「gitのサブコマンド」を「gitコマンド」と表現するのが一般的なようなので、当記事でもそれに倣う。
add
変更内容をインデックスに追加する。
変更したファイルを丸ごと追加するだけでなく、-p
オプションを付与して実行することで変更箇所をハンクに分割しステージングする範囲を選択することも可能。
commit
インデックスに登録された変更内容をローカルブランチに記録する。
この操作により、その時点でのスナップショットがコミットとして作成される。
リモート追跡ブランチ及びリモートブランチは変更されない点に注意すること。
push
ローカルブランチのコミットをローカル追跡ブランチ及びリモートブランチに反映する。
強制push
git push --force-with-lease
(或いはgit push --force
)のように、強制実行するオプションを付与してpushを実行すること。
通常、pushを行う時点では
という状態になっているはずで、後者が更新内容、すなわちpushする対象となっているはずである。Gitもこれを想定しており、この状態をpush可能な条件としている。
ところが、rebase等のコマンドでローカルブランチのコミットを作り直すと前者の条件を満たせなくなり、Gitはこれを「ローカルがリモートより遅れている」と判断するため、この状態でpushするとエラーが発生し失敗する。(エラーメッセージ内でpullを促してくるのはこれが理由)
このように前者を満たさない状態のローカルブランチの内容をリモートブランチに反映すると、結果としてリモートブランチに存在したコミットが消失するため、これはある意味危険な操作である。
それを承知の上で実行するには、前述のオプションを付与してpushを行う必要がある。
二種類のオプションの挙動の差異についてはここでは言及しないが、ネット上に多数情報があるので各自調査すること。
fetch
リモートブランチの変更内容をリモート追跡ブランチに反映する。
リモートブランチの先頭(ブランチ名が指すコミット)を取得し、そのコミットが保持している親コミットを取得し…という流れで順に取得することでコミットグラフが作成されるイメージ。
GitHub上でリモートブランチに対して変更を行った場合や、他者がリモートブランチにpushした内容を取得する場合に使用する。
あくまでもリモート追跡ブランチを更新するコマンドであり、ローカルブランチは変更されないことに注意。
merge
別ブランチの内容をカレントブランチに「合わせ込む」コマンド。
各ブランチの状態及びmergeの挙動に関する設定により、マージコミットが作られるか否かが分かれる。
当記事が想定(略)では、Pull Request完了時にreviewerがGitHub上で実行する以外に使用する機会は無い。
pull
fetch+merge。
GUIのGitクライアントはこれを実行するよう頻繁に促してくるが(特にVSCode/VisualStuidoは顕著)、当記事が(略)では、以下の理由により使用する機会はほぼ無い。
- ローカルのmainブランチは更新せず放置しておいて良い
開発作業は都度featureブランチを作成して行い、mainブランチへのmergeはGitHub上で行うため、ローカルのmainブランチは更新する必要が無い。
featureブランチを最新のmainにrebaseする等の作業を行う際は、ローカル追跡ブランチのmainのみfetchして更新しておけば良い。 - featureブランチへのpullは不要
featureブランチへのpullを促される場面として想定されるのは、rebase等によりコミットを作り直した結果リモート追跡ブランチに存在するコミットがローカルブランチに存在しなくなった場合である。
この状態をGitは「ローカルがリモートより遅れている」と判断しpullを促してくるが、ここでは意図的にこのような状態にしているのであって、当然ながらpullする必要は無い。(してはいけない)
この場面で行うべきはpullではなく強制pushである。
pullする機会は以下のケース程度かと思われる。
- 正式版の最新バージョンを取得して動作確認する
リモートのmainブランチの最新版を取得して動作確認することになるため、ローカルのmainブランチをリモートの最新まで進める必要がある。 - revieweeが修正した内容を確認する
レビューの過程で、フィードバックの反映等の理由でrevieweeが変更をpushした場合、書面レビューで足りなければreviewerとしてfeatureブランチの最新版を取得して確認した方が良いことも多い。
上記いずれの場合もpullを使わずとも実現可能。
reset
HEADが指すコミットを変更する。
「HEAD」という付箋を別のコミットに貼り替えるイメージ。
コミットを消すわけでは無いが、hard reset(git reset --hard
)すると未commitの変更内容は失われるので注意。
cherry pick
別ブランチのコミット(での変更内容)をカレントブランチに適用する。
rebase
(図を用いた説明を追記予定)
コミットの作り直しに慣れるためにも、まずは
- 新規ブランチを作成し、featureブランチの内容を退避
- featureブランチをmainの最新にhard reset
- 退避ブランチのコミットをcherry pick
- 退避ブランチを削除
という手順で作業することを繰り返し、仕組みが理解できた段階でrebaseを使用するよう切り替える、という流れを推奨する。
cat-file
Gitオブジェクトの内容を表示するコマンド。
git cat-file -p <オブジェクトのハッシュ>
のように-p
オプションを付与することで、オブジェクトの種類に応じわかりやすい形で表示してくれる。
GitHub
当記事内では、GitHub社が提供するWebサービスを指す。
バージョン管理システムであるGit及び、それを制御するgitコマンドとは別物であることに注意。
主に、GitHubが提供する以下の機能を利用して開発を行う。
Issue
本来はバグ票のようなものであるが、当記事が想定している開発スタイルではバグ管理に留まらずタスク管理ツールとして用いる。(RedmineやTracのようなバグトラッキングシステムと同様)
「Issueの起票」をコーディング含め全ての作業の起点とすることで、トレーサビリティを担保する。
他の開発者への質問や相談もIssueとして起票することを推奨する。
Pull Request
自身のブランチの作業内容を、指定したリモートリブランチに取り込むよう依頼する機能。
reviewerはここにレビューコメントを記入し、revieweeとやり取りする。
略して「PR」と書くことも多い。
当記事が(略)では、featureブランチでの作業内容を同一リポジトリ内のmainブランチに取り込むためにレビューを依頼する目的で利用することが基本となるため、GitLabのように「Merge Request」という名称のほうがしっくりくるかも知れない。
Organization
組織として複数のリポジトリを纏めて管理する機能。
Milestone
Issueの付属機能。文字通りマイルストーンを作成し、各IssueやPRをマイルストーンに紐付けることができる。
各タスクの期限管理に利用すると良い。
Project
Organization内のリポジトリを跨いでIssueやPRを纏め、タスク管理ツールとして利用できる。
Trelloのようなカンバン形式で表示したり、マイルストーン単位でグルーピングした表形式で表示したりと、見た目の自由度はそれなりにある。
GitHubではIssueとPRはほぼ同列に扱われるためそれら全てをProjectに紐付けることも可能だが、管理が煩雑になるので筆者はIssueのみを紐付けるようにしている。
各Issueに対し進捗状況やpriorityを設定する手間が増えるため、得られるメリットと天秤に掛けた上で利用するか否かを適宜判断すること。
更新履歴
- 2022/10/17
- HEADの項を加筆修正
- 昔ながらの癖で「master」と書いてしまっていた箇所を「main」 に修正
- 2022/10/13
- 書きかけの部分もあるがひとまず投稿