<noframes id="bhrfl"><address id="bhrfl"></address>

    <address id="bhrfl"></address>

    <noframes id="bhrfl"><address id="bhrfl"><th id="bhrfl"></th></address>

    <form id="bhrfl"><th id="bhrfl"><progress id="bhrfl"></progress></th></form>

    <em id="bhrfl"><span id="bhrfl"></span></em>

    全部
    常見問題
    產品動態
    精選推薦

    Git從入門到精通,Git命令大全

    管理 管理 編輯 刪除

    Git說明:https://www.runoob.com/manual/git-guide/

    騰訊Github:https://github.com/Tencent

    阿里巴巴Github:https://github.com/alibaba

    Git鏡像:https://www.gitclone.com/、https://ghproxy.com/

    Git入門

    資料來源:https://www.runoob.com/git/git-tutorial.html、http://git-scm.com/docs

    查看Git命令的幫助信息,git <command> --help

    1.Git 工作區、暫存區和版本庫(以本地舉例)、遠程倉庫

    • 工作區:就是你在電腦里能看到的目錄。
    • 暫存區:英文叫 stageindex。一般存放在 .git 目錄下的 index 文件(.git/index)中,所以我們把暫存區有時也叫作索引(index)。
    • 版本庫:工作區有一個隱藏目錄 .git,這個不算工作區,而是 Git 的版本庫。
    • Git從入門到精通,Git命令大全
    • Git 工作區、暫存區和版本庫
    • 圖中左側為工作區,右側為版本庫。在版本庫中標記為 "index" 的區域是暫存區(stage/index),標記為 "master" 的是 master 分支所代表的目錄樹。
    • 圖中我們可以看出此時 "HEAD" 實際是指向 master 分支的一個"游標"。所以圖示的命令中出現 HEAD 的地方可以用 master 來替換。
    • 圖中的 objects 標識的區域為 Git 的對象庫,實際位于 ".git/objects" 目錄下,里面包含了創建的各種對象及內容。
    • 當對工作區修改(或新增)的文件執行 git add 命令時,暫存區的目錄樹被更新,同時工作區修改(或新增)的文件內容被寫入到對象庫中的一個新的對象中,而該對象的ID被記錄在暫存區的文件索引中。
    • 當執行提交操作(git commit)時,暫存區的目錄樹寫到版本庫(對象庫)中,master 分支會做相應的更新。即 master 指向的目錄樹就是提交時暫存區的目錄樹。
    • 當執行 git reset HEAD 命令時,暫存區的目錄樹會被重寫,被 master 分支指向的目錄樹所替換,但是工作區不受影響。
    • 當執行 git rm --cached 命令時,會直接從暫存區刪除文件,工作區則不做出改變。
    • 當執行 git checkout . 或者 git checkout -- 命令時,會用暫存區全部或指定的文件替換工作區的文件。這個操作很危險,會清除工作區中未添加到暫存區中的改動。
    • 當執行 git checkout HEAD . 或者 git checkout HEAD 命令時,會用 HEAD 指向的 master 分支中的全部或者部分文件替換暫存區和以及工作區中的文件。這個命令也是極具危險性的,因為不但會清除工作區中未提交的改動,也會清除暫存區中未提交的改動。

    2.Git文件狀態

    在Git中文件大概分為四種狀態:已修改(modified)、已暫存(staged)、已提交(committed)、未追蹤(Untrack)

    • .gitignore內的文件,不會擁有任何一種狀態,被git徹底無視。
    • 處于ignore列表的文件,無法被add添加;但是可以強制添加
    • 空目錄、以及子目錄全部是空目錄的目錄不會有Untrack狀態,也無法通過add改變狀態(無效)
    • 工作目錄新增文件時,只要不處于ignore目錄,都會變成Untrack狀態;
    • 沒有add過的文件或者被restore(不帶--staged)的文件,處于Untrack狀態;
    • 初次add和被add后產生修改的文件,會處于modifed狀態。
    • 處于modified狀態的文件,最開始可以進行add和restore兩種操作,此時的add操作叫做 更新要提交的內容,add后變為staged狀態,restore(不加staged標記)后變為Untrack;
    • add后變為staged狀態的文件,可用restore --staged 變回modified狀態;這個staged狀態的內容可以用來恢復內容。沒有被add的modified狀態文件內容沒有被記錄(雖然有撤回,但是本質不一樣);
    • 處于staged狀態的文件,在沒有commit之前再次產生修改時,會同時具有staged和modified兩個狀態(可以把statged狀態的內容拉回來,覆蓋。);但是commit時會使用內容最新的那個狀態;
    • commit會提交所有staged狀態的文件,所以commit可以理解有一個modified到staged狀態的過程(實際可能不存在,因為暫存區本來就有變動的記錄);所以暫存狀態不能理解為處于暫存區,應當指的是被納入下一次提交的文件;任何被追蹤的產生修改的文件都會在暫存區被記錄;成為下一次提交的一部分;
    • 未被追蹤的文件被刪除時,不會產生git狀態。處于modofy未add時,會變成deleted狀態;處于staged狀態會保持暫存狀態;
    • 已經被刪除的(deleted狀態)被追蹤的文件,恢復后會變成modified狀態;
    • 提示
    • add的作用是將文件添加到暫存區,只有被add的文件才會被追蹤
    • 暫存區
    • (1)所謂的暫存區只是一個簡單的索引文件而已。
      (2)暫存區這個索引文件里面包含的是文件的目錄樹,像一個虛擬的工作區,在這個虛擬工作區的目錄樹中,記錄了文件名、文件的時間戳、文件長度、文件類型以及最重要的SHA-1值,文件的內容并沒有存儲在其中,所以說 它像一個虛擬的工作區。
      (3)索引指向的是.Git/objects下的文件。
      (4)暫存區的作用:除非是繞過暫存區直接提交,否則Git想把修改提交上去,就必須將修改存入暫存區最后才能commit。每次提交的是暫存區所對應的文件快照。

    拓展:status提示信息


    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
    
    • 既然是Changes not staged for commit,就說明出現這個提示下的所有文件改動,都是存在于工作區的。stage是暫存區的意思,not stage說明都不在暫存區,那么說明在工作區。
    • (use “git add …” to update what will be committed)。執行這條命令就可以工作區里面的改變加入到暫存區??梢詧绦術it add .把當前目錄下所有改動加入暫存區。
    • (use “git checkout – …” to discard changes in working directory)。執行這條命令將丟棄在工作區的改動??梢詧绦術it checkout *把當前目錄下所有工作區的改動丟棄掉
    Untracked files: 
          (use "git add <file>..." to include in what will be committed)
    
    
    • Untracked files,就說明出現這個提示下的所有文件都是當前HEAD沒有被加入過的文件。這種改動也屬于工作區。
    • (use “git add …” to include in what will be committed)。把Untracked files加入暫存區。
    On branch master
    Your branch is ahead of 'origin/master' by 1 commit.
      (use "git push" to publish your local commits)
    
    
    • 當前分支比遠程分支多了一次commit
    Your branch and 'origin/master' have diverged, and have 1 and 1 different commits each, respectively
    
    

    pull報錯了,查看狀態顯示這個,先留著待解決吧

    3.HEAD是什么

    HEAD是Git中非常重要的一個概念,你可以稱它為指針或者引用,它可以指向任意一個節點,并且指向的節點始終為當前工作目錄,換句話說就是當前工作目錄(也就是你所看到的代碼)就是HEAD指向的節點。

    4.git重命名檢測

    Git 采用了不同的方法:它沒有選擇去存儲與文件移動操作相關的信息,而是采用了重命名檢測算法。在該算法中,如果一個文件在某一次提交中消失了,它依然會存在于其前次提交,而如果某個擁有相同名字或相似內容的文件出現在了另一個位置,Git 就會自動檢測到。如果是這種情況,Git 就會假定該文件被移動過了。

    Git項目文件說明

    Git init后主要有兩個重要的文件和目錄:.git目錄和.gitignore

    1. .gitignore

    .gitignore文件存在于根目錄(與.git同級的目錄)用于在將文件提交到git暫存區時,指定將哪些文件排除;

    有時候你想添加(git add)一個文件到Git,但發現添加不了,多半原因是這個文件被.gitignore忽略了

    git add .不會添加被.gitignore忽視的文件,而git add -f . 強制添加所有文件,即使是.gitignore忽視的文件也添加。

    當.gitignore文件不是你編寫的,但是它編寫的不符合實際需求,你可以使用git check-ignore命令進行檢查,看是哪一個規則有問題了


    #檢測
     git check-ignore -v App.class
    #結果
    .gitignore:3:*.class	App.class
    

    .gitignore只能忽略那些原來沒有被track的文件,如果某些文件已經被納入了版本管理中,則修改.gitignore是無效的。解決方法就是先把本地緩存刪除(改變成未track狀態),然后再提交。


    git rm -r --cached .
    git add .
    git commit -m ‘update .gitignore’
    
    

    也可以手動指定一個文件作為git忽略文件


    git config core.excludesfile ***
    
    

    對于全局Git配置,可以使用如下命令對全部倉庫進行配置。


    git config --global core.excludesfile **/.gitignore(文件相對或絕對位置)
    
    

    忽略規則如下:

    1. 空格不匹配任意文件,可作為分隔符,可用反斜杠轉義
    2. #開頭的文件標識注釋,可以使用反斜杠進行轉義
    3. ! 開頭的模式標識否定,該文件將會再次被包含,如果排除了該文件的父級目錄,則使用 ! 也不會再次被包含??梢允褂梅葱备苓M行轉義
    4. / 結束的模式只匹配文件夾以及在該文件夾路徑下的內容,但是不匹配該文件
    5. / 開始的模式匹配項目跟目錄
    6. 如果一個模式不包含斜杠,則它匹配相對于當前 .gitignore 文件路徑的內容,如果該模式不在 .gitignore 文件中,則相對于項目根目錄
    7. ** 匹配多級目錄,可在開始,中間,結束
    8. ? 通用匹配單個字符
    9. [] 通用匹配單個字符列表
    10. 各種項目的gitignore
    11. 參考地址:https://github.com/github/gitignore

    2. .git目錄

    任意文件夾中,用 git init 命令初始化倉庫,即可在此文件夾下創建 .git 文件夾(.打頭為隱藏文件夾,所以平時可能看不到)。這個文件夾之外的部分叫做工作區(Working Directory),.git 文件夾我們稱做 Git倉庫 (Git Repository)。 通常會有7個文件5個目錄,常見目錄如下:


    COMMIT_EDITMSG
    HEAD
    ORIG_HEAD
    FETCH_HEAD
    config
    description
    index
    hooks/
    info/
    logs/
    objects/
    refs/
    
    

    1. 文件 COMMIT_EDITMSG

    此文件是一個臨時文件,存儲最后一次提交的信息內容,git commit 命令之后打開的編輯器就是在編輯此文件,而你退出編輯器后,git 會把此文件內容寫入 commit 記錄。

    實際應用: git pull 遠程倉庫后,新增了很多提交,淹沒了本地提交記錄,直接 cat .git/COMMIT_EDITMSG 就可以弄清楚最后工作的位置了。

    2. HEAD

    此文件永遠存儲當前位置指針,就像 linux 中的 $PWD 變量和命令提示符的箭頭一樣,永遠指向當前位置,表明當前的工作位置。在 git 中 HEAD 永遠指向當前正在工作的那個 commit。(孤立HEAD?????)

    HEAD 存儲一個分支的 ref,Linux中運行:cat .git/HEAD 通常會顯示:


    ref: refs/heads/master
    
    

    這說明你目前正在 master 分支工作。此時你的任何 commit,默認自動附加到 master 分支之上

    git cat-file -p HEAD, 顯示詳細的提交信息:


    tree 4cbb261560348e1727b5137f3ab6eceae8e1f34d
    parent 22c457fe24f737505356edfb8696c7e50fd9d971
    author Evan You <yyx990803@gmail.com> 1654857613 +0800
    committer Evan You <yyx990803@gmail.com> 1654857613 +0800
    
    chore: test pass
    
    
    image.png
    孤立head,不指向任何commit

    3. ORIG_HEAD

    正因為 HEAD 比較重要,此文件會在你進行危險操作時備份 HEAD,如以下操作時會觸發備份


    git reset
    git merge
    git rebase
    git pull
    
    

    此文件應用示例


    # 回滾到上一次的狀態(慎用!!!)
    git reset --hard ORIG_HEAD
    
    

    4. FETCH_HEAD

    這個文件作用在于追蹤遠程分支的拉取與合并,與其相關的命令有 git pull/fetch/merge,而git pull 命令相當于執行以下兩條命令:


    $ git fetch
    $ git merge FETCH_HEAD
    
    # 顯示如下>>>
    From https://github.com/xxx/xxxx
    * branch            master     -> FETCH_HEAD
    Updating f785638..59db1b2
    
    

    此時會默默備份 HEAD 到 ORIG_HEAD

    5. config

    此文件存儲項目本地的 git 設置,典型內容如下:


    [core]
            repositoryformatversion = 0
            filemode = true
            bare = false
            logallrefupdates = true
            ignorecase = true
    [remote "origin"]
            url = git@gitlab.xxxx.com/xxx.git
            fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "master"]
            remote = origin
            merge = refs/heads/master
    [branch "v2.6.0"]
            remote = origin
            merge = refs/heads/v2.6.0
    [branch "v2.8.0"]
            remote = origin
            merge = refs/heads/v2.8.0
    
    

    [core] 段的內容跟 git config 命令對應

    執行以下命令:


    git config user.name abc
    git config user.email abc@abc.com
    
    

    會在 config 文件中追加以下內容:


    ... ...
    [user]
    name = abc
    email = abc@abc.com
    
    

    git config --global 影響的則是全局配置文件 ~/.gitconfig。

    [remote] 段表示遠程倉庫配置

    [branch] 段表示分支同步設置

    假設當前在 master 分支,執行 git pull 若出現以下提示:


    There is no tracking information for the current branch.
    Please specify which branch you want to merge with.
    See git-pull(1) for details.
    
    

    git pull 就說明 .git/config 文件缺少對應的 [branch "master"] 字段。

    解決方案為:


    git branch -u origin/master master
    # 或者執行一次 push
    git push -u origin master
    
    

    會出現提示:


    Branch 'master' set up to track remote branch 'master' from 'origin'.
    
    

    其實就是生成以下內容在 .git/config中:


    [branch "master"]
    remote = origin
    merge = refs/heads/master
    
    

    手動編輯 .git/config,效果一樣。這就是 upstream 的真正含義,即生成 config 中的這段配置。

    6. description

    說明這個文件主要用于 GitWeb 的描述,如果要啟動 GitWeb 可用如下命令:


    # 確保lighttpd已安裝: brew install lighttpd
    $ git instaweb --start
    
    

    默認會啟動 lighttpd 服務并打開瀏覽器 http://127.0.0.1:1234 (試著改成對外IP并分享給別人?)

    以下顯示當前的 git 倉庫名稱以及描述,默認的描述如下:

    description

    默認描述

    上面這段話就是默認的 description 文件的內容,編輯這個文件來讓你 GitWeb 描述更友好。

    7. hooks/目錄

    存放 git hooks,用于在 git 命令前后做檢查或做些自定義動作。運行 ls -F1 .git/hooks


    prepare-commit-msg.sample  # git commit 之前,編輯器啟動之前觸發,傳入 COMMIT_FILE,COMMIT_SOURCE,SHA1
    commit-msg.sample          # git commit 之前,編輯器退出后觸發,傳入 COMMIT_EDITMSG 文件名
    pre-commit.sample          # git commit 之前,commit-msg 通過后觸發,譬如校驗文件名是否含中文
    pre-push.sample            # git push 之前觸發
    
    pre-receive.sample         # git push 之后,服務端更新 ref 前觸發
    update.sample              # git push 之后,服務端更新每一個 ref 時觸發,用于針對每個 ref 作校驗等
    post-update.sample         # git push 之后,服務端更新 ref 后觸發
    
    pre-rebase.sample          # git rebase 之前觸發,傳入 rebase 分支作參數
    applypatch-msg.sample      # 用于 git am 命令提交信息校驗
    pre-applypatch.sample      # 用于 git am 命令執行前動作
    fsmonitor-watchman.sample  # 配合 core.fsmonitor 設置來更好監測文件變化
    
    
    
    參考
    https://git-scm.com/docs/githooks

    如果要啟用某個 hook,只需把 .sample 刪除即可,然后編輯其內容來實現相應的邏輯。

    比如要校驗每個 commit message 至少要包含兩個單詞,否則就提示并拒絕提交,將 commit-msg.sample 改為 commit-msg 后,編輯如下:


    #!/bin/sh
    grep -q 'Ss+S' $1 || { echo '提交信息至少為兩個單詞' && exit 1; }
    
    

    這樣當提交一個 commit 時,會執行 bash 命令: .git/hooks/commit-msg .git/COMMIT_EDITMSG,退出值不為 0,就拒絕提交。

    8. info/目錄

    此文件夾基本就有兩個文件:

    1. 文件 info/exclude 用于排除規則,與 .gitignore 功能類似。
    2. 可能會包含文件 info/refs ,用于跟蹤各分支的信息。此文件一般通過命令 git update-server-info 生成,內容通常如下:

    9. logs/目錄

    記錄了操作信息,git reflog 命令以及像 HEAD@{1} 形式的路徑會用到。如果刪除此文件夾(危險?。?,那么依賴于 reflog 的命令就會報錯。

    文件夾 objects/

    此文件夾簡單說,就是 git的數據庫,運行 tree .git/objects,可以看到目錄結構:


    .git/objects/
    |-- 0c
    |   `-- d370696b581c38ee01e62b148a759f80facc2d
    |-- 59
    |   `-- 3d5b490556791212acd5a516a37bbfa05d44dd
    |-- 61
    |   `-- be44eedde61d723e5761577a2b420ba0fc2794
    |-- 64
    |   `-- c0aed8ddcbb546bdcec2848938fc82348db227
    |-- d4
    |   `-- 9904676ce8ddde276bdbfa9bbec313e90e0f50
    |-- info
    `-- pack
        |-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
        `-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
    
    

    這些文件分兩種形式:pack壓縮包 形式放在 pack/ 目錄下,除此之外都是 hash文件 形式,被叫做 loost objects。

    這個文件夾以及相應的算法,我沒找到獨立的名稱,就叫它 hash-object 體系吧。因為確實有個 git hash-object 命令存在,是一個底層的負責生成這些 loost objects 文件,如果要看到這些文件各自的含義,執行以下命令:


    git cat-file --batch-check --batch-all-objects
    
    

    可以看到


    04c87c65f142f33945f2f5951cf7801a32dfa240 commit 194
    098217953a6ca169bed33d2be8a07d584fcdaf30 tree 31
    0cd370696b581c38ee01e62b148a759f80facc2d commit 245
    2a810017bfc85d7db2627f4aabdaa1583212bda3 blob 19
    3920a07c1d5694df6b8658592b0939241d70e9e5 tree 93
    593d5b490556791212acd5a516a37bbfa05d44dd tag 148
    61be44eedde61d723e5761577a2b420ba0fc2794 tree 154
    ... ...
    
    

    但你會發現這個列表里有些值在文件夾中并不存在,因為除了 loost objects 它還匯總了 pack 文件中的內容。hash文件

    又稱為 loose object,文件名稱共由40字符的 SHA-1 hash 值組成,其中前兩個字符為文件夾分桶,后38個字符為文件名稱。

    按文件內容可分為四種類型:commit, tree, blob, tag,若執行以下命令會生成所有四種類型:


    echo -en 'xxn' > xx # 共 3 個字符
    git add .
    git commit -m 'update xx'
    git tag -a 'v1.0' -m 'release: 1.0.0'
    
    

    經過以上操作后,對比一下文件樹,發現多了四個 hash文件:


    |-- 0c
    | `-- d370696b581c38ee01e62b148a759f80facc2d
    |-- 18
    | `-- 143661f96845f11e0b4ab7312bdc0f356834ce
    |-- 30
    | `-- 20feea86d222d83218eb3eb5aa9f58f73df04d
    |-- 59
    | `-- 3d5b490556791212acd5a516a37bbfa05d44dd
    |-- 61
    | `-- be44eedde61d723e5761577a2b420ba0fc2794
    |-- 64
    | `-- c0aed8ddcbb546bdcec2848938fc82348db227
    |-- ad
    | `-- f4c9afac7afae3ff3e95e6c4eefe009d547f00
    |-- cc
    | `-- c9bd67dc5c467859102d53d54c5ce851273bdd
    |-- d4
    | `-- 9904676ce8ddde276bdbfa9bbec313e90e0f50
    |-- info
    `-- pack
    |-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
    `-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
    
    

    這四個 hash文件 分別是:


    cc/c9bd67dc5c467859102d53d54c5ce851273bdd # blob
    30/20feea86d222d83218eb3eb5aa9f58f73df04d # commit
    ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00 # tree
    18/143661f96845f11e0b4ab7312bdc0f356834ce # tag 
    
    

    其實這些文件都經過了壓縮,壓縮形式為 zlib。先安裝一下解壓工具 macOS 版 brew install pigz 或 windows 版 pigz,后執行:


    $ pigz -d < .git/objects/cc/c9bd67dc5c467859102d53d54c5ce851273bdd
    
    # BLOB類型,顯示結果為>>>>(注意xx后有個n)
    blob 3xx
    
    
    
    $pigz -d < .git/objects/30/20feea86d222d83218eb3eb5aa9f58f73df04d
    
    # COMMIT類型,顯示結果為>>>>
    commit 248tree adf4c9afac7afae3ff3e95e6c4eefe009d547f00
    parent 0cd370696b581c38ee01e62b148a759f80facc2d
    author jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562044880 +0800
    committer jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562044880 +0800
    
    update xx
    
    
    $ pigz -d < .git/objects/ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00
    
    # TREE類型,顯示結果為>>>>
    tree 154100644 abc*???]}?bJ??X2??100644 asdf???CK?)?wZ???S?100644 iou???CK?)?wZ???S?100644 xx??g?FxY-S?L?Q';?100644 yy???CK?)?wZ???S?
    
    
    
    $ pigz -d < .git/objects/18/143661f96845f11e0b4ab7312bdc0f356834ce
    
    # TAG類型,顯示結果為>>>>
    tag 155object 3020feea86d222d83218eb3eb5aa9f58f73df04d
    type commit
    tag v1.0
    tagger jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562045942 +0800
    
    release: 1.0.0
    
    

    會發現,顯示結果都是 type size+內容 形式,這就是 object 文件的存儲格式:


    [type] [size][NULL][content]
    
    

    type 可選值:commit, tree, blob, tag,NULL 就是C語言里的字符結束符:?,size 就是 NULL后內容的字節長度。

    type 的幾種類型可以使用 git cat-file -t hash 看到,內容可以用 git cat-file -p hash 看到。


    git cat-file -t ccc9bd67dc5c467859102d53d54c5ce851273bdd
    # 顯示結果為>>>>
    blob
    
    git cat-file -p ccc9bd67dc5c467859102d53d54c5ce851273bdd
    # 顯示結果為>>>>
    xx
    
    

    所以 blob 文件就是對原文件內容的全量拷貝,同時前面加了 blob size?,而文件名稱的 hash 值計算是計算整體字符的 SHA-1 值:


    echo -en 'blob 3?xxn' | shasum
    # 顯示結果為>>>>
    ccc9bd67dc5c467859102d53d54c5ce851273bdd  -
    
    

    知道原理后,其它類型格式請自行參考 斯坦福 Ben Lynn 所著的 GitMagic。

    所以,當我們 git show 3020feea86d222d83218eb3eb5aa9f58f73df04d 時,會發生些什么?

    找到 3020feea86d222d83218eb3eb5aa9f58f73df04d 這個 commit,顯示出來
    找到此 commit 關聯的 tree object: adf4c9afac7afae3ff3e95e6c4eefe009d547f00,拉取相應的 blob 文件,并與當前工作區內的文件做 diff,然后顯示出來

    這就是 objects/ 文件夾作為 git數據庫 被使用的真實例子。pack文件

    為什么會有 .pack 文件?

    由于每次 commit 都會生成許多 hash文件,并且由于 blob 文件都是全量存儲的,導致 git 效率下降,于是有了 pack-format,優勢:

    對于大倉庫存儲效率高
    利于網絡傳輸,便于備份
    增量存儲,優化磁盤空間
    將 .git/objects 下的部分文件打包成 pack格式


    $ tree .git/objects/ | wc -l
    311
    
    $ git gc
    Enumerating objects: 288, done.
    Counting objects: 100% (288/288), done.
    Delta compression using up to 4 threads
    Compressing objects: 100% (287/287), done.
    Writing objects: 100% (288/288), done.
    Total 288 (delta 131), reused 90 (delta 0)
    
    $ tree .git/objects/ | wc -l
    12
    
    

    可以看到文件數量減小了不少,其中大部分文件被打到一個 .pack 包中,并且是增量存儲,有部分變更的文件只存儲 基礎hash + 變更內容,磁盤空間優化很明顯。

    git gc 其實運行了兩條命令:git repack 用來打包 和 git prune-packed 用來移除已打包的 hash文件;

    11.文件夾refs

    refs 可以理解成文件系統中的 symbol link,看下結構:


    $ tree .git/refs/
    
    .git/refs
    |-- heads
    | `-- master
    `-- tags
    `-- v1.0
    
    $ cat .git/refs/heads/master
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
    
    $ cat .git/refs/tags/v1.0
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
    
    $ git cat-file -t 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
    commit
    
    

    可以看到 master 和 v1.0 都指向 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 這個 commit。

    refs/heads/ 文件夾內的 ref 一般通過 git branch 生成。git show-ref --heads 可以查看。

    refs/tags/ 文件夾內的 ref 一般通過 git tag 生成。git show-ref --tags 可以查看。

    如下:


    $ git branch abc
    
    $ tree .git/refs/
    
    .git/refs/
    |-- heads
    | |-- abc
    | `-- master
    `-- tags
    `-- v1.0
    
    $ cat .git/refs/heads/abc
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
    
    

    說明新建分支其實就是生成了一個指向某個 commit 的 symbol link,當然在這里叫做 ref。

    git tag 命令本質與 git branch 相同,只生成一個 ref 放在 tags 目錄下,所以被稱為 lightweight tag。

    git tag -a xx 命令會首先生成一個類型為 tag 的 hash文件 放到 objects/ 目錄,然后生成 ref 放到 tags 目錄下指向那個文件。這就叫做 annotated tag,好處是可包含一些元信息如 tagger 和 message,被 git 的 hash-object 算法管理,可被 GPG 簽名等,所以更穩定,更安全。

    使用以下命令來拿到 refs 文件夾存儲的信息:


    $ git show-ref --head --dereference
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0
    5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}
    
    

    我們來看這些信息如何變化的:


    $ touch new_file && git add . && git commit -m 'add new_file'
    [master 44b0d05] add new_file
    1 file changed, 0 insertions(+), 0 deletions(-)
    create mode 100644 new_file
    
    $ git show-ref --head --dereference
    44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 HEAD
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc
    44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 refs/heads/master
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0
    5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}
    
    

    diff 一下可以看到:


    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD
    5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master
    
    

    這兩行發生了變化。也就是每次 commit 時,HEAD 與 heads 都會自動更新。

    12. index文件

    文件保存成二進制對象以后,還需要通知 Git 哪些文件發生了變動。所有變動的文件,Git 都記錄在一個區域,叫做"暫存區"(英文叫做 index 或者 stage)。等到變動告一段落,再統一把暫存區里面的文件寫入正式的版本歷史。

    git update-index命令用于在暫存區記錄一個發生變動的文件。


    $ git update-index --add --cacheinfo 100644 
    3b18e512dba79e4c8300dd08aeb37f8e728b8dad test.txt
    
    

    上面命令向暫存區寫入文件名test.txt、二進制對象名(哈希值)和文件權限。

    git ls-files命令可以顯示暫存區當前的內容。


    $ git ls-files --stage
    100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 test.txt
    
    

    上面代碼表示,暫存區現在只有一個文件test.txt,以及它的二進制對象名和權限。知道了二進制對象名,就可以在.git/objects子目錄里面讀出這個文件的內容。

    git status命令會產生更可讀的結果。


    $ git status
    要提交的變更:
        新文件: test.txt
    
    

    上面代碼表示,暫存區里面只有一個新文件test.txt,等待寫入歷史。

    資料來源

    參考:https://developer.aliyun.com/article/716483

    Git遠程倉庫

    Git 并不像 SVN 那樣有個中心服務器。目前我們使用到的 Git 命令都是在本地執行,如果你想通過 Git 分享你的代碼或者與其他開發人員合作。 你就需要將數據放到一臺其他開發人員能夠連接的服務器上。

    1.添加遠程倉庫


    git remote add [shortname] [url] #添加遠程倉庫
    git remote rm name  # 刪除遠程倉庫
    git remote rename old_name new_name  # 修改倉庫名
    

    2.查看遠端倉庫


    $ git remote
    origin
    $ git remote -v
    origin    git@github.com:tianqixin/runoob-git-test.git (fetch)
    origin    git@github.com:tianqixin/runoob-git-test.git (push)
    
    

    3.獲取遠端倉庫代碼 git fetch


    # 只能fetch到一個空白的分支,然后可以手動merge
    $ git fetch <遠程主機名> <遠程分支名>:<本地分支名>
    

    不填的話都是默認

    4.拉取 git pull


    git pull <遠程主機名> <遠程分支名>:<本地分支名>
    # 允許合并不相關的分支 
    $ git pull --allow-unrelated-histories
    

    git pull操作其實是git fetch 與 git merge 兩個命令的集合。 git fetch 和 git merge FETCH_HEAD 的簡寫。

    相關文檔:https://www.runoob.com/git/git-remote-repo.html

    5.推送 git push


    # 基本
    $ git push <遠程主機名> <本地分支名>:<遠程分支名>
    # 強制推送
    $ git push --force origin master
    # 刪除遠程分支
    $ git push origin --delete master
    # 允許合并不相關的分支
    $ git push --allow-unrelated-histories
    
    
    提示
    如果另一個開發者在我們之前已經做過一次 push 操作,此次 push 命令就會被拒絕傳送提交。這時候,我們必須要先做一次 pull 操作,將其他人新上載的更新取回,并本地合并。

    如果本地分支名與遠程分支名相同,則可以省略冒號,帶上-u 參數相當于記錄了push到遠端分支的默認值,這樣當下次我們還想要繼續push的這個遠端分支的時候推送命令就可以簡寫成git push即可。

    Git 分支

    1.創建分支命令 git branch


    # 創建分支
    $ git branch <branch>
    # 創建分支并跟蹤遠程分支
    $ git branch -u o/master foo
    

    2.切換分支命令 git checkout

    第一作用是切換分支,第二個是撤銷修改。


    # 切換指定分支
    $ git checkout <branch>|<hash>|<tag>
    # 創建一個的分支,它跟蹤遠程分支
    $ git checkout -b 本地分支名x origin/遠程分支名x
    # 從暫存區恢復到工作區
    $ git checkout .
    
    
    提示
    當你切換分支的時候,Git 會用該分支的最后提交的快照替換你的工作目錄的內容, 所以多個分支不需要多個目錄。

    實際測試:

    假設分支1 a文件未提交,分支2 a文件已提交。切換到到分支2時會替換 分支1的a文件。1切換到2時也會替換a文件;

    兩個分支都已經提交的 切換時會互相替換。一個提交一個沒提交時,從a到b,b會保持a的暫存區和工作區

    3.合并分支命令 git merge


    # 合并指定分支到當前分支
    $ git merge <branch>
    
    

    4.刪除分支 git branch -d


    # 刪除指定分支
    $ git branch -d <branch>
    
    

    5.分支列表 git branch


    # 列出所有分支
    $ git branch
    # 查看遠程所有分支
    $ git branch -r
    # 查看本地和遠程所有分支
    $ git branch -a
    
    

    列出分支時,帶*號的分支為當前活動分支

    5.重命名分支 git branch -M


    # 重命名指定分支
    # 不填old默認重命名當前分支
    $ git branch -m old new
    
    # 強制重命名指定分支
    $ git branch -M old new
    

    git rebase 變基

    1.介紹

    Git rebase,通常被稱作變基或衍合, 可以理解為另外一種合并的方式,與merge 會保留分支結構和原始提交記錄不同,rebase 是在公共祖先的基礎上,把新的提交鏈截取下來,在目標分支上進行重放,逐個應用選中的提交來完成合并。

    不同公司,不同情況有不同使用場景,不過大部分情況推薦如下:

    自己單機的時候,拉公共分支最新代碼的時候使用rebase,也就是git pull -r或git pull --rebase。這樣的好處很明顯,提交記錄會比較簡潔。但有個缺點就是rebase以后我就不知道我的當前分支最早是從哪個分支拉出來的了,因為基底變了嘛,所以看個人需求了。

    往公共分支上合代碼的時候,使用merge。如果使用rebase,那么其他開發人員想看主分支的歷史,就不是原來的歷史了,歷史已經被你篡改了。舉個例子解釋下,比如張三和李四從共同的節點拉出來開發,張三先開發完提交了兩次然后merge上去了,李四后來開發完如果rebase上去(注意李四需要切換到自己本地的主分支,假設先pull了張三的最新改動下來,然后執行,然后再git push到遠端),則李四的新提交變成了張三的新提交的新基底,本來李四的提交是最新的,結果最新的提交顯示反而是張三的,就亂套了。

    正因如此,大部分公司其實會禁用rebase,不管是拉代碼還是push代碼統一都使用merge,雖然會多出無意義的一條提交記錄“Merge … to …”,但至少能清楚地知道主線上誰合了的代碼以及他們合代碼的時間先后順序

    2.原理

    變基操作的工作原理很簡單:Git 會讓我們想要移動的提交序列在目標分支上按照相同的順序重新再現一遍。這就相當于我們為各個原提交做了個副本,它們擁有相同的修改集、同一作者、日期以及注釋信息。

    3.命令


    # 可以是commit 版本號、分支名稱,合并多個提交到一個版本
    $ git rebase -i  [startpoint]  [endpoint]  
    # 變基發生沖突時,解決后繼續變基
    $ git rebase --continue
    # 無視沖突,繼續變基操作
    $ git rebase --skip 
    # 發生沖突時中斷變基
    $ git rebase --abort"
    

    -i的意思是--interactive,即彈出交互式的界面讓用戶編輯完成合并操作,[startpoint] [endpoint]則指定了一個編輯區間,如果不指定[endpoint],則該區間的終點默認是當前分支HEAD所指向的commit(注:該區間指定的是一個前開后閉的區間)。

    Git 標簽

    Git 中的tag指向一次commit的id,通常用來給開發分支做一個標記,如標記一個版本號。

    1.添加標簽


    git tag -a version -m "note"
    
    

    注解:git tag 是打標簽的命令,-a 是添加標簽,其后要跟新標簽號,-m 及后面的字符串是對該標簽的注釋。

    2.提交標簽到遠程倉庫


    git push origin -tags




    請登錄后查看

    小碼二開 最后編輯于2024-05-21 16:08:24

    快捷回復
    回復
    回復
    回復({{post_count}}) {{!is_user ? '我的回復' :'全部回復'}}
    排序 默認正序 回復倒序 點贊倒序

    {{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level }}

    作者 管理員 企業

    {{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
    {{item.is_suggest == 1? '取消推薦': '推薦'}}
    沙發 板凳 地板 {{item.floor}}#
    {{item.user_info.title || '暫無簡介'}}
    附件

    {{itemf.name}}

    {{item.created_at}}  {{item.ip_address}}
    打賞
    已打賞¥{{item.reward_price}}
    {{item.like_count}}
    {{item.showReply ? '取消回復' : '回復'}}
    刪除
    回復
    回復

    {{itemc.user_info.nickname}}

    {{itemc.user_name}}

    回復 {{itemc.comment_user_info.nickname}}

    附件

    {{itemf.name}}

    {{itemc.created_at}}
    打賞
    已打賞¥{{itemc.reward_price}}
    {{itemc.like_count}}
    {{itemc.showReply ? '取消回復' : '回復'}}
    刪除
    回復
    回復
    查看更多
    打賞
    已打賞¥{{reward_price}}
    2054
    {{like_count}}
    {{collect_count}}
    添加回復 ({{post_count}})

    相關推薦

    快速安全登錄

    使用微信掃碼登錄
    {{item.label}} 加精
    {{item.label}} {{item.label}} 板塊推薦 常見問題 產品動態 精選推薦 首頁頭條 首頁動態 首頁推薦
    取 消 確 定
    回復
    回復
    問題:
    問題自動獲取的帖子內容,不準確時需要手動修改. [獲取答案]
    答案:
    提交
    bug 需求 取 消 確 定
    打賞金額
    當前余額:¥{{rewardUserInfo.reward_price}}
    {{item.price}}元
    請輸入 0.1-{{reward_max_price}} 范圍內的數值
    打賞成功
    ¥{{price}}
    完成 確認打賞

    微信登錄/注冊

    切換手機號登錄

    {{ bind_phone ? '綁定手機' : '手機登錄'}}

    {{codeText}}
    切換微信登錄/注冊
    暫不綁定
    亚洲欧美字幕
    CRMEB客服

    CRMEB咨詢熱線 咨詢熱線

    400-8888-794

    微信掃碼咨詢

    CRMEB開源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
    返回頂部 返回頂部
    CRMEB客服