Git使用

聲明:這篇文章來源于廖雪峰老師的官方網(wǎng)站,我僅僅是作為學習之用

Git簡介

Git是什么?

Git是目前世界上最先進的分布式版本控制系統(tǒng)。

什么是版本控制系統(tǒng)?

Git的誕生

很多人知道,Linus在1991年開源了Linux,從此,Linux系統(tǒng)不斷發(fā)展,已經(jīng)成為最大的服務器系統(tǒng)軟件了。

Linus雖然創(chuàng)建了Linux,但Linux的壯大是靠全世界熱心的志愿者參與的,這么多人在世界各地為Linux編寫代碼,那么Linux的代碼是如何管理的呢?

事實上,在2002年以前,代碼作者把源文件以diff的方式發(fā)給Linus,然后由Linus本人通過手工的方式合并代碼。

當時也有免費的CVS、SVN這些免費的版本控制系統(tǒng)。但是Linus堅決的反對CVS、SVN,這些集中式的版本控制系統(tǒng)不但速度慢,而且必須聯(lián)網(wǎng)才能使用。

不過到了2002年,經(jīng)過十年的發(fā)展,通過手工方式管理代碼已經(jīng)相當累人啦,而且社區(qū)的兄弟們也對這種方式表達了強烈的不滿,于是Linus選擇了一個商業(yè)的版本控制系統(tǒng)BitKeeper。BitKeeper的東家BitMover公司出于人道精神,授權(quán)Linux社區(qū)免費使用這個版本控制系統(tǒng)。

安定團結(jié)的局面在2005年被打破了,原因是開發(fā)Samba的Andrew試圖破解BitKeeper的協(xié)議,被BitMover公司發(fā)現(xiàn)了,要收回Linux社區(qū)的免費使用權(quán)。

于是Linus花了兩周的時間自己用C寫了一個分布式的版本控制系統(tǒng),這就是Git!一個月之內(nèi),Linux系統(tǒng)的源碼就可以由Git管理了!

Git迅速成為最流行的分布式版本控制系統(tǒng),尤其是2008年,GitHub網(wǎng)站上線了,它為開源項目提供免費Git存儲,無數(shù)開源項目開始遷移至GitHub,包括JQuery,PHP,Ruby等等。

歷史就是這么偶然,如果不是當年BitMover的決絕,那么現(xiàn)在我們可能就沒有免費而且超級好用的Git了。

集中式VS分布式

Linus一直痛恨的CVS和SVN都是集中式的版本控制系統(tǒng),而Git是分布式的版本控制系統(tǒng),集中式和分布式的版本控制系統(tǒng)有什么分別呢?

先說集中式版本控制系統(tǒng),版本庫是放在中央服務器上的,而干活的時候,用的都是自己的電腦,就需要把版本先從中央服務器取得最新的版本,然后開始干活。干完活了,再把自己的勞動成果推給中央服務器。

集中式版本控制系統(tǒng)最大的問題就是必須聯(lián)網(wǎng)才能工作。如果在局域網(wǎng)內(nèi)還好說,帶寬夠大,速度夠大。可如果在互聯(lián)網(wǎng)上,遭遇網(wǎng)速慢的情況,可能提交一個10M的文件就需要5分鐘。5分鐘可以憋死一頭牛啦。

分布式版本控制系統(tǒng)呢?它沒有“中央服務器”,每個人的電腦上都是一個完整的版本庫,這樣你干活的時候就不需要聯(lián)網(wǎng)啦,因為版本庫就在本地啊。既然每個人的電腦上都有一個版本庫,那么如何多人協(xié)作呢?比方說你在自己的電腦上修改了A文件,你的同事也在他的電腦上修改了A文件;你們只需要把修改的地方推給對方就可以互相看到對方的修改了。

和集中式版本控制系統(tǒng)相比,分布式版本控制系統(tǒng)的安全性不知道要高到哪里去。因為每個人電腦里都有完整的版本庫,某個人的電腦壞了也不要緊,隨便從其他人那里復制一個就可以。而集中式版本控制器的中央服務器要是出了問題,所有人都沒法干活了。

在實際使用分布式版本控制系統(tǒng)的時候,其實很少在兩人之間的電腦上推送版本庫的修改,因為可能你們倆不在同一個局域網(wǎng)內(nèi),兩臺電腦互相訪問不了;也可能你的同事今天生了病,根本就沒開機。因此,分布式版本控制系統(tǒng)通常也有一臺充當“中央服務器”的電腦,但這個服務器的作用僅僅是用來方便“交換”大家的修改;沒有它大家一樣干活,只是交換修改不方便了而已。

當然Git優(yōu)勢不單是不必聯(lián)網(wǎng)這么簡單,后面我們還會看到Git極其強大的分支管理,把SVN等遠遠拋在了后面。

安裝Git

在Linux上安裝Git

如果你碰巧使用Debian或Ubuntu Linux,通過一條 sudo apt-get install git 就可以完成git的安裝,非常之簡單。

如果是其它Linux的版本,可以直接通過源碼安裝。先從Git官網(wǎng)下載源碼,然后解壓,依次輸入: ./config, make, sudo make install 這幾個命令就安裝好了。

在Mac上安裝Git

推薦一種簡單的方法。直接從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"

因為Git是分布式版本控制系統(tǒng),所以每個機器都必須自報家門。

注意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倉庫就建好了,而且告訴你是一個空倉庫,多出來的 .git目錄是來跟蹤管理版本庫的,里面的文件不能手動修改,否則Git倉庫會遭到破壞。

如果沒有看到.git目錄,那是因為這個目錄默認是隱藏的,用ls -ah命令就可以看見。

把文件添加到版本庫

