Git
Study-Notes ·本篇讀書筆記參考以下 learning material 所整理,強烈推薦使用該 material 把所有指令都玩過,
讓學習 git 不枯燥且快速理解各個 command.
若已對 git 有一定程度的認識或熟悉,可跳到 Summary 複習常用指令及比較!
🔗 Create new repo in terminal
- 檢查 git 版本以確認 git 已安裝:
$ git --version
- 建立資料夾:
$ mkdir git_test
- 初始化 git repo:
$ cd git_test
、$ git init
-
創一個文件並提交到 master 分支
echo "# My Project" > README.md git add README.md git commit -m "Initial commit"
- 創一個新的分支:Branch
🔗 Commit and push
$ git add .
$ git commit -m “message”
$ git push GH(local) origin/master(remote)
🔗 Branch
- 新增分支:
$ git branch <branchName>
- 查看目前在哪個個分支:
$ git branch -v
- 分支改名:
$ git branch -v <branchName> <new-branchName>
- 刪除分支:
$ git branch -d <branchName>
若還有未合併的內容但硬要刪除,只要改用-D
參數就可以強制刪除 - 切換到新分支:
$ git checkout <branchName>
- 新增分支並切換:
$ branch checkout -b newBranch
- 刪除分支:
$ branch checkout -v <branchName>
- 新增分支並切換:
-
$ git branch -u o/main foo
foo
branch 被設定成 tracko/main
,如果你現在已經 checkout 到 foo 這個 branch 上面了,你就可以省略掉它:$ git branch -u o/main
💡 [補充說明] master 是什麼?
其實不用想的太複雜,他就只是個名字,
要是想幫他改名也是沒有問題,只不過實務上不會有人這樣做。
然後再說明一下,在實務上 master 這條分支上通常是穩定的版本,因此基本上不會直接對 master 做修改,
假設有緊急修改的狀況,通常會用 hotfix 這個方式來解決。
🔗 Merge
1. 合併之前,先看清楚自己在哪個分支
2. 合併之前,請確保工作目錄是乾淨的
3. 合併時請用 git merge [另一個分支] 來將另一個分支的變更合併回來
4. 合併成功後,你可以利用 git log 查看版本紀錄,你可以發現「合併」的過程會自動建立一個新版本!
情境:將 bugFix
這個 branch 合併到 master
1. 切換到 master 這個分支:`$ git checkout master/main`
2. 合併 bugFix:`$ git merge bugFix`
$ git merge
並不會去移動你的 commit(反而會產生一個 merge commit)
🔗 glog
查詢過去的 commit
🔗 Delete the latest commit $ git reset HEAD^1
只刪除 local,不會影響 remote
🔗 rebase
rebasing 是 merge branch 的第二種方法。rebasing 就是取出一連串的 commit,”複製“它們,然後把它們接在別的地方。
雖然聽起來難以理解,rebasing 的優點是可以建立更線性的 commit history。假如只允許使用 rebasing 的話,則我們的 repo 中的 commit log 或者是 commit history 會更加簡潔好看。
現在 bugFix branch 上的工作在 main branch 的最前端,同時我們也得到了一個更加線性的 commit 順序。 ⚠️ 本來的 commit C3 沒有消失(在圖上面呈現陰影),而我們”複製” C3,將它的副本 C3’ 接在 main branch 的後面。
🔗 commit tree
-
HEAD 是一個 reference,它是指向目前所 checkout 的 commit,基本上,其實就是你目前所在的 commit。
在 commit tree 中,HEAD 總是指向最近的一次commit。大部份 git 的指令如果要修改 commit tree 的狀態的話,都會先改變 HEAD 所指向的 commit。
HEAD 通常指向一個 branch 的名稱(比如 bugFix)。當你 commit 的時候,改變了 bugFix 的狀態,這一個變化可以從 HEAD 的改變中看到。
$ git checkout C1
-
相對引用
- 使用
^
向上移動一個 commit - 使用
~<num>
向上移動多個 commit -
移動分支:你可以使用
-f
選項直接讓分支指向另一個 commit。舉個例子:$ git branch -f main HEAD~3
$ git branch -f main C6
- 使用
🔗 git reset / git revert 取消 git 修改
在 git 裡面取消修改同時具有底層的部份(暫存一些獨立的文件或者片段)和高層的部份(修改是如何被取消)。我們主要講的重點是後者。
-
$ git reset
把分支的參考點退回到上一個 commit 來取消修改。你可以認為這是在”重寫歷史”。
往回移動 branch,原來的 branch 所指向的 commit 好像從來沒有存在過一樣。
「改寫歷史」的方法對別人的 remote branch 是無效的
→ 為了取消修改並且把這個狀態分享給別人,我們需要使用git revert
-
$ git revert HEAD
🔗 cherry-pick
$ git cherry-pick <Commit1> <Commit2> <...>
:當你想要複製幾個 commit 並且接在你目前的位置(HEAD
)下面的時候,這會是一個非常直接的方式。
只撿取想要的東西 (在 git flow 中就是 commit) 放到目前的分支上
開發通常都有一條穩定的版本 (master or release)
將其他分支 (dev) 做修改後、合併
有時需有臨時功能要新增 又不想把整個分支 (dev) 合併進 master 時就可使用 cherry-pick
→ 只挑自己想要的 commit 合併到 master
🔗 git interactive rebase
如果你不知道你要的是哪些 commit 時使用互動式 rebase
git rebase -i HEAD~3
#這個命令會打開一個編輯器,並顯示最近的 3 個 commit
如果已經將這些 commit 推送到遠端,就強制推送來更新遠端的 branch:
git push -f origin <branch-name>
🔗 git tag
永遠有一個指向 commit 的記號,例如表示重大的軟體釋出,或者是修正很大的 bug,有沒有其它比 branch 更好的方法?→ $ git tag <tag name> <commit>
當有新的 commit 時,它們也不會移動,你不可以 “checkout” 到 tag 上面 commit,tag 的存在就像是一個在 commit tree 上的表示特定訊息的一個錨。
🔗 git discribe
因為 tag 在 commit tree 上表示的是一個錨點,git 有一個指令可以用來顯示離你最近的錨點(也就是 tag),而且這個指令叫做 $ git describe
當你已經完成了一個 $ git bisect
(一個找尋有 bug 的 commit 的指令),或者是當你使用的是你跑去度假的同事的電腦時, $ git describe
可以幫助你了解你離最近的 tag 差了多少個 commit。
$ git describe <ref>
<ref>
是任何一個可以被 git 解讀成 commit 的位置,如果你沒有指定的話,git 會以你目前所在的位置為準(HEAD
)。
🔗 git fetch 從 remote 下載資料 (⚠️ 不更新!)
- 下載 remote 有的 commit,但是在我們的 local repository 是沒有該 commit。
- 更新我們 remote branch 所指向的地方(例如,
origin/main
) - 基本上,
git fetch
同步了我們的 local repository 以及 remote repository 的最新狀態。 - 然而,
git fetch
並不會影響到在你的 local repository 中的main
branch,他並不會將你的main
branch 更新到最新的狀態。 - 它是會下載同步所需的資料,但是不會更新任何的檔案。
🔗 git pull (將 fetch 下來的檔案更新到 local)
一次下載 (fetch) remote 的更新並且合併(merge) 這些更新在 git 裡面是很常見的事情!這個命令叫作 git pull
。
🔗 git stash
情境:遠端 master 有更動,要將最新版 master 合併到 lcoal 端,但又不想 local 所做的 new changes 不見
$ git stash save
暫存在 local 端更動的檔案
$ git stash list
$ git pull --rebase origin master
從遠端拉最新的 master 到 local: git pull —rebase
$ git stash pop
預設 pop 最上面的
$ git stash pop stash@{2}
指定要 pop 哪一個
ref: git 暫存指令
🔗 git pull –rebase
當 git 歷史紀錄被 diverge 了,所以 git 不會允許你 push
你的 commit。在你上傳你的 commit 之前,它實際上會先強迫你先跟 remote 同步。
push failed
只需要把 C3
接在 remote 最新的版本 C2
的後面就可以了。
$ git pull —rebase ; git push
🔗 git push origin
$ git push origin <source>:<destination>
🔗 git fetch origin
$ git fetch origin <place>
$ git fetch origin foo
:git 會到 remote 上的 foo
branch,抓下所有不在 local 上的 commit,然後將它們放到 local 的 o/foo
branch.
🔗 git fetch origin
$ git fetch origin <source>:<destination>
如果你很想要把 fetch 回來的 commit 直接放到 local branch,那麼你就可以利用一個 colon refspec 來做到。
你不能夠把 fetch 回來的 commit 放到你目前正 checkout 的 branch,如果不是的話,git 就會允許你這麼做。
這裡只有一個重點,<source>
現在是一個在 remote 上的 branch,而且 <destination>
是一個放置這些 commit 的 local 的位置。它剛好就是 $ git push
的相反,而且因為我們在相反方向傳遞資料,所以這也很合理!
在兩個奇怪的情況下,git 不使用 <source>
參數,事實上,在git push
以及git fetch
的情況下,可以允許你”不用”指定 source
,你可以藉由把參數留空,來表示你不想指定 source:
$ git push origin :side
$ git fetch origin :bugFix
→ push
藉由把 source “留空”,成功用 push
刪除了 foo
branch
→ 對於 fetch
來說,source “留空” 表示我們要在 local 上建立一個新的 branch。
🔗 git pull origin
$ git pull origin <source>:<destination>
$ git pull origin foo
相當於:$ git fetch origin foo; git merge o/foo
$ git pull origin bar:bugFix
相當於:$ git fetch origin bar:bugFix; git merge bugFix
Summary
💡
$ git rebase
不會形成新的 commit,讓 commit tree 簡潔乾淨。
💡
$ git merge
會形成新的 commit,保留所有歷史。
💡
$ git fetch
從 remote 下載 local 沒有的檔案,但不更新 local 原有的檔案。
💡
$ git pull
從 remote 下載 local 沒有的檔案,同時更新 local 原有的檔案。
💡
$ git stash
用於暫存當前的更改,讓你可以清空工作區,切換到其他分支或進行其他操作,之後再恢復這些更改。
💡
$ git rebase
& $ git cherry-pick
可達成相同效果,看個人習慣。
但 rebase
是為了重新整理提交歷史,通常用來保持 commit tree 的整潔,
而 cherry-pick
是用來精確選擇和複製特定的 commit 到另一個 branch。