本文用來整理記錄日常作業中經常使用到的 Git 命令,方便日常查詢使用,關于 Git 誕生的歷史及相關內部原理本文暫不涉及,
基礎概念
首先通過下圖(圖片來源于網路)了解日常作業中 Git 的操作流程

基于上圖介紹下 Git 的相關作業空間
| 術語 | 解釋 |
|---|---|
| 本地倉庫/版本庫(Repository) | 一個倉庫包括了所有的版本資訊、所有的分支和Tag資訊,在Git中倉庫的每份拷貝都是完整的,倉庫讓你可以從中取得你的作業副本,作業區目錄下有一個.git的目錄,這個目錄就是Git來跟蹤管理版本庫的, |
| 作業區(workspace) | 本地專案存放源檔案的檔案夾就是作業區 |
| 暫存區(Index/Stage) | 用于臨時跟蹤存盤相關修改 |
| 遠程倉庫(Remote) | 一般指托管在遠程Git服務器上的倉庫,用于托管檔案供多人協作使用,如GitHub、Gitee、自建的Git服務等 |
常用操作
基礎配置
-
配置提交人標識和郵箱
$ git config --global user.name 'your_name' $ git config --global user.email '[email protected]' # 取消配置 git config --unset --global user.name git config --unset --global user.emailconfig 的三個作用域
$ git config --local #local 只對某個倉庫有效 $ git config --global #global 對當前用戶所有倉庫有效 $ git config --system # system 對系統所有登錄的用戶有效 # local的優先級最高,即local的配置資訊可以覆寫global配置的資訊顯示 config 的配置
$ git config --list --local $ git config --list --global $ git config --list --system洗掉 config 中的配置項
git config --global --remove-section name # name為要洗掉的配置項名稱執行
git config后相關的配置將保存到純文本檔案中,對應生成檔案位置如下:- local 在.git/config里面;
- global 在個人 home 目錄下的 .gitconfig里面;
- system 在 git 安裝目錄的下,
所以
git config命令其實只是一個提供便捷命令列的介面, -
其他常用配置
$ git config --global alias.lg "log -10 --pretty=oneline --graph" # 別名設定 $ git config --system core.editor <editor> $ git config --global --edit
倉庫初始化
-
本地有個專案代碼寫了一段時間了,但還沒有用 git 管理起來,現在想用 git 做版本管理
$ cd existing_floder $ git init $ git add . $ git commit -m"Initial commit" -
本地生成了一個 git 的倉庫,開發了一段時間,想把這個git倉庫提交到公司git服務器新建的專案中
$ cd existiong_repo $ git remote add origin git@your_git_server:your_group/your_project.git $ git push -u origin master #如想推送所有分支和tag則按如下執行 $ git push -u origin --all $ git push -u origin --tags # 官方解釋:push 后增加 -u 的目的是對于每個最新的或成功 push 的分支,添加 upstream(跟蹤)參考,該參考由后續供無引數git-pull 和其他命令使用, # 白話解釋:也就是加上了-u引數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以后的推送或者拉取時就可以簡化命令, -
倉庫已經在公司git服務器上創建,檢出倉庫到本地
$ git clone git@your_git_server:your_group/your_project.git #克隆指定分支 $ git clone -b master git@your_git_server:your_group/your_project.gitGit支持多種協議,默認的
git://使用ssh,但也可以使用https等其他協議,使用
https除了速度慢以外,還有個最大的麻煩是每次推送都必須輸入口令,但是在某些只開放http埠的公司內部就無法使用ssh協議而只能用https,同時作為服務端存盤的 Git 倉庫都為裸倉庫,裸倉庫沒有作業區,因為服務器上的 Git 倉庫純粹是為了共享,所以不讓用戶直接登錄到服務器上去改作業區,并且服務器上的Git倉庫通常都以
.git結尾#在Github、GitLab等Git托管服務上創建的倉庫均為裸倉庫,如想手動創建可參考如下命令 #以下命令都可以生成裸倉庫 $ git clone --bare ... $ git init --bare
本地倉庫變更
Git的版本庫里存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區,還有Git為我們自動創建的第一個分支master,以及指向master的一個指標叫HEAD,

