Git的使用心得
由于公司對我的代碼管理不嚴謹,所以荒廢了好久的Git,一直就沒怎么用,后面也多忘光了,所以再次重新梳理了下Git的使用,主要是從一個iOS開發(fā)者的角度去整理的(其實其他的也都差不多,只是MAC的環(huán)境差異罷了,Windows下的貌似不太好搞),這里會羅列一些常用的命令,方便以后自己查找,如果有不對的地方,望請各位大神指教。
本文中的命令指示基本的常用命令,想要系統(tǒng)的學習的,可以參照廖雪峰大大的文章,個人覺得應該可以說是最最完整、易懂的中文Git教程,非常完整。
一、Git的安裝
在Linux上安裝Git,首先,你可以試著輸入git,看看系統(tǒng)有沒有安裝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的安裝,非常簡單。
老一點的Debian或Ubuntu Linux,要把命令改為sudo apt-get install git-core,因為以前有個軟件也叫GIT(GNU Interactive Tools),結果Git就只能叫git-core了。由于Git名氣實在太大,后來就把GNU Interactive Tools改成gnuit,git-core正式改為git。
如果是其他Linux版本,可以直接通過源碼安裝。先從Git官網(wǎng)下載源碼,然后解壓,依次輸入:./config,make,sudo make install這幾個命令安裝就好了。
在Mac OS X上安裝Git
如果你正在使用Mac做開發(fā),有兩種安裝Git的方法。
一是安裝homebrew,然后通過homebrew安裝Git,具體方法請參考homebrew的文檔:http://brew.sh/。
第二種方法更簡單,也是推薦的方法,就是直接從AppStore安裝Xcode,Xcode集成了Git,不過默認沒有安裝,你需要運行Xcode,選擇菜單Xcode->Preferences,在彈出窗口中找到Downloads,選擇Command Line Tools,點Install就可以完成安裝了。
安裝完成后,還需要最后一步設置,在命令行輸入:
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
("Your Name"和"email@example.com"請換成您自己的信息,別亂填,不然忘了也不好 )
因為Git是分布式版本控制系統(tǒng),所以,每個機器都必須自報家門:你的名字和Email地址。你也許會擔心,如果有人故意冒充別人怎么辦?這個不必擔心,首先我們相信大家都是善良無知的群眾,其次,真的有冒充的也是有辦法可查的。
注意git config命令的--global參數(shù),用了這個參數(shù),表示你這臺機器上所有的Git倉庫都會使用這個配置,當然也可以對某個倉庫指定不同的用戶名和Email地址。
二、創(chuàng)建版本庫
什么是版本庫呢?版本庫又名倉庫,英文名repository,你可以簡單理解成一個目錄,這個目錄里面的所有文件都可以被Git管理起來,每個文件的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。
所以,創(chuàng)建一個版本庫非常簡單:
首先,選擇一個合適的地方,創(chuàng)建一個空目錄:
$ mkdir learngit
$ cd learngit
$ pwd
/Users/michael/learngit
pwd命令用于顯示當前目錄。在我的Mac上,這個倉庫位于/Users/michael/learngit。
第二步,通過git init命令把這個目錄變成Git可以管理的倉庫:
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
瞬間Git就把倉庫建好了,而且告訴你是一個空的倉庫(empty Git repository),細心的讀者可以發(fā)現(xiàn)當前目錄下多了一個.git的目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄里面的文件,不然改亂了,就把Git倉庫給破壞了。
如果你沒有看到.git目錄,那是因為這個目錄默認是隱藏的,用ls -ah命令就可以看見。
也不一定必須在空目錄下創(chuàng)建Git倉庫,選擇一個已經(jīng)有東西的目錄也是可以的。不過,不建議你使用自己正在開發(fā)的公司項目來學習Git,否則造成的一切后果概不負責。
現(xiàn)在我們編寫一個readme.txt文件,內容如下:
Git is a version control system.
Git is free software.
一定要放到learngit目錄下(子目錄也行),因為這是一個Git倉庫,放到其他地方Git再厲害也找不到這個文件。
第一步,用命令git add告訴Git,把文件添加到倉庫:
$ git add readme.txt
第二步,用命令git commit告訴Git,把文件提交到倉庫:
$ git commit -m "wrote a readme file"
[master (root-commit) cb926e7] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
簡單解釋一下git commit命令,-m后面輸入的是本次提交的說明,可以輸入任意內容,當然最好是有意義的,這樣你就能從歷史記錄里方便地找到改動記錄。
git commit命令執(zhí)行成功后會告訴你,1個文件被改動(我們新添加的readme.txt文件),插入了兩行內容(readme.txt有兩行內容)。
為什么Git添加文件需要add,commit一共兩步呢?因為commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
三、時光穿梭
我們已經(jīng)成功地添加并提交了一個readme.txt文件,現(xiàn)在,是時候繼續(xù)工作了,于是,我們繼續(xù)修改readme.txt文件,改成如下內容:
Git is a distributed version control system.
Git is free software.
現(xiàn)在,運行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.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
git status命令可以讓我們時刻掌握倉庫當前的狀態(tài),上面的命令告訴我們,readme.txt被修改過了,但還沒有準備提交的修改。
具體修改了什么內容,需要用git diff這個命令看看:
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
git diff顧名思義就是查看difference,顯示的格式正是Unix通用的diff格式,可以從上面的命令輸出看到,我們在第一行添加了一個distributed單詞。
知道了對readme.txt作了什么修改后,再把它提交到倉庫就放心多了,提交修改和提交新文件是一樣的兩步,第一步是git add:
$ git add readme.txt
同樣沒有任何輸出。在執(zhí)行第二步git commit之前,我們再運行git status看看當前倉庫的狀態(tài):
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: readme.txt
#
git status告訴我們,將要被提交的修改包括readme.txt,下一步,就可以放心地提交了:
$ git commit -m "add distributed"
[master ea34578] add distributed
1 file changed, 1 insertion(+), 1 deletion(-)
提交后,我們再用git status命令看看倉庫的當前狀態(tài):
$ git status
# On branch master
nothing to commit (working directory clean)
Git告訴我們當前沒有需要提交的修改,而且,工作目錄是干凈(working directory clean)的。
版本回退
現(xiàn)在,再練習一次,修改readme.txt文件如下:
Git is a distributed version control system.
Git is free software distributed under the GPL.
然后嘗試提交:
$ git add readme.txt
$ git commit -m "append GPL"
[master 3628164] append GPL
1 file changed, 1 insertion(+), 1 deletion(-)
版本控制系統(tǒng)肯定有某個命令可以告訴我們歷史記錄,在Git中,我們用git log命令查看:
$ git log
commit 3628164fb26d48395383f8f31179f24e0882e1e0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Tue Aug 20 15:11:49 2013 +0800
append GPL
commit ea34578d5496d7dd233c827ed32a8cd576c5ee85
Author: Michael Liao <askxuefeng@gmail.com>
Date: Tue Aug 20 14:53:12 2013 +0800
add distributed
commit cb926e7ea50ad11b8f9e909c05226233bf755030
Author: Michael Liao <askxuefeng@gmail.com>
Date: Mon Aug 19 17:51:55 2013 +0800
wrote a readme file
git log命令顯示從最近到最遠的提交日志,我們可以看到3次提交,最近的一次是append GPL,上一次是add distributed,最早的一次是wrote a readme file。
如果嫌輸出信息太多,看得眼花繚亂的,可以試試加上--pretty=oneline參數(shù):
$ git log --pretty=oneline
3628164fb26d48395383f8f31179f24e0882e1e0 append GPL
ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed
cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file
好了,現(xiàn)在我們準備把readme.txt回退到上一個版本,也就是“add distributed”的那個版本,怎么做呢?
首先,Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一樣),上一個版本就是HEAD^,上上一個版本就是HEAD^^,當然往上100個版本寫100個^比較容易數(shù)不過來,所以寫成HEAD~100。
現(xiàn)在,我們要把當前版本append GPL回退到上一個版本add distributed,就可以使用git reset命令:
$ git reset --hard HEAD^
HEAD is now at ea34578 add distributed
看看readme.txt的內容是不是版本add distributed:
$ cat readme.txt
Git is a distributed version control system.
Git is free software.
果然。
還可以繼續(xù)回退到上一個版本wrote a readme file,不過且慢,然我們用git log再看看現(xiàn)在版本庫的狀態(tài):
$ git log
commit ea34578d5496d7dd233c827ed32a8cd576c5ee85
Author: Michael Liao <askxuefeng@gmail.com>
Date: Tue Aug 20 14:53:12 2013 +0800
add distributed
commit cb926e7ea50ad11b8f9e909c05226233bf755030
Author: Michael Liao <askxuefeng@gmail.com>
Date: Mon Aug 19 17:51:55 2013 +0800
wrote a readme file
最新的那個版本append GPL已經(jīng)看不到了!想再回去已經(jīng)回不去了,腫么辦?辦法其實還是有的,只要上面的命令行窗口還沒有被關掉,你就可以順著往上找啊找啊,找到那個append GPL的commit id是3628164...,于是就可以指定回到未來的某個版本:
$ git reset --hard 3628164
HEAD is now at 3628164 append GPL
版本號沒必要寫全,前幾位就可以了,Git會自動去找。當然也不能只寫前一兩位,因為Git可能會找到多個版本號,就無法確定是哪一個了。
再小心翼翼地看看readme.txt的內容:
$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
現(xiàn)在,你回退到了某個版本,關掉了電腦,第二天早上就后悔了,想恢復到新版本怎么辦?找不到新版本的commit id怎么辦?
在Git中,總是有后悔藥可以吃的。當你用$ git reset --hard HEAD^回退到add distributed版本時,再想恢復到append GPL,就必須找到append GPL的commit id。Git提供了一個命令git reflog用來記錄你的每一次命令:
$ git reflog
ea34578 HEAD@{0}: reset: moving to HEAD^
3628164 HEAD@{1}: commit: append GPL
ea34578 HEAD@{2}: commit: add distributed
cb926e7 HEAD@{3}: commit (initial): wrote a readme file
終于舒了口氣,第二行顯示append GPL的commit id是3628164,現(xiàn)在,你又可以回去了。
用git diff HEAD -- readme.txt命令可以查看工作區(qū)和版本庫里面最新版本的區(qū)別:
$ git diff HEAD -- readme.txt
diff --git a/readme.txt b/readme.txt
index 76d770f..a9c5755 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,4 +1,4 @@
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.
+Git tracks changes of files.
撤銷修改
git checkout -- file可以丟棄工作區(qū)的修改:
$ git checkout -- readme.txt
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作區(qū)的修改全部撤銷,這里有兩種情況:
一種是readme.txt自修改后還沒有被放到暫存區(qū),現(xiàn)在,撤銷修改就回到和版本庫一模一樣的狀態(tài);
一種是readme.txt已經(jīng)添加到暫存區(qū)后,又作了修改,現(xiàn)在,撤銷修改就回到添加到暫存區(qū)后的狀態(tài)。
總之,就是讓這個文件回到最近一次git commit或git add時的狀態(tài)。
Git同樣告訴我們,用命令git reset HEAD file可以把暫存區(qū)的修改撤銷掉(unstage),重新放回工作區(qū):
$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt
git reset命令既可以回退版本,也可以把暫存區(qū)的修改回退到工作區(qū)。當我們用HEAD時,表示最新的版本。
再用git status查看一下,現(xiàn)在暫存區(qū)是干凈的,工作區(qū)有修改:
$ 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.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
還記得如何丟棄工作區(qū)的修改嗎?
$ git checkout -- readme.txt
$ git status
# On branch master
nothing to commit (working directory clean)
刪除操作
在Git中,刪除也是一個修改操作,我們實戰(zhàn)一下,先添加一個新文件test.txt到Git并且提交:
$ git add test.txt
$ git commit -m "add test.txt"
[master 94cdc44] add test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt
一般情況下,你通常直接在文件管理器中把沒用的文件刪了,或者用rm命令刪了:
$ rm test.txt
這個時候,Git知道你刪除了文件,因此,工作區(qū)和版本庫就不一致了,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")
現(xiàn)在你有兩個選擇,一是確實要從版本庫中刪除該文件,那就用命令git rm刪掉,并且git commit:
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d17efd8] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
現(xiàn)在,文件就從版本庫中被刪除了。
另一種情況是刪錯了,因為版本庫里還有呢,所以可以很輕松地把誤刪的文件恢復到最新版本:
$ git checkout -- test.txt
git checkout其實是用版本庫里的版本替換工作區(qū)的版本,無論工作區(qū)是修改還是刪除,都可以“一鍵還原”。
命令git rm用于刪除一個文件。如果一個文件已經(jīng)被提交到版本庫,那么你永遠不用擔心誤刪,但是要小心,你只能恢復文件到最新版本,你會丟失最近一次提交后你修改的內容。
四、遠程倉庫
添加遠程倉庫
我們可以自己搭建一臺運行Git的服務器,不過現(xiàn)階段,為了學Git先搭個服務器絕對是小題大作。好在這個世界上有個叫GitHub的神奇的網(wǎng)站,從名字就可以看出,這個網(wǎng)站就是提供Git倉庫托管服務的,所以,只要注冊一個GitHub賬號,就可以免費獲得Git遠程倉庫。
在繼續(xù)閱讀后續(xù)內容前,請自行注冊GitHub賬號。由于你的本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密的,所以,需要一點設置:
第1步:創(chuàng)建SSH Key。在用戶主目錄下,看看有沒有.ssh目錄,如果有,再看看這個目錄下有沒有id_rsa和id_rsa.pub這兩個文件,如果已經(jīng)有了,可直接跳到下一步。如果沒有,打開Shell(Windows下打開Git Bash),創(chuàng)建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"
你需要把郵件地址換成你自己的郵件地址,然后一路回車,使用默認值即可,由于這個Key也不是用于軍事目的,所以也無需設置密碼。
如果一切順利的話,可以在用戶主目錄里找到.ssh目錄(切換回主目錄命令cd ~),ls -ah查看里面有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文件的內容(cat id_rsa.pub命令):
點“Add Key”,你就應該看到已經(jīng)添加的Key:
為什么GitHub需要SSH Key呢?因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協(xié)議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。
當然,GitHub允許你添加多個Key。假定你有若干電腦,你一會兒在公司提交,一會兒在家里提交,只要把<u>每臺電腦的Key都添加到GitHub</u>,就可以在每臺電腦上往GitHub推送了。
最后友情提示,在GitHub上<u>免費托管</u>的Git倉庫,<u>任何人</u>都可以看到喔(但只有你自己才能改)。所以,<u>不要把敏感信息放進去</u>。
如果你不想讓別人看到Git庫,有三個辦法,一個是交點保護費,讓GitHub把公開的倉庫變成私有的,這樣別人就看不見了(不可讀更不可寫)。第二個辦法是自己動手,搭一個Git服務器,因為是你自己的Git服務器,所以別人也是看不見的。這個方法我們后面會講到的,相當簡單,公司內部開發(fā)必備。第三個辦法,就是換一個托管,用碼云托管,在創(chuàng)建項目的時候,把項目屬性設為私有即可,也很簡單,但是不推薦把公司項目放上去,畢竟不是自己的東西,所以<u>公司開發(fā),最好最好還是要自己搭建一個Git服務器</u>,如果把公司項目放上去之后造成的一切結果概不負責。
現(xiàn)在的情景是,你已經(jīng)在本地創(chuàng)建了一個Git倉庫后,又想在GitHub創(chuàng)建一個Git倉庫,并且讓這兩個倉庫進行遠程同步,這樣,GitHub上的倉庫既可以作為備份,又可以讓其他人通過該倉庫來協(xié)作,真是一舉多得。
首先,登陸GitHub,然后,在右上角找到Create a new repo按鈕,創(chuàng)建一個新的倉庫:
在Repository name填入
learngit,其他保持默認設置,點擊Create repository按鈕,就成功地創(chuàng)建了一個新的Git倉庫:目前,在GitHub上的這個
learngit倉庫還是空的,GitHub告訴我們,可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫與之關聯(lián),然后,把本地倉庫的內容推送到GitHub倉庫。
現(xiàn)在,我們根據(jù)GitHub的提示,在<u>本地</u>的learngit倉庫下運行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git
請千萬注意,把上面的michaelliao替換成你自己的GitHub賬戶名,否則,你在本地關聯(lián)的就是我的遠程庫,關聯(lián)沒有問題,但是你以后推送是推不上去的,因為你的SSH Key公鑰不在我的賬戶列表中。
添加后,遠程庫的名字就是origin,這是Git默認的叫法,也可以改成別的,但是origin這個名字一看就知道是遠程庫。
下一步,就可以把本地庫的所有內容推送到遠程庫上:
$ git push -u origin master
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (19/19), 13.73 KiB, done.
Total 23 (delta 6), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
把本地庫的內容推送到遠程,用git push命令,實際上是把當前分支master推送到遠程。
由于遠程庫是空的,我們第一次推送master分支時,加上了-u參數(shù),Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯(lián)起來,在以后的推送或者拉取時就可以簡化命令。
推送成功后,可以立刻在GitHub頁面中看到遠程庫的內容已經(jīng)和本地一模一樣:
從現(xiàn)在起,只要本地作了提交,就可以通過命令:
$ git push origin master
把本地master分支的最新修改推送至GitHub,現(xiàn)在,你就擁有了真正的分布式版本庫!
從遠程倉庫克隆
假設我們從零開發(fā),那么最好的方式是先創(chuàng)建遠程庫,然后,從遠程庫克隆。
首先,登陸GitHub,創(chuàng)建一個新的倉庫,名字叫gitskills:
我們勾選
Initialize this repository with a README,這樣GitHub會自動為我們創(chuàng)建一個README.md文件。創(chuàng)建完畢后,可以看到README.md文件:現(xiàn)在,遠程庫已經(jīng)準備好了,下一步是用命令
git clone克隆一個本地庫:
$ git clone git@github.com:michaelliao/gitskills.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
$ cd gitskills
$ ls
README.md
注意把Git庫的地址換成你自己的,然后進入gitskills目錄看看,已經(jīng)有README.md文件了。