【Git】命令行操作


Git 命令行操作

image

  • 默認當前遠程版本庫:origin,Git里面的origin就是一個名字,它是在你clone一個托管在Github上代碼庫時,git為你默認創建的指向這個遠程代碼庫的標簽。
  • 默認開發分支:master
  • 默認當前分支:HEAD
  • 當前分支的父提交(上一版本):HEAD^
  • 版本別名:提交版本后的括號內為該分支的最新版本,可以作為版本別名進行reset
    image

image

image

個人理解:我們使用git操作,其實就是將工作區指針到處移動而已。

  • 工作區:指針
  • 本地庫:本地數組
  • 遠程庫:遠程數組

SEQUENCER SUBCOMMANDS 序列化子命令

--continue:繼續
Continue the operation in progress using the information in .git/sequencer. Can be used to continue after resolving conflicts in a failed cherry-pick or revert.
使用.git / sequencer中的信息繼續進行操作。可用於在解決失敗的挑選或恢復中的沖突后繼續。

--skip:略過
Skip the current commit and continue with the rest of the sequence.
跳過當前提交,繼續完成序列的其余部分。

--quit:退出
Forget about the current operation in progress. Can be used to clear the sequencer state after a failed cherry-pick or revert.
忘記當前正在進行的操作。可以用來清除序列狀態在一個失敗的挑選或恢復后。

--abort:中止
Cancel the operation and return to the pre-sequence state.
取消操作並返回到預序列狀態。

1 本地庫初始化

git init:初始化本地倉庫

效果

注意:.git目錄中存放的是本地庫相關的子目錄和文件,不要刪除,也不要胡亂修改。

2 設置配置簽名

形式:

用戶名:tom
Email地址:goodMorning@atguigu.com

作用:區分不同開發人員的身份

辨析:這里設置的簽名和登錄遠程庫(代碼托管中心)的賬號、密碼沒有任何關系。

命令

  • 項目級別/倉庫級別:僅在當前本地庫范圍內有效
    • git config user.nametom_pro
    • git config user.emailgoodMorning_pro@atguigu.com
    • 信息保存位置:./.git/config文件
  • 系統用戶級別:登錄當前操作系統的用戶范圍
    • git config --global user.nametom_glb
    • git config --global goodMorning_pro@atguigu.com
    • 信息保存位置:~/.gitconfig文件(~代表根目錄)
  • 級別優先級
    • 就近原則:項目級別優先於系統用戶級別,二者都有時采用項目級別的簽名
    • 如果只有系統用戶級別的簽名,就以系統用戶級別的簽名為准
    • 二者都沒有不允許

使用git config --list進行查看配置列表

3 基本操作

3. 1 狀態查看

git status:查看工作區、暫存區狀態

3. 2 添加

git add [filename]:將工作區的“新建/修改”添加到暫存區

  • filename:為欲添加的文件名,*代表當前目錄的所有文件。

3. 3 提交

git commit -m "commit message" [filename]:將暫存區的內容提交到本地庫

個人理解:

  • add:將我們的代碼交給git監管
  • commit:將我們的代碼提交到本地庫
  • push:將我們的代碼推送到遠程庫

3. 4 查看歷史記錄日志

git log:查看歷史記錄,顯示完整的歷史記錄信息

多屏顯示控制方式:

  • 空格:向下翻頁
  • b:向上翻頁
  • q:退出

git --grep xxx:搜索日志


上面這種方法顯示的歷史記錄可能不太好看:我們可以采用以下方法進行簡化歷史記錄的顯示:

git log --pretty=oneline:我們可以使用此命令讓歷史記錄放在一行上

git log --oneline:縮短了哈希值的顯示,只顯示后面7位

上面的幾種方法只能顯示過去與現在


下面這個命令可以顯示過去、現在和未來

git reflog:這種方式可以看到我們所有的歷史記錄,包括回退前、回退后、過去、現在、將來。

HEAD@{移動到當前版本需要多少步}

git reflog --all:可以查看更完整的所有記錄

3. 5 前進后退

本質:使用git reflog來顯示所有歷史記錄

可以使用以下方法來移動HEAD指針

