branch
概念
Git 中的分支,其实本质上仅仅是一个指向提交结点的可变指针。它会在每次提交之后自动往前移动。
假设原来的提交过程如下:

当新建一个 iss53 分支后,其结果如下:

可以看出,iss53 与 master 指向的是同一个提交结点。但 iss53 却是一个新的分支。
HEAD
Git 保存着一个名为 HEAD 的特别指针,它是一个指向你正在工作中的本地分支的指针(将 HEAD 想象为当前分支的别名)。它会在每次提交之后自动往前移动。也就是说,HEAD 表示当前分支。
-
HEAD~ 表示 HEAD 的父结点。
-
HEAD~2 表示 HEAD 的爷爷结点。可能通过修改 ~ 后的数字指向 HEAD 的哪一个祖先结点。
-
与 HEAD 相关的文件是 .git/HEAD 文件。
$ cat .git/HEAD
ref: refs/heads/master
$ git checkout dev
Switched to branch 'dev'
$ cat .git/HEAD
ref: refs/heads/dev
可以发现,当切换分支后,HEAD 引用的文件位置已经发生了变化。
每新建一个分支,就会在 refs/heads/ 目录下以分支名新建一个文件。该文件存储的是当前分支最后一次提交的 SHA-1 值。
常用操作
分支的操作有:新建、删除、查看、切换、合并、推送新分支到远程仓库、删除远程仓库分支、拉取远程仓库分支。
命令 | 含义 | |
---|---|---|
git branch <name> | 创建新的分支 | |
git branch <name> <checksum> | 在指定的提交结点处创建新的分支 | |
git checkout -b <name> | 创建新的分支并切换到新分支 | |
git checkout -b <name> <checksum> | 在指定的提交结点处创建新的分支并切换到新分支 | |
git checkout -B <name> | 创建新的分支并切换到新分支。该命令会覆盖已有的同名分支 | |
git branch -d <name> | 删除指定的本地分支 | |
git branch | 查看本地所有分支 | |
git branch -vv | 列出本地分支及跟踪的远程分支,并展示两者差异 | |
git branch -a | 查看本地和远程仓库的分支。该操作不会进行网络请求 | |
git checkout <name> | 切换到指定的分支 | |
git merge <name> | 将指定的分支合并到当前分支 | |
git push <别名> <local>:<remote> | 将本地 local 分支推送到远程 remote 分支上 | |
git push <别名> :<remote> | 删除远程 remote 分支 | |
git push <别名> --delete <remote> | 删除远程 remote 分支 | |
git pull <别名> <remote>:<local> | 将远程的 remote 分支拉到本地的 local 分支上,如果没有,则新建 local 分支。同时,会进行合并操作 | |
git fetch <别名> <remote>:<local> | 将远程的 remote 分支拉到本地的 local 分支上,如果没有,则新建 local 分支 |
在指定的提交结点处创建分支时,提交结点可以是 tag,checksum,某个分支名等。因为这些数据都指向了一个提交结点。
跟远程仓库相关的操作,必须通过 push <别名> 将操作推送到仓库中。
新建
新建本地分支
命令 | 含义 |
---|---|
git branch <name> | 创建新的分支 |
git checkout -b <name> | 创建新的分支并切换到新分支 |
git checkout -B <name> | 创建新的分支并切换到新分支 |
新建的分支会将旧分支所有内容都复制一份。包括非暂存和非提交的。
使用 branch 与 checkout 使用 主要区别在于:后者会切换到新建的分支,而前者不会。
使用 -b 与使用 -B 的主要区别在于:如果已存在同名分支,则 -b 会创建失败。而 -B 会强制新建分支,并使用新建分支覆盖旧分支。
$ git checkout -b test
它相当于依次执行下面两个命令:
$ git branch test
$ git checkout test
即先通过 branch <name> 创建一个新的分支,然后通过 checkout 切换到指定的分支。
新建远程分支
使用 git push <别名> <local>:<remote> 将本地分支推送到远程仓库不存在的分支上,远程仓库会自动创建新的分支。
其中 local 指的是本地分支名。remote 指的是远程仓库的分支名。如果省略 local ,则表示删除远程仓库 remote 分支。
删除
删除本地分支
通过 git branch [-d|--delete] <name> 删除指定的分支。
删除时,可以使用 -d 也可以使用 --delete 选项。
$ git branch
* dev
master
test
$ git branch --delete test
Deleted branch test (was fe9eba5).
$ git branch
* dev
master
删除远程分支
有两种实现方式:一种通过 --delete 命令指定删除的远程分支;一种通过省略本地分支名。
如:
$ git push demo2 :web5
To https://github.com/birdandcliff/gitdemo.git
- [deleted] web5
$ git push demo2 --delete web6
To https://github.com/birdandcliff/gitdemo.git
- [deleted] web6
第一种是通过省略本地分支名,删除远程分支 web5。
第二种通过直接使用 --delete 命令,删除远程分支 web6。
查看
- git branch 查看所有本地分支。分支名前有 * 的表示当前分支。
选项 | 含义 |
---|---|
-a | 查看本地分支与远程分支 |
-v | 查看各个分支最后一个提交对象的信息 |
--merged | 查看所有已合并到当前分支中的分支,已删除的不会出现 |
--no-merged | 查看尚未合并到当前分支中的分支,已删除的不会出现 |
如:
$ git branch -a
* dev
master
remotes/demo2/dev
第三个为远程分支(本地没有)。如果不加 -a 选项,则只会出现前两个。第一个分支前面有 * ,表示当前的分支为 dev。
一般来说,--merged 列表中没有 * 的分支通常都可以用 git branch -d 来删掉,因为已经把它们合并到其他分支,删掉也没有任何影响。
切换
通过 git checkout <name> 切换到指定分支。
$ git checkout master
Switched to branch 'master'
后跟 -b 选项时,表示新建分支,并切换到新分支中。
合并
通过 git merge <name> 将指定的分支合并到当前分支中。
合并时,被合并的分支指向的提交结点不会发生变化,合并到的分支指向会发生变化。
合并时,如果产生冲突,需要手动解决冲突后再次 commit 才算合并完成。
$ git merge test
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.
$ git status
On branch dev
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: a.txt
no changes added to commit (use "git add" and/or "git commit -a")
上述分支进行合并时,产生了冲突。所以,需要手动解决冲突,并将结果进行 commit 。
产生冲突后,可以通过 git status 查看冲突时的状态,而且所有的冲突文件都列在 Unmerged paths 下。
问题
在合并时,可能会遇到如下错误提示:
$ git merge test
warning: refname 'test' is ambiguous.
Already up-to-date.
提示说 test 这个名字无法识别,这是因为有可能存在一个叫 test 的 tag,删除掉该 tag 后再进行合并即可。
拉取
-
通过 git pull <别名> <remote>:<local> 将远程的分支拉取到本地。如果本地不存在对应的分支,则新建。同时,会将远程分支内容进行合并。
$ git pull origin web7:webaa remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From https://github.com/birdandcliff/gitdemo * [new branch] web7 -> webaa 7a6c13b..bf9ce37 web7 -> origin/web7 Updating 7a6c13b..bf9ce37 Fast-forward a.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
-
通过 git fetch <别名> <remote>:<local> 将远程分支拉取到本地。
$ git fetch origin web7 From https://github.com/birdandcliff/gitdemo * branch web7 -> FETCH_HEAD
-
pull 与 fetch 的主要区别在于: pull = fetch + merge。使用 pull 时,不但将远程分支的内容拉取到本地,同时会将远程分支的内容与当前分支进行合并。但使用 fetch 就只是将远程分支内容拉取到本地,不会合并到当前分支中。