# Git & CLI & vim [git教程](https://gitbook.tw/) [git新的連接規範](https://dotblogs.com.tw/CYLcode/2020/06/15/102853) [可視化練習](https://learngitbranching.js.org/?fbclid=IwAR0XO2-mlIuUvnUoYkedy2dIKYhzUrtY81qxFggjRnPNa6R-yPnK0mvugmM&locale=zh_TW) #### git ignore ``` /node_modules **/.DS_Store ``` ### vim ![](https://i.imgur.com/P2vmLU9.png) * HJKL是上下左右 Vim 主要是使用模式的切換來進行輸入、移動游標、選取、複製及貼上等操作。 ``` 剛開始使用git 输入git config --list 最后出现一个 (END) ,要等一会才能退出来,这个怎么解决? 原因: git日志过多最后显示end无法输入命令,打印的信息是全部用户配置的,表示可以用了。 直接按 q (或+Enter) ``` [git-bash呼叫vs code](https://www.gushiciku.cn/pl/goLL/zh-tw) [vs code預設git-bash](https://medium.com/@jackaly9527/%E7%9A%AE%E6%AF%9B%E7%AD%86%E8%A8%98-%E5%A6%82%E4%BD%95%E5%9C%A8vs-code%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%A8%AEshell%E6%96%BC%E7%B5%82%E7%AB%AF%E6%A9%9F%E4%B8%8A-f89ce1e59fea) ### git 概念&常用指令 上傳至工作區>出入庫 ![](https://i.imgur.com/ScA3nXX.png) * 用-a可同時上傳加入庫,但要注意的是這個 -a 參數只對已經存在 Repository 的檔案有效, 對還是新加入的檔案(也就是 Untracked file)是無效的。 ``` $ git commit -a -m "版本更新訊息" 若忘記留訊息,會強制跳到編輯器叫你留信息,輸入i(insert),留完以後esc,再:wq離開 ``` * 可以用 |(中文「或者」的意思) 來查詢「Sherly 以及 Eddie 這兩個人的 Commit 紀錄」: ``` $ git log --author="Sherly\|Eddie" ``` * 使用 --oneline參數可排版成一行 * 使用 --grep 參數,可以從 Commit 訊息裡面搜尋符合字樣的內容 * 使用 -S 參數,可以搜尋在所有 Commit 的檔案中,哪些符合特定條件的 * 在查歷史資料的時候,可以搭配 --since 跟 --until 參數查詢 * 找到「從 2017 年 1 月之後,每天早上 9 點到 12 點的 Commit」 ``` $ git log --oneline --since="9am" --until="12am" --after="2017-01" ``` 刪除weclome.html檔案 ``` $ git rm welcome.html git會把檔案刪除的修改紀錄加入工作區 再commit入庫,完成流程 ``` * 我不是真的想把這個檔案刪掉,只是不想讓這個檔案再被 Git 控管了」的話,可以加上 --cached 參數 ``` $ git rm welcome.html --cached rm 'welcome.html' ``` * 跟刪除檔案一樣,變更檔名也是一種「修改」,所以操作上其實也是差不多的 ``` $ git mv hello.html world.html # 把 hello.html 改成 world.html ``` * 雖然 git log 可以檢視整個專案的 Commit 紀錄,但如果只想檢視單一檔案的紀錄,只要在 git log 後面接上那個檔名,如果想看這個檔案到底每次的 Commit 做了什麼修改,可以再給它一個 -p *前面的加號 + 表示是新增的內容,如果是減號 - 表示原本的內容被刪除 * 修改前一次的commit訊息為Welcome To Facebook ``` $ git commit --amend -m "Welcome To Facebook" ``` ### 某些檔案不想放入git 只要在專案目錄裡放一個 .gitignore 檔案,並且設定想要忽略的規則就行了。 ``` $ touch .gitignore ``` 然後編輯這個檔案的內容: ``` # 檔案名稱 .gitignore # 忽略 secret.yml 檔案 secret.yml # 忽略 config 目錄下的 database.yml 檔案 config/database.yml # 忽略所有 db 目錄下附檔名是 .sqlite3 的檔案 /db/*.sqlite3 # 忽略所有附檔名是 .tmp 的檔案 *.tmp # 當然你要忽略自己也可以,只是通常不會這麼做 # .gitignore ``` .gitignore 檔案設定的規則,只對在規則設定之後的檔案有效,如果想套用 .gitignore 的規則,就必須先使用 git rm --cached 指令把這些檔案請出 Git,移出 Git 控管之後,它就會開始會被忽略了。 如果想要一口氣清除那些已經被忽略的檔案,可以使用 git clean 指令並配合 -X, ( -f 參數是指強制刪除的意思) ``` $ git clean -fX ``` ### 救回被刪除的檔案或還原版本 ``` $ git checkout 檔案名稱 $ git checkout . #救回全部檔案 ``` 這個指令會把暫存區(Staging Area)裡的內容或檔案,拿來覆蓋工作目錄(Working Directory)的內容或檔案。 如果在執行這個指令的時候這樣做多加了一個參數: ``` $ git checkout HEAD~2 welcome.html $ git checkout HEAD~2 . #兩個版本前的全部檔案來覆蓋現在的檔案 ``` 這樣就會拿距離現在兩個版本以前的那個 welcome.html 檔案來覆蓋現在的工作目錄裡的 welcome.html 檔案,但要注意的是,這同時也會更新暫存區的狀態喔。 ### reset重作入庫 如果想拆掉最後一次的 Commit,可以使用「相對」或「絕對」的做法。「相對」的做法可以這樣 ``` $ git reset master^ or $ git reset HEAD^ or $ git reset e12d8ef #指定版本SHA-1碼 # ^是前一次,^^^^^是前五次,也可以寫成~5 ``` Reset 的模式 | 模式 | - -mixed(預設) | - -soft| - -hard | | -------- | -------- | -------- | -------- | | 工作目錄 | 不變 | 不變 | 丟掉 | | 暫存區 | 丟掉 | 不變 | 丟掉 | | Commit 拆出來的檔案 | 丟回工作目錄 | 丟回暫存區 | 直接丟掉 | Git 的 Reset 指令用英文來說比較像是「go to」或「become」的概念。 * git reflog(或git log -g)指令可以列出包含修改過程的所有commit歷程,用來查找SHA-1碼來reset(Reflog 預設會保留 30 天) ### Branch 分支只是一個指向某個 Commit 的指標(像標籤貼紙) 不同工程師可以同時進行修改或新功能開發測試,不影響master(主分支) ``` $ git branch #瀏覽分支 $ git branch dog #新增dog(分支名)分支 $ git branch -m dog tiger #將dog分支改名為tiger $ git branch -d dog #刪除dog分支 $ git branch -D dog #強制刪除dog分支 $ git checkout cat #切換至cat分支 ``` 切換分支後,操作就跟一般的差不多,一樣都是先 add 再 commit,但不一樣的是,當你在 Commit 的時候就只有那個分支會前進喔。 * 合併分支 ![](https://i.imgur.com/dr4WkKp.png) 範例:將cat分支合併至master ``` $ git checkout master #先切換至master $ git merge cat #合併 ``` [不小心把還沒合併的分支砍掉了,救得回來嗎?](https://gitbook.tw/chapters/branch/restore-deleted-but-unmerged-branch) [另一種合併方式(使用 rebase)](https://gitbook.tw/chapters/branch/merge-with-rebase) [手邊的工作做到一半,臨時要切換到別的任務](https://gitbook.tw/chapters/faq/stash) ### REBASE ``` $ git rebase -i bb0c9c2 #從現在到bb0c9c2之間的版本都可以被修改 ``` -i 參數是指要進入 Rebase 指令的「互動模式(interactive)」,而後面的 bb0c9c2 是指這次的 Rebase 指令的應用範圍會「從現在到 bb0c9c2 這個 Commit」。這個指令會跳出一個 Vim 編輯器 commit排列的的順序,跟 git log 指令的結果是相反的,但在 SourceTree 介面是一樣的喔。 前面的 pick 的意思是「保留這次的 Commit,不做修改」 * 修改歷史訊息 前面的 pick 改成 **reword**,或是懶得打字也可以只用 r 就好,表示待會我要來修改這些 Commit 的訊息。編輯完後會照順序跳出vim,一個一個訊息修改 * 把Commit 合併/拆解 **合併**:把前面的pick改成 **squash**,合併順序是舊>新,存檔並離開 Vim 編輯器後,它會開始進行 Rebase,而在 Squash 的過程中,它還會跳出 Vim 編輯器讓你編輯合併後的訊息 **拆解**:把前面的pick改成 **edit**(head會停在這裡),之後用git reset指令將當前commit拆開,拆出來的複數個檔案在一個一個add>commit,都拆分完成後,用**git rebase --continue**完成流程讓head回到最新紀錄 [拆解流程](https://gitbook.tw/chapters/rewrite-history/split-one-commit-to-many-commits) **在兩個commit間新增commit**:把較舊的commit前面的pick改成 **edit**(head會停在這裡),新增完欲加入的commit後用**git rebase --continue**完成流程讓head回到最新紀錄 * 調整 Commit 順序 進入互動模式後直接調整(越上面越舊)即可 * 刪除歷史 前面的 pick 改成 **drop**的就會被刪除,或直接把那行刪掉也可以,可能會有[後遺症](https://gitbook.tw/chapters/rewrite-history/remove-and-reorder-commit) ### 想取消 Rebase /Merge /reset --hard 的話 兩種方法 ``` 1.$ git reflog #取得之前的SHA-1碼 2.$ git reset ORIG_HEAD --hard #git內建的復原機制 ``` ### tag Annotated tags are meant for release while lightweight tags are meant for private or temporary object labels. * 輕量標籤lightweight tag ``` $ git tag tag_try 51d54ff #用log刷出歷程後,用欲加標籤的commit SHA-1碼 之後此commit歷程會變這樣: 51d54ff (tag: tag_try) add lion and tiger ``` 輕量標籤僅是一個指向某個 Commit 的指標,沒有含有其它的資訊,所以 Git 比較推薦使用有附註的標籤 * 有附註標籤annotated tag ``` $ git tag tag_try 51d54ff -a -m "I'm trying annotated tag!" ``` -a 參數就是請 Git 幫你建立有附註的標籤,而後面的 -m 則是跟我們在做一般的 Commit 一樣輸入的訊息,如果沒有使用 -m 參數,會自動跳出一個 Vim 編輯器出來。 * 兩種標籤的差別 有附註的標籤比一般的輕量標籤多了一些資訊,可以清楚的看得出來是誰在什麼時候打了這張標籤。不管是哪種標籤,跟分支一樣都是以檔案方式存在 .git/refs/tags 目錄下: ``` $ git show tag_try #可show出tag的資訊 ``` * 刪除標籤 ``` $ git tag -d tag_try ``` ### GitHub GitHub 是目前全球最大的商業網站/Git Server,網站則是使用 Ruby on Rails 開發 ``` $ echo "# hello-GitHub" >> README.md $ git init $ git add README.md $ git commit -m "first commit" $ git branch -M main #前面都是基本招 $ git remote add origin https://github.com/kentyangfake/hello-GitHub.git #在遠端新增一個叫origin的節點 $ git push -u origin master #-u 指令預設之後輸入git push,會把本地的master推到origin ``` 遠端節點慣例用Origin,本地主分支慣例用Master * push的原理: ``` $ git push origin master:main #把本地的master分支內容push到origin這個節點,並上傳(若無,就新增)main這個分支。 #master可換成SHA-1值指定push的commit版本 $ git push origin :main #用終端機刪除遠端分支的方法。原理:push空白到origin這個節點,結果會刪除main分支 ``` * 製作ssh鑰匙 產生一對公鑰(public key)私鑰(private key),不要把私鑰給別人 ``` $ ssh-keygen #產生一對key $ ssh-add #在大多数系统中,默认私钥(~/.ssh/id_rsa 和 ~/.ssh/identity)会自动添加到 SSH 身份验证代理中。 应无需运行 ssh-add path/to/key,除非在生成密钥时覆盖文件名。 Identity added: /c/Users/User/.ssh/id_rsa (User@DESKTOP-I1GMTHB) $ ssh-add -l -E sha256 #确认您的私钥已生成并加载到 SSH ``` [key設定方法](https://youtu.be/CeC_qyQHiCE) [错误:权限被拒绝(公钥)](https://docs.github.com/cn/authentication/troubleshooting-ssh/error-permission-denied-publickey) * 將檔案下載至本機 1. **fetch** ``` $ git fetch ``` ![](https://i.imgur.com/wGNQs1R.png) 抓下來的分支會比本地head還新,再合併即可跟上進度 2. **pull** 原理:git pull = git fetch + git merge ``` $ git pull #==git fetch + git merge,常會產生為了合併而產生的額外 Commit or $ git pull --rebase #如果你不想要額外的 Commit ``` * 推不上去怎麼辦? 1. 先拉再推 2. 無視規則,總之就是聽我的! ``` $ git push -f #-f 或 --force強迫硬推上去,並把別人做的之前的內容蓋掉 ``` * 從伺服器上取得 Repository ![](https://i.imgur.com/uO2f4fq.png) ``` $ git clone 複製位址(ssh或http) hello_kitty #複製下來的資料夾名改成hello_kitty(不輸入就會用repository預設的) ``` * 與其它開發者的互動 - 使用 Pull Request(PR) [教程](https://gitbook.tw/chapters/github/pull-request) 1. 先複製(Fork)一份原作的專案到你自己的 GitHub 帳號底下。 2. 因為這個複製回來的專案已經在你自己的 GitHub 帳號下,所以你就有完整的權限,想怎麼改就怎麼改。 3. 改完後,先推回(Push)你自己帳號的專案。 4. 然後發個通知,讓原作者知道你有幫忙做了一些事情,請他看一下。#發一個請原作來拉回去(Pull)的請求(Request),稱之 Pull Request,簡稱 PR。 5. 原作者看完後說「我覺得可以」,然後就決定把你做的這些修改合併(Merge)到他的專案裡。 [跟上fork專案的最新進度](https://gitbook.tw/chapters/github/syncing-a-fork) ### Git Flow 根據 Git Flow 的建議,主要的分支有 master、develop、hotfix、release 以及 feature 這五種。Master 以及 Develop被稱做長期分支,因為他們會一直存活在整個 Git Flow 裡,而其它的分支大多會因任務結束而被刪除。![](https://i.imgur.com/DcZXJer.png)