一文搞定Git!

icankeep發表於2020-11-08

Git

通俗來講Git就是一個程式碼版本控制系統,多個開發人員可以協同開發,開發時可以將程式碼提交到本地,開發完成後,push到遠端即可,不用到處備份copy程式碼,方便檢視版本間的diff。

安裝git

  1. Windows

    訪問 Git官網 下載安裝

  2. Mac OS X

    終端執行

    $git --version
    
  3. Linux

    訪問 Git官網 下載安裝

快速上手

  1. 首先我們需要在終端介面初始化git資料夾,這裡我們建立了demo資料夾並初始化

    $ mkdir demo && cd demo
    $ git init 
    已初始化空的 Git 倉庫於 /private/tmp/demo/.git/
    

    在初始化後,本地目錄的檔案就是在工作區中,在執行add後就新增到了暫存區,commit之後就正式存在本地倉庫了

  2. 建立一個README.md檔案,並新增Git中

    $ touch README.md
    $ echo '# git demo' > README.md
    $ cat README.md  
    # git demo
    
  3. 新增到stage中

    $ git add README.md
    
  4. 生成一個提交到本地倉庫, -m後跟的內容為本次提交的描述資訊,提交的檔案為已經add到stage中的檔案

    $ git commit -m "add README.md" 
    [master(根提交) 8c28b6f] add README.md
    1 file changed, 1 insertion(+)
    create mode 100644 README.md
    
  5. 隨時檢視本地git狀態

    $ git status 
    位於分支 master
    無檔案要提交,乾淨的工作區
    

    如圖就表示工作區中的檔案均已提交到本地git倉庫中

  6. 新增遠端倉庫(可以到Github中建立遠端倉庫,獲取地址),origin為遠端倉庫名,可以自行指定

    git remote add origin git@github.com:icankeep/demo.git
    

    可以通過下面命令檢視是否新增遠端倉庫成功

    $ git remote show  origin 
    * 遠端 origin
    獲取地址:git@github.com:icankeep/demo.git
    推送地址:git@github.com:icankeep/demo.git
    HEAD 分支:(未知)
    
  7. push本地提交到遠端倉庫

    $ git push origin master    
    總共 0(差異 0),複用 0(差異 0),包複用 0
    remote: 
    remote: Create a pull request for 'master' on GitHub by visiting:
    remote:      https://github.com/icankeep/demo/pull/new/master
    remote: 
    To github.com:icankeep/demo.git
    * [new branch]      master -> master
    

以上我們就完成了最簡單的git提交

進階

檢視分支

  1. 檢視本地分支

    $ git branch   
    * dev
    master
    merge_demo
    rebase_demo
    
  2. 檢視所有分支

    $ git branch -a
    dev
    * master
    merge_demo
    rebase_demo
    remotes/origin/dev
    remotes/origin/master
    

建立並切換分支

一般來說,一個專案組的主分支為master,當我們開發時,會建立一個dev分支,在本地開發測試完成後會合併到線上的分支,如staging,提交到線上staging測試完成後,再合併到主分支釋出。我們可以使用下面的命令進行建立並切換分支

$ git checkout -b dev
切換到一個新分支 'dev'

git checkout一般有以下幾種用途:

  1. 切換建立新分支

  2. 檢出commit,該操作會檢出該commit,並detach(和git reset不一樣,reset是在當前分支回退,checkout不影響當前分支,只是檢出)。如下,switch是新版本git引入切換分支的命令

    $ git checkout 2ce5a34
    注意:正在切換到 '2ce5a34'。
    
    您正處於分離頭指標狀態。您可以檢視、做試驗性的修改及提交,並且您可以在切換
    回一個分支時,丟棄在此狀態下所做的提交而不對分支造成影響。
    
    如果您想要通過建立分支來保留在此狀態下所做的提交,您可以通過在 switch 命令
    中新增引數 -c 來實現(現在或稍後)。例如:
    
    git switch -c <新分支名>
    
    或者撤銷此操作:
    
    git switch -
    
    通過將配置變數 advice.detachedHead 設定為 false 來關閉此建議
    
    HEAD 目前位於 2ce5a34 master demo
    
  3. 去除對暫存區中檔案的修改

    git checkout -- filename
    

