Несколько рабочих каталогов с Git?

Я не уверен, что это что-то поддерживается Git, но теоретически мне кажется, что это должно работать.

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

Мое типичное решение - сделать две проверки, но жаль, что я не могу делиться между ними ветками и ссылками. Мне бы хотелось, чтобы две рабочие директории управлялись одной и той же папкой.git.

Я знаю о локальных решениях git clone (по умолчанию это жесткая ссылка на общие объекты и опция --shared, которая устанавливает альтернативное хранилище объектов с исходным репо), но эти решения только сокращают использование дискового пространства и особенно в случае --shared, похоже, чревато опасностью.

Есть ли способ использовать одну папку.git и создать для нее две рабочие директории? Или Git жестко запрограммирован, чтобы в любой момент был проверен только один рабочий каталог?

4 ответа

Решение

Git 2.5 предлагает с июля 2015 года замену contrib/workdir/git-new-workdir git worktree

См. Коммит 68a2e6a от Junio ​​C Hamano ( gitster )

В примечании к выпуску упоминается:

Замена для contrib/workdir/git-new-workdir это не основывается на символических ссылках и делает совместное использование объектов и ссылок более безопасным, так как заемщики и заемщики знают друг о друге.

Смотрите коммит 799767cc9 (Git 2.5rc2)

Это означает, что теперь вы можете сделать git worktree add <path> [<branch>]

Создайте <path> и оформить заказ <branch> внутрь. Новый рабочий каталог связан с текущим репозиторием, разделяя все, кроме специфических для рабочего каталога файлов, таких как HEAD, index и т. Д. git worktree раздел добавляет:

Git-репозиторий может поддерживать несколько рабочих деревьев, что позволяет вам проверять более одной ветви за раз.
С git worktree add Новое рабочее дерево связано с хранилищем.

Это новое рабочее дерево называется "связанным рабочим деревом", а не "основным рабочим деревом", подготовленным git init " или же " git clone "
Репозиторий имеет одно основное рабочее дерево (если это не пустой репозиторий) и ноль или более связанных рабочих деревьев.

подробности:

Каждое связанное рабочее дерево имеет частный подкаталог в хранилище $GIT_DIR/worktrees каталог.
Имя частного подкаталога обычно является базовым именем пути связанного рабочего дерева, возможно, с добавлением номера, чтобы сделать его уникальным.
Например, когда $GIT_DIR=/path/main/.git команда git worktree add /path/other/test-next next создает:

  • связанное рабочее дерево в /path/other/test-next а также
  • также создает $GIT_DIR/worktrees/test-next каталог (или $GIT_DIR/worktrees/test-next1 если test-next уже занято).

Внутри связанного рабочего дерева:

  • $GIT_DIR установлен, чтобы указать на этот частный каталог (например, /path/main/.git/worktrees/test-next в примере) и
  • $GIT_COMMON_DIR настроен так, чтобы указывать на главное рабочее дерево $GIT_DIR (например /path/main/.git).

Эти настройки сделаны в .git файл, расположенный в верхней директории связанного рабочего дерева.

Когда вы закончите со связанным рабочим деревом, вы можете просто удалить его.
Административные файлы рабочего дерева в хранилище в конечном итоге будут удалены автоматически (см. gc.pruneworktreesexpire в git config), или вы можете запустить git worktree prune в основном или любом связанном рабочем дереве для очистки любых устаревших административных файлов.


Предупреждение: еще есть git worktree Раздел "ОШИБКИ", о котором следует знать.

Поддержка подмодулей является неполной.
НЕ рекомендуется делать несколько проверок суперпроекта.


Примечание: с помощью git 2.7rc1 (ноябрь 2015 г.) вы можете составить список своих рабочих деревьев.
Смотрите коммит bb9c03b, коммит 92718b7, коммит 5193490, коммит 1ceb7f9, коммит 1ceb7f9, коммит 5193490, коммит 1ceb7f9, коммит 1ceb7f9 (08 октября 2015 г.), коммит 92718b7, коммит 5193490, коммит 1ceb7f9, коммит 1ceb7f9, коммит 1ceb7f9 (08 окт. 2015) (08 окт. 2015) зафиксировать 1ceb7f9 (8 октября 2015 г.), зафиксировать 1ceb7f9 (08 октября 2015 г.) и зафиксировать ac6c561 (02 октября 2015 г.) Майкл Раппаццо ( rappazzo )
(Объединено Юнио С Хамано - gitster - в коммите a46dcfb, 26 октября 2015 г.)

worktree: добавлять ' list команда

' git worktree list 'перебирает список рабочего дерева и выводит сведения о рабочем дереве, включая путь к рабочему дереву, текущую извлеченную ревизию и ветвь, а также, если рабочее дерево пустое.

$ git worktree list
/path/to/bare-source            (bare)
/path/to/linked-worktree        abcd1234 [master]
/path/to/other-linked-worktree  1234abc  (detached HEAD)

