Ошибка слияния после преобразования подмодуля Git в поддерево

У меня есть проект, в котором я изначально использовал подмодули для некоторого зависимого кода. Оказывается, подмодули не совсем подходят для этого проекта (и их сложно использовать на практике), поэтому я конвертирую каждый подмодуль в поддерево (используя новую функцию git-subtree).

В моем рабочем репозитории я успешно удалил каждый подмодуль и добавил старый репозиторий подмодулей в качестве поддерева. Нет проблем с этим.

Когда я перехожу к другому клону и пытаюсь извлечь из первого, на этапе слияния я получаю следующую ошибку:

error: The following untracked working tree files would be overwritten by merge:
        sub/.gitignore
        sub/Makefile
        sub/README
        sub/src/main.c
        ... and so on for all files in sub/
Aborting

Похоже, это связано с тем, что файлы в sub/ на самом деле никогда не существовали в основном репозитории, и когда Git применяет патч для обновления .gitmodules, он не удаляет каталог с файлами подмодуля. При обработке следующей фиксации, когда Git пытается создать новые файлы в sub/, которые являются теперь частью основного репозитория, все эти файлы конфликтуют с все еще существующими файлами в sub/.

Обходной путь, который я нашел, - использовать rm -rf sub перед git pull, что позволяет избежать этой проблемы.

Мой вопрос: есть ли какой-нибудь переключатель командной строки, который я могу использовать с git merge, который говорит: «Перезаписать любые файлы, которые существуют в рабочем каталоге»? Еще лучше была бы функция, в которой git merge будет смотреть на содержимое существующего файла, и, если содержимое идентично файлу, который он все равно собирался создать, подавить сообщение об ошибке и продолжить.

ОБНОВЛЕНИЕ: я создал репозитории Git, которые демонстрируют эту проблему, чтобы показать именно то, о чем я говорю. Воспроизвести:

$ git clone https://github.com/ghewgill/q14224966.git
$ cd q14224966
$ git submodule init
$ git submodule update
$ git merge origin/branch

Это должно привести к сообщению об ошибке

error: The following untracked working tree files would be overwritten by merge:
    sub/Makefile
    sub/README
    sub/src/main.c
Please move or remove them before you can merge.
Aborting

person Greg Hewgill    schedule 08.01.2013    source источник
comment
Вы пробовали это: goo.gl/z1XP9?   -  person Efthymis    schedule 21.01.2013
comment
@Efthymis: полная ссылка: stackoverflow.com/questions/1295171/ (пожалуйста, не используйте здесь сокращатели URL). Я попробовал git merge -s recursive -X theirs origin/master, и возникла та же ошибка.   -  person Greg Hewgill    schedule 21.01.2013
comment
Ага, это похоже на недостаток остроумия git merge. И я жаждал этой награды.   -  person Maic López Sáenz    schedule 22.01.2013
comment
Я поддержал ваш ответ, потому что это случилось и со мной: D   -  person Patt Mehta    schedule 26.01.2013


Ответы (4)


Я знаю, что ваш вопрос касался слияния, но у меня были аналогичные проблемы с объединением подмодулей git. Я думаю, что это решение сработает с вашей проблемой, даже если оно не решает напрямую вопрос слияния.

Я обнаружил, что если принудительно проверить ветку, которую вы хотите объединить, а затем вернуться к мастеру, все работает с подмодулями.

Чтобы заставить что-то работать в вашем примере:

$ git clone https://github.com/ghewgill/q14224966.git
$ cd q14224966
$ git submodule init
$ git submodule update
$ git checkout -f origin/branch
$ git checkout master
$ git merge origin/branch

Это работает, потому что в основном он делает ваш rm -rf шаг за вас. Конечно, это немного окольный путь, и, возможно, не стоит этого делать, если у вас есть только один подмодуль, как в вашем примере. Но я обнаружил, что это довольно экономит время при работе в проекте с большим количеством подмодулей.

Кроме того, как было указано в комментариях, если вы не хотите вносить изменения в дерево работы, вы можете использовать это:

$ git clone https://github.com/ghewgill/q14224966.git
$ cd q14224966
$ git submodule init
$ git submodule update
$ git reset origin/branch
$ git reset --hard master

Это работает примерно так же, но позволяет избежать проверки других файлов в процессе. У меня не было возможности использовать это в дикой природе, но это кажется разумным методом.

Также есть $ git merge -s subtree origin/branch. Он работает с вашим примером, но у меня были неожиданные результаты, когда задействовано более одного подмодуля. Хотя, возможно, тебе повезет больше.

person Jonathan Wren    schedule 21.01.2013
comment
Мне потребовалось несколько секунд, чтобы понять суть, понять, насколько умен этот метод. Я заметил, что теперь вы можете заменить кассы на git reset origin/branch; git reset --hard master; это позволяет избежать всех действий рабочего дерева, за исключением желаемых удалений. - person jthill; 26.01.2013
comment
@jthill Вы правы. Вы можете полностью пропустить работу с деревом работ, используя вместо этого сброс. Я обновлю свой ответ, чтобы отразить это. Спасибо! - person Jonathan Wren; 26.01.2013
comment
К сожалению, второе решение, использующее git reset, не работает, поскольку оно отменяет любые изменения, сделанные в ветке master (скажем, в README) после того, как ветка была создана. - person Greg Hewgill; 28.01.2013
comment
Кроме того, первое решение (с checkout -f) оставляет каталог sub/.git на месте, что может вызвать путаницу в будущем, если только он не будет явно удален с помощью rm -rf. - person Greg Hewgill; 28.01.2013

