我使用以下命令生成了补丁
git log -p -m -1 --pretty=email --first-parent XXXX
。
我想将此修补程序应用于另一个存储库(具有相同的文件但不一定具有相同的历史记录)。
使用git apply
时,正确应用了补丁,但我必须自己使用正确的用户提交文件,日期......
使用git am
时出现以下错误:
错误:toto.cpp:补丁不适用
我只是无法理解为什么它可以与apply
一起使用但不能与am
知道如何将其应用于am
?
编辑:
链接答案中给出的答案只是解释apply
在am
期间不会提交更改。更多内容表示am
在后台使用apply
,因此它似乎是am
命令特有的失败。
答案 0 :(得分:4)
git am
命令故意挑剔其输入格式,以便它可以创建一个新的提交,其哈希ID 与的哈希ID相同原始提交,这反过来意味着git am
创建的新提交与原始提交一点一点地相同。
git apply
命令故意对其输入格式不那么挑剔,因为你而不是git apply
会创建一个不<的新提交/ em>与任何原始提交完全相同。
您的问题从命令开始:
git log -p -m -1 --pretty=email --first-parent
表示您正在显示合并。 git format-patch
命令从不在其输出中包含合并,因为git am
无法进行合并。由于git am
的要点是进行逐位相同的提交,而git format-patch
的要点是产生适合输入git am
和git am
的输出,进行合并,git format-patch
不需要提交合并提交。
这些结合使git am
工具无法用于您的特定目的(似乎将合并提交转换为针对其第一个父级的变更集,通过电子邮件传输变更集或类似于电子邮件,然后在另一端应用它以获得具有相似但不相同的元数据的不同提交,就像通过cherry-pick )。您只需编写自己的工具,或使用git apply
并手动提交。
如上段所示,您可以使用一种编写自己的工具的方法是在合并的第一个父级(可能在分离的HEAD上)选择合并(使用-m 1
),使用git format-patch
格式化该修补程序,并使用git am
将该修补程序应用于其他位置。这个脚本很不经过测试,但可能有效:
#! /bin/sh
. $(git --exec-path)/git-sh-setup
# abort if on orphan branch (not worth the necessary hackery)
git rev-parse -q --verify HEAD 2>/dev/null ||
die 'this does not work on an orphan branch'
# stolen out of git-filter-branch
finish_ident() {
# Ensure non-empty id name.
echo "case \"\$GIT_$1_NAME\" in \"\") GIT_$1_NAME=\"\${GIT_$1_EMAIL%%@*}\" && export GIT_$1_NAME;; esac"
# And make sure everything is exported.
echo "export GIT_$1_NAME"
echo "export GIT_$1_EMAIL"
echo "export GIT_$1_DATE"
}
set_ident () {
parse_ident_from_commit author AUTHOR committer COMMITTER
finish_ident AUTHOR
finish_ident COMMITTER
}
# begin format-patch-even-if-merge code
hash=$(git rev-parse --verify "$1") || exit # verify that it's valid
hash=$(git rev-parse $hash^{commit}) || exit # and that it's a commit
parents=$(git rev-parse $hash^@)
set $parents
# if a root commit or ordinary commit, just use git format-patch
case $# in
0|1) git format-patch --stdout -1 $1; exit;;
*) ;; # merge - use cherry-pick
esac
# this is the meat of the trick, here
firstparent=$1
exec 3>&1 1>&2 # save stdout and redirect to stderr
echo "cherry picking $hash onto $firstparent to make it format-able"
# save where we were (branch or hash); arrange to return
# there on exit or ^C etc
if ! returnto=$(git symbolic-ref --short HEAD 2>/dev/null); then
# already detached, save hash ID
returnto=$(git rev-parse HEAD)
fi
trap "git checkout $returnto; exit" 0 1 2 3 15
# Use the identity of the merge creator for new commits.
# NOTE: this should be optional and probably NOT the default,
# so it is commented out here.
#eval "$(set_ident <$hash)" ||
# die "setting author/committer failed for commit $hash"
# Move to a detached head on the first parent of the merge.
git checkout $firstparent || exit
# Now we can cherry-pick the merge to a non-merge.
git cherry-pick -m 1 $hash || exit
# Now show the commit we just made, to original stdout.
git format-patch --stdout -1 HEAD 1>&3
如果您希望完整地传输合并提交,那么通过标准git push
或git fetch
协议可以轻松实现。如果由于某种原因不可用,git bundle
命令可以创建一个文件(Git称之为 bundle ),其中包含跨越障碍传输存储库的某些部分所需的所有数据推/获取本身不能交叉。然后,您可以在捆绑文件上运行git fetch
,以便在以其他方式跨越该障碍后提取必要的信息。
但是,请注意,您需要 all 从合并提交中可以访问的对象:bundle必须包含 other Git 缺少的每个对象。 fetch和push命令的重点是让 Git 计算这组对象:两个Gits相互通信,找出接收Git缺少的发送Git的含义。那是什么进入了捆绑。虽然捆绑包有额外的对象(接收Git可以忽略它们)是正常的,但是传输它们是浪费资源,因此获取或推送将计算最小化对象设置,将其打包成一个&#34;薄包&#34; (捆绑的fetch / push等价物),通过线路发送,让另一端解压缩。