-
git status查看作業目錄和暫存區的狀態
-
git addgit add .將作業區新增、修改和洗掉(Git 2.x 才支持)的檔案添加到暫存區,即對 Git 已跟蹤和未跟蹤的檔案都進行處理,只對當前路徑及其子路徑檔案有效git add -A等同于git add --all將檔案的修改、新增、洗掉添加到暫存區,對整個倉庫下的檔案都有效git add -u將作業區內被修改和洗掉的檔案添加到暫存區(不包括未被 Git 跟蹤的新增檔案),好處是可以避免把作業區內還沒準備好的檔案加到暫存區git add file1 file2 ...一次可添加一個或多個檔案及檔案夾到暫存區git add -p file互動式的選擇作業區及暫存區檔案的相關補丁,并新增到暫存區 -
git commitgit commit -m'xxx'提交暫存區的變更內容到版本歷史,生成 commit 節點git commit -am'xxx'會將已經被Git跟蹤的變更內容,提交到暫存區并提交內容,生成 commit 節點,不推薦此方式,此方式將作業的檔案直接添加到版本歷史中去了,跳過了人工進行暫存的處理,
忽略指定檔案
可以配置 Git 忽略指定的檔案或者是檔案夾,這些配置都放在 .gitignore檔案中,這個檔案可以存在于不同的檔案夾中,可以包含不同的檔案匹配模式,
具體配置在此不做具體描述,下面介紹下使用中可能遇見的問題:
-
某檔案已被提交到遠程倉庫后,想對其忽略
git rm --cached <file>- 在
.gitignore檔案中增加配置 - 重新提交推送至遠程倉庫即可
-
空檔案夾無法被追蹤
因為 Git 是針對檔案的變更做管理追蹤的故需要在檔案夾下添加檔案
.gitkeep,這是一個約定俗成的檔案名,遞回創建.gitkeep檔案
find . -type d -empty -not -path"./.git/*" -exec touch {}/.gitkeep \\;
檔案重命名
-
常規方式
mv readme readme.md
執行
git add readme.md和git rm readme
-
簡便方式
git mv readme readme.md,前提此檔案已被 Git 所管理跟蹤
直接執行commit即可,無效執行add,
洗掉檔案
-
常規方式
$ rm <file> # 洗掉作業區檔案 $ git rm <file> # 加入暫存區 -
簡便方式
$ git rm <file> # 洗掉檔案并加入暫存區
差異對比

