Есть ли одна команда для этого? Чтобы я обновил файл в одной ветви и смог принудительно отправить его во все остальные ветви?
Нет. Концептуально, как работает 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
в этих сценариях оболочки заставляют циклы завершаться рано, если какая-то часть процесса завершается неудачно. В этом случае вы должны очистить вручную, а затем перезапустить цикл с оставшимися ветвями, чтобы добавить новый коммит.