Gitのインストールと動作確認

Gitについて

まず、以下のスライドを見よ。

数値計算屋のためのGit入門

今後、「どのファイルがどんな状態にあるか」「歴史はどうなっているか」をイメージしながらGitを操作すると良い。

Gitのインストール

Windows

WSL2のUbuntuを使う。

sudo apt update
sudo apt install git

を実行せよ。インストールが完了したら、

git --version

を実行せよ。

git version 2.25.1

と表示されればインストール完了である。

Mac

Macの場合は、Homebrewからインストールする。ターミナルから

brew update
brew install git

を実行せよ。インストールが完了したら、

git --version

を実行せよ。

git version 2.26.0

と表示されればインストール完了である。

Gitの初期設定

Gitのインストールが完了したら、初期設定を行う。

まず、デフォルトブランチを変更する。昔はGitのデフォルトブランチはmasterという名前だったが、現在はmainとするのが一般的である。以下を実行して、デフォルトブランチをmainに設定せよ。

git config --global init.defaultBranch main

次に「ユーザ名」と「メールアドレス」の設定を行う。ユーザ名とメールアドレスは、GitHub等で公開リポジトリを作った場合には誰からも見られる状態となることに注意。

ターミナルで以下を実行する。

git config --global user.name "ユーザー名"
git config --global user.email "メールアドレス"

特にこだわりがなければ、ユーザ名は自分の英語名(私は “H. Watanabe”にしている)、メールアドレスは普段使うメールアドレスにしておけば良い(携帯のアドレスなどは避けること)。

上記の設定は.gitconfigに保存される。保存されたか見てみよう。以下を実行せよ。

cat .gitconfig

以下のように表示されれば設定されている。

[init]
    defaultBranch = main
[user]
        name = 先ほど設定したユーザー名
        email = 先ほど設定したメールアドレス

また、よく使うコマンドの省略形(エイリアス)も登録しておこう。とりあえず一つだけ設定してみよう。

git config --global alias.st "status -s"

これは、デフォルトのgit statusは情報過多のため、短い形式で表示させるgit status -sを多用するので、それをgit stとして実行できるように登録したものだ。このあたりは完全に趣味なので、慣れてきたら自分好みに追加・削除・修正していくと良い。ここまでの設定で、.gitconfigは以下のようになったはずだ。

[init]
    defaultBranch = main
[user]
        name = 先ほど設定したユーザー名
        email = 先ほど設定したメールアドレス
[alias]
        st = status -s

また、UbuntuのデフォルトエディタはGNU Nanoであるが、当研究室ではVimを使う。

git config --global core.editor vim

.gitconfigは以下のようになったはずだ。

[init]
    defaultBranch = main
[user]
        name = 先ほど設定したユーザー名
        email = 先ほど設定したメールアドレス
[alias]
        st = status -s
[core]
        editor = vim

これでGitの利用準備が整った。

Gitの動作確認

それでは、実際にGitを使ってリポジトリを作成し、基本的な操作を学んでみよう。最初にコマンドラインから、次にVSCode上から操作する。今後、コマンドライン、VSCodeどちらでGitを利用しても良いが、コマンドラインから使えるようになることを推奨する。

途中、わけがわからなくなったら、ディレクトリごと消してやり直すこと。

リポジトリの作成

まず、適当なテスト用ディレクトリを作り、その中で作業しよう。gitというディレクトリを作り、さらにその中にtestというディレクトリを作ろう。

cd
mkdir git
cd git
mkdir test
cd test

最初にcdとだけ入力しているのは、ホームディレクトリに戻るためだ。

ディレクトリtestの中に、README.mdというファイルを作成しよう。まず、このディレクトリでVSCodeを起動する。

code .

VSCodeが開いたら、左のエクスプローラーの「TEST」の右にある「新しいファイル」ボタンを押して、README.mdと入力しよう。

新しいファイル

README.mdファイルが開かれたら、

# Test

とだけ入力し、保存しよう。これで、以下のようなディレクトリ構成になったはずだ。

