Git: как я могу объединиться между ветвями, сохраняя отдельные ревизии в одной ветки?

В аду есть особое место для людей, которые жестко кодируют абсолютные пути и учетные данные в нескольких случайных местах в веб-приложениях. К сожалению, прежде чем они отправятся в ад, они разрушают Землю. И мы должны иметь дело с их кодом.

Мне нужно выполнить несколько небольших изменений в одном из таких веб-приложений. Я создаю новую ветвь features и выполняю глобальную находку и замену, чтобы обновлять пути и учетные данные в моей локальной среде. Я это делаю. Я также отмечаю это как local.

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

Вперед, я буду сливаться между master и features, и я бы хотел, чтобы local оставался в features и никогда не появлялся в master.

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

Есть ли простой очевидный способ сделать это, что мне не хватает?

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

В противном случае меня интересуют более запутанные, ручные способы обработки ситуации.

Ответ 1

Мое решение этой проблемы использует rebase, а не merge

Начиная с дерева фиксации следующим образом:

a-b-c <-- master
 \
  d <-- local
   \
    e-f-g <-- dev

$git rebase --onto master local dev

       master 
       V 
   a-b-c-e'-f'-g' <-- dev
     \
      d <-- local

$git мастер проверки

$git merge dev

               master 
               V 
   a-b-c-e'-f'-g' <-- dev
     \
      d <-- local

$git rebase --onto master master local

               master 
               V 
   a-b-c-e'-f'-g' <-- dev
                \
                 d' <-- local

$git branch -f dev local

               master 
               V 
   a-b-c-e'-f'-g'
                \
                 d' <-- local
                 ^
                 dev

Ответ 2

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

Ответ 3

Техническое (Git) решение будет использовать git attributes, используя слияние атрибутов.

merge

Атрибут merge влияет на то, как три версии файла объединяются, когда требуется слияние уровня файла во время git merge.

Вы найдете в вопросе SO Как сообщить git, чтобы всегда выбирать мою локальную версию для конфликтующих слияний в определенном файле?" пример используя такой атрибут, чтобы принудительно сохранить локальную версию определенных файлов при слиянии с данной ветвью.

Проблема с настройкой атрибутов слияния заключается в том, что файлы, содержащие пути, могут содержать другой измененный код, который я хочу объединить

Не забывайте, что вы можете связать любой тип script, чтобы управлять этими слияниями через git attributes. Это включает script, способный сохранить изменения, которые вы хотите локально, и слияние остальных. Сложнее написать такой "диспетчер слияний", но это путь к автоматическому решению ad hoc.


Менее техническим решением было бы отделить значения конфигурации от файлов конфигурации:

  • файл конфигурации содержит только имена для замены
  • значения конфигурации - это несколько файлов (по одному для каждой среды) с фактическими значениями для каждого имени.

A script используется для замены имени в фактическом файле конфигурации значениями одного из файлов конфигурационных значений, необходимых для данной среды.

Ответ 4

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

  • выполните свою локальную ветвь
  • Сделать локальное изменение
  • продолжить разработку

при слиянии с мастером:

  • rebase -i master из вашей ветки и переместите локальное изменение в END цепочки патчей.
  • разрешать любые конфликты в процессе. Если локальное изменение находится в файлах конфигурации, и вы не касаетесь их в обычной разработке, тогда у вас не будет проблем. Если, в противном случае, у вас есть конфликт, тогда это случай, когда вы фактически меняетесь в той же области, и вам все равно нужно ваше решение.
  • проверить мастер
  • объединить локальную ветвь -1:

    git слить local ^

Это оставит вас с хозяином, имеющим все изменения на локальном, за исключением последнего.

Если у вас несколько изменений local = only, я предлагаю вам сквош их вместе во время rebase.

Ответ 5

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

master: исходный код, который вы унаследовали

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

feature: ветвь от мастера, и все исправления идут здесь (и, возможно, добавить крючок, чтобы предотвратить слияние с патчем в localcred)

local: ветвь (а не тег!), которая начнется как ветвь localcred, а затем объединит функцию всякий раз, когда вам нужно будет запускать модульные тесты. Все испытания происходят отсюда, но развитие здесь не происходит. Кроме того, эта ветка является одноразовой, потому что вы можете захотеть переустановить внутри feature, а самый быстрый способ справиться с результатом - удалить ветвь local, отделить ее снова от localcred и слить feature перед выполнением тестов. Это, вероятно, будет достаточно распространенной операцией в моем рабочем процессе, что я бы построил псевдоним, чтобы сделать это повторно всего за несколько нажатий клавиш, но я черпаю черту из возможности использования ветвей Git, какие из них люди, которые смотрят на меня, поэтому YMMV.

