Git/GitHub 入門編

# ローカルリポジトリを作ってみよう
自分で初めから何かを作るときは、以下の方法でワークツリーとローカルリポジトリを作成する必要がある。
```terminal
# ワークツリーを作成する
# 今回はホームディレクトリの下にgit_testを作る
$ mkdir ~/git_test
# ワークツリーに移動する
$ cd ~/git_test
# リポジトリを作成する
~/git_test$ git init
```
これでリポジトリが作成された。以下のコマンドで確認してみよう。

```terminal
~/git_test$ ls -a
.  ..  .git
```

このように、.gitディレクトリが作成されていたら成功。

# 他人が作ったリポジトリをコピーしよう
他人が公開しているリポジトリを引っ張ってきて、途中から何かしらの作業をしたいときは、以下の方法でワークツリーとローカルリポジトリをコピーする。
今回はオープンソースのAtomエディタの作業フォルダをコピーしてみる（しばらく時間がかかるので注意）。

```terminal
$ git clone https://github.com/atom/atom.git
Cloning into 'atom'...
remote: Enumerating objects: 204170, done.
remote: Counting objects: 100% (85/85), done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 204170 (delta 64), reused 62 (delta 62), pack-reused 204085
Receiving objects: 100% (204170/204170), 322.97 MiB | 4.50 MiB/s, done.
Resolving deltas: 100% (144753/144753), done.
```

これで、カレントディレクトリの下にatomディレクトリが生成されるはずである。中身を確認してみよう。

```terminal
$ ls atom -a
.                  .github             LICENSE.md                coffeelint.json    package.json  stylelint.config.js
..                 .gitignore          PULL_REQUEST_TEMPLATE.md  docs               packages      vendor
.coffeelintignore  .prettierrc         README.md                 dot-atom           resources
.eslintignore      CHANGELOG.md        SUPPORT.md                exports            script
.eslintrc.json     CODE_OF_CONDUCT.md  apm                       keymaps            spec
.git               CONTRIBUTING.md     atom.sh                   menus              src
.gitattributes     Dockerfile          benchmarks                package-lock.json  static
```

フォルダの構成などは変更される可能性があるが、大体こんな感じになっているはず。

確認できたら、以下のコマンドでフォルダごと消去しておこう。
```terminal
$ rm -rf atom
```

# ファイル変更をステージに追加しよう
ファイル、またはディレクトリをステージに追加するときは、`git add`コマンドを使用する。具体的には
<ol>
    <li>git add <ファイル名></li>
    <li>git add <ディレクトリ名></li>
    <li>git add .</li>
</ol>

として、追加するファイルを指定する。3番の`git add .`は、変更のあったファイル全てを追加するという意味である。

