見出し画像

Git基礎学習まとめ

はじめに

はじめまして。たろいもといいます。
私は過去いくつかのバージョン管理システムは経験済みでGitは少し触った程度です。Gitを使った機械的な運用は可能ですが、理解にとぼしかったの少し深堀してまとめてみました。

Gitとその他のバージョン管理システムの違い

・Gitはリポジトリがローカルとサーバーに存在する。
これが既存との大きな違い。ちなみにSVNを使っていた僕は開発初日にリポジトリからプロジェクトをチェックアウトしてそのあとは更新/コミットくらいしかしていなかったのとGitもそうなんだろう、という固定観念を持っていてそれがGitの理解を阻害していました。

・Gitは履歴を差分ではなくスナップショットとして記録する。
正直ふーん、くらいでしたが詳細を理解する上で知っておくと良いと思います。

他にもあると多々あるとは思いますが、まずは上記2点を知っているだけで理解のスピードが段違いに変わってくるかと思います。

Gitの基本的な操作(コミットまでの流れ)

Gitでファイルをサーバーのリポジトリへ登録するまでのイメージになります。

画像2

ワークツリー、ステージ、ローカルリポジトリってなに?
Gitは履歴を管理するための情報をローカルに3つのエリアを持ち、そのエリアに履歴情報を記録しています。ワークツリー、ステージ、ローカルリポジトリはそのそれぞれのエリアの名前になります。
手順は以下のようになります。
①コミットしたい内容ステージに登録
②ステージに登録した内容をリポジトリにコミット

画像2

Gitの基本的なデータ構造

ローカルでファイルを新規作成してローカルリポジトリにコミットするまでのイメージを記載しました。

・ステージに登録(git add)

画像4

登録(git add)はコミットの準備をするコマンドになります。
実際の動きは以下になります。
①ローカルリポジトリにLogic.javaを圧縮した【圧縮ファイル1】を作成
②インデックスファイル【インデックス1】を作成し「①」の【圧縮ファイル1】と元ファイルの【Logic.java】のマッピング情報を記録する。

・リポジトリにコミット(git commit)

画像4

コミット(git commit)は履歴の登録になります。
実際の動きは以下になります。
①ツリーファイル【ツリー1】を【インデックス1】の情報をもとに作成。
②コミットファイルを作成。内容は紐づくツリーファイル、作成者、日時、コミット時にユーザーが入力するコミットメッセージ。ツリーファイル名を記録することで「ツリー名⇒ツリーファイルの圧縮ファイル名」と圧縮ファイルをたどれるようになっています。※RDBみたいですね。

また、【インデックス1】と【ツリー1】は同じ内容なのですがインデックスファイルは上書き、ツリーファイルは新規作成するので結論、両方必要になります。

・再編集してコミット
ファイルを再編集してコミットする。
ここでは、先ほどコミットしたLogic.javaを再編集してステージに登録、ローカルリポジトリにコミットしたときの動きになります。

画像5

【インデックス1】を見ると圧縮ファイル名が【圧縮ファイル2】と更新されているのに対してツリーファイルは【ツリー2】が新たに作成されています。
また【コミット2】には【コミット1】には無い「親コミット」という情報が記録されます。これは直近の最新のコミットを表しています。
つまりコミットは常に一個前のコミットを記録することでその順序性を担保しています。

Gitの基本的なデータ構造 ~まとめ~

・Gitのリポジトリはローカルとリモートがある。
・Gitは履歴をスナップショットで記録する。
・コミットはコミット単位にスナップショットをとり前回コミットをたどれるようになっている。

ブランチ(branch)

ここからはブランチ(branch)についてです。
ブランチは今自分がどのコミットを指しているか、を指し示す目印のようなものです。※ただのポインタです。

まずはワークツリーでファイルを新規作成しその後変更して合計3回のコミットをしたとします。その場合ローカルリポジトリにはコミットが合計3こ作られます。
※以下が3回コミットをしたイメージ

画像6

ここに出てくる【master】がブランチになります。このようにブランチはコミットを指し示すポインタになります。
ちなみにGitではデフォルトでブランチが一つ用意されていてそれがmasterブランチになります。

次は新しくブランチを作ります。名前はmodにします。
すると以下のようになります。

画像7

ここでワークツリーの【master】ブランチのファイルに変更をしてステージに登録、コミットを行います。すると以下のようになります。

画像8

新たにコミットしたため【コミット4】が追加されいます。
また、【master】ブランチは【コミット4】を指し示しています。
一方、先ほど追加した【mod】ブランチは【コミット3】を指したままです。

これはどういうことかというと、今自分は【master】ブランチで作業をしていてそこでコミットしたという意味になります。ちなみに今自分がどのブランチで作業しているか、を表したものをHEADといい図解すると以下のようにになります。このHEADもブランチと同様にポインタになります。画像9

