Во-первых, примечание: если вы собираетесь использовать git worktree add
в течение нетривиальных периодов (более двух недель за раз), убедитесь, что у вас Git не ниже версии 2.151.
Для вашей конкретной цели я бы рекомендовал не использовать git clone --bare
. Вместо этого используйте обычный клон, а затем git worktree add
, которые вы собираетесь сделать. Вы заметили в комментарий, что:
... вам в конечном итоге придется создать фиктивную ветку для размещения самого репозитория, потому что невозможно одновременно иметь репозиторий и рабочее дерево в одной и той же ветке.
Для этого есть несколько простых обходных путей:
Выберите одно из N рабочих деревьев, которые вы добавите, и используйте его как ветвь в основном рабочем дереве:
git checkout -b branch-1 ssh://[email protected]/project/repo branch-1
Недостаток в том, что теперь у вас есть специальная выделенная «основная» ветвь, которую вы не можете просто удалить в любое время — от нее зависят все остальные ветки.
Или после клона используйте git checkout --detach
в рабочем дереве, чтобы получить отдельный HEAD на ветке по умолчанию:
git clone ssh://[email protected]/project/repo repo.git
cd repo.git
git checkout --detach
Единственным недостатком этого второго подхода является то, что рабочее дерево заполнено файлами, что, вероятно, является пустой тратой места. Для этого также есть решение: создайте пустой коммит, используя пустое дерево, и проверьте его:
git clone ssh://[email protected]/project/repo repo.git
cd repo.git
git checkout $(git commit-tree $(git hash-object -t tree /dev/null) < /dev/null)
Хорошо, последнее не совсем очевидно. Хотя на самом деле это довольно просто. git hash-object -t tree /dev/null
создает хэш-идентификатор пустого дерева, которое уже существует в каждом репозитории. git commit-tree
делает фиксацию, чтобы обернуть это пустое дерево — его нет, поэтому мы должны сделать его, чтобы проверить его — и распечатывает хэш-идентификатор этой новой фиксации, а git checkout
проверяет это как отсоединенный HEAD. Эффект состоит в том, чтобы очистить наш индекс и рабочее дерево, так что единственной вещью в рабочем дереве репозитория является каталог .git
. Пустой коммит, который мы сделали, не находится ни в какой ветке, и у него нет родительского коммита (это единственный корневой коммит), который мы никогда никуда не отправим.
1Причина этого в том, что в Git 2.5, где впервые появился git worktree
, есть ошибка, которую я считаю очень серьезной: git gc
никогда не сканирует файлы HEAD
добавленных рабочих деревьев, а также их индексные файлы. Если добавленные рабочие деревья всегда находятся в какой-то ветке и никогда не имеют git add
ed, но незафиксированной работы, это никогда не вызывает никаких проблем. Если незафиксированная работа не задерживается в течение как минимум 14 дней, времени защиты от сокращения по умолчанию достаточно, чтобы предотвратить ее уничтожение. Но если вы git add
немного поработаете или зафиксируете на отдельном HEAD, отправитесь в отпуск на месяц или иным образом оставьте это добавленное рабочее дерево нетронутым, а затем вернетесь к нему, и git gc --auto
побежал после этих двух - недельный льготный период истек, сохраненные вами файлы были уничтожены!
Эта ошибка была исправлена в Git 2.15.
Почему --bare
идет не так
Корень проблемы здесь в том, что git clone --bare
делает две вещи:
- он создает голый репозиторий (
core.bare
установлен на true
), который не имеет рабочего дерева и не выполняет начального git checkout
; и
- он изменяет стандартную спецификацию
fetch
с +refs/heads/*:refs/remotes/origin/*
на +refs/heads/*:refs/heads/*
.
Этот второй пункт означает, что refs/remotes/origin/
имен нет, как вы обнаружили. Это нелегко исправить из-за (в основном скрытой/внутренней) концепции refmaps, которые очень кратко упоминаются в документации git fetch
(см. ссылку).
Хуже того, это означает, что refs/heads/*
будет обновляться каждые git fetch
. Есть причина, по которой git worktree add
отказывается создавать второе рабочее дерево, ссылающееся на ту же ветку, которая проверена в любом существующем рабочем дереве, и заключается в том, что Git принципиально предполагает, что никто не будет связываться с ссылка refs/heads/name
, к которой прикреплен HEAD
в этом рабочем дереве. Таким образом, даже если вы решили проблему с refmap, когда вы запускаете git fetch
и он обновляет — или даже удаляет, из-за --prune
и удаления того же имени в восходящем потоке — имя refs/heads/name
, разрывы HEAD
добавленного рабочего дерева и работу -дерево само по себе становится проблематичным. (См. раздел Почему Git разрешает отправку в проверенную ветку в добавленном рабочем дереве? Как восстановить?)
Есть еще одна вещь, которую вы могли бы попробовать, которую я вообще не проверял, а именно: сделать голое клонирование, как вы делаете, но затем изменить refspec и выполнить повторную выборку, а также удалить все существующие имена ветвей, так как они не имеют установленных восходящих потоков (или, что то же самое, устанавливают свои восходящие потоки):
git clone --bare ssh://[email protected]/project/repo repo.git
cd repo.git
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git fetch
git for-each-ref --format='%(refname:short)' refs/heads | xargs git branch -d
(или замените xargs
на xargs -n1 -I{} git branch --set-upstream-to=origin/{} {}
).
person
torek
schedule
28.01.2019
--bare
репозитории. Я бы порекомендовал вам оставить голый репозиторий голым и сделать не голый клон, в котором можно создавать рабочие деревья. (В частности,--bare
изменяет refspec выборки таким образом, что любая выборка может серьезно испортить вашу незавершенную работу.) - person torek   schedule 25.01.2019