Несколько рабочих каталогов с Git? - PullRequest
226 голосов
/ 07 июня 2011

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

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

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

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

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

Ответы [ 4 ]

270 голосов
/ 12 мая 2015

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

См. коммит 68a2e6a от Джунио С. Хамано (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 г.) вы можете перечислить свои рабочие деревья.
См. commit bb9c03b , commit 92718b7 , commit 5193490 , commit 1ceb7f9 , commit 1ceb7f9 , commit 5193490 , commit 1ceb7f9 , commit 1ceb7f9 (08 октября 2015), commit 92718b7 , commit 5193490 , commit 1ceb7f9 , коммит 1ceb7f9 (08 октября 2015 г.), коммит 5193490 , коммит 1ceb7f9 (08 октября 2015 г.), коммит 1ceb7f9 (08 октября 2015 г.) и коммит ac6c561 (02 октября 2015 г.) Майкл Раппаццо (rappazzo) .
(Объединено с Junio ​​C Hamano - gitster - в commit 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 г.) от Нгуен Тай Нгок Дуй (pclouds) .
Помощник: Эрик Саншайн (sunshineco) .
(Объединено с Junio ​​C Hamano - gitster - в commit 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" удаление ветки, зарегистрированной в другом рабочем дереве.

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

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

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


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

См. коммит 18eb3a9 (08 апреля 2016 г.) и коммит 70999e9 , коммит 2233066 (27 марта 2016 г.) от Казуки Ямагути (* 1285) *) .
(Объединено Junio ​​C Hamano - 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 г.) Нгуен Тай Тай Нгуц Дуй (pclouds) .
Предложил: Эрик Саншайн (sunshineco) .
(Объединено Junio ​​C Hamano - gitster - в коммит 2c608e0 , 28 июля 2016 г.)

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

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

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


Git 2.13 (2 квартал 2017 года) добавить опцию lock в коммит 507e6e9 (12 апреля 2017) Nguy Thn Thái Ngọc Duy (pclouds) .
Предложил: Дэвид Тейлор (dt) .
Помощник: Джефф Кинг (peff) .
(Объединено Junio ​​C Hamano - 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) .
(Объединено Junio ​​C Hamano - gitster - в коммит a988ce9 , 27 августа 2018 г.)

worktree: добавить --quiet опция

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


Обратите внимание, что "git worktree add" раньше использовался для "поиска доступного имени с помощью stat. а затем mkdir ", что склонно к расе.
Это было исправлено в Git 2.22 (Q2 2019) с помощью mkdir и реакцией на EEXIST в цикле.

См. коммит 7af01f2 (20 февраля 2019 г.) Михал Суханек (hramrach) .
(Объединено Junio ​​C Hamano - gitster - в коммит 20fe798 , 09 апреля 2019 г.)

worktree: исправление worktree add раса

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


Git 2.22 (Q2 2019) исправляет логику, чтобы сказать, имеет ли рабочее хранилище Git рабочее дерево, защищает "git branch -D" от удаления ветви, которая в настоящий момент проверена по ошибке.
Реализация этой логики была нарушена для репозиториев с необычным именем, что, к сожалению, является нормой для подмодулей в наши дни.

См. коммит f3534c9 (19 апреля 2019 г.) Джонатан Тан (jhowtan) .
(Объединено с Junio ​​C Hamano - gitster - in commit ec2642a , 08 мая 2019 г.)

код Потяните запросы 178 Insights

worktree: обновление is_bare эвристика

Когда запускается "git branch -D <name>", Git обычно сначала проверяет, В данный момент ветка извлечена.
Но эта проверка не выполняется, если каталог Git этого репозитория не находится в «<repo>/.git», что является случаем, если этот репозиторий является подмодулем, в котором его каталог Git хранится как «super/.git/modules/<repo>», например.
Это приводит к удалению ветви, даже если она извлечена.

Это потому, что get_main_worktree() в worktree.c устанавливает is_bare на рабочее дерево только с использованием эвристики, что репо является голым, если рабочее дерево путь не заканчивается на "/.git" и не оголяется в противном случае.
Этот код is_bare был введен в 92718b7 worktree: добавить детали в структуру рабочего дерева», 2015-10-08, Git v2.7.0-rc0), следуя эвристике pre-core.bare.

Этот патч делает 2 вещи:

  • Научите get_main_worktree() использовать is_bare_repository() вместо этого, введено в 7d1864c («Представьте переменную конфигурации is_bare_repository () и core.bare», 2007-01-07, Git v1.5.0-rc1) и обновлено в e90fdc3 («Очистка обработки рабочего дерева», 2007-08-01, Git v1.5.3-rc4).
    Это решает проблему "git branch -D <name>", описанную выше.

Однако ... Если репозиторий имеет core.bare=1, но команда "git" запускается с одного из его вторичных рабочих деревьев, is_bare_repository() возвращает false (что нормально, так как рабочее дерево доступно).

И если рассматривать основное рабочее дерево как непокрытое, когда оно голое, возникает проблема:

Например, ошибка удаления ветки из вторичного рабочего дерева, на которую ссылается HEAD основного рабочего дерева, даже если это основное рабочее дерево пустое.

Во избежание этого также проверьте core.bare при настройке is_bare.
Если core.bare=1, доверяйте ему, а в противном случае используйте is_bare_repository().

113 голосов
/ 07 июня 2011

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

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

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

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

12 голосов
/ 07 декабря 2015

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

Предостережение: Это, вероятно, не очень хорошее решение, если вам нужно редактировать несколько веток одновременно, как состояния 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

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

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

3 голосов
/ 07 июня 2011

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

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

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