拉取遠端分支

  1. git fetch會將遠端commit更新到本地,但並不會主動合併

    $ git fetch origin master
    來自 github.com:icankeep/demo
    * branch            master     -> FETCH_HEAD
    
  2. git pull就相當於git fetch origin master && git merge origin/master

將修改儲存進緩衝堆疊

如果暫存區的內容發生了改變,想要將這個改變暫時儲存,可以使用git stash

如下,我們修改了README.md,新增了test檔案

$ git status 
位於分支 dev
要提交的變更:
  (使用 "git restore --staged <檔案>..." 以取消暫存)
        修改:     README.md

尚未暫存以備提交的變更:
  (使用 "git add <檔案>..." 更新要提交的內容)
  (使用 "git restore <檔案>..." 丟棄工作區的改動)
        修改:     test

按照下面命令就可以儲存啦

$ git stash          
儲存工作目錄和索引狀態 WIP on dev: ea15522 Merge branch 'merge_demo'

可以使用git stash listgit stash show stash@{0}檢視stash資訊

$ git stash list  
stash@{0}: WIP on dev: ea15522 Merge branch 'merge_demo'
$ git stash show stash@{0}
 README.md | 1 +
 test      | 1 +
 2 files changed, 2 insertions(+)

將緩衝堆疊中內容提取出來

$ git stash pop  
位於分支 dev
尚未暫存以備提交的變更:
  (使用 "git add <檔案>..." 更新要提交的內容)
  (使用 "git restore <檔案>..." 丟棄工作區的改動)
        修改:     README.md
        修改:     test

修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
丟棄了 refs/stash@{0}(f6965744573d97e52b165e886d625ae1ddccf36a)

檢視提交歷史

當我們開發完程式碼git commit之後,我們就可以通過下面的命令檢視提交歷史

$ git log 
commit abf83aa02ceb51e71d209777e2411b27a0f6b417
Author: passer <whzhoua@gmail.com>
Date:   Sat Nov 7 21:32:32 2020 +0800

    add README.md

如圖,每個commit都有對應的一個hash值(基本不可能碰撞)

檢視檔案修改差異

後續如果我們需要開發,可以通過git diff等檢視修改後的檔案和之前commit檔案的差異

$ git diff README.md
diff --git a/README.md b/README.md
index 7b81bad..b4f319c 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,4 @@ master demo
 1
 2
 3
+modify some things

合併分支

當程式碼在本地開發並測試完成後,我們一般需要將程式碼合併到其他正式分支,解決衝突的具體操作我們在後面rebase中說明

$ git checkout master
$ git merge dev

合併多個提交rebase

rebase是git中非常強大的功能,既可以merge分支,也可以合併修改commit資訊等。當我們本地開發時,可能會經常遇到剛提交完一個commit,發現程式碼中還有一些錯誤,於是又提交一個commit,會造成一個功能太多個commit,提交資訊混亂,於是我們就需要合併這些無用的commit。

  1. git rebase 分支名

    git rebase demo指當前分支合併demo分支,git mergegit rebase的差異在於如果當前分支已經有了新的提交(demo分支中不存在的提交),git merge就會產生一個merge commit(即一個額外的提交),git rebase則不會。實踐出真知,直接開始操作

    在之前master分支"add README.md"提交之後切出新建立的rebase_demo分支和merge_demo分支

    git checkout -b rebase_demo
    echo 'rebase demo' >> README.md
    git commit -am "rebase demo"
    

    通過git log檢視提交歷史,發現已經提交

    $ git log --graph --pretty=oneline --abbrev-commit    
    * 0eec621 (HEAD -> rebase_demo) rebase demo
    * abf83aa (master) README.md
    

    切換到master分支rebase我們的rebase_demo分支

    $ git checkout master
    切換到分支 'master'
    
    $ git rebase rebase_demo
    成功變基並更新 refs/heads/master。
    
    $ git log --graph --pretty=oneline --abbrev-commit    
    * 0eec621 (HEAD -> master, rebase_demo) rebase demo
    * abf83aa README.md
    

    將rebase_demo分支合併到master分支成功,並且沒有產生新的提交,我們接著通過merge的方式來看有沒有新的提交

    git checkout -b merge_demo
    echo 'merge demo' >> README.md
    git commit -am "merge demo"
    

    通過git log檢視提交歷史,發現已經提交

    $ git log --graph --pretty=oneline --abbrev-commit     
    * 65d472a (HEAD -> merge_demo) merge demo
    * abf83aa (master) README.md
    
    git checkout master
    git merge merge_demo
    
    $ git log --graph --pretty=oneline --abbrev-commit      
    * 65d472a (HEAD -> master) merge demoREADME.md
    * abf83aa README.md
    

    可知,當發起合併的分支中沒有新commit時,rebase和merge都是不會產生新的提交的,我們現在來看看當存在新提交時,會是什麼情況

    首先,我們來看下當前的分支情況

    $ git log --graph --pretty=oneline --abbrev-commit  --all
    * 1fe102c (merge_demo) merge demo
    | * 0eec621 (rebase_demo) rebase demo
    |/  
    * abf83aa (HEAD -> master) README.md
    

    master當前位於第一個提交,而rebase_demo分支提交了一個rebase demo, merge_demo提交了一個merge demo,為了產生衝突,我們在master分支提交一個master demo即可

    echo 'master demo' >> README.md
    git commit -am "master demo"
    
    $ git log --graph --pretty=oneline --abbrev-commit  --all
    * 2ce5a34 (HEAD -> master) master demo
    | * 1fe102c (merge_demo) merge demo
    |/  
    | * 0eec621 (rebase_demo) rebase demo
    |/  
    * abf83aa README.md
    

    此時每個分支都對檔案README.md做出了修改,這時將rebase_demo或merge_demo合併到master,都將產生衝突

    $ git rebase rebase_demo   
    自動合併 README.md
    衝突(內容):合併衝突於 README.md
    error: 不能應用 2ce5a34... master demo
    Resolve all conflicts manually, mark them as resolved with
    "git add/rm <conflicted_files>", then run "git rebase --continue".
    You can instead skip this commit: run "git rebase --skip".
    To abort and get back to the state before "git rebase", run "git rebase --abort".
    不能應用 2ce5a34... master demo
    

    通過git status檢視狀態

    $ git status   
    互動式變基操作正在進行中;至 0eec621
    最後一條命令已完成(1 條命令被執行):
    pick 2ce5a34 master demo
    未剩下任何命令。
    您在執行將分支 'master' 變基到 '0eec621' 的操作。
    (解決衝突,然後執行 "git rebase --continue")
    (使用 "git rebase --skip" 跳過此補丁)
    (使用 "git rebase --abort" 以檢出原有分支)
    
    未合併的路徑:
    (使用 "git restore --staged <檔案>..." 以取消暫存)
    (使用 "git add <檔案>..." 標記解決方案)
            雙方修改:   README.md
    
    修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
    

    修改衝突檔案,將<<<<<<< HEAD >>>>>>> 2ce5a34… master demo之間的內容修改成最終的,wq儲存退出,重新git add

    $ vim README.md
    
    # git demo
    <<<<<<< HEAD
    rebase demo
    =======
    # master
    master demo
    >>>>>>> 2ce5a34... master demo
    
    $ git add README.md
    $ git rebase --continue
    

    rebase_demo分支已經rebase到master分支,並沒有產生新的提交

    $ git log --graph --pretty=oneline --abbrev-commit  --all
    * 24e6dfa (HEAD -> master) master demo
    * 0eec621 (rebase_demo) rebase demo
    | * 1fe102c (merge_demo) merge demo
    |/  
    * abf83aa README.md
    

    回到最初狀態繼續嘗試下merge

    通過git reflog可以檢視所以git操作的id,隨時可以回退到對應的id

    $ git reflog 
    24e6dfa (HEAD -> master) HEAD@{0}: rebase (continue) (finish): returning to refs/heads/master
    24e6dfa (HEAD -> master) HEAD@{1}: rebase (continue): master demo
    0eec621 (rebase_demo) HEAD@{2}: rebase (start): checkout rebase_demo
    2ce5a34 HEAD@{3}: commit: master demo
    ...
    

    我們通過git reset回到對應操作id上

    $ git reset --hard 2ce5a34  
    HEAD 現在位於 2ce5a34 master demo
    
    $ git log --graph --pretty=oneline --abbrev-commit  --all
    * 2ce5a34 (HEAD -> master) master demo
    | * 1fe102c (merge_demo) merge demo
    |/  
    | * 0eec621 (rebase_demo) rebase demo
    |/  
    * abf83aa README.md
    

    一切如初,繼續嘗試merge

    $ git merge merge_demo
    自動合併 README.md
    衝突(內容):合併衝突於 README.md
    自動合併失敗,修正衝突然後提交修正的結果。
    
    $ git status   
    位於分支 master
    您有尚未合併的路徑。
    (解決衝突並執行 "git commit")
    (使用 "git merge --abort" 終止合併)
    
    未合併的路徑:
    (使用 "git add <檔案>..." 標記解決方案)
            雙方修改:   README.md
    
    修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
    
    vim README.md
    
    # git demo
    <<<<<<< HEAD
    # master
    master demo
    =======
    merge demo
    >>>>>>> merge_demo
    

    發現解決完衝突之後,會提示需要commit來結束衝突,所以當存在衝突時merge會產生額外一個commit

    git add README.md
    $ git status 
    位於分支 master
    所有衝突已解決但您仍處於合併中。
    (使用 "git commit" 結束合併)
    
    $ git commit -m "merge" 
    [master 52ecde5] merge
    
    $ git log --graph --pretty=oneline --abbrev-commit  --all
    *   52ecde5 (HEAD -> master) merge
    |\  
    | * 1fe102c (merge_demo) merge demo
    * | 2ce5a34 master demo
    |/  
    | * 0eec621 (rebase_demo) rebase demo
    |/  
    * abf83aa README.md
    

    可知產生了52ecde5這個額外的commit,以上就是rebase和merge合併分支上的區別

  2. git rebase合併commit

    檢視當前master分支狀態

    $ git log --graph --pretty=oneline --abbrev-commit 
    * d576335 (HEAD -> master) 開發測試3
    * b30a41e 開發測試2
    * 209a0c1 開發測試
    * 2ce5a34 master demo
    * abf83aa README.md
    

    可知,存在一些開發測試過程中的commit,我們需要對其進行合併,即將3個開發測試的commit合併成一個,可以執行如下命令,3為合併的commit數目

    git rebase -i HEAD~3
    

    就會進入如下介面

    pick 209a0c1 開發測試
    pick b30a41e 開發測試2
    pick d576335 開發測試3
    
    # 變基 2ce5a34..d576335 到 2ce5a34(3 個提交)
    #
    # 命令:
    # p, pick <提交> = 使用提交
    # r, reword <提交> = 使用提交,但修改提交說明
    # e, edit <提交> = 使用提交,進入 shell 以便進行提交修補
    # s, squash <提交> = 使用提交,但融合到前一個提交
    # f, fixup <提交> = 類似於 "squash",但丟棄提交說明日誌
    # x, exec <命令> = 使用 shell 執行命令(此行剩餘部分)
    # b, break = 在此處停止(使用 'git rebase --continue' 繼續變基)
    # d, drop <提交> = 刪除提交
    # l, label <label> = 為當前 HEAD 打上標記
    # t, reset <label> = 重置 HEAD 到該標記
    # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
    # .       建立一個合併提交,並使用原始的合併提交說明(如果沒有指定
    # .       原始提交,使用註釋部分的 oneline 作為提交說明)。使用
    # .       -c <提交> 可以編輯提交說明。
    #
    # 可以對這些行重新排序,將從上至下執行。
    #
    # 如果您在這裡刪除一行,對應的提交將會丟失。
    #
    # 然而,如果您刪除全部內容,變基操作將會終止。
    

    按照提示修改pick為f,儲存並退出即可

    pick 209a0c1 開發測試
    f b30a41e 開發測試2
    f d576335 開發測試3
    

    發現commit已經合併了

    $ git log --graph --pretty=oneline --abbrev-commit
    * 86b2868 (HEAD -> master) 開發測試
    * 2ce5a34 master demo
    * abf83aa README.md
    

    當rebase用於合併commit時

    如果需要修改commit資訊,可以使用如下命令

    git commit --amend
    

    進入如下介面,修改並儲存即可

    開發測試
    
    # 請為您的變更輸入提交說明。以 '#' 開始的行將被忽略,而一個空的提交
    # 說明將會終止提交。
    #
    # 日期:  Sat Nov 7 23:20:29 2020 +0800
    #
    # 位於分支 master
    # 要提交的變更:
    #       修改:     README.md
    #
    

    發現commit資訊已經被修正

    $ git log --graph --pretty=oneline --abbrev-commit 
    * 5d0d00a (HEAD -> master) 正式提交
    * 2ce5a34 master demo
    * abf83aa README.md
    

