Mam repozytorium z gałęziami master i A i dużą ilością działań scalających między nimi. Jak znaleźć zatwierdzenie w moim repozytorium, gdy gałąź A została utworzona na podstawie wzorca?
Moje repozytorium wygląda następująco:
-- X -- A -- B -- C -- D -- F (master)
\ / \ /
\ / \ /
G -- H -- I -- J (branch A)
Szukam wersji A, której nie git merge-base (--all)
znajdzie.
Odpowiedzi:
Szukałem tego samego i znalazłem to pytanie. Dziękujemy za pytanie!
Stwierdziłem jednak, że odpowiedzi, które tu widzę, wydają się nie dawać odpowiedzi, o które prosiłeś (lub których szukałem) - wydają się
G
raczej zatwierdzać, niżA
zatwierdzać.Stworzyłem następujące drzewo (litery przypisane w porządku chronologicznym), aby przetestować:
Wygląda to nieco inaczej niż twoje, ponieważ chciałem się upewnić, że mam (odnosząc się do tego wykresu, nie twojego) B, ale nie A (a nie D lub E). Oto litery dołączone do prefiksów SHA i wiadomości zatwierdzających (moje repo można stąd sklonować , jeśli jest to interesujące dla każdego):
Tak więc, cel: znaleźć B . Oto trzy sposoby, które znalazłem po drobnym majsterkowaniu:
1. wizualnie, z gitk:
Powinieneś zobaczyć drzewo takie jak to (oglądane z poziomu master):
lub tutaj (patrząc z tematu):
w obu przypadkach wybrałem zatwierdzenie znajdujące się
B
na moim wykresie. Po kliknięciu jego pełna wartość SHA jest wyświetlana w polu wprowadzania tekstu tuż pod wykresem.2. wizualnie, ale z terminala:
git log --graph --oneline --all
(Edycja / uwaga: dodawanie
--decorate
może być również interesujące; dodaje wskazanie nazw gałęzi, znaczników itp. Nie dodawanie tego do powyższego wiersza poleceń, ponieważ poniższe dane wyjściowe nie odzwierciedlają jego zastosowania).który pokazuje (zakładając
git config --global color.ui auto
):Lub zwykłym tekstem:
w obu przypadkach widzimy zatwierdzenie 6aafd7f jako najniższy wspólny punkt, tj.
B
na moim wykresie lubA
w twoim.3. Z magią powłoki:
W pytaniu nie określasz, czy chcesz czegoś takiego jak powyższe, czy też pojedynczego polecenia, które da ci tylko jedną wersję i nic więcej. Cóż, oto ten drugi:
Które możesz również umieścić w swoim ~ / .gitconfig jako (uwaga: myślnik końcowy jest ważny; dziękuję Brianowi za zwrócenie na to uwagi) :
Można to zrobić za pomocą następującego wiersza poleceń (zawiłego w cudzysłowie):
Uwaga:
zsh
równie łatwo mogło byćbash
, ale niesh
będzie działać - składnia nie istnieje w wanilii . (Jeszcze raz dziękuję, @conny, za poinformowanie mnie o tym w komentarzu do innej odpowiedzi na tej stronie!)<()
sh
Uwaga: alternatywna wersja powyższego:
Dzięki liori za zwrócenie uwagi na to , że powyższe może spaść przy porównywaniu identycznych gałęzi, i wymyślenie alternatywnej formy diff, która usuwa formę sed z mieszanki i czyni ją „bezpieczniejszą” (tj. Zwraca wynik (a mianowicie ostatnie zatwierdzenie), nawet jeśli porównujesz wzorzec do wzorca):
Jako wiersz .git-config:
Z muszli:
Tak więc w moim drzewie testowym (które było przez chwilę niedostępne, przepraszam; wróciło), to działa teraz zarówno na wzorcu, jak i temacie (podając odpowiednio G i B). Jeszcze raz dziękuję, Liori, za alternatywną formę.
Tak właśnie wymyśliłem [i liori]. Wydaje mi się, że to działa. Pozwala również na dodanie kilku aliasów, które mogą się przydać:
Miłego zabawy!
źródło
diff
„s if-then-else tryb i erase zmienione / usunięte linie z jego wyjściu zamiast liczenia na tyle duże, posiadające kontekst, przez.:diff --old-line-format='' --new-line-format='' <(git rev-list …) <(git rev-list …)|head -1
.git log -n1 --format=format:%H $(git log --reverse --format=format:%H master..topic | head -1)~
też będzie działać, myślęgit merge-base --fork-point ...
zatwierdzenie B (zatwierdzenie6aafd7f
) dla tego drzewa? Byłem zajęty innymi rzeczami, kiedy to opublikowałeś, i brzmiało to dobrze, ale w końcu po prostu spróbowałem i nie dostaję do pracy ... albo dostaję coś nowszego, albo po prostu cichą awarię (bez błędu) wiadomość, ale status wyjścia 1), starając się jak argumentygit co master; git merge-base --fork-point topic
,git co topic; git merge-base --fork-point master
,git merge-base --fork-point topic master
(dla każdej kasie), itd. czy coś robię źle lub brakuje?--fork-point
opiera się na logowaniu, więc zadziała tylko wtedy, gdy wprowadzisz zmiany lokalnie. Nawet wtedy wpisy ponownego logowania mogły wygasnąć. Jest to przydatne, ale w ogóle nie jest niezawodne.Być może szukasz
git merge-base
:źródło
--all
opcję „git merge-base
”git merge-base
pięć godzin po opublikowaniu mojej odpowiedzi (prawdopodobnie w odpowiedzi na moją odpowiedź). Niemniej jednak pozostawię tę odpowiedź taką, jaka jest, ponieważ może być nadal przydatna dla kogoś, kto znajdzie to pytanie za pomocą wyszukiwania.Użyłem
git rev-list
tego rodzaju rzeczy. Na przykład (zwróć uwagę na 3 kropki)wypluje punkt rozgałęzienia. Teraz nie jest idealny; ponieważ kilka razy scaliłeś wzorzec z odgałęzieniem A, podzieli to kilka możliwych punktów rozgałęzienia (w zasadzie oryginalny punkt rozgałęzienia, a następnie każdy punkt, w którym scaliłeś wzorzec z odgałęzieniem A). Powinno to jednak przynajmniej zawęzić możliwości.
Dodałem to polecenie do moich aliasów
~/.gitconfig
jako:więc mogę to nazwać:
źródło
G
zamiastA
, według wykresu w pierwotnym pytaniu.) Mam odpowiedź, która się pojawiA
, że będę publikować w tej chwili.git checkout topic
a następnie uruchomić ten zetopic
zamiastbranch-a
) Wymienia648ca357b946939654da12aaf2dc072763f3caee
i37ad15952db5c065679d7fc31838369712f0b338
- jak37ad159
i648ca35
są w ancestory obecnych oddziałów (ten ostatni jest obecny szeftopic
), ale nie ma też sensu przed rozgałęzieniem. Czy dostajesz coś innego?Jeśli lubisz krótkie polecenia,
Oto wyjaśnienie.
Poniższe polecenie podaje listę wszystkich zatwierdzeń w systemie głównym, które wystąpiły po utworzeniu nazwa_gałęzi
Ponieważ zależy Ci tylko na najwcześniejszym z tych zatwierdzeń, chcesz mieć ostatni wiersz wyniku:
Rodzicem najwcześniejszego zatwierdzenia, który nie jest przodkiem „nazwa_ gałęzi”, z definicji jest w „nazwa_gałęzi” i jest w „głównym”, ponieważ jest przodkiem czegoś w „głównym”. Masz więc najwcześniejsze zatwierdzenie w obu gałęziach.
Komenda
jest tylko sposobem pokazania referencji zatwierdzenia nadrzędnego. Możesz użyć
lub cokolwiek.
PS: Nie zgadzam się z argumentem, że kolejność przodków jest nieistotna. To zależy od tego, czego chcesz. Na przykład w tym przypadku
ma sens wysyłać C2 jako zatwierdzenie „rozgałęzienia”. To wtedy deweloper rozgałęził się na „master”. Kiedy się rozgałęził, gałąź „B” nawet się nie połączyła! Oto, co daje rozwiązanie w tym poście.
Jeśli chcesz, aby ostatnie zatwierdzenie C było takie, że wszystkie ścieżki od początku do ostatniego zatwierdzenia w gałęzi „A” przechodzą przez C, to chcesz zignorować kolejność przodków. Jest to czysto topologiczne i daje wyobrażenie o tym, kiedy masz dwie wersje kodu działające w tym samym czasie. To wtedy wybrałeś podejście oparte na scalaniu, a to zwróci C1 w moim przykładzie.
źródło
git rev-list commit^^!
można uprościć jakogit rev-parse commit^
git rev-list --first-parent ^branch_name master
zgit rev-list --first-parent branch_name ^master
bo jeżeli oddział główny jest 0 commity wyprzedza innego oddziału (szybko), którego przesłanie do niego, nie ma wyjścia będzie tworzony. Dzięki mojemu rozwiązaniu nie powstają żadne dane wyjściowe, jeśli master jest ściśle przed nami (tzn. Gałąź została całkowicie scalona), czego chcę.git rev-list --first-parent ^topic master
zajmie tylko ty z powrotem do pierwszego popełnić po ostatnim scalaniamaster
siętopic
(jeśli w ogóle istnieje).Biorąc pod uwagę, że tak wiele odpowiedzi w tym wątku nie zawiera odpowiedzi, o którą pytało pytanie, oto podsumowanie wyników każdego rozwiązania, wraz ze skryptem, którego użyłem do replikacji repozytorium podanego w pytaniu.
Dziennik
Tworząc repozytorium z podaną strukturą, otrzymujemy dziennik git:
Moim jedynym dodatkiem jest tag, który wyraźnie mówi o punkcie, w którym utworzyliśmy gałąź, a tym samym zatwierdzenie, które chcemy znaleźć.
Rozwiązanie, które działa
Jedynym działającym rozwiązaniem jest rozwiązanie dostarczone przez Lindes poprawnie zwraca
A
:Jako Charles Bailey zauważa , to rozwiązanie jest bardzo kruche.
Jeśli
branch_A
domaster
, a następnie połączyćmaster
siębranch_A
bez interwencji zobowiązuje następnie roztwór Lindes' daje tylko Najnowsze Rozbieżność .Oznacza to, że w moim przepływie pracy myślę, że będę musiał trzymać się tagowania punktu rozgałęzienia długo działających gałęzi, ponieważ nie mogę zagwarantować, że można je później znaleźć w wiarygodny sposób.
To wszystko sprowadza się do
git
brakuhg
wywołań nazwanych gałęzi . Bloger jhw nazywa te rodowody vs. rodziny w swoim artykule Dlaczego lubię Mercurial bardziej niż Git oraz w swoim kolejnym artykule More on Mercurial vs. Git (z wykresami!) . Polecam ludzie czytają im zrozumieć, dlaczego niektórzy nawróceni Nie przegap rtęciowe o nazwie oddziały wgit
.Rozwiązania, które nie działają
Rozwiązanie dostarczone przez mipadi zwraca dwie odpowiedzi
I
iC
:Rozwiązanie według Greg Hewgill powrotu
I
Rozwiązanie dostarczone przez Karla zwraca
X
:Scenariusz
Wątpię, czy wersja git ma na to duży wpływ, ale:
Dzięki Charlesowi Baileyowi za pokazanie mi bardziej zwartego sposobu pisania skryptów w przykładowym repozytorium.
źródło
diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1
. Dziękujemy za podanie instrukcji tworzenia repozytorium :)Zasadniczo nie jest to możliwe. W historii gałęzi rozgałęzienie i scalenie przed odgałęzieniem nazwanym rozgałęziono, a oddział pośredni dwóch nazwanych rozgałęzień wygląda tak samo.
W git gałęzie to tylko bieżące nazwy końcówek części historii. Nie mają tak naprawdę silnej tożsamości.
Nie jest to zwykle duży problem, ponieważ baza do scalania (patrz odpowiedź Grega Hewgilla) dwóch zatwierdzeń jest zwykle znacznie bardziej użyteczna, dając ostatnie zatwierdzenie, które zostały udostępnione przez dwa oddziały.
Rozwiązanie oparte na kolejności rodziców zatwierdzenia oczywiście nie zadziała w sytuacjach, w których oddział został w pełni zintegrowany w pewnym momencie historii oddziału.
Ta technika również upada, jeśli scalenie integracji zostało wykonane z odwróconymi elementami nadrzędnymi (np. Tymczasowa gałąź została użyta do przeprowadzenia testu połączenia z master, a następnie szybko przesłana do gałęzi funkcji, aby dalej budować).
źródło
git
miał odpowiednikhg
nazwanych oddziałów, dzięki czemu zarządzanie wieloletnimi oddziałami serwisowymi byłoby o wiele łatwiejsze.Co powiesz na coś takiego
źródło
diverges = !bash -c 'git rev-parse $(diff <(git log --pretty=oneline ${1}) <(git log --pretty=oneline ${2}) | tail -1 | cut -c 3-42)^'
(bez plików tymczasowych)G
zamiastA
, według wykresu w pierwotnym pytaniu.) Myślę, że znalazłem odpowiedź, którą wkrótce opublikuję.diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1
na pewno coś mi brakuje, ale IMO, wszystkie powyższe problemy są spowodowane, ponieważ zawsze staramy się znaleźć punkt rozgałęzienia w historii, a to powoduje różnego rodzaju problemy z powodu dostępnych kombinacji scalania.
Zamiast tego zastosowałem inne podejście, oparte na tym, że obie gałęzie mają wiele historii, dokładnie cała historia przed rozgałęzieniem jest w 100% taka sama, więc zamiast wracać, moja propozycja dotyczy kontynuacji (od 1 commit), szukając pierwszej różnicy w obu gałęziach. Punkt rozgałęzienia będzie po prostu rodzicem pierwszej znalezionej różnicy.
W praktyce:
I rozwiązuje wszystkie moje zwykłe przypadki. Pewnie, że są granice nieobjęte, ale ... ciao :-)
źródło
comm --nocheck-order -1 -2 <(git rev-list --reverse --topo-order topic) <(git rev-list --reverse --topo-order master) | head -1
Ostatnio musiałem również rozwiązać ten problem i ostatecznie napisałem skrypt Ruby: https://github.com/vaneyckt/git-find-branching-point
źródło
Po wielu badaniach i dyskusjach jasne jest, że nie ma magicznej kuli, która działałaby we wszystkich sytuacjach, a przynajmniej nie w obecnej wersji Git.
Dlatego napisałem kilka łatek, które dodają koncepcję
tail
gałęzi. Za każdym razem, gdy tworzona jest gałąź, tworzony jest również wskaźnik do oryginalnego punktu,tail
ref. To odniesienie jest aktualizowane za każdym razem, gdy gałąź jest ponownie wydawana.Aby znaleźć punkt rozgałęzienia gałęzi deweloperskiej, wystarczy użyć
devel@{tail}
, to wszystko.https://github.com/felipec/git/commits/fc/tail
źródło
Oto ulepszona wersja mojej poprzedniej odpowiedzi poprzedniej odpowiedzi . Opiera się na komunikatach zatwierdzeń z fuzji, aby znaleźć miejsce, w którym gałąź została utworzona po raz pierwszy.
Działa na wszystkich wymienionych tutaj repozytoriach, a nawet zająłem się kilkoma trudnymi, które pojawiły się na liście mailingowej . Do tego napisałem również testy .
źródło
Następujące polecenie ujawni SHA1 dla Commit A.
git merge-base --fork-point A
źródło
Wydaje mi się, że czerpię radość
Ostatnia linia, którą otrzymujesz, jest pierwszym zatwierdzeniem w gałęzi, więc kwestia uzyskania tego jest nadrzędna. Więc
Wydaje się, że działa dla mnie i nie potrzebuje różnic itp. (Co jest pomocne, ponieważ nie mamy tej wersji różnic)
Korekta: To nie działa, jeśli jesteś w gałęzi master, ale robię to w skrypcie, więc to mniejszy problem
źródło
Aby znaleźć zatwierdzenia z punktu rozgałęzienia, możesz użyć tego.
źródło
Czasami jest to praktycznie niemożliwe (z pewnymi wyjątkami, gdzie możesz mieć dodatkowe dane), a rozwiązania tutaj nie działają.
Git nie zachowuje historii odwołań (w tym gałęzi). Przechowuje tylko aktualną pozycję dla każdej gałęzi (głowy). Oznacza to, że z czasem możesz stracić trochę historii gałęzi. Na przykład za każdym razem, gdy rozgałęziasz się, natychmiast tracisz, która gałąź była oryginalna. Wszystko, co robi oddział, to:
Możesz założyć, że pierwszy zaangażowany w to oddział. Tak bywa, ale nie zawsze tak jest. Nic nie stoi na przeszkodzie, abyś po rozpoczęciu powyższej operacji najpierw skierował się do któregoś z oddziałów. Ponadto nie można zagwarantować niezawodności znaczników czasu git. Dopiero gdy zobowiążesz się do tego, że naprawdę staną się strukturalnie gałęziami.
Podczas gdy na diagramach mamy tendencję do numerowania zatwierdzeń koncepcyjnie, git nie ma naprawdę stabilnej koncepcji sekwencji, gdy gałęzie drzewa zatwierdzenia. W takim przypadku możesz założyć, że liczby (wskazujące kolejność) są określane przez znacznik czasu (fajnie jest zobaczyć, jak interfejs użytkownika git obsługuje rzeczy, gdy ustawisz wszystkie znaczniki czasu na to samo).
Oto, czego człowiek oczekuje koncepcyjnie:
Oto, co faktycznie otrzymujesz:
Zakładasz, że B1 jest oryginalną gałęzią, ale może w rzeczywistości być po prostu martwą gałęzią (ktoś zrobił kasę -b, ale nigdy się jej nie zobowiązał). Dopiero po zaakceptowaniu obu uzyskasz prawidłową strukturę gałęzi w git:
Zawsze wiesz, że C1 pojawił się przed C2 i C3, ale nigdy nie wiesz, czy C2 pojawił się przed C3, czy C3 przed C2 (ponieważ możesz na przykład ustawić czas na stacji roboczej na cokolwiek). B1 i B2 również wprowadzają w błąd, ponieważ nie wiadomo, która gałąź była pierwsza. W wielu przypadkach można bardzo dobrze i zazwyczaj dokładnie zgadnąć. To trochę jak tor wyścigowy. Wszystko jest na ogół równe samochodom, wtedy możesz założyć, że samochód, który wyprzedza okrążenie, rozpoczął okrążenie z tyłu. Mamy również konwencje, które są bardzo niezawodne, na przykład mistrz prawie zawsze reprezentuje najdłużej żyjące gałęzie, chociaż niestety widziałem przypadki, w których nawet tak nie jest.
Podany tutaj przykład to przykład zachowania historii:
Prawdziwe tutaj jest również mylące, ponieważ my, ludzie, czytamy to od lewej do prawej, od korzenia do liścia (odnośnik). Git tego nie robi. Tam, gdzie robimy (A-> B) w naszych głowach, robi git (A <-B lub B-> A). Czyta to od ref do root. Refs mogą być gdziekolwiek, ale zwykle są to liście, przynajmniej dla aktywnych gałęzi. Ref wskazuje na zatwierdzenie i zatwierdzenie zawiera tylko znak podobny do ich rodziców / rodziców, a nie do dzieci. Gdy zatwierdzenie jest zatwierdzeniem scalania, będzie miało więcej niż jednego rodzica. Pierwszy element nadrzędny jest zawsze oryginalnym zatwierdzeniem, z którym został scalony. Pozostali rodzice są zawsze zatwierdzeniami, które zostały połączone w pierwotne zatwierdzenie.
Nie jest to bardzo wydajna reprezentacja, a raczej wyrażenie wszystkich ścieżek, które git może pobrać z każdego odwołania (B1 i B2).
Pamięć wewnętrzna Gita wygląda mniej więcej tak (nie że A jako rodzic pojawia się dwukrotnie):
Jeśli zrzucisz surowe zatwierdzenie git, zobaczysz zero lub więcej pól nadrzędnych. Jeśli jest zero, to znaczy, że nie ma rodzica, a zatwierdzenie jest rootem (możesz mieć wiele katalogów głównych). Jeśli jest taki, oznacza to, że nie było scalenia i nie jest to zatwierdzenie główne. Jeśli jest więcej niż jeden, oznacza to, że zatwierdzenie jest wynikiem scalenia, a wszyscy rodzice po pierwszym są zobowiązaniami do scalenia.
Kiedy oba trafią A, ich łańcuch będzie taki sam, wcześniej ich łańcuch będzie zupełnie inny. Pierwsze popełnienie dwóch kolejnych wspólnych zobowiązań jest wspólnym przodkiem i skąd się rozeszły. mogą występować pewne nieporozumienia między warunkami zatwierdzenia, rozgałęzienia i ref. Możesz w rzeczywistości scalić zatwierdzenie. To właśnie robi scalanie. Ref po prostu wskazuje na zatwierdzenie, a gałąź jest niczym innym jak ref w folderze .git / refs / heads, lokalizacja folderu określa, że ref jest gałąź, a nie czymś innym, jak tag.
Kiedy tracisz historię, połączenie polega na zrobieniu jednej z dwóch rzeczy w zależności od okoliczności.
Rozważać:
W takim przypadku scalenie w dowolnym kierunku spowoduje utworzenie nowego zatwierdzenia z pierwszym nadrzędnym jako zatwierdzonym wskazanym przez bieżącą wypisaną gałąź, a drugim nadrzędnym jako zatwierdzonym na końcu gałęzi, którą scaliłeś w bieżącą gałąź. Musi utworzyć nowe zatwierdzenie, ponieważ obie gałęzie mają zmiany od czasu ich wspólnego przodka, które należy połączyć.
W tym momencie D (B1) ma teraz oba zestawy zmian z obu gałęzi (siebie i B2). Jednak druga gałąź nie ma zmian w stosunku do B1. Jeśli scalisz zmiany z B1 do B2, aby zostały zsynchronizowane, możesz spodziewać się czegoś, co wygląda następująco (możesz zmusić git merge, aby zrobił to w ten sposób, używając --no-ff):
Otrzymasz to, nawet jeśli B1 ma dodatkowe zatwierdzenia. Dopóki nie zostaną wprowadzone zmiany w B2, których B1 nie ma, oba odgałęzienia zostaną połączone. Robi szybkie przewijanie do przodu, które jest jak rebase (rebazy także jedzą lub linearyzują historię), z wyjątkiem tego, że w przeciwieństwie do rebase, ponieważ tylko jedna gałąź ma zestaw zmian, nie musi stosować zestawu zmian z jednej gałęzi na drugą względem drugiej.
Jeśli przestaniesz pracować nad B1, wszystko będzie dobrze dla zachowania historii na dłuższą metę. Tylko B1 (który może być mistrzem) będzie się zwykle rozwijał, więc lokalizacja B2 w historii B2 z powodzeniem reprezentuje punkt, w którym została scalona z B1. Właśnie tego oczekuje od ciebie git, aby rozgałęzić B z A, a następnie możesz scalić A w B tak, jak chcesz, w miarę gromadzenia się zmian, jednak po scaleniu B z powrotem w A nie oczekuje się, że będziesz pracował na B i dalej . Jeśli kontynuujesz pracę nad swoim oddziałem po szybkim przeniesieniu go z powrotem do gałęzi, nad którą pracowałeś, to za każdym razem poprzednia historia twojego B. Naprawdę tworzysz nową gałąź za każdym razem po szybkim zatwierdzeniu do źródła, a następnie do gałęzi.
Od 1 do 3 i od 5 do 8 są gałęziami strukturalnymi, które pojawiają się, jeśli śledzisz historię dla 4 lub 9. Nie ma sposobu, aby wiedzieć, do której z tych nienazwanych i niepowiązanych gałęzi strukturalnych należą gałęzie nazwane i odniesienia jako koniec konstrukcji. Możesz założyć na tym rysunku, że 0 do 4 należy do B1, a 4 do 9 należy do B2, ale oprócz 4 i 9 nie mogłem wiedzieć, która gałąź należy do której gałęzi, po prostu narysowałem to w sposób, który daje iluzja tego. 0 może należeć do B2, a 5 może należeć do B1. W tym przypadku istnieje 16 różnych możliwości, do których nazwanych gałęzi może należeć każda gałąź strukturalna.
Istnieje wiele strategii git, które działają w ten sposób. Możesz wymusić, aby git merge nigdy nie przewijał do przodu i zawsze tworzył gałąź scalania. Strasznym sposobem na zachowanie historii gałęzi jest użycie tagów i / lub gałęzi (tagi są naprawdę zalecane) zgodnie z wybraną konwencją. Naprawdę nie poleciłbym pustego zatwierdzenia pustego w gałęzi, w której się łączysz. Bardzo powszechną konwencją jest to, aby nie łączyć się z gałęzią integracji, dopóki nie chce się naprawdę zamknąć gałęzi. Jest to praktyka, do której ludzie powinni się stosować, ponieważ w przeciwnym razie pracujesz nad punktem posiadania oddziałów. Jednak w prawdziwym świecie ideał nie zawsze jest praktyczny, co oznacza, że właściwe postępowanie nie jest wykonalne w każdej sytuacji. Jeśli co
źródło
man git-reflog
i część o datach: „master@{one.week.ago} oznacza„ gdzie master wskazywał tydzień temu w tym lokalnym repozytorium ””. Lub dyskusja na temat<refname>@{<date>}
wman gitrevisions
. Icore.reflogExpire
wman git-config
.Nie do końca rozwiązanie tego pytania, ale pomyślałem, że warto zwrócić uwagę na podejście, które stosuję, gdy mam długą gałąź życia:
W tym samym czasie tworzę gałąź, tworzę również tag o tej samej nazwie, ale z
-init
przyrostkiem, na przykładfeature-branch
ifeature-branch-init
.(To trochę dziwne, że na tak trudne pytanie należy odpowiedzieć!)
źródło
Prostym sposobem, aby ułatwić zobaczenie punktu rozgałęzienia,
git log --graph
jest skorzystanie z tej opcji--first-parent
.Na przykład weź repo z zaakceptowanej odpowiedzi :
Teraz dodaj
--first-parent
:To ułatwia!
Uwaga: jeśli repozytorium ma wiele gałęzi, będziesz chciał określić 2 gałęzie, które porównujesz zamiast używać
--all
:źródło
Problem wydaje się polegać na znalezieniu najnowszego cięcia pojedynczego zatwierdzenia między obiema gałęziami z jednej strony, a najwcześniejszym wspólnym przodkiem z drugiej (prawdopodobnie początkowego zatwierdzenia repo). Jest to zgodne z moją intuicją dotyczącą tego, czym jest punkt „rozgałęzienia”.
Biorąc to pod uwagę, wcale nie jest to łatwe do obliczenia za pomocą normalnych poleceń powłoki git, ponieważ
git rev-list
- nasze najpotężniejsze narzędzie - nie pozwala nam ograniczyć ścieżki, przez którą osiągany jest zatwierdzenie. Najbliżej mamygit rev-list --boundary
, która może dać nam zestaw wszystkich zatwierdzeń, które „zablokowały nam drogę”. (Uwaga:git rev-list --ancestry-path
jest interesujące, ale nie wiem, jak to zrobić, żeby było przydatne).Oto skrypt: https://gist.github.com/abortz/d464c88923c520b79e3d . Jest to stosunkowo proste, ale ze względu na pętlę jest wystarczająco skomplikowane, aby uzasadnić istnienie istoty.
Zauważ, że większość innych proponowanych tutaj rozwiązań nie może działać we wszystkich sytuacjach z prostego powodu:
git rev-list --first-parent
nie jest wiarygodna w linearyzacji historii, ponieważ można łączyć z dowolnym porządkiem.git rev-list --topo-order
z drugiej strony jest bardzo przydatny - do chodzenia zatwierdzeń w kolejności topograficznej - ale wykonywanie różnic jest kruche: istnieje wiele możliwych kolejności topograficznych dla danego wykresu, więc zależy to od pewnej stabilności uporządkowania. To powiedziawszy, rozwiązanie strongk7 prawdopodobnie działa cholernie przez większość czasu. Jednak wolniej jest w tej kopalni, ponieważ muszę przejść całą historię repo ... dwa razy. :-)źródło
Poniżej zaimplementowano git równoważny svn log - stop-on-copy i można go również użyć do znalezienia początku gałęzi.
Podejście
Podobnie jak wszystkie rzeki biegną do morza, wszystkie gałęzie biegną do opanowania, dlatego też znajdujemy bazę scalającą między pozornie niezwiązanymi gałęziami. Gdy wracamy od głowy gałęzi przez przodków, możemy zatrzymać się przy pierwszej potencjalnej bazie scalania, ponieważ teoretycznie powinien to być punkt początkowy tej gałęzi.
Notatki
szczegóły: https://stackoverflow.com/a/35353202/9950
źródło
Możesz użyć następującego polecenia, aby zwrócić najstarsze zatwierdzenie w gałęzi_a, które nie jest dostępne z poziomu master:
Być może dzięki dodatkowej kontroli poczytalności, że rodzic tego zatwierdzenia jest rzeczywiście osiągalny od mistrza ...
źródło
Możesz przejrzeć dziennik oddziału A, aby dowiedzieć się, z którego zatwierdzenia został utworzony, a także pełną historię zatwierdzeń wskazanych przez tę gałąź. Rejestruje się
.git/logs
.źródło
Wydaje mi się, że znalazłem sposób, który rozwiązuje wszystkie wymienione tutaj przypadki narożne:
Charles Bailey ma rację, że rozwiązania oparte na porządku przodków mają jedynie ograniczoną wartość; na koniec dnia potrzebujesz jakiegoś zapisu „to zatwierdzenie pochodzi z gałęzi X”, ale taki zapis już istnieje; domyślnie „git merge” użyłby komunikatu zatwierdzenia, takiego jak „Scal gałąź” gałąź_A „w master”, to mówi, że wszystkie zmiany z drugiego elementu nadrzędnego (zatwierdzenie ^ 2) pochodziły z „gałęzi_A” i zostały scalone z pierwszym rodzic (commit ^ 1), który jest „master”.
Uzbrojeni w tę informację możesz znaleźć pierwsze scalenie „branch_A” (czyli wtedy, gdy naprawdę powstało „branch_A”) i znaleźć bazę scalającą, która byłaby punktem rozgałęzienia :)
Próbowałem z repozytoriami Marka Bootha i Charlesa Baileya i rozwiązanie działa; jak to nie mogło? Jedynym sposobem, aby to nie zadziałało, jest ręczna zmiana domyślnego komunikatu zatwierdzenia dla scalania, tak aby informacje o oddziale zostały naprawdę utracone.
Przydatność:
Następnie możesz zrobić '
git branch-point branch_A
'.Cieszyć się ;)
źródło
git merge -m
do powiedzenia tego , w co się połączyłem, a nie nazwy potencjalnie efemerycznej gałęzi (np. „Łącz zmiany głównych linii w refaktor funkcji xyz”). Załóżmy, że-m
w moim przykładzie byłam mniej pomocna ? Problem jest po prostu nierozstrzygalny w całości, ponieważ mogę stworzyć tę samą historię z jedną lub dwiema gałęziami tymczasowymi i nie ma sposobu, aby powiedzieć różnicę.