伺服器維運

git 命令大全:維運日常常用命令速查表

日常用到的 git 命令不過三十來條:clone 拿到倉庫,status、diff、log 看變化,add、commit 記錄,switch、merge 管分支,pull、push 同步,restore、reset、revert 撤銷。下面按場景列成速查表,每條一句說明,最後講在伺服器上用 Git 管理配置檔案和部署指令碼。

作者 頂聯產品團隊發布於 8 分鐘閱讀

維運日常用到的 git 命令,按場景可以分成六組:初始化與克隆(git init、git clone),檢視狀態與歷史(git status、git diff、git log),暫存與提交(git add、git commit),分支切換與合併(git switch、git merge),遠端同步(git fetch、git pull、git push),撤銷與回退(git restore、git reset、git revert、git stash)。下面每組一張速查表,每條命令一句說明,撤銷類命令另外標出了風險。動手前先用 git --version 看一下版本:git switch 和 git restore 需要 Git 2.23 或更新的版本,CentOS 7 自帶的是 1.8.3.1,沒有這兩個命令,表裡同時給出了老版本的寫法。

先分清四個位置:命令都在它們之間搬運內容

理解了 Git 的四個“位置”,大部分命令的作用就不用死記:

位置 是什麼 進出的命令
工作區 你看到並直接編輯的檔案 編輯檔案即改變它;git restore 丟棄這裡的修改
暫存區 下一次提交將要包含的內容 git add 放進來;git restore --staged 拿出去
本地倉庫 .git 目錄裡的全部提交歷史 git commit 把暫存區記成一個提交;git reset 移動分支的位置
遠端倉庫 GitLab、Gitea 等伺服器上的倉庫 git push 推上去;git fetch、git pull 拉下來

git status 會告訴你每個改動處在哪個位置。第一次在一台機器上使用 Git,先設定身份和幾項常用配置:

git config --global user.name "zhangsan"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main     # 新倉庫的預設分支名,Git 2.28 起支援
git config --global core.editor vim             # 寫提交說明時用的編輯器
git config --global pull.ff only                # pull 只接受快進,避免意外生成合並提交
git config --list --show-origin                 # 檢視全部配置以及各自來自哪個檔案

不加 --global 時,配置只寫進當前倉庫的 .git/config,適合在伺服器上給某個倉庫單獨設定提交身份。

初始化、克隆與檢視:status、diff、log

初始化與克隆

命令 說明
git init 把當前目錄變成一個 Git 倉庫
git init -b main 初始化並指定分支名(Git 2.28 起)
git clone https://git.example.com/ops/scripts.git 克隆倉庫到同名目錄,後面再跟一個路徑就克隆到指定目錄
git clone -b v1.4.2 --depth 1 位址 目錄 只取某個分支或標籤的最新一次提交,在伺服器上部署時省時間和磁碟
git remote -v 檢視遠端倉庫的名字和位址

檢視狀態、差異與歷史

命令 說明
git status 哪些檔案改了、哪些已暫存、哪些是新檔案,以及當前在哪個分支
git status -sb 簡短格式,每個檔案一行,第一行顯示與遠端分支相比領先、落後幾個提交
git diff 工作區裡還沒暫存的改動
git diff --staged 已暫存、即將提交的改動,提交前看一眼
git diff HEAD~3 -- nginx.conf 某個檔案和 3 個提交之前相比改了什麼
git log --oneline --graph --decorate --all 所有分支的提交歷史,一行一個,帶分支圖
git log --stat -5 最近 5 個提交,以及每個提交改了哪些檔案
git log -p -- conf.d/shop.conf 某個檔案每一次提交的具體改動
git log --since="2 weeks ago" --author=zhangsan 按時間和作者篩選
git log -S 'client_max_body_size' 找出增加或刪除過這段文字的提交,定位某項配置是哪次改的
git show 提交號 某個提交的說明和完整改動
git show HEAD~1:conf.d/shop.conf 檢視檔案在上一個提交時的內容,不影響工作區
git blame -L 10,20 conf.d/shop.conf 第 10 到 20 行分別由哪個提交、誰、在什麼時間改的

HEAD 表示當前所在的提交,HEAD~1 是它的上一個,HEAD~3 是往前數第三個。提交號不用寫全,寫前 7 位通常就夠。

暫存、提交與切換分支

暫存與提交

