Ben*_*ing 126 git git-merge git-rebase git-checkout git-branch
我们有一个拥有超过400个提交的Git存储库,其中前几个是很多试错.我们希望通过将许多提交压缩到单个提交来清理这些提交.当然,git-rebase似乎还有很长的路要走.我的问题是它最终会出现合并冲突,而这些冲突并不容易解决.我不明白为什么会有任何冲突,因为我只是压缩提交(不删除或重新排列).很可能,这表明我并不完全理解git-rebase是如何做它的南瓜的.
这是我正在使用的脚本的修改版本:
repo_squash.sh(这是实际运行的脚本):
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Run Code Online (Sandbox Code Playgroud)
repo_squash_helper.sh(此脚本仅由repo_squash.sh使用):
if grep -q "pick " $1
then
# cp $1 ../repo_squash_history.txt
# emacs -nw $1
sed -f ../repo_squash_list.txt < $1 > $1.tmp
mv $1.tmp $1
else
if grep -q "initial import" $1
then
cp ../repo_squash_new_message1.txt $1
elif grep -q "fixing bad import" $1
then
cp ../repo_squash_new_message2.txt $1
else
emacs -nw $1
fi
fi
Run Code Online (Sandbox Code Playgroud)
repo_squash_list.txt :(此文件仅由repo_squash_helper.sh使用)
# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g
Run Code Online (Sandbox Code Playgroud)
我会把"新消息"的内容留给你的想象力.最初,我这样做没有"--strategy他们的"选项(即,使用默认策略,如果我理解文档正确是递归的,但我不确定使用哪种递归策略),它也没有'工作.另外,我应该指出,使用repo_squash_helper.sh中注释掉的代码,我保存了sed脚本工作的原始文件并对其运行了sed脚本,以确保它正在执行我想要它做的事情(它是).同样,我甚至不知道为什么会发生冲突,因此使用哪种策略似乎并不重要.任何建议或见解都会有所帮助,但大多数情况下我只想让这种压力起作用.
在开发我们庞大的"真实"存储库之前,我在测试存储库中使用了类似的脚本.这是一个非常简单的存储库,测试工作干净利落.
我失败的消息是:
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
Run Code Online (Sandbox Code Playgroud)
这是第一次壁球提交后的第一个选择.运行git status产生一个干净的工作目录.如果我再做一次git rebase --continue,我会在几次提交之后得到一个非常相似的消息.如果我再这样做,我会在几十次提交后得到另一个非常相似的消息.如果我再次这样做,这次它会经历大约一百次提交,并产生这样的信息:
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental
Run Code Online (Sandbox Code Playgroud)
如果我然后跑git status,我得到:
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: repo/file_A.cpp
# modified: repo/file_B.cpp
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: repo/file_X.cpp
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: repo/file_Z.imp
Run Code Online (Sandbox Code Playgroud)
"两个修改过的"位对我来说听起来很奇怪,因为这只是一个选择的结果.值得注意的是,如果我看一下"冲突",它可归结为一行,其中一个版本以[tab]字符开头,另一个版本带有四个空格.这听起来像是我如何设置我的配置文件的问题,但它没有任何类型.(我确实注意到core.ignorecase设置为true,但显然git-clone自动完成.考虑到原始源是在Windows机器上,我并不完全惊讶.)
如果我手动修复file_X.cpp,那么不久之后就会发生另一次冲突,这次是在一个版本认为应该存在的文件(CMakeLists.txt)和一个版本认为不应该存在的文件之间.如果我通过说我确实想要这个文件(我这样做)来修复这个冲突,稍后我会得到另一个冲突(在同一个文件中),现在有一些相当重要的变化.它仍然只有冲突的25%左右.
我还应该指出,由于这可能非常重要,因此该项目始于svn存储库.初始历史很可能是从该svn存储库导入的.
在云雀(受Jefromi的评论影响),我决定改变我的repo_squash.sh:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Run Code Online (Sandbox Code Playgroud)
然后,我刚才接受了原始条目.即,"rebase"不应该改变一件事.它最终得到了前面描述的相同结果.
或者,如果我省略策略并将最后一个命令替换为:
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Run Code Online (Sandbox Code Playgroud)
我不再得到"没有任何承诺"的rebase问题,但我仍然留下其他冲突.
test_squash.sh(这是您实际运行的文件):
#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================
#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt
git add test_file.txt
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..
#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================
#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
#========================================================
#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================
Run Code Online (Sandbox Code Playgroud)
test_squash_helper.sh(由test_sqash.sh使用):
# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
echo "Created two line file" > $1
fi
Run Code Online (Sandbox Code Playgroud)
PS:是的,当你看到我使用emacs作为后备编辑时,我知道有些人会畏缩.
PPS:我们知道在rebase之后我们将不得不吹掉现有存储库中的所有克隆.(按照"在发布后不得对存储库进行重新定义"这一行.)
PPPS:谁能告诉我如何为此添加赏金?无论我处于编辑模式还是查看模式,我都无法在此屏幕上看到任何选项.
Cas*_*bel 64
好吧,我有足够的信心扔出答案.也许必须编辑它,但我相信我知道你的问题是什么.
你的玩具回购测试用例中有一个合并 - 更糟糕的是,它与冲突合并.你在整个合并中进行了变革.如果没有-p(不完全适用-i),合并将被忽略.这意味着无论你在冲突解决方案中做了什么,当rebase试图挑选下一个提交时,你的补丁可能不适用.(我相信这显示为合并冲突,因为git cherry-pick可以通过在原始提交,当前提交和共同祖先之间进行三向合并来应用补丁.)
不幸的是,我们在评论中指出,-i和-p(保留合并)不相处得很好.我知道编辑/重写工作,而重新排序则不然.但是,我相信它与南瓜一起工作正常.这没有记录,但它适用于我在下面描述的测试用例.如果你的情况比较复杂,你可能会在做你想做的事情上遇到很多困难,尽管它仍然是可能的.(故事的道德:rebase -i 在合并前清理.)
所以,我们假设我们有一个非常简单的案例,我们想要将A,B和C压缩在一起:
- o - A - B - C - X - D - E - F (master)
\ /
Z -----------
Run Code Online (Sandbox Code Playgroud)
现在,就像我说的,如果X中没有冲突,那git rebase -i -p就像你期望的那样工作.
如果有冲突,事情会变得有点棘手.它会做很好的挤压,但是当它试图重新创建合并时,冲突将再次发生.您必须再次解决它们,将它们添加到索引,然后使用git rebase --continue继续.(当然,您可以通过签出原始合并提交中的版本来再次解决它们.)
如果你碰巧rerere在您的回购启用(rerere.enabled设置为true),这将是比较容易的方式-蹦就能重新使用重新有线重新从您最初有冲突的解决方案,而你所要做的就是检查它要确保它正常工作,请将文件添加到索引,然后继续.(你甚至可以更进一步,开启rerere.autoupdate,它会为你添加它们,所以合并甚至不会失败).但是,我猜你没有启用rerere,所以你必须自己做冲突解决.*
*或者,您可以尝试使用rerere-train.shgit-contrib中的脚本,该脚本尝试"从现有的合并提交中"重新启动数据库" - 基本上,它会检出所有合并提交,尝试合并它们,如果合并失败,它抓住结果并向他们展示git-rerere.这可能非常耗时,而且我从未真正使用它,但它可能非常有用.
hli*_*dka 56
如果您不介意创建新分支,这就是我处理问题的方法:
在主人身上:
# create a new branch
git checkout -b new_clean_branch
# apply all changes
git merge original_messy_branch
# forget the commits but have the changes staged for commit
git reset --soft master
git commit -m "Squashed changes from original_messy_branch"
Run Code Online (Sandbox Code Playgroud)
如果您想从一长段提交中创建一个提交(其中一些是合并提交),最简单的方法是将分支重置到第一次提交之前的点,同时保留所有更改,然后重新提交它们:
git reset $(git merge-base origin/master @)
git add .
git commit
Run Code Online (Sandbox Code Playgroud)
替换origin/master为您分支的分支的名称。
这add .是必要的,因为新添加的文件在重置后显示为未跟踪。
小智 6
我正在寻找类似的要求,即丢弃我的开发分支的中间提交,我发现这个过程对我有用。
在我的工作分支上
git reset –hard mybranch-start-commit
git checkout mybranch-end-commit . // files only of the latest commit
git add -a
git commit -m”New Message intermediate commits discarded”
Run Code Online (Sandbox Code Playgroud)
viola 我们已经将最新的提交连接到分支的开始提交!没有合并冲突问题!在我的学习实践中,我在这个阶段得出了这个结论,有没有更好的方法来达到这个目的。