# Git 使用教學 Git是一種分散式版本控制系統,它被廣泛用於協調和管理程式碼開發。Git由Linus Torvalds於2005年創建,最初是為了管理Linux核心程式碼而設計。如今,Git已經成為許多軟體開發團隊和個人開發者的首選版本控制系統,用於追蹤程式碼的變更、管理不同版本、解決衝突等。 以下是Git的一些主要特點和用法: 1. 版本控制:Git允許你在開發過程中記錄所有程式碼變更,你可以查看特定時間點的程式碼狀態,回滾到之前的版本或建立新的分支。 2. 分布式:每個開發者都有一個完整的本地儲存庫,這意味著即使無法連接到中央伺服器,也可以進行程式碼修改和提交。當然,也可以通過推送(push)和拉取(pull)的方式與其他開發者同步程式碼。 3. 分支管理:Git非常強大的一點在於可以輕鬆建立和管理分支。你可以在不影響主線開發的情況下,建立新的分支,進行獨立的開發工作,最後將分支合併回主線。 4. 協作:多人協作時,每個人可以在自己的本地分支上進行開發,當完成後將分支合併到共享儲存庫中,這樣可以減少衝突和混亂。 5. 解決衝突:在多人協作時,當兩個開發者在同一份程式碼的同一個位置進行了不同的修改,就會產生衝突。Git允許你解決這些衝突,手動選擇哪一個修改要保留。 6. 儲存庫:Git使用儲存庫(repository)來保存所有版本的程式碼和歷史記錄。儲存庫可以是本地的,也可以是在遠程伺服器上。 ## 環境架設 使用環境為 * Windows 10專業版 * Git版本為(2.41.0) 64-bit 第一步->先到官網下載相對應的作業系統 https://git-scm.com/downloads 在安裝時候一定要點擊git-bash Here。 ![](https://hackmd.io/_uploads/rkGDWra92.png) >後續的安裝就一直點擊下一步即可 第二步->安裝好后到啓動目錄搜尋git bash驗證是否有安裝成功。 git bash圖示 ![](https://hackmd.io/_uploads/SJ4fMSTc2.png) 點擊進入后 頁面會長得如下 ![](https://hackmd.io/_uploads/r1QSGB6qn.png) ## 常用的指令 ### 檢查版本號 >git --version ![](https://hackmd.io/_uploads/Hy9rmB6q2.png) ### 清除頁面 >clear ![](https://hackmd.io/_uploads/BkQlVSpq2.png) ### 顯示完整路徑 >pwd ![](https://hackmd.io/_uploads/HkXGBHT5n.png) ### 顯示目錄底下的所有文件 >ls ![](https://hackmd.io/_uploads/HJ-QUSac3.png) >ls -l是可以列出文件以及文件夾的屬性 ![](https://hackmd.io/_uploads/rJThABT5n.png) 以d開頭的是文件夾,以-開頭的是文件 ### 查看文件的内容 >cat <文件名稱+包括標頭檔> ![](https://hackmd.io/_uploads/BJno1L69n.png) 在這裏我是已經預先建立好一個名字為test.txt的檔案,裏面只有一個test文字,透過cat去讀取裏面的内容并且顯示出來。 ### 跳轉爲止 >cd <指定路徑> ### 開啓Visual Studio Code > code . (系統會自動開啓Visual Studio Code 并且預設在目前的目錄底下) ![](https://hackmd.io/_uploads/rkiXaHT9n.png) >![](https://hackmd.io/_uploads/ByTdTBac2.png) ## git初始化配置 第一步->打開git bash或者PowerShell(以下執行過程都以PowerShell爲主) PowerShell界面 ![](https://hackmd.io/_uploads/HJoaIIa5h.png) 第二步->配置名稱以及Email >git config --global user.name "Lit Wei" >git config --global user.email "xxxxx@xxx.com" 第三步->確認.gitconfig是否有生成 有的話就是初始化配置完成 >前往C槽->User->使用者->檢查.gitconfig是否有生成 >![](https://hackmd.io/_uploads/r1vBOITc3.png) >如果看到有.gitconfig生成的話那就代表名稱以及Email都配置成功 **該檔案是可以被打開的,打開后可以查看到使用者所配置的資訊。可以透過Visual Studio Code/Notepad++打開,或者透過指令git config -l來查看使用者配置的訊息** ![](https://hackmd.io/_uploads/SkGEKUp5n.png) ### 初始化代碼倉庫 第一步->先建立一個新的文件夾當成代碼倉庫, >mkdir <文件夾名字> ![](https://hackmd.io/_uploads/rkzuti052.png) 第二步->進入該文件夾 >cd <文件夾名字> ![](https://hackmd.io/_uploads/H1AiYoC9n.png) 第三步->輸入指令初始化代碼倉庫 >git init > >初始化成功畫面 >![](https://hackmd.io/_uploads/BJJa5oC53.png) 第四步->查看該文件夾内是否有隱藏的.git文件夾,有的話則代表初始化成功 >ls -a 列出隱藏的文件夾 >![](https://hackmd.io/_uploads/rJdDssC5h.png) 第五步->跳轉至.git->hooks文件夾裏把所有的.sample檔刪除 >cd .git/ >cd hooks >rm -rf *sample ![](https://hackmd.io/_uploads/SkZApi093.png) 在接著來介紹每一個資料夾的功能,資料夾如下 ![](https://hackmd.io/_uploads/BJQOku7o2.png) #### config檔 使用Cat指令去查看内部有什麽内容 >和上面git初始化那一章節的git config是有差別的 這兩者,一個是全局Global有效,而這個則是針對git-demo這個文件夾有效而已。如果有設置Local和Global的Config的話,則會優先讀取本地的爲主。 透過Cat指令查看Config的參數(主要用處是可以查看使用者配置訊息以及一些參數配置的模式) ![](https://hackmd.io/_uploads/B1K5mimo2.png) Local設定檔的資料 在這裏因爲Local設定檔的User還沒設定,所以在讀取時因讀取不到Local的User資料所以會讀取後者也就是Global Config的User資料。 ![](https://hackmd.io/_uploads/S1KoBjXon.png) Global設定檔的User參數 ![](https://hackmd.io/_uploads/B1NUroXi2.png) ### 查詢資料夾的樹狀結構 >tree <路徑> /F >![](https://hackmd.io/_uploads/B1j9HEroh.png) ### git add 這裏以建立一個新的文檔來示範git add的背後是發生了什麽樣的動作爲主 第一步 先建立一個名字為hello的txt檔并在裏面寫入的資料為hello git ![](https://hackmd.io/_uploads/SyFo7Nrj2.png) 第二步 使用cat指令查看文件是否有成功建立 ![](https://hackmd.io/_uploads/B1qMEESj2.png) 第三步 使用git add指令把hello.txt添加到緩存區裏 ![](https://hackmd.io/_uploads/rkuk84Bo2.png) 第四步 使用tree指令可以看到 多了一些文件出來 ![](https://hackmd.io/_uploads/BkFbIVHj2.png) >git Object有三種,會在下一個部分做記錄,這裏先知道 objects裏的0e41那一段是透過SHA1(Secure Hash Algorithm 1)的演算法去把字串以及數字做加密 ### git commit 是 Git 版本控制系統中的一個重要指令,用於將暫存區中的變更儲存為一個新的提交(commit)。提交是程式碼版本的快照,它包含了在暫存區中的所有變更,以及一些相關的元數據,如作者、日期、提交訊息等。 >git commit -m(代表的是message) "更改的記錄" >![](https://hackmd.io/_uploads/ry9yszKi3.png) 查看指令類型 ![](https://hackmd.io/_uploads/rJC0oGtoh.png) 查看commit后文件的狀態 ![](https://hackmd.io/_uploads/H1pznzFj2.png) 查看commit的文件内容 ![](https://hackmd.io/_uploads/H1O2hGYs3.png) commit上去是以一個Tree的結構上傳 查看分支指向的目標 ![](https://hackmd.io/_uploads/SkrPTzKsh.png) Commit后生成的文件 ![](https://hackmd.io/_uploads/H1Z5TfYoh.png) 如果有突然修改已Commit的文件需要重新add或者commit,而彈出的訊息也會多一個Parent,以下例子是在hello.txt裏新增文字后add以及commit后查看的内容結果 ![](https://hackmd.io/_uploads/rk6rlQKj3.png) 會發現到多一個Parent那個就是第一次commit的記錄 如果透過git cat-file -p 下去查看parent所對應到的位置的話 則會看到以下結果 ![](https://hackmd.io/_uploads/HJM5gXKo2.png) 會查詢到這一次的commit指令是在Parent之後。 ### git查看文件指令 查看文件類型 >git cat-file t <objects目錄下的名字+加密后的前4碼> >![](https://hackmd.io/_uploads/rkkHw4Hj2.png) blob是用來存儲文件内容的類型,只存取内容值,不存取文件名 查看文件的内容 >git cat-file p <objects目錄下的名字+加密后的前4碼> >![](https://hackmd.io/_uploads/r19SsEHo2.png) 查看文件的狀態 >git status >![](https://hackmd.io/_uploads/r12gGBBs2.png) 綠色代表的是可以提交commit上去了 若沒有add的話則是紅色的 **額外補充** 如果是已經git add的檔案有在進行更動的話 使用git status會發現有紅色的警示如下圖 >在這裏是更改了tmp.txt的内容,并且使用 git status去查看目前緩存區裏所有文件的情況能夠發現由於tmp.txt的内容已經經歷過修改,需要使用git add更新或者git restore重置回原本之前初始add好的文件内容狀態后才能繼續commit ![](https://hackmd.io/_uploads/SJtSrqPj2.png) 重新把tmp.txt add多一次讓它update ![](https://hackmd.io/_uploads/BJZk8cvo2.png) 使用git status查看tmp.txt是否還有紅色警示 ![](https://hackmd.io/_uploads/rk9x85wsh.png) 查詢文件的索引區 >git ls-files -s >![](https://hackmd.io/_uploads/B1eQfEQFjn.png) 從索引區移除 >若想把已經在索引區的文件移除(不是工作區)的話可以透過以下指令 >git rm --cached <文件名字> ![](https://hackmd.io/_uploads/BJvBfEti3.png) 從索引區還原至原本的檔案 >原本的狀態 >![](https://hackmd.io/_uploads/BJq-u4Yjh.png) 在tmp.txt新增内容后add上去緩存區 ![](https://hackmd.io/_uploads/S1FS_Vton.png) 可以先到hash值已經有改變了 若想要還原成原本的狀態的話可以使用 >git restore --staged <檔案名字> ![](https://hackmd.io/_uploads/SJr3_NKs3.png) 可以發現到tmp.txt的哈希值已經還原成上一階的狀態 ### git文件狀態 **Untracked**-當檔案尚未加入到 Git 版本控制系統中,且 Git 不追蹤該檔案的任何變更時,該檔案就處於 "Untracked" 狀態。"Untracked" 狀態的檔案是指那些 Git 不會自動辨識或追蹤的檔案。這些檔案不會被納入 Git 的版本歷史記錄,因此 Git 不會記錄它們的任何變更。通常情況下,這些檔案可能是新建立的檔案,或者是從其他地方複製到專案中的檔案。 **Modified**-狀態指的是已經在 Git 版本控制系統中的檔案,但其內容在上一次提交之後已經發生了更改。換句話說,這些檔案在本地儲存庫中已經存在,並且有過提交紀錄,但最近的更改尚未被提交。 當你修改已經被 Git 追蹤的檔案時,這些檔案就會進入 "Modified" 狀態。這意味著這些檔案的內容已經被編輯過,但還沒有通過 git add 和 git commit 指令來保存這些更改到 Git 儲存庫中。 **Staged**-"Staged"(已暫存)狀態指的是已經通過 git add 指令將檔案從工作目錄的 "Modified"(已修改)狀態加入到暫存區(也稱為索引)。暫存區是一個中間狀態,用於準備將更改包含在下一次提交中。當你修改了一個已被 Git 追蹤的檔案,但還沒有提交這些修改時,該檔案處於 "Modified" 狀態。如果你想將這些修改包含在下一次提交中,你需要使用 git add 指令將檔案加入到暫存區,從而將其狀態變為 "Staged"。 **Unmodified**-"Unmodified"(未修改)狀態指的是那些已經被 Git 追蹤且沒有發生更改的檔案。換句話說,這些檔案的內容和最後一次提交時的內容保持一致。當檔案已經被 Git 追蹤,且在你尚未對其進行任何修改時,這些檔案處於 "Unmodified" 狀態。這表示這些檔案的內容與最後一次提交時的內容相同。 圖解其中的關係 ![](https://hackmd.io/_uploads/r19qAmKoh.png) #### 查看log記錄 git log 是 Git 版本控制系統中的一個命令,用於查看專案的提交歷史。它會列出過去的提交記錄,包括每個提交的相關資訊,如提交者、日期、提交訊息以及提交的哈希值等。 git log結果圖 ![](https://hackmd.io/_uploads/By63aVtsn.png) 顯示在最上方的是最新的commit log 最下方是最早的紀錄 >如果你想以簡潔的方式顯示提交歷史,你可以使用 --oneline 選項。這會以簡短的格式列出每個提交,只顯示提交的哈希值和提交訊息。 >還有很多其他選項可以用來定制 git log 的輸出,比如使用 --graph 來顯示提交圖,或使用 --author 來只顯示特定作者的提交。 git log oneline結果圖 ![](https://hackmd.io/_uploads/SkaeREYj2.png) 顯示最近兩筆log結果圖 ![](https://hackmd.io/_uploads/ry5IANYj3.png) ### Branch(分支)& Head **Branch** 在 Git 版本控制系統中,"branch"(分支)是指基於某個提交(通常是某個提交的快照)所建立的一個獨立的開發路徑。每個分支都是一個相對獨立的程式碼空間,允許在不影響主要程式碼線的情況下進行開發、修改和實驗。 使用分支可以同時處理多個不同的開發任務,而不會影響主要專案或其他分支。每個分支都包含了專案檔案的完整副本,允許開發者在不同的分支上進行獨立的修改和實驗。一旦在一個分支上完成了某個開發任務,可以將其合併回主專案或其他分支。 預設情況下,Git 會在建立儲存庫時自動創建一個主分支,通常稱為 "master" 或 "main"。這個主分支是專案的主要路線,它包含了專案的穩定版本。當你在專案中建立新的分支時,你實際上是在基於主分支的某個特定提交上建立一個新的分支。 **Head** 在 Git 中,"HEAD" 是一個特殊的指標,它始終指向當前所在分支中最新的提交。換句話說,HEAD 指示了你目前工作狀態在專案歷史中的位置。 你可以將 HEAD 視為一個標記,指示你目前正在工作的地方,而你的提交和修改都是基於這個標記進行的。 通常情況下,你會在 HEAD 下進行修改和提交。如果你正在特定的分支上工作,那麼 HEAD 將指向該分支上最新的提交。然而,你也可以使用 git checkout 指令切換到某個特定的提交,這會將 HEAD 移動到這個提交上,此時你處於 "detached HEAD" 狀態,表示 HEAD 不再指向任何分支,而是直接指向一個具體的提交。 **使用指令查看HEAD的指向哪一個文件** ![](https://hackmd.io/_uploads/Hkz9JTjs2.png) **查看master的SHA值** ![](https://hackmd.io/_uploads/Syhxe6jin.png) **查看master是什麽類型的文件** ![](https://hackmd.io/_uploads/rJJLg6so3.png) **透過git log查看HEAD是否指向最新commit的文件** ![](https://hackmd.io/_uploads/Hy3te6ss2.png) ### Git Branch 查詢現有的分支 >git branch ![](https://hackmd.io/_uploads/BJ51vWTjh.png) >*號代表的意思是目前的分支 建立分支 >git branch <分支名字> >![](https://hackmd.io/_uploads/B1VrPZas3.png) 改變分支名字 >git branch -m <舊的名字> <新的名字> >![](https://hackmd.io/_uploads/SJ4nt3132.png) 查看遠端分支跟本地分支 >git branch -a 刪除分支 >git branch -D或者d 大D是强制刪除小d是會確認檔案是否merge <分支名字> > **分支刪除需要注意的事項** 1. 如果當前處於所要刪除的分支目錄上,是無法刪除的 ![](https://hackmd.io/_uploads/S1JMnW6on.png) 2. 使用-d的話會彈出一些最後的提醒是否要刪除指定的分支(如果是D的話則不會提醒,會直接刪除) ![](https://hackmd.io/_uploads/HJjd3-asn.png) 3. 分支刪除了會遺留blop、commit的對象,這些對象一般稱爲垃圾對象 切換分支/恢復文件 >git checkout >![](https://hackmd.io/_uploads/BJReuZTs2.png) >切換成dev這個分支 >透過查看HEAD裏的内容也可以發現到指針會從原本的Master指向dev >![](https://hackmd.io/_uploads/HyyB_bpj3.png) 如果使用其他分支在進行作業的話 透過git log可以發現到指針對對應的文件以及 commit的狀態會有所不同。以下範例是透過在dev這個分支裏新增一個dev.txt檔并且透過git add和git commit來查看指針所對應的位置。 新增文件以及commit ![](https://hackmd.io/_uploads/rkUGtWTjn.png) 透過git log查看目前指針位置 ![](https://hackmd.io/_uploads/Hkw4t-Tjn.png) #### git Checkout特定的commit 假如目前所在的位置是指向Master裏最新的commit,想要指定checkout某一個特定的commit可以參考以下步驟 1. 先查看目前已經commit的内容 ![](https://hackmd.io/_uploads/Hko2y313h.png) 2. 假如我要checkout 第一版也就是First Message的話我可以輸入一下指令 >git checkout <特定版本的哈希值> ![](https://hackmd.io/_uploads/rJpWg3Jn2.png) 3. 輸入完指令后會發現到目前的HEAD指向的不是master而是指定的commit版本 ![](https://hackmd.io/_uploads/S1KdWhyhn.png) 4. 使用git log查看目前已commit的情況會發現,會回到第一次commit的情況 ![](https://hackmd.io/_uploads/S1SAZ21n2.png) 5. 一般在checkout 特定的commit版本后不會在當下進行修改而是會開一個新的分支如以下例子 > git checkout -b <分支名字> > ![](https://hackmd.io/_uploads/H1NpM3k3n.png) 使用git log查看也可以發現HEAD的指針是指向tmp這個分支 ![](https://hackmd.io/_uploads/Sk8WQ3yh3.png) **這裏并不是直接指向8328ac這個commit版本,而是透過tmp這個分支去指向8328ac這個commit版本** #### Git Reflog 在 Git 版本控制系統中,git reflog 是一個用於顯示本地儲存庫的引用記錄(reference log)的指令。引用記錄記錄了本地儲存庫中的引用(如分支和 HEAD)的移動和變化歷史,包括分支切換、提交、合併等操作。 通過查看引用記錄,你可以追蹤儲存庫中的引用在過去的操作中如何變化,從而幫助你找回可能遺失的提交、分支或操作。這在意外刪除分支、合併出現問題或需要恢復誤操作時非常有用。 >git reflog >![](https://hackmd.io/_uploads/HJs3Shk3h.png) 以下例子是以 如果在進行工作時不小心誤刪某一個文件或者分支時可以使用的方法 1. 我的dev因爲在之前就已經不小心刪除了 所以我第一件事就是查找dev 第一次commit時的哈希值如下圖并且checkout指定的哈希值 ![](https://hackmd.io/_uploads/By1jLhk23.png) 2. 雖然這個分支已經被刪除了可是它的内容其實是還保留在git倉庫裏的某一個commit内容裏 ![](https://hackmd.io/_uploads/SyA08nJ2n.png) 3. 使用ls可以查看到之前被誤刪的dev.txt也一并還原回來 而且透過git log的方式發現commit的版本也回到 該版本最後一次commit的情況 **這裏一樣是要透過新建一個分支來指向指定的版本** ![](https://hackmd.io/_uploads/H1e7SDny22.png) #### Git Diff 在 Git 中,git diff 是一個用於比較程式碼差異的指令。它可以顯示兩個不同位置的檔案之間的變化,例如比較工作目錄中的檔案與最新提交、不同分支之間的檔案差異等。 1. 比較工作目錄與最新提交 >git diff 2. 比較工作目錄與暫存區 >git diff --staged 3. 比較不同提交之間的差異 >git diff <commit1> <commit2> 4. 比較不同分支之間的差異 > git diff <branch1> <branch2> 5. 比較檔案的差異 >git diff <file1> <file2> git diff 指令會輸出差異的詳細資訊,顯示哪些行發生了變化,以及具體的修改內容。差異以類似於 Unix diff 格式的方式展示,新增的行以 + 開頭,刪除的行以 - 開頭。 git diff範例圖 ![](https://hackmd.io/_uploads/rJsBnpy32.png) ### Branch Merge 在 Git 版本控制系統中,git merge 是一個用來將不同的分支合併在一起的指令。它允許將一個分支的更改應用到另一個分支,從而將兩個不同的開發線合併成一個。 #### Git-Merge Fast Forward "Fast-forward" 合併(Fast-forward merge)是 Git 中一種合併分支的方式,當目標分支的最新提交是源分支的直接祖先時,系統可以直接將目標分支移動到源分支的位置,從而合併這兩個分支。這種合併方式被稱為 "fast-forward",因為在合併過程中,Git 只需將目標分支的指針向前移動,就像快速前進一樣,而不需要進行實際的合併操作。 實際流程 1. 先建立一個分支名為bugfix ![](https://hackmd.io/_uploads/ryyC4ZWn2.png) 2. 建立一個名為bug_fix的文字檔后執行git add 和commit操作 ![](https://hackmd.io/_uploads/H1tBHbZnn.png) 3. 使用git log可以查看到目前HEAD是指向bug fix這個分支而且是超過master分支 ![](https://hackmd.io/_uploads/Byg9DHZ-h2.png) 4. 要把bug_fix這個分支merge之前必須要切換回master分支 ![](https://hackmd.io/_uploads/HywuLWW22.png) 5. 執行Merge操作 >git merge <分支名字> ![](https://hackmd.io/_uploads/B1BywZZnh.png) 第一行Updating是代表的是指針的位置從原本的41b2daf指向了4c602ae >Merge完之後可以用tree指令查看多了一個ORIG_HEAD。而裏面所代表的值就是HEAD之前的狀態。而ORIG_HEAD在這裏的主要目的是如果Merge之後的資料有問題的話還有機會可以回滾之前的狀態。 ![](https://hackmd.io/_uploads/rkEcwZ-n3.png) #### git Merge-3 way merge "3-way merge"(三方合併)是 Git 中一種用於合併分支的進階方式,特別是當目標分支和源分支都有新的提交時,或者存在衝突時。這種合併方式使用了三個不同的提交,以及它們之間的共同祖先,來進行合併。 "3-way merge" 是相對於 "fast-forward" 合併的進階方式,它可以處理更複雜的合併情況,尤其是在目標分支和源分支都有自己的提交,或者當有衝突需要解決時。這種合併方式確保合併的結果包含了兩個分支的所有變更,同時也提供了解決衝突的機會。 演示流程 1. 先建立好一個分支為bug_fix,并且裏面新增一個檔名為bug_fix.txt也執行過add和commit的操作確保目前HEAD指向的位置是領先master ![](https://hackmd.io/_uploads/Ske-3WW2n.png) 2. 回到master,新建一個名字為other_bugfixed.txt并且commit。主要是模擬出當有其他人已經先進行merge之後的狀態 ![](https://hackmd.io/_uploads/rkr0hZZ32.png) 3. 接著切換回去master分支后使用git merge指令后可以看到git log的位子出現了一個新的commit ![](https://hackmd.io/_uploads/S1xv1GZ2h.png) 透過source tree查看實際情況 ![](https://hackmd.io/_uploads/By9ckf-2n.png) #### Git Reset 如果在Merge完之後發現資料有錯需要回滾的話可以使用git reset加上merge之後所產生的ORIG_HEAD來進行操作 >git reset ORIG_HEAD ![](https://hackmd.io/_uploads/H1JR_Zb3h.png) 從以上的範例可以得知,經過git reset的指令后,之前所Merge的檔案都回溯回去了而HEAD指針也指向了master的分支 #### 3 way merge with conflict 以下例子是如果當2個人同時修改同一個檔案并且commit時有發生衝突需要如何處理 1. 首先建立一個名字為test.txt的檔案并且執行第一次的commit ![](https://hackmd.io/_uploads/Sy4lzG-22.png) 2. checkout一個新的分支名為bugfix并且在test.txt裏新增一行新的文字 ![](https://hackmd.io/_uploads/HkudQGbnn.png) 3. 進行add以及commit的操作 ![](https://hackmd.io/_uploads/HJcE4f-2h.png) 4. 切換回去master并且修改test.txt新增一段新的文字 ![](https://hackmd.io/_uploads/B1hc4z-h3.png) 5. 接著一樣進行add以及commit的操作 ![](https://hackmd.io/_uploads/ByM1BMb2n.png) 6. 透過git log指令下去看可以看到2個不同的log master分支下的git log ![](https://hackmd.io/_uploads/HkQUSM-n3.png) bugfix分支下的git log ![](https://hackmd.io/_uploads/r1d_Sfbnh.png) 7. 回到master分支輸入merge指令會發現到有報衝突錯誤 ![](https://hackmd.io/_uploads/BkFgpzbhh.png) 8. 可以透過cat指令查看git其實已經有幫忙做内容的整理 ![](https://hackmd.io/_uploads/B1YFTfZnn.png) 意思是上面add from master是從HEAD進行修改的,下面那行add from bug fix是從bug fix分支加進來的。 9. 可以看到原本只有一個test.txt的可是merge之後產生了3個不同版本的test.txt這三個版本所對應的commit 哈希值也不一樣 ![](https://hackmd.io/_uploads/SJfXl7-32.png) 10. 處理方法有很多種,一種是透過vim下去修改要保留的内容,另一種是透過指令code .打開Visual Studio Code去做更改**在這裏我是選擇保留兩者的内容** ![](https://hackmd.io/_uploads/B1lcxmZnn.png) 11. 接著透過git ls-files -s查看文件又變成1個了 ![](https://hackmd.io/_uploads/rJ2NZQ-h3.png) 12. 最後透過git commit后直接保留預設的log檔即可 ![](https://hackmd.io/_uploads/HJ1pZXZ22.png) #### git Rebase 在 Git 版本控制系統中,git rebase 是一個用於重新應用提交的指令,它可以重新安排、合併和修改提交的歷史記錄。通常用於將一個分支的變更應用到另一個分支,或者重新整理提交的順序以獲得更整潔的歷史。 使用 git rebase 可以將一個分支上的提交應用到另一個分支,這是通過將要合併的分支的基礎點移動到目標分支的最新提交,然後將要合併的分支上的提交應用在其之上。這個過程的結果是,看起來好像提交是在目標分支上連續進行的,而不是在一個分支上。 實作範例 以下環境是在master已經做了一個分支名為bugfix,而master分支裏已經有commit的檔案為test1以及test3 master分支的git log ![](https://hackmd.io/_uploads/SksrdIz3n.png) bugfix分支的git log ![](https://hackmd.io/_uploads/SJQMOLf32.png) 利用Source Tree圖例化結構 ![](https://hackmd.io/_uploads/HkVnuLzhn.png) 接著在bugfix分支裏進行rebase >git rebase master ![](https://hackmd.io/_uploads/SJ_Ut8fhh.png) 透過git log查看結果 ![](https://hackmd.io/_uploads/SymdtLf2h.png) 使用Source Tree查看結果 ![](https://hackmd.io/_uploads/rkR0tIMhh.png) 可以發現到變成綫性的結果已經組合起來了 最後回到master分支進行merge 就會變成fast forward的方式做merge而不是3 way merge ![](https://hackmd.io/_uploads/B1YHq8z2h.png) ### Source Tree SourceTree 是一款為 Git 和 Mercurial 版本控制系統提供圖形界面的桌面應用程式。它讓使用者能夠更輕鬆地進行版本控制操作,而不必完全依賴命令列介面。SourceTree 提供了直觀的界面,可以幫助開發者視覺化地管理、比較、合併和提交代碼,同時還能夠方便地切換分支、查看歷史記錄和解決衝突。 ### Git Remote 在 Git 版本控制系統中,git remote 是用來管理遠端儲存庫的指令。遠端儲存庫是存放在遠端伺服器上的 Git 儲存庫,它可以是你的團隊共享的中央儲存庫,也可以是在網路上的外部儲存庫(如 GitHub、GitLab 或 Bitbucket)。git remote 讓你能夠在本地儲存庫中設定、查看和操作與遠端儲存庫相關的設定。 #### git remote常用指令 顯示遠端儲存庫的名字 >git remote ![](https://hackmd.io/_uploads/HkuPse8nn.png) 新增遠端儲存庫 >git remote add <儲存庫名字> <儲存庫網址> ![](https://hackmd.io/_uploads/r1tkZBqhn.png) 顯示遠端儲存庫Push和Fetch的url >git remote -v ![](https://hackmd.io/_uploads/By6cigIh3.png) 顯示遠端儲存庫的詳細資訊和設定 >git remote show <遠程儲存庫名字> ![](https://hackmd.io/_uploads/SkDA6gI22.png) 刪除遠端儲存庫 >git remote remove <遠程儲存庫名字> 顯示 Git 儲存庫中的參考(references)的資訊,包括分支、標籤、遠端儲存庫等。 >git show-ref ![](https://hackmd.io/_uploads/HJ2Wiov3h.png) ### git Fetch git fetch 指令用於從遠端儲存庫獲取最新的提交和分支資訊,但不會自動合併或修改本地分支。這個指令使你能夠將遠端儲存庫的最新變更下載到你的本地儲存庫中,然後再決定是否將這些變更合併到本地分支中。 查看目前最新的commit ![](https://hackmd.io/_uploads/Bk8mZZLn2.png) 查看遠端儲存庫最新的commit版本 ![](https://hackmd.io/_uploads/HkXkfbI32.png) 本地儲存庫同步 >git fetch <遠端儲存庫名字> ![](https://hackmd.io/_uploads/S1oy7bLh2.png) 查看是否跟遠端儲存庫同步commit版本 ![](https://hackmd.io/_uploads/HkAG7W82h.png) #### Git Fetch與Git Pull的差別 * git fetch:這個指令從遠端儲存庫獲取最新的提交和分支資訊,並將這些資訊下載到你的本地儲存庫中,但不會自動合併或修改你的當前分支。換句話說,git fetch **只是將遠端儲存庫的變更拉下來,但不會將這些變更應用到你的分支上。** * git pull:這個指令包含了 git fetch 的功能,同時也會自動將遠端儲存庫的變更合併到你的當前分支中。實際上,**git pull 等同於執行了 git fetch,然後立即跟著執行了 git merge,將遠端分支的變更合併到你的當前分支。** ### Pull Request 這是指在使用 Git 版本控制系統時,開發者從自己的分支(通常是一個功能分支或修復分支)向主要程式碼庫的分支(例如主分支)發起的一個請求,要求將他們的程式碼變更合併到主程式碼庫中。 ### fork 在 Git 版本控制系統中,fork 操作是指將一個遠端儲存庫(通常是其他使用者的儲存庫)複製到自己的帳戶中,以便進行獨立的開發工作。 ### Git Tag 在 Git 版本控制系統中,標籤是一種用來標記特定提交的指向性引用(reference),通常用於標記重要的里程碑、發行版本或特定的提交。 標籤可以分為兩種類型:輕量標籤(Lightweight Tag)和註解標籤(Annotated Tag)。 #### Lightweight Tag範例 範例文件夾内容 ![](https://hackmd.io/_uploads/SJX5PVc32.png) 範例Tree的結構 ![](https://hackmd.io/_uploads/ry9BONc3h.png) 新增一個輕量標簽 >git tag <標簽名字> ![](https://hackmd.io/_uploads/BJkt_Ncnn.png) 查看tree結構可以發現到多了一個tag的文件夾 ![](https://hackmd.io/_uploads/BJAjuE5n2.png) 透過cat去查看文件夾内的v1.0.0是是指向什麽 ![](https://hackmd.io/_uploads/BkVZYN52n.png) 查看該哈希值是對應到什麽位置 ![](https://hackmd.io/_uploads/SkiBtVc3n.png) 從以上步驟可以得知當我們創建一個輕量標簽時候,系統會自動建立一個名爲tag的文件夾,它所產生的哈希值會指向當前所commit的版本 **特點** * 建立一個標簽并且會生成一個指針儲存在.git/refs/tags #### Annotated Tag範例 >git tag -a <標簽號> -m <標簽訊息> ![](https://hackmd.io/_uploads/H1xPoEc3h.png) **特點** * 建立一個標簽并且會生成一個指針儲存在.git/refs/tags * 也會在.git/objects生成一個tag的哈希值物件 * 在objects裏留下標簽訊息、作者訊息以及日期 ### git tag常見指令 查詢所有tag >git tag ![](https://hackmd.io/_uploads/Bk0iFV52n.png) 新增輕量tag >git tag <標簽名字> ![](https://hackmd.io/_uploads/HyTgcVqnh.png) 刪除tag(local端) >git tag -d <標簽名字> ![](https://hackmd.io/_uploads/r1VjRN9h3.png) 刪除tag(遠端) >git push --delete <遠程倉庫名字> <標簽名字> ![](https://hackmd.io/_uploads/H1WhXB5nh.png) 推送tag >git push <遠程儲存庫名字> <標簽名字> **如果是多個標簽的話則是 git push --tags** ![](https://hackmd.io/_uploads/rJzkfSqhh.png) github頁面 ![](https://hackmd.io/_uploads/B1OezHq2n.png) ### git hooks Git hooks是一種自定義的腳本,可以在 Git 版本控制操作的特定時刻觸發執行。這些時刻包括提交(commit)、合併(merge)、推送(push)等,hooks使開發者能夠在這些操作執行前或執行後,執行自己的自定義邏輯。 每當特定的 Git 操作被執行時,Git 會尋找對應的hooks腳本,並在適當的時間點執行它。這些hooks可以用來執行各種任務,例如驗證提交的格式、檢查程式碼風格、自動化測試等。 Git hooks包括兩種類型: 1. 客戶端鉤子(Client-Side Hooks): 這些hooks在 Git 操作進行前觸發。它們位於本地儲存庫中,可以用來驗證、修改提交等。例如,**pre-commit** hooks可以在進行提交前檢查程式碼風格。 2. 伺服器端鉤子(Server-Side Hooks): 這些hooks在伺服器端的 Git 儲存庫上觸發,通常用於限制推送、檢查合併請求等。例如,**pre-receive** hooks可以在進行推送前執行一些驗證。 #### Pre-Commit hook 1. 新增一個資料夾并且已經使用git初始化指令。 ![](https://hackmd.io/_uploads/ryAEjH533.png) 2. 透過指令進入visual studio code ![](https://hackmd.io/_uploads/HJN8iS9nh.png) 3. File-Preferences-setting 之後尋找exclude后點擊Files可以看到.git的文件 ![](https://hackmd.io/_uploads/HkD5sBq23.png) 4. 把.git旁邊的選項打叉即可看到git出現在左邊可視文件内 ![](https://hackmd.io/_uploads/r1bCjH5h2.png) 5. 點擊hooks即可看到所有預設的hooks文件**文件標頭檔是.sample所以都不可使用需要把.sample刪掉** ![](https://hackmd.io/_uploads/H1e72Bqn2.png) 6. 這裏拿pre-commit.sample拿來做範例,如果檢查到的字形為非ascii碼則不通過 ![](https://hackmd.io/_uploads/HJ6jhScn2.png) **exit預設為1 這裏先改成0后儲存 并且把.sample移除后存檔** 7. 新建一個文件并且内容已經檔名都使用中文,先add進去緩存區 ![](https://hackmd.io/_uploads/rJtM6Hq22.png) 8. 使用commit指令會發現到會報錯就代表pre-commit的指令已經生效由於是0所以動作會繼續執行下去。如果第六步的exit是1的話則會直接中斷結束 ![](https://hackmd.io/_uploads/HJyhaBqn3.png) ### Git進階使用技巧 #### git object 壓縮 >git gc ![](https://hackmd.io/_uploads/BJV4z8923.png) #### 修改最後一次的commit >git commit --amend 目前最後一次的commit message ![](https://hackmd.io/_uploads/rJnCNU5n2.png) 會進入vim界面,可以修改最後一次的commit message ![](https://hackmd.io/_uploads/HkYDBU9n2.png) 修改成first commit2之後用git log重新看 ![](https://hackmd.io/_uploads/ByeqrI5h2.png) #### 修改最後第N次的commit >第一種(默認模式)git reset <commit-id> 第二種(soft)git reset --soft <commit-id> 第三種(Hard)git reset --hard <commit-id> 默認模式->假如要回到第一個commit的話 >git reset <commit-id> ![](https://hackmd.io/_uploads/HkppLL92h.png) 使用git reset ![](https://hackmd.io/_uploads/SyTQP8chn.png) 回到最初的commit狀態 ![](https://hackmd.io/_uploads/ry_rvI5n3.png) 使用git status查看會發現目前的檔案是回到還沒git add新的test2以及test3的時候 ![](https://hackmd.io/_uploads/BJfbuU53h.png) soft模式 **由於同以上步驟這裏省略,唯一的差別是git status** 會發現到使用soft回溯到指定的版本后,git會自動add好,只需commit即可 ![](https://hackmd.io/_uploads/rJmjd8c23.png) Hard模式 使用hard模式的話,你所寫好的程式會完全回到你所指定的commit版本,在這commit之後版本所添加的内容則會完全刪除,會重置成當前所指定的commit版本 ![](https://hackmd.io/_uploads/rJyYYUcnn.png) 而git status則會變成已經完成commit的狀態 ![](https://hackmd.io/_uploads/rkRkc85n3.png)