在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master
分支。HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长。
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
你看,Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
dev
分支,然后切换到
dev
分支
$ git checkout -b dev
等价于
$ git branch dev
$ git checkout dev
查看当前所有分支
$ git branch
列出所有分支,当前分支前面会标一个*
$ git checkout master
可以做一个这样的实验:先Clone一个项目,创建一个新的分支dev,然后切换到dev分支,修改项目中的文件,add并且commit,再回到master分支。这个时候打开项目刚刚修改的文件会发现文件还原了。原因是修改只在分支dev中并提交,而master未变。
我们在master分支下合并dev分支
$ git merge dev
合并后修改的内容就出现了,这时可以删除dev分支
$ git branch -d dev
分支管理的一些BUG及特殊情况
在master分支和分支都有新的提交变成了这样:
这种情况下Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突
远程文件内容为:"and" master内容:"and 1" branch内容:"and 2"
例如:当修改了内容Git会自动提示我们当前master
分支比远程的master
分支要超前1个提交。会提示
CONFLICT (content): Merge conflict in xx.txt
Automatic merge failed; fix conflicts and then commit the result.
(注意,如果是在文件普通追加或者删除则是可以合并的)
Vim 命令打开文件,发现Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,修改冲突的语句,这里冲突的是1和2,
vim + 文件名
打开冲突的文件,手动修改冲突内容,改为1或者2,删除其他不相关内容,:wq保存并退出vim,再次add和commit。
下图所示:
git log
也可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
最后,删除
分支:
$ git branch -d 分支名
合并分支时,默认情况下Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,我们可以使用--no-ff
方式的git merge
会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
这个方式类似于游戏里的存档,在打BOSS时,可以之前先存当前的信息,在之后可以任意的回到此刻。
比如在同一项目有一个紧急任务需要优先处理,而当前的内容可能不能马上commit,这个时候可以使用把工作现场git stash
一下保存,在处理完优先的内容后再git stash pop
,回到工作现场。
恢复现场有两种,
一是用git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除;用git stash list
命令查看工作现场list
另一种方式是用git stash pop
,恢复的同时把stash内容也删了
有时候后新增一个功能也需要新建一分支,但在没merge合并前功能因各种原因需要取消可以通过git branch -D <name>
强行删除。
Git多人协作
当你从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
。
要查看远程库的信息,用git remote
或者,用git remote -v
显示更详细的信息
推送分支
$ git push origin master
如果要推送其他分支,比如
dev
,就改成
$ git push origin dev
抓取分支
git pull
获取最新的提交
然后,在本地合并,解决冲突再推送。
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致;
git branch --set-upstream branch-name origin/branch-name
Git标签
-
命令
git tag <name>
用于新建一个标签,默认为HEAD
,也可以指定一个commit id; -
git tag -a <tagname> -m "blablabla..."
可以指定标签信息; -
git tag -s <tagname> -m "blablabla..."
可以用PGP签名标签; -
命令
git tag
可以查看所有标签。 -
可以用
git show <tagname>
查看标签信息 -
如果标签打错了,也可以删除,因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除
$ git tag -d 标签名
-
命令
git push origin <tagname>
推送标签到远程,或者全部标签推送远程$ git push origin --tags
-
标签在远程的删除,需要先从本地删除然后从远程删除。
$ git tag -d 标签名 $ git push origin :refs/tags/标签名
忽略特殊文件
-
有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,在Git工作区的根目录下创建一个特殊的
.gitignore
文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的
.class
文件; - 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
举个例子:
假设你在Windows下进行Python开发,Windows会自动在有图片的目录下生成隐藏的缩略图文件,如果有自定义目录,目录下就会有
Desktop.ini
文件,因此你需要忽略Windows自动生成的垃圾文件:# Windows: Thumbs.db ehthumbs.db Desktop.ini
然后,继续忽略Python编译产生的
.pyc
、.pyo
、dist
等文件或目录:# Python: *.py[cod] *.so *.egg *.egg-info dist build
加上你自己定义的文件,最终得到一个完整的
.gitignore
文件,内容如下:# Windows: Thumbs.db ehthumbs.db Desktop.ini # Python: *.py[cod] *.so *.egg *.egg-info dist build # My configurations: db.ini deploy_key_rsa
最后一步就是把
Git的官方网站: http://git-scm.com.gitignore
也提交到Git,就完成了!当然检验.gitignore
的标准是git status
命令是不是说working directory clean
。