Когда вы считаете, что ваши исправления готовы к публикации, вы делаете окончательную перезагрузку feature для очистки истории, дампа и воссоздания local для вашего окончательного теста, слияния feature в master и один раз который принимал восходящий поток, объединил master в localcred и обновил патч учетных данных до вершины, затем сбрасывал и воссоздавал local и feature и снова воспроизводил игру.

Если вы хотите быстро протестировать большой набор крошечных вариаций кода без необходимости совершать и объединять каждый раз, проверьте local, внесите изменения до тех пор, пока вы не будете счастливы, не совершаете и не сразу выбираете cherry-pick из local в feature, затем снимите и заново создайте локальные.

Соответствует ли это вашим потребностям?

Ответ 6

Я бы сделал интерактивную перезагрузку против хозяина и переместил вашу команду path-name-fixup-commit до конца. Затем вы можете объединиться до этой точки. Просто продолжайте перемещать свою специальную фиксацию до конца.

Вы также можете найти полезную информацию. Вместо того, чтобы на самом деле фиксировать исправления имени пути, вы могли бы спрятать их. Если вы попробуете этот подход, вы можете проверить вопрос на Как отменить применение кошелька.

Ответ 7

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

Если вы всегда разрабатываете ветвь features, вы можете объединить features в master, а затем в master, git revert local. (Где local - это тег, ссылающийся на фиксацию, где вы настроили пути и т.д. Для своей локальной среды.)

Теперь вы никогда не должны объединять master в features, потому что это тоже объединит реверс local.

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

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

Ответ 8

Я не знаю, будет ли это работать, но:

  • Создайте фиксацию, которая, учитывая "главную" версию конфигурационных файлов, превращает их в нужную вам версию. Обратите внимание на SHA-1. Мы назовем его MAKE_LOCAL
  • Создайте фиксацию, которая, учитывая вашу локальную версию файлов конфигурации, превратит их в версию, подходящую для мастера. Обратите внимание на SHA-1. Мы назовем его MAKE_REMOTE
  • Использование git hooks, когда вы совершаете:
    • git cherry-pick MAKE_REMOTE (или используйте git diff и patch)
    • Разрешить фиксацию начинать
    • git cherry-pick MAKE_LOCAL (или используйте git diff и patch)

Я думаю, что есть еще лучший способ преобразования файлов таким образом, но я не могу вспомнить (если вы можете найти презентацию shacon git от RubyConf и можете пробираться через 800 слайдов, там там с некоторыми замечательными примеры).

Ответ 9

Вопрос старый, но я до сих пор не нашел хорошего ответа. В настоящее время я столкнулся с одной и той же проблемой, и ниже приводится мое обходное решение:

В моем локальном репо есть две ветки: master и local_settings. Отрезав ветвь local_settings от master, я зафиксировал там все локальные пути, не помеченные тегами и не пытающиеся их запомнить. Во время локального развития я переключился на ветвь local_settings, поэтому я могу запустить приложение, используя локальные пути. Но когда пришло время совершить, я запустил текущее состояние и переключился на ветвь master. Затем я вытащил скрытый набор изменений и передал его в master. И последний шаг - вернуться к local_settings, слить из master и продолжить разработку. Напомним: я переношу в ветвь local_settings только изменения, которые будут оставаться локально и никогда не войдут в master; и никакие слияния от local_settings до master.

Теперь скажем, мне нужно добавить "хорошую" модификацию файла с локальным путем, добавленным ранее, но "хорошая" модификация требуется в ветке master. Я делаю свои изменения, когда рабочая копия является головкой для local_settings, запишет ее и проверит master. Шкаф хранит набор изменений, относящийся к local_settings, хотя я уже на master. git stash pop применяет сложенный набор изменений к рабочей копии и заканчивает тем, что имеет diff по отношению к master, но только с недавней модификацией, исключая локальный путь, который был добавлен ранее, и не был частью недавнего набора исправленных изменений. Следовательно, это может быть выполнено без путаницы в ветке master. Затем снова сливаются с master на local_settings.