首先聲明一點,所有的版本控制系統(tǒng),其實只能跟蹤文本文件的改變,比如txt文件,網(wǎng)頁,所有的程序代碼等等。Git也不例外。版本控制系統(tǒng)可以告訴你每次的改動,比如第5行增加了一個單詞“Linux”,而在第8行刪除了一個單詞“Windows”等等...。而圖片、視頻這些二進制文件雖然也能由版本控制系統(tǒng)管理,但沒法跟蹤文件的改變,只能把二進制文件每次改動串起來,也就是只知道圖片從100KB改成了120KB,但是到底改了啥,版本控制系統(tǒng)不知道,也沒法知道。

不幸的是Microsoft的Word格式是二進制格式,因此版本控制系統(tǒng)無法跟蹤Word的改變。如果要真正實用版本控制系統(tǒng),就要以純文本的方式編寫文件。因為文本是有編碼的,如中文有常用的GBK編碼,日文有常用的Shift_JIS編碼,如果沒有歷史遺留問題,強烈推薦使用標準的UTF-8編碼,所有的語言使用同一種編碼,既沒有沖突,又被所有平臺所支持。

另外注意千萬不要用windows自帶的記事本 編輯任何文本文件。原因是記事本保存UTF-8編碼文件時會在每個文件開頭添加0xefbbbf字符。

言歸正傳,現(xiàn)在編寫一個readme.txt文件,內(nèi)容如下:

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 10064 readme.txt

簡單解釋一下git commit命令,-m后面輸入的是本次提交的說明,這樣你就能從歷史紀錄中方便的查找改動紀錄。

時光穿梭機

我們已經(jīng)添加并提交了一個readme.txt文件,現(xiàn)在我們修改readme.txt,改成如下內(nèi)容:

Git is a distributed version control system.
Git is free software.

現(xiàn)在運行git status 命令。git status命令可以讓我們看到倉庫的狀態(tài),上面的命令告訴我們,readme.txt被修改過了。并且告訴我們后序的處理方案。git add將修改添加到倉庫,git checkout丟棄修改。

git status只能告訴我們修改了哪些文件,但具體修改了文件的哪些內(nèi)容就需要用git diff這個命令了。

提交修改

提交修改和提交新文件是一樣的兩個步驟。

第一步 是git add

$ git add readme.txt

在執(zhí)行第二步git commit之前,我們用git status查看倉庫狀態(tài)。git commit命令告訴我們后序的操作,看來Git還是很友好的。

$ git commit -m "add distributed"

提交后,我們再用git status命令查看倉庫狀態(tài)。

$ git status
# On branch master
nothing to commit (working directory clean)

Git 告訴我們當前沒有需要提交的修改,工作目錄是干凈的。

小結(jié)

  • 隨時掌握工作區(qū)狀態(tài),使用git status命令
  • git diff可以查看具體的修改內(nèi)容

版本回退


