Git 學(xué)習(xí)筆記(CheatSheet)(二)

Git 思維導(dǎo)圖

高級(jí)技巧

下面介紹幾個(gè) Git 中非常強(qiáng)大的命令逸嘀,借助這些命令我們可以完成一些非常有用的操作。

git reflog

Git 對(duì)于本地倉庫的操作都進(jìn)行了日志記錄些膨,日志文件存儲(chǔ)在目錄.git/logs

$ tree .git/logs
.git/logs
├── HEAD             # HEAD 指針變更記錄
└── refs
    ├── heads        # 本地分支記錄
    │   ├── master
    ├── remotes      # 遠(yuǎn)程分支記錄
    │   └── origin
    │       └── HEAD
    │       └── master
    └── stash        # stash 記錄

從 Git 的日志目錄中可以看出浦马,Git 主要對(duì)一些引用文件進(jìn)行了日志記錄,包括HEAD指針殊校、分支和儲(chǔ)藏更改記錄晴玖。當(dāng)本地更改了這些指針指向時(shí),該操作就會(huì)被記錄到對(duì)應(yīng)的日志文件中为流。比如呕屎,切換分支會(huì)導(dǎo)致HEAD指針指向變化,則該操作會(huì)被記錄到日志文件.git/logs/HEAD中敬察,比如秀睛,master分支添加或刪除commit時(shí)會(huì)同時(shí)導(dǎo)致master指針和HEAD指針指向變化,則該操作會(huì)同時(shí)被記錄到.git/logs/refs/heads/master.git/logs/HEAD日志文件中...

由于日志記錄了所有指針莲祸、分支和儲(chǔ)藏的變更操作蹂安,因此椭迎,本地倉庫的所有提交都不會(huì)丟失,可隨時(shí)從這些日志文件中索取得回田盈。比如畜号,假設(shè)我們?cè)诋?dāng)前分支指向了回退操作,那么當(dāng)前分支的日志記錄git log會(huì)丟失被回退的那些提交允瞧,如果想找回這些提交简软,搜索該分支日志文件即可。當(dāng)然瓷式,實(shí)際操作中替饿,我們無需手動(dòng)查詢這些日志文件(雖然這些日志文件都是文本文件),因?yàn)?Git 提供了相關(guān)命令可以讓我們直接查詢相應(yīng)日志記錄贸典,該命令為git reflog视卢,其具體語法如下所示:

# 查詢?nèi)罩?git reflog [show] [<ref>]

# 刪除過期日志
git reflog expire [<ref>]

# 刪除日志條目
git reflog delete ref@{specifier}

# 檢測(cè)引用是否存在日志文件
git reflog exists <ref>

大多數(shù)情況下我們都是使用git reflog [show]來查看日志記錄,因此下面我們具體介紹常用的一些查詢操作:

  • 查詢HEAD指針變更操作:查詢HEAD指針變更日志廊驼,如下所示:

    # 日志呈棧結(jié)構(gòu)据过,即最近的操作顯示在前(棧頂),初始操作顯示在最后面(棧底)
    $ git reflog show HEAD
    5279645 (HEAD -> master, dev) HEAD@{0}: checkout: moving from dev to master
    5279645 (HEAD -> master, dev) HEAD@{1}: reset: moving to HEAD
    db8348b HEAD@{4}: commit: feat: 222
    5279645 (HEAD -> master, dev) HEAD@{5}: commit (initial): feat: 111
    

    :由于查詢HEAD指針變更日志是最常使用的操作妒挎,因此绳锅,默認(rèn)情況下,執(zhí)行不帶參數(shù)的git reflog就相當(dāng)于執(zhí)行git reflog show HEAD酝掩。

  • 查詢分支提交變更:分支增加提交或回退提交等操作都會(huì)被記錄到日志中鳞芙,因此我們查詢分支的變更記錄:

    # 查詢 master 指針變更記錄
    $ git reflog show master
    5279645 (HEAD -> master, dev) master@{0}: reset: moving to HEAD~1     # 回退操作
    db8348b master@{1}: commit: feat: 222                                 # 增加提交
    5279645 (HEAD -> master, dev) master@{2}: commit (initial): feat: 111 # 初始提交
    
  • 查詢儲(chǔ)藏變更操作:當(dāng)我們執(zhí)行git stash命令時(shí),stash指針會(huì)被更改期虾,因此這些操作也都會(huì)被記錄到日志中原朝。查詢儲(chǔ)藏變更命令如下:

    $ git reflog show stash
    0ba206e (refs/stash) stash@{0}: WIP on master: 5279645 feat: 111
    54044f5 stash@{1}: WIP on dev: 5279645 feat: 111
    
  • 日志時(shí)間過濾:日志文件每條記錄都帶有一個(gè)時(shí)間戳,因此我們可以對(duì)日志記錄進(jìn)行時(shí)間過濾镶苞,只顯示某個(gè)時(shí)間范圍內(nèi)的記錄喳坠。

    Git 提供了一些時(shí)間標(biāo)識(shí)符方便我們進(jìn)行時(shí)間過濾,常見的時(shí)間標(biāo)識(shí)符如下表所示:

    時(shí)間標(biāo)識(shí)符 含義
    1.minute.ago 1分鐘之前
    1.hour.ago 1小時(shí)之前
    yesterday 昨天
    1.week.ago 一周之前
    1.month.ago 一月之前
    1.year.ago 1小時(shí)之前
    2020-05-18.09:00:00 2020-05-18 09:00:00

    :時(shí)間標(biāo)識(shí)符也支持復(fù)數(shù)形式茂蚓,比如:5.hours.ago表示 5 小時(shí)之前壕鹉。
    :時(shí)間標(biāo)識(shí)符支持聯(lián)合使用,比如:1.day.2.hours.ago表示 1 天 2 小時(shí)之前聋涨。

    舉個(gè)例子:比如查詢master分支 1 小時(shí) 17 分鐘之前的操作:

    $ git reflog master@{1.hour.17.minutes.ago}
    5279645 (HEAD -> master, dev) master@{Sat Jan 9 23:18:05 2021 +0800}: commit (initial): feat: 111
    

最后晾浴,日志文件只存在于本地倉庫中,不會(huì)上傳到遠(yuǎn)程倉庫牍白,并且脊凰,日志條目默認(rèn)只有 90 天有效期限,過期條目可能會(huì)被自動(dòng)刪除(因?yàn)槟承┟顣?huì)觸發(fā)git gc操作)淹朋,也可以通過手動(dòng)執(zhí)行git gcgit reflog expire刪除過期條目笙各。如果想更改日志條目有效期限,可以設(shè)置gc.reflogExpire配置或執(zhí)行git reflog expire --expire=<time>手動(dòng)指定時(shí)間础芍。比如杈抢,下面的命令將刪除 1 分鐘之前的所有日志條目:

# --all 表示清除所有日志,也可以指定清除特定日志文件仑性,比如 HEAD惶楼、master...
$ git reflog expire --expire=1.minute.ago --all

git rebase

git rebase是 Git 提供的一個(gè)具備非常強(qiáng)大功能的命令,rebase中文翻譯為『變基』诊杆,見名知意歼捐,即git rebase可以更改基準(zhǔn)點(diǎn),比如晨汹,一個(gè)分支從另一個(gè)分支某個(gè)提交上創(chuàng)建豹储,使用git rebase可以更改該分支基準(zhǔn)點(diǎn),使新分支從另一個(gè)提交上創(chuàng)建延伸淘这,擴(kuò)展來說剥扣,git rebase不僅僅可以用于修改分支基準(zhǔn)點(diǎn),它還具備修改分支提交歷史記錄铝穷,比如刪除钠怯、合并、更換提交...

