2008-10-15 22 views
23

Załóżmy, że masz:git: squash/fixup wcześniej popełnić

A-B-C 

Teraz twój build/test nie powiedzie się. Poprawka powinny zostać połączone w A. Moja obecna praca-flow jest tak:

$ git commit -m "fixA" 

A-B-C-fixA 

$ git rebase -i A~1 

i squash FIXA w A, spowodować:

A'-B-C 

Czy istnieje polecenie, aby zrobić coś takiego:

A-B-C + (index with fix for A) 

$ git commit -supperdupper A 

Wynik:

A'-B-C 
+1

Dlaczego takie dziwne wymaganie, że fixA musi zostać połączone w A? –

+9

ponieważ powinien on być w A w pierwszej kolejności. – elmarco

+4

Ja robię to samo; Nie jestem pewien, dlaczego ludzie myślą, że to dziwne. Jeśli próbujesz uporządkować commity w małe, logicznie pogrupowane elementy, naturalnie jest przygotowywać kilka niewydanych zatwierdzeń naraz. (Możesz nie wiedzieć, czy naprawdę skończyłeś z A, dopóki nie skończysz C). – andy

Odpowiedz

19

Jeśli szukasz łatwego rozwiązania do wcześniejszego zatwierdzenia, przeczytaj pytanie! Wyjaśnia to wszystko. Ale ponieważ Elmarco prosił o śliskiej drodze, jedziemy:

Jak Git 1.7.0 istnieje --autosquash rozwiązaniem dla rebase, który robi to, co chcesz. Dostępne są również opcje --fixup i --squash dla commit, aby ułatwić sobie pracę. Przy niektórych aliasingach możesz nawet uzyskać całość w jednym poleceniu.

Sugeruję aktualizację do najnowszej Git dla maksymalnej grozy:

git/Documentation/RelNotes $ grep -i -A1 autosquash\\\|fixup * 
1.7.0.txt: * "git rebase -i" learned new action "fixup" that squashes the change 
1.7.0.txt- but does not affect existing log message. 
-- 
1.7.0.txt: * "git rebase -i" also learned --autosquash option that is useful 
1.7.0.txt: together with the new "fixup" action. 
1.7.0.txt- 
-- 
1.7.3.txt: * "git rebase -i" peeks into rebase.autosquash configuration and acts as 
1.7.3.txt: if you gave --autosquash from the command line. 
1.7.3.txt- 
-- 
1.7.4.txt: * "git commit" learned --fixup and --squash options to help later invocation 
1.7.4.txt- of the interactive rebase. 
-- 
1.7.4.txt: * "git rebase --autosquash" can use SHA-1 object names to name which 
1.7.4.txt: commit to fix up (e.g. "fixup! e83c5163"). 
1.7.4.txt- 
+1

Dobry przykład: http://technosorcery.net/blog/2010/02/07/fun-with-the-upcoming-1-7-release-of-git-rebase---interactive---autosquash/ –

+0

To jest również odpowiednim blogiem http://netsplit.com/2012/02/06/git-smash/ – elmarco

+0

W jednym poleceniu: http://stackoverflow.com/a/13671819/1456578 – Deiwin

0

To, co robisz, jest niebezpieczne, jeśli dzielisz oddział, w którym dokonujesz zmian z innymi osobami. W twoim przypadku jesteś przepisywanie commit A i ponowne umieszczenie B i C na górze nowego A, który jest zupełnie nowym obiektem. Każdy, kto już wyciągnął starego A do swoich repozytoriów, może zepsuć swoje repozytorium, ponieważ historia w twoim repozytorium zmieni się "magicznie". Ich git nie mógłby wiedzieć, że historia została przepisana.

Z tego powodu twórcy Gita celowo nie zrobili tego łatwo, ponieważ trzeba zdawać sobie sprawę z konsekwencji takiej operacji.

+4

Jestem świadomy konsekwencji, nie chcę dzielić się tymi zobowiązaniami. – elmarco

+2

W rzeczywistości, ten drugi programista * wie, że historia się zmieniła. Format repozytorium Git gwarantuje, że nigdy nie "uszkodzisz" repozytorium w ten sposób. Problem jest bardziej subtelny, a git odbiorcy może wywołać masę niepotrzebnych konfliktów scalających. –

+0

Problem jest tutaj pokazany - http://www.kernel.org/pub/software/scm/git/docs/user-manual.html#problems-z-rewriting-history –

2

Jeśli chcesz zgnieść dwie ostatnie rewizje potem trzeba powołać

git rebase --interactive <3rd last commit> 