命令 說明
git add 檔案 把這個檔案的改動放進暫存區
git add -A 暫存所有改動,包括新增和刪除的檔案
git add -p 逐塊詢問是否暫存,一次改了幾件事、想分成幾個提交時用
git commit -m "說明" 把暫存區的內容記成一個提交
git commit -am "說明" 自動暫存所有已跟蹤檔案的改動並提交,新檔案不包括在內
git commit --amend 修改上一個提交的說明,或補進漏掉的檔案;已推送的提交不要這樣改
git rm --cached 檔案 不再跟蹤該檔案,但保留在磁碟上,通常配合 .gitignore 使用
git mv 舊路徑 新路徑 移動或重新命名檔案,並記錄這個變化

切換分支:git switch 與 git checkout

命令(Git 2.23 起) 老版本寫法 說明
git branch 同左 列出本地分支,當前分支前有 *;-a 連同遠端分支一起列出,-vv 顯示跟蹤關係
git switch dev git checkout dev 切換到 dev;本地沒有、遠端有同名分支時,自動建立並跟蹤它
git switch -c hotfix git checkout -b hotfix 新建分支並切換過去
git switch -c hotfix v1.4.2 git checkout -b hotfix v1.4.2 從某個標籤或提交新建分支
git switch - git checkout - 回到上一個分支
git switch --detach v1.4.2 git checkout v1.4.2 切到某個標籤,處於“分離 HEAD”狀態,部署指定版本時用
git merge dev 同左 把 dev 合併進當前分支
git merge --no-ff dev 同左 即使能快進也生成一個合併提交,保留分支的痕跡
git merge --abort 同左 出現衝突時放棄這次合併,回到合併前
git rebase main 同左 把當前分支的提交挪到 main 最新提交之後;別人也在用的分支不要 rebase
git cherry-pick 提交號 同左 只把某一個提交複製到當前分支,常用於把修復補到釋出分支
git branch -d hotfix 同左 刪除已合併的分支;沒合併的要用 -D 強制刪除
git branch -m 舊名 新名 同左 重新命名分支

切換分支時,如果工作區裡有未提交的修改、並且和目標分支衝突,Git 會拒絕切換並列出衝突的檔案,先提交或用 git stash 收起來再切。合併出現衝突時,衝突的檔案裡會出現 <<<<<<<、=======、>>>>>>> 三種標記,手工改成最終想要的內容並刪掉標記,git add 這些檔案後執行 git commit 完成合並。

拉取與推送:和遠端倉庫同步

命令 說明
git fetch 下載遠端的新提交,不改動當前分支和工作區
git fetch --prune 同時清理遠端已刪除的分支在本地留下的記錄
git log --oneline HEAD..origin/main fetch 之後,檢視遠端比本地多出的提交
git pull fetch 加合併,把遠端的新提交合進當前分支
git pull --ff-only 只在能快進時更新,本地有分叉就報錯停下;伺服器上更新程式碼用它
git pull --rebase 把本地還沒推送的提交放到遠端提交之後,歷史保持一條直線
git push 把當前分支推送到它跟蹤的遠端分支
git push -u origin hotfix 第一次推送新分支並建立跟蹤關係,之後直接 git push
git push origin --delete hotfix 刪除遠端分支
git tag -a v1.4.3 -m "說明" 在當前提交上打一個附註標籤,釋出時用
git push origin v1.4.3 推送一個標籤;git push --tags 推送全部標籤
git push --force-with-lease 改寫歷史後強制推送,遠端被別人更新過時會拒絕;共用分支不要強推
git remote add origin 位址 給本地倉庫新增遠端倉庫
git remote set-url origin 新位址 倉庫遷移、改用 SSH 位址後修改遠端位址

撤銷、回退與 stash

撤銷類命令最容易出事,表裡多了一列“風險”,執行前先看:

想做的事 命令 老版本寫法 風險
丟棄工作區裡某個檔案的修改 git restore 檔案 git checkout -- 檔案 未提交的修改直接丟失,找不回來
把檔案移出暫存區,修改保留 git restore --staged 檔案 git reset HEAD 檔案 無
把某個檔案恢復成兩個提交前的樣子 git restore --source=HEAD~2 檔案 git checkout HEAD~2 -- 檔案(老寫法會同時改暫存區) 覆蓋該檔案當前的內容
撤銷最近一次提交,改動留在暫存區 git reset --soft HEAD~1 同左 無,適合說明寫錯或漏了檔案
撤銷最近一次提交,改動留在工作區 git reset HEAD~1 同左 無
徹底回到某個提交,之後的一切都不要 git reset --hard 提交號 同左 未提交的修改丟失;已提交的還能從 reflog 找回
撤銷一個已經推送的提交 git revert 提交號 同左 無:生成一個反向提交,不改寫歷史,共用分支用它
刪除未跟蹤的檔案和目錄 git clean -n 預覽,git clean -fd 執行 同左 刪除後無法恢復,務必先看預覽
找回 reset 掉的提交 git reflog 找到提交號,再 git switch -c 新分支 提交號 git checkout -b 新分支 提交號 無

