home

Git/gitlabで共同作業をするための最小限の知識

(the page is encoded in UTF-8)

Gitとは

Gitは,「バージョン管理システム (Version Control System)」 と呼ばれるソフトウェアの一種で,同種のものとしては, CVS, Subversion, Mercurialなどがある. 要するに複数の人間で,ソースコードに関する変更を安全に行うシステムである. 複数の人間で同じシステムをいじるときに, 変更点を他の人に伝えるのにまさか毎回手動でメールをやりとり, というわけにも行かない. かといって,本当に同じファイルを複数人が同時に更新したら, タイミング次第でお互いの更新をお互いにつぶしあうような事態が, 容易に発生してしまう. 手元である程度まとまった更新を行い, 最低でもコンパイルが通る程度までまとまったところで, 変更点を一括して他の人に伝える, というのがそれらのシステムの主な目的である.

前提とする構成

例えば3人チームで作業を行う際の基本的な構成は下図のようになる. 各メンバーの個人マシンと,こちらが準備したgitlabのサーバが, それぞれソースツリーの完全なコピーを持つ. 各自は個人マシンで作業をして,切りのいいところで変更点をサーバに反映させる. 他のメンバーはその変更点を適当なタイミングでサーバから取り寄せて, 自分の個人マシンに適用する.

gitは自由度が高いシステムで,上記以外の構成も可能で, 例えば別に一つのサーバを介す必要はなく, お互いのマシンの間で直接変更をやりとりしたりすることもできるし, コピーされたツリーを再びコピーして孫を作り, 階層化したりすることもできる. だが一番の目的はメンバー間で効率的に変更を 伝え合うことであって, それ以上のややこしいことはさしあたり必要ないし, ハマる元になるので,以降ではこのモデルで作業するために必要な, 最低限のgitの概念と操作方法を説明する. また,この課題ではこちらが 進捗把握やコードレビューをするために gitlabのソースを眺めたりもするので, こまめに途中経過でもサーバにあげてもらうことが重要である.

gitlabサーバへログイン

上記のような構成で作業をするにあたっては, まずはgitlabサーバにアカウントが作られていなくてはならない. このアカウント作成作業はこちらで行う.

トップページにアクセスすると以下のようなsign inページに飛ばされるので, ユーザ名とパスワードを入力してsign inする. この際注意として,LDAP, Standardと表示される二つのタブのうち, "Standard"を選んでsign inする.

初めてsign inした際に,パスワードも変更する. それには,sign in直後のページで左のメニューから"Profile Settings" をクリック.

そこから,"Password"をクリックしてパスワード変更画面へ行く.

なお,以降の作業をするにあたって必ずしも必須ではないが, 推奨される設定作業として,SSHキーの登録がある. その説明は後回しにして,その先の説明を行う.

ユーザ名とメールアドレスの設定

初めてGitを使う際は,編集履歴に記録される名前とメールアドレスを設定しておく. 端末で次のようなコマンドを入力する.

$ git config --global user.name "(名前)"
$ git config --global user.email "(メールアドレス)"
この設定はホームディレクトリの設定ファイルに記録され, そのマシンでGitリポジトリを扱う際に用いられる.
$ cat ~/.gitconfig
[user]
	name = (名前)
	email = (メールアドレス)

プロジェクト開始時の作業

あるプロジェクトを始めようと思った時に必要なセットアップ, すなわち誰かがダウンロードして,展開したソースツリーを, 上で示した状態まで持ってくるために必要な作業を説明する. 概要は,

  1. gitlab上でプロジェクトを作成.すると,内容が空のレポジトリができる(GUI 操作)
  2. gitlabのレポジトリを複製.内容が空のレポジトリが, 手元のマシンに複製される(git clone)
  3. 手元のレポジトリ内でソースツリーを展開し, フォルダをレポジトリに追加(git add)
  4. 変更をコミットする(git commit)
  5. コミットした変更をgitlabサーバに送り込む(git push)
  6. 他のメンバーもgitlab上のレポジトリを複製(git clone)すると, ソースツリー一式が既に入ったレポジトリのコピーが取得できる

