1. 程式人生 > >Git彙總--版本庫操作

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 loggit 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配置檔案中某個配置的值。