git版本控制系統小白教程(下)
前言:本文主要介紹git版本控制系統的一些基礎使用,適合小白入門,因為內容較多,會分為兩部分進行分享,查看上部請點傳送門。
刪除文件
git刪除文件一般有三種情況,第一種是在工作區修改了文件,但是還沒有添加到暫存區;第二種是已經添加到暫存區但是還沒有提交到版本庫中;第三種就是已經提交到版本庫中。
撤銷工作區的修改
在工作區修改了文件,但還未添加到暫存區,即撤銷在工作區對文件的修改動作,在前面查看版本狀態的章節,我們已經介紹了一個命令,就是git restore <file>
,新版本都會提示使用這個命令,舊版本則是提示使用git checkout -- <file>
這個命令,它與新版命令的用法其實沒什麼區別,一樣可以撤銷工作區的修改,但這個命令有兩種使用情況:
1.如果我們在文件修改後,文件還未添加到暫存區,想要把修改的全部撤回,那麼使用該命令可以撤銷修改的部分,回到這個文件的上一個版本。
2.如果文件已經添加到暫存區後,再一次修改了文件,使用該命令就可以撤銷修改,回到添加到暫存區時的那一個版本。
需要注意的是,git checkout -- <file>
命令中有兩個--
,如果沒有了--
,它就不是撤銷命令了,變成了切換分支命令git checkout
,後面的章節中會提到這個命令。
從暫存區移除
我們提交文件到版本庫,一般需要兩步,先添加到暫存區再提交到版本庫,文件添加到暫存區時,我們可能不想提交了,那麼就需要把他從暫存區中移除。
前面查看版本狀態的章節,我們已經介紹了一個命令git restore --staged <file>
,同樣的,這是新版的命令,我們安裝了新版就使用新版命令就好了,仍使用舊版的同學,就可以使用git reset HEAD <file>
這個命令,用法是一樣的。
刪除版本庫的文件
1.git rm 刪除文件
一般我們在作業系統上刪除文件,就是右鍵刪除,但在git中,你在工作區刪除了文件,實際上只是刪除了你本地,版本庫中該文件仍然是存在的,比如tets_login.py文件,在作業系統右鍵刪除後我們查看一下git狀態。
git知道你刪除了哪些文件,並且工作區和版本庫已經不一致了。這時你有兩個選擇,一是執行git rm <file>
,並且執行git commit
命令去刪除版本庫對應的文件,保持工作區和版本庫一致。二是使用git restore <file>
撤銷對工作區的修改,把刪除的文件還原,這樣也可以保持工作區和版本庫一致。
我們也可以不在工作區操作(即右鍵刪除),直接從版本庫中刪除文件,使用git rm <file>
把刪除動作添加到暫存區,然後git commit
真正地從版本庫中刪除。我們想要真正地影響到版本庫,都需要執行commit,否則它就只是對工作區產生影響。
這裡要注意的是,如果我們直接執行了git rm <file>
,但還未提交到版本庫,想要還原到工作區時,就需要先執行git restore --staged <file>
移除暫存區,再執行git restore <file>
撤銷對工作區的修改。
2.git rm –cached 只刪除版本庫的文件
使用git rm
且git commit
之後文件會從本地中刪除,且會從版本庫中刪除。如果我們只想刪除版本庫的文件保留工作區的,我們可以在刪除命令加上一個--cached
參數,同樣的執行時需要commit一下,如果想撤銷,方法同上。我們不需要死記硬背這些命令,查看版本狀態時它會提示你可以使用哪些命令。
3.git rm *.txt 刪除符合規則的文件
我們在忽略特殊文件的章節有提到過glob模式匹配,這裡刪除文件也支援模式匹配,比如git rm *.txt
意思就是說刪除所有的txt文件。
移動文件
git可以使用git mv <old_file> <new_file>
來移動文件,需要輸入舊文件名,新文件位置,新文件位置還可以重命名,如把test.txt移動到doc目錄下,並且重命名為readme.txt:
我們第一步執行的時候,提示No such file or directory
意思是說沒有text.txt文件或者目錄不存在,因為我們目錄沒有創建,因此需要手動創建一個目錄或者直接使用linux命令創建一個目錄,就可以移動成功了,移動後記得要執行commit才會對版本庫生效。
git這一個移動文件的命令實現原理很簡單,其實就是執行了下面三個動作:
mv <old_file> <new_file>
:linux移動命令git rm <old_file>
:刪除舊文件git add <new_file>
:添加新文件到暫存區
版本補錄
什麼是版本補錄,就是我們在修改文件提交版本時遺漏了某些文件的提交,或者有了新的修改但又不想重新提交一個版本,希望補錄在上一個版本中。
我們只需要在執行commit時增加一個--amend
參數,即git commit --amend
,如提交了一個版本後,再次修改了readme.txt,然後執行該命令進行補錄。
上面補錄時加了-a
參數直接一步到為提交到了版本庫,提交到版本庫後會進入版本資訊的vim(vi)介面,vim是unix系統文本編輯器,學過linux的同學應該都有了解。這裡它進入vim介面我們可以修改增加版本的一些備註資訊,如果不需要修改,可以通過按下Esc退出編輯狀態,然後連按兩次大寫字母Z,就可以退出vim了。
退出vim模式後,我們再查看一下版本資訊就會看到最近的一個版本有兩次提交記錄,其中一次是補錄的。
版本標籤
git標籤(tag)的作用主要是快速地定位版本以及版本間的區分,毎次發布一個版本時,我們通常都會在版本庫中打上一個標籤,標籤可以跟蹤到版本。之後不管在什麼時候,我們都可以通過一個標籤,把對應的歷史版本取出來。因此,標籤實際上就是版本庫的一個快照。
但你可能回想,我們commit的時候不是生成了一個版本唯一id嗎?版本id是一連串的字元,不容易記住且無法辨別是哪一個版本。總而言之,標籤就是一個讓人容易記住的有意義的名字,它跟某個commit綁在一起。
附註標籤
git使用的標籤有兩種類型:輕量標籤(lightweight)和附註標籤(annotated)。附註標籤實際就是存儲在版本庫中的一個獨立對象,它有自身的一些校驗資訊,包含標籤名、作者的資訊、標籤日期以及標籤說明。一般我們都會使用附註標籤,方便查看更多的標籤資訊。
關鍵命令:git tag -a <tagname> -m '標籤說明'
這裡的-a指的是附註(annotated)
打上標籤之後,我們查看版本資訊會發現最近的一個版本多了一個tag,因為git默認標籤是打在最新提交的一個版本上的。
輕量標籤
如果我們只是臨時加的標籤,不需要額外的一些標籤資訊,我們就可以使用輕量版標籤。
關鍵命令:git tag <tagname>
,默認是給最近的一個版本打上標籤
查看指定標籤
使用git show <tagname>
查看指定標籤的資訊
從上圖我們可以看出查詢的標籤是一個附註標籤,它有很多一些附帶的資訊,如標籤名、作者的資訊、標籤日期以及標籤說明;那輕量標籤又是如何呢?下面這個就是一個輕量標籤了,它只是簡單的打了一個標籤,沒有更多的資訊了。
查看所有標籤
使用git tag
可以查看版本庫中有哪些標籤,標籤名按字母排序。
查看匹配標籤
這裡查看標籤同樣適用glob模式匹配,關鍵命令:git tag -l 匹配規則
,-l表示以列表形式查看標籤,如查看v開頭的標籤:
因為只打了一個v開頭的標籤,因此只顯示一個。
補錄標籤
默認標籤是打在最新提交的commit上的。那麼如果commit時忘了打標籤怎麼辦呢?答案就是找到歷史提交的commit的id,在標籤命令的然後打上標籤即可。關鍵命令:git tag <tagname> 版本號
當然,補錄的時候我們是可以選擇補錄附註標籤還是輕量標籤的,根據相關命令補充參數即可。
刪除標籤
如果標籤打錯了,可以刪除標籤。關鍵命令:git tag -d <tagname>
,-d表示delete
因為我們創建的標籤默認都只存儲在本地,不會自動推送到遠程,因此打錯的標籤可以在本地直接刪除。
這是為什麼呢,因為git pull推送時並不會把標籤也推送到遠程伺服器上,只有通過顯示命令才能把標籤推送出去,關鍵命令:git push origin <tagname>
,或者一次性推送所有標籤git push origin --tags
,關於推送,會在後面的章節介紹。
如果標籤已經推送出去,要刪除標籤就需要先刪除本地,再從遠程刪除,遠程刪除命令:git push origin :refs/tags/<tagname>
。
分支管理
分支的工作方式
幾乎每個版本控制系統都支援分支管理,使用分支意味著你可以從開發主線上分離出來,在不影響主線的同時繼續工作,在前面多次commit和查看版本,可以知道每次commit,git都會把他們串成一條時間線,這條時間線就是一個分支,目前為止,我們的git里只有一條時間線,即主分支(master分支)。
另外,我們查看版本資訊的時候,會發現最新的commit上總會有一個HEAD
的標識,如下圖:
HEAD
是什麼意思呢,它可以理解為「頭指針」,指向當前工作區的分支,當分支很多時,git如何知道你在哪條分支上工作呢,就是通過HEAD
來標識。因為當前只有master主幹,沒有其他分支,因此HEAD
是一直指向master的。
這裡我們要明白,HEAD
是指向分支的,不是指向commit,master(分支)才是指向commit,開始的時候,master分支是一條時間線,git用master指向最新的commit,再用HEAD
指向master,這樣就能明確知道當前的分支,以及當前分支的提交點,如下圖:
之後每一次commit,master分支就會向前移動一步,隨著不斷的提交,master分支的時間線就會越來越長(在沒有其他分支的情況下)。
當我們創建分支的時候,比如創建一個dev分支,git就會新建一個dev指針,指向跟master一樣的commit,再把HEAD
指向dev,表示當前是在dev分支上,如下圖:
git創建一個分支是很快的,因為它只是增加了一個dev指針,改變一下HEAD
的指向,而工作區的文件是沒有任何變化的,但從現在開始,之後對工作區的修改和commit都是針對dev分支了,每次commit,dev都會往前移一步,而master指針不變,如下圖:
假如我們在dev分支上的工作已經完成了,就可以把dev合併到master上。那git如何分支呢?顯然,直接把master指向dev最新的commit就可以完成合併了,因此,git的合併分支也很快,改一下指針即可,工作區內容不變。
合併完分支之後,甚至可以把dev分支刪掉,這裡刪除分支其實就是把dev指針刪掉,因為我們已經合併完成了,刪掉後,這條時間線就只剩下一個master分支。
分支操作
1.創建分支
關鍵命令:git branch <branchname>
2.查看分支
關鍵命令:git branch
,*號代表當前所屬分支
3.切換分支
關鍵命令:git checkout <branchname>
也可以直接在創建分支時加上一個-b
參數(branch-分支的縮寫),表示創建並切換,完整命令:git checkout -b <branchname>
,相當於執行了創建和切換兩個命令。
🎈知識點:
在刪除文件的章節曾介紹過一個git checkout -- <file>
命令,是用於撤銷工作區的修改,同樣是checkout命令,作用卻相差很大,因此,新版的git提供了git switch
用於切換分支,git restore <file>
用於撤銷工作區的修改,用於區分兩者的功能,雖然提供了新的命令,但舊命令仍然是可用的。
switch切換分支命令如下:
git switch <branchname>
:切換分支git switch -c <branchname>
:創建並切換分支
4.合併分支
現在我們已經切換到dev分支了,我們在dev分支修改一下readme.txt,追加一行:this is a new branch!
dev的工作已經完成,切換到master分支,查看一下readme.txt,發現剛剛在readme.txt追加的一行不見了,因為那是在dev上commit的,不會影響到master。
現在我們來合併分支,關鍵命令:git merge <branchname>
,這個命令用於把當前分支合併到指定的分支。合併後,我們再查看readme.txt就會再次看到追加的那一行。
如果你仔細看,會發現合併成功時,有一行這樣的提示:Fast-forward
,這是告訴我們,這個合併是「快進模式」,也就是直接把master的指針直接指向dev的最新commit,所以合併速度很快。當然,並不是每次合併能如此快速,後面還會講其他方式的合併。
5.刪除分支
合併完成後,dev分支就沒有用了,我們可以選擇把它刪除,只保留master一個主分支,刪除命令:git branch -d <branchname>
,d就是delete的意思。
合併衝突
1.解決衝突
並不是每一次合併都是順利完成的,我們先看一下下面一個例子。
第一步,我們先重新創建一個dev分支,並在readme.txt上追加一行:this is dev!然後commit。
第二步,切回master分支,此時readme.txt是沒有上面追加的那一行的,但我們給它追加一行:this is master!然後commit。
現在master分支和dev分支各自都有一個新的commit,時間線都往前移了一步,如下圖:
前面合併分支時我們也提到過,並不是每一次合併都是快進模式,像上面的情況,合併的時候就會引起衝突,因為兩個commit工作區的文件內容不是一樣的,我們先試著合併:
果然,git告訴我們readme.txt文件存在衝突,使用git status查看工作區情況時也會告訴我們有文件衝突。
這個時候我們查看readme.txt,會發現裡面多了一些內容,git用<<<<<<<
,=======
,>>>>>>>
標記了不同分支的內容。當文件存在衝突時,我們必須手動解決衝突後再提交,即手動修改文件。
2.查看合併情況
手動解決衝突後,工作樹會再次回到乾淨狀態。此時master和dev分支就變成了下圖的樣子:
查看合併情況:git log --graph
,我們可以再加上--pretty==oneline
(一個版本顯示一行)和--abbrev-commit
(僅顯示版本號前幾個字元)兩個參數,更簡潔的查看合併情況:
分支管理策略
1.禁用快進模式
前面合併分支,我們就提到過Fast forward
快進模式,但這種模式在刪除分之後,就會丟掉分支資訊,無法去查看合併的記錄。
因此我們可以強制禁用Fast forward
模式,git會在合併時生成一個新的commit,這樣在查看合併記錄時就可以查看分支資訊了。
合併時禁用快進模式的關鍵命令: git merge --no-ff -m '版本資訊' <brandname>
我們重新建一個test分支來使用一下這個禁用命令:
因為本次合併禁用了快進模式,git在合併時會生成一個新的commit,所以要加上-m
參數,把commit描述寫進去,現在已經合併完成,我們來查看一下合併情況:
之前我們使用快進模式時,合併後,dev和master是指向同一個commit的,合併只是把master指針向前移了一步,而禁用快進之後,合併時創建了一個新的commit,master會指向新的commit,test仍然指向所在分支的最近的一次commit,如下對比圖:
2.分支策略
在實際的工作中,我們應按照以下幾個基本原則進行分支管理:
- ✔️ master要保持一個穩定的版本,即僅用來發布新版本,不在此分支進行日常的開發;
- ✔️ 日常開發在dev分支上,也就是說,dev是不穩定的一個分支,在到達某個時間點後,需要發布版本時,再把dev合併到master上;
- ✔️ 每個人分別在自己的分支上進行開發,根據需要再時不時地合併到dev分支上。
這時候,團隊的分支情況就像下圖:
檢出版本
檢出版本
通過分支管理,我們已經了解到分支的概念,也知道HEAD
是指向分支的,比如下圖中HEAD
是指向master分支的,master分支又默認指向最新的commit,但是有時候我們可能並不想要分支指向最新的commit,由於某些原因最新的commit不可用了,想要在分支中的某一個舊版本進行繼續工作時怎麼辦呢?
我們可以使用checkout來檢出版本,什麼是檢出版本,說白了其實就是修改HEAD
的指向。
檢出版本關鍵程式碼:git checkout 版本號
首先查看一下歷史版本,記住當前master最新的commit是ef69bbf,想要檢出版本的版本號:66b725a
現在我們來檢出這個版本:
檢出版本後,會出現一堆提示,它告訴我們已經檢出到66b725a版本,並且當前處於detached HEAD
(分離頭指針)狀態,這個狀態意思是說,HEAD
指針指向了一個具體的commit,而不是分支,我們說過HEAD
是指向分支的,分支才是指向commit的。這裡它處於分離狀態是因為我們主動把HEAD
指針修改了,使它指向了一個commit。
檢出版本後,再次查看版本記錄,對比發現最近提交的兩個版本已經看不到了,指針已經指向了回退的版本。
如果要查看比回退高的版本,可以執行git reflog
。
除了可以通過版本號檢出版本,還可以通過標籤名去檢出,關鍵程式碼:git checkout <tagname>
,這裡不再做演示。
現在我們已經檢出一個版本了,我們在這個版本上對readme.txt追加一行:this is checkout!添加到暫存區後,查看一下文件的狀態:
它提示了HEAD detached at 66b725a
,意思是說當前不屬於任何分支,因為HEAD
處於「分離頭指針」模式,接下來我們提交這個版本,查看一下版本資訊:
此時HEAD指針指向新的commit,並且是在檢出版本的前面,我們先記下這個最新的commit:99dc7db。然後切換到master分支:
切換到master分之後,它發出一個警告:99dc7db這個commit將被遺留掉,因為這個commit不在任何一個分支上。此時,我們再次查看一下版本資訊:
發現已經看不到99dc7db這個commit的資訊了,HEAD
重新指向了master分支的最新commit:ef69bbf,而我們檢出版本時修改的readme.txt文件也回到了之前的版本。由於99dc7db這個commit當時是處於分離頭指針狀態,不屬於任何分支,因此也沒有任何分支可以追蹤到,但它並不是被刪除了,它仍保留在版本庫中,我們可以通過指定版本號去查看這個commit:
挽留分離頭指針
在「分離頭指針」模式下提交的commit,在沒有歸到任何一個分支時,也就只能通過版本號(99dc7db)指定去訪問了,如果這個commit是master分支所需要的,可以使用合併命令,把這個commit合併到master分支上,實現兩者的兼容:
別名
設置別名
git並不會像一些編譯軟體,在你輸入部分命令時就自動聯想到你想要的命令,必須要手動敲出一個完整且正確的命令。還好git支援別名設置,可以通過 git config
文件來輕鬆地給一些常用、或者複雜的命令設置一個別名,比如git status
命令我們經常使用,我們可以告訴git以後st就表示status
。
關鍵命令:git config --global alias.別名 命令
,--global
是全局參數,也就是說設置的命令在這台電腦的所有git倉庫下都可用。
當然,我們還可以設置其他各種各樣的命令,比如:ck表示checkout
,ci表示commit
,br表示branch
等,具體可以參考//git-scm.com/book/zh/v2/Git-基礎-Git-別名
git config --global alias.ck checkout
git config --global alias.br branch
git config --global alias.cf config
git config --global alias.fc fetch
配置的命令還可以是多個,但需要加上引號來表示,比如我們可以配置一個git last
,讓其顯示最後一次提交資訊,即last是log -1
的別名:
配置文件
還有一些很長的命令我們都可以這樣修改成簡單易記的別名,而這些別名設置都保存在~/.gitconfig
文件中:
別名就在[alias]
後面,要刪除別名,直接把對應的行刪掉即可;如果要修改別名,直接重新設置就行,git會在已有的別名進行覆蓋,除了可查看配置文件,也可以通過git config --list | grep alias
命令來查看這些別名:
GitHub的使用
前面我們所講的操作都是基於本地的,git作為一個分散式的版本控制系統,和svn最顯著的區別就是可以離線使用,不需要創建一個中心的git服務,就可以在本地使用版本控制。
同一個git倉庫,可以分布到不同的機器上。在實際工作中,我們在多人協作時就必須要有一個集中管理的地方,因此就有了各種各樣的程式碼託管服務,GitHub就是其中的一種,這裡就不對其進行過多的介紹。
GitHub官網地址://github.com/,自行註冊一個帳號。
新建項目
登錄後,在首頁點擊Start a project。
然後根據頁面提示,輸入對應的資訊:
填好資訊之後點擊創建,就會跳轉到該倉庫,可以看到倉庫的遠程地址:
git遠程地址
查看本地關聯的遠程倉庫地址,相關命令如下:
git remote
:不帶參數,列出已經存在的遠程分支git remote -v
:列出詳細資訊,在每一個名字後面列出其遠程url, -v 參數為 –verbose 的簡寫,表示顯示對應的克隆地址git remote add 倉庫名 倉庫遠程url
:添加一個遠程倉庫
我們先查看一下本地是否有遠程的倉庫:
沒有內容返回說明當前沒有遠程的倉庫,接下來我們把剛剛創建好的github倉庫添加到本地:
添加成功後再次查看遠程地址就有對應的數據返回了。
git遠程操作
1.查看遠程項目資訊
關鍵程式碼:git remote show 遠程倉庫名
2.推送內容到遠程倉庫
關鍵程式碼:git push 遠程倉庫名 <branchname>
首次執行,會彈出github登錄介面,授權登錄即可,推送完畢查看github的文件,會發現分支的所有文件都已經傳到github了
3.從遠程倉庫拉取程式碼
關鍵程式碼:
git fetch 遠程倉庫名 <branchname>
:從遠程獲取最新版本到本地,但不會自動mergegit pull 遠程倉庫名 <branchname>
:從遠程獲取最新版本並merge到本地
我們先在github模擬其他人修改分支內容,點擊github分支中的Create new file新建一個txt文件,輸入內容後點擊左下角Commit new file提交;
然後在本地的git進行拉取文件,我們先看一下使用git fetch來拉取的效果:
拉取完成後,查看一下本地的工作目錄,發現在github創建的test_fetch.txt並沒有出現在本地,這是為什麼呢?原來git fetch並沒有真正地去影響到本地工作目錄,只是把數據拉取下來放到了本地的遠程倉庫,我們可以通過git merge 遠程倉庫名/<branchname>
來合併分支。
這樣的話,每次拉取都需要執行一遍合併實在太麻煩了,git還有另外一個拉取命令,就是git pull 遠程倉庫名 <branchname>
,它會在拉取程式碼時自動合併到分支,拉取後再看一下工作目錄,數據已經與遠程倉庫一致了:
4.遠程修改
關鍵程式碼:
git remote rename <old_name> <new_name>
:重命名遠程倉庫名稱,只是修改本地git remote rm name
:刪除遠程倉庫地址(github地址如果更新了就可以刪除重新添加)
5.克隆遠程項目
關鍵程式碼:git clone url
當我們本地沒有git項目且已知一個遠程項目可用時,我們就可以通過克隆,把遠程倉庫的整個項目克隆下來進行工作。首先,需要手動創建一個文件夾,在文件里右鍵點擊Git Bash Here進入git命令窗口(也可以通過cd命令切換到該目錄),輸入克隆命令回車即可,這裡克隆的url就是github遠程項目的地址,可以在github上查看。
克隆完成後,就可以進入項目開始你的工作了,默認是進入到master分支,需要在其他分支工作時使用分支切換命令切換即可,也可以在克隆時加上-b參數切換分支,克隆完成後會自動切換到指定的分支,完整命令:git clone -b <branchname> url