以下では,gnuplotを変更するプロジェクトを例に取り, 上記のステップを終えるまでの手順を具体的に説明する.

gitlab上でプロジェクトを作る

画面右上の + ボタンが,プロジェクト追加のとボタン.

これで,gitlabサーバ上に,空のレポジトリが作られる. なお,この状態でプロジェクトのページへ行くと, 以下のようが画面で以降の手順が案内される. 以下で説明するのはこの内の上の方(Create a new repository) に沿ったやり方である.

gitlabサーバ上の空のレポジトリを複製(clone)する

cloneするには https 経由で行う方法と,SSH経由で行う方法がある. 後者を用いるには, SSHキーの登録作業を事前に行っておく必要がある. どちらにしても手元に,「プロジェクト名」という,空のフォルダができるはずである. なお,前者でcloneしても,あとからSSH経由でcloneすればそれ以前の作業は引き継げるので,現時点ではどちらで行っても問題はない.

ソースツリーを準備する

できたフォルダに移動し,そこにソースツリーを展開する.
$ cd プロジェクト名
$ mv どっか/gnuplot-5.0.1.tar.gz .
$ tar xf gnuplot-5.0.1.tar.gz
ディレクトリ構成は以下のようになる.
プロジェクト名/
  +- gnuplot-5.0.1/
     +- config/
     +- demo/
     +- ...

ソースツリーをレポジトリに加える(add)

$ git add gnuplot-5.0.1

これで,gnuplot-5.0.1というディレクトリおよびその下の ファイルが全て追加される.ただし,追加されるとはいっても, その内容がすでにgitlabサーバに反映されている訳ではない.

なお,レポジトリ内には,人間が直接編集するファ イル(広い意味でのソース)だけを入れ,configure やmakeの結果各マシン上で生成されるもの (Makefile, config.h, .o ファイルや実行可能ファ イル)は入れない,というのが普通である.各マシンの 上で,configure, make, make install とすることで ビルドできるようなものを共有したいのだから, これは当然のことである. だから,ソースツリーを 加えるときは常に,解凍したばかりの, まだ何も作業をしていないソースツリーを加えることを推奨する.

追加されたファイルをコミット(commit)

$ git commit -m "コメント" 
とする.コメントのところは適切に書く.例えば今なら, "added source tree" とか. ところで,この段階に至っても,gitlabサーバにデータが 送られているわけではない(それをやるのは次の,push).

コミットした変更を別のマシン(gitlabサーバ)へ送り込む(push)

$ git push

ここに至って,先ほどまでに行われ,コミットされた変更 (つまり,ソースツリーの追加)が,gitlabサーバへ送り込まれる. pushに至って初めて変更が送り込まれるんだったら, では,その前段のcommitはいったい,何をしていると思えばよいのか.

要するに,ここまで行った色々な変更を一つのパッケージにまとめて, あとで別のマシン(gitlabサーバ)に送り込む際の単位を作っている, と理解してもらえれば良いのではないかと思う.

他のメンバーがレポジトリを複製する

これでgitlabサーバにソースファイルが無事送り込まれた. gitlabをブラウザで見れば,ファイルが見られるはずである. 他の人が行った変更やそれらの履歴などを見ることも出来, gitlabを使うことのメリットが感じられる.

こうしておいて他のメンバーが,cloneコマンドを実行すれば, ソースファイル一式がダウンロードできる.

今度は「プロジェクト名」というフォルダの中にソースツリー一式が入った レポジトリが複製されることになる.以上で,プロジェクトごと に必要なセットアップは終わりである.

日常繰り返される作業

いったん全員が同一のレポジトリの複製を手にしたら, 以降の作業の流れは以下のようになる. これらを変更点があるたびにこまめに繰り返す,というサイクルである. もっとも今回の演習に限って言えば, 他の人に渡す変更内容ができるよりも, 試行錯誤をする時間のほうが中心となり, そこまでpushが細かく発生するわけではないかも知れない.

