Jak selektywnie scalać lub wybierać zmiany z innej gałęzi w Git?

1449

Korzystam z git w nowym projekcie, który ma dwie równoległe - ale obecnie eksperymentalne - gałęzie programistyczne:

  • master: import istniejącej bazy kodu oraz kilka modów, których jestem ogólnie pewien
  • exp1: gałąź eksperymentalna # 1
  • exp2: gałąź eksperymentalna # 2

exp1i exp2reprezentują dwa bardzo różne podejścia architektoniczne. Dopóki nie przejdę dalej, nie mam możliwości dowiedzieć się, który z nich (jeśli którykolwiek) zadziała. Gdy robię postępy w jednej gałęzi, czasami mam zmiany, które byłyby przydatne w drugiej gałęzi i chciałbym scalić tylko te.

Jaki jest najlepszy sposób łączenia selektywnych zmian z jednej gałęzi rozwoju do drugiej, pozostawiając za sobą wszystko inne?

Podejścia, które rozważałem:

  1. git merge --no-commit po czym następuje ręczne odstawianie dużej liczby zmian, których nie chcę robić wspólnie między gałęziami.

  2. Ręczne kopiowanie wspólnych plików do katalogu tymczasowego, a następnie git checkoutprzejście do drugiej gałęzi, a następnie dalsze ręczne kopiowanie z katalogu tymczasowego do drzewa roboczego.

  3. Odmiana powyższego. Na razie porzuć expgałęzie i użyj dwóch dodatkowych lokalnych repozytoriów do eksperymentów. Dzięki temu ręczne kopiowanie plików jest znacznie prostsze.

Wszystkie trzy podejścia wydają się nużące i podatne na błędy. Mam nadzieję, że istnieje lepsze podejście; coś podobnego do parametru ścieżki filtru, który uczyniłby git-mergebardziej selektywnym.

David Joyner
źródło
5
Jeśli zmiany w gałęziach eksperymentalnych są dobrze zorganizowane w osobnych zatwierdzeniach, lepiej pomyśleć o połączeniu wybiórczych zatwierdzeń zamiast selektywnych plików. Większość odpowiedzi poniżej zakłada, że ​​tak jest.
akaihola
2
Czy połączenie git merge -s ours --no-commitnastępujących po nich nie git read-treebyłoby dobrym rozwiązaniem? Zobacz stackoverflow.com/questions/1214906/…
VonC
34
Nowsze pytanie ma
jednokreskową
Zapoznaj się z
Ashutosh Chamoli

Odpowiedzi:

475

Używasz polecenia cherry-pick, aby uzyskać indywidualne zatwierdzenia z jednej gałęzi.

Jeśli żądane zmiany nie dotyczą pojedynczych zatwierdzeń, użyj metody pokazanej tutaj, aby podzielić zatwierdzenia na poszczególne zatwierdzenia . Z grubsza mówiąc, używasz, git rebase -iaby uzyskać oryginalne zatwierdzenie do edycji, a następnie git reset HEAD^selektywnie przywrócić zmiany, a następnie git commitzatwierdzić ten bit jako nowy zatwierdzenie w historii.

W Red Hat Magazine jest jeszcze jedna fajna metoda, w której używają git add --patchlub ewentualnie git add --interactivepozwalają na dodanie tylko części przystawki, jeśli chcesz podzielić różne zmiany na pojedynczy plik (wyszukaj na tej stronie hasło „split”).

Po podzieleniu zmian możesz teraz wybrać tylko te, które chcesz.

