0%

Git筆記part2

1. 檢查版號指令

1
git --version

2. 使用者設定指令

1
2
git config --global user.name "kite"
git config --global user.email "kite@hotmail.com"

如果不小心設定錯誤名稱,再執行同樣的指令,就可以接做修改囉。

3. 查看config

如果要找到實際檔案的修改位置以windows來說在使用者目錄底下的.gitconfig

image-20211002191613907

或者下指令也是可以的

1
git config --list

4. 縮短指令

可以把常用的指令進行縮寫設定

1
2
3
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status

設定完之後,之後就打指令git co 就可以代表git checkout。這些設定儲存在.gitconfig裡哦。

5. 刪除指令

以指令方式

1
git config --global --unset alias.co

或者去打開.gitconfigt檔進行編輯也是可以的

6. 小技巧

1
2
3
4
[alias]
st = status
sample_code = clone https://github.com/kitefree/crud_test_sync_db.git
code = !code

可以用 sample_code 的方式去抓一個固定的git 內容

code = !code ,只要前面加驚嘆號就是呼叫外部程式的方式

6.1. 常用指令參考

1
2
3
4
5
6
7
8
9
10
11
[alias]
s = status
co = checkout
cob = checkout -b
del = branch -D
br = branch --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(contents:subject) %(color:green)(%(committerdate:relative)) [%(authorname)]' --sort=-committerdate
save = !git add -A && git commit -m 'chore: savepoint'
undo = reset HEAD~1 --mixed
res = !git reset --hard
done = !git push origin HEAD
lg = !git log --pretty=format:\"%C(magenta)%h%Creset -%C(red)%d%Creset %s %C(dim green)(%cr) [%an]\" --abbrev-commit -30

7. 也可以在系統層級自創指令

~/.bash_profile

1
alias hello="echo Hello Boudhayan!! How are you?"

img

8. 也可以使用shell function 包裝git 指令

1
2
3
function gcp() {
git commit -am "$1" && git push
}
1
gcp "initial commit"

可以參考 An intro to Git Aliases: a faster way of working with Git 這篇

9. 依專案設定使用者指令

1
2
git config --local user.name Snow
git config --local user.email Snow@gmail.com

我們可以看到在專案底下的.git\config 檔案打開,這邊就多一個user設定

image-20211003100029940

10. 全新開始

1
git init

11. ⽬錄不想再被 Git 控制?

砍掉.git資料夾就好囉

12. 確認目錄的狀態

這張圖有助於了解後續指令的相關概念

查看來源圖片

1
git status

增加或修改檔案預設會在Unstaged files狀態

13. 追蹤檔案(stage file)

13.1. 所有檔

把目前所有更新的檔案都加入到Staged files 狀態可使用以下指令

1
git add .
1
git add -A

或者是

1
git add --all

其實 git add .git add -A 效果是不同的

13.2. 單個檔

git add file name

1
git add index.html

13.3. 多個檔

git add file1 file2

1
git add index.html readme.md

13.4. 針對副檔名批次追蹤

1
git add *.html

14. 提交到倉庫

1
git commit -m "message"

下完這個指令之後是提交到local repo 倉庫

15. 歷史記錄

1
git log

image-20211003213017296

1
git log --graph

image-20211003212950939

16. 如何自定指令

比如hexo 這樣

17. git 重要四個物件

blobtreecommittag

blob(binary large object)

查閱物件常用指令

1
2
3
4
git cat-file<SHA1>
-t (git object type)
-s (git object size)
-p (git object contnet)
1
git cat-file -t 
1
git cat-file -p
1
git cat-file -s

17.1. commit 物件

舉例來說:

1
2
3
4
5
6
7
8
9
10
11
kite-nb@DESKTOP-ORUP4EV MINGW64 ~/Downloads/gitTest/.git/objects (GIT_DIR!)
$ git cat-file -t ef81
commit

kite-nb@DESKTOP-ORUP4EV MINGW64 ~/Downloads/gitTest/.git/objects (GIT_DIR!)
$ git cat-file -p ef81
tree 8151fedb3b9ed570e97ec2c51a878b717c446343
author kite <kitefree@gmail.com> 1686465526 +0800
committer kite <kitefree@gmail.com> 1686465526 +0800

first commit

17.2. tree 物件

舉例來說:

1
2
3
4
5
6
7
8
kite-nb@DESKTOP-ORUP4EV MINGW64 ~/Downloads/gitTest/.git/objects (GIT_DIR!)
$ git cat-file -t 8151
tree