手元のレポジトリ内で普通にファイルを編集する

これは通常通りエディタで行えば良い. なお,新しいファイルやディレクトリを追加する場合は, 上でソースツリーのディレクトリを追加した時と同じように, git addコマンドを使って追加する.

切りのいいところでコミットする

$ git commit -m "コメント" -a
既に説明したcommitをここでも使うのだが,ひとつだけ注意がある. それは, commitはそれに先立ってaddコマンドで指定されたファイルしか, 変更されたとはみなしてくれない,ということである. つまり,既存のファイルをただ変更するだけでは, commitはそれに気づいてくれない.それを勝手に発見してくれるようにするために, -aオプションを指定する.もしくは変更したいファイルをいちいち git addで指定しても良いのだが,普通は余りやりたくないのではないか.

コミット結果をgitlabサーバに送り込む

$ git push
ここは先程と同じ.新たに説明することはない.

他のメンバーがpushした変更を取り込む

$ git pull
これは,他の人がpushした変更を取り込む手段である. 自分が変更をしていようといまいと,時折実行するのが良い. もしくはとなりに座って作業しているのであれば, 他の人がpushしたら自分はpullをするようにすればよい.

衝突(conflict)を理解する

ここまでの話で作業は始められる. 以降は時間を見つけて好きなときに読んで下さい. トラブルに遭遇してから読んでも良いが, 事前に読んでおけば心の平穏は保ちやすい.

授業時間中は皆で相談しながら コーディングは一人で,というスタイルを推奨するが, 時には並行して作業をすることもあるだろう (そもそもリビジョン管理システムの本来の目的は, それを可能にすることである).すると, 複数のメンバーが同じファイルを変更し, commit, pushをするという事態が避けられない. これが,変更の「衝突(conflict)」という現象で, このようなときにgitが何を言ってくるか, 自分は何をすればよいかを知っておくことは, 安心して作業をするために重要である.

衝突はいつ「判明」するか

今,A, B二人が編集をしているとする. 当然のことながら衝突は二つの, 別の場所で行われた変更が「出会う」ところで判明する. それには少なくとも, A, Bのどちらかが, pushを行っていることが前提である. つまり,レポジトリ内でのファイルの編集や, commitだけでは衝突は発生しない. より具体的には以下の時点で衝突が, 明るみに出る.

その語感から受ける印象とは異なり,commitだけでは衝突は発生しないことに注意. git特有の言葉遣いだが,commitはあくまでそのレポジトリ内で, 変更点を「パッケージ化」するだけの,ローカルな操作である. データベースを始めとして,「コミット」という言葉は普通, 他と衝突したら失敗する操作,逆に言うと,コミットが成功すれば その変更はグローバルに反映されたとみなせるような操作を 意味するのだが,gitではそうではない.したがって,commitが 成功したあと,いざpushをしようとして衝突が判明する.

それぞれの場合のgitの挙動を説明しておく.

ケース1: push時の衝突

これは操作自身が失敗する.出るのは以下のようなメッセージ:
nanamomo:gnuplot-5.0.1% git push
To git@doss-gitlab.eidos.ic.i.u-tokyo.ac.jp:tau/my-awful-project.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@doss-gitlab.eidos.ic.i.u-tokyo.ac.jp:ta\
u/my-awful-project.git'
ヒント: Updates were rejected because the remote contains work that you do
ヒント: not have locally. This is usually caused by another repository pushing
ヒント: to the same ref. You may want to first integrate the remote changes
ヒント: (e.g., 'git pull ...') before pushing again.
ヒント: See the 'Note about fast-forwards' in 'git push --help' for details.
つまり,Bに「まずAの変更を取り込むためにpullせよ」と言っている. ので,Bは素直にこれに従う.
$ git pull
よって,結局ケース2の場合に帰着される.

ケース2: pull時の衝突, かつBがcommitする以前の変更と衝突

つまりBはまだファイルの変数作業をしている最中で,まだそれを commitはしていなうちに,pullを行った場合. この場合のメッセージがこれ.
nanamomo:gnuplot-5.0.1% git pull
remote: Counting objects: 7, done.        
remote: Compressing objects: 100% (5/5), done.        
remote: Total 7 (delta 3), reused 0 (delta 0)        
Unpacking objects: 100% (7/7), done.
From doss-gitlab.eidos.ic.i.u-tokyo.ac.jp:tau/my-awful-project
   b672bcb..a6f0c56  master     -> origin/master
Updating b672bcb..a6f0c56
error: Your local changes to the following files would be overwritten by merge:
        gnuplot-5.0.1/CodeStyle
Please, commit your changes or stash them before you can merge.
Aborting
stashというのはコミット以前の変更を, なくしてしまうという操作で,大概の場合これがやりたい操作ではないだろう. なのでまずは自分の変更を(自分のレポジトリ内に)commitすることになる.
$ git commit -m "..." -a
$ git pull
そこで結局すべては,ケース2-2の場合に帰着する.

ケース2: pull時の衝突, かつBがcommitした変更と衝突

場合により,gitが両者の変更を行単位で勝手に統合してくれる場合と, そうでなくさじを投げられる(例えば同じ行を二人が編集した)場合がある. さじを投げられた場合は以下のメッセージが出る.
nanamomo:gnuplot-5.0.1% git pull
remote: Counting objects: 4, done.        
remote: Compressing objects: 100% (3/3), done.        
remote: Total 4 (delta 2), reused 0 (delta 0)        
Unpacking objects: 100% (4/4), done.
From doss-gitlab.eidos.ic.i.u-tokyo.ac.jp:tau/my-awful-project
   76bcaeb..bbbec8a  master     -> origin/master
Auto-merging gnuplot-5.0.1/ChangeLog
CONFLICT (content): Merge conflict in gnuplot-5.0.1/ChangeLog
Automatic merge failed; fix conflicts and then commit the result.
後者の場合はファイル中に両者の相違点が織り込まれたファイルができる.
<<<<<<<< HEAD
Gitはクソだ
=======
Gitはクールだ
>>>>>>>> 72616890b37a0fb7b2f8cadf83524ed591430d5f
これを見ながら手動で変更する. どちらの場合でも,Bはその後改めて,commitをする.その後push をすれば,めでたく,A, B両者の変更点を統合したバージョンが gitlabサーバにとどく.
 ... 手動で変更をマージして, <<<<<<<< とかがないファイルを作る ...
$ git commit -m "..." -a
$ git push

これだけは,というコマンドまとめ

SSHキーの登録方法

レポジトリとのやりとりはhttpsを利用して行う方法と, SSHを利用して行う方法がある.httpsの場合,pushやpullをするたびに, gitlabサーバ上のユーザ名とパスワードを入力する必要がある. SSHを利用するためには事前に公開鍵を登録する必要がある. 以下はその手順について述べる.

  1. sign in直後の画面で,左のメニューから,"Profile Settings"をクリック.
  2. Profile Settingsの画面から,"SSH Keys"をクリック.
  3. "ADD SSH KEY"ボタンをクリック
  4. キーを貼り付けるボックスに,SSHの公開鍵の中身をペーストし, "ADD KEY"ボタンをクリック.

    公開鍵は,すでに他の目的で生成したことがある人は, ~/.ssh/id_rsa.pub (または ~/.ssh/id_dsa.pub)という名前で生成されている (~/ はホームディレクトリの意味). 生成したことがない人は,
    $ ssh-keygen
    
    というコマンドで生成する.すでに存在する場合上書きしてよいか, という警告が出るので,その場合は上書きせずにコマンドを中断し, 既にあるものを使えば良い.
なお,SSHキーを登録していない状態では,プロジェクトのトップページに 以下のようなオレンジのバナーが表示される. ここから "add an SSH key"をクリックしても同じ登録画面に到達できる.