git rebase的具體語法如下所示:

git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)

上述命令可以簡(jiǎn)化為如下格式:

# 將 topic_branch 變基到 base_branch 的最新提交曙聂,
# 當(dāng)未指定 topic_branch 時(shí)晦炊,則默認(rèn)變基當(dāng)前分支
git rebase [base_branch] [topic_branch]

# 交互式變基
git rebase -i [branch]

# 變基(繼續(xù) | 跳過 | 停止 | 退出 | 繼續(xù)編輯 | 顯示當(dāng)前差異包)
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)

下面我們主要對(duì)git rebase的兩個(gè)主要功能進(jìn)行講解:

  • 分支合并:可以使用rebase模式進(jìn)行分支合并。

    默認(rèn)情況下宁脊,Git 使用merge模式進(jìn)行分支合并断国,該方式會(huì)基于當(dāng)前合并的兩個(gè)分支最新提交以及兩者之間的公共提交做一個(gè)簡(jiǎn)單三路合并,生成一個(gè)合并提交點(diǎn)朦佩,這種情況下并思,當(dāng)我們查看歷史記錄時(shí),會(huì)看到有向無環(huán)圖存在语稠,且各分支結(jié)點(diǎn)以提交時(shí)間順序進(jìn)行排列宋彼。比如對(duì)于如下示意圖:

    separate branch to be merged

    如果我們想master分支合并dev分支,執(zhí)行如下命令:

    $ git switch master
    Switched to branch 'master'
    
    $ git merge dev -m 'C5: merge branch dev'
    Merge made by the 'recursive' strategy.
     3.txt | 1 +
     1 file changed, 1 insertion(+)
     create mode 100644 3.txt
    
    $ git log --format='%h - %cd : %s' --date=format:'%Y-%m-%d %H:%M:%S' --graph
    *   1069fb9 - 2021-01-11 01:10:01 : C5: merge branch dev
    |\
    | * 7ee751d - 2021-01-11 01:09:28 : dev: C3
    * | 3c29c30 - 2021-01-11 01:09:50 : master: C4
    |/
    * 44b275c - 2021-01-11 01:08:47 : master: C2
    * 5454078 - 2021-01-11 01:08:29 : master: C1
    

    :如果我們使用--graph進(jìn)行查看仙畦,可以看到输涕,不同分支之間的提交不一定按時(shí)間順序進(jìn)行排列(比如上述代碼中C3早于C4,但卻排在C4后面)慨畸,這是因?yàn)榉种创蚱嚼晨玻燥@示效果有點(diǎn)異常,如果不使用--graph寸士,則可以以時(shí)間順序正常排列各個(gè)提交檐什。

    此時(shí)的示意圖如下所示:

    git merge

    以上就是merge模式合并結(jié)果碴卧,接下來我們來看下rebase模式合并分支效果:

    1. 首先現(xiàn)在我們將倉庫回退到未合并之前的狀態(tài):

      $ git reset --hard HEAD~1
      HEAD is now at 3c29c30 master: C4
      

      此時(shí)倉庫的示意圖如下所示:

      separate branch to be rebase
    2. 然后將dev變基,將基準(zhǔn)點(diǎn)移動(dòng)到master分支最新提交點(diǎn)上:

      $ git switch dev
      Switched to branch 'dev'
      
      # 變基前的分支提交記錄
      $ git log --format='%h - %cd : %s' --date=format:'%Y-%m-%d %H:%M:%S'
      7ee751d - 2021-01-11 01:09:28 : dev: C3
      44b275c - 2021-01-11 01:08:47 : master: C2
      5454078 - 2021-01-11 01:08:29 : master: C1
      
      # 變基
      $ git rebase master
      Successfully rebased and updated refs/heads/dev.
      
      # 變基后的分支提交記錄
      $ git log --format='%h - %cd : %s' --date=format:'%Y-%m-%d %H:%M:%S'
      ca22a79 - 2021-01-11 01:12:55 : dev: C3     # 注意 C3 的哈希值被更改了
      3c29c30 - 2021-01-11 01:09:50 : master: C4
      44b275c - 2021-01-11 01:08:47 : master: C2
      5454078 - 2021-01-11 01:08:29 : master: C1
      

      git rebase時(shí)可能會(huì)存在沖突乃正,此時(shí)解決完沖突后住册,只需執(zhí)行git add .,然后執(zhí)行git rebase --continue繼續(xù)變基過程即可瓮具。

      通過查看變基前和變基后的dev分支提交歷史荧飞,我們可以看到,變基前名党,dev分支是基于C2提交點(diǎn)的叹阔,而變基后,dev分支是基于C4提交點(diǎn)的传睹,也就是git rebase master會(huì)將dev分支基準(zhǔn)點(diǎn)移動(dòng)到master分支最新提交處耳幢。

      此時(shí)倉庫的示意圖如下所示:

      git rebase

      這里簡(jiǎn)單介紹下git rebase的實(shí)現(xiàn)原理:以本例子進(jìn)行闡述,當(dāng)執(zhí)行git rebase master時(shí)蒋歌,Git 首先會(huì)找到這兩個(gè)分支(即devmaster分支)的最近共同祖先提交C2帅掘,然后將當(dāng)前分支(即dev分支)超前共同祖先C2的所有提交一一打散并提取出相應(yīng)的修改保存為臨時(shí)patch文件,文件存儲(chǔ)在.git/rebase目錄下堂油;然后將當(dāng)前分支指向master分支最新提交點(diǎn)C4上修档;最后,依序?qū)?code>patch文件應(yīng)用到dev分支上府框,這個(gè)步驟相當(dāng)于重新播放之前dev分支的各個(gè)提交點(diǎn)進(jìn)行的修改操作吱窝,這樣就保證了生成新的提交的內(nèi)容是一致的(假設(shè)不存在沖突)。

    3. 此時(shí)master分支就可以合并dev分支:

      # 切換到 master 分支
      $ git switch master
      Switched to branch 'master'
      
      # 合并 dev 分支
      $ git merge dev -m 'C5: merge branch dev with rebase'
      Updating 3c29c30..ca22a79
      Fast-forward (no commit created; -m option ignored) # 采用 Fast-forward 模式合并
       3.txt | 1 +
       1 file changed, 1 insertion(+)
       create mode 100644 3.txt
      
      # 查看提交歷史迫靖,沒有分支合并信息
      $ git log --oneline --graph
      * ca22a79 (HEAD -> master, dev) dev: C3
      * 3c29c30 master: C4
      * 44b275c master: C2
      * 5454078 master: C1
      

      由于變基后院峡,dev分支和master分支位于同一條時(shí)間線上,因此系宜,git merge默認(rèn)采用Fast-forward模式合并分支照激,這樣分支合并信息就被消除了,提交歷史記錄呈現(xiàn)一條線盹牧,非常簡(jiǎn)潔俩垃。

      :前面我們說過,盡量禁用Fast-forward模式汰寓,以保留合并分支信息口柳,而rebase模式卻是打平分支,消除分支合并信息有滑,與我們的建議截然相反跃闹。其實(shí),分支合并需要依據(jù)具體場(chǎng)景進(jìn)行選擇,當(dāng)我們?cè)诒镜剡M(jìn)行開發(fā)時(shí)望艺,我們最好保留自己的分支合并信息苛秕,而在協(xié)同工作時(shí),如果我們直接git pull origin master找默,master分支就會(huì)前進(jìn)(假設(shè)拉取到新提交)想帅,這樣當(dāng)我們合并分支到master時(shí),其他人的提交就交織到我們的分支中啡莉,默認(rèn)以merge模式生成一個(gè)合并提交,通常來說旨剥,我們不希望合并其他人的提交咧欣,因?yàn)檫@樣會(huì)污染我們的分支,并且隨著合并次數(shù)增多轨帜,提交歷史會(huì)非称枪荆混亂,在這種情況下蚌父,采用rebase模式進(jìn)行分支合并就是一個(gè)不錯(cuò)的選擇哮兰。

      具體來說,rebase模式常用于協(xié)同開發(fā)苟弛,常見的場(chǎng)景有如下兩種:

      • 變基本地私有分支:當(dāng)我們基于master分支創(chuàng)建一個(gè)私有分支喝滞,并做了一些提交后,此時(shí)通常會(huì)先執(zhí)行git pull origin master拉取新提交膏秫,這樣本地master分支就前進(jìn)了右遭,然后對(duì)私有分支執(zhí)行git rebase master,將私有分支變基到master分支最新提交點(diǎn)處缤削,最后再合并到master分支窘哈,這樣就能消除其他人的提交對(duì)我們分支的污染,提交記錄呈一條線亭敢,沒有分叉滚婉,并且在一個(gè)區(qū)間內(nèi)都是我們自己的提交,不會(huì)在中間夾雜其他人的提交帅刀,這樣歷史記錄就非常清晰让腹。

      • 變基追蹤分支:當(dāng)我們對(duì)本地追蹤分支修改并進(jìn)行了多個(gè)提交后,如果要這些提交上傳到遠(yuǎn)程倉庫劝篷,首先都會(huì)git pull拉取遠(yuǎn)程倉庫更新哨鸭,但這樣做遠(yuǎn)程倉庫該分支的新提交(假設(shè)成功拉取到新提交)需要與我們的提交進(jìn)行一個(gè)merge操作,如下圖所示:

        track branch to be merged

        當(dāng)我們執(zhí)行git pull時(shí)娇妓,上述示意圖中像鸡,本地追蹤分支dev就會(huì)與遠(yuǎn)程分支origin/dev進(jìn)行一個(gè)分支合并,生成一個(gè)新的合并提交,如下圖所示:

        track branch merged

        我們并不想讓自己的提交與其他的提交交織在一起只估,因?yàn)檫@樣相當(dāng)于自己的提交被污染了志群,因此,對(duì)于追蹤分支的上傳蛔钙,推薦使用git pull --rebase方式拉取更新锌云,這種方式相當(dāng)于執(zhí)行git fetch && git rebase origin/dev,Git 會(huì)將我們的提交變基到遠(yuǎn)程分支origin/dev的最新提交點(diǎn)吁脱,如下圖所示:

        track branch rebased

        這樣桑涎,所有人的提交都不會(huì)互相污染,分支提交歷史呈一條線展示兼贡,且每個(gè)人的多個(gè)提交都集中在一個(gè)連續(xù)區(qū)間內(nèi)攻冷,方便查閱。

        :變基追蹤分支指的是更改本地已提交但未上傳到服務(wù)器的提交遍希,千萬不要變基已存在于服務(wù)器上的分支提交等曼,因?yàn)檫@相當(dāng)于修改了遠(yuǎn)程倉庫分支歷史,會(huì)導(dǎo)致協(xié)同開發(fā)產(chǎn)生問題凿蒜。

        :如果項(xiàng)目比較大禁谦,協(xié)同開發(fā)的人比較多,這種情況下废封,可能每天遠(yuǎn)程分支都有很多次新提交州泊,此時(shí)如果使用變基追蹤分支,可能存在過多沖突漂洋,解決這些沖突會(huì)非常耗時(shí)耗力拥诡,這種情況下,也許直接合并會(huì)更加簡(jiǎn)單氮发。

  • 交互式變基:交互式變基主要的作用就是用于更改分支提交歷史記錄渴肉。其語法如下所示:

    # 編輯 commit 之后的所有提交(不包含 commit)
    git rebase { -i | --interactive } <commit>
    

    交互式變基提供了一系列操作可以讓我們修改分支提交歷史記錄,具體操作如下表所示:

    操作 描述
    pick 保留該提交
    reword 修改該提交信息
    edit 編輯該提交
    squash 將該提交合并到前一個(gè)提交中(即合并到當(dāng)前提交的上一個(gè)舊提交中)
    fixup squash作用一致爽冕,但直接丟棄該提交信息
    exec (該行剩余部分)使用 shell 運(yùn)行命令
    break 到該提交處暫停變基(后續(xù)使用git rebase --contine從此處繼續(xù)開始變基)
    drop 移除該提交
    label 為當(dāng)前HEAD打一個(gè)標(biāo)記
    reset 重置HEAD到該標(biāo)記
    merge 合并提交

    git rebase -i <commit>需要指定一個(gè)提交commit仇祭,然后會(huì)彈出vi編輯模式以提交時(shí)間順序展示該commit之后的所有提交(即展示比該commit新的提交),比如颈畸,倒數(shù)第三個(gè)提交可以使用HEAD~2表示乌奇,則命令git rebase -i HEAD~2會(huì)展示最新的兩個(gè)提交,不包含HEAD~2眯娱。在編輯模式中礁苗,可以移動(dòng)提交行來更改提交順序,也可以刪除某個(gè)提交行徙缴,這樣改提交就會(huì)從提交歷史中進(jìn)行移除(但是不支持刪除全部提交,此時(shí) Git 會(huì)自動(dòng)停止變基)。

    下面列舉一些常用的分支歷史記錄修改操作:

    • reword:該操作可以修改提交信息潘靖。

      舉個(gè)例子:比如本地倉庫存在如下提交記錄:

      $ git log --oneline
      a75e892 (HEAD -> master) C3
      8837671 c2
      c830b70 C1
      

      可以看到,8837671提交的信息使用了小寫字母蚤蔓,如果我們希望將其修改為大寫卦溢,則可以如下進(jìn)行操作:

      # 首先啟動(dòng)交互式變基,展示前兩條提交
      $ git rebase -i HEAD~2
      

      上面命令執(zhí)行完后秀又,會(huì)彈出vi編輯模式单寂,其內(nèi)容如下所示:

      pick 8837671 c2
      pick a75e892 C3
      

      此時(shí),我們將c2對(duì)應(yīng)的提交8837671修改為reword吐辙,然后保存退出:

      reword 8837671 c2 # 修改提交信息
      pick a75e892 C3   # 保留該提交
      

      此時(shí)凄贩,另一個(gè)vi編輯模式窗口會(huì)自動(dòng)彈出,我們可在此修改提交信息袱讹,此處我們將c2修改為C2,保存并退出昵时。

      到此捷雕,我們就成功修改了8837671的提交信息,如下所示:

      $ git log --oneline
      2de4c14 (HEAD -> master) C3
      35e3b4c C2                  # 已成功修改
      c830b70 C1
      
    • squash:該操作可以壓縮提交壹甥,也即將多個(gè)提交壓縮到前一個(gè)提交中救巷。

      舉個(gè)例子:比如對(duì)于本地倉庫,其提交歷史記錄如下所示:

      $ git log --oneline
      7b8e084 (HEAD -> master) C4
      2de4c14 C3
      35e3b4c C2
      c830b70 C1
      

      假設(shè)現(xiàn)在我們想將C2C3壓縮成一個(gè)提交句柠,此時(shí)可以如下操作:

      # 啟動(dòng)交互式變基浦译,展示 C1 之后的所有提交
      $ git rebase -i c380b70
      

      此時(shí)會(huì)彈出編輯窗口,我們將C3設(shè)置為squash溯职,表示它會(huì)壓縮到C2中:

      pick 35e3b4c C2 # 保留 C2
      squash 2de4c14 C3 # 壓縮 C3(壓縮到上一個(gè)提交精盅,即 C2)
      pick 7b8e084 C4   # 保留 C4
      

      此時(shí)會(huì)彈出另一個(gè)窗口,該窗口同時(shí)包含C2C3的提交信息谜酒,我們可以手動(dòng)編輯壓縮合并生成的新提交信息:

      # 默認(rèn)采用上一個(gè)提交信息叹俏,這里我將其修改為如下
      squash C2 and C3
      

      此時(shí),我們就完成了C2C3的合并僻族,效果如下所示:

      $ git log --oneline
      cfed515 (HEAD -> master) C4
      fb5e7f3 squash C2 and C3    # 合并成功
      c830b70 C1
      
    • fixup:該操作也squash作用一樣粘驰,也是用于壓縮提交评抚。但與squash不同的是晨抡,該操作會(huì)丟棄被壓縮提交(即被fixup標(biāo)注的提交)的提交信息俏拱。

      舉個(gè)例子:比如我們還是壓縮C2C3找田,但是直接將C3壓縮到C2

      # 倉庫初始狀態(tài)
      $ git log --oneline
      3ba4ca0 (HEAD -> master) C4
      31b3716 C3
      b32f587 C2
      c830b70 C1
      
      # 啟動(dòng)變基
      $ git rebase -i HEAD~3
      

      此時(shí)將C3標(biāo)注為fixup妇汗,表示將C3直接壓入到上一個(gè)提交(即C2)中:

      pick b32f587 C2  # 保留
      fixup 31b3716 C3 # 壓入到上一個(gè)
      pick 3ba4ca0 C4  # 保留
      

      由于是直接壓縮乖仇,因此直接就返回了廉油,不會(huì)像squash需要再?gòu)棾鲆粋€(gè)窗口合并提交信息势誊,此時(shí)的效果如下所示:

      $ git log --oneline
      1a32d3c (HEAD -> master) C4
      fd46dfc C2
      c830b70 C1
      
    • edit:該操作可以用于編輯提交。

      該操作具體的執(zhí)行邏輯為:當(dāng)提交被標(biāo)注為edit時(shí)枝哄,Git 會(huì)自動(dòng)變基到該提交上肄梨,然后我們就可以編輯該提交,比如增加挠锥、刪除一些內(nèi)容众羡,然后使用git comit --amend修改此次提交,也可以在該提交上增添新提交蓖租,這樣就相當(dāng)于在之前的提交歷史記錄中插入一些新提交...

      舉個(gè)例子:除了重置粱侣、增加提交外,edit操作也常常用來將提交切分為多個(gè)小提交蓖宦,其實(shí)就是重置+增加提交的操作齐婴,比如,前面我們將倉庫提交C2C3壓縮到一起稠茂,如下所示:

      $ git log --oneline
      cfed515 (HEAD -> master) C4
      fb5e7f3 squash C2 and C3
      c830b70 C1
      

      現(xiàn)在如果我們想重新分離開C2C3柠偶,則可以借助edit操作,如下所示:

      # 啟動(dòng)變基
      $ git rebase -i HEAD~2
      

      我們將fb5e7f3標(biāo)注為edit

      edit fb5e7f3 squash C2 and C3
      pick cfed515  C4
      

      保存退出編輯窗口后睬关,Git 會(huì)自動(dòng)變基到fb5e7f3中:

      $ git show HEAD --oneline -s
      fb5e7f3 (HEAD) squash C2 and C3
      

      我們將該提交重新拆分為兩個(gè)小提交C2C3

      # 暫存區(qū)移除 C3 內(nèi)容
      $ git rm --cached 3.txt
      rm '3.txt'
      
      # 拆分出 C2 內(nèi)容并重新提交
      $ git commit --amend -m 'C2'
      [detached HEAD b32f587] C2
       Date: Tue Jan 12 11:37:16 2021 +0800
       1 file changed, 1 insertion(+)
       create mode 100644 2.txt
      
      # 添加 C3 內(nèi)容
      $ git add 3.txt
      
      # 提交 C3 內(nèi)容
      $ git commit -m 'C3'
      [detached HEAD 31b3716] C3
       1 file changed, 1 insertion(+)
       create mode 100644 3.txt
      
      # 拆分完成后诱担,繼續(xù)變基過程
      $ git rebase --continue
      Successfully rebased and updated refs/heads/master.
      

      此時(shí),變基結(jié)束电爹,我們成功將一個(gè)大提交拆分為多個(gè)小提交蔫仙,如下所示:

      $ git log --oneline
      3ba4ca0 (HEAD -> master) C4
      31b3716 C3                  # 拆分
      b32f587 C2                  # 拆分
      c830b70 C1
      
    • drop:如果要?jiǎng)h除某個(gè)提交,只需將該提交標(biāo)注為drop丐箩,或者直接在編輯窗口中刪除該提交即可摇邦。

      舉個(gè)例子:假設(shè)我們當(dāng)前倉庫本地分支提交歷史記錄如下所示:

      $ git log --oneline
      45fcf79 (HEAD -> master) C3
      6992e50 C2
      c830b70 C1
      

      假設(shè)現(xiàn)在我們想要?jiǎng)h除C2C3這兩個(gè)提交,其步驟如下所示:

      # 啟動(dòng)交互式變基
      $ git rebase -i HEAD~2
      

      此時(shí)彈出的編輯窗口內(nèi)容如下:

      pick 6992e50 C2
      pick 45fcf79 C3
      

      這里我們將C2標(biāo)注為drop屎勘,然后直接刪除C3施籍,同時(shí)驗(yàn)證這兩種刪除方法:

      drop 6992e50 C2
      

      保存并退出編輯窗口,此時(shí)查看倉庫提交歷史記錄:

      $ git log --oneline
      c830b70 (HEAD -> master) C1
      

      可以看到概漱,我們已經(jīng)成功刪除了C2C3法梯。

    • 更換提交順序:更換提交順序只需在編輯窗口中直接更換提交的順序即可。

      舉個(gè)例子:比如我們當(dāng)前本地倉庫提交歷史記錄如下所示:

      $ git log --oneline
      45fcf79 (HEAD -> master) C3
      6992e50 C2
      c830b70 C1
      

      假設(shè)現(xiàn)在我們想更換C2C3的順序犀概,其步驟如下:

      # 啟動(dòng)交互式變基
      $ git rebase -i HEAD~2
      

      此時(shí)的編輯窗口如下所示:

      pick 6992e50 C2
      pick 45fcf79 C3
      

      要更換C2C3提交順序立哑,只需在編輯窗口中更換兩者順序即可:

      pick 45fcf79 C3
      pick 6992e50 C2
      

      如此我們就已經(jīng)完成提交順序更換,此時(shí)的提交歷史記錄如下所示:

      $ git log --oneline
      0486bd0 (HEAD -> master) C2
      afa4a24 C3
      c830b70 C1
      