kite-nb@DESKTOP-ORUP4EV MINGW64 ~/Downloads/gitTest/.git/objects (GIT_DIR!)
$ git cat-file -p 8151
100644 blob b5b9ef6c6a00ef52d45f5e557c6a71e4d1367681 file.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file02.txt

18. 目前GIT 使用的是sha1的雜湊演算法

檔案內容碰撞的實例

1
2
3
$ shasum shattered-*
38762cf7f55934b34d179ae6a4c80cadccbb7f0a *shattered-1.pdf
38762cf7f55934b34d179ae6a4c80cadccbb7f0a *shattered-2.pdf

如果用 sha256 就不會碰撞

1
2
3
$ shasum -a 256 shattered-*
2bb787a73e37352f92383abe7e2902936d1059ad9f1ba6daaa9c1e58ee6970d0 *shattered-1.pdf
d4488775d29bdef7993367d541064dbdda50d383f89f0aa13a6ff2e0894ba5ff *shattered-2.pdf

檔案名稱沒有碰撞

1
2
3
$ git ls-files -s
100644 ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0 0 shattered-1.pdf
100644 b621eeccd5c7edac9b7dcba35a8d5afd075e24f2 0 shattered-2.pdf

參考網站:https://shattered.io/

1
2
3
4
5
6
7
$ git cat-file 8982 -p
tree 2b3149179c8e3ecf0bbedf6816141cd4c6fe03c9
parent 98322f878cfa4ca296318fe55f03483555a7e40c
author DESKTOP-I40U13Q\kite <kitefree@gmail.com> 1686837812 +0800
committer DESKTOP-I40U13Q\kite <kitefree@gmail.com> 1686837812 +0800

add folder01

19. Git 裡的四種物件的關係

image-20230616085021570

  1. 把檔案加入 Git 之後,檔案的內容會被轉成 Blob 物件儲存。
  2. ⽬錄以及檔名會存放在 Tree 物件內,Tree 物件會指向 Blob 物件,或是其它的 Tree 物
    件。
  3. Commit 物件會指向某個 Tree 物件。除了第⼀個 Commit 之外,其它的 Commit 都會指向
    前⼀次的 Commit 物件。
  4. Tag 物件(Annotated Tag)會指向某個 Commit 物件。
  5. 分⽀雖然不屬於這四個物件之⼀,但它會指向某個 Commit 物件。
  6. 當開始往 Git Server 上推送之後,在 .git/refs 底下就會多出⼀個 remote 的⽬錄,裡⾯
    放的是遠端的分⽀,基本上跟本地的分⽀是差不多的概念,同樣也會指向某個 Commit 物
    件。
  7. HEAD 也不屬於這四個物件之⼀,它會指向某個分⽀。

20. 垃圾回收機制

image-20230616102934912
當每次使⽤ git add 指令把檔案加到暫存 區的時候,即使檔案的內容只改了⼀個字,因為算出來的 SHA-1 值不同,所以 Git 也會為它做 出⼀顆全新的 Blob 物件,⽽不是只記錄「差異」。也因為 Git 在切換 Commit 的時候會像「拎 葡萄」⼀樣整串抽出來,不需要⼀個⼀個去拼湊歷史紀錄,所以在做 Checkout 的時候效能相對 的比較好。有些⼈也會⽤快照(Snapshot)來形容這個「拎葡萄」的概念。

我們來了解一下目前的檔案狀況:

1
git ls-files -s
1
2
3
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git ls-files -s
100644 5bbd493e709d6f1d06c653ef6cc0df91f1b3a065 0 index.html

使用git gc指令,進行手動回收垃圾,如下:

1
2
3
4
5
6
7
8
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git gc
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (6/6), done.
Total 6 (delta 1), reused 6 (delta 1), pack-reused 0

透過git verify-pack -v查閱路徑objects/pack/底下打包好的壓縮物件內容

1
git verify-pack -v

執行如下:

1
2
3
4
5
6
7
8
9
10
11
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git verify-pack -v .git/objects/pack/pack-e1c18eef9de03245886714260e8358ee3fe255e5.idx
cd61bd77fbdcfb74ff2b4121b95bcb49481e49b9 commit 254 179 12
31473df02f02abb405e1263288cb80f3f2f21844 commit 202 143 191
5bbd493e709d6f1d06c653ef6cc0df91f1b3a065 blob 267 191 334
c4711c95748c9bc71663450934fff3ad296c245b tree 38 49 525
7f4709e94455b029ee72933dce31e517ae196806 tree 38 49 574
56efbdba61ff12f90f864930b9a85eac0259666d blob 18 30 623 1 5bbd493e709d6f1d06c653ef6cc0df91f1b3a065
non delta: 5 objects
chain length = 1: 1 object
.git/objects/pack/pack-e1c18eef9de03245886714260e8358ee3fe255e5.pack: ok

