Gitlabの使い方
Git(ギット)とは?
- バージョン管理システム(Version Control System)の一種です
- ウィキペディアによるGit解説@
- 班が開発しているプログラム群やドキュメント類は,班員全員で共有して使う必要があります.そのために,Gitを使います.
- グループ開発の場合,特にバージョン管理が重要です
- この講義では,2017年まではSubversion(サブバージョン)を使っていました
- 2018年からGitに切り替えました.
- Gitを便利に使えるようにしたウェブサービスとして, GitLab@を使います.
- gitlabの開発元では「GitLabを誰でも使えるようにした公開サーバ」が運営されていますが,この講義で使うのは情報コース内に立てた以下のサーバです.
- 情報コースのGitLabサーバ https://gitlab.cis.iwate-u.ac.jp/
GitLabを用いたプログラムの共同開発の流れ
大まかな流れは次のようになります.
- GitLabサーバ上に,開発中のプログラムの格納場所(リポジトリ)を作る
- この格納場所は,URI(https://gitlab.cis.iwate-u.ac.jp/xxx/yyyとか)でアクセス可.
- ここに格納されたファイル群がすべて バージョン管理 される
- 格納場所からプログラムを読み出し(clone, pull)て,自分用のコピー(作業コピーという)を作る
- 自分用の作業コピーで修正・追加を行い,独自に開発を進める
- 自分が修正追加した作業コピーを,適当なタイミングで格納場所に書き戻す(commit, push)
- 他の人と修正が衝突した時にはマージする
事前設定
gitlabサーバにログイン
情報コースのGitLabサーバ https://gitlab.cis.iwate-u.ac.jp/にアクセスし,LDAPタブが選ばれていることを確認してユーザ名(h30jXXX)とパスワードを入力してください.
課題
GitLabサーバにログインしてみましょう
ユーザ名とメールアドレス設定
Gitを使うことが初めての場合は,変更履歴に記録される名前とメールアドレスを設定する必要があります.端末(ターミナル)を開いて以下で設定してください.
$ git config --global user.name "(名前)" $ git config --global user.email "h30jXXX@cis.iwate-u.ac.jp"
設定した名前とメールアドレスは以下のようにして確認できます.きちんと設定できているか確認してみましょう.
$ git config user.name $ git config user.email
課題
Gitで使う名前とメールアドレスを設定しましょう
グループの作成(班長のみ)
以下の作業は班長だけが実行してください.
GitLabでは,(GitLab内の)ユーザが所属できる「グループ」を作ることができます.班ごとにグループを作りましょう.下の図の「2018_g00」は, 本年度, 自分のグループNo.に置き換えて下さい(3班ならグループ名を2020_g03
にします).
グループが出来たら,他の班メンバーを招待します.下の画像を参考に,班員全員を招待してください.
(班長の)課題
班のグループを作成して,メンバー全員をGitLabグループメンバーとして追加して下さい
班のリポジトリを作る
班長がGitLabに新しい「プロジェクト」(今の段階ではリポジトリと同義に捉えてもらって構いません)を作り,そこにファイルを追加して,他のメンバーはその作業コピーを取り寄せる,という練習をしてみます.
先に流れを説明します.個々の詳細手順はその後で.
- 班長がGitLabで新規プロジェクトを作成(
Web GUI
から).中身が空のリポジトリが出来る. - リポジトリを複製し,手元(ローカル)に作業コピーを作成する.(
git clone
) - 手元にある空の作業コピーにファイルやディレクトリを追加してみる.(
git add
) - 変更をコミットする.(
git commit
) - コミットした変更を,GitLabサーバに送り込む(
git push
).ここで初めて,リポジトリに修正が反映される. - 班長以外の他のメンバーが,GitLabのリポジトリを複製(
git clone
)してみる.班長が追加したファイルやディレクトリが手元の作業コピーに存在していることを確認する.
プロジェクトを作る(班長)
以下の画像を参考に作ります.リポジトリ名称を適切に決めて下さい.
これで空のプロジェクトが作られます.以下のような画面が表示されるはずです.これ以降は,「Create a new repository」に記載してある手順に沿って作業します.
(班長の)課題
新規プロジェクトを作成しましょう
空のリポジトリの複製(clone)
SSH
でクローンする方法と,HTTPS
でクローンする方法があります.SSHの場合は別途,公開鍵の登録が必要になりますので,ここではHTTPSを使ってみます.(ただし,HTTPSを使う場合はいちいちユーザ名とパスワードを入力しなければなりませんので,慣れてきたらSSHに切り替えた方が良いかもしれません)
$ cd ~; mkdir csd $ cd csd $ git clone https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git
ユーザ名とパスワードを聞かれると思いますので(GitLabのログインに使っているh30jXXXとそのパスワードを)入力して下さい.なお,URIについては,Web GUIからクリップボードにコピーしておけば入力の手間が省けます.
これで手元に「プロジェクト名」という空のディレクトリができます.このディレクトリが作業コピーです.空ですが,実際には不可視ディレクトリ.git
が含まれています.確認しましょう.
$ ls -a プロジェクト名 . .. .git
(班長の)課題
プロジェクトの作業コピーを手元に作りましょう
ファイルやディレクトリを追加する
手元の(空の)作業コピーに,ディレクトリとファイルを追加してみます.
$ cd プロジェクト名
$ mkdir work
$ touch work/README
READMEファイルは適当に編集(本当に中身は何でもOK)して下さい.
$ emacs work/README
ここまでの状態で,作業コピーは以下のような状態です.
プロジェクト名 └── work └── README
もちろん,(GitLab上の)リモートリポジトリは空のままです.手元の作業コピーとリモートリポジトリとの違いの状況は,git status
で確認できます.(以下のように表示されると思いますが,メッセージは少し異なるかも知れません)
$ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) work/ nothing added to commit but untracked files present (use "git add" to track)
メッセージにしたがってgit add
してみます.
$ git add work $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: work/README
statusの出力も少し変化しています.このように,ディレクトリをaddした場合はその配下のファイルもすべて追加されます.
なお,ここでいう追加は,あくまでもGitに「これから追加修正するよ」という宣言をしているだけであり,実際にGitLabサーバ(上のリモートリポジトリ)に追加された訳ではないので注意して下さい.
(班長の)課題
作業コピーにディレクトリとファイルを追加し,git addしてみましょう
変更のコミット
先ほど宣言?しておいた変更をコミットします.
$ git commit -m "コメント" [master (root-commit) 74e0faf] Initial commit new file: work/README 1 file changed, 1 insertion(+) create mode 100644 work/README
-m
で指定するコメントは必須項目です.Gitに限らず,バージョン管理システムでは「何をどう変更したか」という履歴の記録が重要ですので,必ず適切なコメントを入力しておく必要があります.今回の場合なら,"Add work dir and README"とか.いずれ,commit時には必ずコメント入力が必要と覚えて下さい.
ちなみに,-m
を入力し忘れてしまっても,(勝手に)エディタが起動して必ずコメント入力を求められます.
1 2 3 4 5 6 7 8 9 10 11 | # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # # Initial commit # # Changes to be committed: # new file: work/README # |
こんな感じにコメント入力を促されますので,適切に入力してからエディタを終了して下さい.面倒なら,適当なラインの#
を消せばコメント扱いではなくなりますので,それでも構いません(上の場合なら,10行目のnew file: ...のラインを活かす,等).
なお,この際に立ち上がるエディタ(の種類)は,以下で設定可能です.
$ git config --global core.editor "(エディタ名)"
(班長の)課題
適切なコメントとともにgit commitしましょう
コミットした変更をGitLabサーバに送り込む
上記の手順まで進めても,まだGitLabサーバには変更が反映されていません(GitLabサーバ側には,workディレクトリやREADMEファイルは未だ存在しません).
以下のコマンドを実行して初めて,コミットしておいた変更が(サーバ上に)反映されます.
$ git push Counting objects: 4, done. Writing objects: 100% (4/4), 290 bytes | 290.00 KiB/s, done. Total 4 (delta 0), reused 0 (delta 0) To https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git * [new branch] master -> master
(班長の)課題
git pushして変更をサーバに反映させましょう
他のメンバーがGitLabのリポジトリを複製する
班長がここまで頑張れば,ようやくGitLabサーバ側にも(コミットが反映されて)workディレクトリとREADMEファイルが追加されます.他のメンバーも,ブラウザで https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名
にアクセスすればファイルが見られるはずですし,今後は他の人が行った変更やその履歴も閲覧することができます.
他のメンバーも,以下で作業コピーを作れます.
$ cd ~; mkdir csd $ cd csd $ git clone https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git
今度は「プロジェクト名」ディレクトリの中に work
ディレクトリとwork/README
ファイルがあるはずです.確認してみましょう.
$ ls -R work ./work: README
課題
git cloneして,班長が行った変更が反映されているか確認しましょう
再びグループ開発の流れ
班員全員がいったん,作業コピーを手元に置けば,グループ開発を進めることができます.以降の作業はおおよそ次のような流れです.
班員Aの作業
- 手元の作業コピー内で普通に開発を進める.ファイルやディレクトリの追加は
git add
コマンドで. - ある程度進んで切りのいいところまで来たら,コミットする(
git commit -a
) - commitをGitLabサーバに送り込む(
git push
)
班員B,班員Cの作業
- 手元で開発を進めながらも,時折,適当なタイミングで他のメンバーがpushした変更を取り込む(
git pull
) - 自身の変更も,適度なところでコミット(
git commit -a
)してサーバに送る(git push
)
コミット時の注意点
git commit
は,実はaddコマンドで指定されたファイルしか,変更済とみなしてくれません.要するに,既に存在しているファイルをただ変更しただけでは,commitはそれには気づきません.そこで,大抵の場合は-a
オプションを追加する必要があります.
$ git commit -a -m "コメント"
覚えておくべき基本コマンド
手元に作業コピーを作る
git clone ...
日常の開発作業のために・・・
git add ...
git commit -a -m "コメント"
git push
git pull
手元の作業コピーの状態(サーバ上のリポジトリとどう違うか)を調べるために・・・
git status
git diff
git log
これだけ覚えておけば当面は何とかなると思います.なお,各コマンドの詳細については,git help "(コマンド名)"
と入力すればマニュアルが閲覧できます.(git help commit
とか)
衝突(conflict)とその解決
ここから先は読まなくても作業自体は進められます.が,非常に起こりやすい状況を想定して書いていますので,あらかじめ読んでおくと心の準備にはなるかと思います.
さて,Git管理下にあるプロジェクトにおいて,同じファイルを別々の人が(別個に)編集してしまった場合,そのファイルに不整合が生じてしまいます.これを衝突(コンフリクト)または競合と呼びます.ここでは衝突を解決するための一般的な手順を解説します.
ただし,本質的な解決は,衝突を引き起こした班員同士で話し合う必要があることに注意しましょう.きちんとした作業分担が行われていれば,実際の開発において衝突はほとんど起こらない,と言われていますが,グループ開発に慣れていない皆さんの場合は容易に衝突が起こることでしょう.
衝突の判明時期
衝突は,別の作業コピーで別々に行われた変更が「出会う」ときに判明します.つまり,少なくとも班員A,班員Bのどちらかがpushを行った後,ということになります.(作業コピー内でファイルを編集したり,commitしたり,というだけでは衝突は発生しません)
- 班員Aがpushした.その後,班員Bもpushしようとした
- 班員Aがpushした.その後,班員Bがpullしようとした(班員Aの変更が班員Bの作業コピーにやってくるので,そこで衝突)
- この場合,班員Bがcommitする前か,commitしてしまった後か,で違いがあります
以下では,もっとも起こりやすい(であろう)最初のケース1について記述しますが,結局は最後の班員Bがcommitした後にpullした場合に帰着します.
これから考える状況
開発中に,班員Aと班員Bの2人が同じプログラムファイル index.html を編集してしまいました.班員A,班員B,それぞれがcommit -aしてリポジトリにpushしましたが,
- はじめに,班員Aが index.html のpushを行って,
- 次に,班員Bが index.html のpushを行った.
とします.
編集箇所が重ならないコンフリクト
Gitは,班員A,班員B,それぞれの変更を両方とも採用したプログラムファイルを自動で作ってくれます.班員Bが(commit後に)pushしようとすると,
(班員Aが既にpushした後,下のように班員Bがpushしているとします) [班員B]$ git push To https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
と怒られます.Gitの指示通りgit pull
を実行してみましょう.すると,以下のように自動的にマージ(結合)されます.途中,自動的にgit commit
が実行されるため,コメント入力を求められますので(エディタが自動起動します),そのまま保存終了して下さい.エディタは,マージした旨のコメントが既に記述された状態で起動しますので,そのまま保存終了で大丈夫です.
[班員B]$ git pull remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名 84e7c00..cf63f21 master -> origin/master Auto-merging index.html Merge made by the 'recursive' strategy. index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
ここで index.html の中身を見てみると,班員Aの修正内容と班員Bの修正内容,どちらも反映されているはずです.
確認後,git push
してリポジトリに修正を反映させます.
[班員B]$ emacs index.html (反映を確認) [班員B]$ git push Counting objects: 6, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 688 bytes | 688.00 KiB/s, done. Total 6 (delta 2), reused 0 (delta 0) To https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git cf63f21..c1df088 master -> master
ここまでの手順で衝突は解消されます.班員A,班員Bともにgit status
で確認しておきましょう.
$ git pull ... (班員Aが実行した場合は更新があるのでいろいろ表示されます) ... $ git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean
編集箇所が重なってしまったコンフリクト
結論を先に書くと,このような衝突が起きてしまった場合は,衝突を引き起こした班員同士で話し合いをして解決策を見つける必要があります.
Gitは,この解決策を見つける作業をサポートしてくれます.
班員Aと班員Bは,index.html の同じ箇所を修正してしまったとします.先にpushを行った班員Aは,衝突は気にせず完了できます.問題は後からpushする班員Bです.push時にはエラーが発生してしまいます.
(git commit -aは既に済ませた前提です) [班員B]$ git push To https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
やっぱり怒られてしまいました.とりあえず,指示通りgit pull
を実行してみます.
[班員B]$ git pull remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From https://gitlab.cis.iwate-u.ac.jp/グループ名/プロジェクト名 c1df088..05b45d9 master -> origin/master Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.
ここで,衝突が起きたファイルを開くと,次のように両者の相違点が織り込まれたファイルとなっています.
[班員B]$ emacs index.html (衝突が起きている場所に以下の記述が見つかります) ... <<<<<<< HEAD <title>俺は班員Bだ</title> ======= <title>俺は班員Aだ</title> >>>>>>> 05b45d93e19eecf4702a1061a496f5028af37f23 ...
記号<<<<<<<
と>>>>>>>
のことを衝突マーカーと呼びます.この部分を見て,班員Aと班員Bが話しあい,どうにかして衝突を解決するようなソースの修正方法を見つけます.(ここだけはGitは何もしてくれません)
その後,班員Bが手動でindex.htmlを変更し,改めてcommit -a
及びpush
を行います.これで解決できます.
[班員B]$ emacs index.html (衝突マーカーが無いファイルを作る) [班員B]$ git commit -a -m "conflict resolved" [班員B]$ git push
もちろん,この後,班員Aも忘れずにgit pull
しておく必要があります.
Gitのもう少し進んだ使い方
Gitに限りませんが,バージョン管理システムには非常に多くの機能があり,それを使いこなすことで生産性が高められます.興味のある人は,ぜひ,次のような機能の使い方も(自分で調べて)試してみて下さい.
- ステージ
git reset
やgit stash
など- 特定のファイルを特定のバージョンに戻したりもできます(
git checkout
) - コミットすべき重要なファイルと,そうでもないファイルを分けて扱うことができるようになります
- ブランチ(branch)とタグ(tag)
git branch
など- タグは「ある時点での作業コピーのスナップショット」,ブランチは「本流(trunkという)とは異なる別の開発版」のイメージ
- push前のまずいcommitを消す
git rebase
(ほか執筆中)
SSHの公開鍵登録(動作しません)
(山中はうまくできなかった(Apr 26, 2020).うまく設定できた人教えてください.)
(以下は,教育用計算機の設定の関係で動作しません. 試さないでください.計算機に詳しい学生さんが調べてくれました.ありがとうございます.)
教育用端末を使う範囲内であれば,SSH公開鍵を(GitLabに)登録・設定しておくことで,git push
やgit pull
の度にいちいちユーザ名とパスワードを入力する必要がなくなります.
まずは公開鍵を作ります.
$ ssh-keygen -t rsa -C "git configで設定したuser.emailアドレス" -b 4096
途中,パスフレーズを聞かれますので適切に入力します(面倒なら空EnterでもOK).正常に終われば,ホームディレクトリ以下に.ssh
という不可視ディレクトリが作成され,その下にid_rsa
とid_rsa.pub
という二つのファイルが生成されます.このid_rsa.pub
が公開鍵になります.(id_rsa
の方は秘密鍵なので間違わないように!)
この公開鍵id_rsa.pub
(の中身)をGitLabに登録します.以下の画像を参考にしてください.
これで設定は終わりです.やり方に間違いがなければ,次回から(HTTPSではなく)SSH経由でGitコマンドを利用することができます.