本節內容 1.github介紹 很多人都知道,Linus在1991年創建了開源的Linux,從此,Linux系統不斷發展,已經成為最大的伺服器系統軟體了。 Linus雖然創建了Linux,但Linux的壯大是靠全世界熱心的志願者參與的,這麼多人在世界各地為Linux編寫代碼,那Linux的代碼是如何 ...
本節內容
- github介紹
- 安裝
- 倉庫創建& 提交代碼
- 代碼回滾
- 工作區和暫存區
- 撤銷修改
- 刪除操作
- 遠程倉庫
- 分支管理
- 多人協作
- github使用
- 忽略特殊文件.gitignore
1.github介紹
很多人都知道,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已是:
- 一個擁有143萬開發者的社區。其中不乏Linux發明者Torvalds這樣的頂級黑客,以及Rails創始人DHH這樣的年輕極客。
- 這個星球上最流行的開源托管服務。目前已托管431萬git項目,不僅越來越多知名開源項目遷入GitHub,比如Ruby on Rails、jQuery、Ruby、Erlang/OTP;近三年流行的開源庫往往在GitHub首發,例如:BootStrap、Node.js、CoffeScript等。
- alexa全球排名414的網站。
2. git安裝
安裝Git
最早Git是在Linux上開發的,很長一段時間內,Git也只能在Linux和Unix系統上跑。不過,慢慢地有人把它移植到了Windows上。現在,Git可以在Linux、Unix、Mac和Windows這幾大平臺上正常運行了。
要使用Git,第一步當然是安裝Git了。根據你當前使用的平臺來閱讀下麵的文字:
在Linux上安裝Git
首先,你可以試著輸入git
,看看系統有沒有安裝Git:
1 2 3 |
$ 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的安裝,非常簡單。
3.版本庫創建
什麼是版本庫呢?版本庫又名倉庫,英文名repository,你可以簡單理解成一個目錄,這個目錄裡面的所有文件都可以被Git管理起來,每個文件的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。
所以,創建一個版本庫非常簡單,首先,選擇一個合適的地方,創建一個空目錄:
1 2 3 4 5 |
$ 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文件,內容如下:
1 2 3 4 |
$ vim first_git_file.txt
first time using git, excited!
第一次用git哈哈
|
一定要放到git_trainning目錄下(子目錄也行),因為這是一個Git倉庫,放到其他地方Git再厲害也找不到這個文件。
和把大象放到冰箱需要3步相比,把一個文件放到Git倉庫只需要兩步。
第一步,用命令git add
告訴Git,把文件添加到倉庫:
1 |
$ git add first_git_file.txt
|
執行上面的命令,沒有任何顯示,說明添加成功。
第二步,用命令git commit
告訴Git,把文件提交到倉庫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ 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
< /alex @alexs-macbook-pro. local >
|
中間紅色部分的意思是,你在往git庫里提交代碼時,你需要告訴git你是誰,這樣git就會紀錄下來是誰改的代碼,其實就是為了日後查詢方便,你只需要提供一個名字和郵件地址就可以,這裡我的git直接通過主機名自己創建了一個,但你可以通過git config --global --edit修改
簡單解釋一下git commit
命令,-m
後面輸入的是本次提交的說明,可以輸入任意內容,當然最好是有意義的,這樣你就能從歷史記錄里方便地找到改動記錄。
嫌麻煩不想輸入-m "xxx"
行不行?確實有辦法可以這麼乾,但是強烈不建議你這麼乾,因為輸入說明對自己對別人閱讀都很重要。
為什麼Git添加文件需要add
,commit
一共兩步呢?因為commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如:
1 2 3 |
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
|
4. 代碼回滾
4.1代碼修改並提交
我們已經成功地添加並提交了一個first_git_file.txt文件,現在,是時候繼續工作了,於是,我們繼續修改first_git_file.txt文件,改成如下內容:
1 2 3 |
First time using git, excited! update ...
insert line here...
第一次用git哈哈
|
現在,運行git status
命令看看結果:
1 2 3 4 5 6 7 8 9 |
$ 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
這個命令看看:
1 2 3 4 5 6 7 8 9 10 11 |
$ 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
:
1 2 3 4 5 |
$ 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
命令看看倉庫的當前狀態:
1 2 3 |
$ git status
# On branch master
nothing to commit (working directory clean)
|
Git告訴我們當前沒有需要提交的修改,而且,工作目錄是乾凈(working directory clean)的。
4.2 代碼回滾
現在,你已經學會了修改文件,然後把修改提交到Git版本庫,現在,再練習一次,修改first_git_file.txtt文件如下:
1 2 3 4 5 |
First time using git, excited! update ...
insert line here..改之前的.
第一次用git哈哈
insert line again haha...
加點新內容
|
然後嘗試提交:
1 2 3 4 5 |
$ 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
1 2 |
first time using git, excited!
第一次用git哈哈
|
版本2
1 2 3 |
first time using git, excited!
insert line here...
第一次用git哈哈
|
版本3
1 2 3 4 |
first time using git, excited!
insert line here...
第一次用git哈哈
insert line again haha...
|
版本4
1 2 3 4 5 |
First time using git, excited! update ...
insert line here..改之前的.
第一次用git哈哈
insert line again haha...
加點新內容
|
當然了,在實際工作中,我們腦子裡怎麼可能記得一個幾千行的文件每次都改了什麼內容,不然要版本控制系統乾什麼。版本控制系統肯定有某個命令可以告訴我們歷史記錄,在Git中,我們用git log
命令查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ 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
參數:
1 2 3 4 5 |
$ 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
命令:
1 2 |
$ git reset --hard HEAD^
HEAD is now at be02137 update again
|
此時再看你的文件內容,果然就退回去了
1 2 3 4 5 |
$ more first_git_file.txt
First time using git, excited! update ...
insert line here...
第一次用git哈哈
insert line again haha...
|
此時還可以繼續再往前回退一個版本,不過且慢,然我們用git log
再看看現在版本庫的狀態:
1 2 3 4 |
$ 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
,於是就可以指定回到未來的某個版本:
1 2 |
git reset --hard 4459657
HEAD is now at 4459657 add new content
|
版本號沒必要寫全,前幾位就可以了,Git會自動去找。當然也不能只寫前一兩位,因為Git可能會找到多個版本號,就無法確定是哪一個了。
再小心翼翼地看看first_git_file.txt的內容:
1 2 3 4 5 |
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用來記錄你的每一次命令:
1 2 3 4 5 6 7 8 9 10 11 |
$ 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,現在,你又可以乘坐時光機回到未來了。
5. 工作區和暫存區
Git和其他版本控制系統如SVN的一個不同之處就是有暫存區的概念。
先來看名詞解釋。
工作區(Working Directory)
就是你在電腦里能看到的目錄,比如我的git_trainning文件夾就是一個工作區:
1 2 |
$ 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
做個修改,比如加上一行內容:
1 2 3 4 5 6 |
First time using git, excited! update ...
insert line here..改之前的.
第一次用git哈哈
insert line again haha...
加點新內容
update v5
|
然後,在工作區新增一個readme.md
文本文件(內容隨便寫)。
先用git status
查看一下狀態:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ 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
再查看一下:
1 2 3 4 5 6 7 8 9 |
$ 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
< /file >
|
現在,暫存區的狀態就變成這樣了:
(盜圖關係, 這裡readme.txt = first_git_file.txt , LICENSE = readme.md)
所以,git add
命令實際上就是把要提交的所有修改放到暫存區(Stage),然後,執行git commit
就可以一次性把暫存區的所有修改提交到分支。
1 2 3 4 |
$ git commit -m "知道暫存區stage的意思了"
[master 9d65cb2] 知道暫存區stage的意思了
2 files changed, 2 insertions(+)
create mode 100644 readme.md
|
一旦提交後,如果你又沒有對工作區做任何修改,那麼工作區就是“乾凈”的:
1 2 3 |
$ git status
On branch master
nothing to commit, working directory clean
|
現在版本庫變成了這樣,暫存區就沒有任何內容了:
(盜圖關係, 這裡readme.txt = first_git_file.txt , LICENSE = readme.md)
暫存區是Git非常重要的概念,弄明白了暫存區,就弄明白了Git的很多操作到底幹了什麼。
6. 撤銷修改
自然,你是不會犯錯的。不過現在是凌晨兩點,你正在趕一份工作報告,你在readme.md
中添加了一行:
1 2 3 |
#git study repo
git is great
but my stupid boss still prefers SVN.
|
在你準備提交前,一杯咖啡起了作用,你猛然發現了“stupid boss”可能會讓你丟掉這個月的獎金!
既然錯誤發現得很及時,就可以很容易地糾正它。你可以刪掉最後一行,手動把文件恢復到上一個版本的狀態。如果用git status
查看一下: