Git筆記
===
###### tags: `git` `web`
## 目的
**進行版本管理**
- Git可以把檔案的狀態作為更新歷史記錄保存起來
因此可以把編輯過的檔案復原到以前的狀態,也可以顯示編輯過內容的差異。
- 當有人想將編輯過的舊檔案上傳到伺服器、覆蓋其他人的最新檔案時,系統會發出警告,因此可以避免在無意中覆蓋他人的編輯內容。

## 數據庫(Repository)
是記錄檔案或目錄狀態的地方,儲存內容的修改歷史記錄。
Git的數據庫分為**遠端數據庫**和**本地端數據庫**兩種。
- 遠端數據庫: 配有專用的伺服器,為了讓多人共享而建立的數據庫。
- 本地端數據庫:為了方便用戶個人使用,在自己的機器上配置的數據庫。
創建本地端數據庫的方法有兩種:
1. 新建數據庫
2. 複製遠端數據庫
## 紀錄修改的提交
若想把變更與新增的檔案/目錄儲存到數據庫中,就需要進行提交。
執行提交時,系統會要求輸入提交訊息。
請務必輸入提交訊息,因為在訊息空白的狀態下執行提交是會失敗的。
以下是Git的標準提交訊息:
```
第1行:提交修改內容的摘要
第2行:空行
第3行以後:修改的理由
```
> ps.提交訊息是查看其他人提交的修改內容或自己檢查歷史記錄時重要的資料。所以要用心填寫讓人容易理解的提交訊息。
## 工作目錄與索引
**工作目錄**(Working Tree)是保存目前正在處理檔案的目錄,Git 相關的操作都會在這個目錄下完成。
**索引**(Index)位於工作目錄和數據庫之間,是為了向數據庫提交作準備的暫存區域。
Git在執行提交的時候,不是直接將**工作目錄**的狀態保存到數據庫,而是將**索引**的狀態保存到數據庫。
因此,要提交文件,首先需要把文件加入到**索引**區域中。
## 初期設定
### 用戶名、信箱
```
$ git config --global user.name "<使用者姓名>"
$ git config --global user.email "<電子郵件>"
可以使用
$ git config --list
這個指令來看你的 Git 設定內容
```
### 在windows下讓讓非ASCII字符的檔案名正確顯示
```
$ git config --global core.quotepath off
```
### alias
終端機來操作 Git 很繁瑣,因此 Git 也有提供 alias 的功能
```
$ git config --global alias.st status
```
## 在本地新建數據庫
### 新建
在桌面建立 tutorial 資料夾(可更換路徑、名稱)
```
mkdir tutorial
```
```
$ cd tutorial
$ git init
```
成功會顯示
```
Initialized empty Git repository in /Users/Desktop/tutorial/.git/
```
### 移除
```
$ rm -rf .git
```
## 提交文件
在 tutorial 目錄中建立一個文件( test.txt )
```
touch test.txt
```
### git status
1. 可以用 status 指令確認工作目錄和索引的狀態
```
$ git status
```
> 目前狀態
```
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# test.txt
nothing added to commit but untracked files present (use "git add" to track)
// test.txt還不是歷史記錄追踪的對象,只要將其加入到索引,就可以登錄成為追踪對象。
```
### git add
2. 把 test.txt 加入到**索引**,就可以追蹤它的變更了(每次修改都要重新add)
```
// $ git add <file> <file> ....
// $ git add . 和 -A 可以加入所有文件到索引
這邊是
$ git add test.txt
快速追中所有檔案
$ git add .
```
取消追蹤
```shell
$ git rm --cached <filePath>
## does not unstage a file, it actually stages the removal of the file(s) from the repo (assuming it was already committed before) but leaves the file in your working tree (leaving you with an untracked file).
## 它不會取消暫存文件,它實際上會從repo中刪除文件(假設它之前已經提交過),但是將文件留在工作樹中(留下未跟踪的文件)。
$ git rm --cached <file>
## 取消追蹤某個檔案
### That said, if you used git rm --cached on a new file that is staged, it would basically look like you had just unstaged it since it had never been committed before.
### 也就是說,如果你在一個新的文件中使用了git rm --cached,它基本上看起來就像你剛剛從未提交它一樣,因為它之前從未提交過。
$ git reset HEAD <file>
## 取消追蹤所有檔案
$ git reset -- <filePath>
## will unstage any staged changes for the given file(s).
## 將取消暫存給定文件的任何分階段更改。
$ git reset HEAD
## 回到未pull之前的版本
```
> 目前狀態
```
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: test.txt
#
```
### git commit
3. 加到索引後,就可以提交檔案囉
```
// $ git commit
// -m "" 快速提交所有
// -a 提交所有
這邊是
$ git commit test.txt -m "first commit"
若要提交所有追中的檔案
$ git commit -m "fast commit all files"
```
> 目前狀態
```
# On branch master
nothing to commit (working directory clean)
// status命令顯示沒有其他更新需要提交
```
#### 修改最後一次 commit 訊息
如果想修改最後一次的 commit 訊息,可以使用
```
git commit --amend -m "Something New..."
```
#### 追加檔案到最後一次 commit 訊息
```
git commit --amend --no-edit
```
### git log
4. 可以用 log 指令,顯示數據庫的歷史提交記錄。
```
$ git log
// --graph 參數可以圖表方式顯示分支歷史
// --oneline 參數可以簡化輸出訊息,以簡潔方式呈現
commit ac56e474afbbe1eab9ebce5b3ab48ac4c73ad60e
Author: hdboy <felicity860128@gmail.com>
Date: Sun Nov 27 22:30:53 2016 +0800
first commit
```
安裝 git 時,也會安裝 gitk,可以在GUI下確認提交記錄
```
$ gitk
```
### git diff
可以比對兩次提交改動的部分
```
// git diff commit1_hash commit2_hash
// 最少打4碼就好
$ git diff 42dd f100
```
## 還原
- 還原工作目錄與索引,會跟最後一次 commit 保持一樣。
```
git reset --hard
```
- 恢復單一檔案到最新 commit 狀態。
```
git checkout 檔案名稱
```
:::info
HEAD: 目前所在分支位置的指標
:::

