コンテンツにスキップ

バージョン管理(Git/GitHub)

バージョン管理とは、ファイルの更新履歴を記録する仕組みです。ソースコードのバックアップとして使えます。 ここではバージョン管理の基本の基本について紹介します。バージョン管理は現代のソフトウェア開発において必須の技術です。これから先、授業や研究でコーディングする際は、常にバージョン管理をすることを強くオススメします。バージョン管理は全ての学生が知っておいて良い概念ですが、比較的新しい技術なので、 これまでは必修の講義で教える機会がありませんでした。 なので、今年度からは、電気系学生必修のこのソフト1の講義で少し触れておくことになりました。

有益な資料:

  • ドットインストールのGitの回: 短い動画でGitを紹介しています。これを眺めて雰囲気を知るといいかもしれません。
  • Git/gitlabで共同作業をするための最小限の知識:田浦先生による3年生向けの選択実験の「大規模ソフトウェアを手探る」の講義資料です。特に多人数での協調作業について詳しく説明されており、非常に有益です。また、田浦先生の講義ではコードのホスト先としてGitLabという仕組みを使っています。一方、本講義(ソフトウェア1)ではGitHubを使います。このあたりの差が、本講義を読んだあと田浦先生の資料を読むと明らかになり、面白いと思います。
  • 数値計算屋のためのGit入門:慶應大学渡辺先生によるGit入門の資料です。Gitコマンドの裏側で何が起きているかを含めた非常にわかりやすい解説です。本講義を終えたあとに見てみると理解が深まると思います。
  • The Missing Semester of Your CS Education, MIT EECS:MITの講義のビデオと資料です。今日説明するGitをはじめ、情報系学生が知っておくべき現代的なtoolの使い方がまとめられています。こういった知識は先輩から伝授されたりしてだんだん学んでいくものですが、体系的に知っていれば効率的に物事を進められると思います。卒論を開始するまでにこれを全部見ておくと、卒論以降が効率的になると思います。情報系以外の学生にもむしろオススメです(今後学ぶ機会が少ないかもしれないので)

コラム

ここでは基礎の基礎だけ教えます。「個人で自分のコードのバックアップをとる」ことを目指します。今日扱わない重要なトピックとしては以下があります。

  • ブランチ:これはGitの根幹をなす重要な概念ですが、個人でバックアップを取る分には知らないでも大丈夫です。全てmainブランチという「デフォルト設定」でやります。
  • 多人数での開発:Gitの真髄は多人数で協業でコーディングすることが可能になる点ですが、これもやりません。興味のある方は上記の田浦先生の講義を是非とってみましょう。

バージョン管理とは何か?

バージョン管理とは、一言でいえばテキストファイルの更新履歴を記録してくれる仕組みです。 普通、main.cを更新して上書き保存すると、その最新の状態しか残りません。過去のどの時点でどういう変更がされたかを記録するのがバージョン管理システムです。 歴史的にはsubversionやCVSなど色々あったのですが、近年はgitという仕組みがデファクトスタンダードになっています。 gitはコマンド名であり、様々なgitコマンドが準備されています。これにより、手元のファイルをバージョン管理できます。

そして、手元でgitでバージョン管理されたファイルに対して、それをウェブ上にアップロードしてウェブサーバ上に保存してくれるサービスが存在します。これがGitHubなどです。 このGit + GitHubの仕組みを図示したものが下記です。

バージョン管理(+GitHub保存)が無い場合は、パソコンが壊れたら全てのデータは失われてしまいます。 また、3日前のコードと4日前のコードの差を見る、といったこともできません。 そして、現在のコードはそのままに新機能を増やしたいとき、しばしばmain_最新版.cのようなファイルを作ってしまいます。これにより、ファイルがどんどん煩雑になります。

ここでバージョン管理をしたものが右図です。 ローカルの作業ディレクトリに対して、$ git initコマンドを用いて.gitというディレクトリを作ります。このディレクトリの中にはgitの作業データが入っています。 この中身は触らなくて大丈夫です。 この.gitがあるディレクトリが、gitの管理対象になります。このディレクトリ中のファイルに対して、$ git add$ git commitといったコマンド を用いて変更履歴を保存します。

