add branch

pull/1/head
wanglei 2020-08-02 17:39:45 +08:00
parent ab7551cc37
commit 221ffda42a
2 changed files with 355 additions and 0 deletions

View File

@ -0,0 +1,228 @@
## 1.分支的新建与合并
现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会用到这样的工作流程:
1.开发某个网站。
2.为实现某个新的需求,创建一个分支。
3.在这个分支上开展工作。
假设此时,你突然接到一个电话说有个很严重的问题需要紧急修补,那么可以按照下面的方式处理:
1.返回到原先已经发布到生产服务器上的分支。
2.为这次紧急修补建立一个新分支,并在其中修复问题。
3.通过测试后,回到生产服务器所在的分支,将修补分支合并进来,然后再推送到生产服务器上。
4.切换到之前实现新需求的分支,继续工作。
分支的新建与切换
首先,我们假设你正在项目中愉快地工作,并且已经提交了几次更新
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/1.png)
现在,你决定要修补问题追踪系统上的 #53 问题。顺带说明下Git 并不同任何特定的问题追踪系统打交道。这里为了说明要解决的问题,才把新建的分支取名为 iss53。要新建并切换到该分支运行 git checkout 并加上 -b 参数:
```
$ git checkout -b iss53
Switched to a new branch 'iss53'
```
这相当于执行下面这两条命令:
```
$ git branch iss53
$ git checkout iss53
```
图 3-11 示意该命令的执行结果
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/2.png)
接着你开始尝试修复问题在提交了若干次更新后iss53 分支的指针也会随着向前推进,因为它就是当前分支(换句话说,当前的 HEAD 指针正指向 iss53见图 3-12
```
$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'
```
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/3.png)
现在你就接到了那个网站问题的紧急电话,需要马上修补。有了 Git ,我们就不需要同时发布这个补丁和 iss53 里作出的修改,也不需要在创建和发布该补丁到服务器之前花费大力气来复原这些修改。唯一需要的仅仅是切换回 master 分支。
不过在此之前,留心你的暂存区或者工作目录里,那些还没有提交的修改,它会和你即将检出的分支产生冲突从而阻止 Git 为你切换分支。切换分支的时候最好保持一个清洁的工作区域。稍后会介绍几个绕过这种问题的办法(分别叫做 stashing 和 commit amending。目前已经提交了所有的修改所以接下来可以正常转换到 master 分支:
```
$ git checkout master
Switched to branch 'master'
```
此时工作目录中的内容和你在解决问题 #53 之前一模一样你可以集中精力进行紧急修补。这一点值得牢记Git 会把工作目录的内容恢复为检出某分支时它所指向的那个提交对象的快照。它会自动添加、删除和修改文件以确保目录的内容和你当时提交时完全一样。
接下来,你得进行紧急修补。我们创建一个紧急修补分支 hotfix 来开展工作,直到搞定(见图 3-13
```
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 3a0874c] fixed the broken email address
1 files changed, 1 deletion(-)
```
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/4.png)
有必要作些测试,确保修补是成功的,然后回到 master 分支并把它合并进来,然后发布到生产服务器。用 git merge 命令来进行合并:
```
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
README | 1 -
1 file changed, 1 deletion(-)
```
请注意合并时出现了“Fast forward”的提示。由于当前 master 分支所在的提交对象是要并入的 hotfix 分支的直接上游Git 只需把 master 分支指针直接右移。换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时只会简单地把指针右移因为这种单线的历史分支不存在任何需要解决的分歧所以这种合并过程可以称为快进Fast forward
现在最新的修改已经在当前 master 分支所指向的提交对象中了,可以部署到生产服务器上去了(见图 3-14
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/5.png)
在那个超级重要的修补发布以后,你想要回到被打扰之前的工作。由于当前 hotfix 分支和 master 都指向相同的提交对象,所以 hotfix 已经完成了历史使命,可以删掉了。使用 git branch 的 -d 选项执行删除操作
```
$ git branch -d hotfix
Deleted branch hotfix (was 3a0874c).
```
现在回到之前未完成的 #53 问题修复分支上继续工作(图 3-15
```
$ git checkout iss53
Switched to branch 'iss53'
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)
```
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/6.png)
值得注意的是之前 hotfix 分支的修改内容尚未包含到 iss53 中来。如果需要纳入此次修补,可以用 git merge master 把 master 分支合并到 iss53或者等 iss53 完成之后,再将 iss53 分支中的更新并入 master。
## 2.分支的合并
在问题 #53 相关的工作完成之后,可以合并回 master 分支。实际操作同前面合并 hotfix 分支差不多,只需回到 master 分支,运行 git merge 命令指定要合并进来的分支:
```
$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)
```
请注意,这次合并操作的底层实现,并不同于之前 hotfix 的并入方式。因为这次你的开发历史是从更早的地方开始分叉的。由于当前 master 分支所指向的提交对象C4并不是 iss53 分支的直接祖先Git 不得不进行一些额外处理。就此例而言Git 会用两个分支的末端C4 和 C5以及它们的共同祖先C2进行一次简单的三方合并计算。图 3-16 用红框标出了 Git 用于合并的三个提交对象:
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/7.png)
这次Git 没有简单地把分支指针右移而是对三方合并后的结果重新做一个新的快照并自动创建一个指向它的提交对象C6见图 3-17。这个提交对象比较特殊它有两个祖先C4 和 C5
值得一提的是 Git 可以自己裁决哪个共同祖先才是最佳合并基础;这和 CVS 或 Subversion1.5 以后的版本)不同,它们需要开发者手工指定合并基础。所以此特性让 Git 的合并操作比其他系统都要简单不少。
![这里写图片描述](https://github.com/bitcarmanlee/easy-algorithm-interview-photo/blob/master/tools/git/branch/8.png)
既然之前的工作成果已经合并到 master 了,那么 iss53 也就没用了。你可以就此删除它,并在问题追踪系统里关闭该问题。
```
$ git branch -d iss53
```
## 3.遇到冲突时的分支合并
有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分Git 就无法干净地把两者合到一起(译注:逻辑上说,这种问题只能由人来裁决。)。如果你在解决问题 #53 的过程中修改了 hotfix 中修改的部分,将得到类似下面的结果:
```
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
```
Git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status 查阅:
```
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a"
```
任何包含未解决冲突的文件都会以未合并unmerged的状态列出。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。可以看到此文件包含类似下面这样的部分:
```
<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53
```
可以看到 ======= 隔开的上半部分,是 HEAD即 master 分支,在运行 merge 命令时所切换到的分支)中的内容,下半部分是在 iss53 分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。比如你可以通过把这段内容替换为下面这样来解决
```
<div id="footer">
please contact us at email.support@github.com
</div>
```
这个解决方案各采纳了两个分支中的一部分内容,而且我还删除了 <<<<<<<======= 和 >>>>>>> 这些行。在解决了所有文件里的所有冲突后,运行 git add 将把它们标记为已解决状态(译注:实际上就是来一次快照保存到暂存区域。)。因为一旦暂存,就表示冲突已经解决。如果你想用一个有图形界面的工具来解决这些问题,不妨运行 git mergetool它会调用一个可视化的合并工具并引导你解决所有冲突
```
$ git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html
Normal merge conflict for 'index.html':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (opendiff):
```
如果不想用默认的合并工具Git 为我默认选择了 opendiff因为我在 Mac 上运行了该命令),你可以在上方"merge tool candidates"里找到可用的合并工具列表,输入你想用的工具名。我们将在第七章讨论怎样改变环境中的默认值。
退出合并工具以后Git 会询问你合并是否成功。如果回答是,它会为你把相关文件暂存起来,以表明状态为已解决。
再运行一次 git status 来确认所有冲突都已解决:
```
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
```
如果觉得满意了,并且确认所有冲突都已解决,也就是进入了暂存区,就可以用 git commit 来完成这次合并提交。提交的记录差不多是这样:
```
Merge branch 'iss53'
Conflicts:
index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
#
```
如果想给将来看这次合并的人一些方便,可以修改该信息,提供更多合并细节。比如你都作了哪些改动,以及这么做的原因。有时候裁决冲突的理由并不直接或明显,有必要略加注解。