ここで突然バグが見つかったとしましょう。そしてそのバグは【コミット3】で発見されたとします。こういう時はブランチを【コミット3】を指し示す【mod】に切り替えて作業をします。ここではバグ修正を2つ行って2回コミットしたとします。そうすると以下のようなります。

画像10

まずHEADが【master】ブランチではなく【mod】ブランチを指し示しています。これは今、【mod】ブランチで作業をしている、ということを意味しています。またブランチを切り替えたことで【コミット3】からコミットが枝分かれしそれぞれ別のコミットとして切り分けが出来ています。この機能によって複数の開発を同時に行うことができるのです。
これがブランチの意味とメリットになります。

ブランチとHEADについて実際にどのように記録しているのか、実例をつけておきます。

画像11

上記を見て下さい。
まずcat HEADでHEADの中身を表示しているのですがそこにはrefs/heads/masterとあります。これが今自分が【master】ブランチで作業をしているよ、ということになります。

また cat refs/heads/masterで【master】ブランチの中身を見ると
a0bbbfe7d187cb5b24e35a3715a436a815247432
とあります。これは実はコミット時に圧縮したファイルをファイル1、ファイル2と表現していましたが実際はファイルのメタ情報、ファイルの中味を変換したハッシュIDになります。
そのハッシュIDが表示されています。
ローカルの変更状態を確認するgit logコマンドを実行するとコミットはこのハッシュIDで表示されます。

ブランチ(branch) ~まとめ~

・ブランチとはどのコミットファイルを指ししめしたポインタ。
・HEADは今自分が作業しているブランチを指ししめしたポインタ。

マージ(merge)

マージは今作業しているブランチにマージ元のブランチを指定して反映します。マージは3種類あります。
以下でそれぞれをイメージで確認していきます。※ここではすべて【mod】ブランチを【master】ブランチにマージする例になります。

・オートマージ
一般的なイメージに一番近いものだと思います。【mod】ブランチで変更したコミットを【master】ブランチにマージします。個人的にポイントはマージ後、最新の【コミット5】が出来ることでした。確かにマージすれば更新されるのだから当たり前といば当たり前ですが。
※ちなみにコンフリクトについては別で記載します。

画像13

・ファストフォワード
これは【mod】ブランチだけコミットが進んだ状態でマージしたときのマージになります。マージしてもファイルに変更がないことから新たにスナップショットを作る必要がなく結果コミットも作られません。単純にマージ先の参照が変わるだけになります。

画像13

・コンフリクト
最後のマージです。オートマージがGitでは出来ない場合に発生します。
これは「実ファイルの変更内容」が原因です。
以下はマージをしてコンフリクトが起きた場合の例になります。

マージを実行

$ git merge mod
Auto-merging src/Logic.java
CONFLICT (content): Merge conflict in src/Logic.java
Automatic merge failed; fix conflicts and then commit the result.

CONFLICTとありますね。コンフリクトが発生しています。それではどのファイルでコンフリクトしたのかgit statusコマンドで確認しましょう。

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
 (use "git push" to publish your local commits)
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:   Logic.java
no changes added to commit (use "git add" and/or "git commit -a")

both modified: Logic.javaとあります。これは各【master】【mod】ブランチの両方のLogic.javaに変更があってコンフリクトしたよ、といっています。
では実際に対象のLogic.javaをみてみましょう。
【master】ブランチにマージしているので確認するのは【master】ブランチのLogic.javaファイルになります。

画像14

このようにマージ元先の両方の変更が書かれた状態になっています。
見方は
9行目~12行目の「<<<<<<< HEAD ~ =======」がHEAD、つまり現在作業中の【master】ブランチの変更となります。

12行目~16行目の「======= ~ >>>>>>> mod」【mod】ブランチの変更になります。
ここから先は手で編集です。今回は【master】ブランチの変更が正しく【mod】ブランチの変更は不要だったというストーリーで修正します。

画像15

こんな感じですね。あとは登録、コミットして完了でになります。

$ git add .
$ git commit
hint: Waiting for your editor to close the file...
[master 68019b9] Merge branch 'mod' コンフリクトを解消

これで完了です。

マージ(merge) ~まとめ~

・マージはオートマージ、ファストフォワード、コンフリクトの3種類。
・マージ実行時はどこのブランチで作業をしているか確認する。
・マージ実行前にgit statusやgit diff(git diff --staged)で編集中の状態でないか確認する。
・コンフリクトは落ち着いて丁寧に修正する。

フェッチ(fetch)

フェッチはリモートリポジトリの変更をローカルにとってくるコマンドです。複数人で開発していると他の人がリモートリポジトリにプッシュした変更を自身のローカルに取り込む必要があります。

ここでポイントはとってくるのはリモートリポジトリの変更でワークツリーのファイルは更新されません。ここではHogeDTO.javaというファイルを他の人が作ってくれて「プッシュしといたからとりこんどいてや」と言われた時のとしてイメージを書きます。

