Есть ли способ заставить git добавить файл из master в 10 других ветвей?
Есть ли способ сказать git, чтобы добавить новый файл в каждую другую ветвь?
Вместо git checkout <branch>; git merge master для каждой ветви есть ярлык для добавления newfile в каждую ветвь ниже?
master
+-----newfile
|
---
/ |
/ |
/ |
foo bar baz
EDIT: это сценарий bash (нуждается в некоторой очистке, если реализован), который я придумал, но надеюсь, что есть лучший способ.
MSG='added newfile which contains a method to workaround issue IS-797'
for branch in $(git branch | grep -v master | sed 's#^[ *t]##g'); do
echo merging into $branch
git checkout $branch && git merge master -m "$MSG"
done
3 ответов:
Существует несколько способов пойти об этом. Большинство советов в других комментариях / ответах (на момент написания этой статьи) являются подозрительными, на мой взгляд. Вот краткое изложение различных вариантов, с плюсами и минусами.
Во-первых, есть (по крайней мере) три различных варианта того, как может выглядеть окончательная история. У каждого есть свои плюсы и минусы. Что-то вроде того. Скорее, у каждого есть свои минусы, и плюсы в том, что у него нет минусов других способов.
Это может выглядеть так, как файл был "всегда есть", так как до этого были созданы пораженные ветви. Большой недостаток заключается в том, что это переписывание истории, и это имеет последствия.
Это может выглядеть так, как будто файл был независимо добавлен каждой веткой. Файл, скорее всего, окажется с трудночитаемой историей, и есть много возможностей для избежания конфликтов слияния. Но он не переписывает историю.
Это может выглядеть так, как будто файл был добавлен один раз, а затем объединен в каждую ветвь. Некоторые люди действительно ненавидят коммиты слияния, и даже как тот, кто Не особенно ненавидит коммиты слияния, я думаю, что этот подход приводит к беспорядочной топологии ветвей. Но он избегает переписывания истории и будет хорошо работать в будущем; то есть это функционально самый чистый вариант, но эстетически грубый.
Рассматривая каждую из них более подробно:
Переписывание Истории :
Если вы один используете свое РЕПО, и вы никогда не делали ничего, что делает ваш существующий идентификатор фиксации ("хэши") важен, тогда это довольно привлекательный вариант. Даже если вы разделяете РЕПО, вы можете сделать эту работу до тех пор, пока вы координируете с другими пользователями РЕПО.
Для больших переписок мой общий совет-согласовать со всеми, чтобы иметь установленную дату/время, когда вся работа будет перенесена в репо. Затем все отбрасывают свои клоны, вы делаете переписывание, и все повторно клонируются. Если это непрактично, то большой перепис, вероятно, не является хорошим идея; но если вы хотите рассмотреть ее в любом случае, прочитайте "восстановление из восходящей ребазы" в
git rebasedocs для получения дополнительной информации о том, что должно произойти.Хотя
rebase, я полагаю, является прототипическим способом переписать историю, в данном случае это почти наверняка неправильный способ. Если у вас много ветвей, вам придется делать это много раз. Если у вас сложная история, это становится очень трудно сделать правильно. В частности, он плохо справляется со слияниями (даже с недавно добавленными усовершенствованный).Вместо этого вы можете использовать
git filter-branch. Самый простой способ-это--tree-filter. Вы бы поместили копию файла где-нибудь вне рабочего дерева, а затемgit filter-branch --tree-filter 'cp /path/to/stored/copy/of/file path/to/file/in/worktree' -- --allЗатем
filter-branchприступит к проверке каждого коммита в вашей истории, выполнит командуcp, чтобы добавить файл в рабочее дерево для этого коммита, и запишет новый коммит с полученным содержимым. Тогда он будет перемещать ветви от старых коммитов к новым. Если у вас есть теги и вы хотите, чтобы они тоже перемещались, добавьте--tag-name-filterаргумент типаПроблема в том, что если ваша история велика, то процесс идет медленно. Вы можете ускорить его, используя ramdisk для рабочего дерева (см. опциюgit filter-branch --tree-filter 'cp /path/to/stored/copy/of/file path/to/file/in/worktree' --tag-name-filter cat -- --all-d), или используя--index-filterвместо--tree-filter(но это требует использования различных команд для прямого обновления индекса, и это не так просто).
каждая ветвь добавляет файл
Это то, что
Единственное отличие состоит в том, что когда вы делаете это таким образом, git создает независимые коммиты, которые индивидуально добавляют файл в каждую ветвь.cherry-pickдаст вам. Так как речь идет о том, чтобы автоматизируйте процесс, непонятно, почему кто-то прокомментировалcherry-pick, как будто это решение. Процесс такой же ручной (и такой же скриптовый), как и слияние.Для этого можно использовать тот же базовый сценарий, что и для слияния, просто заменив команду
mergeкомандойcherry-pickкоманда.
слияния
Для этого вы уже разработали разумное решение-вам нужен сценарий. Единственное, что я хотел бы отметить, это то, чтоgit for-each-refчасто является более подходящей командой для подачи в сценарий, а неgit branch. https://git-scm.com/docs/git-for-each-ref
Вы можете использовать
git rebase, чтобы достичь этого, но прочитайте, почему вы не должны использовать git rebase из этой статьи
Если у вас уже есть фиксация на
master, я бы просто сорвал ее на каждой ветке. Это, безусловно, самый простой и надежный сценарий; сбой в одной ветви должен быть несущественным для других ветвей.commit='deadbeef' for branch in $(git branch | sed -n '/master/!s#^[ *\t]##p'); do echo "cherry-picking onto $branch" git checkout "$branch" && git cherry-pick "$commit" doneВ качестве отступа обратите внимание на предпочтение строчных переменных оболочки, цитирование, не используя флаг
gвsed, Когда вы ожидаете только одно совпадение в каждой строке, и избегая бесполезного использованияgrep.Использование
Выбор отдельного коммита, конечно, также гораздо более специфичен, чем слияние всех\tв сценарий sed не совсем переносим; возможно, попробуйте[*[:space:]]вместо[ *\t].masterв каждую ветвь, что вполне может быть полным нет-нет для других, кто борется с этим.
Comments