GIT常用命令解析

豎橫山發表於2023-04-19

術語

  • 工作區/work tree
    倉庫所在的資料夾叫做工作區,就是你在電腦裡能看到的目錄。
  • 暫存區
    也就是在git add之後,git commit之前的緩衝區,一般在.git/index檔案中。
  • 倉庫
    git commit之後,根據暫存區的檔案生成一個commit id,暫存區的檔案歸檔到倉庫中,一般在.git/objects裡面

git

# 輸出git版本資訊
$ git -v
git version 2.38.1.windows.1

# 輸出常用的命令列表
$ git -h

# 用於臨時配置 Git 的配置選項。
# 臨時把commit id長度限制為 7 個字元
$ git -c log.abbrevCommit=true -c log.abbrevCommitLength=7  log

git config

# 全域性修改配置
$ git config --global user.email=you@example.com
$ git config --global user.name=Your Name
# 獲取某個配置項的值
$ git config user.name
# 列出所有配置項
$ git config --list
$ 刪除某個配置項
$ git config --unset --global user.name

git init

# 初始化一個新的git倉庫
$ git init

git clone 克隆遠端倉庫

# 克隆遠端倉庫到本地資料夾my-linux
$ git clone git://git.kernel.org/pub/scm/.../linux.git my-linux

$ 初始化一個純版本倉庫,沒有工作區。用於建立遠端倉庫
$ git init --bare

git add

對於將工作區的修改新增到暫存區非常重要,只有暫存過的修改才能被git commit提交到倉庫

# 新增指定的檔案到暫存區,可以用正則匹配
$ git add <file>
$ git add git-*.sh
# 新增當前目錄下所有檔案到暫存區
$ git add . 
# 新增之前已經被git管理的檔案到暫存區
$ git add -u
# 新增之前被git忽略的檔案到暫存區
$ git add -f <file>

git status

可以清楚地瞭解工作區和暫存區的檔案變化情況

# 顯示詳細的修改狀態
$ git status 
# 以短格式顯示狀態,更簡潔
$ git status -s 
# 順便顯示當前分支名
$ git status -b
# 顯示忽略的檔案
$ git status --ignored

git diff

能夠顯示各個區域之間的差異,理解其返回結果對於分析版本變化和除錯非常重要

# 顯示工作區與暫存區的差異。
$ git diff 
# 顯示暫存區與倉庫的差異
$ git diff --staged 
$ git diff --cached
# 顯示工作區與倉庫的差異
$ git diff HEAD
# 顯示兩個提交之間的差異
$ git diff 02d1a7b d2974d6
# 顯示兩個分支之間的差異。
$ git diff dev master
# 匯出差異檔案成壓縮包
$ git diff --name-only   144d196 188df06a | xargs tar -cvf pacakge.tar.gz

git commit

# 提交暫存區的修改,並新增提交資訊,生成commit id
$ git commit -m "message"
# 提交工作區和暫存區的所有修改,不包含新建的檔案
$ git commit -am "message"
# 修改最後一次提交的資訊
$ git commit --amend
# 繞過鉤子程式
$ git commit --no-verify
# 允許空白提交,git tag可能會用到
$ git commit --allow-empty

git notes

非常適合用來為提交新增額外的評論資訊,這些資訊儲存在.git/notes目錄中

# 新增註釋資訊到指定提交,如果不指定提交則預設為HEAD
$ git notes add -m "This is a note"  7d68213
# 刪除指定提交的註釋資訊
$ git notes remove 7d68213    
# 顯示指定提交的註釋資訊,如果不指定提交則預設為HEAD
$ git notes show 7d68213
# 顯示全部的註釋資訊
$ git notes list
# 編輯指定提交的註釋資訊
$ git notes edit 7d68213

git restore

可以用於回滾工作區和暫存區的修改,將檔案恢復到指定狀態,非常適合於版本控制和修改回退。

# 放棄對工作區檔案的修改,恢復到倉庫HEAD的狀態,可用正則匹配
$ git restore hello.c                   
$ git restore '*.c'
$ git restore .
# 恢復工作區檔案到指定提交的狀態
$ git restore --source 7d68213 hello.c
$ git restore -s 7d68213 hello.c
# 僅恢復暫存區的檔案到HEAD狀態,不影響工作區,相當於撤銷git add hello.c操作
$ git restore --staged hello.c

