學習完廖雪峯老師的git課堂,結合教學內容總結一下。linux
Git是分佈式版本控制系統。git
集中式vs分佈式github
1.集中式shell
典型:CVS,SVNbootstrap
解析:版本庫是集中存放在中央服務器的,中央服務器就比如是一個圖書館,你要改一本書,必須先從圖書館借出來,而後回到家本身改,改完了,再放回圖書館。安全
2.分佈式bash
分佈式版本控制系統根本沒有「中央服務器」,每一個人的電腦上都是一個完整的版本庫。但其實,有一臺充當「中央服務器」的電腦,但這個服務器的做用僅僅是用來方便「交換」你們的修改,沒有它你們也同樣幹活,只是交換修改不方便而已。服務器
查看git是否安裝app
$git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git框架
1.Debian或Ubuntu Linux像如上提示安裝
2.老版本Debian或Ubuntu Linux :sudo apt-get install git-core
3.其餘linux發行版,用源碼安裝
(1).Git官網下載源碼,而後解壓
(2).依次執行
./config,make,sudo make install
倉庫,也叫版本庫,英文名repository,你能夠簡單理解成一個目錄。
1.建立一個空目錄
$mkdir learngit
$cd learngit
$pwd
/Users/michael/learngit
2.經過git init命令把這個目錄變成Git能夠管理的倉庫
$git init
Initialized empty Gitrepositoryin /Users/michael/learngit/.git/
以readme.txt爲例。注意這個文件必定要在git倉庫下,即目錄learngit或其子目錄下。
1.用命令git add告訴Git,把文件添加到倉庫
$git add readme.txt
2.用命令git commit告訴Git,把文件添加到倉庫
$ git commit -m "wrote a readme file"
[master (root-commit) cb926e7] wrote a readme file
1file changed,2insertions(+)
create mode 100644 readme.txt
將文件添加到版本庫後,用命令git status查看倉庫狀態。
$git status
On branch master
nothing to commit (working directory clean)
git告訴咱們工做目錄是乾淨(working directory clean)!
修改readme.txt文件後,用命令git status查看倉庫狀態。
$git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use"git add"and/or"git commit -a")
上面的命令告訴咱們,readme.txt被修改過了,但尚未準備提交的修改。
查看修改:
$ git diff readme.txt
如上命令會提示修改詳細。
提交修改:
第一步:git add *
$git add readme.txt
在進行第二步git commit前,查看一下倉庫狀態
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD..." to unstage) modified: readme.txtgit告訴咱們,將要被提交的修改包括readme.txt,下一步:放心提交
第二步:git commit *
$ git commit -m "add distributed"
[master ea34578]add distributed
1 file changed,1 insertion(+),1 deletion(-)
提交後,再次查看倉庫狀態
$ git status
On branch master
nothing to commit (working directory clean)
Git告訴咱們工做目錄是乾淨(working directory clean)的,沒有要提交的修改。
Git容許咱們在版本的歷史之間穿梭,使用命令 git reset --hard commit_id
1.HEAD指向當前版本,上一個版本就是HEAD^,上上一個版本就是HEAD^^
2.回到上個版本:git reset --hard HEAD^ , 回到上上個版本:git reset --hard HEAD^^
3.回到歷史某個版本:首先用git log 查看提交歷史,而後利用commit id使用命令如:git reset --hard 3628164 (版本號不必寫全,前幾位就能夠了,Git會自動去找)
4.從歷史某個版本回到如今:首先用git reflog查看命令歷史,以便肯定要回到將來的哪一個版本。
$ git reflog
ea34578 HEAD@{0}: reset: moving to HEAD^
3628164 HEAD@{1}: commit: append GPL
ea34578 HEAD@{2}: commit: add distributed
cb926e7 HEAD@{3}: commit (initial): wrote a readme file
回到append GPL版本即: git reset --hard 3628164
1.工做區(Working Directory)
就是你在電腦裏能看到的目錄,好比個人learngit文件夾就是一個工做區。
2.版本庫(Repository)
工做區有一個隱藏目錄.git,這個不算工做區,而是Git的版本庫。
3.暫存區(stage/index)
Git的版本庫裏存了不少東西,其中最重要的就是成爲stage或者index的暫存區。還有Git爲咱們自動建立的master分支,以及指向master的指針叫HEAD。
回憶一下,把文件添加到版本庫分兩步。
第一步:git add,添加文件。實際就是把文件添加到暫存區。
第二步:git commit,提交修改。實際就是把暫存區的內容提交到當前分支。
能夠簡單理解爲:第一步是將要提交的修改通通放到暫存區。第二步是一次性提交全部修改。
繼續實踐:
如今git庫有個文件readme.txt,而且工做空間乾淨了。
咱們修改文件readme.txt,再添加一個新文件test.txt。
而後查看工做空間狀態。
$ git status
On branch master
Changes not staged for commit:
(use "git add..." to update what will be committed)
(use "git checkout --..." to discard changes in working directory)
modified: readme.txtUntracked files:
(use "git add..." to include in what will be committed)
test.txtno changes added to commit (use "git add" and/or "git commit -a")
Git很是清楚地告訴咱們,readme.txt被修改了,而test.txt還歷來沒有被添加過,因此它的狀態是Untracked。
把兩個文件添加到暫存區
$git add readme.txt
$git add test.txt
再次查看狀態
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD..." to unstage)new file: LICENSE modified: readme.txtGit提示咱們有未提交的修改,咱們圖示一下暫存區狀態。
經過圖示可清楚看到,經過git add命令將文件提交到了暫存區。
下面咱們提交文件到Git版本庫。
$git commit -m "understand how stage works"
[master 27c9860] understand how stage works
2 files changed, 675 insertions(+)
create mode 100644 LICENSE
查看狀態
$git status
On branch master
nothing to commit (working directory clean)
圖示狀態
GIt之因此優秀,是由於不同凡響的是,它管理的是修改,並不是文件。
提交修改:
①修改→git add添加修改→git commit提交修改
②第一次修改→git add添加修改→git commit提交修改→第二次修改→git add添加修改→git commit提交修改
③第一次修改→git add添加修改→第二次修改→git add添加修改→git commit一塊兒提交修改
若是②中第二次修改沒有git add添加而直接git commit提交,那麼git status查看狀態,就會以下提示
$git status
......
no changes added to commit (use"git add"and/or"git commit -a")
用git diff HEAD -- readme.txt命令能夠查看工做區和版本庫裏面最新版本的區別。
1.修改文件,還未git add
使用命令git checkout -- file丟棄工做區的修改
1.若是暫存區無未提交的修改,版本庫會覆蓋本地 2.若是暫存區有未提交的修改,暫存區會覆蓋本地
總之,就是讓這個文件回到最近一次git commit或git add時的狀態。
2.修改文件,已經git add
第一步:使用命令git reset HEAD file能夠把暫存區的修改撤銷掉(unstage),從新放回工做區。
$ git reset HEAD readme.txt
Unstaged changes after reset:
readme.txt
第二步:丟棄工做區的修改
$git checkout -- readme.txt
查看一下狀態,工做空間乾淨了。
$ git status
On branch master
nothing to commit (working directory clean)
在Git中,刪除也是一個修改操做。
1.新建一個文件,並提交到版本庫
$ git add test.txt
$ git commit -m "add test.txt"
2.從版本庫刪除它
第一步:在工做空間中刪了,而後查看狀態,git告訴咱們工做區和版本庫不同了
$rm test.txt
$git statusOn branch master
Changes not staged for commit:
(use "git add/rm..." to update what will be committed) (use "git checkout --..." to discard changes in working directory) deleted: test.txtno changes added to commit (use "git add" and/or "git commit -a")
第二步:從版本庫刪除該文件
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d17efd8] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
另外一種狀況是,若是本地刪除了該文件,發現是誤刪,能夠用版本庫恢復本地誤刪的文件。
$git checkout -- test.txt
git checkout實際上是用版本庫裏的版本替換工做區的版本,不管工做區是修改仍是刪除,均可以「一鍵還原」。
設置
因爲你的本地Git倉庫和GitHub倉庫之間的傳輸是經過SSH加密的,因此,須要一點設置:
第1步:建立SSH Key。打開Shell(Windows下打開Git Bash)
$ ssh-keygen -t rsa -C "youremail@example.com"
而後用戶主目錄裏能夠找到.ssh目錄,裏面有id_rsa和id_rsa.pub兩個文件,這兩個就是SSH Key的祕鑰對,id_rsa是私鑰,id_rsa.pub是公鑰。
第2步:登錄GitHub,打開「Account settings」→「SSH Keys」頁面→「New SSH key」,填上任意Title,在Key文本框裏粘貼id_rsa.pub文件的內容。
總結:Git支持SSH協議,經過SSH Key,它就能識別出你推送的提交確實是你推送的,而不是別人冒充的。
(1) 登陸Github 點擊右上角+ 選擇"New repository",添入Repository name,點擊Create repository
(2)Git提示咱們如何新建本地倉庫,並與之關聯,或把本地已有倉庫與之關聯。
1.新建本地倉庫,並與之關聯
$ mkdir GitTest
$ cd GitTest
$ git init
Initialized empty Git repository in /home/linn/GitTest/.git/
$ echo "# GitTest">>README.md
$ git add README.md
$ git commit -m "first commit"
$ git remote add origin https://github.com/dancenn/Gi...
$ git push -u origin master
2.與既存本地倉庫關聯
$ git remote add origin https://github.com/dancenn/Gi...
$ git push -u origin master
(1) 登陸Github 點擊右上角+ 選擇"New repository",添入Repository name,點擊Create repository
勾選Initialize this repository with a README,這樣GitHub會自動爲咱們建立一個README.md文件。
(2)用命令git clone克隆一個本地庫,並查看
$ git clone https://github.com/dancenn/Gi...
Cloning into 'GitTestClone'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
$ cd GitTestClone
$ ll
total 4
-rw-rw-r--. 1 linn linn 14 Aug 23 23:38 README.md
8.3.1.建立與合併分支
一開始先有master分支的時候,分支是一條線。master指向最新的提交,HEAD指向master。以此確認當前分支以及當前分支的提交點。
每次提交,master分支都向前移動一步,因此master分支會愈來愈長。以下:
若是新建一個dev分支,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
$ git checkout -b dev
Switched to a new branch'dev'
用git branch命令查看當前分支:
$ git branch
dev
master
如今,對工做區的提交就是針對dev分支的了,好比新提交一次後,dev指針往前移動一步,而master指針不變:
好比對readme.txt作個修改,而後提交:
$ git add readme.txt
$ git commit -m "branch test"
[dev fec145a] branch test
1 file changed, 1 insertion(+)
如今,dev分支的工做完成,咱們就能夠切換回master分支:
$git checkout master
Switched to branch 'master'
而後就能夠把dev合併到master上。Git直接把master指向dev當前的提交,就完成了合併:
$ git merge dev
Updating d17efd8..fec145a
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
注意到上面的Fast-forward信息,Git告訴咱們,此次合併是「快進模式」,也就是直接把master指向dev的當前提交,因此合併速度很是快。
固然,也不是每次合併都能Fast-forward,咱們後面會講其餘方式的合併。
合併完甚至能夠刪除dev,就又只剩master分支了:
$ git branch -d dev
Deleted branch dev (was fec145a).
$ git branch
master
小結
Git鼓勵大量使用分支:
查看分支:git branch
建立分支:git branch
切換分支:git checkout
建立+切換分支:git checkout -b
合併某分支到當前分支:git merge
刪除分支:git branch -d
2.解決衝突
準備了新的feature1分支,並提交了一些修改。切換到master分支,提交了不一樣的修改。
如今,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 merge 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命令能夠看到分支合併圖。
3.分支管理策略
合併分支:
1.快速合併:Fast forward模式,若是可能,Git就會用這種模式,但刪除分支後,會丟掉分支信息。
2.普通合併:禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就能夠看出分支信息。
好比咱們要把dev分支合併到master分支上。
$ git checkout master
Switched to branch'master'
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
用git log看看分支歷史:
$ git log --graph --pretty=oneline --abbrev-commit
7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
59bc1cb conflict fixed
...
能夠看到,不使用Fast forward模式,merge後就像這樣:
小結:
1.master分支應該是穩定的,用來發布版本,不在上面開發
2.dev分支用來開發,須要發佈版本時,就合併到master
3.不是直接在dev分支幹活,不一樣開發團隊在dev上建立本身的臨時分支,開發完往dev上合併
4.合併方式,儘可能使用普通合併,即加參數--no-ff。能看到合併歷史,而fast forward看不到合併歷史。
8.3.4.bug分支
若是你正在dev上開發,忽然接到一個緊急bug修改的任務,這時候本地工做還沒完,怎麼辦?幸虧Git提供了git stash的功能,它能把當前工做現場「儲藏」起來。
(1).「儲藏」現場
$ git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge
(2).修改bug
$ git checkout -b issue-101
$ git add readme.txt
$ git commit -m "fix bug 101"
$ git checkout master
$ git merge --no-ff -m "merged bug fix 101"issue-101
$ git branch -d issue-101
(3).恢復現場
(3.1)先查看
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
(3.2).1 方法一
$ git stash apply
$ git stash drop
(3.2).2 方法二
$ git stash pop
(3.2).3 屢次stash場合
先用git stash list查看,而後恢復指定的stash,用命令:
$ git stash list
$ git stash apply stash@{0}
(3.3)再次查看,就沒有儲藏內容了
$ git stash list
5.Feature分支
新加一個功能,最好新建一個feature分支。
假設接到了一個新任務:開發代號爲new01的新功能,因而
$ git checkout -b feature-new01
$ git add new01.py
$ git commit -m"add feature new01"
此時新功能開發完並提交,下一步合併。但是忽然接到命令該功能廢棄。因此須要刪除分支。
$ git branch -d feature-new01
error: The branch 'feature-new01' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-new01'.
Git友情提醒,feature-new01分支尚未被合併,若是刪除,將丟失掉修改,若是要強行刪除,須要使用命令git branch -D feature-new01。
$ git branch -D feature-new01
Deleted branch feature-new01 (was756d4af).
簡述:咱們一般習慣在版本庫打一個標籤來標識某一時刻的版本。之後能夠經過標籤取歷史版本。標籤是指向某個commit的指針,很像分支,可是分支能移動,標籤不能移動。
舉個栗子:
「請把上週一的那個版本打包發佈,commit號是6a5819e...」
「一串亂七八糟的數字很差找!」
「我要找的commit id是打了tag 「v1.1」的那個版本」
「找到了:git show v1.1」
8.4.1.建立標籤
(1).切換到須要打標籤的分支上
$ git checkout master
Switched to branch 'master'
(2).建立標籤
$ git tag v1.0
默認標籤是打在最新提交的commit上的。若是想給歷史提交打標籤
(2.1).找到歷史提交的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
(2.2).給歷史提交建立標籤
$ git tag v0.9 6224937
git tag查看標籤:
$ git tag
v0.9
v1.0
8.4.2.刪除標籤
(1).刪除標籤
$ git tag -d v0.1
Deleted tag 'v0.1' (was e078af9)
建立的標籤都只存儲在本地,不會自動推送到遠程。因此,打錯的標籤能夠在本地安全刪除。
(2).推送標籤
$ git push origin v0.1
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
[new tag] v1.0 -> v1.0
(3).一次性推送所有還沒有推送到遠程的本地標籤:
$ 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
(4).刪除遠程標籤
(4.1) 先從本地刪除
$ git tag -d v0.9
Deleted tag 'v0.9' (was 6224937)
(4.2)從遠程刪除
$ git push origin :refs/tags/v0.9
To git@github.com:michaelliao/learngit.git
[deleted] v0.9
能夠登錄git hub看看是否刪除成功。
如何參與開源項目?
8.5.1.Fork開源倉庫
以人氣比較高的很是強大的CSS框架bootstrap項目爲例。
訪問它的項目主頁https://github.com/twbs/boots...,點「Fork」就在本身的帳號下克隆了一個bootstrap倉庫。
8.5.2.從本身的帳號下clone
git clone git@github.com:april/bootstrap.git
必定要從本身的帳號下clone倉庫,這樣你纔有權限推送修改。
Bootstrap的官方倉庫twbs/bootstrap、你在GitHub上克隆的倉庫my/bootstrap,以及你本身克隆到本地電腦的倉庫,他們的關係就像下圖顯示的那樣:
若是你但願bootstrap的官方庫能接受你的修改,你就能夠在GitHub上發起一個pull request。固然,對方是否接受你的pull request就不必定了。
8.6.1 忽略Git工做目錄中的特殊文件
在Git工做區的根目錄下建立一個特殊的.gitignore文件,而後把要忽略的文件名填進去,Git就會自動忽略這些文件。
如如下這樣,想添加一個文件到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
用git check-ignore命令檢查:
$ git check-ignore -v App.class
.gitignore:3:*.class App.class
Git會告訴咱們,.gitignore的第3行規則忽略了該文件,因而咱們就能夠知道應該修訂哪一個規則。
小結
忽略某些文件時,須要編寫.gitignore;
.gitignore文件自己要放到版本庫裏,而且能夠對.gitignore作版本管理!
8.6.2 配置別名
$git config --globalalias.st status
以上命令就是給status配置了別名。git status 能夠寫成git st了。
8.6.3 搭建Git服務器
第一步,安裝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
把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 clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.剩下的推送就簡單了。