Jak naprawić złe połączenie i odtworzyć dobre commity na stałe połączenie?

Przypadkowo popełniłem niechciany plik (filename.orig podczas rozwiązywania scalania) do mojego repozytorium kilka commitów temu, aż do teraz nie zauważyłem tego. Chcę całkowicie usunąć plik z historii repozytorium.

Czy możliwe jest przepisanie historii zmian w taki sposób, że filename.orig w ogóle nie została dodana do repozytorium?

Author: Trevor Boyd Smith, 2008-11-21

12 answers

Proszę nie używać tego przepisu, jeśli Twoja sytuacja nie jest opisana w pytaniu. Ten przepis polega na naprawieniu złego scalenia i odtworzeniu twoich dobrych commitów na stałe scalenie.

Chociaż filter-branch zrobi to, co chcesz, jest to dość skomplikowane polecenie i prawdopodobnie wybrałbym to za pomocą git rebase. To pewnie osobiste preferencje. filter-branch może to zrobić pojedynczym, nieco bardziej złożonym poleceniem, podczas gdy rozwiązanie rebase wykonuje równoważny logiczny operacje krok po kroku.

Wypróbuj następujący przepis:

# 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

(zauważ, że w rzeczywistości nie potrzebujesz tymczasowej gałęzi, możesz to zrobić z 'odłączoną głową' , ale musisz zanotować identyfikator zatwierdzenia wygenerowany przez krok git commit --amend, aby podać komendę git rebase zamiast używać tymczasowej nazwy gałęzi.)

 286
Author: CB Bailey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-04 00:20:19

Intro: Masz 5 Rozwiązań Dostępnych

Oryginalny plakat mówi:

Przypadkowo popełniłem niechcianą file...to moje repozytorium kilka commitów temu...Chcę całkowicie usunąć plik z historii repozytorium.

Czy to możliwość przepisania historii zmian w taki sposób, że filename.orig nigdy dodany do repozytorium?

Istnieje wiele różnych sposobów na całkowite usunięcie historii pliku od git:

  1. Zmiana commitów.
  2. twarde resety (ewentualnie Plus rebase).
  3. Nieinteraktywny rebase.
  4. interaktywne rebasy.
  5. filtrowanie gałęzi.

W przypadku oryginalnego plakatu, zmiana commita nie wchodzi w grę sam w sobie, ponieważ później dokonał kilku dodatkowych commitów, ale dla dobra kompletności, wyjaśnię również, jak to zrobić, dla każdego, kto justs chce zmienić swoje poprzednie / align = "left" /

Zauważ, że wszystkie te rozwiązania wymagają zmiany / ponownego zapisu historii / zatwierdzeń w jeden sposób inny, więc każdy ze starymi kopiami commitów będzie musiał zrobić dodatkowa praca, aby zsynchronizować ich historię z nową historią.


Rozwiązanie 1: Zmiana Commitów

W przypadku przypadkowej zmiany (np. dodania pliku) w poprzednim commit, a nie chcesz, aby historia tej zmiany już istniała, to możesz po prostu zmienić poprzedni commit do usunięcia pliku z niego:

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

Rozwiązanie 2: twardy Reset (ewentualnie Plus Rebase)

Jak rozwiązanie # 1, jeśli chcesz pozbyć się poprzedniego commita, to ma również możliwość po prostu wykonania twardego resetu do rodzica: {]}

git reset --hard HEAD^

To polecenie zresetuje Twoją gałąź do poprzedniego 1 St rodzica / align = "left" /

jednakże, jeśli, podobnie jak oryginalny plakat, wykonałeś kilka commitów po na commit, do którego chcesz cofnąć zmianę, nadal możesz użyć twardych resetów zmodyfikować go, ale wymaga to również użycia rebase. Oto kroki, które możesz użyć, aby zmienić commit w historii:

# 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 <old-commit> master

# Verify your changes
git diff master@{1}

Rozwiązanie 3: Nieinteraktywna Rebase

To zadziała, jeśli chcesz całkowicie usunąć commit z historii:

# 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}

Rozwiązanie 4: Interaktywne Rebasy

To rozwiązanie pozwoli Ci osiągnąć te same rzeczy, co rozwiązania #2 i # 3, tj. zmodyfikować lub usunąć commity w historii dalej niż bezpośrednio poprzedni commit, więc to, które rozwiązanie wybierzesz, zależy od Ciebie. Interaktywne rebases nie są dobrze dostosowane do rebasingu setek commitów, dla względy wydajnościowe, więc użyłbym nieinteraktywnych rebases lub gałęzi filtra rozwiązanie (patrz niżej) w tego typu sytuacjach.

Aby rozpocząć interaktywną rebase, użyj następującej opcji:

git rebase --interactive <commit-to-amend-or-remove>~

# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~

