Кілька робочих каталогів з Git?


242

Я не впевнений, чи це щось підтримує Git, але теоретично здається, що це має працювати на мене.

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

Моє типове рішення для цього - зробити два каси, але шкода, що я не можу ділитись між ними гілками та реф. Я хотів би, щоб це було лише два робочих каталоги, якими керує одна і та ж папка .git.

Мені відомі локальні рішення для клонування Git (за замовчуванням, яке полягає у жорсткому зв’язку спільних об'єктів, та параметр --shared, який встановлює альтернативний сховище об’єктів з оригінальним репо), але ці рішення лише зменшують використання місця на диску , і особливо у випадку - спільного, здається загроженою небезпекою.

Чи є спосіб використовувати одну .git папку та мати дві робочі каталоги, підкріплені нею? Або Git жорстко кодиться, щоб у будь-який час було перевірено лише один робочий каталог?


1
git-new-workdirбуде замінено на git checkout --to=<path>Git 2.5. Дивіться мою відповідь нижче
VonC

3
Насправді команда буде git worktree add <path> [<branch>](Git 2.5 rc2). Дивіться мою відредаговану відповідь нижче
VonC

ви повинні змінити прийняту відповідь VonC, оскільки все змінилося з моменту, коли ви задали своє запитання.
xaxxon

дякую за оновлену відповідь!
jtolds

Відповіді:


293

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, індекс тощо. 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 (08 окт 2015), здійснюють 5193490 , зробити 1ceb7f9 (08 жовтня 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)

Також доступна опція фарфорового формату.

Формат порцеляни має рядок на атрибут.

  • Атрибути перераховані з міткою та значенням, розділеними одним пробілом.
  • Булеві атрибути (наприклад, 'голі' та 'відокремлені') перераховані лише як мітка та є лише тоді і лише тоді, коли значення є істинним.
  • Порожній рядок вказує кінець робочого дерева

Наприклад:

$ 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" дозволено видалення гілки, що перевіряється на іншому робочому дереві.

Див. Комісію f292244 (29 березня 2016 р.) Від Kazuki Yamaguchi ( rhenium) .
Допоможи: Ерік Саншайн ( sunshineco) .
(Об'єднав Хуніо С Хамано - gitster- у комітеті 4fca4e3 , 13 квітня 2016 р.)

branch -d: відмовитись від видалення філії, яка наразі перевірена

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


Так само до git 2.9 (червень 2016 року) перейменування філії, перевіреної в іншому робочому дереві, не коригувало символічну HEAD у вказаному іншому робочому дереві.

Див. Команду 18eb3a9 (08 квітня 2016 р.) Та виконувати 70999e9 , здійснити 2233066 (27 березня 2016 р.) Від Kazuki Yamaguchi ( rhenium) .
(Об’єднав Хуніо С Хамано - gitster- у комітеті 741a694 , 18 квітня 2016 р.)

branch -m: оновлення всіх головних робочих дерев

Під час перейменування гілки в даний час оновлюється лише HEAD поточного робочого дерева, але він повинен оновлювати HEAD усіх робочих дерев, які вказують на стару гілку.

Це поточна поведінка, / HEAD / шлях / до / 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 (Q3 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) Nguyễn Thái Ngọc Duy ( 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 worktree add" використовується для "знаходження доступного імені зі статусом, а потім mkdir", схильним до перегонів.
Це було виправлено за допомогою Git 2.22 (Q2 2019), використовуючи mkdirта реагуючи на EEXISTцикл.

Див. Комісію 7af01f2 (20 лютого 2019 р.) Від Міхала Сучанека ( hramrach) .
(Об'єднав Хуніо С Хамано - gitster- в комітеті 20fe798 , 09 квітня 2019 р.)

worktree: виправити worktree addгонку

Git запускає цикл stat, щоб знайти доступне ім'я робочого дерева, а потім - mkdirна знайдене ім'я.
Поверніть його на mkdirцикл, щоб уникнути чергового виклику робочого дерева, додайте пошук того самого вільного імені та створення каталогу.


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

Див. Комісію f3534c9 (19 квітня 2019 р.) Джонатана Тана ( jhowtan) .
(Об’єднав Хуніо С Хамано - gitster- в комітеті ec2642a , 08 травня 2019 р.)

Запит на отримання коду 178 Статистика

worktree: оновлення is_bareевристики

Коли " git branch -D <name>" запускається, Git зазвичай спочатку перевіряє, чи ця гілка в даний момент перевірена.
Але ця перевірка не виконується, якщо каталог Git цього репозиторію не знаходиться в " <repo>/.git", що має місце, якщо цей репозиторій є підмодулем, який зберігає super/.git/modules/<repo>, наприклад, каталог "Git" як " ".
Це призводить до того, що гілка буде видалена, навіть якщо вона перевірена.

Це тому, що get_main_worktree()в worktree.cнаборах is_bareна робочому дереві лише з використанням евристики, що репо є голою, якщо шлях робочого дерева не закінчується " /.git", а не оголений інакше.
Цей is_bareкод був введений у 92718b7 (" worktree: додати деталі до структури робочого дерева", 2015-10-08, Git v2.7.0-rc0), слідуючи pre-core.bareевристиці.

Цей патч робить дві речі:

  • Навчити 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()повертає помилку (що добре, оскільки є робоче дерево).

І ставлення до головного робочого дерева як неоголеного, коли воно голо, викликає проблеми:

Наприклад, не видалення гілки з вторинного робочого дерева, на яке посилається HEAD головного робочого дерева, навіть якщо це основне робоче дерево є оголеним.

Щоб уникнути цього, також перевірте core.bareпри налаштуванні is_bare.
Якщо core.bare=1, довіряйте, і інше, використовуйте is_bare_repository().


1
Це найкрутіша річ, яку вони створили, просто те, що я шукав. Дякую за це!

Як видалити лише робоче дерево та зберегти гілку
Randeep Singh

@DotnetRocks ви можете видалити будь-яке робоче дерево (локальну папку на вашому комп’ютері): це не матиме жодного впливу на гілку: головна .git repo все ще буде включати повну історію з усіма її гілками, незалежно від того робоче дерево видалено.
VonC

Так, але якщо просто видалити робоче дерево, перейшовши до цієї папки в моїй системі та видалити, то git не дозволяє мені перевірити цю гілку і каже, що вже зареєструватися на <path> (шлях шляху до робочого дерева). Але виглядає так, якби я зробив наступне: rm -rf <path> git worktree prune, то це працює. Це так ?
Рандіп Сінгх

1
@Jayan Дякую Я зрозумів, що 2,5 та 2,7 вже вийшли.
VonC

113

У gitрозповсюдження входить сценарій, що називається git-new-workdir. Ви використовуєте його наступним чином:

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

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

Звучить трохи крихко, але це варіант.


3
+1 Я виправлений, це досить приголомшливо. Здається, миттєво ділиться історією та гілками між двома різними перевіреними сховищами, не натискаючи / витягуючи, лише символізуючи. Я абсолютно не знав, що git може це впоратися. На жаль, це не включено до мого розповсюдження.
meagar

2
Для тих , хто використовує msysgit (вікна) , ви можете використовувати цю перенесену версію сценарію: github.com/joero74/git-new-workdir
AMOS

9
Зазвичай це добре, але якщо ви випадково відредагували одну й ту саму гілку в різних місцях, виправити речі не можна.
grep

Для тих, хто застряг на Git <2.5 та тих, хто має підмодулі, спробуйте, для git-new-workdir-recursiveяких це обгортка git-new-workdir.
Walf

13

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

Caveat: Це, мабуть, не гарне рішення, якщо вам потрібно редагувати кілька гілок одночасно, як, наприклад, стан OP. Це наявність декількох гілок Видано одночасно , що ви НЕ намір редагувати. (Кілька робочих каталогів, підкріплених однією папкою .git.)

З того часу, як я вперше прийшов до цього питання, було кілька речей:

  1. Що таке " голий сховище ". Це по суті вміст .gitкаталогу, не розташовуючись у робочому дереві.

  2. Те, що ви можете вказати місце розташування репо, яке ви використовуєте (місце розташування вашого .gitdir) у командному рядку за допомогою 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покаже всі відмінності від гілки2 до гілки1 як непомітні зміни - адже HEAD вказує на гілку2. (Ось чому я використовую цю -fопцію checkout, тому що насправді я взагалі не планую вносити будь-які зміни на локальному рівні. Я можу перевірити будь-який тег чи гілку для будь-якого робочого дерева, поки я використовую цю -fопцію.)

Для мого використання, коли на одному комп’ютері співіснують кілька кас, не потребуючи їх редагування , це прекрасно працює. Я не знаю, чи є спосіб створити кілька головних заголовків для декількох робочих дерев без такого сценарію, як описано в інших відповідях, але я сподіваюся, що це корисно комусь іншому.


Це саме те, що я шукав, але я не можу змусити його працювати ... я отримую "фатально: Не сховище git: '<localgitdir>'". Будь-які ідеї?
Dan R

Nevermind, виявляється, я використовував "~" у своєму імені каталогу, а git не сподобався. Коли я використовував повний шлях, це спрацювало чудово.
Dan R

@DanR, радий, що це допомогло. :) Ви також можете використовувати $HOME. Існує ще одне застереження щодо вищевказаного методу, який я виявив пізніше, що стосується файлів, які не існують в тій чи іншій гілці. Якщо ви зареєструєте A на dir1, то замовляєте B на dir2, а потім примушуєте оформляти замовлення C у dir1, якщо є файл, який існує в A, але не в B або C, файл не буде видалений з dir1 навіть силами оформлення замовлення . Тож вам може знадобитися експериментувати git cleanв такому випадку - або робити те, що я зробив, і просто використовувати цей метод лише для заповнення щойно створеного каталогу.
Wildcard

Дякую за пораду. Я все одно буду обгортати це як частину інструменту CLI в рубіні, тому переконуюсь, що він завжди починається з нуля.
Dan R

@DanR, це може зацікавити вас переглянути код, про який я писав у той час . Більшість із них, як правило, застосовні до будь-якого сценарію встановлення git, за винятком перевірок синтаксису CFEngine. (Можливо, ви можете замінити це чеками синтаксису Ruby.) :)
Wildcard

3

Єдине рішення, про яке я можу придумати - це клонувати два каталоги та додавати їх у віддалені сховища один одного. Потім ви можете продовжувати витягувати речі зі зміненого в інший, фактично нічого не натискаючи на віддалений сховище.

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


Привіт. Хлопець вище поділився крутим рішенням, командою git worktree . Він працює прекрасніше, ніж клонувати кілька разів одне сховище. Спробуйте, ця нова функція вам сподобається.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.