Git是版本管理工具的一種,是分布式的控制系統。之前用過SVN,它是中心版本控制系統,與Git有很大的不同,當今主流的版本控制都是用的Git,所以希望借此課程的機會,了解一下Git的使用。
一、Git的基礎
1、Git的基本運作流程
(1) workspace->index->Repository
在本地寫的代碼都在workspace中,通過add指令暫存到index中,然后通過commit提交到本地倉庫Repository中。如果有多位項目成員,則每個成員都對應一套本地倉庫管理系統,各自寫的代碼互不干擾。這也就是分布式的體現,成員之間並行進行,互不干擾。
(2) checkout
可以切換Repository中的某個分支。這是版本控制的重要功能,在不同分支上工作。
(3) pull, push, fetch/clone
這是遠端Remote與本地之間的數據同步操作。
2、Git與SVN的不同

可見SVN並沒有本地的倉庫,只有一個集中的服務器,所有同步操作直接與服務器進行,這意味着必須要聯網才能查看歷史版本信息,進行版本控制,而Git擁有本地倉庫,本地即有全部的版本歷史信息,如果沒有網絡,只需提交到本地倉庫,之后再與Remote同步即可。
svn這種集中式十分依賴於中心服務器,缺點不言而喻。Git分布式有去中心化的特點,每個使用者都是平等的,每人都有一套完整的版本庫,並且每個人的又有各自修改后寫入的新代碼。使用者之間可以比較交換,同步。SVN和Git的關系類似於C/S和P2P。
二、Git的操作與使用
1、Git的基本配置
Github, Gitee等雲平台都是基於Git的,它擔任一個托管代碼的服務器的角色,但它與SVN中的服務器還是不一樣,因為它們與各個主機之間的關系是平等的。
無論是使用Github還是Gitee都需要本地有個Git工具,當與雲端服務器同步時,都需要Git與它們通信。
(1) 安裝Git
-
先下載安裝Git: https://git-scm.com/download;
-
配置用戶名郵箱:
git config --global user.name "your_name"
git config --global user.email "your_email"
-
生成SSH Key:
ssh-keygen -t rsa -C your_email
-
查看Key:
open ~/.ssh
將查到的Key復制下來,這就是公鑰,之后會用來配置GitHub/Gitee
(2) 配置GitHub
在GitHub->settings->SSH keys中,如圖,粘貼進之前查看到的Key即可。此時Git便可以與Github通信,每次同步Github時不需要再登陸。
2、Git的基本操作
-
git init - - 初始化代碼倉庫
-
git clone - - 克隆遠程倉庫
PS:如果希望在克隆的時候,自己定義要新建的項目目錄名稱,可以在上面的命令末尾指定新的名字:$ git clone git://github.com/schacon/grit.git mygrit
-
git add - - 把需要提交的所有修改放到暫存區(Stage)
- git add file – 提交指定文件
- git add . || git add -A – 提交所有文件
- git add *.js – 提交所有.js格式文件
- git add -f file – 強制添加
-
git diff - - 查看當前目錄的所有修改(#當暫存區中沒有文件時,git diff比較的是,工作區中的文件與上次提交到版本庫中的文件。
#當暫存區中有文件時,git diff則比較的是,當前工作區中的文件與暫存區中的文)- git diff HEAD - - file – 比較工作區中的文件與版本庫中文件的差異。HEAD指向的是版本庫中的當前版本,而file指的是當前工作區中的文件。
-
git commit -m “message” - - 提交代碼
-
git rm - - 會把文件從當前目錄刪除(不會保存刪除的文件)。如果需要從Git倉庫中刪除,但保留在當前工作目錄中,亦即從跟蹤清單中刪除,可以使用git rm -r --cached readme.md
PS:如果一個文件已經被提交到版本庫,那么你永遠不用擔心誤刪,但是要小心,你只能恢復文件到最新版本,你會丟失最近一次提交后你修改的內容。 -
git log - - 查看歷史記錄,git log命令顯示從最近到最遠的提交日志
- git log --graph – 查看分支合並圖
-
git reflog - - 用來記錄你的每一次命令
-
git remote - - 查看當前的遠程庫
-
git remote -v - - 可以顯示對應的克隆地址(對於多個遠程倉庫很有用)
-
git remote add [short_name] - - 可以添加新的遠程倉庫
-
git remote add origin < address > - - 關聯一個遠程庫
-
git fetch [remote-name] - - 可以從遠程倉庫抓取數據到本地。
-
git pull - - 更新數據
-
git push [remote_name] [branch_name] - - 推送數據到遠程倉庫 默認使用origin和master
-
git push -u origin master [-f] - - 第一次將本地庫的所有內容推送到遠程庫上
-
git remote show origin - - 查看遠程倉庫信息
-
git remote rename [old_name] - - 遠程倉庫重命名
-
git remote rm [remote_name] - - 刪除遠程倉庫
-
git branch -d < name > - - 刪除本地分支
-
git tag - - 顯示當前庫中的標簽
-
git branch - - 可顯示當前所有分支。可以使用–merged和–no-merged查看已經合並、未合並的分支。
-
git branch <branch_name> - - 創建新分支
-
git branch -r - - 查看遠程倉庫分支
-
git checkout <branch_name> - - 切換到指定的分支
-
git checkout -b <branch_name> - - 創建新分支並切換到該分支
-
git merge 合並分支
舉例:
將hotfix分支合並到master上需要:
git checkout master
git merge hotfix
合並之后可以使用git branch -d hotfix刪除分支。
如果合並時存在沖突,需要手工修改
合並分支時,加上—no-ff參數就可以用普通模式合並,合並后的歷史有分支,能看出來曾經做過合並,而fast forward合並就看不出來曾經做過合並。 -
git checkout . --恢復上次提交狀態
- git checkout --file - - 文件在工作區的修改全部撤銷
- 一種是file修改后還沒有被放到暫存區,現在,撤銷修改就回到和版本庫一模一樣的狀態;
- 一種是file已經添加到暫存區后,又作了修改,現在,撤銷修改就回到添加到暫存區后的狀態。
-
git status – 用於顯示工作目錄和暫存區的狀態。使用此命令能看到那些修改被暫存到了, 哪些沒有, 哪些文件沒有被Git tracked到。git status不顯示已經commit到項目歷史中去的信息。
-
git reset --hard HEAD^ - - #版本回退
- git reset --hard commitId - - 取消回退,commitId為你想要回到的未來版本號
- PS:Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,上一個版本就是HEAD,上上一個版本就是HEAD^,當回退版本較早時可以寫成HEAD~100。
-
git stash - - 儲藏可以獲取你工作目錄的中間狀態——也就是你修改過的被追蹤的文件和暫存的變更——並將它保存到一個未完結變更的堆棧中,隨時可以重新應用。
現在你想切換分支,但是你還不想提交你正在進行中的工作;所以你儲藏這些變更。為了往堆棧推送一個新的儲藏,只要運行git stash。把所有未提交的修改(包括暫存的和非暫存的)都保存起來,用於后續恢復當前工作目錄。
PS:需要說明一點,stash是本地的,不會通過git push命令上傳到git server上。 -
git stash list - - 查看現有的所有儲藏,此命令顯然暗示了git stash可以多次保存工作進度,並用在恢復時候選擇。
-
git stash pop [–index] [ < stash > ] - - 重新應用已經實施的儲藏(刪除儲藏)
- 如果不使用任何參數,會恢復最新保存的工作進度,並將恢復的工作進度從存儲的工作進度列表中清除。
- 如果提供< stash>參數(來自git stash list顯示的列表),則從該< stash>中恢復。恢復完畢也將從進度列表中刪除< stash>。
- 選項–index除了恢復工作區的文件外,還嘗試恢復暫存區。
-
git stash drop [< stash >] - - 刪除一個存儲的進度。(默認刪除最新的進度)
-
git stash clear - - 清空當前所有的stash
-
git stash branch < branchname > < stash > - - 基於儲藏進度創建分支。
三、Git的使用案例與心得
1、場景一:在本地創建工程
在工程目錄里使用:git init
,便可初始化一個版本庫
-
查看本地版本庫的狀態:
git status
如圖,可以看到當前本地版本庫與remote的庫的同步情況(remote操作后面再講)。
-
開始在本地添加工程文件:
git add HomeWork/Git使用心得體會.md
此指令會將文件放到暫存區(Index)中,如下圖中的Staged Changes:
-
再提交到本地版本庫(Repository)當中:
git commit -m "add HomeWork/Git使用心得體會.md"
-
版本控制初步:
- 假設有一個文件"git.md",對它做了四次修改,第i次版本,會將里面內容改成"version i",每次都對其提交。
用
git log
指令可查看提交記錄: 現在欲將其恢復到版本2,則使用命令:
git reset --hard HEAD^^
其中HEAD指向當前分支最新提交的一個版本,一個代表回退一個版本,兩個即可回退到version 2。該指令有多種切換方法:git reset —hard HEAD^^/HEAD~100/commit-id/commit-id的頭幾個字符
- 此時若要返回至version 4,則需要查看commit-id,可使用:
git reflog
,之后再使用reset指令即可。 - 所以這么看下來,很像是鏈表操作,HEAD即指針,可移動其切換版本。
2、場景二:與遠程版本庫進行交互
因為Git是分布式的,成員各自編寫的代碼都在本地倉庫,最終完成一個工程肯定要統一合並,一般都合並到GitHub之類的服務器端。但因為寫代碼,彼此之間並不知道對方寫了什么,改了什么,增加了什么,所以在合並時,勢必會有沖突,所以最好是先拉取GitHub的內容與自己的合並,再提交到GitHub,以減少沖突,但仍然不能完全避免沖突。
-
先將遠程倉庫拉取下來(fetch/clone 或 pull)
如果第一次,自然是fetch/clone。否則,需要和遠程同步時,則先需要pull,可以使用
git pull [遠程倉庫名] [要拉取的分支]
-
再進行push
-
如果直接push,沖突概率很大,因為在你編寫代碼的時候,同事提交它修改的代碼,而你的寫的和他的就可能有很大不同。所以要先pull,因為遠程倉庫里的東西,是一段時間后“達成的共識”,將它拉取下來基本沒錯,因為它是在比較過提交的各方的代碼之后確定下來的暫時版本。拉取下來之后,也可能會有沖突,自己在本地進行修改,解決沖突部分,這樣也不會因為直接提交到遠程,導致遠程的代碼混亂,使得各個成員也失去了一個標准參照的版本。
-
push即是merge遠程和本地兩個不同的分支
注意:同一分支里的某個版本實際上是對上一個版本的增量補丁,即記錄不同行對比的差異(line diff)。
3、場景三:團隊協作
團隊的每個成員都各自負責不同的某一個功能塊,各占一個分支,各自在本地有一個倉庫,有自己的master和branch,branch下可以有更多的branch,最后成員完成時,將自己的分支合並到自己的master,再提交到服務器端。
-
合並的分類
(1) 快進式合並
這個在合並的時候會將分支和主線合並成一條時間線,如圖:
(2) 相對應的,要體現多條分支,須使用:
--no-ff
參數比如:
git merge --no-ff mybranch
-
截取一段分支合並圖,可見,很像一個有向無環的活動圖。帶箭頭的有向邊相當於活動,圖中的點相當於事件,或者說是一個版本。在多人合作當中,可以並行進行,但效率取決於關鍵路徑的長短,即如果有人進度落后,是不能按時合並成一個期望的版本的。
4、場景四:GitBase
本地與服務端的同步,即pull再push,pull實際上就是將兩個分支(本地分支和服務器分支)merge,這就會表現出兩條分支線:
實際上可以用另外一種方法,讓分支歷史看起來像沒有經過任何合並一樣,用 git rebase:
-
gitbase的原理:
這個命令會把"mywork"分支里的每個提交(commit)取消掉,並且把它們臨時 保存為補丁(patch)(這些補丁放到".git/rebase"目錄中),然后把"mywork"分支更新 為最新的"origin"分支,最后把保存的這些補丁應用到"mywork"分支上。當"mywork"分支更新之后,它會指向這些新創建的提交(commit),而那些老的提交會被丟棄。
-
沖突的解決:
在rebase的過程中,也許會出現沖突(conflict). 在這種情況,Git會停止rebase並會讓你去解決沖突;在解決完沖突后,用
git-add
命令去更新這些內容的索引(index), 然后,無需執行git -commit
, 只要執行:git rebase --continue
這樣git會繼續應用(apply)余下的補丁。
在任何時候,你可以用--abort參數來終止rebase的行動,並且"mywork" 分支會回到rebase開始前的狀態。
git rebase --abort
5、場景五:Fork + Pull request
如果向開源項目貢獻代碼,則需要先fork一下別人的倉庫(即拷貝),當寫完自己的代碼后,發起Pull request給原倉庫,原倉庫的所有者來決定要不要merge進去。
-
例如,在github上fork一個gitignore項目,里面有很多寫好的.gitignore文件:
-
之后便看到這個倉庫進了自己的,如果自己有添加或修改了代碼,可以點擊Pull request:
-
提交之后,在原倉庫的Pull request里看到提交的請求:
參考鏈接:
https://blog.csdn.net/weixin_43606158/article/details/90729743