Reputation: 171
I accidentally pushed a commit with some huge files, and then reverted it. But it causes anyone pulling this branch to fetch these files in history, so I decide to remove or squash these two commits. However, some branch has been merged in. I don't know how to make "git rebase -i" keep the branches structure.
The history now looks like:
H - new commits
|
G - merge
| \
| F - commits on another branch
| |
E | - some other commits
| |
D | - corrected B
| |
C | - revert B
| |
B | - huge files
| /
A - early commit
Can I change it to following?
h - new commits
|
g - merge
| \
| F - commits on another branch
| |
e | - some other commits
| |
d | - corrected B
| /
A - early commit
Upvotes: 3
Views: 1340
Reputation: 4528
Yes you can but you shouldn't.
It will require you to rewrite the history and replace the already pushed history on the server (which requires force push and most often results in everyone yelling at you).
But if you really want to then git filter-branch
is what you want to use, much like in this SO answer. So you would do something like this:
git filter-branch --commit-filter '
if [ "$GIT_COMMIT" = "<your commit to remove here>" ]
then
skip_commit "$@";
else
git commit-tree "$@";
fi' HEAD
There are a few more examples here.
Upvotes: 2
Reputation: 118340
No, you cannot.
The hash of each commit includes not just the hashed contents of all the files in the index, but also the hashes of the commit's parents.
"Corrected B" would have a different parent than it has now. That would change the hash.
It is possible to fix this, but it is not possible to avoid force-pushing the fix, and have everyone force-pull it, once that's done.
The only real way to fix this would be the following process:
git checkout A
Check out the parent commit, before the branch forked. Then, create two work branches:
git checkout -b corrected-mainline
git checkout -b corrected-fork
You're now on the corrected-fork branch. Now, on this branch, you should be able to git cherry-pick
all the commits up to the F
commit, skipping the commit with the huge files, and its revert. If any of these commits are a merge, do the same merge.
Now, do the same on the corrected-mainline
branch
git checkout corrected-mainline
Now, git cherry-pick
all the commits on the mainline branch, up to the last commit before the G
merge. Then, do a merge with the corrected-fork
branch. This is now your corrected G
merge commit.
Finish cherry-picking any commits that were originally on top of G
in your diagram, finishing with the last commit on your original branch.
At this point, the contents of your work branch should be the same as the contents of your original branch. Verify that this is so.
Then, delete your original branch, the rename your work branch to its name. Or, use git reset --hard
to reset your original branch to your corrected work branch.
Then do a git push --force
, to push your corrected branch up. Of course, anyone that pulls the branch will end up getting a force-pull.
Upvotes: 0