簡介
Git 是什么?
Git 是一個開源的分布式版本控制系統。
什么是版本控制?
版本控制是一種記錄一個或若干文件內容變化,以便將來查閱特定版本修訂情況的系統。
什么是分布式版本控制系統?
介紹分布式版本控制系統前,有必要先了解一下傳統的集中式版本控制系統。
集中化的版本控制系統,諸如 CVS,Subversion 等,都有一個單一的集中管理的服務器,保存所有文件的修訂版本,而協同工作的人們都通過客戶端連到這臺服務器,取出最新的文件或者提交更新。
這么做最顯而易見的缺點是中央服務器的單點故障。如果宕機一小時,那么在這一小時內,誰都無法提交更新,也就無法協同工作。要是中央服務器的磁盤發生故障,碰巧沒做備份,或者備份不夠及時,就會有丟失數據的風險。最壞的情況是徹底丟失整個項目的所有歷史更改記錄。
分布式版本控制系統的客戶端并不只提取最新版本的文件快照,而是把代碼倉庫完整地鏡像下來。這么一來,任何一處協同工作用的服務器發生故障,事后都可以用任何一個鏡像出來的本地倉庫恢復。因為每一次的提取操作,實際上都是一次對代碼倉庫的完整備份。
為什么使用 Git?
Git 是分布式的。這是 Git 和其它非分布式的版本控制系統,例如 svn,cvs 等,最核心的區別。分布式帶來以下好處:
工作時不需要聯網
首先,分布式版本控制系統根本沒有“中央服務器”,每個人的電腦上都是一個完整的版本庫,這樣,你工作的時候,就不需要聯網了,因為版本庫就在你自己的電腦上。既然每個人電腦上都有一個完整的版本庫,那多個人如何協作呢?比方說你在自己電腦上改了文件A,你的同事也在他的電腦上改了文件A,這時,你們倆之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。
更加安全
集中式版本控制系統,一旦中央服務器出了問題,所有人都無法工作。
分布式版本控制系統,每個人電腦中都有完整的版本庫,所以某人的機器掛了,并不影響其它人。
原理
版本庫
當你一個項目到本地或創建一個 git 項目,項目目錄下會有一個隱藏的 .git
子目錄。這個目錄是 git 用來跟蹤管理版本庫的,千萬不要手動修改。
哈希值
Git 中所有數據在存儲前都計算校驗和,然后以校驗和來引用。 這意味著不可能在 Git 不知情時更改任何文件內容或目錄內容。 這個功能建構在 Git 底層,是構成 Git 哲學不可或缺的部分。 若你在傳送過程中丟失信息或損壞文件,Git 就能發現。
Git 用以計算校驗和的機制叫做 SHA-1 散列(hash,哈希)。 這是一個由 40 個十六進制字符(0-9 和 a-f)組成字符串,基于 Git 中文件的內容或目錄結構計算出來。 SHA-1 哈希看起來是這樣:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用這種哈希值的情況很多,你將經常看到這種哈希值。 實際上,Git 數據庫中保存的信息都是以文件內容的哈希值來索引,而不是文件名。
文件狀態
在 GIt 中,你的文件可能會處于三種狀態之一:
-
已修改(modified)
已修改表示修改了文件,但還沒保存到數據庫中。
-
已暫存(staged)
已暫存表示對一個已修改文件的當前版本做了標記,使之包含在下次提交的快照中。
-
已提交(committed)
已提交表示數據已經安全的保存在本地數據庫中。
工作區域
與文件狀態對應的,不同狀態的文件在 Git 中處于不同的工作區域。
-
工作區(working)
當你
git clone
一個項目到本地,相當于在本地克隆了項目的一個副本。工作區是對項目的某個版本獨立提取出來的內容。 這些從 Git 倉庫的壓縮數據庫中提取出來的文件,放在磁盤上供你使用或修改。
-
暫存區(staging)
暫存區是一個文件,保存了下次將提交的文件列表信息,一般在 Git 倉庫目錄中。 有時候也被稱作`‘索引’',不過一般說法還是叫暫存區。
-
本地倉庫(local)
提交更新,找到暫存區域的文件,將快照永久性存儲到 Git 本地倉庫。
-
遠程倉庫(remote)
以上幾個工作區都是在本地。為了讓別人可以看到你的修改,你需要將你的更新推送到遠程倉庫。
同理,如果你想同步別人的修改,你需要從遠程倉庫拉取更新。
安裝
Linux
Debian/Ubuntu
如果你使用的系統是 Debian/Ubuntu , 安裝命令為:
$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
> libz-dev libssl-dev
$ apt-get install git-core
$ git --version
git version 1.8.1.2
Centos/RedHat
如果你使用的系統是 Centos/RedHat ,安裝命令為:
$ yum install curl-devel expat-devel gettext-devel \
> openssl-devel zlib-devel
$ yum -y install git-core
$ git --version
git version 1.7.1
Windows
在Git 官方下載地址下載 exe 安裝包。按照安裝向導安裝即可。
建議安裝 Git Bash 這個 git 的命令行工具。
Mac
在Git 官方下載地址下載 mac 安裝包。按照安裝向導安裝即可。
配置
Git 自帶一個 git config
的工具來幫助設置控制 Git 外觀和行為的配置變量。 這些變量存儲在三個不同的位置:
-
/etc/gitconfig
文件: 包含系統上每一個用戶及他們倉庫的通用配置。 如果使用帶有--system
選項的git config
時,它會從此文件讀寫配置變量。 -
~/.gitconfig
或~/.config/git/config
文件:只針對當前用戶。 可以傳遞--global
選項讓 Git 讀寫此文件。 - 當前使用倉庫的 Git 目錄中的
config
文件(就是.git/config
):針對該倉庫。
每一個級別覆蓋上一級別的配置,所以 .git/config
的配置變量會覆蓋 /etc/gitconfig
中的配置變量。
在 Windows 系統中,Git 會查找 $HOME
目錄下(一般情況下是 C:\Users\$USER
)的 .gitconfig
文件。 Git 同樣也會尋找 /etc/gitconfig
文件,但只限于 MSys 的根目錄下,即安裝 Git 時所選的目標位置。
用戶信息
當安裝完 Git 應該做的第一件事就是設置你的用戶名稱與郵件地址。 這樣做很重要,因為每一個 Git 的提交都會使用這些信息,并且它會寫入到你的每一次提交中,不可更改:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
再次強調,如果使用了 --global
選項,那么該命令只需要運行一次,因為之后無論你在該系統上做任何事情, Git 都會使用那些信息。 當你想針對特定項目使用不同的用戶名稱與郵件地址時,可以在那個項目目錄下運行沒有 --global
選項的命令來配置。
很多 GUI 工具都會在第一次運行時幫助你配置這些信息。
.gitignore
.gitignore
文件可能從字面含義也不難猜出:這個文件里配置的文件或目錄,會自動被 git 所忽略,不納入版本控制。
在日常開發中,我們的項目經常會產生一些臨時文件,如編譯 Java 產生的 *.class
文件,又或是 IDE 自動生成的隱藏目錄(Intellij 的 .idea
目錄、Eclipse 的 .settings
目錄等)等等。這些文件或目錄實在沒必要納入版本管理。在這種場景下,你就需要用到 .gitignore
配置來過濾這些文件或目錄。
配置的規則很簡單,也沒什么可說的,看幾個例子,自然就明白了。
這里推薦一下 Github 的開源項目:https://github.com/github/gitignore
在這里,你可以找到很多常用的模板,如:Java、Nodejs、C++ 的 .gitignore
模板等等。
命令
國外網友制作了一張 Git Cheat Sheet,總結很精煉,各位不妨收藏一下。
本節選擇性介紹 git 中比較常用的命令行場景。
創建
克隆一個已創建的倉庫
# 通過 SSH
$ git clone ssh://user@domain.com/repo.git
#通過 HTTP
$ git clone http://domain.com/user/repo.git
創建一個新的本地倉庫
$ git init
添加修改
添加修改到暫存區
# 把指定文件添加到暫存區
$ git add xxx
# 把當前所有修改添加到暫存區
$ git add .
# 把所有修改添加到暫存區
$ git add -A
提交修改到本地倉庫
# 提交本地的所有修改
$ git commit -a
# 提交之前已標記的變化
$ git commit
# 附加消息提交
$ git commit -m 'commit message'
儲藏
有時,我們需要在同一個項目的不同分支上工作。當需要切換分支時,偏偏本地的工作還沒有完成,此時,提交修改顯得不嚴謹,但是不提交代碼又無法切換分支。這時,你可以使用 git stash
將本地的修改內容作為草稿儲藏起來。
官方稱之為儲藏,但我個人更喜歡稱之為存草稿。
# 1. 將修改作為當前分支的草稿保存
$ git stash
# 2. 查看草稿列表
$ git stash list
stash@{0}: WIP on master: 6fae349 :memo: Writing docs.
# 3.1 刪除草稿
$ git stash drop stash@{0}
# 3.2 讀取草稿
$ git stash apply stash@{0}
撤銷修改
撤銷本地修改
# 移除緩存區的所有文件(i.e. 撤銷上次git add)
$ git reset HEAD
# 將HEAD重置到上一次提交的版本,并將之后的修改標記為未添加到緩存區的修改
$ git reset <commit>
# 將HEAD重置到上一次提交的版本,并保留未提交的本地修改
$ git reset --keep <commit>
# 放棄工作目錄下的所有修改
$ git reset --hard HEAD
# 將HEAD重置到指定的版本,并拋棄該版本之后的所有修改
$ git reset --hard <commit-hash>
# 用遠端分支強制覆蓋本地分支
$ git reset --hard <remote/branch> e.g., upstream/master, origin/my-feature
# 放棄某個文件的所有本地修改
$ git checkout HEAD <file>
刪除添加.gitignore
文件前錯誤提交的文件
$ git rm -r --cached .
$ git add .
$ git commit -m "remove xyz file"
撤銷遠程修改
創建一個新的提交,并回滾到指定版本
$ git revert <commit-hash>
徹底刪除指定版本
# 執行下面命令后,commit-hash 提交后的記錄都會被徹底刪除,使用需謹慎
$ git reset --hard <commit-hash>
$ git push -f
更新與推送
更新
# 下載遠程端版本,但不合并到HEAD中
$ git fetch <remote>
# 將遠程端版本合并到本地版本中
$ git pull origin master
# 以rebase方式將遠端分支與本地合并
$ git pull --rebase <remote> <branch>
推送
# 將本地版本推送到遠程端
$ git push remote <remote> <branch>
# 刪除遠程端分支
$ git push <remote> :<branch> (since Git v1.5.0)
$ git push <remote> --delete <branch> (since Git v1.7.0)
# 發布標簽
$ git push --tags
查看信息
顯示工作路徑下已修改的文件
$ git status
顯示與上次提交版本文件的不同
$ git diff
顯示提交歷史
# 從最新提交開始,顯示所有的提交記錄(顯示hash, 作者信息,提交的標題和時間)
$ git log
# 顯示某個用戶的所有提交
$ git log --author="username"
# 顯示某個文件的所有修改
$ git log -p <file>
顯示搜索內容
# 從當前目錄的所有文件中查找文本內容
$ git grep "Hello"
# 在某一版本中搜索文本
$ git grep "Hello" v2.5
分支與標簽
增刪查分支
# 列出所有的分支
$ git branch
# 列出所有的遠端分支
$ git branch -r
# 基于當前分支創建新分支
$ git branch <new-branch>
# 基于遠程分支創建新的可追溯的分支
$ git branch --track <new-branch> <remote-branch>
# 刪除本地分支
$ git branch -d <branch>
# 強制刪除本地分支,將會丟失未合并的修改
$ git branch -D <branch>
切換分支
# 切換分支
$ git checkout <branch>
# 創建并切換到新分支
$ git checkout -b <branch>
標簽
# 給當前版本打標簽
$ git tag <tag-name>
# 給當前版本打標簽并附加消息
$ git tag -a <tag-name>
合并與重置
merge 與 rebase 雖然是 git 常用功能,但是強烈建議不要使用 git 命令來完成這項工作。
因為如果出現代碼沖突,在沒有代碼比對工具的情況下,實在太艱難了。
你可以考慮使用各種 Git GUI 工具。
合并
# 將分支合并到當前HEAD中
$ git merge <branch>
重置
# 將當前HEAD版本重置到分支中,請勿重置已發布的提交
$ git rebase <branch>
Github
Github 作為最著名的代碼開源協作社區,在程序員圈想必無人不知,無人不曉。
這里不贅述 Github 的用法,確實有不會用的新手同學,可以參考官方教程:https://guides.github.com/
clone 方式
Git 支持三種協議:HTTPS / SSH / GIT
而 Github 上支持 HTTPS 和 SSH。
HTTPS 這種方式要求你每次 push 時都要輸入用戶名、密碼,有些繁瑣。
而 SSH 要求你本地生成證書,然后在你的 Github 賬戶中注冊。第一次配置麻煩是麻煩了點,但是以后就免去了每次 push 需要輸入用戶名、密碼的繁瑣。
以下介紹以下,如何生成證書,以及在 Github 中注冊。
生成 SSH 公鑰
如前所述,許多 Git 服務器都使用 SSH 公鑰進行認證。 為了向 Git 服務器提供 SSH 公鑰,如果某系統用戶尚未擁有密鑰,必須事先為其生成一份。 這個過程在所有操作系統上都是相似的。 首先,你需要確認自己是否已經擁有密鑰。 默認情況下,用戶的 SSH 密鑰存儲在其 ~/.ssh
目錄下。 進入該目錄并列出其中內容,你便可以快速確認自己是否已擁有密鑰:
$ cd ~/.ssh
$ ls
authorized_keys2 id_dsa known_hosts
config id_dsa.pub
我們需要尋找一對以 id_dsa
或 id_rsa
命名的文件,其中一個帶有 .pub
擴展名。 .pub
文件是你的公鑰,另一個則是私鑰。 如果找不到這樣的文件(或者根本沒有 .ssh
目錄),你可以通過運行 ssh-keygen
程序來創建它們。在 Linux/Mac 系統中,ssh-keygen
隨 SSH 軟件包提供;在 Windows 上,該程序包含于 MSysGit 軟件包中。
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/schacon/.ssh/id_rsa):
Created directory '/home/schacon/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/schacon/.ssh/id_rsa.
Your public key has been saved in /home/schacon/.ssh/id_rsa.pub.
The key fingerprint is:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local
首先 ssh-keygen
會確認密鑰的存儲位置(默認是 .ssh/id_rsa
),然后它會要求你輸入兩次密鑰口令。如果你不想在使用密鑰時輸入口令,將其留空即可。
現在,進行了上述操作的用戶需要將各自的公鑰發送給任意一個 Git 服務器管理員(假設服務器正在使用基于公鑰的 SSH 驗證設置)。 他們所要做的就是復制各自的 .pub
文件內容,并將其通過郵件發送。 公鑰看起來是這樣的:
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== schacon@mylaptop.local
在你的 Github 賬戶中,依次點擊 Settings > SSH and GPG keys > New SSH key
然后,將上面生成的公鑰內容粘貼到 Key
編輯框并保存。至此大功告成。
后面,你在克隆你的 Github 項目時使用 SSH 方式即可。
如果覺得我的講解還不夠細致,可以參考:https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/
小結
最后,放一張我總結的腦圖總結一下以上的知識點。