git 重用提交 resue commit

git 中重用提交(reuse commit)主要有两种方式:一种是git cherry-pick(摘樱桃), 另一种是git patch(打补丁).

git cherry-pick

  1. 方式一:指定某个分支的提交id作为摘取的目标

    将一个分支上的某个commit合并到另一个分支,可用使用cherry-pick命令实现。
    比如将dev分支上commit_id为abcdefg1234567的提交合并到master分支:

    1. 切换到master分支:git checkout master
    2. 执行cherry-pick命令:git cherry-pick abcdefg1234567
    3. 推送到远程master仓库:git push

注意master上新的commit id与dev上的id并不相同,即只是将dev上的修改拷贝过来作为一个新的提交,这就会带来一个问题:cherry-pick之后,dev想再次merge到master,要先对dev分支进行rebase变基

  1. 方式二:指定某一个分支

    a - b - c - d   Master
         \
           e - f - g Feature

    操作的步骤差不多雷同,同样是需要先切换分支,再次cherry-pick分之名即可

    # 切换到 master 分支
    $ git checkout master
    
    # Cherry pick 操作
    $ git cherry-pick f
    a - b - c - d - f   Master
         \
           e - f - g Feature

git cherry-pick在某些使用场景上和git merge比较相似,但是cherry-pick更强调某一个瞬间的摘取

git patch

git patch有不同的方案:主要分别是diff/apply方案format-patch/am方案

为什么有了diff还要有format-patch呢?主要还是使用场景的不同:

  • diff仅保留了文件重A状态变成B状态的差别,而不会保留commit记录消息等信息,而format-patch则是完整保留了每次提交的完成信息,这也解释了diff可以多个commit生成单个patch,而format-patch则是每个commit都生成一个patch文件。
  • 两者是成对使用的,即git apply使用的补丁必须是git diff生成的;git am的补丁必须是git format-patch生成的。当然补丁检查都是用git apply –check/–stat。
    # 生成补丁
    git diff > commit.patch
    # 检查补丁
    git apply --check commit.patch
    # 应用补丁
    git apply commit.patch

diff本质上是对两个版本之间文件做数据比较得出差异,重点在于两个版本。没有指定任何版本,那默认就是对lastCommit和working copy之间作比较。

diff apply

希望把修改的内容生成patch,可以如下操作:

git diff > commit.patch

对于已经add但是未commit的修改:

git diff --cached > commit.patch

本地修改已commit,希望把最近一次的修改生成patch:

# 注意:commitId为倒数第二个提交ID
git diff commitId > commit.patch

生成patch文件后,我们切换到希望应用patch的分支,然后按下面的步骤操作:

# 检查patch是否可以应用
git apply --check commit.patch
# 打单个补丁
git apply commit.patch
# 打多个补丁
git apply ../patch/*.patch

format-patch am

# 生成patch
git format-patch -1
# 检查patch
git apply --check 0001-made-some-change.patch
# 应用patch
git am 0001-made-some-change.patch
# 当前分支所有超前master的提交
git format-patch -M master

# 某次提交以后的所有patch
git format-patch commitId

# 从根到指定提交的所有patch
git format-patch --root commitId

# 某两次提交之间的所有patch
git format-patch commitId1..commitId2
# 某次提交(含)之前的几次提交
# -n指patch数
git format-patch -n commitId 

# 比如单次提交,生成commitId这次提交的patch
git format-patch -1 commitId

# 生成commitId前一次到commitId这两次修改的patch
git format-patch -2 commitId

git am可以一次合并一个文件,或者批量合并一个目录下所有的patch。在使用git am之前, 你要首先git am –abort一次,来放弃掉以前的am信息,这样才可以进行一次全新的am。不然会遇到这样的错误:

.git/rebase-apply still exists but mbox given.

单个补丁

# 先检查patch文件:
git apply --stat newpatch.patch
# 检查能否应用成功:
git apply --check newpatch.patch
# 打补丁:使用-s或--signoff选项,可以commit信息中加入Signed-off-by信息
git am --signoff < newpatch.patch

批量补丁

git am --abort
git am patch/*.patch

git rebase 变基

git-rebase-变基

rebase 变基

          A---B---C    dev
         /
    D---E---F---G    master

git rebase 变基场景描述:

  • 两个分支master、dev,其中dev分支是在master分支上的提交点E拉出的分支
  • 在两个分支合并之前,master分支有了新的提交F、G
  • 此时合并dev分支到master分支是不被允许的
  • 因为git不知道怎么处理ABC与FG的关系了,会提醒你需要先在本地rebase

变基: 简单说就是修改dev分支的基础节点由E变到G

通俗解释:

rebase,变基,可以直接理解为改变基底。dev分支是基于master分支的E拉出来的分支,dev的基底是E。而master在E之后有新的提交,就相当于此时要用master上新的提交来作为dev的新基底。实际操作为把E之后dev的提交存下来,然后删掉原来这些提交,再找到master的最新提交位置,把存下来的提交再接上去(新节点新commit id),如此dev分支的基底就相当于变成了G而不是原来的E了

具体操作

由于master是主分支,所以我们都是去改变其它分支的基,故先切到需要变基的分支

无冲突rebase

    # 没有冲突的情况下,依次执行如下步骤即可
    git checkout dev

    git rebase master -i   (-i表示交互模式)

    git push -f     (因为修改了commit记录,需要强push)

有冲突rebase

    git checkout dev

    git rebase master -i   (-i表示交互模式)
    # 有冲突时,到这一步已经开始报错,这时需要手动解决冲突才可以
    git rebase --continue
    # 如果没有冲突,则说明已经可以继续后一步了,但是如果还有冲突,则需要不断处理冲突并执行这一步,知道没有冲突报错为止
    git push -f     (因为修改了commit记录,需要强push)

git rebase 产生冲突的处理方式

  1. git rebase –skip

抛弃本地的 commit,采用远程的 commit。慎用:因为你本地的修改都会失去。

  1. git rebase –abort

效果是:终止这次 rebase 操作

  1. git rebase –continue

手动处理冲突的文件:执行git add .,再 git rebase –continue,反复操作直到解决完所有冲突,并合并到分支上。

大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序

springboot 源码fork后下载到本地的血泪历程

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者

百度百科

spring boot的介绍以及它的优点缺点种种信息,笔者不再累赘。此篇旨在记录一下笔者自己下载spring boot源码时的过程,真的是费时费力又费心。

由于笔者想要跟踪阅读源码,也可能在浏览源码时在源代码是做笔记记录,或对其代码做部分的调整和验证,而每次修改的记录又没法完全靠脑子记录下来,所以笔者把springboot的源码fork到自己的GitHub分支上,直接Git clone fork后的分支,这样本地的修改都可以在git中有所记录,而且也可以把笔记和一些心得临时记录在git中push到自己的分支,以便后续整理汇总。

问题一: time out

直接git clone 总是time out,跟网络有一定的关系,不可描述的原因导致有些时候我们用gayhub的时候必须科学上网,如果没法科学上网那就多尝试几次,总有一年可以搞定。

Git clone time out

问题二:OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054

Git clone的时候如果出现了如下图的错误,这个时候需要关闭SSL CERT verification,通过命令

SSL_ERROR_SYSCALL

git config http.sslVerify “false”

如果还是报错,那就把全局的也调一下

git config –global http.sslVerify “false”

问题三:Git clone 完成没有文件

我们经过很长一段时间的下载终于把所有的文件都下载完成了,而且也提示了如下图的完成下载仪式,可以在文件中什么都没有。 别慌,这个时候需要再一个命令把下载的内容都变出来。

git checkout FETCH_HEAD

上边的命令执行完成后,我们就可以看到文件了,而且执行日志中也会有给我们的提示,提示我们要自己 git checkout -b <new-branch-name>, 意思是让我们建一个本地的分支,不然现在的分支没有一个正经的名字。