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 <zhangjiejing@zhangjiejing-desktop.(none)>
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 <zhangjiejing@zhangjiejing-desktop.(none)>
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 <commit id>
例如:
$ git checkout old_cc
$ git cherry-pick 38361a68 # 这个 38361a68 号码,位于:
$ git log
commit 38361a68138140827b31b72f8bbfd88b3705d77a
Author: Siwei Shen <siwei.shen@focusbeijing.com>
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 <paths>' or 'git rm <paths>'
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号码>