git
└── test
    └── README.md

この状態でターミナルに戻り、リポジトリとして初期化しよう。

git init

すると、.gitというディレクトリが作成され、testディレクトリがリポジトリとして初期化される。以下を実行してみよ。

ls -la

README.mdに加え、.gitというディレクトリが作成されたことがわかるはずだ。

git initした直後は、「現在のディレクトリtestをGitで管理することは決まったが、まだGitはどのファイルも管理していない」という状態になる。

git init直後

この状態を確認してみよう。ターミナルで以下を実行せよ。

git status

以下のような表示が得られるはずである。なお、環境変数LANGが日本語になっていると、日本語のメッセージが表示される。

On branch master

No commits yet

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)

これは、まだ何もコミットがされておらず、README.mdというファイルがUntracked、つまりGitの管理下に置かれていないことを表している。

状態を見るのにいちいちこんな長いメッセージを見せられても困るので、-sオプションをつけて見よう。

git status -s

こんな表示が出力されたはずだ。

?? README.md

??というのは、Untracked、つまりGitの管理下に無いよ、という意味だ。いちいちgit status -sと入力するのは面倒なので、最初にstatus -sstという別名をつけておいた。

git st

と入力すると、git status -sと入力したのと同じことになる。以後、こっちを使うことにしよう。

インデックスへの追加

さて、Untrackedな状態のファイルを、Gitの管理下に置こう。そのためには、git addを実行する。

git add README.md

現在の状態を見てみよう。

git st

こんな表示になるはずだ。

A  README.md

これは「README.mdが追加されることが予約されたよ」という意味で、インデックスにREADME.mdが追加された状態になっている。

インデックス

では、記念すべき最初のコミットをしよう。Gitはコミットをする時に、コミットメッセージが必要となる。最初のメッセージは慣例によりinitial commitとすることが多い。

git commit -m "initial commit"

これによりコミットが作成され、README.mdはGitの管理下に入った。

git commit

状態を見てみよう。

git st

何も表示されないはずである。ロングバージョンのステータスも見てみよう。

$ git status
On branch master
nothing to commit, working tree clean

なお、このように$ コマンド名の表記がある場合は、$以後だけを入力して実行したら、行頭に$がついていないような表記が出力された、という意味である。今後、$がついている場所のコマンドを$抜きで入力すること。

自分がいまmasterブランチにいて、何もコミットをする必要がなく、ワーキングツリーがきれい(clean)、つまりリポジトリが記憶している最新のコミットと一致していることを意味している。

ファイルの修正

では、ファイルを修正してみよう。VSCodeでREADME.mdに行を付け加えて保存しよう。

# Test

Hello git

Hello gitの最後の改行を忘れないように。

状態を見てみよう。

$ git st
 M README.md

ファイル名の前にMという文字がついた。これはModifiedの頭文字で、「リポジトリが知っている状態から修正されているよ」という意味だ。

git modified

また、この状態でgit diffを実行してみよう。

$ git diff
diff --git a/README.md b/README.md
index 8ae0569..58c814b 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
 # Test
+
+Hello git

git diffは、何もオプションをつけずに実行すると、

差分を表示する。

行頭に+がついた箇所が追加された行である。

この修正をリポジトリに教えるために、git addしよう。

git add README.md

また、状態を見てみよう。

$ git st
M  README.md

先ほどは赤字で二桁目にMが表示されていたのが、今回は緑字で一桁目にMが表示されているはずである。これは、修正されたファイルがインデックスに追加されたことを示す。

git add

この状態でコミットしよう。

git commit -m "adds new line"

-m以後がコミットメッセージだ。日本語も使えるが、文字化けする可能性があるので、とりあえず英語で書いておいた方が良い。

修正がリポジトリに登録され、ワーキングツリーがきれい(clean)な状態となった。

git commit

git addなしのコミット

Gitでは、原則として

という作業を繰り返す。実際、多人数で開発する場合はこうして「きれいな歴史」を作る方が良いのだが、当面は一人で開発するので、git addを省略して良い。