重置提交reset

簡單粗暴,直接命令上手,下面命令將commitId改成要回退到的commit hash值在執行就可以回退到指定的commit

git reset --hard commitId

除了–hard還有–mixed和–soft, git reset預設是–mixed

  1. mixed

    $ git reset --mixed 2ce5a34 
    重置後取消暫存的變更:
    M       README.md
    

    使用git status檢視狀態,發現之前commit的檔案已經回退到工作區

    $ git status   
    位於分支 master
    尚未暫存以備提交的變更:
    (使用 "git add <檔案>..." 更新要提交的內容)
    (使用 "git restore <檔案>..." 丟棄工作區的改動)
            修改:     README.md
    
    修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
    
  2. soft

    $ git reset --soft 2ce5a34
    $ git status 
    位於分支 master
    要提交的變更:
    (使用 "git restore --staged <檔案>..." 以取消暫存)
            修改:     README.md
    

    發現使用soft重置之後,修改內容會新增到暫存區

  3. hard

    $ git reset --hard  2ce5a34  
    HEAD 現在位於 2ce5a34 master demo
    
    $ git status 
    位於分支 master
    無檔案要提交,乾淨的工作區
    

    使用hard重置提交,提交的內容均會消失,如果不小心重置了,可以用git reflog檢視並重置到指定的操作上哦!