現(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"

像這樣可以不斷修改不斷提交到版本庫,這樣Git就能跟蹤文本的改變。當你覺得文件修改到一定程度的時候,就可以“保存一個快照”,這個快找在Git中成為commit。一旦你把文件改亂了,或者誤刪了文件,就可以從最近的一個commit恢復,然后繼續(xù)工作。這樣就不會把前幾個月的工作成果丟失。

現(xiàn)在我們的Git倉庫中有三個版本啦。

版本1: wrote a readme file

版本2: add distributed

版本3: append GPL

查看歷史紀錄

版本控制系統(tǒng)有某個命令可以查看歷史紀錄,在Git中git log就是也。

$ git log
commit 8ae8b878c335c5ae8ed381460345d2b03e8e3c6e
Author: zhangxiaojun <adam.zhang@ydx.hk>
Date:   Fri Mar 11 00:01:24 2016 +0800
    
    append GPL

commit 3eb82b8e2c94fbc5fdb0ca248fa7c15789df922c
Author: zhangxiaojun <adam.zhang@ydx.hk>
Date:   Thu Mar 10 23:59:53 2016 +0800

    add distributed

commit e9da25c20a570b2f6280d3bb47a5e0d3935a5a8b
Author: zhangxiaojun <adam.zhang@ydx.hk>
Date:   Thu Mar 10 23:58:00 2016 +0800

    wrote a readme file

git log可以顯示從最近到最遠的提交日至。如果嫌棄輸出太多的信息,可以加上--pretty=oneline參數(shù):

$ git log --pretty=oneline
8ae8b878c335c5ae8ed381460345d2b03e8e3c6e append GPL
3eb82b8e2c94fbc5fdb0ca248fa7c15789df922c add distributed
e9da25c20a570b2f6280d3bb47a5e0d3935a5a8b wrote a readme file

版本號

一大串類似8ae8b87...d2b03ec6e的數(shù)字是commit id(版本號)。和SVN不一樣,Git的commit id不是1、2、3...的遞增數(shù)字,而是由SHA1計算出來的一個非常大的數(shù)字,用十六進制表示。為什么commit id需要一串這么大的數(shù)字來表示呢?因為Git是分布式控制系統(tǒng),后面我們還要研究多個人在同一個版本庫工作,如果大家都用1、2、3...作為版本號,那么肯定會引發(fā)沖突。

每提交一個版本,實際Git就會把它們自動串成一條時間線。如果使用可視化工具查看Git歷史,就可以清楚地看到提交歷史的時間線。

時光穿梭機

首先Git必須要知道當前版本是哪個版本,在Git中用HEAD表示當前版本,也就是最新的commit id,上一個版本用HEAD^表示,上上個版本是HEAD^^,再往上一個版本就是HEARD~3

現(xiàn)在我們從當前版本“append GPL”退回到上一個版本“add distributed”,就可以使用git reset命令:

$ git reset --hard HEAD^
HEAD is now at 3eb82b8 add distributed

最新版本"append GPL"已經(jīng)不見了!好比從21世紀坐時光穿梭機回到了19世紀,現(xiàn)在又想回去了,腫么辦?

辦法還是有的,只要剛才的命令行窗口沒有關,可以往上找到"append GPL"對應的commit id,于是就可以回到未來的某個版本

$ git reset --hard 8ae8
HEAD is now at 8ae8b87 append GPL

Git的版本回退速度是非常快,因為Git在內(nèi)部有個指向當前版本的HEAD指針,當你回退版本時,Git僅僅是把HEAD指向append GPL。然后順便把工作區(qū)的文件更新了,所以你讓HEAD指向哪個版本號,你就把當前版本定位在了哪里。

現(xiàn)在你回退到了某個版本,關掉了電腦,第二天早上就后悔了,想要恢復到最新的版本怎么辦?找不到新版本的commit id怎么辦?

在Git中總是有后悔藥可醫(yī)吃的。Git提供了一個命令git reflog來紀錄你的每一次命令:

$ git reflog
e9da25c HEAD@{0}: reset: moving to HEAD^^
8ae8b87 HEAD@{1}: reset: moving to 8ae8
3eb82b8 HEAD@{2}: reset: moving to HEAD^
8ae8b87 HEAD@{3}: commit: append GPL
3eb82b8 HEAD@{4}: commit: add distributed
e9da25c HEAD@{5}: commit (initial): wrote a readme file

長出一口氣,第5行顯示 append GPLcommit id8ae8b87。現(xiàn)在我們可以乘坐時光機回到未來了。

小結(jié)

小結(jié)一下

  • HEAD指向的版本就是當前版本,Git允許我們在版本之間時光穿梭,使用命令git reset --hard commit_id
  • 穿梭前,用git log查看提交歷史紀錄,以便確認要退到哪個版本。
  • 要重返未來,用git reflog查看命令歷史,以便確定回到未來的哪個版本。

遠程倉庫


用過集中式版本管理系統(tǒng)的同學會說,前面這些功能SVN里早就有了,沒有看出Git有什么特別的地方。

沒錯,如果只是一個倉庫管理文件歷史,Git和SVN真的沒啥區(qū)別。為了保證你學習Git物超所值,將來絕對不會后悔,我們開始介紹Git的殺手級功能之一:遠程倉庫

Git是分布式版本控制系統(tǒng),同一個Git倉庫,可以分布到不同的機器上。怎么分布呢?最早,肯定只有一臺機器有一個原始版本,此后,別的機器可以“克隆”這個原始版本庫,而且每臺機器的版本庫其實都是一樣的,并沒有主次之分。

你肯定會想,至少需要兩臺機器才能玩遠程庫不是?但是直有一臺電腦怎么玩?

  • 其實一臺電腦也可以玩,只要倉庫在不同的目錄下,但是在世紀開發(fā)中這種方式完全沒有意思。
  • 實際開發(fā)時,需要自己搭建一臺運行Git的服務器。
  • 有一個叫GitHub的神奇網(wǎng)站,這個網(wǎng)站是專門提供Git倉庫托管服務的,所以,只要注冊一個GitHub賬號,就可以免費獲得Git遠程倉庫。

我們現(xiàn)在就將Git倉庫托管到GitHub上,來看一下具體的步驟。

第一步,創(chuàng)建 SSH KEY。在用戶主目錄下,看看有沒有.ssh目錄,如果有,再看看這個目錄下有沒有id_rsaid_rsa.pub這兩個文件,如果有了可以直接進行下一步。如果沒有,打開Shell(Windows下打開Git Bash),創(chuàng)建SSH KEY:

$ ssh-keygen -t rsa -C "youremail@example.com"

需要把郵件地址換成自己的郵件地址,然后一路回車,使用默認值即可,也無需設置密碼。

如果一切順利會在用戶主目錄找到.ssh目錄里面有id_rsaid_rsa.pub這兩個文件。這兩個就是SSH的秘鑰對,id_rsa是私鑰,id_rsa.pub是公鑰,公鑰可以放心的告訴別人。

第二步,登錄GitHub,打開“Account settings”,“SSH Keys”頁面。然后點擊“Add SSH Key”,填上任意Title,在Key文本框里粘貼id_rsa.pub文件內(nèi)容,通俗的講就是把公鑰告訴GitHub。

為什么GitHub需要知道你的SSH Key呢?因為GitHub需要認證你推送的提交確實是你推送的,而不是別人冒充的。而Git支持SSH協(xié)議,所以GitHub只要知道了你的公鑰,就可以確定只有你自己才能推送。

當然GitHub允許你添加多個key。假定你有若干電腦,你一會兒在公司提交,一會兒在家里提交。只要把每臺電腦的key都添加到GitHub,就可以在每臺電腦上往GitHub推送了。

最后友情提示,在GitHub上免費托管的Git倉庫,任何人都可以看到喔(但是只有你自己才能修改)。所以不要把敏感信息放進去。
如果你不想讓別人看到Git倉庫,有兩個辦法。一個是交點保護費把公開的倉庫變成私有的,這樣別人就看不到了。另一個辦法是自己動手搭建一個Git服務器。

小結(jié)

有了Git遠程倉庫,再也不用擔心自己的硬盤了,代碼的存放也會有管家替我管理啦,另外我也可以把純文本的學習筆記讓GitHub替我托管啦。

添加遠程倉庫


現(xiàn)在的情景是,你已經(jīng)在本地創(chuàng)建了一個Git倉庫后,又想在GitHub創(chuàng)建一個Git倉庫,并且讓這兩個倉庫進行遠程同步,這樣GitHub上的倉庫即可以作為備份,又可以讓其他人通過倉庫來協(xié)作,真是一舉多得。

首先登錄GitHub,然后點擊右上角的加號“Create a new rep”按鈕,創(chuàng)建一個新的倉庫:

在Repository name 中填入learngit,其它保持默認設置,點擊"Create repository"按鈕,就成功創(chuàng)建了一個新的Git倉庫:

hah
hah

現(xiàn)在我們要做的是將本地倉庫與遠程倉庫關聯(lián),根據(jù)GitHub的提示,在本地的learngit倉庫運行命令:

$ git remote add origin git@github.com:honeywolf/learngit.git

honeywolf替換成自己的GitHub賬戶名,否則你在本地關聯(lián)的是我的遠程倉庫,關聯(lián)沒有問題,但是以后推送代碼是推送不了的,因為你的SSH Key公鑰不再我的帳戶列表中。

添加后,遠程倉庫的名字就是origin,這事Git默認的叫法,也可以改成別的,但是origin這個名字一看就知道是遠程庫。

下一步就是把本地的內(nèi)容推送到遠程庫:

$ git push -u origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 270 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:honeywolf/learngit.git
* [new branch]      master -> master
Branch master set up to track remote branch master from origin.

把本地內(nèi)容推送到遠程,用git push命令,實際上是把當前分支master推送到遠程。
由于遠程庫是空的,我們第一次推送master分支時,加上了-u參數(shù),Git不但會把本地的master分支和遠程的master分支關聯(lián)起來,在以后的推送或者拉取時可以簡化命令。

從現(xiàn)在起,只要本地作了提交,就可以通過命令:

$ git push origin master

把本地master分支的最新修改推送至GitHub,現(xiàn)在,你就真正擁有了分布式版本庫。

創(chuàng)建與合并分支


在版本回退里,我們已經(jīng)知道,每次提交,Git會把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git中這個分支叫做主分支,即master分支。HEAD嚴格來講并不是指向提交(commit_id),而是指向mastermaster才是指向提交的,所以,HEAD指向的就是當前分支。

一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點。

每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長。

當我們創(chuàng)建一個新的分支dev的時候,Git新建了一個指針叫做dev,指向master相同的提交,這樣一個新的分支就誕生了。再把HEAD指向dev,就表示當前分支在dev上。這樣看來Git創(chuàng)建分支的速度很迅猛,因為除了增加一個指針dev,改變HEAD的指向,工作區(qū)的文件都沒有發(fā)生變化。

不過從現(xiàn)在開始,對工作區(qū)的修改和提交就是針對dev分支啦,比如新提交一次后,dev指針往前移動了一步,而master指針是不變的。

假如我們在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并分支呢?最簡單的辦法,就是直接把master指向dev的當前提交,就完成了合并。所以Git分支的合并也很迅捷,工作區(qū)的內(nèi)容也不會發(fā)生改變。

合并完成以后,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉后,我們就剩下了一條master分支。

下面開始實戰(zhàn)。

首先,我們創(chuàng)建一個dev分支,然后切換到dev分支。

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b參數(shù)表示創(chuàng)建并切換,相當于以下兩條指令

$ git branch dev
$ git checkout dev
Switched to a new branch 'dev'

git branch命令會列出所有分支,當前分支前面有個*號:

$ git branch
* dev
master

然后我們在dev上可以做正常的提交,比如對readme.txt的修改,加上一行:

creating a new branch is quick

然后提交

$ git commit -a -m "branch test"
[dev e96c626] branch test
1 file changed, 2 insertions(+)

現(xiàn)在dev的工作完成了,我們就可以切換回master分支。

$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

現(xiàn)在我們切回master分支后,再查看readme.txt文件,剛才添加的內(nèi)容不見了!因為那個提交實在dev分支上,而master分支此刻的提交點并沒有發(fā)生變化。
現(xiàn)在,我們把dev的工作成果合并到master分支:

$ git merge dev -m "merge dev"
Updating e9da25c..e96c626
Fast-forward 
readme.txt | 2 ++
1 file changed, 2 insertions(+)

git merge命令用于合并指定分支到當前分支。合并后,再查看 readme.txt文件內(nèi)容,就可以看到,和dev分支最后提交的是完全一樣的。

注意到上面的Fast-forward信息,Git告訴我們,這次合并是“快進模式”,也就是直接把master指向dev的當前提交,所以合并的速度非常的快。

當然并不是每次合并都能使用Fast-forward模式,后面我們會講道其他方式的合并。

合并完成后,就可以放心地刪除dev分支了。

$ git branch -d dev
Deleted branch dev (was e96c626).

因為創(chuàng)建、合并和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合并后再刪除分支,這和直接在master分支上工作效果是一樣的,但過程更安全。

小結(jié)

Git鼓勵大量使用分支:
查看分支 git branch

創(chuàng)建分支 git branch <name>

切換分支 git checkout <name>

創(chuàng)建+切換分支 git checkout -b <name>

合并到當前分支 git merge <name>

刪除分支 git branch -d <name>

解決沖突


人生不如意事有九八,合并分支往往也不是一帆分順的。

準備新的feature1分支,繼續(xù)我們的新分支開發(fā):

$ git checkout -b feature1
Switched to a new branch 'feature1'

修改readme.txt最后一行,改為:

$ git add readme.txt 
$ git commit -m "AND simple"
[feature1 8974d1b] 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.
(use "git push" to publish your local commits)

Git 會自動提示我們當前的master分支比遠程的master分支要超前一個提交。

master分支上把readme.txt最后一行改為:

Creating a new branch is quick & simple.

提交:

$ git add readme.txt 
$ git commit -m "& simple"
[master e4a1309] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

現(xiàn)在master分支和feature1分支各自都分別有新的提交:

在這種情況下Git無法執(zhí)行快速合并,只能試圖把各自的修改合并起來,但這種合并就可能會有沖突,我們試試看:

$  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.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

我們可以查看readme.txt的內(nèi)容:

Git is a version control system.
Git is a free software.
<<<<<<< HEAD
Create a new branch is quick & simple.
=======
Create a new branch is quick AND simple.
>>>>>>> feature1

Git使用<<<<<<<=======>>>>>>>標記出不同的內(nèi)容,我們修改成如下后保存。

Create a new branch is quick and simple.

再提交:

$ git add readme.txt 
$ git commit -m "conflic fixed"
[master 4f8be33] conflic fixed

用帶參數(shù)的git log也可以看到分支的合并情況:

$ git log --graph --pretty=oneline --abbrev
*   4f8be331c4a0972055f295cf8b8c4e7cec15e8d3 conflic fixed
|\  
| * 8974d1bf72c971d6904568f5ada712559a13a1b2 AND simple
* | e4a1309271c343c844caaa4217ca8724024b8642 & simple
|/  
* e96c626c9adf06390a936c51e3de6723e1d2e3b3 branch test
* e9da25c20a570b2f6280d3bb47a5e0d3935a5a8b wrote a readme file

最后刪除feature1分支:

$ git branch -d feature1
Deleted branch feature1 (was 8974d1b).

工作完成。

小結(jié)

當Git無法自動合并分支時,就必須首先要解決沖突。解決沖突后,再提交,合并完成。
git log --graph命令可以看到分支合并圖。

分支管理策略


通常,合并分支時,如果可能,Git會用Fast forward模式,但在這種模式下,刪除分支后,會丟掉分支信息。

如果強制禁用Fast forward模式,Git會在merger時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。

下面我們實戰(zhàn)一下--no-ff方式的git merge

首先我們?nèi)匀灰獎?chuàng)建并切換dev分支