git cherypick

在多分支工作流中姻灶,當(dāng)我們需要獲取另一個(gè)分支的所有變動(dòng)時(shí)铛绰,通常采用的都是分支合并(git merge)策略,但是如果我們只對(duì)分支的一個(gè)或某幾個(gè)提交感興趣产喉,那么也可以只摘取這幾個(gè)提交捂掰,將他們各自的修改一一應(yīng)用到我們當(dāng)前分支上敢会,Git 中,具備提交摘取的命令為git cherry-pick这嚣,其具體語法如下所示:

# 支持摘取多個(gè) commit
git cherry-pick [<options>] <commit-ish>...
git cherry-pick (--continue | --skip | --abort | --quit)

git cherry-pick的本質(zhì)是摘取提交鸥昏,將其修改應(yīng)用到當(dāng)前分支上。
git cherry-pick支持摘取一個(gè)或多個(gè)提交姐帚,每一個(gè)提交應(yīng)用到當(dāng)前分支吏垮,都會(huì)生成一個(gè)新的提交,該提價(jià)的修改完全與摘取的提交一致罐旗。其實(shí)git cherry-pick就是將摘取的提交在當(dāng)前分支上進(jìn)行重復(fù)播放膳汪。

git cherry-pick常用的命令選項(xiàng)有如下:

  • -n, --no-commit:應(yīng)用摘取提交時(shí),只進(jìn)行更新九秀,但不提交遗嗽。
    :默認(rèn)情況下,git cherry-pick在應(yīng)用摘取提交完成時(shí)鼓蜒,會(huì)自動(dòng)進(jìn)行提交痹换,生成一個(gè)新提交。

  • -e, --edit:如果想更改提交信息都弹,可以添加-e, --edit娇豫。
    :默認(rèn)情況下,git cherry-pick直接將摘取的提交信息作為新生成提交的提交信息缔杉。

  • -m parent-number, --mainline parent-number:當(dāng)摘取的提交是一個(gè)合并提交時(shí),此時(shí)git cherry-pick無法區(qū)分應(yīng)當(dāng)使用哪個(gè)分支上進(jìn)行的修改搁料,因此默認(rèn)失敗處理或详。此時(shí)必須指定一個(gè)parent-number,表示要摘取的變動(dòng)分支郭计。parent-number取值由1開始霸琴,具體查找方法可參考:高級(jí)技巧 - 查看合并提交的parent-number

