Git・GitHub、おおむね理解する（初心者向け）

# PMをやりたい！
**追い詰められた状況のPMって強制的に技術力身につくくない！？**
#### いや、それどころじゃない
そうじゃん。共同開発ってGitHub必須じゃん。用意してもらったブランチにひたすらプルリク送ってるだけの人間が、知らないファイルのソース管理できるわけがない。

てことで、gitコマンドなんか「add, comit, clone」ぐらいしか使ったことない人間が
**ハッカソンでPMできるぐらい（のはず）の能力を手にできる記事を書きます！**（予定）

:::note warn
過去に最低１回はGit・GitHubを使ったことがある人に向けた記事とします。
:::


## Gitの超基本操作を知る（ステージ、コミット、ログ）
ほとんどVSCodeのGUIでしか触ってなかったから再学習（git init すら打ったこと無い）

:::note info
用語説明（噛み砕いた説明です）
* ワークツリー
コーディングする場所、作業場
* ステージングエリア
コミットするファイルを１つずつ上げる場所
* ステージ
ワークツリーのファイルをステージングエリアに移動（add）すること
* コミット
ステージングエリアのファイルをリポジトリに移動すること
* リポジトリ
コミットされたファイルを保存する場所
:::

1. 作業したいディレクトリで "git init" を実行する

    ```
    git init
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/34be7071-ff01-421d-9b76-c2e8d8fc29a2.png)

1. すると、「.git」フォルダが生成されます。実行したディレクトリにワークツリーが設定され、ステージングエリア・リポジトリが作成される
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/b3abb969-5efe-40a8-a657-247a15cd652d.png)

1. ファイルを作成。今回は"index.html"を作成しました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/1f6c7ff4-3b30-4bdb-b8b9-557ed1859fe6.png)

1. "git add ファイル名"を実行
    ファイルをステージングエリアに追加します（add）
    
    :::note info
    **バージョンを遡るときに分かりづらいので、基本的にファイルは一つずつadd、コミットします。**
        :::
    
    ```
    git add index.html
    ```
    ですが、ワークツリー（ディレクトリ内の）すべてのファイルを同時にステージングエリアに追加することもできます。initの直後にやるらしい
     ```
    git add .
    ```
    VSCodeでは「＋」の変更をステージです
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/58d465b5-e762-4c05-bec6-4f9f7b292593.png)
    実行後
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/889c2f50-494b-43df-a9c3-35aa478e50bf.png)

1. git commit -m "メッセージ" を実行
    ステージングエリアに追加された変更をリポジトリにコミットします
    
    :::note warn
    メッセージ無しでコミットすることはできません
        :::

    ```
    git commit -m "HTMLひな型"
    ```
    実行後
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/76194978-4de9-4a9b-92fe-981305378fc5.png)
    VSCodeでのGUIでも、同じくメッセージを記入せずコミットしようとすると失敗します。

    **add, commitがCLIでもできるようになりました！**

    

1. ログを確認する
    今のコミットで行われたことを確認してみます
    ```
    git log --stat
    ```
    ![SnapCrab_No-0135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/3dbfcbfe-b983-47d4-b874-cd644015c736.png)

    識別するためのコミットの持つ一意の値、誰のコミットか、コミットした日時、オプションで変更を加えたファイル数、追加・削除・変更が何行行われたか。が分かりました

## Gitの基本操作を知る
### 差分を確認する
index.htmlに一行pタグを加えます。そしてステージにaddします。
その後、h1タグの内容を変更します

これで、新しい順に、ワークツリー・ステージングエリア・リポジトリ それぞれが異なるファイルとなりました。
この状態で "git diff" を実行します
```
git diff
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/f1342270-31ee-41bd-8f06-eff1c1e73988.png)
（エディタは":q"で終了します。）
a がステージングエリア、b がワークツリーのファイルです。
@@ -6,7 +6,7 @@
これは、a の６行目からを７行表示、b のa の６行目からを７行表示するという意味です
以下の出力結果は、赤い部分が消去、緑の部分が追加されたことを示しています。
**以下同様の意味です。**
 
次に "git diff --cached " を実行します
```
git diff --cached
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/151d17e6-a616-427a-b809-a1639bfccbee.png)

a がリポジトリ、b がステージングエリアのファイルです。
    
次に "git diff HEAD " を実行します
```
git diff HEAD
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/b84d278b-f8a7-4c72-af32-1841a0df6bfc.png)

a が（現在参照状態にある）リポジトリ、b がワークツリーのファイルです。
**このように差分の確認方法は三種類あります。**（普通GitHubで見ると思うけど…）

### ステージしたファイルをステージングエリアから取り除く
「ステージしたけどステージする必要無かった！」って状況を想定します。
index.cssを作成し、ステージします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/9bfd7387-71f7-4eff-8371-6f7510afef50.png)
 このファイルをリムーブします
