kyle shanks
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: 'Git 使用 - 本地' disqus: kyleAlien --- Git 使用 - 本地 === ## OverView of Content 如有引用參考請詳註出處,感謝 :smile: > Git 原本就是基於 Linux 在方便管理 Linux kernal 而開發出來的,現在已被廣泛的應用在各種大小項目中,詳細可以 [**參考文章**](https://zlargon.gitbooks.io/git-tutorial/content/) 本章大部份都是實做 參考文章 [TOC] ## 安裝 Git * 在 Linux or Ubuntu 中**使用 shell 視窗用指令安裝** ```shell= sudo apt-get install git sudo apt-get install git-core ``` > 當然你也可以去下載源碼壓縮包,自己進行 make/make install * Window 中須去網站中下載 [**Git for windows**](https://gitforwindows.org/) 並安裝 > ![](https://i.imgur.com/QrhZfMD.png) :::info * Git 源碼的依賴包:`Curl`、`Zlib`、`Openssl`、`Expat`、`Libiconv` ``` mermaid graph TD; Git應用-->Curl; Git應用-->Zlib; Git應用-->Openssl; Git應用-->Expat; Git應用-->Libiconv; ``` ::: ## Git 基礎使用 雖然 Git 有各種 UI 界面可以操作,**但是 Git 的各種命令才是應該掌握的核心技能** ### 建立 Git 專案 - init * 接著 **到 project 中** 創建倉庫代碼,**創建完後就可以看到一個 ==隱藏的 .git 文件==資料夾** ```shell= # 創建指令 git init ``` 1. 當然你可以創自己的需要的檔案 ```shell= mkdir ~/桌面/EnjoyGit cd ~/桌面/EnjoyGit git init ``` > ![](https://i.imgur.com/z94ESjp.png) 2. Android Project 資料夾下,在創建專案時就有預設 `.git` 檔案 > ![](https://i.imgur.com/vU6PaiA.png) :::info * **當不需要該倉庫時只需要 `rm -rf .git` 就可以了** ::: ### 基礎配置 - config * config 配置有三個部分:子區塊可以覆蓋父區塊設定(類似 Override 的概念) | config 配置位置 | 功能介紹 | | -------- | -------- | | `/etc/gitconig` | 系統截別設置(選項 `--system`) | | `~/.gitconfig` | 用戶級別設置(選項 `--global`) | | `.git/config` | 項目節別設置,內建在每個項目之中 | * 以下使用用戶級別設置(`--global`),看看幾個常用指令 | 指令 | 功能介紹 | | -------- | -------- | | git config --global <option\> | **==global 代表了對於所有的 git Project 都採用這些設置==** | | git config --list | 也可以寫成 -l,它會**查看目前 git 的設置** | | - | - | | git config --global color.ui <true \ false\> | 開啟或關閉預設的色彩 | | git config --global core.editor vim | 預設.git 檔案的編譯器,使用 vim 當編譯器 | | git config --global core.ignorecase <true \ false\> | 是否忽略檔案的大小寫,**在 window & Mac 中預設是不會分大小寫的,而 Linux 系統則會區分** | * 首先要身份配置,這樣才可以知道 git 的基礎訊息,在下載 Android Source 時也會需要你去配置 Git 訊息 ```shell= # 配置,該配置可以隨時根改 git config --global user.name "Alien" git config --global user.email "Alien@gmaill.com" # 查詢 git config --global user.name git config --global user.email ``` > ![](https://i.imgur.com/KyjCIwy.png) ```shell= # 查詢全部設置 git config --list ``` > ![](https://i.imgur.com/aroP3nc.png) ### 定義指令別名 - alias * 可以在 config 中設定自己需要的指令 **別名**,這個動作可以方便去記憶,或是加快輸入指令,但 **若是常用指令則建議不要用 alias 別名** | 指令格式 | 功能 | | -------- | -------- | | git config --global alias.<別名> <原指令> | 全域的別名 | | git config alias.<別名> <原指令> | 本域的別名 | 1. 設定 `--global alias` ```shell= # 設定 status 別名為 st git config --global alias.st status # 查看全域變數設定 git config --global --list ``` > ![](https://i.imgur.com/l16FPGj.png) 2. 設定 `--global alias` ```shell= # 設定 status 別名為 sta git config alias.sta status # 查看本域 config 設定 git config --list ``` > ![](https://i.imgur.com/gjyjyYU.png) ## Git 檔案管理 ### 檔案新增 -add & 修改 -diff & 查看 -show * 現在來觀察我們對於同一個創建出來的檔案修改後 git 會有哪裡改變,先修改一下上面的 Hello_Git.c 文件為 txt 檔案(懶的在寫 c 程式範例 :D) | 指令 | 功能 | | -------- | -------- | | git diff | 查看 ==全部== 修改的內容 | | git diff <file or commit id\> | 查看 **指定** 修改的內容 | | git diff --cache/staged | 查看已提交 (commit) 的檔案 | | git add ./-A/-All | 該目錄下所有檔案加入本地倉庫 | | git commit -m <message\> | 使用非編譯模式提交代碼 | | git show <id\> | 可查看指定代碼的內容 | | git show --oneline | 可以簡單的看到主要的 6 碼 id & 標題 | 1. `git status` 查看哪些檔案被修改 ```shell= # 改名 mv Hello_Git.c Hello_Git.txt # 接續輸出 echo "Time to Work" >> Hello_Git.txt # 使用 git status 查看 git status ``` > ![](https://i.imgur.com/mhiDgN0.png) 2. `git diff <file>` 查看特定檔案的**修改內容**,diff 是與最後一次 patch 比對差異,`+` 號代表了新增 ```shell= git diff Hello_Git.txt ``` > ![](https://i.imgur.com/sVU82gL.png) 3. `git diff` 假設有**多個檔案被更改** ```shell= # 新增檔案 echo "YoMan" > Yo.txt # 使用 git status 查看,會發先多了一個 Untracked files git status # 將該目錄下所有檔案加入 git 倉庫 git add . # 再次查看狀態 git status ``` > ![](https://i.imgur.com/CgLyz2L.png) :::info 可使用 git add -a / --all / . 加入該目錄下所有檔案 ::: 4. 提交後的檔案(已 Commit)要查看必須要使用 git diff `--cached` or `--staged` 才能查看 ```shell= # 提交檔案 可使用 -m 代替,跳過文字編譯 git commit -m "Test commit -m" -m "Hey" # -m <標頭> -m <描述...可多個> # 查看 commit 後檔案是看不到的 git diff Hello_Git.txt # 必須使用 --cached or --staged git diff --cached Hello_Git.txt git diff --staged Hello_Git.txt ## 並使用 git log 查看 git log # 會顯示出 提交的時間、作者、描述、檔案 git log 6833f5 # 使用 ID 查詢指定提交 ``` > ![](https://i.imgur.com/vM0cYd7.png) 5. `show log --oneline` 查看簡單的 id & title > ![](https://i.imgur.com/hyEONQF.png) ### 刪除檔案 - rm | 指令 | 功能 | | -------- | -------- | | git rm <file\> | 刪除指定檔案 | | git rm -rf <filefloor\> | 刪除資料夾包括內容 | | git add -u/--update | 一次性加入所有更動的檔案,不只是修改的檔案,也包括刪除的檔案,**但是未追蹤檔案並不算在 update 中** | 1. 刪除檔案 git rm 查看 git status 的狀態,會顯示 **deleted 綠色的字(已 add 狀態)** ```shell= // 刪除指定檔案 git rm Yo.txt ``` > ![](https://i.imgur.com/zgsplEE.png) 2. 更新檔案狀態 git add -u ```shell= // 更新檔案 git add -u // 同上 git add --update ``` > ![](https://i.imgur.com/SfcC15V.png) ### 移動檔案 - mv * 與一般 Linux 操作相同,使用 mv 操作,它與 **git mv 的差別在於 ++不用多做出 add/rm 的動作++**(自動添加到儲存的狀態),但是提交仍然需要自己 commit 提交 | 指令 | 功能 | | -------- | -------- | | git mv <file\> <move Path\> | 移動檔案,**被移動的檔案其代表了也是移除的意思** | ```shell= # 在倉庫內新增資料夾 mkdir MyPath ls -l # 移動檔案 git mv Hello_Git.txt MyPath/ # 確認狀態 git status # renamed 狀態 # 提交 git commit -m "test move file" # 描述時中間有空白格就必須使用雙引號括起來 ``` > ![](https://i.imgur.com/GFSTVXn.png) :::warning * git mv **不可以搬移檔案到倉庫以外的地**方,否則會出 fatal: outside repository 的錯誤,並無法移動 > ![](https://i.imgur.com/NjW8TeH.png) ::: ### 重新命名 - mv * 在 git 中改名的方式可以如同 Linux 中的方式使用 mv 修改,**差別也在於一般指令的 mv 會被認定為檔案移除並要自己添加** 使用 `git mv` 會自動幫你做添加到追蹤中檔案 | 指令 | 功能 | | -------- | -------- | | git mv <old name\> <new name\>| 改名 | ```shell= # 使用 mv 改名稱 git mv Hello_Git.txt Hi_Git.txt git status git commit -m "test mv change File Name" ``` > ![](https://i.imgur.com/SO8KzfJ.png) :::info * **git 會自動忽略空資料夾**,並不會把空資料夾方入倉庫中,所以 rm -rf Myfile 在 git status 上並不會有差異 > ![](https://i.imgur.com/HBTASVO.png) ::: ### 忽略文件 - .gitignore * 大部分我們不需要全部的文件都上傳,而是上傳需要的文件,在提交文件時 Git 會檢查該目錄下有沒有 **==`.gitignore` 文件==**,它會判斷文件的內文容來忽略某些文件 1. 可以在每一個目錄中都存在 `.gitignore` 檔案 2. 作用範圍包含整個資料夾 & 資料夾下的子資料夾 ```shell= # 修改文件內容 vim .gitignore ... /src/test # 新增提交項目 git add . # 提交 git commit -m "fix gitignore file" ``` :::info * Android project 中的 build 資料夾就不需要,因為**它是由編譯器產生的,可直接使用**,在產生專案時它會自動產生 **`.gitignore` 文件**,不須自己創建 > Android 會自動幫我們指定 `.iml` 結尾的全部文件、build 目錄,在默認狀況下就不會加入版本控制中 * 在這當中如果有測試文件就可以排除,就是在 `.gitignore` 中添加測試文件就可以 > ![](https://i.imgur.com/ypqwS0x.png) ::: * 當然也可以使用指令,只加入某個檔案 | 指令 | 功能 | | -------- | -------- | | git add -f/--force <1...多個檔案>| **強制只加入某個檔案**,其他檔案則忽略 | ```shell= # 新增一個除錯檔案 touch myDebug.d ls -l # 改變一檔案 echo "1" >> NewFile.txt # 觀察所有檔案變化 git diff # 只加入 NewFile.txt 檔案 git add -f NewFile.txt git status ``` > ![](https://i.imgur.com/qch3awg.png) ### 檔案狀態 - status * 從 **git status** 可以看到幾種狀態 | 狀態 | 檔案狀態 | 意義 | | - | -------- | -------- | | `Untracked` | Untracked files | 全新檔案(尚未添加) | | `Unmodified`/`Staged` | Changed to commit | 檔案更改並 git 知道,但未提交(已經在 Git 控管之下) | | `Modofied` | Changes not staged for commit | 檔案更改,但 git 尚未暫存(不會進入下次的 commit 中) | > ![](https://i.imgur.com/0AC0JQj.png) 1. **全新檔案 `Untracked files`**:git 尚未檔案感知到有該檔案的存在 ```shell= # 新增檔案 echo "New File" >> NewFile.txt ls -l # 查看檔案狀態 git status ``` > ![](https://i.imgur.com/xfX6K1n.png) 2. **改動檔案 `Changes not staged for commit`**:如果對已添加追蹤的檔案(已 `git add <file>`)改動,那該檔案就會退回到 已追蹤,但尚未保存(Stage) 的狀態 ```shell= # 去任檔案內容並修改 cat Hi_Git.txt echo "Changes not staged for commit" >> Hi_Git.txt cat Hi_Git.txt # 查看檔案狀態 git status ``` > ![](https://i.imgur.com/WjKS73D.png) :::success * **可使用 git checkout <file\> 退回到未修改前的狀態** ::: 3. **add 改動的檔案 `Changed to commit`**:git 更新追蹤檔案的狀態 ```shell= # 去檔案修改其內容 echo "Changes not staged for commit" >> Hi_Git.txt git diff Hi_Get.txt git status ``` > ![](https://i.imgur.com/FvotX5X.png) :::success * 可使用 **git reset HEAD <file\>** 去把檔案退到未 add/patch 之前的狀態 ::: ### 檔案還原 - checkout * 在檔案狀態中我們有提到,可以還原檔案,那我們在這裡整理一下 | 指令 | 使用時機 | 功能 | | -------- | -------- | -------- | | git checkout -- <file\>| 已追蹤的檔案(非新檔案) | 可以退回到上一次追蹤的狀態,**==退到 delete 之前也可以使用==** | | git reset HEAD | 已 add 的檔案 | **未指定檔案則還原 ++全部++ 檔案** | | git reset HEAD <file\> | 已 add 的檔案 | **指定檔案,還原檔案狀態** | 1. **git checkout <指定檔案**> 從下圖中可以看出 `Hi_Git.txt` 檔案的改動尚未被 git 追蹤,透過 `git checkout Hi_Git.txt` 可以將檔案退回到 git 追蹤的內容 > ![](https://i.imgur.com/Pz5VMnf.png) 2. **git reset Head <指定檔案>** 對於已經添加(已經 add)的檔案進行 `git reset Head` 指令,可以將其退回到 HEAD 的狀態(HEAD 之後說明) > ![](https://i.imgur.com/D404GjO.png) ## Patch 管理 (Commit) :::success * **Patch& Commit**: commit 在英文中只有動詞的意思,所以如果要表達會變成 commit a commit (提交一個完整的程式) 比較繞口,所以**使用 commit a patch 來取代,具體來說 commit(n.) Patch 有很相像的意思,Patch 完善已有的程序** ::: | 指令 | 功能 | | -------- | -------- | | git log --pretty=raw | 查看 parent 的資訊,**沒有加入 `=raw` 是無法查看到 parent的** | * **git log --pretty=raw** 來觀察 parent,可以看**出上一次的 commit 就會是下一次的 parent**,這是一種 **父子關係** ```shell= # 在 EnjoyGit 資料夾中 git log --pretty=raw ``` > ![](https://i.imgur.com/MlY7ivA.png) ### HEAD 關鍵字 * **HEAD 為 git 的關鍵字,意指==目前所在 patch 的位置==,也就是++串列的頭++** (常常是代表你當前所在的位置) > ![](https://i.imgur.com/zH50H7l.png) | 指令 | 功能 | | -------- | -------- | | git log <-\-online> | 這除了可以觀察出提交的 title、ID 外 (-\-oneline 可有可無),還**可以看出 HEAD 在哪** | | git show HEAD<^> / HEAD<~1> | 以目前的 HEAD 作為基準點,**往前查詢,假設查詢該頭的前 2 個 patch 訊息可以使用 `git show HEAD^^`、`git show HEAD~2`**,可加入 --oneline 去簡化 | | git reset HEAD^ / HEAD<~1> | 以目前的 HEAD 作為基準點,**退回前一次 Patch**,檔案保留 | | git reset **-\-hard** HEAD^ | **跳轉上一次 Patch,++強制清除++ 版本之外的檔案**,但是**未 track 的檔案不會刪除** | | git rest **-\-hard** <commit_id\> | **跳轉指定 Patch,強制改變到該 id patch 的版本(可往前 or 往後)**,但是**未 track 的檔案不會改變** | 1. 觀察 **git log** 的 HEAD FILE ```shell= # 完整板 git log # 簡化板 git log --oneline ``` > ![](https://i.imgur.com/jXdSICt.png) 2. 以 HEAD 為基準,**git show HEAD~3** 查看前 3 版本的 Patch 的訊息 ```shell= # 先查看所有的 Patch git log --online # 離 HRAD 前 3 版本的 Patch git show HEAD^^^ # 功能同上,只是加了簡化板 oneline git show HEAD~3 --oneline ``` > ![](https://i.imgur.com/ChWXzh9.png) 3. 使用 **`git reset HEAD^`** 退回到 上次提交 (commit) 之前,**檔案依然存在,但是退回到 `Modified` 狀態** > 等同於 `git reset HEAD~1` ```shell= # 新增檔案並提交 touch HeadRest.txt git status git add HeadRest.txt git commit -m "New test file" -m "add one file to test Head reset" git status git log --oneline # 跳回前一次 Patch git reset Head^ # 觀察倉庫狀態 git status # 觀察實際檔案狀況 ls -l # 檔案沒有被移除 ``` > ![](https://i.imgur.com/oQh0jgr.png) 4. 使用 **git reset ++--hard++ HEAD^**,會發現檔案被移除 ```shell= # 加入上次刪除的檔案 & 並觀察 log git add HeadReset.txt git commit -m "Test reset Hard" git log --oneline # 強制跳回前一次 Patch git reset --hard HEAD^ git log --oneline # 觀察倉庫狀態 git status # 觀察實際檔案狀況 ls -l # 檔案 HeadReset.txt 被移除 !!! ``` > 可以發現 **Untrack 的檔案 myDebug.d 並無差異**(因為它還沒進入 git 的掌控) > > ![](https://i.imgur.com/OyhNs1U.png) 5. **git reset --hard <commit_id>** 以下會演示,退回到最初 patch & 前進到最新 patch 的差異 ```shell= git log --oneline ls -l # 退回到最初檔案 git reset --hard 6833f51 ls -l # 會發現少了 Newfile.txt 檔案 # 前進到最新檔案 git rest --hard fb411ed ls -l # Newfile.txt 檔案回來了 ``` > 可以發現 **Untrack 的檔案 myDebug.d 並無差異** > ![](https://i.imgur.com/gOfJhWA.png) :::danger 1. **git reset --hard HEAD^** 代表 **移除** 了上一次更改的內容,如果忘了紀錄上一次版號,要救回只能使用 ctrl + z 大法(或是 `reflog`),才有可能救的回來 > `--hard` 要小心使用 (好好紀錄上一次的 commit id 就很重要了) 2. 切回到上個版本忘了紀錄最今版本的 ID 還是可以查的到,git 會將所有 commit 過的 patch 都存下來 ::: ### 找回遺失的 Patch - reflog * 在 git 中,**凡是動到 HEAD 的操作都會被 git 記錄下來**,那我們先看看哪些動作會讓 HEAD 改變 1. git commit:提交動作 2. git reset --hard <commit_id\>:**轉移切換** HEAD 3. git cherry-pick/revert:**挑入/挑出** patch 的時候 4. git checkout <branch\>:**切換**分支的時候 5. git merge/rebase ...:**合併**分支的時候 | 指令 | 功能 | | -------- | -------- | | git reflog | **查看 HRAD 移動 or 改變的簡單紀錄** | | git log -g | 同上功能,但是紀錄詳細 | | git show @{<數字>} | 查看指定 HEAD 的詳細訊息 | 1. **git reflog** 查看簡單的 Patch 訊息 ```shell= # 查看 HEAD 紀錄 git reflog # 查看 指定 HEAD 紀錄 git show HEAD${1} git show HEAD${1} ``` > ![](https://i.imgur.com/tprx2ZY.png) 2. **git log -g** 查看詳細 HEAD 訊息 ```shell= git log -g ``` > ![](https://i.imgur.com/dtsg5gX.png) ### 修改、訂正 Patch - amend * 有時後會出現不小心提交了描述錯誤的檔案 or add 錯檔案,按照之前的作法必須 git reset HEAD^ 退回上一個版本,再進行修改,最後提交 | 指令 | 功能 | | -------- | -------- | | git reset --soft HEAD^ | **退回到 change to be committed,++不需要再次 add 動作++** | ```shell= # 退回上一個版本,保留檔案,但是要重新 add git reset HEAD^ # 加入正確檔案 git add RIGHT.txt # 提交 git commit -m "Fix Commit Error" ``` * 而在你尚未 push 之前,我們可以使用 commit 的` --amend` 選項直接修改提交的內容 (他的行為是創建另一個新的 id) | 指令 | 功能 | | -------- | -------- | | git commit **-\-amend** | **使用 vim (要設定 core.editor)編譯** 最後一次提交的檔案 | | git commit **-\-amend** -m <title\> -m <describe\> | 不使用編譯器,改為直接修改並提交 | | git commit **-\-amend** -m <title\> | amend 還有 ==**合併提交** 的功能== ,產生一個 新的提交 ID 並且 **覆蓋** | 1. 先提交一個錯誤描述 ```shell= echo "Test git reset --soft HEAD^" >> Hi_Git.txt # 更新修改的資料 git add -u git status # 提交,但描述 -m 錯誤 git commit -m "Error test" git status git log --oneline ``` > ![](https://i.imgur.com/0oTaUcm.png) 2. 使用 **git reset --soft HEAD^**,將檔案退回到 change to be committed 狀態(不用重新 add) ```shell= # 使用 git reset --soft HEAD^ git reset --soft HEAD^ # 查看 HEAD 是否移動 git log --oneline # 查看 status,退回到 Change to be committed 狀態 git status # 再次提交 git commit -m "Right Test" git log --oneline ``` > ![](https://i.imgur.com/0AmNBQZ.png) :::warning * 在圖片中可以看到它的提交 ID 已經不一樣了,e39d77b(Error) -> 0933fd7(right),因為 **==每一次提交,必定都會有不同的 ID==** | reset 指令 | 功能 | | -------- | -------- | | git reset HEAD^ | 回到前一個 patch,且恢復檔案的狀態(不刪除檔案,但必須重新 add 檔案) | | git reset --soft HEAD^ | 到前一個 patch,但保持檔案狀態為 Changes to be committed(免去 add 動作) | | git reset --hard HEAD^ | 回到前一個 patch,且強制清除檔案的修改內容(會強制移除) | > ![](https://i.imgur.com/A8jXhft.png) ::: 3. **git commit --amend** 編譯最後一次提交的訊息 ```shell= git commit --amend # 提交 ID 會改變 git log --oneline # 查看修改的描述 git show ``` > ![](https://i.imgur.com/chMe0vv.png) 4. **git commit --amend -m** 不使用編譯器 ```shell= # 使用指令修改 git commit --amend -m "AmendTest" -m "Use command fix by --amend" git show git log --oneline ``` > ![](https://i.imgur.com/euGox2S.png) 5. **git commit 合併提交**,這次修改檔案並提交 ```shell= echo "Test git commit --amend -m <title> to merg commit" >> Hi_Git.txt git add -u git commit --amend -m "merga by commit --amend" git show ``` > ![](https://i.imgur.com/6TUcJcx.png) :::danger * 要特別 **注意使用 --amend**: 因為**就算進入 vim 後不修改 :`q!` 它仍會為你產生新的 ID**,補救方案,**使用 git reflog 查看歷史 HEAD,並使用 git reset --soft HEAD@{1}** > ![](https://i.imgur.com/IHkGIga.png) **--實做--** > ![](https://i.imgur.com/ZFi0qiT.png) ::: ### 移除單個 Patch - rebase * 之前使用移除 Patch 的方式是 git reset --hard <commit_id\>,這個方法就是移動 HEAD 會導致失去中間的檔案,而這些遺失的檔案就可以使用 git cherry-pick 取回 > 改動後的檔案由於**有新的 ID,其 parent 關係也會改變** 1. 移動 HEAD 到指定檔案 2. `cherry-pick` or `rebase -i` 需要的檔案 3. 執行串接動作 * 以下指令可以用來重新排序、刪除特定檔案 | 指令 | 功能 | | -------- | -------- | | git cherry-pick | **取回遺失檔案,串接上目前的 HEAD,++串接上的 ID 會更改,並且成為 HEAD++** | | git rebase -i <**哪裡之後的開始修改's ID**> | -i 同等於 `--interactive`,**啟動 ==互動模式==,使用文字編譯器 vim 來編譯其順序**,簡單來說它完成多次 cherry-pick 的動作 | 1. 使用 **git cherry-pick** ```shell= # 查看目前的 git 串接狀態 git log --online # 使用 reset 移動 HEAD 到 2381fc2 git reset --hard 2381fc2 # 確認移動狀況 git log --online # 取得之前區要的檔案並且串接上 (有新 id & 成為新 HEAD) git cherry-pick fb411ed git cherry-pick f278864 ``` > ![](https://i.imgur.com/2kijlaD.png) **--實做--** > ![](https://i.imgur.com/Tymv4HZ.png) :::info * **移除過後的檔案也可以使用 cherry-pick 串接回來成為 HEAD** ```shell= // 原來 (remove Target File) -> Test Git Status -> AmendTest // 使用 cherry-pick 串回 Test Git Status -> AmendTest -> (remove Target File) ``` ::: :::warning * 只要每個 patch 的改動幅度不要太大 git 都可以幫我們順利接好,但是偶爾還是會發生無法拼接的情況 > 兩次 Patch 改動很相似,導致 Conflict 版本衝突,無法合併 Code,這時候 git 會要求我們手動去解 Confilct ::: 2. 使用 **git rebase -i <after id\>** ```shell= # 先查詢目前 log 的狀況 git log --oneline # 使用 rebase -i <id>,編譯 <id> 之前的檔案 git rebase -i 320cb7e # 先查詢目前 log 的狀況 git log --oneline ``` 修改前 > ![](https://i.imgur.com/HL2gG5F.png) 進入文字編譯階段:可以看到排序方式是以逆序,越下面越新 > ![](https://i.imgur.com/FJU00iv.png) 修改後,Mark(隱藏) `7464849 Test1` 後,儲存(`:wq`)並退出 > ![](https://i.imgur.com/K4gH4uG.png) :::info * **使用 `git rebase --abort` 可以遺棄修改的內容** ::: ### 版本衝突 - cherry-pick/rebase * 版本衝突會發生在 **==修改相同的檔案==(modify),新增(add)並提交後(patch),又突然要移除某一個版本(cherry-pick)** | 指令 | 功能 | | -------- | -------- | | git cherry-pick --abort | 遺棄修改的內容 | | git cherry-pick --continue | 提交修正衝突後的內容 | | git rebase --skip | 跳過正在執行的 Rebase TODO 動作 | | git rebase --abort | 遺棄修改的內容 | | git rebase --continue | 提交修正衝突後的內容 | :::warning 衝突這滿麻煩的... 還是修改一段程式後就提交,縮小差距(修改一小段就提交),提高 git 的判斷正確性 > 但如果是多人協作同個專案的話,那衝突倒是滿正常的 ::: * **第一種 cherry-pick** 1. patch 了三次 **相同的檔案** ```shell= echo "1.Alien" >> num.txt # 依序寫資料 git add -u # 更新 git commit -m "add Alien in num.txt" # 提交 echo "2.Kyle" >> num.txt # 依序寫資料 git add -u # 更新 git commit -m "add Kyle in num.txt" # 提交 echo "3.Pan" >> num.txt # 依序寫資料 git add -u # 更新 git commit -m "add Pan in num.txt" # 提交 git log --oneline ``` > ![](https://i.imgur.com/QrZGRm6.png) 2. git reset --hard 到第一次提交的地方 > ![](https://i.imgur.com/7heAWkp.png) ```shell= # 移動到第一次提交的 patch git reset --hard 29d7d18 # 會發生衝突 git cherry-pick 4332364 # 查看 diff 差異 git diff # 查看 status 狀態 git status ``` 發生 both modified 代表兩個 patch 都有改動到該檔案(目前就是指 `num.txt` 檔案發生衝突) > ![](https://i.imgur.com/2yRaeae.png) 3. **解決衝突的檔案**:使用 vim 編輯 `num.txt` 檔案,自己解決衝突 ```shell= # 編譯衝突文件 vim num.txt ``` 在 vim 編譯中把 2.Kyle 移除,並移除其他的符號(`===`, `>>>`, `<<<`, ... 等等,其他非修改的字)並儲存檔案(`:x!` or `:wq`) | 符號 | 代表 | | -------- | -------- | | <<<<<<< HEAD | 在這個符號以下會(到 = 符號)顯示,**這裡是 ==HEAD 對該檔案做的修改==** | | ======= | 分隔線 | | >>>>>>> 4332364... <Patch 描述> | **==目前打算 cherry-pick 的 Patch,對這個檔案做的修改==** | 這種標記可能會出現在多個地方(多地方衝突) > ![](https://i.imgur.com/Mj7LwDG.png) 4. git diff 觀察修改後的檔案 ```shell= # 查看修改後 diff 差異 git diff ``` | 前 | 後 | 內容 | 修改 | | -------- | -------- | -------- | - | | | | 1.Alien(HEAD) | 無修改 | | -(與 HEAD 比較) | | 2.Kyle | 移除 | | | + | 3.Pan(與 HEAD 比較) | 新增 | > ![](https://i.imgur.com/82g8k1J.png) 5. 更新檔案 `git add -u`,並繼續提交 `git cherry-pick --continue` ```shell= git add -u git status # 提交後的檔案要查看必須要使用 git diff `--cached` or `--staged` git diff --cached # cherry-pick --continue 繼續提交,進入編譯狀態,儲存後離開 git cherry-pick --continue git log --oneline ``` > ![](https://i.imgur.com/QFS8ccS.png) `git cherry-pick --continue` 進入編譯,儲存離開 > ![](https://i.imgur.com/8yUZ0rr.png) 查看 log 紀錄,以下顯示成功移除指定 Patch 並加入指定 Patch > ![](https://i.imgur.com/2aNGUDs.png) :::success * **Cherry-pick 後,原本的 patch id 就會改變,再接上你需要的 branch** ::: * **第二種 rebase**:rebase 發生充同的處理方法與 cherry-pick 差不多 (所以加快速度...很累的) 1. 把 Kyle 新增回 `num.txt `檔案,更新並提交,**目的將順序由 `1 -> 3` 改回 `1 -> 2`** ```shell= echo "2.Kyle" >> num.txt cat num.txt git add -u git commit -m "Add Kyle in num.txt" git log --oneline ``` > ![](https://i.imgur.com/ldFqAn3.png) 2. `git rebase -i HEAD^^` 使用 vim 移動 Patch 順序 (交換兩個 Patch 順序,如下圖),當然也可以使用指定 id ```shell= # 發生衝突 git rebase -i HEAD^^ ``` 修改前後順序 > ![](https://i.imgur.com/2GLztta.png) 改變順序後,發生衝突 > ![](https://i.imgur.com/Qv8VXzi.png) 3. 處理衝突:使用 vim 編輯 `num.txt` 檔案,手動處理衝突 ```shell= vim num.txt git diff git add -u git diff --cached # 提交 git rebase --continue ``` 修改前後,並儲存 > ![](https://i.imgur.com/XLh4Es7.png) 新增後的差異 > ![](https://i.imgur.com/xXCRiqp.png) continue 提交 Patch 過後會接續處理,Pan Patch 的問題 > ![](https://i.imgur.com/t4foPlF.png) :::info * **使用 rebase 會產生暫時的隱藏檔案**, .<原檔名>.swp,這個例子就會產生 `.num.txt.swp`,在 rebase 結束後就會消失 ::: 4. 再次編譯 vim `num.txt` 的檔案 :::info * 為什麼要解兩次的衝突? **你把 `rebase` 想成多次 `cherry-pick`,由於是多次,所以可能產生多次的衝突**,所以你必須解決每次提交的衝突點 > 建議如果有多次 commit,在你要合併回新分支時最好使用 `merge` 而不是 `rebase`(你可能不會記得當初為什麼要改動) ::: ```shell= vim num.txt git diff git add -u git diff --cached # 提交 git rebase --continue # 查看 ``` vim 編譯順序的內容 > ![](https://i.imgur.com/5V1AKoL.png) 新增檔案後並提交 > ![](https://i.imgur.com/b5Haqkh.png) 查看 log,**交換成功 !!!** > ![](https://i.imgur.com/9e475PT.png) :::success * rebase 衝突需要一個一個解決 ::: ### 指定還原 - revert * 以往要修改其中一個 Patch 時,需要使用 `cherry-patch` 多個檔案 or `rebase` 編譯多個檔案;`revert` 指定還原的概念是說,**把定特的 Patch 內容做修改,再加入新串列** > 這個概念就像是發現多的字串,於是在後面串一個 Patch 來消除多餘的字(類似於 +100 -100 的概念) > ![](https://i.imgur.com/KhH4C2R.png) | 指令 | 功能 | | -------- | -------- | | git revert -i <id\> | 修改內容,修改後會串接上新的 Patch | | git revert --continue | **add 檔案後的操作** | | git revert --abort | 拋棄這次的 revert 動作 | > ![](https://i.imgur.com/2uejFE6.png) :::info revert 也如同 cherry-pick & rebase 一樣會有版本衝突的問題,必須手動編譯解決 ::: 1. 按照目前的 `gitk` 狀態圖,假設發現 `fix bug 1` 修復有問題,但是已經提交兩次 Patch (`fix bug3` & `fix bug 2` 已經提交) > ![](https://i.imgur.com/7ua2AEo.png) 2. 這時就可以使用 revert -i <指定 ID> ```shell= # revert 指定 id (也就是 fux bug 1) git revert db37e77 # 狀態 git status ``` 發生衝突檔案,這時就有手動解決 > ![](https://i.imgur.com/rH1tNHc.png) 3. 使用 vim 編輯器解決衝突檔案,最後在儲存起來 ```shell= # 編輯檔案 vim fixBug.txt ``` > ![](https://i.imgur.com/Bxwok7e.png) 4. `git add` 添加改變後的檔案 ```shell= git add fixBug.txt ``` 綠色的點點就是我們 add 之後的檔案(`Local changes checked in to index but not committed`) > ![](https://i.imgur.com/GRWP0l2.png) 5. `revert -- continue` 添加完檔案後就繼續 `revert` ```shell= # 繼續 revert git revert --continue git status ``` 編譯 commit 描述,之後就可以儲存離開 > ![](https://i.imgur.com/tuSjz3D.png) 6. 查看結果:以 `gitk` 查看 `revert` 後的結果 > ![](https://i.imgur.com/z4XVdSr.png) ```shell= # 狀態 git status # 查看文件 cat fixBug.txt ``` > ![reference link](https://i.imgur.com/CbGBSXt.png) ## `.git` 目錄 以下將會簡單介紹 `.git` 目錄,以及它們的功能作用 ### config 文件 * config 內部存有部分使用者設定的值 (git config 設定),也有 branch 的資料 ```typescript= [core] repositoryformatversion = 0 filemode = false bare = false logallrefupdates = true symlinks = false ignorecase = true [remote "origin"] url = git@github.com:XXX/TestGit.git fetch = +refs/heads/\*:refs/remotes/origin/\* [branch "master"] remote = origin merge = refs/heads/master [branch "test"] remote = origin merge = refs/heads/test ``` > ![](https://i.imgur.com/pUlVmDp.png) ### index 文件 * 存有開法者本地所有的 Patch 資訊 (16 進位儲存) > ![](https://i.imgur.com/3Wom2P4.png) ### refs/tags 目錄 * 存有所有我們所創建的標籤,若要指定刪除遠端標籤,就是指定該目錄 > ![](https://i.imgur.com/PFAh4Cg.png) ## Appendix & FAQ :::info ::: ###### tags: `Git`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully