手把手帶你玩git之各種撤銷

git 各種撤銷

因?yàn)間it有三個(gè)區(qū):工作區(qū)勾拉,索引區(qū)和版本區(qū)。所以git的撤銷有很多種盗温,比如:

  • 撤銷工作區(qū): 剛寫了幾行代碼藕赞,不想要了,想撤銷卖局。
  • 撤銷版本區(qū): 剛提交了一次代碼斧蜕,但由于疏忽,漏掉了幾個(gè)文件或者備注信息寫錯(cuò)了砚偶,想撤銷后重新提交批销。

撤銷工作區(qū)

一個(gè)已經(jīng)提交的文件中新加了一行,現(xiàn)在想撤銷染坯,怎么辦均芽?

新加一行

因?yàn)樾录恿艘恍校髠?cè)出現(xiàn)** > **符號(hào)单鹿,表示有修改掀宋。這個(gè)修改只發(fā)生在“工作區(qū)”,尚未進(jìn)入“索引區(qū)”。

$ git status
On branch master
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)

    modified:   src/main/java/io/downgoon/hello/boot/HelloWorld.java

如何撤銷布朦?

  • 命令行方式
$ git checkout -- src/main/java/io/downgoon/hello/boot/HelloWorld.java

符號(hào) ** -- ** 可以理解為“索引區(qū)”囤萤。

  • GUI 方式
Replace with Index

注意: 菜單在 Team 的下面。Eclipse里面說(shuō)的 Replace With 對(duì)應(yīng)的就是 git checkout 命令是趴,解答了許多人問(wèn) “為什么Eclipse Git 沒(méi)有checkout菜單”涛舍。


撤銷版本區(qū)(重新編輯最后一次提交)

剛提交了一次代碼,但由于疏忽唆途,漏掉了幾個(gè)文件或者備注信息寫錯(cuò)了富雅,想撤銷后重新提交。怎么辦肛搬?

git的設(shè)計(jì)者也考慮到人容易犯錯(cuò)誤没佑,特地為這種場(chǎng)景設(shè)計(jì)了一個(gè)“改過(guò)自新(amend)”的機(jī)會(huì)。這種情況都不需用更具一般性的 git reset 命令温赔,然后再git commit蛤奢,而是直接git commit --amend

如下代碼創(chuàng)建了c.txt和d.txt兩個(gè)文件陶贼,本打算兩個(gè)文件一起提交的啤贩,但一時(shí)筆誤,只把c.txt提交了拜秧,d.txt沒(méi)有提交痹屹。

?  GitTutorial git:(master) echo "ccc" > c.txt
?  GitTutorial git:(master) ? echo "ddd" > d.txt
?  GitTutorial git:(master) ? git add *
?  GitTutorial git:(master) ? git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   c.txt
    new file:   d.txt

?  GitTutorial git:(master) ? git commit c.txt -m 'add c.txt and d.txt'
[master 2ef4359] add c.txt and d.txt
 1 file changed, 1 insertion(+)
 create mode 100644 c.txt
?  GitTutorial git:(master) ?

如何把d.txt也提交? 直接 git commit --amend c.txt d.txt 進(jìn)入vi編輯區(qū)枉氮,重新編輯提交注釋信息(注意:新增加的d.txt在命令行上已經(jīng)攜帶d.txt文件了)志衍,保存退出(:wq)。

add c.txt and d.txt

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Wed Nov 30 20:36:05 2016 +0800
#
# On branch master
# Changes to be committed:
#       new file:   c.txt
#       new file:   d.txt
#

不妨假設(shè)新的注釋內(nèi)容修改為:

add c.txt and d.txt (don't forget d.txt)

:wq 保存退出后聊替,顯示

2 files changed, 2 insertions(+)

?  GitTutorial git:(master) git commit --amend c.txt d.txt
[master 0935027] add c.txt and d.txt (don't forget d.txt)
 Date: Wed Nov 30 20:36:05 2016 +0800
 2 files changed, 2 insertions(+)
 create mode 100644 c.txt
 create mode 100644 d.txt
?  GitTutorial git:(master) git status
On branch master
nothing to commit, working directory clean
?  GitTutorial git:(master)

如果你愿意楼肪,你還可以繼續(xù)后悔最后一次提交,比如我們還需要追加e.txt文件佃牛,這次我們用GUI圖形界面演示(演示前淹辞,先自行增加e.txt,并加入索引區(qū)):

進(jìn)入commit會(huì)話框
選擇Amend圖標(biāo)

上圖所示的步驟:

  1. 右擊Project -> Team -> Commit ...
  2. 選擇右上角的 **Amend (Edit Previous Commit) 圖標(biāo)
  3. 勾選需要追加的 e.txt
  4. 修改注釋內(nèi)容俘侠,并提交象缀。

事后可以查看日志,并對(duì)比 git loggit reflog有什么不同 爷速? 自己做實(shí)驗(yàn)觀察央星。

amend失靈的時(shí)候

但是如果我們不是追加,而是想刪除一些呢惫东? 比如從之前的提交了c.txt莉给,d.txt和e.txt毙石,要amend成只提交d.txt呢? 嘗試的結(jié)果居然不可以(命令 git commit --amend d.txt -m 'only d.txt, remove c.txt and e.txt')颓遏。

需要真的撤銷徐矩,再提交。


撤銷版本區(qū)(真的撤銷再提交)

撤銷實(shí)操

所謂的回滾其實(shí)就是將分支游標(biāo)master指向之前的提交叁幢,重置命令 git reset 上場(chǎng):git reset --hard commit-ID 即可滤灯。

$ git reset --hard <tag/branch/commit id>

接著需要強(qiáng)制推送到遠(yuǎn)程:

$ git push <reponame> --force 

但是強(qiáng)制推送遠(yuǎn)程,這個(gè)操作很危險(xiǎn)曼玩,通常權(quán)限會(huì)被禁止:

remote: GitLab: You are not allowed to force push code to a protected branch on this project.
To http://gitlab.com/blueocean/boxstore.git
! [remote rejected] master -> master (pre-receive hook declined)

gitlab中鳞骤,項(xiàng)目的masterowner都很可能沒(méi)有這個(gè)權(quán)限,只有gitlab的管理員才有這個(gè)權(quán)限黍判。

撤銷圖解

章節(jié)開(kāi)頭提到:

所謂的回滾其實(shí)就是將分支游標(biāo)master指向之前的提交豫尽,重置命令 git reset 上場(chǎng):git reset --hard commit-ID 即可。

  • 一個(gè)分支提交序列
一個(gè)分支提交序列.png
  • 回退到HEAD

當(dāng)執(zhí)行git reset HEAD時(shí)顷帖,不會(huì)做任何事情美旧。因?yàn)楫?dāng)前分支本身就是指向HEAD的。

  • 回退1步驟
$ git reset HEAD~1

其中符號(hào)HEAD~1表示HEAD回退1步(即:HEAD的父親節(jié)點(diǎn))窟她。

HEAD~1 is shorthand case for “the commit right before HEAD”, or put differently “HEAD’s parent”陈症。

回退完后,HEAD指針:

回退1步
  • 回退2步
$ git reset HEAD~2

指針指向:

回退2步

復(fù)雜的參數(shù)

git reset 概念很簡(jiǎn)單震糖,就是回退到某個(gè)提交點(diǎn)。但是它的參數(shù)蠻復(fù)雜趴腋,如下三條命令的區(qū)別是什么吊说?

git reset --hard <commit-id>
git reset --soft <commit-id>
git reset --mixed <commit-id>  (默認(rèn)情況)

