2021 年度 OSS リテラシー 3 : Git, GitHub 入門 (1)

インストール

まず始めに, git がインストールされているか確認する. dpkg -l コマンドでインストールされているパッケージを一覧することができる. インストールされている場合は, 以下のように表示される. 先頭に ii がついているが, これはインストールされていることを意味する.

$ dpkg -l | grep git

  ... (略) ... 
  ii  git                           1:2.30.2-1                            amd64        fast, scalable, distributed revision control system
  ... (略) ... 

インストールされていない場合は, apt-get でインストールする.

$ sudo apt-get update
  ... (略) ... 

$ sudo apt-get install git
  ... (略) ... 

また, 以下のようにコマンドを打つと git のバージョンが確認できる.

$ git --version

  git version 2.30.2

git の初期設定を行う

$ git config --global user.name "名前"  (自分の名前をローマ字で書くこと)

$ git config --global user.email "メールアドレス" (自分のメールアドレス)

これにより, ~/.gitconfig に設定が記録される.

$ cat ~/.gitconfig 

  [user]
       name = sugiyama
       email = sugiyama@matsue-ct.jp

git の基本的な使い方 (ローカルリポジトリ)

git init

まず, リポジトリの初期化を行う. ディレクトリを作成した上で, git init を実行する.

$ cd   (ホームディレクトリに移動)

$ mkdir git-tutorial

$ cd git-tutorial

$ git init

  Initialized empty Git repository in /home/sugiyama/git-tutorial/.git/

git status

作成したリポジトリの状態を確認するには git status を実行する. 現在は "master" ブランチにいることが表示される. また, 最終行に "nothing to commit" と書かれているように, コミット (commit) すべきファイルが存在しないことも示されている.

$ git status

  On branch master    <-- 現在は master ブランチであることを示す

  Initial commit

  nothing to commit (create/copy files and use "git add" to track)

コミットが無いということは, 現時点では作成したリポジトリ (git-tutorial) には 何のファイルの何の状態も記録されていないということを意味する. 最初のコミットをするために管理の対象になる README.md ファイルを作成する. 管理対象となるファイルをワークツリー (git-tutorial ディレクトリ) 内に作成すれば良い.

$ touch README.md   (中身は空なファイルを作る)

$ git status

  On branch master

  Initial commit

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

      README.md

  nothing added to commit but untracked files present (use "git add" to track)

"Untracked files" に作成した README.md ファイルが表示されていることが確認できる. このように Git のワークツリーやリポジトリに対して何らかの操作を行うと git status コマンドでの表示が変化する.

git add

Git リポジトリのワーキングツリーでファイルを作成しただけでは, Git リポジトリのバージョン管理の対象としてファイルは登録されていない. そのため, README.md ファイルは git status コマンドの表示で "Untracked files" に表示されていた.

そこでファイルを Git リポジトリの管理対象とするために git add コマンドを実行して, ステージ領域と呼ばれる場所にファイルを登録する. ステージ領域とはコミットをする前の一時領域である.

$ git add README.md

$ git status

  On branch master

  Initial commit

  Changes to be committed:
    (use "git rm --cached <file>..." to unstage)

  new file:   README.md

README.md ファイルをステージ領域に登録したことにより, git status コマンドの 表示が変化した. "Changes to be committed" に README.md ファイルが表示されていることが確認できる.

git commit

git commit コマンドは, ステージ領域に登録されている時点のファイル群を 実際にリポジトリの歴史として記録するコマンドである. この記録を元にファイルをワーキングツリーに復元することが可能となる.

早速, git commit を実行する. "-m" オプションの値 "first commit" は コミットメッセージと呼ばれるもので, 後述する git log で参照できる. コミットメッセージにはコミットに関する要約を書くべきものである.

$ git commit -m "first commit"

  [master (root-commit) 1bab8be] first commit
   1 file changed, 0 insertions(+), 0 deletions(-)
   create mode 100644 README.md

詳細なコミットメッセージを残す場合は -m オプションを付けずに git commit を実行する. まず, コミットした時に立ち上がるエディターを vi に指定する.

$ export EDITOR=vi

先に commit したので, 実行すると "nothing to commit" と言われる. そのため, README.md ファイルを修正する.

$ git commit

  On branch master
  nothing to commit, working tree clean

$ vi README.md

  Git tutorial    (ファイル名に何らかの文字列を書き込む)

改めてコミットする. そのまま git commit するとステージ領域に書き出していないと 文句を言われる. git add してから git commit する.

$ git commit

  On branch master
  Changes not staged for commit:
  modified:   README.md

  no changes added to commit   (変更が add されていないことを示す)

$ git add README.md 

