Во-первых, примечание: если вы собираетесь использовать git worktree add
для нетривиальных периодов (более двух недель одновременно), убедитесь, что ваш Git имеет версию не ниже 2.15. 1
Для вашей конкретной цели я бы порекомендовал , а не , используя git clone --bare
.Вместо этого используйте обычный клон, а затем git worktree add
, которые вы собираетесь делать.Вы отмечаете в комментарии , что:
... в конечном итоге вам необходимо создать фиктивную ветвь для размещения самого хранилища, поскольку невозможно иметь оба хранилищаи рабочее дерево в одной ветви в одно и то же время.
Для этого есть несколько простых способов:
Выберите одно из N рабочих деревьев,вы добавите и будете использовать его в качестве ветви основного рабочего дерева:
git checkout -b branch-1 ssh://git@git.example.com/project/repo branch-1
Недостатком является то, что теперь у вас есть специальная выделенная "основная" ветвь, которую вы не можете просто удалить в любое время -все остальные ветви зависят от него.
Или, после клона, используйте git checkout --detach
в рабочем дереве, чтобы получить отдельный HEAD в ветви по умолчанию:
git clone ssh://git@git.example.com/project/repo repo.git
cd repo.git
git checkout --detach
Единственный недостаток этого второго подхода состоит в том, что рабочее дерево полно файлов, возможно, это пустая трата пространства.У этого тоже есть решение: создать пустой коммит, используя пустое дерево, и проверить это:
git clone ssh://git@git.example.com/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
проверяет это какОТДЕЛЬНАЯ ГОЛОВА.В результате мы освобождаем наш индекс и рабочее дерево, так что единственное, что есть в рабочем дереве хранилища - это каталог .git
.Пустой коммит, который мы сделали, не принадлежит ни одной ветке и не имеет родительского коммита (это однокорневой коммит), который мы никогда нигде не протолкнем.В Git 2.5, где впервые появился git worktree
, есть ошибка, которую я считаю очень плохой: git gc
никогда не сканирует добавленные рабочие деревья HEAD
и их индексные файлы.Если добавленные рабочие деревья всегда находятся в некоторой ветке и никогда не имеют git add
редактируемой, но незафиксированной работы, это никогда не вызывает никаких проблем.Если незавершенная работа не выполняется в течение по крайней мере 14 дней, для защиты от удаления из нее достаточно времени защиты по умолчанию.Но если вы git add
выполняете какую-то работу или делаете коммит на отдельном HEAD, уходите в отпуск на месяц или иным образом оставляете это добавленное рабочее дерево без изменений, а затем возвращаетесь к нему, и a git gc --auto
побежалипо истечении этого двухнедельного льготного периода сохраненные вами файлы были уничтожены!
Эта ошибка была исправлена в Git 2.15.
Почему --bare
идет не так
Корень проблемы здесь в том, что git clone --bare
делает две вещи:
- он создает пустой репозиторий (
core.bare
установлен на true
), который имеетнет рабочего дерева и не имеет начальной git checkout
; и - изменяет значение по умолчанию
fetch
refspec с +refs/heads/*:refs/remotes/<em>origin</em>/*
на +refs/heads/*:refs/heads/*
.
Этот второй элемент означает, что refs/remotes/origin/
нетимена, как вы обнаружили.Это не так легко исправить из-за (в основном скрытого / внутреннего) концепции refmaps , которая очень кратко описана в документации git fetch
(см. Ссылку).
Хуже того, это означает, что refs/heads/*
будет обновляться каждые 1099 *. Существует причина, по которой git worktree add
отказывается создавать второе рабочее дерево, которое ссылается на такую же ветку , которое извлечено в любом существующем рабочем дереве, и заключается в том, что Git принципиально предполагает, что никто не будет путать со ссылкой refs/heads/<em>name</em>
, к которой прикреплено HEAD
в этом рабочем дереве. Таким образом, даже если вы работали над проблемой refmap, когда вы запускаете git fetch
и он обновляет - или даже удаляет, из-за --prune
и вышестоящего удаления того же имени - имени refs/heads/<em>name</em>
, добавленное рабочее дерево HEAD
ломается, и само дерево работы становится проблематичным. (См. Почему Git разрешает отправку в извлеченную ветку в добавленном рабочем дереве? Как мне восстановить? )
Есть еще одна вещь, которую вы можете попробовать, которую я вообще не тестировал, а именно: сделать голый клон, как вы делаете, но затем измените refspec , повторно выберите и удалите все существующие имена веток, так как они не имеют установленных восходящих потоков (или, что то же самое, задают свои восходящие потоки):
git clone --bare ssh://git@git.example.com/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/{} {}
).