tft每日頭條

 > 生活

 > git的一些高級用法

git的一些高級用法

生活 更新时间:2025-02-06 12:00:24

我先開一會兒吐槽大會,git 這東西我用了兩年,根本尼瑪用不明白。

我搞不明白的一個重要原因就是,命令的功能太雜,有時候一個需求可以用好幾種命令解決,而且有的命令還 tm 有别名。這導緻什麼問題呢,我在網上找到的答案五花八門,竟然都能達成目的,難以找到規律,毫無套路可言。對于我這種不喜歡動腦子,隻喜歡玩套路的人來說,簡直不能接受。

以前我用 Git,就知道add .,然後commit -m,最後push origin master一套帶走,或者就是把 Git 作為下載器,去clone别人的項目。但是在工作中呢,和别人一起開發代碼,就需要處理一些複雜情況,比如解決沖突,比如手殘恢複,等等等實用場景,這些我在後文都會列舉。

對于工具的學習,我認為應該多做減法,隻撿最有用的學,那些奇技淫巧不學也罷,應該把時間投入更有價值的事情中。

所以本文不是一個大而全 Git 命令的使用手冊,而是根據實際工作中最常見問題,提供小而美的解決方案,僅僅涉及四個命令:add,commit,reset,checkout

PS:舊文 一起刷題學習 Git/SQL/正則表達式 介紹了一個可視化學習 Git 的網站,非常優秀,值得一刷。

一、預備知識

首先,在進入 Git 的各種神仙操作之前,一定要明白 git 的三個「分區」是什麼,否則的話你一定沒辦法真正理解 Git 的原理

本地 Git 的三個分區分别是:working directory,stage/index area,commit history。

git的一些高級用法(我用四個命令總結了)1

https://rogerdudler.GitHub.io/git-guide

working directory是「工作目錄」,也就是我們肉眼能夠看到的文件,後文我們稱其為work dir區。

當我們在work dir中執行git add相關命令後,就會把work dir中的修改添加到「暫存區」stage area(或者叫index area)中去,後文我們稱暫存區為stage區。

當stage中存在修改時,我們使用git commit相關命令之後,就會把stage中的修改保存到「提交曆史」commit history中,也就是HEAD指針指向的位置。後文我們稱「提交曆史」為history區。

關于commit history我們多說幾句,任何修改隻要進入commit history,基本可以認為永遠不會丢失了。每個commit都有一個唯一的 Hash 值,我們經常說的HEAD或者master分支,都可以理解為一個指向某個commit的指針。

work dir和stage區域的狀态,可以通過命令git status來查看,history區域的提交曆史可以通過git log命令來查看。

好的,如果上面的内容你都能夠理解,那麼本文就完全圍繞這三個概念展開,下面就是一個「狀态轉移圖」:

git的一些高級用法(我用四個命令總結了)2

二、本地 Git 極簡教程

需求一,如何把work dir中的修改加入stage

git的一些高級用法(我用四個命令總結了)3

這個是最簡單,使用 git add 相關的命令就行了。順便一提,add有個别名叫做stage,也就是說你可能見到git stage相關的命令,這個命令和git add命令是完全一樣的。

風險等級:無風險。

理由:不會改變任或撤銷任何已作出的修改,而且還會将work dir中未追蹤的修改(Untracked file)添加到暫存區stage中進行追蹤。

需求二,如何把stage中的修改還原到work dir中

git的一些高級用法(我用四個命令總結了)4

這個需求很常見,也很重要,比如我先将當前work dir中的修改添加到stage中,然後又對work dir中的文件進行了修改,但是又後悔了,如何把work dir中的全部或部分文件還原成stage中的樣子呢?

來個實際場景,我先新建兩個文件,然後把他們都加到stage:

$toucha.txtb.txt $gitadd. $gitstatus Onbranchmaster Changestobecommitted: newfile:a.txt newfile:b.txt

然後我又修改了a.txt文件:

$echohelloworld>>a.txt $gitstatus Onbranchmaster Changestobecommitted: newfile:a.txt newfile:b.txt Changesnotstagedforcommit: modified:a.txt

現在,我後悔了,我認為不應該修改a.txt,我想把它還原成stage中的空文件,怎麼辦?

答案是,使用 checkout 命令:

$gitcheckouta.txt Updated1pathfromtheindex $gitstatus Onbranchmaster Changestobecommitted: newfile:a.txt newfile:b.txt

看到了麼,輸出顯示從index區(也就是stage區)更新了一個文件,也就是把work dir中a.txt文件還原成了stage中的狀态(一個空文件)。

當然,如果work dir中被修改的文件很多,可以使用通配符全部恢複成stage:

$gitcheckout.

有一點需要指出的是,checkout命令隻會把被「修改」的文件恢複成stage的狀态,如果work dir中新增了新文件,你使用git checkout .是不會删除新文件的。

風險等級:中風險。

理由:在work dir做出的「修改」會被stage覆蓋,無法恢複。所以使用該命令你應該确定work dir中的修改可以抛棄。

需求三,将stage區的文件添加到history區

git的一些高級用法(我用四個命令總結了)5

很簡單,就是 git commit 相關的命令,一般我們就是這樣用的:

$gitcommit-m'一些描述'