$ git commit

  [master cb505d7] 2017-11-20 README.md is modified by sugiyama
   1 file changed, 1 insertion(+)

git commit を実行すると, エディター (vi) が起動する. コミットメッセージを記述する. コミットメッセージの書式は開発者同士で取り決めておくと良い. 以下の例では変更内容の要約を 1 行で記述している. また, コミットメッセージを何も書かないと commit されないので注意すること.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#       modified:   README.md
#

2018-11-03 modified by sugiyama

コミット後の状態を確認するには再度 git status を実行する.

$ git status

  On branch master
  nothing to commit, working tree clean

git log

git log コマンドはリポジトリにコミットされたログを確認できるコマンドである. 誰がいつコミットやマージをして, どのような差分が発生したかなどを確認できる. 先ほどの git commit コマンドが実際に記録されていることを確認してみる.

$ git log

  commit 2595cb1a2054d1ba71f2b08298c8e358031dd3fe (HEAD -> master)
  Author: SUGIYAMA Ko-ichiro <sugiyama@XXXX.jp>
  Date:   Mon Oct 25 12:32:35 2021 +0900

      2021-10-25 SUGIYAMA

  commit 6945552b6668022fc923b49d2da2e45c4552fcff
  Author: SUGIYAMA Ko-ichiro <sugiyama@XXXX.jp>
  Date:   Mon Oct 25 12:31:35 2021 +0900

      first commit

コミット欄の隣に書かれている "2595cb1a2054d1ba71f2b08298c8e358031dd3fe" が, このコミットを指し示すハッシュである. Author 欄には Git で設定されているユーザ名とメールアドレスが記録される.

git に登録されたファイルが複数になった場合, 特定のファイルのログだけ 確認したくなることがある. その場合はファイル名を指定する.

$ git log README.md 

  ... (略) ...

コミットで行われた差分も確認する場合には -p をつけると, コミットメッセージの後ろにファイルの差分が表示される.

$ git log -p

  commit 2595cb1a2054d1ba71f2b08298c8e358031dd3fe (HEAD -> master)
  Author: SUGIYAMA Ko-ichiro <sugiyama@gfd-dennou.org>
  Date:   Mon Oct 25 12:32:35 2021 +0900

      2021-10-25 SUGIYAMA

  diff --git a/README.md b/README.md
  index e69de29..b399233 100644
  --- a/README.md
  +++ b/README.md
  @@ -0,0 +1 @@
  +Git tutorial     <-- 先頭に + がついているのが追加した行

  ... (略) ...

git diff

git diff は, ワークツリー・ステージ領域・最新コミット間の差分を 確認するために利用するコマンドである.

改めて README.md に書き込みをした後に git diff を実行することで 現在のワークツリーとステージ領域の差分を確認できる.

$ vi README.md

  It's Git tutorial  (末尾に追加)

$ git diff

  diff --git a/README.md b/README.md
  index b399233..7a4de2a 100644
  --- a/README.md
  +++ b/README.md
  @@ -1 +1,2 @@
   Git tutorial
  +It's Git tutorial    <-- 先頭に + がついているのが追加した行

ステージ領域に変更を add する. この状態で git diff コマンドを実行すると ワークツリーとステージ領域の状態と差分が無いために何も表示されない. 最新コミットとの差分を確認するためには HEAD をつけて git diff を 実行する.

$ git add README.md

$ git diff

$ git diff HEAD

  diff --git a/README.md b/README.md
  index b399233..7a4de2a 100644
  --- a/README.md
  +++ b/README.md
  @@ -1 +1,2 @@
   Git tutorial
  +It's Git tutorial

git commit を実行する前に git diff HEAD を実行して, 今回のコミットが 前回のコミットからどのような差分があるのか確認をする癖をつけると良いだろう. 確認後に git commit する.

$ git commit -m "Add index"

  [master 383b15a] Add index
   1 file changed, 1 insertion(+)

コミットの確認のために git log を実行する.

$ git log

  ... (略) ... 

過去のコミットとの比較する場合は log の commit 欄のハッシュを用いる. 以下の例では log を確認し, 初期状態との比較を行う. "+" が付いている行が変更された部分である.

$ git log

  ... (中略) ...

  commit 6945552b6668022fc923b49d2da2e45c4552fcff       <-- ここのハッシュを使う (各自の環境に合わせること)
  Author: SUGIYAMA Ko-ichiro <sugiyama@gfd-dennou.org>
  Date:   Mon Oct 25 12:31:35 2021 +0900

      first commit

$ git diff 6945552b6668022fc923b49d2da2e45c4552fcff

  diff --git a/README.md b/README.md
  index e69de29..7a4de2a 100644
  --- a/README.md
  +++ b/README.md
  @@ -0,0 +1,2 @@
  +Git tutorial
  +It's Git tutorial

