# 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

* 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 概念&常用指令
上傳至工作區>出入庫

* 用-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 的時候就只有那個分支會前進喔。
* 合併分支

範例:將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
```

抓下來的分支會比本地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

```
$ 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 裡,而其它的分支大多會因任務結束而被刪除。