BsBsこうしょう

これは考えたことではなく思ったことです。

本当に何も分からない人のためのGit

前文・謝辞

この記事は、『OUPC2021 開催記』で触れたドキュメントのうち、GitHubの使い方を示したものです。

a8pfactory.hatenablog.com

執筆とポリシー作成に協力してくれたkotamanegiとKowerKointに、この場で改めて感謝を申し上げます。

用語集

  • ブランチ (branch)

https://backlog.com/ja/git-tutorial/stepup/01/
ファイル改変の履歴を分岐しながら記録していくためのもの。OUPCではmainブランチから他のブランチを分岐させていく。

  • コミット (commit)

追加や改変したファイルをgitに登録して、git上に記録として残すためのもの。コミットを複数つなげることによって履歴を記録していく。コミットしなかったファイルはgit上ではなかったものとして扱われるので注意。

  • プルリクエスト (Pull Request, PR)

ブランチではmainブランチやその他のブランチから分岐させて変更を反映することができたが、逆に親ブランチに派生先のブランチの変更を取り込むための操作。2つのブランチの変更履歴(コミット)をまとめて1つのブランチにする。

ワークフロー

0. 作業前にやること

git checkout main
git pull

今回はmainブランチの変更はほぼないが、とりあえず打っておくコマンド。

1. ブランチの作成

git checkout main
git checkout -b hogehoge

ブランチを作成するのは、必ずmainブランチに移動してから!!

hogehogeはブランチ名。ブランチ名は author/problem とする。

1. ブランチをすでに作成している場合

git checkout hogehoge
git pull

特にプルリクエスト(PR)を作成した後、自分以外にブランチを編集する人が現れる可能性が高い。そのため、その変更を必ず取り込んでから作業を開始する。

2. コミット・プッシュ

git add (ファイル名)
...
git commit -m "コミットメッセージ。ターミナルが対応しているなら日本語も使える"
# 何回かcommitを行うこともある。
git push

コミットはPRより細かい作業単位で区切る。ひとつの問題を作ることを考えた時、「rime add . problem hoge + 問題文」「generator追加」「validator追加」「解法追加」などの区切りが考えられる。

Gitでは通常コミットメッセージの編集のためにviが立ち上がるが、mオプションをつけていればこれをいい感じに処理してくれる。

コミットメッセージの動詞は'add', 'fix', 'modify', 'delete'くらいしか使わないと思う。

git push は頻繁に行っても良い。少なくとも、その日の作業を終了した時にはコミット+プッシュを行う。

4. プルリクエスト、レビュー

問題が完成したらGitHubにアクセスする。上部に'create Pull Request'という項目があるはずなので、そこをクリックする。

Slackでおおよその前提は共有できているため、PRに書くメッセージはテキトーでも良い。しかし、 PRを出した時点で完了している作業 について書いておくと良い。例えばどのような解答(ACするのか、WAするのか、言語は何か)があるかについての情報など。

参考 https://github.com/kotamanegi/OUPC2021/pull/4

f:id:A8pf:20220407152239p:plain
kotamanegi/OUPC2021はprivateリポジトリなので、スクリーンショットを添付する。

また、'Files Changed'タブからピックアップしたいファイルの行をクリックしてコメントをしておく(Add single comment)と、コードで分かりにくい場所の補足説明をすることができる。

Tips

ここまでの作業は、VSCode上で行うこともできる。特に、コミットとプッシュはVSCode上で行った方が分かりやすい。プルは少し分かりにくい。

困った時

1. git checkout ができない

2. git push ができない

3. git pull ができない

git checkout git push git pullができない原因は、大きく2つ。 - 衝突している - commitしていない作業がある

衝突については、後で説明する。

Gitでは、複数の人が同じファイルを同時に編集したとき(衝突)、そのファイルが壊れないようにする仕組みが備わっている。そのため、自分の手元にあってかつGitに登録されていない変更があったときには、その変更が破壊されないようにgit pullが止まる仕組みになっている。 またgit pushについても、自分の手元の環境をGitHubに再現できるようにするために、登録されていない変更があると止まるようになっている。