View File

@ -0,0 +1,127 @@
git的远程仓库是指托管在网络上的项目仓库。对于公司来说绝大部分公司都会自己搭建自己的git仓库。对于RD来说自然免不了要经常跟远程仓库 remote打交道。今天我们就来对远程仓库的使用做一个总结。
第一步自然是将代码从仓库clone过来
```
lei.wang ~/gitcode $ git clone git@xxx:lei.wang/user_labels_merge.git
Cloning into 'user_labels_merge'...
remote: Counting objects: 30, done.
remote: Compressing objects: 100% (25/25), done.
remote: Total 30 (delta 7), reused 0 (delta 0)
Receiving objects: 100% (30/30), 5.70 KiB | 0 bytes/s, done.
Resolving deltas: 100% (7/7), done.
Checking connectivity... done.
```
```
lei.wang ~/gitcode/user_labels_merge $ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
```
因为刚clone过来代码所以此时工作区是干净的。
然后查看一下当前配置项里有哪些远程仓库,使用`git remote`命令即可就会列出远程库的名字。在我们clone了刚才的项目之后默认会看到一个origin的远程仓库。
```
lei.wang ~/gitcode/user_labels_merge $ git remote
origin
```
与大部分命令一样,`-v`选项表示列出详细信息:
```
lei.wang ~/gitcode/user_labels_merge $ git remote -v
origin git@xxx:lei.wang/user_labels_merge.git (fetch)
origin git@xxx:lei.wang/user_labels_merge.git (push)
```
如果要给此远程仓库添加一个新名字,方便后续使用,可以用以下方式:
```
lei.wang ~/gitcode/user_labels_merge $ git remote add test_name git@xxx:lei.wang/user_labels_merge.git
lei.wang ~/gitcode/user_labels_merge $ git remote
origin
test_name
lei.wang ~/gitcode/user_labels_merge $ git fetch test_name
From xxx:lei.wang/user_labels_merge
* [new branch] master -> test_name/master
lei.wang ~/gitcode/user_labels_merge $ git remote -v
origin git@xxx:lei.wang/user_labels_merge.git (fetch)
origin git@xxx:lei.wang/user_labels_merge.git (push)
test_name git@xxx:lei.wang/user_labels_merge.git (fetch)
test_name git@xxx:lei.wang/user_labels_merge.git (push)
```
以上操作,首先使用`git remote add new_short_name url`命令 相当于给url对应的git仓库起了个别名。可以看到对应的远程库多了个名称test_name然后我们用`git fetch test_name`从 远程库拉取代码。再用`git remote -v`查看 一下可以看到test_name相关的信息。
如果要想删除这个test_name也很简单
```
lei.wang ~/gitcode/user_labels_merge $ git remote remove test_name
lei.wang ~/gitcode/user_labels_merge $
lei.wang ~/gitcode/user_labels_merge $ git remote
origin
lei.wang ~/gitcode/user_labels_merge $ git remote -v
origin git@xxx:lei.wang/user_labels_merge.git (fetch)
origin git@xxx:lei.wang/user_labels_merge.git (push)
```
使用`git remote remove remote_name`即可。
如果要查看远程库的相关信息,使用`git remote show remote_name`
```
lei.wang ~/gitcode/user_labels_merge $ git remote show origin
* remote origin
Fetch URL: git@xxx:lei.wang/user_labels_merge.git
Push URL: git@xxx:lei.wang/user_labels_merge.git
HEAD branch: master
Remote branch:
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)
```
要给远程库重命名:
```
lei.wang ~/gitcode/user_labels_merge $ git remote rename origin origin_test
lei.wang ~/gitcode/user_labels_merge $ git remote
origin_test
[xxx@xxx merge_user_labels_mr]$ git remote -v
origin git@xxx:lei.wang/user_labels_merge.git (fetch)
origin git@xxx:lei.wang/user_labels_merge.git (push)
```
原来此项目是在lei.wang下面现在我在远程库中将其移动到了名为dmo的namespace下面。此时本地对应的远程库也要进行相应修改
```
[xxx@xxx merge_user_labels_mr]$ git fetch
Access denied.
fatal: The remote end hung up unexpectedly
```
可以看到,将项目移动以后,这个时候如果想拉取代码的话会报错。其实这是废话,你原来指定的远程库里代码都没有了,能不报错嘛。
这个时候就需要修改本地仓库指向的远程库:
```
[xxx@xxx merge_user_labels_mr]$ git remote set-url origin git@xxx:dmo/user_labels_merge.git
[xxx@xxx merge_user_labels_mr]$
[xxx@xxx merge_user_labels_mr]$ git remote -v
origin git@xxx:dmo/user_labels_merge.git (fetch)
origin git@xxx:dmo/user_labels_merge.git (push)
[xxx@xxx merge_user_labels_mr]$ git fetch
[xxx@xxx merge_user_labels_mr]$
```
使用命令`git remote set-url remote-name new_url` 即可。至此,搞定,手工。。。