# Git 教學 - [Git 教學](#git-教學) - [Git 版本控制概念](#git-版本控制概念) - [Staging Area](#staging-area) - [Commit](#commit) - [Repository](#repository) - [Remote repository](#remote-repository) - [流程](#流程) - [基本流程 (local)](#基本流程-local) - [分散式流程 (remote)](#分散式流程-remote) - [圖解](#圖解) - [.git](#git) - [config](#config) - [hooks](#hooks) - [.gitignore](#gitignore) - [.gitkeep](#gitkeep) - [常用指令](#常用指令) - [git init](#git-init) - [建立裸儲存庫](#建立裸儲存庫) - [git config](#git-config) - [對目前目錄的 git repo 進行設定](#對目前目錄的-git-repo-進行設定) - [對目前帳號下所有的 git repo 進行設定](#對目前帳號下所有的-git-repo-進行設定) - [對目前機器下所有帳號下的 git repo 進行設定](#對目前機器下所有帳號下的-git-repo-進行設定) - [設定區分大小寫](#設定區分大小寫) - [設定身份](#設定身份) - [特別指定 repo 內檔案身份](#特別指定-repo-內檔案身份) - [設定 commit message 編輯器](#設定-commit-message-編輯器) - [設定 alias](#設定-alias) - [將異動納入 Staging Area](#將異動納入-staging-area) - [查看目前版本控制的狀態](#查看目前版本控制的狀態) - [將檔案於版本控制記錄中搬移(重新命名)](#將檔案於版本控制記錄中搬移重新命名) - [將檔案於版本控制記錄中移除](#將檔案於版本控制記錄中移除) - [將檔案自 staging area 移除,但不移除檔案](#將檔案自-staging-area-移除但不移除檔案) - [建立版本](#建立版本) - [寫入單行的 commit message](#寫入單行的-commit-message) - [修改目前最後一個 commit message](#修改目前最後一個-commit-message) - [多重宇宙](#多重宇宙) - [列出宇宙](#列出宇宙) - [建立宇宙](#建立宇宙) - [重新命名目前的宇宙](#重新命名目前的宇宙) - [刪除宇宙](#刪除宇宙) - [強制刪除宇宙,丟棄任何差異](#強制刪除宇宙丟棄任何差異) - [切換宇宙及時間旅行](#切換宇宙及時間旅行) - [切換宇宙](#切換宇宙) - [時間旅行](#時間旅行) - [還原異動](#還原異動) - [依目前宇宙開立新宇宙](#依目前宇宙開立新宇宙) - [合併宇宙](#合併宇宙) - [以現在宇宙為主,將其它宇宙合到現在宇宙](#以現在宇宙為主將其它宇宙合到現在宇宙) - [解決合併多重宇宙衝突](#解決合併多重宇宙衝突) - [平準宇宙](#平準宇宙) - [調整 commit](#調整-commit) - [解決平準宇宙衝突](#解決平準宇宙衝突) - [時間基準變更](#時間基準變更) - [跳至指定 hash,不保留 Staging Area 異動](#跳至指定-hash不保留-staging-area-異動) - [跳到指定 hash 但保留 Staging Area 異動](#跳到指定-hash-但保留-staging-area-異動) - [跳到指定 hash 並丟棄所有異動(含 Staging Area)](#跳到指定-hash-並丟棄所有異動含-staging-area) - [回到最近一次的破壞性操作之前](#回到最近一次的破壞性操作之前) - [製作一個版本還原異動](#製作一個版本還原異動) - [時空暫存堆疊](#時空暫存堆疊) - [將已有進過版本控制的檔案異動暫存至堆疊](#將已有進過版本控制的檔案異動暫存至堆疊) - [將異動取回](#將異動取回) - [版本歷程](#版本歷程) - [query repo logs](#query-repo-logs) - [query log by file](#query-log-by-file) - [repo 異動記錄](#repo-異動記錄) - [複製遠端 repo](#複製遠端-repo) - [同步遠端 repo 資訊](#同步遠端-repo-資訊) - [同步遠端 repo 並合併](#同步遠端-repo-並合併) - [推送目前 repo 資訊至遠端 repo](#推送目前-repo-資訊至遠端-repo) - [針對版本給有意義的名稱](#針對版本給有意義的名稱) - [具破壞性的操作](#具破壞性的操作) - [git reset](#git-reset) - [git rebase](#git-rebase) - [情境](#情境) - [為什麼有時候無法切換宇宙,它老是說檔案已存在?](#為什麼有時候無法切換宇宙它老是說檔案已存在) - [開發到一半有緊急的調整切換宇宙怎麼辦?](#開發到一半有緊急的調整切換宇宙怎麼辦) - [外包廠商提供程式碼都沒有版本控制怎麼辦?](#外包廠商提供程式碼都沒有版本控制怎麼辦) - [Git 切換宇宙越來越慢了,有沒有辦法加速?](#git-切換宇宙越來越慢了有沒有辦法加速) - [reset / rebase / cherry-pick 搞得我好亂呀!](#reset--rebase--cherry-pick-搞得我好亂呀) - [明明沒改什麼,怎麼老是與其它開發者衝突?](#明明沒改什麼怎麼老是與其它開發者衝突) - [曾被版本控制的檔案已刪除,要怎麼查到其記錄?](#曾被版本控制的檔案已刪除要怎麼查到其記錄) - [reference](#reference) ## Git 版本控制概念 - 開發流程的問題都不是 Git 版本控制要解決的問題,例如:如何把程式上線,已合併的功能無法上線要怎麼辦 - git 只能對檔案進行記錄,無法只記錄目錄 - 會隨著不同環境、不同開發者而變動的檔案,不可進版本控制 - 帳號密碼不直接進版本控制 - commit message 要有意義,最好是單一目的 - 同一次的 commit 內不要混合多種調整,減少一次要查看的異動檔案 ### Staging Area 異動監控區 ### Commit 將異動監控區做成1個版本 ### Repository 將各種版本記錄下來的儲存區 ### Remote repository 遠端儲存區 ## 流程 ![git 流程](images/git.png) ### 基本流程 (local) 不需要有遠端異動儲存區,即可開始你自己的小圈圈版本控制,但你電腦壞了,記錄就會消失 - git init - git add files to staging area - git commit create a version ### 分散式流程 (remote) 為了不讓記錄消失,所需可以抄寫一份資料到 remote, 除了remote 外,只要有 clone 過 remote 的資料夾都可以作為還原點, ssh 的效能比 https 快非常多 - base 基本流程 (local) - git remote add origin [repo url] - git pull origin master - git push origin master push 過的記錄就不可以再改寫記錄,否則會影響其它分散的git repo ### 圖解 ![git 圖解](images/git-flow.png) - 版本(commit) - 每一個 commit 都有一個file snapshot hash,通常簡寫取前6碼即可代表 - 宇宙(branch) - 在指定的版本上,建立一個宇宙,當切換到這個宇宙上產生的版本都與其它宇宙無關,為了避免與版本簡寫衝突,不使用純數字開頭命名宇宙 - 合併宇宙(merge) - 重要里程(tag) - 簡寫6碼不容易被人記憶,所以對重要的版本做別名有利於人類識別 - 切換宇宙及時間旅行(checkout) - 時間基準變更(reset) ### .git 版本控制資訊存放的隱藏目錄 #### config `.git/config` 存放目前 repo 設定 #### hooks `.git/hooks` 存放各 git 生命週期觸發的 shell sample ### .gitignore 設定不進入版本控制的檔案,支援正規表示式,可在多個目錄進行設定,優先順序為目前目錄 > git repo 根目錄 [https://gitignore.io 可以快速產生此檔](https://gitignore.io) ### .gitkeep 為了讓目錄結構進版本控制用,內容為空白,有些目錄會以此檔放置於該目錄下或是產生 .gitignore 後,將目錄進版本控制 ## 常用指令 ### git init 宣告版本控制初始化 #### 建立裸儲存庫 建立裸儲存庫,只存放 git 資訊,不做開發用 ```bash git init --bare ``` ### git config 設定優先順序為 local > global > system #### 對目前目錄的 git repo 進行設定 For writing options: write to the repository .git/config file. This is the default behavior. For reading options: read only from the repository .git/config rather than from all available files. ```bash git config --local --list ``` #### 對目前帳號下所有的 git repo 進行設定 For writing options: write to global ~/.gitconfig file rather than the repository .git/config, write to $XDG_CONFIG_HOME/git/config file if this file exists and the ~/.gitconfig file doesn’t. For reading options: read only from global ~/.gitconfig and from $XDG_CONFIG_HOME/git/config rather than from all available files. ```bash git config --global --list ``` #### 對目前機器下所有帳號下的 git repo 進行設定 For writing options: write to system-wide $(prefix)/etc/gitconfig rather than the repository .git/config. For reading options: read only from system-wide $(prefix)/etc/gitconfig rather than from all available files. ```bash git config --system --list ``` #### 設定區分大小寫 ```bash git config --local core.ignorecase false ``` #### 設定身份 ```bash git config --global user.name "user" git config --global user.email "user@test.com" ``` #### 特別指定 repo 內檔案身份 ```bash git config --local user.name "user" git config --local user.email "user@test.com" ``` #### 設定 commit message 編輯器 ```bash git config --global core.editor vim ``` #### 設定 alias ```bash git config --global alias.co checkout git config --global alias.br branch git config --global alias.st status ``` ### 將異動納入 Staging Area `git add` 支援正規式,請不要使用 `git add .` 或 `git add --all` 將所有異動檔案進入 Staging Area,很常加到不該加的檔案, add 只會把當下檔案的快照加入 staging area,後續有再異動存檔,需要再次 add ```bash git add sample.txt ``` ### 查看目前版本控制的狀態 查看目前版本控制、staging area 、merge、rebase 的狀態 ```bash git status ``` ### 將檔案於版本控制記錄中搬移(重新命名) ```bash git mv nuLog.config nulog.config ``` ### 將檔案於版本控制記錄中移除 將檔案自 staging area 移除,且從版本控制以及實體檔案系統中移除檔案 ```bash git rm Thumbs.db ``` #### 將檔案自 staging area 移除,但不移除檔案 Use this option to unstage and remove paths only from the index. Working tree files, whether modified or not, will be left alone. ```bash git rm --cached .env ``` ### 建立版本 將 staging area 的資料建立版本,預設會跳出 commit message 編輯器 vim/nano 等,未加入 staging area 的異動不受影響, commit message 保持簡單,清楚並有意義 ```bash git commit ``` #### 寫入單行的 commit message ```bash git commit -m 'this is a commit' ``` #### 修改目前最後一個 commit message 已 push 過的 message 不可修改 ```bash git commit --amend ``` ### 多重宇宙 建立宇宙,存放平行時空的記錄 #### 列出宇宙 ```bash git branch ``` #### 建立宇宙 ```bash git branch branch_a ``` #### 重新命名目前的宇宙 ```bash git branch -m branch_b ``` #### 刪除宇宙 ```bash git branch -d branch_b ``` #### 強制刪除宇宙,丟棄任何差異 ```bash git branch -D branch_b ``` ### 切換宇宙及時間旅行 #### 切換宇宙 ```bash git checkout branch_a ``` #### 時間旅行 ```bash git checkout abcdef ``` #### 還原異動 從版本控制庫取回最新版本的檔案,將會取消異動 ```bash git checkout file.txt ``` #### 依目前宇宙開立新宇宙 ```bash git checkout -b branch_a ``` ### 合併宇宙 #### 以現在宇宙為主,將其它宇宙合到現在宇宙 ```bash git merge branch_b ``` #### 解決合併多重宇宙衝突 請找相關人員確定後再進行檔案調整後,add/commit進行解衝突 ### 平準宇宙 ```bash git rebase master ``` #### 調整 commit 改寫 commit,請不要對己 push 過的 commit 進行 rebase ```bash git rebase -i abcdef ``` #### 解決平準宇宙衝突 請找相關人員確定後再進行檔案調整後,add/commit/apply/continue 進行解衝突 ### 時間基準變更 版本間間移動,預設是使用 `--mixed` #### 跳至指定 hash,不保留 Staging Area 異動 ```bash git reset HEAD --mixed ``` #### 跳到指定 hash 但保留 Staging Area 異動 ```bash git reset HEAD --soft ``` #### 跳到指定 hash 並丟棄所有異動(含 Staging Area) ```bash git reset HEAD --hard ``` #### 回到最近一次的破壞性操作之前 ```bash git reset ORIG_HEAD --hard ``` ### 製作一個版本還原異動 ```bash git revert HEAD~1 ``` ### 時空暫存堆疊 #### 將已有進過版本控制的檔案異動暫存至堆疊 ```bash git stash ``` #### 將異動取回 ```bash git stash pop ``` ### 版本歷程 #### query repo logs ```bash git log ``` #### query log by file ``` git log file_a.txt ``` ### repo 異動記錄 30天內的異動記錄,可以配合 reset/rebase/checkout 做操作 ```bash git reflog ``` ### 複製遠端 repo ssh 的效能比 https 快非常多,請記得產好 private key 後將 public key 設定在 remote server 上 ```bash git clone git@test.com/gest.git ``` ### 同步遠端 repo 資訊 不影響 Woking Directory 以及 Staging Area ```bash git fetch ``` ### 同步遠端 repo 並合併 等於 `git fetch` and `git merge` ```bash git pull origin master ``` ### 推送目前 repo 資訊至遠端 repo ```bash git push origin master ``` ### 針對版本給有意義的名稱 ```bash git tag v0.1.1 abcdef -a -m 'this is version 0.1.1' ``` ### ## 具破壞性的操作 ### git reset 跳至指定 commit 後,改寫歷史 ### git rebase 改寫歷史 ## 情境 ### 為什麼有時候無法切換宇宙,它老是說檔案已存在? 通常會是系統實際檔名大小寫與版本控制內的檔名大小寫不一致所造成, 若以發生,可以用 git mv 做操作成正確的檔名,再切換宇宙 ### 開發到一半有緊急的調整切換宇宙怎麼辦? - [建議]先在目前宇宙留下版本,再進行宇宙切換,只要不把版本推出,就可以在切換回原宇宙時進行版本歷程修改 - [不建議]使用時空暫存堆疊堆放,不過經驗談是後進先出,堆一堆放一陣子你就對不回正確的宇宙而只能把時空暫存堆疊清除了 ### 外包廠商提供程式碼都沒有版本控制怎麼辦? 把它解壓縮後 git init 後設定commit,之後每次只要保留 .git 資料夾,把 zip 解壓縮的檔案蓋回來就可以確認差異 ### Git 切換宇宙越來越慢了,有沒有辦法加速? - 有可能是非文字類型的檔案太多了,需要配合 git lfs 進行管理 - 放棄修改歷程,刪掉 .git folder 重新開始版本控制 - 一段固定時間對目前的版本控制記錄封存,重新開始 ### reset / rebase / cherry-pick 搞得我好亂呀! - reset - 用途 - 改變時間基準 - 限制 - 人為損壞不保固 - rebase - 用途 - 調整目前commit log(合併、刪除、改寫等) - MR/PR 至 master 之前的選項 - 限制 - 在還沒有 push 過的歷程才可以進行 - 被改寫過歷程的宇宙已合併為 master 就應刪除,不可再使用 - cherry-pick - 用途 - 明確的知道 commit 內容都是自己所需 - cherry-pick 的來源宇宙無法直接合併至目前宇宙 - 限制 - commit 內容夠乾淨完整 ### 明明沒改什麼,怎麼老是與其它開發者衝突? - 留意自己是否有誤用 reset / rebase 操作到已推送出去的版本歷史 ### 曾被版本控制的檔案已刪除,要怎麼查到其記錄? ```bash git log --full-history -- file_path/file_name ``` ## reference - [Git 版本控制使用分享簡報](https://docs.google.com/presentation/d/181dZwi05PUebQQ2dUQElzzHM-1l-asKU4k9fSJw8dHQ/edit#slide=id.p) - [連猴子都能懂的git入門指南](https://backlog.com/git-tutorial/tw/contents/) - [gitignore.io](https://gitignore.io) - [Learn Git Branching](https://learngitbranching.js.org/index.html) - [30 天精通 Git 版本控管 (03):建立儲存庫](https://ithelp.ithome.com.tw/articles/10132804) - [Sourcetree](https://www.sourcetreeapp.com)