Як об’єднати підкаталог у Git?


84

Чи можна об'єднати лише зміни для підкаталогу з локальної гілки Git у віддалену гілку Git або це "все або нічого"?

Наприклад, я маю:

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

і

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

Я хочу лише об'єднати вміст гілки-a dir-1 із вмістом гілки-b dir-1.


1
Я думаю , що це дублікат: stackoverflow.com/questions/449541 / ...
Karl Voigtland

Відповіді:


81

Як альтернатива запитанню SO " Як ви об'єднуєте вибіркові файли з git-merge? ", Я щойно знайшов цей потік GitHub, який може бути більш пристосований для об'єднання цілого підкаталогу на основі дерева читання git :

  • Моє сховище => cookbooks
    Мій цільовий каталог сховища =>cookbooks/cassandra
  • Віддалене сховище => infochimps
    Віддалене джерело сховища, в яке я хочу об'єднати cookbooks/cassandra=>infochimps/cookbooks/cassandra

Ось команди, які я використовував для їх об’єднання

  • Додайте сховище та отримайте його
git віддалене додавання -f infochimps git: //github.com/infochimps/cluster_chef.git
  • Виконайте злиття
git merge --allow-non-related-histories -s ours - не фіксувати infochimps / master

(це виконує злиття за допомогою стратегії 'ours' ( -s ours), яка відкидає зміни з вихідної гілки. Це реєструє факт infochimps/masterоб'єднання, фактично не змінюючи жодного файлу в цільовій гілці)

  • Об'єднати лише infochimps/cookbooks/cassandraвcassandra
git read-tree --prefix = cassandra / -u infochimps / master: кулінарні книги / кассандра

Це зчитує дерево лише для необхідного підкаталогу джерела, тобто cookbooks/cassandraдля вищої гілки сховища джерел .

Зверніть увагу, що ім'я цільового підкаталогу також має бути cookbooks/cassandra, інакше ви побачите:

fatal: Not a valid object name
  • Внесіть зміни
 git commit -m 'злиття в infochimps cassandra'

Додаток

Це дивно, [редагуйте мене] - але цей read-treeкрок може провалитися так:

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

... навіть коли обидва файли ідентичні . Це може допомогти:

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

Але звичайно, перевірте вручну, що це робить те, що ви хочете.


3
@Martin git-scm.com/docs/git-rev-parse#_specifying_revisions шукає <rev>:<path>, наприклад HEAD:README, :README,master:./README
VonC

6
Для git read-treeмене крок зазнав невдачі:error: Entry 'foo/bar/baz.php' overlaps with 'bar/baz.php'. Cannot bind.
Вестон Рутер,

1
@VonC, але ні, мені потрібна історія. Ця відповідь не враховує випадок, коли файли в дереві мають локальні зміни. Тож його потрібно об’єднати.
Weston Ruter

Для мене overlaps withзгадана вище помилка також з’являється в ідентичних файлах . Як це дивно?
ulidtko

1
@ChrisHalcrow Щоб записати факт, що infochimps/masterбув об’єднаний, але фактично не змінюючи жодного файлу в цільовій гілці. Тому що наступний крок git read-tree --prefix=cassandraзробить модифікацію. В остаточному коміті буде записано фактичний вміст "злиття".
VonC

29

Для мого прикладу, припустимо, у вас є гілка 'джерело' та гілка 'адресат', які обидва відображають власні версії (або ні, якщо це лише локально) і перетягуються до останнього коду. Скажімо, я хочу підкаталог у сховищі, який називається newFeature, який існує лише у гілці '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

Це набагато менш заплутано, ніж все інше, що я бачив, і це прекрасно для мене спрацювало, знайдене тут .

Зверніть увагу, що це не є "реальним злиттям", тому у вас не буде інформації про коміти про newFeature у цільовій гілці, а лише про зміни у файлах у цьому підкаталозі. Але оскільки ви, мабуть, збираєтеся об’єднати всю гілку пізніше або відкинути її, це може не бути проблемою.


2
Чи зберігає вона історію?
Бібрак,

2
@Bibrak такий підхід НЕ зберігає історію. Навпаки, не з поточними командами.
Раві Гідвані

6

Я отримав це з теми форуму в Eclipse, і це спрацювало як шарм:

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

1
Я спробував це і в кінцевому підсумку отримав каталог із гілки джерела в цільовій гілці, який я не хотів. Незважаючи на те, що це було вказано з набором директорій, я не хотів, щоб ця друга команда
марафон

2
Це не зливається, воно замінює папку на папку з вихідної гілки.
Gp2mv3

@ Gp2mv3 Я думаю, це виглядає твердо. checkoutробить папки однаковими => різниця полягає лише в un- checkoutfolders => mergeрізниця. Це дійсна стратегія.
Печерний чоловік

5

Враховуючи сценарій OP, де вони мають дві гілки, але хочуть об’єднати лише історію dir-1 з гілки-a у галузь-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

0

Створіть сховище Git, щоб містити і галузь-а, і галузь-б:

git checkout branch-a
git diff branch-b dir-1 > a.diff
patch -R -p1 < a.diff

14
Ця відповідь потребує додаткової інформації. Що з цього є фактичним кодом проти коментарів?
qodeninja

2
Запитувач хоче об’єднати. Використання патча для перенесення змін автоматично комбінує всі коміти в один патч, і історія втрачається.
Ерік,

0

Використовуйте git cherry-pickдля вибору потрібних комітів та об’єднання лише цих комітів. Ключовий фокус тут полягає в тому, щоб отримати ці коміти простим способом (щоб вам не потрібно було розбирати їх, перевіряючи вручну журнал Git і вводячи їх вручну). Ось як: використовувати git logдля друку ідентифікатора SHA-1 коміту, наприклад:

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

'commit-a' - це коміт безпосередньо перед початковою точкою гілки для злиття, а 'commit-b' - останній коміт у гілці, який слід об’єднати. '--reverse' друкує ці коміти в зворотному порядку для збору вишні пізніше.

Тоді зробіть так:

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

Це два кроки, простий і стабільний!


чудовий і єдиний спосіб об’єднати єдиний каталог з іншої галузі та зберегти історію. але ... ... якщо вам потрібен лише один коміт, створіть гілку з усіх комітів з першої команди, а потім об'єднайте цю нову гілку в одне сквош-злиття ....
том

1
Це передбачає, що знайдені коміти впливають лише на відповідний каталог. Якщо коміт впливає як на каталог, який ви хочете об'єднати, так і на той, який ви цього не зробите, це призведе до вибору занадто багато.
Скотт

Two steps, simple and stable!зовсім не просто :)
Рафа,

То як, на вашу думку, це просто? @Rafa
Роберт,

@ Роберт, я не мав на увазі, що ваша відповідь не проста; вина в усьому git, що має жахливий інтерфейс та жахливу ментальну модель, повну таємниць та випадкових імен та значень.
Рафа
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.