Git: как принудительно протолкнуть файл во все ветви - PullRequest
0 голосов
/ 28 июня 2018

У меня есть пример репозитория проектов Android, где я создаю образцы для каждой темы в Android и храню в разных ветках Git, чтобы в любой момент я мог переключиться на соответствующую ветку и получить образец кода для конкретной темы.

В основном у меня есть один проект Android в Git, но каждая ветвь имеет различную кодовую базу. В настоящее время у меня более 20 веток.

Но есть некоторые файлы конфигурации, такие как build.gradle, в котором хранятся версии библиотек, версии SDK и т. Д. Эти файлы мне нужно обновлять независимо от веток.

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

Скажем, если приходит обновление библиотеки, я обновляю версию в ветке master и нажимаю. Но это влияет только на главную ветку.

Все оставшиеся 19 веток будут иметь старый код версии. Мне придется переключиться на каждую ветку и объединить этот файл конфигурации с мастером.

Есть ли какая-нибудь отдельная команда для этого? Чтобы я обновил файл в одной ветви и смог принудительно отправить его во все остальные ветви?

1 Ответ

0 голосов
/ 28 июня 2018

Есть ли одна команда для этого? Чтобы я обновил файл в одной ветви и смог принудительно отправить его во все остальные ветви?

Нет. Концептуально, как работает Git, это не имеет смысла: git push не выдвигает файлов . git push толкает фиксирует .

Фиксация - это полный снимок каждого файла. То есть, с учетом некоторого хеш-идентификатора коммита - хеш-идентификаторы являются фактическими именами коммитов - вы можете сделать так, чтобы Git перечислял каждый файл с его содержимым, навсегда замороженным в любой форме, в которой он находился в области index / staging-area в то время ты (или кто бы то ни было) бежал git commit. Более полезно, учитывая тот же самый хэш-идентификатор, вы можете git checkout <hash-ID> и заполнить ваше рабочее дерево (и вашу индексную / промежуточную область!) Этими файлами.

Следовательно, если build.gradle существует в снимке (т. Е. В коммите), то при извлечении этого коммита вы получите этот файл build.gradle в той форме, в которой он был в этом снимке. Пока что все это должно быть довольно просто и в основном то, что вы хотите. Итак, теперь давайте посмотрим, что значит сделать новый коммит.

Мы уже отмечали, что каждый коммит имеет хеш-идентификатор. Эти идентификаторы кажутся случайными, хотя на самом деле они представляют собой криптографический хэш полного содержимого коммита. Они слишком сложны для непосредственного использования людьми. Таким образом, Git предоставляет способ хранения последнего хеш-идентификатора некоторой ветви - то, что Git называет кончик ветви - под понятным человеку именем, например master.

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

... <-E  <-F  <-G   <--master

(с заглавными буквами, обозначающими фактические хеш-идентификаторы), вы добавите новый коммит, если Git сделает коммит H, у которого parent - хеш G. Затем Git записывает новый хэш в имя master, так что master указывает на H:

... <-E  <-F  <-G  <-H   <--master

Так как git push толкает фиксирует (не файлы), вам нужно будет извлечь каждый совет ветви, включая все его файлы, а затем создать новый коммит, который станет кончиком этой ветви. , с обновленным build.gradle файлом:

          o--...--o   <--branch1
         /
...--o--o--...--o   <--branch2
      \
       o--...--o   <--branch3

становится:

          o--...--o--X   <--branch1
         /
...--o--o--...--o--Y   <--branch2
      \
       o--...--o--Z   <--branch3

, где X, Y и Z - новые коммиты, добавленные в каждую ветку. Теперь вы можете отправить все эти новые коммиты одновременно:

git push origin branch1 branch2 branch3

, который отправит новый коммит на сервер. Пока вы избегаете , используя --force, Git сервера будет гарантировать, что эти коммиты будут строго добавляться к их ветвям, так что ветви server растут точно таким же образом что ваши собственные ветви выросли.

Обратите внимание, что если основное содержимое файла (файл build.gradle) точно совпадает в любых двух коммитах, эти два коммита совместно используют объект Git, содержащий это содержимое. Это означает, что создание десятков ( "около 20+ веток" ) новых коммитов занимает очень мало места - фактический размер коммита невелик, так как файлы становятся общими (это возможно потому что ни один объект Git не может быть когда-либо изменен после его фиксации). Даже если содержимое файла различается, объекты Git сильно сжимаются, как только они появляются в пакетных файлах Git (именно так работает интеллектуальный протокол).

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

... build the base commit on some branch BR1, then:
for branch in BR2 BR3 BR4 BR5 BR6; do
    git checkout $branch &&
        git checkout BR1 -- build.gradle &&
        git commit --reuse-message=BR1 || break
done

Этот цикл проверяет каждую подсказку ветки по имени, извлекает build.gradle из коммита подсказки BR1 и фиксирует его, повторно используя сообщение фиксации из этой фиксации.

Если файлы (потенциально) разные, но идея состоит в том, чтобы применить одно и то же , то изменить для каждого:

for branch in BR2 BR3 BR4 BR5 BR6; do
    git checkout $branch &&
        git cherry-pick BR1 || break
done

Этонемного интереснее: мы превращаем все, что было изменено в кончике BR1, в патч (вроде - Git умнее, чем утилита patch) и применяем это к текущей проверенной ветке и сделайте новый коммит, повторно используя сообщение фиксации, как и раньше. Обратите внимание, что это также работает, если все файлы build.gradle идентичны.

Строки || break в этих сценариях оболочки заставляют циклы завершаться рано, если какая-то часть процесса завершается неудачно. В этом случае вы должны очистить вручную, а затем перезапустить цикл с оставшимися ветвями, чтобы добавить новый коммит.

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