修改readme.txt文件,并提交一個新的commit:

我們切回master

準備合并dev分支,請注意--no-f參數(shù),表示禁止使用Fast forward:

$ git merge --no-f -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)

因為本次合并要創(chuàng)建一個新的commit,所以加上-m參數(shù),把commit描述寫了進去。

合并后我們用git log查看分支歷史:

$ git log --graph --pretty=oneline
*   9e2804ba5091cf25825479b4483d98d1767f31b0 merge with no-ff
|\  
| * 41b696e22b98cd19baa45824a355656658a1d06d add merge
|/  
*   4f8be331c4a0972055f295cf8b8c4e7cec15e8d3 conflic fixed
|\  
| * 8974d1bf72c971d6904568f5ada712559a13a1b2 AND simple
* | e4a1309271c343c844caaa4217ca8724024b8642 & simple
|/  
* e96c626c9adf06390a936c51e3de6723e1d2e3b3 branch test
* e9da25c20a570b2f6280d3bb47a5e0d3935a5a8b wrote a readme file

分支策略

在實際開發(fā)中,我們應該按照幾個基本原則進行分支管理:

首先:master分支應該是非常穩(wěn)定的,也就是僅僅用來發(fā)布新版本,平時不能在上面干活;

那在哪里干活呢?干活都在dev分支上,也就是說dev分支是不穩(wěn)定的,到某個時候,比如1.0版本發(fā)布時,再把dev分支合并到master上。在master上發(fā)布1.0版本。