そして、その内容をGitHubのウェブ上のリポジトリに同期させます。これにより、コードのマスター情報はGitHub上に保存されます。これは安全なバックアップにもなります。 そして今後、ローカル側で作業してファイルを更新したときは、それを$ git pushという操作でGitHubのリポジトリに反映させます。 逆に、GitHubリポジトリ上で更新があった場合は、それを$ git pullというコマンドで手元にもってこれます。

ここで重要なのはバージョン管理という概念を学ぶことです。 具体的なプログラム(Git/GitHub)の使い方を知ることは副次的なものです(とはいえ、今日の講義は実質ほとんどGit/GitHubの使い方の説明ですが)。 バージョン管理は情報の変更という概念をプログラマブルに扱う仕組みです。この考え方は非常に重要です。 近年の例だと、infrastructure as codeといって、 サーバなどの大規模システムの構成をGitのような「変更を追従出来る仕組み」で管理する概念が有効であることがわかってきました。 また、コードだけではなく、卒論などの文章を書くときに使うTeXファイルもバージョン管理します。 よって、バージョン管理は重要です。 これから先、授業や研究でコーディングする際は、常にバージョン管理をすることを強くオススメします。

また、よくある勘違いとして「最初はコードがグチャグチャだから、コーディングを進めて綺麗になってからバージョン管理しよう」というものがあります。 これは間違いで、最初のグチャグチャなときから始めると良いです。なぜなら、デメリットが1つもないからです。 そして、もし何かおかしいことになってしまったら、(1) ローカルの.gitを消す (2) GitHubのリポジトリを消す、とすれば全て綺麗サッパリなくなるので、 やり直せます。なので、恐れずに、バージョン管理ライフを始めましょう。

コラム

長年、GitHubはGitホストサービスで一番有名だったのですが、無料でプライベートリポジトリを作れないという問題がありました。 そのため、無料で作れるBitbucketというサービスをみんな使ったりしてました。近年はGitHubでも無料で作れるようになりました。 また、自分のサーバでGitホストの仕組みを運営できるGitLabというものもあります。これは上記の田浦先生が使っているものです。 これらのウェブホストサービスの違いによって、仕組みに違いもあります。例えばこれから「Pull Request」という単語を教えるのですが、 これはGitHubの言い方であり、同様の操作はGitLabでは「Merge Request」です。

注意

  • repl.itを使っている人は、今日の説明すべての「ローカル」とか「My PC」は、repl.itだと思ってください。repl.itもウェブサービスなのでややこしいですが。。。
  • また、どうやらrepl.itと、今日話すgitをコマンドで実行する話は、非常に相性が悪いようです。具体的には、コマンドを実行したはずなのにしなかったようになる(!)ことがあります。 その場合は、大変申し訳ないのですが、対処療法でその都度やり直してみてください。
  • もしrepl.itとローカル環境のどちらも用意している方は、ローカル環境で以下を進めてください。
  • ちなみに、repl.itは独自にGit連携機能があるようです(画面の左側のタブのVersion control)しかし本講義ではrepl.itはあくまでローカルPCの代わりという位置づけなので、repl.itの機能は使わないで解説をすすめます。

リポジトリを作ってclone

