SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-13

GitHub 是什麼?Git 與 GitHub 對 VPS 的差異

了解 Git 與 GitHub 的實際差異:Git 在你的電腦或 VPS 上管理版本,GitHub 則是代管服務,並說明部署檔案、備份與程式碼審查的用途。

GitHub 是什麼?

GitHub 是一項代管服務,用來儲存 Git repository,並以此建立網站。Git 是執行於你自己的電腦或伺服器上的版本控制程式。GitHub 是建構在 Git 之上的單一公司產品,自 2018 年起由 Microsoft 所有。你可以每天使用 Git,卻完全不開啟 GitHub。沒有 Git,就無法使用 GitHub。

當你擁有 VPS(virtual private server)時,這項區分立即變得重要。Git 會記錄設定檔與部署指令碼的歷程。當伺服器沒有保存這份歷程時,GitHub 可儲存一份副本,也能用來執行建置與程式碼審查。本指南會以一個範例,說明如何從空白資料夾開始,在伺服器上完成部署;每個新詞彙都會在首次出現時定義。

Git 自行執行的內容

Git 是版本控制系統:它會記錄目錄隨時間的狀態,讓你查看哪些內容變更、何時變更,以及變更原因。Git 於 2005 年為 Linux kernel 開發而撰寫。它是分散式系統,表示每個 repository 副本都包含完整歷史記錄。其設計中沒有中央伺服器。同事的筆電和任何伺服器一樣,都是完整的副本。

安裝 Git 並設定身分。Git 沒有名稱和電子郵件地址時,會拒絕記錄 commit,因為這兩項資訊都會寫入 commit 本身。

sudo apt update && sudo apt install -y git
git --version
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

在 Ubuntu 24.04 上,git --version 會輸出 git version 2.43.0。過去幾年的任何版本,在以下所有操作中的行為都相同。

範例:存放 VPS 部署檔案的 repository

repository 通常簡稱為「repo」,是 Git 監控的目錄。執行 git init 後,Git 會在其中建立隱藏的 .git 資料夾,該資料夾就是 repository。刪除 .git 後,剩下的只是沒有歷程記錄的普通目錄。

mkdir vps-deploy && cd vps-deploy
git init -b main
printf '.env\n*.key\n' > .gitignore

-b main 將第一個分支命名為 main。省略此選項時,Git 會輸出一段很長的提示,說明預設分支名稱。.gitignore 會列出 Git 永遠不應追蹤的路徑。第一天就將 secrets 檔案寫入其中,因為檔案一旦提交過,即使刪除仍會留在歷程記錄中;若要正確移除,就必須改寫其後的每個 commit。

提交:歷史記錄的基本單位

現在新增一個指令碼並記錄它。

printf '#!/bin/sh\nsudo systemctl restart caddy\n' > restart.sh
git add restart.sh .gitignore
git commit -m "Add restart script and gitignore"
git log --oneline

git add 會將變更移至暫存區,也就是下一次提交將包含的內容清單。git commit 會將這份清單寫入歷史記錄,成為一筆項目。提交包含每個受追蹤檔案的快照、訊息、作者、時間戳記,以及指向前一筆提交的指標。git log --oneline 會為每筆提交列印一行,每行開頭都是簡短的雜湊值,例如 a1b2c3d。這個雜湊值就是提交的名稱,幾乎所有 Git 指令都接受此名稱。

略過 git add 步驟時,git commit 會回答 no changes added to commit (use "git add" and/or "git commit -a")。這不是錯誤。Git 是在告知你暫存區是空的,因此沒有任何內容可建立快照。當你不確定目前狀態時,應執行 git status:它會列出目前的分支、已暫存的變更,以及 Git 能看到但尚未追蹤的檔案。

分支:歷史的第二條路線

分支是指向某個提交的可變指標。main 是一個分支,對 Git 而言沒有任何特殊之處。建立分支不需成本,因為 Git 只會寫入新的指標,不會複製檔案。

git switch -c add-backup
printf '#!/bin/sh\nrestic backup /srv\n' > backup.sh
git add backup.sh
git commit -m "Add nightly backup"
git switch main
ls

執行 git switch main 後,清單中不再顯示 backup.sh。沒有任何檔案遭到刪除。該檔案存在於 add-backup 分支,而 main 從未包含它,因此 Git 在你切換分支時,會將檔案從工作目錄移除。這種情況每個人都只會第一次感到意外。git switch add-backup 會將它還原。

