学习完廖雪峰老师的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.剩下的推送就简单了。