git addを省略するには、コミットする時にgit commit -aオプションをつけて、「修正されたファイル全てをコミットする」ようにすれば良い。先ほどcommit -aciという別名(エイリアス)をつけたので、今後はそれを使うことにしよう。

まず、VSCodeでさらにファイルを修正しよう。README.mdに以下の行を付け加えよう。やはり最後の改行を忘れないように。

# Test

Hello git
Bye git

この状態で、git addせずにgit commitしようとすると、「何をコミットするか指定が無いよ(インデックスに何も無いよ)」と怒られる。

$ git commit -m "modifies README.md"
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

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

上記メッセージには、まずgit addするか、git commit -aしろとあるので、ここでは後者を実行しよう。先程git commit -agit ciという別名をつけたので、git ciを使えばgit addなしにいきなりコミットできる。

git ci -m "modifies README.md"

以後、慣れるまでは場合はgit ciを使うことでgit addを省略して良い。

歴史の確認

これまでの歴史を確認して見よう。上記の通りに作業して来たなら、3つのコミットが作成されたはずだ。git logで歴史を振り返ってみよう。

$ git log
commit e63355285c9fd1929925568522dbc321e9b3e8c7 (HEAD -> master)
Author: H. Watanabe <kaityo@users.sourceforge.jp>
Date:   Wed Mar 31 12:01:12 2021 +0900

    modifies README.md

commit 2eb03e9589b263f4d2ab61b09644da49134d8e21
Author: H. Watanabe <kaityo@users.sourceforge.jp>
Date:   Wed Mar 31 11:38:38 2021 +0900

    adds new line

commit 85f4e334b0094170d4382c7a86e4a185f12e3cd1
Author: H. Watanabe <kaityo@users.sourceforge.jp>
Date:   Wed Mar 31 11:35:44 2021 +0900

    initial commit

いつ、誰が、どのコミットを作ったかが表示される。最初に設定した名前とメールアドレスはここに表示される。GitHubなどでpublicなリポジトリを作った場合、全世界に公開されるので注意。

commitの後の英数字は「コミットハッシュ」と呼ばれ、コミットにつけられた識別子である。これは環境によって異なるため、同じ操作をしても異なるハッシュが作成される。

デフォルトの表示では見づらいので、一コミット一行で表示しても良い。

$ git log --oneline
e633552 (HEAD -> master) modifies README.md
2eb03e9 adds new line
85f4e33 initial commit

個人的にはこちらの方が見やすいので、llog --onelineのエイリアスにしてしまっても良いと思う。もしそうしたい場合は、

git config --global alias.l "log --oneline"

を実行せよ。以後、

git l

で、コンパクトなログを見ることができる。

VSCodeからの操作

Gitは、VSCodeからも操作することができる。今、README.mdを開いているVSCodeで何か修正して、保存してみよう。例えば以下のように行を追加する。

# Test

Hello git
Bye git
Git from VSCode

修正を保存した状態で左を見ると、「ソース管理」アイコンに「1」という数字が表示されているはずだ。これは「Gitで管理されているファイルのうち、一つのファイルが修正されているよ」という意味だ。

VSCode Git Icon

この「ソース管理アイコン」をクリックしよう。

VSCode add

すると、ソース管理ウィンドウが開き、「変更」の下に「README.md」がある。そのファイル名の右にある「+」マークをクリックしよう。README.mdが「変更」から「ステージング済みの変更」に移動したはずだ。

VSCode staged

これは、

git add README.md

という操作をしたことと等価だ。

この状態で「メッセージ」のところにコミットメッセージを書いて、上の「チェックマーク」をクリックすると、コミットできる。例えばメッセージとして「commit from VSCode」と書いてコミットしてみよう。

VSCode commit

これでコミットができた。ちゃんとコミットされたかどうか、ターミナルから確認してみよう。

$ git log --oneline
c67341b (HEAD -> master) commit from VSCode
e633552 modifies README.md
2eb03e9 adds new line
85f4e33 initial commit

VSCodeから作ったコミットが反映されていることがわかる。

