--- title: Astro課程 0629 - Git tags: astro, git --- [git 練習場](https://learngitbranching.js.org/?locale=zh_TW) [git interview問題](https://gitbook.tw/interview) # Astro課程 0629-0701 git ## 使用brew 安裝 ``` tingtinghsu@Hui-TingdeMacBook-Air % which brew (這個指令在哪裡) /usr/local/bin/brew ``` ## 確認git版本 ``` tingtinghsu@Hui-TingdeMacBook-Air % git --version git version 2.24.0 ``` ## 初次設定git ``` git config --list git config --global user.name "tingtinghsu" git config --global user.email "tingtinghsu.tw[at]gmail.com" ``` 注意:早期的時候寄email是不需要密碼的 也可以把以上指令中的global換成local,不同專案用不同公司/email (因為不想發現是誰做的,統包商vs其他承包商) cd /tmp (把資料放在/tmp,重開機就不見了) ## `git init` 告訴git, 以後這個資料夾就是我管! ``` tingtinghsu@Hui-TingdeMacBook-Air projects % mkdir gitdemo tingtinghsu@Hui-TingdeMacBook-Air projects % cd gitdemo tingtinghsu@Hui-TingdeMacBook-Air gitdemo % pwd /Users/tingtinghsu/Documents/projects/gitdemo tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git init Initialized empty Git repository in /Users/tingtinghsu/Documents/projects/gitdemo/.git/ ``` `.git` 所有紀錄都會在裡面 小技巧:不要給廠商 `.git`檔 (拿到肉體,不要給靈魂(對方歷史紀錄)) 發案方(甲方)記得拿到歷史紀錄 ![source](https://i.imgur.com/JzBMYV7.png) [Reference](https://ithelp.ithome.com.tw/articles/10211058) 儲存庫(遠端) ![](https://i.imgur.com/3rRkJwz.png) CRUD 新增讀取修改刪除 ## git status ``` tingtinghsu@Hui-TingdeMacBook-Air gitdemo % touch first_day.rb tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) first_day.rb ``` ## git add ``` tingtinghsu@Hui-TingdeMacBook-Air gitdemo % touch second_day.rb tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) #變成兩個檔案 first_day.rb second_day.rb tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git add first_day.rb tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: first_day.rb Untracked files: (use "git add <file>..." to include in what will be committed) second_day.rb ``` ## `git rm -cached` 復原staging的狀態 使用`git rm -cached 檔案名稱`將暫存區域的檔案移回工作目錄 ``` tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git rm --cached first_day.rb rm 'first_day.rb' tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) first_day.rb second_day.rb ``` ## `git commit -m "<寫入訊息>"` 使用 `git commit -m "簡述此次修改的內容"` 將資料推入儲存庫,此語法是一個歸檔的動作,敘述修改內容有助於之後的閱讀 **(如果只輸入git commit 會進入 vim畫面)** ``` tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git commit -m "first day in Astro" [master (root-commit) 0eca0f5] first day in Astro 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 first_day.rb create mode 100644 second_day.rb ``` 什麼時候要commit? - 下班了 - 功能做到一個段落 ## `git log`, `git log --oneline` `git log`會顯示`commit`的作者、代碼及修改時間 `git log --oneline`將上述內容簡化成一行(不顯示作者) ``` tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git log commit c3c15882363d0e28f0b9180a80094ad8e6eb396e (HEAD -> master) Author: tingtinghsu <tingtinghsu.tw@gmail.com> Date: Mon Jun 29 15:56:24 2020 +0800 edit file using vim commit 0eca0f50a4d69facf96c5d395621b3f69e497ee4 Author: tingtinghsu <tingtinghsu.tw@gmail.com> Date: Mon Jun 29 15:42:31 2020 +0800 tingtinghsu@Hui-TingdeMacBook-Air gitdemo % git log --oneline c3c1588 (HEAD -> master) edit file using vim 0eca0f5 first day in Astro ``` 重要!`git add .`和 `git add --all`會將**所有**在工作目錄的檔案加入至暫存區域,範圍影響太大,盡量不要使用 # Git 小劇場 ## `git restore 檔名` 可將刪掉之後的檔案拿回來 ``` tingtinghsu@Hui-TingdeMacBook-Air projects % cd git-examples tingtinghsu@Hui-TingdeMacBook-Air git-examples % ls git-branch1 git-branch3 git-rebase git-branch2 git-conflict git-stash tingtinghsu@Hui-TingdeMacBook-Air git-examples % cd git-branch1 tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % ls config hello.html index.html welcome.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % rm index.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % ls config hello.html welcome.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git restore index.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % ls config hello.html index.html welcome.html ``` ## `git restore` 和 `git checkout` 的不同 `git checkout` => 有可能是兩種選項 `git restore` (刪掉儲存的檔案) `git switch` (切換分支) ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch * cat master tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git help checkout tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % ls config hello.html index.html welcome.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % touch cat tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % ls cat config hello.html index.html welcome.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git rm cat fatal: pathspec 'cat' did not match any files tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git checkout cat Already on 'cat' tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git checkout master Switched to branch 'master' tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % ls cat config hello.html index.html welcome.html tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch cat * master ``` ## `git help checkout` 可以查詢指令 `git checkout .` (回歸到最近的一次commit狀態) SL 大法: Save and load `git restore .` 註:`git checkout`和`git reset`也容易混淆!請參考[細說git reset和git checkout的不同之處](https://medicineyeh.wordpress.com/2015/01/22/%E7%B4%B0%E8%AA%AAgit-reset%E5%92%8Cgit-checkout%E7%9A%84%E4%B8%8D%E5%90%8C%E4%B9%8B%E8%99%95/) ## `git`壞掉了,這行是誰寫的? `git blame 檔名` 罵人之前先檢查,阿老師說吼通常會blame到自己 ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git blame index.html abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 1) <!DOCTYPE html> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 2) <html> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 3) <head> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 4) <meta charset="utf-8"> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 5) <title>首頁</title> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 6) </head> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 7) <body> 657fce78 (Eddie Kao 2017-08-02 16:53:43 +0800 8) <div class="container"> 657fce78 (Eddie Kao 2017-08-02 16:53:43 +0800 9) </div> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 10) </body> abb4f438 (Eddie Kao 2017-08-02 16:49:49 +0800 11) </html> ``` ## `git log -p` 單一檔案的歷史紀錄 ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git log -p index.html commit 657fce783a23e26721ec4f778b9e0e108253e92d Author: Eddie Kao <eddie@5xruby.tw> Date: Wed Aug 2 16:53:43 2017 +0800 add container diff --git a/index.html b/index.html index d1146e2..e90bdb3 100644 --- a/index.html +++ b/index.html @@ -5,5 +5,7 @@ <title>首頁</title> </head> <body> + <div class="container"> + </div> </body> </html> commit abb4f43814af7bcf47afa9b779aaba63599e562b Author: Eddie Kao <eddie@5xruby.tw> Date: Wed Aug 2 16:49:49 2017 +0800 ``` # `git branch` 分支 關鍵下一秒 (Next, 2007) 男主角可以看到不久之後的未來 未來 git master 可能會改成 git main (?) # `git head` * HEAD 現在 磁頭指的地方就是現在所在的分支上 分支指是一張貼在某個`commit`上的貼紙 分支往前移動,被HEAD(空心小圈圈)指的貼紙就往前貼 要先把權杖/令牌交給分身(HEAD,空心小圈圈),再叫分身去做事 ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch cat tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git status On branch master nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch cat * master tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git checkout cat Switched to branch 'cat' tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git status On branch cat nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch * cat master ``` 圖形不一定會開叉,因為設計概念是貼貼紙 剛開完分支時並沒有感覺到特別的變化 (就像原地跳起來、原地落下) (回到主要分支的時候,其他分支上建立的東西不會被刪掉) 本體`master`盡量留在原地,不要往前移動; 讓分支往前移。 # Day2 ## 狀況一:建立新分支,並將新分支的commit合併到主分支 **重要:合併分支可以視為移動貼紙!** 流程 1. `git branch cat` 建立新分支 2. `git checkout cat` 切換到新分支 3. `git commit -m "多一個commit" --allow-empty` 在新分支上`commit --allow-empty`內容不一定要有檔案(這指令不太有意義,只是為了教學用) 4. `git checkout master` 切回主分支 6. `git merge cat` 主分支合併新分支 使用`Fast-forward 快轉模式`合併(收割成果) 練習: ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git status On branch cat Untracked files: (use "git add <file>..." to include in what will be committed) cat tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git add . tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git commit -m "add cat" [cat 454e202] add cat 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch all * cat master tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git checkout master Switched to branch 'master' tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git merge cat Updating e12d8ef..454e202 Fast-forward cat | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git branch all cat * master ``` ## 狀況二:複雜一點的分支 - 開始跟別人合作 - 如果要把`cat`分支和`dog`分支合併在一起 (~~例如跟郭台銘合併家產~~) Eg. 如何用`dog`分支去合併`cat`分支? 1. 長出一顆新的`commit` (合併用的節點)(~~公正的律師~~) 2. 被head指的`dog`貼紙往前移 註:`git merge`時盡量加上註解`-m " "`,不然會進入vim畫面 這次使用的`git merge`就不是`fast-forward`,而是`'recursive' strategy` 練習: ``` tingtinghsu@Hui-TingdeMacBook-Air git-examples % cd git-branch2 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git status On branch master nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git branch cat dog * master tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git checkout dog Switched to branch 'dog' tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git merge cat -m "dog branch merge cat" Merge made by the 'recursive' strategy. cat1.html | 0 cat2.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat1.html create mode 100644 cat2.html tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git branch cat * dog master ``` 不想要快轉合併,可以在指令加上`--no-ff` (只是為了創造分支的存在感) ## 狀況三:刪掉已經合併過的分支 `git branch -d cat` `git branch -D` 檔案沒有完全不見(所以可以救回來) 不管是FF合併或是Y型合併 - 歷史紀錄並不會消失 (因為是沒有人看管的末端枝節) - 電腦多了40bytes (撕掉貼紙佔用的空間) ## 狀況四:忘記切換分支 只要想辦法把貼紙切換過來就可以 因為`git`的本體是`commit` (做了什麼事情),而不是貼紙的名字 名字互換: ``` git branch -m cat ccc (在master上改cat名字成ccc) git checkout ccc git branch -m master cat git checkout cat git branch -m ccc master ``` ## 狀況五:發生衝突 Conflict - 順利長出共同節點的前提是**沒有衝突** - git沒有辦法解決conflict,但會標出conflict的地方 練習:不管是誰合併誰,都會產生衝突 ``` tingtinghsu@Hui-TingdeMacBook-Air git-examples % cd git-conflict tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git status On branch payment nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git branch master member * payment tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git merge member Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result. ``` - 解決衝突並不一定代表解決問題 => 如果沒有修好出現conflict的程式碼就直接`commit`,問題還是沒有解決 => 去找同事討論該如何修改程式產生衝突的地方再commit ``` tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git checkout member index.html: needs merge error: you need to resolve your current index first tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git status On branch payment You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Changes to be committed: new file: member.html Unmerged paths: (use "git add <file>..." to mark resolution) both modified: index.html tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git add . tingtinghsu@Hui-TingdeMacBook-Air git-conflict % vim index.html tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git commit -m "remove payment link" [payment 031dc59] remove payment link tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git merge member Already up to date. tingtinghsu@Hui-TingdeMacBook-Air git-conflict % git status On branch payment nothing to commit, working tree clean ``` # rebase - rebase:把Y型分支拉直(複製一份分支再嫁接的概念) - rebase發生衝突時,先解決衝突,再continue ## rebase與merge的差別 優點 - 省一顆commit的節點 - 讓合併的歷史紀錄`分支樹`變得比較`瘦`、`精簡` 缺點 - 比較麻煩 - 歷史紀錄不會保留(但這可能也是優點) 練習: ``` tingtinghsu@Hui-TingdeMacBook-Air git-examples % cd git-branch2 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git status On branch dog nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git checkout cat Switched to branch 'cat' tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git status On branch cat nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git rebase dog First, rewinding head to replay your work on top of it... Fast-forwarded cat to dog. ``` 什麼時候適合用rebase? - 用來整理自己的commit - 分叉的地方對其他人不太有意義的時候 # reset ## 當做好的commit不太滿意,想要砍掉 `git reset "commit的id"` (reset有become的意思) 後面接的三種參數: 決定被砍掉的commit要被丟到哪裏 ``` --soft (暫存區) --mixed (預設值,放在工作目錄)(比soft多了git add指令) --hard (拋棄) ``` 練習: ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git log --oneline 454e202 (HEAD -> master, cat, all) add cat e12d8ef add database.yml in config folder 85e7e30 add hello 657fce7 add container abb4f43 update index page cef6e40 create index page cc797cd init commit tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git reset 657fce7 tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git log --oneline 657fce7 (HEAD -> master) add container abb4f43 update index page cef6e40 create index page cc797cd init commit tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) cat config/ hello.html ``` 比較常用代名詞 (master, cat, HEAD) ## `git reset HEAD --hard` 我想回到現在這個地方 (修改過但還沒commit的變化,全部都不要了) > ^ = caret 卡洛特 ~ = tilde 提爾達 ^ 倒退一步 ^^ 倒退二步 ~10 倒退10步 ## `git reset HEAD^ --hard` => 類似ctrl Z 我想回到上一步 (但不能再回來了) # checkout ## `reset` 跟`checkout`指令的差別? `git checkout`: 我要去別的地方看一下 HEAD移動,`master`留在原地,所以分支的末端不會消失 `git reset`: HEAD和master一起移動到之前的commit,分支的末端消失 練習: 1. `cat` 合併 `dog` ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git checkout cat Switched to branch 'cat' tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git status On branch cat nothing to commit, working tree clean # Merge branch dog into cat tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git merge dog Merge made by the 'recursive' strategy. dog1.html | 0 dog2.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 dog1.html create mode 100644 dog2.html ``` 2. 把cat貼紙撕起來,貼回上一步的commit ![](https://i.imgur.com/CbQCSik.png) git reset HEAD^ --hard (變回合併前的狀態) ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git log --oneline 28f7314 (HEAD -> cat) Merge branch 'dog' into cat 053fb21 (dog) add dog 2 b69eb62 add dog 1 b174a5a add cat 2 c68537b add cat 1 e12d8ef (master) add database.yml in config folder 85e7e30 add hello 657fce7 add container abb4f43 update index page cef6e40 create index page cc797cd init commit tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git reset HEAD^ --hard zsh: no matches found: HEAD^ tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git reset HEAD\^ --hard HEAD is now at b174a5a add cat 2 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git log --oneline b174a5a (HEAD -> cat) add cat 2 c68537b add cat 1 e12d8ef (master) add database.yml in config folder 85e7e30 add hello 657fce7 add container abb4f43 update index page cef6e40 create index page cc797cd init commit ``` [錯誤訊息:zsh: no matches found: HEAD^](https://github.com/ohmyzsh/ohmyzsh/issues/449) ## 狀況七:不小心用reset HARD模式刪掉commit 使用`git reflog`,然後hard reset回來 ``` tingtinghsu@Hui-TingdeMacBook-Air git-examples % cd git-branch1 tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git reset HEAD~5 --hard HEAD is now at cc797cd init commit ``` 刪掉只剩一個commit...該怎麼辦? ![](https://i.imgur.com/7YuVK3q.png) 解法:找出HEAD的移動軌跡,包括 `checkout` `commit` `rebase` `reset` `merge` ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git reflog cc797cd (HEAD -> master) HEAD@{0}: reset: moving to HEAD~5 e12d8ef HEAD@{1}: checkout: moving from cat to master b174a5a HEAD@{2}: reset: moving to ORIG_HEAD 7243723 HEAD@{3}: rebase finished: returning to refs/heads/cat 7243723 HEAD@{4}: rebase: add cat 2 5b8f484 HEAD@{5}: rebase: add cat 1 053fb21 HEAD@{6}: rebase: checkout dog b174a5a HEAD@{7}: checkout: moving from master to cat e12d8ef HEAD@{8}: reset: moving to ORIG_HEAD b174a5a HEAD@{9}: merge cat: Fast-forward e12d8ef HEAD@{10}: reset: moving to ORIG_HEAD ... ``` 找到`e12d8ef HEAD@{1}`的序號 `git reset e12d --hard` ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch1 % git reset e12d --hard HEAD is now at e12d8ef add database.yml in config folder ``` commit又全部長回來了! ![](https://i.imgur.com/VUiQKmH.png) 註:`rm -rf .git` 才會砍掉所有紀錄 ## 狀況八:不小心刪掉分支,如何復原? 刪掉dog分支的話,再把貼紙貼回去原來的commit就好 `git branch -d dog` `git branch -D dog` `git branch new_dog 053fb21` (`git branch 分支名稱 想賦予名稱的commit id`) ``` tingtinghsu@Hui-TingdeMacBook-Air git-examples % cd git-branch2 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git branch -d dog error: The branch 'dog' is not fully merged. If you are sure you want to delete it, run 'git branch -D dog'. tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git branch -D dog Deleted branch dog (was 053fb21). tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git reflog e12d8ef (HEAD -> master) HEAD@{0}: checkout: moving from cat to master b174a5a (cat) HEAD@{1}: reset: moving to ORIG_HEAD ... tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git branch new_dog 053f ``` ![](https://i.imgur.com/toiCpCL.png) 冷知識:HEAD移動到不同分支上的commit節點 git checkout HEAD^1^2 = git checkout HEAD^^2 ## 如何回到rebase之前的狀態? 1. 先rebase: 從cat分支:`git rebase dog` (rebase current branch onto dog) 2. 再查詢當時分支存在的id: `git reflog` 3. 然後用reset回復: `git reset b174 --hard` 註:還可以繼續用reset `id`結果又回到剛才rebase的狀態!(~~方唐鏡~~) => 結論:reset可以用來做很多事! # tag `git tag 2.0` (類似git branch 2.0的意思) `git tag 1.0 abb4` `cd -`: 回到上一動(上次的目錄) 所以`git checkout -`: 也是回到上一次路徑的意思 ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git tag 2.0 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git tag 1.0 abb4 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git checkout 2.0 Note: switching to '2.0'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c <new-branch-name> Or undo this operation with: git switch - Turn off this advice by setting config variable advice.detachedHead to false HEAD is now at 053fb21 add dog 2 tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git checkout 1.0 Previous HEAD position was 053fb21 add dog 2 HEAD is now at abb4f43 update index page tingtinghsu@Hui-TingdeMacBook-Air git-branch2 % git checkout - Previous HEAD position was abb4f43 update index page HEAD is now at 053fb21 add dog 2 ``` ## tag和branch有什麼不同? branch會跟著commit走 tag會留下來 「留下來,或者我跟你走!」 # 修改commit -m 的訊息 `git rebase bb0c -i` (interactive模式) 或者使用git GUI軟體,在commit的點上按右鍵操作 ![](https://i.imgur.com/FCPzs7y.png) ![](https://i.imgur.com/07BDINJ.png) 做過修改後,之後的子分支會產生平行時空(序號改變) ## 整合太瑣碎零散的commit 把add cat1 和 add cat2拆掉 squash with previous commit ![](https://i.imgur.com/epj3iTq.png) ![](https://i.imgur.com/ndMAkXJ.png) 修改後的commit,之後的序號都改了 ![](https://i.imgur.com/uQ3dmKC.png) ## 拆掉很大一包的commit 1. 停下來: 先把HEAD指向需要修改的commit 2. 倒退一步 `git reset HEAD^` 3. 分別拆開檔案 `git add` 4. `git rebase --continue` (但有接完壞掉的情況,取消 `git rebase --abort`) ``` tingtinghsu@Hui-TingdeMacBook-Air git-branch3 % cd ../git-rebase tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git status interactive rebase in progress; onto bb0c9c2 Last commands done (2 commands done): pick 382a2a5 add database settings edit 3323ec7 All cat (+2 squashed commits) Squashed commits: [1de2076] add cat 2 [cd82f29] add cat 1 Next commands to do (5 remaining commands): pick 4c5255f add dog 1 pick 1d68ddc add dog 2 (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch 'master' on 'bb0c9c2'. (use "git commit --amend" to amend the current commit) (use "git rebase --continue" once you are satisfied with your changes) nothing to commit, working tree clean tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git reset HEAD\^ tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git status interactive rebase in progress; onto bb0c9c2 Last commands done (2 commands done): pick 382a2a5 add database settings edit 3323ec7 All cat (+2 squashed commits) Squashed commits: [1de2076] add cat 2 [cd82f29] add cat 1 Next commands to do (5 remaining commands): pick 4c5255f add dog 1 pick 1d68ddc add dog 2 (use "git rebase --edit-todo" to view and edit) You are currently editing a commit while rebasing branch 'master' on 'bb0c9c2'. (use "git commit --amend" to amend the current commit) (use "git rebase --continue" once you are satisfied with your changes) Untracked files: (use "git add <file>..." to include in what will be committed) cat1.html cat2.html cat3.html cat4.html nothing added to commit but untracked files present (use "git add" to track) tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git add cat1 cat2 fatal: pathspec 'cat1' did not match any files tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git add cat1.html cat2.html tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git commit -m "cat1 cat2" [detached HEAD aeff088] cat1 cat2 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat1.html create mode 100644 cat2.html tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git add cat3.html cat4.html tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git commit -m "add cat3 cat4" [detached HEAD dce6353] add cat3 cat4 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat3.html create mode 100644 cat4.html tingtinghsu@Hui-TingdeMacBook-Air git-rebase % git rebase --continue Successfully rebased and updated refs/heads/master. ``` 成功把整包的貓拆成兩個`cat1 cat2`, `cat 3 cat4`兩個檔案! ![](https://i.imgur.com/vzWCKnB.png) 問題: source tree如何install command line? ![](https://i.imgur.com/kjeeLHv.png) 然後直接在command line叫出source tree `stree .` # 在GitHub建立新專案 New repository,網頁版右上角 `+` ## 全新專案 ``` …or create a new repository on the command line echo "# gitfile" >> README.md git init git add README.md git commit -m "first commit" git remote add origin https://github.com/tingtinghsu/gitfile.git git push -u origin master ``` 選擇`https`,`https://github.com/tingtinghsu/gitfile.git` 在推送的過程中會需要輸入帳號密碼 選擇`SSH` ,本地檔案會存一份公鑰和私鑰(不要在公用電腦上做) ## git push origin master的解釋 ``` Git push origin master 把master的進度推一份到origin Git push origin master Git push abc master Git push xyz master Git push heroku master Git remote add heroku git@heroku.com:tingtinghsu Git push piano master (把貓推到鋼琴上) ``` `git push origin master`其實是縮寫 ``` git push origin master => git push origin master:master (遠端建立master branch的意思) 所以 git push origin master:cat (遠端建立cat branch的意思) ``` 會用到`git push origin master:cat`的情境 ``` git push heroku master (heroku只能接受推master) git push heroku dev git push heroku dev:master (dev推上heroku遠端節點,再把遠端推成master branch的意思) ``` 練習: ``` git push origin master:cat Total 0 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'cat' on GitHub by visiting: remote: https://github.com/tingtinghsu/gitfile/pull/new/cat remote: To github.com:tingtinghsu/gitfile.git * [new branch] master -> cat ``` ## 在本地電腦產生SSH Key ```  ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/Users/tingtinghsu/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /Users/tingtinghsu/.ssh/id_rsa. Your public key has been saved in /Users/tingtinghsu/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Iq4xYSKyLdY27bThg4cZM+RZTPb+BhYj9TOT8jbLl2s tingtinghsu@Hui-TingdeMacBook-Air.local The key's randomart image is: +---[RSA 3072]----+ | | | o . | | + o . . | | . + = * | |+ = + + S + | |o=.O.. = + | |o.++O+. = o . | |...B=oo = E | | . .+. . o.. | +----[SHA256]-----+ ~ cd /Users/tingtinghsu/ ~ ls Applications Documents Dropbox Movies Pictures Desktop Downloads Library Music Public ~/.ssh ~/.ssh  ls id_rsa id_rsa.pub known_hosts ~/.ssh  code . ~/.ssh  cat id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDVr2bi2kTesH5q1qbs0kx2rlWjCV5poV87HI5d8J0WQXXEt5oRCEi3+TP1UklablEGTD2zRjIBLkzN7AIZadALzkFMdfyLT7Y8b1ll5kDc3lNyb6mCHfqjUe5XAWit2oVUS1zqaCbcBdRUvEmxR0vprO3ksaCPbHbYDkaK7lkq/rPgfGwMXVz1FHjj7vD4x7v1NcQnAyw0ZyGgV9/VeYs7eCILHdRXpAbstmFDyXcHYJwVgnRbEh0B4cCJ4/489sVsucZNgH/FghAOYAdX864I0OtNu44xBGyafauU2hOFgkxcRaio4Y09eUUD8IoEDmErShL/QT64ZJnxdG3sfswik3TvKuQWmbWn4CKN9Vxx0+1esg9dUgY7wjfDvhPgssEDmLp5ZLDy1PLhNf7uflSWmzwhrfa9XmNPW+X8ijpHwJSmfaKDvPaB78yNg9O3avjV6IMJW7FaV1tqqQyp9XLDpOnH180KMTNZjV68yUE+Jd11n6IJR0xy8J8WK04XkfE= tingtinghsu@Hui-TingdeMacBook-Air.local ``` ## 為GitHub設定SSH 1. 確定該資料夾是否有設定遠端: `git remote -v` ``` fatal: not a git repository (or any of the parent directories): .git (要選擇有.git檔案的資料夾才可推) ``` 2. 移除設定推送的遠端位置: `git remote rm origin` 3. `-u` 設定預設的本地與推送的遠端位置配對 (通常最近手邊在忙的專案) `git push -u origin master` 註:如果沒有`-u`,每次`git push`會出現 `The current branch master has no upstream branch.` ``` git push origin master The authenticity of host 'github.com (140.82.114.3)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'github.com,140.82.114.3' (RSA) to the list of known hosts. Enumerating objects: 9, done. Counting objects: 100% (9/9), done. Delta compression using up to 4 threads Compressing objects: 100% (7/7), done. Writing objects: 100% (9/9), 899 bytes | 299.00 KiB/s, done. Total 9 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), done. To github.com:tingtinghsu/gitfile.git * [new branch] master -> master git push fatal: The current branch master has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin master ``` # 從GitHub把檔案pull下來 `git fetch` 抓線上有的、但是本機沒有的檔案 ``` ~/Documents/projects/gitdemo   master  git fetch remote: Enumerating objects: 4, done. remote: Counting objects: 100% (4/4), done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From github.com:tingtinghsu/gitfile 2ad4ead..5f08c95 master -> origin/master ``` ![](https://i.imgur.com/5UQLI3b.png) `git merge origin master` 把本機的檔案跟上進度 ``` ~/Documents/projects/gitdemo   master  git merge origin/master Updating 2ad4ead..5f08c95 Fast-forward origin.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 origin.html ``` ## git pull `git pull` = `git fetch` + `git merge` ``` ~/Documents/projects/gitdemo   master  git pull origin master From github.com:tingtinghsu/gitfile * branch master -> FETCH_HEAD Updating 5f08c95..a5feaf1 Fast-forward "\bpull.html" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "\pull.html" ``` ## git clone `git網址` eg. 加入新專案、加入新團隊 在什麼都沒有的情況下,沒辦法fetch 必須先直接把整包下載下來 fetch是用來補上進度 # 一起合作專案的時候 策略一:先拉下來一份,再merge,然後再推上去 => 但這樣會分支圖很醜 策略二:每個人建立新分支,在分支上做事情 注意: `git push -f`使用前要先告訴同事! 因為其他同事之後還要先砍掉原有檔案再`git clone` (因為歷史紀錄已經改變) ## Pull Request 先fork其他人的專案到我自己的地方來 做完修改後再送PR ## merge策略 1. `create a merge commit` Y型合併,多一次commit(如果發PR的人落後orgin/master的專案進度,commit數還會再增加) 2. `squash and merge` 一次送了多個commit在同一個PR,但擠壓完只有增加一次commit 3. `rebase and merge` 直接加上發PR的人送的commit數 ## 自己fork的專案落後原作專案的commit進度,如何跟上? => 想辦法同步 [手冊:Syncing a fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork) [怎麼跟上當初 fork 專案的進度?](https://gitbook.tw/chapters/github/syncing-a-fork.html) ``` # 新加上原作的remote master (名稱為upstream) git remote add upstream https://github.com/kaochenlong/dummy-git.git # 拉一份下來,再合併 (讓本機master往前移動) git pull upstream master ``` # Git的世界觀 1. git的物件有計算公式 加密演算法:可以拿來驗證,但無法反推 只要內容不變,數值就不變 一樣的內容(就算都是空的內容),數值不變 mkdir不會產生亂數 **但是!commit本身的內容幾乎等於亂數** 2. git不在乎檔名是什麼,只在乎檔案內容的變化 `cat index.html | grep title` `|` 的意思:把前面的東西丟給`|`的後面 `cat index.html | git hash-object --stdin` 會算出一個SHA1的一長串數值 git add (內容壓縮後,存在物件區) ``` git cat-file -t 看型態 git cat-file -p 看內容 ``` ## 切換branch時發生什麼事? 葡萄梗:從整串抽出來分支看 ## Git不是做差異備份! git 即使只改一個字,都重新計算,產生一顆物件