git rm

ファイルの削除の練習をしてみる. 一度ファイルを作成し, それを削除する. ファイルを追加する場合に add => commit の順で行ったのと同様に, ファイルを削除する場合は rm => commit する. git rm した段階で ワーキングツリー内から該当ファイルは削除されるが, commit しないと最終的に反映されない.

まず始めに, テストファイルを用意する.

$ touch test.txt  (ファイル作成)

$ ls 

  README.md  test.txt

$ git status  (status の確認)

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

       test.txt

  nothing added to commit but untracked files present (use "git add" to track)

$ git add test.txt (ステージ領域にファイルを追加)

$ git commit -m "add test.txt"  (コミット)

  [master 1a061f5] add test.txt
   1 file changed, 0 insertions(+), 0 deletions(-)
   create mode 100644 test.txt

次にテストファイルを削除する.

$ git rm test.txt

  rm 'test.txt'

$ ls  (ファイルが消えることが確認できる) 

  README.md

$ git status 

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

       deleted:    test.txt

$ git commit -m "remove test.txt"   (git リポジトリに反映)

  [master 322bfbf] remove test.txt
   1 file changed, 0 insertions(+), 0 deletions(-)
   delete mode 100644 test.txt

ブランチの操作

ブランチを活用することで複数の開発者が効率的に同時並行で開発を行うことができる. 開発者は Git のデフォルトのブランチである master ブランチから新たにブランチを作成し, その新規作成したブランチで開発作業を行う. 開発者が複数いる場合はブランチも複数存在することになる. 開発者は開発作業を一区切りさせたら, それぞれのブランチを master ブランチにマージする.

git branch

git branch は, ブランチ名の一覧を表示するとともに, 現在のブランチを 確認するためのコマンドである.

$ git branch

  * master

出力の中で, "*" (アスタリスク) の付いているのが現在のブランチである. 上記出力は, 現在は master ブランチにいること, ブランチは master のみ であることを意味する.

git checkout -b

feature-A ブランチの切り替えとコミット

現在の master ブランチから新しいブランチを作成するには, git checkout -b コマンドを用いる.

feature-A ブランチを作るには, 以下のようにコマンドを実行する.

$ git checkout -b feature-A

  Switched to a new branch 'feature-A'

このコマンドは, ブランチを作成し, 現在のブランチを feature-A に 移動するという 2 つの操作を同時に行うものである. 再び git branch コマンドを 実行すると, 現在は feature-A ブランチにいることがわかる.

$ git branch

  * feature-A
  master

それでは先に作成した README.md に 1 行追加してみる.

$ vi README.md

  test (feature-A)    [最終行に追加]

この変更をコミットする.

$ git status

  On branch feature-A          (<-- ここにブランチが feature-A であることが書かれている)
  Changes to be committed:
   (use "git reset HEAD <file>..." to unstage)

  modified:   README.md

$ git add README.md 

$ git commit -m "Add feature-A"

  [feature-A 8d8cf00] Add feature-A
   1 files changed, 1 insertion(+)

現在の状況を絵に表すと以下のようになる. 赤が master ブランチ, オレンジが 今作成した feature-A ブランチである. README.md を編集してコミットしたので, feature-A ブランチが master ブランチから分岐した.

master ブランチへの切り替え

次に, feature-A ブランチの変更が master ブランチに「影響しない」ことを 確認する. ブランチの切り替えは git checkout コマンドを用いる. 切り替えるだけなら -b オプションは必要ない.

$ git checkout master

先ほど feature-A ブランチで変更を加えた README.md を表示してみる. cat コマンドで確認すると, 先ほど加えた test (feature-A) が存在しないことがわかる.

$ cat README.md 

  Git tutorial
  It's Git tutorial
                        <-- 先ほど加えた "test (feature-A)" がない.
1 つ前のブランチへ切り替える

1 つ前のブランチ (feature-A) に戻す. 1 つ前のブランチに戻すには引数に "-" をつければよい. 表示されるメッセージから feature-A に切り替わったことがわかる. もちろん, "-" の代わりに feature-A と書いても良い.

$ git checkout -

  Switched to branch 'feature-A'

$ cat README.md 

  Git tutorial
  Git tutorial desu
  test (feature-A)

git merge

feature-A で行った作業 (例えば, バグ修正) が終わったあとは, feature-A の変更を master ブランチに統合する. まずは統合先のブランチ (master) へ移動する.

$ git checkout master

  Switched to branch 'master'


$ git status

  On branch master             (<-- master ブランチであることがわかる)
  nothing to commit, working tree clean

