так что в основном [мой цикл] проверяет все ветви, а затем проверяет файл x..так мне нужно фиксировать в каждой ветви b или нет? почему нет?
Ответ и нет, и да. Вы почти наверняка нуждаетесь в некоторых коммитах.
Помните, ветвь имя в действительности является указателем , указывающим на один конкретный коммит. Одно из этих имен имеет имя HEAD
, прикрепленное к it, 1 , а все остальные имена указывают на некоторый коммит tip. Давайте нарисуем эти коммиты и имена. Допустим, есть четыре имени и три таких коммита с ветвлением:
T <-U <-X <--name1 (HEAD)
/
... <-V <-Y <--name2, name3
\
W <-Z <-- name4
Здесь заглавными буквами обозначены некоторые реальные хеш-идентификаторы коммитов.
1 Точнее, не более к одному имени прикреплено HEAD
. Если HEAD
является отсоединенным , это указывает непосредственно на некоторый коммит. В этом случае git rev-parse --abbrev-ref HEAD
будет просто печатать HEAD
. Часто бывает полезно проверить это, хотя, если вы делаете это по ходу работы и точно знаете, что у вас нет отдельного ГОЛОВКИ, в этом нет необходимости.
Ваши первые шаги:
current_branch="$(git rev-parse --abbrev-ref HEAD)"
git add .
git commit -am "added x"
Ваша текущая ветвь равна name1
, что указывает на фиксацию X
. Первая строка устанавливает current_branch
в name1
. Ваш текущий коммит является коммитом X
: файлы этого коммита существуют в вашем индексе и в вашем рабочем дереве , потому что вы запустили git checkout name1
в какой-то момент в недавнем прошлом, который заполнил и индекс, и рабочее дерево из коммита X
.
Вы используете git add .
для копирования всех файлов в текущем каталоге или любом подкаталоге в индекс, 2 , чтобы они могли быть зафиксированы. Это включает в себя этот новый файл x
, который вы только что создали в своем рабочем дереве. Ваш индекс готов к фиксации.
2 Точнее, он копирует в индекс из рабочего дерева все такие файлы, которые (а) уже есть в индексе или (б) не игнорируются. Часть (a) здесь подразумевается частью (b) - файл, который находится в индексе, по определению не игнорируется - но это стоит подчеркнуть.
Затем третья строка делает git commit
. (Непонятно, почему вы используете git commit -a
вместе с git add .
, но -a
добавит файлы в некоторые другие каталоги, если это необходимо. Вы могли бы также запустить git add --all
, предполагая Git 2.0 или более позднюю версию, и пропустить -a
.) Предполагая, что это успешно, 3 теперь есть новый дополнительный коммит после X
, и name1
указывает на этот новый коммит, поэтому картинка теперь должна выглядеть следующим образом:
T--U--X--α <-- name1 (HEAD)
/
...--V--Y <-- name2, name3
\
W--Z <-- name4
(у меня закончились латинские буквы, так что это коммит альфа.)
3 Все это время мы предполагаем, что все работает, или что если команда терпит неудачу, этот сбой хорош.
Кажется, ваша следующая команда в вашем скрипте не имеет здесь никакой функции:
git fetch origin
Это получит новые коммиты, которые есть у Git на origin
, которых у вас нет, и обновит ваши origin/*
имена для удаленного отслеживания, но вы не будете использовать имена для удаленного отслеживания после этой точки, поэтому зачем обновлять их на данный момент?
Проблемные части встречаются здесь, в цикле:
git for-each-ref --format='%(refname)' refs/heads | while read b ; do
git checkout "$b"
git checkout "$current_branch" -- x
# proposed: git commit -m "some message"
done
Сначала вывод %(refname)
будет читать refs/heads/name1
, refs/heads/name2
и так далее. git checkout
проверит каждый из них как отдельную ГОЛОВУ , что не то, что вы хотите. Это легко исправить с помощью %(refname:short)
, в котором пропущена часть refs/heads/
.
В нашем гипотетическом примере вы получите имена name1
, name2
, name3
и name4
. Итак, вы начнете с того, что попросите Git снова извлечь commit α
- который идет очень быстро, так как он уже там - и затем использовать имя name1
для извлечения файла x
в индекс и рабочее дерево. Те тоже уже есть.
ThПредложение добавить git commit
. Этот конкретный git commit
будет завершаться с ошибкой с ошибкой, говорящей о том, что нечего коммитить, что в данном конкретном случае, вероятно, то, что вам нужно: уже есть файл x
с правильным содержанием в подсказке коммит ветки name1
, т.е. в коммите α
.
Затем цикл переходит на git checkout name2
, то есть фиксирует Y
. Это заменит ваш индекс и содержимое рабочего дерева теми, что извлечены из commit Y
, и присоединит HEAD
к имени name2
. Строка git checkout name1 -- x
извлечет файл x
из коммита α
в индекс и рабочее дерево, а предложенный git commit
сделает новый коммит и, следовательно, заставит имя name2
двигаться вперед, указывая на это новый коммит. Обратите внимание, что имя name3
продолжает указывать на коммит Y
. Давайте нарисуем новый коммит, который мы можем назвать β
(бета):
U--X--α <-- name1 (HEAD)
/
T β <-- name2
/ /
...--V--Y <-- name3
\
W--Z <-- name4
Теперь ваш цикл переходит на name3
, который все еще указывает на фиксацию Y
, поэтому Git вернет индекс и рабочее дерево к тому, что они были минуту назад, когда вы проверяли коммит Y
через имя name2
. Git теперь извлечет файл x
из коммита α
, как и раньше, и сделает еще один новый коммит.
Здесь все становится очень интересно! Новый коммит имеет то же дерево , что и коммит β
. Он также имеет того же автора и коммиттера. В зависимости от того, как вы создаете ваше сообщение -m
, оно может иметь то же сообщение журнала , что и commit β
. Если Git делает этот коммит в ту же метку времени в секунду , которую он использовал при совершении коммита β
, новый коммит фактически равен существующим коммитом β
и все в порядке.
С другой стороны, если Git требуется достаточно времени, чтобы новый коммит получил другую метку времени, новый коммит отличается от коммита β
. Давайте предположим, что это происходит, и мы получаем commit γ
(гамма):
U--X--α <-- name1 (HEAD)
/
T β <-- name2
/ /
...--V--Y--γ <-- name3
\
W--Z <-- name4
Наконец, цикл будет повторять этот процесс еще раз для name4
, который в настоящее время указывает на фиксацию Z
, но в итоге будет указывать на новый коммит δ
(delta):
U--X--α <-- name1 (HEAD)
/
T β <-- name2
/ /
...--V--Y--γ <-- name3
\
W--Z--δ <-- name4
Общие проблемы
Здесь возникает одна проблема, когда несколько имен указывают на один и тот же базовый коммит. В этом случае вы должны решить, хотите ли вы откорректировать все имен таким же образом - то есть, чтобы name2
и name3
оба продвинулись, чтобы указать на фиксацию β
- или вы не хотите этого:
Если вы делаете этого хотите, вы должны убедиться, что вы используете какую-либо операцию обновления имени ветви (git branch -f
, git merge --ff-only
и т. Д.) обновить все имена, которые указывают на конкретную фиксацию. В противном случае вы рассчитываете на то, что все коммиты будут выполнены в течение одной секунды, поэтому все метки времени будут совпадать.
Если вы не хотите этого - если вам нужно, чтобы имена были как бы индивидуализированы, - вы должны убедиться, что ваши git commit
проходят как минимум с интервалом в одну секунду, чтобы они получали уникальные метки времени.
Если вы уверены, что все ваши имена указывают на разные коммиты, эта проблема исчезнет.
Другие вещи, о которых стоит подумать:
Указывает ли какое-либо из имен какой-либо существующий коммит, что имеет файл с именем x
? Если это так, вы перезапишете this x
из x
, который мы извлекаем из коммита α
(первый коммит, который мы делаем в текущей ветви, в начале всего процесса.)
Если у каких-либо имен do есть x
- кто-то, безусловно, считает, что, будучи той ветвью, на которой мы были в первую очередь, - тогда это x
соответствует тот, что в коммите α
? Если это так, предложенный git commit
потерпит неудачу, если мы не добавим --allow-empty
. Но в нашем конкретном случае здесь это, вероятно, хорошая вещь, поскольку это означает, что мы можем избежать специального случая для проверки соответствия $b
1256 *.
У вас на самом деле есть имена ветвей для всех ... ну, не понятно, как называть эти вещи. Смотрите Что именно мы подразумеваем под "ветвью"? для деталей. Назовем их линиями развития . У вас есть (локальное) название ветви для каждой такой строки? Это может быть причиной того, что у вас есть git fetch origin
здесь: чтобы вы могли накапливать обновления для всех ваших origin/*
имен удаленного отслеживания.
Если у вас есть origin/feature1
и origin/feature2
, на этом этапе вы можете создать (локальные) имена ветвей feature1
и feature2
, чтобы добавить файл x
до кончика коммитов этих двух веток. Вам понадобятся имена веток, чтобы запомнить вновь созданные коммиты. Но простое использование git for-each-ref
сверх refs/heads
не приведет к желаемому результату: вы можете использовать git for-each-ref
сверх refs/remotes/origin
, накапливать имена минус часть origin/
в наборе вместе со всеми именами refs/heads
(минус refs/heads/
части конечно), и используйте эти в качестве имен ветвей.