Reputation: 35790
Simple Version:
If I have a branch "foo-555", with a bunch of commits with messages like:
and I want to remove all the commits that don't start with "foo 555:", is there any way to do that using git filter-branch (or any other tool for that matter)?
Original Version (More Detailed):
In our repository we have a convention where every commit message starts with a certain pattern:
Redmine #555: SOME_MESSAGE
We also do a bit of rebasing to bring in the potential release branch's changes to a specific issue's branch. In other words, I might have branch "foo-555", but before I merge it in to branch "pre-release" I need to get any commits that pre-release has that foo-555 doesn't (so that foo-555 can fast-forward merge in to pre-release).
However, because pre-release sometimes changes, we sometimes wind up with situations where you bring in a commit from pre-release, but then that commit later gets removed from pre-release. It's easy to identify commits that came from pre-release, because the number from their commit message won't match the branch number; for instance, if I see "Redmine #123: ..." in my foo-555 branch, I know that its not a commit from my branch.
So now the question: I'd like to remove all of the commits that "don't belong" to a branch; in other words, any commit that:
but of course "555" will vary from branch to branch. Is there any way to use filter-branch (or any other tool) to accomplish this? Currently the only way I can see to do it is to do go an interactive rebase ("git rebase -i") and manually remove all the "bad" commits.
Upvotes: 13
Views: 6984
Reputation: 334
Based on Gingi answer but simplified if statement
git filter-branch --commit-filter '
if [[ $(git show -s --format=%B "$GIT_COMMIT") == "fix" ]]
then
skip_commit "$@";
else
git commit-tree "$@";
fi' master
Note that after rewriting history you should delete all local and remote tags, otherwise you'll have gangling branches
Upvotes: 0
Reputation: 2304
Here's a fast solution that uses filter-branch
instead of rebasing. There's no interactivity or needing to resolve conflicts.
git filter-branch --commit-filter '
if [ `git rev-list --all --grep "<log-pattern>" | grep -c "$GIT_COMMIT"` -gt 0 ]
then
skip_commit "$@";
else
git commit-tree "$@";
fi' HEAD
You'll probably want to then clean up with:
git reflog expire --expire=now
git gc --prune=now
Upvotes: 11
Reputation: 93710
Write a script to remove lines with Redmine #555
:
#!/bin/sh
mv $1 $1.$$
grep -v 'Redmine #555' < $1.$$ > $1
rm -f $1.$$
Of course you can do that however you want (eg echo
a script of commands to ed
).
Then launch your rebase with EDITOR
set to your script:
EDITOR=/path/to/script git rebase -i REVISION
Of course it still won't be guaranteed to complete -- there may be errors during the rebase caused by leaving out revisions. You can still fix them and git rebase --continue
manually.
Upvotes: 5