舉個(gè)例子:假設(shè)本地倉庫存在masterdev分支,現(xiàn)在突然發(fā)現(xiàn)線上版本出現(xiàn)漏洞昭伸,因此緊急從master分支上創(chuàng)建一個(gè)hotfix/add_file分支梧乘,然后做了兩個(gè)提交,如下圖所示:

cherrypick - initial

簡(jiǎn)單起見庐杨,每個(gè)提交都只是增加了相應(yīng)數(shù)字的文件选调。

當(dāng)漏洞修改完成后,就需要將hotfix/add_file分支合并到master分支中:

$ git switch master
Switched to branch 'master'

# 合并 hotfix 分支
$ git merge --no-ff hotfix/add_file -m 'fix: C6 => merge branch hotfix/add_file'
Merge made by the 'recursive' strategy.
 4.txt | 1 +
 5.txt | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 4.txt
 create mode 100644 5.txt

此時(shí)的示意圖如下所示:

cherrypick - master merge hotfix

同樣的灵份,hotfix/add_file分支上的修改也要合并到dev分支上仁堪,此時(shí),dev分支可以git cherry-pick或直接合并hotfix/add_file分支填渠,也可以git cherry-pick主分支master上的合并提交C6弦聂,下面對(duì)這兩種方法分別進(jìn)行講解:

  • git cherry-pick摘取hotfix/add_file分支所有提交:這里我們不采用合并方式鸟辅,而是將hotfix/add_file分支的所有提交,即C4C5直接摘取到dev分支中:

    # 切換到 dev 分支
    $ git switch dev
    Switched to branch 'dev'
    
    # 查看 hotfix/add_file 分支所有提交
    $ git log --oneline hotfix/add_file
    35a10e5 (hotfix/add_file) fix: C5     # 目標(biāo)提交
    f79b3b1 fix: C4                       # 目標(biāo)提交
    d0b972f feat: C2
    537ba3c feat: C1
    
    # cherry-pick C4 和 C5
    $ git cherry-pick f79b3b1 35a10e5
    [dev 8bf6c28] fix: C4                 # 應(yīng)用 C4
     Date: Tue Jan 12 21:40:29 2021 +0800
     1 file changed, 1 insertion(+)
     create mode 100644 4.txt
    [dev 6e3d3ef] fix: C5                 # 應(yīng)用 C5
     Date: Tue Jan 12 21:40:46 2021 +0800
     1 file changed, 1 insertion(+)
     create mode 100644 5.txt
    
    # 合并成功
    $ git log --oneline
    6e3d3ef (HEAD -> dev) fix: C5
    8bf6c28 fix: C4
    12b54a1 feat: C3
    d0b972f feat: C2
    537ba3c feat: C1
    

    從提交歷史中莺葫,我們已經(jīng)可以看到成功摘取C4C5dev分支上了匪凉,此時(shí)的示意圖如下所示:

    cherrypick - dev pick hotfix
  • cherry-pick合并提交:第二種方法是摘取合并提交,即摘取C6應(yīng)用到dev分支上捺檬。具體步驟如下:

    1. 首先我們將dev分支重置到C3提交:

      # 回退到 C3
      $ git reset --hard 12b54a1
      HEAD is now at 12b54a1 feat: C3
      
      # 回退成功
      $ git log --oneline
      12b54a1 (HEAD -> dev) feat: C3
      d0b972f feat: C2
      537ba3c feat: C1
      

      此時(shí)的倉庫示意圖如下所示:

      cherrypick - reset dev
    2. 此時(shí)dev分支可以摘取C6再层,需要注意的是,由于C6是一個(gè)合并提交欺冀,因此需要指定摘取分支树绩,對(duì)于C6而言,其是由master分支合并hotfix/add_file分支生成的合并提交隐轩,這里我們應(yīng)當(dāng)選擇摘取分支為hotfix/add_file

      # 查找 C6 的哈希值
      $ git log --oneline master | grep C6
      02d311d fix: C6 => merge branch hotfix/add_file
      
      # 查看 C6 的 parent-number
      $ git cat-file -p 02d311d | grep -i parent
      parent d0b972f09705aaf330c59be6eedbd69a1e49ccbc # parent-number = 1
      parent 35a10e51533ae17c42ecdf3ad9598334cdaeca08 # parent-number = 2
      
      # 比對(duì) C6 和 parent_commit1 的差異饺饭,可以看到,parent_commit1 就是 hotfix/add_file职车,
      # 因此 parent-number = 1
      $ git diff --stat d0b972f 02d311d
       4.txt | 1 +
       5.txt | 1 +
       2 files changed, 2 insertions(+)
      
      # 比對(duì) C6 和 parent_commit2 的差異(此步可忽略瘫俊,因?yàn)樯弦徊揭颜页?parent-number)
      $ git diff --stat 35a10e5 02d311d
      
      # 摘取 parent-number = 1 的提交
      $ git cherry-pick -m 1 02d311d
      [dev fdfc2c9] fix: C6 => merge branch hotfix/add_file
       Date: Tue Jan 12 21:55:29 2021 +0800
       2 files changed, 2 insertions(+)
       create mode 100644 4.txt
       create mode 100644 5.txt
      
      # 查看摘取合并結(jié)果
      $ git log --oneline
      fdfc2c9 (HEAD -> dev) fix: C6 => merge branch hotfix/add_file
      12b54a1 feat: C3
      d0b972f feat: C2
      537ba3c feat: C1
      

      可以看到,我們成功將C6的修改應(yīng)用到了dev分支悴灵,此時(shí)的示意圖如下所示:

      cherrypick - pick merge commit

      :可以看到扛芽,git cherry-pick合并提交的操作還是相對(duì)麻煩的,建議盡量避免對(duì)合并提交進(jìn)行摘取积瞒。

