Git详解2
Git
本文章內容承接上文,請注意查看標題。
全文非原創,出處鏈接。
遠程倉庫
到目前為止,我們已經掌握了如何在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:
測試
$ ssh -T git@github.com
Hi tooytoos! You've successfully authenticated, but GitHub does not provide shell access.
為什么GitHub需要SSH Key呢?因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。
當然,GitHub允許你添加多個Key。假定你有若干電腦,你一會兒在公司提交,一會兒在家里提交,只要把每臺電腦的Key都添加到GitHub,就可以在每臺電腦上往GitHub推送了。
最后友情提示,在GitHub上免費托管的Git倉庫,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放進去。
如果你不想讓別人看到Git庫,有兩個辦法,一個是交點保護費,讓GitHub把公開的倉庫變成私有的,這樣別人就看不見了(不可讀更不可寫)。另一個辦法是自己動手,搭一個Git服務器,因為是你自己的Git服務器,所以別人也是看不見的。這個方法我們后面會講到的,相當簡單,公司內部開發必備。
確保你擁有一個GitHub賬號后,我們就即將開始遠程倉庫的學習。
小結
“有了遠程倉庫,媽媽再也不用擔心我的硬盤了。”——Git點讀機
添加遠程庫
現在的情景是,你已經在本地創建了一個Git倉庫后,又想在GitHub創建一個Git倉庫,并且讓這兩個倉庫進行遠程同步,這樣,GitHub上的倉庫既可以作為備份,又可以讓其他人通過該倉庫來協作,真是一舉多得。
首先,登陸GitHub,然后,在右上角找到“Create a new repo”按鈕,創建一個新的倉庫:
在Repository name填入learngit,其他保持默認設置,點擊“Create repository”按鈕,就成功地創建了一個新的Git倉庫:
目前,在GitHub上的這個learngit倉庫還是空的,GitHub告訴我們,可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫與之關聯,然后,把本地倉庫的內容推送到GitHub倉庫。
現在,我們根據GitHub的提示,在本地的learngit倉庫下運行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git請千萬注意,把上面的michaelliao替換成你自己的GitHub賬戶名,否則,你在本地關聯的就是我的遠程庫,關聯沒有問題,但是你以后推送是推不上去的,因為你的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參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以后的推送或者拉取時就可以簡化命令。
推送成功后,可以立刻在GitHub頁面中看到遠程庫的內容已經和本地一模一樣:
從現在起,只要本地作了提交,就可以通過命令:
$ git push origin master把本地master分支的最新修改推送至GitHub,現在,你就擁有了真正的分布式版本庫!
SSH警告
當你第一次使用Git的clone或者push命令連接GitHub時,會得到一個警告:
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established. RSA key fingerprint is xx.xx.xx.xx.xx. Are you sure you want to continue connecting (yes/no)?這是因為Git使用SSH連接,而SSH連接在第一次驗證GitHub服務器的Key時,需要你確認GitHub的Key的指紋信息是否真的來自GitHub的服務器,輸入yes回車即可。
Git會輸出一個警告,告訴你已經把GitHub的Key添加到本機的一個信任列表里了:
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.這個警告只會出現一次,后面的操作就不會有任何警告了。
如果你實在擔心有人冒充GitHub服務器,輸入yes前可以對照GitHub的RSA Key的指紋信息是否與SSH連接給出的一致。
小結
要關聯一個遠程庫,使用命令git remote add origin git@server-name:path/repo-name.git;
關聯后,使用命令git push -u origin master第一次推送master分支的所有內容;
此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
分布式版本系統的最大好處之一是在本地工作完全不需要考慮遠程庫的存在,也就是有沒有聯網都可以正常工作,而SVN在沒有聯網的時候是拒絕干活的!當有網絡的時候,再把本地提交推送一下就完成了同步,真是太方便了!
?從遠程庫克隆
上次我們講了先有本地庫,后有遠程庫的時候,如何關聯遠程庫。
現在,假設我們從零開發,那么最好的方式是先創建遠程庫,然后,從遠程庫克隆。
首先,登陸GitHub,創建一個新的倉庫,名字叫gitskills:
我們勾選Initialize this repository with a README,這樣GitHub會自動為我們創建一個README.md文件。創建完畢后,可以看到README.md文件:
現在,遠程庫已經準備好了,下一步是用命令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目錄看看,已經有README.md文件了。
如果有多個人協作開發,那么每個人各自從遠程克隆一份就可以了。
你也許還注意到,GitHub給出的地址不止一個,還可以用https://github.com/michaelliao/gitskills.git這樣的地址。實際上,Git支持多種協議,默認的git://使用ssh,但也可以使用https等其他協議。
使用https除了速度慢以外,還有個最大的麻煩是每次推送都必須輸入口令,但是在某些只開放http端口的公司內部就無法使用ssh協議而只能用https。
小結
要克隆一個倉庫,首先必須知道倉庫的地址,然后使用git clone命令克隆。
Git支持多種協議,包括https,但通過ssh支持的原生git協議速度最快。
分支管理
分支就是科幻電影里面的平行宇宙,當你正在電腦前努力學習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上:
?
?
你看,Git創建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區的文件都沒有任何變化!
?
不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變:
?
?
假如我們在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 * devmaster?
git branch命令會列出所有分支,當前分支前面會標一個*號。
?
然后,我們就可以在dev分支上正常提交,比如對readme.txt做個修改,加上一行:
?
Creating a new branch is quick.?
然后提交:
?
$ git add readme.txt $ git commit -m "branch test" [dev fec145a] branch test1 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-forwardreadme.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分支上工作效果是一樣的,但過程更安全。
小結
?
Git鼓勵大量使用分支:
?
查看分支:git branch
?
創建分支:git branch <name>
?
切換分支:git checkout <name>
?
創建+切換分支:git checkout -b <name>
?
合并某分支到當前分支:git merge <name>
?
刪除分支:git branch -d <name>
?
解決沖突
?
人生不如意之事十之八九,合并分支往往也不是一帆風順的。
?
準備新的feature1分支,繼續我們的新分支開發:
?
$ git checkout -b feature1 Switched to a new branch 'feature1'?
修改readme.txt最后一行,改為:
?
Creating a new branch is quick AND simple.?
在feature1分支上提交:
?
$ git add readme.txt $ git commit -m "AND simple" [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文件的最后一行改為:
?
Creating a new branch is quick & simple.?
提交:
?
$ git add readme.txt $ git commit -m "& simple" [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 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. <<<<<<< HEAD Creating a new branch is quick & simple. ======= Creating a new branch is quick AND simple. >>>>>>> feature1?
Git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容,我們修改如下后保存:
?
Creating a new branch is quick and simple.?
再提交:
?
$ git add readme.txt $ git commit -m "conflict fixed" [master 59bc1cb] conflict fixed?
現在,master分支和feature1分支變成了下圖所示:
?
?
用帶參數的git log也可以看到分支的合并情況:
?
$ git log --graph --pretty=oneline --abbrev-commit * 59bc1cb conflict fixed |\ | * 75a857c AND simple * | 400b400 & simple |/ * fec145a branch test ...?
最后,刪除feature1分支:
?
$ git branch -d feature1 Deleted branch feature1 (was 75a857c).?
工作完成。
小結
?
當Git無法自動合并分支時,就必須首先解決沖突。解決沖突后,再提交,合并完成。
?
用git log --graph命令可以看到分支合并圖。
?
分支管理策略
?
通常,合并分支時,如果可能,Git會用Fast forward模式,但這種模式下,刪除分支后,會丟掉分支信息。
?
如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
?
下面我們實戰一下--no-ff方式的git merge:
?
首先,仍然創建并切換dev分支:
?
$ git checkout -b dev Switched to a new branch 'dev'?
修改readme.txt文件,并提交一個新的commit:
?
$ git add readme.txt $ git commit -m "add merge" [dev 6224937] add merge 1 file changed, 1 insertion(+)?
現在,我們切換回master:
?
$ git checkout master Switched to branch 'master'?
準備合并dev分支,請注意--no-ff參數,表示禁用Fast forward:
?
$ git merge --no-ff -m "merge with no-ff" dev Merge made by the 'recursive' strategy.readme.txt | 1 + 1 file changed, 1 insertion(+)?
因為本次合并要創建一個新的commit,所以加上-m參數,把commit描述寫進去。
?
合并后,我們用git log看看分支歷史:
?
$ git log --graph --pretty=oneline --abbrev-commit * 7825a50 merge with no-ff |\ | * 6224937 add merge |/ * 59bc1cb conflict fixed ...?
可以看到,不使用Fast forward模式,merge后就像這樣:
?
?
分支策略
?
在實際開發中,我們應該按照幾個基本原則進行分支管理:
?
首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面干活;
?
那在哪干活呢?干活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev分支合并到master上,在master分支發布1.0版本;
?
你和你的小伙伴們每個人都在dev分支上干活,每個人都有自己的分支,時不時地往dev分支上合并就可以了。
?
所以,團隊合作的分支看起來就像這樣:
?
?
小結
?
Git分支十分強大,在團隊開發中應該充分應用。
?
合并分支時,加上--no-ff參數就可以用普通模式合并,合并后的歷史有分支,能看出來曾經做過合并,而fast forward合并就看不出來曾經做過合并。
?
Bug分支
?
軟件開發中,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}小結
?
修復bug時,我們會通過創建新的bug分支進行修復,然后合并,最后刪除;
?
當手頭工作沒有完成時,先把工作現場git stash一下,然后去修復bug,修復后,再git stash pop,回到工作現場。
?
Feature分支
?
軟件開發中,總有無窮無盡的新的功能要不斷添加進來。
?
添加一個新功能時,你肯定不希望因為一些實驗性質的代碼,把主分支搞亂了,所以,每添加一個新功能,最好新建一個feature分支,在上面開發,完成后,合并,最后,刪除該feature分支。
?
現在,你終于接到了一個新任務:開發代號為Vulcan的新功能,該功能計劃用于下一代星際飛船。
?
于是準備開發:
?
$ git checkout -b feature-vulcan Switched to a new branch 'feature-vulcan'?
5分鐘后,開發完畢:
?
$ git add vulcan.py $ 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 756d4af] add feature vulcan 1 file changed, 2 insertions(+) create mode 100644 vulcan.py?
切回dev,準備合并:
?
$ git checkout dev?
一切順利的話,feature分支和bug分支是類似的,合并,然后刪除。
?
但是,
?
就在此時,接到上級命令,因經費不足,新功能必須取消!
?
雖然白干了,但是這個分支還是必須就地銷毀:
?
$ 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友情提醒,feature-vulcan分支還沒有被合并,如果刪除,將丟失掉修改,如果要強行刪除,需要使用命令git branch -D feature-vulcan。
?
現在我們強行刪除:
?
$ git branch -D feature-vulcan Deleted branch feature-vulcan (was 756d4af).?
終于刪除成功!
小結
?
開發一個新feature,最好新建一個分支;
?
如果要丟棄一個沒有被合并過的分支,可以通過git branch -D <name>強行刪除。
?
多人協作
?
當你從遠程倉庫克隆時,實際上Git自動把本地的master分支和遠程的master分支對應起來了,并且,遠程倉庫的默認名稱是origin。
?
要查看遠程庫的信息,用git remote:
?
$ git remote origin?
或者,用git remote -v顯示更詳細的信息:
?
$ git remote -v origin git@github.com:michaelliao/learngit.git (fetch) origin git@github.com:michaelliao/learngit.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:michaelliao/learngit.git Cloning into 'learngit'... remote: Counting objects: 46, done. remote: Compressing objects: 100% (26/26), done. remote: Total 46 (delta 16), reused 45 (delta 15) Receiving objects: 100% (46/46), 15.69 KiB | 6 KiB/s, done. Resolving deltas: 100% (16/16), done.?
當你的小伙伴從遠程庫clone時,默認情況下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:
?
$ git branch * master?
現在,你的小伙伴要在dev分支上開發,就必須創建遠程origin的dev分支到本地,于是他用這個命令創建本地dev分支:
?
$ git checkout -b dev origin/dev?
現在,他就可以在dev上繼續修改,然后,時不時地把dev分支push到遠程:
?
$ git commit -m "add /usr/bin/env" [dev 291bea8] add /usr/bin/env 1 file changed, 1 insertion(+) $ git push origin dev Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 349 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git fc38031..291bea8 dev -> dev你的小伙伴已經向origin/dev分支推送了他的提交,而碰巧你也對同樣的文件作了修改,并試圖推送:
?
$ git add hello.py $ git commit -m "add coding: utf-8" [dev bd6ae48] add coding: utf-8 1 file changed, 1 insertion(+) $ git push origin dev To git@github.com:michaelliao/learngit.git ! [rejected] dev -> dev (non-fast-forward) error: failed to push some refs to 'git@github.com:michaelliao/learngit.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (e.g. 'git pull') hint: 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: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 3 (delta 0) Unpacking objects: 100% (3/3), done. From github.com:michaelliao/learngit fc38031..291bea8 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 dev origin/<branch>?
git pull也失敗了,原因是沒有指定本地dev分支與遠程origin/dev分支的鏈接,根據提示,設置dev和origin/dev的鏈接:
?
$ git branch --set-upstream dev origin/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 Automatic merge failed; fix conflicts and then commit the result.?
這回git pull成功,但是合并有沖突,需要手動解決,解決的方法和分支管理中的解決沖突完全一樣。解決后,提交,再push:
?
$ git commit -m "merge & fix hello.py" [dev adca45d] merge & fix hello.py $ git push origin dev Counting objects: 10, done. Delta compression using up to 4 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 747 bytes, done. Total 6 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git 291bea8..adca45d 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。
?
這就是多人協作的工作模式,一旦熟悉了,就非常簡單。
?
小結
?
-
查看遠程庫信息,使用git remote -v;
-
本地新建的分支如果不推送到遠程,對其他人就是不可見的;
-
從本地推送分支,使用git push origin branch-name,如果推送失敗,先用git pull抓取遠程的新提交;
-
在本地創建和遠程分支對應的分支,使用git checkout -b branch-name origin/branch-name,本地和遠程分支的名稱最好一致;
-
建立本地分支和遠程分支的關聯,使用git branch --set-upstream branch-name origin/branch-name;
-
從遠程抓取分支,使用git pull,如果有沖突,要先處理沖突。
?
標簽管理
?
發布一個版本時,我們通常先在版本庫中打一個標簽(tag),這樣,就唯一確定了打標簽時刻的版本。將來無論什么時候,取某個標簽的版本,就是把那個打標簽的時刻的歷史版本取出來。所以,標簽也是版本庫的一個快照。
?
Git的標簽雖然是版本庫的快照,但其實它就是指向某個commit的指針(跟分支很像對不對?但是分支可以移動,標簽不能移動),所以,創建和刪除標簽都是瞬間完成的。
?
Git有commit,為什么還要引入tag?
?
“請把上周一的那個版本打包發布,commit號是6a5819e...”
?
“一串亂七八糟的數字不好找!”
?
如果換一個辦法:
?
“請把上周一的那個版本打包發布,版本號是v1.2”
?
“好的,按照tag v1.2查找commit就行!”
?
所以,tag就是一個讓人容易記住的有意義的名字,它跟某個commit綁在一起。
?
創建標簽
?
在Git中打標簽非常簡單,首先,切換到需要打標簽的分支上:
?
$ git branch * devmaster $ git checkout master Switched to branch 'master'?
然后,敲命令git tag <name>就可以打一個新標簽:
?
$ git tag v1.0?
可以用命令git tag查看所有標簽:
?
$ git tag v1.0?
默認標簽是打在最新提交的commit上的。有時候,如果忘了打標簽,比如,現在已經是周五了,但應該在周一打的標簽沒有打,怎么辦?
?
方法是找到歷史提交的commit id,然后打上就可以了:
?
$ git log --pretty=oneline --abbrev-commit 6a5819e merged bug fix 101 cc17032 fix bug 101 7825a50 merge with no-ff 6224937 add merge 59bc1cb conflict fixed 400b400 & simple 75a857c AND simple fec145a branch test d17efd8 remove test.txt ...?
比方說要對add merge這次提交打標簽,它對應的commit id是6224937,敲入命令:
?
$ git tag v0.9 6224937?
再用命令git tag查看標簽:
?
$ git tag v0.9 v1.0?
注意,標簽不是按時間順序列出,而是按字母排序的。可以用git show <tagname>查看標簽信息:
?
$ git show v0.9 commit 622493706ab447b6bb37e4e2a2f276a20fed2ab4 Author: Michael Liao <askxuefeng@gmail.com> Date: Thu Aug 22 11:22:08 2013 +0800 add merge ...?
可以看到,v0.9確實打在add merge這次提交上。
?
還可以創建帶有說明的標簽,用-a指定標簽名,-m指定說明文字:
?
$ git tag -a v0.1 -m "version 0.1 released" 3628164?
用命令git show <tagname>可以看到說明文字:
?
$ git show v0.1 tag v0.1 Tagger: Michael Liao <askxuefeng@gmail.com> Date: Mon Aug 26 07:28:11 2013 +0800version 0.1 releasedcommit 3628164fb26d48395383f8f31179f24e0882e1e0 Author: Michael Liao <askxuefeng@gmail.com> Date: Tue Aug 20 15:11:49 2013 +0800append GPL?
還可以通過-s用私鑰簽名一個標簽:
?
$ git tag -s v0.2 -m "signed version 0.2 released" fec145a?
簽名采用PGP簽名,因此,必須首先安裝gpg(GnuPG),如果沒有找到gpg,或者沒有gpg密鑰對,就會報錯:
?
gpg: signing failed: secret key not available error: gpg failed to sign the data error: unable to sign the tag?
如果報錯,請參考GnuPG幫助文檔配置Key。
?
用命令git show <tagname>可以看到PGP簽名信息:
?
$ git show v0.2 tag v0.2 Tagger: Michael Liao <askxuefeng@gmail.com> Date: Mon Aug 26 07:28:33 2013 +0800 signed version 0.2 released -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin) iQEcBAABAgAGBQJSGpMhAAoJEPUxHyDAhBpT4QQIAKeHfR3bo... -----END PGP SIGNATURE----- commit fec145accd63cdc9ed95a2f557ea0658a2a6537f Author: Michael Liao <askxuefeng@gmail.com> Date: Thu Aug 22 10:37:30 2013 +0800 branch test?
用PGP簽名的標簽是不可偽造的,因為可以驗證PGP簽名。驗證簽名的方法比較復雜,這里就不介紹了。
小結
?
-
命令git tag <name>用于新建一個標簽,默認為HEAD,也可以指定一個commit id;
-
git tag -a <tagname> -m "blablabla..."可以指定標簽信息;
-
git tag -s <tagname> -m "blablabla..."可以用PGP簽名標簽;
-
命令git tag可以查看所有標簽。
?
操作標簽
?
如果標簽打錯了,也可以刪除:
?
$ git tag -d v0.1 Deleted tag 'v0.1' (was e078af9)?
因為創建的標簽都只存儲在本地,不會自動推送到遠程。所以,打錯的標簽可以在本地安全刪除。
?
如果要推送某個標簽到遠程,使用命令git push origin <tagname>:
?
$ git push origin v1.0 Total 0 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git * [new tag] v1.0 -> v1.0?
或者,一次性推送全部尚未推送到遠程的本地標簽:
?
$ git push origin --tags Counting objects: 1, done. Writing objects: 100% (1/1), 554 bytes, done. Total 1 (delta 0), reused 0 (delta 0) To git@github.com:michaelliao/learngit.git * [new tag] v0.2 -> v0.2 * [new tag] v0.9 -> v0.9?
如果標簽已經推送到遠程,要刪除遠程標簽就麻煩一點,先從本地刪除:
?
$ git tag -d v0.9 Deleted tag 'v0.9' (was 6224937)?
然后,從遠程刪除。刪除命令也是push,但是格式如下:
?
$ git push origin :refs/tags/v0.9 To git@github.com:michaelliao/learngit.git - [deleted] v0.9?
要看看是否真的從遠程庫刪除了標簽,可以登陸GitHub查看。
小結
?
-
命令git push origin <tagname>可以推送一個本地標簽;
-
命令git push origin --tags可以推送全部未推送過的本地標簽;
-
命令git tag -d <tagname>可以刪除一個本地標簽;
-
命令git push origin :refs/tags/<tagname>可以刪除一個遠程標簽。
?
使用GitHub
?
我們一直用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/michaelliao/learngit,創建一個your-github-id.txt的文本文件,寫點自己學習Git的心得,然后推送一個pull request給我,我會視心情而定是否接受。
?
小結
?
-
在GitHub上,可以任意Fork開源倉庫;
-
自己擁有Fork后的倉庫的讀寫權限;
-
可以推送pull request給官方倉庫來貢獻代碼。
?
自定義Git
?
在安裝Git一節中,我們已經配置了user.name和user.email,實際上,Git還有很多可配置項。
?
比如,讓Git顯示顏色,會讓命令輸出看起來更醒目:
?
$ git config --global color.ui true?
這樣,Git會適當地顯示不同的顏色,比如git status命令:
?
?
文件名就會標上顏色。
?
我們在后面還會介紹如何更好地配置Git,以便讓你的工作更高效。
?
忽略特殊文件
?
有些時候,你必須把某些文件放到Git工作目錄中,但又不能提交它們,比如保存了數據庫密碼的配置文件啦,等等,每次git status都會顯示Untracked files ...,有強迫癥的童鞋心里肯定不爽。
?
好在Git考慮到了大家的感受,這個問題解決起來也很簡單,在Git工作區的根目錄下創建一個特殊的.gitignore文件,然后把要忽略的文件名填進去,Git就會自動忽略這些文件。
?
不需要從頭寫.gitignore文件,GitHub已經為我們準備了各種配置文件,只需要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:https://github.com/github/gitignore
?
忽略文件的原則是:
?
?
舉個例子:
?
假設你在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 status?status這個單詞真心不好記。
?
如果敲git st就表示git status那就簡單多了,當然這種偷懶的辦法我們是極力贊成的。
?
我們只需要敲一行命令,告訴Git,以后st就表示status:
?
$ git config --global alias.st status?
好了,現在敲git st看看效果。
?
當然還有別的命令可以簡寫,很多人都用co表示checkout,ci表示commit,br表示branch:
?
$ git config --global alias.co checkout $ git config --global alias.ci commit $ git config --global alias.br branch?
以后提交就可以簡寫成:
?
$ git ci -m "bala bala bala..."?
--global參數是全局參數,也就是這些命令在這臺電腦的所有Git倉庫下都有用。
?
在撤銷修改一節中,我們知道,命令git reset HEAD file可以把暫存區的修改撤銷掉(unstage),重新放回工作區。既然是一個unstage操作,就可以配置一個unstage別名:
?
$ git config --global alias.unstage 'reset HEAD'?
當你敲入命令:
?
$ git unstage test.py?
實際上Git執行的是:
?
$ git reset HEAD test.py?
配置一個git last,讓其顯示最后一次提交信息:
?
$ git config --global alias.last 'log -1'?
這樣,用git last就能顯示最近一次的提交:
?
$ git last commit adca45d317e6d8a4b23f9811c3d7b7f0f180bfe2 Merge: bd6ae48 291bea8 Author: Michael Liao <askxuefeng@gmail.com> Date: Thu Aug 22 22:49:22 2013 +0800merge & fix hello.py?
甚至還有人喪心病狂地把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 lg的效果:
?
?
為什么不早點告訴我?別激動,咱不是為了多記幾個英文單詞嘛!
?
配置文件
?
配置Git的時候,加上--global是針對當前用戶起作用的,如果不加,那只針對當前的倉庫起作用。
?
配置文件放哪了?每個倉庫的Git配置文件都放在.git/config文件中:
?
$ cat .git/config [core]repositoryformatversion = 0filemode = truebare = falselogallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = git@github.com:michaelliao/learngit.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master [alias] last = log -1?
別名就在[alias]后面,要刪除別名,直接把對應的行刪掉即可。
?
而當前用戶的Git配置文件放在用戶主目錄下的一個隱藏文件.gitconfig中:
?
$ cat .gitconfig [alias]co = checkoutci = commitbr = branchst = status [user]name = Your Nameemail = your@email.com?
配置別名也可以直接修改這個文件,如果改錯了,可以刪掉文件重新通過命令配置。
?
小結
?
給Git配置好別名,就可以輸入命令時偷個懶。我們鼓勵偷懶。
?
搭建Git服務器
?
在遠程倉庫一節中,我們講了遠程倉庫實際上和本地倉庫沒啥不同,純粹為了7x24小時開機并交換大家的修改。
?
GitHub就是一個免費托管開源代碼的遠程倉庫。但是對于某些視源代碼如生命的商業公司來說,既不想公開源代碼,又舍不得給GitHub交保護費,那就只能自己搭建一臺Git服務器作為私有倉庫使用。
?
搭建Git服務器需要準備一臺運行Linux的機器,強烈推薦用Ubuntu或Debian,這樣,通過幾條簡單的apt命令就可以完成安裝。
?
假設你已經有sudo權限的用戶賬號,下面,正式開始安裝。
?
第一步,安裝git:
?
$ sudo apt-get install git?
第二步,創建一個git用戶,用來運行git服務:
?
$ sudo adduser git?
第三步,創建證書登錄:
?
收集所有需要登錄的用戶的公鑰,就是他們自己的id_rsa.pub文件,把所有公鑰導入到/home/git/.ssh/authorized_keys文件里,一行一個。
?
第四步,初始化Git倉庫:
?
先選定一個目錄作為Git倉庫,假定是/srv/sample.git,在/srv目錄下輸入命令:
?
$ sudo git init --bare sample.git?
Git就會創建一個裸倉庫,裸倉庫沒有工作區,因為服務器上的Git倉庫純粹是為了共享,所以不讓用戶直接登錄到服務器上去改工作區,并且服務器上的Git倉庫通常都以.git結尾。然后,把owner改為git:
?
$ sudo chown -R git:git sample.git?
第五步,禁用shell登錄:
?
出于安全考慮,第二步創建的git用戶不允許登錄shell,這可以通過編輯/etc/passwd文件完成。找到類似下面的一行:
?
git:x:1001:1001:,,,:/home/git:/bin/bash?
改為:
?
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell?
這樣,git用戶可以正常通過ssh使用git,但無法登錄shell,因為我們為git用戶指定的git-shell每次一登錄就自動退出。
?
第六步,克隆遠程倉庫:
?
現在,可以通過git clone命令克隆遠程倉庫了,在各自的電腦上運行:
?
$ git clone git@server:/srv/sample.git Cloning into 'sample'... warning: You appear to have cloned an empty repository.?
剩下的推送就簡單了。
?
管理公鑰
?
如果團隊很小,把每個人的公鑰收集起來放到服務器的/home/git/.ssh/authorized_keys文件里就是可行的。如果團隊有幾百號人,就沒法這么玩了,這時,可以用Gitosis來管理公鑰。
?
這里我們不介紹怎么玩Gitosis了,幾百號人的團隊基本都在500強了,相信找個高水平的Linux管理員問題不大。
?
管理權限
?
有很多不但視源代碼如生命,而且視員工為竊賊的公司,會在版本控制系統里設置一套完善的權限控制,每個人是否有讀寫權限會精確到每個分支甚至每個目錄下。因為Git是為Linux源代碼托管而開發的,所以Git也繼承了開源社區的精神,不支持權限控制。不過,因為Git支持鉤子(hook),所以,可以在服務器端編寫一系列腳本來控制提交等操作,達到權限控制的目的。Gitolite就是這個工具。
?
這里我們也不介紹Gitolite了,不要把有限的生命浪費到權限斗爭中。
?
小結
?
-
搭建Git服務器非常簡單,通常10分鐘即可完成;
-
要方便管理公鑰,用Gitosis;
-
要像SVN那樣變態地控制權限,用Gitolite。
?
總結
經過幾天的學習,相信你對Git已經初步掌握。一開始,可能覺得Git上手比較困難,尤其是已經熟悉SVN的童鞋,沒關系,多操練幾次,就會越用越順手。
Git雖然極其強大,命令繁多,但常用的就那么十來個,掌握好這十幾個常用命令,你已經可以得心應手地使用Git了。
友情附贈國外網友制作的Git Cheat Sheet,建議打印出來備用:
Git Cheat Sheet
現在告訴你Git的官方網站:http://git-scm.com,英文自我感覺不錯的童鞋,可以經常去官網看看。什么,打不開網站?相信我,我給出的絕對是官網地址,而且,Git官網決沒有那么容易宕機,可能是你的人品問題,趕緊面壁思過,好好想想原因。
如果你學了Git后,工作效率大增,有更多的空閑時間健身看電影,那我的教學目標就達到了。
?
轉載于:https://www.cnblogs.com/tooy/p/7150087.html
總結
- 上一篇: 高丽蒙古战争的结果如何又有怎样的影响
- 下一篇: 部队伤残清查合格有通知书吗