git reflog 記錄了 HEAD 在本地移動過的每一個位置,reset --hard 掉的提交在一段時間內都能從這裡找回來;但從來沒有提交過的修改,Git 沒有任何記錄。

stash 用來臨時收起手頭的修改,比如在伺服器上臨時改過配置,現在要先拉取新版本:

命令 說明
git stash 或 git stash push -m "說明" 收起已跟蹤檔案的修改,工作區恢復乾淨;push -m 需要 Git 2.13 起,老版本寫 git stash save "說明"
git stash -u 連同未跟蹤的新檔案一起收起
git stash list 列出所有收起的記錄,最新的是 stash@{0}
git stash show -p stash@{0} 檢視某條記錄的具體改動
git stash pop 恢復最近一條並從列表刪除;有衝突時這條記錄會保留
git stash apply stash@{1} 恢復指定的一條,記錄仍保留在列表裡
git stash drop stash@{1} 刪除指定的一條

在伺服器上用 Git 管理配置檔案和部署指令碼

給配置目錄加上版本記錄

配置檔案放進 Git,“誰改了什麼、改壞了怎麼退回”就有了答案。以 nginx 為例:

cd /etc/nginx
git init
git config user.name "ops-web01"
git config user.email "[email protected]"
printf '%s\n' '*.key' '*.pem' 'ssl/' > .gitignore
git add -A && git commit -m "init: nginx config on web01"

# 每次改配置:改完先看差異,檢查透過、載入成功後再提交
vim conf.d/shop.conf
git diff
nginx -t && systemctl reload nginx && git commit -am "shop.conf: client_max_body_size 50m"

# 新配置出了問題:把這個檔案退回上一版,重新載入後把回退也記下來
git restore --source=HEAD~1 conf.d/shop.conf
nginx -t && systemctl reload nginx && git commit -am "revert shop.conf"

幾點注意:

  • 私鑰和帶密碼的檔案不進倉庫。 先寫好 .gitignore;已經提交過的,git rm --cached 只能讓以後不再跟蹤,歷史裡依然有,應當更換這些金鑰和密碼。
  • Git 不管許可權和屬主。 它只記錄檔案的可執行位,不記錄屬主、屬組和其他許可權位,也不記錄空目錄。從 Git 恢復 /etc 下的檔案後,用 ls -l 核對一下許可權。
  • 倉庫要推到別處。 只在本機的倉庫,伺服器壞了它也跟著沒了。加一個遠端倉庫定期 git push,就多了一份異地的配置歷史。
  • 整個 /etc 都想管起來,可以用 etckeeper(RHEL 系在 EPEL 軟體源中):它把 /etc 放進 Git,並在用 apt、dnf 安裝軟體前後自動提交。

Git 記錄的是“檔案變成了什麼樣”,記錄不了“誰登入了哪台伺服器、敲了哪些命令”。兩者配合才完整:Toplink DCIM 的 IPMI 遠端管理裡,經瀏覽器控制檯發起的 SSH 會話會全程錄影並記錄命令級日誌,配置出了問題,可以先用 git log 定位是哪次改動,再對照會話記錄找到當時的操作過程。

用 Git 釋出部署指令碼或程式碼

伺服器拉取倉庫,用唯讀的部署金鑰,並且只接受快進更新:

# 1. 生成本機專用的部署金鑰,把 .pub 公鑰新增到倉庫的部署金鑰(唯讀)
ssh-keygen -t ed25519 -f ~/.ssh/deploy_scripts -N '' -C "deploy@web01"
# 2. 在 ~/.ssh/config 裡為倉庫寫一個別名,固定金鑰和連接埠
Host git-ops
    HostName git.example.com
    Port 22022
    User git
    IdentityFile ~/.ssh/deploy_scripts
    IdentitiesOnly yes
# 3. 克隆、日常更新、按標籤釋出與回滾
ssh -T git-ops                                    # 測試金鑰是否被倉庫接受
git clone git-ops:ops/scripts.git /opt/scripts
cd /opt/scripts && git pull --ff-only             # 日常更新
git fetch --tags && git switch --detach v1.4.3    # 釋出指定版本
git switch --detach v1.4.2                        # 有問題,切回上一個版本

