---
# System prepended metadata

title: Git Middle Level

---

# Git 中階應用

視覺化 Git 操作
https://git-school.github.io/visualizing-git/

## Outline

```markmap

# Git

## Local Repository
- init
- add
- commit
- clone

## Branch Operation
- branch
- checkout
- merge
- rebase
- tag

## Rework Skill
- reset
- cherry-pick
- revert
- squash
- stash

## Merge Conflict
- resolve
- 2-way merge
- 3-way merge

## Cowork with git
- remote
- fetch
- pull
- push

```

## Local Repository

    這是我的 Repository；我git，我不求人

```markmap
## Local Repository
- init
- add
- commit
- clone
```

1. 建立自己的 local repository

```bash=
cd mysource
git init
git add .
git commit -m "init commit"
```

2. 把程式碼加入 repository

    - **working directory**
    
        工作目錄，目錄裡有`.git`資料夾(repository)的目錄
    
    - **untracked file** 還沒有被監管的檔案
        
        當檔案曾經被`coomit`過，檔案就是被監管(tracking)，所以`untracked file`就是新增的檔案
        
    - **changes not staged for commit** 被監管的檔案有異動，還沒有被監管的檔案commit
    
        被監管的檔案有被異動，不論修改或刪除都是這個狀態

    - **staging area** 暫存區域
    
        還沒`commit`之前，你可以在`staging area`檢視你的工作

    - **repository** 本地端的儲存庫

        被`commit`的記錄都會被放到儲存庫，用`git log`可以查看歷史記錄。你可以視它為資料庫，檔案型資料庫。

3. **init**

    `init`會在目錄下建立`.git`目錄，並初始化儲存庫
    
    - `--bare`
    
        亦裸裸的儲存庫，是用來做為共同開發時的單一來源庫，可以被用來`clone`的庫。你也可以在本機創建裸庫，作為`remote`。

4. **add**

    - stage
        
        `git`使用`edit/stage/commit`模式
    
    - `add -p`
        
        `git add`預設是以檔案為單位，但也可以以`code block`為單位，放在`staging area`的東西是為之後的`commit`做準備

5. **commit**

    - 一個`commit`一個工作，要確保你的程式不論在那個`commit`的狀態下都是可用的
    
    - `commit`會有其上游(base)，以`sha1`來表示，使用上只要取`sha1`的前8位就可以代表一個`commit`

6. **clone**
    
    - 一生一次，之後的更就都使用`pull`
    
    - `--depth=1`
      
      不要對整個`git tree`抓取，我只想要**快點取得**最新的程式碼，不要連整個歷史都給我

---

## Branch Operation

    我上下其手，在一棵名為 git 的樹上

```markmap
## Branch Operation
- branch
- checkout
- merge
- rebase
- tag
```

1. branch & checkout

    - **HEAD**
        
        指向作用中(current)的分支
    
    - **detached HEAD**
        
        當 HEAD 不是指向分支，而是其中的一個 commit 時
    
    - **reset --soft**
    
        通常不用，`--hard`才是王道
    
    - **merge**
    
        分支操作，把目標分支給合併到`HEAD`所在的分支，合併後，共享相同的未來
    
    - **rebase**
    
        我`HEAD`所在的分支的歷史和過去，建立在另一個分支之上，`rebase`後，你的過去就不一樣了
    
2. **tag**

    標籤是被貼在特定的`commit`，貼了之後就不能移動，不像`branch`可以想方設法讓它移到別的`commit`。`tag`通常被用來做版本(發布)管理。
    
    - 用`branch`管理需求
    - `commit`管理技術工作
    - `tag`管理發行版本。

---

## Rework Skill

    玩弄這棵樹

```markmap
## Rework Skill
- reset
- cherry-pick
- revert
- squash
- stash
```

1. **reset**

    reposition HEAD

    - **hard**
        HEAD移到目標commit，且工作區的環境對齊目標 commit 狀態
    - **mixed**
        HEAD移到目標commit，工作區不動，與 commit 差異放到 unstage
    - **soft**
        HEAD移到目標commit，工作區不動，與 commit 差異放到 stage area
        
2. **cherry-pick**

    挑檢櫻桃，就跟它的名字一樣，你可以只挑你想要的櫻桃(commit)來吃

3. **commit --amend**

    訊息寫的不夠好，你有機會重來

4. **revert**

    有問題 commit，你可以針對它、墮掉它

5. **rebase -i**

    在 rebase 時，順便把你的工作重新整理

6. **squash**

    在 merge 時，把要被 merge 的分支上的差異壓成一個 commit 丟過來，不會產生 merge commit

![](https://learn.microsoft.com/en-us/azure/devops/repos/git/media/merging-with-squash/squash-branch-merge.png?view=azure-devops =450x)

7. **stash**

    - 你正在開發新工作，此時線上有個 bug 要解，你可以將工作到一半的東西先丟到一旁，之後在拿回來即可
    
    - 會有這個需求的原因是，當你的工作區有異動沒有被 commit 時，你無法接換分支

---

## Merge Conflict

    三姑六婆，會造成理解上的障礙

```markmap
## Merge Conflict
- resolve
- 2-way merge
- 3-way merge
```

1. **conlict** & resolved

    意圖不明會發生在
    
    - 有 2 個意圖
    - 沒有前後關係
    - 因為 2 個意圖都可以追朔到一個來源
    - 且改動到相同的 code block

    關係曖昧不明時，就需要有人從旁下指導

2. merge
    
    - 2-way merge
    - 3-way merge
    - manual merge

---

## Cowork with git

    多人爬樹，原則還是不變

```markmap
## Cowork with git
- remote
- fetch
- pull
- push
```

1. remote

    - git 設計上是分散式的`SCM(source code management)`，所以你專案的上游`upstream`可以不只一個
    
    - 當然，也可以只有一個上游，將 git 當成中央式(centralized repository)是通用的做法。

2. fetch & pull
    
    - FETCH_HEAD 指向遠端分支最新的位置，fetch 會更新它
    
    - pull = fetch + merge 你都知道了，你也可以自己來
    
    - fast-forward

3. push
    
    - tracking branch 你從遠端 checkout 出來的分支會 tracking 它的來源
    
    - 預設 push 會找遠端相同名稱的分支推送，當然你也可以自己來
    
    - don't FORCE push 樹根都被你改了，你要叫別人怎麼辦(用到的人全部重新 clone)

4. misc.
    
    - rebase **before** publish your own branch
    
    - **after** publish, always using merge
    
    - workflow is conventional
    
    - PR 

----

## 窗外篇

### merge

- 快轉 fast-forward
![](https://i.imgur.com/oPLxsue.png)

- 完整記錄
![](https://i.imgur.com/jtXK79v.png)

### commit

- split your work
- once a work been completed, commit it
- commit offent

### commit message

-

### ignore file




<br>