git reset

用於重置當前分支的HEAD為指定狀態,它會改變reflog和倉庫歷史。

# 重置HEAD為指定提交,重置暫存區但保留工作區修改
$ git reset 7d68213
$ git reset --mixed 7d68213
# 重置HEAD為指定提交,但保留工作區修改
$ git reset --soft 7d68213
# 回滾HEAD兩個版本,但保留工作區修改
$ git reset --soft HEAD~2
# 重置HEAD為指定提交,重置暫存區和工作區,丟棄所有修改
$ git reset --hard dc7ccb1
# 重置時保留未跟蹤檔案。
$ git reset --mixed --keep 2d3a2b9

git rm

用於從git中刪除檔案。它會將檔案從工作區和暫存區移除,並且提交修改,使檔案從倉庫中永久刪除

# 刪除指定檔案,並提交移除操作到暫存區,刪除前會檢查檔案狀態,以免誤刪未提交的修改。
$ git rm index.php
# 刪除指定檔案,並提交移除操作到暫存區,刪除前不會檢查檔案狀態
$ git rm -f index.php
# 刪除暫存區和倉庫中的檔案,但保留工作區中的檔案
$ git rm --cached xx.log
# 遞迴刪除整個目錄
$ git rm -r runtime/
# 刪除目錄中的所有檔案的暫存區和倉庫版本,但保留工作區中的檔案,一般用於.gitignore檔案新增.idea資料夾時,.idea從暫存區和倉庫刪掉同時保留工作區檔案
$ git rm -r --cached .idea/

git mv

用於移動或重新命名檔案和目錄。它實際上是git rm和git add的組合,用於跟蹤檔案移動和重新命名

# 移動/重新命名檔案或目錄 <source><destination>
$ git mv index.php index.php.bak

git branch

用於列出、建立和刪除分支

# 列出本地所有分支
$ git branch
# 建立本地新分支
$ git branch feature/devlop
# 刪除本地分支,只能刪除已合併的分支,或者HEAD和master一致的分支(還沒commit過)
$ git branch -d feature/devlop
# 刪除本地分支,可以刪除未合併的分支
$ git branch -D feature/devlop
# 列出本地分支及其最後一個提交
$ git branch -v
# 列出本地已經合併到當前分支的分支
$ git branch --merged
# 列出尚未合併到當前分支的分支
$ git branch --no-merged
# 列出本地和遠端所有分支
$ git branch -a
# 重新命名分支
$ git branch -m master main
# 複製一個分支到新建立的分支上,擁有前分支完整的歷史
$ git branch -c new_branch
# 將本地分支與指定的遠端分支建立追蹤關係。
$ git branch --set-upstream-to main main
# 刪除對遠端分支的追蹤設定。
$ git branch --unset-upstream new_branch
# 從 c83c925f6 提交處建立新的分支。
$ git branch test c83c925f6

git checkout

用於切換分支或恢復工作區檔案

# 切換到指定分支
$ git checkout master:切換到指定分支
# 建立新分支並切換到該分支
$ git checkout -b develop
# 丟棄工作區的修改,恢復與暫存區一致的版本
$ git checkout -- index.php
# 工作區中檔案的版本回滾到倉庫指定提交
$ git checkout cdc5737 -- index.php
# 切換到指定tag所指向的提交,配合rebase使用可以在重要的標記狀態繼續工作,同時不必擔心丟失之前的提交
$ git checkout v1.0

git switch

git 2.23版本新增的命令,用於切換分支或恢復工作區檔案

# 切換到master分支
$ git switch master  
# 建立並切換到新分支new-feature 
$ git switch -c new-feature  
# 刪除已合併的分支 
$ git switch -d old-feature
# 強制切換,重置工作區
$ git switch -f master 
# 建立空分支  
$ git switch --orphan new-branch 

git merge

用於將兩個或多個開發歷史合併在一起

# 將指定分支 develop 合併到當前分支,如果可以快進則快進,如果不能快進則建立一個新的合併提交,保留完整的分支記錄
# 快進指的是如果develop HEAD是master HEAD的上游,則可以直接把master HEAD改成develop HEAD
$ git merge develop
# 禁用快進,建立一個新的合併提交,即使可以進行快進合併,保留完整的分支記錄,常用於開發階段
$ git merge --no-ff develop
# 僅在可以進行快進合併時才會合併指定分支
$ git merge --ff-only develop
# 將指定分支 develop 的多個提交壓縮到一個提交,合併到當前分支的暫存區,需要手動提交
$ git merge --squash develop
# 執行合併但不建立提交,需要手動提交
$ git merge --no-commit develop