それでは早速リポジトリを作ってみましょう。始め方として、(A) GitHubリポジトリを作り、それを手元に持ってくる、 (B) $ git initコマンドで手元のディレクトリをgit管理下におき、それをGitHubに上げる、と二通りあるのですが、 ここでは簡単な(A)でいってみましょう。

  • まず上図の①のように、GitHubのウェブ画面からリポジトリを作ります。これはポチポチクリックして情報を記入するだけです。
  • 次に②のように、GitHubの内容を手元のPCにコピーします。この作業をcloneと言います。このcloneは最初に一回するだけです。

    • cloneの作業は、具体的には次のようにします。ここではsimple_calcというリポジトリを持ってきます。cloneすべき作業ディレクトリの位置に移動したうえで、次のようにgit clone [取得したURL]を実行します
      $ git clone https://github.com/hoge528/simple_calc.git
      
      すると下記のようにユーザ名とパスワードの入力を求められますので入力してください
      Cloning into 'simple_calc'...
      Username for 'https://github.com': hoge528    <- GitHubのユーザ名を打つ
      Password for 'https://hoge528@github.com':    <- GitHubのパスワードを打つ
      remote: Enumerating objects: 3, done.
      remote: Counting objects: 100% (3/3), done.
      remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
      Unpacking objects: 100% (3/3), done.
      
      これでローカルにリポジトリをcloneできました。確認してみましょう
      $ ls         <- 確認
      simple_calc  <- git管理したに置かれたディレクトリが出来てる
      
      $ cd simple_calc
      $ ls -al
      drwxr-xr-x 1 runner runner  26 Nov 10 13:42 .
      drwxr-xr-x 1 runner runner 128 Nov 10 13:41 ..
      drwxr-xr-x 1 runner runner 136 Nov 10 13:42 .git        <- .gitディレクトリ。触らない
      -rw-r--r-- 1 runner runner  13 Nov 10 13:42 README.md   <- 初期ファイルのREADME.md
      
  • そして③のように、手元で好きなようにコードを編集します。ここではmain.cというファイルを足しています。この内容をGitHub側に同期するために、pushを行います

注意

repl.itの場合はcloneした直後にREADMEが無い可能性(!?)があります。その場合は$ git pull origin mainとして、ユーザ名とパスワードを入力してください。

やってみよう(15分)

それでは早速上記をやってみましょう。GitHubにログインしましょう。

上図の緑色の「New」を押して、新しくリポジトリを作ります

上図のような画面になります。適当なリポジトリ名を打ちましょう。ここでは「simple_calc」としています。 今回は公開しないので「private」設定にします。公開する場合は「public」にします。publicにすると、世の中の人全てから見えるようになります。 皆さんは普段GitHubにアクセスして人のリポジトリを色々見ていると思いますが、それらはここでpublicに設定されたリポジトリです。 さらに、初期状態でREADME.mdを含んでくれるようにチェックします。そしてリポジトリを作ります。

さて、これで下記のようなリポジトリページが作られました。 ここが、ソースコードがおかれるマスター情報になります。パソコンが壊れても、ここが残っているので安心です。 また、ブラウザからビジュアルに中身を確認でき、履歴などもクリックを繰り返すことで確認できます。 さて、ここで右上の「Code」をクリックします。するとCloneのために必要なURLが表示されます。 ここでコピーマークをクリックすることで、URLがコピーされます。

あとは、上で述べたように、手元の作業ディレクトリでgit cloneを行ってください。手元でREADME.mdが見れればOKです。

ファイル変更履歴の記録:add, commit

さて、それでは手元のファイルに対して、gitコマンドを色々試してみましょう。 まずは更新の管理のための便利コマンドを紹介します。simple_calcディレクトリに移動したうえで、 git statusを実行してみましょう。これは、「現在のgit管理の状態を見せろ」という命令です。 実行すると以下のようになります。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

これは、「コードは何も編集されていないよ」という意味です。 git statusには副作用はないので、いつでも実行してOKです。

さて、それではコードを編集していきます。 ここでは、新しくmain.cというファイルを作って、そこに適当に書き込んでみましょう。 ディレクトリ内は以下のようになります。

.
├── .git
├── README.md
└── main.c

コラム

main.cを作ったあと、先ほど紹介した、更新管理の便利コマンドを実行してみましょう

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    main.c

nothing added to commit but untracked files present (use "git add" to track)
ここでUntracked files:main.cとなっている(端末上では赤字になっています)のは、 「main.cという新しいファイルがいるが、そいつはgit管理の対象になっていないよ」と言っています。

さて、このmain.cの更新をgit管理の対象にしましょう。 git add 更新を反映したいファイル を実行します。

$ git add main.c
これは、「main.cの更新をgit管理の対象にする」ことを意味します。

コラム

git statusで見てみましょう。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   main.c
上からわかる通り、今度はmain.cChanges to be comittednew fileとなっています。 これはターミナルから見ると、字の色が緑色に変わっているのでわかりやすいです。 これにより、「main.cが新しく作られた。それはgit管理されている」を意味します。

