How do you fix a bad merge, and replay your good commits onto a fixed merge?
几次提交之前,我不小心将一个不需要的文件(
是否有可能重写更改历史,使
如果您的情况不是问题中描述的情况,请不要使用此配方。这个方法用于修复一个错误的合并,并将您的好提交重放到一个固定的合并中。
虽然
尝试以下配方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 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 |
(请注意,您实际上不需要临时分支,您可以使用"分离的头"执行此操作,但您需要记录由
Intro:you have five solutions available
The original poster states:
okay.
I accidentally committed an unwanted file...to my repository several commits
ago...I want to completely delete the file from the repository history.Ok.
Is it
possible to rewrite the change history such thatfilename.orig was never
added to the repository in the first place?Ok.
有很多不同的方法可以从一个完整的文件Git:
okay.
在原始邮寄的情况下,修改邮寄并不是一个真正的选择。自从他在事后作出了许多额外的承诺,但为了喝酒。从完整的角度看,我也会解释如何做,为了任何人想改变他们以前的习惯。
okay.
Note that all of these solutions involve altering/re-writing history/commits在另一个方面,任何人都必须用旧的信件副本去做。为了使他们的故事与新的故事重新同步,做了一些额外的工作。
okay.解决办法1:修正承诺
如果你意外改变(如添加文件)但是,你不需要任何改变的故事你可以简单地修改前面的承诺,从下面删除文件:
okay.
1 2 | git rm <file> git commit --amend --no-edit |
解决方案2:硬复位
就象解决方案,如果你只是想像以前一样爬上去,那么你此外,还有一个简单的选择,对其父母来说是一个艰难的复苏:
okay.ZZU1
指挥官将很难把你的分支重新分配给第一位亲戚Commit.
okay.
但是,如果,像原始的海报一样,你在那之后作出了许多承诺。你想要的改变,你仍然可以用硬的复制改变它,但也要用反弹动作。这是那一步你可以用这个来改变历史的另一个方面:
okay.
1 2 3 4 5 6 7 8 9 10 11 12 | # Create a new branch at the commit you want to amend git checkout -b temp <commit> # Amend the commit git rm <file> git commit --amend --no-edit # Rebase your previous branch onto this new commit, starting from the old-commit git rebase --preserve-merges --onto temp master # Verify your changes git diff master@{1} |
解决方案3:非互动性再生
如果你只是想从历史上清除一个错误
okay.
1 2 3 4 5 6 7 8 9 10 11 | # Create a new branch at the parent-commit of the commit that you want to remove git branch temp <parent-commit> # Rebase onto the parent-commit, starting from the commit-to-remove git rebase --preserve-merges --onto temp <commit-to-remove> master # Or use `-p` insteda of the longer `--preserve-merges` git rebase -p --onto temp <commit-to-remove> master # Verify your changes git diff master@{1} |
解决办法4:交互式反叛
这个解决方案会让你完成同样的事情作为解决方案 353,3,I.E.Modify or remove commits further back in history than your immediately如前所述,所以你选择使用的解决方案是属于你的。交互式反叛并不是一个很好的方法来重建委员会性能原因,所以我会使用非交互反弹或过滤器分支Solution(see below)in those order of situations.
okay.
To begin the interactive rebase,use the following:
okay.
1 2 3 4 | git rebase --interactive <commit-to-amend-or-remove>~ # Or `-i` instead of the longer `--interactive` git rebase -i <commit-to-amend-or-remove>~ |
这将导致Git将提交历史记录倒回到提交要修改或删除的内容。然后它会给你一份以设置为使用的编辑器git的相反顺序重绕提交(这是VIM默认值):好的。
1 2 3 4 5 | pick 00ddaac Add symlinks for executables pick 03fa071 Set `push.default` to `simple` pick 7668f34 Modify Bash config to use Homebrew recommended PATH pick 475593a Add global .gitignore file for OS X pick 1b7f496 Add alias for Dr Java to Bash config (OS X) |
号
要修改或删除的提交将在此列表的顶部。要删除它,只需删除列表中的行。否则,将"pick"替换为第一行的"编辑",如下所示:好的。
1 2 | edit 00ddaac Add symlinks for executables pick 03fa071 Set `push.default` to `simple` |
接下来,输入
1 2 3 4 5 6 7 8 | Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue |
。
此时,您可以删除该文件并修改提交,然后继续重新平衡:好的。
1 2 3 | git rm <file> git commit --amend --no-edit git rebase --continue |
就这样。作为最后一步,无论您是修改了提交还是删除了它总的来说,最好是确认没有其他意外的变化是通过将其与钢筋前的状态进行比较而生成的:好的。
1 | git diff master@{1} |
。解决方案5:过滤分支
最后,如果您想彻底清除一个文件从历史上就存在了,其他的解决方案都没有达到任务。好的。
1 2 | git filter-branch --index-filter \ 'git rm --cached --ignore-unmatch <file>' |
。
这将从根提交开始,从所有提交中删除
1 2 | git filter-branch --index-filter \ 'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD |
同样,在
1 | git diff master@{1} |
。过滤支路备选方案:BFG repo cleaner
我听说bfg repo cleaner工具比
Git过滤器分支允许您进行复杂的shell脚本重写你的Git历史,但如果您只需删除不需要的数据,如大文件或密码。对于这些操作,您可能需要考虑BFG基于JVM的repo cleaner替代Git过滤器分支,通常至少快10-50倍这些用例,具有非常不同的特性:好的。
文件的任何特定版本都将被完全清除一次。与git filter分支不同,bfg没有给您机会处理根据在您的历史。此约束使bfg,非常适合于清除坏数据的任务-您不需要不管坏数据在哪里,你只希望它消失。好的。
默认情况下,bfg充分利用多核机器,并行清理提交文件树。Git过滤器分支清洗按顺序提交(即以单线程方式),尽管可以在针对每个提交执行的脚本。好的。
命令选项很多比git filter分支更具限制性,并且只专注于删除不需要的数据的任务-例如:
--strip-blobs-bigger-than 1M 。好的。其他资源
Pro Git§;6.4 Git工具-重写历史记录。 Git过滤器分支(1)手册页。 Git提交(1)手册页。 Git重置(1)手册页。 Git Rebase(1)手册页。 bfg repo cleaner(另见创建者本人的回答)。 好啊。
如果你从那以后什么都没有做过,那就把文件交给
git rm 和git commit --amend 。如果你有
1
2 git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD号
将执行从
merge-point 到HEAD 的每个更改,删除filename.orig并重写更改。使用--ignore-unmatch 意味着,如果由于某种原因,更改中缺少了filename.orig,那么命令不会失败。这是git filter branch手册页示例部分中推荐的方法。Windows用户注意:文件路径必须使用正斜杠
这是最好的方法:http://github.com/guides/completely-remove-a-file-from-all-revisions
只需确保先备份文件的副本。
编辑
Neon的编辑在审查过程中不幸遭到拒绝。请参阅下面的Neons文章,它可能包含有用的信息!
例如,要删除所有意外提交到Git存储库的
*.gz 文件:
1
2
3
4
5
6
7 $ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now对我来说还是不管用?(我目前在Git 1.7.6.1版)
1 $ du -sh .git ==> e.g. 100M。
不知道为什么,因为我只有一个主分支。不管怎样,我最终通过推到一个新的空的、空的Git存储库(例如,Git repo)中,彻底清理了我的Git repo。
1
2
3 $ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M(是的!)
然后我克隆到一个新的目录,并将它的.git文件夹移到这个目录中。例如
1
2
3
4 $ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M。
(是的!终于打扫干净了!)
在确认一切正常后,您可以删除
../large_dot_git 和../tmpdir 目录(可能在几周或几个月后,以防万一…)重写Git历史记录需要更改所有受影响的提交ID,因此在项目中工作的每个人都需要删除他们以前的repo副本,并在清理历史记录后执行新的克隆。IT不便的人越多,你就越需要一个好的理由来做这件事——你多余的文件并不是真正造成问题的原因,但是如果你只是在做这个项目,如果你愿意的话,你最好清理一下Git的历史记录!
为了尽可能简单,我建议使用bfg repo cleaner,这是一种比
git-filter-branch 更简单、更快的替代方案,专门设计用于从git历史中删除文件。其中一个让您的生活更容易的方法是,它实际上在默认情况下处理所有引用(所有标记、分支等),但它也快10-50倍。您应该仔细执行以下步骤:http://rtyLy.GITHUB.COM/BFG RePoCuff/Suffe用法-但核心位仅此:下载BFG jar(需要Java 6或以上)并运行此命令:
1 $ java -jar bfg.jar --delete-files filename.orig my-repo.git。
您的整个存储库历史记录将被扫描,任何名为
filename.orig 的文件(您最近提交的文件中没有)将被删除。这比使用git-filter-branch 做同样的事情要容易得多!完全公开:我是bfg repo cleaner的作者。
1
2
3
4
5
6
7
8
9
10 You should probably clone your repository first.
Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all
Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD
Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all号
我发现最简单的方法是由
leontalbot 提出的(作为评论),这是anoopjohn发表的一篇文章。我认为它值得自己的空间作为答案:(我把它转换成了bash脚本)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 #!/bin/bash
if [[ $1 =="" ]]; then
echo"Usage: $0 FILE_OR_DIR [remote]";
echo"FILE_OR_DIR: the file or directory you want to remove from history"
echo"if 'remote' argument is set, it will also push to remote repository."
exit;
fi
FOLDERNAME_OR_FILENAME=$1;
#The important part starts here: ------------------------
git filter-branch -f --index-filter"git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
if [[ $2 =="remote" ]]; then
git push --all --force
fi
echo"Done."所有的信用证都交给
Annopjohn 和leontalbot 指出。注释
请注意,该脚本不包含验证,因此请确保您不会出错,并且在出现问题时有备份。它对我有用,但在你的情况下可能行不通。小心使用(如果您想知道发生了什么,请按照链接操作)。
为了添加到CharlesBailey的解决方案中,我只使用了一个git-rebase-i从以前的提交中删除不需要的文件,它工作起来很有魅力。步骤:
1
2
3
4
5
6
7
8
9
10
11 # Pick your commit with 'e'
$ git rebase -i
# Perform as many removes as necessary
$ git rm project/code/file.txt
# amend the commit
$ git commit --amend
# continue with rebase
$ git rebase --continue当然,江户十一〔三〕是一条必经之路。
遗憾的是,这不足以完全从您的repo中删除
filename.orig ,因为它仍然可以被标签、reflog条目、远程程序等引用。我建议删除所有这些引用,然后调用垃圾收集器。您可以使用本网站的
git forget-blob 脚本一步完成所有这些操作。埃多克斯1〔6〕
如果这是您要清理的最新提交,我尝试了Git版本2.14.3(Apple Git-98):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 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这就是
git filter-branch 的设计目的。您还可以使用:
埃多克斯1〔12〕