I have a bit of a messy branch with 20 or so commits, and I'm preparing for a merge back to master. I've rebased it off master, and looking closer, I realise that there are files being modified in ways that are totally irrelevant to this branch, and not ready to be committed. The changes to those files aren't confined to specific commits.
So basically, I don't want anything to do with those files to be included in this branch if possible. Is there a good way to go about this? My fall back position #1 is obviously to just copy over the latest copy of each of those files then commit. But then the history will still contain the changes, and the Git Gods will frown upon me.
Fall back position #2 is to do the same, then squash the whole branch history down to one commit.
Any improvements on these?
Say your history is
$ git lola * 6473d7f (master) Update | * 9bcfa7e (HEAD, topic) Munge a, b, and c | * 99af942 Munge b and c | * 8383e2c Munge a and b |/ * d1363f4 Baseline
Note: lola
is a non-standard but helpful alias.
The commits have modified three different files.
$ git log --decorate=short --pretty=oneline --name-status topic 9bcfa7e946a92c226ad50ce430a9e4ae55b32490 (HEAD, topic) M a M b M c 99af942dbb922effcad8a72e96bec9ee9afcc437 Munge b and c M b M c 8383e2c8d6092550fec13d3c888c037b3a68af15 Munge a and b M a M b d1363f4fba67d94999b269b51bdb50a8a68ba27a Baseline A a A b A c
The changes to file b
are the ones you want to keep, and you want to discard all changes to a
and c
. One way to do this is with git filter-branch
.
$ git checkout -b tmp topic Switched to a new branch 'tmp' $ git merge-base topic master d1363f4fba67d94999b269b51bdb50a8a68ba27a $ git filter-branch --tree-filter 'git checkout d1363f -- a c' master..tmp Rewrite 8383e2c8d6092550fec13d3c888c037b3a68af15 (1/3) Rewrite 99af942dbb922effcad8a72e96bec9ee9afcc437 (2/3) Rewrite 9bcfa7e946a92c226ad50ce430a9e4ae55b32490 (3/3) Ref 'refs/heads/tmp' was rewritten
The tree filter above checks out the commits in the named range and restores files a
and c
to the content at the “merge base,” that is, the commit at which topic
branched away from master
.
Now tmp
has all topic
’s changes to b
but no changes to any other file.
$ git log --decorate --pretty=oneline --name-status tmp 9ee7e2bd2f380cc338b0264686bcd6f071eb1087 (HEAD, tmp) Munge a, b, and c M b 226c22f150af1ddc1f9adc19f97fc4f220851ada Munge b and c M b 45e706f7b22c37ee2025ee0d04c651135e7b31cd Munge a and b M b d1363f4fba67d94999b269b51bdb50a8a68ba27a Baseline A a A b A c
As a safety measure, git filter-branch
stores a backup of your original ref. When you’re satisfied with your changes and want to delete the backup tmp
, run
$ git update-ref -d refs/original/refs/heads/tmp
To checkout to a specific version you can do
git checkout <sha1> <file>
where sha1
is the unique sha1 hash of that version you want the file to be in.
I ran into a couple of problems as Windows user:
- Use Double Quotes for --tree-filter command.
- Use forward slashes for filepath.
- Dont start filepath with forward slash.
Here is how my command ended up looking:
git filter-branch -f --tree-filter "git checkout a59f75 -- source/_posts/blog1.md source/_posts/blog2.md" master..tmp
You could make your changes to remove functionality as a new commit, then create a patch from it. Do your rebasing/squashing or whatever you need to to prepare to merge back to master, then when you want to add the new functionality back in, you can apply the patch in reverse mode with
git apply -R
Note I've never done this... but it should work in theory I think. :-)
来源:https://stackoverflow.com/questions/9751928/git-best-way-to-remove-all-changes-from-a-given-file-for-one-branch