git bisect

git bisect命令可以讓我們很方便快速查找到出現(xiàn) bug 的提交川尖,它的原理是對(duì)給定范圍的提交進(jìn)行二分查找,由用戶判斷當(dāng)前提交是否存在 bug茫孔,依次迭代不斷縮小規(guī)模進(jìn)行二分查找叮喳,這樣我們就可以很快從一個(gè)大范圍提交區(qū)間找到引入 bug 的那個(gè)提交。

git bisect命令的具體語法如下所示:

# 啟動(dòng)二分查找
git bisect start [<paths>...]
# 當(dāng)前提交存在 bug
git bisect bad [<rev>]
# 當(dāng)前提價(jià)良好(即不存在 bug)
git bisect good [<rev>...]
# 退出二分查找
git bisect reset [<commit>]
git bisect terms [--term-good | --term-bad]
git bisect skip [(<rev>|<range>)...]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd>...
git bisect help

可以看到缰贝,git bisect攜帶了很多的子命令選項(xiàng)馍悟,但是通常我們只會(huì)使用git bisect { start | good | bad | reset }這四個(gè)子命令來進(jìn)行二分查找。其中:

  • git bisect start:該命令會(huì)啟動(dòng)二分查找過程剩晴。其具體語法如下所示:

    git bisect start [end_point] [start_point]
    

    其中锣咒,end_point表示最近的提交,start_point表示最早之前的提交赞弥,如果兩者都指定了毅整,那么二分查找第一個(gè)提交就是start_pointend_point的中間提交,如果未指定start_pointend_point绽左,則進(jìn)入二分搜索時(shí)毛嫉,還需手動(dòng)使用git bisect bad <commit>git bisect good <commit>手動(dòng)指定end_pointstart_point

  • git bisect good:執(zhí)行該命令會(huì)將當(dāng)前提交設(shè)置為良好狀態(tài)妇菱,即表示當(dāng)前提交不存在 bug承粤。其語法如下所示:

    git bisect good [<rev>...]
    

    我們也可以在啟動(dòng)二分查找后暴区,手動(dòng)指定一個(gè)提交設(shè)置為良好狀態(tài)(比如git bisect good 31af8d,表示提交31af8d未引入 bug)辛臊,這樣可以人為縮短搜索范圍仙粱。

  • git bisect bad:執(zhí)行該命令會(huì)將當(dāng)前提交設(shè)置為出錯(cuò)狀態(tài),即表示當(dāng)前提交存在 bug彻舰。其語法如下所示:

    git bisect bad [<rev>]
    

    我們也可以在啟動(dòng)二分查找后伐割,手動(dòng)指定一個(gè)提交設(shè)置為出錯(cuò)狀態(tài)(比如git bisect bad 31af8d,表示提交31af8d存在 bug)刃唤,這樣可以人為縮短搜索范圍隔心。

  • git bisect reset:該命令會(huì)結(jié)束二分查找過程。其語法如下所示:

    git bisect reset [<commit>]
    

    默認(rèn)情況下尚胞,該命令會(huì)退出二分查找過程硬霍,然后回到先前的提交,即執(zhí)行git bisect start時(shí)的提交笼裳。如果想退出時(shí)回到其他提交唯卖,直接在后面添加目標(biāo)提交<commit>即可。