git merge コマンドを実行する. git merge コマンドを実行するとエディターが 起動する. エディターに vi を使いたい場合は予め環境変数 EDIOR を vi にしておくこと.

$ export EDITOR=vi

$ git merge --no-ff feature-A

  Merge made by the 'recursive' strategy.
  README.md | 1 +
  1 files changed, 1 insertion(+)

README.md ファイルの中を確認すると, feature-A ブランチに加えた変更が master ブランチに反映されていることがわかる.

$ cat README.md 

  Git tutorial
  It's Git tutorial
  test (feature-A)    <-- この行が増えた. 

現在の状況を図に表すと以下のようになる. feature-A ブランチで行った変更が master ブランチに取り込まれた.

git log --graph

git log --graph コマンドを用いると, feature-A ブランチが分岐し 統合されたことをグラフィカルに表示することができる.

$ git log --graph

  *   commit dc415cb8fa8bcb58cef82270cf885364f127ce9f
  |\  Merge: fe38f9a 8d8cf00
  | | Author: sugiyama <sugiyama@matsue-ct.jp>
  | | Date:   Sat Nov 25 06:03:35 2017 +0900
  | | 
  | |     Merge branch 'feature-A'
  | | 
  | * commit 8d8cf007d422a92f4616648c3583fd8c01f0176f
  |/  Author: sugiyama <sugiyama@matsue-ct.jp>
  |   Date:   Sat Nov 25 05:44:56 2017 +0900
  |   
  |       Add feature-A
  | 
  * commit 322bfbf4f15e9e75fe1a8f6c6ebdb3d9590c79c0
  | Author: sugiyama <sugiyama@matsue-ct.jp>
  | Date:   Tue Nov 21 02:37:07 2017 +0900
  | 
  |     remove test.txt
  | 

  .....(以下略).....

コミットを変更する操作

git reset

バージョン管理のありがたいところは, 過去の情報に簡単に戻れることである. ここでは feature-A ブランチを分岐する前に戻って fix-B というブランチを作成する.

リポジトリの HEAD, ステージ, 現在のワーキングツリーを指定した状態まで 戻すには, git reset --hard コマンドを用いる. 引数に戻りたい場所のハッシュを 与えることで, そのときの状態を完全に復元することができる.

まずは戻す場所のハッシュを確認する. ブランチ分岐の前の commit の 横の文字列がハッシュである.

$ git log --graph

  ...(中略)....

  | | 
  | * commit 935fb5ea065a779ae954d8942ad3962a47790296 (feature-A)
  |/  Author: SUGIYAMA Ko-ichiro <sugiyama@gfd-dennou.org>
  |   Date:   Mon Oct 25 12:58:32 2021 +0900
  |   
  |       Add feature-A
  | 
  * commit db754a4ef09a6008b94253d2d37e7d29f8d371bf      <-- 分岐前のコミットのハッシュをコピー
  | Author: SUGIYAMA Ko-ichiro <sugiyama@gfd-dennou.org>
  | Date:   Mon Oct 25 12:48:24 2021 +0900
  | 
  |     remove test.txt

  ...(以下, 略)....

上記で得たハッシュを用いて git reset する.

$ git reset --hard db754a4ef09a6008b94253d2d37e7d29f8d371bf

  HEAD is now at db754a4 remove test.txt

リセットした後に README.md を確認すると, 最終行が消えていることがわかる.

$ cat README.md 

  Git tutorial
  It's Git tutorial
                        <-- 先ほど加えた "test (feature-A)" がない.

ここで fix-B ブランチを作成する. README.md ファイルを編集したあと, add と commit を行う.

$ git checkout -b fix-B

  Switched to a new branch 'fix-B'

$ vi README.md

  test (fix-B)    [末尾に追加] 

$ git add README.md 

$ git commit -m "ADD fix-B"

  [fix-B 1ba6722] ADD fix-B
   1 file changed, 1 insertion(+)

また, git log コマンドを実行すると, master ブランチからの続きとして "fix-B" の変更が存在することがわかる.

$ git log --graph

  * commit ed30636b35054a87ef8d3febd1078ea8e5eef2c1 (HEAD -> fix-B)
  | Author: SUGIYAMA Ko-ichiro <sugiyama@gfd-dennou.org>
  | Date:   Mon Oct 25 13:59:42 2021 +0900
  | 
  |     ADD fix-B
  | 
  * commit db754a4ef09a6008b94253d2d37e7d29f8d371bf (master)
  | Author: SUGIYAMA Ko-ichiro <sugiyama@gfd-dennou.org>
  | Date:   Mon Oct 25 12:48:24 2021 +0900
  | 
  |     remove test.txt
  | 
  ... (略)...

