Как лучше всего использовать git для переключения веток с помощью одного и того же коммита, а все остальные одинаковые? - PullRequest
1 голос
/ 08 апреля 2020

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

             K--L--M--N--O   <-- master (using MySQL)
            /
...--F--G--H
            \
             I   <-- PostgreSQL 

Коммит K устанавливает параметры соединения на MySQL, а коммит I заменяет параметры соединения с базой данных на PostgreSQL.

Теперь я хочу ветвь PostgreSQL, чтобы иметь все коммиты L, M, NO. Только один коммит отличается, но все остальные одинаковы. Примерно так:

             K---L--M--N--O   <-- master (using MySQL with the K commit) 
            /   /             <-- PostgreSQL (with the I commit)
...--F--G--H   /
            \ /
             I   

Однако после некоторых исследований я обнаружил, что это может быть невозможно? Говорят, что ветвь является указателем на этот коммит, который не является историческим. Это правда?

Поэтому я искал еще больше и нашел здесь несколько ответов: { ссылка }
{ ссылка }

Эти ответы предложил вишня. Итак, я использовал

git checkout PostgreSQL
git cherry-pick L^..O

Теперь это выглядит следующим образом.

             K--L--M--N--O   <-- master (using MySQL)
            /
...--F--G--H
            \
             I--L'--M'--N'--O'    <-- PostgreSQL 

Это работает, но журнал git не очень чистый. Я использую git log --all --decorate --oneline --graph, коммиты LMNO и L 'M' N 'O' имеют разные хэши, и они повторяются, появляются в журнале. Кроме того, в будущем мне нужно совершить обе ветки. Я полагаю, это не лучшая практика? Есть ли хороший способ решить эту проблему?

1 Ответ

1 голос
/ 08 апреля 2020

... Говорят, что ветвь - это просто указатель на этот коммит, который sh не является записью истории. Это правда?

Это правильно. История в Git представляет собой набор коммитов . имя ветви просто выбирает один коммит в качестве конца истории для этой ветви. Выполнение git checkout <em>branch-name</em> выбирает данное имя ветви в качестве текущей ветви и, следовательно, фиксацию, на которую указывает имя в качестве текущей фиксации. Это извлекает выбранный коммит в индекс Git (из которого Git сделает коммит next ) и в ваше рабочее дерево (где вы можете просматривать и работать с вашими файлами).

Выполнение git commit принимает все, что находится в индексе Git на тот момент, добавляет соответствующие метаданные, включая текущий коммит ha sh ID, записывает new commit - который получает свой уникальный идентификатор ha sh в процессе - и затем записывает идентификатор ha sh нового коммита в имя текущей ветви, таким образом, имя указывает на новый коммит.

Ни один коммит - ни один внутренний Git объект любой формы, на самом деле - никогда не может быть изменен, ни один бит, как только он будет сделан. Причина этого в том, что идентификатор ha sh представляет собой простую криптографическую c контрольную сумму содержимого объекта. Измените содержимое, и вы измените идентификатор ha sh: у вас есть новый, другой объект; существующий объект остается неизменным.

Поскольку commit L (независимо от того, какой у него реальный идентификатор ha sh) уже существует, его содержимое теперь заморожено на все времена. Это включает в себя тот факт, что у него есть только один родитель, commit K. Вы можете сделать новый коммит слиянием - коммит с двумя (или более) родителями - чьи родители оба I и что угодно, например, скажем O - и сделать ваше имя филиала PostgreSQL указывает на этот новый коммит:

             K--L--M--N--O   <-- master (using MySQL)
            /             \
...--F--G--H               \
            \               \
             I---------------P   <-- PostgreSQL

Вы можете получить это состояние, запустив:

git checkout PostgreSQL; git merge master

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

Снимок (т. Е. Данные, содержащиеся во всех файлах), хранящиеся в коммите P, может быть чем угодно, хотя обычно рекомендуется Git создавать снимок сам по себе. Git делает это путем объединения проделанной работы (внесенных изменений) с базы слияния *1042*, в этом случае фиксируя H, путем дифференцирования снимка в H против этого в I, чтобы увидеть что вы изменили, отобразив снимок в H против этого в O, чтобы увидеть, что они изменились, и объединив два набора изменений.

Поскольку снимок в O содержит изменения, которые были введенный в снимок в K, разница от H до O будет включать изменения, которые вы увидите, сравнив H с K. Таким образом, результат этого объединения будет включать изменения, которые вы не хотите.

Опять же, снимок в P может быть любым, что вам нравится: вы можете сказать Git не делать коммит P пока , а затем внесите дополнительные изменения в копии ваших файлов, которые Git сохранили в индексе Git. 1 То есть вы можете запустить git merge --no-commit: Git сделает все возможное, чтобы объединить изменения, как обычно, и, возможно, даже добиться успеха самостоятельно. Но затем вы взяли бы получившиеся файлы - как в настоящее время хранятся в индексах Git и вашего рабочего дерева - и изменили бы их, по сути, на "отмену" фиксации K. Поскольку вы можете редактировать только копию рабочего дерева, вы должны git add обновленные файлы, как обычно, после чего:

git merge --continue

(или git commit) передаст результат. Это производит то, что иногда называют злым слиянием .

Вместо злого слияния вы можете совершить обычное (не злое? Доброе?) Слияние и немедленно запустите:

git revert <hash-of-K>   # or master~4, if I counted correctly

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

             K--L--M--N--O   <-- master (using MySQL)
            /             \
...--F--G--H               \
            \               \
             I---------------P--Q   <-- PostgreSQL

Если вы сравните снимок в P со снимком в Q, изменения, которые вы увидите, будут противоположны изменениям, которые вы увидите, если сравнить снимок в H с изменением в K. Таким образом, commit Q будет соответствовать P, за исключением отмены (возврата) K.

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


1 Технически это не отдельные копии, но суть в том, что коммит P будет сделан из того, что есть в индексе Git. Если вы используете git merge --no-commit, а затем изменяете файлы рабочего дерева, вы также должны запустить git add для этих измененных файлов рабочего дерева, чтобы обновить индекс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...