前提条件を確認
リモートにHogeDTO.javaがプッシュされていることを確認

画像16

ローカルの状態を確認

$ ls
Logic.java
$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
 (use "git push" to publish your local commits)

nothing to commit, working tree clean
$ git branch
* master
 mod

フェッチを実行

$ git fetch origin
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), 772 bytes | 22.00 KiB/s, done.
From https://github.com/gituser0214/projectTest
  b2a661c..723469d  master     -> origin/master
$ git branch
* master
 mod
$ ls
Logic.java
$ git branch -a
* master
 mod
 remotes/origin/HEAD -> origin/master
 remotes/origin/master
$ ls -a
./  ../  Logic.java

remotes/origin/masterというブランチを取り込んでいますがワークツリーに取り込みたかったHogeDTO.javaはまだ取り込めていません。

フェッチして取得してきたremotes/origin/masterブランチに切り替えてみる。

$ git checkout remotes/origin/master
Note: switching to 'remotes/origin/master'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
 git switch -c <new-branch-name>
Or undo this operation with:
 git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 723469d Create HogeDto.java
$ git status
HEAD detached at origin/master
nothing to commit, working tree clean
$ git branch
* (HEAD detached at origin/master)
 master
 mod
$ ls -a
./  ../  HogeDto.java  Logic.java

最後のls -a の結果を見ると無事HogeDTO.javaがフェッチできている事が確認できました。

最後にローカルを【master】ブランチに戻してremotes/origin/masterの情報をマージします。

$ git checkout master
Previous HEAD position was 723469d Create HogeDto.java
Switched to branch 'master'
Your branch and 'origin/master' have diverged,
and have 4 and 1 different commits each, respectively.
 (use "git pull" to merge the remote branch into yours)

$ git branch
* master
 mod

$ git merge origin/master
hint: Waiting for your editor to close the file...
Merge made by the 'recursive' strategy.
src/HogeDto.java | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 src/HogeDto.java

$ ls -a
./  ../  HogeDto.java  Logic.java

最後のls -a でワークツリーにHogeDTO.javaが取り込めている事が確認できました。

フェッチ(fetch) ~まとめ~

・フェッチがリモートリポジトリの変更をローカルリポジトリに取り込んでいる。
・ワーキングツリーにファイルを取り込むには取り込んだリモートリポジトリのブランチをマージする。

使いそうなコマンド集

最後に現場で使いそうならコマンドを並べてみます。
メモ程度に書きます。この瞬間レガシーなのでなので詳細はググりましょう。

・変更の登録

## ファイル名を指定
$ git add <ファイル名>

## ディレクトリ名を指定
$ git add <ディレクトリ名>

## 変更全て
$ git add .

・登録の取り消し(最新のコミットに戻す)

## ファイル名を指定
$ git reset HEAD <ファイル名>

## ディレクトリ名を指定
$ git reset HEAD <ディレクトリ名>

## 変更全て
$ $ git reset HEAD .


・コミット

## コミット
$ git commit

## コミットコメントを指定
$ git commit -m
 "<コミットコメント>"

## 
$ git commit -v 

・クローン

$ git clone <URL>

## リモートブランチ指定
$ git clone -b <ブランチ名> <URL>

・フェッチ

$ git fetch

## リモートブランチ指定
$ git clone -b <リモートブランチ名> <URL>

・マージ

##ローカルリポジトリ同士のマージ
$ git merge <ブランチ名>

##リモートリポジトリのブランチとマージ
$ git merge <リポジトリ名>/<ブランチ名>

## origin/masterの例
$ git merge origin/master

・ローカルの変更を確認

$ git log

## ファイル指定
$ git log -p <ファイル名>

## 件数指定
$ git log -n <件数>

## 簡易(1行表示)
$ git log -- oneline

・変更差分の確認

## ワークツリーとステージの差分確認
$ git diff

## ステージとローカルリポジトリの差分確認
$ git diff --staged

・ブランチ

## ブランチを確認
$ git branch -a

## ブランチの切り替え
$ git checkout <ブランチ名>

## ブランチの作成
$ git branch <ブランチ名>

・コマンドのエイリアス

$ git config --global alias.<エイリアス> <コマンド>

最後に

初めてのnoteということでいろいろお勉強いなりました。
あと長文になってしまった。
でも、ここまで読んでくれて本当に本当にありがとうございます!!!
感謝です。

画像17

はぁ。。ペンタブがほしいなぁ





この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
ありがとうございます♪
7
taroimoかいてたろいもと読みます。日々学んだことをなるべくまとめていければとおもいます。 最後にフォロー&スキを押していただけると励みになります。 記事内の質問・疑問はコメントへどうぞ。 落ち着いたらアイコンをちゃんとしよう。
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。