1800 INFORMACJI
źródło
14
Z mojego rozumienia jest to niepotrzebnie bardziej skomplikowane niż odpowiedź, którą głosowało się wyżej.
Alexander Bird
54
Jest to technicznie poprawna odpowiedź, prawidłowa odpowiedź wydaje się „skomplikowana”. --- Wyższa głosowana odpowiedź to tylko szybka i brudna odpowiedź „robi podstęp”, która dla większości ludzi jest wszystkim, o co chodzi (:
Jacob
3
@akaihola: HEAD ^ jest poprawny. Zobacz man git-rev-pars: Sufiks ^ parametru rewizji oznacza pierwszego rodzica tego obiektu zatwierdzenia. Notacja przedrostka ^ służy do wykluczenia zatwierdzeń osiągalnych z zatwierdzenia.
Tyler Rick
13
Chciałem tylko podzielić się innym podejściem, które wydaje się najczystsze i mniej skomplikowane ze wszystkich: jasonrudolph.com/blog/2009/02/25/… całkowita prostota i niesamowite
superuseroi
14
Zdezorientowany debatą na temat tego, które podejście jest „prawidłowe”? Rozważ różnicę między plikami i zatwierdzeniami (patrz Uwagi na dole) . OP chce scalić PLIKI i nie wspomina o ZOBOWIĄZANIACH. Wyższa głosowana odpowiedź jest specyficzna dla plików; w zaakceptowanej odpowiedzi użyto metody „pick-pick”, która jest specyficzna dla zatwierdzeń. Cherry-pick może być kluczem do selektywnego scalania zatwierdzeń, ale może być bardzo bolesne przy przenoszeniu plików z jednej gałęzi do drugiej. Chociaż zatwierdzenia są sercem siły gita, nie zapomnij, że pliki nadal mają znaczenie!
Kay V
970

Miałem dokładnie taki sam problem jak wspomniany powyżej. Ale znalazłem to wyraźniej w wyjaśnianiu odpowiedź.

Podsumowanie:

  • Sprawdź ścieżki z gałęzi, którą chcesz scalić,

    $ git checkout source_branch -- <paths>...
    

    Wskazówka: Działa to również bez --widocznego w połączonym poście.

  • lub do selektywnego łączenia kawałków

    $ git checkout -p source_branch -- <paths>...
    

    Alternatywnie użyj resetowania, a następnie dodaj z opcją -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Wreszcie dokonaj

    $ git commit -m "'Merge' these changes"
    
Chris Steinbach
źródło
9
Połączony artykuł Barta J jest najlepszym podejściem. Jasne, proste, jedno polecenie. To ten, którego zamierzam użyć. :)
Pistos
256
To nie jest prawdziwe połączenie. Wybieracie zmiany według pliku zamiast przez zatwierdzenie, a stracicie wszelkie istniejące informacje o zatwierdzeniu (autor, wiadomość). To prawda, że ​​jest to dobre, jeśli chcesz scalić wszystkie zmiany w niektórych plikach i dobrze, że musisz ponownie wykonać wszystkie zatwierdzenia. Ale jeśli pliki zawierają zarówno zmiany do scalenia, jak i inne do odrzucenia, jedna z metod podanych w innych odpowiedziach będzie ci lepiej służyć.
akaihola
10
@mykhal i inni: powoduje to automatyczne stopniowanie plików w indeksie, więc jeśli wymeldowałeś się, foo.czrób to, git reset HEAD foo.caby rozproszyć ten plik, a następnie możesz go różnicować. Dowiedziałem się tego po wypróbowaniu go i
wróceniu
12
aby zobaczyć zmiany, których możesz użyć:git diff --cached
OderWat
9
Zgodnie z tą odpowiedzią git checkout -p <revision> -- <path> będzie to samo, co wydawanie pierwszych trzech poleceń, które opisałeś :)
7hi4g0
337

Aby selektywnie scalić pliki z jednej gałęzi do drugiej, uruchom

git merge --no-ff --no-commit branchX

gdzie branchXjest gałąź, z której chcesz się połączyć w bieżącą gałąź.

Ta --no-commitopcja spowoduje wyodrębnienie plików, które zostały scalone przez Git, bez faktycznego ich zatwierdzania. Dzięki temu będziesz mógł modyfikować scalone pliki w dowolny sposób, a następnie samodzielnie je zatwierdzić.

W zależności od sposobu scalenia plików istnieją cztery przypadki:

1) Chcesz prawdziwego scalenia.

W takim przypadku akceptujesz scalone pliki tak, jak Git scalił je automatycznie, a następnie zatwierdzasz.

2) Istnieje kilka plików, których nie chcesz scalać.

Na przykład chcesz zachować wersję w bieżącej gałęzi i zignorować wersję w gałęzi, z której się łączysz.

Aby wybrać wersję w bieżącym oddziale, uruchom:

git checkout HEAD file1

