git am / format-patch / cherry-pick

https://blog.csdn.net/ic_soc_arm_robin/article/details/8063221

介绍:

UNIX世界的软件开发大多都是协作式的,因此,Patch(补丁)是一个相当重要的东西,因为几乎所有的大型UNIX项目的普通贡献者,都是通过 Patch来提交代码的。作为最重要的开源项目之一,Linux,也是这样的。普通开发者从软件仓库clone下代码,然后写入代码,做一个Patch, 最后用E-mail发给Linux Kernel的维护者就好了。Git最初作为Linux的版本控制工具,提供了透明、完整、稳定的Patch功能。

我们先介绍一下Patch是什么。如果一个软件有了新版本,我们可以完整地下载新版本的代码进行编译安装。然而,像Linux Kernel这样的大型项目,代码即使压缩,也超过70MB,每次全新下载是有相当大的代价的。然而,每次更新变动的代码可能不超过1MB,因此,我们只要能够有两个版本代码的diff的数据,应该就可以以极低的代价更新程序了。因此,Larry Wall开发了一个工具:patch。它可以根据一个diff文件进行版本更新。

不过在git中,我们没有必要直接使用diff和patch来做补丁,这样做既危险又麻烦。git提供了两种简单的patch方案。一是用git diff生成的标准patch,二是git format-patch生成的Git专用Patch。

diff 和format-patch的比较:

兼容性:很明显,git diff生成的Patch兼容性强。如果你在修改的代码的官方版本库不是Git管理的版本库,那么你必须使用git diff生成的patch才能让你的代码被项目的维护人接受。
除错功能:对于git diff生成的patch,你可以用git apply --check 查看补丁是否能够干净顺利地应用到当前分支中;如果git format-patch 生成的补丁不能打到当前分支,git am会给出提示,并协助你完成打补丁工作,你也可以使用git am -3进行三方合并,详细的做法可以参考git手册或者《Progit》。从这一点上看,两者除错功能都很强。
版本库信息:由于git format-patch生成的补丁中含有这个补丁开发者的名字,因此在应用补丁时,这个名字会被记录进版本库,显然,这样做是恰当的。因此,目前使用Git的开源社区往往建议大家使用format-patch生成补丁。
在使用git-am之前, 你要首先git am –abort 一次,来放弃掉以前的am信息,这样才可以进行一次全新的am,不然会遇到这样的错误。

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

git-am 可以一次合并一个文件,或者一个目录下所有的patch,或者你的邮箱目录下的patch.

下面举两个例子:

你现在有一个code base: small-src, 你的patch文件放在~/patch/0001-trival-patch.patch

cd small-src
git-am ~/patch/0001-trival-patch.patch

如果成功patch上去, 你就可以去喝杯茶了。

如果失败了, git 会提示错误, 比如:

error: patch failed: android/mediascanner.cpp:452
error: android/mediascanner.cpp: patch does not apply

这样你就需要先看看patch, 然后改改错误的这个文件,让这个patch能够patch上去。

你有一堆patch, 名字是上面提到的那一堆patch, 你把他们放在~/patch-set/目录下(路径随意)