- 移動 HEAD 到指定 commit 查看當時內容

```
git checkout commit前4碼
```


:::warning
但是移動回去後,所做的任何變更(包括 add、commit),都會在回到 master 時消失,若想要回到某時間點開發時,可以從那個時間點新增 branch 出來開發,完成後再 merge 回去就好。
:::
```
git checkout -b 分支名稱
```
- 把 HEAD 移回 master
```
git checkout master
```
### 切換到遠端分支
遠端分支預設會隱藏,可用 ``git branch -a`` 查看
若要切換到分支可用
```
$ git checkout origin/feature
```
想在此分支上開發則必須在本地建立同名分支
加上參數 **-t** 預設會在本地建立同名分支
```
$ git checkout -t origin/feature
```
查看分支
```
$ git branch
* feature
master
```
### 撤銷但保留最新一次的 commit
如果有一個 repo git log 長這樣
```
1 -> 2 -> 3 (head)
```
想要把 log 紀錄變成這樣
```
1 -> 2 -> 3 -> 2 (head)
```
會有此需求是,版本 3 修改的內容要撤回,還原到版本 2,但又希望版本 3 的 commit 紀錄保留下來,方便之後回來查看當時修改的東西,可以下
```shell=
$ git revert head
// 或是版本號也可以
$ git revert 99d44ee
```
## push
為了將本地端的修改歷史共享到遠程數據庫,必須上傳本地端儲存的修改歷史。
### git remote
請使用remote命令增加一個遠端數據庫
<name>指向遠端數據庫登錄名稱、<url>指向遠端數據庫的URL。
```
$ git remote add <name> <url>
EX:
git remote add origin https://github.com/XXXXX/gittest.git
```
#### 移除(更改遠端數據庫)
有兩種方法
1.
```
git remote set-url origin [url]
```
2.
```
git remote rm origin
git remote add origin [url]
```
#### 查看 remote 資訊
```
$ git remote -v
origin https://git.gss.com.tw/ryan_lee/xxx (fetch)
origin https://git.gss.com.tw/ryan_lee/xxx (push)
```
### git push
當要push變更到遠端數據庫時,請使用push命令
加上<repository>指定要push的地址、<refspec>指定要push的分支。
```
$ git push <repository> <refspec>
Ex:
$ git push -u origin master
```
在執行提交時指定參數 -u 項目,之後就可省略指定分支名稱。但當push到空白的遠端數據庫時,您就必須指定遠端數據庫和分支的名稱,不能省略。
```
$ git push
```
> 在主控台,執行push或pull命令時想省略遠端數據庫名稱的話,會使用origin名稱作為遠端數據庫,因此一般都會為遠端數據庫命名為origin。
## clone
如果遠端數據庫中有其他人的修改歷史,可以把它完整地複製下來,就可以進行操作了。
使用clone命令來複製遠端數據庫
<repository>指定遠端數據庫的URL,<directory>指定要複製的目錄名稱。
```
$ git clone <repository> <directory>
EX:
git clone https://github.com/XXXXX/gittest.git
可以指定存入的資料夾名稱
git clone https://github.com/XXXXX/gittest.git test
```
:::info
``git clone`` 會將專案完整複製回來
**完整**的意思是指資料夾內將會包含: **工作目錄** (Working Directory) 與 **本地儲存庫** (Local Repository)
白話理解就是 **程式的檔案** 與 **.git 資料夾**
:::
### 搬遷遠端 git 儲存庫
#### 本地已經有完整的專案
修改 remote orgin 為新專案位址
```
$ git remote set-url origin https://github.com/hbdoy/new_project
```
推上去
```
$ git push --all
$ git push --tags
```
#### 本地沒有完整專案
不需要 clone 一大包東西下來,可以 clone 一個 bare git repo (只有本地儲存庫),再推到新位址去就好
```
$ git clone --bare https://github.com/hbdoy/xxx
```
```
$ cd xxx.git
$ git push --mirror https://github.com/hbdoy/new_project
```
:::success
有關更多有關 bare repo 的介紹可以看 [Git Bare Repo](/my9DygI8TOSzIpLA4yZryA)
:::
## pull
若是共享的遠端數據庫由多人同時作業,那麼作業完畢後所有人都會把修改歷史push到遠端數據庫。所以需要同步其他人的修改內容到自己的本地端數據庫。
本地上次的push到執行下一次的push的期間,如果有其他人push更新了遠端數據庫,而本地端沒有更新的話,那麼你的push會被拒絕。
這個時候,需要進行合併操作導入其他人的修改歷史,否則push都會被拒絕。
使用pull命令以執行pull。如果省略數據庫名稱的話,會對以origin命名的數據庫執行pull。
```
$ git pull <repository> <refspec>
```
### 修改紀錄衝突
Git執行合併會自動合併修改的部分,但也有不能自動合併的時候。
如果遠端數據庫和本地端數據庫的同一個地方都發生了修改的情況下(ex:檔案中同一行的地方),這時Git不能自動判斷要導入那一個修改內容就會發生錯誤。
```
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
```
打開test.txt,Git將用標示行顯示內容發生衝突的地方
``=======``分隔的上方是本地端的編輯內容,下方是遠端的編輯內容哦
```
<<<<<<< HEAD
123 我打在第三行
=======
abc 我也打在第三行
>>>>>>> 4c0182374230cd6eaa93b30049ef2386264fe12a
```
可以修改為
```javascript=
000
111
123
abc
```
修改完後記得 ``add``、``commit`` ~
## 分支 branch