Spowoduje to pobranie wersji z file1bieżącej gałęzi i zastąpienie file1automatyzowanego przez Git.

3) Jeśli chcesz wersję w branchX (a nie prawdziwe scalenie).

Biegać:

git checkout branchX file1

Będzie to pobrać wersję file1w branchXi nadpisywania file1automatycznie połączone przez Git.

4) Ostatni przypadek dotyczy wybrania tylko określonych połączeń file1.

W takim przypadku możesz edytować zmodyfikowany plik file1bezpośrednio, zaktualizować go do dowolnej wersji file1, a następnie zatwierdzić.

Jeśli Git nie może automatycznie scalić pliku, zgłosi plik jako „nie połączony ” i utworzy kopię, w której będziesz musiał ręcznie rozwiązać konflikty.



Aby wyjaśnić dalej przykładem, powiedzmy, że chcesz połączyć branchXsię z bieżącą gałęzią:

git merge --no-ff --no-commit branchX

Następnie uruchom git statuspolecenie, aby wyświetlić status zmodyfikowanych plików.

Na przykład:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Gdzie file1, file2i file3są pliki git mają pomyślnie auto-scalone.

Co to oznacza to, że zmiany masteri branchXdla wszystkich tych trzech plików zostały połączone razem bez żadnych konfliktów.

Możesz sprawdzić, jak zostało wykonane scalenie, uruchamiając git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Jeśli uznasz, że scalenie jest niepożądane, możesz to zrobić

  1. edytuj plik bezpośrednio
  2. zapisać
  3. git commit

Jeśli nie chcesz scalać file1i chcesz zachować wersję w bieżącym oddziale

Biegać

git checkout HEAD file1

Jeśli nie chcesz scalić file2i chcesz tylko wersjębranchX

Biegać

git checkout branchX file2

Jeśli chcesz file3zostać scalony automatycznie, nie rób nic.

W tym momencie Git już to połączył.


file4powyżej jest nieudane scalenie przez Git. Oznacza to, że nastąpiły zmiany w obu gałęziach, które występują w tej samej linii. W tym miejscu będziesz musiał ręcznie rozwiązać konflikty. Możesz odrzucić scalone wykonane, edytując plik bezpośrednio lub uruchamiając polecenie kasy dla wersji w gałęzi, którą chcesz file4zostać.


Wreszcie, nie zapomnij git commit.

alvinabad
źródło
10
Ostrożnie: jeśli git merge --no-commit branchXjest to tylko przewijanie do przodu, wskaźnik zostanie zaktualizowany, a więc
no
16
@ cfi Co z dodawaniem, --no-ffaby zapobiec takiemu zachowaniu?
Eduardo Costa
5
Zdecydowanie sugeruję zaktualizowanie tej odpowiedzi za pomocą opcji „--no-ff” Eduardo. Przeczytałem całą rzecz (która poza tym była świetna) tylko po to, aby moje połączenie zostało szybko przewinięte.
Funktr0n
7
To rozwiązanie zapewnia najlepsze wyniki i elastyczność.
Thiago Macedo
20
W odróżnieniu od odpowiedzi z największą liczbą głosów, to rozwiązanie zachowało moją historię scalania, co jest dla mnie ważne, ponieważ wplatam częściowe zatwierdzenia w obie strony. Nie wypróbowałem wszystkich innych sugerowanych rozwiązań, więc być może niektóre z nich również to robią.
ws_e_c421,
107

Nie podoba mi się powyższe podejście. Korzystanie z pick-pick jest świetne do wybierania pojedynczej zmiany, ale jest to ból, jeśli chcesz wprowadzić wszystkie zmiany z wyjątkiem niektórych złych. Oto moje podejście.

Nie ma --interactiveargumentu, który można przekazać, aby połączyć git.

Oto alternatywa:

Masz pewne zmiany w „funkcji” gałęzi i chcesz wprowadzić niektóre, ale nie wszystkie z nich, do „master” w nie niechlujny sposób (tzn. Nie chcesz wybierać i zatwierdzać każdego z nich)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Więc po prostu zawiń to w skrypcie powłoki, zmień master na $ na i zmień funkcję na $ z i możesz zacząć:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp
nosatalian
źródło
Naprawiłem formatowanie - jest to całkiem niezła metoda, jeśli chcesz dokonać wyboru zatwierdzeń
1800 INFORMACJE
Używam teraz tej techniki i wydaje się, że działała naprawdę dobrze.
dylanfm
4
Możesz zmienić git rebase -i $tona git rebase -i $to || $SHELL, aby użytkownik mógł zadzwonić git --skipitp., Jeśli to konieczne, jeśli rebase nie powiedzie się. Warto również połączyć łańcuchy razem z &&zamiast nowych linii.
sircolinton
2
Niestety wydaje się, że link w odpowiedzi jest martwy.
ThomasW
Link jest nie tylko martwy, ale ma ostrzeżenie o złej reputacji WOT. Dlatego go usunąłem.
Jean-François Corbett,
93

Jest jeszcze jedna droga:

git checkout -p

Jest to połączenie pomiędzy git checkouti git add -pmoże być dokładnie tym, czego szukasz:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.
Chronial
źródło
10
Jest to zdecydowanie najłatwiejsza i najprostsza metoda, pod warunkiem, że masz tylko możliwą do zarządzania liczbę zmian do scalenia. Mam nadzieję, że więcej osób zauważy tę odpowiedź i ją poprze. Przykład: git checkout --patch exp1 file_to_merge
Tyler Rick
1
Podobna odpowiedź wysłana na to pytanie: stackoverflow.com/a/11593308/47185
Tyler Rick
Och, nie wiedziałem, że kasa ma łatkę! Zamiast tego zrobiłem checkout / reset / add -p.
Daniel C. Sobral
2
Naprawdę najprostsza metoda. git checkout -p featurebran nazwa pliku. A najlepsze jest to, że po uruchomieniu polecenia daje on ay / n / e /? / ... itd. opcja decydowania o sposobie scalenia pliku. Próbowałem z e i mogłem nawet edytować łatkę przed zastosowaniem ... Jak fajnie. Prawdziwy jeden linijka do łączenia wybranych plików z innych gałęzi.
zaatakowany
55

Chociaż niektóre z tych odpowiedzi są całkiem dobre, wydaje mi się, że żadna z nich nie odpowiedziała na oryginalne ograniczenie OP: wybieranie określonych plików z poszczególnych gałęzi. To rozwiązanie to robi, ale może być uciążliwe, jeśli istnieje wiele plików.

Powiedzmy, że masz master, exp1i exp2gałęzie. Chcesz scalić jeden plik z każdej gałęzi eksperymentalnej w master. Zrobiłbym coś takiego:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

To da ci różnice w plikach dla każdego pliku, który chcesz. Nic więcej. Nic mniej. Przydaje się, że masz radykalnie różne zmiany plików między wersjami - w moim przypadku zmieniając aplikację z Rails 2 na Rails 3.

EDYCJA : spowoduje to scalenie plików, ale nastąpi inteligentne scalenie. Nie byłem w stanie wymyślić, jak użyć tej metody, aby uzyskać informacje o różnicach w pliku (być może nadal będą występować w przypadku skrajnych różnic. Irytujące małe rzeczy, takie jak białe znaki, zostaną ponownie włączone, chyba że skorzystasz z -s recursive -X ignore-all-spaceopcji)

Eric Hu
źródło
4
Uwaga: możesz zrobić wiele plików z danego oddziału wszystkie w git checkout exp1 path/to/file_a path/to/file_x
jednym wierszu
2
To jest piękne. Zrobiłem, git checkout feature <path>/*aby uzyskać grupy plików.
isherwood
Działa to dobrze, ale dodano dwa dodatkowe obiekty zatwierdzania. Nie jest to wielka sprawa, ale trochę niechlujny
MightyPork
@MightyPork masz rację. Niestety, ponieważ napisałem to tak dawno temu, nie jestem już pewien, dlaczego istnieją kroki „git stash” i „git merge stash” zamiast „git commit”.
Eric Hu
2
Och, to jasne, tak myślę. W ten sposób scala jeden plik, niekoniecznie zastępując poprzednie zmiany w gałęzi docelowej.
MightyPork
48

1800 INFORMACJA odpowiedź jest całkowicie poprawna. Jednak jako git noob „użycie git cherry-pick” nie wystarczyło mi, aby to rozgryźć bez większego kopania w Internecie, więc pomyślałem, że opublikuję bardziej szczegółowy przewodnik na wypadek, gdyby ktoś inny podobna łódź.

Moim przypadkiem użycia było wybiórcze ściąganie zmian z czyjejś gałęzi github do mojej. Jeśli masz już oddział lokalny ze zmianami, musisz wykonać tylko kroki 2 i 5-7.

  1. Utwórz (jeśli nie utworzono) lokalny oddział ze zmianami, które chcesz wprowadzić.

    $ git branch mybranch <base branch>

  2. Przełącz się na to.

    $ git checkout mybranch

  3. Rozwiń potrzebne zmiany z konta drugiej osoby. Jeśli jeszcze tego nie zrobiłeś, będziesz chciał dodać je jako pilota.

    $ git remote add repos-w-changes <git url>

  4. Ściągnij wszystko ze swojej gałęzi.

    $ git pull repos-w-changes branch-i-want

  5. Przeglądaj dzienniki zatwierdzeń, aby zobaczyć, jakie zmiany chcesz:

    $ git log

  6. Wróć do gałęzi, w której chcesz przenieść zmiany.

    $ git checkout originalbranch

  7. Cherry wybieraj swoje zobowiązania, jeden po drugim, za pomocą skrótów.

    $ git cherry-pick -x hash-of-commit

Porada: http://www.sourcemage.org/Git_Guide

Cory
źródło
3
Wskazówka: najpierw użyj git cherrypolecenia (najpierw instrukcja), aby zidentyfikować zatwierdzenia, które nie zostały jeszcze scalone.
akaihola
To działa .. 1. utworzył nowy oddział 2. utworzył niektóre pliki / wprowadził pewne zmiany 3. zatwierdził 4. wypisał gałąź master 5. Uruchom git cherry-pick -x hash-of-commit i rozwiąż konflikty scalania, czy jesteś dobry iść.
RamPrasadBismil
Twój link już nie działa. Czy możesz to zaktualizować?
creep3007
42

Oto jak możesz zamienić Myclass.javaplik w mastergałęzi na Myclass.javaw feature1gałęzi. Będzie działać, nawet jeśli Myclass.javanie istnieje master.

git checkout master
git checkout feature1 Myclass.java

Zauważ, że to zastąpi - nie scali - i zignoruje lokalne zmiany w gałęzi master.

maestr0
źródło
6
To się nie połączy. Po prostu nadpisze zmiany na wzorcu zmianami z gałęzi feature1.
Skunkwaffle
3
Idealnie szukałem tego rodzaju połączenia, w którym theirszastąpienie ours=> +1 Pozdrawiam;)
olibre
1
Czasami wszystko, co chcesz zrobić, to zastąpić cały plik, więc tego właśnie chciałem, ale musisz upewnić się, że chcesz stracić wszystkie zmiany wprowadzone w tym pliku.
MagicLAMP,
1
Najczystsze rozwiązanie, biorąc pod uwagę, że OP szczególnie chciał zastąpić cały plik ekwiwalentem w innej gałęzi:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust
29

Prosty sposób, aby faktycznie połączyć określone pliki z dwóch gałęzi, a nie tylko zastąpić określone pliki plikami z innej gałęzi.

Krok pierwszy: Zróżnicuj gałęzie

git diff branch_b > my_patch_file.patch

Tworzy plik poprawki różnicy między bieżącą gałęzią a gałęzią_b

Krok drugi: Zastosuj poprawkę do plików pasujących do wzorca

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

przydatne uwagi na temat opcji

Można użyć *jako symbolu wieloznacznego we wzorcu dołączania.

Cięcia nie wymagają ucieczki.

Możesz także użyć opcji --exclude i zastosować ją do wszystkiego oprócz plików pasujących do wzorca lub odwrócić łatkę za pomocą opcji -R

Opcja -p1 to pozostałość po komendzie łatki * unix i fakt, że zawartość pliku łatki poprzedza każdą nazwę pliku za pomocą a/lub b/(lub więcej w zależności od tego, jak wygenerowano plik łatki), którą należy usunąć, aby mógł się zorientować prawdziwy plik do ścieżki do pliku, do którego należy zastosować łatkę.

Sprawdź stronę podręcznika dla git-Apply, aby uzyskać więcej opcji.

Krok trzeci: nie ma kroku trzeciego

Oczywiście chcesz zatwierdzić swoje zmiany, ale kto powiedział, że nie masz innych powiązanych poprawek, które chcesz zrobić przed dokonaniem zatwierdzenia.

masukomi
źródło
1
Było to bardzo przydatne, gdy w bieżącej gałęzi wprowadzono wiele „dodatkowych” zmian, które należało zachować. Mam różnicę tylko zmian wprowadzonych przez branch_b jako: git diff HEAD ... branch_b (tak - trzy okresy to magiczna sztuczka).
Saad Malik,
@masukomi, W kroku 2 nie powinieneś dodawać pliku łatki utworzonego w kroku 1 jako argumentu?
Spiralis
Dla mnie wszystkie zmiany są odrzucane. Masz pomysł, dlaczego?
LinusGeffarth
początkowa myśl @LinusGeffarth jest taka, że ​​może masz gałęzie do tyłu podczas tworzenia łatki? skontaktuje się poza SO, aby sprawdzić, czy damy radę.
masukomi
24

Oto, w jaki sposób możesz sprawić, aby historia śledziła tylko kilka plików z innej gałęzi przy minimalnym wysiłku, nawet jeśli bardziej „prosta” fuzja przyniosłaby o wiele więcej zmian, których nie chcesz.

Po pierwsze, podejmiesz niezwykły krok, by z góry zadeklarować, że to, co zamierzasz wykonać, to scalenie, bez robienia czegokolwiek z plikami w twoim katalogu roboczym:

git merge --no-ff --no-commit -s ours branchname1

. . . gdzie „nazwa gałęzi” oznacza to, z czego chcesz się połączyć. Jeśli miałbyś dokonać od razu, nie wprowadziłby żadnych zmian, ale nadal pokazywałby pochodzenie z drugiej gałęzi. Możesz dodać więcej oddziałów / tagów / itp. do linii poleceń, jeśli potrzebujesz. W tym momencie jednak nie ma żadnych zmian do zatwierdzenia, więc pobierz pliki z innych wersji, następnie.

git checkout branchname1 -- file1 file2 etc

Jeśli łączysz się z więcej niż jednym innym oddziałem, powtórz w razie potrzeby.

git checkout branchname2 -- file3 file4 etc

Teraz pliki z drugiej gałęzi są w indeksie, gotowe do zatwierdzenia, z historią.

git commit

i będziesz musiał wiele wyjaśnić w tym komunikacie zatwierdzenia.

Pamiętaj jednak, że w przypadku, gdy nie było to jasne, jest to zrujnowane. Nie jest to zgodne z duchem tego, do czego służy „gałąź”, a wybieranie wiśni jest tutaj bardziej uczciwym sposobem na robienie tego, co robisz. Jeśli chcesz wykonać kolejne „scalenie” innych plików w tym samym oddziale, którego nie przyniosłeś ostatnim razem, zatrzyma Cię komunikat „już aktualne”. Jest to objaw braku rozgałęzienia, kiedy powinniśmy mieć, w gałęzi „od” powinna znajdować się więcej niż jedna inna gałąź.

jejese
źródło
3
Twoje pierwsze polecenie ( git merge --no-ff --no-commit -s outs branchname1) jest dokładnie tym, czego szukałem! Dzięki!
RobM
1
Z wieloma gałęziami, wymaganą historią, potrzebą scalenia pojedynczych plików i zmianą zawartości pliku przed wypchnięciem, wydaje się to przyzwoitą alternatywą. Na przykład dev => master, ale chcesz zmienić definicję hosta lub podobną przed wypchnięciem do master.
timss
15

Wiem, że jestem trochę spóźniony, ale to jest mój proces roboczy do łączenia wybranych plików.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit
Felix
źródło
Użyłem niewielkiej zmienności tego. Zamiast się połączyć, wybrałem wiśnie. To działa. Jedynym minusem tego podejścia jest utrata odniesienia do pierwotnego skrótu zatwierdzenia.
Matt Florence
15

Najprościej jest ustawić repo w gałęzi, z którą chcesz się połączyć, a następnie uruchomić,

git checkout [branch with file] [path to file you would like to merge]

Jeśli uciekniesz

git status

zobaczysz plik już wyreżyserowany ...

Następnie uruchomić

git commit -m "Merge changes on '[branch]' to [file]"

Prosty.

dsieczko
źródło
3
To prawie najlepsza odpowiedź, jaką znalazłem. proszę zobaczyć jasonrudolph.com/blog/2009/02/25/… Tak jasne, zwięzłe i to po prostu działa!
superuseroi
1
które całkowicie zastąpią zawartość plików z gałęzi źródłowej, a nie scalą
Amare,
Właśnie miałem odpowiedzieć w ten sposób, myślałem, że wymyślę nowe rzeczy, na które jeszcze nie ma odpowiedzi! Ale to najprostszy sposób na zrobienie tego. To powinno być na górze!
Irfandy Jip,
15

Znalazłem ten post zawierający najprostszą odpowiedź. Wystarczy zrobić:

$ #git checkout <branch from which you want files> <file paths>

Przykład:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Zobacz post, aby uzyskać więcej informacji.

Szlagier
źródło
3
Tak naprawdę to się nie łączy, zastępuje plik w bieżącym oddziale.
Igor Ralic
1
@igrali To przydatny komentarz, ale w porównaniu z trudnością „właściwych” sposobów, aby to zrobić, jest to dobre obejście. Trzeba tylko być bardzo ostrożnym.
owensmartin
12

Dziwne, że git wciąż nie ma tak wygodnego narzędzia „po wyjęciu z pudełka”. Bardzo go używam, gdy aktualizuję starą gałąź wersji (która wciąż ma wielu użytkowników oprogramowania) tylko przez kilka poprawek z aktualnej gałęzi wersji. W takim przypadku często potrzebne jest szybkie pobranie tylko niektórych wierszy kodu z pliku w bagażniku, ignorując wiele innych zmian (które nie powinny wchodzić w starą wersję) ... I oczywiście interaktywne scalanie trójstronne jest potrzebny w tym przypadku, git checkout --patch <branch> <file path>nie nadaje się do tego celu selektywnego scalania.

Możesz to zrobić łatwo:

Wystarczy dodać ten wiersz do [alias]sekcji w pliku globalnym .gitconfiglub lokalnym .git/config:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Oznacza to, że używasz Beyond Compare. W razie potrzeby zmień oprogramowanie na wybrane. Możesz też zmienić to na automatyczne łączenie trójstronne, jeśli nie potrzebujesz interaktywnego scalania selektywnego:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Następnie użyj takiego:

git mergetool-file <source branch> <file path>

To da ci prawdziwą możliwość selektywnego łączenia drzewa dowolnego pliku w innej gałęzi.

JimStar
źródło
10

Nie jest to dokładnie to, czego szukałeś, ale było dla mnie przydatne:

git checkout -p <branch> -- <paths> ...

Jest to mieszanka niektórych odpowiedzi.

Felipe
źródło
2
Jest to rzeczywiście przydatne i można je dodać do najlepszej dla mnie odpowiedzi, @@ alvinabad. Podczas wykonywania: git checkout HEAD file1aby zachować bieżącą wersję i rozłączyć plik file1, można użyć -popcji, aby wybrać część pliku do scalenia. dzięki za podstęp!
Simon C.
To moja ulubiona odpowiedź. Proste, do rzeczy i działa
Jesse Reza Khorasanee
8

Zrobiłbym

git diff commit1..commit2 filepattern | git-apply --index && git commit

W ten sposób możesz ograniczyć zakres zatwierdzeń dla pliku z gałęzi.

Skradzione z: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html

Lumpidu
źródło
W niektórych sytuacjach może to być bardzo przydatne. Jeśli jednak zmiany dotyczą innej gałęzi, możesz po prostu przejść do kasy od końca tej gałęzi, jak w powyższej odpowiedzi Barta J.
cdunn2001
Kim jest Bart Js?
Czarny
8

Miałem dokładnie taki sam problem jak wspomniany powyżej. Ale ten blog git wyjaśnił mi odpowiedź.

Polecenie z powyższego linku:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
Susheel Javadi
źródło
testowałeś to? jestem pewien, że pliki zostaną zastąpione z <branch_you_want_to_merge_from> zamiast scalone
Amare
7

Podoba mi się odpowiedź „git-interactive-merge” powyżej, ale jest jedna łatwiejsza. Pozwól, aby git zrobił to za Ciebie, używając kombinacji interaktywnej rebase i na:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Tak więc przypadek polega na tym, że chcesz C1 i C2 z gałęzi „cecha” (punkt rozgałęzienia „A”), ale na razie nie ma reszty.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Który, jak wyżej, przenosi cię do interaktywnego edytora, w którym wybierasz linie „pick” dla C1 i C2 (jak wyżej). Zapisz i wyjdź, a następnie przejdzie do rebase i da ci gałąź „temp”, a także HEAD w master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Następnie możesz po prostu zaktualizować master do HEAD i usunąć gałąź temp i możesz już iść:

# git branch -f master HEAD
# git branch -d temp
Przebrnąć
źródło
7

Co git reset --soft branch? Dziwi mnie, że nikt jeszcze o tym nie wspominał.

Dla mnie to najłatwiejszy sposób wybiórczego wybierania zmian z innej gałęzi, ponieważ to polecenie umieszcza w moim drzewie roboczym wszystkie zmiany różnic i mogę łatwo wybrać lub przywrócić to, którego potrzebuję. W ten sposób mam pełną kontrolę nad zatwierdzonymi plikami.

GarryOne
źródło
6

Wiem, że to pytanie jest stare i istnieje wiele innych odpowiedzi, ale napisałem własny skrypt o nazwie „pmerge”, aby częściowo scalić katalogi. To praca w toku i wciąż uczę się skryptów git i bash.

To polecenie używa, git merge --no-commita następnie usuwa zmiany, które nie pasują do podanej ścieżki.

Zastosowanie: git pmerge branch path
Przykład:git merge develop src/

Nie testowałem tego szeroko. Katalog roboczy powinien być wolny od wszelkich niezatwierdzonych zmian i nieśledzonych plików.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
Andy
źródło
4

Możesz użyć read-treedo odczytu lub scalenia danego zdalnego drzewa z bieżącym indeksem, na przykład:

git remote add foo [email protected]/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Aby wykonać scalenie, użyj -mzamiast tego.

Zobacz także: Jak scalić podkatalog w git?

kenorb
źródło
3

Proste podejście do selektywnego łączenia / zatwierdzania według pliku:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes

Dave C.
źródło
3

Jeśli nie masz zbyt wielu plików, które się zmieniły, nie będziesz mieć dodatkowych zobowiązań.

1. Tymczasowo zduplikuj oddział
$ git checkout -b temp_branch

2. Zresetuj do ostatnio poszukiwanego zatwierdzenia
$ git reset --hard HEAD~n , gdzie njest liczba zatwierdzeń, które musisz cofnąć

3. Pobierz każdy plik z oryginalnego oddziału
$ git checkout origin/original_branch filename.ext

Teraz możesz zatwierdzać i wymuszać push (aby zastąpić zdalnie), jeśli to konieczne.

JBaczuk
źródło
3

Jeśli tylko trzeba scalić konkretny katalog i zostawić wszystko w nienaruszonym stanie i jeszcze zachować historię, można ewentualnie spróbować ... utworzyć nową target-branchoff z masterprzed eksperymentem.

Poniższe kroki zakładają, że masz dwie gałęzie target-branchi source-branch, a katalog dir-to-merge, który chcesz scalić, znajduje się w source-branch. Załóżmy również, że masz inne katalogi, takie jak dir-to-retainw celu, których nie chcesz zmieniać i przechowywać historię. Zakłada się również, że występują konflikty scalania w dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.
code4kix
źródło
2

Kiedy tylko kilka plików zmieniło się między bieżącymi zatwierdzeniami dwóch gałęzi, ręcznie scalam zmiany, przeglądając różne pliki.

git difftoll <branch-1>..<branch-2>

raratiru
źródło