舉個(gè)例子:假設(shè)我們當(dāng)前倉庫存在 5 個(gè)提交歷史躬柬,為了方便演示拜轨,我們假設(shè)某個(gè)提交刪除了ReadMe.md文件,現(xiàn)在想找出刪除該文件的提交允青,操作過程如下:

# 所有提交
$ git log --oneline
15c3cdb (HEAD -> master) feat: 555
7b93852 feat: 444
c40d88f feat: 333
b9ce941 docs: add ReadMe.md
58bdc1d feat: 111

# HEAD 58bdc1d 表示對(duì)所有提交進(jìn)行二分查找橄碾,這里也可以忽略不寫
$ git bisect start HEAD 58bdc1d
Bisecting: 1 revision left to test after this (roughly 1 step)
[c40d88fc907538e7392509c7b221ebf78ae42516] feat: 333             # 表示當(dāng)前處于 c40d88f,也就第三個(gè)提交

# 查看當(dāng)前提交颠锉,可以看到法牲,確實(shí)是處于第三個(gè)提交
$ git show HEAD --oneline --stat -s
c40d88f (HEAD) feat: 333

# 當(dāng)前提交存在 ReadMe.md
$ ls
1.txt  3.txt  ReadMe.md

# 由于當(dāng)前提交存在 ReadMe.md,故設(shè)置為良好狀態(tài)
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[7b9385209361db20ce6b1d4e1e7c81d999ae84b5] feat: 444             # 此時(shí)處于 7b93852木柬,即第四個(gè)提交

# 當(dāng)前提交不存在 ReadMe.md
$ ls
1.txt  3.txt  4.txt

# 由于當(dāng)前提交不存在 ReadMe.md皆串,故將其設(shè)置為 bad 狀態(tài)
$ git bisect bad
7b9385209361db20ce6b1d4e1e7c81d999ae84b5 is the first bad commit # 這里表示當(dāng)前提交就是第一個(gè)引入 bug 的提交
commit 7b9385209361db20ce6b1d4e1e7c81d999ae84b5
Author: Why8n <Why8n@gmail.com>
Date:   Sun Jan 10 18:18:08 2021 +0800

    feat: 444

 4.txt     | 1 +
 ReadMe.md | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)
 create mode 100644 4.txt
 delete mode 100644 ReadMe.md                                    # 刪除了文件 ReadMe.md

# 找到引入 bug 的提交后淹办,就可以退出二分查找了
$ git bisect reset
Previous HEAD position was 7b93852 feat: 444
Switched to branch 'master'

# 因?yàn)閯h除了 ReadMe.md 的提交還進(jìn)行了其他修改眉枕,因此這里不能直接使用 git revert
# 但是既然找到了刪除 ReadMe.md 的提交,那么我們只需從該提交之前的提交獲取 ReadMe.md 文件怜森,進(jìn)行恢復(fù)即可
# 7b93852 提交刪除了 ReadMe.md速挑,該提交之前的提交為 c40d88f
$ git log --oneline | grep 7b93852 -A 1
7b93852 feat: 444
c40d88f feat: 333

# 查詢 c40d88f 所有文件,獲取 ReadMe.md 的對(duì)象文件
$ git ls-tree c40d88f
100644 blob 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c    1.txt
100644 blob 55bd0ac4c42e46cd751eb7405e12a35e61425550    3.txt
100644 blob c200906efd24ec5e783bee7f23b5d7c941b0c12c    ReadMe.md # 目標(biāo)文件

# 將目標(biāo)文件內(nèi)容寫到當(dāng)前工作目錄
$ git cat-file -p c20090 > ReadMe.md

以上副硅,就是git bisect的整個(gè)基本操作過程姥宝。

查看合并提交的parent-number

前面我們介紹過的git revertgit cherry-pick等命令,在遇到合并提交時(shí)恐疲,都需要指定主線分支腊满,也即parent-number套么。Git 似乎并沒有直接提供查詢parent-number的命令,因此我們只能手動(dòng)進(jìn)行查找碳蛋。

我們以一個(gè)例子來驅(qū)動(dòng)講解查找合并提交的parent-number胚泌,假設(shè)現(xiàn)在我們有一個(gè)合并提交02d311d,可以通過如下命令查看其內(nèi)容:

# 法一
git show --pretty=raw <merge_commit>

# 法二
git cat-file -p <merge_commit>

比如肃弟,查看合并提交02d311d玷室,結(jié)果如下:

$ git show --pretty=raw 02d311d
commit 02d311d5c9bb0989c1285e068fc3c2a4de02b027
tree 03c45eebbff1c1fa9f9152d9800d4e65f4602052
parent d0b972f09705aaf330c59be6eedbd69a1e49ccbc    # parent1_commit,其 parent-number = 1
parent 35a10e51533ae17c42ecdf3ad9598334cdaeca08    # parent2_commit笤受,其 parent-number = 2
author Why8n <Why8n@gmail.com> 1610459729 +0800
committer Why8n <Why8n@gmail.com> 1610459729 +0800

    fix: C6 => merge branch hotfix/add_file

該命令會(huì)顯示合并提交的父提交穷缤,第一個(gè)parent1_commitparent-number就是1,第二個(gè)parent2_commitparetn-number就是2箩兽,依次類推...

然后津肛,我們只需一一比對(duì)parent_commit與合并提交之間的差別,就可以判斷得出應(yīng)當(dāng)使用哪個(gè)parent_commit了:

git diff --stat <parent_commit> <merge_commit>

更多詳細(xì)內(nèi)容比肄,請(qǐng)參考:Git cherry-pick syntax and merge branches

其他

git blame

如果我們想查看文件每一行對(duì)應(yīng)的版本以及最后修改的作者時(shí)快耿,則可以使用git blame命令。其具體語法如下所示:

git blame [<options>] [<rev-opts>] [<rev>] [--] <file>

下面列舉幾種常用的git blame操作:

  • 指定顯示行數(shù):可以通過添加-L <start>,<end>選項(xiàng)來指定只顯示文件startend之間的行:

    # 只顯示 .gitignnore 文件第 1 到 3 行的內(nèi)容
    $ git blame -L 1,3 .gitignore
    aa658574bfc (Josh Steadmon 2019-01-15 14:25:50 -0800 1) /fuzz-commit-grap
    5e472150800 (Josh Steadmon 2018-10-12 17:58:40 -0700 2) /fuzz_corpora
    5e472150800 (Josh Steadmon 2018-10-12 17:58:40 -0700 3) /fuzz-pack-header
    
    # 只顯示 .gitignnore 文件第 3 行修改信息
    $ git blame -L 3,+1 HEAD~1 .gitignore
    5e472150800 (Josh Steadmon 2018-10-12 17:58:40 -0700 3) /fuzz-pack-headers
    
  • 顯示特定版本的文件修改git blame默認(rèn)只顯示文件最后一個(gè)版本的修改(當(dāng)然文件中每一行內(nèi)容都可能處于不同的版本中)芳绩,如果想顯示某個(gè)提交該文件的信息時(shí)掀亥,可以指定該提交版本:

    # 顯示 .gitignore 文件倒數(shù)第二次提交的第一行修改信息
    $ git blame -L 1,+1 HEAD~1 .gitignore
    aa658574bfc (Josh Steadmon 2019-01-15 14:25:50 -0800 1) /fuzz-commit-graph
    

git gc

前文說過,Git 本質(zhì)是一個(gè)全量快照的文件系統(tǒng)妥色,因此當(dāng)我們暫存次數(shù)過多時(shí)搪花,Git 對(duì)象數(shù)據(jù)庫會(huì)存儲(chǔ)很多對(duì)象文件,有些對(duì)象文件實(shí)際上沒有被任何提交對(duì)象直接或間接進(jìn)行引用嘹害,這些對(duì)象稱為『松散對(duì)象(loose objects)』撮竿。

我們使用命令git gc來垃圾回收這些松散對(duì)象,減小倉庫大小笔呀。簡(jiǎn)單來說幢踏,當(dāng)運(yùn)行git gc時(shí),Git 會(huì)收集所有松散對(duì)象并將它們存入一個(gè)packfile文件中许师,并將多個(gè)packfile文件合并成一個(gè)大的packfile文件房蝉,然后移除不被任何提交引用且超過一定期限的對(duì)象文件。除此之外微渠,git gc還會(huì)將所有的引用文件(即.git/refs)打包到另一個(gè)單獨(dú)的文件中搭幻。

更多 Git 垃圾回收相關(guān)內(nèi)容,可參考如下文章:

分頁器

Git 中幾乎所有命令都提供了分頁器(Pager)功能逞盆,當(dāng)命令輸出超出一頁時(shí)檀蹋,會(huì)自動(dòng)啟動(dòng)分頁器。

分頁器的交互方式并不人性化云芦,可通過如下幾種方法進(jìn)行分頁:

  • --no-pager:手動(dòng)為 Git 添加--no-pager選項(xiàng)俯逾,可禁止啟動(dòng)分頁器:

    $ git --no-pager log -n 10 --oneline
    
  • 全局配置分頁器贸桶,使用less命令進(jìn)行翻頁:

    $ git config --global core.pager "less -FRSX"
    

別名

可以通過git config alias來為其他命令設(shè)置一個(gè)簡(jiǎn)短的別名,方便使用桌肴,比如:

$ git config --global alias.br branch

$ git br # ==> 擴(kuò)展為:git branch

上述命令為branch設(shè)置了一個(gè)別名br刨啸,此時(shí)使用git br就相當(dāng)于使用git branch

:Git 的別名就是一個(gè)字符串识脆,使用時(shí)會(huì)自動(dòng)擴(kuò)展為設(shè)置的內(nèi)容设联,自動(dòng)拼接到git指令后面。

Git 也可以為外部命令指定別名灼捂,只需在命令前面添加!即可:

$ git config --global alias.ls '!ls -alrt'

$ git ls # ==> ls -alrt

舉個(gè)例子:這里我們?cè)O(shè)置一個(gè)別名(命令)离例,來顯示本地倉庫對(duì)象數(shù)據(jù)庫所有對(duì)象文件及其類型:

# 由于命令太長(zhǎng),我們選擇直接在全局配置文件中進(jìn)行修改悉稠,方便很多
$ git config --global --edit

然后在標(biāo)簽[alias]下設(shè)置如下內(nèi)容:

[alias]
    ; 注釋:sdo represent show data objects
    sdo = "!find .git/objects -type f | awk -F '/' '{ hash=$3$4; cmd = sprintf(\"git cat-file -t %s\", hash); printf(\"%s\t\", hash); system(cmd); }'"

此時(shí)使用命令git sdo就可以顯示對(duì)象數(shù)據(jù)庫中所有的對(duì)象文件及其類型:

# sdo represents show data objects
$ git sdo
0767f3e206a0a431633b2063bbda680026c33f70        commit
10f86d6b803b8962653f16a9967a4578215dcb22        tree
778d49177a4b6da0e967ac3e9308076ad500e6e7        blob

git clean

如果需要移除工作區(qū)中未被追蹤的文件或文件夾宫蛆,可以使用git clean命令,其語法如下所示:

git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<paths>]...

其中的猛,常用的選項(xiàng)有:

  • -n, --dry-run:表示只顯示將要被移除的文件/文件夾耀盗,而不進(jìn)行真正的刪除。

  • -d:表示移除文件和文件夾卦尊。
    :默認(rèn)情況下叛拷,為了盡可能減少文件刪除,git clean不會(huì)刪除未被追蹤的文件夾岂却。

  • -f, --force:表示強(qiáng)制執(zhí)行刪除操作忿薇。
    :如果配置了選項(xiàng)clean.requireForcefalse的話,git clean默認(rèn)不進(jìn)行刪除動(dòng)作躏哩,此時(shí)可通過添加-f選項(xiàng)真正執(zhí)行刪除操作署浩。