次に、既存のファイルの更新もやってみましょう。README.mdの中身を適当に更新してください。 このとき、repl.it上ではmdファイルは表示モードになっているかもしれません。その時は右上の「Edit」を押してください。 更新したあと、git addでこの変更を反映させます。

$ git add README.md
これで、今行ったREADMEの変更も管理対象になりました。

コラム

git statusで見てみましょう。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   README.md
    new file:   main.c
上からわかる通り、今度はREADME.mdmodifiedになっています。 これは「README.mdは更新されている。それはgit管理されている」を意味します。

次に、更新内容に「更新メッセージ」をつけて、「アップロード準備OK」の状態にします。 以下のコマンドを実行してください。

$ git commit -m "added main.c and updated readme"

ここで、-mオプションは「更新メッセージをつける」という意味です。その後の"added main.c and updated readme"は更新メッセージ (コミットメッセージ)になります。 ここでは、編集した内容を簡単な文章で説明しておきます。この内容は自由です。"updated"のように一言だけではなく、 行った変更をちゃんと説明する文章にしておくと良いです。 これにより、git addしたファイルが「アップロード準備OK」になりました。 以下のようなログが表示されるでしょう。

[main 07c2e59] added main.c and updated readme
 2 files changed, 8 insertions(+), 1 deletion(-)
 create mode 100644 main.c

注意として、メッセージの前後のダブルクオーテーションを忘れないようにしましょう。 つまり、次ではダメです。

# ダブルクオーテーションを忘れているのでダメ
$ git commit -m added main.c and updated readme       

注意

repl.it上では、上記のコマンドを色々やってるうちに、以下のようなメッセージを吐き直前の動作がキャンセルされ(!?)、 さらに自分がいるディレクトリが変更される(!?)ことがよくあります。その場合、同じ作業をやり直してください。

fatal: not a git repository (or any parent up to mount point /home/runner)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
どうやらrepl.itにはコマンド実行を忘れるという特性があるらしく、本講義で扱うような gitコマンド実行と相性が悪いらしいです。。。。

コラム

初めてgit commitを実行した場合は、以下のようなエラーメッセージが出てくると思います。

*** Please tell me who you are.

Run

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'runner@9db19ad573aa.(none)')    

これは、「gitを使うときはメールアドレスと名前を教えろ」と言うことです。なので、 下記のように入力してください。

$ git config --global user.email "hoge528@gmail.com"
$ git config --global user.name "Yusuke Matsui"
ここで"hoge528@gmail.com"と"Yusuke Matsui"の部分には自分のものを入力してください。 そのうえで、git commitをやり直してください。

