# Git 基本操作
###### tags: `git`
## Reference
[Git 與 Github 版本控制基本指令與操作入門教學](https://blog.techbridge.cc/2018/01/17/learning-programming-and-coding-with-python-git-and-github-tutorial/)
[Git 教學(1) : Git 的基本使用](http://gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/)
[Git 教學(2):Git Branch 的操作與基本工作流程](http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/)
[30天精通git版本控制](https://ithelp.ithome.com.tw/users/20004901/ironman/525?page=1)
[Git版本控制branch model分支模組基本介紹](https://blog.wu-boy.com/2011/03/git-版本控制-branch-model-分支模組基本介紹/)
## 常用基本指令

做一下流程的整理:
修改檔案 => 加入 stage (git add) => 提交( git commit )=> 繼續修改其他檔案
### 設定帳戶
- 使用者
```
git config --global user.name "<Your Name>"
```
- Email
```
git config --global user.email "<your@gmail.com>"
```
### 查看已設定內容
```
git config --list
```
其實 Git 的設定檔是儲存在你的家目錄的.gitconfig 隱藏檔中,你可以使用編輯器將他打開
```
cat ~/.gitconfig
```
### 建立local的Repository
到目標資料夾內
將專案資料夾建立成git repository
```
git init
```
### 檢視狀態
```
git status
```
### 加入追蹤
如果在status顯示
==untracked==
表示尚未add
:::info
若有檔案修改,就要記得再add修改的檔案
若要放棄修改,可以使用git checkout -- 檔案名稱
:::
```
git add "file"
```
**Tips: 一次加入全部檔案**
```
git add .
```
### 加入repository
如果在status顯示
==Changed to be committed==
表示尚未commit
```
// 不含comment,指令後才輸入comment
git commit
// 指令和comment一起
git commit -m "update hello.py"
// add跟commit一起
git commit -a -m "修改了 hello.py"
```
### 查看過去commit紀錄
```
git log
```
### 不想加入版本控制的追蹤
commit會忽略
```
// 把檔案列入
vim .gitignore
```
檢查是否已經加入
```
git ls-files --error-unmatch "file path"
```
### 將repository做本機和遠端的連結
- 本地端專案知道 origin 對應到遠端網址
這個 origin 名稱是在 Git 版本控管中慣用的預設遠端分支的參照名稱,主要目的是用來代表一個遠端儲存庫的 URL 位址。
```
git remote add <remote name> <remote 網址>
Ex: git remote add origin <remote網址>
```
這些註冊進工作目錄的遠端儲存庫設定資訊,都儲存在 .git/config 設定檔中
```
Ex:
[remote "origin"]
url = https://github.com/doggy8088/sandbox-empty2.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "jquery"]
url = https://github.com/jquery/jquery.git
fetch = +refs/heads/*:refs/remotes/jquery/*
```
- Local 程式push到遠端檔案庫
```
git push --set-upstream origin master
```
可以簡化為
```
git push -u <remote name> <branch name>
/*
可以拆解成
1 git push origin master
2 git checkout master
3 git branch -u origin/master
*/
```
只要成功設定好 upstream 後,第二次以後要上傳分支時,就只需要透過 git push 就可以了
Ex:
```
git push
```
### 複製別人的repository
```
git clone 網址
```
### Fetch相關
若使用網頁操作,那網頁上的東西就會比本地還要新
這時候就要用fetch抓下最新的
```
git fetch
```
但上述只是單純抓下來,並沒有merge
要merge的話必須在輸入
```
git merge origin/master
```
不過其實有兩者合併的做法
就是pull
```
// pull = fetch + merge
// git pull <repository> <branch>
git pull origin master
```
git 2.9.0 開始預設行為不允許合併沒有共同祖先的分支。
所以有可能出現refusing to merge unrelated histories
這時就要在指令後面加上option
`--allow-unrelated-histories`
## Git branch 相關操作流程
當專案開始執行時,我們這時候必須將程式碼分成兩部份,一個是 master 另一個就是 develop,master 主要用來 Release 產品專用,沒事就不要去動它,假如要繼續開發新功能,或者是修正 Bug issue 就利用 develop 這分支來開發,等待開發完成,要 Release 下一版產品時就將 develop merge 到 origin/master 分支,這樣才對,避免有人把 origin/master 改爛
### 查看所有的branch和目前所在的branch
```
git branch
```
### 新增branch
```
// 單純新增
git branch "name"
// 新增+切換過去
git checkout -b "name"
```
### 切換branch
```
git checkout "name"
```
### 合併branch
1. git merge
把開發好的東西合併回master
```
// 在master 輸入
git merge cat
```
2. git rebase
- 不單單只是將兩個不同的branch合併起來
而是**將某一支branch基於另一隻branch的內容合併起來**
- 當多個使用者開發時,master branch可能不再是你當初checkout出去的狀態
但我們會希望自己目前這支branch的內容保有master branch最新的狀態
- git rebase 會基於 master branch 目前最後一次的 commit 內容
再往後把你在新的branch上commit的內容加上去
```
// 在新的branch輸入!
// branch 基於 master branch 做 rebase
git rebase master
```

此不同於 git merge 的線圖會把 cat branch 合併到 master branch ,
而是把原本的 cat branch 接到 master branch 因此只有一條線
:::info
開發過程中,若你在開發的 branch 功能比較多, commit 的量也比較多時,建議使用rebase將你現在的 branch 整理過再合併回主幹,這樣會產生較漂亮的線圖
:::
### Conflict
很常發生在merge or rebase的途中產生conflict
這時git會停下來請你去處理
- 發生 confict 時的處理步驟
1. 將發生 confict 的檔案打開,處理內容( 別忘了刪除<<<、===、>>> )。
2. 使用 git add 將處理好的檔案加入 stage。
3. 反覆步驟 1~2 直到所有 confict 處理完畢。
4. git commit 提交合併訊息。
5. 完成
### 刪除branch
```
git branch -d <branch>
```
## 取消上一次操作 相關指令
版本控制最大的好處之一就是讓你永遠可以後悔,因此我們常會希望把已暫存的檔案、已提交的 commit 或是已合併的 branch 取消修改
### 取消merge
```
// 取消剛剛的 merge 動作
git reset --hard ORIG_HEAD
```
### 取消已暫存的檔案
有時候手殘不小心將還沒修改完的檔案使用 git add 加入了 stage
```
git reset HEAD "file"
```
### 取消commit過的檔案
若是我想完全放棄這次修改 (將檔案狀態回復到最新的一次 commit 時的狀態)
```
git checkout -- "file"
```
### 修改上一次的commit
手誤打太快, commit 訊息打錯時
```
git commit --amend
```
### 強制回復到上一次commit 的版本
有時候我們想要放棄所有修改回到 commit 時的狀態
```
git reset --hard HEAD
```
### 想回復到很久之前的版本
查看所有訊息
```
git reflog
```
根據 commit id 回覆到指定版本
```
git reset --hard commit_id
```
### 強制更新遠端branch
```
git push -f
```
### 刪除git commit中的所有file
```
git rm -r --cached .
```
## Fork 和 clone 一個 open source(開源)的計畫
到網頁上找到目標專案
點選右上角 fork 按鈕,複製一份專案到我們這
點選右邊綠色按鈕clone down,複製連結

```
// 複製到本地端
$ git clone https://github.com/happycodergit/react.git
// 移動到 目標 資料夾
$ cd 目標dir
// 切出自己的新分支(使用 -b)
$ git checkout -b happycoder@feature_branch
// 做一些 README.md 檔案修改,然後 commit 到自己 fork 過來的專案
$ git commit -a -m "Update README"
$ git push origin happycoder@feature_branch
```
你可以透過將自己的修改 commit 到自己 fork 過來的專案,然後到原始專案頁面點選 new pull request 按鈕發 pull request(會比對程式碼的差異)。若對方 review 完後接受就可以將自己的程式碼合併到原始專案中
```
// 若完成 pull request 記得讓 master(或是合併進去的 branch)保持同步
$ git pull upstream master
```
## 情境
### git 刪除push到遠程服務器的commit
```shell=
1.通过找到想要退回到的commit_id
$ git log
2.本地回到上一个commit_id
$ git reset --hard <commit_id>
3.推送到服务器,一定要加 --force 参数
$ git push origin HEAD:master --force
```