-
-
Save milki/2010094 to your computer and use it in GitHub Desktop.
#!/bin/sh | |
# xargs -tpa /home/milki/tmp/redo-merges/merges -L 3 /home/milki/tmp/redo-merges/remerge | |
# plan | |
# HEAD points to either the last redone merge or not a merge | |
# if last redone merge, NEW merge base is base of last merge | |
# git rev-parse HEAD^2 last "branch" | |
# else head is NEW merge base | |
# reset to NEW merge base of $2 | |
# branch, cherry-pick from HOLD merge baes to $2~1, return to newmaster | |
# merge branch | |
# rebase to base of $3 | |
# if same OLD merge base, no rebase | |
# else, rebase | |
# $1 previous merge | |
# $2 current merge | |
# $3 next merge | |
# assuming HEAD is result from remerging $1 | |
# origin/master is HEAD for original tree | |
echo "args: $1 $2 $3" | |
REDOFFMERGE=0 | |
git checkout newmaster | |
git branch -f oldmaster | |
#### STEP 1: DETERMINE NEW MERGE BASE #### | |
LASTBASE=`git rev-parse --short $1^2~1` | |
OLDBASE=`git rev-parse --short $2^2~1` | |
if [ $OLDBASE = $LASTBASE ] # same related branch | |
then | |
echo '==Related merge' | |
NEWBASE=`git rev-parse --short oldmaster^2~1` # use the same base as last merge | |
else | |
# Check if bad ff merge at OLDBASE | |
MSG=`git log -n 1 --format=%s $2~1` | |
if echo $MSG | grep --silent split | |
then | |
echo '**FF MERGE DETECTED**' | |
REDOFFMERGE=1 | |
fi | |
echo '==New merge' | |
NEWBASE=`git rev-parse --short oldmaster` # new branch base | |
fi | |
### END STEP 1 | |
### STEP 2 PART 0: RE-MERGE FF BRANCH #### | |
if [ $REDOFFMERGE = 1 ] | |
then | |
echo "Fix ff merge $2~1 onto $NEWBASE" | |
BRANCH=`git log -n 1 $2~1 --stat --name-only | tail -n 2 | sed '/^$/d' | head -n 1 | sed "s/#\(.*\)/\1/"` | |
MSG="Merge branch '$BRANCH'" | |
git checkout -B temp $NEWBASE | |
git cherry-pick $2~1 | |
git checkout newmaster | |
git merge --no-ff -m "$MSG" temp # no-ff merge | |
fi | |
#### STEP 2 PART 1: RE-MERGE BRANCH #### | |
echo "==Fix regular merge $2 onto $NEWBASE" | |
MSG=`git show --format=%s $2` # use same merge message | |
git checkout -B temp $NEWBASE | |
git cherry-pick $2^2 | |
if [[ $? -ne 0 ]] | |
then | |
echo "conflict in cherry-pick" | |
echo "Recover $2" | |
git read-tree $2 | |
git clean -fd | |
git checkout-index -fa | |
git status | |
echo "Using date from $2^2" | |
#CDATE=`git show -s --format=%ci $2^2` | |
#ADATE=`git show -s --format=%ai $2^2` | |
GIT_COMMITTER_DATE="$CDATE" GIT_AUTHOR_DATE="$ADATE" git commit -m "$MSG" | |
fi | |
git checkout newmaster | |
git merge --no-ff -m "$MSG" temp # no-ff merge | |
# Possible conflict | |
if [[ $? -ne 0 ]] | |
then | |
echo "conflict in remerge" | |
echo "Recover $2" | |
git read-tree $2 | |
git clean -fd | |
git checkout-index -fa | |
git status | |
echo "Using date from $2" | |
GIT_COMMITTER_DATE="$CDATE" GIT_AUTHOR_DATE="$ADATE" git commit -m "$MSG" | |
fi | |
### END STEP 2 ### | |
### STEP 3: Rebase to base next merge if unrelated to current merge### | |
if [ -n "$3" ] | |
then | |
NEXTOLDBASE=`git rev-parse --short $3^2~1` | |
NEWMASTER=`git rev-parse --short newmaster` | |
if [ $NEXTOLDBASE != $OLDBASE ] # If next isn't related, rebase | |
then | |
echo "Rebase to next merge-base $NEXTOLDBASE onto $NEWMASTER" | |
git rebase --committer-date-is-author-date -p --onto newmaster $2 $NEXTOLDBASE | |
git branch -f newmaster | |
fi | |
fi | |
### END STEP 3 ### | |
#git log --stat oldmaster..HEAD |
The real problem with the messed up history is that the rebased non-ff merges retain their original branch HEAD instead of the new HEAD.
New approach. Re-merge EVERY merge. This way, the merges have the right parents. Its looking good so far. But step 3 seems to lead to a noop.
When run in the script:
Successfully rebased and updated refs/heads/newmaster
When run manually:
Successfully rebased and updated detached HEAD
I expect to be in a detached HEAD when doing rebase --onto, so I'm not sure why the script is behaving differently at this point.
Replace git show with git rev-parse. Everything looks ok for a while until I start missing some merges. My merge file is probably missing some merge commits.
I lost some merges from a single bad merge commit which resulted in non-linear history. When I rebased into this commit, it skipped a couple merges as well as pulled in references to the old repo tree. Manually handling just this part was all that was needed. This script works just fine otherwise. I am done with this!
All new non-ff merges and old merges from rebase end up with new author/commit dates. This messes up the original history and git log --graph is ugly because branches that were only 2 commits long are now span from original date to new merge date.