Spowoduje to, że Git cofnie commit historia powrót do rodzica Zatwierdź, które chcesz zmodyfikować lub usunąć. Następnie przedstawi Ci listę przewijanie commitów w odwrotnej kolejności w dowolnym edytorze git (jest to Vim domyślnie):

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)

Commit, który chcesz zmodyfikować lub usunąć, będzie na górze tej listy. Aby go usunąć, po prostu usuń jego wiersz z listy. W przeciwnym razie zamień "pick" na "edytuj" na linii 1 st , jak tak:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

Następnie wpisz git rebase --continue. Jeśli zdecydujesz się usunąć / align = "left" / , następnie, że to wszystko, co musisz zrobić (poza weryfikacją, patrz ostatni krok dla tego rozwiązania). Jeśli natomiast chcesz zmodyfikować commit, to git ponownie zastosuje commit, a następnie wstrzyma rebase.

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

W tym momencie możesz usunąć plik i zmienić commit, a następnie kontynuować rebase:

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

To jest to. Jako ostatni krok, niezależnie od tego, czy zmodyfikowałeś commit, czy go usunąłeś całkowicie, to zawsze dobry pomysł, aby sprawdzić, że żaden inny nieoczekiwane zmiany w 1996 roku został wybrany do Izby Gmin jako kandydat na prezydenta RP.]}

git diff master@{1}

Rozwiązanie 5: Filtrowanie Gałęzi

Wreszcie, To rozwiązanie jest najlepsze, jeśli chcesz całkowicie wymazać wszystkie ślady istnienia pliku z historii, a żadne z innych rozwiązań nie zadanie.

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'

Usunie <file> ze wszystkich commitów, zaczynając od głównego commita. Jeśli zamiast tego po prostu chcesz przepisać zakres commitów HEAD~5..HEAD, a następnie can przekaż to jako dodatkowy argument filter-branch, jak wskazano w ta odpowiedź :

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

Ponownie, po zakończeniu filter-branch, zwykle dobrze jest zweryfikować że nie ma innych nieoczekiwanych zmian, różnicując swoją gałąź z jej poprzedni stan przed operacją filtrowania:

git diff master@{1}

Filter-Branch alternatywa: BFG Repo Cleaner

Słyszałem, że narzędzie BFG Repo Cleaner działa szybciej niż git filter-branch, więc możesz chcieć Zobacz też To jest nawet oficjalnie wymienione w dokumentacja filter-branch jako realną alternatywę:

Git-filter-branch pozwala na tworzenie złożonych skryptów skryptowych twojej historii Gita, ale prawdopodobnie nie potrzebujesz tej elastyczności, jeśli po prostu usuwasz niechciane dane , takie jak duże pliki lub hasła. Dla tych operacji warto rozważyć BFG Repo-Cleaner , oparty na JVM alternatywa dla git-filter-branch, zazwyczaj co najmniej 10-50x szybsza dla te przypadki użycia i o zupełnie innych cechach:

  • Każda konkretna wersja pliku jest czyszczona dokładnie raz . BFG, w przeciwieństwie do git-filter-branch, nie daje możliwości obsługi plik inaczej w zależności od tego, gdzie lub kiedy został popełniony w Twoim historia. To ograniczenie daje podstawową korzyść z wydajności BFG, i doskonale nadaje się do zadania czyszczenie złych danych-nie dbać gdzie złe dane są, po prostu chcesz, aby zniknął.

  • Domyślnie BFG w pełni wykorzystuje maszyny wielordzeniowe, czyszcząc drzewa plików commitów równolegle. git-filter-branch cleans w związku z tym, że nie jest to możliwe, nie jest to możliwe.]} możliwość zapisu filtrów zawierających własną paralelizm, w Skrypty wykonywane przed każdym zatwierdzeniem.

  • Na opcje poleceń są bardzo bardziej restrykcyjny niż Git-filter branch, i dedykowany tylko do zadania usuwania niechcianych danych-np.: --strip-blobs-bigger-than 1M.

Dodatkowe Zasoby

  1. Pro Git § 6.4 Narzędzia Git-Przepisywanie Historii .
  2. git-filter-branch (1) strona podręcznika .
  3. strona podręcznika git-commit(1) .
  4. git-reset (1) strona podręcznika .
  5. git-rebase (1) Strona Podręcznika .
  6. BFG repo Cleaner (Zobacz także tę odpowiedź od samego twórcy ).
 194
Author: Community,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:26

Jeśli od tego czasu nic nie popełniłeś, po prostu git rm plik i git commit --amend.

Jeśli masz

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

Przejdzie przez każdą zmianę z merge-point na HEAD, usunie nazwę pliku./ align = "left" / Użycie --ignore-unmatch oznacza, że polecenie nie zawiedzie, jeśli z jakiegoś powodu nazwa pliku./ align = "left" / Jest to zalecany sposób z sekcji przykłady na stronie podręcznika git-filter-branch .

Uwaga dla użytkowników Windows: ścieżka do pliku musi używać forward ukośniki

 117
Author: Schwern,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-06 23:29:09

This is the best way:
http://github.com/guides/completely-remove-a-file-from-all-revisions

Upewnij się, że najpierw wykonasz kopię zapasową plików.

EDIT

[[7]}edycja autorstwa Neon została niestety odrzucona podczas recenzji.
Zobacz post Neons poniżej, może zawierać przydatne informacje!

Np. usunięcie wszystkich *.gz plików przypadkowo wprowadzonych do repozytorium git:

$ 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

To nadal nie działa dla ja? (Jestem obecnie w wersji git 1.7.6.1)

$ du -sh .git ==> e.g. 100M
Nie wiem dlaczego, bo miałem tylko jedną gałąź. Tak czy inaczej, w końcu mój Git repo został naprawdę wyczyszczony przez wciśnięcie do nowego pustego i gołego repozytorium git, np.
$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(tak!)

Następnie sklonowałem to do nowego katalogu i przeniosłem go .git folder do tego. np.

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

(tak! w końcu posprzątane!)

Po sprawdzeniu, że wszystko jest dobrze, możesz usunąć ../large_dot_git i ../tmpdir katalogi (może za kilka tygodni lub miesięcy, na wszelki wypadek...)

 47
Author: Darren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:46

Przepisanie historii Git wymaga zmiany wszystkich identyfikatorów zmian, więc każdy, kto pracuje nad projektem, będzie musiał usunąć swoje stare kopie repo i wykonać świeży klon po wyczyszczeniu historii. Im więcej osób to utrudnia, tym bardziej potrzebujesz dobrego powodu, aby to zrobić - twój zbędny plik tak naprawdę nie sprawia problemu, ale jeśli tylko ty pracujesz nad projektem, równie dobrze możesz wyczyścić historię Gita, jeśli chcesz!

Aby uczynić go jak jak to tylko możliwe, polecam użycie BFG Repo-Cleaner, prostszej i szybszej alternatywy dla git-filter-branch specjalnie zaprojektowanej do usuwania plików z historii Gita. Jednym ze sposobów, w jaki ułatwia Ci to życie, jest to, że domyślnie obsługuje Wszystkie refy (wszystkie tagi, gałęzie itp.), ale jest również 10 - 50x szybciej.

Należy uważnie postępować zgodnie z instrukcjami tutaj: http://rtyley.github.com/bfg-repo-cleaner/#usage - ale rdzeń jest właśnie taki: Pobierz BFG jar (wymaga Javy 6 lub nowszej) i uruchom następujące polecenie:

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

Cała historia repozytorium zostanie zeskanowana, a każdy plik o nazwie filename.orig (którego nie ma w Twoim najnowszy commit ) zostanie usunięty. Jest to znacznie łatwiejsze niż użycie git-filter-branch do zrobienia tego samego!

pełne ujawnienie: jestem autorem BFG Repo-Cleaner.

 25
Author: Roberto Tyley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-11 06:40:47
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
Author: paulalexandru,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-10 07:00:30

Aby dodać, że do rozwiązania Charlesa Baileya, użyłem git rebase-i do usunięcia niechcianych plików z wcześniejszego commita i zadziałało jak urok. Kroki:

# 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
Author: Sverrir Sigmundarson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-16 13:10:21

Najprostszy sposób, jaki znalazłem, zasugerował leontalbot (jako komentarz), czyli post opublikowany przez Anoopjohn . Myślę, że warto mieć własną przestrzeń jako odpowiedź:

(przekonwertowałem go na skrypt 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."

Wszystkie napisy trafiają do Annopjohn, i do leontalbot za wskazanie.

Uwaga

Pamiętaj, że skrypt nie zawiera walidacji, więc upewnij się, że nie popełniasz błędów i że masz kopię zapasową na wypadek, gdyby coś poszło nie tak. U mnie zadziałało, ale to może nie zadziałać w twojej sytuacji. Używaj go ostrożnie(kliknij link, jeśli chcesz wiedzieć, co się dzieje).

 4
Author: lepe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 02:31:20

Zdecydowanie, git filter-branch jest droga do zrobienia.

Niestety, to nie wystarczy, aby całkowicie usunąć filename.orig z repo, ponieważ nadal może być odwołany przez tagi, wpisy reflog, piloty i tak dalej.

Zalecam usunięcie wszystkich tych odniesień, a następnie wywołanie garbage collector. Możesz użyć skryptu git forget-blob z tej strony internetowej, aby zrobić to wszystko w jednym kroku.

git forget-blob filename.orig

 3
Author: nachoparker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-30 12:54:26

Jeśli jest to najnowszy commit, który chcesz wyczyścić, próbowałem z git w wersji 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
Author: Clark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-29 15:40:36

This is what git filter-branch został zaprojektowany dla.

 0
Author: CesarB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 10:26:19

Możesz również użyć:

git reset HEAD file/path

 -1
Author: paolo granada lim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-31 16:44:30