Git master master w gałęzi funkcji

1012

Powiedzmy, że mamy następującą sytuację w Git:

  1. Utworzone repozytorium:

    mkdir GitTest2
    cd GitTest2
    git init
    
  2. Niektóre zmiany w wzorcu mają miejsce i zostają popełnione:

    echo "On Master" > file
    git commit -a -m "Initial commit"
    
  3. Feature1 rozgałęził master i część pracy została wykonana:

    git branch feature1
    git checkout feature1
    echo "Feature1" > featureFile
    git commit -a -m "Commit for feature1"
    
  4. W międzyczasie wykryto błąd w kodzie głównym i utworzono gałąź poprawki:

    git checkout master
    git branch hotfix1
    git checkout hotfix1
    
  5. Błąd został naprawiony w gałęzi poprawek i scalony z powrotem w master (być może po żądaniu ściągnięcia / sprawdzeniu kodu):

    echo "Bugfix" > bugfixFile
    git commit -a -m "Bugfix Commit"
    git checkout master
    git merge --no-ff hotfix1
    
  6. Prace nad funkcją 1 trwają:

    git checkout feature1
    

Powiedz, że potrzebuję poprawki w mojej gałęzi funkcji, być może dlatego, że błąd również tam występuje. Jak mogę to osiągnąć bez powielania zatwierdzeń w mojej gałęzi funkcji?

Chcę zapobiec otrzymaniu dwóch nowych zatwierdzeń w mojej gałęzi funkcji, które nie mają związku z implementacją funkcji. Wydaje mi się to szczególnie ważne, jeśli używam żądań ściągnięcia: wszystkie te zatwierdzenia zostaną również uwzględnione w żądaniu ściągnięcia i będą musiały zostać przejrzane, chociaż zostało to już zrobione (ponieważ poprawka jest już w systemie głównym).

Nie mogę zrobić git merge master --ff-only: „fatalny: nie można szybko przewinąć do przodu, przerywanie.”, Ale nie jestem pewien, czy to mi pomogło.

theomega
źródło
8
Jeśli oddział feature1jest całkowicie lokalny, spójrz na git rebase.
Jokester
19
Dzięki, jako początkujący git, git rebasewydaje mi się czarną magią ....
theomega
13
jeśli gałąź jest cecha - tylko poprawka błędu nie powinna tam wystąpić (przynajmniej jeśli nie jest to błąd blokujący), ponieważ celem tej gałęzi jest pokazanie nowej funkcji. Błąd zostanie naprawiony po połączeniu z masterem, w którym znajduje się zatwierdzenie z poprawką.
gipi
21
Prawdopodobnie warto zauważyć dla początkujących, że w 3. git branch feature1i git checkout feature1mogą być łączone w git checkout -b feature14. i mogą być całkowicie zredukowane dogit checkout -b hotfix1 master
Naruto Sempai 13.03.15
3
Czy byłbyś skłonny wrócić i zmienić przyjętą odpowiedź, ponieważ obecnie zaakceptowana odpowiedź jest okropna.
Wszechobecny

Odpowiedzi:

1213

Jak scalimy gałąź główną w gałąź funkcji? Łatwy:

git checkout feature1
git merge master

Nie ma sensu wymuszać szybkiego łączenia w przód, ponieważ nie można tego zrobić. Zaangażowałeś się zarówno w gałąź funkcji, jak i gałąź główną. Szybkie przewijanie do przodu jest teraz niemożliwe.

Spójrz na GitFlow . Jest to rozgałęziony model git, który można śledzić, a ty nieświadomie już to zrobiłeś. Jest to także rozszerzenie Gita, które dodaje polecenia do nowych kroków przepływu pracy, które wykonują czynności automatycznie, które w innym przypadku byłyby konieczne ręcznie.

Więc co zrobiłeś dobrze w swoim przepływie pracy? Masz dwie gałęzie do pracy, twoja gałąź feature1 jest w zasadzie gałęzią „develop” w modelu GitFlow.

