我们有一个超过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 their”选项(即使用默认策略,如果我正确理解文档的话,它是递归的,但我不确定使用的是哪种递归策略),而且它也不起作用。另外,我应该指出,在使用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
这是第一次壁球提交后的第一个选择。运行git status
会生成一个干净的工作目录。如果我然后执行git rebase --continue
,在几次提交后,我得到一个非常相似的消息。如果我再做一次,在几十次提交之后,我将得到另一条非常相似的消息。如果我再做一次,这一次它会经过大约100次提交,并产生以下消息:
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
“两者都经过了修改”这一点对我来说听起来很奇怪,因为这只是一个选择的结果。值得注意的是,如果我查看“冲突”,它可以归结为一行,其中一个版本以[制表符]开头,另一个版本以四个空格开头。这听起来可能是我如何设置我的配置文件的问题,但没有这样的东西。(我确实注意到了核心。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
然后,我只接受原来的元素。也就是说,“改基”不应该改变任何事情。最终得到的结果与前面描述的相同。
或者,如果我省略策略,并将最后一个命令替换为:
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_squash .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
附注:是的,我知道当你们中的一些人看到我使用emacs作为备用编辑器时感到畏缩。
p.p.s.:我们确实知道,在重基之后,我们将不得不放弃现有存储库的所有克隆。(这句话的意思是:“你不应该在一个存储库发布之后重新建立它的基础”。)
p.p.p.s.:谁能告诉我怎么给这个加赏金吗?不管我是在编辑模式还是查看模式,我都看不到这个选项。