f:id:A8pf:20220407151623p:plain

VSCodeを使っていれば、右側の'Source Control'タブに、反映していない変更がプッシュ通知の形で表示される。また、ターミナルでgit statusと打つことで、コミットしていない変更とプッシュしていない変更を確認することができる。

4. 衝突(conflict)がどうとか言われた

Gitではしばしば衝突が発生する。今回は最もプリミティブ(しかし効果的)な解決方法を紹介する。

それは、プロジェクトを全て消してしまうことだ。

まず、この作業を行う前に、プッシュ、プルに失敗した全ての変更を全く関係ない場所に移動する。

次に、OUPC2021のトップディレクトリの一つ上に移動して、

rm -rfd OUPC2021
git clone https://github.com/kotamanegi/OUPC2021.git

を実行。これで作業を最初からやり直せる。その後、自分が作業していたブランチに移動して、自分がした変更をブランチに反映する。

Tips: 正しい衝突の取り除き方

実は上で紹介した方法は、gitのマージ操作を全て手動で行っているのと同じだ。CLIだけで衝突を取り除いてマージを完了させるのは難しいが、VSCodeを使うと、幾分楽にこの操作を行うことができる。

f:id:A8pf:20220407151919p:plain

VSCodeから変更を同期したときにconflictが発生すると、図のように衝突している行がハイライトされる。 基本的にハイライトされた各ポイントについてAccept Current Changeもしくは Accept Incoming Change をクリックすることで正しい方を選択して解決できる。 手動で編集して解決したい場合は一度 Accept Both Changes を一度クリックしてから編集するなどで適切に対処する。 conflictを解決できたら再びadd→commit→pushを行う。

追記(2022/04/07): rebaseを行った時のconflictは色々試した結果、基本的に Accept Incoming Change をするのが良いと判明した。Current Changeを取り込んでも直後のコミットで取り消されるため。

OUPC2021でのルール

問題を追加するとき

(作問者名)/(問題名)というブランチ名でmainブランチから派生させる。そのブランチ内で問題を追加して、mainブランチへとプルリクを作成する。

問題を修正するとき

ブランチに直接pushする。重大な修正はSlackチャンネルやPRのConversationでも告知する。

告知する際には、すでに解答を上げた人たち宛て(PR内のコミットの履歴、もしくは解答のディレクトリ名から分かるはず)にメンション (@) を使う。

問題を削除するとき

PRのConversationあるいはSlackで問題を削除することを通知。受け取り次第、BadlyluckyがPRをcloseする。

問題に解答を追加するとき

ローカルで rime test に通ることを確認すれば、問題を置いているブランチに直接pushしてよい。CIが落ちた(×が出た)場合は、その都度修正する。

すでにある解答を修正するときも、問題を置いているブランチに直接pushしてよい。

conflictなど、訳分からん状態になったら詳しい人(Badlylucky, KowerKointなど)をSlackで呼ぶ。

他人の解答を変更する時

本人に修正を依頼してください。プルリクや直接pushなどは禁止です。

追記(2022/04/07): これよく考えたら私守ってませんね。rimeに乗ってないなどの本当に軽微な修正があったためスピードを重視。

(自分のでも人のでも)解答を削除するとき

まず、解答を削除したい問題ブランチに移動し、新しくブランチを作成する。

git checkout (問題ブランチ)
git checkout -b (新しいブランチ名)

ブランチ名は先頭がブランチの作者の名前になっていれば何でもよい。

これで問題を追加したブランチから枝分かれしたものが出来上がるので、そのうえで自分の解答を削除する。そのあとadd→commit→push。

作業が完了した後、GitHubに移動し、PRを作る。右のReviewersに問題作成者を指定する。問題作成者は余計な変更が入っていないことを確認した後、PRをマージして削除を完了させる。