Day05 git 柳暗花明


內容標題

  1. 切換版本
  2. 分支
  3. detached head
  4. 合併
  5. 小結語
  6. 參考資料

切換版本

現在我們要來了解如何切換版本了!首先,我們先來認識 Head!我這裡延續上個資料夾 discuss_git 來做展示。我們先輸入 git log 來看一下版本的資訊:

$ git log
commit 27f1e3e8264a97374afa1a78613a0274e6d76929 (HEAD -> master)
Author: Justin <justin900429@gmail.com>
Date:   Tue Feb 18 16:29:35 2020 +0800

    Modified test.cpp and create test.js

commit 9536cf16258f9d5451f735137e3679c8e8177cd7
Author: Justin <justin900429@gmail.com>
Date:   Mon Feb 17 17:20:10 2020 +0800

    First commit

目前我們 commit 兩次,所以會看到兩個資訊。看到最後的 commit,會發現最後有一個 (HEAD->MASTER),這裡,我們先解釋一下 HEAD 是什麼,HEAD 代表的是我們目前所在的 commit。如果我想切換到舊的 commit,這時就要用 checkout 指令了!指令如下:

$ git checkout [SHA-1]

這裡的 [SHA-1] 不需要輸入全部,基本上前五個就行了!例如:

$ git checkout 9536c
Note: switching to '9536c'.

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 9536cf1 First commit

這邊會出現一個關於 detached head 的警告,因為目前沒有 分支 指著這個 commit,如果在這個點commit 的話,是一個很危險的事,我們之後再詳細說明,這邊我們就暫且不理他。這時候我們就會在我們第二次 commit 之前了!我們用 git log 來查看一下:

$ git log
commit 9536cf16258f9d5451f735137e3679c8e8177cd7 (HEAD)
Author: Justin <justin900429@gmail.com>
Date:   Mon Feb 17 17:20:10 2020 +0800

    First commit

這裡只會顯示 First Commit,因為新的 commit 只會連舊的 commit。這時我們輸入 ls

$ ls   
hello       test.cpp

我們資料夾內容也都變回第一次 commit 的內容了!如果我想開發第二個版本就能從這裡開始!下面我們會講關於 分支

分支

接下來我們要來介紹分支!還記得我們上面所提到的 master 是一條分支嗎?那分支到底是幹嘛的呢?我們看個情境:

今天教授要小愷做出兩種不同版本的遊戲,第一種的主角喜歡吃水餃,第二種的主角喜歡吃水果。但是小愷心想要是兩種都做一次不是麻煩,畢竟只有主角的地方有不同,其他的地方都一樣,因此小愷聰明的使用分支!

看完這個情景,就可以知道分支就是開發出不一樣的東西,或是有不同人一起開發,所以需要分支的出現。這裡我們用圖說明:
drawing
這裡我都只畫出 commitSHA-1 是示意用的。首先,master 就是我們的主分支,同時,我們的 HEAD 現在指到了最新的 commit。現在我們在 commit 一次:
drawing
這邊,masterHEAD 都遞進了一次,如果 小愷 決定在這時候開2個分支,他就會輸入:

$ git branch [name_for_branch]

例如:

$ git branch dumpling
$ git branch fruit

此時,就會多出 dumplingbranch 兩條分支,圖會像這樣:
drawing
我們可以看到,在原本的 commt 上面的多了兩個分支的標記。接著,小愷決定先開發 dumpling,所以他先換到那個分支,換分支的指令是:

$ git checkout [branch_name]

例如:

$ git branch dumpling

此時,我們的 HEAD 會跑到 dumpling 上:
drawing
代表說,我們現在如果 commit,都會構成 dumpling 這個分支。例如我們 commit 新的東西,圖會像這樣:
drawing
我們的 masterfruit不會跟著移動。開發完 dumpling 後,小愷要前往 fruit 開發,此時指令是:

$ git checkout fruit

此時的 HEAD 會指向我們的 fruit。在 fruit 這個分支 commit 新的東西,此時圖會像這樣:
drawing
看到這裡大家應該對 分支 有點概念了!另外,如果想要查看有哪些分支,並且現在身處哪個分支,可以輸入:

$ git branch

會顯示:

  dumpling
* fruit
  master

每一列都是一個分支,且所處分支前會有一個星號。到這裡,大家應該對分支有更好的瞭解!接下來,要講到關於detached head 這個問題。

detached head

我們前面有提到關於這個問題的產生,在於沒有分支指向 我們前往的舊 commit。這裡可能就會有人問說:“這個commit 不是我們 master 分支的一部分嗎?” 確實是! 所以我們再說的仔細一點:“從這個 commit 所產生的新 commit 沒有分支指著他”。那這會有什麼問題嗎?就是我們如果要前往這個新 commit 點只能靠 SHA-1 前往,而且一旦離開這個 commit,他的 SHA-1 就不容易找到了。圖的話會像這樣:
drawing
這邊 78 就是我們新的 commit,如果我們沒有指著他,他的 SHA-1 就會很難找到,不像我們要回到 12,可以直接輸入:

# 因為有 master 幫我指著這個分支,所以回到 master 就是回到 12
$ git checkout master

git log 也沒辦法,因為沒有 commit 指著他!當然,解決的方法非常簡單,用分支就行!假設我們想要在分支 34 commit,可以先移到這個 commit 並輸入:

$ git branch [name]

34 的分支就建立完成,如果知道這個 commitSHA-1,也可以不用移,直接輸入:

$ git branch [name] [SHA-1]

之後,先用 checkout 移動到那個分支,就可以在那個 commitcommit 東西了!自然 detached head 也被解決了!

合併

教授突然想到,主角喜歡水餃與水果好像不衝突,於是要小愷把兩個版本合併,於是小愷將兩個分支合併。

這裡我們可以看到,當所需的版本要融合,或是要統整所有版本,就可以利用合併來處理!現在,我們用之前創立的 discuss git 來做示範!首先,我在最新的 commit 創立兩個分支叫 dumplingfruit。並且在 fruit 創立 test.md、在 dumpling 創立 test.java,之後都個別 commit,讓HEAD 留在 dumpling。圖會像這樣:
drawing
這時候如果我們要合併,就只要輸入:

$ git merge [branch]

例如,當我在 dumpling 分支,想要合併 fruit 分支,就要輸入:

$ git merge fruit

此時會跳出編輯器,請求使用者輸入紀錄,儲存後便完成合併了!此時會長這樣:
drawing
這邊可以發現,git 為了處理合併的內容,做了一個新的 commit,此時我們用 ls 查看:

$ ls
hello       test.cpp    test.java   test.js     test.md

發現原本兩個分支的檔案都包含了!

誰併入誰有差別嗎? 基本上沒有! 但如果某個分支是最主要在使用的話,最好可以把它當成主要對象,因為 git log 後,可以看到主支的所有歷史,而副支的歷史是看不到的,反之也是!或是說哪個分支曾經有寫入一些重要的資料,也把他當主支,理由也是相同的!

剛剛我們看到的 merge 是很順利的!但假設我們剛剛的檔案有衝突呢? 這裡我重新開始,假設我們分支剛建立好,接著在兩個分支都建立 test.txt,並且在dumpling 的分支輸入 Justin is handsome.,同時在 fruit 的分支是輸入 Justin is very handsome.。此時 HEADdumpling,此時我輸入:

$ git merge fruit
CONFLICT (add/add): Merge conflict in test.txt
Auto-merging test.txt
Automatic merge failed; fix conflicts and then commit the result.

這裡出現的錯誤訊息就是代表檔案有衝突發生,此時我們要作出抉擇倒底是要哪個版本的!假設我們最後決定留有 very 的版本,那我們需要開啟目前所在的檔案做修正!開啟後會出現:

drawing
這邊可以看到 <<<<< HEAD 下一行的資料代表是所在分支的版本,也就是 dumpling 的版本。而 >>>> fruit 的上一行代表是 fruit 的版本,中間的 ===== 是分隔符。此時我們只要刪到剩:

drawing
儲存退出後,重新 add 並且 commit,而 commit 的內容就是合併的紀錄commit 完之後合併就解決了!此時輸入:

# --oneline 代表只顯示一行的資訊 --graph 代表以圖像化的方式顯示
$ % git log --oneline --graph
*   0114840 (HEAD -> dumpling) Make the very there.
|\  
| * eab77df (fruit) Add test.txt
* | 95c20fd Add test.txt
|/  
* 27f1e3e (master) Modified test.cpp and create test.js
* 9536cf1 First commit

就可以發現已經合併了! 分支與合併暫時先告一段了!其實 合併 方式還有其他,例如 rebasecherry-pick等方法,但這些就留個大家深入研究了!

小結語

今天的內容稍多,希望大家可以消化消化,有任何問題或發現有錯誤,需要麻煩各位在下方留言告訴我~明天我們要來認識 github 並暸解 pull push clone 等遠端指令!

參考資料

  1. 為你自己學 Git [高見龍 著]
  2. missing semester from MIT
  3. merging to branches, doesn't matter which one you merge into?
#Git
你今天 git 了嗎?
這七天的介紹是希望能讓大家認識 git 是什麼以及基礎操作 git 的模式,同時也會包含 git 運作的流程與內容,最後幾天也會分享 GitHub 是什麼,以及如何使用 GitHub 的功能等。






Related Posts

[19] 原生功能 Natives - Symbol、Native Prototypes

[19] 原生功能 Natives - Symbol、Native Prototypes

[ week 1 ] 前端、後端的差異?

[ week 1 ] 前端、後端的差異?

DAY 01 : 資料結構緒論

DAY 01 : 資料結構緒論

[Day07] Monad

[Day07] Monad

Command line 常用指令

Command line 常用指令

Python Table Manners - 管理繁瑣任務

Python Table Manners - 管理繁瑣任務



Comments