2022-12-03
git基礎(実践)
Written by: @ekusiadadus
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 などで、チーム開発するときは、意味のあるメッセージを残すように心がけましょう!
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.変更履歴を遷移する
HEAD は、今どこにいるかを示す参照でした。 いまは、
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
とすると、ブランチが cat になります。
animal.cpp をコマンドライン引数で、“cat”を持つとき、“Meow!”と標準出力するように書き換えます。
git にスナップショットを保存します。
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)---
マージが成功すると、このようなログになります。
(実行するとこんな感じ)