我們知道git有:工作區(qū),索引區(qū)和版本庫(kù)的概念优炬。這三個(gè)選項(xiàng)參數(shù)就是影響對(duì)三個(gè)區(qū)的不同處理颁井。簡(jiǎn)單說(shuō),選項(xiàng)參數(shù)就是reset的程度:

  • soft: 程度最輕蠢护,僅僅是把版本庫(kù)的重置雅宾,索引區(qū)和工作區(qū)沒(méi)做任何修改。
  • mixed: 程度中葵硕,也是默認(rèn)操作眉抬,它把版本區(qū)和索引區(qū)都重置了,但是工作區(qū)沒(méi)有重置懈凹。
  • hard: 程度最強(qiáng)蜀变,三個(gè)區(qū)全部重置了。

人們習(xí)慣--hard介评,為什么呢库北?因?yàn)橹刂煤笈澜ⅲ藗兂3P枰萌庋廴ズ藢?duì),核對(duì)的方式往往是打開(kāi)某個(gè)目錄看看或文件看看寒瓦,而這些都是在工作區(qū)的狀態(tài)情屹。

三選項(xiàng)對(duì)比

以下圖例:綠色的表示reset的了;紅色表示沒(méi)有reset杂腰。

  • soft
soft reset.png

版本區(qū)被重置了垃你,但是索引區(qū)和工作區(qū)都沒(méi)有被重置。

  • mixed
mixed reset.png

版本區(qū)和索引區(qū)都被重置了颈墅,但是工作區(qū)都沒(méi)有被重置蜡镶。

  • hard
hard reset.png

版本區(qū)、索引區(qū)和工作區(qū)都被重置了恤筛。

快速實(shí)驗(yàn)

快速做個(gè)實(shí)驗(yàn)驗(yàn)證一下官还,分別創(chuàng)建c1.txtc2.txtc3.txt毒坛,三個(gè)文件分別提交三次望伦,然后回退到中間那次提交:

  • 連續(xù)3次提交
echo "c1" > c1.txt
git add c1.txt && git commit -m 'c1'
echo "c2" > c2.txt
git add c2.txt && git commit -m 'c2'
echo "c3" > c3.txt
git add c3.txt && git commit -m 'c3'
  • 回退到中間
git reset HEAD~1

我們選擇的是默認(rèn)的--mixed模式。

  • 查看狀態(tài)
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    c3.txt

nothing added to commit but untracked files present (use "git add" to track)

$ ls
c1.txt c2.txt c3.txt

的確從文件系統(tǒng)上看煎殷,文件c3.txt并沒(méi)有消失屯伞,但是從索引區(qū)看,它消失了(就是回退到中間了)豪直。

查看日志

查看日志時(shí)候劣摇,現(xiàn)在只能看到最前面的兩條commit:

$ git log
c4b06a0 c2
81ee32e c1

但是這并不是表示git就放棄對(duì)reset的追蹤了,實(shí)際上弓乙,我們還可以對(duì)剛才的reset進(jìn)行回滾末融,我們查看更詳細(xì)的日志:

$ git reflog
c4b06a0 HEAD@{0}: reset: moving to HEAD~1
c51558f HEAD@{1}: commit: c3
c4b06a0 HEAD@{2}: commit: c2
81ee32e HEAD@{3}: commit (initial): c1

如果我們回到之前的c3,也完全可以暇韧。

git revert 與 git reset 的區(qū)別

剛才勾习,git reset 后,可能因?yàn)闄?quán)限的問(wèn)題無(wú)法強(qiáng)行git push --force懈玻。但是難道如果程序員誤操作提交了一次錯(cuò)誤的東西到master就沒(méi)法回滾了(指不需要gitlab管理員來(lái)回滾)巧婶?

