# 【Treeman】Git相關

# 【GitLab】相關

# 【GitLab】gitlab-runner 執行環境種類

下面整理成表格給你（主流可用的 executor）：

<div id="bkmrk-executor-%E9%81%8B%E4%BD%9C%E6%96%B9%E5%BC%8F-%E5%84%AA%E9%BB%9E-%E7%BC%BA%E9%BB%9E-"><div><table class="w-fit min-w-(--thread-content-width)" data-end="1090" data-start="28"><thead data-end="74" data-start="28"><tr data-end="74" data-start="28"><th data-col-size="sm" data-end="39" data-start="28" style="width: 105px;">Executor</th><th data-col-size="md" data-end="46" data-start="39" style="width: 119px;">運作方式</th><th data-col-size="sm" data-end="51" data-start="46" style="width: 94px;">優點</th><th data-col-size="sm" data-end="56" data-start="51" style="width: 144px;">缺點</th><th data-col-size="sm" data-end="63" data-start="56" style="width: 106px;">適用情境</th><th data-col-size="lg" data-end="74" data-start="63" style="width: 240px;">設定/註冊要點</th></tr></thead><tbody data-end="1090" data-start="101"><tr data-end="336" data-start="101"><td data-col-size="sm" data-end="113" data-start="101" style="width: 105px;">**shell**</td><td data-col-size="md" data-end="158" data-start="113" style="width: 119px;">直接在 Runner 主機的 shell 執行（bash / PowerShell）</td><td data-col-size="sm" data-end="181" data-start="158" style="width: 94px;">最快、零容器開銷；可直接用主機已安裝工具</td><td data-col-size="sm" data-end="204" data-start="181" style="width: 144px;">隔離最弱、容易「弄髒」主機；多專案易衝突</td><td data-col-size="sm" data-end="232" data-start="204" style="width: 106px;">單機/內網、快速 smoke test、固定工具鏈</td><td data-col-size="lg" data-end="336" data-start="232" style="width: 240px;">`gitlab-runner register --executor shell`；確保 `gitlab-runner` 使用者權限（如加入 `docker` 群組）與 `builds_dir` 可寫</td></tr><tr data-end="620" data-start="337"><td data-col-size="sm" data-end="350" data-start="337" style="width: 105px;">**docker**</td><td data-col-size="md" data-end="380" data-start="350" style="width: 119px;">每個 job 以容器執行；`image:` 可指定環境</td><td data-col-size="sm" data-end="398" data-start="380" style="width: 94px;">環境可重現、隔離佳、跨專案穩定</td><td data-col-size="sm" data-end="442" data-start="398" style="width: 144px;">需管理映像；使用 Docker 需選擇掛 `docker.sock` 或 DinD</td><td data-col-size="sm" data-end="472" data-start="442" style="width: 106px;">大多數 CI/CD 場景（前後端 build、容器化）</td><td data-col-size="lg" data-end="620" data-start="472" style="width: 240px;">`--executor docker --docker-image <base>`；若 job 需 `docker build`：① 掛 `/var/run/docker.sock`，或 ② DinD：`privileged=true` + `services: docker:dind`</td></tr><tr data-end="794" data-start="621"><td data-col-size="sm" data-end="638" data-start="621" style="width: 105px;">**kubernetes**</td><td data-col-size="md" data-end="658" data-start="638" style="width: 119px;">每個 job 建一個 Pod 來跑</td><td data-col-size="sm" data-end="676" data-start="658" style="width: 94px;">彈性擴縮、資源/隔離/配額完善</td><td data-col-size="sm" data-end="692" data-start="676" style="width: 144px;">需 K8s 叢集與維運成本</td><td data-col-size="sm" data-end="709" data-start="692" style="width: 106px;">中大型團隊、尖峰負載、雲原生</td><td data-col-size="lg" data-end="794" data-start="709" style="width: 240px;">`--executor kubernetes`；於 `config.toml` 設 namespace、serviceAccount、pull secrets 等</td></tr><tr data-end="949" data-start="795"><td data-col-size="sm" data-end="805" data-start="795" style="width: 105px;">**ssh**</td><td data-col-size="md" data-end="830" data-start="805" style="width: 119px;">Runner 透過 SSH 登入遠端主機執行</td><td data-col-size="sm" data-end="857" data-start="830" style="width: 94px;">可用現成遠端環境、無需在目標機器裝 Runner</td><td data-col-size="sm" data-end="876" data-start="857" style="width: 144px;">隔離弱、擴展性差、權限管控要嚴格</td><td data-col-size="sm" data-end="891" data-start="876" style="width: 106px;">特定機器/設備、臨時需求</td><td data-col-size="lg" data-end="949" data-start="891" style="width: 240px;">`--executor ssh`；於 `config.toml` 填 host/user/key、路徑與環境</td></tr><tr data-end="1090" data-start="950"><td data-col-size="sm" data-end="963" data-start="950" style="width: 105px;">**custom**</td><td data-col-size="md" data-end="1000" data-start="963" style="width: 119px;">以自訂腳本（prepare/run/cleanup）對接任意執行環境</td><td data-col-size="sm" data-end="1015" data-start="1000" style="width: 94px;">彈性最高、可接非官方平台</td><td data-col-size="sm" data-end="1034" data-start="1015" style="width: 144px;">需自寫與維護整套腳本，診斷較複雜</td><td data-col-size="sm" data-end="1047" data-start="1034" style="width: 106px;">特殊平台、內部排程器</td><td data-col-size="lg" data-end="1090" data-start="1047" style="width: 240px;">`--executor custom`；提供自訂 hook 腳本並測好錯誤處理</td></tr></tbody></table>

</div></div>

# 【GitLab】升級相關

升級路徑  
[https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/)

# 【GitLab】runner 使用 k8s 流程說明

# 一、前置條件

- 已有可用的 **Kubernetes 叢集**（kubectl/helm 可連通）
- GitLab 可連到 Runner（你的 GitLab URL 例：`http://10.2.11.139`）
- 在 GitLab 介面建立 Runner（Project/Group/Instance → CI/CD → Runners → **New runner**），取得 **Authentication Token（`glrt-...`）**

---

# 二、建立命名空間

```bash
kubectl create namespace gitlab-runner     # 放 Runner 本體
kubectl create namespace ci-jobs           # job 跑的位置（也可與上面同一 ns）

```

---

# 三、用 Helm 裝 Runner（K8s executor）

1. 加入 chart

```bash
helm repo add gitlab https://charts.gitlab.io
helm repo update

```

2. 建一份 `values.yaml`

```yaml
gitlabUrl: "http://10.2.11.139/"
# 新流程建議用 runnerToken（glrt-...）
runnerToken: "glrt-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

rbac:
  create: true

serviceAccount:
  create: true
  name: gitlab-runner

runners:
  executor: kubernetes
  # job 要跑在哪個 ns；不填則跟 Runner 同 ns
  namespace: ci-jobs
  # 預設 job 容器用什麼 image（可被 .gitlab-ci.yml 的 image 覆蓋）
  image: alpine:3.20
  pollTimeout: 180

  # 是否允許特權容器（多數情況不需要，建議先關）
  privileged: false

  # 幫 job Pod 加上標籤（可選）
  podLabels:
    app: gitlab-ci-job

  # 預設資源限制（可選）
  resources:
    limits:
      cpu: "1"
      memory: "1Gi"
    requests:
      cpu: "200m"
      memory: "256Mi"

  # Cache（可選：接 S3/MinIO；此段示意，若未啟用請刪除）
  cache:
    type: s3
    path: "gitlab-runner"
    s3ServerAddress: "minio.minio.svc.cluster.local:9000"
    s3BucketName: "gitlab-ci-cache"
    s3AccessKey: "minio-access"
    s3SecretKey: "minio-secret"
    s3Secure: false

```

> 新流程下：**tags / run-untagged / locked** 等屬性建議在 **GitLab UI** 的 Runner 設定頁調整，不再用 CLI 參數寫入。

3. 安裝

```bash
helm upgrade --install gitlab-runner gitlab/gitlab-runner \
  -n gitlab-runner -f values.yaml

```

4. 驗證

```bash
kubectl -n gitlab-runner get pods
# 應看到 gitlab-runner 的 Pod（或 deployment/rs）

```

---

# 四、最小 `.gitlab-ci.yml`（驗證 K8s executor 正常）

```yaml
stages: [ping]

hello:
  stage: ping
  image: alpine:3.20
  script:
    - echo "Runner: $CI_RUNNER_DESCRIPTION"
    - echo "Executor: Kubernetes OK"
    - cat /etc/os-release

```

跑起來時的 log 開頭應出現：**Preparing the "kubernetes" executor**  
K8s 裡 `ci-jobs` 命名空間會看到對應的 **Pod**（生命周期：Running → Succeeded）。

---

# 五、在 K8s executor 內建 Docker 映像（推薦用 Kaniko）

K8s executor 不建議 DinD。常見做法是用 **Kaniko**（rootless，無需 Docker daemon）。

1. 在 GitLab 專案 CI Variables 設定：

- `CI_REGISTRY`（你的 registry 位址）
- `CI_REGISTRY_IMAGE`（目標 repo）
- `CI_REGISTRY_USER` / `CI_REGISTRY_PASSWORD`（登入憑證）

2. Kaniko Job 範例

```yaml
stages: [build]

docker-build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:latest
    entrypoint: [""]
  variables:
    # registry 登入（若用 GitLab Container Registry，可改為 CI 提供的 token）
    DOCKER_CONFIG: /kaniko/.docker
  script:
    - >
      mkdir -p /kaniko/.docker &&
      cat > /kaniko/.docker/config.json <<EOF
      {
        "auths": {
          "${CI_REGISTRY}": {
            "auth": "$(printf "%s:%s" "$CI_REGISTRY_USER" "$CI_REGISTRY_PASSWORD" | base64 -w0)"
          }
        }
      }
      EOF
    - echo -e 'FROM alpine:3.20\nCMD ["echo","kaniko build OK"]' > Dockerfile
    - >
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
      --destination "${CI_REGISTRY_IMAGE}:$CI_COMMIT_SHA"
      --destination "${CI_REGISTRY_IMAGE}:latest"

```

> 這個 job 會在 K8s 中跑 Kaniko，把映像打好推到 Registry，**不需要** Docker daemon、也不需要 privileged。

---

# 六、（可選）Sidecar 服務示例（K8s 會幫你生多容器 Pod）

```yaml
stages: [test]

pg-test:
  stage: test
  image: postgres:16-alpine
  services:
    - name: postgres:16-alpine
      alias: db
  variables:
    POSTGRES_PASSWORD: secret
  script:
    - psql -h db -U postgres -c "SELECT version();"

```

> 在 K8s executor 下，`services:` 會轉成 **同一個 Pod 的 sidecar 容器**，網路互通用 `alias` 連線。

---

# 七、常見疑難排解

- **Pipeline 卡 Pending**
    
    
    - 確認 Runner 在 GitLab UI 顯示 *Online*、tags 是否匹配（或允許 untagged）。
- **Pod 起不來 / CrashLoopBackOff**
    
    
    - `kubectl -n ci-jobs describe pod <pod>` 看 event / 拉不到 image / 權限問題。
