git基礎(実践)
環境:Windows10,WSL Ubuntu 18.04, Git2.28.0, (Backlog git)
言語:C++(好きな言語使ってください)
実際に、理論編の話を、1からgitで開発していきます。
§1 gitリポジトリを作成する
1.ディレクトリを作成する
gitリポジトリを作成するには、ディレクトリが必要です。/home/[user]/以下に"demo"ディレクトリを作成します。
Ubuntuでは、mkdirでディレクトリを作成できます。
git initコマンドが成功すると、.gitディレクトリ(隠し)が作成されます。
ls -la .git
として、.gitのフォルダ構成をみると以下のようになっています。
オブジェクトと参照の存在が確認できます。(objectsとrefs)
この2つのディレクトリに、全てのリポジトリデータがこの2つのディレクトリの中に格納されています。
git hlep init
と打ち込んでみましょう。そうすると、initコマンドの詳細が見ることができます。
git status
と打ち込んでみましょう。そうすると、今何が起こっているのかを確認することができます。
現状は、なにも操作を行っていないので、"No commits yet"となっています。
git基礎(理論)で、そのコマンドがどのような操作(DAGであるgitの履歴に対する処理)を行っているのかを考えてみましょうと、書きましたが、現状はDAGがただ始点のみで、辺が張られていないので、No commits yetとなっています。
2.ファイルを追加する
gitリポジトリにファイルを追加してみましょう!
"hello world"という文字列内容の書かれた、hello.txtを作成します。
demo\<root directry>
|
+-hello.txt(blob, contents="hello world")
さて、これを新しいスナップショットとして、このディレクトリの一番最初の状態として、変更履歴を保存します。
git snapshot
git には上のコマンドは存在しません。
現在のディレクトリ全てをスナップショットとして保存するようなコマンドはありません。理由はいくつかありますが、gitは、次にどのような変更をスナップショットに加えるかというオプションを与えて、より柔軟な管理方法を提供するためです。
gitは、ステージングという概念を持っています。gitは、次のスナップショットにどのような変更を加えるかというオプションを与えます。
git status
上から、gitは、リポジトリに変更があったことを認識していますが、それをスナップショットに加えることはしていません。
なぜなら、これは、ユーザにこの変更を次のスナップショットに加えるか、否かのオプションを与えるためです。
git add hello.txt
変更が追加されました。これは、スナップショット(git commit)にこの変更を加えるという意味です。
git commit
デフォルトエディタが立ち上がり、コミットメッセージを入力します。コミットメッセージは、あとからなぜその変更を加えたのかが一目でわかるようなメッセージであるべきです。しかし、今回はテストなので"Add hello.txt"としています。
よいコミットメッセージの書き方を載せておきます。OSSなどで、チーム開発するときは、意味のあるメッセージを残すように心がけましょう!
Write good commit messages!
Even more reasons to write good commit messages!
commitに成功すると、上のようになります。
ここで、[master(root-commit)f75edd5]のf75edd5は今のcommitのハッシュ値です。ここでのcommitは正確には、treeのポインタを保持しています。
git cat-file -p f75edd5
treeのハッシュ値が保持されています・・・。
更に、
git cat-file -p 68aba62e560c0ebc3396e8ae9335232cd93a3f60
git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
と打つと、上のようにhello.txtの変更内容がわかります。
つまり、"git commit"は、参照の集合をスナップショットとして保存していることがわかります。
3.ログを確認する
gitのlogコマンドは、変更履歴を視覚的にわかりやすく表示してくれます。
git log
上のように、git log は変更履歴を表示してくれます。現状は、一つのcommitしかないので、一つの変更履歴のみ表示されます。
新しくcommitしてみましょう。
echo "another line" >> hello.tx
git add hello.txt
git commit -m "Add another line in hello.txt
git log
現状は、2つのコミットしかないですが、git log になんのオプションも書かないと線形的に書かれます。
git log --all --graph --decorate
のようにオプションを付けることでより視覚的にわかりやすくログを表示してくれます。
左辺の赤い線がグラフの辺を表しています
4.オブジェクトと参照
これを見ていきましょう。
git基礎(理論)でも紹介しましたが、gitではハッシュ関数によって暗号化された参照を人間にとってわかりやすくマッピングします。
masterとは、gitリポジトリを作成したときに必ず作成される、最新のメインブランチの特別な参照です。今回は、masterは、 3800812708f08da07746b7800b880050590ee71eのポインタです。
HEADも、gitリポジトリの特別な参照です。HEADは、今自分がいる場所を示しています。
5.変更履歴を遷移する
commit 3800812708f08da07746b7800b880050590ee71e (HEAD -> master)
ここにいます。
git checkout f75edd575792baddf801cf3f7e1479f1b9a6da14
この状態で、"cat hello.txt"とすると、hello.txtに"another line"を加える前の状態に戻ります。
また、HEAD(今いる場所)が、ひとつ前のスナップショットに戻っていることに注目してください!!
git checkout master
とすると、最新のスナップショットの場所に戻ります。(masterは、最新のスナップショットの参照)
hello.txtも、最新の状態になっていることが確認できます。
git add コマンドを行わずに、git checkoutを行うと変更履歴が保存されていない状態で、状態遷移をすると、blobやtreeが書き換わってしまうので、注意してください。
6.diffコマンド
最新のスナップショットの状態で、hello.txtの内容を書き換えてみましょう
git diff コマンドでは、現在のスナップショット(HEAD)から今の状態で、変更された内容を出力します。
オプションとして、
git diff ポインタ1 ポインタ2 ファイル名
でポインタ1の示すスナップショットと、ポインタ2の示すスナップショットのファイルの変更された内容を出力します。
7.ブランチ
"Hello" を出力する簡単なC++(好きな言語でいいです)を作成します。
そうして、新しく、"cat"ブランチを作成します。
git checkout -b cat
新しいブランチ"cat"が作成されたのが見えます。
HEADがさしているmasterが現在のブランチで、新しいスナップショットを作成したときは、masterに変更履歴が加えられます。
git checkout cat
animal.cppをコマンドライン引数で、"cat"を持つとき、"Meow!"と標準出力するように書き換えます。
catブランチにcat関数を加えたので、masterブランチとは違う状態にいます。
このような状態を,parallel programmingと言ったりします。
同様にして、dogブランチを追加して、コマンドライン引数にdogを持つとき、"Woof!"と出力するように書き換えます。
上のログを見るとグラフ構造がよくわかると思います。これもgit log オプションのおかげです。
図を書くと
〇<---〇<---〇(master)+〇cat
|
+〇dog(HEAD)
のようにmasterから、dog,catが分裂しています。
これから、masterにdog,catブランチを統合していきます。
まず、HEADをmasterにします。
その後、
git merge cat
でcatブランチをマージします。
この状態で、animal.cppはcatブランチのanimal.cppとなっています。
次に、dogブランチをマージします。
CONFLICTが出ました。これは、二つの平行するブランチのマージの衝突が起こったことを意味します。Auto-mergingが行われていますが、この衝突が起こった時はこの課題を修正することが必要です。このような、マージの衝突が起こった場合に使うツールがgitに入っています。それが、git mergetoolです。
このツールを使うと、上のようにマージの衝突の詳細を比較してくれます。
このツールを使って、修正を施していきます。
今回は、main関数内のif文をelse ifに書き換える等の修正を施さなければならないです。
そのあと、gitにmergeを続けてもらいます。
git merge --continue
〇<---〇<---〇(master)+〇cat---
| |<---〇master(cat,dogマージ)
+〇dog(HEAD)---
(実行するとこんな感じ)