分布式版本控制系统Git的安装与使用
作業要求
1.(本次作業要求來自:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2103
2.? 我的Github遠程倉庫地址: https://github.com/llgeill/llg-centos-git--test
3.? 我的Github遠程倉庫地址截圖
作業內容
1.Git的由來
很多人都知道,Linus在1991年創建了開源的Linux,從此,Linux系統不斷發展,已經成為最大的服務器系統軟件了。
Linus雖然創建了Linux,但Linux的壯大是靠全世界熱心的志愿者參與的,這么多人在世界各地為Linux編寫代碼,那Linux的代碼是如何管理的呢?
事實是,在2002年以前,世界各地的志愿者把源代碼文件通過diff的方式發給Linus,然后由Linus本人通過手工方式合并代碼!
你也許會想,為什么Linus不把Linux代碼放到版本控制系統里呢?不是有CVS、SVN這些免費的版本控制系統嗎?因為Linus堅定地反對CVS和SVN,這些集中式的版本控制系統不但速度慢,而且必須聯網才能使用。有一些商用的版本控制系統,雖然比CVS、SVN好用,但那是付費的,和Linux的開源精神不符。
不過,到了2002年,Linux系統已經發展了十年了,代碼庫之大讓Linus很難繼續通過手工方式管理了,社區的弟兄們也對這種方式表達了強烈不滿,于是Linus選擇了一個商業的版本控制系統BitKeeper,BitKeeper的東家BitMover公司出于人道主義精神,授權Linux社區免費使用這個版本控制系統。
安定團結的大好局面在2005年就被打破了,原因是Linux社區牛人聚集,不免沾染了一些梁山好漢的江湖習氣。開發Samba的Andrew試圖破解BitKeeper的協議(這么干的其實也不只他一個),被BitMover公司發現了(監控工作做得不錯!),于是BitMover公司怒了,要收回Linux社區的免費使用權。
Linus可以向BitMover公司道個歉,保證以后嚴格管教弟兄們,嗯,這是不可能的。實際情況是這樣的:
Linus花了兩周時間自己用C寫了一個分布式版本控制系統,這就是Git!一個月之內,Linux系統的源碼已經由Git管理了!牛是怎么定義的呢?大家可以體會一下。
Git迅速成為最流行的分布式版本控制系統,尤其是2008年,GitHub網站上線了,它為開源項目免費提供Git存儲,無數開源項目開始遷移至GitHub,包括jQuery,PHP,Ruby等等。
歷史就是這么偶然,如果不是當年BitMover公司威脅Linux社區,可能現在我們就沒有免費而超級好用的Git了。
2.什么是Git
- 從定義上來說Git就是一個分布式版本控制系統
- 從作用上來說Git就是管理一些文本或者代碼的版本更新,例如內容的改動
- 從管理的對象來說,所有的版本控制都只能針對文本內容,像word這樣的二進制內容,所謂版本,就是文件快照,不能比較差異,只能算帶備份的網盤
3.為什么git叫做分布式版本控制系統
如果要想了解分布式是什么意思,那么我們得先去了解它的對立面集中式
集中式版本控制系統
- 工具:CVS及SVN都是集中式的版本控制系統
- 內容:集中式版本控制系統集中存放在中央服務器的,而干活的時候,用的都是自己的電腦,所以要先從中央服務器取得最新的版本,然后開始干活,干完活了,再把自己的活推送給中央服務器。中央服務器就好比是一個圖書館,你要改一本書,必須先從圖書館借出來,然后回到家自己改,改完了,再放回圖書館。
- 缺點:集中式版本控制系統最大的毛病就是必須聯網才能工作,如果在局域網內還好,帶寬夠大,速度夠快,可如果在互聯網上,遇到網速慢的話,可能提交一個10M的文件就需要5分鐘,這還不得把人給憋死啊。
?
分布式版本控制系統
工具:git
內容:布式版本控制系統沒有“中央服務器”,每個人的電腦上都是一個完整的版本庫,這樣,你工作的時候,就不需要聯網了,因為版本庫就在你自己的電腦上。既然每個人電腦上都有一個完整的版本庫,那多個人如何協作呢?比方說你在自己電腦上改了文件A,你的同事也在他的電腦上改了文件A,這時,你們倆之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。
優點:和集中式版本控制系統相比,分布式版本控制系統的安全性要高很多,因為每個人電腦里都有完整的版本庫,某一個人的電腦壞掉了不要緊,隨便從其他人那里復制一個就可以了。而集中式版本控制系統的中央服務器要是出了問題,所有人都沒法干活了。在實際使用分布式版本控制系統的時候,其實很少在兩人之間的電腦上推送版本庫的修改,因為可能你們倆不在一個局域網內,兩臺電腦互相訪問不了,也可能今天你的同事病了,他的電腦壓根沒有開機。因此,分布式版本控制系統通常也有一臺充當“中央服務器”的電腦,但這個服務器的作用僅僅是用來方便“交換”大家的修改,沒有它大家也一樣干活,只是交換修改不方便而已。當然,Git的優勢不單是不必聯網這么簡單,后面我們還會看到Git極其強大的分支管理,把SVN等遠遠拋在了后面(svn其實也有分支功能,不過是在服務器上的分支)
?
集中式與分布式的差異性
4.開始安裝Git
Git安裝
可以直接從官網下載相應操作系統的Git然后進行安裝,當然也可以使用命令行的方式
- 例如在使用Centos操作系統的時候,可以使用如下命令快速安裝
- yum install -y git
- 或著不想使用命令行方式就直接去官網下載壓縮包
Git bash配置
用戶名和郵箱地址的作用
修改用戶名和郵箱地址
- $?git?config?--global user.name "username"
- $?git?config?--global?user.email??"email"
查看用戶名和郵箱地址
- $?git?config?user.name
- $?git?config?user.email
5.開始使用Git(版本庫)
以下所有操作都在centos上操作完成
什么是版本庫
版本庫又名倉庫,英文名repository,你可以簡單理解成一個文件夾,這個文件夾里面的所有文件都可以被Git管理起來,每個文件的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。所以,創建一個版本庫非常簡單。
開始創建版本庫
首先,選擇一個合適的地方,創建一個空目錄
#創建一個文件件 [llg@localhost 桌面]$ mkdir llg-test-git #進入文件夾目錄 [llg@localhost 桌面]$ cd llg-test-git/ #顯示當前文件夾路徑 [llg@localhost llg-test-git]$ pwd接著,最重要的一步 通過git init?的命令將當前文件夾變成一個版本庫
[llg@localhost llg-test-git]$ git init 初始化空的 Git 版本庫于 /home/llg/桌面/llg-test-git/.git/ [llg@localhost llg-test-git]$最后,通過ls -al 命令,發現多了個git文件夾。這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄里面的文件,不然改亂了,就把Git倉庫給破壞了。
[llg@localhost llg-test-git]$ ls -al 總用量 4 drwxrwsr-x 3 llg llg 18 9月 14 19:48 . drwsrwsrwt. 13 llg llg 4096 9月 14 19:44 .. drwxrwsr-x 7 llg llg 119 9月 14 19:48 .git [llg@localhost llg-test-git]$?
開始添加文件到版本庫
首先需要注意的是版本管理的受眾范圍
- 首先這里再明確一下,所有的版本控制系統,其實只能跟蹤文本文件的改動,比如TXT文件,網頁,所有的程序代碼等等,Git也不例外。版本控制系統可以告訴你每次的改動,比如在第5行加了一個單詞“Linux”,在第8行刪了一個單詞“Windows”。而圖片、視頻這些二進制文件,雖然也能由版本控制系統管理,但沒法跟蹤文件的變化,只能把二進制文件每次改動串起來,也就是只知道圖片從100KB改成了120KB,但到底改了啥,版本控制系統不知道,也沒法知道。
- 不幸的是,Microsoft的Word格式是二進制格式,因此,版本控制系統是沒法跟蹤Word文件的改動的,前面我們舉的例子只是為了演示,如果要真正使用版本控制系統,就要以純文本方式編寫文件。
- 因為文本是有編碼的,比如中文有常用的GBK編碼,日文有Shift_JIS編碼,如果沒有歷史遺留問題,強烈建議使用標準的UTF-8編碼,所有語言使用同一種編碼,既沒有沖突,又被所有平臺所支持。
- 使用Windows的童鞋要特別注意:千萬不要使用Windows自帶的記事本編輯任何文本文件。原因是Microsoft開發記事本的團隊使用了一個非常弱智的行為來保存UTF-8編碼的文件,他們自作聰明地在每個文件開頭添加了0xefbbbf(十六進制)的字符,你會遇到很多不可思議的問題,比如,網頁第一行可能會顯示一個“?”,明明正確的程序一編譯就報語法錯誤,等等,都是由記事本的弱智行為帶來的。建議你下載Notepad++代替記事本,不但功能強大,而且免費!記得把Notepad++的默認編碼設置為UTF-8 without BOM即可:
首先,使用vi創建一個文件并且添加一些內容。確保此文件必須在這個版本倉庫里面也就是我們惡毒llg-test-git文件夾,不然放在其他地方是不能被版本控制的。
[llg@localhost llg-test-git]$ vi llg.txt [llg@localhost llg-test-git]$接著,將文件顯示的通過git add命令添加到版本庫里面,不過在這之前我們使用git status 來查看一下還沒使用git add命令時候的狀態和使用git add之后的狀態,比較一下不同。通過比較,我們發現了當使用git add 命令的時候其實是建立起了文件跟蹤的功能,之后使用git status 就可以看到有一個新文件
[llg@localhost llg-test-git]$ git status # 位于分支 master # # 初始提交 # # 未跟蹤的文件: # (使用 "git add <file>..." 以包含要提交的內容) # # llg.txt 提交為空,但是存在尚未跟蹤的文件(使用 "git add" 建立跟蹤) [llg@localhost llg-test-git]$ [llg@localhost llg-test-git]$ git add llg.txt [llg@localhost llg-test-git]$ git status # 位于分支 master # # 初始提交 # # 要提交的變更: # (使用 "git rm --cached <file>..." 撤出暫存區) # # 新文件: llg.txt # [llg@localhost llg-test-git]$?
最后,使用git commit -m告訴Git,把文件提交到倉庫。-m是提交的一些說明信息,必須要寫,可以方便自己和他人了解這次提交了什么東西。?
?
[llg@localhost llg-test-git]$ git commit -m "git練習測試" [master(根提交) abde635] git練習測試 1 file changed, 1 insertion(+) create mode 100644 llg.txt [llg@localhost llg-test-git]$?
疑問:為什么Git添加文件需要add,commit一共兩步呢?
因為commit可以一次提交很多文件,所以你可以多次add不同的文件,比如下圖,可以跟蹤多個文件然后全部一次性提交到版本庫里面.
[llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ vi two.txt [llg@localhost llg-test-git]$ vi three.txt [llg@localhost llg-test-git]$ git add one.txt [llg@localhost llg-test-git]$ git add two.txt [llg@localhost llg-test-git]$ git add three.txt [llg@localhost llg-test-git]$ git commit -m "這次測試一次提交三個文件" [master 44449dc] 這次測試一次提交三個文件 3 files changed, 4 insertions(+) create mode 100644 one.txt create mode 100644 three.txt create mode 100644 two.txt [llg@localhost llg-test-git]$ [llg@localhost llg-test-git]$ git status # 位于分支 master 無文件要提交,干凈的工作區 [llg@localhost llg-test-git]$?
6.Git版本回退
在學習版本回退之前我們線來了解下兩個常用的命令git status和git diff
git status
剛剛在創建版本庫的時候我們已經使用了,用來查看git的狀態,一般可以拿來查看我們做的某些操作之后的狀態,例如修改文件,添加文件等等
例如我們修改一個文件然后使用git status查看,發現了提示llg.txt是修改過的文件
?
[llg@localhost llg-test-git]$ vi llg.txt [llg@localhost llg-test-git]$ git status # 位于分支 master # 尚未暫存以備提交的變更: # (使用 "git add <file>..." 更新要提交的內容) # (使用 "git checkout -- <file>..." 丟棄工作區的改動) # # 修改: llg.txt # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") [llg@localhost llg-test-git]$
?
?
git sdiff
由于文件修改過后,我們有時候可能想知道具體修改了什么內容,位置在那里,區別是什么等等。,所以我們需要git sdiff命令來獲得這個修改前后的對照信息
[llg@localhost llg-test-git]$ git diff diff --git a/llg.txt b/llg.txt index f77faef..2f3d837 100644 --- a/llg.txt +++ b/llg.txt @@ -1 +1 @@ -my name is liliguang +My name is liliguang.I created three files a moment ago. [llg@localhost llg-test-git]$?
之后繼續測試,修改其他兩個文件內容 [llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ vi two.txt [llg@localhost llg-test-git]$ git diff diff --git a/llg.txt b/llg.txt index f77faef..2f3d837 100644 --- a/llg.txt +++ b/llg.txt @@ -1 +1 @@ -my name is liliguang +My name is liliguang.I created three files a moment ago. diff --git a/one.txt b/one.txt index 25ab921..34144e1 100644 --- a/one.txt +++ b/one.txt @@ -1 +1,2 @@ this is one +llg diff --git a/two.txt b/two.txt index 42b3fbc..0f62931 100644 --- a/two.txt +++ b/two.txt @@ -1,2 +1,2 @@ this is two - +llg [llg@localhost llg-test-git]$?
[llg@localhost llg-test-git]$ vi two.txt [llg@localhost llg-test-git]$ git diff diff --git a/llg.txt b/llg.txt index f77faef..2f3d837 100644 --- a/llg.txt +++ b/llg.txt @@ -1 +1 @@ -my name is liliguang +My name is liliguang.I created three files a moment ago. diff --git a/one.txt b/one.txt index 25ab921..34144e1 100644 --- a/one.txt +++ b/one.txt @@ -1 +1,2 @@ this is one +llg diff --git a/two.txt b/two.txt index 42b3fbc..e730358 100644 --- a/two.txt +++ b/two.txt @@ -1,2 +1,6 @@ this is two - +llg +sss +aaa +bbb +wef [llg@localhost llg-test-git]$?
?
開始使用版本回退的前提
版本回退的意思就是回退到某一次commit那里,這優點類似與打通關游戲,你可以在任意通過的關卡中來往穿梭
git log
首先,我們需要使用git log 命令來找出我們之前的commit信息。從下圖可以看到提交順序是按照最新日期排序的,最新的在最頂部,commit 后面的就是commit ID?了,后面的版本回退都靠它
?
[llg@localhost llg-test-git]$ git log commit 3956cc36e1017c14e79d9de0944bb9baa6d2ff51 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:35:26 2018 +0800測試兩個文件提交留一個文件不提交commit 44449dc5261b8a66bac14ce302519f26895d0163 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:11:22 2018 +0800這次測試一次提交三個文件commit abde635c93434ac4172caa4e0c6383e9dfc3d522 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:07:31 2018 +0800git練習測試[llg@localhost llg-test-git]$?
如果嫌輸出信息太多,看得眼花繚亂的,可以試試加上--pretty=oneline參數?
?
[llg@localhost llg-test-git]$ git log --pretty=oneline 3956cc36e1017c14e79d9de0944bb9baa6d2ff51 測試兩個文件提交留一個文件不提交 44449dc5261b8a66bac14ce302519f26895d0163 這次測試一次提交三個文件 abde635c93434ac4172caa4e0c6383e9dfc3d522 git練習測試 [llg@localhost llg-test-git]$?
?
開始使用版本回退
首先,Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,也就是最新的提交3956cc...(注意我的提交ID和你的肯定不一樣),上一個版本就是HEAD^,上上一個版本就是HEAD^^,當然往上100個版本寫100個^比較容易數不過來,所以寫成HEAD~100。
git reset
我們要把當前版本<測試兩個文件提交留一個文件不提交>回退到上一個版本<這次測試一次提交三個文件>,就可以使用git reset命令:
從下圖我們可以分析出,其實HEAD代表的是頭指針,畢竟這個git使用c語言寫的,我們對C應該也是不陌生的,所以這是一個雙向鏈表,可以向前也可以向后,只要我們知道commit ID。
?
[llg@localhost llg-test-git]$ git reset --hard HEAD HEAD 現在位于 3956cc3 測試兩個文件提交留一個文件不提交 [llg@localhost llg-test-git]$ git reset --hard HEAD^ HEAD 現在位于 44449dc 這次測試一次提交三個文件 [llg@localhost llg-test-git]$ git reset --hard HEAD^ HEAD 現在位于 abde635 git練習測試 [llg@localhost llg-test-git]$?
?
當我們跳的比較遠的時候,也可以直接指定commit ID 來跳轉?
?
[llg@localhost llg-test-git]$ git reset --hard 3956cc3 HEAD 現在位于 3956cc3 測試兩個文件提交留一個文件不提交 [llg@localhost llg-test-git]$?
當我們想要回到之前最新的版本的時候,我們會想到用git log,但是很不幸我們發現之前的版本信息都沒有了。所以我們還有一個命令用來找回之前的commit ID ,就是git reflog命令了。
?
[llg@localhost llg-test-git]$ git log commit abde635c93434ac4172caa4e0c6383e9dfc3d522 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:07:31 2018 +0800 git練習測試 [llg@localhost llg-test-git]$?
?
?
?
git reflog
可以拿到所有的操作記錄
從下面我們可以找到帶有commit字樣的就是我們之前創建的版本,但是只提供了ID的七為數,但是沒關系,git會自動識別出來
?
[llg@localhost llg-test-git]$ git reflog abde635 HEAD@{0}: reset: moving to HEAD^ 44449dc HEAD@{1}: reset: moving to HEAD^ 3956cc3 HEAD@{2}: commit: 測試兩個文件提交留一個文件不提交 44449dc HEAD@{3}: commit: 這次測試一次提交三個文件 abde635 HEAD@{4}: commit (initial): git練習測試 [llg@localhost llg-test-git]$ git reset --hard 3956cc3 HEAD 現在位于 3956cc3 測試兩個文件提交留一個文件不提交 [llg@localhost llg-test-git]$?
?
?
?
?
7.git的工作區和暫存區
首先是官圖解釋,從下圖可以看出如果我們想直接提交文件到master分支上,那么直接使用 git commit -a即可
接著再看看其余大佬繪制的圖
版本庫(Repository)
工作區有一個隱藏目錄.git,這個不算工作區,而是Git的版本庫。
Git的版本庫里存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區,還有Git為我們自動創建的第一個分支master,以及指向master的一個指針叫HEAD。
?
假如我們在commit之后沒有任何操作,那么我們暫存區就是空的
當我們添加了文件或者修改了文件,并且使用了add命令那么暫存區stage就又有了文件
?
當我們使用commit提交之后,那么早存區又變成了空
?
關于git diff 與暫存區的關系
- git diff 比較的是工作區文件與暫存區文件的區別(上次git add 的內容)
- git diff --cached 比較的是暫存區的文件與倉庫分支里(上次git commit 后的內容)的區別
8.Git如何撤回修改的操作
場景1:當你改亂了工作區某個文件的內容,想直接丟棄工作區的修改時,用命令git checkout -- file。
首先修改工作區的文件,不執行add操作
檢查git狀態,發現提示使用git checkout -- file 命令撤回操作 ,執行之后發現已經成功撤回
[llg@localhost llg-test-git]$ git status # 位于分支 master # 尚未暫存以備提交的變更: # (使用 "git add <file>..." 更新要提交的內容) # (使用 "git checkout -- <file>..." 丟棄工作區的改動) # # 修改: one.txt # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") [llg@localhost llg-test-git]$ [llg@localhost llg-test-git]$ git checkout -- one.txt [llg@localhost llg-test-git]$?
?
?
?
?
?
場景2:當你不但改亂了工作區某個文件的內容,還添加到了暫存區時,想丟棄修改,分兩步,第一步用命令git reset HEAD <file>,就回到了場景1,第二步按場景1操作。
首先修改文件并且通過add放到暫存區?
?
[llg@localhost llg-test-git]$ git status # 位于分支 master # 要提交的變更: # (使用 "git reset HEAD <file>..." 撤出暫存區) # # 修改: one.txt # [llg@localhost llg-test-git]$?
?
?
?
根據提示使用git reset HEAD <file> 撤出暫存區,執行成功?
?
[llg@localhost llg-test-git]$ git status # 位于分支 master # 要提交的變更: # (使用 "git reset HEAD <file>..." 撤出暫存區) # # 修改: one.txt # [llg@localhost llg-test-git]$ git reset HEAD one.txt 重置后撤出暫存區的變更: M one.txt [llg@localhost llg-test-git]$ git status # 位于分支 master # 尚未暫存以備提交的變更: # (使用 "git add <file>..." 更新要提交的內容) # (使用 "git checkout -- <file>..." 丟棄工作區的改動) # # 修改: one.txt # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") [llg@localhost llg-test-git]$?
場景3:已經提交了不合適的修改到版本庫時,想要撤銷本次提交,參考版本回退一節,不過前提是沒有推送到遠程庫。
9.Git如何刪除文件
此處的刪除文件是指當一個文件提交到版本庫后,也就是master分支。如果在工作區刪除這個文件,那么將會使工作區文件與版本庫文件不對應,在這里需要分清兩種使用情況。
工作區刪除了文件并且確實要從版本庫中也刪除該文件
那么就應該使用git rm 命令
[llg@localhost llg-test-git]$ rm delete.txt [llg@localhost llg-test-git]$ git rm -- delete.txt rm 'delete.txt' [llg@localhost llg-test-git]$ git status # 位于分支 master # 要提交的變更: # (使用 "git reset HEAD <file>..." 撤出暫存區) # # 刪除: delete.txt # [llg@localhost llg-test-git]$?
?
?
當然我們通過git status 命令可以看出這一操作只是保留在了暫存區,所以我們還需要commit命令提交這一更改,其實可以把這一命令想象成修改的命令
?
[llg@localhost llg-test-git]$ git commit -m "正式從版本庫中刪除該文件" [master ad66495] 正式從版本庫中刪除該文件 1 file changed, 1 deletion(-) delete mode 100644 delete.txt [llg@localhost llg-test-git]$ git status # 位于分支 master 無文件要提交,干凈的工作區 [llg@localhost llg-test-git]$?
?
工作區誤刪了某些文件,需要從版本庫中重新弄回來
根據git status 提示的方法,我們可以從版本庫中checkout中某些文件
[llg@localhost llg-test-git]$ git checkout -- delete.txt另外一個一個簡單的方法就是重新把版本庫更新回來,但是這個前提是你得保證只有這么一個刪除文件,如果有其他文件修改了并且未放到暫存區,那么就非常可怕了。
?
[llg@localhost llg-test-git]$ git reset --hard HEAD^ HEAD 現在位于 eda17f9 這是一個將要被刪除的文件 [llg@localhost llg-test-git]$ ls -al 總用量 24 drwxrwsr-x 3 llg llg 98 9月 15 19:56 . drwsrwsrwt. 13 llg llg 4096 9月 14 19:44 .. -rw-rw-r-- 1 llg llg 34 9月 15 19:56 delete.txt drwxrwsr-x 8 llg llg 183 9月 15 19:56 .git -rw-rw-r-- 1 llg llg 57 9月 14 21:06 llg.txt -rw-rw-r-- 1 llg llg 16 9月 14 22:05 one.txt -rw-rw-r-- 1 llg llg 14 9月 14 21:06 three.txt -rw-rw-r-- 1 llg llg 13 9月 14 21:06 two.txt [llg@localhost llg-test-git]$?
10.初識遠程倉庫
既然git的其中一個目的就是為了協作開發,那么遠程倉庫就是為了這一個目的的。通過將倉庫放在一個遠程的服務器上,讓所有人都可以訪問這個倉庫,可以一起更新和提交。
實際情況往往是這樣,找一臺電腦充當服務器的角色,每天24小時開機,其他每個人都從這個“服務器”倉庫克隆一份到自己的電腦上,并且各自把各自的提交推送到服務器倉庫里,也從服務器倉庫中拉取別人的提交。
完全可以自己搭建一臺運行Git的服務器,不過現階段,為了學Git先搭個服務器絕對是小題大作。好在這個世界上有個叫GitHub的神奇的網站,從名字就可以看出,這個網站就是提供Git倉庫托管服務的,所以,只要注冊一個GitHub賬號,就可以免費獲得Git遠程倉庫。
在繼續閱讀后續內容前,請自行注冊GitHub賬號。由于你的本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密的,所以,需要一點設置:
第1步:創建SSH Key。在用戶主目錄下,看看有沒有.ssh目錄,如果有,再看看這個目錄下有沒有id_rsa和id_rsa.pub這兩個文件,如果已經有了,可直接跳到下一步。如果沒有,打開Shell(Windows下打開Git Bash),創建SSH Key:
[llg@localhost ~]$ ssh-keygen -t rsa -C "903857227@qq.com"如果一切順利的話,可以在用戶主目錄里找到.ssh目錄,里面有id_rsa和id_rsa.pub兩個文件,這兩個就是SSH Key的秘鑰對,id_rsa是私鑰,不能泄露出去,id_rsa.pub是公鑰,可以放心地告訴任何人。
第2步:登陸GitHub,打開“Account settings”,“SSH Keys”頁面:
然后,點“Add SSH Key”,填上任意Title,在Key文本框里粘貼id_rsa.pub文件的內容:
[llg@localhost ~]$ cd .ssh/?
[llg@localhost .ssh]$ ls -al 總用量 16 drwsrwsrwt. 2 llg llg 57 9月 15 20:28 . drwsrwsrwt. 39 llg llg 4096 9月 15 19:59 .. -rw------- 1 llg llg 1679 9月 15 20:29 id_rsa -rw-r--r-- 1 llg llg 398 9月 15 20:29 id_rsa.pub -rw-r--r-- 1 llg llg 175 4月 27 20:05 known_hosts [llg@localhost .ssh]$ vi id_rsa.pub?
為什么GitHub需要SSH Key呢?因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。
當然,GitHub允許你添加多個Key。假定你有若干電腦,你一會兒在公司提交,一會兒在家里提交,只要把每臺電腦的Key都添加到GitHub,就可以在每臺電腦上往GitHub推送了。
最后友情提示,在GitHub上免費托管的Git倉庫,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放進去。
如果你不想讓別人看到Git庫,有兩個辦法,一個是交點保護費,讓GitHub把公開的倉庫變成私有的,這樣別人就看不見了(不可讀更不可寫)。另一個辦法是自己動手,搭一個Git服務器,因為是你自己的Git服務器,所以別人也是看不見的。這個方法我們后面會講到的,相當簡單,公司內部開發必備。
確保你擁有一個GitHub賬號后,我們就即將開始遠程倉庫的學習。
11.從github上添加一個遠程倉庫
首先我們github上登陸自己帳號后創建一個倉庫
接著我們可以看到創建完倉庫后給我們的提示
從本地的已有倉庫直接推送到遠程倉庫上
根據上邊提示輸入命令
?
[llg@localhost llg-test-git]$ git remote add origin https://github.com/llgeill/llg-centos-git--test.git [llg@localhost llg-test-git]$ git push -u origin master Username for 'https://github.com': 903857227@qq.com Password for 'https://903857227@qq.com@github.com': Counting objects: 12, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (12/12), 1.01 KiB | 0 bytes/s, done. Total 12 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'master' on GitHub by visiting: remote: https://github.com/llgeill/llg-centos-git--test/pull/new/master remote: To https://github.com/llgeill/llg-centos-git--test.git * [new branch] master -> master 分支 master 設置為跟蹤來自 origin 的遠程分支 master。?
?
發現文件已經推送完畢??
Git遠程倉庫的優點?
- 要關聯一個遠程庫,使用命令git remote add origin git@server-name:path/repo-name.git;
- 關聯后,使用命令git push -u origin master第一次推送master分支的所有內容;
- 此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
- 分布式版本系統的最大好處之一是在本地工作完全不需要考慮遠程庫的存在,也就是有沒有聯網都可以正常工作,而SVN在沒有聯網的時候是拒絕干活的!當有網絡的時候,再把本地提交推送一下就完成了同步,真是太方便了!
12.從遠程倉庫克隆一個已有倉庫
首先我們從github上創建一個遠程倉庫
首先在github上找到可以克隆的地址
下一步是用命令git clone克隆一個本地庫,我們可以發現項目已經克隆下來
?
[llg@localhost 桌面]$ git clone git@github.com:llgeill/llg-centos-git-test-1.git 正克隆到 'llg-centos-git-test-1'... The authenticity of host 'github.com (192.30.253.112)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. RSA key fingerprint is MD5:16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'github.com,192.30.253.112' (RSA) to the list of known hosts. remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 接收對象中: 100% (3/3), done. [llg@localhost 桌面]$ ls -al 總用量 40 drwsrwsrwt. 14 llg llg 4096 9月 15 21:10 . drwsrwsrwt. 39 llg llg 4096 9月 15 20:39 .. drwxr-xr-x 11 llg llg 196 9月 2 17:11 1-Brixton版教程示例 drwxr-xr-x 34 llg llg 4096 9月 2 15:57 2-Dalston版教程示例 -rw-r--r-- 1 root root 12288 4月 23 20:58 .android-studio.desktop.swp drwxrwxr-x 10 llg llg 302 9月 13 22:23 dist -rwsrwsrwt 1 llg llg 1968 5月 2 08:27 .keystore drwxrwsr-x 5 llg llg 118 9月 14 08:38 llg drwxrwsr-x 3 llg llg 35 9月 15 21:10 llg-centos-git-test-1 drwxrwsr-x 3 llg llg 80 9月 15 20:01 llg-test-git drwxrwsr-x 5 llg llg 131 9月 7 20:13 llg-user-gateway drwxrwsr-x 6 llg llg 151 9月 14 08:51 llg-web-springboot-class drwxr-xr-x 39 llg llg 4096 9月 2 19:41 SpringCloudBook-master drwx------ 14 llg llg 315 9月 3 07:46 spring-cloud-llg drwxrwsr-x 5 llg llg 61 9月 5 18:53 untitled drwxr-xr-x 12 llg llg 4096 6月 29 2016 計算機組成原理201407?
測試從本地倉庫推送回遠程倉庫
[llg@localhost llg-centos-git-test-1]$ vi llg.txt [llg@localhost llg-centos-git-test-1]$ git add llg.txt [llg@localhost llg-centos-git-test-1]$ git commit -m "測試" [master 4bdb6c4] 測試 1 file changed, 1 insertion(+) create mode 100644 llg.txt [llg@localhost llg-centos-git-test-1]$ git status # 位于分支 master # 您的分支領先 'origin/master' 共 1 個提交。 # (使用 "git push" 來發布您的本地提交) # 無文件要提交,干凈的工作區 [llg@localhost llg-centos-git-test-1]$ git push -u origin masterWarning: Permanently added the RSA host key for IP address '192.30.253.113' to the list of known hosts. Counting objects: 4, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 274 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:llgeill/llg-centos-git-test-1.git 07b4bcc..4bdb6c4 master -> master 分支 master 設置為跟蹤來自 origin 的遠程分支 master。?
13.Git的分支管理
分支就是科幻電影里面的平行宇宙,當你正在電腦前努力學習Git的時候,另一個你正在另一個平行宇宙里努力學習SVN。
如果兩個平行宇宙互不干擾,那對現在的你也沒啥影響。不過,在某個時間點,兩個平行宇宙合并了,結果,你既學會了Git又學會了SVN!
分支在實際中有什么用呢?假設你準備開發一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由于代碼還沒寫完,不完整的代碼庫會導致別人不能干活了。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。
現在有了分支,就不用怕了。你創建了一個屬于你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到開發完畢后,再一次性合并到原來的分支上,這樣,既安全,又不影響別人工作。
其他版本控制系統如SVN等都有分支管理,但是用過之后你會發現,這些版本控制系統創建和切換分支比蝸牛還慢,簡直讓人無法忍受,結果分支功能成了擺設,大家都不去用。
但Git的分支是與眾不同的,無論創建、切換和刪除分支,Git在1秒鐘之內就能完成!無論你的版本庫是1個文件還是1萬個文件。
14.創建與合并分支管理
在版本回退里,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git里,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是當前分支。
原理解析
一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:
?
創建新分支?
當我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上
git checkout命令加上-b參數表示創建分支并切換分支
?
[llg@localhost llg-test-git]$ git checkout -b dev 切換到一個新分支 'dev' [llg@localhost llg-test-git]$?
上面的命令等價與下面兩條命令的結合
?
[llg@localhost llg-test-git]$ git branch dev [llg@localhost llg-test-git]$ git checkout dev 切換到分支 'dev'?
我們可以使用git branch 查看當前分支的情況?
?
[llg@localhost llg-test-git]$ git branch * dev master?
?你看,Git創建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區的文件都沒有任何變化!
新分支上提交版本庫
不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變
?
[llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ git add one.txt [llg@localhost llg-test-git]$ git commit -m "在分支上提交版本信息" [dev 8c8117b] 在分支上提交版本信息 1 file changed, 2 insertions(+) [llg@localhost llg-test-git]$ git status # 位于分支 dev 無文件要提交,干凈的工作區 [llg@localhost llg-test-git]$?
當我們重新切換到master分支時候發現one.txt根本沒有變化,因為我們commit的是dev分支
?
[llg@localhost llg-test-git]$ git checkout master 切換到分支 'master' [llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ git checkout dev 切換到分支 'dev' [llg@localhost llg-test-git]$ vi one.txt?
合并分支
假如我們在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合并?。注意下面的Fast-forwar 信息,Git告訴我們這次合并是“快進模式”,也就是直接把master指向dev的當前提交,所以合并速度非常快。當然,也不是每次合并都能Fast-forward,我們后面會講其他方式的合并。
?
[llg@localhost llg-test-git]$ git checkout master 切換到分支 'master' [llg@localhost llg-test-git]$ git merge dev 更新 3956cc3..8c8117b Fast-forward one.txt | 2 ++ 1 file changed, 2 insertions(+)?
刪除分支
合并完分支后,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉后,我們就剩下了一條master分支
?
[llg@localhost llg-test-git]$ git branch dev * master [llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ git branch -d dev 已刪除分支 dev(曾為 8c8117b)。 [llg@localhost llg-test-git]$ git branch * master?
?小結
因為創建、合并和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合并后再刪掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全。
- 查看分支:git branch
- 創建分支:git branch <name>
- 切換分支:git checkout <name>
- 創建+切換分支:git checkout -b <name>
- 合并某分支到當前分支:git merge <name>
- 刪除分支:git branch -d <name>
?
15.解決分支的沖突問題
首先創建一個分支并且提交了一次版本倉庫,主分支也提交了一次版本倉庫,如圖所示
這種情況下,Git無法執行“快速合并”,只能試圖把各自的修改合并起來,但這種合并就可能會有沖突
?
[llg@localhost llg-test-git]$ git merge fantasy 自動合并 dele 沖突(內容):合并沖突于 dele 自動合并失敗,修正沖突然后提交修正的結果。?
果然沖突了!Git告訴我們,readme.txt文件存在沖突,必須手動解決沖突后再提交。git status也可以告訴我們沖突的文件
?
[llg@localhost llg-test-git]$ git status # 位于分支 master # 您的分支領先 'origin/master' 共 7 個提交。 # (使用 "git push" 來發布您的本地提交) # # 您有尚未合并的路徑。 # (解決沖突并運行 "git commit") # # 未合并的路徑: # (使用 "git add <file>..." 標記解決方案) # # 雙方修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")?
手工修改沖突?
我們可以直接查看dele的內容,Git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容
我們手動修改沖突位置,之后提交文件到暫存區并且commit
?
[llg@localhost llg-test-git]$ git add dele [llg@localhost llg-test-git]$ git commit -m "cscs" [master b1dd3cc] cscs?
現在,master分支和feature1分支變成了下圖所示
查看分支合并情況?
用帶參數的git log --graph?也可以看到分支的合并情況
?
* commit b1dd3cc1e90369e4fe2fc93f5a75b46a8918973f |\ Merge: 478f0bc 43149ab | | Author: llg <903857227@qq.com> | | Date: Sun Sep 16 15:10:03 2018 +0800 | | | | cscs | | | * commit 43149abf7f5724e74f64b394521778ceaded39b1 | | Author: llg <903857227@qq.com> | | Date: Sun Sep 16 15:01:24 2018 +0800 | | | | ccc | | * | commit 478f0bc2b144ac6fce457e05c9e596e314bd1f11 | | Author: llg <903857227@qq.com> | | Date: Sun Sep 16 15:02:12 2018 +0800 | | | | ss | | * | commit a22d668c065ad42c38232850fad8082250aa63a2 |\ \ Merge: 57e8b85 1575642 | |/ Author: llg <903857227@qq.com> | | Date: Sun Sep 16 14:55:13 2018 +0800 | | | | Merge branch 'fantasy' | | | | ceshi | |?
?
16.分支管理策略
通常,合并分支時,如果可能,Git會用Fast forward模式,但這種模式下,刪除分支后,會丟掉分支信息。
如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
下面我們實戰一下--no-ff(禁用Fast forward)方式的git merge,一些前提條件略過??
?
[llg@localhost llg-test-git]$ git merge --no-ff -m "merge with no-ff" dev Merge made by the 'recursive' strategy. dele | 1 + 1 file changed, 1 insertion(+) 合并分支時,加上--no-ff參數就可以用普通模式合并,合并后的歷史有分支,能看出來曾經做過合并,而fast forward合并就看不出來曾經做過合并。 [llg@localhost llg-test-git]$ git log --graph --pretty=oneline --abbrev-commit * 3a5456c merge with no-ff |\ | * b68ba37 add merge |/ * 0ed0fe3 測試 * b1dd3cc cscs |\ | * 43149ab ccc * | 478f0bc ss * | a22d668 Merge branch 'fantasy' |\ \ | |/ | * 1575642 xx * | 57e8b85 Merge branch 'fantasy' |\ \ | |/ | * 87bc5d7 ss * | 709521d 版本沖突 * | 8c8117b 在分支上提交版本信息 |/ * 3956cc3 測試兩個文件提交留一個文件不提交 * 44449dc 這次測試一次提交三個文件 * abde635 git練習測試?
我們來看看不適用no-ff下的策略,發現會直接合并,分支信息不能再找到
?
[llg@localhost llg-test-git]$ git log --graph --pretty=oneline --abbrev-commit * 8699b49 dele * 3a5456c merge with no-ff |\ | * b68ba37 add merge |/ * 0ed0fe3 測試 * b1dd3cc cscs |\ | * 43149ab ccc * | 478f0bc ss * | a22d668 Merge branch 'fantasy' |\ \ | |/ | * 1575642 xx * | 57e8b85 Merge branch 'fantasy' |\ \ | |/ | * 87bc5d7 ss * | 709521d 版本沖突 * | 8c8117b 在分支上提交版本信息 |/ * 3956cc3 測試兩個文件提交留一個文件不提交 * 44449dc 這次測試一次提交三個文件 * abde635 git練習測試? 可以看到,不使用Fast forward模式,merge后就像這樣。
?
分支策略
在實際開發中,我們應該按照幾個基本原則進行分支管理:
首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev分支合并到master上,在master分支發布1.0版本;
你和你的小伙伴們每個人都在dev分支上干活,每個人都有自己的分支,時不時地往dev分支上合并就可以了。
所以,團隊合作的分支看起來就像這樣:
?
17.解決Bug的臨時分支?
軟件開發中,bug就像家常便飯一樣。有了bug就需要修復,在Git中,由于分支是如此的強大,所以,每個bug都可以通過一個新的臨時分支來修復,修復后,合并分支,然后將臨時分支刪除。
當你接到一個修復一個代號101的bug的任務時,很自然地,你想創建一個分支issue-101來修復它,但是,等等,當前正在dev上進行的工作還沒有提交:
?
[llg@localhost llg-test-git]$ git status # 位于分支 master # 您的分支領先 'origin/master' 共 15 個提交。 # (使用 "git push" 來發布您的本地提交) # # 尚未暫存以備提交的變更: # (使用 "git add <file>..." 更新要提交的內容) # (使用 "git checkout -- <file>..." 丟棄工作區的改動) # # 修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")?
?
并不是你不想提交,而是工作只進行到一半,還沒法提交,預計完成還需1天時間。但是,必須在兩個小時內修復該bug,怎么辦?
封鎖現場
幸好,Git還提供了一個stash功能,可以把當前工作現場“儲藏”起來,等以后恢復現場后繼續工作,再通過git status 查看發現工作區已經干凈。?
?
[llg@localhost llg-test-git]$ git stash Saved working directory and index state WIP on master: 7dec792 ss HEAD 現在位于 7dec792 ss [llg@localhost llg-test-git]$ git status # 位于分支 master # 您的分支領先 'origin/master' 共 15 個提交。 # (使用 "git push" 來發布您的本地提交) # 無文件要提交,干凈的工作區 首先確定要在哪個分支上修復bug,假定需要在master分支上修復,就從master創建臨時分支?
?
[llg@localhost llg-test-git]$ git checkout -b issue-101 切換到一個新分支 'issue-101'?
現在修復bug,我們通過修改文件模擬這一個過程
?
[llg@localhost llg-test-git]$ vi delete.txt [llg@localhost llg-test-git]$ git add delete.txt [llg@localhost llg-test-git]$ git commit -m "ss" [issue-101 e0b9de9] ss 1 file changed, 1 insertion(+)?
修復完成后,切換到master分支,并完成合并,最后刪除issue-101分支
?
[llg@localhost llg-test-git]$ git checkout master 切換到分支 'master' 您的分支領先 'origin/master' 共 15 個提交。 (使用 "git push" 來發布您的本地提交) [llg@localhost llg-test-git]$ git merge --no-ff -m "merge bug fix 101" issue-101 Merge made by the 'recursive' strategy. delete.txt | 1 + 1 file changed, 1 insertion(+)?
還原現場?
太棒了,原計劃兩個小時的bug修復只花了5分鐘!
現在,是時候接著回到dev分支干活了,切換到dev分支然后用git stash list命令看看
?
[llg@localhost llg-test-git]$ git stash list stash@{0}: WIP on master: 7dec792 ss?
工作現場還在,Git把stash內容存在某個地方了,但是需要恢復一下,有兩個辦法:
一是用git stash apply恢復,但是恢復后,stash內容并不刪除,你需要用git stash drop來刪除;
另一種方式是用git stash pop,恢復的同時把stash內容也刪了:?
?
[llg@localhost llg-test-git]$ git stash pop # 位于分支 master # 您的分支領先 'origin/master' 共 17 個提交。 # (使用 "git push" 來發布您的本地提交) # # 尚未暫存以備提交的變更: # (使用 "git add <file>..." 更新要提交的內容) # (使用 "git checkout -- <file>..." 丟棄工作區的改動) # # 修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") 丟棄了 refs/stash@{0} (fa2bbbe411ba20c94501e912ec120fa944c06b92) 再用git stash list查看,就看不到任何stash內容了??
?
[llg@localhost llg-test-git]$ git stash list [llg@localhost llg-test-git]$?
小結
- 修復bug時,我們會通過創建新的bug分支進行修復,然后合并,最后刪除;
- 當手頭工作沒有完成時,先把工作現場git stash一下,然后去修復bug,修復后,再git stash pop,回到工作現場
?
18.強制刪除分支
當需要開發一個新功能的時候,最好創建一個新的分支進行代碼編輯,但是因為一些情況不需要這個分支了,但是這個分支從來沒有合并過,所以要使用 git branch -D?的方式強制刪除
?
[llg@localhost llg-test-git]$ vi feichuan.txt.swp [llg@localhost llg-test-git]$ git add feichuan.txt.swp [llg@localhost llg-test-git]$ git commit -m "ss" [feature-vulcan 4cd8736] ss 1 file changed, 1 insertion(+) create mode 100644 feichuan.txt.swp [llg@localhost llg-test-git]$ git checkout master M dele 切換到分支 'master' 您的分支領先 'origin/master' 共 17 個提交。 (使用 "git push" 來發布您的本地提交) [llg@localhost llg-test-git]$ git branch -D feature-vulcan 已刪除分支 feature-vulcan(曾為 4cd8736)。?
19.rebease整理分叉歷史
在上一節我們看到了,多人在同一個分支上協作時,很容易出現沖突。即使沒有沖突,后push的童鞋不得不先pull,在本地合并,然后才能push成功。
每次合并再push后,分支變成了這樣
總之看上去很亂,有強迫癥的童鞋會問:為什么Git的提交歷史不能是一條干凈的直線?
其實是可以做到的!
Git有一種稱為rebase的操作,有人把它翻譯成“變基”。
先不要隨意展開想象。我們還是從實際問題出發,看看怎么把分叉的提交變成直線。
在和遠程分支同步后,我們對hello.py這個文件做了兩次提交。用git log命令看看:
?
$ git log --graph --pretty=oneline --abbrev-commit * 582d922 (HEAD -> master) add author * 8875536 add comment * d1be385 (origin/master) init hello * e5e69f1 Merge branch 'dev' |\ | * 57c53ab (origin/dev, dev) fix env conflict | |\ | | * 7a5e5dd add env | * | 7bd91f1 add new env ...?
注意到Git用(HEAD -> master)和(origin/master)標識出當前分支的HEAD和遠程origin的位置分別是582d922 add author和d1be385 init hello,本地分支比遠程分支快兩個提交。
現在我們嘗試推送本地分支:
?
$ git push origin master To github.com:michaelliao/learngit.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@github.com:michaelliao/learngit.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.?
很不幸,失敗了,這說明有人先于我們推送了遠程分支。按照經驗,先pull一下:?
?
$ git pull remote: Counting objects: 3, done. remote: Compressing objects: 100% (1/1), done. remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0 Unpacking objects: 100% (3/3), done. From github.com:michaelliao/learngit d1be385..f005ed4 master -> origin/master * [new tag] v1.0 -> v1.0 Auto-merging hello.py Merge made by the 'recursive' strategy. hello.py | 1 + 1 file changed, 1 insertion(+)?
再用git status看看狀態:
?
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits)nothing to commit, working tree clean?
加上剛才合并的提交,現在我們本地分支比遠程分支超前3個提交。
用git log看看:
?
$ git log --graph --pretty=oneline --abbrev-commit * e0ea545 (HEAD -> master) Merge branch 'master' of github.com:michaelliao/learngit |\ | * f005ed4 (origin/master) set exit=1 * | 582d922 add author * | 8875536 add comment |/ * d1be385 init hello ...?
對強迫癥童鞋來說,現在事情有點不對頭,提交歷史分叉了。如果現在把本地分支push到遠程,有沒有問題?
有!
什么問題?
不好看!
有沒有解決方法?
有!
這個時候,rebase就派上了用場。我們輸入命令git rebase試試:
?
$ git rebase First, rewinding head to replay your work on top of it... Applying: add comment Using index info to reconstruct a base tree... M hello.py Falling back to patching base and 3-way merge.. Auto-merging hello.py Applying: add author Using index info to reconstruct a base tree... M hello.py Falling back to patching base and 3-way merge... Auto-merging hello.py?
?
?
?
輸出了一大堆操作,到底是啥效果?再用git log看看:
?
?
$ git log --graph --pretty=oneline --abbrev-commit * 7e61ed4 (HEAD -> master) add author * 3611cfe add comment * f005ed4 (origin/master) set exit=1 * d1be385 init hello?
原本分叉的提交現在變成一條直線了!這種神奇的操作是怎么實現的?其實原理非常簡單。我們注意觀察,發現Git把我們本地的提交“挪動”了位置,放到了f005ed4 (origin/master) set exit=1之后,這樣,整個提交歷史就成了一條直線。rebase操作前后,最終的提交內容是一致的,但是,我們本地的commit修改內容已經變化了,它們的修改不再基于d1be385 init hello,而是基于f005ed4 (origin/master) set exit=1,但最后的提交7e61ed4內容是一致的。
這就是rebase操作的特點:把分叉的提交歷史“整理”成一條直線,看上去更直觀。缺點是本地的分叉提交已經被修改過了。
最后,通過push操作把本地分支推送到遠程:
?
Mac:~/learngit michael$ git push origin master Counting objects: 6, done. Delta compression using up to 4 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done. Total 6 (delta 2), reused 0 (delta 0) remote: Resolving deltas: 100% (2/2), completed with 1 local object. To github.com:michaelliao/learngit.git f005ed4..7e61ed4 master -> master?
再用git log看看效果:
$ git log --graph --pretty=oneline --abbrev-commit * 7e61ed4 (HEAD -> master, origin/master) add author * 3611cfe add comment * f005ed4 set exit=1 * d1be385 init hello?
?
遠程分支的提交歷史也是一條直線。
?
總結?
-
rebase操作可以把本地未push的分叉提交歷史整理成直線;
-
rebase的目的是使得我們在查看歷史提交的變化時更容易,因為分叉的提交需要三方對比。
20.什么是標簽管理
發布一個版本時,我們通常先在版本庫中打一個標簽(tag),這樣,就唯一確定了打標簽時刻的版本。將來無論什么時候,取某個標簽的版本,就是把那個打標簽的時刻的歷史版本取出來。所以,標簽也是版本庫的一個快照。
Git的標簽雖然是版本庫的快照,但其實它就是指向某個commit的指針(跟分支很像對不對?但是分支可以移動,標簽不能移動),所以,創建和刪除標簽都是瞬間完成的。
Git有commit,為什么還要引入tag?
“請把上周一的那個版本打包發布,commit號是6a5819e...”
“一串亂七八糟的數字不好找!”
如果換一個辦法:
“請把上周一的那個版本打包發布,版本號是v1.2”
“好的,按照tag v1.2查找commit就行!”
所以,tag就是一個讓人容易記住的有意義的名字,它跟某個commit綁在一起。
21.創建Git倉庫版本標簽
在Git中打標簽非常簡單,首先,切換到需要打標簽的分支上
?
[llg@localhost llg-test-git]$ git branch dev issue-101 * master [llg@localhost llg-test-git]$ git checkout dev 切換到分支 'dev'?
創建當前版本標簽
然后,敲命令git tag <name>就可以打一個新標簽
[llg@localhost llg-test-git]$ git tag v1.0創建其他版本標簽
默認標簽是打在最新提交的commit上的。有時候,如果忘了打標簽,比如,現在已經是周五了,但應該在周一打的標簽沒有打,怎么辦?
方法是找到歷史提交的commit id,然后打上就可以了:
?
[llg@localhost llg-test-git]$ git log --pretty=oneline --abbrev-commit 8699b49 dele 3a5456c merge with no-ff b68ba37 add merge 0ed0fe3 測試 b1dd3cc cscs 478f0bc ss 43149ab ccc a22d668 Merge branch 'fantasy' 1575642 xx 57e8b85 Merge branch 'fantasy' 709521d 版本沖突 87bc5d7 ss 8c8117b 在分支上提交版本信息 3956cc3 測試兩個文件提交留一個文件不提交 44449dc 這次測試一次提交三個文件 abde635 git練習測試 [llg@localhost llg-test-git]$ git tag v1.0 [llg@localhost llg-test-git]$ git tag v0.9 3a5456c [llg@localhost llg-test-git]$ git tag v0.9 v1.0?
查看所有標簽?
?
[llg@localhost llg-test-git]$ git tag v0.8 v0.9 v1.0?
查看標簽詳細信息
注意,標簽不是按時間順序列出,而是按字母排序的。可以用git show <tagname>查看標簽信息
[llg@localhost llg-test-git]$ git show v0.9 commit 3a5456c07d50d99437d01fbf538a41a4556d7504 Merge: 0ed0fe3 b68ba37 Author: llg <903857227@qq.com> Date: Sun Sep 16 16:04:56 2018 +0800merge with no-ff創建帶說明文字的標簽
還可以創建帶有說明的標簽,用-a指定標簽名,-m指定說明文字
?
[llg@localhost llg-test-git]$ git tag -a v0.8 -m "version 0.8 released" b68ba37 [llg@localhost llg-test-git]$ git show v0.8 tag v0.8 Tagger: llg <903857227@qq.com> Date: Mon Sep 17 09:31:27 2018 +0800version 0.8 releasedcommit b68ba371b04d5b7b6305454c160d7ef88178ea2d Author: llg <903857227@qq.com> Date: Sun Sep 16 16:04:19 2018 +0800add mergediff --git a/dele b/dele index 20c0828..a36d773 100644 --- a/dele +++ b/dele @@ -10,3 +10,4 @@ sad end1111111111111111111111 +2222222222222222222222?
22.操作標簽
刪除標簽
如果標簽打錯了,也可以刪除
?
[llg@localhost llg-test-git]$ git tag -d v0.8 已刪除 tag 'v0.8'(曾為 658e2ab)?
推送某個標簽到遠程倉庫?
因為創建的標簽都只存儲在本地,不會自動推送到遠程。所以,打錯的標簽可以在本地安全刪除。
如果要推送某個標簽到遠程,使用命令git push origin <tagname>
?
[llg@localhost llg-test-git]$ git push origin v1.0 Username for 'https://github.com': 903857227@qq.com Password for 'https://903857227@qq.com@github.com': Total 0 (delta 0), reused 0 (delta 0) To https://github.com/llgeill/llg-centos-git--test.git * [new tag] v1.0 -> v1.0?
?推送所有標簽到遠程倉庫?
或者,一次性推送全部尚未推送到遠程的本地標簽:
?
[llg@localhost llg-test-git]$ git push origin --tag Username for 'https://github.com': 903857227@qq.com Password for 'https://903857227@qq.com@github.com': Total 0 (delta 0), reused 0 (delta 0) To https://github.com/llgeill/llg-centos-git--test.git * [new tag] v0.9 -> v0.9?
刪除遠程標簽
首先在本地刪除標簽
?
[llg@localhost llg-test-git]$ git tag -d v0.8 已刪除 tag 'v0.9'(曾為 3a5456c)?
然后,從遠程刪除。刪除命令是?git push origin :refs/tags/xxx
?
[llg@localhost llg-test-git]$ git push origin :refs/tags/v0.8 Username for 'https://github.com': 903857227@qq.com Password for 'https://903857227@qq.com@github.com': To https://github.com/llgeill/llg-centos-git--test.git - [deleted] v0.8?
最后去官網github上查看tag是否被刪除,發現確實被刪除了
?
23.如何使用Github
我們一直用GitHub作為免費的遠程倉庫,如果是個人的開源項目,放到GitHub上是完全沒有問題的。其實GitHub還是一個開源協作社區,通過GitHub,既可以讓別人參與你的開源項目,也可以參與別人的開源項目。
在GitHub出現以前,開源項目開源容易,但讓廣大人民群眾參與進來比較困難,因為要參與,就要提交代碼,而給每個想提交代碼的群眾都開一個賬號那是不現實的,因此,群眾也僅限于報個bug,即使能改掉bug,也只能把diff文件用郵件發過去,很不方便。
但是在GitHub上,利用Git極其強大的克隆和分支功能,廣大人民群眾真正可以第一次自由參與各種開源項目了。
參與別人的開源項目?
如何參與一個開源項目呢?比如人氣極高的bootstrap項目,這是一個非常強大的CSS框架,你可以訪問它的項目主頁https://github.com/twbs/bootstrap,點“Fork”就在自己的賬號下克隆了一個bootstrap倉庫,然后,從自己的賬號下clone:
??
?
[llg@localhost 桌面]$ git clone git@github.com:llgeill/bootstrap.git 正克隆到 'bootstrap'... remote: Counting objects: 126027, done. remote: Compressing objects: 100% (40/40), done. remote: Total 126027 (delta 44), reused 45 (delta 35), pack-reused 125952 接收對象中: 100% (126027/126027), 123.12 MiB | 4.97 MiB/s, done. 處理 delta 中: 100% (83420/83420), done.?
一定要從自己的賬號下clone倉庫,這樣你才能推送修改。如果從bootstrap的作者的倉庫地址git@github.com:twbs/bootstrap.git克隆,因為沒有權限,你將不能推送修改。
Bootstrap的官方倉庫twbs/bootstrap、你在GitHub上克隆的倉庫my/bootstrap,以及你自己克隆到本地電腦的倉庫,他們的關系就像下圖顯示的那樣:
如果你想修復bootstrap的一個bug,或者新增一個功能,立刻就可以開始干活,干完后,往自己的倉庫推送。
如果你希望bootstrap的官方庫能接受你的修改,你就可以在GitHub上發起一個pull request。當然,對方是否接受你的pull request就不一定了。
?
23.如何使用碼云
使用GitHub時,國內的用戶經常遇到的問題是訪問速度太慢,有時候還會出現無法連接的情況(原因你懂的)。
如果我們希望體驗Git飛一般的速度,可以使用國內的Git托管服務——碼云(gitee.com)。
和GitHub相比,碼云也提供免費的Git倉庫。此外,還集成了代碼質量檢測、項目演示等功能。對于團隊協作開發,碼云還提供了項目管理、代碼托管、文檔管理的服務,5人以下小團隊免費。
?碼云的免費版本也提供私有庫功能,只是有5人的成員上限。
使用碼云和使用GitHub類似,我們在碼云上注冊賬號并登錄后,需要先上傳自己的SSH公鑰。選擇右上角用戶頭像 -> 菜單“修改資料”,然后選擇“SSH公鑰”,填寫一個便于識別的標題,然后把用戶主目錄下的.ssh/id_rsa.pub文件的內容粘貼進去:
?
已有本地倉庫關聯遠程倉庫?
如果我們已經有了一個本地的git倉庫(例如,一個名為learngit的本地庫),如何把它關聯到碼云的遠程庫上呢?
首先,我們在碼云上創建一個新的項目,選擇右上角用戶頭像 -> 菜單“控制面板”,然后點擊“創建項目”:
創建遠程倉庫
關聯遠程倉庫
由于剛才已經關聯了github,如果用同一個tag orgin的話將會關聯失敗,所以我用了orgins 關聯碼云
?
[llg@localhost llg-test-git]$ git remote add origin git@gitee.com:eill/llg-test-git.git fatal: 遠程 origin 已經存在。 [llg@localhost llg-test-git]$ git push -u origin master Username for 'https://github.com': 903857227@qq .com Password for 'https://903857227@qq.com@github.com': 分支 master 設置為跟蹤來自 origin 的遠程分支 master。 Everything up-to-date這樣一來,我們的本地庫就可以同時與多個遠程庫互相同步:
刪除關聯
[llg@localhost llg-test-git]$ git remote rm origin [llg@localhost llg-test-git]$ git remote -v origins git@gitee.com:eill/centos-git-gitee-test.git (fetch) origins git@gitee.com:eill/centos-git-gitee-test.git (push)碼云也同樣提供了Pull request功能,可以讓其他小伙伴參與到開源項目中來。你可以通過Fork我的倉庫:https://gitee.com/liaoxuefeng/learngit,創建一個your-gitee-id.txt的文本文件, 寫點自己學習Git的心得,然后推送一個pull request給我,這個倉庫會在碼云和GitHub做雙向同步。
24.自定義Git配置
在安裝Git一節中,我們已經配置了user.name和user.email,實際上,Git還有很多可配置項。
開啟命令行顏色
比如,讓Git顯示顏色,會讓命令輸出看起來更醒目
[忽略特殊文件
有些時候,你必須把某些文件放到Git工作目錄中,但又不能提交它們,比如保存了數據庫密碼的配置文件啦,等等,每次git status都會顯示Untracked files ...,有強迫癥的童鞋心里肯定不爽。
好在Git考慮到了大家的感受,這個問題解決起來也很簡單,在Git工作區的根目錄下創建一個特殊的.gitignore文件,然后把要忽略的文件名填進去,Git就會自動忽略這些文件。
不需要從頭寫.gitignore文件,GitHub已經為我們準備了各種配置文件,只需要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:https://github.com/github/gitignore
忽略文件的原則是:
舉個例子:
假設你在Windows下進行Python開發,Windows會自動在有圖片的目錄下生成隱藏的縮略圖文件,如果有自定義目錄,目錄下就會有Desktop.ini文件,因此你需要忽略Windows自動生成的垃圾文件:
# Windows: Thumbs.db ehthumbs.db Desktop.ini然后,繼續忽略Python編譯產生的.pyc、.pyo、dist等文件或目錄:
# Python: *.py[cod] *.so *.egg *.egg-info dist build加上你自己定義的文件,最終得到一個完整的.gitignore文件,內容如下:
# Windows: Thumbs.db ehthumbs.db Desktop.ini# Python: *.py[cod] *.so *.egg *.egg-info dist build# My configurations: db.ini deploy_key_rsa最后一步就是把.gitignore也提交到Git,就完成了!當然檢驗.gitignore的標準是git status命令是不是說working directory clean。
使用Windows的童鞋注意了,如果你在資源管理器里新建一個.gitignore文件,它會非常弱智地提示你必須輸入文件名,但是在文本編輯器里“保存”或者“另存為”就可以把文件保存為.gitignore了。
有些時候,你想添加一個文件到Git,但發現添加不了,原因是這個文件被.gitignore忽略了:
$ git add App.class The following paths are ignored by one of your .gitignore files: App.class Use -f if you really want to add them.如果你確實想添加該文件,可以用-f強制添加到Git:
$ git add -f App.class或者你發現,可能是.gitignore寫得有問題,需要找出來到底哪個規則寫錯了,可以用git check-ignore命令檢查:
$ git check-ignore -v App.class .gitignore:3:*.class App.classGit會告訴我們,.gitignore的第3行規則忽略了該文件,于是我們就可以知道應該修訂哪個規則。
小結
-
忽略某些文件時,需要編寫.gitignore;
-
.gitignore文件本身要放到版本庫里,并且可以對.gitignore做版本管理!
配置別名
有沒有經常敲錯命令?比如git status?status這個單詞真心不好記。
如果敲git st就表示git status那就簡單多了,當然這種偷懶的辦法我們是極力贊成的。
我們只需要敲一行命令,告訴Git,以后st就表示status(這個功能在linux上也有)
[llg@localhost llg-test-git]$ git config --global alias.st status [llg@localhost llg-test-git]$ git st # 位于分支 dev # 尚未暫存以備提交的變更: # (使用 "git add <file>..." 更新要提交的內容) # (使用 "git checkout -- <file>..." 丟棄工作區的改動) # # 修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")配置文件
配置Git的時候,加上--global是針對當前用戶起作用的,如果不加,那只針對當前的倉庫起作用。
配置文件放哪了?每個倉庫的Git配置文件都放在.git/config文件中:
$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = git@github.com:michaelliao/learngit.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master [alias] last = log -1別名就在[alias]后面,要刪除別名,直接把對應的行刪掉即可。
而當前用戶的Git配置文件放在用戶主目錄下的一個隱藏文件.gitconfig中:?
$ cat .gitconfig [alias] co = checkout ci = commit br = branch st = status [user] name = Your Name email = your@email.com 配置別名也可以直接修改這個文件,如果改錯了,可以刪掉文件重新通過命令配置。小結
給Git配置好別名,就可以輸入命令時偷個懶。我們鼓勵偷懶。
25.搭建Git服務器
在遠程倉庫一節中,我們講了遠程倉庫實際上和本地倉庫沒啥不同,純粹為了7x24小時開機并交換大家的修改。
GitHub就是一個免費托管開源代碼的遠程倉庫。但是對于某些視源代碼如生命的商業公司來說,既不想公開源代碼,又舍不得給GitHub交保護費,那就只能自己搭建一臺Git服務器作為私有倉庫使用。
搭建Git服務器需要準備一臺運行Linux的機器,強烈推薦用Ubuntu或Debian,這樣,通過幾條簡單的apt命令就可以完成安裝。
假設你已經有sudo權限的用戶賬號,下面,正式開始安裝。(在這里我使用的是Centos)
第一步,安裝git
[llg@localhost llg-test-git]$ sudo yum install git第二步,創建一個git用戶,用來運行git服務
[llg@localhost llg-test-git]$ sudo adduser git第三步,創建證書登錄:
收集所有需要登錄的用戶的公鑰,就是他們自己的id_rsa.pub文件,把所有公鑰導入到/home/git/.ssh/authorized_keys文件里,一行一個。
第四步,初始化Git倉庫:
先選定一個目錄作為Git倉庫,假定是/srv/sample.git,在/srv目錄下輸入命令:
[llg@localhost llg-test-git]$ sudo git init --bare sample.git 初始化空的 Git 版本庫于 /home/llg/桌面/llg-test-git/sample.git/Git就會創建一個裸倉庫,裸倉庫沒有工作區,因為服務器上的Git倉庫純粹是為了共享,所以不讓用戶直接登錄到服務器上去改工作區,并且服務器上的Git倉庫通常都以.git結尾。然后,把owner改為git(用戶權限)
[llg@localhost llg-test-git]$ sudo chown -R git:git sample.git第五步,禁用shell登錄:
出于安全考慮,第二步創建的git用戶不允許登錄shell,這可以通過編輯/etc/passwd文件完成。找到類似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash改為:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell這樣,git用戶可以正常通過ssh使用git,但無法登錄shell,因為我們為git用戶指定的git-shell每次一登錄就自動退出。
第六步,克隆遠程倉庫:
現在,可以通過git clone命令克隆遠程倉庫了,在各自的電腦上運行:
[llg@localhost llg-test-git]$ git clone git@127.0.0.1:/llg-test-git/sample.git/剩下的推送就簡單了。
管理公鑰
如果團隊很小,把每個人的公鑰收集起來放到服務器的/home/git/.ssh/authorized_keys文件里就是可行的。如果團隊有幾百號人,就沒法這么玩了,這時,可以用Gitosis來管理公鑰。
這里我們不介紹怎么玩Gitosis了,幾百號人的團隊基本都在500強了,相信找個高水平的Linux管理員問題不大。
管理權限
有很多不但視源代碼如生命,而且視員工為竊賊的公司,會在版本控制系統里設置一套完善的權限控制,每個人是否有讀寫權限會精確到每個分支甚至每個目錄下。因為Git是為Linux源代碼托管而開發的,所以Git也繼承了開源社區的精神,不支持權限控制。不過,因為Git支持鉤子(hook),所以,可以在服務器端編寫一系列腳本來控制提交等操作,達到權限控制的目的。Gitolite就是這個工具。
這里我們也不介紹Gitolite了,不要把有限的生命浪費到權限斗爭中。
小結
-
搭建Git服務器非常簡單,通常10分鐘即可完成;
-
要方便管理公鑰,用Gitosis;
-
要像SVN那樣變態地控制權限,用Gitolite。
26.總結
經過兩天的學習,站在巨人的肩膀上,我把命令都執行了一遍,大概有了個印象。
Git雖然極其強大,命令繁多,但常用的就那么十來個,掌握好這十幾個常用命令,你已經可以得心應手地使用Git了。
友情附贈國外網友制作的Git Cheat Sheet,建議打印出來備用:
Git Cheat Sheet
Git的官方網站:http://git-scm.com
?
?
?
?
?
?
作者:廖雪峰
鏈接:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
來源:廖雪峰的官方網站
?
轉載于:https://www.cnblogs.com/liliguang/p/9661646.html
總結
以上是生活随笔為你收集整理的分布式版本控制系统Git的安装与使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rhel7+apache+c cgi+动
- 下一篇: LUOGU 9月 月赛