提取commit

我們在一個分支中可能會需要另一個分支的一個commit,這個時候我們就可以用到下面這個命令進行提取

如下狀態,我們如果在rebase_demo分支獲取master分支5d0d00a這個commit的內容,我們就可以使用cherry-pick

$ git log --graph --pretty=oneline --abbrev-commit --all
* 5d0d00a (HEAD -> master) 正式提交
* 2ce5a34 master demo
| * 1fe102c (merge_demo) merge demo
|/  
| * 0eec621 (rebase_demo) rebase demo
|/  
* abf83aa README.md
git cherry-pick 5d0d00a

給commit打上標籤

給當前分支最新的提交打上標籤

$ git tag v1.0

檢視標籤

$ git tag   
v1.0
$ git log --graph --pretty=oneline --abbrev-commit 
*   ea15522 (HEAD -> dev, tag: v1.0, master) Merge branch 'merge_demo'

將標籤推送到遠端倉庫

$ git push origin v1.0
總共 0(差異 0),複用 0(差異 0),包複用 0
To github.com:icankeep/demo.git
 * [new tag]         v1.0 -> v1.0

推送本地的所有tag

git push origin --tags

將標籤打到具體的commit上

$ git tag v0.7 8c28b6f 

可以看到v0.7已經打到8c28b6f上了

$ git show v0.7   
commit 8c28b6fbe67d37723f43f1df20a0d360cabf14f5 (tag: v0.7)
Author: passer <whzhoua@gmail.com>
Date:   Sun Nov 8 13:51:04 2020 +0800

    add README.md

如果打錯標籤了,需要刪除,可以按照下面的命令

$ git tag -d v0.7 
已刪除標籤 'v0.7'(曾為 8c28b6f)

當然,這只是在本地刪除標籤,如果該標籤已經推送到遠端,想要刪除的話,還得加上下面這一步

$ git push origin :refs/tags/v0.7
To github.com:icankeep/demo.git
 - [deleted]         v0.7

相關文章