git mergetool

用於調解merge衝突的命令,在發生merge衝突時,直接執行

# 指定使用哪個工具調解衝突
$ git mergetool --tool=vimdiff
$ git mergetool --tool=kdiff3
$ git mergetool --tool=meld

git log

用於檢視提交歷史

# 顯示最近兩次提交
$ git log -2
# 顯示某個作者的提交日誌 
$ git log --author="ben"
# 顯示某個日期範圍內的提交 
$ git log --since=1.month --until="2018-02-28"
# 顯示包含某個關鍵詞的提交
$ git log --grep="README"
# 顯示某個檔案被修改的歷史 
$ git log --follow README.md
# 一條命令顯示簡潔日誌、分支和版本標籤
$ git log --oneline --decorate --all --graph
# 顯示提交統計資訊 
$ git log --stat
# 顯示某個tag到當前提交的日誌 
$ git log v1.0.0..HEAD
# 顯示某個tag和最後一個tag之間的所有提交
git log v1.0.0..v1.2.1
# 顯示某個分支的日誌
$ git log master

git stash

用於臨時儲存和還原未提交的工作
當你在某個分支開發某個新的特性,程式碼還在工作區,突然需要切換到另一個分支解決一個bug,如果直接切換分支可能會提示程式碼會被覆蓋

error: Your local changes to the following files would be overwritten by checkout:
    ttt
Please commit your changes or stash them before you switch branches.

需要你先提交再切換分支,然後你因為程式碼沒有完成各種原因並不想提交,這個時候就可以用git stash,具體操作如下

  1. git stash儲存當前工作進度
  2. 切換分支並修復Bug
  3. git stash pop恢復工作進度
  4. 繼續開發新特性
# 顯示所有儲存的stash,可以使用git stash apply恢復指定的stash。
$ git stash list
# 顯示指定stash的更改資訊,不恢復stash。
$ git stash show stash@{0}
# 以補丁的形式顯示更改資訊。
$ git stash show -p
# 恢復指定的stash到一個新分支上。
$ git stash branch new_branch
# 恢復最近一次的stash,並從stash list中移除它。
$ git stash pop
# 從stash list中移除一個stash,不會恢復它。
$ git stash drop
# 清除所有stash。
$ git stash clear
# 恢復指定的stash,但是不從stash list中移除它,可以用於測試恢復效果。
$ git stash apply stash@{0}
# 儲存當前工作並新增註釋,便於之後查詢恢復。
$ git stash save "some comments"
# 互動式地選擇要暫存的更改部分,不儲存全部的工作進度。
$ git stash save --patch
# 只暫存已經新增至暫存區的更改,未新增的更改不會被暫存。
$ git stash save --keep-index

git tag

用於建立和管理標籤,可以給專案的重要歷史提交打上標籤,以示重要的釋出版本或里程碑

# 打一個輕量級標籤
$ git tag v1.0
# 打一個帶註釋的標籤
$ git tag -a v2.0 -m "Release version 2.0"
# 給過去的某次提交打標籤
$ git tag -a v1.1 9fa1799
# 檢視v1.1的標籤資訊和提交資訊
$ git show v1.1
# 從標籤v1.1建立新分支
$ git checkout -b release-v1.1 v1.1
# 刪除本地標籤v1.1
$ git tag -d v1.1
# 推送單個新標籤到遠端
$ git push origin v1.0
# 推送所有新標籤到遠端
$ git push origin --tags
# 刪除遠端標籤
$ git tag -r <tagname>

git worktree

用於管理同一倉庫的多個工作樹

需要用到再研究@todo

git fetch

將遠端倉庫的最新提交下載到本地,但是不會自動合併到當前分支。這意味著使用fetch獲取的資料需要手動merge到本地分支之後才會生效

# 獲取所有遠端倉庫最新提交:
$ git fetch --all
# 檢視本地和遠端倉庫差異,然後合併
$ git log origin/dev..dev
$ git diff origin/dev dev
$ git merge