- **需要特權容器**
    
    
    - 在 `values.yaml` 的 `runners.privileged: true`（謹慎評估安全）。
- **Cache/S3 連線**
    
    
    - 先用最小流程跑通，再逐一加上 cache 設定；MinIO/GCS/S3 憑證與位址要正確。

---

# 八、如果不用 Helm（純手工方式，了解即可）

- 部署一個 `gitlab/gitlab-runner` 的 Deployment
- 用 ConfigMap/Secret 提供 `config.toml`（`executor = "kubernetes"`、`[runners.kubernetes]` 參數）
- 綁一個 ServiceAccount + RBAC（允許建立/刪除 Pod）

> 但維運上 **Helm chart** 會簡單很多（升級/回滾/佈署差異）。

---

需要我幫你把 **values.yaml** 客製到你的環境（例如 MinIO 位址、特定節點選擇器、Pod 安全性設定、預設資源）嗎？我可以直接產一份可用的版本給你。

# 【GitLab】CI/CD Settings 說明

## ✅ General pipelines

[![image-1755143476227.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/H93CenHZEaHCIiBM-image-1755143476227.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/H93CenHZEaHCIiBM-image-1755143476227.png)

---

## **1. Public pipelines** 

- **功能**：允許任何人（即使沒有專案權限）查看 Pipeline 與 Job 詳細資訊，包括 log 和 artifacts。
- **影響**：
    
    
    - 開啟：方便外部協作或開源專案 debug。
    - 關閉：僅有有權限的使用者能查看。

---

## **2. Auto-cancel redundant pipelines** 

- **功能**：在同一分支上，如果新的 Pipeline 啟動，會自動取消舊的 Pending / Running pipeline。
- **好處**：減少資源浪費（例如多次 push 只保留最後一次）。
- **注意**：取消的 pipeline 會標記為 *canceled*。

---

## **3. Skip outdated deployment jobs** 

- **功能**：部署任務還沒執行時，如果有新的 pipeline 部署成功，舊的部署 job 就會跳過（不執行）。
- **應用情境**：避免舊版本覆蓋新版本的部署。

---

## **4. Use separate caches for protected branches** 

- **功能**：對受保護分支（protected branch）使用獨立的 CI cache。
- **好處**：防止非受保護分支的 cache 汙染主分支的 cache（例如 main / master）。
- **影響**：可能增加存儲空間使用。

---

## **5. CI/CD configuration file**

- **功能**：指定 `.gitlab-ci.yml` 檔案位置。
- **預設**：`.gitlab-ci.yml` 在專案根目錄。
- **用途**：
    
    
    - 如果 CI 設定檔放在子目錄，可以改成 `my/path/ci.yml`。
    - 適合 Monorepo 管理多個 CI 配置。

---

## **6. Git strategy**

- **選項**：
    
    
    - **git clone**：每個 job 都會全新 clone 一份 repo。
    - **git fetch**（已勾選）：重複使用上一次 job 的工作目錄（workspace），若不存在才 clone。
- **比較**：
    
    
    - `clone`：乾淨，但速度較慢。
    - `fetch`：快，但可能殘留舊檔案（需注意 job 隔離）。

---

## **7. Git shallow clone**

- **數值**：`20`
- **功能**：限制 clone/fetch 的提交數（Depth）。
    
    
    - 範例：`20` 代表只取最後 20 次 commit 歷史。
    - 0 或空白 → 取全部 commit &amp; tags。
- **好處**：減少 clone 時間與流量。

---

## **8. Timeout**

- **值**：`1h`（1 小時）
- **功能**：Job 最長允許執行時間。
- **到時動作**：超時會被 GitLab 強制標記為 *failed*。
- **可用格式**：秒數（`3600`）、人類可讀（`1h`、`30m`）。

---

我看你的設定，**Git strategy 用 `fetch` + shallow clone 20**，這樣拉程式會比較快，但如果你有需要依賴完整 commit 歷史（例如 `git describe`、全量 tag 檢出），要把 shallow clone 設成 `0`。

## ✅ Pipeline status

[![image-1755161177902.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/ocjYK2w6k5HYYImA-image-1755161177902.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/ocjYK2w6k5HYYImA-image-1755161177902.png)

這張圖是 **GitLab 專案的 CI/CD 狀態徽章（Badges）設定頁**，它提供了一些可以放到 README 或網頁上的「狀態顯示圖示」，用來即時反映專案的 CI/CD 情況。

分三塊說明：

---

## 1. **Pipeline status**（流水線狀態）

- 顯示當前分支的 CI/CD Pipeline 狀態（成功、失敗、進行中等）。
- 會即時更新成對應的 SVG 圖示。
- 三種嵌入格式：
    
    
    - **Markdown**：給 README.md 用
        
        ```markdown
        [![pipeline status](http://10.2.2.3/user/k8s-deploy/badges/master/pipeline.svg)](http://10.2.2.3/user/k8s-deploy/-/commits/master)
        
        ```
    - **HTML**：可用在網站或 Wiki
        
        ```html
        <a href="`http://10.2.2.3/user/k8s-deploy`/-/commits/master"> <img alt="pipeline status" src="http://10.2.11.139/srou/live-k8s-deploy/badges/master/pipeline.svg" /> </a> 
        ```
    - **AsciiDoc**：給 AsciiDoc 格式文件用

---

## 2. **Coverage report**（測試覆蓋率）

- 需要在 `.gitlab-ci.yml` 內啟用測試覆蓋率計算（通常透過 `coverage:` 關鍵字解析測試輸出）。
- 顯示該分支最近一次 Pipeline 的測試覆蓋率百分比。
- 格式同上，也提供 **Markdown / HTML / AsciiDoc** 三種嵌入方式。

---

## 3. **Latest release**（最新版本）

- 會顯示專案的最新 Release（透過 GitLab Release 功能發佈的版本）。
- 如果沒有 Release，就會顯示 `none`（像你現在的狀態）。
- 格式同上，也提供三種嵌入方式。

---

## 4. **使用方式**

- 你可以把 Markdown 格式複製到專案的 `README.md`，讓別人一眼看到專案的 CI/CD 狀態與測試覆蓋率。
- 如果有內部 Wiki、Confluence、或是外部網站，也可以用 HTML 格式嵌入。
- 例如放在 README.md：
    
    ```markdown
    # k8s-deploy
    [![pipeline status](http://10.2.2.3/user/k8s-deploy/badges/master/pipeline.svg)](http://10.2.2.3/user/k8s-deploy/-/commits/master)
    [![coverage report](http://10.2.2.3/user/k8s-deploy/badges/master/coverage.svg)](http://10.2.2.3/user/k8s-deploy/-/commits/master)
    
    ```

---

## ✅ Auto DevOps

[![image-1755163898687.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/jtagvnHk9QHX7gfj-image-1755163898687.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/jtagvnHk9QHX7gfj-image-1755163898687.png)

你這張圖是 GitLab **Auto DevOps** 的設定頁面，它是 GitLab 提供的一套「自動化 CI/CD Pipeline」功能，讓專案在沒有自訂 `.gitlab-ci.yml` 的情況下，也能自動完成建置、測試、部署等流程。

---

## 1. **Default to Auto DevOps pipeline**

- **啟用狀態（打勾）**  
    表示如果專案 **沒有 `.gitlab-ci.yml`**，GitLab 就會自動使用 Auto DevOps 預設流程。
- **instance enabled**  
    表示這個功能已在 GitLab 系統層級啟用，專案可以直接用。
- 如果你已經在專案內放了 `.gitlab-ci.yml`，這個設定不會生效（因為會優先用自訂 Pipeline）。

---

## 2. **提示訊息**

```
Add a Kubernetes cluster integration with a domain, or create an AUTO_DEVOPS_PLATFORM_TARGET CI variable.

```

- Auto DevOps 預設會部署到 Kubernetes，所以 GitLab 建議：
    
    
    1. **整合 Kubernetes 叢集**（在 GitLab CI/CD &gt; Clusters 中設定）
    2. 或者在 CI/CD 變數中設定 `AUTO_DEVOPS_PLATFORM_TARGET`（指定部署平台）

---

## 3. **Deployment strategy（部署策略）**

三種選項：

1. **Continuous deployment to production**（持續部署到正式環境）
    
    
    - 每次 Pipeline 成功就會自動部署到 Production。
2. **Continuous deployment to production using timed incremental rollout**（定時漸進式部署）
    
    
    - 部署會分批釋出，逐漸更新所有節點（需要 Kubernetes 支援）。
3. **Automatic deployment to staging, manual deployment to production**（自動部署到測試環境，手動部署到正式環境）
    
    
    - 先自動部署到 Staging 環境，Production 需要手動觸發。

---

## 4. **總結影響**

- 如果勾選這個設定，且專案沒有 `.gitlab-ci.yml`，就會自動跑 GitLab 預設的 Auto DevOps 流程。
- Auto DevOps 預設：
    
    
    - 會進行 **build → test → code quality → security scan → review app → deploy** 等階段
    - 預期環境是 **Kubernetes**
    - 如果沒有 K8s，就需要自行設定變數（像 `AUTO_DEVOPS_PLATFORM_TARGET`）或調整成 Docker / VM 方式

---

## ✅ Runners

[![image-1755165012676.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/nmnTFc8gP7cEPqaU-image-1755165012676.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/nmnTFc8gP7cEPqaU-image-1755165012676.png)

---

## 1. **Runners 概念**

- **Runner** 是 GitLab CI/CD 的執行代理，用來接收並執行 pipeline job。
- 可以註冊在不同機器上（VM、Docker、K8s 等）。
- 有兩種主要分類：
    
    
    - **Specific runners**（專案專用）
    - **Shared runners**（全 GitLab 共用）

---

## 2. **Specific runners**

這個區塊是**專屬於當前專案**的 Runner。  
圖片中有兩個 active 的 Runner：

<table id="bkmrk-runner-%E5%90%8D%E7%A8%B1-%E9%A1%9E%E5%9E%8B-tag-%E7%8B%80%E6%85%8B-"><thead><tr><th>Runner 名稱</th><th>類型</th><th>Tag</th><th>狀態</th></tr></thead><tbody><tr><td>\#347 (UgMp--WG)</td><td>Docker Executor</td><td>`app-docker-204`</td><td>🟢 active</td></tr><tr><td>\#343 (Mw1g8wwh)</td><td>Shell/VM Executor</td><td>`app-docker-vm-204`</td><td>🟢 active</td></tr></tbody></table>

**Tag 作用：**

- `.gitlab-ci.yml` 中 `tags: ["app-docker-204"]` 會指定 job 只能由對應的 Runner 執行。
- 如果 job 沒有 `tags`，而 Runner 又設定了「不允許 untagged job」，則不會被這個 Runner 接手。

---

## 3. **Runner 註冊方式**

左邊「Set up a specific runner for a project」提供：

- GitLab 伺服器 URL：
    
    ```
    http://10.X.X.X/
    
    ```
- 專案 Registration Token（圖片中有一段）  
    用 `gitlab-runner register` 時會用到。

---

## 4. **Shared runners**

- 當前 GitLab 沒有設定任何 Shared Runner（全域共用）。
- 「Enable shared runners for this project」是打開的，但因為 instance 沒有 shared runner，所以沒有作用。

---

## 5. **Group runners**

- 用於同一 GitLab group 底下多個專案共享 Runner。
- 目前這個專案**不屬於任何 group**，所以無法使用 group runner。

---

## 6. **運作重點**

- 你有兩台 runner：
    
    
    - `app-docker-204` → Docker executor，跑 job 時會用容器。
    - `app-docker-vm-204` → Shell executor，直接在 VM 上跑。
- 兩者可以同時存在，**但必須在 `.gitlab-ci.yml` 指定正確的 tag**，否則可能出現「no runner can run the job」的錯誤。

---

## ✅ Artifacts

![Uploaded image](https://files.oaiusercontent.com/file-VSmdSqQQ4aLHzxvEZRTbGw?se=2025-08-14T10%3A06%3A53Z&sp=r&sv=2024-08-04&sr=b&rscc=max-age%3D299%2C%20immutable%2C%20private&rscd=attachment%3B%20filename%3Da5c9340a-4243-4526-80a4-3d6fb139c9e1.png&sig=qpggpUA2RX8qugutzOAQ%2Bzu9vjr15UwAMRKI3wlNk8w%3D)

這張圖是 **GitLab 專案的 Artifact 保留設定**。

---

- **Artifacts**
    
    
    - Artifact 是 GitLab CI/CD 在 Job 結束後產生的檔案（例如：build 結果、測試報告、壓縮檔）。
    - 這些檔案可以被下載、或傳到後續的 Job 使用。
- **Keep artifacts from most recent successful jobs**（已勾選 ✅）
    
    
    - 啟用後，系統會**只保留最後一次成功的 Pipeline** 所產生的 artifact。
    - 舊的 artifact 會被自動刪掉，節省儲存空間。
    - 適合只想保留最新版本的成果（例如：最後打包的 image、最後的前端編譯檔）。
- **作用機制**
    
    
    1. 你的 pipeline 跑成功後 → Job 產生 artifact → 存下來。
    2. 下一次成功的 pipeline 出現 → GitLab 自動刪掉前一次成功的 artifact → 只留最新的。

---

### 使用情境

- 適合 **持續整合/持續部署（CI/CD）**，因為通常只需要最新的 build 產物。
- 不適合需要保留歷史版本（例如回溯除錯、審計）的專案，此時應該在 `.gitlab-ci.yml` 裡設定 `expire_in` 來控制保存期限。

---

這張圖是 **GitLab CI/CD 專案層級的 Variables 設定頁面**，主要是用來存放敏感資訊（例如密碼、金鑰、帳號等），在 Pipeline 中可以直接引用。

---

## ✅ **Variables（變數）**

### 1. **Variables（變數）**

- 存放的資料可以在 `.gitlab-ci.yml` 的 job script 中透過 `$KEY` 方式存取，例如 `$PassWd`、`$UserName`。
- 避免將敏感資料直接寫在 `.gitlab-ci.yml` 裡。

---

### 2. **欄位說明**

<table id="bkmrk-%E6%AC%84%E4%BD%8D-%E8%AA%AA%E6%98%8E-type-%E4%B8%80%E8%88%AC%E9%83%BD%E6%98%AF-vari"><thead><tr><th>欄位</th><th>說明</th></tr></thead><tbody><tr><td>**Type**</td><td>一般都是 `Variable`，也可能是 `File`（將值存成檔案再提供給 Job）。</td></tr><tr><td>**Key**</td><td>變數名稱（例如 `PassWd`、`UserName`），在 CI/CD job 中用 `$PassWd` 存取。</td></tr><tr><td>**Value**</td><td>變數值（已隱藏），只有有權限的使用者才能看到。</td></tr><tr><td>**Protected**</td><td>✅ 表示這個變數只會在 **Protected branch** 或 **Protected tag** 的 pipeline 中被注入。</td></tr><tr><td>**Masked**</td><td>✅ 代表在 Job log 中會以 `*****` 隱藏，防止外洩（必須符合 GitLab Mask 規則）。</td></tr><tr><td>**Environments**</td><td>變數適用的環境，例如 `All`、`production`、`staging` 等。</td></tr></tbody></table>

---

### 3. **圖中範例**

- **PassWd**：
    
    
    - Protected ✅
    - Masked ✅（所以在 job log 裡看不到明碼）
    - 適用於所有環境（All）。
- **UserName**：
    
    
    - Protected ✅
    - Masked ❌（會以明碼顯示在 job log，如果有 `echo $UserName` 會直接看見）。

---

### 4. **使用範例**

假設你在 `.gitlab-ci.yml` 中要用這兩個變數：

```yaml
stages:
  - test

show-vars:
  stage: test
  script:
    - echo "User: $UserName"
    - echo "Password: $PassWd"

```

- 如果 **Masked** 是 ✅ → job log 會看到 `Password: *****`
- 如果 **Masked** 是 ❌ → job log 會看到明碼 `Password: mypassword`

---

在 **GitLab** 裡，

**Protected branch**（受保護分支） 和 **Protected tag**（受保護標籤）  
是用來防止重要分支或標籤被隨意修改、刪除、或由沒有權限的人 push 的安全機制。

---

## Protected branch（受保護分支） 和 Protected tag （受保護標籤）

## 1. **Protected branch（受保護分支）**

### 主要特點：

- 只能特定角色（例如 Maintainer、Owner）**push** 及 **merge**。
- 一般用來保護 **主要分支**（例如 `master`、`main`、`release`）。
- 通常會搭配 **Code Review / Merge Request** 流程使用，避免直接修改。
- 也可以設定 **誰可以直接 push**、**誰只能 merge**。

**範例情境**

- `master` 分支設為 Protected → 只有 Maintainer 能 merge 到 `master`。
- Developer 想要改 `master`，必須透過 **Merge Request** 並經過審核。

---

## 2. **Protected tag（受保護標籤）**

### 主要特點：

- 限制誰可以建立或刪除特定的 Tag。
- 通常用來保護 **版本發布標籤**（例如 `v1.0.0`）。
- 避免有人隨便重建同名 Tag 導致部署混亂。

**範例情境**

- `v*` 標籤設為 Protected → 只有 Maintainer 可以建立 `v1.2.0` 這類 Tag。
- 防止 Developer 私自發佈或覆蓋版本。

---

## 3. **為什麼與 CI/CD Variables 有關**

當你在 GitLab CI/CD 中將變數設定為 **Protected = ✅** 時：

- 這個變數 **只會注入** 到針對 **Protected branch** 或 **Protected tag** 觸發的 Pipeline。
- 如果在非受保護分支（例如 feature/test）觸發 Pipeline，變數不會被注入，避免敏感資料外洩。

**舉例**

```yaml
deploy:
  stage: deploy
  script:
    - echo $PROD_API_KEY

```

- 如果 `$PROD_API_KEY` 是 Protected ✅：
    
    
    - 在 `master`（Protected branch） 觸發 → 可以用。
    - 在 `dev`（非 Protected branch） 觸發 → 不會注入，變成空值。

---

這是 **Protected Branch / Tag 與 CI/CD 變數注入邏輯對照表**：

<table id="bkmrk-pipeline-%E8%A7%B8%E7%99%BC%E4%BE%86%E6%BA%90-%E8%AE%8A%E6%95%B8-pro"><thead><tr><th>**Pipeline 觸發來源**</th><th>**變數 Protected 設定**</th><th>**變數是否注入**</th><th>**說明**</th></tr></thead><tbody><tr><td>Protected branch（例如 `master`）</td><td>✅ Protected</td><td>✅ 注入</td><td>變數只對受保護分支生效，符合條件可使用</td></tr><tr><td>非 Protected branch（例如 `feature/test`）</td><td>✅ Protected</td><td>❌ 不注入</td><td>變數不會被注入，避免敏感資料外洩</td></tr><tr><td>Protected tag（例如 `v1.0.0`）</td><td>✅ Protected</td><td>✅ 注入</td><td>變數會注入到受保護標籤的 Pipeline</td></tr><tr><td>非 Protected tag（例如 `test-1`）</td><td>✅ Protected</td><td>❌ 不注入</td><td>變數不會被注入</td></tr><tr><td>任意 branch/tag</td><td>❌ 未 Protected</td><td>✅ 注入</td><td>不受限制，所有 Pipeline 都能用變數</td></tr></tbody></table>

---

💡 **建議用法**

- **部署金鑰 / 密碼 / Token** → 設為 Protected ✅，避免在測試分支外洩。
- **測試環境參數** → 可以不 Protected ❌，讓所有分支都能測試。

---

## ✅ Pipeline triggers

[![image-1755166426157.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/Ebb7N5JEB8Q5SCDZ-image-1755166426157.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/Ebb7N5JEB8Q5SCDZ-image-1755166426157.png)

你這張圖是 **GitLab Pipeline Trigger** 設定頁面，它的用途是建立一組 **Trigger Token**，讓外部系統或腳本可以直接觸發專案的 CI/CD Pipeline，而不用透過 Git push 或 Merge Request。

---

### 1. **Trigger Token**

- 這是一組唯一的金鑰（圖中 `glptt-49dbb6d...`）。
- 持有這個 Token 的人，就能用 API 或 Webhook 觸發該專案的 Pipeline。
- 權限等同於建立它的帳號，但只能用來觸發 Pipeline，不能直接讀取或修改程式碼。

---

### 2. **觸發方式**

#### **(1) 用 `cURL`**

```bash
curl -X POST --fail \
  -F token=YOUR_TOKEN \
  -F ref=分支或tag名稱 \
  http://10.10.10.10/api/v4/projects/2406/trigger/pipeline

```

- `ref`：要在哪個 branch 或 tag 執行 Pipeline，例如 `master` 或 `release-v1.0`。

#### **(2) 在 `.gitlab-ci.yml` 裡呼叫**

```yaml
script:
  - "curl -X POST --fail -F token=YOUR_TOKEN -F ref=master http://10.10.10.10/api/v4/projects/2406/trigger/pipeline"

```

#### **(3) 用 Webhook 直接 GET**

```
http://10.10.10.10/api/v4/projects/2406/ref/master/trigger/pipeline?token=YOUR_TOKEN

```

- 適合用在簡單的 HTTP 呼叫（像是 Jenkins、監控系統、外部自動化工具）。

---

### 3. **傳入 Pipeline 變數**

可以讓外部觸發時同時傳遞變數：

```bash
curl -X POST --fail \
  -F token=YOUR_TOKEN \
  -F ref=master \
  -F "variables[RUN_NIGHTLY_BUILD]=true" \
  http://10.10.10.10/api/v4/projects/2406/trigger/pipeline

```

- 這樣在 `.gitlab-ci.yml` 中可以用 `$RUN_NIGHTLY_BUILD` 判斷。

---

### 4. **使用場景**

- **定時觸發**：配合 cron job，每天固定時間呼叫 API。
- **外部事件觸發**：例如其他系統部署完成後自動通知 GitLab 進行後續工作。
- **跨專案觸發**：一個專案完成後，透過 Token 觸發另一個專案的 Pipeline。

---

💡 建議：

- **不要把 Trigger Token 放在公開的地方**（例如公開 repo）。
- 如果 Token 洩漏，任何人都可以觸發你的 Pipeline（甚至傳遞惡意變數）。
- 若外部要用，最好配合 **Protected Branch** 來限制觸發範圍。

---

## ✅ Deploy freezes（部署凍結）

[![image-1755166820184.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/BlhkH9dPEtm8Al6e-image-1755166820184.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/BlhkH9dPEtm8Al6e-image-1755166820184.png)

你這張圖是 **GitLab 的 Deploy freezes**（部署凍結）設定頁面，用來在特定時間區間**禁止部署**，避免在敏感時段（例如假日、系統維護時間、尖峰時段）誤發佈程式。

---

## **圖中設定解讀**

- **Freeze start**：`10 1 * * *`  
    代表每天 **01:10** 開始凍結部署（Cron 語法：`分 時 日 月 星期`）。
- **Freeze end**：`10 3 * * *`  
    代表每天 **03:10** 結束凍結部署。
- **Time zone**：`[UTC +8] Taipei`  
    表示時間以台北時區計算。

---

## **功能運作方式**

1. 這裡的時間範圍只是**定義凍結時段**。
2. 要生效，必須在 `.gitlab-ci.yml` 的部署 job 中加上：
    
    ```yaml
    deploy_job:
      stage: deploy
      script:
        - echo "Deploying..."
      rules:
        - if: $CI_DEPLOY_FREEZE == "true"
          when: never
        - when: on_success
    
    ```
    
    或使用 GitLab 內建的 [`deploy_freeze` 規則](https://docs.gitlab.com/ee/ci/environments/deploy_freeze.html)。
3. 凍結期間，對應的部署 job 會直接跳過，不會觸發。

---

## **使用場景**

- 避免在系統流量高峰期誤佈署造成中斷。
- 避免在假日、深夜維護窗口外誤觸部署。
- 防止新人誤觸 Pipeline 造成上線事故。

---

## ✅ Token Access

[![image-1755167118948.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/scaled-1680-/HpdWXnxljbXyYfiM-image-1755167118948.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-08/HpdWXnxljbXyYfiM-image-1755167118948.png)

這是 GitLab 專案設定裡的 **Token Access（CI\_JOB\_TOKEN 存取控制）** 功能，主要用來限制 CI/CD 任務中 **CI\_JOB\_TOKEN** 變數可以存取的其他專案，避免被濫用。

---

- **Limit CI\_JOB\_TOKEN access**（開關）
    
    
    - 預設關閉（灰色），代表 **任何專案** 都能被這個專案的 CI\_JOB\_TOKEN 存取。
    - 開啟後（藍色），就可以指定**只有被列入清單的專案**能被存取。
- **Add an existing project to the scope**
    
    
    - 在這裡輸入 GitLab 專案路徑（例如：`gitlab-org/gitlab`），將其加入允許清單。
    - 這樣 CI\_JOB\_TOKEN 就只能存取清單內的專案 API。
- **Projects that can be accessed**
    
    
    - 圖中顯示的 `Live K8s Deploy` 就是允許被目前專案的 CI\_JOB\_TOKEN 直接存取的另一個專案。

---

## **用途**

1. **跨專案存取 API**  
    在 CI/CD pipeline 中，可能需要用 `curl` 或 `wget` 呼叫其他 GitLab 專案的 API，例如下載 artifact、觸發 pipeline。
2. **限制安全風險**  
    如果沒有啟用限制，惡意程式碼可以利用 CI\_JOB\_TOKEN 存取同一 GitLab instance 中**所有公開或有權限的專案**。
3. **最佳實務**
    
    
    - 如果專案需要 CI\_JOB\_TOKEN 存取別的專案 → 開啟並只加允許的專案。
    - 如果專案不需要跨專案存取 → 建議直接開啟限制並不加任何專案。

---

### **範例：跨專案下載 Artifact**

假設專案 A 的 pipeline 要下載專案 B 的最新 build：

```yaml
script:
  - curl --header "JOB-TOKEN: $CI_JOB_TOKEN" \
         "https://gitlab.example.com/api/v4/projects/123/jobs/artifacts/main/download?job=build" \
         -o artifact.zip

```

如果你啟用了限制，就必須在專案 B 裡把專案 A 加進「Projects that can be accessed」清單，否則會 `403 Forbidden`。

---

# 【GitLab】使用ssh clone 專案

以下是在 GitLab 使用 **SSH** 方式 `clone` 專案的完整步驟，假設你的 GitLab 伺服器是公司內部或自架的，也適用：

---

## 1️⃣ 產生並設定 SSH Key

1. **建立 SSH Key**
    
    ```bash
    ssh-keygen 
    
    ```
    
    
    - 出現檔案路徑提示時直接按 **Enter** (預設<span class="s1">~/.ssh/</span><span class="s1">id\_rsa.pub)</span>
2. - 登入 GitLab → 右上角 **使用者頭像** → **Preferences** → **SSH Keys**。
    - 貼上 <span class="s1">~/.ssh/</span><span class="s1">id\_rsa.pub</span> 內容。

---

## 2️⃣ 測試連線

確認 GitLab 可透過 SSH 連線：

```bash
ssh -T git@your.gitlab.host

```

- 第一次會詢問 `Are you sure you want to continue connecting (yes/no)?` → 輸入 `yes`。
- 成功訊息：`Welcome to GitLab, @你的帳號!`

---

## 3️⃣ 取得專案的 SSH URL

在 GitLab 專案頁面：

- 點選 **Code** → **Clone** → 選 **SSH**。
- 範例 URL：
    
    ```
    git@gitlab.example.com:group-name/project-name.git
    
    ```

---

## 4️⃣ 執行 Clone

```bash
git clone git@gitlab.example.com:group-name/project-name.git

```

- 會將程式碼拉到目前目錄下的 `project-name` 資料夾。

---

## 5️⃣ 常見問題排查

<table id="bkmrk-%E5%95%8F%E9%A1%8C-%E6%AA%A2%E6%9F%A5%E6%96%B9%E5%BC%8F-permission-d"><thead><tr><th>問題</th><th>檢查方式</th></tr></thead><tbody><tr><td>**Permission denied (publickey)**</td><td>確認 `ssh-agent` 有加入金鑰：`ssh-add -l`</td></tr><tr><td>**找不到 Host**</td><td>檢查網路、防火牆，或在 `~/.ssh/config` 加入 Host 設定</td></tr><tr><td>**公司內網 GitLab**</td><td>需確保內部 DNS 或 VPN 正確</td></tr></tbody></table>

---

### 範例 `.ssh/config`（可簡化命令）

```text
Host gitlab-company
    HostName gitlab.example.com
    User git
    IdentityFile ~/.ssh/id_ed25519

```

然後可用：

```bash
git clone git@gitlab-company:group-name/project-name.git

```

---

以上步驟完成後，你就能用 SSH 方式安全地 `git clone` GitLab 專案並進行後續開發。

# 【GitHub】相關

# 【Git】Github 新增專案檔案

### create a new repository on the command line

```JavaScript
echo "# sample_proj" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/treemanou/sample_proj.git
git push -u origin main
```

### push an existing repository from the command line

<div id="bkmrk-"><div></div></div>```shell
git remote add origin https://github.com/treemanou/sample_proj.git
git branch -M main
git push -u origin main
```

# 【Git】解決github項目體積過大的問題

首先查找出大文件

$ git rev-list --objects --all <span class="hljs-params">| grep "$(git verify-pack -v .git/objects/pack/\*.idx |</span> sort -k <span class="hljs-number">3</span> -n <span class="hljs-params">| tail -5 |</span> awk <span class="hljs-string">'{print$1}'</span>)<span class="hljs-string">"</span>

> –objects：列出該提交涉及的所有文件ID。  
>   
> –all：所有分支的提交，相當於指定了位於/refs下的所有引用。  
>   
> verify-pack命令用於顯示已打包的內容，我們用它來找到那些大文件。  
>   
> -v（verbose）參數是打印詳細信息。

前面我們通過rev-list得到了文件名-ID的對應關係，通過verify-pack得到了最大的5個文件ID。 用後者篩選前者便能得到最大的5個文件的文件名，比如：

$ git rev-<span class="hljs-built_in">list</span> --objects --all | grep <span class="hljs-string">"$(git verify-pack -v .git/objects/pack/\*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"</span> b96695fd6e62fff41723ab324368da394f3a504e package-lock.json <span class="hljs-number">76</span>ec4d5d5fd79da922b322d53fbd1298dc128dd3 node\_modules/acorn/dist/acorn.mjs.<span class="hljs-built_in">map</span> <span class="hljs-number">286</span>dc92a240dac765d81dfbda451fd19f9611e84 node\_modules/ajv/dist/ajv.bundle.js d2cc86eb230313af9bbdbd3faed7f8e7c7247788 node\_modules/source-<span class="hljs-built_in">map</span>-support/node\_modules/source-<span class="hljs-built_in">map</span>/dist/source-<span class="hljs-built_in">map</span>.min.js.<span class="hljs-built_in">map</span> aad0620d70e16717ec338fc1d332279739e0d97c node\_modules/terser/node\_modules/source-<span class="hljs-built_in">map</span>/dist/source-<span class="hljs-built_in">map</span>.debug.js

然後我們需要刪除他們

```shell
$ git filter-branch --force --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch YOU-FILE-NAME' --tag-name-filter cat -- --all
```

> <span class="bash">filter-branch命令可以用來重寫Git倉庫中的提交  
>   
> –index-filter參數用來指定一條Bash命令，然後Git會檢出（checkout）所有的提交， 執行該命令，然後重新提交。  
>   
> –all參數表示我們需要重寫所有分支（或引用）。  
>   
> YOU-FILE-NAME 你查找出來的大文件名字</span>

重複幾次，直到大文件全部刪除完畢。

如果你確定某一個文件夾下面都不是你需要的，那麼你可以直接刪除整個文件夾，比如：

<span class="hljs-meta">$</span><span class="bash"> git filter-branch --force --prune-empty --index-filter <span class="hljs-string">'git rm -rf --cached --ignore-unmatch node\_modules'</span> --tag-name-filter cat -- --all</span>

完成後，以強制覆蓋的方式推送你的repo, 命令如下：

<span class="hljs-meta">$</span><span class="bash"> git push --force --all</span>

<span class="bash">最後，雖然上面我們已經刪除了文件, 但是我們的repo裡面仍然保留了這些objects, 等待垃圾回收(GC), 所以我們要用命令徹底清除它, 並收回空間，命令如下:</span>

```
$ rm -rf .git/refs/original/ 
$ git reflog expire --expire=now --all 
$ git gc --prune=now 

Enumerating objects: 116, done. 
Counting objects: 100% (116/116), done. 
Delta compression using up to 4 threads Compressing objects: 100% (53/53), done. Writing objects: 100% (116/116), done. 
Total 116 (delta 55), reused 116 (delta 55)
```

<div data-lang="shell" id="bkmrk-%24-git-push---force---0"><div><div data-lang="hljs shell"><textarea style="display: none;">$ git push --force --all </textarea></div></div></div>

# 【Git】語法觀念

# 【Ｇit】相關連結

github 線上教學  
[GitHub Learning Lab](https://lab.github.com/)

[Git基本原理介绍 | Escape (escapelife.site)](https://www.escapelife.site/posts/da89563c.html)

[Learn Git Branching (gitee.io)](https://oschina.gitee.io/learn-git-branching/)

[Git基本原理介绍 | Escape (escapelife.site)](https://www.escapelife.site/posts/da89563c.html#toc-heading-1)

# 【Git】還原相關

以下是關於 Git 還原（恢復）功能的完整說明

## Git 還原功能總表

<table id="bkmrk-%E6%8C%87%E4%BB%A4-%E9%82%84%E5%8E%9F%E7%AF%84%E5%9C%8D-%E6%98%AF%E5%90%A6%E6%9C%83%E5%88%AA%E9%99%A4%E6%9A%AB%E5%AD%98%E5%8D%80-%E6%98%AF%E5%90%A6%E6%9C%83"><thead><tr><th>指令</th><th>還原範圍</th><th>是否會刪除暫存區</th><th>是否會刪除未追蹤檔案</th><th>適用情境</th></tr></thead><tbody><tr><td>`git checkout .`</td><td>工作目錄</td><td>否</td><td>否</td><td>還原已追蹤檔案</td></tr><tr><td>`git restore .`</td><td>工作目錄</td><td>否</td><td>否</td><td>推薦用法，等同於 checkout .</td></tr><tr><td>`git restore --staged`</td><td>暫存區</td><td>是</td><td>否</td><td>還原 `git add` 的檔案</td></tr><tr><td>`git reset`</td><td>暫存區</td><td>是</td><td>否</td><td>退回 staging 區</td></tr><tr><td>`git reset --hard HEAD`</td><td>工作目錄 + 暫存區</td><td>是</td><td>是（若搭配 clean）</td><td>徹底放棄所有變更（⚠危險）</td></tr><tr><td>`git clean -fd`</td><td>未追蹤檔案/資料夾</td><td>無關</td><td>是</td><td>清空未納入 Git 管控的檔案</td></tr><tr><td>`git revert <commit>`</td><td>版本歷史</td><td>無關</td><td>否</td><td>安全回復 commit（保留紀錄）</td></tr></tbody></table>

---

# Git 還原功能完整整理與使用說明

在日常開發中，Git 提供多種指令用來還原工作目錄、暫存區，或是回復錯誤的 commit。根據不同的需求，選擇合適的還原方式可避免資料遺失或版本混亂。

---

## 1. `git checkout .`

還原所有「已追蹤檔案」回到最新 commit 的狀態。

- **作用範圍**：僅限工作目錄（Working Directory）
- **不會還原**：未追蹤（untracked）檔案與暫存區（Staging Area）

### 適用情境：

- 編輯了多個檔案，想快速放棄變更，但未進行 `git add`。

---

## 2. `git restore`（推薦用法）

`git restore` 是 Git 2.23+ 提供的新指令，取代過去 `checkout` 的混用狀況。

### 範例：

```bash
git restore .                # 還原所有檔案
git restore path/to/file.js # 還原單一檔案

```

- 與 `git checkout .` 行為一致，語意更清楚
- 可搭配 `--staged` 還原暫存區（如下）

---

## 3. `git restore --staged`

還原暫存區的內容，讓已 `git add` 的檔案退回至工作目錄狀態。

```bash
git restore --staged path/to/file.js

```

- **不會影響工作區的內容**
- 常用於誤將檔案加入 staging 區的情況

---

## 4. `git reset`

還原 staging 區（暫存區），讓所有已 `git add` 的檔案回到修改中狀態。

```bash
git reset

```

- 與 `git restore --staged` 功能類似
- 不會更動工作目錄內容

---

## 5. `git reset --hard HEAD`

將 **工作目錄與暫存區** 都還原成最近一次提交（HEAD）的狀態。

```bash
git reset --hard HEAD

```

- **非常危險**，會清除未提交的所有變更
- 包含 `git add` 過的內容也會消失

### 適用情境：

- 變更混亂，想完全清空所有未提交的東西

---

## 6. `git clean -fd`

清除所有「未被追蹤的檔案與資料夾」。

```bash
git clean -fd

```

- `-f`：強制執行
- `-d`：包括未追蹤的資料夾

### 適用情境：

- 清除 build 檔案或不小心留下的臨時檔案

---

## 7. `git revert`

對某個已提交的 commit 產生一個「反向操作的 commit」。

```bash
git revert <commit_hash>

```

- 保留歷史紀錄
- 安全的還原方式，尤其在多人開發時

### 適用情境：

- 上線後發現某次 commit 有錯誤，需在版本歷史中保留修正記錄

---

# 【Git】TorotoiseGit 占用資源解決方法

[![image-1637655094637.png](https://bookstack.treemanou.com/uploads/images/gallery/2021-11/scaled-1680-/zStYJGsH1yNVYwL1-image-1637655094637.png)](https://bookstack.treemanou.com/uploads/images/gallery/2021-11/zStYJGsH1yNVYwL1-image-1637655094637.png)

#### 【原因】 TorotoiseGit 會掃描硬碟內有git 版控資料夾的的異動，並產生相對應的圖示，有時會造成主機磁碟讀寫過高

[![image-1615526155670.png](http://bookstack/uploads/images/gallery/2021-03/scaled-1680-/image-1615526155670.png)](http://bookstack/uploads/images/gallery/2021-03/image-1615526155670.png)

#### 【解決】限縮掃描範圍

##### 任一個資料夾按右鍵-&gt;【TortoiseGit】-&gt;【setting】

[![image-1637655137735.png](https://bookstack.treemanou.com/uploads/images/gallery/2021-11/scaled-1680-/Db1UzjkJVnPu00ig-image-1637655137735.png)](https://bookstack.treemanou.com/uploads/images/gallery/2021-11/Db1UzjkJVnPu00ig-image-1637655137735.png)

[![image-1637655158072.png](https://bookstack.treemanou.com/uploads/images/gallery/2021-11/scaled-1680-/AOJj9ie0iWquXI26-image-1637655158072.png)](https://bookstack.treemanou.com/uploads/images/gallery/2021-11/AOJj9ie0iWquXI26-image-1637655158072.png)

Exclude paths: 將本機的每一個磁碟加入

Include paths: 將有用到git的目錄加入

# 【Git】windows系統 warning: LF will be replaced by CRLF in

## windows系統下使用git出現：warning: LF will be replaced by CRLF in

```shell
$ git add readme.txt readme.txt
warning: LF will be replaced by CRLF in learngit/readme.txt.
The file will have its original line endings in your working directory
```

原因是windows系統的換行符和Unix下的換行符不同，但是git會幫我們自動進行換行符的轉換，所以出現了這個warning

```

# 解決的方法就是，禁止自動轉換：
$git config –-global core.autocrlf false

```

[https://noelhsiao.medium.com/%E9%81%87%E5%88%B0-warning-lf-will-be-replaced-by-crlf-in-%E8%A9%B2%E6%80%8E%E9%BA%BC%E8%A7%A3-ab9882ee07be](https://noelhsiao.medium.com/%E9%81%87%E5%88%B0-warning-lf-will-be-replaced-by-crlf-in-%E8%A9%B2%E6%80%8E%E9%BA%BC%E8%A7%A3-ab9882ee07be)

# 【Git】checkout 與 switch 比較

我來幫你整理 `git switch` 和 `git checkout` 的差異，並用表格讓你快速比較。

---

## 1️⃣ 歷史背景

- **`git checkout`** 是 Git 早期就有的指令，功能很多，但語意太複雜，既可以切換分支、也可以檢出檔案，容易讓初學者混淆。
- **`git switch`**（Git 2.23 之後加入）是為了讓「切換分支」這個動作更直觀而出現的，專注於分支切換，不負責檔案檢出。

---

## 2️⃣ 功能比較表

<table id="bkmrk-%E5%8A%9F%E8%83%BD-git-checkout-git-"><thead><tr><th>功能</th><th>`git checkout`</th><th>`git switch`</th></tr></thead><tbody><tr><td>切換到已存在分支</td><td>✅ `git checkout branch_name`</td><td>✅ `git switch branch_name`</td></tr><tr><td>建立並切換到新分支</td><td>✅ `git checkout -b new_branch`</td><td>✅ `git switch -c new_branch`</td></tr><tr><td>檢出特定 commit / tag</td><td>✅ `git checkout commit_hash`</td><td>❌ 不支援</td></tr><tr><td>檢出特定檔案版本</td><td>✅ `git checkout commit_hash -- file.txt`</td><td>❌ 不支援</td></tr><tr><td>語意單純性</td><td>❌ 功能太多易混淆</td><td>✅ 專注於切換分支</td></tr><tr><td>推薦使用情境</td><td>老專案 / 習慣舊指令</td><td>新專案 / 想簡化操作</td></tr></tbody></table>

---

## 3️⃣ 使用範例

### 切換到現有分支

```bash
# 傳統方式
git checkout dev

# 新方式（推薦）
git switch dev

```

---

### 建立並切換到新分支

```bash
# 傳統方式
git checkout -b feature/login
# 新方式（推薦）
git switch -c feature/login

```

---

## 4️⃣ 為什麼官方推薦 `git switch`

- 讓指令語意更明確，不再把「檔案檢出」和「分支切換」混在一起。
- 搭配 `git restore`（處理檔案版本還原）更符合直覺：
    
    
    - `git switch` → 專注切換分支
    - `git restore` → 還原檔案內容

---

💡 **簡單記**：

- **切換分支** → 用 `git switch`
- **還原檔案** → 用 `git restore`
- **需要舊版本的行為** → 用 `git checkout`

---

太好了！以下是 `git switch` 和 `git restore` 如何取代舊指令 `git checkout` 的**完整對照表**，讓你更容易理解並開始使用新指令：

---

## ✅ `git checkout` 對照表：對應的新指令用法

<table id="bkmrk-%E5%8E%9F%E6%9C%AC-git-checkout-%E7%94%A8%E6%B3%95-%E5%B0%8D"><thead><tr><th>原本 `git checkout` 用法</th><th>對應的新指令</th><th>功能說明</th></tr></thead><tbody><tr><td>`git checkout <branch>`</td><td>`git switch <branch>`</td><td>切換到已存在的分支</td></tr><tr><td>`git checkout -b <new-branch>`</td><td>`git switch -c <new-branch>`</td><td>建立並切換到新分支</td></tr><tr><td>`git checkout <commit>`</td><td>❌（建議保留 `checkout`）</td><td>切換到某個 commit 狀態（暫不取代）</td></tr><tr><td>`git checkout <file>`</td><td>`git restore <file>`</td><td>還原工作區中的單一檔案</td></tr><tr><td>`git checkout -- <file>`</td><td>`git restore <file>`</td><td>同上</td></tr><tr><td>`git checkout <commit> -- <file>`</td><td>`git restore --source <commit> <file>`</td><td>還原某個 commit 中的檔案</td></tr><tr><td>`git checkout .`</td><td>`git restore .`</td><td>還原所有工作區的檔案</td></tr><tr><td>`git checkout HEAD <file>`</td><td>`git restore --source=HEAD <file>`</td><td>還原最新 commit 的某個檔案</td></tr><tr><td>`git checkout HEAD^`</td><td>❌（建議保留 `checkout`）</td><td>切換到上一個 commit（建議保留舊指令）</td></tr></tbody></table>

---

##  

## 💡 建議做法

<table id="bkmrk-%E4%BB%BB%E5%8B%99-%E5%BB%BA%E8%AD%B0%E7%94%A8%E6%B3%95-%E5%88%87%E6%8F%9B%E5%88%86%E6%94%AF-git-swi"><thead><tr><th>任務</th><th>建議用法</th></tr></thead><tbody><tr><td>切換分支</td><td>`git switch dev`</td></tr><tr><td>建新分支並切過去</td><td>`git switch -c feature/new-ui`</td></tr><tr><td>還原單一檔案</td><td>`git restore src/App.tsx`</td></tr><tr><td>還原某 commit 的檔案</td><td>`git restore --source=abc123 file.txt`</td></tr><tr><td>還原整個工作目錄</td><td>`git restore .`</td></tr></tbody></table>

---

## 🚀 Git 版本要求

- `git switch` 和 `git restore` 需 Git **2.23 或以上**
- 使用 `git --version` 檢查目前版本

---

# 【Git】HEAD 是什麼

當然可以！`HEAD` 是 Git 中最核心也是最常見的概念之一，很多 Git 指令（如 `reset`、`checkout`、`rebase`）都與它有關。下面我會從簡單到深入，幫你完整說明：

---

## 一句話理解 HEAD 是什麼：

> **`HEAD` 是你目前所在的分支的「指標」，也就是「你現在工作的 commit」位置。**

---

## 基本概念圖：

假設你有三個 commit：

```
A → B → C
        ↑
      HEAD

```

- 這表示目前 `HEAD` 指向 commit C
- 如果你新增一個 commit，會變成：

```
A → B → C → D
              ↑
            HEAD

```

##  HEAD 的 3 種狀態：

<table id="bkmrk-%E7%8B%80%E6%85%8B%E9%A1%9E%E5%9E%8B-%E8%AA%AA%E6%98%8E-head-%E2%86%92-%E5%88%86%E6%94%AF%E5%90%8D%E7%A8%B1%EF%BC%88"><thead><tr><th>狀態類型</th><th>說明</th></tr></thead><tbody><tr><td>`HEAD` → 分支名稱（正常狀態）</td><td>你在某個分支上（例如 `main`、`dev`）</td></tr><tr><td>`HEAD` → commit（detached）</td><td>你處於「分離 HEAD 狀態」（Detached HEAD）</td></tr><tr><td>`HEAD` → 其他指標</td><td>比如在 Rebase、Merge 中會指向暫存區域</td></tr></tbody></table>

---

## ✅ 常見用途說明

### 1. `HEAD` 是你目前的開發位置

當你執行：

```bash
git commit

```

其實 Git 是「把你的 commit 接在 `HEAD` 指向的 commit 後面」。

---

### 2. `HEAD` 是 Git 重設的基準點

```bash
git reset --hard HEAD

```

這表示：**把你的暫存區與工作目錄都還原成目前這個 commit 的狀態。**

---

### 3. `HEAD^`、`HEAD~1`：往上移動一個 commit

<table id="bkmrk-%E8%A1%A8%E7%A4%BA%E6%B3%95-%E6%84%8F%E6%80%9D-head-%E7%8F%BE%E5%9C%A8%E9%80%99%E5%80%8B-com"><thead><tr><th>表示法</th><th>意思</th></tr></thead><tbody><tr><td>`HEAD`</td><td>現在這個 commit</td></tr><tr><td>`HEAD^`</td><td>上一個 commit（parent）</td></tr><tr><td>`HEAD~1`</td><td>同 `HEAD^`</td></tr><tr><td>`HEAD~2`</td><td>往上兩個 commit</td></tr></tbody></table>

---

## 🚨 Detached HEAD（分離 HEAD 狀態）

當你執行：

```bash
git checkout <某個 commit 哈希>

```

你會進入「Detached HEAD」狀態：

```
A → B → C
        ↑
     (HEAD)

```

你不再是在某個分支上，而是「直接指向某個 commit」。這時如果你做出 commit，它們會變成「懸空」，容易被 Git 回收（垃圾回收）。

所以如果你看到這樣的訊息：

```
You are in 'detached HEAD' state.

```

請小心：最好先建立分支來保存變更：

```bash
git checkout -b temp-branch

```

---

## 🧭 總結：HEAD 是 Git 的當前位置指標

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E7%94%A8%E9%80%94%E8%AA%AA%E6%98%8E-%E8%A1%A8%E7%A4%BA%E7%9B%AE%E5%89%8D-commit-"><thead><tr><th>功能</th><th>用途說明</th></tr></thead><tbody><tr><td>表示目前 commit</td><td>所有操作（commit、reset）都以它為基礎</td></tr><tr><td>指向當前分支</td><td>預設情況 HEAD 是指向你當前分支</td></tr><tr><td>變更分支 = 移動 HEAD</td><td>切分支就是改變 HEAD 的位置</td></tr><tr><td>可用於版本回朔、還原</td><td>`HEAD^`、`HEAD~2` 常見於 reset、diff 等</td></tr><tr><td>被分離 = Detached HEAD</td><td>指向 commit 而非分支，要小心變更遺失</td></tr></tbody></table>

---

如果你想要我畫一張圖示 `HEAD` 與分支移動的流程圖，也可以說一聲，我能幫你生成視覺化版本！是否需要？

##   
**為什麼我們要「移動 HEAD」？**

簡單來說：

> **移動 HEAD = 改變我們正在「看的版本」、「工作的版本」、或「建立新歷史的起點」。**

Git 是一個版本控制系統，HEAD 就像「目前播放的時間軸指標」，移動它可以讓你做版本切換、回朔、修正、實驗開發等動作。

---

## ✅ 移動 HEAD 的常見目的（整理成表）

<table id="bkmrk-%E7%94%A8%E9%80%94%2F%E7%9B%AE%E7%9A%84-%E8%AA%AA%E6%98%8E-%F0%9F%94%84-%E5%88%87%E6%8F%9B%E5%88%86%E6%94%AF%E6%88%96%E7%89%88%E6%9C%AC-%E6%94%B9"><thead><tr><th>用途/目的</th><th>說明</th></tr></thead><tbody><tr><td>🔄 **切換分支或版本**</td><td>改變目前正在編輯/開發的版本，比如 `git switch dev`（切換 HEAD 到 dev）</td></tr><tr><td>🧭 **回朔 commit**</td><td>想把 HEAD 拉回到前一版：`git reset --hard HEAD^`</td></tr><tr><td>🧪 **版本測試或除錯某一版**</td><td>用 `git checkout <commit>` 移動到某個版本試驗、測試（會進入 detached HEAD）</td></tr><tr><td>🧹 **清除或重建 commit 歷史**</td><td>用 `git reset` 或 `rebase` 改變 HEAD 來整理版本歷史</td></tr><tr><td>🧱 **建立新的分支開發點**</td><td>移動 HEAD 到 commit 後再用 `git switch -c new-branch` 開分支</td></tr><tr><td>🔥 **放棄變更並回到穩定版本**</td><td>移動 HEAD 到穩定 commit 位置，還原所有內容</td></tr></tbody></table>

---

##  舉幾個具體例子

---

### 1️⃣ 切換分支（背後其實是移動 HEAD）

```bash
git switch dev

```

> 📌 HEAD 從 main → dev，代表你現在工作的是 dev 的最新 commit。

---

### 2️⃣ 回到前一版（移動 HEAD 到上一個 commit）

```bash
git reset --hard HEAD^

```

> 📌 HEAD 往回一格，捨棄最近一次的提交內容（危險但有用）。

---

### 3️⃣ 想測試舊版功能（Detached HEAD 狀態）

```bash
git checkout abc1234

```

> 📌 HEAD 不再指向分支，而是指向一個 commit，你進入「Detached HEAD」狀態，可以安全看舊版本。

---

### 4️⃣ 切 HEAD + 建立新分支（從中間版本開新分支）

```bash
git checkout abc1234
git switch -c fix-login

```

> 📌 移動 HEAD 到舊版，再基於此版本開新分支，進行 hotfix。

---

##  把 HEAD 當「播放頭」的比喻

想像 Git 是個影片編輯器：

- commit 是一個個時間點的「場景」
- HEAD 是播放頭
- 你移動 HEAD，就等於跳到那一幕開始剪接

這樣你就能理解：

- reset 是「退回到前一段」
- rebase 是「換剪接順序」
- checkout 是「跳到某個片段」
- commit 是「加入新片段」

---

## ✅ 小結：為什麼要移動 HEAD？

<table id="bkmrk-%E7%9B%AE%E7%9A%84-%E8%A7%A3%E9%87%8B-%F0%9F%A7%AD-%E5%88%87%E6%8F%9B%E4%BD%A0%E6%AD%A3%E5%9C%A8%E5%B7%A5%E4%BD%9C%E7%9A%84%E7%89%88%E6%9C%AC-%E5%9B%A0"><thead><tr><th>目的</th><th>解釋</th></tr></thead><tbody><tr><td>🧭 切換你正在工作的版本</td><td>因為 HEAD 代表你「現在的位置」</td></tr><tr><td>🔙 回到過去版本</td><td>移動 HEAD 讓你回到某個穩定 commit</td></tr><tr><td>🧪 實驗新東西 / 修 bug</td><td>從舊 commit 移動 HEAD，開新分支來做測試與修復</td></tr><tr><td>🔧 重構 Git 歷史</td><td>通過 reset 或 rebase 移動 HEAD 重寫 commit 歷史</td></tr></tbody></table>

---

這是個很關鍵的 Git 概念問題！  
當你的 `HEAD` **不在任何分支上，而是直接指向某個 commit**，這個狀態稱為：

---

## ⚠️ **Detached HEAD**（分離 HEAD 狀態）

---

## ✅ 什麼是 Detached HEAD？

正常情況：

```
HEAD → main → C

```

Detached HEAD 狀況：

```
HEAD → C  （但不是任何分支）

```

你直接「跳到一個 commit」，例如透過：

```bash
git checkout abc1234

```

或：

```bash
git switch --detach abc1234

```

這時你就**脫離了任何分支的控制**，你在 **那個 commit 上自由工作**，但：

> 🧨 所做的一切（如新增 commit）都**不屬於任何分支**，如果不小心離開就會「失聯」。

---

## Detached HEAD有什麼影響？

## 有什麼影響？

<table id="bkmrk-%E7%B5%90%E6%9E%9C-%E8%AA%AA%E6%98%8E-%E2%9C%85-%E5%8F%AF%E4%BB%A5%E4%BF%AE%E6%94%B9%E6%AA%94%E6%A1%88%E3%80%81%E5%81%9A-com"><thead><tr><th>結果</th><th>說明</th></tr></thead><tbody><tr><td>✅ 可以修改檔案、做 commit</td><td>你仍然可以正常工作（只是沒有在分支上）</td></tr><tr><td>🧨 新的 commit 沒有分支指向</td><td>所有新 commit 是「懸空狀態」，不儲存在任何分支上</td></tr><tr><td>🧹 一旦切換到其他分支就可能遺失那些 commit</td><td>除非你把它們另存（例如建立新分支），否則 Git 垃圾回收後就找不回來</td></tr><tr><td>🧠 `git status` 會提示 Detached HEAD</td><td>告訴你現在是在 Detached 狀態</td></tr></tbody></table>

---

## 什麼情況會進入 Detached HEAD？

<table id="bkmrk-%E6%83%85%E5%A2%83-%E6%8C%87%E4%BB%A4-%E6%83%B3%E8%A6%81%E7%9C%8B%E8%88%8A%E7%89%88%E6%9C%AC-git-che"><thead><tr><th>情境</th><th>指令</th></tr></thead><tbody><tr><td>想要看舊版本</td><td>`git checkout <commit-hash>`</td></tr><tr><td>從某個 commit 測試 / 編譯</td><td>`git switch --detach <commit>`</td></tr><tr><td>在 CI/CD 工具中 checkout 特定 commit</td><td>例如 GitHub Actions 的 `actions/checkout@v2` 預設為 Detached</td></tr><tr><td>你用 `git reset --hard` 到某個 commit</td><td>HEAD 也會臨時分離</td></tr></tbody></table>

---

## ✅ 正確處理方式：建立分支保存你做的事！

如果你在 Detached HEAD 狀態下做了 commit，請馬上建立一個分支把它們保存下來：

```bash
git switch -c fix-login-bug

```

這樣你的 commit 就有名字，不會被回收。

---

##  總結：Detached HEAD 是可用但有風險的模式

<table id="bkmrk-%E7%89%B9%E6%80%A7-%E7%8B%80%E6%85%8B%E8%AA%AA%E6%98%8E-%E5%8F%AF%E4%BB%A5%E7%B7%A8%E8%BC%AF%E8%88%87-commit"><thead><tr><th>特性</th><th>狀態說明</th></tr></thead><tbody><tr><td>可以編輯與 commit</td><td>✅ 是</td></tr><tr><td>commit 是否屬於分支</td><td>❌ 否</td></tr><tr><td>離開當前 commit 後是否會遺失</td><td>✅ 會（除非建立分支）</td></tr><tr><td>建議</td><td>**要保留修改就馬上建立分支**</td></tr></tbody></table>

---

# 【Git】每個 commit 不都是從某個分支出來的嗎？

這是個非常棒的深入問題，顯示你對 Git 的理解已經進入「底層思維模式」，我們來一一說明：

---

## 一句話解釋你提的問題：

> **不是每個 commit 都屬於分支，但每個分支都指向某個 commit。**

這句話是 Git 的核心設計邏輯，反直覺但重要。

---

##  先理解 Git 的本質：**commit 是資料庫，分支是指標**

在 Git 裡：

- 每個 commit 是獨立、完整、不可變的「快照」
- 「分支」其實只是指向某個 commit 的**指標（pointer）**
- `HEAD` 是你當下的游標，會指向一個「分支」或「commit」

### ✅ 當你做 commit 時發生了什麼？

1. Git 會建立一個新 commit（有一個父 commit）
2. `HEAD` 所在的分支（如 `main`）會更新，指向這個新的 commit

所以我們通常**覺得 commit 是從分支「出來」的**，但其實：

> 🔍 分支並不綁定在 commit 上，是 commit 被分支「指到了」。

---

## ❗Detached HEAD：從 commit 建立 commit，但不屬於分支

來看個例子：

```bash
git checkout abc1234   # 進入 detached HEAD
echo "foo" > a.txt
git add a.txt
git commit -m "detached commit"

```

這時你新增的 commit 並不屬於任何分支！

```
A --- B --- C    ← main
           \
            D   ← HEAD (沒有分支名稱指向)

```

如果你切走：

```bash
git switch main

```

那個 `D` commit **就被遺棄了**，未來可能被 Git 垃圾回收（GC）清掉。

---

## 分支只是「指到某個 commit」的名稱

你可以用 `git branch` 動態為任何 commit 建立分支：

```bash
git branch recover-commit abc1234

```

這個 `abc1234` 本來不是任何分支，但現在你「取了個名字」給它，它就變成你認為的「分支」。

---

## 實際演示：證明 commit 不一定屬於分支

```bash
git init
echo "init" > a.txt
git add .
git commit -m "init commit"
git checkout HEAD^ --detach
echo "detached" > a.txt
git commit -am "detached commit"
git log --oneline --decorate

```

你會看到第二個 commit 沒有任何分支名稱指向它，只是個孤兒 commit。

---

## 所以回到你的問題：

### ❓「每個 commit 不都是從某個分支出來的嗎？」

🔸 **表面上是，但實際上：**

- commit 是獨立存在的節點
- 分支是後來「指」過去的
- commit 是內容，分支是參考名稱（reference）

就像硬碟上的檔案可以沒被任何資料夾引用一樣，但它依然存在（直到被清理）

---

##  類比理解

<table id="bkmrk-git-%E6%A6%82%E5%BF%B5-%E9%A1%9E%E6%AF%94%E6%88%90%E9%9B%BB%E8%85%A6%E7%B3%BB%E7%B5%B1-commi"><thead><tr><th>Git 概念</th><th>類比成電腦系統</th></tr></thead><tbody><tr><td>commit</td><td>實際的檔案內容（在磁碟上）</td></tr><tr><td>分支（branch）</td><td>檔案捷徑（shortcut）</td></tr><tr><td>HEAD</td><td>你正在開啟的那個捷徑</td></tr><tr><td>detached HEAD</td><td>你直接打開某個檔案（沒從捷徑）</td></tr></tbody></table>

---

# 【Git】head, tag, commit, branch的關係

這是一個非常關鍵的 Git 理解核心：  
**`HEAD`、`tag`、`commit`、`branch` 彼此的關係**。  
我幫你用「定義 + 關聯圖 + 對照表 + 比喻」完整整理如下：

---

## ✅ 一張圖：理解 Git 中 HEAD、分支、Tag、Commit 的關係

```
          HEAD
            ↓
        ┌────────┐
        │ branch │──┐
        └────────┘  │
                    ↓
         ┌────────────────┐
         │     commit     │◄──┐
         └────────────────┘   │
                  ▲          │
        ┌─────────┴──────┐   │
        │     another     │  │
        │     branch      │  │
        └─────────────────┘  │
                             │
                        ┌─────────┐
                        │  tag    │──► 同樣也指向 commit
                        └─────────┘

```

---

## 🧠 核心定義

<table id="bkmrk-%E5%90%8D%E7%A8%B1-%E5%AE%9A%E7%BE%A9%E8%AA%AA%E6%98%8E-commit-git-%E7%9A%84"><thead><tr><th>名稱</th><th>定義說明</th></tr></thead><tbody><tr><td>**commit**</td><td>Git 的基本單位，是一個「版本快照」，有父子連結，不會變動</td></tr><tr><td>**branch**</td><td>指向某個 commit 的「可移動指標」，像捷徑，可以新增、移動</td></tr><tr><td>**HEAD**</td><td>代表你目前工作的「游標」，通常指向一個分支（或直接指向 commit）</td></tr><tr><td>**tag**</td><td>指向某個 commit 的「不可變名稱」（常用來標記版本，如 `v1.0.0`）</td></tr></tbody></table>

---

## 🔗 它們的關係總結

<table id="bkmrk-%E9%97%9C%E4%BF%82-%E8%AA%AA%E6%98%8E-branch-%E2%9E%9D-commi"><thead><tr><th>關係</th><th>說明</th></tr></thead><tbody><tr><td>`branch` ➝ `commit`</td><td>分支就是指向某個 commit 的「指標」</td></tr><tr><td>`HEAD` ➝ `branch` or `commit`</td><td>HEAD 是你「目前所在的位置」，可以指向分支或直接指向 commit（detached HEAD）</td></tr><tr><td>`tag` ➝ `commit`</td><td>Tag 永遠指向某個 commit，是不可變的</td></tr><tr><td>commit 之間</td><td>commit 是鏈狀連結（每個有一個 parent commit）</td></tr></tbody></table>

---

## 📁 類比說明（像檔案系統）

<table id="bkmrk-git-%E6%A6%82%E5%BF%B5-%E9%A1%9E%E6%AF%94-%E8%AA%AA%E6%98%8E-commit-"><thead><tr><th>Git 概念</th><th>類比</th><th>說明</th></tr></thead><tbody><tr><td>commit</td><td>檔案版本</td><td>是實際的內容快照（不可變）</td></tr><tr><td>branch</td><td>資料夾捷徑</td><td>可以移動、可以新建、可以切換</td></tr><tr><td>HEAD</td><td>滑鼠游標</td><td>你目前的編輯焦點（在哪個捷徑/檔案上）</td></tr><tr><td>tag</td><td>唯一標籤貼紙</td><td>永久貼在某個版本上，表示這是一個重要的版本點</td></tr></tbody></table>

---

## 📌 補充：變動性比較

<table id="bkmrk-%E5%85%83%E4%BB%B6-%E6%98%AF%E5%90%A6%E5%8F%AF%E8%AE%8A%E5%8B%95-%E5%B8%B8%E8%A6%8B%E6%93%8D%E4%BD%9C-commit"><thead><tr><th>元件</th><th>是否可變動</th><th>常見操作</th></tr></thead><tbody><tr><td>commit</td><td>❌ 不可變</td><td>一旦建立就不會改變</td></tr><tr><td>branch</td><td>✅ 可變動</td><td>可以移動、刪除、重新建立</td></tr><tr><td>tag</td><td>✅ 可刪除（內容不可變）</td><td>可以刪除再重建，但本身是不可移動的指標</td></tr><tr><td>HEAD</td><td>✅ 一直在動</td><td>跟著你切換分支、commit 而變動</td></tr></tbody></table>

---

## ✅ 常見操作對應關係

<table id="bkmrk-%E6%93%8D%E4%BD%9C%E8%AA%AA%E6%98%8E-head-%E8%AE%8A%E5%8B%95-branch-"><thead><tr><th>操作說明</th><th>HEAD 變動</th><th>branch 變動</th><th>commit 變動</th><th>tag 變動</th></tr></thead><tbody><tr><td>`git switch dev`</td><td>✅</td><td>❌</td><td>❌</td><td>❌</td></tr><tr><td>`git commit`</td><td>✅</td><td>✅（會跟著 HEAD 一起動）</td><td>✅（新增）</td><td>❌</td></tr><tr><td>`git reset --hard HEAD^`</td><td>✅</td><td>✅</td><td>✅（可能丟失）</td><td>❌</td></tr><tr><td>`git tag v1.0`</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>`git checkout <commit>`</td><td>✅（分離）</td><td>❌</td><td>❌</td><td>❌</td></tr><tr><td>`git switch -c new-feature`</td><td>✅</td><td>✅（新建）</td><td>❌</td><td>❌</td></tr></tbody></table>

---

## ✅ 總結一句話：

> - **commit** 是內容
> - **branch/tag** 是「名字」，用來指向某個 commit
> - **HEAD** 是「你目前在哪裡」，通常跟著分支動，也可以離開分支進入 detached 狀態

---

# 【Git】git revert <commit> 和 git checkout <commit> 差異

`git revert <commit>` 和 `git checkout <commit>` 雖然都能「回到某個 commit」，但本質上用途完全不同。以下為詳細說明與比較。

---

## 一、`git revert <commit>`

### 功能說明：

- 用來\*\*「反向操作」某個已提交的 commit\*\*。
- Git 會建立一個新的 commit，內容是將指定的 commit 所做的變更**還原回去**。
- 原本的 commit 仍然會保留在歷史中。

### 特性：

- **安全**，適合多人協作情境。
- **保留歷史紀錄**。
- 可選擇 revert 多個 commit（可搭配 `-n` 進行批次處理）。

### 使用情境：

- 已經 push 到遠端，但發現某個 commit 有誤，需要「撤銷」那次變更，但不想刪除歷史。
- 緊急修復 bug，並希望讓其他人看見這次的「撤銷行為」。

### 範例：

```bash
git revert a1b2c3d

```

會產生一個新的 commit，把 `a1b2c3d` 的內容還原。

---

## 二、`git checkout <commit>`（或 `git switch --detach <commit>`）

### 功能說明：

- 將當前工作目錄切換到某個過去的 commit（**進入 detached HEAD 狀態**）。
- 不會建立新的 commit，純粹只是瀏覽某個歷史狀態。
- 如果在此狀態下修改檔案並 commit，會產生「無分支」的 commit（容易遺失）。

### 特性：

- 不會變動歷史，也不會建立新 commit。
- 適合測試、比較、build 老版本。

### 使用情境：

- 想**檢查過去某次版本的內容**、debug、測試、build。
- 需要比較不同版本的行為。

### 範例：

```bash
git checkout a1b2c3d
# 或推薦用法（Git 2.23+）
git switch --detach a1b2c3d

```

---

## 三、兩者比較表

<table id="bkmrk-%E6%8C%87%E4%BB%A4-%E5%8B%95%E4%BD%9C%E9%A1%9E%E5%9E%8B-%E6%98%AF%E5%90%A6%E5%BB%BA%E7%AB%8B%E6%96%B0-commit"><thead><tr><th>指令</th><th>動作類型</th><th>是否建立新 commit</th><th>是否修改歷史</th><th>是否可安全推送遠端</th><th>常見用途</th></tr></thead><tbody><tr><td>`git revert <commit>`</td><td>**建立反向 commit**</td><td>✅ 是</td><td>❌ 否</td><td>✅ 是</td><td>撤銷已推送的錯誤 commit</td></tr><tr><td>`git checkout <commit>`</td><td>**切換版本**</td><td>❌ 否</td><td>❌ 否</td><td>⚠️ 不適合推送 detached commit</td><td>查看歷史、測試、debug</td></tr></tbody></table>

---

## 四、補充說明：若想基於某個舊 commit 建立新分支

```bash
git checkout -b new-branch a1b2c3d

```

這樣可以保留該版本的狀態，並在其上繼續開發，而不會讓 HEAD 處於 detached 狀態。

---

如果你有實際情境（例如：某個 commit 上錯 production，或想回復某功能），可以提供情況，我能幫你判斷該用 revert 還是 checkout 或其他方案。

# 【Git】常用指令

# Git 進階指令速查表（實戰版）

<table id="bkmrk-%E9%A1%9E%E5%88%A5-%E6%8C%87%E4%BB%A4-%E8%AA%AA%E6%98%8E-%E5%B8%B8%E8%A6%8B%E6%83%85%E5%A2%83-merge-"><thead><tr><th>類別</th><th>指令</th><th>說明</th><th>常見情境</th></tr></thead><tbody><tr><td>Merge</td><td>`git merge branch`</td><td>合併 branch</td><td>feature 合併到 main</td></tr><tr><td>Rebase</td><td>`git rebase branch`</td><td>重排 commit</td><td>整理 commit 歷史</td></tr><tr><td>Rebase</td><td>`git rebase -i HEAD~3`</td><td>互動式 rebase</td><td>squash commit</td></tr><tr><td>Cherry-pick</td><td>`git cherry-pick <commit>`</td><td>套用指定 commit</td><td>hotfix</td></tr><tr><td>Revert</td><td>`git revert <commit>`</td><td>回復某 commit</td><td>線上 rollback</td></tr><tr><td>Reflog</td><td>`git reflog`</td><td>查看 HEAD 歷史</td><td>找回被 reset 的 commit</td></tr></tbody></table>

---

# Rebase vs Merge

<table id="bkmrk-%E6%93%8D%E4%BD%9C-%E6%8C%87%E4%BB%A4-%E7%B5%90%E6%9E%9C-merge-git-m"><thead><tr><th>操作</th><th>指令</th><th>結果</th></tr></thead><tbody><tr><td>merge</td><td>`git merge main`</td><td>保留 merge commit</td></tr><tr><td>rebase</td><td>`git rebase main`</td><td>重新排列 commit</td></tr></tbody></table>

### Merge

```
A---B---C main
     \
      D---E feature
           \
            F (merge)

```

### Rebase

```
A---B---C main
           \
            D'---E' feature

```

📌 **結論**

<table id="bkmrk-%E6%93%8D%E4%BD%9C-%E4%BD%BF%E7%94%A8%E6%83%85%E5%A2%83-merge-%E4%BF%9D%E7%95%99%E6%AD%B7%E5%8F%B2-r"><thead><tr><th>操作</th><th>使用情境</th></tr></thead><tbody><tr><td>merge</td><td>保留歷史</td></tr><tr><td>rebase</td><td>整理 commit</td></tr></tbody></table>

---

# Interactive Rebase（整理 commit）

```bash
git rebase -i HEAD~3

```

會出現

```
pick a1b2 commit1
pick c3d4 commit2
pick e5f6 commit3

```

可修改為

```
pick a1b2 commit1
squash c3d4 commit2
squash e5f6 commit3

```

結果：

```
commit1 + commit2 + commit3
變成一個 commit

```

常用於：

```
PR 整理 commit

```

---

# Cherry-pick（套用某 commit）

```bash
git cherry-pick <commit-id>

```

用途：

<table id="bkmrk-%E6%83%85%E5%A2%83-%E7%AF%84%E4%BE%8B-hotfix-%E5%BE%9E-dev-%E8%A4%87"><thead><tr><th>情境</th><th>範例</th></tr></thead><tbody><tr><td>hotfix</td><td>從 dev 複製到 main</td></tr><tr><td>patch</td><td>套用某 bug fix</td></tr></tbody></table>

例如：

```
dev
A--B--C--D

main
A--B

```

```
git cherry-pick D

```

結果

```
main
A--B--D

```

---

# Revert（安全 rollback）

```bash
git revert <commit>

```

Git 會產生一個 **反向 commit**

例如：

```
A--B--C

```

revert B

```
A--B--C--D
        ↑
     undo B

```

📌 適合

```
production rollback

```

---

# Reflog（救命指令）

查看 HEAD 歷史

```bash
git reflog

```

例如

```
a1b2 HEAD@{0}: reset: moving to HEAD~1
c3d4 HEAD@{1}: commit: fix bug

```

恢復：

```bash
git reset --hard c3d4

```

很多工程師不知道：

```
Git 幾乎不會真的丟資料
reflog 都能救回

```

---

# Detached HEAD

發生情況

```bash
git checkout <commit-id>

```

Git 會顯示

```
You are in 'detached HEAD' state

```

代表：

```
HEAD 沒有 branch

```

解法

建立 branch：

```bash
git checkout -b new-branch

```

---

# Clean（清除未追蹤檔案）

查看將刪除

```bash
git clean -n

```

刪除

```bash
git clean -fd

```

常用於：

```
CI pipeline
重新 build

```

---

# Force Push

```bash
git push -f

```

⚠️ 危險

可能覆蓋他人 commit

安全方式：

```bash
git push --force-with-lease

```

這會檢查遠端是否被更新。

---

# Reset / Restore / Checkout 差異

<table id="bkmrk-%E6%8C%87%E4%BB%A4-%E5%BD%B1%E9%9F%BF%E7%AF%84%E5%9C%8D-reset-commit"><thead><tr><th>指令</th><th>影響範圍</th></tr></thead><tbody><tr><td>reset</td><td>commit + staging</td></tr><tr><td>restore</td><td>working tree</td></tr><tr><td>checkout</td><td>branch 或檔案</td></tr></tbody></table>

---

# Git 三層結構（核心概念）

Git 有三個區域：

```
Working Directory
↓
Staging Area (Index)
↓
Repository (Commit)

```

對應指令：

<table id="bkmrk-%E6%93%8D%E4%BD%9C-%E6%8C%87%E4%BB%A4-working-%E2%86%92-stag"><thead><tr><th>操作</th><th>指令</th></tr></thead><tbody><tr><td>working → staging</td><td>`git add`</td></tr><tr><td>staging → repo</td><td>`git commit`</td></tr><tr><td>repo → working</td><td>`git checkout`</td></tr><tr><td>repo → staging</td><td>`git reset`</td></tr></tbody></table>

---

# DevOps 常用 Git 重置流程

很多 CI pipeline 會用：

```bash
git fetch origin
git reset --hard origin/main
git clean -fd

```

作用：

```
確保 workspace 乾淨

```

---

# Git Flow（常見分支策略）

常見 branch：

```
main
develop
feature/*
hotfix/*
release/*

```

流程：

```
feature -> develop
develop -> release
release -> main
hotfix -> main

```

---

# Git Debug 指令

<table id="bkmrk-%E6%8C%87%E4%BB%A4-%E7%94%A8%E9%80%94-git-blame-file"><thead><tr><th>指令</th><th>用途</th></tr></thead><tbody><tr><td>`git blame file`</td><td>查看誰修改</td></tr><tr><td>`git bisect`</td><td>找 bug commit</td></tr><tr><td>`git show`</td><td>查看 commit</td></tr><tr><td>`git diff commit1 commit2`</td><td>比較 commit</td></tr></tbody></table>

---

# 工程師最強 Git 指令 TOP 10

<table id="bkmrk-%E6%8C%87%E4%BB%A4-%E7%94%A8%E9%80%94-git-log---onel"><thead><tr><th>指令</th><th>用途</th></tr></thead><tbody><tr><td>`git log --oneline --graph`</td><td>查看歷史</td></tr><tr><td>`git reflog`</td><td>救 commit</td></tr><tr><td>`git rebase -i`</td><td>整理 commit</td></tr><tr><td>`git cherry-pick`</td><td>套 commit</td></tr><tr><td>`git revert`</td><td>rollback</td></tr><tr><td>`git clean -fd`</td><td>清 workspace</td></tr><tr><td>`git reset --hard`</td><td>重置</td></tr><tr><td>`git stash`</td><td>暫存</td></tr><tr><td>`git blame`</td><td>找修改人</td></tr><tr><td>`git bisect`</td><td>找 bug</td></tr></tbody></table>

---

💡 **給你一個小建議**

如果你常用 Git（你目前 DevOps / CI / Jenkins workflow 看起來是這樣），  
最推薦記住 **這 6 個核心指令**：

```
git log --oneline --graph
git reflog
git rebase -i
git cherry-pick
git reset --hard
git clean -fd

```

- 

這份會非常接近 **資深工程師 Git 操作指南**。

# 【Git】茶包射手