我们有一个包含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
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
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
我将“新消息”的内容留给您想象。最初,我没有使用“ --strategy他们的”选项(例如,使用默认策略,如果我正确地理解了文档,则使用递归策略,但是不确定使用哪种递归策略),并且也没有这样做。工作。另外,我应该指出,使用repo_squash_helper.sh中注释掉的代码,我保存了sed脚本可以使用的原始文件,并针对它运行sed脚本,以确保它正在执行我想要的操作(它是)。再说一次,我什至不知道为什么会发生冲突,因此使用哪种策略似乎无关紧要。任何建议或见解都会有所帮助,但大多数情况下,我只是想使这种挤压工作正常进行。
更新了与Jefromi讨论时获得的更多信息:
在处理庞大的“真实”存储库之前,我在测试存储库上使用了类似的脚本。这是一个非常简单的存储库,并且测试工作正常。
失败时收到的消息是:
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
这是第一次壁球犯规之后的第一选择。运行将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
如果再运行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
对我来说,“两者都修改了”的声音听起来很奇怪,因为这只是选择的结果。还值得注意的是,如果我查看“冲突”,它会简化为一行,其中一个版本以[tab]字符开头,另一版本以4个空格开头。听起来这可能与我如何设置配置文件有关,但其中没有任何内容。(我确实注意到将core.ignorecase设置为true,但显然git-clone会自动做到这一点。考虑到原始源在Windows计算机上,我对此并不感到完全惊讶。)
如果我手动修复file_X.cpp,它随后很快就会失败,并出现另一个冲突,这一次是一个版本认为应该存在的文件(CMakeLists.txt)和一个版本认为不应存在的文件。如果通过说我确实想要此文件来解决此冲突(我确实要这样做),则在稍后进行一些提交后,我又遇到了另一个冲突(在同一文件中),现在这里有一些相当不重要的更改。仍然只有25%可以解决冲突。
我还应该指出,由于这可能非常重要,因此该项目始于svn存储库。最初的历史很可能是从svn存储库导入的。
更新#2:
在一个百灵鸟上(受Jefromi的评论影响),我决定将repo_squash.sh更改为:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
然后,我照原样接受了原始条目。即,“重新设置”不应该改变任何事情。最终得到与先前描述相同的结果。
更新#3:
或者,如果我省略该策略,并用以下命令替换最后一个命令:
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
我不再遇到“什么都没提交”的变基问题,但是我仍然遇到其他冲突。
使用玩具存储库进行更新以重新创建问题:
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
#========================================================
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
PS:是的,当您看到我使用emacs作为后备编辑器时,我知道有些人畏缩。
PPS:我们知道,在重新设置基准之后,我们将必须销毁现有存储库的所有克隆。(按照“您在发布存储库后不要为其重新建立基础”的思路。)
PPPS:谁能告诉我如何为此增加赏金?无论是处于编辑模式还是查看模式,我都不会在此屏幕上的任何位置看到该选项。
rebase --interactive
-这些都是git尝试尝试的动作列表。我希望您可以将其减少到引起冲突的单个壁球,并避免使用辅助脚本的所有额外复杂性。其他丢失的信息是何时发生冲突-git何时应用补丁形成壁球,或者当它试图越过壁球并应用下一个补丁时?(您确定您的GIT_EDITOR垃圾邮件不会发生任何不良情况吗?对简单测试用例进行另一次投票。)
rebase -p
)