現在の状況を図に表すと以下のようになる. feature-A を作ったところまで 戻ってから fix-B を作ったので, feature-A と fix-B の分岐点は同じである.

feature-A ブランチをマージしたあとの状態に進む

git reflog を実行すると, このレポジトリで行われた操作が全て確認できる. git reflog の出力を見ると, feature-A ブランチの作成とマージ, fix-B ブランチの 作成などを確認することができる.

$ git reflog

  ed30636 (HEAD -> fix-B) HEAD@{0}: commit: ADD fix-B
  db754a4 (master) HEAD@{1}: checkout: moving from master to fix-B
  db754a4 (master) HEAD@{2}: reset: moving to db754a4ef09a6008b94253d2d37e7d29f8d371bf
  fa4199f HEAD@{3}: merge feature-A: Merge made by the 'recursive' strategy.            <-- ここが feature-A を master にマージしたところ
  db754a4 (master) HEAD@{4}: checkout: moving from feature-A to master
  935fb5e (feature-A) HEAD@{5}: checkout: moving from master to feature-A
  db754a4 (master) HEAD@{6}: checkout: moving from feature-A to master
  935fb5e (feature-A) HEAD@{7}: commit: Add feature-A
  db754a4 (master) HEAD@{8}: checkout: moving from master to feature-A
  db754a4 (master) HEAD@{9}: commit: remove test.txt
  29dd09a HEAD@{10}: commit: add test.txt
  383b15a HEAD@{11}: commit: Add index
  2595cb1 HEAD@{12}: commit: 2021-10-25 SUGIYAMA
  6945552 HEAD@{13}: commit (initial): first commit

feature-A ブランチをマージした後の状態は 4 行目の "merge feature-A: ..." であるので, そこに戻すには上記の例ではハッシュ fa4199f を使えば良い (ハッシュ値は各自の環境に合わせること). git log で 出力されるハッシュでも, git reflog で出力されるハッシュでも, どちらも使うことができる.

$ git status

  On branch fix-B
  nothing to commit, working tree clean

$ git checkout master

  Switched to branch 'master'

$ git reset --hard fa4199f

  HEAD is now at fa4199f Merge branch 'feature-A'
fix-B の修正を master に反映

現時点の master ブランチは feature-A のマージを行った直後の状態になっているので, README.md は以下のようになっている.

$ cat README.md 

  Git tutorial
  It's Git tutorial
  test (feature-A)

一方で, 先に作成した fix-B ブランチの README.md は以下のようになっている.

$ git checkout fix-B

  Switched to branch 'fix-B'

$ cat README.md 

  Git tutorial
  It's Git tutorial
  test (fix-B)

fix-B ブランチの README.md は過去の master ブランチの README.md を 元にしているので, master ブランチの最新の README.md の内容と食い違いが生じている. fix-B を feature-A にマージすると何がおこるだろうか?

$ git checkout master

$ git merge --no-ff fix-B

  Auto-merging README.md
  CONFLICT (content): Merge conflict in README.md
  ^^^^^^^^^
  Automatic merge failed; fix conflicts and then commit the result.
  ^^^^^^^^^^^^^^^^^^^^^^^            

README.md ファイルに食い違いがあるので, コンフリクトが生じ, マージが失敗する. コンフリクト (CONFICT) は競合や衝突という意味である. git status を実行すると マージに失敗していることが表示される. メッセージには, コンフリクトを手動で修正して git commit を行うか, マージを中断する場合は git merge --abort を行うことが書かれている.

$ git status

  On branch master
  You have unmerged paths.
    (fix conflicts and run "git commit")           <--- ここ!
    (use "git merge --abort" to abort the merge)   <--- ここ!

  Unmerged paths:
    (use "git add <file>..." to mark resolution)

       both modified:   README.md

  no changes added to commit (use "git add" and/or "git commit -a")

今回はコンフリクトを手動で修正してコミットすることにする. コンフリクトの生じたファイルを開くと以下のように衝突した部分が 表示される.

$ cat README.md 

  Git tutorial
  It's Git tutorial
  <<<<<<< HEAD
  test (feature-A)
  =======
  test (fix-B)
  >>>>>>> fix-B

今回は衝突した部分を両方共に生かすことにする. vi でファイルを開いて 以下のように修正する.

$ vi README.md 

  Git tutorial
  It's Git tutorial
  test (feature-A)
  test (fix-B)

修正した結果をコミットする.

$ git add README.md 

$ git commit -m "fix conflict"

  [master 2bd4cd4] fix conflict

コンフリクトが解消されているので, 問題なくコミットされる.

現在の状況を図に表すと以下のようになる. feature-A を master にマージした ところまで戻ってから fix-B をマージした. この図からもわかるように, fix-B は feature-A の修正を取り込んだ後の master の状態は知らないので, fix-B を master にマージするときにコンフリクトが生じた.

GitHub のアカウント作成

<URL:https://github.com/> から GitHub のアカウントを作る. GitHub のアカウントは一生使うことを意識して, 恥ずかしくないアカウント名にすること.

Step2 で示されるプランでは, "Unlimited public repositories for free" を選択すること. 申請するとメールで確認がやってくる.

Personal access tokens の作成

以前は後述する GitHub へ git commit するときにパスワード認証が使えたが, つい最近にパスワード認証が禁止されてしまった.そのため, Personal access tokens を作成し,それを使う必要が生じた.

メニューの Settings から,Developer settings へ進み,Personal access tokens を選択する. そして,Create New Token をクリックする.

Note に適当なメモを残し,repo にチェックを入れておく

最終的に token が表示される.

もし token を忘れてしまった場合は,Web からログインし (2 段階認証), Personal access tokens のページに行く.「Regenerate token」が あるのでそれをクリックすると,token が再発行される.

リモートリポジトリ (GitHub) との連携

以下では GitHub 上のリモートリポジトリとの連携方法の基礎を行う.

まずは GitHub に同名のレポジトリ (git-tutorial) を用意する. 今回はライセンスや README の作成にはチェックを入れなくて良い.

git remote

リモートリポジトリを登録する. リポジトリのパスは Web 画面に表示されたように https://github.com/<ユーザ名>/git-tutorial.git である. これをローカルリポジトリの リーモートリポジトリとして登録するために git remote add コマンドを利用する. 以下のコマンド中のユーザ名は適宜自分のものに変更すること.

$ git remote add origin https://github.com/<ユーザ名>/git-tutorial.git

以降は origin という名前 (識別子) で GitHub のリポジトリを指すことができるようになる.

git push

現在のブランチのローカルリポジトリの内容をリモートリポジトリに 送信するためには, git push コマンドを用いる.

まずは現在のブランチを確認する. master ブランチであることがわかる.

$ git branch

    feature-A
    fix-B
  * master

$ git push -u origin master

  Username for 'https://github.com': <ユーザ名>    <-- GitHub のユーザ名を入れる
  Password for 'https://sugiymki@github.com':      <-- GitHub の token を入れる
  Counting objects: 27, done.
  Delta compression using up to 4 threads.
  Compressing objects: 100% (16/16), done.
  Writing objects: 100% (27/27), 2.23 KiB | 0 bytes/s, done.
  Total 27 (delta 4), reused 0 (delta 0)
  remote: Resolving deltas: 100% (4/4), done.
  To https://github.com/sugiymki/git-tutorial.git
   * [new branch]      master -> master
  Branch master set up to track remote branch master from origin.

git push に -u origin master というオプションを与えたので, orgin という名前のリモートリポジトリ (今回は GitHub の git-tutorial リポジトリ) の master ブランチに ローカルリポジトリの master ブランチの内容が送信される.

当然のことながら, リモートリポジトリに master 以外のブランチを 作成することができる. 以下の例ではローカルリポジトリで feature-D というブランチを作成し, それをリモートリポジトリに送信する.

$ git checkout -b feature-D

  Switched to a new branch 'feature-D'

$ git push -u origin feature-D

  Username for 'https://github.com': <ユーザ名>    <-- GitHub のユーザ名を入れる
  Password for 'https://sugiymki@github.com':      <-- GitHub の token を入れる
  Total 0 (delta 0), reused 0 (delta 0)
  To https://github.com/sugiymki/git-tutorial.git
   * [new branch]      feature-D -> feature-D
  Branch feature-D set up to track remote branch feature-D from origin.

GitHub をブラウザで見ると, 新たに feature-D というブランチが存在することが確認できる.

現在の状況を図に表すと以下のようになる. feature-D が master から分岐した.

リモートリポジトリから取得

今までの作業で, GitHub に作成したリポジトリをリモートリポジトリとして 登録し, feature-D ブランチを push した. 次に, 「別の開発者」として リポジトリを取得してソースの修正やマージを行ってみる (下図の「開発者 B」の立場で).

git clone

まず, テスト用のディレクトリを自分のホームディレクトリ直下に作成する.

$ cd

$ mkdir ~/test-remote

$ cd test-remote

初めて GitHub 上のリモートリポジトリを取得する時には, git pull でなく git clone コマンドを用いる. GitHub 上のリポジトリは公開されているので, ユーザ名やパスワードを入力する必要はない.