我們看這行56efbdba61ff12f90f864930b9a85eac0259666d blob 18 30 623 1 5bbd493e709d6f1d06c653ef6cc0df91f1b3a065

這個blob只有18btyes 檔案大小,原因是因為它參照了5bbd這個blob 物件進行儲存,做到了類似差異備份的機制,因此檔案變小了。

最後,說明什麼時機點git 會自動執行gc的機制:

  1. 當在 .git/objects ⽬錄的物件或是打包過的 packfile 數量過多的時候,Git 會⾃動觸發資源回收指令。

  2. 當執⾏ git push 指令把內容推⾄遠端伺服器時(如果你有仔細觀察過 Push 指令的訊息的話…)。

話說回來,其實 Git 也不是真的很在意浪費空間這回事,反正現在硬碟這麼便宜,能夠快速、
有效率的操作才是 Git 關切的重點。

21. git branch

git branch(list and branches we have)
git branch (create a branch with branch_name,if there already have branch with the name you want to create,it will return error)

git branch -D (delete branch,can’t delete current active branch or branch not existing)
git branch –delete dev

git branch -m (rename branch with new name)

21.1. 刪除分支

1
git branch -d <branch_name>

21.2. 強制刪除分支

1
git branch -D <branch_name>

22. git merge

先切換到master分支,再下指令進行合併:

1
git merge cat

如果在master分支上都未做變動,進行merge時,會是fast-forward(快轉)

如果在master分支上有做變動,進行merge時,會是3 ways merge

sourcetree進行操作

image-20230616165944213

22.1. 快轉模式(fast-forward)情況下,還是想要有小耳朵

1
git merge cat --no-ff
1
2
3
4
5
6
7
8
9
10
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git merge cat --no-ff
hint: Waiting for your editor to close the file...
[1612:0616/212512.631:ERROR:service_worker_storage.cc(1933)] Failed to delete the database: Database IO error
Merge made by the 'recursive' strategy.
cat4.html | 0
cat5.html | 0
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 cat4.html
create mode 100644 cat5.html

sourcetree方式

image-20230616213033199

image-20230616213205677

mergerebase發生衝突時,具體步驟如下:

1.優先解決衝突檔案的內容修正

  1. 接著進行git add指令

3-1 git merge 的情境,則繼續下git commit指令

3-2 git rebase的情境,指令為 git rebase --continue

ORIG_HEAD

Git 有另⼀個特別的紀錄點叫做 ORIG_HEAD ,當你在做⼀些比較「危險」的操作(例如像 merge 、 rebase 或 reset 之類的),Git 就會把 HEAD 的狀態存放在這裡,讓你隨時可以跳回危險動作之前的狀態。

git rebase

互動模式

1
git rebase -i <commit_sha1>

pick:預設都是這個指令,代表會使用這個 Commit。
reword:使用這個 Commit,但是執行到此 Commit 時會開啟 vim 供更改 Commit 訊息。
edit:使用這個 Commit,但是執行到此 Commit 時會暫停,直到執行 git rebase –continue。
squash:將這個 Commit 與前一個 Commit 合併,訊息也會合併。
fixup:與 squash 相同,但會捨棄這個 Commit 的訊息。
drop:刪除這個 Commit,結果同直接刪除行。

23. 狀況題

23.1. 如果在 git add 之後⼜修改了那個檔案 的內容?

23.2. 我想要找某個⼈或某些⼈的 Commit

1
git log --oneline --author="Sherly"

23.3. 想找commit罵髒話

1
git log --oneline --grep="WTF"

23.4. 想找commit 提到ruby

1
git log -S "Ruby"

23.5. 看你早上commit 了什麼

1
git log --oneline --since-="9am" --until="12am" --after="2017-01"

23.6. 我不是真的想把這個檔案刪掉,只是不想讓這個檔案再被Git控管了

1
git rm welcome.html --cached

23.7. 修改Commit紀錄

要修改Commit紀錄有好幾種⽅法:

  1. 把.git⽬錄整個刪除(誤)。

  2. 使⽤git rebase來修改歷史。

  3. 先把Commit⽤git reset拆掉,整理後再重新Commit。

  4. 使⽤--amend參數來修改最後⼀次的Commit。

    1
    git commit --amend -m "Welcome To Facebook"

    image-20211004171011513

    雖然在commit 數上沒有變動,訊息上也更改了,但注意觀查其實在sha值上已經換過了,畢竟已經有做修改動作

23.8. 追加檔案到最近⼀次的Commit

先將目前的檔案加入到staged狀態

