Git彙總--版本庫操作
版本庫操作
日誌–log
顯示提交歷史! 當不使用任何引數呼叫,相當於使用了預設的引數HEAD,即顯示當前HEAD能夠訪問到的所有歷史提交。可以指定某個遠端或者分支進行檢視:
$ git log upstream/master
引數 | 說明 |
---|---|
--oneline |
最精簡的日誌輸出 |
--graph |
分支圖顯示 |
-<n> |
顯示最近的幾條日誌 |
--stat |
顯示每次提交的變更概要 |
--all |
顯示所有分支的歷史記錄 |
提交–commit
commit 分為兩種:一種是常規的 commit,也就是使用 git commit
提交的 commit;另一種是 merge commit,在使用 git merge
合併兩個分支之後,你將會得到一個新的 merge commit。
merge commit 和普通 commit 的不同之處在於 merge commit 包含兩個 parent commit,代表該 merge commit 是從哪兩個 commit 合併過來的。
# cdc4a1為merge生成的commitID
$ git show cdc4a1
commit cdc4a1c252171c24b7c98b92a6fb94010637de44 (HEAD -> hotfix-inspection, origin/hotfix-inspection)
Merge: 81cf95f5 41cc17e6
使用命令git describe
將提交顯示為一個易記的名稱。
- 這個易記的名稱來自於建立在該提交上的里程碑;
- 如果提交沒有對應的里程碑,但是在其祖先版本上建有里程碑,則使用類似
<tag>-<num>-g<commit>
(“基礎版本號” - 距離“基礎版本”的數字 - 該提交的SHA1雜湊值縮寫)的格式顯示; - 如果提交本身沒有包含里程碑,其祖先版本上也沒有里程碑,可以通過傳遞
--always
引數顯示精簡提交ID,否則出錯。
$ git describe
v2.0.0-143-gcffed5c2
補充:最後一次的提交資訊,會儲存在.git/COMMIT_EDITMSG
中,這對於對提交資訊格式校驗很有幫助, 具體可以檢視:Git提交資訊規範化
$ cat .git/COMMIT_EDITMSG
feat(git): git總結
可以在commit命令後加引數-s
,為在提交說明的最後新增“Signed-off-by:”簽名。
$ git commit -s -m "提交說明"
沒有對工作區的檔案進行任何修改,Git預設不會執行提交,引數--allow-empty
允許執行空白提交。
# 重新修改最新的提交,改正作者和提交者的錯誤資訊
$ git commit --amend --allow-empty --reset-author
提交空資料夾: 預設情況下,Git不能對空資料夾進行提交。所以,我們可以在相關資料夾下建立一個檔案
.gitkeep
來進行佔位。
建議:
- 一次提交只幹一件事
- 每次提交儘量完整,可以使用
git stash
或其他分支保持當前進度 - 儘量保持暫存區和HEAD一致(即add後的內容,及時commit)
對比變更
上述提到的工作區、暫存區、HEAD,如何做比較呢?
命令 | 說明 |
---|---|
git diff |
工作區和暫存區比較 |
git diff --cached/--staged |
暫存區和HEAD(master)比較 |
git diff HEAD/master |
工作區和HEAD(當前工作分支,注意非遠端) |
獲取–fetch or pull
fetch
從另一個儲存庫下載物件和引用。
在執行git fetch命令的時候,可以通過 --no-tags
引數設定不獲取里程碑只獲取分支及提交
$ git fetch --no-tags
或在註冊遠端版本庫的時候,使用--no-tags
引數避免將遠端版本庫的里程碑引入本地版本庫
$ git remote add --no-tags
獲取的引用名稱及其指向的物件名稱將寫入.git / FETCH_HEAD
中。
$ cat .git/FETCH_HEAD
7ccd4971011fe8b0df6599efb38d011f25975979 branch 'master' of x.x.x.x:namespace/project-name
git fetch origin 就相當於執行了下面的命令,將遠端版本庫的所有分支複製為本地的遠端分支
$ git fetch origin +refs/heads/*:refs/remotes/origin/*
示例:合併upstream/master提交到本地
獲取到的提交會更新到本地跟蹤共享版本庫(遠端)master分支的本地引用.git/refs/remotes/upstream/master
中
$ git fetch upstream master
# 合併操作
$ git merge upstream/master
pull
git pull
又都做了哪些操作?
$ git config --list
remote.origin.url=[email protected]:project-namespace/project-name.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
# 另外一個分支
branch.feature-v2.1.remote=origin
branch.feature-v2.1.merge=refs/heads/feature-v2.1
# 另外一個遠端
remote.upstream.url=[email protected]:project-namespace/project-name.git
remote.upstream.fetch=+refs/heads/*:refs/remotes/upstream/*
$ git pull
- 不帶引數執行
git pull
相當於執行了git pull <remote>
- 當前分支未設定
branch.<branchname>.remote
,則為origin - 獲取的遠端版本庫的URL地址由
remote.<remote>.url
給出 - 如果為註冊的遠端版本庫設定了fetch引數,即通過
remote.<remote>.fetch
配置了一個引用表示式,則使用該引用表示式執行獲取操作 - 合併的分支,如果設定了
branch.<branchname>.merge
,則對其設定的分支執行合併,否則報錯退出
所以,上述fetch示例可以通過下述命令替代
$ git pull upstream master
推送–push
$ git push <remote> <branchname>
- 不帶引數執行git push相當於執行了
git push <remote>
- 當前分支未設定
branch.<branchname>.remote
,則為origin - 如果為註冊的遠端版本庫設定了push引數,即通過
remote.<remote>.push
配置了一個引用表示式,則使用該引用表示式執行推送 - 否則使用“:”作為引用表示式。該表示式的含義是同名分支推送,即對所有在遠端版本庫有同名分支的本地分支執行推送。
$ git push branch.<branchname>.remote branchname
個人建議:
push預設的規則使得
git push
可以像git push origin <currentBranch>
一樣的效果,但是個人並不建議這種操作(你要明確你的確是想向origin提交;然後你的本地分支和遠端分支名稱必須一致<當然強烈建議建立分支本地和遠端保持一致!>),以上,還是希望提供完整的命令!
重置–reset or reflog or revert
.git/refs/heads/<branch>
中記錄了其分支中對應的最新提交ID,下述為master分支最新提交ID:
$ cat .git/refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
reset
重置命令git reset
的一個用途就是修改引用(如master)的遊標。可以將“遊標”指向任意一個存在的提交ID。
$ cat .git/HEAD
ref: refs/heads/master
在執行重置命令的時候沒有使用任何引數對所要重置的分支名進行設定,這是因為重置命名實際上所針對的是頭指標HEAD。
方式一:不會重置引用
$ git reset [-q] [<commit>] [--] <paths>...
包含了路徑<paths>
的用法。不會重置引用,更不會改變工作區,而是用指定提交狀態(<commit>
)下的檔案(<paths>
)替換掉暫存區中的檔案。
$ git reset HEAD/commitID [--] <filePaths>
注意: 為了避免路徑和引用(或者提交ID)同名而衝突,可以在<paths>
前用兩個連續的短線作為分隔。
方式二:重置引用
$ git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
不使用路徑<paths>
的用法。會重置引用。根據不同的選項,可以對暫存區或者工作區進行重置。會徹底的丟棄歷史(git log
檢視不到提及歷史)。
引數 | 影響範圍 |
---|---|
–hard | 引用指向新的提交ID,替換暫存區和工作區 |
–soft | 只更改引用的指向,不改變暫存區和工作區 |
–mixed(預設即為--mixed ) |
更改引用的指向以及重置暫存區,但是不改變工作區 |
$ git reset --hard HEAD^
$ git reset --hard <commitID>
!!!注意: 使用重置命令很危險,會徹底的丟棄歷史。那麼還能夠通過瀏覽提交歷史的辦法找到丟棄的提交ID,再使用重置命令恢復歷史麼?不可能!因為重置讓提交歷史也改變了。在一個共享的倉庫中這會造成問題的。如果其他人已經有你將要重寫的提交,你應當避免使用 reset;如果有任何其他提交在合併之後建立了,那麼這個方法也會無效;移動引用實際上會丟失那些改動。
reflog
顯示操作歷史!
上述reset的第二種方式會丟失歷史,如果真的做了上述操作,該如何還原呢?
$ ll .git/logs
-rw-r--r-- 1 ligang staff 111K 11 30 15:02 HEAD
drwxr-xr-x 5 ligang staff 160B 11 29 09:44 refs
通過.git/logs
目錄下日誌檔案記錄了分支的變更。預設非裸版本庫(帶有工作區)都提供分支日誌功能,這是因為帶有工作區的版本庫都有如下設定。
$ git config core.logallrefupdates
Git提供了一個**git reflog
** 命令,對這個檔案進行操作。使用show
子命令可以顯示此檔案的內容。
$ git reflog show HEAD
61f26bdb (HEAD -> master) [email protected]{0}: commit: camile.txt
622c9bd0 [email protected]{1}: reset: moving to HEAD^
4e0e3777 [email protected]{2}: commit: camile.txt
622c9bd0 [email protected]{3}: reset: moving to HEAD^
注意: 使用git reflog
的輸出和直接檢視日誌檔案最大的不同在於顯示順序的不同,即最新改變放在了最前面顯示,而且只顯示每次改變的最終的SHA1雜湊值
# 還原某個提交!!
$ git reset --hard [email protected]{2}
git log
與git reflog
區別:
git log
顯示提交歷史;git reflog
顯示操作歷史(包括已經被刪除的 commit 記錄和 reset 的操作)。
revert
revert 命令十分直觀易用,相當於做一次被 revert 的提交的「反操作」並形成一個新的 commit。
上面commit中提到了,commit分為常規commit和merge的commit兩種。所以,revert也對應兩種方式:
常規commit
使用 git revert <commit id>
即可,git 會生成一個新的 commit,將指定的 commit 內容從當前分支上撤除
merge的commit
revert merge commit 這時需要新增 -m
選項以代表這次 revert 的是一個 merge commit,-m 選項接收的引數是一個數字,數字取值為 1 和 2,也就是 Merge 行裡面列出來的第一個還是第二個。
$ git log
commit e6930af365f89b2bdc47d4e190b0e7e1c10bc0b8
Merge: e215da30 fb5baba1
# 具有merge標識,是從e215da30和fb5baba1兩個commit合併而來
$ git revert -m 1 e6930af365f89b2bdc47d4e190b0e7e1c10bc0b8
注意: revert後的程式碼想在合併進來,必須進行反向revert,否則會丟掉相關內容!http://blog.psjay.com/posts/git-revert-merge-commit/
檢出–checkout
git checkout
會重寫工作區。
方式一:包含了路徑<paths>
的用法
不會改變HEAD頭指標,主要是用於指定版本的檔案覆蓋工作區中對應的檔案
$ git checkout [-q] [<commit>] [--] <paths>...
<commit>
是可選項,如果省略則相當於從暫存區(index)進行檢出。這和上一章的重置命令大不相同:重置的預設值是 HEAD,而檢出的預設值是暫存區。因此重置一般用於重置暫存區(除非使用--hard
引數,否則不重置工作區),而檢出命令主要是覆蓋工作區(如果<commit>
不省略,也會替換暫存區中相應的檔案)。
方式二:不使用路徑<paths>
的用法
會改變HEAD頭指標。之所以後面的引數寫作<branch>
,是因為只有HEAD切換到一個分支才可以對提交進行跟蹤,否則仍然會進入“分離頭指標”的狀態。在“分離頭指標”狀態下的提交不能被引用關聯到而可能會丟失。
$ git checkout [<branch>]
關於 ”分離頭指標“
HEAD指向的提交將作為新提交的父提交,檢視當前HEAD的指向。
$ cat .git/HEAD
分離頭指標,指的就是HEAD頭指標指向了一個具體的提交ID,而不是一個引用(分支)。
$ git log --pretty=oneline -1 6f44ded81092794fc186e7869eadb4ca5ef225bb (HEAD -> master) test2 $ git checkout 6f44de # reflog是HEAD頭指標的變遷記錄,目前是非master分支 $ git reflog -1 6f44ded8 (HEAD, master) [email protected]{0}: checkout: moving from master to 6f44de $ cat .git/HEAD 6f44ded81092794fc186e7869eadb4ca5ef225bb
方式三:建立和切換到新的分支
新的分支從<start_point>
指定的提交開始建立。新分支和我們熟悉的master分支沒有什麼實質的不同,都是在refs/heads
名稱空間下的引用。
$ git checkout [-m] [[-b|--orphan] <new_branch>] [<start_point>]
注意,這裡不能對遠端分支進行checkout,遠端分支不是真正意義上的分支,是類似於里程碑一樣的引用。如果針對遠端分支執行檢出命令,會看到大段的錯誤警告。
分支–branch
# 基於遠端分支建立本地分支的過程中
$ git checkout feature-2.2
$ git checkout -b feature-2.2 origin/feature-2.2
-
刪除本地分支
$ git branch -d/-D develop
-
刪除遠端分支
冒號前面的空格不能少,原理是把一個空分支push到server上,相當於刪除該分支。
$ git push origin :develop
-
刪除本地倉庫相對於遠端origin不存在的倉庫
$ git remote prune origin
merge
會保留修改內容的歷史記錄。--no-ff
(non fast-forward)總會生成一個merge commit,不新增該引數時,只有衝突時才會生成merge commit。
rebase
rebase 是在原有提交的基礎上將差異內容反映進去。這個時候可能會有衝突,當出現衝突時,解決衝突後的提交不是使用 commit
命令,而是執行 rebase
命令指定 --continue
選項。若要取消 rebase,指定 --abort
選項。
注意: 變基操作的 實質是丟棄一些現有的提交,然後相應地新建一些內容一樣但實際上不同的提交。 如果你已經將提交推送至某個倉庫,而其他人也已經從該倉庫拉取提交併進行了後續工作,此時,如果你用 git rebase 命令重新整理了提交併再次推送,你的同伴因此將不得不再次將他們手頭的工作與你的提交進行整合,如果接下來你還要拉取並整合他們修改過的提交,事情就會變得一團糟。
merge還是rebase?
有一種觀點認為,倉庫的提交歷史即是 記錄實際發生過什麼。 它是針對歷史的文件,本身就有價值,不能亂改,這些痕跡就應該被保留下來,讓後人能夠查閱。從這個角度看來,改變提交歷史是一種褻瀆。
另一種觀點則正好相反,他們認為提交歷史是 專案過程中發生的事。 沒人會出版一本書的第一版草稿,軟體維護手冊也是需要反覆修訂才能方便使用。
總的原則是,只對尚未推送或分享給別人的本地修改執行變基操作清理歷史,從不對已推送至別處的提交執行變基操作,這樣,你才能享受到兩種方式帶來的便利。
cherry-pick
$ git cherry-pick <commitID>
從眾多的提交中挑選出一個提交應用在當前的工作分支中。操作過程相當於將該提交匯出為補丁檔案,然後在當前HEAD上重放形成無論內容還是提交說明都一致的提交。對於從一個分支單獨一個或者兩個提交而不是合併整個分支的所有變更是非常有用的。可以通過git cherry-pick --abort
取消cherry-pick操作。
里程碑–tag
https://blog.csdn.net/ligang2585116/article/details/46468709
配置–config
版本庫級別的配置檔案 ~/.gitconfig
,對應.git/config
$ git config -e
全域性配置檔案--global
(使用者主目錄下) ~/Documents/github/Blog/.git/config
$ git config -e --global
系統級配置檔案--system
(/etc目錄下) /private/etc/gitconfig
$ git config -e --system
git config
使用INI檔案格式。
- 使用命令
$ git config <section>.<key>
,來讀取INI配置檔案中某個配置的鍵值; - 使用命令
$ git config <section>.<key> <value>
,來更改和設定INI配置檔案中某個配置的值。