:::info
概念釐清:
1. 在切換分支時,除非兩分支皆沒有新的 commit,否則需要在原分支 commit 或 stash 後才能切換到另外一個分支。
2. master 分支為正式版本,若要新增或修復功能時,不能直接在上面修改,而是要新增一個開發分支來改動,免得炸裂XD
:::
### 新增

```
// $ git branch <branch-name>
$ git branch dev
```
### 移除
```
$ git branch -d <branch-name>
```
### 切換

```
// $ git checkout <branch-name>
$ git checkout dev
```
### 查看所有支線

```
$ git branch
```
clone 時遠端分支也會 clone 下來,但是會隱藏,可以用此命令查看
```
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/develop
remotes/origin/feature
remotes/origin/master
```
### 把分支推上去遠程數據庫
本地端分支推上遠程數據庫
第一次執行會提示打下面指令
```
$ git push --set-upstream origin dev
```
之後就直接push就好
```
$ git push
```
也可以直接推送所有分支
```
$git push --all
```
### 分支合併 merge

跳回到master,把dev合併進來

```
// $ git merge <branch-name>
$ git checkout master
$ git merge dev
```

之後再push上去遠程數據庫
```
$ git push
```
### 分支合併衝突
上面例子中新增一個分支並對內容修改時,原先的 master 並沒有新的 commit,所以 merge 時僅是把分支的程式碼新增到 master 上。
儘管新增分支時,master 也有新的 commit,只要沒有衝突,這些合併過程皆會自動執行。
(merge前)

