Study Notes

Git

本篇讀書筆記參考以下 learning material 所整理,強烈推薦使用該 material 把所有指令都玩過, 讓學習 git 不枯燥且快速理解各個 command.
若已對 git 有一定程度的認識或熟悉,可跳到 Summary 複習常用指令及比較!

The Best Learning Material

🔗 Create new repo in terminal

  1. 檢查 git 版本以確認 git 已安裝:$ git --version
  2. 建立資料夾:$ mkdir git_test
  3. 初始化 git repo:$ cd git_test$ git init
  4. 創一個文件並提交到 master 分支

     echo "# My Project" > README.md
     git add README.md
     git commit -m "Initial commit"
    
  5. 創一個新的分支:Branch

🔗 Commit and push

$ git add .
$ git commit -m “message”
$ git push GH(local) origin/master(remote)

🔗 Branch

  1. 新增分支:$ git branch <branchName>
  2. 查看目前在哪個個分支: $ git branch -v
  3. 分支改名:$ git branch -v <branchName> <new-branchName>
  4. 刪除分支:$ git branch -d <branchName>
    若還有未合併的內容但硬要刪除,只要改用 -D 參數就可以強制刪除
  5. 切換到新分支:$ git checkout <branchName>
    1. 新增分支並切換:$ branch checkout -b newBranch
    2. 刪除分支: $ branch checkout -v <branchName>
  6. $ git branch -u o/main foo

    foo branch 被設定成 track o/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 會更加簡潔好看。

rebase1.png 現在 bugFix branch 上的工作在 main branch 的最前端,同時我們也得到了一個更加線性的 commit 順序。 ⚠️ 本來的 commit C3 沒有消失(在圖上面呈現陰影),而我們”複製” C3,將它的副本 C3’ 接在 main branch 的後面。

rebase2.png

🔗 commit tree

🔗 git reset / git revert 取消 git 修改

在 git 裡面取消修改同時具有底層的部份(暫存一些獨立的文件或者片段)和高層的部份(修改是如何被取消)。我們主要講的重點是後者。

🔗 cherry-pick

$ git cherry-pick <Commit1> <Commit2> <...>:當你想要複製幾個 commit 並且接在你目前的位置(HEAD)下面的時候,這會是一個非常直接的方式。

cherry-pick 使用場景

撿取想要的東西 (在 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

interactive_rebase.png

如果已經將這些 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_tag.png

🔗 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_discribe1.png

git_discribe2.png

🔗 git fetch 從 remote 下載資料 (⚠️ 不更新!)

🔗 git pull (將 fetch 下來的檔案更新到 local)

git_pull.png

一次下載 (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 同步。

pull_rebase.png push failed 只需要把 C3 接在 remote 最新的版本 C2 的後面就可以了。

image.png

$ git pull —rebase ; git push

image.png

🔗 git push origin

$ git push origin <source>:<destination>

image.png

🔗 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 的相反,而且因為我們在相反方向傳遞資料,所以這也很合理!

image.png

image.png

在兩個奇怪的情況下,git 不使用 <source> 參數,事實上,在git push以及git fetch的情況下,可以允許你”不用”指定 source,你可以藉由把參數留空,來表示你不想指定 source:

push 藉由把 source “留空”,成功用 push 刪除了 foo branch

→ 對於 fetch 來說,source “留空” 表示我們要在 local 上建立一個新的 branch。

🔗 git pull origin

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。