版本及范圍表示法

大多數(shù) Git 命令都會(huì)攜帶一個(gè)revision(修訂版本)作為參數(shù),因此 Git 也內(nèi)置了一些版本及其范圍的簡(jiǎn)便引用方法扫尺,大致有如下:

  • 版本指定:版本指定可引用一個(gè)提交或多個(gè)提交:

    • <sha1>:表示對(duì)象文件的哈希字符串筋栋。比如:dae86e1950b1277e545cee180551750029cfe735dae86e
    • <refspec>:表示符號(hào)引用正驻。比如:master弊攘、heads/masterrefs/heads/master都表示master分支,比如HEAD表示HEAD引用文件...
    • @:一個(gè)單獨(dú)的@等同于HEAD拨拓。
    • <refspec>@{<date>}:表示指定時(shí)間段的引用肴颊。比如:master@{yesterday}氓栈,HEAD@{5 minutes ago}渣磷。
    • <refspec>@{<number>}:表示引用refspec之前的第number個(gè)提交。比如:master@{0}等同于master授瘦,master@{1}master分支第二個(gè)最新提交醋界。
    • @{<number>}:與<refspec>@{<number>}功能一致竟宋,只是refspecHEAD,即表示當(dāng)前分支的最新第number個(gè)提交形纺。
    • <rev>^[n]:表示提交rev的前n個(gè)父提交丘侠,n表示字符^的重復(fù)個(gè)數(shù)。比如:a95fabe^表示提交a95fabe的前一個(gè)提交逐样,HEAD^^表示HEAD的前第二個(gè)提交...
      n也可以為數(shù)字蜗字,但只能為01。比如a95fabe^0等同于a95fabe脂新,HEAD^1等同于HEAD^挪捕。
    • <rev>~<number>:表示提交rev的第number祖先提交。比如:a95fabe~0等同于a95fabe争便,a95fabe~1表示a95fabe的前一個(gè)提交级零,HEAD~5表示當(dāng)前分支的最新第 5 個(gè)提交。
      <rev>~<number>也支持<rev>~<n>操作滞乙,比如:HEAD~等同于HEAD~1奏纪,HEAD~~等同于HEAD~2...
      :該模式也支持<rev>^{tree},表示獲取提交rev的樹對(duì)象斩启。
    • <rev>:<path>:表示版本rev下的文件序调。比如:HEAD:1.txt表示當(dāng)前版本下的1.txt文件內(nèi)容,a95fabe:1.txt表示版本a95fabe下的1.txt文件內(nèi)容兔簇。
      :該模式可以讓我們很方便對(duì)不同版本的同一文件進(jìn)行比對(duì):
      # HEAD:1.txt 相對(duì)于 HEAD~1:1.txt 的文件差異
      $ git diff HEAD~1:1.txt HEAD:1.txt
      
  • 范圍指定:當(dāng)分支提交歷史記錄包含多個(gè)提交時(shí)炕置,可以指定提交范圍:

    • ^<rev>:表示不包含rev`的提交。
    • <rev1>..<rev2>:表示包含rev2男韧,但是不包含rev1朴摊,即(rev1,rev2]
    • <rev1>...<rev2>:表示同時(shí)包含rev1rev2此虑,即[rev1,rev2]甚纲。

更多其他版本與版本范圍表示法,可參考:Git 版本及版本范圍表示法

顯示引用哈希值

可以通過git rev-parse查看引用或?qū)ο笪募V担?/p>

# 顯示 master 最新提交哈希值
$ git rev-parse master
378a269ba0c11542ade35eef1df88e094d935548

# 顯示 HEAD 樹對(duì)象簡(jiǎn)短哈希值
$ git rev-parse --short HEAD^{tree}
5466733

顯示所有對(duì)象文件

其命令為:

git rev-list [OPTION] <commit-id>... [ -- paths... ]

舉個(gè)例子:

  • 顯示最新版本所有的對(duì)象文件

    $ git rev-list --objects HEAD
    
  • 顯示倉庫所有對(duì)象文件

    $ git rev-list --objects --all
    

附錄

本人配置

以下是本人的 Git 配置選項(xiàng):

# 用戶名
git config --global user.name Why8n
# 郵箱
git config --global user.email whyncai@gmail.com
# 默認(rèn)編輯器
git config --global core.editor nvim

# 解決 git status 中文亂碼
git config --global core.quotepath false

# 設(shè)置 git gui 界面編碼
git config --global gui.encoding utf-8

# 設(shè)置 git log 提交內(nèi)容編碼
git config --global i18n.commitencoding utf-8 

# 分頁器替換
git config --global core.pager "less -FRSX"

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末朦前,一起剝皮案震驚了整個(gè)濱河市介杆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌韭寸,老刑警劉巖春哨,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異恩伺,居然都是意外死亡赴背,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凰荚,“玉大人燃观,你說我怎么就攤上這事”闵” “怎么了缆毁?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)到涂。 經(jīng)常有香客問我脊框,道長(zhǎng),這世上最難降的妖魔是什么践啄? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任缚陷,我火速辦了婚禮,結(jié)果婚禮上往核,老公的妹妹穿的比我還像新娘箫爷。我一直安慰自己,他們只是感情好聂儒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布虎锚。 她就那樣靜靜地躺著,像睡著了一般衩婚。 火紅的嫁衣襯著肌膚如雪窜护。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天非春,我揣著相機(jī)與錄音柱徙,去河邊找鬼。 笑死奇昙,一個(gè)胖子當(dāng)著我的面吹牛护侮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播储耐,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼羊初,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了什湘?” 一聲冷哼從身側(cè)響起长赞,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闽撤,沒想到半個(gè)月后得哆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哟旗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年贩据,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了栋操。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乐设,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绎巨,到底是詐尸還是另有隱情近尚,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布场勤,位于F島的核電站戈锻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏和媳。R本人自食惡果不足惜格遭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望留瞳。 院中可真熱鬧拒迅,春花似錦、人聲如沸她倘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硬梁。三九已至前硫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荧止,已是汗流浹背屹电。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跃巡,地道東北人危号。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像素邪,于是被迫代替她去往敵國(guó)和親葱色。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • [TOC] Git 內(nèi)部實(shí)現(xiàn)原理剖析[http://www.reibang.com/p/8154ac47d406...
    Whyn閱讀 600評(píng)論 0 0
  • 一娘香、Git是什么苍狰? 是一個(gè)開源的分布式版本控制系統(tǒng),可以有效烘绽、高速的處理從很小到非常 大的項(xiàng)目版本管理淋昭。 Git ...
    名字誰不會(huì)取閱讀 1,036評(píng)論 0 0
  • 1.直接進(jìn)入沙盒 2. 本地操作相關(guān)篇節(jié)2.1 基礎(chǔ)篇2.2 處理復(fù)雜問題2.2.1 修改提交樹2.3 雜項(xiàng) 3....
    徽先生閱讀 404評(píng)論 0 0
  • 久違的晴天,家長(zhǎng)會(huì)安接。 家長(zhǎng)大會(huì)開好到教室時(shí)翔忽,離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,513評(píng)論 16 22
  • 今天感恩節(jié)哎歇式,感謝一直在我身邊的親朋好友驶悟。感恩相遇!感恩不離不棄材失。 中午開了第一次的黨會(huì)痕鳍,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,559評(píng)論 0 11