你和你的小伙伴每個人都在dev分支上干活,每個人都有自己的分支,時不時地往dev分支上合并就可以了。

小結(jié)

Git分支十分強大,在團隊開發(fā)中應該充分應用。

合并分支時,加上--no-ff參數(shù)就可以以普通模式合并,合并后的歷史分支,能看出來作了哪些合并,而Fast forward合并就看不出來曾經(jīng)做過合并。

Bug分支


軟件開發(fā)中,bug就像家常便飯一樣。有了bug就需要修復,在Git中,由于分支是如此強大。所以每個bug都可以通過一個臨時分支修復,修復后合并分支,然后將臨時分支刪除。

當你接到一個修復代號為101的bug任務時,很自然的,你想創(chuàng)建一個分支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

并不是你不想提交,而是工作還沒有完成,還沒法提交,預計完成還需一天時間。但是,必須在兩個小時之內(nèi)修復改bug,怎么辦?

Git考慮的很周到,為我們提供了git stash功能,可以把工作區(qū)現(xiàn)場“存儲”起來,等以后恢復現(xiàn)場后繼續(xù)工作。

$ git stash
Saved working directory and index state WIP on dev: 41b696e add merge
HEAD is now at 41b696e add merge

現(xiàn)在用git status來查看情況,工作區(qū)就是干凈的,除非有沒有被Git管理的文件。因此你就可以放心大膽的地創(chuàng)建新的分支來修復bug。

首先要確定在哪個分支上修復bug,假定需要在master上修復,就從master創(chuàng)建臨時分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
(use "git push" to publish your local commits)
$ git checkout -b issue-101
Switched to a new branch 'issue-101'

現(xiàn)在修復bug,然后提交:

$ git add readme.txt 
$ git commit -m "fix bug 101"
[issue-101 20f6aa2] 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 6 commits.
(use "git push" to publish your local commits)

$ git merge --no-ff -m "merge fix bug 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 20f6aa2).

太棒了,原計劃兩個小時的bug修復只花費了5分鐘!現(xiàn)在是時候回到dev分支干活啦。

$ git checkout dev
Switched to branch 'dev'
$ git status
On branch dev
nothing to commit, working directory clean

工作區(qū)是干凈的,剛才的工作現(xiàn)場到哪里去了?用git stash list查看

$ git stash list
stash@{0}: WIP on dev: 41b696e add merge

工作現(xiàn)場還在,Git把工作現(xiàn)場存儲到了某個地方,但是需要恢復一下,有兩個辦法:

一是用git stash apply恢復,但是恢復后,stash內(nèi)容并不清楚,你需要用git stash drop來刪除;

另一種方法是用 git stash pop,恢復的同時把stash的內(nèi)容也刪了:

$ 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} (8ba1ca0c4d887d79faba741dca8ff37fa5612fb6)

再用git stash list查看,就看不到任何stash的內(nèi)容了:

$ git stash list

你可以多次stash,恢復的時候先用git stash list查看,然后恢復指定的stash,用命令:

git stash apply stash@{1}

小結(jié)

修復bug時,我們會通過創(chuàng)建新的bug分支進行修復,然后合并,最后刪除

當手頭工作沒有完成時,我們用git stash將工作區(qū)緩存,然后去修復bug。bug改完后,再git stash pop

Feature分支


軟件開發(fā)過程匯總,總有無窮盡的新功能要不斷添加進來。