$ git clone https://github.com/<ユーザ名>/git-tutorial.git

  Cloning into 'git-tutorial'...
  remote: Counting objects: 27, done.
  remote: Compressing objects: 100% (12/12), done.
  remote: Total 27 (delta 4), reused 27 (delta 4), pack-reused 0
  Unpacking objects: 100% (27/27), done.

$ ls

  git-tutorial

$ cd git-tutorial

git status や git branch を実行すると, git clone を行った直後には master ブランチにいることがわかる. また, git status のメッセージ中に 'origin/master' とあるように, clone 元のリモートリポジトリは origin という名前で参照できるように自動的に設定されている.

$ git status

  On branch master
  Your branch is up-to-date with 'origin/master'.
  nothing to commit, working tree clean

$ git branch

  * master

git branch に -a オプションをつけて実行すると, リモートリポジトリの 中に存在するブランチも表示することができる. リモートリポジトリ内に feature-D ブランチが存在することがわかる.

$ git branch -a

  * master
    remotes/origin/HEAD -> origin/master
    remotes/origin/feature-D
    remotes/origin/master

リモートリポジトリの feature-D リポジトリをローカルリポジトリに チェックアウトするためには以下のようなオプションをつけて git checkout を実行する.

$ git checkout -b feature-D origin/feature-D

  Branch feature-D set up to track remote branch feature-D from origin.
  Switched to a new branch 'feature-D'

origin はリモートリポジトリを意味するので, origin/feature-D とすることで リモートリポジトリの feature-D リポジトリをチェックアウトすることができる. -b の直後の feature-D はローカルリポジトリ内でのブランチの名前である. 通常はリモートリポジトリのブランチ名と揃えると良い (ここでは feature-D).

feature-D ブランチのファイルを編集し, git add と git commit を行う.

$ git branch

  * feature-D
    master

$ vi README.md 

  Git tutorial

  It's Git tutorial

  test (feature-A)

  test (fix-B)

  test (feature-D)       <-- 追記

$ git add README.md

$ git commit -m "ADD feature-D"

  [feature-D 885d038] ADD feature-D
   1 file changed, 5 insertions(+)

変更をリモートリポジトリに反映させる.

$ git push

  Username for 'https://github.com': <ユーザ名>    <-- GitHub のユーザ名を入れる
  Password for 'https://sugiymki@github.com':      <-- GitHub の token を入れる
  Counting objects: 3, done.
  Delta compression using up to 4 threads.
  Compressing objects: 100% (2/2), done.
  Writing objects: 100% (3/3), 294 bytes | 0 bytes/s, done.
  Total 3 (delta 0), reused 0 (delta 0)
  To https://github.com/sugiymki/git-tutorial.git
     195ac74..885d038  feature-D -> feature-D

GitHub から確認すると, master ブランチは変更がないが, feature-D ブランチは README.md が書き換わっていることがわかる.

master ブランチは以下の通り.

feature-D ブランチは以下の通り.

現在の状況を図に表すと以下のようになる. feature-D にコミットしたので master ブランチから feature-D ブランチが分岐した.

git pull

今, 2 つ目のローカルリポジトリ (~/test-remote/git-tutorial) 以下で feature-D ブランチを修正した. しかし, 最初から利用してきた 1 つ目の ローカルリポジトリ (~/git-tutorial) 以下には先の修正は反映されていない. このような場合はどうすれば良いだろうか?

現在の状況は下図に当てはめると, 開発者 B のローカルリポジトリと GitHub のリモートリポジトリは feature-D ブランチが master ブランチから分岐したことを知っているが, 開発者 A のローカルリポジトリはそのことを知らない, ということになる. 開発者 A のローカルリポジトリにリモートリポジトリの feature-D ブランチの最新データを 持って来たいというのが現在の問題意識である.

別の開発者が commit した内容を反映させる (= ブランチを最新の 状態にする) ためのコマンドが git pull である. git pull は git fetch と git merge を同時に行うコマンドである (git fetch と git merge について調べるのは課題で).

まず cd コマンドで元のリポジトリに移動し, 現在のブランチを確認する.

$ cd ~/git-tutorial 

$ git branch

    feature-A
  * feature-D
    fix-B
    master

feature-D ブランチであることを確認してから, git pull を実行する. README.md の中身が変更されていること (リモートリポジトリの最新版に置き換えられたこと) を確認する.

$ cat README.md 

  Git tutorial
  It's Git tutorial
  test (feature-A)
  test (fix-B)