Remotes:GitHub 終於出現的地方

到目前為止,所有操作都在完全沒有網路的單一機器上執行。remote 是另一份相同 repository 的命名 URL。GitHub 會代你代管其中一份副本。主要 remote 的慣用名稱是 origin

透過 GitHub 網站建立空的 repository,然後將本機 repository 連接到它。這裡建議使用 SSH,而不是 HTTPS:SSH key 是由你控制的檔案,不會像 personal access token 一樣過期。

ssh-keygen -t ed25519 -C "vps-deploy"
cat ~/.ssh/id_ed25519.pub
ssh -T git@github.com

將輸出的 public key 貼到 GitHub 帳號的 SSH keys 頁面,然後再次執行測試。可正常運作的 key 會回應 Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.。GitHub 不會提供 shell,因此這個拒絕訊息代表成功。git@github.com: Permission denied (publickey). 表示你的 key 從未送出,或未獲接受;請確認貼上的是 .pub 檔案,而不是旁邊的 private key。

git remote add origin git@github.com:yourname/vps-deploy.git
git push -u origin main

git push 會將你的 commits 傳送到 remote。-u 會記錄本機 main 追蹤 remote main,因此之後只需執行簡短的 git push 即可。git clone <url> 是在新機器上的反向操作:它會連同歷史記錄複製整個 repository,並替你設定 origin。HTTPS remote 也能使用,且會透過與網頁相同的協定傳輸,因此在封鎖對外 port 22 的網路上更容易運作。如果這句話需要進一步說明,HTTP request 實際由哪些部分組成 會介紹其運作細節。

Pull request、issue 與 fork:這些是 GitHub 的功能,不是 Git

以上內容都屬於 Git,且可搭配任何伺服器使用。以下 3 個詞是 GitHub 的功能。其他代管平台會仿效這些功能,但 Git 本身並不了解它們。

pull request(PR)是將一個分支合併至另一個分支的請求,並附帶供討論的頁面。你將 add-backup 推送上去,針對 main 開啟 PR,網站會逐個 commit 顯示差異。使用者可以針對單行留言。自動化檢查會回報該分支是否通過檢查。按下合併後,GitHub 會在自己的副本上執行合併,然後更新 main。這個名稱源自早期工作流程:你會請維護者將你的分支 pull 到他們的分支中。

issue 是用於追蹤錯誤或工作的編號討論串。它儲存在 GitHub 的資料庫中,而不是你的 repository 內。選擇代管平台前,這點值得注意:複製 repository 後,你會取得所有 commit,但不會取得任何 issue。要匯出 issue,必須呼叫 API。

fork 是其他人 repository 的伺服器端副本,歸你所有。你可以寫入該副本,將分支推送到其中,再從你的副本向原 repository 開啟 pull request。你可以藉此協作尚未認識你的專案。fork 是一份存放在 GitHub 上,並記錄來源位置的 clone。

軟體與使用者都透過相同的 API 讀取這 3 種物件。在自己的伺服器上執行的 pull request 審查代理程式會監看新的 PR、讀取差異,並發佈程式碼行留言。在 repository 根目錄放置 AGENTS.md 檔案等慣例之所以存在,是因為現在讀取 repository 的不只有使用者,也包括工具。

VPS 擁有者實際上能用 GitHub 做什麼

先從伺服器外部的儲存開始。部署指令碼與 playbook 應放在不受其設定的伺服器上。從全新的映像檔重建 VPS,再執行 clone 與部署。將該 repository 設為 private,並為伺服器提供 deploy key:這是繫結至單一 repository、而非整個帳戶的 SSH key,並設為唯讀。唯讀 deploy key 外洩時,只會暴露一個 repository。帳戶 key 外洩時,則會暴露你能 push 至的所有內容。

sudo git clone git@github.com:yourname/vps-deploy.git /srv/vps-deploy
cd /srv/vps-deploy
git pull --ff-only

--ff-only 會拒絕建立 merge commit。對只接收變更的伺服器而言,merge 一律代表意外,因此這個 flag 會將難以判讀的歷程轉為明確錯誤 fatal: Not possible to fast-forward, aborting.。這表示伺服器上的內容出現了不應存在的變更。先找出原因,再次執行 pull。

