October 14, 2023

Viiisit [Git] - Time Machine!

#git

What is Git?

Git 像是時光機,能保存檔案,也能回顧過去的紀錄,是一種記載著連續性歷史紀錄的系統。

有接觸 Git 工具的人可能會說:「 Git 是一種分散式版本控制系統。」

聽到這會覺得,每個字都看得懂,但是,仍然不知道實質上是做些什麼?

來看看自己是不是有過數個檔案、數本報告改了又改,檔案命名換了又換,到最後要繳交的時候,根本搞不清楚自己要的最終版本是哪一個? 又或者,當與別人一起共同編輯資料,如果對方沒說明,不會知道資料哪裡變更了?

這種時候,Git 真的就是最好的幫手,因為他可以紀錄每個檔案在何時被新增、何時被修改或者被誰異動過,就這樣紀錄著每個檔案!

首先,Git 免費且開源 (Open Source) ,大家都可以搜尋 Git 網站,下載並使用!

當知道 Git 會保留歷史紀錄,使我們完全可以在發生狀況的當下,一一檢視哪裡出問題以及該找誰負責,對於想要有效率地檢視檔案內容哪裡有變動的人來說,真的是一個很方便的工具!

而,Git 之所以稱之為分散式版本控制系統,源自於他不需要伺服器軟體,就可以運作版本控制,不用擔心伺服器壞了或者沒有網路的環境就無法運作。一般而言,大部分 Git 的操作都在本地數據庫 (Local Repository) 裡執行,也就是在自己的電腦上完成操作,所以使用上可以不用過於擔心。


Environment Settings (MacOs)

Download git

Step 1. Open Terminal

Step 2. Initial setting

Step 3. git init

Step 4. git status

How it works:

Working Tree or Changes not staged for commit
Untracked files or Modified files
git add >

Staging Area
Changes to be committed
git commit >

Repository
Committed


Commands

Working Tree

1
2
3
git add                                - add untrack files to staging area
git add . (. = here) - add all the files here
git add --all or git add -A - add all the files

Changes not staged for commit

1
2
git restore                            - discard the changes in working tree
git restore . - discard all the changes here

Staging Area

1
git commit -m "message"                - commit tracked files to local repository

Local Repository

1
2
3
4
git clone 'URL'                        - fetch the online sources URL(such as GitHub) to the local repository

git pull - fetch and merge the remote repository to my files
git push - push to remote repository

Useful Commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
git blame `file name`                  - log out the file information
git branch - see all the branches
git branch `branch name` - add a new branch
git branch -m <old_name> <new_name> - rename the branch
git branch -d `branch name` - delete a branch

git checkout - save the deleted file & switch the branch

git log - log out the records
git log -- oneline - log out the simplified records
git log -p `file name` - log out the specified file record
l - show the detailed list
ls - show the file name
ls *.html (`*` means all) - show all the HTML file

git merge -m “merge message” - merge branches (merge consults)
git mv - renamed the file in git

git rebase - change the base of branches
git reflog or git log -g - see all the edited records
git reset HEAD, branch, commit - gets rid of all the current staged files and gives us control over where HEAD should point to.
git reset HEAD\^ - return the previous one move
git reset HEAD~`number` - backward to wanted moves
git reset HEAD\^ --mixed - (drfault) the hidden file will go to working tree
git reset HEAD\^ --soft - the hidden file will go to staging area
git reset HEAD\^ --hard - the hidden file will disappear
git restore - save the deleted file (return to the previous move)

git rm - remove the file from git
rm - remove the directory in git
rm *.html - remove all the html files
rm -rf - force to remove the ./git directory in the folder

git switch - switch the HEAD of branch
git switch `commit number` --detach - switch the HEAD of commit files

Advanced 進階說明

釐清 git commit 操作

git commit 並不是將暫存區的檔案清空再放入資料庫。
實際上,git commit 是將暫存區的內容保存到本地資料庫中,創建一個新的提交(commit)。

當使用 git add 命令將修改的檔案加入暫存區後,
這些修改的檔案會成為下一個提交(commit)的一部分。

當執行 git commit 時,
Git 會將這些暫存區中的檔案儲存為一個新的提交,
並將其保存到本地資料庫中。

如果已經 git add 檔案了,但在還沒 git commit 之前刪除,有辦法復原嗎?

可以的!

git add 指令會將文件的內容加入到 Git 的暫存區(index)中,
暫存區是一個存放即將提交的文件和更改的區域。

在執行 git add 後,Git 將文件的內容儲存到暫存區中,
但尚未正式提交到 Git 儲存庫的歷史記錄中。
因此,即使沒有進行 commit,文件的更改仍然可以在暫存區中找到。

當使用 git restore 或 git checkout 等命令時,Git 會根據暫存區中的內容來恢復文件。
暫存區允許在 commit 之前對文件進行檢查和修改,並可以根據需要將這些更改提交到儲存庫中。

最後,當執行 git commit 命令時,
Git 將暫存區中的內容正式提交到儲存庫中,並創建一個新的 commit 紀錄。
而,這些更改就成為了儲存庫的歷史記錄中的一部分,
並且可以通過 commit 的 SHA-1 鍵值來追蹤和檢查。

git rebase

git rebase --interactive commit_number -> git rebase -i commit_number

EDITOR='code --wait' git rebase -i commit_number
這項指令可以在 VSCode 編輯(要注意順序跟在 git graph 相反)
(如果沒有寫 EDITOR='code --wait' 就會進入 Vim 的編輯器去編輯)

當執行剛剛的指令後, VSCode 會開啟一個檔案,
並在想要更改的 commit 將 pick 改成 reword 存檔並關掉檔案,
VSCode 就會跳出另一個視窗讓你修改 commit。

另外在介紹另一個動詞,squash !
squash 是一種合併(merge)多個連續的 commit 記錄成單一的 commit 的操作。
想要更改的 commit 將 pick 改成 squash 存檔並關掉檔案,
VSCode 就會跳出另一個視窗讓你修改 commit,這時就能將多個 commits 合成一個 commit。

Remark:
在執行 EDITOR='code --wait' 之前,
透過 Settings 去尋找:
確保有將 VSCode 開啟 Shell Command: Install 'code' command in PATH

git rebase vs git merge

在不同的分支上開發新功能、解決 bug,完成後就需要把功能合併回主要的分支。
合併分支的方式主要有兩種:mergerebase
merge 的特點是不改變過去 commit 的歷史記錄,而 rebase 會在合併的當下重新改寫過去 commit 的歷史記錄。

merge conflicts! 解衝突!

當執行 git rebase 時,可能會遇到合併衝突(merge conflicts)。
這些衝突發生在 Git 嘗試將一個分支的變更應用到另一個分支的過程中,但無法自動解決變更之間的衝突。
在專案上其實很常見,所以要有耐心的且知道大家的 code 在寫什麼才不會弄錯不要的 code 到專案上唷!

當 Git 嘗試將兩個分支上的相同文件的相同部分合併在一起時,如果他們的內容不一致,就會產生內容衝突。

1
2
3
4
5
<<<<<<< HEAD
This is the new content from the current branch.
=======
This is the old content from the branch being rebased.
>>>>>>> branch-name

在這種情況下,你需要手動選擇要保留的內容,然後刪除 <<<<<<<=======>>>>>> 標記。
解決衝突後,你需要執行 git add 以加入已解決的文件。
然後執行 git rebase --continue 以繼續 rebase 。

除了上述之外,文件刪除/重命名衝突、檔案和目錄移動衝突,也是會需要我們手動解衝突的狀況!

整理一下解決合併衝突的一般步驟:

  1. 手動編輯衝突的文件以解決衝突。
  2. 使用 git add 將已解決的文件標記為已解決。
  3. 使用 git rebase --continue 繼續 rebase 操作,直到沒有其他衝突。

cat 指令、pipe (|) 符號

1
2
3
4
5
cat `file_name`             - 可以看到檔案
cat index.html

git cat-file commitnum -t - 可以看出是什麼類型
git cat-file commitnum -p - 可以看出內容物

| pipe

用於將一個命令的輸出作為另一個命令的輸入。兩者之間有前後關係!
In Unix-based systems, the pipe symbol is extensively used in the command-line interface (CLI) to chain multiple commands together, allowing the output of one command to be processed or used as input for another command.

1
2
cat index.html | grep n 可以抓出有包含 n 的字
cat index.html | grep n | grep d 可以抓出有包含 n 的字以及包含 d 的字
1
cat index.html | git hash-object --stdin

cherry-pick

在 Git 使用 cherry-pick 指令的場景通常是想將特定的 commit 變更合併到當前的分支中,
而不是將整個分支合併。這可以選擇性地導入某個 commit 的修改,而不需要合併整個分支的歷史。

cherry-pick 的基本語法如下:

1
git cherry-pick <commit-hash>

其中 <commit-hash> 是你擷取的 commit 的 SHA-1 值。
(我通常直接使用 VSCode 裡的 Extension -> Git Graph 來操作 cherry-pick)

使用 cherry-pick 之後,Git 會將該 commit 的變更應用到當前分支中,並創一個新的 commit。這個新的 commit 和原始 commit 的內容是相同的,但它有不同的 SHA-1 值,
因為它是在不同的時間和不同的歷史上提交的。

需要注意的是,如果你使用 cherry-pick 來選擇並擷取某個 commit,
則這個 commit 及其相關的變更會被完整複製到目標分支中。
這包括新增的檔案、修改的檔案和刪除的檔案等。
因此,在使用 cherry-pick 時,
需要確保這個 commit 的變更不會與目標分支中的其他變更產生衝突。


迅速地介紹完 Git ,希望大家在使用上都能得心應手!