可以用git revert HEAD,它跟git reset的不同主要有兩點(diǎn):

  • git reset是指HEAD指針涂乌,重新指向某個(gè)commit-id的位置艺栈。并且它后續(xù)的commit-id會(huì)被刪除。git revert會(huì)產(chǎn)生一條新的commit骂倘,原有的commit-log并不會(huì)發(fā)生任何變化眼滤。

  • git reset只是改變HEAD的指向。而git reset是真的 回收历涝,它不僅可以回收最后一次的诅需,還可以回收中間的漾唉,它回收中間的,中間之后的并不回收堰塌。比如赵刑,連續(xù)三次提交了三個(gè)文件,分別是c1.txt, c2.txt和c3.txt场刑,如果回收第二個(gè)般此,那么會(huì)剩2個(gè)文件,分別是c1.txt和c3.txt牵现,而不會(huì)只有c1.txt铐懊。

  • 連續(xù)三次提交3個(gè)文件

mkdir revertlab && cd revertlab && git init
echo "c1" > c1.txt
git add c1.txt && git commit -m 'c1'
echo "c2" > c2.txt
git add c2.txt && git commit -m 'c2'
echo "c3" > c3.txt
git add c3.txt && git commit -m 'c3'

然后撤銷中間那次:

$ git revert HEAD~1

會(huì)自動(dòng)編寫 commit
Revert "c2"

This reverts commit c86b27c706d6a88082958818643e6b26db8b7300.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#       deleted:    c2.txt
#

查看日志,發(fā)現(xiàn)的確是產(chǎn)生一條新的commit:

$ git log --oneline
c94de9d Revert "c2"
a6681f2 c3
c86b27c c2
402e6df c1

查看本地文件:

$ ls
c1.txt c3.txt

很驚訝的是瞎疼,它是精準(zhǔn)的撤銷中間那次操作c2科乎,c3.txt文件依然保留了。


總結(jié)

  • 撤銷工作區(qū): git checkout -- <files>
  • 撤銷版本區(qū)(重新編輯最后一次提交): git commit --amend <files>
  • 撤銷版本區(qū)(真的撤銷再提交):git reset --soft HEAD^ 接著 git commit <files> -m ''
  • git resetgit revert的區(qū)別贼急。

參考資料

最后編輯于
?著作權(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)離奇詭異碴倾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)掉丽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門影斑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人机打,你說(shuō)我怎么就攤上這事∑福” “怎么了残邀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)柑蛇。 經(jīng)常有香客問(wèn)我芥挣,道長(zhǎng),這世上最難降的妖魔是什么耻台? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任空免,我火速辦了婚禮,結(jié)果婚禮上盆耽,老公的妹妹穿的比我還像新娘蹋砚。我一直安慰自己扼菠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布坝咐。 她就那樣靜靜地躺著循榆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪墨坚。 梳的紋絲不亂的頭發(fā)上秧饮,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音泽篮,去河邊找鬼盗尸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛帽撑,可吹牛的內(nèi)容都是我干的泼各。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼油狂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼历恐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起专筷,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤弱贼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后磷蛹,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一柿菩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雨涛,春花似錦枢舶、人聲如沸懦胞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)医瘫。三九已至,卻和暖如春旧困,著一層夾襖步出監(jiān)牢的瞬間醇份,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工吼具, 沒(méi)想到剛下飛機(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)容

  • 以下筆記主要參考gitgot痊臭,大致了解git使用和原理。 第一部分我們從個(gè)人的視角去研究如何用好Git登夫,并且揭示G...
    carolwhite閱讀 2,369評(píng)論 0 1
  • git常用命令 GIT常用命令備忘:http://stormzhang.com/git/2014/01/27/gi...
    新篇章閱讀 8,458評(píng)論 1 26
  • 作者:Thomas Hanning广匙,原文鏈接,原文日期:2015-08-27譯者:CMB恼策;校對(duì):shanks鸦致;定稿...
    mbillchan閱讀 423評(píng)論 0 0
  • 呆萌時(shí)代的睡姿——尿床 弄不明白的時(shí)候我們瞪大了眼睛不知道是為何? 瞌睡蟲(chóng)在眼前直晃涣楷,口水會(huì)把你們淹沒(méi)——我被局限...
    Zcj_落兮閱讀 188評(píng)論 0 0