Как да коригирате лошо сливане и да възпроизведете вашите добри ангажименти към фиксирано сливане?

Случайно поправих нежелан файл ( filename.orig при сливане разрешен) в моето хранилище няколко отстъпки, но все още не съм забелязал това. Искам да премахна напълно файла от историята на хранилището.

Възможно ли е да се пренапише историята на промените, така че filename.orig никога да не се добавя към хранилището?

385
21 нояб. определени от Грант Лимберг 21 ноември. 2008-11-21 07:11 '08 в 7:11 am 2008-11-21 07:11
@ 13 отговора

Моля, не използвайте тази рецепта, ако ситуацията не е описана в въпроса. Тази рецепта е предназначена да коригира лошо сливане и да повтори вашите добри съобщения до фиксирано сливане.

Въпреки че filter-branch ще направи това, което искате, това е доста сложна команда и вероятно ще реша да го направя с git rebase . Това вероятно е лично предпочитание. filter-branch може да направи това с една, малко по-сложна команда, докато решението за rebase извършва еквивалентни логически операции една стъпка в даден момент.

Опитайте следната рецепта:

288
21 нояб. отговорът е даден CB Bailey 21 ноември. 2008-11-21 16:02 '08 в 16:02 часа 2008-11-21 16:02

Въведение: Имате 5 налични решения.

Оригиналният плакат гласи:

Случайно направих нежелан файл ... в моето хранилище няколко коммити обратно ... Искам напълно да премахна файла от историята на хранилището.

Възможно ли е да се пренапише историята на промените, така че filename.orig никога да не бъде добавена в хранилището?

Има много начини да изтриете цялата история на файловете от git:

  • Промяната се поема.
  • Твърди капки (вероятно плюс ребаза).
  • Неинтерактивно рестартиране.
  • Интерактивни пермутации.
  • Филтрация на клони.

В случая с оригиналния плакат фиксирането на поемане не е само по себе си опция, тъй като след това той прави няколко допълнителни точки, но за пълнота ще обясня и как да го направя за всеки, който просто иска да промени предишната си фиксация.

Имайте предвид, че всички тези решения включват промяна / пренаписване на история / фиксиране в един случай на друг, така че някой със стари копия на ангажименти ще трябва да направи допълнителна работа, за да ресинхронизира своята история с новата история.


Решение 1: Промяната се поема

Ако случайно сте направили промени (например добавяне на файл) към предишен ангажимент и не искате историята на тази промяна да съществува вече, можете просто да промените предишния ангажимент, за да изтриете файла от него:

 git rm <file> git commit --amend --no-edit 

Решение 2: Твърдо нулиране (вероятно плюс ребаза)

Като решение # 1, ако просто искате да се отървете от предишния си ангажимент, тогава също имате възможност просто да направите хардуерно нулиране за родителя си:

 git reset --hard HEAD^ 

Тази команда ще бъде по-трудна - нулирайте клона си до предишния низ от родителски стълб, за да го извършите.

Въпреки това , ако, както и оригиналния плакат, сте направили няколко корекции след фиксиране, които искате да отмените, все още можете да използвате твърди капки, за да го промените, но това също е свързано с използването на rebase. Ето стъпките, които можете да използвате, за да промените ангажимента в историята:

 # 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: Интерактивни възстановявания

Това решение ще ви позволи да изпълнявате същите задачи като решения # 2 и # 3, т.е. Промяната или изтриването е завършена повече в историята, отколкото предишното ви поемане, така че решението, което решите да използвате, зависи от вас. Интерактивните пермутации не са много подходящи за презареждане на стотици коммити, за изпълнение, така че аз бих използвал неинтерактивни преинсталации или филтър (виж по-долу) в такива ситуации.

За да стартирате интерактивно рестартиране, използвайте следното:

border=0
 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" с "edit" на ред 1 st например:

На този етап можете да изтриете файла и да промените фиксацията, след което да продължите да се премествате:

 git rm <file> git commit --amend --no-edit git rebase --continue 

Какво е това? Като последна стъпка, независимо дали сте променили фиксацията или сте я изтрили напълно, винаги е полезно да проверите дали не са направени други неочаквани промени във вашия клон, ако сте променили състоянието му преди рестартирането:

 git diff master@{1} 

Решение 5: Филтрация на клони

И накрая, това решение е най-добре, ако искате напълно да унищожите всички следи от съществуването на файл от историята и нито едно от другите решения не е подходящо за задачата.

 git filter-branch --index-filter \ 'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD 