Существует также опция формата фарфора.

Фарфоровый формат имеет строку для каждого атрибута.

  • Атрибуты перечислены с меткой и значением, разделенным одним пробелом.
  • Логические атрибуты (такие как "bare" и "detached") перечислены только в качестве метки и присутствуют только в том случае, если значение равно true.
  • Пустая строка указывает на конец рабочего дерева

Например:

$ git worktree list --porcelain

worktree /path/to/bare-source
bare

worktree /path/to/linked-worktree
HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
branch refs/heads/master

worktree /path/to/other-linked-worktree
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
detached

Примечание: если вы перемещаете папку рабочего дерева, вам необходимо вручную обновить gitdir файл.

См. Коммит 618244e (22 января 2016 г.) и коммит d4cddd6 (18 января 2016 г.). Автор Nguyễn Thái Ngọc Duy ( pclouds )
Помогает: Эрик Саншайн ( sunshineco )
(Объединено Юнио С Хамано - gitster - в коммите d0a1cbc, 10 февраля 2016 г.)

Новый документ в git 2.8 (март 2016 года) будет включать:

Если вы переместите связанное рабочее дерево, вам необходимо обновить gitdir'файл в каталоге записи.
Например, если связанное рабочее дерево перемещено в /newpath/test-next И его .git файл указывает на /path/main/.git/worktrees/test-next затем обновите /path/main/.git/worktrees/test-next/gitdir ссылаться /newpath/test-next вместо.


Будьте осторожны при удалении ветки: до git 2.9 (июнь 2016 г.) вы могли удалить одну из используемых веток в другом рабочем дереве.

Когда " git worktree "функция используется" git branch -d msgstr "разрешено удаление ветки, которая извлечена в другом рабочем дереве.

См. Коммит f292244 (29 марта 2016 г.) Казуки Ямагучи ( rhenium )
Помогает: Эрик Саншайн ( sunshineco )
(Объединено Юнио С Хамано - gitster - в коммите 4fca4e3, 13 апреля 2016 г.)

branch -d: отказаться от удаления ветки, которая в данный момент извлечена

Когда ветка проверяется текущим рабочим деревом, удаление ветки запрещено.
Однако, когда ветвь проверяется только другими рабочими деревьями, удаление некорректно завершается успешно.
использование find_shared_symref() чтобы проверить, используется ли ветвь, а не просто сравнивать с заголовком текущего рабочего дерева.


Точно так же до git 2.9 (июнь 2016 г.) переименование ветви, отмеченной в другом рабочем дереве, не корректировало символическое HEAD в указанном другом рабочем дереве.

См. Коммит 18eb3a9 (8 апреля 2016 г.) и коммит 70999e9, коммит 2233066 (27 марта 2016 г.) от Kazuki Yamaguchi ( rhenium )
(Объединено Юнио С Хамано - gitster - в коммите 741a694, 18 апреля 2016 г.)

branch -m: обновить все заголовки для каждого рабочего дерева

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

Это текущее поведение, заголовок /path/to/wt не обновляется:

  % git worktree list
  /path/to     2c3c5f2 [master]
  /path/to/wt  2c3c5f2 [oldname]
  % git branch -m master master2
  % git worktree list
  /path/to     2c3c5f2 [master2]
  /path/to/wt  2c3c5f2 [oldname]
  % git branch -m oldname newname
  % git worktree list
  /path/to     2c3c5f2 [master2]
  /path/to/wt  0000000 [oldname]

Этот патч исправляет эту проблему, обновляя все соответствующие заголовки рабочего дерева при переименовании ветви.


Механизм блокировки официально поддерживается git 2.10 (3 квартал 2016 г.)

См. Коммит 080739b, коммит 6d30862, коммит 58142c0, коммит 346ef53, коммит 346ef53, коммит 58142c0, коммит 346ef53, коммит 346ef53 (13 июня 2016 г.) и коммит 984ad9e, коммит 6835314 (03 июня 2016 г.) от Nguyễn Thái Ngọc Duy ( pclouds )
Предложил: Эрик Саншайн ( sunshineco )
(Объединено Юнио С Хамано - gitster - в коммите 2c608e0, 28 июля 2016 г.)

git worktree lock [--reason <string>] <worktree>
git worktree unlock <worktree>

Если связанное рабочее дерево хранится на переносном устройстве или в общем сетевом ресурсе, который не всегда монтируется, вы можете предотвратить удаление его административных файлов, выполнив команду git worktree lock команда, опционально указывающая --reason объяснить, почему рабочее дерево заблокировано.

<worktree>: Если последние компоненты пути в пути рабочего дерева уникальны среди рабочих деревьев, его можно использовать для идентификации рабочих деревьев.
Например, если вам нужно только рабочие деревья на " /abc/def/ghi" а также " /abc/def/ggg ", затем " ghi " или же " def/ghi "Достаточно указать на прежнее рабочее дерево.