以 root 執行 clone,之後改用其他使用者執行 Git,會得到 fatal: detected dubious ownership in repository at '/srv/vps-deploy'。Git 拒絕讀取由其他使用者擁有的 repository,因為惡意的 .git/config 可能讓 Git 執行指令。請使用 chown 修正擁有權,而不要加入 safe.directory 例外;例外只會停用檢查,並未移除根本原因。

GitHub Actions:建置與部署管線

Actions 是 GitHub 的 CI/CD 系統(持續整合與持續交付)。將 YAML 檔案提交至 .github/workflows/ 下方後,GitHub 會在指定的事件發生時執行該檔案。

name: check
on:
  push:
    branches: [main]
jobs:
  shellcheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: sudo apt-get update && sudo apt-get install -y shellcheck
      - run: shellcheck *.sh

該檔案稱為 workflowjob 會在一台機器上執行。step 是一個命令或一個已發布的 action。uses: 會從其他 repository 載入 action,而 @v7 會鎖定其 major version(截至 August 2026,actions/checkout 的目前版本為 v7)。務必鎖定版本,因為未鎖定的 action 可能會執行你未讀過的程式碼,且該程式碼可存取你的 secrets。

runs-on: ubuntu-latest 會向 GitHub 要求一台新的虛擬機器,並在 job 結束時捨棄。公開 repository 可免費使用標準 runner;截至 August 2026,免費方案每月也包含 private repository 的 2,000 分鐘。若要依此數字編列預算,請先查看目前的 pricing page。

Secrets 儲存在 repository 設定中,並以 ${{ secrets.DEPLOY_KEY }} 讀取。由 fork 發出的 pull request 所觸發的 workflow 會取得唯讀 token,且無法存取這些 secrets;否則陌生人可能開啟一個唯一工作是列出 secrets 的 PR。

在自己的 VPS 上執行 Actions runner

runs-on: self-hosted 會改為將工作傳送到你擁有的機器。儲存庫的 runner 設定頁面會提供下載指令、儲存庫網址,以及有效期限為 one hour 的註冊 token。將後兩項填入 REPO_URLRUNNER_TOKEN,接著只需執行 three commands 即可完成設定。

./config.sh --url "$REPO_URL" --token "$RUNNER_TOKEN"
sudo ./svc.sh install
sudo ./svc.sh start
./svc.sh status

svc.sh status 應顯示服務為 active,並列出最近的 log 行。runner 會對 GitHub 建立 outbound HTTPS 連線並要求工作,因此不需要為它開放任何 inbound port。svc.sh install 會寫入 systemd unit;這是最常被略過的步驟:沒有這項設定,runner 會隨 SSH 工作階段結束而退出,之後的每個工作都會無預警地停留在 queued 狀態。VPS 上完整的 self-hosted runner 設定說明長期執行的 runner 所需的強化設定與清理工作。

這樣部署就不再需要讓可從網際網路連入的 SSH key,因為工作已經在該主機上執行。build cache 也會在各次執行之間保留,不會計算執行分鐘數。

有一項警告不可忽略。GitHub 的官方文件建議只在 private repositories 使用 self-hosted runners,因為 public repository 的 fork 可以透過建立 pull request,在你的 runner 上執行危險程式碼。runner 會執行該分支 workflow file 指定的任何內容。在你能控制 push 權限的 private repo 中,風險較低。在 public repo 中,應將任何 self-hosted runner 視為陌生人可以在其上執行程式碼的機器。

完全需要 GitHub 嗎?

不需要。Git 是標準,GitHub 則提供便利性。Forgejo 和 Gitea 都是可自行代管的 forge;forge 是附帶 issue 與 pull request 的 Git 主機。兩者都以單一 Go binary 發布,都能在小型 VPS 上執行;Forgejo 則是 Gitea 於 2022 年分支出的版本,目前為 Codeberg 提供支援。由於兩者使用相同的 wire protocol,搬移 repository 只需執行一個 command。

git remote -v
git remote set-url origin git@git.example.com:you/vps-deploy.git
git push origin main