基於索引值操作[推薦]

  • git reset --hard [局部索引值]
  • git reset --hard a6ace91

使用^符號:只能后退

  • git reset --hard HEAD^:一個^表示后退一步,n個表示后退n步

使用~符號:只能后退

  • git reset --hard HEAD~n:表示后退n步

回退后提交只能使用git push -f強制提交,因為在他前面已經有其他新節點了,得強行覆蓋掉。

3. 6 reset 命令的三個參數對比

image

原本本地庫、暫存區、工作區三者版本一致:

--soft參數

  • 僅僅在本地庫移動HEAD指針(本地庫回退)
  • 暫存區還是原樣
  • 工作區還是原樣

    這里可以看出,我們的本地庫版本退后了一步,而其他的都沒改變

--mixed參數(默認

  • 在本地庫移動HEAD指針(本地庫回退)
  • 重置暫存區(重置,即 后退版本)(暫存區回退)
  • 工作區還是原樣

--hard參數(危險,會完全回到)

  • 在本地庫移動HEAD指針(本地庫回退)
  • 重置暫存區(暫存區回退)
  • 重置工作區(工作區回退)

讓單個文件回滾到指定版本

1.進入到文件所在文件目錄,或者能找到文件的路徑

查看文件的修改記錄

git log fileName

2.回退到指定版本

git reset 版本號 fileName

3.提交到本地參考:
git commit -m “提交的描述信息”

4.更新到工作目錄

git checkout fileName

5.提交到遠程倉庫

git push origin master

這樣指定的文件回退到指定版本了

3. 7 刪除文件並找回

前提:刪除前,文件存在時的狀態提交到了本地庫。

操作:git reset --hard [指針位置]

  • 刪除操作已經提交到本地庫:指針位置指向歷史記錄
  • 刪除操作尚未提交到本地庫:指針位置使用HEAD

還原(撤銷)某個版本

git revert <commit id> :git 會生成一個新的 commit,將指定的 commit 內容從當前分支上撤除。

在 Git 開發中通常會控制主干分支的質量,但有時還是會把錯誤的代碼合入到遠程主干。 雖然可以直接回滾遠程分支, 但有時新的代碼也已經合入,直接回滾后最近的提交都要重新操作。 那么有沒有只移除某些 Commit 的方式呢?可以一次 revert操作來完成。

考慮這個例子,我們提交了 6 個版本,其中 3-4 包含了錯誤的代碼需要被回滾掉。 同時希望不影響到后續的 5-6。

* 982d4f6 (HEAD -> master) version 6
* 54cc9dc version 5
* 551c408 version 4, harttle screwed it up again
* 7e345c9 version 3, harttle screwed it up
* f7742cd version 2
* 6c4db3f version 1

這種情況在團隊協作的開發中會很常見:可能是流程或認為原因不小心合入了錯誤的代碼, 也可能是合入一段時間后才發現存在問題。 總之已經存在后續提交,使得直接回滾不太現實。

下面的部分就開始介紹具體操作了,同時我們假設遠程分支是受保護的(不允許 Force Push,大多情況都是這樣,導致使用不了reset)。 思路是從產生一個新的 Commit 撤銷之前的錯誤提交。

使用 git revert <commit> 可以撤銷指定的提交, 要撤銷一串提交可以用 git revert <commit1>..<commit2> 語法。 注意這是一個前開后閉區間,即不包括 commit1,但包括 commit2。(這個跟Java中的截取字符串一樣,都是為了方便)

git revert -n f7742cd..551c408
# 這樣也可以 git revert --no-commit f7742cd..551c408
git commit -a -m 'This reverts commit 7e345c9 and 551c408'

其中 f7742cd 是 version 2,551c408 是 version 4,這樣被移除的是 version 3 和 version 4。 注意 revert 命令會對每個撤銷的 commit 進行一次提交--no-commit 后可以最后一起手動提交。

git revert -n COMMITgit revert --no-commit COMMIT:這就相當於在本節點恢復撤銷以前的COMMIT,然后不自行提交。也就是說只改變了本地的工作區代碼,並不改變節點提交版本號。

-n 是代表不自動提交,只是將回滾的代碼添加到,暫存區(效果類似 git add),因為 revert 默認是回滾一個版本,如果自動提交,最后的代碼會變成 verson1 – version2 – version3 – version4 – revert-to-version3 – revert-to-version2 會生成2個commit 記錄,所以,一般都要使用 -n(也寫作 --no-commit) 這個參數。

注意:reset是回到以前版本的節點上,而revert -n還是在本節點,只是恢復撤銷了。
-n, --no-commit
Usually the command automatically creates a sequence of commits. This flag applies the changes necessary to cherry-pick each named commit to your working tree and the index, without making any commit. In addition, when this option is used, your index does not have to match the HEAD commit. The cherry-pick is done against the beginning state of your index.

此時 Git 記錄是這樣的:

* 8fef80a (HEAD -> master) This reverts commit 7e345c9 and 551c408
* 982d4f6 version 6
* 54cc9dc version 5
* 551c408 version 4, harttle screwed it up again
* 7e345c9 version 3, harttle screwed it up
* f7742cd version 2
* 6c4db3f version 1

現在的 HEAD(8fef80a)就是我們想要的版本,把它 Push 到遠程即可。

確認 diff

如果你像不確定是否符合預期,畢竟批量干掉了別人一堆 Commit,可以做一下 diff 來確認。 首先產生 version 4(551c408)與 version 6(982d4f6)的 diff,這些是我們想要保留的:

git diff 551c408..982d4f6

然后再產生 version 2(f7742cd)與當前狀態(HEAD)的 diff:

git diff f7742cd..HEAD

如果 version 3, version 4 都被 version 6 撤銷的話,上述兩個 diff 為空。 可以人工確認一下,或者 grep 掉 description 之后做一次 diff。 下面介紹的另一種方法可以容易地確認 diff。

bd1b80cb 和0f2f7e4a 是錯誤的提交可以通過git revert bd1b80cb 0f2f7e4a 來進行單獨提交的回滾

3. 8 比較文件差異

git diff [文件名]:將工作區中的文件和暫存區進行比較

git diff [本地庫中歷史版本] [文件名]:將工作區中的文件和本地庫歷史記錄比較

git diff:不帶文件名比較多個文件

git diff HEAD^ HEAD:比較上一版本和現在版本的區別,即 上一版本如何到達現在版本,HEAD^ → HEAD

git diff --name-only:只輸出有變動的文件名

4 分支管理

4. 1 什么是分支?

在版本控制過程中,使用多條線同時推進多個任務。

4. 2 分支的好處?

  • 同時並行推進多個功能開發,提高開發效率
  • 各個分支在開發過程中,如果某一個分支開發失敗,不會對其他分支有任何影響。失敗的分支刪除重新開始即可。

4. 3 分支操作

git branch:查看本地分支。

電腦B本地clone倉庫默認只會clone下master分支,而其他電腦A推送的分支是不會默認同步下來的。
clone下來的遠程倉庫並不會將所有分支都clone下來。

git branch -a:查看所有分支,包括本地分支與遠程分支。
image

git branch [分支名] [commitId]:創建分支。當我們運行了git branch xx 之后,它就會默認基於你當前所在的這個分支最后一次提交的文件,進行一個復制,作為這個新創建的 xx 分支的初始代碼。

git branch -v:(version)查看本地分支的版本情況

git branch -d [分支名]:(delete)刪除分支

注意:在刪除分支的時候,一定要先退出要刪除的分支,然后才能刪除。

git checkout [分支名]:切換分支

合並分支

  • 第一步:切換到接受修改的分支(被合並,增加新內容)上:git checkout [被合並分支名]
  • 第二步:執行merge命令:git merge [有新內容分支名]
  • 第三步:刪除?:git branch -d [分支名]

查看分支:

注意:當前分支前面有個標記“*”。

創建分支:

切換分支:

合並分支:
現在先在dev分支下的readme文件中新增一行並提交本地

切換到master分支下觀察readme文件

將dev分支的內容與master分支合並:

刪除分支:

注意:在刪除分支的時候,一定要先退出要刪除的分支,然后才能刪除。

合並所有分支之后,需要將master分支提交線上遠程倉庫中:

合並提交

對於多分支的代碼庫,將代碼從一個分支轉移到另一個分支是常見需求。

這時分兩種情況。一種情況是,你需要另一個分支的所有代碼變動,那么就采用合並(git merge)。另一種情況是,你只需要部分代碼變動(某幾個提交),這時可以采用 Cherry pick。

git將某分支的某次提交合並到另一分支
代碼開發的時候,有時需要把某分支(比如develop分支)的某一次提交合並到另一分支(比如master分支),這就需要用到git cherry-pick命令。

  1. 首先,切換到develop分支,敲 git log 命令,查找需要合並的commit記錄,比如commitID:7fcb3defff

  2. 然后,切換到master分支,使用 git cherry-pick -n 7fcb3defff 命令,就把該條commit記錄合並到了master分支,這只是在本地合並到了master分支

    -n 是代表不自動提交,只是將回滾的代碼添加到,暫存區(效果類似 git add),因為 revert 默認是回滾一個版本,如果自動提交,最后的代碼會變成 verson1 – version2 – version3 – version4 – revert-to-version3 – revert-to-version2 會生成2個commit 記錄,所以,一般都要使用 -n(也寫作 --no-commit) 這個參數。

    -n, --no-commit
    Usually the command automatically creates a sequence of commits. This flag applies the changes necessary to cherry-pick each named commit to your working tree and the index, without making any commit. In addition, when this option is used, your index does not have to match the HEAD commit. The cherry-pick is done against the beginning state of your index.

  3. 最后,git push 提交到master遠程(可以用命令也可以用工具的push操作),至此,就把develop分支的這條commit所涉及的更改合並到了master分支。

解決沖突

  • 沖突的表現

  • 沖突的解決
    • 第一步:編輯文件,刪除特殊符號
    • 第二步:把文件修改到滿意的程度,保存退出
    • 第三步:git add [文件名]
    • 第四步:git commit -m "日志信息"

      注意:此時commit一定不能帶具體文件名

臨時存儲

git stash temporarily shelves (or stashes) changes you've made to your working copy so you can work on something else, and then come back and re-apply them later on. Stashing is handy if you need to quickly switch context and work on something else, but you're mid-way through a code change and aren't quite ready to commit.

git stash:能夠將所有未提交的修改(工作區和暫存區)保存至堆棧中,用於后續恢復當前工作目錄。

特別注意:保存的是修改,而不是一整個項目快照,所以你在A分支的修改,也可以同步到B分支,而不修改B分支的其他模塊代碼。

前提:必須是處於git下的文件,未add到git的文件無法使用。

  • git stash:保存當前工作進度,將工作區和暫存區恢復到修改之前。

  • git stash save "message":作用同上,message為此次進度保存的說明。

  • git stash list:顯示保存的工作進度列表,編號越小代表保存進度的時間越近。

  • git stash pop [stash@{num}]:恢復工作進度到工作區,此命令的stash@{num}是可選項,在多個工作進度中可以選擇恢復,不帶此項則默認恢復最近的一次進度相當於git stash pop stash@{0}

  • git stash apply [stash@{num}]:恢復工作進度到工作區且該工作進度可重復恢復,此命令的stash@{num}是可選項,在多個工作進度中可以選擇恢復,不帶此項則默認恢復最近的一次進度相當於git stash apply stash@{0}

  • git stash drop [stash@{num}]:刪除一條保存的工作進度,此命令的stash@{num}是可選項,在多個工作進度中可以選擇刪除,不帶此項則默認刪除最近的一次進度相當於git stash drop stash@{0}

  • git stash clear:刪除所有保存的工作進度。

檢出

我們一般使用檢出(checkout)都是檢出其他的分支,也就是分支切換,其實檢出還可以檢出暫存區、本地庫(如果暫存區沒有)或遠程庫(如果本地庫沒有)文件到我們的工作區,來達到一個拉取、恢復工作區代碼的作用。

重置修改

當我們修改了工作區的一些代碼后,發現有些文件的修改我們並不需要,需要還原,我們就可以使用checkout來重置、放棄修改。

git checkout -- filename:這里的--是為了表明后面的字段名為文件名路徑名,而不是分支名,可有可無。

其實原理也就是上面我們說的,檢出本地庫,恢復到沒修改過的狀態。

拉取單個文件

在master分支下拉取某個文件

git fetch
git checkout master -- path/to/file

回滾單個文件

git reset HEAD^ filename
git checkout -- filename

注意:這里我們的reset並不能--hard只能使用默認的--mixed那么工作區的內容就不會改變,所以我們需要使用checkout來將我們的本地庫數據拉取到工作區。

那么git reset為什么不能通過路徑進行硬/軟重置呢?

Because there's no point (other commands provide that functionality already), and it reduces the potential for doing the wrong thing by accident.
因為沒有意義(其他命令已經提供了這種功能) ,並且它減少了偶然做錯事情的可能性。

A "hard reset" for a path is just done with git checkout HEAD -- (checking out the existing version of the file).
對路徑的“硬重置”只需使用 git checkout HEAD -- < path > (檢查文件的現有版本)即可完成。

A soft reset for a path doesn't make sense.
對路徑進行軟重置是沒有意義的。

A mixed reset for a path is what git reset -- does.
一個路徑的混合重置就是 git reset -- < path > 所做的。

參考文章


也就是說,我們可以使用git checkout HEAD -- < path >來硬重置路徑。
如果省略版本號,那么就默認為HEAD,這就是為什么我們能夠重置我們的修改,放棄我們的修改。

二進制文件沖突解決

如果二進制文件發生沖突,不方便查看git插入的沖突標記, 解決比較棘手,通常最簡單的解決方法是提前溝通好,相同修改的地方二選一.
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>…​

git checkout --ours[--theirs] [--] PATH:這里的--是為了表明后面的字段名為文件名路徑名,而不是分支名,可有可無。

--ours 表示檢出當前分支,即保存當前分支的改動,丟棄另外分支的改動.
--theirs 表示檢出另外分支, 即保存另外分支的改動,丟棄當前分支的改動.

舉個栗子:
有分支A和B, 當前我們在分支A上, 需要把分支B合並到分支A, HashMap.c文件發生沖突了.
git checkout --ours -- HashMap.c 表示沖突的地方采用A分支上的修改,丟棄B分支上的修改.
git checkout --theirs -- HashMap.c 表示沖突的地方采用B分支上的修改,丟棄A分支上的修改.

解決完沖突后,就可以像往常一樣 git add git commit了。

遠程庫

克隆

git clone [遠程地址]

拉取

  • pull=fetch+merge
  • git fetch [遠程庫地址別名] [遠程分支名]:把遠程庫的代碼拉取到本地庫。
  • git merge [遠程分支名]git merge [遠程庫地址別名/遠程分支名]:把本地庫的代碼與工作區合並
  • git pull [遠程庫地址別名] [遠程分支名]

推送

git push [別名] [分支名]

命令行常用操作步驟

情況一:創建自己的項目

1. 創建遠程庫


2. 創建遠程庫地址別名

注意:這里的remote地址以及別名,都只對當前git倉庫生效,對於其他git倉庫不生效,即 作用域僅在本git倉庫中。

git remote -v 查看當前所有遠程地址別名
git remote add [別名] [遠程地址]

3. 推送

git push [別名] [分支名]

推送需要輸入賬號和密碼:

情況二:克隆別人的項目

1. 克隆遠程倉庫(從無到有)

git clone [遠程地址]

效果

  • 完整的把遠程庫下載到本地
  • 創建 origin 遠程地址別名
  • 初始化本地庫

情況三:更新已有的項目(更新位於不同分支)

1. 拉取(從舊到新)

  • git pull = git fetch + git merge
  • git fetch [遠程庫地址別名] [遠程分支名]
  • git merge [遠程分支名]git merge [遠程庫地址別名/遠程分支名]
  • git pull [遠程庫地址別名] [遠程分支名]

要特別注意的是,自己寫完了代碼,沒有提交推送之前不要pull,得先提交推送然后發生沖突了再pull,否則git一看沒有提交修改,說明文件沒有修改,那就直接pull過來了,可能覆蓋了你的本地文件。

如果遇到了上述問題:請看情況九


那為什么要先commit,然后pull,然后再push,我pull了,豈不是把自己改的代碼都給覆蓋掉了嘛,因為遠程沒有我改的代碼,我pull,豈不是覆蓋了我本地的改動好的地方了?那我還怎么push?

答:這個先 commit 再 pull 最后再 push 的情況就是為了應對多人合並開發的情況,

commit 是為了告訴 git 我這次提交改了哪些東西,不然你只是改了但是 git 不知道你改了,也就無從判斷比較;

pull是為了本地 commit 和遠程commit 的對比記錄,git 是按照文件的行數操作進行對比的,如果同時操作了某文件的同一行那么就會產生沖突,git 也會把這個沖突給標記出來,這個時候就需要先把和你沖突的那個人拉過來問問保留誰的代碼,然后在 git add && git commit && git pull 這三連,再次 pull 一次是為了防止再你們協商的時候另一個人給又提交了一版東西,如果真發生了那流程重復一遍,通常沒有沖突的時候就直接給你合並了,不會把你的代碼給覆蓋掉

出現代碼覆蓋或者丟失的情況:比如A B兩人的代碼pull 時候的版本都是1,A在本地提交了2,3並且推送到遠程了,B 進行修改的時候沒有commit 操作,他先自己寫了東西,然后 git pull 這個時候 B 本地版本已經到3了,B 在本地版本3的時候改了 A 寫過的代碼,再進行了git commit && git push 那么在遠程版本中就是4,而且 A 的代碼被覆蓋了,所以說所有人都要先 commit 再 pull,不然真的會覆蓋代碼的

情況四:更新已有的項目(更新位於同一分支)(當前分支被其他人更新)

在CR過程中,目標分支可能被其他人更新,導致CR版本不是最新,稱為落后目標分支,這時需要對CR進行變基(rebase),相當於基於目標分支最新版重新patch了一個commit

1. 變基

rebase:變基,改變我們的基礎節點。
使用方法:git rebase [分支名] [提交id],我們就可以將此分支的提交id變基過來,即 將本地工作區指針移動到該提交。

  • git pull --rebase = git fetch + git rebase
  • git fetch [遠程庫地址別名] [遠程分支名]
  • git rebase [遠程分支名]git rebase [遠程庫地址別名/遠程分支名]:把本地庫的代碼變基到工作區
  • git pull --rebase

理解:也就是說,我們先在基點origin(C)的基礎上先更新其他人的修改(D),即 改變我們的基礎節點(變基),我們在變基后的基點D的基礎上進行增加我們的修改E,組合成為了新的節點(R)。

2. 遇到沖突被中斷,處理沖突,繼續變基

步驟:
Step1. 在本地倉庫中, 更新並合並代碼。
也就是說我們需要在pull過來的最新版的基礎上繼續補充我們的變動,即 變基為最新版本。

# 這里分開寫是想進行分步講解
git fetch origin    # 將遠程主機的最新內容拉到本地
git rebase origin/customer/ceb  # 將本地資源與遠程資源合並變基,后發生沖突,會標明所有沖突

git pull --rebase  # 注意這里是拉取資源和變基兩步,拉取資源無沖突,變基有沖突,所以我們后面解決了沖突之后要繼續變基

Step2. 依據提示分別打開沖突的文件, 逐一修改沖突代碼

Step3. 所有沖突都修改完畢后, 提交修改的代碼。
也就是說我們需要在我們解決沖突進行修改的基礎上繼續補充我們的變動,即 變基為修改版本。

git add -u  # 加入修改后的代碼
git rebase --continue   # 因為上一步rebase命令產生了沖突,終止了,所以這里解決沖突后繼續執行rebase,查看是否還有沖突未解決,如果還有沖突未解決就得繼續修改

git add -u <--> git add –update
提交所有被刪除和修改的文件到數據暫存區
git add .
提交所有修改的和新建的數據暫存區
git add -A <--> git add –all
提交所有被刪除、被替換、被修改和新增的文件到數據暫存區

Step4. 更新patch

git push origin HEAD:refs/for/customer/ceb  # 在完全解決沖突之后才能推入遠程

提示:

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".

命令:

vim text.txt # 修改沖突的文件
git add text.txt
# 因為沖突已經修改完了,產生了一個新的本地節點,所以需要繼續變基到新的沖突修改完的節點,相當於變基到新的節點把commit提交了
# 沖突修改前 → 提交
# 變基為 沖突修改前 → 沖突修改后 → 提交
git rebase --continue
git push origin HEAD:refs/for/master

理解:也就是說,我們先在基點origin(C)的基礎上先更新其他人的修改(D),即 改變我們的基礎節點(變基),我們在變基后的基點D的基礎上進行增加我們的修改E,組合成為了新的節點(R)。

git merge和git rebase的區別

使用下面的關系區別這兩個操作:
git pull = git fetch + git merge
git pull --rebase = git fetch + git rebase

現在來看看git merge和git rebase的區別。

假設有3次提交A,B,C。
image

我們在分支origin的基礎上進行了修改,代碼編寫,即 在遠程分支origin的基礎上創建一個名為"mywork"的分支並提交了,同時有其他人在"origin"上做了一些修改並提交了。
(我們的本地代碼編寫分支為mywork(E),其他人的修改為D)
image

提交樹節點介紹:

  • C:基礎節點,我們的代碼編輯和別人的代碼編輯修改,都在此節點開始完成,以此節點為基點;
  • D:其他人對C版本的代碼修改;
  • E:我們當前對C版本的代碼修改;
  • M:其他人的修改與我們的修改進行合並(merge);
  • R:先更新其他人的修改,再在其他人的基礎上更新我們的修改(變基,即 改變了我們的基礎節點)。

其實這個時候E不應該提交,因為提交后會發生沖突。如何解決這些沖突呢?有以下兩種方法:

1、git merge
用git pull命令把"origin"分支上的修改pull下來與本地提交合並(merge)成版本M,但這樣會形成圖中的菱形,讓人很困惑。
image

理解:也就是說,我們自己的修改代碼編寫,即 本地提交分支(E)與其他人的修改(D)進行了分支合並(merge),合並為了一個新的節點(M)

2、git rebase
創建一個新的提交R,R的文件內容和上面M的一樣,但我們將E提交廢除,當它不存在(圖中用虛線表示)。由於這種刪除,小李不應該push其他的repository.rebase的好處是避免了菱形的產生,保持提交曲線為直線,讓大家易於理解。
image

理解:也就是說,我們先在基點origin(C)的基礎上先更新其他人的修改(D),即 改變我們的基礎節點(變基),我們在變基后的基點D的基礎上進行增加我們的修改E,組合成為了新的節點(R)。

在rebase的過程中,有時也會有conflict,這時Git會停止rebase並讓用戶去解決沖突,解決完沖突后,用git add命令去更新這些內容,然后不用執行git-commit,直接執行git rebase --continue,這樣git會繼續apply余下的補丁。
在任何時候,都可以用git rebase --abort參數來終止rebase的行動,並且mywork分支會回到rebase開始前的狀態。

情況五:發起CR(Code Review)

CR(Code Review)又稱為代碼評審,是指在軟件開發過程中,對源代碼的系統性檢查。通常的目的是查找系統缺陷,保證軟件總體質量和提高開發者自身水平。

某些公司可能會擁有自己的CR,也就是說,我們的代碼提交時並不是直接提交到github,而是先提交到公司的CR,進行代碼評審,然后評審通過了之后我們才合入github。

發起CR:每個公司發起CR的命令可能不一樣,大家按照公司要求書寫即可。
git push origin HEAD:refs/for/master

  • git push 肯定是推送
  • origin : 是遠程的庫的名字
  • HEAD: 是一個特別的指針,它是一個指向你正在工作的本地分支的指針,可以把它當做本地分支的別名,git這樣就可以知道你工作在哪個分支
  • refs/for :意義在於我們提交代碼到服務器之后是需要經過code review 之后才能進行merge的
  • refs/heads 不需要

情況六:代碼已經提交了,但是發現有點問題,所以想增加patch

增加patch(打補丁):我們的代碼此時還沒有並入代碼庫,還存留在CR中,所以我們可以對此次提交增加patch。
git commit --amend:修正最后一次提交,傳遞此選項將修改最后一次提交。階段性更改將被添加到先前的提交中,而不是創建一個新的提交。該命令將打開系統配置的文本編輯器,並提示更改先前指定的提交消息。

This option adds another level of functionality to the commit command. Passing this option will modify the last commit. Instead of creating a new commit, staged changes will be added to the previous commit. This command will open up the system's configured text editor and prompt to change the previously specified commit message.

git add cr.txt
git commit --amend
git push origin master:refs/for/master

為什么我們可以就某次提交來打補丁呢?

從上圖可以看出,我們的提交有commit idchange id兩種,而commit id會隨着提交次數而變化,一次操作就會生成一個commit id,而change id會隨着變化而變化,打補丁的時候,我們的change id不改變,只會改變commit id,所以會保證變化的一致性,保證我們可以打補丁。

情況七:想臨時存儲代碼(分支錯誤,切換分支,代碼還不想提交)

應用場景:
1 當正在dev分支上開發某個項目,這時項目中出現一個bug,需要緊急修復,但是正在開發的內容只是完成一半,還不想提交,這時可以用git stash命令將修改的內容保存至堆棧區,然后順利切換到hotfix分支進行bug修復,修復完成后,再次切回到dev分支,從堆棧中恢復剛剛保存的內容。
2 由於疏忽,本應該在dev分支開發的內容,卻在master上進行了開發,需要重新切回到dev分支上進行開發,可以用git stash將內容保存至堆棧中,切回到dev分支后,再次恢復內容即可。
總的來說,git stash命令的作用就是將目前還不想提交的但是已經修改的內容進行保存至堆棧中,后續可以在某個分支上恢復出堆棧中的內容。這也就是說,stash中的內容不僅僅可以恢復到原先開發的分支,也可以恢復到其他任意指定的分支上。git stash作用的范圍包括工作區和暫存區中的內容,也就是說沒有提交的內容都會保存至堆棧中。

git stash   # 能夠將所有未提交的修改(工作區和暫存區)保存至堆棧中,用於后續恢復當前工作目錄。
git checkout xxx    # 切換分支
git stash pop   # 將當前stash中最近保存的內容彈出,並應用到當前分支對應的工作目錄上。

情況八:沖突的產生與解決

案例:模擬產生沖突。

  1. 同事在下班之后修改了線上倉庫的代碼

    注意:此時我本地倉庫的內容與線上不一致的。

  2. 第二天上班的時候,我沒有做 git pull 操作,而是直接修改了本地的對應文件的內容

  3. 需要在下班的時候將代碼修改提交到線上倉庫(git push

    提示我們要在再次push之前先git pull操作。

【解決沖突】

  1. git pull

    此時 git 已經將線上(push)本地倉庫(commit)的沖突合並到了對應的文件中

  2. 打開沖突文件,解決沖突
    解決方法:需要和同事(誰先提交的)進行商量,看代碼如何保留,將改好的文件再次提交即可。

  3. 重新提交

線上效果:

新手上路小技巧:上班第一件事先 git pull,可以在一定程度上避免沖突的產生。

情況九:git更新以后本地改動代碼不見了被覆蓋了

git pull會把本地未提交修改覆蓋,這是當然的,畢竟add只是存放在暫存區暫存着了,並沒有持久保存,想要持久保存就得commit上傳到本地庫中。

個人理解:

  • add:將我們的代碼交給git監管
  • commit:將我們的代碼提交到本地庫
  • push:將我們的代碼推送到遠程庫

一般我們git更新不會覆蓋掉我們本地的代碼,但是也不是絕對的,在一定條件下,會被覆蓋,在這種我本地文件沒有上傳git,沒法使用git版本控制,又被git上的版本干掉了我本地代碼的情況下,我相信小伙伴都會想說已經mmp的,這里我們說下這個方法,主要是使用idea,webstrom的 localHistory 來找回我們的代碼。

  1. 選中整個項目右擊,你就能看到本地的歷史記錄

  2. 點擊本地歷史記錄,你能看到所有本地的記錄

為了避免此情況:可以寫完代碼先 commit 再 pull 最后再 push,養成習慣。

git可視化操作——git GUI

我們介紹完了git的命令行操作,可能會有些朋友覺得這種操作實在是太麻煩了,所以接下來我們介紹一種git的可視化操作。
詳情可以查看:git GUI可視化操作


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM