Hg: Jak zrobić rebase, takie jak rebase gita

207

W Git mogę to zrobić:

1. Rozpocznij pracę nad nową funkcją:
$ git co -b newfeature-123 # (lokalny oddział rozwoju funkcji)
zrobić kilka zmian (M, N, O)

mistrz A --- B --- C
                \
newfeature-123 M --- N --- O

2. Wyciągnij nowe zmiany z głównego urządzenia nadrzędnego:
$ git pull
(wzorzec zaktualizowany o ff-commits)

mistrz A --- B --- C --- D --- E --- F.
                \
newfeature-123 M --- N --- O

3. Wyłącz system główny, aby moja nowa funkcja 
można opracować w oparciu o najnowsze zmiany upstream:
(od newfeature-123)
$ git rebase master

mistrz A --- B --- C --- D --- E --- F.
                            \
newfeature-123 M --- N --- O


Chcę wiedzieć, jak zrobić to samo w Mercurial, i szukałem odpowiedzi w Internecie, ale najlepsze, co mogłem znaleźć, to: git rebase - czy mogę to zrobić

Ten link zawiera 2 przykłady:
1. Przyznaję, że to: (zastępując poprawki z przykładu tymi z mojego własnego przykładu)

hg w górę -CF  
gałąź hg -f nowa funkcja-123  
przeszczep hg -a -b nowa cecha-123 

nie jest tak źle, z tym wyjątkiem, że pozostawia MNO w fazie wstępnej jako nierozłożoną głowę i tworzy 3 nowe zatwierdzenia M ', N', O ', które reprezentują ich rozgałęzienie poza zaktualizowaną linią główną.

Zasadniczo problem polega na tym, że kończę na tym:

mistrz A --- B --- C --- D --- E --- F.
                \ \
newfeature-123 \ M '--- N' --- O '
                  \
newfeature-123 M --- N --- O

nie jest to dobre, ponieważ pozostawia po sobie lokalne, niechciane zatwierdzenia, które należy porzucić.

  1. Inną opcją z tego samego linku jest
hg qimport -r M: O
hg qpop -a
hg w górę F
oddział hg newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

a to daje pożądany wykres:

mistrz A --- B --- C --- D --- E --- F.
                            \
newfeature-123 M --- N --- O

ale te polecenia (wszystkie 6!) wydają się o wiele bardziej skomplikowane niż

$ git rebase master

Chcę wiedzieć, czy jest to jedyny odpowiednik w Hg, czy jest dostępny inny prosty sposób, taki jak Git.

jpswain
źródło
7
„nie jest to dobre, ponieważ pozostawia po sobie lokalne, niechciane zobowiązania, które należy porzucić”. - właściwie git robi to samo. Nie zmienia ani nie usuwa zatwierdzeń w oryginalnej gałęzi, po prostu tworzy nowe, które stosują ten sam zestaw zmian na master. Nadal możesz uzyskiwać dostęp do starych za pomocą git reflogi nie znikną one całkowicie, dopóki nie zostaną zebrane śmieci. Jeśli chcesz trzymać je w nazwanej gałęzi, aby nie trzeba było używać dziennika, po prostu zrób to git branch feature-123_originalprzed zmianą bazy.
MatrixFrog,
5
Losowe pytanie: czy sam narysowałeś zestawy zmian / gałęzie, czy istnieje narzędzie, które to robi?
Amir Rachum
2
Zrobiłem to sam z TextWrangler ustawionym na „nadpisywanie”.
jpswain,

Odpowiedzi:

235

VonC ma odpowiedź, której szukasz - rozszerzenie Rebase. Warto jednak poświęcić sekundę lub dwie na zastanowienie się, dlaczego ani mq, ani rebase nie są domyślnie włączone w mercurial: ponieważ mercurial polega na nieusuwalnych zestawach zmian. Kiedy pracuję w sposób, który opisujesz, czyli prawie codziennie, oto wzór, który biorę:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

i to naprawdę wszystko, co jest konieczne. Skończyłem z klonem newfeature-123, który z łatwością mogę zepchnąć z powrotem do głównej linii, gdy jestem z niego zadowolony. Najważniejsze jednak, że nigdy nie zmieniłem historii . Ktoś może spojrzeć na moje zestawy i zobaczyć, na czym były pierwotnie zakodowane i jak zareagowałem na zmiany w głównej linii podczas mojej pracy. Nie wszyscy myślą, że to ma wartość, ale jestem głęboko przekonany, że zadaniem kontroli źródła jest pokazanie nam nie tego, co chcielibyśmy, aby się wydarzyło, ale co się faktycznie wydarzyło - każda martwa chwila i każdy refaktor powinien pozostawić nieusuwalny ślad, a następnie i inne techniki edycji historii to ukrywają.

Teraz idź i wybierz odpowiedź VonC, kiedy odkładam mydło. :)

Ry4an Brase
źródło
18
Uwaga: z kursu, Git nie dokładnie pozwala przepisać historię, tylko stworzyć nową łatwo ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). Dodając RebaseExtension, Mercurial zapewnia dokładnie taki sam wygodny sposób na zastąpienie starej historii nową. Czemu? Ponieważ scalenie nie zawsze jest właściwą odpowiedzią, szczególnie gdy twój zestaw zmian powinien być postrzegany jako ewolucja nad F, a nie odwrotnie (P scalone nad O)
VonC
19
VonC, zgadzam się, połączenie nie zawsze jest właściwym wyborem, ale myślę, że różnica polega na tym, co ktoś chce, aby historia VCS była w stanie powiedzieć. Myślę, że historia powinna zawsze być w stanie odpowiedzieć na pytania typu „Jaki był sposób, w jaki starałem się ją najpierw zintegrować, co nie zadziałało i myślałem, że w tym czasie był bezużyteczny”. Naukowcy trzymają dzienniki w długopisie z numerowanymi stronami, a inżynierowie oprogramowania AFAIC powinni zapisać każdy bajt, który kiedykolwiek wpisali. Przepisywanie historii, nawet ustalanie pochodzenia, ale na pewno CollapseExtension, HistEdit itp. Naruszają to. To całkowicie kwestia osobistego wyboru.
Ry4an Brase
14
+1 do osobistego wyboru. Z Gitem konsekwentnie używam rebase do trywialnych rozbieżności i scalam do nietrywialnych. To pozwala mi zachować historię scalania tam, gdzie uważam, że jest to ważne, ale przez cały czas utrzymuję dziennik w czystości i liniowości.
Kos
16
Robię też mnóstwo interaktywnych zmian, ponieważ najpierw robię wiele małych zatwierdzeń, a następnie łączę, oznaczam i usuwam je, a następnie scalam z (lub rebase na) gałęzi głównej. Lubię kodowanie i zarządzanie zmianami jako osobne kroki.
Kos
6
Filozofia „zapisz każdy bajt na ścieżce rozwoju” nie jest tak naprawdę odpowiednia dla dużych projektów open source, w których autorzy proponują zestaw łatek, a następnie przerabiają je na podstawie informacji zwrotnych od opiekunów. W końcu główne repozytorium projektu ma tylko odpowiednią wersję wszystkich zmian, ze wszystkimi powiązanymi zmianami w jednym zatwierdzeniu. git Interactive rebase jest naprawdę dobry do czyszczenia działających zmian w sekwencji zatwierdzeń, które nie pozostawiają złamanego drzewa, oraz z komunikatami zatwierdzania odzwierciedlającymi to, co zdecydujesz powiedzieć po zakończeniu pracy nad całością.
Peter Cordes,
104

Być może szukasz Rebase Extension . (wdrożony w ramach SummerOfCode 2008 )

W takich przypadkach przydatne może być „odłączenie” lokalnych zmian, zsynchronizowanie repozytorium z głównym nurtem, a następnie dodanie prywatnych zmian do nowych zdalnych zmian. Ta operacja nazywa się rebase.

Dostawać od :

alternatywny tekst

do:

alternatywny tekst


Jak skomentowano poniżej przez steprobe :

W przypadku, gdy nie wprowadzasz zmian i masz dwie gałęzie w swoim repozytorium, możesz zrobić ( używająckeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Dziedzicz oryginalną nazwę oddziału).

Mojca wspomina:

Lubię używać hg rebase --source {L1's-sha} --dest {R2's-sha}, ale nie wiedziałem, że mogę dodać --keepbranchesna końcu.

Jak pokazano poniżej przez Jonathana Blackburn :

 hg rebase -d default --keepbranches
VonC
źródło
1
Patrzyłem na Rebase Extension, ale wciąż nie jest dla mnie jasne. Czy mógłbyś wyjaśnić kroki, które należy wykonać, aby opisać wyżej?
jpswain,
6
W przypadku, gdy nie wprowadzasz zmian i masz dwie gałęzie w swoim repozytorium, możesz zrobić: hg up newfeature-123a następniehg rebase -d master --keepbranches
steprobe
2
Uważam, że nic nie jest nie tak z rebase, to tylko kwestia wyboru. Problem polega na tym, że rebase jest nadużywany i jestem z @ Ry4an w tej sprawie, nie przepisuj historii, abyś mógł wiedzieć, co się stanie i kiedy.
Jorge Vargas
@steprobe: dziękuję za podpowiedź dotyczącą korzystania z opcji --keepbranches. Lubię używać hg rebase --source {L1's-sha} --dest {R2's-sha}, ale nie wiedziałem, że mogę dodać --keepbranchesna końcu. Zostało to wspomniane w innych odpowiedziach o niższym rankingu, ale dobrze byłoby również wprost napisać to do tej odpowiedzi.
Mojca
@Mojca Nie ma problemu. Odpowiednio zredagowałem odpowiedź.
VonC
44

Zakładając, że masz nowoczesną instalację Hg, możesz po prostu dodać:

[extensions]
rebase = 

do ~ / .hgrc.

Następnie można użyć polecenia hg rebase, hg pull --rebasealbo hg help rebase.

sblom
źródło
2
Aby dodać do tego polecenia, musisz wykonać: hg rebase -s o -d f
Simple-Solution
21

Nie sądzę, aby powyższe odpowiedzi osiągnęły cel PO, którym było utrzymanie jego gałęzi zadań, po prostu opartej na późniejszym punkcie gałęzi nadrzędnej.

Załóżmy, że zaczynam od tego wykresu (wygenerowanego przy użyciu rozszerzenia graphlog. Poważna geekowska miłość do graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Jeśli jestem w gałęzi feature3 i chcę ją wyrenderować przy ponownym zatwierdzeniu, rozumiem, że bym uruchomił hg rebase -d default. Ma to następujący wynik:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

Misja zrealizowana? Nie wydaje mi się Problem polega na tym, że gdy zatwierdzenia na gałęzi feature3 zostały ponownie oparte na ponownie, gałąź Feature3 została usunięta . Moje commity zostały przeniesione do domyślnego oddziału, czego starałem się przede wszystkim uniknąć.

W Git wynik wyglądałby tak:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Zauważ, że gałąź Feature3 nadal istnieje, dwa zatwierdzenia są nadal w gałęzi Feature3 i domyślnie nie są widoczne. Bez zachowania gałęzi zadania nie widzę, jak funkcjonalnie różni się to od scalenia.

AKTUALIZACJA : Odkryłem --keepbranchesflagę obsługiwaną przez hg rebase i cieszę się, że wszystko jest w porządku. Używając hg rebase -d default --keepbranchesdokładnie replikuję zachowanie Git, którego pragnąłem. Kilka pseudonimów później, a ja odpłacam się jak niczyja sprawa.

Jonathan Blackburn
źródło
7
Właśnie odkryłem flagę --keepbranches przy rebase. Problem rozwiązany. Gdyby to był mój kod, zrobiłbym to domyślnym, ale to tylko ja.
Jonathan Blackburn,
1
Myślę, że byłoby przydatne, gdybyś dodał tę część informacji do swojej odpowiedzi powyżej - zajęło mi to trochę czasu, aby ją znaleźć.
HansMari
3

Ponieważ niektórzy ludzie twierdzą, że uważają, że dobrze jest zachować każdą iterację wszystkiego, zaznaczę, że w przypadku większych projektów typu open source zaakceptowanie zmian połączonych i iteracji rozwoju spowodowałoby niechlujną historię zmian głównej linii i historia zmian jest mniej przydatna do sprawdzania, w jaki sposób dotarła tam obecna wersja.

Działa to dobrze, gdy przesłane zmiany są sprawdzane przez osoby, które ich nie napisały, zanim zostaną zaakceptowane, więc zmiany, które wchodzą w linię główną, są na ogół debugowane i działają. Następnie, gdy cofniesz się do początku linii, zobaczysz wszystkie zmiany, które się z nią wiążą, a nie jakiś punkt w środku rozwoju zmiany, której jest częścią.

Strona współtwórców x265 wyjaśnia, jak ponownie zatwierdzić zestaw zmian, nad którymi pracujesz, aby przygotować je do przesłania do projektu x265. (W tym użycie TortoiseHG do zatwierdzenia niektórych, ale nie wszystkich zmian w pojedynczym pliku, takich jak git gui stage / unstage diff hunk do zatwierdzenia).

Proces polega na zaktualizowaniu hg do wskazówki nadrzędnej, a następnie niezakceptowaniu wszystkich zmian w katalogu roboczym. Odłóż na półkę te, które nie są częścią tego, co chcesz przesłać, a następnie podziel resztę na tyle, ile jest odpowiednich osobnych zatwierdzeń, z ładnymi komunikatami zatwierdzania.

Myślę, że skopiujesz / wkleisz, a następnie edytujesz komunikaty zatwierdzenia z poprzednich iteracji poprawionego zestawu łat. A może możesz przeszczepić swoje stare zatwierdzenia (cherry-pick w języku git), a następnie zmieniać je jeden po drugim, aby otrzymać stare komunikaty zatwierdzeń jako punkt wyjścia do edycji.

Peter Cordes
źródło