程式碼目錄如果就在網站根目錄下,.git 目錄可能被人直接下載(存取 https://站點/.git/config 試一下)。在 nginx 里加上 location ~ /\.git { deny all; },或者用 git archive 匯出不含 .git 的檔案再發布。

沒有程式碼託管平台、只想“推送即部署”,可以在伺服器上建一個裸倉庫,用鉤子把推送的內容檢出到釋出目錄:

# 伺服器上(釋出目錄 /var/www/site 要事先建好,並且 deploy 使用者可寫)
git init --bare /srv/git/site.git
printf '#!/bin/sh\ngit --work-tree=/var/www/site --git-dir=/srv/git/site.git checkout -f main\n' > /srv/git/site.git/hooks/post-receive
chmod +x /srv/git/site.git/hooks/post-receive

# 自己的電腦上
git remote add prod ssh://[email protected]:22022/srv/git/site.git
git push prod main

伺服器上常見的 Git 報錯

報錯 原因 處理
fatal: detected dubious ownership in repository at '/opt/scripts' 倉庫目錄屬於另一個使用者,例如用 deploy 克隆、用 root 執行;Git 2.35.2 起出於安全考慮拒絕操作 用屬主的身份執行,如 sudo -u deploy git -C /opt/scripts pull --ff-only;確認可信後才用 git config --global --add safe.directory /opt/scripts 放行
fatal: Not possible to fast-forward, aborting. 伺服器上有本地提交,或者遠端的歷史被改寫過 git log --oneline origin/main..HEAD 看本地多了什麼,確認無用後 git reset --hard origin/main
fatal: Need to specify how to reconcile divergent branches. 本地和遠端分叉了,又沒有設定 pull 的方式 設定 pull.ff only 或 pull.rebase true,再按上一行處理分叉
error: Your local changes to the following files would be overwritten by merge: 有人直接在伺服器上改了受 Git 管理的檔案 先 git diff 看改了什麼;要保留就 git stash,拉取後 git stash pop
指令碼執行報 /bin/bash^M: bad interpreter 檔案在 Windows 上編輯過,換行符是 CRLF 倉庫里加 .gitattributes,寫入 *.sh text eol=lf,執行 git add --renormalize . 後提交
Permission denied (publickey). 部署公鑰沒有新增到倉庫,或者用錯了金鑰 ssh -vT git-ops 檢視實際提交的是哪把金鑰
git diff 顯示 old mode 100644 和 new mode 100755 檔案的可執行位變了 確實需要可執行就提交;在 Windows 上提交指令碼,用 git update-index --chmod=+x deploy.sh 設定可執行位
You are in 'detached HEAD' state. 切到了標籤或某個提交上 部署指定版本時屬於正常;要在這裡改程式碼,先 git switch -c 新分支名

報錯資訊裡通常已經給出了下一步的建議命令(以 hint: 開頭的幾行),先讀完再動手,尤其不要看到報錯就習慣性地加 --force 或 reset --hard。

常見問題

git status 裡的中文檔名顯示成一串帶反斜槓的數字,怎麼辦?

這是 Git 預設把非 ASCII 字元轉義後顯示。執行 git config --global core.quotepath false,git status、git diff 裡就會直接顯示中文;仍是亂碼的話,再檢查終端和系統的字符集是不是 UTF-8。

git push 每次都要輸入帳號密碼,怎麼免輸?

推薦改用 SSH 位址:把公鑰新增到程式碼平台的帳號或倉庫,再把遠端位址換成 git@ 開頭的寫法。必須用 HTTPS 時可以配置 credential.helper,其中 store 會把憑據明文存進 ~/.git-credentials,多人共用的伺服器上不要用;不少平台也已不接受帳號密碼,要改用存取令牌。

Git 能用來備份整個伺服器嗎?

不適合。Git 適合管理文字檔案的變更歷史,大檔案和二進位制檔案會讓倉庫迅速膨脹,執行中的資料庫檔案直接提交也得不到一致的副本。配置檔案和指令碼用 Git 管理,資料和系統用專門的備份工具。

想看看在你的機房裡怎麼用?

從一次產品示範開始,梳理你的業務鏈路。

查看價格
服務熱線 400-112-2951

讓我們聊聊你的 IDC 業務

掃碼新增企業微信,預約產品示範或申請試用。

Toplink 企業微信二維碼

長按儲存或使用企業微信 / 微信掃描

也可致電
400-112-2951
Telegram
@TopLink88