今回は[ローカルリポジトリを作ってみよう](#ローカルリポジトリを作ってみよう)で作成した`git_test`内で、作業を進めていこう。

まずは`index.html`ファイルを作る。
```terminal
$ cd ~/git_test
$ echo '<h1>Learning Git</h1>' > index.html
```
適当なエディタで`index.html`を開き、内容が記述されていることを確認できたら、以下のコマンドでステージに追加する。

```terminal
$ git add .
```
# コミットしよう
ステージに追加出来たら、次にコミットしてみよう。コミットする際には、「どこを、なぜ変更したのか」を伝える**コミットメッセージ**を記述する。

簡単に書く際は、変更内容の要点と理由を一行で簡潔に書く。

正式に書く際は、
```
[1] 変更内容の要約
[2] （空行）
[3] 変更した理由
```

のように3行に分けて書く。

今回は簡単に書いてみよう。`git commit -m 'メッセージ'`でコミットすることができる。
```terminal
$ git commit -m 'initial commit'
```

コミットしたら、ターミナルに`git log`と打ち込んで、コミット履歴を確認してみよう。
```terminal
$ git log
commit 2bb499ef40c63157f59630492ec8fe1fdcbb1a47 (HEAD -> master)
Author: user name <github@example.com>
Date:   Tue May 16 17:07:52 2023 +0900

    initial index.html
```

のように表示されていたら成功。

この他、変更点を確認しながらコミットメッセージを記述できる`git commit -v`コマンドも存在する。気になったら調べてみてほしい。

# 現在の変更状況を確認しよう
Gitでコミットするときに、コミットすべきではない変更までコミットしてしまわないように、現在の変更状況を確認するべきである。それを可能にするためのコマンドが`git status`である。

例として、適当なエディタでindex.htmlを開き、次のように変更してみよう。
```html:index.html
<h1>Learning Git</h1>
<p>git status</p>
```

ここで先ほどのコマンドを打ち込むと、
```terminal
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")
```
と表示されるはずだ。`Changes not staged for commit`は「コミットできるようにステージされていない変更」という意味である。では、このファイルをステージしてみよう。
```terminal
$ git add index.html
```

この状態で`git status`を実行すると、
```terminal
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html

```
となる。`Changes to be committed`は「コミットされる変更」という意味である。

とりあえず、この変更をコミットしておこう。
```terminal
$ git commit -m 'git statusを追記'
[master 9acfe61] git statusを追記
 1 file changed, 1 insertion(+)
```

最後に、変更状況を確認してみよう。
```terminal
$ git status
On branch master
nothing to commit, working tree clean
```

コミットされる変更は何もないと表示されるはずだ。

# 変更差分を確認しよう
ワークツリーとステージの間の差分を確認するコマンドが`git diff`である。特定のファイルの差分だけを見たいときは、`git diff <ファイル名>`を使う。また、ステージとリポジトリの間の差分を確認するには`git diff --staged`を使う。それぞれ詳しく見ていこう。

前の章の最後の状態で、ターミナルに`git diff`と打ち込んでも何も表示されないはずだ。そこで、適当なエディタでindex.htmlを開き、次のように変更してみよう。
```html:index.html
<h1>Learning Git</h1>
<p>git status</p>
<p>git diff</p>
```
そして、もう一度`git diff`を実行しよう。
```terminal
$ git diff
diff --git a/index.html b/index.html
index ba1c523..01ed784 100644
--- a/index.html
+++ b/index.html
@@ -1,2 +1,3 @@
 <h1>Learning Git</h1>
 <p>git status</p>
+<p>git diff</p>
```
この`+<p>git diff</p>`というのが、追加された記述を表している。

次に、変更をステージし、もう一度`git diff`を打ち込んでみよう。
```terminal
$ git add .
$ git diff
```
何も表示されなくなったはずだ。これは、`git add`したことで、ステージが更新され、ワークツリーとステージの差分がなくなったことを表している。

では、ステージとリポジトリの間の差分を確認してみよう
```terminal
$ git diff --staged
diff --git a/index.html b/index.html
index 7cb8569..01ed784 100644
--- a/index.html
+++ b/index.html
@@ -1,2 +1,3 @@
 <h1>Learning Git</h1>
 <p>git status</p>
+<p>git diff</p>
```

確認したら、変更をコミットしよう。
```terminal
$ git commit -v
```
と打ち込むと、登録したエディタが起動し、以下のように表示されるはずだ。
```text:COMMIT_EDITMSG

# 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:   index.html
#
# ------------------------ >8 ------------------------
…
```
一番上の行が空行になっているので、ここにコミットメッセージを入力していこう。
```text:COMMIT_EDITMSG
git diffを追記
# Please enter the commit message for your changes. Lines starting
…
```
入力したら`Ctrl+S`で保存し、エディタを閉じよう。すると、

```terminal
$ git commit -v
[master cf8304f] git diffを追記
 1 file changed, 2 insertions(+), 1 deletion(-)
```
となって、無事にコミットされるはずである。

# 変更履歴を確認しよう
`git log`コマンドを使用すると、コミットの履歴を確認できる。
```terminal
$ git log
commit cf8304fb706eb1e1a330f8d3cab97412184fb7e0 (HEAD -> master)
Author: user name <github@example.com>
Date:   Tue May 16 18:23:08 2023 +0900

    git diffを追記

commit 9acfe61b9d8b76ef2e29ef2a705da785244017be
Author: user name <github@example.com>
Date:   Tue May 16 17:49:26 2023 +0900

    git statusを追記

commit 2bb499ef40c63157f59630492ec8fe1fdcbb1a47
Author: user name <github@example.com>
Date:   Tue May 16 17:07:52 2023 +0900

    initial index.html
```
このように、コミットした人、日付、コミットメッセージなどが表示される。

コミットメッセージだけ簡潔に表示したい場合は、
```terminal
$ git log --oneline
b0f71d2 (HEAD -> master) git diffを追記
d84fc50 git statusを追記
eb1ac84 initial index.html
```
とする。

さらに、表示するコミット数を制限したい場合は、`-n`オプションを使って表示数を指定する。例えば最新のコミット2つを表示したい場合は、
```terminal
$ git log --oneline -n 2
b0f71d2 (HEAD -> master) git diffを追記
d84fc50 git statusを追記
```
のようにする。

`git log -p <ファイル名>`と打ち込むと、指定したファイルの中身が確認できる。ファイル名を省略した場合は変更があったすべてのファイルの中身が表示される。これを`-n`オプションと組み合わせることで、最新のコミットの中身だけを確認することができる。

例
```terminal
$ git log -n 1 -p index.html
```

# ファイルの削除を記録しよう
ファイルまたはディレクトリを削除するときは、`git add`ではなく`git rm <ファイル名>`または`git rm -r <ディレクトリ名>`を使う。

このコマンドを入力すると、ワークツリーからファイルまたはディレクトリが消え、削除したという変更がステージされる。これをコミットすることで、削除されたバージョンが作成される。

Gitの記録からだけファイルを消して、ワークツリーには残しておきたいときは、`git rm --cached <ファイル名>`を使う。

# ファイルの移動を記録しよう
ファイルの移動を記録するときは`git mv <旧ファイル> <新ファイル>`を使う。

これは
```terminal
$ mv <旧ファイル> <新ファイル>
$ git rm <旧ファイル>
$ git add <新ファイル>
```
を実行するのと同じである。

# GitHubにプッシュしよう
リモートリポジトリに、ローカルの内容をアップロードすることをプッシュという。ほかのチームメンバーに自分の書いたコードを共有したいときや、GitHubにローカルの内容を保存したいときにプッシュする。

まず、リモートリポジトリのURLを毎回入力しないで済むように、URLに別名を与える。それが以下のコマンドである。
```terminal
$ git remote add origin <URL>
```
これで、リモートリポジトリにoriginというリモート名をつけることができた。

プッシュする際は以下のようにする。
```terminal
$ git push origin <ブランチ名>
```
なお、デフォルトのブランチ名は`master`である。これを実行すると、ユーザ名とパスワードを求められる。パスワードはGitHubのアカウントのパスワードではなく、アクセストークンのことを指す。アクセストークンの発行方法については各自で調べていただきたい。

`git push origin master`を何度も繰り返す場合は、以下のように、
```terminal
$ git push -u origin master
```
`-u`オプションをつけることで、以降
```terminal
$ git push
```
だけでプッシュできるようになる。

# バージョン管理したくないファイルを除外しよう
<ul>
<li>パスワードなどの秘密情報が記載されたファイル</li>
<li>チーム開発で必要ではないファイル(キャッシュなど)</li>
</ul>

などはバージョン管理の対象から除外する必要がある。そのためには、`.gitignore`ファイルを作成し、除外するファイルを指定する必要がある。`.gitignore`は基本的にプロジェクトの一番上のディレクトリに置くが、どこに置いても機能する。

```text:.gitignore
# 特定のファイルを除外
<ファイル名>
# 特定のディレクトリ以下を除外
<ディレクトリ名>/

# 先頭の / はルートディレクトリを表す。
# ワイルドカードとして * が使える。
```
\
例として`secret.txt`ファイルを作ってみよう。
```terminal
$ touch secret.txt
$ ls
index.html  secret.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

        secret.txt

nothing added to commit but untracked files present (use "git add" to track)
```
`git status`を実行すると、`secret.txt`がUntrackedであると出てきた。そこで`.gitignore`ファイルを作成しよう。

```terminal
$ echo 'secret.txt' > .gitignore
$ cat .gitignore
secret.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

        .gitignore

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

Untrackedなファイルの一覧から、`secret.txt`が消え、新たに作成した`.gitignore`が追加されている。

この変更をステージしてコミットしておこう。

```terminal
$ git add .
$ git commit -m '.gitignoreを追加'
[master 96fcc67] .gitignoreを追加
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore
```

# ファイルの変更を取り消そう
ファイルを、最新のステージの状態に戻すコマンドが`git checkout`である。

```terminal
$ git checkout -- <ファイル名>
$ git checkout -- <ディレクトリ名>
# "--"は、ブランチ名とファイル名が被った時、どちらを指しているか分かるように付ける。

# 全変更を取り消す
$ git checkout -- .
```

例として、`index.html`の末尾に以下の行を追加してみよう。
```terminal
$ echo '<p>git co</p>' >> index.html
$ cat index.html
<h1>Learning Git</h1>
<p>git status</p>
<p>git diff</p>
<p>git co</p>
```
`cat`で確認すると、確かに追加されている。

`git status`で変更を確認してみよう。

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

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")
```
`index.html`が変更されていると表示されている。ここで`git checkout`コマンドを使う。

```terminal
$ git checkout -- index.html
```
さて、もう一度変更を確認してみよう。
```terminal
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
```
以上のように、変更されたファイルはないと表示された。

`index.html`の中身を見てみると、
```terminal
$ cat index.html
<h1>Learning Git</h1>
<p>git status</p>
<p>git diff</p>
```
となっており、先ほど追加した行が削除されていることが分かる。

# ステージに追加した変更を元に戻そう
ステージを、最新のコミットの状態に戻すコマンドが`git reset`である。

```terminal
$ git reset HEAD <ファイル名>
$ git reset HEAD <ディレクトリ名>

# 全変更を取り消す
$ git checkout HEAD .
```
ここでHEADとは、（今自分がいるブランチの）最新のコミットを表している。

以下に実行例を示す。
```terminal
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
$ echo '<p>git reset</p>' >> index.html
$ git add .
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

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

        modified:   index.html

# コミットされる変更にindex.htmlが追加されている
$ git reset HEAD index.html
Unstaged changes after reset:
M       index.html
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")
# ステージが元に戻った
```

ここまでのデータの流れを図で表すと以下のようになる。
![git_dataflow.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691652/ba2ac556-1b4f-a412-1148-4eaeba749ba9.png)

# 直前のコミットを修正しよう
以下のコマンドを使うことで、最新のコミットを上書きすることができる。また、コミットメッセージのみを修正することもできる。
```terminal
$ git commit --amend
```
ただし、リモートリポジトリにプッシュしたコミットには使用してはいけない（他の人との一貫性が保たれなくなるため）。