Отново, след завършване на filter-branch , обикновено се препоръчва да проверите дали няма други неочаквани промени, ако разделите клона си от предишното си състояние преди операцията за филтриране:

 git diff master@{1} 

Алтернативен филтър Филтър: Почистващо средство за репо BFG

Чух, че инструментът BFG Repo Cleaner е по-бърз от git filter-branch , така че можете да проверите това като опция. Той дори беше официално споменат във филтърната документация като жизнеспособна алтернатива:

git -filter-branch ви позволява да създавате сложни презаписваеми скриптове за вашата git история, но вероятно нямате нужда от тази гъвкавост, ако просто изтриете ненужни данни, като големи файлове или пароли. За тези операции, можете да разгледате BFG Repo-Cleaner , основаващ се на JVM алтернатива на Git филтри, обикновено поне 10-50 пъти по-бързо за тези случаи на употреба и с напълно различни характеристики:

  • Всяка конкретна версия на файл се почиства точно веднъж. BFG, за разлика от git -filter-branch, не ви позволява да обработвате файл по различен начин в зависимост от това къде и кога е бил направен във вашата история. Това ограничение осигурява основното предимство на BFG и е подходящо за почистване на лоши данни - не сте там, където са лошите данни, просто искате да изчезне.

  • По подразбиране BFG се възползва изцяло от многоядрените машини, като почиства файловете паралелно. Гит-филтърният клон се изчиства последователно (т.е. в режим с една нишка), въпреки че е възможно да се пишат филтри, които включват свой собствен паралелизъм, в скриптове, изпълнени срещу всяко поправка.

  • Параметрите са по-ограничителни от клона на git-филтъра и са предназначени само за премахване на нежелани данни, например: --strip-blobs-bigger-than 1M .

Допълнителни ресурси

197
21 апр. Отговор от user456814 Apr 21 2014-04-21 02:10 '14 в 2:10 2014-04-21 02:10

Ако не сте извършили нищо, просто git rm файла и git commit --amend .

Ако имате

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

ще премине всяка промяна от merge-point на merge-point към HEAD , изтриване на име на файл.или и пренаписване на промяната. Използването --ignore-unmatch означава, че командата не се проваля, ако поради някаква причина filename.orig липсва в резултат на промяната. Това се препоръчва от раздела "Примери" на страницата на git-филтър-клон .

Забележка за потребителите на Windows: пътят на файла трябва да използва наклонени черти

117
14 марта '09 в 23:44 2009-03-14 23:44 отговорът е даден от Schwern на 14 март 2009 г. в 23:44 2009-03-14 23:44

Това е най-добрият начин:
http://github.com/guides/completely-remove-a-file-from-all-revisions

Просто не забравяйте да архивирате файловете.

EDIT

Редактирането на Neon, за съжаление, беше отхвърлено по време на прегледа.
Вижте публикацията на Neons по-долу, тя може да съдържа полезна информация!


Например, за да изтриете всички *.gz файлове случайно *.gz в хранилището на git:

 $ du -sh .git ==> eg 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)

 $ du -sh .git ==> eg 100M 

Не знам защо, тъй като имах само един главен клон. Във всеки случай, най-накрая получих чист git хранилище, като пуснах нов празен и празен git хранилище, например

 $ git init --bare /path/to/newcleanrepo.git $ git push /path/to/newcleanrepo.git master $ du -sh /path/to/newcleanrepo.git ==> eg 5M 

(Да!)

След това го клонирам в нова директория и премествам папката git към тази. например

 $ mv .git ../large_dot_git $ git clone /path/to/newcleanrepo.git ../tmpdir $ mv ../tmpdir/.git . $ du -sh .git ==> eg 5M 

(Да! Накрая почистете!)

След като се уверите, че всичко е наред, можете да изтриете ../large_dot_git и ../tmpdir (може би за няколко седмици или месец, само за всеки случай ...)

47
04 февр. Отговорът е даден от Дарън на 04 февруари. 2010-02-04 08:52 '10 в 8:52 2010-02-04 08:52

Пренаписването на историята на Git изисква промяна на всички засегнати идентификационни номера за корекции, така че всеки, който работи по проекта, ще трябва да премахне старите си копия и да създаде нов клон, след като сте изчистили историята. Колкото повече хора се чувстват неудобно, толкова повече трябва да направите това - допълнителният ви файл не създава проблеми, но ако работите само върху проект, можете да изчистите историята на Git, ако желаете!

