Jak scalić podkatalog w Git?

84

Czy można scalić tylko zmiany w podkatalogu z lokalnego oddziału Git do zdalnej gałęzi Git, czy jest to „wszystko albo nic”?

Na przykład mam:

branch-a
 - content-1
 - dir-1
   - content-2

i

branch-b
 - content-1
 - dir-1
   - `content-2

Chcę tylko połączyć zawartość branch-a dir-1 z zawartością branch-b dir-1.

Brad Gessler
źródło
1
Myślę, że to jest duplikat: stackoverflow.com/questions/449541/…
Karl Voigtland

Odpowiedzi:

81

Jako alternatywa dla pytania SO „ Jak połączyć wybrane pliki z git-merge? ”, Właśnie znalazłem ten wątek GitHub, który mógłby być bardziej przystosowany do scalania całego podkatalogu, opartego na drzewie odczytu git :

  • Moje repozytorium => cookbooks
    katalog docelowy mojego repozytorium =>cookbooks/cassandra
  • Zdalne repozytorium => infochimps
    Zdalne źródło repozytorium, które chcę połączyć z cookbooks/cassandra=>infochimps/cookbooks/cassandra

Oto polecenia, których użyłem do ich scalenia

  • Dodaj repozytorium i pobierz je
git remote add -f infochimps git: //github.com/infochimps/cluster_chef.git
  • Wykonaj scalanie
git merge --allow-unrelated-histories -s ours --no-commit infochimps / master

(to wykonuje scalanie przy użyciu strategii „nasza” ( -s ours), która odrzuca zmiany z gałęzi źródłowej. Zapisuje fakt infochimps/masterscalenia, bez faktycznej modyfikacji żadnego pliku w gałęzi docelowej)

  • Scal tylko infochimps/cookbooks/cassandrawcassandra
git read-tree --prefix = cassandra / -u infochimps / master: cookbooks / cassandra

Powoduje to odczytanie drzewa tylko dla wymaganego podkatalogu źródłowego, tj. cookbooks/cassandraW górnej gałęzi repozytorium źródłowego .

Zwróć uwagę, że nazwa podkatalogu docelowego również powinna być cookbooks/cassandra, lub zobaczysz:

fatal: Not a valid object name
  • Zatwierdź zmianę
 git commit -m 'scalanie w infochimps cassandra'

Uzupełnienie

To dziwne, [edytuj mnie] - ale read-treekrok może się nie udać w ten sposób:

error: Entry 'infochimps/cookbooks/cassandra/README' overlaps with 'cookbooks/cassandra/README'. Cannot bind.

... nawet jeśli oba pliki są identyczne . To może pomóc:

git rm -r cassandra
git read-tree --prefix=cassandra/ -u infochimps/master:cookbooks/cassandra

Ale oczywiście sprawdź ręcznie, czy robi to, co chcesz.

VonC
źródło
3
@Martin git-scm.com/docs/git-rev-parse#_specifying_revisions szukać <rev>:<path>np HEAD:README, :README,master:./README
VonC
6
git read-treeKrok nie dla mnie:error: Entry 'foo/bar/baz.php' overlaps with 'bar/baz.php'. Cannot bind.
Weston RüTER
1
@VonC, ale nie, potrzebuję historii. Ta odpowiedź nie uwzględnia przypadku, w którym istnieją lokalne modyfikacje plików w drzewie. Więc musi się scalić.
Weston Ruter
U mnie ten overlaps withbłąd też wyskakuje na identycznych plikach . Jakie to dziwne?
ulidtko
1
@ChrisHalcrow Aby zarejestrować fakt, że infochimps/masterzostał scalony, ale bez faktycznej modyfikacji żadnego pliku w gałęzi docelowej. Ponieważ następny krok git read-tree --prefix=cassandradokona modyfikacji. Ostateczne zatwierdzenie zapisze rzeczywistą zawartość „scalania”.
VonC
29

Na przykład załóżmy, że masz gałąź „source” i gałąź „destination”, które odzwierciedlają własne wersje (lub nie, jeśli są tylko lokalne) i są ściągane do najnowszego kodu. Powiedzmy, że chcę podkatalogu w repozytorium o nazwie newFeature, który istnieje tylko w gałęzi „source”.

git checkout destination
git checkout source newFeature/
git commit -am "Merged the new feature from source to destination branch."
git pull --rebase
git push

Jest znacznie mniej zagmatwany niż wszystko inne, co widziałem i to działało idealnie dla mnie, znalezione tutaj .

Zwróć uwagę, że nie jest to „prawdziwe scalanie”, więc nie będziesz mieć informacji o zatwierdzeniu nowej funkcji w gałęzi docelowej, a jedynie modyfikacje plików w tym podkatalogu. Ale ponieważ prawdopodobnie zamierzasz później scalić całą gałąź z powrotem lub ją odrzucić, może to nie stanowić problemu.

Chris Arena
źródło
2
Czy zachowuje historię?
Bibrak
2
@Bibrak to podejście NIE chroni historii. Przynajmniej nie z bieżącymi poleceniami.
Ravi Gidwani
6

Dostałem to z wątku na forum w Eclipse i działało to jak marzenie:

git checkout source-branch
git checkout target-branch <directories-or-files-you-do-**NOT**-want> 
git commit
git checkout target-branch
git merge source-branch
tobiasbayer
źródło
1
Próbowałem tego i otrzymałem katalog z gałęzi źródłowej w gałęzi docelowej, którego nie chciałem. Pomimo tego, że zostało to określone w zestawie reżerów, nie chciałem tego drugiego polecenia.
maraton
2
To się nie łączy, zastępuje folder folderem z gałęzi źródłowej.
Gp2mv3
@ Gp2mv3 Myślę, że wygląda solidnie. checkoutsprawia, że ​​foldery są takie same => różnica jest tylko unfolders checkout=> mergeróżnica. To ważna strategia.
Caveman
5

Biorąc pod uwagę scenariusz OP, w którym mają dwie gałęzie, ale chcą scalić tylko historię dir-1 z gałęzi-a do gałęzi-b :

# Make sure you are in the branch with the changes you want
git checkout branch-a

# Split the desired folder into its own temporary branch
# This replays all commits, so it could take a while
git subtree split -P dir-1 -b temp-branch

# Enter the branch where you want to merge the desired changes into
git checkout branch-b

# Merge the changes from the temporary branch
git subtree merge -P dir-1 temp-branch

# Handle any conflicts
git mergetool

# Commit
git commit -am "Merged dir-1 changes from branch-a"

# Delete temp-branch
git branch -d temp-branch
xno
źródło
0

Utwórz repozytorium Git, aby zawierało zarówno gałąź-a, jak i gałąź-b:

git checkout branch-a
git diff branch-b dir-1 > a.diff
patch -R -p1 < a.diff
Sun Yin
źródło
14
Ta odpowiedź wymaga więcej informacji. Co to jest rzeczywisty kod, a co komentarze?
qodeninja
2
Żądający chce scalić. Użycie łaty do przeniesienia zmian automatycznie zgniata wszystkie zatwierdzenia w jedną łatkę, a historia zostanie utracona.
Eric
0

Użyj, git cherry-pickaby wybrać żądane zatwierdzenia i połączyć tylko te zatwierdzenia. Kluczowa sztuczka polega na tym, aby uzyskać te zatwierdzenia w łatwy sposób (aby nie trzeba było ich rozgryzać ręcznie, sprawdzając dziennik Gita i wprowadzając je ręcznie). Oto jak: użyj, git logaby wydrukować identyfikator SHA-1 zatwierdzenia , na przykład:

git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>

„commit-a” to zatwierdzenie bezpośrednio przed punktem początkowym gałęzi do scalenia, a „commit-b” to ostatnie zatwierdzenie w gałęzi do scalenia. '--reverse' drukuje te zatwierdzenia w odwrotnej kolejności do późniejszego wybrania.

Następnie zrób to tak:

git cherry-pick $(git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>)

To dwa kroki, proste i stabilne!

Robert
źródło
doskonały i jedyny sposób na połączenie jednego reżimu z innej branży i zachowanie historii. ale ... ... jeśli chcesz tylko jednego zatwierdzenia, utwórz gałąź ze wszystkich zatwierdzeń z pierwszego polecenia, a następnie połącz tę nową gałąź w jedno scalenie typu squash ....
tom
1
Zakłada się, że znalezione zatwierdzenia dotyczą tylko danego katalogu. Jeśli zatwierdzenie wpływa zarówno na katalog, który chcesz scalić, jak i na katalog, którego nie chcesz, będzie to zbyt duży wybór.
Scott
Two steps, simple and stable!wcale nie proste :)
Rafa
Jak myślisz, w jaki sposób to proste? @Rafa
Robert
@Robert Nie chciałem sugerować, że twoja odpowiedź nie jest prosta; błąd jest cały git, który ma okropny interfejs i okropny model mentalny pełen tajemnic i przypadkowych nazw i znaczeń.
Rafa