Вы не можете заставить git-merge (или любую другую команду) принудительно затирать файлы, о которых он не думает, что он знает, нет. git изо всех сил старается не делать ничего полностью необратимого.

Но с большим количеством подмодулей вы можете сделать удаление немного проще и безопаснее с git submodule foreach:

$ git submodule foreach 'rm -rf $toplevel/$path'
Entering 'sub'
$ git merge origin/branch
Updating a231acd..6b4d2f4
Fast-forward
...
person Eevee    schedule 25.01.2013
comment
Это немного упрощает rm -rf, но это все равно решение. - person Greg Hewgill; 28.01.2013
comment
увы да. но вам нужно сделать что-то особенное для этой ситуации, так что с таким же успехом можно сделать простейшую вещь, которая работает - person Eevee; 28.01.2013

(Предостережение: я никогда не работал с поддеревьями и не знаю, насколько сложным является ваше фактическое репо, поэтому эти решения могут на самом деле не работать для вас.)

Поигравшись с вашим образцом репозитория, я нашел два решения, которые, похоже, работают, хотя и создают разные деревья фиксации:

  1. Используйте 1_

    ~/q14224966[master]> git reset --hard origin/master
    HEAD is now at a231acd add submodule
    ~/q14224966[master]> touch other.c && git add . && git commit -m "New commit."
    [master bc771ac] New commit.
     0 files changed
     create mode 100644 other.c
    ~/q14224966[master]> git merge -s resolve origin/branch 
    Trying really trivial in-index merge...
    error: Merge requires file-level merging
    Nope.
    Trying simple merge.
    Simple merge failed, trying Automatic merge.
    Adding sub/Makefile
    Adding sub/README
    Adding sub/src/main.c
    Merge made by the 'resolve' strategy.
     .gitmodules    | 3 ---
     sub            | 1 -
     sub/Makefile   | 1 +
     sub/README     | 1 +
     sub/src/main.c | 1 +
     5 files changed, 3 insertions(+), 4 deletions(-)
     delete mode 160000 sub
     create mode 100644 sub/Makefile
     create mode 100644 sub/README
     create mode 100644 sub/src/main.c
    ~/q14224966[master]> ls
    README   main.c   other.c  sub/
    ~/q14224966[master]> cd sub/
    ~/q14224966/sub[master]> ls
    Makefile  README    src/
    ~/q14224966/sub[master]> git status
    # On branch master
    # Your branch is ahead of 'origin/master' by 5 commits.
    #
    nothing to commit (working directory clean)
    ~/q14224966/sub[master]> cd ..
    ~/q14224966[master]> git status
    # On branch master
    # Your branch is ahead of 'origin/master' by 5 commits.
    #
    nothing to commit (working directory clean)
    

    Вот получившееся дерево фиксации: git дерево фиксации - параметр слияния

  2. Используйте перебазирование вместо слияния:

    ~/q14224966[master]> git reset --hard origin/master 
    HEAD is now at a231acd add submodule
    ~/q14224966[master]> touch other.c && git add . && git commit -m "New commit."
    [master ae66060] New commit.
     0 files changed
     create mode 100644 other.c
    ~/q14224966[master]> git rebase origin/branch 
    First, rewinding head to replay your work on top of it...
    Applying: New commit.
    ~/q14224966[master]> ls
    README   main.c   other.c  sub/
    ~/q14224966[master]> cd sub/
    ~/q14224966/sub[master]> ls
    Makefile  README    src/
    ~/q14224966/sub[master]> git status
    # On branch master
    # Your branch is ahead of 'origin/master' by 4 commits.
    #
    nothing to commit (working directory clean)
    ~/q14224966/sub[master]> cd ..
    ~/q14224966[master]> git status
    # On branch master
    # Your branch is ahead of 'origin/master' by 4 commits.
    #
    nothing to commit (working directory clean)
    

    Вот получившееся дерево фиксации: git дерево фиксации - параметр перебазирования

person etlovett    schedule 26.01.2013
comment
Я пробовал использовать merge -s resolve, но это не сработает, если файлы из подмодуля все еще существуют (убедитесь, что вы начали с git reset --hard origin/master && rm -rf sub && git submodule update). - person Greg Hewgill; 28.01.2013

Он пытался

git fetch - все

git reset --hard origin / master

но это не работает.

Вы можете использовать стратегию слияния «наша»:

git merge - наш старый мастер

Вы также можете использовать git-stash для сохранения изменений, а затем применить git-stash для их восстановления.

person Patt Mehta    schedule 26.01.2013
comment
Использование merge -s ours вообще не выполняет слияния и не решает проблему. - person Greg Hewgill; 28.01.2013
comment
да это проблема не получается слить: D а ты пробовал прятать? - person Patt Mehta; 28.01.2013
comment
К сожалению, приставка не помогает, потому что при распаковке вы все равно должны выполнить какое-то слияние. - person Greg Hewgill; 28.01.2013