本節內容html
假設你在的公司要上線一個新功能,大家開發團隊爲實現這個新功能,寫了大約5000行代碼,上線沒2天,就發現這個功能用戶並不喜歡,你老闆讓你去掉這個功能,你怎麼辦?你說簡單,直接把5000行代碼去掉就好了,可是個人親,說的簡單,你的這個功能寫了3周時間,但你還能記得你是新增長了哪5000行代碼麼?因此你急須要一個工具,能幫你記錄每次對代碼作了哪些修改,而且能夠輕易的把代碼回滾到歷史上的某個狀態。 這個神奇的工具就叫作版本控制。 node
版本控制工具主要實現2個功能:linux
在開發中,這是剛需,必須容許能夠很容易對產品的版本進行任意回滾,版本控制工具實現這個功能的原理簡單來說,就是你每修改一次代碼,它就幫你作一次快照git
一個複雜點的軟件,每每不是一個開發人員能夠搞定的,公司爲加快產品開發速度,會招聘一堆跟你同樣的開發人員開發這個產品,拿微信來舉例,如今假設3我的一塊兒開發微信,A開發聯繫人功能,B開發發文字、圖片、語音通信功能,C開發視頻通話功能, B和C的功能都是要基於通信錄的,你說簡單,直接把A開發的代碼copy過來,在它的基礎上開發就行了,能夠,可是你在他的代碼基礎上開發了2周後,這期間A沒閒着,對通信錄代碼做了更新,此時怎麼辦?你和他的代碼不一致了,此時咱們知道,你確定要再把A的新代碼拿過來替換掉你手上的舊通信錄功能代碼, 如今人少,3我的之間溝通很簡單,但想一想,若是團隊變成30我的呢?來回這樣copy代碼,很快就亂了, 因此此時亟需一個工具,能確保一直存儲最新的代碼庫,全部人的代碼應該和最新的代碼庫保持一致github
一、VSS-- Visual Source Safe
此工具是Microsoft提供的,是使用的至關廣泛的工具之一,他能夠與VS.net進行無縫集成,成爲了獨立開發人員和小型開發團隊所適合的工具,基本上Window平臺上開發的中小型企業,當規模較大後,其性能一般是沒法忍受的,對分支與並行開發支持的比較有限。web
二、CVS--Concurrent Versions System,
此工具是一個開源工具,與後面提到的SVN是同一個廠家:Collab.Net提供的。
CVS是源於unix的版本控制工具,對於CVS的安裝和使用最好對unix的系統有所瞭解能更容易學習,CVS的服務器管理須要進行各類命令行操做。目前,CVS的客戶端有winCVS的圖形化界面,服務器端也有CVSNT的版本,易用性正在提升。
此工具是至關著名,使用得至關普遍的版本控制工具之一,使用成熟的「Copy-Modify-Merge"開發模型,能夠大大的提升開發效率,適合於項目比較大,產品發佈頻繁,分支活動頻繁的中大型項目。sql
三、SVN --CollabNet Subversion
此工具是在CVS 的基礎上,由CollabNet提供開發的,也是開源工具,應用比較普遍。
他修正cvs的一些侷限性,適用範圍同cvs,目前有一些基於SVN的第三方工具,如TortoiseSVN,是其客戶端程序,使用的也至關普遍。在權限管理,分支合併等方面作的很出色,他能夠與Apache集成在一塊兒進行用戶認證。
不過在權限管理方面目前尚未個很好用的界面化工具,SVNManger對於已經使用SVN進行配置的項目來講,基本上是沒法應用的,但對於從頭開始的項目是能夠的,功能比較強大,可是搭建svnManger比較麻煩。
是一個跨平臺的軟件,支持大多數常見的操做系統。做爲一個開源的版本控制系統,Subversion 管理着隨時間改變的數據。 這些數據放置在一箇中央資料檔案庫中。 這個檔案庫很像一個普通的文件服務器, 不過它會記住每一次文件的變更。 這樣你就能夠把檔案恢復到舊的版本, 或是瀏覽文件的變更歷史。Subversion 是一個通用的系統, 可用來管理任何類型的文件, 其中包括了程序源碼。數據庫
4. GIT
由於最初是從Linux起家的,很是依賴文件系統的一些特性,這些在 Linux 下表現的很好,而 Windows 下特別糟糕Git 中文教程
Git是一個開源的分佈式版本控制系統,用以有效、高速的處理從很小到很是大的項目版本管理.
Git 是 Linus Torvalds 爲了幫助管理 Linux 內核開發而開發的一個開放源碼的版本控制軟件。
Torvalds 開始着手開發 Git 是爲了做爲一種過渡方案來替代 BitKeeper,後者以前一直是 Linux 內核開發人員在全球使用的主要源代碼工具。開放源碼社區中的有些人以爲 BitKeeper 的許可證並不適合開放源碼社區的工做,所以 Torvalds 決定着手研究許可證更爲靈活的版本控制系統。儘管最初 Git 的開發是爲了輔助 Linux 內核開發的過程,可是咱們已經發如今不少其餘自由軟件項目中也使用了 Git。例如 最近就遷移到 Git 上來了,不少 Freedesktop 的項目也遷移到了 Git 上。
bootstrap
五、BitKeeper
是由BitMover公司提供的,BitKeeper自稱是「分佈式」可擴縮SCM系統。
不是採用C/S結構,而是採用P2P結構來實現的,一樣支持變動任務,全部變動集的操做都是原子的,與svn,cvs一致。vim
不少人都知道,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網站上線了(github是一個基於git的代碼託管平臺,付費用戶能夠建私人倉庫,咱們通常的免費用戶只能使用公共倉庫,也就是代碼要公開。),它爲開源項目免費提供Git存儲,無數開源項目開始遷移至GitHub,包括jQuery,PHP,Ruby等等。
歷史就是這麼偶然,若是不是當年BitMover公司威脅Linux社區,可能如今咱們就沒有免費而超級好用的Git了。
今天,GitHub已經是:
要使用Git,第一步固然是安裝Git了。根據你當前使用的平臺來閱讀下面的文字:
首先,你能夠試着輸入git
,看看系統有沒有安裝Git:
$ git The program 'git' is currently not installed. You can install it by typing: sudo apt-get install git
像上面的命令,有不少Linux會友好地告訴你Git沒有安裝,還會告訴你如何安裝Git。
若是你碰巧用Debian或Ubuntu Linux,經過一條sudo apt-get install git
就能夠直接完成Git的安裝,很是簡單。
什麼是版本庫呢?版本庫又名倉庫,英文名repository,你能夠簡單理解成一個目錄,這個目錄裏面的全部文件均可以被Git管理起來,每一個文件的修改、刪除,Git都能跟蹤,以便任什麼時候刻均可以追蹤歷史,或者在未來某個時刻能夠「還原」。
因此,建立一個版本庫很是簡單,首先,選擇一個合適的地方,建立一個空目錄:
$ mkdir git_trainning $ cd git_trainning/ $ git init Initialized empty Git repository in /Users/alex/git_trainning/.git/
瞬間Git就把倉庫建好了,並且告訴你是一個空的倉庫(empty Git repository),細心的讀者能夠發現當前目錄下多了一個.git
的目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄裏面的文件,否則改亂了,就把Git倉庫給破壞了。
若是你沒有看到.git
目錄,那是由於這個目錄默認是隱藏的,用ls -ah
命令就能夠看見。
首先這裏再明確一下,全部的版本控制系統,其實只能跟蹤文本文件的改動,好比TXT文件,網頁,全部的程序代碼等等,Git也不例外。版本控制系統能夠告訴你每次的改動,好比在第5行加了一個單詞「Linux」,在第8行刪了一個單詞「Windows」。而圖片、視頻這些二進制文件,雖然也能由版本控制系統管理,但無法跟蹤文件的變化,只能把二進制文件每次改動串起來,也就是隻知道圖片從100KB改爲了120KB,但到底改了啥,版本控制系統不知道,也無法知道。
不幸的是,Microsoft的Word格式是二進制格式,所以,版本控制系統是無法跟蹤Word文件的改動的,前面咱們舉的例子只是爲了演示,若是要真正使用版本控制系統,就要以純文本方式編寫文件。
由於文本是有編碼的,好比中文有經常使用的GBK編碼,日文有Shift_JIS編碼,若是沒有歷史遺留問題,強烈建議使用標準的UTF-8編碼,全部語言使用同一種編碼,既沒有衝突,又被全部平臺所支持。
言歸正傳,如今咱們編寫一個first_git_file.txt文件,內容以下:
$ vim first_git_file.txt first time using git, excited! 第一次用git哈哈
必定要放到git_trainning目錄下(子目錄也行),由於這是一個Git倉庫,放到其餘地方Git再厲害也找不到這個文件。
和把大象放到冰箱須要3步相比,把一個文件放到Git倉庫只須要兩步。
第一步,用命令git add
告訴Git,把文件添加到倉庫:
$ git add first_git_file.txt
執行上面的命令,沒有任何顯示,說明添加成功。
第二步,用命令git commit
告訴Git,把文件提交到倉庫:
$ git commit -m "commit my first git file"
[master (root-commit) 621e6e4] commit my first git file Committer: Alex Li <alex@Alexs-MacBook-Pro.local> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly. Run the following command and follow the instructions in your editor to edit your configuration file: git config --global --edit After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author
1 file changed, 2 insertions(+)
create mode 100644 first_git_file.txt
中間紅色部分的意思是,你在往git庫裏提交代碼時,你須要告訴git你是誰,這樣git就會紀錄下來是誰改的代碼,其實就是爲了往後查詢方便,你只須要提供一個名字和郵件地址就能夠,這裏個人git直接經過主機名本身建立了一個,但你能夠經過git config --global --edit修改
簡單解釋一下git commit
命令,-m
後面輸入的是本次提交的說明,能夠輸入任意內容,固然最好是有意義的,這樣你就能從歷史記錄裏方便地找到改動記錄。
嫌麻煩不想輸入-m "xxx"
行不行?確實有辦法能夠這麼幹,可是強烈不建議你這麼幹,由於輸入說明對本身對別人閱讀都很重要。
爲何Git添加文件須要add
,commit
一共兩步呢?由於commit
能夠一次提交不少文件,因此你能夠屢次add
不一樣的文件,好比:
$ git add file1.txt $ git add file2.txt file3.txt $ git commit -m "add 3 files."
咱們已經成功地添加並提交了一個first_git_file.txt文件,如今,是時候繼續工做了,因而,咱們繼續修改first_git_file.txt文件,改爲以下內容:
First time using git, excited! update ... insert line here... 第一次用git哈哈
如今,運行git status
命令看看結果:
$ 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: first_git_file.txt no changes added to commit (use "git add" and/or "git commit -a")
雖然Git告訴咱們first_git_file.txt被修改了,但若是能看看具體修改了什麼內容,天然是很好的。好比你休假兩週從國外回來,第一天上班時,已經記不清上次怎麼修改的readme.txt,因此,須要用git diff
這個命令看看:
$ git diff first_git_file.txt diff --git a/first_git_file.txt b/first_git_file.txt index 2d13c2c..248d853 100644 --- a/first_git_file.txt +++ b/first_git_file.txt @@ -1,3 +1,4 @@ -first time using git, excited! +First time using git, excited! update ... insert line here... 第一次用git哈哈 +insert line again haha...
輸出中+號綠色顯示的就是修改或新增的內容,-號紅色顯示的就是去掉或被修改的內容
知道了對first_git_file.txt 做了什麼修改後,再把它提交到倉庫就放心多了,提交修改和提交新文件是同樣的兩步,第一步是git add
:
$ git add . # . 表明把當前目錄下全部改動的文件都提交到代碼庫 Alexs-MacBook-Pro:git_trainning alex$ git commit -m "commit changes" [master 50ad6b5] commit changes Committer: Alex Li <alex@Alexs-MacBook-Pro.local> 1 file changed, 1 insertion(+)
提交後,咱們再用git status
命令看看倉庫的當前狀態:
$ git status # On branch master nothing to commit (working directory clean)
Git告訴咱們當前沒有須要提交的修改,並且,工做目錄是乾淨(working directory clean)的。
如今,你已經學會了修改文件,而後把修改提交到Git版本庫,如今,再練習一次,修改first_git_file.txtt文件以下:
First time using git, excited! update ... insert line here..改以前的. 第一次用git哈哈 insert line again haha... 加點新內容
而後嘗試提交:
$ git add first_git_file.txt $ git commit -m "add new content" [master 4459657] add new content Committer: Alex Li <alex@Alexs-MacBook-Pro.local> 1 file changed, 2 insertions(+), 1 deletion(-)
像這樣,你不斷對文件進行修改,而後不斷提交修改到版本庫裏,就比如玩RPG遊戲時,每經過一關就會自動把遊戲狀態存盤,若是某一關沒過去,你還能夠選擇讀取前一關的狀態。有些時候,在打Boss以前,你會手動存盤,以便萬一打Boss失敗了,能夠從最近的地方從新開始。Git也是同樣,每當你以爲文件修改到必定程度的時候,就能夠「保存一個快照」,這個快照在Git中被稱爲commit
。一旦你把文件改亂了,或者誤刪了文件,還能夠從最近的一個commit
恢復,而後繼續工做,而不是把幾個月的工做成果所有丟失。
如今,咱們回顧一下first_git_file.txt文件一共有幾個版本被提交到Git倉庫裏了:
版本1
first time using git, excited! 第一次用git哈哈
版本2
first time using git, excited! insert line here... 第一次用git哈哈
版本3
first time using git, excited! insert line here... 第一次用git哈哈 insert line again haha...
版本4
First time using git, excited! update ... insert line here..改以前的. 第一次用git哈哈 insert line again haha... 加點新內容
固然了,在實際工做中,咱們腦子裏怎麼可能記得一個幾千行的文件每次都改了什麼內容,否則要版本控制系統幹什麼。版本控制系統確定有某個命令能夠告訴咱們歷史記錄,在Git中,咱們用git log
命令查看:
$ git log commit 445965781d1fd0d91e76d120450dd18fd06c7489 Author: Alex Li <alex@Alexs-MacBook-Pro.local> Date: Tue Oct 4 18:44:29 2016 +0800 add new content commit be02137bb2f54bbef0c2e99202281b3966251952 Author: Alex Li <alex@Alexs-MacBook-Pro.local> Date: Tue Oct 4 17:55:16 2016 +0800 update again commit 50ad6b526810bb7ccfea430663757ba2337b9816 Author: Alex Li <alex@Alexs-MacBook-Pro.local> Date: Tue Oct 4 17:46:51 2016 +0800 commit changes commit 621e6e44d04fa6a1cdc37826f01efa61b451abd1 Author: Alex Li <alex@Alexs-MacBook-Pro.local> Date: Tue Oct 4 17:42:50 2016 +0800 commit my first git file
git log
命令顯示從最近到最遠的提交日誌,咱們能夠看到4次提交,最近的一次是add new content,上一次是update again,最先的一次是commit my first git file
。 若是嫌輸出信息太多,看得眼花繚亂的,能夠試試加上--pretty=oneline
參數:
$ git log --pretty=oneline 445965781d1fd0d91e76d120450dd18fd06c7489 add new content be02137bb2f54bbef0c2e99202281b3966251952 update again 50ad6b526810bb7ccfea430663757ba2337b9816 commit changes 621e6e44d04fa6a1cdc37826f01efa61b451abd1 commit my first git file
須要友情提示的是,你看到的一大串相似3628164...882e1e0
的是commit id
(版本號),和SVN不同,Git的commit id
不是1,2,3……遞增的數字,而是一個SHA1計算出來的一個很是大的數字,用十六進制表示,並且你看到的commit id
和個人確定不同,以你本身的爲準。爲何commit id
須要用這麼一大串數字表示呢?由於Git是分佈式的版本控制系統,後面咱們還要研究多人在同一個版本庫裏工做,若是你們都用1,2,3……做爲版本號,那確定就衝突了。
回滾回滾回滾
好了,如今咱們啓動時光穿梭機,準備把first_git_file.txt回退到上一個版本,也就是「update again」的那個版本,怎麼作呢?
首先,Git必須知道當前版本是哪一個版本,在Git中,用HEAD
表示當前版本,也就是最新的提交be02137bb2f54bbef0c2e99202281b3966251952(注意個人提交ID和你的確定不同),上一個版本就是HEAD^
,上上一個版本就是HEAD^^
,固然往上100個版本寫100個^
比較容易數不過來,因此寫成HEAD~100
。
如今,咱們要把當前版本「add new content」回退到上一個版本「update again」,就能夠使用git reset
命令:
$ git reset --hard HEAD^
HEAD is now at be02137 update again
此時再看你的文件內容,果真就退回去了
$ more first_git_file.txt First time using git, excited! update ... insert line here... 第一次用git哈哈 insert line again haha...
此時還能夠繼續再往前回退一個版本,不過且慢,然咱們用git log
再看看如今版本庫的狀態:
$ git log --pretty=oneline be02137bb2f54bbef0c2e99202281b3966251952 update again 50ad6b526810bb7ccfea430663757ba2337b9816 commit changes 621e6e44d04fa6a1cdc37826f01efa61b451abd1 commit my first git file
最新的那個版本add new content已經看不到了!比如你從21世紀坐時光穿梭機來到了19世紀,想再回去已經回不去了,腫麼辦?
辦法其實仍是有的,只要上面的命令行窗口尚未被關掉,你就能夠順着往上找啊找啊,找到那個add new content的commit id
是445965781d1fd0d91e76d120450dd18fd06c7489
,因而就能夠指定回到將來的某個版本:
git reset --hard 4459657 HEAD is now at 4459657 add new content
版本號不必寫全,前幾位就能夠了,Git會自動去找。固然也不能只寫前一兩位,由於Git可能會找到多個版本號,就沒法肯定是哪個了。
再當心翼翼地看看first_git_file.txt的內容:
First time using git, excited! update ... insert line here..改以前的. 第一次用git哈哈 insert line again haha... 加點新內容
果真,我胡漢三又回來了。
Git的版本回退速度很是快,由於Git在內部有個指向當前版本的HEAD
指針,當你回退版本的時候,Git僅僅是把HEAD從指向add new content
如今,你回退到了某個版本,關掉了電腦,次日早上就後悔了,想恢復到新版本怎麼辦?找不到新版本的commit id怎麼辦?
在Git中,老是有後悔藥能夠吃的。當你用$ git reset --hard HEAD^回退到update again版本時,再想恢復到最新add new content的版本,就必須找到add new contentL的commit id。Git提供了一個命令git reflog用來記錄你的每一次命令:
$ git reflog 4459657 HEAD@{0}: reset: moving to 4459657 be02137 HEAD@{1}: reset: moving to HEAD^ 4459657 HEAD@{2}: commit: add new content be02137 HEAD@{3}: reset: moving to be02137bb 50ad6b5 HEAD@{4}: reset: moving to 50ad6b5 621e6e4 HEAD@{5}: reset: moving to 621e6e44 50ad6b5 HEAD@{6}: reset: moving to HEAD^ be02137 HEAD@{7}: commit: update again 50ad6b5 HEAD@{8}: commit: commit changes 621e6e4 HEAD@{9}: commit (initial): commit my first git file
終於舒了口氣,第二行顯示add new content
的commit id是4459657,如今,你又能夠乘坐時光機回到將來了。
Git和其餘版本控制系統如SVN的一個不一樣之處就是有暫存區的概念。
先來看名詞解釋。
工做區(Working Directory)
就是你在電腦裏能看到的目錄,好比個人git_trainning文件夾就是一個工做區:
$ ls git_trainning/ first_git_file.txt
版本庫(Repository)
工做區有一個隱藏目錄.git
,這個不算工做區,而是Git的版本庫。
Git的版本庫裏存了不少東西,其中最重要的就是稱爲stage(或者叫index)的暫存區,還有Git爲咱們自動建立的第一個分支master
,以及指向master
的一個指針叫HEAD
。
分支和HEAD
的概念咱們之後再講。
前面講了咱們把文件往Git版本庫裏添加的時候,是分兩步執行的:
第一步是用git add
把文件添加進去,實際上就是把文件修改添加到暫存區;
第二步是用git commit
提交更改,實際上就是把暫存區的全部內容提交到當前分支。
由於咱們建立Git版本庫時,Git自動爲咱們建立了惟一一個master
分支,因此,如今,git commit
就是往master
分支上提交更改。
你能夠簡單理解爲,須要提交的文件修改統統放到暫存區,而後,一次性提交暫存區的全部修改。
俗話說,實踐出真知。如今,咱們再練習一遍,先對first_git_file.txt
作個修改,好比加上一行內容:
First time using git, excited! update ...
insert line here..改以前的.
第一次用git哈哈
insert line again haha...
加點新內容
update v5
而後,在工做區新增一個readme.md
文本文件(內容隨便寫)。
先用git status
查看一下狀態:
$ 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: first_git_file.txt Untracked files: (use "git add <file>..." to include in what will be committed) readme.md no changes added to commit (use "git add" and/or "git commit -a")
Git很是清楚地告訴咱們,first_git_file.txt
被修改了,而readme.md
還歷來沒有被添加過,因此它的狀態是Untracked
。
如今,使用命令git add .
,再用git status
再查看一下:
$ git add .
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: first_git_file.txt
new file: readme.md
如今,暫存區的狀態就變成這樣了:
(盜圖關係, 這裏readme.txt = first_git_file.txt , LICENSE = readme.md)
因此,git add
命令實際上就是把要提交的全部修改放到暫存區(Stage),而後,執行git commit
就能夠一次性把暫存區的全部修改提交到分支。
$ git commit -m "知道暫存區stage的意思了" [master 9d65cb2] 知道暫存區stage的意思了 2 files changed, 2 insertions(+) create mode 100644 readme.md
一旦提交後,若是你又沒有對工做區作任何修改,那麼工做區就是「乾淨」的:
$ git status On branch master nothing to commit, working directory clean
如今版本庫變成了這樣,暫存區就沒有任何內容了:
(盜圖關係, 這裏readme.txt = first_git_file.txt , LICENSE = readme.md)
暫存區是Git很是重要的概念,弄明白了暫存區,就弄明白了Git的不少操做到底幹了什麼。
天然,你是不會犯錯的。不過如今是凌晨兩點,你正在趕一份工做報告,你在readme.md
中添加了一行:
#git study repo
git is great
but my stupid boss still prefers SVN.
在你準備提交前,一杯咖啡起了做用,你猛然發現了「stupid boss」可能會讓你丟掉這個月的獎金!
既然錯誤發現得很及時,就能夠很容易地糾正它。你能夠刪掉最後一行,手動把文件恢復到上一個版本的狀態。若是用git status
查看一下:
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: readme.md
no changes added to commit (use "git add" and/or "git commit -a")
你能夠發現,Git會告訴你,git checkout -- file
能夠丟棄工做區的修改:
$ git checkout -- readme.md $ more readme.md #git study repo
你剛纔添加的2行罵老闆的話就被撤銷了,
命令git checkout -- readme.md
意思就是,把readme.md
文件在工做區的修改所有撤銷,這裏有兩種狀況:
一種是readme.md
自修改後尚未被放到暫存區,如今,撤銷修改就回到和版本庫如出一轍的狀態;
一種是readme.md
已經添加到暫存區後,又做了修改,如今,撤銷修改就回到添加到暫存區後的狀態。
總之,就是讓這個文件回到最近一次git commit
或git add
時的狀態。
git checkout -- file
命令中的--
很重要,沒有--
,就變成了「切換到另外一個分支」的命令,咱們在後面的分支管理中會再次遇到git checkout
命令。
如今假定是凌晨3點,你不但寫了一些胡話,還git add
到暫存區了:
$ cat readme.md Git is a distributed version control system. Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files. My stupid boss still prefers SVN. $ git add readme.md
慶幸的是,在commit
以前,你發現了這個問題。用git status
查看一下,修改只是添加到了暫存區,尚未提交:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.md
Git一樣告訴咱們,用命令git reset HEAD file
能夠把暫存區的修改撤銷掉(unstage),從新放回工做區:
$ git reset HEAD readme.md Unstaged changes after reset: M readme.md
git reset
命令既能夠回退版本,也能夠把暫存區的修改回退到工做區。當咱們用HEAD
時,表示最新的版本。
再用git status
查看一下,如今暫存區是乾淨的,工做區有修改
$ 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: readme.md # no changes added to commit (use "git add" and/or "git commit -a")
還記得如何丟棄工做區的修改嗎?
$ git checkout -- readme.md $ more readme.md #git study repo
整個世界終於清靜了!
在Git中,刪除也是一個修改操做,咱們實戰一下,先添加一個新文件test.txt到Git而且提交:
$ git add . $ git commit -m "add test.txt" [master a8fa95a] add test.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test.txt
通常狀況下,你一般直接在文件管理器中把沒用的文件刪了,或者用rm
命令刪了
$ rm test.txt
這個時候,Git知道你刪除了文件,所以,工做區和版本庫就不一致了,git status
命令會馬上告訴你哪些文件被刪除了:
$ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: test.txt no changes added to commit (use "git add" and/or "git commit -a")
如今你有兩個選擇,一是確實要從版本庫中刪除該文件,那就用命令git rm
刪掉,而且git commit
:
x$ git rm test.txt rm 'test.txt' $ git commit -m "remove test" [master 03df00a] remove test 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test.txt
如今,文件就從版本庫中被刪除了。
另外一種狀況是刪錯了,由於版本庫裏還有呢,因此能夠很輕鬆地把誤刪的文件恢復到最新版本:
$ git checkout -- test.txt
git checkout
實際上是用版本庫裏的版本替換工做區的版本,不管工做區是修改仍是刪除,均可以「一鍵還原」。
到目前爲止,咱們已經掌握瞭如何在Git倉庫裏對一個文件進行時光穿梭,你不再用擔憂文件備份或者丟失的問題了。
但是有用過集中式版本控制系統SVN的童鞋會站出來講,這些功能在SVN裏早就有了,沒看出Git有什麼特別的地方。
沒錯,若是隻是在一個倉庫裏管理文件歷史,Git和SVN真沒啥區別。爲了保證你如今所學的Git物超所值,未來絕對不會後悔,同時爲了打擊已經不幸學了SVN的童鞋,本章開始介紹Git的殺手級功能之一(注意是之一,也就是後面還有之二,之三……):遠程倉庫。
Git是分佈式版本控制系統,同一個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:
$ ssh-keygen -t rsa -C "youremail@example.com"
你須要把郵件地址換成你本身的郵件地址,而後一路回車,使用默認值便可,因爲這個Key也不是用於軍事目的,因此也無需設置密碼。
若是一切順利的話,能夠在用戶主目錄裏找到.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
文件的內容:
點「Add Key」,你就應該看到已經添加的Key
爲何GitHub須要SSH Key呢?由於GitHub須要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協議,因此,GitHub只要知道了你的公鑰,就能夠確認只有你本身才能推送。
固然,GitHub容許你添加多個Key。假定你有若干電腦,你一下子在公司提交,一下子在家裏提交,只要把每臺電腦的Key都添加到GitHub,就能夠在每臺電腦上往GitHub推送了。
最後友情提示,在GitHub上免費託管的Git倉庫,任何人均可以看到喔(但只有你本身才能改)。因此,不要把敏感信息放進去。
若是你不想讓別人看到Git庫,有兩個辦法,一個是交點保護費,讓GitHub把公開的倉庫變成私有的,這樣別人就看不見了(不可讀更不可寫)。另外一個辦法是本身動手,搭一個Git服務器,由於是你本身的Git服務器,因此別人也是看不見的。這個方法咱們後面會講到的,至關簡單,公司內部開發必備。
確保你擁有一個GitHub帳號後,咱們就即將開始遠程倉庫的學習。
如今的情景是,你已經在本地建立了一個Git倉庫後,又想在GitHub建立一個Git倉庫,而且讓這兩個倉庫進行遠程同步,這樣,GitHub上的倉庫既能夠做爲備份,又可讓其餘人經過該倉庫來協做,真是一舉多得。
首先,登錄GitHub,而後,在右上角找到「New repository」按鈕,建立一個新的倉庫:
建立好的倉庫
目前,在GitHub上的這個oldboy_website倉庫仍是空的,GitHub告訴咱們,能夠從這個倉庫克隆出新的倉庫,也能夠把一個已有的本地倉庫與之關聯,而後,把本地倉庫的內容推送到GitHub倉庫。
如今,咱們根據GitHub的提示,在本地已有的git_trainning倉庫下運行命令:
$ git remote add origin git@github.com:triaquae/oldboy_website.git #添加遠程倉庫 $ git push -u origin master #推到遠程 The authenticity of host 'github.com (192.30.253.113)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no)? yes #第一次推會出現,寫yes Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts. Counting objects: 20, done. Delta compression using up to 8 threads. Compressing objects: 100% (14/14), done. Writing objects: 100% (20/20), 1.76 KiB | 0 bytes/s, done. Total 20 (delta 4), reused 0 (delta 0) remote: Resolving deltas: 100% (4/4), done. To git@github.com:triaquae/oldboy_website.git * [new branch] master -> master Branch master set up to track remote branch master from origin.
請千萬注意,把上面的triaquae替換成你本身的GitHub帳戶名,不然,你在本地關聯的就是個人遠程庫,關聯沒有問題,可是你之後推送是推不上去的,由於你的SSH Key公鑰不在個人帳戶列表中。
添加後,遠程庫的名字就是origin
,這是Git默認的叫法,也能夠改爲別的,可是origin
這個名字一看就知道是遠程庫。
把本地庫的內容推送到遠程,用git push
命令,其實是把當前分支master
推送到遠程。
此時刷新遠程倉庫頁面, 就看到了你剛從本地推上來的代碼了
從如今起,只要本地做了提交,就能夠經過命令:
$ git push origin master
what ? 不信?那幫你試一下吧
建立一個index.html文件,同時上傳到遠程
$ vim index.html $ git add . $ git commit -m "add home page" [master 8675486] add home page 1 file changed, 6 insertions(+) create mode 100644 index.html $ git push origin master #推到遠程 Counting objects: 3, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 362 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:triaquae/oldboy_website.git 03df00a..8675486 master -> master
而後刷新下遠程倉庫頁面,就看到你的新建立的文件了
咱們講了先有本地庫,後有遠程庫的時候,如何關聯遠程庫。
如今,假設咱們從零開發,那麼最好的方式是先建立遠程庫,而後,從遠程庫克隆。
首先,登錄GitHub,建立一個新的倉庫,名字叫gitskills
:
咱們勾選Initialize this repository with a README
,這樣GitHub會自動爲咱們建立一個README.md
文件。建立完畢後,能夠看到README.md
文件:
如今,遠程庫已經準備好了,下一步是用命令git clone
克隆一個本地庫:
在本地找一個你想存放這個遠程倉庫的目錄,而後在本地命令行用git clone 命令來克隆這個遠程庫
$ git clone git@github.com:triaquae/gitskills.git Cloning into 'gitskills'... Warning: Permanently added the RSA host key for IP address '192.30.253.112' to the list of known hosts. remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (3/3), done. Checking connectivity... done. $ cd gitskills/ #進入剛clone下來的目錄 $ ls README.md
若是有多我的協做開發,那麼每一個人各自從遠程克隆一份就能夠了。
你也許還注意到,GitHub給出的地址不止一個,還能夠用https://github.com/triaquae/gitskills.git 這樣的地址。實際上,Git支持多種協議,默認的git://
使用ssh,但也能夠使用https
等其餘協議。
使用https
除了速度慢之外,還有個最大的麻煩是每次推送都必須輸入口令,可是在某些只開放http端口的公司內部就沒法使用ssh
協議而只能用https
。
分支就是科幻電影裏面的平行宇宙,當你正在電腦前努力學習Git的時候,另外一個你正在另外一個平行宇宙裏努力學習SVN。
若是兩個平行宇宙互不干擾,那對如今的你也沒啥影響。不過,在某個時間點,兩個平行宇宙合併了,結果,你既學會了Git又學會了SVN!
分支在實際中有什麼用呢?假設你準備開發一個新功能,可是須要兩週才能完成,第一週你寫了50%的代碼,若是馬上提交,因爲代碼還沒寫完,不完整的代碼庫會致使別人不能幹活了。若是等代碼所有寫完再一次提交,又存在丟失天天進度的巨大風險。
如今有了分支,就不用怕了。你建立了一個屬於你本身的分支,別人看不到,還繼續在原來的分支上正常工做,而你在本身的分支上幹活,想提交就提交,直到開發完畢後,再一次性合併到原來的分支上,這樣,既安全,又不影響別人工做。
其餘版本控制系統如SVN等都有分支管理,可是用過以後你會發現,這些版本控制系統建立和切換分支比蝸牛還慢,簡直讓人沒法忍受,結果分支功能成了擺設,你們都不去用。
但Git的分支是不同凡響的,不管建立、切換和刪除分支,Git在1秒鐘以內就能完成!不管你的版本庫是1個文件仍是1萬個文件。
在學習版本回退部分時,你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git裏,這個分支叫主分支,即master
分支。HEAD
嚴格來講不是指向提交,而是指向master
,master
纔是指向提交的,因此,HEAD
指向的就是當前分支。
一開始的時候,master
分支是一條線,Git用master
指向最新的提交,再用HEAD
指向master
,就能肯定當前分支,以及當前分支的提交點:
每次提交,master
分支都會向前移動一步,這樣,隨着你不斷提交,master
分支的線也愈來愈長, 當咱們建立新的分支,例如dev
時,Git新建了一個指針叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示當前分支在dev
上:
假如咱們在dev
上的工做完成了,就能夠把dev
合併到master
上。Git怎麼合併呢?最簡單的方法,就是直接把master
指向dev
的當前提交,就完成了合併:
因此Git合併分支也很快!就改改指針,工做區內容也不變!
合併完分支後,甚至能夠刪除dev
分支。刪除dev
分支就是把dev
指針給刪掉,刪掉後,咱們就剩下了一條master
分支:
真是太神奇了,你看得出來有些提交是經過分支完成的嗎?
下面開始實戰。
首先,咱們建立dev
分支,而後切換到dev
分支:
$ git checkout -b dev Switched to a new branch 'dev'
git checkout
命令加上-b
參數表示建立並切換,至關於如下兩條命令:
$ git branch dev $ git checkout dev Switched to branch 'dev'
而後,用git branch
命令查看當前分支:
$ git branch * dev master
git branch
命令會列出全部分支,當前分支前面會標一個*
號。
而後,咱們就能夠在dev
分支上正常提交,好比對readme.txt作個修改,加上一行:
Creating a new branch is quick.
而後提交:
$ git add readme.txt $ git commit -m "branch test" [dev fec145a] branch test 1 file changed, 1 insertion(+)
如今,dev
分支的工做完成,咱們就能夠切換回master
分支:
$ git checkout master Switched to branch 'master'
切換回master
分支後,再查看一個readme.txt文件,剛纔添加的內容不見了!由於那個提交是在dev
分支上,而master
分支此刻的提交點並無變:
如今,咱們把dev
分支的工做成果合併到master
分支上:
$ git merge dev Updating d17efd8..fec145a Fast-forward readme.txt | 1 + 1 file changed, 1 insertion(+)
git merge
命令用於合併指定分支到當前分支。合併後,再查看readme.txt的內容,就能夠看到,和dev
分支的最新提交是徹底同樣的。
注意到上面的Fast-forward
信息,Git告訴咱們,此次合併是「快進模式」,也就是直接把master
指向dev
的當前提交,因此合併速度很是快。
固然,也不是每次合併都能Fast-forward
,咱們後面會講其餘方式的合併。
合併完成後,就能夠放心地刪除dev
分支了:
$ git branch -d dev Deleted branch dev (was fec145a).
刪除後,查看branch
,就只剩下master
分支了:
$ git branch * master
由於建立、合併和刪除分支很是快,因此Git鼓勵你使用分支完成某個任務,合併後再刪掉分支,這和直接在master
分支上工做效果是同樣的,但過程更安全。
人生不如意之事十之八九,合併分支每每也不是一路順風的。
準備新的feature1
分支,繼續咱們的新分支開發:
$ git checkout -b feature1 Switched to a new branch 'feature1'
修改readme.txt最後一行,改成:
added this line from branch feature 1
在feature1
分支上提交:
$ git add readme.txt
$ git commit -m "add feature" [feature1 75a857c] AND simple 1 file changed, 1 insertion(+), 1 deletion(-)
切換到master
分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
Git還會自動提示咱們當前master
分支比遠程的master
分支要超前1個提交。
在master
分支上把readme.txt文件的最後一行改成:
added this line from master
提交:
$ git add readme.txt $ git commit -m "master update" [master 400b400] & simple 1 file changed, 1 insertion(+), 1 deletion(-)
如今,master
分支和feature1
分支各自都分別有新的提交,變成了這樣:
這種狀況下,Git沒法執行「快速合併」,只能試圖把各自的修改合併起來,但這種合併就可能會有衝突,咱們試試看:
$ git merge feature1 Auto-merging readme.txt CONFLICT (content): Merge conflict in readme.txt Automatic merge failed; fix conflicts and then commit the result.
果真衝突了!Git告訴咱們,readme.txt文件存在衝突,必須手動解決衝突後再提交。git status
也能夠告訴咱們衝突的文件:
$ git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # # Unmerged paths: # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: readme.txt # no changes added to commit (use "git add" and/or "git commit -a")
咱們能夠直接查看readme.txt的內容:
#git study repo Creating a new branch is quick. <<<<<<< HEAD added this line from master ======= added this line from branch feature 1 >>>>>>> feature1
Git用<<<<<<<
,=======
,>>>>>>>
標記出不一樣分支的內容,咱們修改以下後保存:
#git study repo Creating a new branch is quick. added this line from master added this line from branch feature 1
再提交
$ git add readme.txt $ git commit -m "conflict fixed" [master 59bc1cb] conflict fixed
如今,master
分支和feature1
分支變成了下圖所示:
用帶參數的git log
也能夠看到分支的合併狀況:
$ git log --graph --pretty=oneline
* feedd786cad3e18323a41846fcc1b0d52fc0c98e fix conflict
|\
| * 01f8f8d168e113fac9fbe24c4cfa6d4c351a9821 update from branch
* | 743ccee30f3d74f1993f17e7312032b7399b1306 from master
|/
* edfbc29982927236596539e0f1971b0575f803c0 branch test
* 8675486bfeeb340914369e80d2cfcf3e854e88a3 add home page
在實際開發中,咱們應該按照幾個基本原則進行分支管理:
首先,master
分支應該是很是穩定的,也就是僅用來發布新版本,平時不能在上面幹活;
那在哪幹活呢?幹活都在dev
分支上,也就是說,dev
分支是不穩定的,到某個時候,好比1.0版本發佈時,再把dev
分支合併到master
上,在master
分支發佈1.0版本;
你和你的小夥伴們每一個人都在dev
分支上幹活,每一個人都有本身的分支,時不時地往dev
分支上合併就能夠了。
因此,團隊合做的分支看起來就像這樣:
軟件開發中,bug就像屢見不鮮同樣。有了bug就須要修復,在Git中,因爲分支是如此的強大,因此,每一個bug均可以經過一個新的臨時分支來修復,修復後,合併分支,而後將臨時分支刪除。
當你接到一個修復一個代號101的bug的任務時,很天然地,你想建立一個分支issue-101
來修復它,可是,等等,當前正在dev
上進行的工做尚未提交:
$ git status # On branch dev # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: hello.py # # 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: readme.txt #
並非你不想提交,而是工做只進行到一半,還無法提交,預計完成還需1天時間。可是,必須在兩個小時內修復該bug,怎麼辦?
幸虧,Git還提供了一個stash
功能,能夠把當前工做現場「儲藏」起來,等之後恢復現場後繼續工做:
$ git stash Saved working directory and index state WIP on dev: 6224937 add merge HEAD is now at 6224937 add merge
如今,用git status
查看工做區,就是乾淨的(除非有沒有被Git管理的文件),所以能夠放心地建立分支來修復bug。
首先肯定要在哪一個分支上修復bug,假定須要在master
分支上修復,就從master
建立臨時分支:
$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 6 commits. $ git checkout -b issue-101 Switched to a new branch 'issue-101'
如今修復bug,須要把「Git is free software ...」改成「Git is a free software ...」,而後提交:
$ git add readme.txt $ git commit -m "fix bug 101" [issue-101 cc17032] fix bug 101 1 file changed, 1 insertion(+), 1 deletion(-)
修復完成後,切換到master
分支,並完成合並,最後刪除issue-101
分支:
$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 2 commits. $ git merge --no-ff -m "merged bug fix 101" issue-101 Merge made by the 'recursive' strategy. readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) $ git branch -d issue-101 Deleted branch issue-101 (was cc17032).
太棒了,原計劃兩個小時的bug修復只花了5分鐘!如今,是時候接着回到dev
分支幹活了!
$ git checkout dev Switched to branch 'dev' $ git status # On branch dev nothing to commit (working directory clean)
工做區是乾淨的,剛纔的工做現場存到哪去了?用git stash list
命令看看:
$ git stash list stash@{0}: WIP on dev: 6224937 add merge
工做現場還在,Git把stash內容存在某個地方了,可是須要恢復一下,有兩個辦法:
一是用git stash apply
恢復,可是恢復後,stash內容並不刪除,你須要用git stash drop
來刪除;
另外一種方式是用git stash pop
,恢復的同時把stash內容也刪了:
$ git stash pop # On branch dev # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: hello.py # # 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: readme.txt # Dropped refs/stash@{0} (f624f8e5f082f2df2bed8a4e09c12fd2943bdd40)
再用git stash list
查看,就看不到任何stash內容了:
$ git stash list
你能夠屢次stash,恢復的時候,先用git stash list
查看,而後恢復指定的stash,用命令:
$ git stash apply stash@{0}
當你從遠程倉庫克隆時,實際上Git自動把本地的master
分支和遠程的master
分支對應起來了,而且,遠程倉庫的默認名稱是origin
。
要查看遠程庫的信息,用git remote
:
$ git remote origin
或者,用git remote -v
顯示更詳細的信息:
$ git remote -v origin git@github.com:triaquae/gitskills.git (fetch) origin git@github.com:triaquae/gitskills.git (push)
上面顯示了能夠抓取和推送的origin
的地址。若是沒有推送權限,就看不到push的地址。
推送分支,就是把該分支上的全部本地提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的遠程分支上:
$ git push origin master
若是要推送其餘分支,好比dev
,就改爲:
$ git push origin dev
可是,並非必定要把本地分支往遠程推送,那麼,哪些分支須要推送,哪些不須要呢?
master
分支是主分支,所以要時刻與遠程同步;
dev
分支是開發分支,團隊全部成員都須要在上面工做,因此也須要與遠程同步;
bug分支只用於在本地修復bug,就不必推到遠程了,除非老闆要看看你每週到底修復了幾個bug;
feature分支是否推到遠程,取決於你是否和你的小夥伴合做在上面開發。
總之,就是在Git中,分支徹底能夠在本地本身藏着玩,是否推送,視你的心情而定!
多人協做時,你們都會往master
和dev
分支上推送各自的修改。
如今,模擬一個你的小夥伴,能夠在另外一臺電腦(注意要把SSH Key添加到GitHub)或者同一臺電腦的另外一個目錄下克隆:
$ git clone git@github.com:triaquae/gitskills.git Cloning into 'gitskills'... remote: Counting objects: 16, done. remote: Compressing objects: 100% (7/7), done. remote: Total 16 (delta 0), reused 10 (delta 0), pack-reused 0 Receiving objects: 100% (16/16), done. Checking connectivity... done.
當你的小夥伴從遠程庫clone時,默認狀況下,你的小夥伴只能看到本地的master
分支。不信能夠用git branch
命令看看:
$ git branch * master
如今,你的小夥伴要在dev
分支上開發,就必須建立遠程origin
的dev
分支到本地,因而他用這個命令建立本地dev
分支:
$ git checkout -b dev origin/dev
如今,他就能夠在dev
上繼續修改,而後,時不時地把dev
分支push
到遠程:
$ git add . $ git commit -m "small updates" [dev f1b762e] small updates 2 files changed, 5 insertions(+), 1 deletion(-) Alexs-MacBook-Pro:gitskills alex$ git push origin dev Counting objects: 4, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 438 bytes | 0 bytes/s, done. Total 4 (delta 0), reused 0 (delta 0) To git@github.com:triaquae/gitskills.git 33ec6b4..f1b762e dev -> dev
你的小夥伴已經向origin/dev分支推送了他的提交,而碰巧你也對一樣的文件做了修改,並試圖推送:
$ git add . $ git commit -m "add Dog class" [dev 7e7b1bf] add Dog class 2 files changed, 7 insertions(+) $ git push origin dev To git@github.com:triaquae/gitskills.git ! [rejected] dev -> dev (fetch first) error: failed to push some refs to 'git@github.com:triaquae/gitskills.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.
推送失敗,由於你的小夥伴的最新提交和你試圖推送的提交有衝突,解決辦法也很簡單,Git已經提示咱們,先用git pull
把最新的提交從origin/dev
抓下來,而後,在本地合併,解決衝突,再推
$ git pull remote: Counting objects: 4, done. remote: Compressing objects: 100% (3/3), done. remote: Total 4 (delta 0), reused 4 (delta 0), pack-reused 0 Unpacking objects: 100% (4/4), done. From github.com:triaquae/gitskills 33ec6b4..f1b762e dev -> origin/dev There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details. git pull <remote> <branch> If you wish to set tracking information for this branch you can do so with: git branch --set-upstream-to=origin/<branch> dev
git pull
也失敗了,緣由是沒有指定本地dev
分支與遠程origin/dev
分支的連接,根據提示,設置dev
和origin/dev
的連接:
$ git branch --set-upstream-to=origin/dev dev Branch dev set up to track remote branch dev from origin.
再pull:
$ git pull Auto-merging hello.py CONFLICT (content): Merge conflict in hello.py Auto-merging branch_test.md CONFLICT (content): Merge conflict in branch_test.md Automatic merge failed; fix conflicts and then commit the result.
這回git pull
成功,可是合併有衝突,須要手動解決,解決的方法和分支管理中的解決衝突徹底同樣。解決後,提交,再push:
$ git add . $ git commit -m "merge & fix hello.py" [dev 93e28e3] merge & fix hello.py $ git push origin dev Counting objects: 8, done. Delta compression using up to 8 threads. Compressing objects: 100% (7/7), done. Writing objects: 100% (8/8), 819 bytes | 0 bytes/s, done. Total 8 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), done. To git@github.com:triaquae/gitskills.git f1b762e..93e28e3 dev -> dev
所以,多人協做的工做模式一般是這樣:
首先,能夠試圖用git push origin branch-name
推送本身的修改;
若是推送失敗,則由於遠程分支比你的本地更新,須要先用git pull
試圖合併;
若是合併有衝突,則解決衝突,並在本地提交;
沒有衝突或者解決掉衝突後,再用git push origin branch-name
推送就能成功!
若是git pull
提示「no tracking information」,則說明本地分支和遠程分支的連接關係沒有建立,用命令git branch --set-upstream branch-name origin/branch-name
。
這就是多人協做的工做模式,一旦熟悉了,就很是簡單。
咱們一直用GitHub做爲免費的遠程倉庫,若是是我的的開源項目,放到GitHub上是徹底沒有問題的。其實GitHub仍是一個開源協做社區,經過GitHub,既可讓別人參與你的開源項目,也能夠參與別人的開源項目。
在GitHub出現之前,開源項目開源容易,但讓廣大人民羣衆參與進來比較困難,由於要參與,就要提交代碼,而給每一個想提交代碼的羣衆都開一個帳號那是不現實的,所以,羣衆也僅限於報個bug,即便能改掉bug,也只能把diff文件用郵件發過去,很不方便。
可是在GitHub上,利用Git極其強大的克隆和分支功能,廣大人民羣衆真正能夠第一次自由參與各類開源項目了。
如何參與一個開源項目呢?好比人氣極高的bootstrap項目,這是一個很是強大的CSS框架,你能夠訪問它的項目主頁https://github.com/twbs/bootstrap,點「Fork」就在本身的帳號下克隆了一個bootstrap倉庫,而後,從本身的帳號下clone:
git clone git@github.com:michaelliao/bootstrap.git
必定要從本身的帳號下clone倉庫,這樣你才能推送修改。若是從bootstrap的做者的倉庫地址git@github.com:twbs/bootstrap.git
克隆,由於沒有權限,你將不能推送修改。
Bootstrap的官方倉庫twbs/bootstrap
、你在GitHub上克隆的倉庫my/bootstrap
,以及你本身克隆到本地電腦的倉庫,他們的關係就像下圖顯示的那樣:
若是你想修復bootstrap的一個bug,或者新增一個功能,馬上就能夠開始幹活,幹完後,往本身的倉庫推送。
若是你但願bootstrap的官方庫能接受你的修改,你就能夠在GitHub上發起一個pull request。固然,對方是否接受你的pull request就不必定了。
若是你沒能力修改bootstrap,但又想要試一把pull request,那就Fork一下個人倉庫:https://github.com/triaquae/gitskills ,建立一個your-github-id.txt
的文本文件,寫點本身學習Git的心得,而後推送一個pull request給我,我會視心情而定是否接受。
小結
在GitHub上,能夠任意Fork開源倉庫;
本身擁有Fork後的倉庫的讀寫權限;
能夠推送pull request給官方倉庫來貢獻代碼。
有些時候,你必須把某些文件放到Git工做目錄中,但又不能提交它們,好比保存了數據庫密碼的配置文件啦,等等,每次git status
都會顯示Untracked files ...
,有強迫症的童鞋內心確定不爽。
好在Git考慮到了你們的感覺,這個問題解決起來也很簡單,在Git工做區的根目錄下建立一個特殊的.gitignore
文件,而後把要忽略的文件名填進去,Git就會自動忽略這些文件。
不須要從頭寫.gitignore
文件,GitHub已經爲咱們準備了各類配置文件,只須要組合一下就能夠使用了。全部配置文件能夠直接在線瀏覽:https://github.com/github/gitignore
忽略文件的原則是:
.class
文件;舉個例子:
假設你在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.class
Git會告訴咱們,.gitignore
的第3行規則忽略了該文件,因而咱們就能夠知道應該修訂哪一個規則。
小結
忽略某些文件時,須要編寫.gitignore
;
.gitignore
文件自己要放到版本庫裏,而且能夠對.gitignore
作版本管理!
補充:
Git,分佈式版本控制系統 1. 版本控制 blogv1 blogv2 blog90 ... blogv100 PS: 文件管理版本,每一個版本須要保留 以上是使用文件或文件夾進行版本管理,以上方式有缺點: 多個文件,保留全部版本時,須要將多個文件保存在本地 協同操做,多人協同操做時,須要將文件發來發去... 容易丟失,被刪除意味着永遠失去(能夠選擇網盤) 簡單總結: vss: 不用 svn: 集中式版本控制系統;服務端有全部版本;客戶端只有一個版本 GIT:分佈式版本控制系統 共同點: - 服務端(保存代碼的倉庫) - 客戶端(我的用戶) 2. 需求來了 原始方法: a. 創業初期:單身過久,硬盤沾滿,創業:北京熱; 同城交友 b. 開發新功能:直播(2個月) 拷貝一份,進行編輯,1個月 在回來:繼續開發 c. 緊急修復bug,2個小時 從線上在拷貝一份,修復bug;上線 Git方式: a. 創業初期:單身過久,硬盤沾滿,創業:北京熱; 同城交友 進入程序目錄 git init => 初始化 生成 .git 文件,用於保存git相關全部信息,如:每一個版本 git status => 查看狀態 git add 文件名 git commit -m '提交信息' 配置用戶信息: git config --local user.name '名稱' git config --local user.email '郵箱' 其餘: git ls-tree head 查看版本中全部文件 git ls-files -s 查看暫存區和版本中全部文件 b. 開發新功能:短信提醒 有短信功能: 開發短信功能 建立msg.txt文件 git add . git commit -m '開發短信功能' git log 回滾(無短信功能): git reset --hard 92dea56e57466115efab904a5f1d9adc361111e8 再回去: git reflog git reset --mix a615783 git checkout 文件名 c. 開發新功能:直播(2個月) 方式一:stash 工做區工做: git stash -> 將當前已經作過的修改,保存到一個臨時地方 修復bug git add . git commit -m '修復bug' git stash pop -> 臨時地方內容從新放回工做區 又出了一個bug git stash 修復bug git add . git commit -m '修復bug' git stash pop 出現衝突,手動解決 <<<<<<< Updated upstream 同城交友網站 張小戈 ======= 同城交友網站 張小戈 開發直播功能到一半 >>>>>>> Stashed changes 同城交友網站 張小戈 開發直播功能到一半 git add . git commit -m '修復bug' 其餘: git stash git stash apply ‘名稱’ git stash drop ‘名稱’ git stash list git stash pop 方式二:分支 master:只保留線上版本 dev: 保存全部開發版本 建立分支(注意當前所在分支): git branch dev 進入分支: git checkout dev 開發新功能: 開發... git add . git commit -m '開發功能1' ... 出現bug git checkout master git branch bug git checkout bug 修復bug ... git add . git commit -m '修復bug' 合併 git checkout master git merge bug 無衝突:過 有衝突: 手動解決 git add . git commit -m '解決衝突' git branch -d bug 再次回到dev,繼續開發 git checkout dev ... git add . git commit -m '開發完畢' 合併 git checkout master git merge dev 無衝突:過 有衝突: 手動解決 git add . git commit -m '解決衝突' d. 張戈,公司和家都要進行開發工做 遠程倉庫: 公共(本身): github https://code.csdn.net/ https://git.oschina.net/ 公司(gitlab): 本身搭建 1. 註冊帳號 2. 建立代碼倉庫 wwwww https://github.com/a877252372/wwwww.git 公司: git remote add origin https://github.com/a877252372/wwwww.git # origin是別名,https://github.com/a877252372/wwwww.git是代碼倉庫wwwww的url地址 git checkout master git push origin master # 推送 git checkout dev git push origin dev # 推送 下班回家 回家: git clone https://github.com/a877252372/wwwww.git cd wwwww git branch dev origin/dev git checkout dev 寫代碼 git add . git commit ... git push origin dev 公司: git checkout dev git fetch origin dev git pull origin dev 與git fetch origin dev比,勁大 功能11 git add . git commit ... git push origin dev 功能12:(忘記提交,下班回家) git add . git commit ... git push origin dev 回家: git branch dev git pull origin dev 功能13: git add . git commit ... git push origin dev 公司: 獲取代碼, git pull origin dev 無衝突:過 有衝突: 手動解決 git add . git commit -m '解決衝突' e. 協同開發 同一個遠程倉庫 A B C github: - 組織 - 邀請 1. 拉代碼 2. 開發... 3. 合併 先提交:沒問題 f. fork 1. fork別人項目 2. git clone xxxxxx 3. 修改 4. 提交【本身】 5. new pull request 6. 等 7. fork別人項目 3. 其餘 用戶名和密碼: https:... ssh ssh-keygen.exe 公鑰 私鑰 公鑰放在github git clone git@github.com:oyoui/bjstack.git git push ... 忽略文件: .gitignore https://github.com/github/gitignore/blob/master/Python.gitignore ssh公鑰獲取: git checkout xyp ssh-keygen.exe 不停的回車 cd~ cd .ssh/ ls # --->id_rsa id_rsa.pub cat id_rsa.pub # 獲取的就是公鑰 總結: - 命令 - 場景 - 衝突 - 協同開發 PS: xxx
git init #初始化 git add manage.py #提交單個文件 git add . #提交全部文件 git commit -m "all file" #提交註釋 git status #狀態 git ls-tree head #查看版本中全部文件 git ls-files -s #查看暫存區和版本中全部文件 git log #查看日誌 git reflog #查看全部日誌 #配置用戶信息 git config --local user.name '名稱' git config --local user.email '郵箱' #回滾 git log git reset --head 2b349bf #回滾 git reflog git reset --head 2b349bf #stash #git stash #將當前修改的內容保存到一個地方