這篇主要是 Git 的筆記,也是第一週的作業,但其實自己現在用到 Git 的功能很少,所以也當成之後的參考資料
Git 版本控制 原理 & 基本指令
版本控制的原理
版本控制,就是把檔案所有歷史紀錄的版本都保存下來,方便之後比對、回顧。以前在做報告的時候,不知道大家有沒有類似的經驗,把新修改的檔案另存成一個個新檔案然後在檔名上加上各種…
- 期末報告.doc
- 期末報告_2.doc
- 期末報告_最終版.doc
- 期末報告_確定版.doc
- 期末報告_最終確定版.doc
- 最後變得自己和組員都搞不清楚哪個是哪個… 於是我們有了 Git 這套工具來更有系統的管理檔案的版本歷程
用簡易版控來模擬 Git
為了讓大家在之後能更了解 Git 在做什麼,這裡就用熟悉的資料夾、檔案、和複製貼上功能做一個和 Git 類似的簡易版控。首先我們有一個協作的專案資料夾
專案資料夾
- index.html
- main.css
- index.js
一開始只有你一個人在自己的電腦做,昨天稍微修改了 main.css 和 index.js,為了方便管理,你複製出一份新的,命名為專案資料夾_2
。
我們把剛才那個 專案資料夾_2
放在雲端空間上,開始和 A、B 同事一起協作吧:
- A 同事把檔案抓下來,修改了一點 index.html,於是在自己的電腦裡面複製出一份新的叫做
專案資料夾_3
的檔案 - B 同事也把檔案抓下來修改了一點 index.js,也在自己的電腦裡面複製了一個叫
專案資料夾_3
的檔案 - 要在上傳同步回去的時候就發生都叫
專案資料夾_3
但修改內容不一樣的衝突情形了 - 於是我們決定給每個人的版本一個亂碼,已識別不同的版本:
- A 同事的
專案資料夾_3
變成了 fdfjkdslfhds - B 同事的
專案資料夾_3
變成了 jkojidesahal
- A 同事的
- 但是要如何識別這兩個亂碼的資料夾是誰的版本,是什麼時候的版本呢?於是我們做一個
版本紀錄.txt
以記錄不同的版本資訊:- 06/14 14:30, A, fdfjkdslfhds
- 06/14 12:50, B, jkojidesahal
- 在新增一個文件
最新版本.txt
用來記錄最新版本- latest: jkojidesahal
- 我們可以把不需要進行版本控制的檔案移出資料夾,例如剛才紀錄版本資訊的 txt 檔,完成之後我們的版本控制目錄就會像這樣啦:
最新版本.txt
版本紀錄.txt
fdfjkdslfhd
- index.html
- main.css
- index.js
jkojidesahal
- index.html
- main.css
- index.js
其實 Git 在做的事和上面的流程非常相似,等到我們實際操作過一次,就會覺得原來 Git 是一套很直觀易用的工具惹!
基礎指令
- 版本控制初始化
git init
- 為此資料夾建立 git 版本控制初始化,該資料夾內會多一個 .git 的隱藏資料夾
- 查看版本控制狀態
git status
- 可以查看有沒有檔案有做修改了但還沒 commit
- 也可以查到現在在哪一個 branch
- 按 q 可以跳出
加入檔案至版本控制
git add 要加入的檔案
git add .
//把此資料夾內全部檔案到加入版本控制把檔案移出版本控制
git rm --cached 要移出的檔案
讓 Git 幫忙刪掉檔案並執行
add
git rm welcome.html
讓 Git 幫忙改檔名並執行
add
git mv hello.html world.html
新建一個版本
git commit -m "要為該版本建立的版本訊息"
git commit -am "要為該版本建立的版本訊息"
// 可以同時完成 add 和 commit,但是新加入此資料夾的檔案不適用哦檢查
git log
// 可以看到每一個版本的 message、該版本號碼(對,長得像一串亂碼的那個)和建立日期
git log —-oneline
// 顯示版本的列表
git log --oneline --grep="WTF
:使⽤ --grep 參數,可以從 Commit 訊息裡⾯搜尋符合字樣的內容
git log --oneline --author="Sherly\|Eddie"
:可以搜尋作者(|
是或的功能)
git log -S "word"
:找到檔案內含有的字
git log --oneline --since="9am" --until="12am"
:找到時間區間內的 commit 紀錄
git log <file_name>
:看這個檔案的 Commit 紀錄
git log -p <file_name>
:看這個檔案到底每次的 Commit 做了什麼修改看到是誰修改的
git blame <file_name>
看看這個檔案的修改紀錄
git blame -L 5, 10 <file_name>
看看這個檔案指定行數的修改紀錄穿越過去與未來
git checkout 那串版本號
// 可以到過去的版本
git checkout master
// 回到主 branch我在 .gitignore,請忽略我
在版本控制的資料夾內,可以指定某些檔案不一起做版本控制。- 步驟:
touch .gitignore
// 建立一個 .gitignore 檔案vim .gitignore
// 用 vim 開啟 .gitignore,在裡面把要忽略的檔名打進去
這樣做的好處是,就算使用 git add .,加進 .gitignore 的檔案也不會被加入版本控制內,往後不需要每次都要手動移出了。
- 步驟:
基礎 Git 流程
- 在 CLI 中切換到要做版本控制的資料夾的位置
cd D:\文件\test-git-demo
- 讓 Git 初始化
git init
- 建立 .gitignore,決定那些檔案要忽略不做版控
touch .gitignore
// 建立 .gitignore
vim .gitignore
// 打開 .gitignore,把不要版控的檔案名稱打進去 - 把檔案加入版本控制 COMMIT
git add 檔案名稱
也可以用git add .
一次加入全部 - 把所有修改生成一個版本 ADD
git commit -m "message"
git commit -am "message"
// 可以同時完成步驟 4 和 5 - 在各版本中穿梭
git checkout 版本號碼
// 可以切換各個版本
git checkout master
// 回到主 branch
多人協作就用 Branch
- 我們可以把整個專案稱為 repository,簡稱 repo,這裡面一開始就存在的 branch 叫做 master,也就是主要的 branch
- 多人同時開發時,我們可以基於 master 再複製建立出不同的 branch 出來個別進行開發
- 開發完再合併進去 master,如此就可以在不影響 master 的情況下同時進行不同支線的開發,譬如開一條 branch 是用來修復 bug,另一條 branch 是在做增加新功能,各自開發完之後在合併融入 master
HEAD 到底是誰?
其實也是一組 SHA-1 值,用來指向當前最新的 commit
Branch 指令
- 建立名為 new-branch 的新 branch
git branch new-branch
- 查查看現在有哪些 branch
git branch -v
- 刪除 branch
git branch -d 要刪除的 branch
- 刪除的 branch 如果還沒合併
git branch -D 要刪除的 branch
- 切換到另一個 branch
git checkout 要切到哪一個 branch
- 新建並切換到一個
branch git checkout -b sister
- 把 branch 給合併進來
git merge 要合併進來的 branch
確認自己在哪一個 branch,git merge 這裡要填入的是你要把哪一個 branch 合併進來 merge
時不要使用快轉模式合併(保留分支紀錄):git merge <要合併進來的 branch> —no-ff
Rebase 指令
- 在 A 分支時執行
git rebase B
:我是 A 分支,我現在要重新定義我的參考基準,並且
將使⽤ B 分支當做我新的參考基準
- 取消 rebase,回到 rebase 之前:
git reset ORGIN_HEAD --hard
Conflict 怎麼解決
兩個 branch 內的同一個檔案都有被更改,因為電知道要以哪一個為準,要 merge 時就會發生衝突
文字檔
- 兩個 branch 內的同一個檔案都有被更改,因為電腦不知道要以哪一個為準,要 merge 的時候就會發生衝突
- 發生衝突時,手動把該檔案打開,可以看到它把有衝突的地方標記起來,只要手動修改出,確認需要的內容,並存檔,再重新 merge 一次即可順利解決 conflict
非文字檔
- 決定要以誰的為準:
- 以我的為準:
git checkout —ours <file_name>
- 以我的為準:
git checkout —theirs <file_name>
- 以我的為準:
互動模式
使用 rebase -i <SHA-1>
:這裡要用的 SHA-1 code 是指從現在到該次的 commit 這段期間,會進入互動模式
pick
保留這次的 commit 不做修改reword
修改當次 commit 的 messagesquash
和前一次的 commit 合併 // 用來把多個零散的 commit 整理成一個 commit- 把一個 commit 拆成多個
edit // 指令用在要分的 commit git reset HEAD~1 // 拆除該 commit git add A git commit -m "111" git add B git commit -m "222" //即可拆成 111 和 222 兩個 commit,各含有 A 和 B 的修改內容
- 改 commit 順序:直接搬動位置
drop
刪除 commit
## Reset, Rebase, Revert 的差別
Tag 標籤
通常在開發軟體有完成特定的⾥程碑,例如軟體版號 1.0.0 或是 beta-release 之類的,這時就很適合使⽤標籤做標記。
lightweight tage
git tag <tag_name> <SHA-1>
輕量標籤則是用來個⼈使用或是暫時標記用途
annotated tag
git tag <tag_name> <SHA-1> -a -m "This is a tag"
有附註標籤主要用來做像是軟體版號之類的用途
刪除標籤
git tag -d <tag_name>
分支和標籤的區別
分支會隨著 commit 而移動,但標籤不會
Cherry-pick
可以用來複製某次或某些 commit 來用
git cherry-pick <SHA-1>
該次的 commit 就會被接到現在的 commit 之上了
git cherry-pick <SHA-1> <SHA-1> <SHA-1>
也可以一次撿好幾個來用
git cherry-pick <SHA-1> --no-commit
撿過來但先不要合併, 該 commit 會被放到暫存區(也就是 add 完的狀態)等待 commit
把檔案真正從 Git 裡面刪掉
以刪掉 database.yml 為例:
git filter-branch -f --tree-filter "rm -f config/database.yml"
把所有 commit 裡的 abc.yml 刪掉,強制覆寫 filter-branch 的備份點。rm .git/refs/original/refs/heads/master
斷掉這個剛才做 filter-branch 的備份點git reflog expire --all --expire=now
讓 reflog 立刻過期git fsck --unreachable
找到 unreachable 的物件git gc --prune=now
啟動資源回收機制- 如果內容已經 push,記得用
git push -f
把線上的紀錄蓋掉
Dangling 跟 Unreachable 物件有什麼不同?
- Unreachable 物件:沒有任何物件或指標指著它,所以如同字⾯說的「無法到達」。雖然
它「無法到達」,但它可以指著其它物件。 - Dangling 物件:跟 Unreachable 物件⼀樣,沒有任何物件或指標指著它,就完全是懸在天
邊的⼀顆物件。
GitHub
可以理解為放 repo 的雲端
push
- 把本地的 repo 同步推上 GitHub
git remote add origin https://github.com/Nicolakacha/test.git
//新增一個節點git push -u origin master
把本地的 master 分支推上去後,在 Server 上更新 master 分支的進度,或是如果不
存在該分支的話,就建立⼀個 master 分支
// -u 是 upstream 的意思:每個分支可以設定一個「上游」,下次執行git push
指令而不加任何參數時,它就會用來當預設值git push origin master: cat
這樣當把本地端的 master 分支推上去之後,就不會在線上建立 master 分支,而是建立(或
更新進度)⼀個叫做 cat 的分支
fetch
Git 看了⼀下線上版本的內容後,把你目前線上有但你這邊沒有的內容抓了⼀份下來
git merge origin/master
抓下來以後,需要把本地的 branch 和抓下來的 branch merge起來
pull
- 其實就等於替代了上面 fetch 加上 merge
- 把 GitHub 上的 repo 同步拉下到本地
git pull origin master
clone
- 把別人的 repo 下載一份到本地
- 不能修改,也不能 push 回去別人 GitHub 上的 repo
git clone <SSH address>
fork
- 把別人的 repo 複製一份叉到自己的 GitHub 上
多人協作的 Git & GitHub 流程
- 在 GitHub 上 fork 別人的 repo
- pull到 local
- 開新 branch
- 開始在這個新 branch 裡面開發 <- 做上面的基礎 Git 流程
- 開發完之後 push 自己的 GitHub
- 發起 pull request 請求把這個 branch merge 到原本的 repo 裡
- 在上面討論,等待對方確認是否可以 merge
- merge 成功,刪掉 branch
GitHub Page
GitHub Page 是 GitHub 上免費提供可以做靜態網頁展示的的功能: GitHub Page
GitHub 官方建議的工作流程參考 GitHub Flow
狀況實例
打錯字了,想改最後一次的 commit message
git commit --amend -m "message"
// 雖然這只是改訊息,不管如何它就是修改了⼀次的歷史,所以請儘量不要在已經 Push
出去之後再修改,否則可能會造成其它⼈的困擾
Commit 之後,想要再把某一個檔案加到這次的 commit
git add <file>
// 一樣先加入檔案
git commit --amend --no-edit
// 把檔案併入最後一次的 commit
我 commit 了但後悔了
git reset HEAD^ --hard
// 完全拆掉該 commit
git reset HEAD^ --soft
// 剛才那個修改的檔案還是在,只是在沒有 commit 的狀態
git reset HEAD~1
// 相對路徑:拆掉HEAD,回到上一個 commit
git reset 6d873ad~1
// 絕對路徑:回到 6d873ad 的上一個 commit
git revert HEAD --no-edit
再做⼀個新的 Commit,來取消你不要的 Commit
還沒 commit 但後悔了想回到原來的狀態
git checkout -- <file>
// 拋棄剛才的改變
git checkout .
//拋棄剛才所有檔案的改變
用 reset 把 commit 拆掉之後,又想救回來
- 使用
git reflog
或是git log -g
看到紀錄 - 使用
git reset cd3d692 --hard
把拆掉的 commit 再組回來
只 add 部分內容
git add -p
會詢問是部是要加整個區塊進去,如果選 y
,整個檔案會被加進去
如果選 e
就會進入編輯器,可以把不要 add 的部分刪掉
改 branch 的名稱
git branch -m <new_branch_name>
想把本地端沒有的遠端 branch 抓下來
git checkout <github_branch_name>
- 但如果遠端的 branch 如果不是從 local 端發出,或在 local 端之外有新的 commit,就沒辦法直接下載完整的 branch,要先
git fetch
抓到遠端 branch 的新狀況,然後把新狀況merge
進我們 local 端的 branch,或更直接的開一個同名的 branch 並pull
下來之後,才是完整的 branch。 - 參考資料
不小心把 branch 刪掉,想要救回來
- 使用
reflog
找到該 branch 物件最後指向的 commit 的 SHA-1值 - 使用
git branch <branch_name> <剛才找到的SHA-1>
把一個新的 branch 標籤貼在這個 commit 上 git checkout <branch_name>
切換過去看看,這樣就救回來啦
想要一口氣清除忽略的檔案
git clean -fX
檔案被刪掉想要救回來
git checkout <file_name>
不小心把重要檔案放在 Git 裡怎麼刪掉?
使用 filter-branch
:一次大量修改 commit
git filter-branch --tree-filter "rm -f <file_name>"
一次全部移除所有 commit 裡面的這個檔案
唔...又後悔了,想要變回來怎麼辦?使用指令:git reset refs/original/refs/heads/master --hard
push 不上去怎麼辦?
可能因為本地電腦裡的內容較舊,需要先拉回一份線上版本再推:
git pull --rebase
// 加上 rebase 可以不產生為了 合併分支而產生的 commitgit push
重新再 push 一次
Git Hook
預先設計好腳本,在某件事發生的時候觸發我們寫好的腳本
像是可以在 commit 之前檢查程式碼有沒有符合規範
怎麼寫作業和交作業?
- 把課綱複製一份到自己的 GitHub 上
- 把自己 GitHub 上的課綱 clone 到本機
- 新開一個 branch,在新的 branch 上寫作業
git branch week1
- 把修改的內容加入倒版本控制裡面並 commit 新建一個版本
git commit -am "版本名稱"
- 把在本機的 branch 推回遠端
git push origin week1
- 在 GitHub 上 create pull request,至此完成作業
- 到學習系統的作業列表按新增作業,貼上 PR 連結
- 如果 pull request 被 merged,就代表作業被改完了
- 把遠端的 master 和本機的 master 同步
git pull origin master
- 把本地原來新增出來用來寫作業的 branch 刪除
git branch -d week1
怎麼同步老師的 repo?
- git pull <web URL.> master
- git push origin master