1
git add .

接著下這個指令就會合併到最後一次commit 囉

1
git commit --amend --no-edit

sourcetree軟體部分,一樣使用amend last commit,可以達到同樣的效果,合併到最後一次commit
image-20211005093726530

23.9. 新增目錄卻沒有被追蹤?

請記得⼀件很重要的觀念,就是Git在計算、產⽣物件的時候,是根據「檔案的內容」去做計算的,所以光是新增⼀個⽬錄,Git是沒辦法處理它的。

如果需要git追蹤這個目錄的話可以在目錄底下加上.gitkeep檔案

23.10. 有些檔案我不想放在Git裡⾯

可能的狀況

  1. 資料庫存取密碼

  2. AWS伺服器的存取金鑰

  3. 程式編譯的中間檔或暫存檔(因為每次的編譯都會產生新檔,對於專案來說沒有利用價值)

要做到忽略追蹤要新增.gitignore檔,填寫忽略規則

1
2
3
4
5
6
7
8
9
10
11
#忽略secret.yml檔案
secret.yml

#忽略config⽬錄下的database.yml檔案
config/database.yml

#忽略所有db⽬錄下附檔名是.sqlite3的檔案
/db/*.sqlite3

#忽略所有附檔名是.tmp的檔案
*.tmp

23.11. 可以忽略這個忽略嗎?

直接硬闖關

1
git add -f 檔案名稱

23.12. 為什麼.gitignore 沒效果?

有一種狀況你先建了index.html 也commit 了。

之後再新增.gitignore 說忽略index.html,

這時如果去異動index.html,會發現還是會被追蹤。

如果是這樣的話,可以使用指令停止追蹤

1
git rm --cached index.html

對應sourcetree軟體 是Stop Tracking功能

image-20211005101224894

23.13. 一口形清除忽略的檔案

如果想要⼀⼝氣清除那些已經被忽略的檔案,可以使⽤ git clean 指令並配合 -X 參數:

1
git clean -fX

那個額外加上的 -f 參數是指強制刪除的意思,這樣⼀來就可清除那些被忽略的檔案。

23.14. 新增檔案時,檔案還沒加入追蹤,想清空工作目錄

1
git clean -f

23.15. 檢視特定檔案的commit 記錄

1
git commit index.html

如果要看更詳細的可以加上p

1
git commit -p index.html

使用sourcetree查看的方式,在檔案上右鍵找Log selected

image-20230614220428502

23.16. 這行程式是誰寫的?

1
git blame index.html

如下:

1
2
3
4
5

$ git blame index.html
7fe12367 (DESKTOP-I40U13Q\kite 2023-06-14 21:53:04 +0800 1) hello
5a85a883 (DESKTOP-I40U13Q\kite 2023-06-14 21:57:35 +0800 2) this 2 line

如果行數太多,加上L參數

1
git blame -L 1,1 index.html
1
2
3
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (main)
$ git blame -L 1,1 index.html
7fe12367 (DESKTOP-I40U13Q\kite 2023-06-14 21:53:04 +0800 1) hello

sourcetree上的操作

image-20230614221304355

23.17. 不小心把檔案或目錄刪掉了…

刪除檔案指令

1
rm *.html

指令過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (main)
$ rm *.html

kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (main)
$ git st
On branch main
Your branch is ahead of 'origin/main' by 3 commits.
(use "git push" to publish your local commits)

Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: home.html
deleted: index.html

單一檔案救回指令

1
git checkout index.html

全數救回指令

1
git checkout .

sourcetree

image-20230614222305610

還原某個檔案至前面的版本

1
git checkout HEAD~2 index.html	

index.html檔距離現今前二次的版本

距離現在兩個版本以前的那個 welcome.html 檔案來覆蓋現在的⼯作⽬錄裡的 welcome.html 檔案

1
git checkout HEAD~2 .

目前工作區檔案距離現今前二次的版本

拿距離現在兩個版本以前的檔案來覆蓋現在⼯作⽬錄的檔案,同時也更 新暫存區裡的狀態

23.18. 剛才的commit 後悔了,想拆掉重做…

相對的作法

1
git reset e12d8ef^

以這個object位置前往一次

如果要前往二次以上,舉例來說五次的寫法如下

1
git reset e12d8ef~5

以上的寫法也可以成寫

1
git reset head~5

1
git reset master~5

HEADmaster指針指向e12d8ef情況下,效果是一樣的。

sourcetree

image-20230615091430499

image-20230615091503830

reset 三種模式

模式 mixed模式 soft模式 hard模式
commit 拆出來的檔案 丟回工作目錄 丟至暫存區 直接丟掉

23.19. 不小心使用hard模式reset了某個commit救得回來嗎?

git reset 之前的sha1 值就可以了,舉例來說:

1
git reset a95148f --hard

如果忘記、甚至沒記當時的sha1值,怎麼辦?

使用reflog指令,執行如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git reflog
ae90fda (HEAD -> master) HEAD@{0}: reset: moving to head~2
a95148f HEAD@{1}: reset: moving to a95148f
ae90fda (HEAD -> master) HEAD@{2}: reset: moving to head~2
a95148f HEAD@{3}: reset: moving to a95148f
ae90fda (HEAD -> master) HEAD@{4}: reset: moving to head~2
a95148f HEAD@{5}: reset: moving to a95148f
ae90fda (HEAD -> master) HEAD@{6}: reset: moving to head~2
a95148f HEAD@{7}: reset: moving to a95148f
ae90fda (HEAD -> master) HEAD@{8}: reset: moving to head~2
a95148f HEAD@{9}: reset: moving to a95148f
ae90fda (HEAD -> master) HEAD@{10}: reset: moving to head~2
a95148f HEAD@{11}: reset: moving to a95148f
ae90fda (HEAD -> master) HEAD@{12}: reset: moving to head~2
a95148f HEAD@{13}: commit: file03
f1e0d52 HEAD@{14}: commit: file02
ae90fda (HEAD -> master) HEAD@{15}: commit (initial): add file01

試了很多次,所以會看到很多次reset資料,

HEAD 有移動的時候(例如切換分⽀或是 reset 都會造成 HEAD 移動),Git 就會在 Reflog 裡記上⼀筆。

git log 如果加上 -g 參數,也可以看到 Reflog 喔!

可以只commit 一個檔案的部分內容嗎?

使用 git add -p方式,舉例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git add file03 -p
diff --git a/file03 b/file03
index e69de29..0eb7e4c 100644
--- a/file03
+++ b/file03
@@ -0,0 +1,3 @@
+123
+456
+789
\ No newline at end of file
(1/1) Stage this hunk [y,n,q,a,d,e,?]? e

這時候會開啟檔案,再將要刪掉的行數刪掉,儲存關掉即可。

sourcetree

image-20230615104916492

image-20230615104949216

23.20. 那如果改⼀半切換分⽀會發⽣什麼事?

舉例來說,在cat分支時,新增了cat3.html、修改index.html檔案,這時切換分支至master,結果仍保留之前在工作區的更新檔案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (cat)
$ touch cat3.html

kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (cat)
$ vim index.html

kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (cat)
$ git checkout master
Switched to branch 'master'
M index.html

kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git st
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html

Untracked files:
(use "git add <file>..." to include in what will be committed)
cat3.html

no changes added to commit (use "git add" and/or "git commit -a")

不⼩⼼把還沒合併的分⽀砍掉了,救得回來嗎?

1
2
3
4
5
6
7
kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git branch -D cat
Deleted branch cat (was 6e34a6f).

kite@DESKTOP-I40U13Q MINGW64 /d/kite/tmp/test (master)
$ git branch cat 6e34a

當在刪除的時候,我們會看到後面有一個當時的sha1值。

如果真的不小心誤砍了,可以透過以下指令進行回復:

1
git branch cat 6e34a

如果真的連sha1值都記不下來,其實還是可以用git reflog指令找出重要操作的log。(Reflog 預設會保留 30 天,所以 30 天內應該都還找得到)

24. 問題

24.1. 其實 git add .git add -A 效果是不同的

git add .

這個指令會把⽬前當下這個⽬錄,以及它的⼦⽬錄、⼦⼦⽬錄、⼦⼦⼦⽬ 錄…裡的異動全部加到暫存區,但在這個⽬錄的以外的就不歸它管了。

git add --all

就沒這個問題,這個指令不管在專案的哪⼀層⽬錄執⾏,效果都是⼀樣的,在這個專案裡所有 的異動都會被加⾄暫存區。

24.2. ⼀定要⼆段式嗎?

1
git commit -a -m "update content"

這樣即使沒有先 add 也可以完成 Commit。但要注意的是這個 -a 參數只對已經存在
Repository 的檔案有效,對還是新加入的檔案(也就是 Untracked file)是無效的。

25. 參考網站

An intro to Git Aliases: a faster way of working with Git

10 git aliases for a faster and productive git workflow

Git 的 100 個情境應用

https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1#merge

How to Animate Your Git Commit History with git-story

https://www.freecodecamp.org/news/animate-your-git-repo-with-git-story/

https://www.youtube.com/watch?v=3o_01F04bZ4&ab_channel=VisualStudioCode