添加一個新功能時,你肯定不希望因為一些實驗性質(zhì)的代碼,把主分支搞亂了。所以每添加一個新功能最好添加一個新的feature分支,在上面開發(fā)新功能。完成后,合并,刪除該feature分支。

現(xiàn)在你終于接到了一個新任務:開發(fā)代號為Vulcan的新功能,該功能計劃用于下一代星際飛船。

于是準備開發(fā)

$ git checkout -b feature-vulcan
Switched to a new branch 'feature-vulcan'

5分鐘后,開發(fā)完畢:

$ git status
On branch feature-vulcan
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file:   vulcan.py
$ git commit -m "add feature vulcan"
[feature-vulcan 8f9c087] add feature vulcan
1 file changed, 1 insertion(+)
create mode 100644 vulcan.py

切回dev,準備合并:

$ git checkout dev
Switched to branch 'dev'

一切順利的話,feature分支和bug分支是類似的,合并,然后刪除。
但是,就在此時,接到上級命令,因經(jīng)費不足,新功能必須取消!

雖然白干了,但是這個分支還是必須就地銷毀:

$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-  vulcan'.

Git提示,該分支未完全merge,還不能刪除,如果要強行刪除,需要使用git branch -D feature- vulcan命令:

現(xiàn)在我們強行刪除:

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 8f9c087).

小結(jié)

開發(fā)一個新的feature,最好創(chuàng)建一個新的分支;

如果要丟棄一個沒有被合并過的分支,可以通過 git branch -D feature-vulcan強行刪除。

多人協(xié)作


當你從遠程倉庫克隆時,實際上Git自動把本地的master分支和遠程的master分支對應起來了,并且,遠程倉庫的默認名稱是origin

要查看遠程倉庫的信息用git remote

$ git remote
origin

或者用git remote -v顯示更詳細的信息:

$ git remote -v
origin git@github.com:hoenywolf/learngit.git
 (frtch)
origin git@github.com:hoenywolf/learngit.git
 (push)

上面顯示了可以抓取和推送的origin地址。如果沒有推送權(quán)限就看不到push地址。

推送分支

推送分支,就是把分支上的所有本地數(shù)據(jù)提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的分支上:

$ git push origin master

如果要推送其他分支,比如dev,就改成:

$ git push origin dev

但是,并不是一定要把本地分支往遠程推送,那么哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要時刻與遠程倉庫同步;
  • dev是開發(fā)分支,團隊所有的成員都要在上面工作,所以也需要與遠程同步;
  • bug分支只用于在本地修復bug,就沒必要推送到遠程了,除非老板要看看你每周修復了幾個bug
  • feature分支是否推送到遠程取決于你是否和你的伙伴合作在上面開發(fā)。

總之在Git中,分支完全可以在本地自己藏著玩,是否推送,視你的心情而定!

抓取分支

多人協(xié)作時,大家都會往master分支和dev分支上推送各自的修改。

現(xiàn)在模擬一個你的小伙伴,可 以在另一臺電腦(注意要把SSH Key添加到GitHub)或者同一臺電腦的另一個目錄下克隆:

$ git clone git@github.com:honeywolf/learngit.git

當你的小伙伴從遠程倉庫clone時,默認情況下,你的小伙伴只能看到本地master分支。

$ git branch
* master

現(xiàn)在,你的小伙伴要在dev分支上開發(fā),就必須創(chuàng)建遠程orgindev分支到本地,于是他用這個命令創(chuàng)建本地分支:

$ git checkout -b dev origin/dev

現(xiàn)在,他就可以在dev分支上繼續(xù)修改,然后時不時地不斷把dev分支push到遠程:

$ git commit -m "add /usr/bin/env"
....
$ git push origin dev
....

你的小伙伴已經(jīng)向origin/dev分支推送了他的提交,而碰巧你也對同樣的文件作了修改,并試圖推送:

$ git push origin dev
To git@github.com:honeywolf/learngit.git
! [rejected]        dev -> dev (fetch first)
error: failed to push some refs to 'git@github.com:honeywolf/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.

推送失敗,因為你的小伙伴的最新提交和你試圖推送的提交有沖突,解決辦法也很簡單,Git已經(jīng)提示我們,先用 git pull把最新的提交從origin/dev抓下來,然后,在本地合并,解決沖突,再推送:

$ git pull
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 8 (delta 0), reused 8 (delta 0), pack-reused 0
Unpacking objects: 100% (8/8), done.
From github.com:honeywolf/learngit
a947c9b..2f95e78  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分支的鏈接,根據(jù)提示,設置devorigin/dev的鏈接:

$ git branch --set-upstream-to=origin/dev dev

再pull:

$ git pull

這回git pull成功,但是合并有沖突,需要手動解決,解決的辦法和分支管理中解決沖突完全一樣。解決后,然后提交,再push:

$ git commit -m "merge & fix hello.py"
...
$ git push origin dev
...

因此多人協(xié)作的工作模式通常是這樣的:

  1. 首先,自己試圖用 git push origin branch-name推送自己的修改;
  2. 如果推送失敗,則因為遠程分支比你的本地更新,需要用git pull試圖合并
  3. 如果合并有沖突,則解決沖突,并在本地提交;
  4. 沒有沖突或者解決掉沖突后,再用git push origin brach-name推送就能成功。

如果git pull提示"no tracking information",則說明本地分支和遠程分支的鏈接關系沒有創(chuàng)建,用命令git branch --set-upstream branch-name origin/branch-name

這就是多人協(xié)作的工作模式,一旦熟悉了,就非常簡單。

小結(jié)

  • 查看遠程庫信息,使用git remote -v
  • 本地新建的分支如果不推送到遠程,對其他人就是不可見的;
  • 從本地推送分支,使用git push origin branch-name,如果推送失敗先用git pull抓取遠程的最新提交;
  • 在本地創(chuàng)建和遠程分支對應的分支,使用git checkout -b branch-name origin/branch-name,本地和遠程分支的名稱最好一致;
  • 建立本地分支和遠程分支的關聯(lián),使用git branch --set-upstream-to=origin/<branch-name> <branch-name>
  • 從遠程抓取分支,使用git pull,如果有沖突,要先處理沖突。