```
git rm --cached index.css
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/36f24123-89e6-4e7a-be25-4280f0007e97.png)
ステージングエリアから取り除かれました！

### Gitで管理したくないファイルを設定する(.gitignore)
node_modulesフォルダとか、Gitに上げたくないファイルがあると思います。
今回は、「secret.txt」を管理しないことにします。
「.gitignore」ファイルを作成し、"secret.txt"と書き込みます
```
secret.txt
```

:::note info
フォルダを設定する場合は、"フォルダ名/"です。
```
node_modules/
```
:::
     
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/3cb9dcb9-913e-4f6a-8d17-edabca580b66.png)
ステージしたら「ignoreされてるよ！」って言われました
すべてのファイルをステージしても、ステージングエリアに「secret.txt」は含まれていません

### Git管理下にあるファイルの名前を変更する(git mv)
普通に名前を変更してしまうと、名前を変えただけなのに、ワークツリー上のファイルとリポジトリのファイルが異なるファイルとして扱われてしまいます。そこで、gitからワークツリーのファイル名を変更する必要があります。
一旦先頬までの変更をコミットします。
新たに「mistype.js」を作成し、ステージ、コミットします。
タイプミスをしてしまっていたことに気づいたので、正しいスペルに修正します。
"git mv 変更前の名前 変更後の名前"
```
git mv mistype.js misstype.js
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/1a15488c-bf71-4703-a17a-f5c67f079c60.png)
R（renamed）で、自動的にステージされました。

:::note warn
ファイル名の変更も、きちんと直後にコミットしましょう。
:::

### ブランチを切る
機能や画面を追加します。
マスターブランチだけで作業していましたが、追加作業中に変更を加えたくないのでブランチを分けます。

:::note info
masterブランチは、バグが開発途中の状態やバグが含まれた状態にしないのが一般的らしいです。
例えば、「今できてるのどんな感じ？」って言われて、言い訳をせずにすぐ渡せる状態にしておくべきってことです。
:::

"git branch ブランチ名"
今回は「new_botton」を生やします。
```
git branch new_botton
```
その後、
```
git branch
```
現在存在するブランチを表示してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/20c5e873-0a35-429e-87a7-b98cec359c92.png)
branchが増えたことが分かります。

ブランチを切り替えます。
"git chechout ブランチ名"
```
git checkout new_botton
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/6c99a16c-6d8b-4540-bc2a-98923cc87ce7.png)
ブランチが切り替わったことが確認できました。
ボタンタグを追加し、そのままコミットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/dee89f0c-88f1-494e-ab0e-44bbe8a37952.png)
ここで、またmasterブランチに戻ります。
すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/852598be-5f2b-4b77-a4bd-4845f48cd5fa.png)
先程の変更は加えられていないことが分かりました。

### マージする
new_bottonブランチで加えた変更によって、特に問題は起きていないので、masterブランチに変更を反映させます。

統合させるブランチで、
"git marge 取り込むブランチ名"

今回は、masterブランチに移動して、
```
git merge new_botton
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/6598b53e-226e-47c1-857a-afd1d3b617c9.png)
masterブランチにも、ボタンタグが追加されました。

### コンフリクトを起こす
続いて、masterブランチから、conflictブランチを生やします。
そして、masterブランチでpタグを増やしてコミットします。
また、conflictブランチでh2タグを増やしてコミットします。

この状態でマージします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/7af701dc-60fa-418a-9bb5-c0279a71330d.png)
すると、このように失敗しました。
異なるブランチで、同時に同じファイルを操作することは行ってはいけません。
これが起こってしまった状態を「コンフリクト」といいます。
このとき、conflictブランチで行われた変更を優先させて、マージエディターを閉じた状態がこちらです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/c14a470b-98bb-423b-852e-93ff7710f34b.png)
ステージされています。
コンフリクトを解消させただけでは、マージはされません。
コミットすることで、マージが完了します。

### ブランチを削除する
conflictブランチは、あくまで実験用に生やしたブランチなので、もう必要ありませんから削除します。
"git branch -d ブランチ名"
```
git branch -d conflict
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/a0cc83bf-c19e-4446-99fa-4118d7aff712.png)

### ブランチを作成、切り替えを一回のコマンドで
git branch new_branch -> git checkout new_branch
で、２回打つのは面倒くさい！

"git checkout -b ブランチ名"
今回は、「new_text」を作るとします。
```
git checkout -b new_text
```
かなり楽になりました！

### ここで疑問点
通常、マージ完了時にコミットされるはずなのにコミット履歴が無い…？
拡張機能の「Git Graph」で確認してみると

https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/eabeb944-ae44-46d4-826c-7712e347ac71.png)

ブランチが分かれてない？

https://qiita.com/nnhkrnk/items/7a3409587d5e5c217447

調べてみると、この記事で理由は判明しました。
要は、親のブランチで一度もコミットしないでマージされた場合、生やしたブランチを自動的に親のブランチと同一化されたような状態になるみたい。（fast-foward merge）

:::note info
こうしたくないときは、
```
git merge --no-ff ブランチ名
```
で通常のマージにできる。
:::



### 過去のコミットに戻ってみる
ブランチを変更するときに使っていた"git checkout ブランチ名"ですが、これは"HEAD"というポインタが指すモノを変更するコマンドです。つまり、いつもはブランチに対して指されていた"HEAD"はコミットに対しても指すことができます。

試しに「mistype.js」を「misstype.js」に変更したときのコミットに遡ってみます。
先ほどの拡張機能で、その時のコミットを示すハッシュ値を調べます。（git log より楽なので）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/0954cd32-c0e4-4d6d-b79d-133d1917c02e.png)
"3ca3f4c21157feb22dbbe37f6523521b362d1892"であることが分かりました。
```
git checkout 3ca3
```
すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/72f56d23-278c-46c2-a245-6019cd8ad02f.png)
ワークツリーのファイルが過去の状態に戻りました。
今、"HEAD"はブランチを指していません。この状態を「datached HEAD」と呼びます。
この状態でコミットをするとどうなるでしょうか？
「misstype.js」の内容を変更して、コミットしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/1c348a30-3de6-4023-aea5-73a65d8cbe9b.png)
コミットはできました。が、これは正しい状態ではありません。
仕様的に、ブランチを持たないコミットは時間が経つと消去されます。

### 過去のコミットからブランチを切ってマージする
先ほどの誤った操作を、正しい方法で実行します。
もう一度"HEAD"を"3ca3"に向けます。
コミットがブランチに繋がっていないことが問題なので、ブランチを切ってからファイルを編集すれば正しくコミットの内容が残ります。
ブランチを切ってからJSファイルを編集し、コミットします。
masterブランチに戻ると、このような状態になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/689023bd-bb6c-49b9-af89-9f3df2dda81d.png)
ここで、先ほどのブランチをマージします。すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/2a686190-2533-4dcd-91c5-093949685362.png)
masterのJSファイルに、先ほどの編集がマージされました。

### コミットを取り消す
コミットした後に、追加した変更を無かったことにしたい場合の操作です。
"git revert ハッシュ値"

今回は「index.html」に「index.css」を適用させ、コミットした後、それを無かったことにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/16c639d2-e64a-43c5-ac07-dba66c9fb40b.png)
コミットのハッシュ値は、"9969800ad1d9e49ba354a9e713273c8bf1b74b9c"になりました。
```
git revert 996980
```
コミットメッセージの入力を求めるためエディタが開きます。":wq"で終了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/f5ab0e00-f6e6-437a-b11d-a0d0626a8a17.png)
先ほど追加した7行目が消去されました。
つまり、直前のコミットの状態に戻った状態のコミットが追加されたということです。
revertを用いると、消去したコードの内容もコミットに保存されているので、後から内容を確認することが可能です。

:::note warn
コミットを取り消す機能に、"reset" もありますが、今回はパスします。
:::

## GitHubで共同開発をする
### 環境構築
今回は、Reactで共同開発を行うシナリオを想定します。
まずは「Vite」を利用し、ローカルで環境構築を行います。
各種設定まで完了したら、ローカルレポジトリにすべてコミットします。
そして、左上の「Branch の発行」からリモートリポジトリを作成します。
プライベート、パブリック、どちらでも良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/f3cbb3ca-e6c9-41ee-b29f-84a205f335df.png)
GitHubにプロジェクトがコピーされました。

### 共同編集者を追加する
レポジトリの「Settings」から「Collaborators」、「Add people」を選択し、共同編集者を招待します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/303f153f-e965-4d89-8ceb-48afb6782c3e.png)
招待者には、リモートレポジトリへの参加メールが届きます。
追加されたメンバーには、「Code」の「SSH」のテキストをコピーしてもらいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/dfdeb2d2-d765-4b4c-ae11-c9f9ab8b7cce.png)
そして、プロジェクトを保存したいディレクトリで
```
git clone コピーしたテキスト
```
を実行します。
これで、リモートレポジトリの内容が、メンバーのローカルレポジトリにもコピーされました。

### GitHubでレポジトリを作成、自分で機能追加を行う
機能追加をするので、ブランチを用意します。
プロジェクト名の下にある「Branch」から、ブランチ名を入力し、masterからブランチを切ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/58269e65-cd91-4fef-bfec-65a385fb292a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/bce906fd-be17-412d-b099-c1c29ee97634.png)
VSCodeに戻り、以下を実行します。
```
git pull
```
リモートレポジトリの変更を、ローカルリポジトリに反映する機能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/c9e95626-ad64-4457-829a-133b42e9433f.png)
先ほど作ったブランチが追加されました。
ブランチを切り替え、「componentsフォルダ」を作成し、コンポーネントを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/688c9517-00fc-45ff-9bad-b4dc7833faa6.png)
野菜のリストを表示するコンポーネントを作成しました。
いつも通りコミットします。
そして、
```
git push
```
でリモートリポジトリにも変更を反映します。
masterブランチに切り替え、マージします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/2b4aaf53-22b6-403e-8a02-40e23b661d39.png)

### メンバーが機能追加をする
2つの機能追加を2人並行進行で行います。
#### 管理者側の操作
直前のコミットから、ブランチを2つ用意します。
先に新しいコンポーネントを作成し、mainにマージ、プッシュしました。（黄色のブランチです。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/c8c5a8c4-3bc7-4eda-b2a6-f0c8fdba6264.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/319c0c99-77c8-44d0-af9b-0bdc5e41fd3a.png)
#### メンバー側の操作
メンバーにも同じようにコンポーネントを作成し、プッシュしてもらいました。
リモートレポジトリの管理者以外は、mainにマージすることはできません。
「プルリクエスト（プルリク）」を送る必要があります。
作った新機能に問題が無いか確認してもらい、マージしてもらうのをお願いする機能です。
もう少し詳しいことを説明すると、
新機能を開発したブランチの、最後のコミットを確認・マージしてもらう機能です！
メンバーがGitHubのリポジトリページを見ると、「プルリクエストを送ろう」みたいなボタンが出現しています。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/976b658a-42e7-4969-ba9b-0705a5f36a25.png)
選択するとこのような画面になるので、タイトルと詳細な説明を入力してプルリクエストを送ります。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/af448857-ff8f-4fcf-8bf9-394c7b71de2b.png)

#### 管理者側の操作

リポジトリページから、「Pull requests」へ移動します。
すると、先ほどプルリクエストを送ってもらったブランチが表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/6df0d125-5297-4377-91e7-46f0c079543b.png)
開くと、このようなページに飛びます。
英語は読めませんが、コンフリクトが起こりますよ！って書かれてるのがなんとなく分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/9a074c39-9563-4c5a-9324-37a4bb89032d.png)
「web editor」をクリックすると、こんな画面になります。
どのようなコンフリクトが起こるのか表示してくれています。
ここでコンフリクトを解消させることもできますが、今回は開発者にコンフリクトする部分を消去してもらうことにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/fb357bbc-72f4-4b09-8b67-e7b40e12cf56.png)
前のページに戻って、一番右の「Files changed」に移動すると、ファイルごとに変更を確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/b054756d-408a-4139-b64c-f6072be8ccc7.png)
ファイル名の右端にあるコメントアイコンを押すと、ファイルに対してのコメントができます。ファイル中の行番号にカーソルを合わせると出てくる＋アイコンを押すと、問題のある特定の変更に対してコメントができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/8e693d60-4907-48a4-b831-d2de1f38d735.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/453c809a-7f4d-49f4-bc6a-f4a428627d1e.png)
ページの右上、「Finish your review」を選択すると、変更全体（プルリク）に対してコメントができます。
その祭、コメント・承認・変更を要求、のいずれかの属性を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/e5db00dc-2ad5-420f-b1a7-a3072393ad9d.png)
メンバーにレビューの通知が届き、お願いした修正を行って、コミットしてくれました。
（プルリクエストはブランチに対して行われているので、再度プルリクをする必要はありません！）
再度プルリクのページを訪れると変更内容が変わっています。
が、まだ変なミスが残っています…
ミートボールメニュー（「…」）に、「Edit file」があります。
これぐらいなら自分で直しちゃいましょう！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/d93bef80-a45d-45c9-9e53-854be7024cc5.png)
編集が完了したら、コミットを求められます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/bdc278b3-dbe7-41af-b833-4f819423f9da.png)
そしたらマージしましょう！
「Conversation」からマージができます。
その後、コンフリクトが起きたので消した部分を追加しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/533548d5-ada2-432f-a2c4-13e4e3bc5f52.png)

:::note warn
"おおむね理解する"なので「git fetch」は取り扱わないこととします
:::


### これだけ触れればどうにか運用できそう


## 今どうなってる？
表示画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/cbd78dd6-10b2-4f08-858e-e929edd6edb5.png)
ブランチ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831057/0108fd3f-6ed3-4954-9074-a873be8131fb.png)

## この記事を作りながら手を動かしたリモートリポジトリ
#### Git編

https://github.com/higuchi-learn/git_pra

#### GitHub編

https://github.com/higuchi-learn/react-pre-project