За да го направите възможно най-лесно, препоръчвам да използвате BFG Repo-Cleaner , по-бърза и по-бърза алтернатива на git-filter-branch специално създадена за премахване на файлове от историята на Git. Един от начините да направите живота си по-лесен е, че той всъщност обработва всички връзки по подразбиране (всички тагове, клонове и т.н.), но също така и 10 - 50 пъти по-бързо.

Трябва внимателно да следвате стъпките тук: http://rtyley.github.com/bfg-repo-cleaner/#usage - но основният бит е прост: изтеглете буркана BFG (необходим е Java 6 или по-висок) и изпълнете следната команда:

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

Цялата ви история на хранилището ще бъде сканирана и всеки файл с име filename.orig (който не е в последното фиксиране ) ще бъде изтрит. Това е много по-лесно, отколкото да използвате git-filter-branch да направите същото!

Пълно разкриване: Аз съм автор на BFG Repo-Cleaner.

25
31 марта '13 в 15:35 2013-03-31 15:35 отговорът е даден от Роберто Тили на 31 март '13 в 15:35 2013-03-31 15:35
 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 
12
10 июня '16 в 9:35 2016-06-10 09:35 Отговорът е даден paulalexandru Юни 10 '16 в 9:35 2016-06-10 09:35

Най-лесният начин, който открих, беше предложен от leontalbot (като коментар), който е публикуван от Anoopjohn . Мисля, че си струва своето място като отговор:

(Превърнах го в bash скрипт)

 #!/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 да го посочат.

забележка

Имайте предвид, че скриптът не включва проверки, затова се уверете, че не грешите и имате резервно копие, ако нещо се обърка. Тя работи за мен, но може да не работи в твоята ситуация. ИЗПОЛЗВАЙТЕ ТОВА С ПРЕДУПРЕЖДЕНИЕ (следвайте връзката, ако искате да знаете какво се случва).

4
17 мая '16 в 5:26 2016-05-17 05:26 отговор, даден от lepe 17 май '16 в 5:26 2016-05-17 05:26

За да добавите това към решението на Чарлз Бейли, аз просто използвах git rebase -i, за да премахна нежеланите файлове от по-ранен коммит, и той работи като чар. стъпки:

 # 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 
4
16 окт. Отговор, даден от Sverrir Sigmundarson 16 октомври 2013-10-16 16:10 '13 в 16:10 2013-10-16 16:10

Определено, git filter-branch е начинът да се върви.

За съжаление, това не е достатъчно, за да премахнете изцяло filename.orig от Вашето РЕПО, тъй като тя все още може да бъде посочена от тагове, рефлог записи, конзоли и т.н.

Препоръчвам да премахнете всички тези връзки и след това да се обадите на колектора за боклук. Можете да използвате скрипта git forget-blob от този уебсайт, за да направите всичко в една стъпка.

git forget-blob filename.orig

3
30 янв. Отговорът е даден от nachoparker Jan 30 2017-01-30 15:54 '17 в 15:54 2017-01-30 15:54

Ако това е последната заявка, която искате да изчистите, се опитах да използвам git версия 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 
1
29 марта '18 в 18:40 2018-03-29 18:40 Отговорено от Кларк на 29 март 18 18:40 ч. 2018-03-29 18:40

трябва да използвате разклонението на филтъра git, представете си, че искате да премахнете всички целеви / DIRS в предишните ви ангажименти, можете да изпълните само 4 команди за това:

 $ git filter-branch --tree-filter 'rm -f */target' HEAD $ rm -rf */target (you use this one to delete target/ dirs on current HEAD) $ git commit -am 'delete all target/ dirs' --allow-empty $ git push 
0
10 дек. Отговорът е даден от Walterwhites 10 декември. 2018-12-10 21:41 '18 в 21:41 2018-12-10 21:41

За това е предназначен git filter-branch .

0
21 нояб. отговорът е даден CesarB 21 ноември. 2008-11-21 13:26 '08 в 13:26 2008-11-21 13:26

Можете също да използвате:

git reset HEAD file/path

-1
03 сент. отговорът е даден paolo granada lim 03 Sep. 2009-09-03 07:00 '09 в 7:00 2009-09-03 07:00

Други въпроси относно тагове или Задай въпрос