每個 commit 都會一併搬移,因為每份 clone 都已經包含完整歷史。無法一併搬移的是 GitHub 在其上層建立的功能:issue 以及 pull request 討論串。CI 也不會轉移。Forgejo 有自己的 Actions 實作,會從 .forgejo/workflows/ 讀取格式相近的 YAML;其文件也直接說明限制:GitHub Actions 與 Forgejo Actions 並不相同,部分功能可能無法立即運作。此外,Forgejo 也需要自己的 runner。請將這個步驟規劃為移植,而不是複製。

多數專案持續使用 GitHub 的真正原因是貢獻者。公開程式碼必須放在使用者已經擁有帳號的平台。私有的部署 script 則不必如此。這是兩個分開的決策,你可以分別採用不同的做法。

先出現什麼問題,以及錯誤訊息的含義

推送遭到拒絕。 你會看到:

 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:yourname/vps-deploy.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

自上次提取後有內容被推送,通常是你在網頁編輯器中所做的修改。執行 git pull --rebase,將你的提交重新套用在對方提交之上,然後再次推送。避免在共用分支上執行 git push --force,因為這會從伺服器上的該分支移除其他提交。

fatal: refusing to merge unrelated histories 你已在本機執行 git init,同時又讓 GitHub 建立含有 README 的儲存庫。兩邊的歷程沒有任何共用提交,因此 Git 無法自行判斷。最乾淨的做法是將 GitHub 上的副本複製到新資料夾,再把檔案移入其中。

error: src refspec main does not match any 你指定的分支在此處不存在。通常是因為儲存庫目前還沒有任何提交,或你的分支名稱是 master。執行 git branch --show-current 即可確認。

機密資訊進入提交。 立即輪替該憑證。從推送完成的那一刻起,應將它視為已公開,因為 fork、mirror 與快取檢視都可能保留副本,而你無法刪除這些副本。

FAQ

GitHub 和 Git 是同一回事嗎?

不是。Git 是安裝在機器上的版本控制程式,不需要網路或帳戶也能運作。GitHub 是商業託管服務,用來儲存 Git repository,並在其上提供網頁介面、issue、pull request 與 CI。Git 於 2005 年發布,GitHub 則在 2008 年以 Git 為基礎推出。Git 可以永久獨立運作,不必使用 GitHub。GitHub 的每項功能底層都依賴 Git。

在 VPS 上使用 Git 需要 GitHub 帳戶嗎?

不需要。git initgit commitgit log 可在完全未設定 remote 的伺服器上運作,這已足以追蹤 /etc 檔案的變更或部署 script。當你需要一份能在伺服器故障後保留的歷史副本,或需要讓第二台機器進行 clone 時,帳戶才會有用。Forgejo 和 Gitea 等自架 forge 可在自有硬體上提供相同功能;將 plain SSH remote 指向另一台機器上的 bare repository,則完全不需要 forge 軟體。

什麼是 pull request?

pull request 是將一個 branch 合併至另一個 branch 的請求,並附有討論頁面。你先 push 一個 branch,再針對 main 開啟 PR。託管服務會逐一顯示每個 commit 的變更,讓審查者能針對個別行留言,並讓自動化檢查回報通過或失敗。這是 GitHub 的功能,不是 Git 的功能,因此 Git 本身沒有對應的 command。其他託管服務也實作相同概念,有時會稱為 merge request。

我應該在自己的 VPS 上執行 GitHub Actions runner 嗎?

對 private repository 而言,通常可以。工作會在你已經付費使用的硬體上執行,不會計算使用分鐘數,build cache 也能持續保留;部署時不必再將 inbound SSH key 暴露於網際網路,因為 runner 會主動連線至 GitHub 並取得工作。對 public repository 而言,GitHub 不建議這麼做:任何人都能 fork 你的 repository,並開啟 pull request,讓其中的 workflow 在你的機器上執行程式碼。

之後可以將 repository 移出 GitHub 嗎?

程式碼可以,而且很容易。每個 clone 都包含完整歷史,因此執行 git remote set-url origin <new url> 後再 push,即可移動每個 commit 所包含的全部內容。會留在 GitHub 的,是 GitHub 管理的那一層:issue、pull request 討論與 Actions 歷史都儲存在 GitHub 的資料庫中,不在你的 .git 資料夾裡。Migration 工具可以透過 API 複製 issue,而 workflow 檔案通常需要針對新託管服務的 CI 進行修改。考量到這點,應將正式文件放在 repository 中,而不是只放在 issue 討論串裡。

#github#git#version-control#ci-cd#developer-tools