再簡單提一些常見場景, 比如說commit完之後,突然發現一些錯别字需要修改,又不想為改幾個錯别字而新開一個commit到history區,那麼就可以使用下面這個命令:

$gitcommit--amend

這樣就是把錯别字的修改和之前的那個commit中的修改合并,作為一個commit提交到history區。

風險等級:無風險。

理由:不會改變任或撤銷任何已作出的修改,而且還會将stage區的修改加入history區并分配一個 Hash 值。隻要不亂動本地的.git文件夾,進入history的修改就永遠不會丢失。

需求四,将history區的文件還原到stage區

git的一些高級用法(我用四個命令總結了)6

這個需求很常見,比如說我用了一個git add .一股腦把所有修改加入stage,但是突然想起來文件a.txt中的代碼我還沒寫完,不應該把它commit到history區,所以我得把它從stage中撤銷,等後面我寫完了再提交。

$echoaaa>>a.txt;echobbb>>b.txt; $gitadd. $gitstatus Onbranchmaster Changestobecommitted: modified:a.txt modified:b.txt

如何把a.txt從stage區還原出來呢?可以使用 git reset 命令:

$gitreseta.txt $gitstatus Onbranchmaster Changestobecommitted: modified:b.txt Changesnotstagedforcommit: modified:a.txt

你看,這樣就可以把a.txt文件從stage區移出,這時候進行git commit相關的操作就不會把這個文件一起提交到history區了。

上面的這個命令是一個簡寫,實際上reset命令的完整寫法如下:

$gitreset--mixedHEADa.txt

其中,mixed是一個模式(mode)參數,如果reset省略這個選項的話默認是mixed模式;HEAD指定了一個曆史提交的 hash 值;a.txt指定了一個或者多個文件。

該命令的自然語言描述是:不改變work dir中的任何數據,将stage區域中的a.txt文件還原成HEAD指向的commit history中的樣子。就相當于把對a.txt的修改從stage區撤銷,但依然保存在work dir中,變為unstage的狀态。

風險等級:低風險。

理由:不會改變work dir中的數據,會改變stage區的數據,所以應确保stage中被改動數據是可以抛棄的。

需求五,将work dir的修改提交到history區

git的一些高級用法(我用四個命令總結了)7

這個需求很簡單啦,先git add然後git commit就行了,或者一個快捷方法是使用命令git commit -a。

風險等級:無風險。

理由:顯而易見。

需求六,将history區的曆史提交還原到work dir中

git的一些高級用法(我用四個命令總結了)8

這個場景,我說一個極端一點的例子:比如我從 GitHub 上clone了一個項目,然後亂改了一通代碼,結果發現我寫的代碼根本跑不通,于是後悔了,幹脆不改了,我想恢複成最初的模樣,怎麼辦?

依然是使用checkout命令,但是和之前的使用方式有一些不同:

$gitcheckoutHEAD. Updated12pathsfromd480c4f

這樣,work dir和stage中所有的「修改」都會被撤銷,恢複成HEAD指向的那個history commit。

注意,類似之前通過stage恢複work dir的checkout命令,這裡撤銷的也隻是修改,新增的文件不會被撤銷。

當然,隻要找到任意一個commit的 HASH 值,checkout命令可就以将文件恢複成任一個history commit中的樣子:

$gitcheckout2bdf04asome_test.go Updated1pathfrom2bdf04a #前文的用法顯示updatefromindex

比如,我改了某個測試文件,結果發現測試跑不過了,所以就把該文件恢複到了它能跑過的那個曆史版本……

風險等級:高風險。

理由:這個操作會将指定文件在work dir的數據恢複成指定commit的樣子,且會删除該文件在stage中的數據,都無法恢複,所以應該慎重使用。

三、其他技巧

需求一,合并多個commit

比如說我本地從17bd20c到HEAD有多個commit,但我希望把他們合并成一個commit推到遠程倉庫,這時候就可以使用reset命令:

$gitreset17bd20c $gitadd. $gitcommit-m'balabala'

回顧一下剛才說的reset命令的作用,相當于把 HEAD 移到了17bd20c這個commit,而且不會修改work dir中的數據,所以隻要add再commit,就相當于把中間的多個commit合并到一個了。

需求二,由于HEAD指針的回退,導緻有的commit在git log命令中無法看到,怎麼得到它們的 Hash 值呢

再重複一遍,隻要你不亂動本地的.git文件夾,任何修改隻要提交到commit history中,都永遠不會丢失,看不到某些commit隻是因為它們不是我們當前HEAD位置的「曆史」提交,我們可以使用如下命令查看操作記錄:

$gitreflog

比如reset,checkout等等關鍵操作都會在這裡留下記錄,所有commit的 Hash 值都能在這裡找到,所以如果你發現有哪個commit突然找不到了,一定都可以在這裡找到。

需求三,怎麼解決沖突

記住,Git 雖然高大上,但也不要迷戀,一定要懂得借助先進的工具。

比較流行的代碼編輯器或者 IDE 都會集成方便的可視化 Git 工具,至于解決沖突,可視化的表現方式不是比你在命令行裡git diff看半天要清晰明了得多?隻需要點點點就行了。

所以說,隻要明白本文講的這些基本操作,夠你用的了,平時能用圖形化工具就多用圖形化工具,畢竟工具都是為人服務的。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved