[TOC]
初始倉庫(git init)
$ git init
把文件添加到倉庫(git add)
$ git add 文件名 #單個文件
或
$ git add 文件名 文件名 文件名 #多個文件
或
$ git add . #全部文件 *也是一樣
把文件提交到倉庫(git commit)
$ git commit -m "本次提交的說明"
查看倉庫狀態
查看倉庫當前狀態(git status)
$ git status
查看倉庫具體狀態(git diff具體修改了什么內容)
$ git diff #全部
$ git diff 文件名 #單個文件的狀態
查看歷史記錄(git log/reflog)
$ git log #不能察看已經刪除了的commit記錄
$ git reflog #查看所有分支的所有操作記錄
當前版本(HEAD)
HEAD #表示當前版本
HEAD^ #表示上個版本
HEAD^^ #表示上上一個版本
HEAD~100 #表示往上100個版本
版本回滾(git reset)
$ git reset --hard HEAD^ #回退到上一個版本
'或'
$ git reset --hard 3628164 #指定回到某個版本3628164是commit id
刪除文件(git rm)
$ git rm 文件名
$ git commit -m "刪除說明"
本地倉庫與遠程庫關聯(git remote add)
$ git remote add origin git@github.com:michaelliao/learngit.git
#michaelliao表示GitHub賬戶名,遠程庫的名字就是origin
本地庫的內容推送到遠程(git push)
$ git push -u origin master #我們第一次推送master分支時,加上了-u參數
$ git push origin master #其他時候不用加-u
從遠程庫克隆(git clone)
$ git clone git@github.com:michaelliao/gitskills.git
分支管理(branch)
創建+切換分支
$ git checkout -b dev #首先,我們創建dev分支,然后切換到dev分支
git checkout命令加上-b參數表示創建并切換,相當于以下兩條命令:
$ git branch dev #創建dev分支
$ git checkout dev #切換到dev分支
查看當前分支
$ git branch #git branch命令會列出所有分支
* dev
master
#當前分支前面會標一個*號
切換分支(git checkout)
$ git checkout master #切換回master分支
合并某分支到當前分支(git merge)
- 通常,合并分支時,如果可能,Git會用Fast forward模式,但這種模式下,刪除分支后,會丟掉分支信息。
$ git merge dev #我們把dev分支的工作成果合并到master分支上(git merge命令用于合并指定分支到當前分支)
2.如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
下面我們實戰一下--no-ff方式的git merge
$ git merge --no-ff -m "merge with no-ff" dev
因為本次合并要創建一個新的commit,所以加上-m參數,把commit描述寫進去。
合并后,我們用git log看看分支歷史:
$ git log --graph --pretty=oneline --abbrev-commit
* 7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
* 59bc1cb conflict fixed
...
小結
Git分支十分強大,在團隊開發中應該充分應用。
合并分支時,加上--no-ff參數就可以用普通模式合并,合并后的歷史有分支,能看出來曾經做過合并,而fast forward合并就看不出來曾經做過合并。
查看分支合并圖(git log --graph)
$ git log --graph --pretty=oneline --abbrev-commit
# --pretty=oneline:一行顯示,只顯示哈希值和提交說明
#--abbrev-commit:僅顯示SHA-1的前幾個字符,而非所有的40個字符
刪除分支(git branch -d)
$ git branch -d dev #合并完成后,就可以放心地刪除dev分支了
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
#
把當前工作現場“儲藏”起來(git stash)
并不是你不想提交,而是工作只進行到一半,還沒法提交,預計完成還需1天時間。但是,必須在兩個小時內修復該bug,怎么辦?
幸好,Git還提供了一個stash功能,可以把當前工作現場“儲藏”起來,等以后恢復現場后繼續工作:
$ git stash
查看“儲藏”的工作現場(git stash list)
$ git stash list
恢復工作現場
工作現場還在,Git把stash內容存在某個地方了,但是需要恢復一下,有兩個辦法:
一是用git stash apply恢復,但是恢復后,stash內容并不刪除,你需要用git stash drop來刪除;
另一種方式是用git stash pop,恢復的同時把stash內容也刪了:
$ git stash pop
小結
修復bug時,我們會通過創建新的bug分支進行修復,然后合并,最后刪除;
當手頭工作沒有完成時,先把工作現場git stash一下,然后去修復bug,修復后,再git stash pop,回到工作現場。
Feature(功能)分支
軟件開發中,總有無窮無盡的新的功能要不斷添加進來。
添加一個新功能時,你肯定不希望因為一些實驗性質的代碼,把主分支搞亂了,所以,每添加一個新功能,最好新建一個feature分支,在上面開發,完成后,合并,最后,刪除該feature分支。
強行刪除
$ git branch -D feature-vulcan
- 小結
開發一個新feature,最好新建一個分支;
如果要丟棄一個沒有被合并過的分支,可以通過git branch -D <name>強行刪除。
標簽管理(tag)
發布一個版本時,我們通常先在版本庫中打一個標簽(tag),這樣,就唯一確定了打標簽時刻的版本。將來無論什么時候,取某個標簽的版本,就是把那個打標簽的時刻的歷史版本取出來。所以,標簽也是版本庫的一個快照。
Git的標簽雖然是版本庫的快照,但其實它就是指向某個commit的指針(跟分支很像對不對?但是分支可以移動,標簽不能移動),所以,創建和刪除標簽都是瞬間完成的。
Git有commit,為什么還要引入tag?
“請把上周一的那個版本打包發布,commit號是6a5819e...”
“一串亂七八糟的數字不好找!”
如果換一個辦法:
“請把上周一的那個版本打包發布,版本號是v1.2”
“好的,按照tag v1.2查找commit就行!”
所以,tag就是一個讓人容易記住的有意義的名字,它跟某個commit綁在一起。
創建標簽(git tag <name>)
$ 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
創建帶有說明的標簽(用-a指定標簽名,-m指定說明文字)
$ git tag -a v0.1 -m "version 0.1 released" 3628164
查看標簽(git tag)
$ git tag #查看所以標簽
查看標簽信息(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
...
小結
命令git tag <name>用于新建一個標簽,默認為HEAD,也可以指定一個commit id;
git tag -a <tagname> -m "blablabla..."可以指定標簽信息;
git tag -s <tagname> -m "blablabla..."可以用PGP簽名標簽;
命令git tag可以查看所有標簽。
操作標簽
刪除標簽(git tag -d <tagname>)
$ 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
小結
命令git push origin <tagname>可以推送一個本地標簽;
命令git push origin --tags可以推送全部未推送過的本地標簽;
命令git tag -d <tagname>可以刪除一個本地標簽;
命令git push origin :refs/tags/<tagname>可以刪除一個遠程標簽。
搭建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來管理公鑰。
管理權限
有很多不但視源代碼如生命,而且視員工為竊賊的公司,會在版本控制系統里設置一套完善的權限控制,每個人是否有讀寫權限會精確到每個分支甚至每個目錄下。因為Git是為Linux源代碼托管而開發的,所以Git也繼承了開源社區的精神,不支持權限控制。不過,因為Git支持鉤子(hook),所以,可以在服務器端編寫一系列腳本來控制提交等操作,達到權限控制的目的。Gitolite就是這個工具。
小結
搭建Git服務器非常簡單,通常10分鐘即可完成;
要方便管理公鑰,用Gitosis;
要像SVN那樣變態地控制權限,用Gitolite。