# 只獲取origin遠端倉庫master分支的最新兩個提交
$ git fetch origin master --depth 2
# 獲取origin遠端倉庫,並清除伺服器上已經刪除的分支
$ git fetch --prune origin
# 將一個淺倉庫轉為完整倉庫:
$ git fetch --unshallow
# 預覽將要獲取的提交,但不實際獲取
$ git fetch --dry-run

git pull

用於從遠端倉庫獲取最新資料後與本地當前分支自動合併,實際上是git fetch和git merge的組合

# 從origin遠端倉庫獲取master分支併合並: 
$ git pull origin master
# 使用rebase合併:
$ git pull --rebase
# 只在可以快速向前合併的情況下進行合併:
$ git pull --ff-only
# 從所有遠端倉庫獲取資料併合並: 
$ git pull --all
# 刪除遠端倉庫origin並更新引用:
$ git pull -d origin
# 拉取遠端倉庫的標籤
$ git pull --tags

git push

將本地提交推送到遠端倉庫,與其他協作者分享同步工作成果

# 推送master分支到origin遠端倉庫: 
$ git push origin master
# 推送全部本地分支到origin:
$ git push origin --all
# 刪除遠端倉庫origin的dev分支:
$ git push origin --delete dev
# 強制推送master分支到origin,會覆蓋遠端分支:
$ git push origin master --force
# 只有在遠端master分支是當前master分支的上游分支時,才強制推送:
$ git push origin master --force-with-lease
# 推送所有標籤到遠端倉庫
$ git push <remote> --tags

git remote

用於管理遠端倉庫的別名,從而指定不同的遠端倉庫及其URL。Git支援為同一個倉庫新增多個遠端倉庫。可以透過多次執行git remote add命令新增多個遠端倉庫別名,並指定不同的倉庫URL。

git remote add origin https://github.com/user/repo.git
git remote add upstream https://gitlab.com/user/repo.git 
git remote add alias https://bitbucket.org/user/repo.git
# 列出當前所有的遠端倉庫別名
$ git remote
# 新增一個新的遠端倉庫origin,URL為https://github.com/user/repo.git
$ git remote add origin https://github.com/user/repo.git
# 重新命名遠端倉庫upstream為up
$ git remote rename upstream up
# 移除遠端倉庫up
$ git remote remove up
# 修改遠端倉庫origin的URL
$ git remote set-url origin https://gitee.com/user/repo.git
# 列出遠端倉庫origin的詳細資訊
$ git remote show origin

git submodule

主要用於管理倉庫內巢狀的子模組。
子模組允許我們將一個倉庫當作另一個倉庫的子目錄,這個子目錄又是一個完整的倉庫。
這樣,我們可以獨立開發子模組中的專案,同時在主專案中可以指定相應提交記錄。
當克隆倉庫時,需要執行git submodule init和git submodule update以初始化子模組及其歷史。

git show

可以讓我們檢視某次提交的所有細節,包括提交資訊、修改的檔案、各檔案差異等。
這在開發和除錯程式碼過程中非常有用,可以快速定位引入某個問題的提交,檢查相關檔案變化等。

# 顯示HEAD指向的提交資訊
$ git show
# 顯示master分支的最新提交815e1c2的資訊
$ git show 815e1c2
# 僅顯示該提交修改的檔案列表
$ git show 815e1c2 --name-only
# 顯示該提交的檔案差異 
$ git show 815e1c2 -p
# 以medium格式顯示提交資訊
$ git show 815e1c2 --pretty=medium
# 顯示要應用提交815e1c2,需要執行的cherry-pick命令
$ git show 815e1c2 --cherry-pick

git shortlog

用於簡潔地瀏覽提交日誌,方便檢視貢獻者的提交,git log的簡潔版

# 顯示所有提交的簡要日誌,按提交者排序
$ git shortlog
# 僅顯示最近10次提交
$ git shortlog -n 10
# 顯示提交者和提交次數的彙總,不顯示提交摘要
$ git shortlog -s
# 顯示所有提交者的郵箱和提交SHA-1校驗和
$ git shortlog -e --all

git describe

根據最近的標籤來描述未打標籤的提交
在持續整合或開發過程中,經常需要知道當前程式碼距離最後一個釋出標籤的提交次數,以判斷當前進度或是否需要釋出新版本。
此時,可以使用git describe快速獲取這一資訊,例如:

# 表示當前提交g6145309距離標籤v1.2.35次提交。
$ git describe
v1.2.3-5-g6145309

git apply

將補丁檔案的更改應用到當前的Git樹物件或工作樹上,這在版本升級、臨時測試補丁效果以及分發開發組內的修補等方面非常有用

# 應用0001-fix-bug.patch補丁
$ git apply 0001-fix-bug.patch
# 僅顯示應用該補丁後檔案的變化統計:
$ git apply --stat 0001-fix-bug.patch
# 檢查補丁是否可以應用成功:
$ git apply --check 0001-fix-bug.patch
# 應用補丁,更新索引而非工作樹
$ git apply --cached 0001-fix-bug.patch
# 反轉補丁的更改
$ git apply --reverse 0001-fix-bug.patch
# 在無法應用的部分中建立reject檔案
$ git apply --reject 0001-fix-bug.patch

git cherry-pick

用於將指定提交的更改應用到當前分支,這與git merge的效果類似。
但是,cherry-pick會記錄為一個全新的提交,而非合併提交。
git cherry-pick是一個高階工具,可以透過應用特定提交的更改來調整專案的開發進展。
但是,如果對其原理不太清晰,反而可能產生許多無意義的提交,破壞提交歷史的整潔性。

# 應用master分支的最近3次提交
$ git cherry-pick master~3..master
# 記錄為新的提交,而非應用指定提交
$ git cherry-pick -x 8fd4e43
# 編輯提交資訊後進行提交:
$ git cherry-pick -e 8fd4e43
# 不進行提交,僅更新工作樹
$ git cherry-pick -n 8fd4e43
# 應用提交更改,不建立新的提交:
$ git cherry-pick --no-commit 8fd4e43
# 使用ours合併策略應用提交
$ git cherry-pick -Xours 8fd4e43

git rebase

git rebase用於將指定分支的變更重新定位到另一個分支上,可以看作是”變基”。
它會取消指定分支的所有提交,並將其內容應用到另一分支上,出現新的提交記錄。
git rebase是一個高階工具,可以使提交歷史變得更加整潔。
但是,如果在公開分享的分支上使用,可能會對其他開發者產生影響。

# 將experiment分支rebase到master分支  
$ git checkout experiment 
$ git rebase master
# 互動式rebase,合併最近3次提交  
$ git rebase -i HEAD~3
# 繼續rebase操作  
$ git rebase --continue  
# 終止rebase操作  
$ git rebase --abort

git revert

git revert用於建立一個新的提交來撤銷指定提交的更改。
它會建立一個全新的提交,回滾指定的提交,但是不會修改提交歷史,線上倉庫需要回滾的時候可以用revert取代reset,避免了git push -f強推的操作

# 撤銷最近一次提交 
$ git revert HEAD
# 撤銷指定的提交 
$ git revert 8fd4e43
# 編輯revert資訊
$ git revert -e 8fd4e43
# 撤銷指定提交,不記錄revert到操作
$ git revert --no-commit 8fd4e43

git reflog

reset回滾之後,以前的提交就不會顯示在git log裡面了,找不到提交id就沒辦法reset回來,可以在reflog查到分支變更記錄

# 查詢master最近5次變更
$ git reflog show master | head - 5
750c435 (HEAD -> master) master@{0}: commit: xx
2d3a2b9 (tag: v1.0) master@{1}: reset: moving to 2d3a2b9d
dc7ccb1 master@{2}: reset: moving to dc7ccb1
2d3a2b9 (tag: v1.0) master@{3}: reset: moving to 2d3a2b9dc

# 回滾到master兩次改變之前的commit,git log也會恢復
$ git reset --hard master@{2}

git cat-file

用於獲取git物件的內容、型別和長度資訊

# 顯示提交物件a6be41的內容
$ git cat-file -p a6be41
# 顯示提交物件a6be41的型別
$ git cat-file -t a6be41
# 顯示提交物件a6be41的大小
$ git cat-file -s a6be41
# 顯示提交物件a6be41的頭資訊
$ git cat-file -e a6be41
# 顯示多個物件(a6be41,cat-file, 9fceb46)的型別
$ git cat-file --batch-check=type a6be41 cat-file 9fceb46
本作品採用《CC 協議》,轉載必須註明作者和本文連結
遇強則強,太強另說