基本的にVSCodeからGitの全ての操作を行うことができるが、当面の間はコマンドラインから実行した方が良い。慣れてきたらVSCodeその他のGUIツールを使うと良いだろう。

マージと衝突の処理

Gitでは、原則としてmaster/mainブランチでは作業をしない。通常は、ブランチを切って、作業はブランチ上で行い、master/mainブランチにはマージするのみ、とする運用を採用することが多い。

以下では、ブランチの切り方、マージの仕方、またマージに失敗(衝突)したときの対処方法をかんたんに見ておこう。

ブランチの作成と切り替え

ブランチを作成するには、git branch ブランチ名とする。

git branch newbranch

git branchを実行すると、ブランチ一覧を見ることができる。

$ git branch
* master
  newbranch

ここで、*がついているブランチが、今見ているブランチ、すなわちHEADがついているブランチである。git branchでブランチを作成しても、「今見ているブランチ」は変更されないので注意。

ブランチを切り替えるには、git checkout ブランチ名を使う。我々は省略形coを定義しているのでそれを使っても良い。

$ git checkout newbranch
Switched to branch 'newbranch'

ブランチ名はタブ補完も効くので適宜活用すること。

切り替わったかどうかを見てみよう。

$ git branch
  master
* newbranch

newbranch*がついていることがわかる。

ブランチを削除するには、git branch -d ブランチ名とする。「今見ているブランチ」は削除できないので、一度masterに戻ってから削除しよう。

git checkout master
git branch -d newbranch

消えたか見てみよう。

$ git branch
* master

先程作成したブランチが消えた。

さて、ブランチを作成するとき、すぐにその作成したブランチに移りたいことの方が良い。ブランチの作成と、そのブランチへの切り替えを同時に行うにはgit checkout -b ブランチ名とする。

$ git checkout -b newbranch
Switched to a new branch 'newbranch'

先程と異なり、「新しいブランチにスイッチしたよ」というメッセージが表示される。確認してみよう。

$ git branch
  master
* newbranch

新しいブランチが作成され、そのブランチにスイッチしていることがわかる。

ブランチ上での作業

まず、現在の状況を確認してみよう。

$ git log --oneline
c67341b (HEAD -> newbranch, master) commit from VSCode
e633552 modifies README.md
2eb03e9 adds new line
85f4e33 initial commit

このログから、

ということがわかる。ブランチはコミットの別名であり、新しいブランチを作っただけでまだ何も作業をしていないため、現在はブランチは二つとも同じコミットを指している。ただし、HEADがnewbranchを指しているため、今後の変更はnewbranchに対して行われる。

この状態で、またREADME.mdを修正しよう。最後の一行を追記して保存せよ。

# Test

Hello git
Bye git
Git from VSCode
modified at newbranch

この状態でコミットしよう。

git ci -m "modified at newbranch"

またログを見てみる。

$ git log --oneline
8f4db0d (HEAD -> newbranch) modified at newbranch
c67341b (master) commit from VSCode
e633552 modifies README.md
2eb03e9 adds new line
85f4e33 initial commit

このログから

ということがわかる。コミットすると歴史が増え、HEADが指しているブランチが一緒に動く、と理解すると良い。

マージ

次に、newbranchで行った修正をmasterに反映しよう。ブランチを移動する。

git checkout master

すると、VSCodeが表示する中身も古い状態に戻る。

# Test

Hello git
Bye git
Git from VSCode

この状態でnewbranchの変更をmasterに反映させるには、git mergeコマンドを使う。

$ git merge newbranch
Updating c67341b..8f4db0d
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)

VS Codeの表示が、先程修正したものに変わっていることを確認せよ。

ここで「Fast-forward」とあるのは、「masterブランチを移動させただけで、コミットは作成されていないよ」ということを意味する。ログを見てみよう。

8f4db0d (HEAD -> master, newbranch) modified at newbranch
c67341b commit from VSCode
e633552 modifies README.md
2eb03e9 adds new line
85f4e33 initial commit