-
比較暫存區與 HEAD 所指向分支所含檔案的差異
$ git diff --cached $ git diff --staged # 以上命令皆可 -
比較作業區與暫存區所含檔案的差異
$ git diff #默認比較的是作業區與暫存區所有檔案的差異 $ git diff -- file1 file2 #可以指定一個或多個具體檔案名,在作業區與暫存區進行比較 -
比較作業區與 HEAD 所指向分支所含檔案的差異
$ git diff HEAD $ git diff HEAD -- file1 file2 # -- 為了讓git命令讀取命令引數的時候消除歧義用的,雙連字符后面的是路徑或檔案 -
比較 COMMIT 間的檔案差異
$ git diff 5d39588 56f6198 $ git diff HEAD HEAD~1 $ git diff HEAD HEAD^1 # 上述兩命令等價 HEAD 指向5d39588 commit,HEAD~1 指向 5d39588 的父 commit 56f6198 $ git diff HEAD HEAD^1 -- readme.md #后面的commit為比較基準通過下圖來看下 diff 操作時
~與^差異G H I J \ / \ / D E F \ | / \ \ | / | \|/ | B C \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2 $ git log * 29392c8 (HEAD -> master, tag: A) A |\ | * a1ef6fd (tag: C) C | | | \ *-. \ 8ae20e9 (tag: B) B |\ \ \ | | |/ | | * 03160db (tag: F) F | | |\ | | | * 9df28cb (tag: J) J | | * 2afd329 (tag: I) I | * a77cb1f (tag: E) E * cd75703 (tag: D) D |\ | * 3043d25 (tag: H) H * 4ab0473 (tag: G) G可以看出
~與^都是指父節點,n個~與n個^或者^1與~1都是等價的,它們都是以線性的形式來找其父節點,區別在于
~與^后跟數字的情形,~n仍是以線性來找父節點,但^n會按其所有的父節點來計算查找
查看 commit 歷史
# 查看當前所在分支記錄
$ git log
# 高版本命令列會進入互動界面,如不進入互動界面直接輸出日志按以下命令執行
$ git --no-page log #--no-page 禁用page分頁,git 很多命令默認都是page分頁的
# 日期格式
$ git log --date=format:%Y-%m-%d\ %H:%M:%S
$ git log --date=iso
$ git log --date=short
# 以單行簡潔方式展示歷史
$ git log --oneline
# 查看最近5條記錄
$ git log -n5 --oneline
$ git log -n5 --oneline #"-<number>"同"-n<number>",
# 查看具體分支記錄
$ git log temp
# 查看所有分支log記錄
$ git log --all
# 以圖形化展示記錄
$ git log --all --graph
# 美化記錄
$ git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# 查看某檔案中某人何時修改了何處
$ git blame <file>
重寫 commit 歷史
# 將本次暫存區的變更與上次commit的變更合并生成新的commit,并替換 message
# 前提當前commit為最新的
$ git commit --amend # 此操作只可修改未被 push 到遠程倉庫的 commit 訊息
# 修改舊commit的message資訊、合并commit訊息
$ git rebase -i <base> # 變基只用于本地管理未被push到遠程倉庫中代碼,如果push到遠程后又使用了rebase,則會影響團隊其他成員,
撤銷變更
-
checkout
checkout命令用于從歷史提交(或者暫存區域)中拷貝檔案到作業目錄,也可用于切換分支,
當給定某個檔案名(或者打開-p選項,或者檔案名和-p選項同時打開)時,git會從指定的提交中拷貝檔案到暫存區域和作業目錄,比如,
git checkout HEAD~ foo.c會將提交節點HEAD~(即當前提交節點的父節點)中的foo.c復制到作業目錄并且加到暫存區域中,(如果命令中沒有指定提交節點,則會從暫存區域中拷貝內容,)注意當前分支不會發生變化,# 作業區回退到暫存區的版本 # 指定檔案 $ git checkout -- file # 所有內容 $ git checkout * # 指定提交中檔案回退到暫存區和作業目錄 $ git checkout HEAD index.html -
reset(復位)
reset命令把當前分支指向另一個位置,并且有選擇的變動作業目錄和索引,也用來在從歷史倉庫中復制檔案到索引,而不動作業目錄(即
git reset命令既可以回退版本,也可以把暫存區的修改回退到作業區),它應該只被用于 本地 修改,切記不應該在和其他開發者共享的分支執行 reset,# 移動分支位置,并清空暫存區跟蹤檔案 $ git reset <commit> # 默認為--mixed # 移動分支位置,并清空作業區和暫存區跟蹤檔案 $ git reset --hard <commit> # 只移動分支位置 $ git reset --soft <commit> # 分支位置不變,清空暫存區跟蹤檔案 $ git reset # 分支默認為HEAD # 分支位置不變,清空暫存區指定的跟蹤檔案 $ git reset <file> # 分支默認為HEAD,把暫存區的修改撤銷掉(unstage),重新放回作業區 $ git reset # 撤銷所有暫存區域檔案, -
revert(撤銷)
在遠程分支中可以通過撤銷某個提交引入的更改,revert操作方式是在最后加上一個撤銷了此更改的新提交,而不是從專案歷史中移除這個提交,這避免了Git丟失專案歷史,而且對于版本歷史和協作的可靠性來說是很重要的,
git revert用于在公共的分支上撤銷提交,git reset用于復位本地的歷史變更
因為兩個命令的目的不同,它們的實作也不一樣:reset完全地移除了一堆更改,revert保留了原來的更改,用一個新的提交來實作撤銷,# 創建一個新的commit,此commit將要revert的commit記錄中的變更都撤銷 $ git revert <commit> -
clean
git clean命令將未被 Git 跟蹤的檔案從作業目錄中移除,與rm作用一樣,只是提供了簡便方式,
git clean命令經常和git reset --hard一起使用,新增的檔案沒有被加入暫存區,它們不會被git reset --hard影響,必須使用git clean洗掉它們,此操作請謹慎使用,# 告訴你哪些檔案在命令執行后會被移除,而不是真的洗掉它, $ git clean -n # 移除當前目錄下未被跟蹤的檔案 $ git clean -f # 移除未跟蹤的檔案,但限制在某個路徑下, $ git clean -f <path> # 移除未跟蹤的檔案,以及目錄, $ git clean -df -
reflog
git relog在 HEAD 更新時(如切換分支、拉取新更改、重寫歷史或只是添加新的提交),參考日志都會添加一個新條目(操作記錄包括對應的commitId),它的作用在于在執行某些撤銷變更、rebase等命令后某些commit記錄無法在log中查詢到,但又需要對其恢復則可使用此功能來找尋其對應的commit記錄,
分支與Tag
# 查詢本地已有分支
$ git branch
# 查詢本地已有分支并輸出詳細資訊
$ git branch -v
# 查詢已存在的本地分支及遠程分支并輸出詳細資訊
$ git branch -av
# 創建分支
$ git branch <new-branch> # 默認以HEAD當前最終指向的Commit為基準
$ git branch <new-branch> <commitId/branch> # 已指定的Commit或分支為基準
# 洗掉分支
$ git branch -d <branch> # 如果執行報錯(報此分支還沒有被完全merge,也就是還沒合并到上游分支或HEAD上,洗掉會存在代碼丟失風險),如已確認洗掉對當前工程無影響,則可改用 -D 強制洗掉
# 切換分支
$ git checkout <branch>
$ git switch <branch> # 新版本Git提供,此命令從字面上更容易理解,不易混淆
# 創建并切換分支
$ git checkout -b <new-branch> <commitId/branch> # 如不指定commitId或branch默認以HEAD當前最終指向的Commit為基準
$ git switch -c <new-branch> <commitId/branch> # 新版本Git提供,此命令從字面上更容易理解,不易混淆
# 以遠程分支為基準創建分支
$ git checkout --track origin/hack
# 使用 Tag 標識當前commit
$ git tag <tag-name>
merge 與 rebase
如果期望提交歷史以線性結構展示則使用rebase,merge操作對合并要求更寬松但展示的歷史結構會復雜
$ git merge <branch>
$ git rebase <branch>
# 終止變基操作
$ git rebase --abort
# 繼續變基操作
$ git rebase --continue
# 使用配置的merge工具來解決沖突
$ git mergetool
# 解決沖突后追蹤已解決的檔案
$ git add <resolved-file>
# 解決沖突后洗掉已解決的檔案
$ git rm <resolved-file>
臨時緊急任務處理
作業只進行到一半,還沒法提交,預計完成還需1天時間,但是,必須在兩個小時內修復該bug,怎么辦?
幸好Git還提供了一個stash功能,可以把當前作業現場“儲藏”起來,等以后恢復現場后繼續作業:
# stash 是堆疊結構,執行pop后會將最上面也就是最新存盤的彈出來
? Git_Learning git:(rebase) ? git stash
Alias tip: g stash
Saved working directory and index state WIP on rebase: 3fb18e1 add test
? Git_Learning git:(rebase) git stash list
Alias tip: gstl
? Git_Learning git:(rebase) git --no-pager stash list
Alias tip: g --no-pager stash list
stash@{0}: WIP on rebase: 3fb18e1 add test
? Git_Learning git:(rebase)
# git stash apply 只應用堆疊上最新的代碼但不將其從stash中洗掉,即apply可多次執行
#pop即應用代碼又洗掉
? Git_Learning git:(rebase) git stash pop
Alias tip: gstp
On branch rebase
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (fdee00b4363f4a3d718ee0aa0825a52bff036d1d)
git stash list 命令顯示的最左一列顯示的是 stash 的序號,如stash@{2} 和 stash@{1},序號中數字大的代表的是較早的stash,我們pop的時候可以加具體的序號,不加序號的(預設情況下)為 stash@{0},用法:git stash pop stash@{2} git stash pop = git stash pop stash@{0}
注意 git stash不會保存作業區新增的檔案(未被跟蹤的),需執行git stash push -u -m '指定stash名字'
cherry-pick
在master分支上修復了bug后,因為dev分支是從master分支分出來的,所以,這個bug在當前dev分支上也存在,
那怎么在dev分支上修復同樣的bug?重復操作一次,提交不就行了?
為了方便操作,Git專門提供了一個cherry-pick命令,讓我們能復制一個特定的提交到當前分支:
$ git cherry-pick 3fb18e1
[master b257a0d] fix bug
1 file changed, 1 insertion(+), 1 deletion(-)
Git自動給dev分支做了一次提交,注意這次提交的commit是b257a0d,它并不同于master的3fb18e1,因為這兩個commit只是改動相同,但確實是兩個不同的commit,用git cherry-pick,我們就不需要在dev分支上手動再把修bug的程序重復一遍,
遠程倉庫
# 顯示本地與遠程倉庫的連接
$ git remote
# 顯示本地與遠程倉庫連接詳細資訊
$ git remote -v
# 創建一個新的遠程倉庫連接,執行clone操作時,會自頂創建與遠程倉庫的連接,名稱為origin
$ git remote add <name> <url>
# 移除名為 <name> 的遠程倉庫的連接,
$ git remote rm <name>
# 重命名遠程連接
$ git remote rename <old-name> <new-name>
# 將本地分支與遠程分支關聯
$ git branch --set-upstream <upstream> [<branchname>]
# 將本地分支與遠程分支取消關聯
$ git branch --unset-upstream [<branchname>]
# 拉取遠程倉庫指定分支的到本地,但不與本地當前分支合并,如不指定<branch>則拉取遠程倉庫的所有分支參考
$ git fetch <remote> <branch>
# 拉取當前分支對應的遠程副本中的更改,并立即并入本地副本(前提本地分支與遠程分支已關聯upstream)
$ git pull <remote>
# 等價于
$ git fetch && git merge
# 底層使用 rebase 合并遠程分支和本地分支,而不是使用 merge,--rebase 標記可以用來保證線性的專案歷史,防止合并提交(merge commits)的產生,很多專案組傾向于使用 rebase 而不是 merge,因為提交歷史更簡潔明了
$ git pull --rebase <remote>
# 本地倉庫master分支推動到遠程倉庫的master分支
$ git push origin master:master # :前面的是本地分支名稱,:后面的是遠端分支的名稱,
# 本地倉庫master分支推送到遠端關聯分支
$ git push origin master # 后面沒帶遠端的master,那是因為git已經為本地的master分支和遠端的分支建立了所謂的 upstream 的關聯,它知道本地master對應遠端的master分支
# 將當前本地倉庫所在分支提交到遠程相同分支下
$ git push # origin 是預設的 remote url;
# 強制推送,集成分支不推薦使用
$ git push -f
# 將本地所有分支推送到遠程倉庫
$ git push -all
# 推送分支時,標簽不會被自動推送上去,`--tags` 將你所有的本地標簽推送到遠程倉庫中去,
$ git push --tags
Git集成使用禁忌
前面提到過在本地分支執行 git checkout/git reset時要謹慎,下面的命令在多人協作的集成環境下則完全禁止使用
- 禁止向集成分支執行
git push -f - 禁止向集成分支執行變更 commit 歷史的操作,如
reset、rebase
參考
Git官方教程
廖雪峰Git教程
圖解Git
文章持續更新,可以微信搜一搜「 DHC的博客 」第一時間閱讀,若有錯誤或者不當之處,還請大家留言指正,一起學習交流!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/173116.html
標籤:Java
上一篇:解讀JVM虛擬機