$ git pull origin feature-D

  remote: Counting objects: 3, done.
  remote: Compressing objects: 100% (2/2), done.
  remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
  Unpacking objects: 100% (3/3), done.
  From https://github.com/sugiymki/git-tutorial
   * branch            feature-D  -> FETCH_HEAD
     195ac74..885d038  feature-D  -> origin/feature-D
  Updating 195ac74..885d038
  Fast-forward
   README.md | 5 +++++
   1 file changed, 5 insertions(+)

$ cat README.md 

  Git tutorial

  It's Git tutorial

  test (feature-A)

  test (fix-B)

  test (feature-D)

開発フロー: GitHub flow

多人数が同じブランチで作業をすると, コンフリクトが起きやすくなる. 特に開発者全員が master ブランチを使う状況では, 常に他の開発者のことを意識せねばならず, かえって開発の効率が悪くなることがある.

このような状況が起きにくくなるように, Git ではいくつかの標準的な使い方 (開発フロー) が提案されている. ここでは GitHub 社が実践しているシンプルなワークフローである GitHub flow をごく限定的に説明する. GitHub flow の実践は来週行う. また, 全容や詳細を知りたい場合は <URL:https://gist.github.com/Gab-km/3705015> (日本語) を参照されたい.

Git flow の鉄則は,

  • 永続的なブランチは master のみ.
  • 新作業は master ブランチから新ブランチを作成してから行う.
  • 作成した新ブランチに修正/追加をコミットする.
  • 新ブランチは最終的には master ブランチにマージする (pull request する).

ことである. 新機能の追加でもバグ修正でも必ず作業用のブランチ (「feature ブランチ」や「トピックスブランチ」と呼ぶ) を作り, そのブランチで作業する. ブランチ名は行う具体的な作業名になっていることが望ましい. ここで注意すべきは, 1 つの作業を 1 つのブランチで行うことである.

例えば,

1) コードのインデントが崩れていたので修正

2) 英単語にスペルミスがあったので修正

3) 新たなメソッドを追加

という 3 つの作業を行いたい場合は, これらをまとめて 1 つの ブランチで行うのではなく, それぞれを別々のブランチで行うべきである. 他の開発者のためにも, 修正・追加の意図が伝わりやすいように, コミットの粒度に気をつけるべきである.

この開発フローではマスターブランチ以外は作業中のブランチとなるため, 気軽に作業中のブランチを push することができる. 定期的に GitHub などのリモートリポジトリに push すると良い.

課題

以下の課題を実行し,提出物 (1)--(4) を提出すること.

課題 1

  • Ruby などの任意の言語でプログラムを作成し, それを master ブランチに git commit しなさい. commit するのはプログラム完成後である必要はない. プログラム製作の途中途中で最低 2 回は commit すること.
    • リポジトリは, git-tutorial を使うこと.
    • 提出物(1):git log --graph の実行結果. ターミナル上で git log --graph を実行し, それを wbt のオンラインテキストにコピペする.
    • プログラム:fizzbuzz プログラム (C 言語版) を Ruby などの C 言語以外の言語に書き換えること.

課題 2

  • 上記で作成したプログラム git-tutorial リポジトリの master ブランチに登録した後,それを GitHub の同リポジトリに push せよ.
    • 提出物(2):GitHub 上のプログラムを Web ブラウザで表示し, そのスクリーンショット (png, jpg, ...) を wbt に登録せよ. 但し,スクリーンショットに URL が入るようにすること.

課題 3

  • 上記で作成したプログラムの修正を行う. feature ブランチ (トピックスブランチ) を切ってから作業すること. fizzbuzz プログラムを作成している場合には,追加内容は「7 の倍数の時は git と言う」とする.
    • 提出物(3):GitHub 上の修正したプログラムを Web ブラウザで表示し, そのスクリーンショット (png, jpg, ...) を wbt に登録せよ.
    • 参考: 作業手順の例
      1. feature ブランチを作成. ブランチ名は fizzbuzz プログラムの修正であることが分かりやすくなる名前とすること.
      2. feature ブランチにうつり, fizzbuzz プログラムを修正.
      3. 修正後, git add, git commit を実行.
      4. その後, master ブランチに feature ブランチを git merge する.
      5. ローカルのリポジトリの master ブランチを, GitHub 上のリポジトリの master ブランチに push する.
      6. 自分の Web GitHub をブラウザで表示し, 変更点が反映されていることを確かめる.

課題 4

  • 提出物(4) : 以下を調べた結果を電子ファイルで提出せよ.
    • Git における HEAD とは一体何か?
    • "git push origin master" コマンドが何を意味するか答えよ.
    • "git clone","git fetch","git pull" の 3 つのコマンドの違いを説明せよ.
    • "git checkout SHA1","git reset SHA1","git revert SHA1" の 3 つのコマンドの違いを説明せよ.