1

I need to rewrite a branch, discarding all changes made to a specific subfolder (e.g. whose path is path/to/folder) and its contents between a range of revisions starting at a specified start revision and ending at HEAD. Changes prior to the start revision are accepted.

I've considered using git-filter-branch to do this, but don't know what filter to use. I think using 'git-reset' to the subfolder for each commit might work, but this post has me questioning whether this approach will work.

Question: How do I rewrite the branch as specified in the first paragraph above?

Community
  • 1
  • 1
bright
  • 4,700
  • 1
  • 34
  • 59

2 Answers2

2

Use git-filter-branch with --tree-filter, probably with --prune-empty. The whole command sequence could be like this:

GOOD_STATE=<some-revision>
DIR_TO_PRESERVE="relative/path/to/directory"
BRANCH_TO_REWRITE=<branch-to-rewrite>
export GOOD_STATE DIR_TO_PRESERVE BRANCH_TO_REWRITE
git filter-branch --tree-filter \
    'rm -rf "${DIR_TO_PRESERVE}" && git checkout -f ${GOOD_STATE} -- "${DIR_TO_PRESERVE}"' \
    --prune-empty ${BRANCH_TO_REWRITE} --not ${GOOD_STATE}

As a faster alternative --index-filter can be used. But as a command you shouldn't use git rm... because this would remove the path completely. Instead you should reset the index for a given folder using git reset:

git filter-branch --index-filter \
    'git reset -q ${GOOD_STATE} -- "${DIR_TO_PRESERVE}"'' \
    --prune-empty ${BRANCH_TO_REWRITE} --not ${GOOD_STATE}

To speed up things one could temporarily copy the repository to a faster filesystem (I used tmpfs on Linux for that, and 9007 commits have been rewritten using --index-filter in 14 min 37 seconds).

user3159253
  • 16,836
  • 3
  • 30
  • 56
  • Hmm - I tried to use --index-filter with the same command on a trivial test repo and it seemed to work. However on the actual repo with thousands of commits, it does not. Could you please provide a full command line to use with --index-filter? Also, some explanation of what is going on would be great. I don't quite understand how it does what it does. – bright Dec 02 '15 at 15:39
0

What eventually worked was using git-filter-branch with a combination of git rm to remove new files added in commits and 'git checkout' to restore the state.

git filter-branch -f --prune-empty --index-filter 'git rm -rfq -- path/to/subdir && git checkout -f <the-good-state-sha1> -- path/to/subdir' -- start..HEAD

Here start is the starting commit and <the-good-state-sha1> is the hash of start's parent commit. Note: path/to/subdir needs to be specified twice as shown, once in the rm command and once in the checkout command.

bright
  • 4,700
  • 1
  • 34
  • 59