一、git概述
區別于svn,git是個分布式的代碼版本控制系統。
在進行所有的操作之前,都需要設置一下個人信息,以區分不同提交者的身份
$ git config -global user.name "your name"
$ git config -global user.email "yourname@example.com"
二、理解代碼的位置
通常情況下,在分布式版本控制中,代碼存放在三個重要的位置:遠程倉庫(Remote)、本地倉庫(Repository)和當前工作空間(workkkspace)。
通常的一次代碼修改包含以下步驟:
- 通過fetch/clone將代碼從遠程倉庫(Remote)下載到本地(Repository)
- 通過checkout從本地倉庫中找出需要修改的代碼開始修改
- git維護的index數據結構維護代碼的變化狀態
- 通過add命令讓index知道修改的內容
- 通過commit命令將修改提交到本地倉庫
- 通過push命令將修改提交到遠程倉庫
2.1 git clone:從遠程倉庫下載代碼
clone :將遠程倉庫下載到本地,指定目錄名則使用指定的目錄名,不指定則和遠程倉庫保持一致
$ git clone <版本庫的網址> <本地目錄名>
$ git clone https://github.com/jquery/jquery.git #下載JQuery源碼
2.2 git remote:管理遠程主機名
remote:通常情況下遠程主機使用URL表示,不利于開發者記憶,可以使用remote命令給遠程主機命名
下載代碼庫后,如果沒有指定遠程主機的名字,則默認為:origin
$ git remote #列出這個倉庫相關的所有遠程主機名
origin
$ git clone -o jQuery https://github.com/jquery/jquery.git #使用-o選項指定這個代碼庫的遠程主機名
$ git remote
jQuery
$ git remote -v #查看遠程主機的地址
origin git@github.com:jquery/jquery.git (fetch)
origin git@github.com:jquery/jquery.git (push)
$ git remote show <主機名> #查看遠程主機的詳細信息
$ git remote add <主機名> <網址> #添加遠程主機
$ git remote rm <主機名> #刪除遠程主機
$ git remote rename <原主機名><新主機名>
2.3 git fetch:同步本地代碼庫為最新狀態
fetch:已經有clone可以拉取遠程倉庫的代碼了,為什么還需要fetch命令?
原創解答:如果遠程倉庫只有你一個人使用的話,你使用clone和fetch都是等效的,但是版本控制就是為群體協作而生的,只有一種情況下,你需要使用fetch命令。如下描述:
當你和你的隊友都clone了遠程倉庫的代碼,你的隊友修改完了并且push到了遠程倉庫,這個時候你的本地Repository和remote處于不同步的狀態,你如果使用push提交代碼,會提示錯誤,這個時候你就需要使用fetch同步你本地的Repository和remote然后再提交。
$ git fetch <遠程主機名><分支名稱> #默認情況下,幫你取回的是master分支,如果你要取回其他分支,請指定。
注:比較clone命令,fetch命令不再提供<本地目錄名 >的選項,因為使用這個命令的時候一般本地目錄都已經存在,僅僅是做了Repository和Remote的同步操作
$ git fetch origin master #取回origin主機的master分支
通常情況下,取回了遠程分支后,要讀取操作遠程分支就需要使用 <遠程主機名/分支名>的格式
$ git branch -r #查看遠程分支
origin/master
$ git branch -a #查看所有分支
* master
remotes/origin/master
$ git checkout -b newBrach origin/master #在遠程分支的基礎上創建新分支
$ git merge origin/master #合并遠程分支
2.4 git push:同步本地Repository到Remote
push:本地分支所有的修改都已經提交到local Repository后,需要將更新同步到Remote Repository。
只有你進行了push操作后,你的隊友從遠程倉庫下載代碼才可以看到那你的更新。所以本地修改更新完一定不能忘記push操作
注:git push 基于分支進行操作,默認不推送tag,除非使用 --tags指定
$ git push <遠程主機名> <本地分支名>:<遠程分支名> #推送操作是用本地同步遠程,所以后面的參數一定是<本地分支名:遠程分支名>
注:如果省略遠程分支名 ,通常表示你的本地分支名和遠程分支名是一致的,如果不一致,比如你在本地修改了分支名,則在推送時一定要指定遠程分支名,否則將會在遠程創建新的分支
# git push origin master#表示將本地的master分支更新到遠程主機
通常情況下,我們要刪除一個遠程的分支是這樣操作。
# git push origin --delete master
利用push的特性,我們使用一個空的分支去更新遠程的特定分支,也可以達到刪除的目的
# git push origin :master
如果我們本地的分支和遠程的分支使用的命名是完全一致的,我們可以這樣操作:
# git push origin
如果本地只有一個分支,操作更加簡單
# git push
如果你的這個倉庫關聯了多個遠程主機(使用git remote 可以查看),你還想不加任何參數使用git push怎么辦?
解決方法:指定默認的遠程主機
$ git push -u origin master #執行了這個操作后就不需要使用任何參數來使用git push了
最后一個問題:不使用任何參數的git push默認只推送當前分支,即simple模式,還有一種matching模式,會推送所有遠程分支對應的本地分支。你可以通過修改配置文件的方式來選擇自己需要的模式。
$ git config --global push.default.matching
或者
$ git config --global push.default.simple
那么在simple模式下能不能強制推送呢?也是可以的
$ git push --force origin #將本地所有的分支都推送到遠程主機,如果該分支在遠程主機不存在,就創建分支
2.5 git pull :獲取遠程分支同本地分支合并
實際中只有一種情況用到這個命令,當你和你的隊友同時在本地操作同一個分支,他比你先提交,也就意味著此刻你的本地庫比遠程庫要舊,這個時候你如果要push就會被拒絕,可以使用pull把這個分支最新代碼拉下來。
從圖示中就很容易看出來,這個命令直接從遠程拿個分支到本地的工作空間和你正在進行操作的分支進行合并。
$ git pull <遠程主機名><遠程分支名>:<本地分支名> #注意比較這個命令和push的格式的區別
$ git pull origin next:master
$ git pull origin next #獲取遠程的next分支和當前工作空間中正在操作的分支合并
等價于:
$ git fetch origin
$ git merge origin/next
雖然也可以簡單的使用git pull,但是并不建議這么做,需要的自己查手冊吧
三、理解代碼的狀態
既然是版本控制,肯定要維護代碼不同版本的狀態,git是通過定義了四個不同的存儲區域來維護代碼狀態的。
如圖所示,自左向右存在4個不同區域,對應了代碼的四個不同的新舊程度。
- workspace:用戶直接面對的空間就是workspace,這個空間和你操作任何普通目錄時沒有區別的,也不具備跟蹤文件修改狀態的功能,用戶直接在這個空間修改代碼,所以這個空間的代碼狀態最新。
- index:普通目錄不具備跟蹤版本功能,index本質是個數據結構,維護指向不同的文件版本的索引,只有在該空間建立索引,才能跟蹤文件的狀態。
- local Repository:本地倉庫存儲一次更新的完整版本。
- remote Repository:遠程倉庫存儲所有的軟件版本。通常代碼狀態最舊。
代碼版本更新的過程為:workspace-> index -> local repository -> remote repository
3.1 workspace -> index :為指定文件建立版本跟蹤信息
workspace中的文件不具有任何版本信息,和普通目錄一樣,但是可以通過創建索引的形式在index中創建文件的版本信息。
文件修改后,我們通常想知道這次修改的具體內容,可以通過和歷史版本的對比得到。
$ git diff <文件名> #列出工作空間中的指定文件和index中維護的文件不同,如果不指定,則會列出工作空間內所有被跟蹤的文件同index中維護的版本的差異
注意:不要在提交之后只用該命令,提交后workspace和index的狀態已經同步了,這個命令是沒有輸出的
修改完成后,我們需要將修改,同步到index
$ git add <文件名> #為指定文件創建版本信息
3.2 index -> local repository :提交穩定的版本到本地倉庫
通過將index中最新的版本提交的本地倉庫實現一次完整版本的跟蹤。
注:index跟蹤每一次修改,local Repository跟蹤每一個版本,僅僅是版本控制的粒度的大小不同
提交版本之前,可以通過和歷史版本的對比查看更新的內容
$ git diff -cached
注意:不要在提交后使用這個命令,提交后index和local Repository的版本已經同步了,這個操作沒有輸出。
提交修改,更新本地代碼庫。
$ git commit <文件名>
3.3 workspace -> local repository
注意:只有一種情況可以這么操作,就是該文件已經使用git add在index中建立版本記錄的時候可以實現這個狀態的轉移
$ git diff HEAD #查看工作空間中的代碼和本地代碼庫的舊版本的相比更新的地方
直接從本地工作空間提交代碼到本地代碼庫
$ git commit -a <文件名>
四、理解分支
很多情況下,我們需要做一些類似與實驗性的特性的開發,這個時候直接在主分支上進行操作不是很合適,我們可以基于主分支創建一個新的分支進行開發,如果開發通過了測試就可以合并到主分支;如果這個實驗性分支不合適,就可以直接刪除。以此降低開發對主分支的影響。
如上圖所示,一般項目都會維護至少兩條分支,master和dev,master可以理解為我們通常下載軟件時使用的release版本,主要用于穩定版本的發布,一般的開發工作不在master上操作。
開發流程:
正常的開發流程是這個樣的,每個開發者基于dev分支創建新的分支,開發完了以后將分支合并到dev分支,dev分支上的功能通常就是可以測試的了,dev分支打上完整的發布標簽后測試團隊就可以介入對這個版本進行測試,測試通過后就可以合并到master作為穩定版本進行發布。
多人協作:
- 首先,可以試圖使用 git push origin branch-name 推送自己的修改
- 如果推送失敗,則因為遠程分支比你的本地代碼要新,需要先用git pull 試圖合并
- 如果合并有沖突,則解決沖突,并在本地提交
- 沒有沖突或者解決了沖突后,再用 git push origin branch-name 推送
注:如果git pull 提示 “No tracking infomation”,則說明本地分支和遠程分支的鏈接關系沒有創建,用命令 git branch --set-upstream branch-name origin/branch-name
4.1 分支操作:git branch
4.2 分支間切換:git checkout
五、總結
回答好下面三個問題就可以基本掌握git了,下面的問題自己去了解吧。
問題一:版本在哪里?(commit 和 tag的區別)####
tag只是commit的一個特殊的狀態,可以認為是里程碑。
問題二:什么是版本控制(HEAD是什么)?####
HEAD是指向你當前操作版本的分支
問題三:如何實現任意版本的切換(log和reflog)?####
log記錄了你當前操作版本之前的每一次提交的日志,reflog其實是真正的日志,記錄每次操作,這個日志和文件系統的日志基本是一樣的。