Git Tutorial (in)
Create and merge branches
Each commit, Git regard they strung together a timeline, this timeline is a branch. In Git, this branch is called the main branch, that master
branch. HEAD
Strictly speaking is not directed to submit, but the point master
, master
is the point of submission, so the HEAD
point is the current branch.
Each submission, master
the branch will move one step forward, so, as you continue to commit, master
branch lines are getting longer and longer.
When we create a new branch, for example, dev, Git built a pointer called dev, points at the same master, and then point to HEAD dev, says that the current branch on the dev:
For example
First, we create dev
a branch, then switch to dev
the branch:
$ git checkout -b dev
Switched to a new branch 'dev'
git checkout
Command with the -b
parameter indicates and switches, the equivalent of the following two commands:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
Then, view the current branch with git branch command (there is currently a branch before an asterisk):
$ git branch
* dev
master
Then, we can properly filed on dev branch, for example, to readme.txt
make a modification, add a line, then submit.
Switch back to master
the branch, found just modify the content of "not effective" because that is submitted in dev
the branch, and the master
branch point at the moment of the submission has not changed:
$ git checkout master
Switched to branch 'master'
Now, we have dev
consolidated the results of the work branch to master
branch:
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
Note: Thegit merge
command is used to specify the branch to merge the current branch. The above Fast-forward
information, Git tells us that this merger is a "fast-forward mode", that is directly to the master
point dev
currently committed, so the merger is very fast.
After the merger is completed, you can safely delete the dev branch:
$ git branch -d dev
Deleted branch dev (was b17d20e).
Create and switch to a new dev
branch, you can use: git switch -c dev
switch directly to an existing master
branch, you can use:git switch master
command
View branch:git branch
Create a branch:git branch <name>
Branch switching: git checkout <name>
orgit switch <name>
Create a + to switch branches: git checkout -b <name>
orgit switch -c <name>
Merge a branch to the current branch:git merge <name>
Deleted branches:git branch -d <name>
Merge and resolve conflicts
conflict:
- New branch
dev
, to switch todev
the branch and the modifieda.txt
rearadd
andcommit
- Switching to
master
the branch, modifieda.txt
afteradd
andcommit
- Try to merge the branch conflict!
$ git merge dev
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.
solution
Git tells us that a.txt
there is a conflict files, you must manually resolve the conflict before submitting. git status
Files can also tell us the conflict:
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
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")
Submit:
$ git add a.txt
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed
FIG combined as follows:
With arguments git log
you can also see the consolidation of branch:
$ git log --graph --pretty=oneline --abbrev-commit
* cf810e4 (HEAD -> master) conflict fixed
|\
| * 14096d0 (dev) AND simple
* | 5dc6824 & simple
|/
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file
Finally, remove the dev
branches:
$ git branch -d dev
Deleted branch dev (was 14096d0).
Branch Management Strategy
Typically, when merging branches, if possible, Git will use Fast forward
mode, but this mode, delete the branch, the branch will lose information.
If you want to force disable Fast forward
mode, Git will be in merge
generating a new time commit
, so, from the branch history information can be seen on branch
Combat: the master
merger branch dev
branches:
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
After the merger, we git log
look at the branch history:
$ git log --graph --pretty=oneline --abbrev-commit
* e1e9c68 (HEAD -> master) merge with no-ff
|\
| * f52c633 (dev) add merge
|/
* cf810e4 conflict fixed
...
Do not use Fast forward
patterns, merge
after like this:
分支策略
实际开发中,首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。所以,团队合作的分支看起来就像这样:
Bug分支
修复bug
时,我们会通过创建新的bug
分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash
一下,然后去修复bug
,修复后,再git stash pop
,回到工作现场;
在master
分支上修复的bug
,想要合并到当前dev
分支,可以用git cherry-pick <commit>
命令,把bug
提交的修改“复制”到当前分支,避免重复劳动。
举例:
当你在dev
分支工作时,有一个bug
需要尽快解决,此时使用git stash
保存工作现场,切换到master
分支,创建新分支修复:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
(use "git push" to publish your local commits)
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
再切换到dev
分支。此时,用git stash list
命令列出该分支下的所有工作现场:
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
工作现场还在,Git
把stash
内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply
或者git stash apply stash@{0}
恢复,但是恢复后,stash
内容并不删除,你需要用git stash drop
来删除;
另一种方式是用git stash pop
,恢复的同时把stash
内容也删了.
修复完成后,因为dev
分支是来源于master
分支,所以,使用git cherry-pick <提交的版本号>
命令,此处版本号为4c805e2
,来使dev
分支进行同样的提交,避免重复劳动。
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
feature分支
开发一个新feature,最好新建一个分支;
如果要丢弃一个没有被合并过的分支,可以通过git branch -D
强行删除。
添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature
分支,在上面开发,完成后,合并,最后,删除该feature
分支。
实例:
- 你接到了一个新任务:开发代号为Vulcan的新功能,该功能计划用于下一代星际飞船。
$ git switch -c feature-vulcan
Switched to a new branch 'feature-vulcan'
- 5分钟后,开发完毕:
$ git add vulcan.py
$ git status
On branch feature-vulcan
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: vulcan.py
$ git commit -m "add feature vulcan"
[feature-vulcan 287773e] add feature vulcan
1 file changed, 2 insertions(+)
create mode 100644 vulcan.py
- 切回dev,准备合并,但是此时接到上级命令,因经费不足,新功能必须取消!虽然白干了,但是这个包含机密资料的分支还是必须就地销毁:
$ git switch dev
$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.
- 销毁失败。Git友情提醒,
feature-vulcan
分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D
参数。
$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 287773e).
多人协作
当你从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
。
要查看远程库的信息,用git remote
:
$ git remote
origin
git remote -v
查看更详细的信息:
$ git remote -v
origin [email protected]:qujialin/learn.git (fetch)
origin [email protected]:qujialin/learn.git (push)
上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
推送分支
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
$ git push origin master
如果要推送其他分支,比如dev
,就改成:
$ git push origin dev
抓取分支
多人协作时,大家都会往master
和dev
分支上推送各自的修改。
创建远程origin
的dev
分支到本地:
$ git checkout -b dev origin/dev
你的小伙伴已经向origin/dev
分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
$ cat env.txt
env
$ git add env.txt
$ git commit -m "add new env"
[dev 7bd91f1] add new env
1 file changed, 1 insertion(+)
create mode 100644 env.txt
$ git push origin dev
To github.com:michaelliao/learngit.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to '[email protected]:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull
把最新的提交从origin/dev
抓下来,然后,在本地合并,解决冲突,再推送:
$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> dev
git pull
也失败了,原因是没有指定本地dev
分支与远程origin/dev
分支的链接,根据提示,设置dev
和origin/dev
的链接:
$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
再pull
:
$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.
这回git pull
成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push
:
$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict
$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
7a5e5dd..57c53ab dev -> dev
因此,多人协作的工作模式通常是这样:
首先,可以试图用`git push origin <branch-name>`推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用`git pull`试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用`git push origin <branch-name>`推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
Rebase
在上一节我们看到了,多人在同一个分支上协作时,很容易出现冲突。即使没有冲突,后push的童鞋不得不先pull,在本地合并,然后才能push成功。
每次合并再push后,分支变成了这样:
$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/
* | 12a631b merged bug fix 101
|\ \
| * | 4c805e2 fix bug 101
|/ /
* | e1e9c68 merge with no-ff
|\ \
| |/
| * f52c633 add merge
|/
* cf810e4 conflict fixed
总之看上去很乱,有强迫症的童鞋会问:为什么Git的提交历史不能是一条干净的直线?
其实是可以做到的!
Git has called rebase
operation, it was translated into "Variable group." Entrygit rebase
$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
This time with a git log
look:
$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master) add author
* 3611cfe add comment
* f005ed4 (origin/master) set exit=1
* d1be385 init hello
...
Originally submitted bifurcation has now become a straight line up! This magical operation is how to achieve? In fact, the principle is very simple. We observe, discover our local Git submit "move" position, put f005ed4 (origin/master) set exit=1
after this, the entire commit history became a straight line. rebase before and after the operation, the final submission is the same, but our local commit
content has changed, no longer based on their modification d1be385 init hello
, but on f005ed4 (origin/master) set exit=1
, but the final submission of 7e61ed4
the content is the same.
This is rebase
characteristic of the operation: the commit history "finishing" bifurcated into a straight line, looks more intuitive. The disadvantage is that the local bifurcation submission has been modified.
Finally, by push
operating the remote push local branch:
$ git push origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:qujialin/Learn.git
f005ed4..7e61ed4 master -> master
Remote branch commit history is a straight line. as follows:
$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master, origin/master) add author
* 3611cfe add comment
* f005ed4 set exit=1
* d1be385 init hello
...
Reference links:
https://www.liaoxuefeng.com/wiki/896043488029600/900003767775424