How do you fix a bad merge, and replay your good commits onto a fixed merge?

后端 未结 12 892
时光取名叫无心
时光取名叫无心 2020-11-21 10:25

I accidentally committed an unwanted file (filename.orig while resolving a merge) to my repository several commits ago, without me noticing it until now. I want

相关标签:
12条回答
  • 2020-11-21 10:28

    If it's the latest commit you want to clean up, I tried with git version 2.14.3 (Apple Git-98):

    touch empty
    git init
    git add empty
    git commit -m init
    
    # 92K   .git
    du -hs .git
    
    dd if=/dev/random of=./random bs=1m count=5
    git add random
    git commit -m mistake
    
    # 5.1M  .git
    du -hs .git
    
    git reset --hard HEAD^
    git reflog expire --expire=now --all
    git gc --prune=now
    
    # 92K   .git
    du -hs .git
    
    0 讨论(0)
  • 2020-11-21 10:28

    You can also use:

    git reset HEAD file/path

    0 讨论(0)
  • 2020-11-21 10:31

    Definitely, git filter-branch is the way to go.

    Sadly, this will not suffice to completely remove filename.orig from your repo, as it can be still be referenced by tags, reflog entries, remotes and so on.

    I recommend removing all these references as well, and then calling the garbage collector. You can use the git forget-blob script from this website to do all this in one step.

    git forget-blob filename.orig

    0 讨论(0)
  • 2020-11-21 10:34

    Please don't use this recipe if your situation is not the one described in the question. This recipe is for fixing a bad merge, and replaying your good commits onto a fixed merge.

    Although filter-branch will do what you want, it is quite a complex command and I would probably choose to do this with git rebase. It's probably a personal preference. filter-branch can do it in a single, slightly more complex command, whereas the rebase solution is performing the equivalent logical operations one step at a time.

    Try the following recipe:

    # create and check out a temporary branch at the location of the bad merge
    git checkout -b tmpfix <sha1-of-merge>
    
    # remove the incorrectly added file
    git rm somefile.orig
    
    # commit the amended merge
    git commit --amend
    
    # go back to the master branch
    git checkout master
    
    # replant the master branch onto the corrected merge
    git rebase tmpfix
    
    # delete the temporary branch
    git branch -d tmpfix
    

    (Note that you don't actually need a temporary branch, you can do this with a 'detached HEAD', but you need to take a note of the commit id generated by the git commit --amend step to supply to the git rebase command rather than using the temporary branch name.)

    0 讨论(0)
  • 2020-11-21 10:34

    If you haven't committed anything since, just git rm the file and git commit --amend.

    If you have

    git filter-branch \
    --index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD
    

    will go through each change from merge-point to HEAD, delete filename.orig and rewrite the change. Using --ignore-unmatch means the command won't fail if for some reason filename.orig is missing from a change. That's the recommended way from the Examples section in the git-filter-branch man page.

    Note for Windows users: The file path must use forward slashes

    0 讨论(0)
  • 2020-11-21 10:36

    Rewriting Git history demands changing all the affected commit ids, and so everyone who's working on the project will need to delete their old copies of the repo, and do a fresh clone after you've cleaned the history. The more people it inconveniences, the more you need a good reason to do it - your superfluous file isn't really causing a problem, but if only you are working on the project, you might as well clean up the Git history if you want to!

    To make it as easy as possible, I'd recommend using the BFG Repo-Cleaner, a simpler, faster alternative to git-filter-branch specifically designed for removing files from Git history. One way in which it makes your life easier here is that it actually handles all refs by default (all tags, branches, etc) but it's also 10 - 50x faster.

    You should carefully follow the steps here: http://rtyley.github.com/bfg-repo-cleaner/#usage - but the core bit is just this: download the BFG jar (requires Java 6 or above) and run this command:

    $ java -jar bfg.jar --delete-files filename.orig my-repo.git
    

    Your entire repository history will be scanned, and any file named filename.orig (that's not in your latest commit) will be removed. This is considerably easier than using git-filter-branch to do the same thing!

    Full disclosure: I'm the author of the BFG Repo-Cleaner.

    0 讨论(0)
提交回复
热议问题