# Git基礎 ###### tags: `好想工作室` 2021.04.13(Tue.)~04.14(Wed.) ## 1. 版本控制 > 參考:[連猴子都能懂得Git入門指南:Git的基本介紹](https://backlog.com/git-tutorial/tw/intro/intro1_1.html) > 參考:[為自己學Git:什麼是 Git?為什麼要學習它?](https://gitbook.tw/chapters/introduction/what-is-git.html) > 參考:[git:1.1 關於版本控制](https://git-scm.com/book/zh-tw/v2/%E9%96%8B%E5%A7%8B-%E9%97%9C%E6%96%BC%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6) > 參考:[程式碼時光機:應用 Git 版本控制,即使犯錯也不怕](https://www.appcoda.com.tw/git-xcode/) 首先,GitHub 和 Git 是不同的東西,Git 是一個在你自己電腦本機端運行的版本控制系統,而 GitHub 則是一個線上服務,讓你將 Git 版本資訊儲放至雲端,就像是 iCloud 雲端硬碟,但它是專屬於程式碼的應用,而且功能更強大。 簡單來說的話,Git是一個工具、Github是一個網站。 **版本控制分為三種:** **1. 本地端版本控制系統** 我們在一個目錄中,對檔案做備份,可能就直接複製貼上,然後改個檔名而已。而我們對這個目錄中的檔案做修改(無論是複製貼上刪除新增),每次的更動都可以說是一個版本,只是用這樣的方法,我們無法看出過去修改過了什麼。 ![](https://i.imgur.com/9Q5xtDy.png) **2. 集中化的版本控制系統** 但是上面那種方法,僅限於個人使用,於是為了可以和其他人一起協同合作,就有了集中化的版本控制。他的做法是有一個伺服器來管理所有版本的檔案,而用戶端會們會連到這台伺服器取出檔案來修改使用。但缺點是如果這一個伺服器server掛了,那伺服器上的所有資料就會遺失。(本地端版本控制也有相同問題) ![](https://i.imgur.com/Sxu6ETx.png) **3. 分散式版本控制系統** 用戶端把整個倉儲做了鏡像。 這樣一來,假設有任何一個協同合作的伺服器故障,事後都可以用任何一個用戶端的鏡像來還原,因為每個地方都有完整的資料備份。 而且不需要伺服器,也不需要網路都可以先做,等到有網路後,在做同步上傳就可以了。 ![](https://i.imgur.com/PDujkHu.png) 而Git就是屬於第3種的分散式版本控制系統。 ## 2. Git基本操作 > 參考:[為自己學Git:使用者設定](https://gitbook.tw/chapters/config/user-config.html) > 參考:[git:1.6初次設定Git](https://git-scm.com/book/zh-tw/v2/%E9%96%8B%E5%A7%8B-%E5%88%9D%E6%AC%A1%E8%A8%AD%E5%AE%9A-Git) > 參考:[連猴子都能懂得Git入門指南](https://backlog.com/git-tutorial/tw/intro/intro1_1.html) 先放上一些專有名詞與中文的對照: | 英文 | 中文 | | ---- | ---- | |Revision|上次提交的狀態與現在狀態的差異記錄| |Commit|提交。| |Push|推送| |Pull|拉取| ### **1. Commit** (A)若想把變更與新增的檔案/目錄儲存到數據庫中,執行commit (B)執行提交後,數據庫裡會產生上次提交的狀態與現在狀態的差異記錄 ### **2. Push** 本地 -> 遠程數據庫 (A)為了將本地端數據庫的修改歷史共享到遠程數據庫,必須上傳本地端數據庫中儲存的修改歷史 (B)執行Push之後,本地端的修改歷史會被上傳到遠端數據庫。 (C)遠端數據庫的修改歷史就會和本地端數據庫的修改歷史保持同步。 ### **3. Pull** 遠程數據庫 -> 本地 (A)欲同步遠端數據庫以更新本地端數據庫,執行pull (B)執行pull之後,會從遠端數據庫下載最新的修改歷史,將其同步到自己的本地端數據庫。 **** 一些基本指令: ### **0. 新增檔案** ``` $touch welcome.html ``` ### **1. 首次安裝Git後,設定名稱、電子信箱** 在你安裝 Git 後首先應該做的事是設定使用者名稱及電子郵件。 注意:若你有傳遞 --global 參數,只需要做這工作一次,因為在此系統,不論 Git 做任何事都會採用此資訊。 (A)`$ git config --global user.name "名字"` (B)`$ git config --global user.email 信箱@example.com` ### **2. 檢查設定值** `$ git config --list` ### **3. 檢查特定設定值** `$ git config <key> ` 例如說想要找之前設定username,那就打`$ git config user.name ` ### **4. 取得說明文件** 如果使用Git時遇到問題,想要取得 Git 命令說明文件,有3種方法: (A)`$ git help <verb>` (B)`$ git <verb> --help` (C)`$ man git-<verb>` 例如說我想找config的命令說明文件,那用(A)的方法就是打`$ git help config` ### **5. 在現有資料夾中初始化目錄** 初始化這個目錄,讓 Git 對這個目錄開始進行版控。 `$git init` ### **6. 查詢目錄狀態** 這個指令的用途是用來查詢現在這個目錄的「狀態」。 `$git status` 檔案目前的狀態是 Untracked files,意思是這個檔案尚未被加到 Git 版控系統裡,還沒開始正式被 Git「追蹤」,它只是剛剛才加入這個目錄而已。 那麼建議應該馬上追蹤這些原本就有的檔案,然後進行第一次提交。 你可以通過多次 git add 指令來追蹤完所有你想要追蹤的檔案,然後執行 git commit 提交: ``` $ git add *.c $ git add LICENSE $ git commit -m 'initial project version' ``` 舉例來說,我有一個welcome.html的檔案還是Untracked files,那我可以先: ``` $git add *.html //此種方法可以將所有html檔一次加到暫存區 $git add welcome.html //此種方法就是只單加welcome.html到暫存區 $git add --all //此種方法是將所有檔案都加上去 ``` 此時,再用`$git status`看一下目錄狀態,就可以發現剛才那個檔案從 Untracked 變成 new file 狀態了。這表示這個檔案已經被安置到暫存區(Staging Area),等待稍後跟其它檔案一起被存到儲存庫裡。而這個暫存區也稱之索引(index) ### **7. 提交你的修改** 如果僅是透過 git add 指令把異動加到暫存區是不夠的,這樣還不算是完成整個流程。要讓暫存區的內容永久的存下來的話,使用的是 git commit 指令: ``` $ git commit -m "init commit" ``` 在後面加上的 -m "init commit" 是指要要說明「你在這次的 Commit 做了什麼事」,只要使用簡單、清楚的文字說明就好,中、英文都可,重點是清楚,讓不久之後的你或是你的同事能很快的明白就行了。(指"init commit"裏頭的文字就是拿來說明你對文件做了什麼修改。 要完成 Commit 指令才算是完成整個流程! **重點:** 「Git 每次的 Commit 都只會處理暫存區(Staging Area)裡的內容」。也就是說,如果在執行 git commit 指令的時候,那些還沒被加到暫存區裡的檔案,就不會被 Commit 到儲存庫裡。 **整理:** 在 Git 裡,主要可以分成「工作目錄(Working Directory)」、「暫存區(Staging Area)」以及「儲存庫(Repository)」三個區塊,透過不同的 Git 指令,可以把檔案移往不同的區域 ![](https://i.imgur.com/J2Whsa9.png) (A)git add 指令:檔案從工作目錄 -> 暫存區(或索引)。 (B)git commit 指令把暫存區的內容 -> 儲存庫。 ### **8. 檢視提交的歷史記錄** 在產生數筆提交(commit)或者克隆(clone)一個已有歷史記錄的版本庫之後,你或許會想要檢視之前發生過什麼事; 最基本也最具威力的工具就是 git log 命令。 ``` $git log ``` git log 命令有大量且多樣的選項,能精確地找出你想搜尋的結果: (A)**顯示每筆提交所做的修改內容**:`-p`用來顯示每筆提交所做的修改內容; 你還可以加上`-2`選項,限制只輸出最後兩筆提交內容。 ``` $git log -p -2 ``` (譯註:使用 - + 來表示差異,- 是刪除行,+ 是新增行;未修改的上下文資訊預設是三行,用來定位有修改的地方) (B)**若想檢視每筆提交簡略的統計資訊**:`--stat`選項在每筆提交訊息的下方列出「被更動的檔案」、「總共有多少檔案被更動」、「這些檔案中有多少行被加入或移除」; 它也會在最後印出總結訊息。 ``` $ git log --stat ``` (C)**改變原本預設輸出的格式**: `--pretty`, 用來改變原本預設輸出的格式; 有數個內建的選項供你選用, 其中`oneline`選項將每一筆提交顯示成單獨一行,對於檢視大量的提交時很有用; 更進一步,`short`、`full`、`fuller` 選項輸出的格式大致相同,但分別會少一些或者多一些資訊。 ``` $git --pretty=oneline ``` (譯註:「作者(author)」與「提交者(committer)」之間的差別, 作者是最初修改的人,而提交者則是最後套用該工作成果的人) ### 9. 刪除檔案 (A)**方法一**: 使用系統指令 rm 或是檔案總管之類的工具來刪除檔案: ``` $rm welcome.html ``` 把刪除當作是修改,所以修改完要再add到暫存區: ``` $git add welcome.html ``` 既然到暫存區,如果確定要做「刪除」這個修改的話,就可以給他commit上去: ``` $git commit -m "delet it" ``` (B)**方法二**: 不過其實可以直接讓git幫你砍掉: 他就會直接到暫存區,少了add這個步驟 ``` $git rm welcome.html ``` ### 10. 不是真的想把這個檔案刪掉,只是不想讓他再被 Git 控管 運用關鍵字`-cached`,加上第9點的`$git rm 檔名`即可: ``` $git rm welcome.html -cached ``` 這樣一來,他並不是真的被刪除掉,而是單純把檔案從git裡面給移除掉而已。 ### 11. 變更檔名 跟刪除檔案一樣,變更檔名也是一種「修改」,所以操作上其實也是差不多的,所以方法一就不打了,直接運用方法二: ``` $git mv hello.html world.html //意思是將檔名從hello改成了world ``` 雖然只是改檔名,但對 Git 來說會被認為是兩個動作,一個是刪除 hello.html 檔案,一個是新增 world.html 檔案(變成 Untracked 狀態)。 ### 12. 復原 要修改最後一次的 Commit 訊息,只要直接在 Commit 指令後面加上 --amend 參數即可: ``` $git commit --amend -m"修改內容" ``` ### 13. 把檔案併入最近一次 Commit 剛完成Commit才發現有一個檔案忘了加到,但又不想為了這個檔案重新再發一次 Commit時,就可以使用`$git reset`以及`--amend` (A)第一步驟: 利用`$git reset`把最後一次的 Commit 拆掉,加入新檔案後再重新 Commit。 ``` $git reset ``` (B)第二步驟: 使用`--amend`進行Commit(第12點有提到`--amend`) ``` $ git commit --amend --no-edit ``` `--no-edit`參數的意思是指「我不要編輯 Commit 訊息」 ### 14. 新增目錄 假入我現在加入了一個目錄,看一下狀態`$git status`,會發現 Git 的狀態沒有變化。 ``` $ mkdir images ``` 這是因為 Git 在計算、產生物件的時候,是根據「檔案的內容」去做計算的,所以光是新增一個目錄,Git 是沒辦法處理它的。也就是說,空的目錄無法被提交! 要解決這個問題,就只要在那個空目錄裡隨便放一個檔案就行了。 如果目前沒東西可放,或不知道該放什麼檔案,可以放一個名為 `.keep`或 `.gitkeep`的空檔案,讓 Git 能「感應」到這個目錄的存在。 ``` $ touch images/.keep ``` ### 15. 忽略不需要的檔案 有些比較機密的檔案,例如資料庫的存取密碼或是 AWS 伺服器的存取金鑰,或者說任何對這個專案不必要的東西,當我不想把他們放在 Git 裡面一起備份時,可以這樣處理: 先新增一個檔案叫做`.gitignore`: ``` $ touch .gitignore ``` 然後點進去檔案做編輯的動作,而編輯的內容,就是你希望git去忽略的東西,例如說你這樣編輯他: ``` # 忽略 secret.yml 檔案 secret.yml # 忽略 config 目錄下的 database.yml 檔案 config/database.yml # 忽略所有 db 目錄下附檔名是 .sqlite3 的檔案 /db/*.sqlite3 # 忽略所有附檔名是 .tmp 的檔案 *.tmp # 當然你要忽略自己也可以,只是通常不會這麼做 # .gitignore ``` **忽略這個忽略:** 雖然.gitignore這個檔案有列了一些忽略的規則,但其實也可以忽略這個忽略的規則。只要在git add的時候再加上`-f`的參數,就可以無視規則,強迫闖關: ``` $ git add -f 檔案名稱 ``` **注意:** 如果說新增.gitignore裡想忽略掉的列表前,就已經存在其中一個符合忽略規則的檔案,則他不會被忽略,因為他在.gitignore新增之前就存在了,有點像是不溯及既往的規則。 **清除忽略的檔案:** 如果想要一口氣清除那些已經被忽略的檔案,可以使用 git clean 指令並配合 -X 參數: ``` $ git clean -fX ``` 那個額外加上的 -f 參數是指強制刪除的意思,這樣一來就可清除那些被忽略的檔案。 ### 16. 想知道某行程式是誰寫的 如果想知道檔案中,程式的某行是誰寫的,可以利用`$ git blame`: ``` $ git blame welcome.html ``` **只想看其中幾行:** ``` $ git blame -L 5,10 welcome.html ``` 利用`-L`後面的數字即限制的行數,以上面為例,就是只會顯示出第5 ~ 10 行的資訊。 ### 17. 不小心把檔案、目錄刪除了 利用`checkout .`來還原不小心用`rm`刪除的檔案、目錄: ``` $ rm *.html //刪掉全部html檔案 ``` ``` $ git checkout welcome.html //找回welcome.html一個檔案 $ git checkout . //可以一次找回剛剛誤刪的檔案或目錄 $ git checkout HEAD~2 welcome.html //拿距離現在兩個版本以前的那個 welcome.html檔案 //來覆蓋現在的工作目錄裡的 welcome.html 檔案 //同時也會更新暫存區的狀態 $ git checkout HEAD~2 . //拿距離現在兩個版本以前的檔案來覆蓋現在工作目錄的檔案 同時也更新暫存區裡的狀態 ``` 另外,因為整個Git的紀錄都是放在根目錄的.git目錄裡,所以如果這個目錄被刪了,也就是表示歷史紀錄也被刪了,那就真的沒得救了。 ### 18. 還原版本 如果commit完後悔了,想要拆掉重新來過,可以用`reset`: 先假如我們目前的Git紀錄: ``` $ git log --oneline 0084a3e (HEAD -> master)檔案1 51adeab 檔案2 93131b2 檔案3 a04a64f 檔案4 9544e00 檔案5 ``` (A)相對的方法: ``` $ git reset 0084a3e^ //而這裡剛好HEAD跟master都指向0084a3e個commit //所以也可以像下面這樣寫: $ git reset HEAD^ $ git reset master^ ``` 在最後面的`^`符號代表的是往前幾次,像這裡就是往前一次,也就是說上面程式碼代表的是,要退回`0084a3e`的前一次版本。 換句話說如果是`^^`,則是到`0084a3e`的前兩次的版本,以此類推。不過如果要五次以上的話則直接`0084a3e~5` (B)絕對的方法: 因為`0084a3e`的前一次正好是`51adeab`,所以可以直接如下所做: ``` $ git reset 51adeab ``` **git reset參數:** | 模式 | - - mixed(預設) | - - soft | - - hard| | ---- | --------------- | ------ | --- | |commit拆出來的檔案|丟回工作目錄|丟回暫存區|直接丟掉| **** **終端機常用的指令** > 網站:[終端機及常用指令介紹](https://gitbook.tw/chapters/command-line/command-line.html) 補充:想清空畫面上的內容,除打clear之外,也可以按 `CTRL` + `L`