Git本地分支与合并-Git详解(3)

本节概要

  • git branch (创建分支)
  • git tag (给固定的commit做标记)
  • git checkout (在分支之间切换)
  • git stash (这样在切换分支之前保存本地修改)
  • git merge (合并分支)

当我们在一些其他的工作不想直接在master分支上做的时候,比如:增加一些新特性,或者问题的修复,测试等,这是偶,可以创建分支branch。

1
2
3
4
5
6
7
8
9
10
$ git init git_checkout_merge
$ cd git_checkout_merge
$ touch master.txt #并添加一些内容
$ git add master.txt
$ git commit -m 'Initial commit on master'
$ git branch test_branch
$ git checkout test_branch
$ vim master.txt # 再修改一下
$ git commit -a -m 'Initial on test_branch'
$ git checkout master

注,2个分支的 master.txt 内容不一样

git tag

branch 总是指向最新的commit,当需要指向固定的commit时,可以使用 git tag。比如,用于release 时候,打个tag,便于,出现问题,回滚道上一次tag状态。

命令格式: git tag tagname hash

1
2
3
4
5
6
7
8
9
10
11
12
$ git log --oneline --decorate --graph --all
* f2a8fa3 (test_branch) Initial commit on test_branch
* 3354ddc (HEAD, master) Initial master.txt on master
$ git tag 'v0' 3354ddc
$ git tag -a 'INITIAL_COMMIT' 3354ddc # 功能同上,可交互输入注释
$ git tag #列出tag
v0
INITIAL_COMMIT
$ git lol # 可以看到 3354ddc 有2个tag
* f2a8fa3 (test_branch) Initial commit on test_branch
* 3354ddc (HEAD, tag: v0, tag: c, master) Initial master.txt on master

git show 用开查看对象信息

1
2
3
4
# 3个命令效果相同
$ git show 3354ddc
$ git show v0
$ git show INITIAL_COMMIT

如果用git checkout 切换到某个tag 上,由于tag 无法记录修改历史,会建议你用当前的tag建立一个分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git checkout v0
Note: checking out 'v0'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at 3354ddc... Initial master.txt on master
$ git checkout -b test_v0

git stash 用法

在一个分支中做了修改,并更新了暂存区,没有commit,这时,切换分支的化,会提示,将会丢失暂存区的内容,可以使用git stash 来保存此分支的工作区,和暂存区的状态,但并未提交

1
2
3
4
5
$ git stash save -a "stash1"
$ git stash list
stash@{0}: On fix_v0: stash1
stash@{1}: test stash
stash@{2}: WIP on master: 3354ddc Initial master.txt on master

git stash list 列出了所有stash对象,可以用 git stash pop 或者 apply 来还原工作区和暂存区

1
2
$ git stash apply --index stash@{0} #还原,不删除stash对象
$ git stash pop --index stash@{0} #还原同时删除对应的stash对象

删除 stash

1
2
3
4
5
$ git stash list
stash@{0}: test stash
stash@{1}: WIP on master: 3354ddc Initial master.txt on master
$ git stash drop stash@{1}
Dropped stash@{1} (65dd98455b4c193f6d8056566b710cdb760a01f5)

删除所有 stash

1
$ git stash clear

合并分支

1
2
3
4
5
[rb git_checkout_merge (fix_v0)]$ git checkout master
Switched to branch 'master'
[rb git_checkout_merge (master)]$ git merge fix_v0
CONFLICT (modify/delete): master.txt deleted in HEAD and modified in fix_v0. Version fix_v0 of master.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.

处理冲突,这里,我在master 上删除了 master.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[rb git_checkout_merge (master *+|MERGING)]$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
deleted by us: master.txt
no changes added to commit (use "git add" and/or "git commit -a")
[rb git_checkout_merge (master *+|MERGING)]$ git add master.txt
[rb git_checkout_merge (master +|MERGING)]$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
new file: master.txt
[rb git_checkout_merge (master +|MERGING)]$ git commit -m "merge fix_v0 to master"
[master 92297a8] merge fix_v0 to master
[rb git_checkout_merge (master)]$ git status
On branch master
nothing to commit, working directory clean

放弃合并

1
$ git merge --abort

总结:

  • 分支是一个commit的引用,在这些分支上工作,会产生各自的历史。
  • 切换分支,就是HEAD引用的移动,以及工作区和暂存区的还原。

如下图,在commit(58ac6) 创建了分支 test,并进行了2次 commit,同时master 也 在commit(58ac6)向后继续开发

graph LR hd(HEAD) master(master) test(test) c1(3a789c) c2(678779) c3(5b8ac6) c4(2df781) c5(ac2dfc) m1(fa2dcc) m2(ac255c) m3(8f29a3) c1 --> c2 c2 --> c3 c3 --> c4 c4 --> c5 c3 --> m1 m1 --> m2 m2 --> m3 master --> m3 hd --> master test --> c5 style c1 fill:#85f801 style c2 fill:#85f801 style c3 fill:#85f801 style c4 fill:#85f801 style c5 fill:#85f801 style m1 fill:#85f801 style m2 fill:#85f801 style m3 fill:#85f801 style master fill:#a63a30 style test fill:#a63a30

fast-forward-merage,快速合并,会丢弃一些历史commit,如下图

1
$ git merge test

graph LR hd(HEAD) master(master) test(test) c1(3a789c) c2(678779) c3(5b8ac6) c4(2df781) c5(ac2dfc) c1 --> c2 c2 --> c3 c3 --> c4 c4 --> c5 master --> c5 hd --> master test --> c5 style c1 fill:#85f801 style c2 fill:#85f801 style c3 fill:#85f801 style c4 fill:#85f801 style c5 fill:#85f801 style master fill:#a63a30 style test fill:#a63a30

non-fast-forward-merge ,也称“三方合并”,会生成一个新的 commit 来记录修改历史

一般,系统会自动判断是否使用 non-fast-forward-merge,也可以显示声明使用

  • –no-ff 显示告诉Git不要fast forward
  • cherry-pick
  • –squash
    graph LR hd(HEAD) master(master) test(test) c1(3a789c) c2(678779) c3(5b8ac6) c4(2df781) c5(ac2dfc) m1(fa2dcc) m2(ac255c) m3(8f29a3) ma(578c2a) c5 --> ma m3 --> ma c1 --> c2 c2 --> c3 c3 --> c4 c4 --> c5 c3 --> m1 m1 --> m2 m2 --> m3 master --> ma hd --> master test --> c5 style c1 fill:#85f801 style c2 fill:#85f801 style c3 fill:#85f801 style c4 fill:#85f801 style c5 fill:#85f801 style m1 fill:#85f801 style m2 fill:#85f801 style m3 fill:#85f801 style ma fill:#85f801 style master fill:#a63a30 style test fill:#a63a30