ちなみにrepl.itの場合、 ここで設定した内容は単なるテキストファイルとして/home/runner/.gitconfigに保存されています(repl.itではないローカル環境の場合、/home/ユーザ名/.gitconfig

コラム

ここでgit statusを行うと、main.cREADME.mdも、アップロード準備OKになったので消えています。

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
ここでYour branch is ahead of 'origin/main' by 1 commit.となっているのは、 現在のディレクトリは、GitHubにある本体のリポジトリに比べ、 1コミットぶんだけ進んでいるという意味です。

ここまでが、ローカル側でのバージョン管理です。次にこれをGitHubのリポジトリにアップロードします

更新のアップロード:push

さて、ここまでで、ローカル側での準備が整いました。この更新をGitHubのリポジトリに反映しましょう。 すなわち、git addおよびgit commitで「アップロード準備OK」にした更新を、git pushのコマンドで実際にGitHub上のリポジトリにアップロードします。

$ git push origin main
ここで、originとは、GitHubウェブサイト上にあるリポジトリそのものを指します。mainとは、通常使っているブランチの名前です。 ここではブランチの説明はしないので、とりあえず全てmainにしてください。 よって、このコマンドは、「手元の更新情報を、origin(ウェブ上のリポジトリ)のmainブランチ(デフォルト)にアップロード(git push)する」という意味です これを実行すると、GitHubのアカウント名とそのパスワードを聞かれるので入力してください。 最終的にログは以下のようになります。

Username for 'https://github.com': hoge528
Password for 'https://hoge528@github.com': 
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 354 bytes | 354.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To https://github.com/hoge528/simple_calc.git
   4cd03fc..2cd5212  main -> main

ここで、GitHubのリポジトリ画面にアクセスしてみると、更新が反映されていることがわかります。これでアップロードが出来ました。 手元でgit statusをしてみると最初と同様のクリーンな結果が返ります。

このgit add, git commit, git pushが更新の1サイクルです。 いくらかコーディングをすすめたら、それらをaddし、まとめてcommitしてメッセージをつけ、pushでアップロードします。 これを、少なくともと一日の最後には必ず行うようにしましょう。

問題が発生したら:

  • エラーメッセージをググって調べてみましょう
  • 調べてわからなかったら、誰か詳しい人に聞いてみましょう。
  • どうしてもおかしいことになってしまったら、編集中のファイル(今回でいうとmain.cREADME.md)を一旦どこかに退避させて、ディレクトリを完全に削除したうえで、cloneからやり直しましょう。そして、退避させたmain.cを置きなおせばいいです。

コラム

実はつい最近までgitのデフォルトのブランチ名はmasterでした。なので、$ git push origin masterとしていました。 近年のBLM運動により、masterという名称は相応しくないという結論になり、デフォルトブランチ名がmasterからmainに変更されたのです。今新しくリポジトリを作ると全てデフォルトでmainが使われているので、これからはmainで大丈夫です。一方、過去のコードに触る場合は、デフォルトブランチ名がmasterであるほうが今はまだ一般的なので、注意してください。

更新のダウンロード: pull

さて、上記とは逆に、「リポジトリ上での更新を手元にもってくる」というコマンドがgit pullです。 手元は変わらずにGitHubリポジトリのみが変更されることってあるのか?と思うでしょう。 今回のワークフローではそれは発生しないです。ですがそれでも、 複数台のPCから同じリポジトリを編集しているときなど、GitHubリポジトリ側が手元よりも進むことはありえます。 今回はそれをシミュレートします。

GitHubリポジトリのブラウザの画面からREADME.mdをクリックし、右側の鉛筆アイコン(Edit this file) をクリックし、README.mdの中身を適当に編集してみましょう。 みなさんはこれまで宿題のアップロードでこの方法をやってきたので、簡単にできますね。

これにより、GitHubリポジトリのほうが新しくなりました。この情報を手元にもってくるには、次のコマンドを実行します

$ git pull origin main

結果は次のようになります。ユーザ名とパスを打ってください。

Username for 'https://github.com': hoge528
Password for 'https://hoge528@github.com': 
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/hoge528/simple_calc
 * branch            main       -> FETCH_HEAD
   2cd5212..9ea0035  main       -> origin/main
Updating 2cd5212..9ea0035
Fast-forward
 README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
これによって、GitHubリポジトリ側での変更が手元に反映されたことがわかります。 この作業も何度も行うことができます。 このように新しい更新を反映することを忘れないために、コーディングを始めるときは、まずgit pullしてからスタートすることをオススメします。

変更履歴の確認:log

さて、手元において、ファイルの変更履歴を確認するには次のコマンドを実行します。

$ git log

結果は例えば次のようになります。

commit 9ea0035492bf3b718a7b41f219a5dbea8c60331c (HEAD -> 
main, origin/main, origin/HEAD)
Author: hoge528 <70310899+hoge528@users.noreply.github.com>
Date:   Wed Nov 11 00:04:23 2020 +0900

    Update README.md

commit 2cd5212f9c9cf80814151a1f4ce6c194868c4195
Author: Yusuke Matsui <hoge528@gmail.com>
Date:   Tue Nov 10 14:56:39 2020 +0000

    added main.c and updated readme

commit 4cd03fc8923dc02a4ca1f83665223d3d419f6adf
Author: hoge528 <70310899+hoge528@users.noreply.github.com>
Date:   Tue Nov 10 13:56:37 2020 +0900

    Initial commit
これは、これまでに行ったコミットが表示されています。このように、gitを使うと過去を振り返ることができます。

コラム

履歴の確認はブラウザ上からリポジトリを見ても確認することができます。 上のように「X commits」となっているところをクリックします すると上のようにこれまでのコミット一覧が表示されます。これらをクリックすると、さらに詳しい履歴を確認できます。

使い方まとめ

さて、ここまでの使い方をまとめておきます。 なんらかのコーディングをしたいとします。

  • 初期化:
    • GitHub上でリポジトリを作り、それをcloneで手元に持ってくる
  • コーディング:以下を繰り返す
    • コーディングを開始するときに、まずpullでGitHubリポジトリの更新を取得
    • コーディングする
    • 区切りがいいところになったら、add, commit, pushで更新をGitHubリポジトリに反映
  • おかしなことが発生したら:
    • 全部消して、cloneからやり直す
  • 履歴を確認したい:
    • ウェブ上からGitHubリポジトリを見て確認
  • もっと複雑な操作をしたい:
    • ググるか詳しい人に聞く。おかしなことが発生したら全部消してcloneからやり直す

コラム

ここまではGitの操作をgitコマンドてCLIから行いました。一方で、GUIソフトウェアをインストールして、それらのコマンド操作をGUIで出来るようにする仕組みもあります。 そのようなソフトをGitクライアントといいます。Gitクライアントを使うと、addcommitといったgit操作をポチポチクリックするだけで出来ます。 有名どころの一覧はこんな感じです。 SourceTreeなどは昔ながらの有名どころです。GitHub公式のGitHub Desktopもあります。 個人的にはGitKrakenがビューア機能が豊富で好きです。

ですが、実は最近はvscodeに標準でgitクライアントが入っているので、それで十分かもしれません。下は、古いファイルと新しいファイルの差分を可視化している例です。 このような可視化はGitHubにアクセスしてブラウザを見ればできるのですが、クライアントをインストールしておけばローカルでも見れます。

gitコマンドを直接使うかクライアントを使うかは意見が分かれるところです。松井自身は、gitコマンドは直接叩き、必要に応じてビューアとしてクライアントを使っています。

また、「CLIの」クライアントというものもありそれをtigといいます。こちらはGUIが使えないサーバ上などでもクライアント相当のことができるので、 根強い人気があったりします。 tigの日本語解説サイト およびtigの公式

また、上記とは別に、GitHubにアクセスしなければ実行できない様々な操作(例えばissueを読むとか)は、 実はブラウザを経由しなくても、GitHub公式のCLIツールであるGitHub CLIで行うこともできます。 余談ですが、あとから紹介する「他人から送られてきたpull requestの確認をローカルで行う」ときに、GitHub CLIがあると、簡単コマンドで実行できるので便利です。

やってみよう(20分)

それでは上記のadd, commit, push, pull, logをやってみましょう。

無視するファイル:.gitignore

gitを使う上で重要な点として、gitが管理するのはあくまでテキストファイル(ソースコード等)であり、バイナリファイル(a.out、画像など)は扱うべきではないということです。 gitはテキストの行レベルで更新を検知します。 例えばjpg画像データはほんのちょっと画像が変わっただけでも内部表現は全く変わるため、 gitで変更を追跡するとものすごい量になります。 よって、バイナリファイルや、使い捨てるファイル(毎回値が変わるログファイルとか)、巨大すぎるファイル、などはgitで管理しないほうがいいです。

そういったファイルを追加してしまうと具体的にどうなるかというと、.gitフォルダのサイズが肥大化します。 これによりgit操作が遅くなったり、エラーになったりします。 個人プロジェクトで、この.gitが数百MBぐらいになってしまう場合は、すべきでないファイルを管理してる可能性が高いです。

管理しないためには、git addしなければいいだけです。しかし、それではgit statusするたびに 毎回それらの管理対象でないファイルが表示されてしまい不便です。また、いつか間違ってaddしてしまうかもしれません。 それを防ぐため、.gitignoreというファイルが使われます。

これは中身はただのテキストファイルです。 この中に、git管理したくないファイルのファイル名を書いておきます。 そうすると、gitはそれらを無視してくれるようになり、丸く収まります。 ちなみに、言語ごとの.gitignoreの雛形は公開されているので、これをまずコピペしてくればいいです。 このなかには、言語固有の、典型的な「無視すべきファイル」が最初からまとめられています。注意として、.gitignoreそのものはちゃんとaddして追加してください。

コラム

じゃあ巨大なバイナリファイルのバックアップはどうすればいいのか?という疑問がわきます。実はこれに対する明確な答えは無いのです。 以下のような選択肢が考えられます

  • DropboxやOneDriveなどの自動同期システムで管理
  • ラボサーバや、AWS S3や、Google Cloud Storage (GCS)といったリモートストレージを用意してそこでバックアップ。必要に応じてそれらに直接アクセスしたり、ダウンロードしてくる
  • Git Large File Storage (LFS)といった特殊な仕組みを利用

個人レベルで現実的な回答はDropbox / OneDriveでしょう。研究を始めると、ラボサーバやS3など、ネットワーク越しに巨大データを置いたりします。 また、GitにはLFSという巨大ファイルを扱う仕組みがあるのですが、これはクセが強いので慣れるまでオススメしません。

ところで、Dropboxを使うならソースコードもDropboxで十分にバックアップとれるんじゃないの?と思うかもしれませんが、それは正しくないです。 まず、DropboxではGitのように厳密な変更履歴を保持できません。 そして、Dropboxのような自動同期システムは、「あるときにうっかり自分でも気付かずにファイルを消してしまった」という状況に弱いです。 なのでGitの代わりにはなりません。Dropboxを使う場合は、Dropbox管理したうえでさらにGitでも管理すると堅牢です。

バージョン管理はバックアップ

さて、長々と説明してきましたが、バージョン管理が実用上強力なバックアップであることを示します。 上の図にあるように、手元のPCは卒論提出直前に高い確率で壊れます。ここでソースコードをGit/GitHubでしっかりバージョン管理をしていたなら、 別PCにcloneすることですぐに研究を再開できます。 バージョン管理を知っている人間からすると、バージョン管理をしないでプログラミングすることは、シートベルトをしないで運転しているように見えます。

この場面ではDropboxも有効な手段ですが、Gitであればより完璧な履歴が保持できます(例えばDropboxだとうっかり容量制限に達していて記録できていなかった、なんてこともあり得ます) また、普段から、新しい環境でcloneしたときにちゃんと動くようにリポジトリ内容を整備しておくクセをつけておくといいです (作業手順や依存ライブラリをREADME.mdに書くなど)

ちなみに、右下の緑の部分で示してしているように、公開リポジトリであれば誰でもclonepullが出来ます。 誰かがリポジトリにpushをすることも、リポジトリ主がその人に許可を与えれば可能です。

コラム

ちなみに、これまでのGitHub Classroomによる宿題提出は何をしていたのでしょうか? このように、一人一人のためのリポジトリを自動で作り、みなさんがそこを編集していたのでした。 repl.itと相性が悪いということがあってブラウザ上からコードをコピペしてもらってきましたが、それはやむを得ない使い方でした。 本来は、上図のkomabahanakoさんのように、pull/pushで編集するのがキチンとしたやり方です。

他人リポに貢献:forkとpull request

さて、次の話題はForkPull requestに関するものです。 これはアディショナルな内容ですので、参考までの紹介です。 皆さんはこれまでにフォークだとかプルリクだとかいう単語を聞いたことがあるかもしれません。 これらの概念は、他人のリポジトリを更新したいときに使います。 例:他人がGitHubで公開しているプログラムにバグをみつけた。バグを修正するパッチを書いたので、それを本体に反映してほしい。

ここでは、hoge528さんがsimle_calcリポジトリを公開しているとします。

  • taro123さんが、このリポジトリを更新したいと考えました。そこでまず、hoge528/simple_calcをforkします。 これは、対象のリポジトリのコピーを自分のGitHubアカウント下に作ることです。ここではtaro123/simple_calcになります。
  • forkしたリポジトリは自分のものなので、従来通りローカルにcloneし、add/commit/push/pullて編集を繰り返します。
  • そしてtaro123/simple_calcに十分な変更が加わったら、その変更を本体に反映してくれませんか?という意味のPull requestを送ります。
  • hoge528は送られてきたpull requestを見て、本体に反映するか、再編集を依頼するか、したりします。

このように、ForkとPull request (PR)を使うことで、安全かつ分離した形で他人のコードの修正をすることができます。 Forkのポイントは、人のリポジトリのコピーを自分の領域にもってくることで、そのリポ(のコピー)を自由に編集できることです。 AさんがBさんのリポジトリを編集したいときに、

  • 従来通りである「Bさんの許可を得てそのリポジトリへのアクセス権限をもらって、編集」方式
  • fork/PRを使う「勝手にフォークして勝手に編集。完成した変更部分だけをBさんに送って、取り入れるかどうか決めてもらう」方式

を考えると、後者のほうが開発のサイクルが早いですし、より多くの人が開発に簡単に参加できます。 この機能により、GitHubのオープンソースの世界では、人々が協力して同じコードを編集することができるのです。

ところで、今日は自分のバックアップについてのみ話す、と言っていたのになぜfork/PRの話をしたのでしょうか? というのは、このforkという機能は、自分の書いたコードを多くの人に配るという目的にも適しています。 なので、将来的に、講義の課題などでfork機能を使うことがあるかもしれません(宿題リポジトリをフォークして更新してね、など) そのときに驚かないために、載せておきました。

注意として、「AさんがBのリポジトリをフォークした」とう情報はBリポを見ればすぐにわかります(誰がフォークしたか、という欄があります) なので、フォークやPRは、基本的に公開の世界で情報をやり取りするものだと思っておいてください (プライベートforkというのもできるようですが、advancedなようです)

やってみよう(20分)

それでは実際にfork/PRの練習をしてみましょう。以下の注意があります。

  • 以下の作業を行うとGitHubのアカウント名が外部に公開され、ソフト1を取ってることもわかってしまいます。なのでそれが嫌な場合は以下の作業は実行しないでも大丈夫です。
  • フォークによって作ったリポジトリは、作業が終わったあとは消しても大丈夫です。
  • 以下では、mainブランチでfork/PRをしています。普通はブランチを新たに切ると思いますが、ここはあくまで練習です。

さて、それでは、松井が準備した練習リポジトリであるyo2020を開きましょう。ここでのタスクは、このREADMEに何か書き込むことです ここで右上の「Fork」を押します。こうすることで、松井のリポジトリをみなさんのGitHub領域にコピーします。

さて、自分のGitHub画面に行ってましょう。GitHub画面の右上のアイコンマークを押し「Your Profile」を押すと下の画面に行けます。ここで「Rpositories」タブをクリックしてください。 ここで、自分のリポジトリとしてyo2020が増えていることを確認しましょう。これをクリックしてみましょう

これを見てみると、松井(matsui528)のリポジトリであるmatsui528/yo2020が、皆さん側の領域(この場合はhoge528/yo2020)にforkされていることがわかります。

これのREADME.mdを自由に編集してください。感想兼アンケートになっています。これはGitHubウェブ上でブラウザ上からやってもいいですし、 今日習ったやり方を使ってpull/add/commit/pushしてもいいです。

さて、その編集を行うと、画面は次のようになります。

これで編集が出来ました。あとはこの情報を元のmatsui528のGitHubリジトリに反映させたいです。 そのために、Pull requestを使います。ここで、上のように、「Pull request」ボタンを押してみてください。

上のような画面になります。ここでは変更履歴の確認が出来ます。「Create pull request」を押してPR作成画面に移りましょう。

すると上のPRを作る画面になります。必要に応じてタイトルや説明文を書きましょう。そして「Create pull request」を押してPRを作ります。

そして、松井の本体のほうのリポジトリ(matsui528/yo2020)を見てみましょう。 上の図のように、「Pull request」タブを押すと、先ほど作ったPRが表示されます。

この内容を、リポジトリの持ち主である松井(matsui528)が確認します。この変更を取り込んでもいいし、 コードの更なる編集を指示することもできます。無事マージされると、次のようになります。