先程とコミットの連なり(歴史)は全く変わっておらず、masterの指す先がc67341bから8f4db0dに変わっただけなのがわかる。また、HEADmasterを指していることにも注意。原則としてGitでは、HEADが指しているブランチに対して作業を行う。

不要になったブランチは消しておこう。

$ git branch -d newbranch
Deleted branch newbranch (was 8f4db0d).

衝突

さて、Gitは複数名で編集作業を行うためのツールなので、自分がなにかを修正してる際に、他の人が同じファイルを修正してしまった、ということがあるだろう。両方の修正を取り込むことができれば問題ないが、同じファイルの同じ場所を修正してしまった場合、どちらの修正をどのように取り込むかを決めなくてはならない。

このように、二つのブランチをマージしようとして、自動でマージできない状態を「衝突(conflict)」と呼ぶ。以下では、意図的に衝突を起こし、その修正をしてみよう。

まず、新しいブランチconflictを作り、そちらに移動する。

git checkout -b conflict

VSCodeで、最後にこんな行を付け加えよう。

# Test

Hello git
Bye git
Git from VSCode
modified at newbranch
modified at conflict

そしてコミットして、masterに戻る。

$ git commit -a -m "modified at conflict"
[conflict ab8cbff] modified at conflict
 1 file changed, 1 insertion(+)

$ git checkout master
Switched to branch 'master'

ここでは、新たにab8cbffというコミットが作られたことがわかる。

さて、masterでも同じ箇所を修正しよう。

# Test

Hello git
Bye git
Git from VSCode
modified at newbranch
modified at master

そしてコミットする。

$ git commit -a -m "modified at master"
[master 2f3fb32] modified at master
 1 file changed, 1 insertion(+)

新たに2f3fb32というコミットが作られた。

さて、もともと8f4db0dというコミットがあったのだが、ここからab8cbffというコミットと2f3fb32というコミットが作られ、歴史が分岐した。ここでconflictブランチで作業をしていたのを思い出し、マージを試みる。

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

「自動でマージできず、衝突(conflict)したのでなんとかしてね」と言われている。この状態でstatusを見るとこうなる。

$ git status -s
UU README.md

UUとあるのが衝突しているファイルだ。エディタで見るとこのように表示されるはず。

vscode conflict

ここで、どちらの修正を取り込むかを聞かれているので、適切なものを選ぶ。ここでは「両方の変更を取り込む」を選んでみよう。内容はこうなるはず。

# Test

Hello git
Bye git
Git from VSCode
modified at newbranch
modified at master
modified at conflict

一般に、コードなどが衝突した場合は、どちらか(もしくは両方)を取り込むといった単純な対応ではうまくいかず、意図する動作になるように修正が必要なことが多いが、ここではこれで修正が済んだことにして、コミットしよう。Gitに「衝突が解決したよ」と伝えるには、git addを使う。

git add README.md

この状態でコミットすると、分岐した歴史が一つに戻る。

git commit -m "merged"
[master 91a8668] merged

ログを見てみよう。

$ git log --oneline
91a8668 (HEAD -> master) merged
2f3fb32 modified at master
ab8cbff (conflict) modified at conflict
8f4db0d modified at newbranch
c67341b commit from VSCode
e633552 modifies README.md
2eb03e9 adds new line
85f4e33 initial commit

masterで作ったコミット2f3fb32の後に、新たなコミット91a8668が作られていることがわかる。また、conflictブランチが取り残されていることにも注意。conflictブランチの世界線では、まだmasterが新たにコミットを作っており、さらに自分の修正を取り込んだことを知らない。しかし、masterブランチの世界線ではconflictブランチの修正は取り込んであるため、もはやconflictブランチは不要だ。消してしまおう。

$ git branch -d conflict
Deleted branch conflict (was ab8cbff).

マージ済みであることがわかっているブランチは、問題なく消すことができる。マージされていないブランチは「マージされていないよ」と警告が出て消すことができないので、マージするか、取り込まなくても良いことがわかっている場合はgit branch -D ブランチ名で強制的に消すことができる。