cd opencore
git am ~/patch-set/*.patch

(这里git就会按照文件名的顺序一次am这些patch)
如果一切顺利, 你所有的patch都OK了, 你又Lucky了。

不过不顺利的时候十有八九,如果git am中间遇到了patch,am就会停到打这个
patch的地方, 告诉你是哪个patch打不上去。

比如我现在有一个文件file,有两个patch.
file 的内容是

the text

more text

两个patch分别是:

0001-add-line.patch:

From 48869ccbced494e05738090afa5a54f2a261df0f Mon Sep 17 00:00:00 2001
From: zhangjiejing 
Date: Thu, 22 Apr 2010 13:04:34 +0800
Subject: [PATCH 1/2] add line

---
 file |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/file b/file
index 067780e..685f0fa 100644
--- a/file
+++ b/file
@@ -3,3 +3,5 @@ file:
 some text

 more text
+
+add line
--
1.6.3.3
0002-change-line.patch:

From f756e1b3a87c216b7e0afea9d15badd033171578 Mon Sep 17 00:00:00 2001
From: zhangjiejing 
Date: Thu, 22 Apr 2010 13:05:19 +0800
Subject: [PATCH 2/2] change line

---
 file |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/file b/file
index 685f0fa..7af7852 100644
--- a/file
+++ b/file
@@ -1,6 +1,6 @@
 file:

-some text
+Change line text

 more text

--
1.6.3.3

运行

git am *.patch

来merge这些patch, 报错, Patch failed at 0001 add line这样我们看0001这
个patch,原来patch需要的是some text, 而file里面是the text, 所以我们用编
辑器把这行改成some text,

vi file
git apply 0001-add-line.patch
git add file
git am --resolved

在解决完冲突以后, 比如用git add来让git知道你已经解决完冲突了。

如果你发现这个冲突是无法解决的, 要撤销整个am的东西。 可以运行git am –abort
如果你想只是忽略这一个patch,可以运行git am –skip来跳过这个patch.
使用git format-patch生成所需要的patch:

# git format-patch -s 1bbe3c8c197a35f79bfddaba099270a2e54ea9c7

please replace the hash code with your repo previous commit.
then you can find the patch under repo directory.
then mail your patch to configuration admin. 

# git format-patch -M master         // 当前分支所有超前master的提交

# git format-patch -s 4e16           // 某次提交以后的所有patch, --4e16指的是SHA1 ID, -s : signed off

# git format-patch -1                //  单次提交
# git format-patch -3                // 从master往前3个提交的内容,可修改为你想要的数值
# git format-patch –n 07fe           // -n指patch数,07fe对应提交的名称, 某次提交(含)之前的几次提交
# git format-patch -1 commit-id      // + commit-id 选定的那个commit打patch

# git format-patch -s --root origin  // 从origin到指定提交的所有patch

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

今天发现有个需求,就是我提交了错误的提交,如何再切换到先前的版本,但是并没有建立branch的时候,
原来用git checkout 就可以了,

# git checkout 7a1eba1f6d   // SHA1 ID 这种情况只是切换,但是不做任何处理,如果需要反转就用

# git revert HEAD

还原

git revert HEAD              还原最近一次的修改,但版本commit会向前推进,只是创建一个相反的内容,再次提交
git revert commit-id         还原指定版本的修改

复位

git reset HEAD^
git reset HEAD~1             撤销最后一次提交,默认模式-mixed,回退commit与index,
git reset commit-id          复位到指定的commit,但是会把修改的内容保留在目录下,后面可以修改并继续提交
git reset --soft             回退commit,但不回退index
git reset --hard HEAD^       撤销最后一次提交并清除本地修改。
git reset --hard commit-id   # 整個 Repository 恢復到某個版本的狀態

rebase

git rebase -i HEAD~*         用来修改commit的内容,可以删除或者修改名字或者移动位置或者合并,然后ctrl+o,ctrl+x保存退出。

  # Commands:
  #  p, pick = use commit
  #  r, reword = use commit, but edit the commit message
  #  e, edit = use commit, but stop for amending
  #  s, squash = use commit, but meld into previous commit
  #  f, fixup = like "squash", but discard this commit's log message

查看

git log -p test.c          // 查看这个文件提交的记录,及内容的修改

git log test.c              // 仅查看提交的记录

git log --oneline -2        // 显示一行,显示最近的两次提交

git log --pretty=short

配置

# git config --global alias.lg "log --graph"
# git config -l
alias.lg=log --graph --pretty=format:'%Cred%h%Creset: %s %Cgreen(%cr)%Creset %C(yellow)%d%Creset' --abbrev-commit --date=short
Explain:

%Cred : 显示红色
%h : 简短的commit id
%Creset : 颜色复位成常规的颜色
%s : 显示提交的内容标题
%cr : 显示提交的日期,这个按照比今天前多少天
%Cyellow : 显示黄色
%d : ref name

添加

git add . 

追加

git commit --amend  // 追加到前一次提交,这个类似于 git rebase -i HEAD~2

显示

git show        // 显示最近的一次提交的内容

git show --name-only   // 只显示修改的文件名,而不具体显示修改的行的内容

搜索

git grep -n -w  “searched name”   // git 提供的比较方便的搜索方式, -w 只搜索全匹配的字符串,适合于搜索函数名

应用补丁

git am -3 *.patch

git am --ignore-space-change -3 xxx.patch "忽略空格的改变,这个可以忽略windows和linux换行符的差异"

查看责任人

git blame -L 40,60 test.c      // 责任40行 - 60行
git blame -L 40,+21 test.c     // 责任第40行,及后面21行

git blame --since=3.weeks -- test.c

如何把已经提交的commit, 从一个分支放到另一个分支
实际问题:
在本地 master 分支上做了一个commit ( 38361a68138140827b31b72f8bbfd88b3705d77a ) , 如何把它放到 本地 old_cc 分支上?
办法之一: 使用 cherry-pick. 根据git 文档:

Apply the changes introduced by some existing commits
就是对已经存在的commit 进行apply (可以理解为再次提交)
简单用法:

git cherry-pick 

例如:

$ git checkout old_cc
$ git cherry-pick 38361a68     # 这个 38361a68 号码,位于:

$ git log 
commit 38361a68138140827b31b72f8bbfd88b3705d77a 
Author: Siwei Shen 
Date:   Sat Dec 10 00:09:44 2011 +0800

1. 如果顺利,就会正常提交。结果:

Finished one cherry-pick.
# On branch old_cc
# Your branch is ahead of 'origin/old_cc' by 3 commits.

2. 如果在cherry-pick 的过程中出现了冲突

Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add ' or 'git rm '
and commit the result with: 
    
            git commit -c 15a2b6c61927e5aed6718de89ad9dafba939a90b

就跟普通的冲突一样,手工解决:
2.1 $ git status # 看哪些文件出现冲突

both modified: app/models/user.rb

2.2 $ vim app/models/user.rb # 手动解决它。
2.3 $ git add app/models/user.rb
2.4 git commit -c <新的commit号码>

你可能感兴趣的:(git)