# Git 指令及操作流程 > Learning note ###### tags: `Git` ## 基本指令 * **git init**:創建一個 git 資料夾(初始化),裡面有預先配置好的東西 * **rm -r .git**:刪除 git * **rm -rf .git**:刪除 git 包含裡面的檔案 * **git status**:顯示目前 git 狀態 ## 決定是否加入版本控制 有兩種狀態: **untracked**:沒有加入版本控制的檔案、**staged**:要加入版本控制的檔案 * **git add code.js**:將 code.js 加入版本控制 * **git add .**:將目前資料夾底下**所有的檔案**都加入版本控制 * **git rm --cached code.js**:將 code.js 移回 untracked 狀態 ## 新建版本 * **git commit**:進入 vim 編輯器,輸入對該版本的敘述(**:q** 退出;**:wq** 存檔退出) * **git commit -m "first commit"**:新建一個版本名稱叫 first commit * **git commit -am "first commit"**:新建一個版本名稱叫 first commit 且將當前資料夾底下**所有檔案**都加入版本控制 > **※底下流程圖有講解 -m 與 -am 的差別** > **※ commit 可以想成 是新建一個資料夾** ## 歷史紀錄 * **git log**:顯示詳細歷史紀錄、commit ID、作者、建立時間、信息(也算是版本名稱) * **git log --oneline**:顯示簡易歷史紀錄、commit ID 前七碼(代指commit ID)、信息(版本名稱) ## 假設 code.js 檔案有進行修改,後續流程: * **git diff**:在 commit 之前,查看這次修改的內容 1. **git status**:查詢狀態,會顯示有檔案被修改過,跑出兩個選擇 2. **git add code.js**:把修改過 code.js 加入 git **git restore code.js**:把 code.js 改回還未修改時的狀態 > **我們選擇將 code.js 加入 git,使用 git status 查詢現在狀態,會跑出 untracked、staged 狀態** **※ 每次只要修改過檔案,都必須將修改過的檔案加入 git →創建新的 commit** 3. **git commit -m "second commit"**:建新版本名稱為 second commit 4. **git log**:這時會顯示兩個版本名稱 first commit、second commit > **※底下流程圖有講解檔案修改後的流程** ## 回到過去/現在的版本 * **git checkout b2dfb1b**:切換到 commit ID 為 **b2dfb1b** 的版本 *(用 git log 查詢 commit ID 複製貼上到 checkout 後方)* * **git checkout master**:回到 master 這個分支的最新狀態 ## 要忽略的檔案(不想放入版本控制的檔案) * **touch .gitignore**:創建 .gitignore * **vim .gitignore**:輸入要被忽略的檔名,假設輸入 test,test 會放入 .gitignore(**i** 輸入;**ESC** 普通模式;**:wq** 存檔退出) *此時使用 git status 查詢狀態,只會顯示 .gitignore 因為我們已經將要忽略的檔案放入 .gitignore* * **cat .gitignore**:顯示被忽略的檔案 `※會放在 .gitgnore 都是一些使用者個人相關檔案、作業系統產生的檔案、對此專案不重要的檔案` ## Git 流程圖 ![Git work flow](https://i.imgur.com/SzGFRYp.png) --- # Branch 概念 ![branch](https://i.imgur.com/DIq4GKi.png) # Branch 指令 * **git branch -v**:顯示有哪些 Branch > **顯示 master 是主要的分支,會顯示在 master 這個分支內最新的 commit 的版本號** master 通常是最主要的分支,但不一定會有最新的 commit。 EX. 想像新開的 branch 會拿來開發其他功能、超前部署,等到確定都沒有問題的時候會再 merge 回 master,在 merge 回來之前,最新的 commit 可能在其他 branch 上。 * **git branch new-feature**:新建一個名為 new-feature 的 Branch > 新建的概念會像是: > 將 master 複製一份命名為 new-feature,所以 new-feature 的 commit 版本編號也會與 master 一樣,只是名字不同而已 * **git branch -d new-feature**:刪除 new-feature 這個 branch ## 切換 branch * **git checkout new-feature**:切換到 new-feature 的 branch * **git checkout master**:切換回 master 的 branch > 這時使用 git branch -v 會顯示我們在 new-feature 這個 Branch **※ 前面有 * 的符號,名字也被反白,就是目前所在的 branch(下圖畫紅線的部分)** ![切換branch](https://i.imgur.com/GVgvRtk.png) ## 合併 branch * **git merge new-feature**:new-feature 合併進來我目前在的 branch > 簡單講: **假設我目前在 master 這個 branch,我使用 git merge new-feature,就會將 new-feature 合併進來(也就是 master 會更新,擁有 new-feature的資料)**,確定合併後就可以將 new-feature 的 branch 刪除 ## 合併時遇到衝突 conflict 簡單講: 合併時,沒有衝突,就沒事 合併時 1.遇到衝突2.點開衝突的檔案,將標記的地方修改後存檔3.重新 commit 就完成了(遇到衝突必須手動完成) 以下舉個範例: 有兩個 branch,master、new-feature,我們都更改了 note.txt 裡面的第一行 1. 我們將 new-feature 合併進去 master 時,note.txt 遇到衝突(git 不知道要將哪個版本保留) (下圖)遇到衝突的狀況會顯示 ![](https://i.imgur.com/cFFxdUs.png) **這張圖的意思就是在說,git 在合併時遇到衝突無法自動合併,需進行手動修改並且 commit** 接下來 2. 開啟衝突檔案 ![](https://i.imgur.com/BW2VI76.png) ▼修改後,只留下要保留的內容,存檔 ![](https://i.imgur.com/HQEaVdR.png) 3. 最後將修改後的檔案重新 commit 就完成了,也會存下 new-feature、master 的 commit ![](https://i.imgur.com/Kz0VXnc.png) // 開發新功能開一個新的 branch 是一個好習慣 ### 一般在 Git 做整合時會用到的指令(流程):開新的 branch →add → commit → push → pull → merge 理想開發新功能流程: 先開一個從 master 複製的分支 **branch**,在新分支上加入檔案 **add** -> **commit** 之後,進行開發,都沒問題再 **push** 到 GitHub 發起 pull request ,GitHub merged 後 **pull** 最新版本到 local,pull 這個過程中會將 local 端的檔案更新至 GitHub 最新內容, 這過程會 **merge** 到 master,如有遇到衝突再進行修改,順利完成下載合併後,將當時複製的那個 branch 刪除。 --- # 狀況劇 ## commit message 送出後發現打錯字如何修改 * **git commit --amend**:進入 Vim 編輯器,修改 commit message(**:wq** 存檔退出),git log 就可看到已修改後的名稱,但這個方法只能**修改最後一次 commit message** * **git rebase**:**修改更之前的 commit message**,[詳細操作流程](https://gitbook.tw/chapters/rewrite-history/change-commit-message.html) 如果你已經 commit 而且又 push 了,那就乖乖認命吧,這種情形下你在 local 端改的話可能會造成其他人的困擾,如果還沒 merge 前**有個破解法**(如下) * **git push -f**:情況如果是 push 之後在 merge 之前,還可以再透過 git push -f 來強制覆蓋該 branch 上的 commit 紀錄來調整,如果 branch 切得乾淨、分工狀態理想,不一定會影響到別人的成果。 > 舉例來說:剛剛 push 上去了一個擁有錯誤 commit 的 branch 上去,此時可以在 local 端先 git reset HEAD^ 將打錯的 commit 或想要修改的檔案修改之後,重新 add, commit,再 git push -f 上去,強制覆蓋遠端的 branch 紀錄,如圖示:![流程](https://i.imgur.com/N0kA12L.png) **※ 最好的方法還是 push 之前先檢查一下,避免錯的東西被放到遠端。** ## 我 commit 了可是我又不想 commit 了 * **git rest HEAD^**:回到還沒 commit 前,但是保留已經修改完檔案的狀態,所以要重新在 commit **HEAD**:最新的 commit **^**:是指前一個的意思 **HEAD^**:最新 commit 的前一個狀態(就是指上一個 Commit 狀態) ## 還沒 commit 可是已經修改檔案,要如何回到還沒修改檔案前的狀態 * **git restore note.txt**:將 note.txt 回到還未修改檔案前的狀態 * **git restore .**:將專案裡面所有檔案還沒 commit ,但已經修改過的檔案回復到未修改前的狀態 範例: 先用 vim 修改 note.txt → git status 查看狀態,發現 note.txt 有被修改過 → 他給提示可以使用 git restore <file名字>可以回復到還沒更改前的狀態(紅線處) → 輸入 git restore note.txt → 再次查閱狀態,會發現檔案回到還沒修改前 ![](https://i.imgur.com/5Wyk5fS.png) ## 更改 branch 的名稱 * **git branch -m newpig**:將 branch 改名為 newpig(-m 後面輸入要改的名稱) 1. 先進入要修改名稱的 branch 2. 輸入 git branch -m newpig 3. 成功將 branch 改名為 newpig ![](https://i.imgur.com/j2XUyJK.png) ## 下載遠端的 branch 假設在 GitHub 上面,其他一起合作此專案的同事有上傳新的 Branch 我們想要下載這個新的 Branch 到 local 這邊舉例我們目前 local 只有 master,feature 是同事上傳的 只要直接輸入(下面的指令就成功下載) * **git checkout feature**:下載 feature(我們要的 branch) 這個 branch 到 local ![](https://i.imgur.com/STlvySV.png) ### 補充觀念: 如果 feature 不是從 local 端發出的,或 feature 在 local 端之外有新的 commit,是沒有辦法直接下載的,要先 git fetch 抓到遠端 branch feature 的狀況,然後 merge 這個新狀況進我們 local 端的 branch feature,或更直接的開一個 branch feature 並 pull 下來之後,才是完整的 branch feature。 對於 **git fetch** 與 **git pull** 的真實意義,可以參考這篇文章:https://gitbook.tw/chapters/github/pull-from-github.html # Git hook 用途觀念小小講解 發生某事的時候通知我 例如:有人 commit 、 push 通知我 通常都是使用在 commit 或是 push 之前,檢查是不是有放一些不該放的檔案、帳號密碼、改到不該改的東西、程式碼是否符合規範