標簽管理


發(fā)布一個版本時,我們通常在版本庫中打一個標簽,這樣,就唯一確定了打標簽時刻的版本。將來無論什么時候,取這個標簽的版本,就是把那個打標簽時刻的歷史版本取出來。所以標簽也是版本庫的一個快照。

Git標簽雖然是版本庫的快照,但其實它就是指向某個commit 指針(跟分支很像對不對?但是分支是可以移動的,標簽不可以移動)所以,創(chuàng)建和刪除標簽都是瞬間完成的。

創(chuàng)建標簽

在Git中打標簽非常簡單,首先,切換到需要打標簽的分支上:

$ git branch 
* dev
master
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

然后敲命令git tag <name>就可以打一個新標簽:

$ git tag v1.0

可以用git tag命令查看所有的標簽

$ git tag
v1.0

默認的標簽都是打在最新的commit提交上的。有時候,會忘記打標簽。比如現(xiàn)在已經(jīng)周五了,但應該在周一打的標簽沒有打,該怎么辦呢?

方法是找到歷史提交的 commit id,然后打上就可以了:

$ git log --pretty=oneline --abbrev-commit
4e90863 merge fix bug 101
20f6aa2 fix bug 101
9e2804b merge with no-ff
41b696e add merge
4f8be33 conflic fixed
e4a1309 & simple
8974d1b AND simple
e96c626 branch test
e9da25c wrote a readme file

比方說要對add merge這次提交打標簽,它對應的commit id 是41b696e,敲入命令

$ git tag v0.9 41b696e
$ git tag
v0.9
v1.0

注意標簽不是按時間順序列出,而是按字母排序的。可以用git show <tagname>查看標簽信息:

$ git show v0.9
commit 41b696e22b98cd19baa45824a355656658a1d06d
Author: honeywolf <adam.h@gmail.com>
Date:   Fri Mar 11 23:42:57 2016 +0800

add merge
...

可以看到,v0.9確實打在add merge這次提交上。
還可以創(chuàng)建帶有說明的標簽,用-a指定標簽名,-m指定說明文字:

$ git tag -a v0.1 -m "version 0.1 released" 20f6aa220

用命令git show <tagname>可以看到說明文字

$ git show v0.1
tag v0.1
Tagger: honeywolf <adam.h@gmail.com>
Date:   Sat Mar 12 14:56:11 2016 +0800

version 0.1 released

commit 20f6aa22033491f033d54efe1088fc04e7fbff38
Author: honeywolf <adam.h@gmail.com>
Date:   Sat Mar 12 00:16:50 2016 +0800

    fix bug 101

還可以通過-s用私鑰簽名一個標簽:

$ git tag -s v0.2 -m "signed version 0.2 released" 4e90863dc

簽名采用PGP簽名,因此,必須首先安裝gpg(GnuPG),如果沒有找到gpg,或者沒有gpg密鑰對,就會報錯:

error: cannot run gpg: No such file or directory
error: could not run gpg.
error: unable to sign the tag

如果報錯,請參考GnuPG

小結(jié)

  • 命令git tag <name>用于新建一個標簽,默認為HEAD,也可以指定一個commit id;
  • git tag -a <tagname> -m "balabalabala..."可以指定標簽信息;
  • git tag -s <tagname> -m "balabalabala..."可以用PGP簽名標簽;
  • 命令git tag可以查看所有標簽。

操作標簽


如果標簽打錯了,可以刪除:

$ git tag -d v0.1
Deleted tag 'v0.1' (was c9e4c38)

因為創(chuàng)建的標簽都只存儲在本地,不會自動推送到遠程。所以,打錯的標簽可以在本地安全刪除。

如果要推送到遠程,使用命令git push origin <tagname>

$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:honeywolf/learngit.git
* [new tag]         v1.0 -> v1.0

或者一次性推送全部尚未推送到遠程的本地標簽:

git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:honeywolf/learngit.git
* [new tag]         v0.9 -> v0.9

如果標簽已經(jīng)推送到遠程,要刪除遠程標簽就麻煩一點,先從本地刪除:

git tag -d v0.9
Deleted tag 'v0.9' (was 41b696e)

然后從遠程刪除。刪除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9
To git@github.com:honeywolf/learngit.git
- [deleted]         v0.9

要看看是否真的從遠程刪除了標簽,可以登錄GitHub查看。

小結(jié)

  • 命令git push origin <tagname>可以推送一個本地標簽
  • 命令git push origin --tags可以推送全部未推送過的本地標簽;
  • 命令git tag -d <tagname>可以刪除一個本地標簽;
  • 命令git push origin :refs/tags/<tagname>可以刪除一個遠程標簽。

使用GitHub


我們一直用GitHub作為免費的倉庫,如果是個人開源項目,放到GitHub上是完全沒有問題的。其實GitHub還是一個開源協(xié)作社區(qū),通過GitHub,即可以讓別人參與你的 開源項目,也可以參與別人的開源項目。

在GitHub出現(xiàn)以前,開源項目開源容易,但讓廣大人民群眾參與進來比較困難,因為要參與,就要提交代碼,而給每個想提交代碼的群眾都開一個賬號那是不現(xiàn)實的,因此,群眾也僅限于報個bug,即使能改掉bug,也只能把diff文件用郵件發(fā)過去,很不方便。

但是在GitHub上,利用Git及其強大的克隆和分支功能,廣大人民群眾真正可以第一次自由參與各種開源項目了。

如何參與一個開源項目呢?比如人氣極高的booststrap項目,這是一個非常強大的CSS框架,你可以訪問它的項目主頁https://github.com/twbs/bootstrap,點擊"Fork"就在自己的帳號下克隆了一個bootstrap倉庫,然后,從自己的帳號下clone:

git clone git@github.com:honeywolf/bootstrap.git

一定要從自己的帳號下clone倉庫,這樣你才能推送修改。如果從bootstrap作者的倉庫地址git@github.com:twbs/bootstrap.git克隆,因為沒有權(quán)限,你將不能推送修改。

如果你希望修復bootstrap的一個bug,或者新增一個功能,立刻就可以開始干活,干完后,往自己的倉庫推送。

如果你希望bootstrap的官方庫能接受你的修改,你就可以在GitHub上發(fā)起一個pull request。當然,對方是否接受你的pull request就不一定了。

小結(jié)

  • 在GitHub上,可以任意Fork開源倉庫;
  • 自己擁有Fork后的倉庫的讀寫權(quán)限;
  • 可以推送pull request給官方倉庫來貢獻代碼。

自定義Git

在安裝Git一節(jié)中,我們已經(jīng)配置了user.nameuser.email,實際上,Git還有很多可配置項。比如,讓Git顯示顏色,會讓命令輸出看起來更醒目:

$ git status
On branch dev
Your branch is up-to-date with 'origin/dev'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified:   readme.txt

文件名就會標上顏色。我們后序還會介紹如何更好的配置Git,以便讓你的工作更高效。

忽略特殊文件


有些時候,你必須把某些文件放到Git工作目錄中,但是又不想提交它們,比如保存了數(shù)據(jù)庫密碼的配置文件啦,等等,每次git status都會顯示Untracked files ...,有強迫癥的童鞋心里肯定不爽。

好在Git考慮到了大家的感受,這個問題解決起來也很簡單,在Git工作區(qū)的根目錄下創(chuàng)建一個特殊的.gitignore文件,GitHub已經(jīng)為我們準備了各種配置文件,只要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:https://github.com/github/gitignore

忽略文件的原則是:

  1. 忽略操作系統(tǒng)自動生成的文件,比如縮略圖等;
  2. 忽略編譯生成的中間文件、可執(zhí)行文件等,也就是如果一個文件是通過另一個文件自動生成的,那自動生成的文件就沒必要放進版本庫,比如Java編譯產(chǎn)生的.class文件;
  3. 忽略你自己的帶有敏感信息的配置文件,比如存放口令的配置文件。

舉個例子:
假設你在Windows下進行Python開發(fā),Windows會自動在有圖片的目錄下生成隱藏的縮略圖文件,如果有自定義目錄,目錄下就會有Desktop.ini文件,因此你需要忽略Windows自動生成的垃圾文件:

# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini

然后,繼續(xù)忽略Python編譯產(chǎn)生的.pyc.pyodist等文件或目錄:

# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build

加上你自己定義的文件,最終得到一個完整的.gitignore文件,內(nèi)容如下:

# 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了。

OC語言的.gitignore

# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xcuserstate

## Obj-C/Swift specific
*.hmap
*.ipa

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md

fastlane/report.xml
fastlane/screenshots

小結(jié)

  • 忽略某些文件時,需要編寫.gitignore;
  • .gitignore文件本身要放到版本庫里,并且可以對.gitignore做版本管理!

配置別名

有沒有經(jīng)常敲錯命令?比如git status? status這個單詞真心不好記。

如果敲git st就表示git status那就簡單多了,當然這種偷懶的辦法我們是極力贊成的。

我們只需要敲一行代碼,告訴Git,以后st就是status:

$ git config --global alias.st status

當然還有別的命令可以簡寫,很多人都用co表示checkout,ci表示commit,br表示branch:

$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch

--global參數(shù)是全局參數(shù),也就是這些命令在這臺電腦的所有Git倉庫下都有用。

在撤銷修改一節(jié)中,我們知道,命令git reset HEAD file可以把暫存區(qū)的修改撤銷掉(unstage),重新放回工作區(qū)。既然是一個unstage操作,就可以配置一個unstage別名:

$ git config --global alias.unstage 'reset HEAD'

配置一個git last,讓其顯示最后一次提交信息:

$ git config --global alias.last 'log -1'

甚至還有人喪心病狂地把lg配置成了:

    git config --global alias.lg "log --color --graph --    pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)  %C(bold blue)<%an>%Creset' --abbrev-commit"

配置文件

配置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:honeywolf/learngit.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master
[branch "dev"]
    remote = origin
    merge = refs/heads/dev

別名就在[alias]后面,要刪除別名,直接把對應的行刪掉即可。

而當前用戶的Git配置文件放在用戶主目錄下的一個隱藏文件.gitconfig中:

$ cat .gitconfig 
[filter "lfs"]
    clean = git-lfs clean %f
    smudge = git-lfs smudge %f
    required = true
[user]
    name = honeywolf
email = adam.honey@ydx.hk
[color]
    ui = true
[alias]
    st = status
    co = checkout
    ci = commit
    br = branch
    unstage = reset HEAD
    last = log -1
    lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

配置別名也可以直接修改這個文件,如果改錯了,可以刪掉文件重新通過命令配置。

小結(jié)

給Git配置好別名,就可以輸入命令時偷個懶。我們鼓勵偷懶 。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 原文地址主要用到的命令: git config user.name 設置用戶名 git config user....
    AFinalStone閱讀 483評論 0 2
  • 轉(zhuǎn)載自:http://www.open-open.com/lib/view/open1414396787325.h...
    Bbooo閱讀 493評論 0 3
  • 一:Git是什么? Git是目前世界上最先進的分布式版本控制系統(tǒng)。 二:SVN與Git的最主要的區(qū)別? SVN是集...
    毛子阿卡西閱讀 260評論 0 1
  • 一:Git是什么? Git是目前世界上最先進的分布式版本控制系統(tǒng)。 二:SVN與Git的最主要的區(qū)別? SVN是集...
    傲慢二鍋頭閱讀 442評論 0 0
  • 4fa4c4103e6c閱讀 284評論 0 0