Następnie trzeba wybrać ostatnie zatwierdzenie i zmiażdżenie drugiego do ostatniego zatwierdzenia. Nie możesz zgnieść najwyższego zatwierdzenia historii.

1

I created some aliases w celu ułatwienia korzystania z poleceń git commit --fixup i git commit --squash dodanych w git 1.7. Dodaj je do swoich ~/.gitconfig:

[alias] 
    fixup = !sh -c 'REV=$(git rev-parse $1) && git commit --fixup [email protected] && git rebase -i --autosquash $REV^' - 
    squash = !sh -c 'REV=$(git rev-parse $1) && git commit --squash [email protected] && git rebase -i --autosquash $REV^' - 

Zastosowanie:

$ git commit -am 'bad commit' 
$ git commit -am 'good commit' 

$ git add .   # Stage changes to correct the bad commit 
$ git fixup HEAD^ # HEAD^ can be replaced by the SHA of the bad commit 

Zła popełnić może być kilka zobowiązuje się do tyłu.

3

Mój obecny przepływ pracy git jest tak --fixup/--squash intensywne, że napisałem nowy git-fixup polecenia, który obsługuje większość irytujących bitów automatycznie:

  • git fixup przedstawia zmodyfikowane pliki zgrupowane pod który ostatni zobowiązuje że kontakcie te same pliki
  • git fixup -a zobowiązuje wszystkie te zmiany jako --fixup zmian z odpowiadającymi im „rodzic” zobowiązuje
  • git fixup -r robi automatyczne git rebase --autosquash dla wszystkich fixup zobowiązuje

Wiele zmian są takie, że tylko trzy komendy powyżej są wystarczające, aby otrzymać pracę, nie kopiowaniem wklejanie z popełniają-id-tych lub czytania thru git log do znaleźć odpowiednie cele.

Źródło: https://github.com/ohmu/git-crust

0

Myślę, że głównym problemem jest to, że git (a kontrola wersji ogólnie) zmusza do myślenia w kategoriach sekwencji zmian, ale changeset lub funkcje oddziału lub cokolwiek nazwać spójna grupa powiązanych zmian ogólnie nie jest logiczna sekwencyjnie. Kolejność, w jakiej został napisany kod, jest przypadkowa i niekoniecznie związana z kolejnością, w jakiej należy ją przeczytać.

Nie mam na to rozwiązania, ale napisałem Perl script, aby pomóc zautomatyzować proces przepisywania historii. Jest podobny do skryptu Python @MikaEloranty, którego nie widziałem, kiedy go pisałem.

commit --fixup i rebase --autosquash są świetne, ale nie robią wystarczająco dużo. Kiedy mam sekwencję zatwierdzeń A-B-C i piszę więcej zmian w moim drzewie roboczym, które należą do jednego lub więcej z tych istniejących zatwierdzeń, muszę ręcznie spojrzeć na historię, zdecydować, które zmiany należą do których zatwierdza, je wystawić i utworzyć zatwierdza fixup!. Ale git ma już dostęp do wystarczającej ilości informacji, aby móc to wszystko dla mnie zrobić.

Do każdej porcji w git diff skrypt korzysta git blame aby znaleźć zobowiązują się, że ostatni dotknął odpowiednich linii i zwraca git commit --fixup napisać odpowiednie fixup! zobowiązuje zasadniczo robi to samo robię ręcznie wcześniej.

Jeśli skrypt nie może rozwiązać porcji do pojedynczego, jednoznacznego zatwierdzenia, zgłasza to jako nieudany fragment, a będziesz musiał wrócić do ręcznego podejścia do tego. Jeśli dwukrotnie zmienisz wiersz w dwóch osobnych zatwierdzeniach, skrypt rozwiąże zmianę w tym wierszu do najnowszego z tych zatwierdzeń, które nie zawsze mają poprawną rozdzielczość. IMHO w odmianie "normalnej formy" nie powinieneś zmieniać wiersza dwa razy w dwóch różnych zatwierdzeniach, każde zatwierdzenie powinno przedstawiać ostateczną wersję linii, która dotyka, aby pomóc recenzentowi (recenzentom). Jednak może się zdarzyć w gałęzi błędów, aby wymyślić przykład, aby linia foo(bar()); mogła zostać dotknięta przez zatwierdzenie A (zmiana nazwy foo na fox) i zatwierdzenie B (zmiana nazwy bar na baz).

Jeśli uznasz ten skrypt za przydatny, możesz go poprawić i powtórzyć go, a być może pewnego dnia otrzymamy taką funkcję w poprawnej wersji git. Chciałbym zobaczyć narzędzie, które może zrozumieć, jak należy rozwiązać konflikt scalania, gdy zostanie wprowadzone przez interaktywny rebase.