阅读自廖雪峰Git教程:www.liaoxuefeng.com/wiki/896043…
一、Git简介
part1:
- Git是目前世界上最先进的分布式版本控制系统。
- “能自动记录每次文件的改动,还可以让同事协作编辑,不用自己管理一堆类似的文件,也不需要把文件传来传去。如果想查看某次改动,只需要瞄一眼就可以。”
- “结束了手动管理多个‘版本’的史前时代,进入到版本控制的21世纪。”
- “如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。”
part2: 集中式VS分布式
- “集中式版本控制系统,版本库是集中存放在中央服务器的,干活时用的都是自己的电脑,所以要先从中央服务器取得最新的版本,开始干活,干完活了,再把自己的活推送给中央服务器。”
- “集中式版本控制系统最大的毛病就是必须联网才能工作。遇到网速慢的话,可能提交一个10M的文件就需要5分钟。”
- “分布式版本控制系统根本没有‘中央服务器’,每个人的电脑上都是一个完整的版本库。多人协作时,同事之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。”
- “分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活。”
- “分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。”
- “后面我们还会看到Git极其强大的分支管理。”
二、安装Git & 创建版本库
- 版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
- 初始化一个Git仓库,使用git init命令。
//选择一个合适的地方,创建一个空目录
$ mkdir learngit
$ cd learngit
//pwd命令用于显示当前目录。在我的Mac上,这个仓库位于/Users/michael/learngit
$ pwd
/Users/michael/learngit
//通过git init命令把这个目录变成Git可以管理的仓库
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
- 添加文件到Git仓库,使用命令git add < file >,使用命令git commit -m < message >
//现在我们编写一个readme.txt文件,一定要放到learngit目录下(子目录也行)
//第一步,用命令git add告诉Git,把文件添加到仓库
$ git add readme.txt
//第二步,用命令git commit告诉Git,把文件提交到仓库
$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
//git commit命令中-m后面输入的是本次提交的说明
三、时光机穿梭
- 要随时掌握工作区的状态,使用git status命令。
- 如果git status告诉你有文件被修改过,用git diff < file >可以查看修改内容。
part1:版本回退
- Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。
- 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本(看得眼花缭乱的,可以试试加上 --pretty=oneline参数)。
- HEAD指向的版本就是当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,往上100个版本写100个^数不过来,写成HEAD~100。
- 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。
- 版本号没必要写全,前几位就可以,Git会自动去找。
part2:工作区和暂存区
- 工作区(Working Directory)就是你在电脑里能看到的目录,比如learngit文件夹就是一个工作区。
- 版本库(Repository):工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
- 我们把文件往Git版本库里添加的时候,是分两步执行的:第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
part3:管理修改
- Git跟踪并管理的是修改,而非文件。
- 当用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
part4:撤销修改
- 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
- 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD < file >,就回到了场景1,第二步按场景1操作。
- 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
part5:删除文件
- 确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit。
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
- 如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
四、远程仓库
- GitHub网站,提供Git仓库托管服务。只要注册一个GitHub账号,就可以免费获得Git远程仓库。
- 使用前,按步骤创建、添加SSH Key。
part1:添加远程库
- 要关联一个远程库,在本地的repo-name仓库下运行命令:git remote add origin git@server-name:path/repo-name.git。
$ git remote add origin git@github.com:michaelliao/learngit.git
//把上面的michaelliao替换成自己的GitHub账户名,learngit替换成自己的文件夹名
- 关联后,使用命令git push -u origin master第一次推送master分支的所有内容。
- 此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改。
part2:删除远程库
- 如果添加的时候地址写错了,或者就是想删除远程库,可以用git remote rm < name >命令。使用前,建议先用git remote -v查看远程库信息。
$ git remote -v
origin git@github.com:michaelliao/learn-git.git (fetch)
origin git@github.com:michaelliao/learn-git.git (push)
- 然后,根据名字删除,比如删除origin。此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。
$ git remote rm origin
part3:从远程库克隆
- 要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。
$ git clone git@github.com:michaelliao/gitskills.git
//Git库的地址换成你自己的
- Git支持多种协议,包括https,但ssh协议速度最快。
五、分支管理
- “你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。”
part1:创建与合并分支
Git鼓励大量使用分支:
- 查看分支:git branch
- 创建分支:git branch
- 切换分支:git checkout 或者git switch
- 创建+切换分支:git checkout -b 或者git switch -c
- 合并某分支到当前分支:git merge
- 删除分支:git branch -d (有好多图!不记得了回去看:www.liaoxuefeng.com/wiki/896043… )
part2:解决冲突
- 当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
- 解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
- 用git log --graph命令可以看到分支合并图。
part3:分支管理策略
Git分支十分强大,在团队开发中应该充分应用。
- 进行分支管理的几个基本原则:首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
- 合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
$ git merge --no-ff -m "merge with no-ff" dev
//合并dev分支,因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去
part4:Bug分支
- 修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除。
$ git checkout -b issue-101
//修复Bug之后,然后提交
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
//修复完成后,切换到master分支,并完成合并,最后删除issue-101分支
$ git switch master
$ git merge --no-ff -m "merged bug fix 101" issue-101
- 当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。
$ git stash
//用git stash list命令查看刚才存储的工作现场
$ git stash list
//用git stash pop恢复,恢复的同时把stash内容也删了
$ git stash pop
//也可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash
//用git stash apply恢复,但是恢复后需要用git stash drop来删除
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
$ git stash apply stash@{0}
$ git stash drop stash@{0}
- 在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick < commit >命令,把bug提交的修改“复制”到当前分支,避免重复劳动。
$ git branch
* dev
master
//把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
part5:Feature分支
- “软件开发中,总有无穷无尽的新的功能要不断添加进来。添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。”
//开发代号为Vulcan的新功能
$ git switch -c feature-vulcan
Switched to a new branch 'feature-vulcan'
- 如果要丢弃一个没有被合并过的分支,可以通过git branch -D < name >强行删除。
$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 287773e).
part6:多人协作
多人协作的工作模式通常是这样:
- 首先,可以试图用git push origin < branch-name >推送自己的修改;
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用git push origin < branch-name >推送就能成功!
- 查看远程库信息,使用git remote -v;本地新建的分支如果不推送到远程,对其他人就是不可见的。
- 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to < branch-name > origin/< branch-name >。
- “总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!”
part7:Rebase
- rebase操作可以把本地未push的分叉提交历史整理成直线;
- rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。
六、标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
- 命令git tag < tagname >用于新建一个标签,默认为HEAD,也可以指定一个commit id;
- 命令git tag -a < tagname > -m "blablabla..."可以指定标签信息;
- 命令git tag可以查看所有标签。
- 命令git push origin 可以推送一个本地标签;
- 命令git push origin --tags可以推送全部未推送过的本地标签;
- 命令git tag -d 可以删除一个本地标签;
- 命令git push origin :refs/tags/可以删除一个远程标签。