Git 2.13 (Q2 2017) добавить lock опцион в коммите 507e6e9 (12 апреля 2017 г.) Нгуен Тхай Нгук Дуй ( pclouds )
Предложил: Дэвид Тейлор ( dt )
Помогает: Джефф Кинг ( peff )
(Объединено Юнио С Хамано - gitster - в коммите e311597, 26 апреля 2017 г.)

Разрешить блокировать рабочее дерево сразу после его создания.
Это помогает предотвратить гонку между git worktree add; git worktree lock " а также " git worktree prune ".

Так git worktree add' --lock является эквивалентом git worktree lock после git worktree add, но без условий гонки.


Git 2.17+ (Q2 2018) добавляет git worktree move / git worktree remove: смотри этот ответ.


Git 2.19 (Q3 2018) добавить " --quiet "возможность сделать" git worktree add "менее многословный.

См. Коммит 371979c (15 августа 2018 г.) Элии Пинто ( devzero2000 )
Помогают: Мартин Агрен, Дуй Нгуен ( pclouds ) и Эрик Саншайн ( sunshineco )
(Объединено Юнио С Хамано - gitster - в комитете a988ce9, 27 августа 2018 г.)

worktree: добавлять --quiet вариант

Добавить ' --quiet вариант git worktree что касается другого git команды.
' add 'это единственная команда, затронутая этим, так как все другие команды, кроме' list ', в настоящее время по умолчанию молчат.

git дистрибутив поставляется с готовым скриптом git-new-workdir, Вы бы использовали его следующим образом:

git-new-workdir project-dir new-workdir branch

где project-dir - это имя каталога, содержащего ваш .git репозиторий. Этот сценарий создает другой каталог `.git'со многими символическими ссылками на исходный, за исключением файлов, которые не могут быть общими (например, текущая ветка), что позволяет вам работать в двух разных ветках.

Это звучит немного хрупко, но это вариант.

Я наткнулся на этот вопрос в надежде найти решение, которого не нашел здесь. Так что теперь, когда я нашел то, что мне нужно, я решил опубликовать это здесь для других.

Предостережение: это, вероятно, не очень хорошее решение, если вам нужно редактировать несколько веток одновременно, например, состояния OP. Вы не собираетесь редактировать несколько веток одновременно. (Несколько рабочих каталогов, поддерживаемых одной папкой.git.)

С тех пор, как я впервые пришел к этому вопросу, я кое-чему научился:

  1. Что такое " голое хранилище ". По сути, это содержание .git каталог, не находясь в рабочем дереве.

  2. Тот факт, что вы можете указать местоположение репо, которое вы используете (местоположение вашего .git dir) в командной строке с git вариант --git-dir=

  3. Тот факт, что вы можете указать местоположение вашей рабочей копии с --work-tree=

  4. Что такое "зеркальное репо".

Это последнее довольно важное различие. На самом деле я не хочу работать с репо, мне просто нужно, чтобы копии разных веток и / или тегов проверялись одновременно. На самом деле, я должен гарантировать, что ветви не будут отличаться от ветвей моего пульта. Так что зеркало идеально подходит для меня.

Так что для моего случая использования я получил то, что мне было нужно, выполнив:

git clone --mirror <remoteurl> <localgitdir> # Where localgitdir doesn't exist yet
mkdir firstcopy
mkdir secondcopy
git --git-dir=<localgitdir> --work-tree=firstcopy checkout -f branch1
git --git-dir=<localgitdir> --work-tree=secondcopy checkout -f branch2

Большое предостережение об этом заключается в том, что для этих двух копий нет отдельного заголовка. Итак, после вышесказанного, работает git --git-dir=<localgitdir> --work-tree=firstcopy status покажет все отличия от branch2 к branch1 как незафиксированные изменения - потому что HEAD указывает на branch2. (Вот почему я использую -f возможность checkout потому что я на самом деле не планирую вносить какие-либо изменения локально. Я могу проверить любой тег или ветку для любого рабочего дерева, если я использую -f опция).

Для моего случая использования нескольких проверок, сосуществующих на одном компьютере без необходимости их редактирования, это прекрасно работает. Я не знаю, есть ли какой-нибудь способ иметь несколько HEAD для нескольких рабочих деревьев без сценария, как описано в других ответах, но я надеюсь, что это в любом случае полезно для кого-то еще.

Единственное решение, которое я могу придумать, - это клонировать две директории и добавлять их как удаленные репозитории друг друга. Затем вы можете продолжать вытаскивать вещи из измененного в другой, фактически не выталкивая что-либо в удаленный репозиторий.

Я предполагаю, что вы хотите иметь два рабочих каталога, а не два клона удаленного, потому что вы не хотите выдвигать некоторые ветви на удаленный. В противном случае два клона вашего пульта будут работать нормально - вам просто нужно сделать несколько нажатий и нажатий, чтобы синхронизировать все три.

Другие вопросы по тегам