(merge後)

但是和 pull 一樣,若是發生無法自動判斷的情形時,就需要手動處理合併衝突。
```
<<<<<<< master
123 我打在第三行
=======
abc 我也打在第三行
>>>>>>> feature1
```
修改完後記得 ``add``、``commit`` ~
## 標籤 tag
如果想替 commit 加上標註的話,可以使用 tag。
ex: 此次 commit 為 v1 版本。
### 查詢標籤
```
git tag
// 詳細查詢(包括 tag 內容描述)
git tag -n
// 同時顯示 tag
git log --oneline
// 查看該 tag 詳細 commit 訊息
git show tag_name
```
### 新增標籤
```
// git tag 標籤名稱
git tag v1
// 加上描述文字(沒有 hash 則加到最新 commit)
git tag 標籤名稱 -am "內容描述" [commit hash]
```
### 刪除標籤
```
git tag -d 標籤名稱
```
### 切換到指定標籤之 commit
```
git checkout 標籤內容
```
### Push tag
```
git push --tags
```
## 暫存 stash
若要切換分支時,不想 commit 未完成的內容時,可以先暫存起來。
### 暫存當前目錄
```
git stash
```
### 瀏覽暫存目錄
```
git stash list
```
### 還原暫存
```
git stash pop
```
### 清除最新暫存
```
git stash drop
```
### 清除全部暫存
```
git stash clear
```
## gitignore
.gitignore 檔案可以過濾掉不想推上git的東西
```
test/
123.txt
*.index
```
若是不小心在 gitignore 設定前把檔案推上去
```
git rm -r --cached 檔案or目錄名稱
```
## vim
打開test.txt,不存在則建立
```
$ vim test.txt
```
提交時可能會用到vim,常見的參數
- i: 插入
- esc鍵 離開當前功能 像是i(插入)
- :q離開
- :wq寫入之後離開
## 常用指令
- ``ls -la`` : 顯示目錄中資料夾與檔案
- ``pwd`` : 看當前路徑
- ``touch xxx.txt`` : 新建檔案
- ``cd D:/test`` : 移動到指定路徑
- ``clear、cls`` : 清除畫面
- ============================
- ``git init`` : 建立本地數據庫
- ``git status`` : 查看目前狀態
- ``git log`` : 查看提交紀錄
- ``git add .`` : 所有檔案加入追蹤
- ``git commit -m "some text"`` : 快速提交
- ``git push`` : 推送到遠端數據庫
- ``git pull`` : 與遠端數據庫同步
- ``git branch 分支名稱`` : 建立分支
- ``git checkout XXX`` : 切換到分支/某commit
- ``git merge 分支名稱`` : 合併分支
## 私人專案
github有限制私人專案的數量
bitbucket可以存很多私人專案
## 參考來源
1. [連猴子都能懂的git](https://backlogtool.com/git-guide/tw/)
2. [30 天精通 Git 版本控管](https://github.com/doggy8088/Learn-Git-in-30-days/blob/master/zh-tw/README.md)
3. [Git 官方](https://git-scm.com/book/zh-tw/v1)
<style>
.ui-infobar { display: none; }
</style>