Utworzyłeś gałąź master z poprawki i scaliłeś ją z powrotem. A teraz utknąłeś.

Model GitFlow prosi o połączenie poprawki również z gałęzią programistyczną, która w twoim przypadku jest „feature1”.

Tak więc prawdziwą odpowiedzią byłoby:

git checkout feature1
git merge --no-ff hotfix1

Dodaje to wszystkie zmiany wprowadzone w tej poprawce do gałęzi funkcji, ale tylko te zmiany. Mogą one powodować konflikty z innymi zmianami programistycznymi w gałęzi, ale nie będą powodować konfliktu z gałęzią główną, jeśli w końcu scalisz gałąź funkcji z powrotem do jednostki głównej.

Zachowaj ostrożność przy zmianie bazy. Bazuj tylko wtedy, gdy dokonane zmiany pozostały lokalne w twoim repozytorium, np. Nie wypchnąłeś żadnych oddziałów do innego repozytorium. Rebasing to świetne narzędzie do uporządkowania lokalnych zobowiązań w użyteczną kolejność przed wypchnięciem ich na świat, ale późniejsze ich wycofanie zepsuje rzeczy początkującym początkującym użytkownikom.

Sven
źródło
7
Nie. Zatwierdzenie naprawiające błąd pojawia się tylko raz w gałęzi poprawki, nawet jeśli nazwa gałęzi zostanie usunięta po scaleniu z gałęzią master i devel. Zatwierdzanie scalania pokazuje tylko zmiany wprowadzone przez scalanie, które wygląda jak duplikat zatwierdzenia. Ale tak działa git: Rozgałęź i scal ponownie. Prawdziwe prace programistyczne odbywają się tylko w zatwierdzeniach niezwiązanych z scalaniem, a scalanie jest akceptowane tylko wtedy, gdy rezultatem jest działające oprogramowanie.
Sven
42
To powinna być zaakceptowana odpowiedź. Działa również z funkcją żądania ściągania GitHub.
Nostalg.io 16.04.15
125
Myślę, że warto zauważyć, że git merge masterpołączenie połączy się z lokalną kopią wzorca, więc nawet jeśli zrobiłeś coś git pullw gałęzi funkcji po tym, jak ktoś inny połączył inną gałąź w wzorzec, musisz git checkout masterwtedy git pull, a potem git checkout feature1znowu , a potem git merge master.
damick,
50
@damick Lub tylko git fetchigit merge origin/master
Yngvar Kristiansen
20
@damick @ yngvar-kristiansen git pull origin masterautomatycznie połączy orgin/mastersię z bieżącym oddziałem
L422Y
612

Powinieneś być w stanie dokonać zmiany bazy na master:

git checkout feature1
git rebase master

Zarządzaj wszystkimi pojawiającymi się konfliktami. Kiedy dojdziesz do zatwierdzeń z poprawkami błędów (już w master), Git powie, że nie było żadnych zmian i że być może zostały już zastosowane. Następnie kontynuujesz rebase (pomijając zatwierdzenia już w master) za pomocą

git rebase --skip

Jeśli wykonasz polecenie git logw gałęzi funkcji, zobaczysz, że zatwierdzenie poprawki błędu pojawia się tylko raz, w części głównej.

Aby uzyskać bardziej szczegółową dyskusję, zapoznaj się z dokumentacją książki Git na git rebase( https://git-scm.com/docs/git-rebase ), która obejmuje ten dokładny przypadek użycia.

================ Edytuj dla dodatkowego kontekstu ====================

Ta odpowiedź została udzielona specjalnie na pytanie zadane przez @theomega, biorąc pod uwagę jego szczególną sytuację. Zwróć uwagę na tę część:

Chcę zapobiec zatwierdzeniom [...] w mojej gałęzi funkcji, które nie mają związku z implementacją funkcji.

Odbudowanie jego prywatnego oddziału na kapitanie jest dokładnie tym, co przyniesie ten wynik. W przeciwieństwie do tego, połączenie mistrza z jego gałęzią zrobiłoby dokładnie to, czego konkretnie nie chce się wydarzyć : dodanie zmiany, która nie jest związana z implementacją funkcji, nad którą pracuje za pośrednictwem swojego oddziału.

Aby zwrócić się do użytkowników, którzy czytają tytuł pytania, pomiń faktyczną treść i kontekst pytania, a następnie przeczytaj tylko górną odpowiedź na ślepo, zakładając, że zawsze będzie ona dotyczyć ich (innego) przypadku użycia, pozwól mi rozwinąć:

  • bazuj tylko na prywatnych oddziałach (tj. które istnieją tylko w twoim lokalnym repozytorium i nie zostały udostępnione innym). Odtworzenie udostępnionych gałęzi „zepsułoby” kopie, które mogą mieć inne osoby.
  • jeśli chcesz zintegrować zmiany z gałęzi (bez względu na to, czy jest to gałąź główna czy inna) w gałąź publiczną (np. wypchnąłeś gałąź, aby otworzyć żądanie ściągnięcia, ale teraz występują konflikty z master i musisz zaktualizować Twój oddział, aby rozwiązać te konflikty), musisz je połączyć (np. git merge masterjak w odpowiedzi @ Svena).
  • możesz także połączyć oddziały z lokalnymi oddziałami prywatnymi, jeśli takie są twoje preferencje, ale pamiętaj, że spowoduje to „zagraniczne” zatwierdzenia w twoim oddziale.

Na koniec, jeśli jesteś niezadowolony z faktu, że ta odpowiedź nie najlepiej pasuje do Twojej sytuacji, mimo że dotyczyła @theomega, dodanie poniższego komentarza nie będzie szczególnie pomocne: nie kontroluję, która odpowiedź jest wybrana, robi to tylko @ theomega.

David Sulc
źródło
136
Nie, to nie jest bezpieczne: jeśli zmienisz bazę, zmienisz historię oddziału, co wpłynie na programistów, którzy wyciągnęli gałąź. inf act, git nie pozwala domyślnie na wypychanie gałęzi opartej na zmianie: musisz wymusić aktualizację -fprzy wypychaniu w celu zastąpienia gałęzi wersją opartą na zmianie. Bądź ostrożny!
David Sulc
17
Jak profesjonalne zespoły używające git radzą sobie z tym problemem? Czy po prostu uważaj, pomyśl dokładnie, a następnie zrób -f? Czy mój pełny przepływ pracy jest wadliwy, ponieważ potrzebuję -f?
theomega
30
Cóż, zaryzykowałbym zasadę „świętości”, która mówi, że nie bazujesz (ani nie zmieniasz w inny sposób historii zatwierdzeń) na udostępnianym kodzie: dotyczy tylko twojego lokalnego kodu. Zasadniczo powinieneś zmienić zasady, by „wyczyścić” przed udostępnieniem. W twoim przypadku możesz zepchnąć nową gałąź z inną bazą (o innej nazwie) i poprosić kolegów o oparcie swoich zmian na tej gałęzi (tj. Poprzez zmianę bazy z gałęzi lokalnej na nową, jak wyżej). Następnie usuń feature1z Github.
David Sulc
19
Większość profesjonalnych zespołów, nad którymi pracowałem, prawie nigdy nie korzysta z bazy danych - po prostu domyślnie łączą wszystko, aby nigdy nie nastąpiła modyfikacja historii. To mój preferowany sposób pracy. Z drugiej strony niektóre zespoły używają rebase do „czyszczenia” zatwierdzeń, zanim je wypchną (ale nigdy po wypchnięciu).
Jonathan Hartley
11
Tak, NIGDY nie powinieneś bazować na oddziałach publicznych. Jednak pytanie OP wydawało się dotyczyć integracji nowych zobowiązań podjętych masterw oddziale prywatnym (wspomina o „swoim” oddziale lokalnym). W takim przypadku rebasejest w porządku i jest to ten sam przypadek użycia, o którym wspominałeś o „czyszczeniu”.
David Sulc
69

Na podstawie tego artykułu powinieneś:

  • utwórz nowy oddział oparty na nowej wersji master

    git branch -b newmaster

  • połącz swoją starą gałąź funkcji w nową

    git checkout newmaster

  • rozwiązać konflikt w nowej gałęzi funkcji

Pierwsze dwa polecenia można połączyć z git checkout -b newmaster.

W ten sposób Twoja historia pozostaje przejrzysta, ponieważ nie potrzebujesz scalania z powrotem. I nie musisz być tak bardzo ostrożny, ponieważ nie musisz robić bazy Git.

zimi
źródło
7
byłoby miło, gdybyś powiązał polecenie git z każdym punktem. W przeciwnym razie wydaje mi się, że jest to rzeczywiście bezpieczniejsza i czystsza opcja.
VirgileD
@zimi O co chodzi, jeśli mamy zdalny oddział? Czy ponownie odtworzymy gałąź nowej funkcji aktualizacji? Czy możemy po prostu ustawić zdalne przesyłanie danych?
BILL,
@VirgileD Właśnie opublikowałem własną odpowiedź z dodatkowymi szczegółami, w tym związanymi z nimi poleceniami git.
jkdev,
29

git merge

możesz wykonać poniższe kroki

1. Scal origin/mastergałąź z featuregałęzią

# step1: change branch to master, and pull to update all commits
$ git checkout master
$ git pull

# step2: change branch to target, and pull to update commits
$ git checkout feature
$ git pull

# step3: merge master to feature(⚠️ current is feature branch)
$ git merge master

2. Scal featuregałąź z origin/mastergałęzią

origin/masterjest zdalną gałęzią główną, a masterlokalną gałęzią główną

$ git checkout master
$ git pull origin/master

$ git merge feature
$ git push origin/master
xgqfrms
źródło
Wydaje się, że rebase jest przereklamowany! Stare dobre połączenie :)!
Forever
27

Odpowiedź Zimi ogólnie opisuje ten proces. Oto szczegóły:

  1. Utwórz i przełącz się na nowy oddział. Upewnij się, że nowa gałąź jest oparta, masteraby zawierała najnowsze poprawki.

    git checkout master
    git branch feature1_new
    git checkout feature1_new
    
    # Or, combined into one command:
    git checkout -b feature1_new master
    
  2. Po przejściu do nowej gałęzi scal zmiany z istniejącej gałęzi funkcji. Spowoduje to dodanie zatwierdzeń bez powielania zatwierdzeń poprawek.

    git merge feature1
    
  3. W nowej gałęzi rozwiąż wszelkie konflikty między funkcją a gałęzią główną.

Gotowy! Teraz skorzystaj z nowego oddziału, aby nadal rozwijać swoją funkcję.

jkdev
źródło
2
Problem polega na tym, że deweloper marnuje czas na ciągłe tworzenie nowych gałęzi, gdy muszą aktualizować w stosunku do mastera. Robilibyśmy wiele oddziałów, prawdopodobnie 3 razy dziennie podczas aktywnej pracy. Powinieneś napisać instrukcje na temat czyszczenia wszystkich lokalnych gałęzi śmieci oraz tego, jak się ich pozbyć na odległość. Potrzebujemy również porady na temat nazewnictwa wszystkich tych gałęzi, abyśmy nie byli zdezorientowani. Bez tego przekształci system oddziałów w chaos.
pauljohn32
4
Masz rację, nie należy tego robić cały czas. Tylko wtedy, gdy (1) zmiany w master są konieczne dla twojej funkcji lub (2) masz zamiar połączyć swoją gałąź z master i mogą wystąpić konflikty. Aby uniknąć bałaganu, możesz usunąć swój oddział po scaleniu.
jkdev,
11

Oto skrypt, którego możesz użyć do scalenia głównej gałęzi z bieżącą gałęzią.

Skrypt wykonuje następujące czynności:

  • Przełącza na gałąź główną
  • Pobiera gałąź główną
  • Powraca do aktualnego oddziału
  • Scala gałąź główną w bieżącą gałąź

Zapisz ten kod jako plik wsadowy (.bat) i umieść skrypt w dowolnym miejscu w repozytorium. Następnie kliknij go, aby go uruchomić i jesteś gotowy.

:: This batch file pulls current master and merges into current branch

@echo off

:: Option to use the batch file outside the repo and pass the repo path as an arg
set repoPath=%1
cd %repoPath%

FOR /F "tokens=*" %%g IN ('git rev-parse --abbrev-ref HEAD') do (SET currentBranch=%%g)

echo current branch is %currentBranch%
echo switching to master
git checkout master
echo.
echo pulling origin master
git pull origin master
echo.
echo switching back to %currentBranch%
git checkout %currentBranch%
echo.
echo attemting merge master into %currentBranch%
git merge master
echo.
echo script finished successfully
PAUSE
Aaron M.
źródło
10

Możesz być w stanie wykonać „wybranie”, aby pobrać dokładnie te zatwierdzenia, które potrzebujesz w gałęzi funkcji.

Zrób, git checkout hotfix1aby dostać się do gałęzi poprawki 1. Następnie wykonaj a, git logaby uzyskać skrót SHA-1 (duża sekwencja losowych liter i cyfr, które jednoznacznie identyfikują zatwierdzenie) danego zatwierdzenia. Skopiuj ten (lub pierwsze 10 lub więcej znaków).

Następnie, git checkout feature1aby wrócić do gałęzi funkcji.

Następnie, git cherry-pick <the SHA-1 hash that you just copied>

Spowoduje to przeciągnięcie tego zatwierdzenia i tylko tego zatwierdzenia do gałęzi funkcji. Ta zmiana będzie miała miejsce w gałęzi - po prostu „wybrałeś ją”. Następnie wznów pracę, edytuj, zatwierdzaj, wypychaj itd. Do zadowolenia swojego serca.

Kiedy w końcu wykonasz kolejne scalenie z jednej gałęzi do swojej gałęzi funkcji (lub odwrotnie), Git rozpozna, że ​​już scaliłeś się w tym konkretnym zatwierdzeniu, wie, że nie musi to robić ponownie, i po prostu „przeskocz”.

Bob Gilmore
źródło
Nie uważam tego za dobry pomysł. Następnie IMO, zatwierdzenie poprawki pojawi się naprawdę w historii twojej gałęzi funkcji, czego w zasadzie nie chcesz.
Martin Pecka
1
„Kiedy ostatecznie wykonasz kolejne scalenie z jednej gałęzi do swojej gałęzi funkcji (lub odwrotnie), git rozpozna, że ​​już scaliłeś [...]” - czy tak to faktycznie działa? Nie wydaje mi się, żeby to git mergedziałało w tym „powtórzeniu zatwierdzeń” - tak, jakbyś sugerował („i po prostu pomiń”). Mieszanie zbioru wiśni i łączenie może najwyraźniej prowadzić do problemów; patrz: news.ycombinator.com/item?id=3947950
Guildenstern
0

Jestem w dziale funkcji i dokonałem refaktoryzacji. Chcę teraz scalić główne zmiany z moją gałęzią funkcji. Jestem daleko w tyle. Uwaga: Nie chcę pobierać zmian głównych do mojego lokalnego, ponieważ moja gałąź funkcji ma moduły przeniesione z jednego miejsca do drugiego. Stwierdziłem, że wykonywanie poniżej bez ściągania nie działa. jest napisane „Już aktualne”.

 //below does not get the latest from remote master to my local feature branch without git pull
    git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge master

Działa to poniżej, zauważ użycie git merge origin / master:

 git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge origin/master
Tony
źródło