Articles

Mastering podmodules Git

Podmodules, krok po kroku

będziemy teraz badać każdy krok korzystania z podmodules w projekcie współpracy, upewniając się, że wyróżniamy domyślne zachowania, pułapki i dostępne ulepszenia.

aby ułatwić Ci śledzenie, ułożyłem kilka przykładowych reposów z ich „pilotami” (właściwie tylko katalogami)., Możesz rozpakować archiwum w dowolnym miejscu, a następnie otworzyć powłokę (lub Git Bash, jeśli jesteś w systemie Windows) w katalogu Git-subs, który tworzy:

Pobierz przykładowe repos

znajdziesz tam trzy katalogi:

  • main działa jako repo kontenera, lokalny do pierwszego współpracownika,
  • wtyczka działa jako centralny repo konserwacji dla modułu, a
  • remotes zawiera system plików Piloty do dwóch poprzednich repo.

w przykładowych poleceniach poniżej, znak zachęty zawsze wyświetla, które repo robimy.,

dodawanie podmodułu

Zacznijmy od dodania naszej wtyczki jako podmodułu wewnątrz naszego kontenera (który znajduje się w main). Sama wtyczka ma prostą strukturę:

.
├── README.md
├── lib
│ └── index.js
└── plugin-config.json

więc wejdźmy do main i użyjmy polecenia git submodule add. Pobiera adres URL pilota i podkatalog, w którym „tworzy instancję” podmodułu.

ponieważ używamy ścieżek zamiast adresów URL dla naszych pilotów, trafiliśmy na dziwny, choć dobrze znany, problem: ścieżki względne dla pilotów są interpretowane względem naszego głównego pilota, nie do katalogu głównego repo., To jest super dziwne, nigdzie nie opisane, ale widziałem to za każdym razem. Więc zamiast mówić ../ remotes / plugin, we just say ../ align = „left” /

main (master u=) $ git submodule add ../plugin vendor/plugins/demo
Cloning into 'vendor/plugins/demo'…
done.
main (master + u=) $

to dodało kilka ustawień w naszej lokalnej konfiguracji:

main (master + u=) $ cat .git/config


url = ../remotes/plugin

i to również zainscenizowało dwa pliki:

Huh?! Co to jest ?plik gitmodules? Spójrzmy na to:

main (master + u=) $ cat .gitmodules

path = vendor/plugins/demo
url = ../plugin

to strasznie przypomina nasz lokalny config… więc po co powielanie? Właśnie dlatego, że nasz lokalny config jest … lokalny., Nasi współpracownicy tego nie zobaczą (co jest zupełnie normalne), więc potrzebują mechanizmu, aby uzyskać definicje wszystkich podmoduł, które muszą skonfigurować we własnych operacjach repos. Oto co .gitmodules jest dla; zostanie on odczytany później przez polecenie Git submodule INIT, co zobaczymy za chwilę.

podczas gdy jesteśmy na statusie, zauważ, jak minimalistyczny jest to, jeśli chodzi o nasz podmoduł: po prostu chodzi o zbyt ogólny nowy plik, zamiast mówić nam więcej o tym, co się dzieje w nim., Nasz podmoduł został rzeczywiście wprowadzony do podkatalogu:


└── vendor
└── plugins
└── demo
├── .git
├── README.md
├── lib
│ └── index.js
└── plugin-config.json

Status, podobnie jak logi i diffy, jest ograniczony do aktywnego repo (obecnie kontener), a nie do podmodułów, które są zagnieżdżonymi repo. Jest to często problematyczne (bardzo łatwo jest pominąć regresję, gdy ogranicza się do tego widoku), więc zalecam skonfigurowanie statusu świadomego podmodułu raz na zawsze:

git config --global status.submoduleSummary true

i teraz:

Aaaah, to jest znacznie lepsze., Status rozszerza swoje podstawowe informacje, aby dodać, że podmoduł obecny w vendor / plugins / demo otrzymał 3 commity newsów (ponieważ właśnie go stworzyliśmy, oznacza to, że zdalna gałąź miała tylko trzy commity), ostatni z nich jest dodatkiem (zwróć uwagę na wspornik kątowy skierowany w prawo >) z pierwszą linią Komunikatu o zatwierdzeniu, która brzmi „Fix repo name…”.

aby naprawdę sprowadzić do domu, że mamy do czynienia z dwoma oddzielnymi repo tutaj, wejdźmy do katalogu podmodułu:

aktywne repo się zmieniło, ponieważ nowy .,git przejmuje: w bieżącym katalogu (demo, katalog podmodułu), a .git rzeczywiście istnieje, jeden plik, a nie Katalog. Zajrzyjmy do środka:

demo (master u=) $ cat .git
gitdir: ../../../.git/modules/vendor/plugins/demo

ponownie, od wersji Git 1.7.8, Git nie zostawia katalogów repo w katalogu roboczym kontenera, ale scentralizuje je w kontenerze .katalog git (inside .git / modules) i używa referencji gitdir w podmodułach.,

uzasadnienie tego jest proste: pozwala repo kontenera na posiadanie gałęzi bez podmodułu, bez konieczności usuwania repo z katalogu roboczego i przywracania go później.

Oczywiście, podczas dodawania podmodułu, możesz wybrać użycie określonej gałęzi, a nawet określonego commita, używając opcji-b CLI (jak zwykle domyślną wartością jest master). Zauważ, że nie jesteśmy teraz na odłączonej głowie, w przeciwieństwie do tego, co stanie się później: dzieje się tak dlatego, że Git wymeldował master, a nie konkretny SHA1. Musielibyśmy określić SHA1 do-b, aby uzyskać oderwaną głowę od startu.,

wróćmy więc do repo kontenera i zakończmy dodawanie podmodułu i przesuńmy go do pilota:

demo (master u=) $ cd -
main (master + u=) $ git commit -m "Ajout submodule plugin demo"
main (master u+1) $ git push

chwytając repo, które używa podmodułów

aby zilustrować problemy ze współpracą nad repo, które używa podmodułów, podzielimy osobowości i będziemy działać jako nasz kolega, który klonuje pilota kontenera, aby rozpocząć z nami współpracę. Sklonujemy to w katalogu kolegi, abyśmy mogli od razu stwierdzić, którą czapkę osobowości mamy na sobie w danym momencie.,

pierwszą rzeczą, którą należy zauważyć, jest to, że brakuje naszego podmodułu w katalogu roboczym; tylko jego katalog podstawowy jest tutaj:

vendor
└── plugins
└── demo

Jak to się stało? Wynika to po prostu z faktu, że do tej pory nasz nowy repo (kolega) nie jest jeszcze świadomy naszego podmodułu: informacji dla niego nie ma nigdzie w jego lokalnej konfiguracji (sprawdź its .git / config jeśli mi Nie wierzysz). Musimy to wypełnić, na podstawie czego .gitmodules ma do powiedzenia, co dokładnie robi Git submodule init:

Our .git / config jest teraz świadomy naszego podmodułu., Jednak nadal nie pobraliśmy go z jego pilota, nie mówiąc o tym, że jest obecny w naszym katalogu roboczym. A jednak nasz status jest czysty!

widzisz, musimy pobrać odpowiednie commity ręcznie. To nie jest coś, co zrobił nasz pierwszy klon, musimy to zrobić przy każdym pociągnięciu. Wrócimy do tego za chwilę, ponieważ jest to klon zachowania, który może automatyzować, gdy zostanie poprawnie wywołany.,

w praktyce, gdy mamy do czynienia z podmodułami używającymi repos, Zwykle grupujemy dwie komendy (INIT i update) w jedną:

colleague (master u=) $ git submodule update --init

szkoda, że Git kazał ci to zrobić samemu. Wyobraź sobie, że na większych projektach nici dentystycznych, gdy podmoduły mają swoje własne podmoduły, itd., to szybko stanie się koszmarem.

tak się składa, że Git udostępnia opcję CLI dla klonowania, aby automatycznie podmodułować update-INIT rekurencyjnie zaraz po klonowaniu: raczej trafnie nazwaną opcję rekurencyjną.,

więc spróbujmy jeszcze raz:

teraz jest lepiej! Zauważ, że jesteśmy teraz na odłączonej głowicy wewnątrz podmodułu (jak będziemy od teraz):

git-subs $ cd colleague/vendor/plugins/demo
demo ((master)) $

widzisz podwójny zestaw nawiasów w moim monicie, zamiast pojedynczego zestawu?, Jeśli twój monit nie jest skonfigurowany tak jak mój, aby wyświetlać odłączoną głowicę zgodnie z opisem (z wbudowanym skryptem zachęty Gita, musisz zdefiniować zmienną środowiskową GIT_PS1_DESCRIBE_STYLE=branch), zobaczysz coś takiego:

demo ((fe64799...)) $

w każdym razie status potwierdza, gdzie jesteśmy:

demo ((master)) $ git status
HEAD detached at fe64799
nothing to commit, working directory clean

uzyskanie aktualizacji z podmodułu zdalnego

ok, teraz, gdy mamy nasz własny repo (główny) i nasz „kolega” (kolega) wszystko skonfigurowane do współpracy, wejdźmy w buty trzeciej osoby: ten, który utrzymuje wtyczkę., Przejdźmy do tego:

teraz dodajmy dwa pseudo-commity i opublikujmy je na zdalnym serwerze:

na koniec Załóżmy ponownie nasz „pierwszy programista”:

plugin (master u=) $ cd ../main
main (master u=) $

Załóżmy, że teraz chcemy umieścić te dwa commity w naszym podmodule. Aby to osiągnąć, musimy zaktualizować jego lokalny repo, zaczynając od przeniesienia do jego katalogu roboczego, aby stał się naszym aktywnym repo.

na marginesie, nie polecam używania pull do tego rodzaju aktualizacji., Aby poprawnie pobierać aktualizacje w katalogu roboczym, polecenie to wymaga, abyś był w odpowiedniej aktywnej gałęzi, której zazwyczaj nie masz (przez większość czasu jesteś na odłączonej głowie). Musiałbyś zacząć od kasy tego oddziału. Ale co ważniejsze, zdalna gałąź mogła bardzo dobrze posunąć się dalej od zmiany, którą chcesz ustawić, a przyciągnięcie wprowadziłoby zmiany, których możesz nie chcieć w lokalnej bazie kodu.,

dlatego zalecam ręczne rozdzielenie procesu: najpierw git fetch, aby pobrać wszystkie nowe dane ze zdalnego pamięci podręcznej, a następnie zaloguj się, aby zweryfikować to, co masz i dokonać płatności na żądanym SHA1. Oprócz kontroli drobnoziarnistej, takie podejście ma dodatkową zaletę pracy niezależnie od aktualnego stanu (aktywna gałąź lub odłączona Głowica).

OK, więc jesteśmy dobrzy, żadnych zbędnych zobowiązań., Tak czy inaczej, ustawmy jawnie ten, który nas interesuje (oczywiście masz inny SHA1):

demo (master u-2) $ git checkout -q 0e90143

(- q jest tylko po to, aby oszczędzić nam Git gadania o tym, jak kończymy na odłączonej głowie. Zazwyczaj byłoby to zdrowe przypomnienie, ale w tym przypadku wiemy, co robimy.)

teraz, gdy nasz podmoduł został zaktualizowany, możemy zobaczyć wynik w statusie repo kontenera:

w „klasycznej” części statusu widzimy nowy typ zmiany commitów, co oznacza zmianę odwołanego commitu., Inną możliwością (która może być połączona z tą) jest nowa zawartość, co oznaczałoby, że wprowadziliśmy lokalne zmiany w katalogu roboczym podmodułu.

dolna część, włączona przez nasz status.submoduleSummary = true ustawienie wcześniejsze jawnie określa wprowadzone commity (ponieważ używają nawiasu kątowego skierowanego w prawo >) od ostatniego commita kontenera, który dotknął podmodułu.

w rodzinie „terrible default behaviors”, git diff pozostawia wiele do życzenia:

co do…?, Jest opcja CLI, która pozwala nam zobaczyć coś bardziej użytecznego:

main (master * u=) $ git diff --submodule=log
Submodule vendor/plugins/demo fe64799..0e90143:
> Pseudo-commit #2
> Pseudo-commit #1

nie ma obecnie żadnych innych lokalnych zmian poza commitem podmodule… zauważ, że pasuje to prawie dokładnie do dolnej części naszego rozszerzonego wyświetlania statusu Gita.

konieczność wpisywania tego rodzaju opcji CLI za każdym razem (która, nawiasem mówiąc, nie pojawia się w bieżących ofertach zakończenia Gita) jest raczej nieporęczna. Na szczęście istnieje dopasowane ustawienie konfiguracji:

teraz musimy tylko wykonać commit kontenera, który finalizuje aktualizację naszego podmodułu., Jeśli musisz dotknąć kodu kontenera, aby działał z tą aktualizacją, zatwierdź go, naturalnie. Z drugiej strony, unikaj mieszania zmian związanych z podmodułami i innych rzeczy, które odnoszą się tylko do kodu kontenera: poprzez porządne rozdzielenie tych dwóch, późniejsze migracje do innych podejść do ponownego użycia kodu są łatwiejsze (również, jak zwykle, Atomic commits FTW).

ponieważ mamy zamiar pobrać tę aktualizację podmodułu w repo naszego kolegi, będziemy naciskać zaraz po zatwierdzeniu (co nie jest ogólną dobrą praktyką).

main (master * u=) $ git commit -am "Setting submodule on PC2"
main (master u+1) $ git push

Ściąganie podmodułu-za pomocą repo

kliknij!, Czapka „koleżanka”!

więc pobieramy aktualizacje ze zdalnego repo kontenera…

(możesz nie mieć „pomyślnie rebased and updated…” i zobaczyć „Merge made by the 'recursive' strategy ” zamiast tego. Jeśli tak, moje serce idzie do Ciebie, i należy natychmiast dowiedzieć się, dlaczego ciągnie powinien rebase).

zwróć uwagę na drugą połowę tego wyświetlacza: chodzi o moduł podrzędny, zaczynający się od „pobieranie modułu podrzędnego…”.

to zachowanie stało się domyślne w Git 1.7.5, z ustawieniem konfiguracji fetch.,recurseSubmodules domyślnie ustawia się na On-demand: jeśli projekt kontenera otrzyma aktualizacje do odwołanych zatwierdzeń podmodułów, te podmoduły zostaną pobrane automatycznie. (Pamiętaj, pobieranie jest pierwszą częścią ciągnięcia.)

nadal, a to jest krytyczne: Git automatycznie pobiera, ale nie automatycznie aktualizuje. Twoja lokalna pamięć podręczna jest aktualna z podmodułem remote, ale katalog roboczy podmodułu został przyklejony do poprzedniej zawartości. Przynajmniej możesz zamknąć tego laptopa, wskoczyć do samolotu, i nadal iść naprzód, gdy będziesz offline., Chociaż to automatyczne pobieranie jest ograniczone do już znanych podmodułów: żadne nowe, jeszcze nie skopiowane do lokalnej konfiguracji, nie są automatycznie pobierane.

Git automatycznie pobiera, ale nie aktualizuje.

bieżący znak zachęty, z gwiazdką (*), podpowiada lokalne modyfikacje, ponieważ nasz WD nie jest zsynchronizowany z indeksem, ten ostatni jest świadomy nowo odwołanych commitów podmodułu. Sprawdź status:

zauważ jak nawiasy kątowe wskazują w lewo (<)?, Git widzi, że obecny WD nie posiada tych dwóch commitów, wbrew oczekiwaniom projektu kontenera.

jest to ogromne niebezpieczeństwo: jeśli nie zaktualizujesz katalogu roboczego podmodułu, twój następny commit kontenera cofnie ten podmoduł. To pułapka pierwszego rzędu.

jest zatem obowiązkowe, aby zakończyć aktualizację:

tak długo, jak staramy się stworzyć ogólne dobre nawyki, preferowaną komendą tutaj będzie submodule Git update-INIT-recursive, w celu automatycznego init każdego nowego submodule, i rekurencyjnie aktualizować je, jeśli zajdzie taka potrzeba.,

jest jeszcze jeden przypadek edge: jeśli zdalny URL podmodułu zmienił się od ostatniego użycia (być może jeden ze współpracowników zmienił go w .gitmodules), musisz ręcznie zaktualizować lokalny config, aby to dopasować. W takiej sytuacji, przed aktualizacją submodule git, musisz uruchomić submodule git sync.

powinienem wspomnieć, że nawet jeśli Git submodule update domyślnie sprawdza odwołany SHA1, możesz to zmienić na, na przykład, rebase dowolnej lokalnej pracy podmodule (o tym wkrótce porozmawiamy)., Można to zrobić, ustawiając ustawienia konfiguracji aktualizacji dla podmodułu, aby rebase w lokalnej konfiguracji kontenera.

i przykro mi, ale nie, nie ma lokalnego ustawienia konfiguracji, ani nawet opcji CLI, która może automatycznie aktualizować po ściągnięciu. Aby zautomatyzować takie rzeczy, musisz użyć aliasów, niestandardowych skryptów lub starannie przygotowanych lokalnych hooków., Poniżej znajduje się przykładowy alias spull (pojedynczy wiersz, podzielony na wyświetlacz):

git config --global alias.spull '!git pull && git submodule sync --recursive && git submodule update --init --recursive'

Jeśli chcesz zachować możliwość przekazywania własnych argumentów do git pull, możesz albo zdefiniować funkcję w locie i ją wywołać, albo użyć niestandardowego skryptu. Pierwsze podejście wyglądałoby tak (znowu jednolinijkowo):

niezbyt czytelne, co? Wolę niestandardowe podejście do scenariusza., Załóżmy, że umieściłeś plik skryptu git-spull gdzieś w swojej ścieżce (mam katalog ~ / perso / bin w mojej ścieżce tylko do takich rzeczy):

#! /bin/bash
git pull "$@" &&
git submodule sync --recursive &&
git submodule update --init --recursive

następnie nadajemy mu prawa wykonania:

chmod +x git-spull

i teraz możemy go używać tak, jakbyśmy użyli aliasu.

aktualizacja podmodułu w kontenerze

jest to najtrudniejszy przypadek użycia i powinieneś trzymać się od niego jak najwięcej, preferując konserwację za pośrednictwem centralnego, dedykowanego repo.,

może się jednak zdarzyć, że kod podmodułu nie może być testowany, ani nawet kompilowany poza kodem kontenera. Wiele motywów i wtyczek ma takie ograniczenia.

pierwszą rzeczą do zrozumienia jest to, że ponieważ zamierzasz tworzyć commity, musisz zacząć od odpowiedniej podstawy, która będzie wskazówką dla gałęzi. Dlatego musisz sprawdzić, czy ostatnie commity gałęzi nie „zepsują” twojego projektu kontenera. Jeśli tak, tworzenie własnej gałęzi specyficznej dla kontenera w podmodule brzmi kusząco, ale ta ścieżka prowadzi do silnego sprzężenia między podmodułem a kontenerem, co nie jest wskazane., Możesz przestać „submodulować” ten kod w tym konkretnym projekcie i po prostu osadzić go jak zwykłą zawartość.

przyznajmy, że z czystym sumieniem możesz dodać do aktualnej gałęzi master podmodule. Zacznijmy od zsynchronizowania naszego lokalnego stanu na zdalnych:

innym sposobem na to byłoby, z repo kontenera, jawne zsynchronizowanie lokalnej gałęzi podmodułu nad jego śledzoną zdalną gałęzią (pojedyncza linia na górze, ostatnia – po której następuje Biała spacja):

możemy teraz edytować kod, sprawić, by działał, przetestować go, itp., Kiedy już będziemy gotowi, możemy wykonać dwa commity i dwa niezbędne pchnięcia(jest to bardzo proste, a w praktyce zbyt częste, aby o niektórych z nich zapomnieć).

Po prostu dodajmy fałszywą pracę i zróbmy Dwa powiązane commity, na poziomie podmodułu i kontenera:

w tym momencie największym niebezpieczeństwem jest zapomnienie o wciśnięciu podmodułu. Wracasz do projektu kontenera, zatwierdzasz go i wciskasz tylko kontener. Jest to łatwy błąd do popełnienia, szczególnie wewnątrz IDE lub GUI. Kiedy twoi koledzy próbują uzyskać informacje, rozpętuje się piekło., Spójrz na pierwszy krok:

absolutnie nic nie wskazuje na to, że Git nie mógł pobrać odwołanego commita ze zdalnego podmodułu. Pierwsza podpowiedź tego znajduje się w statusie:

zauważ Ostrzeżenie: najwyraźniej, nowo odwołany commit dla podmodułu nigdzie nie został znaleziony. W rzeczy samej, jeśli spróbujemy zaktualizować katalog roboczy podmodułu, otrzymamy:

main (master * u=) $ git submodule update
fatal: reference is not a tree: 12e3a529698c519b2fab790630f71bd531c45727
Unable to checkout '12e3a529698c519b2fab790630f71bd531c45727' in submodule path 'vendor/plugins/demo'

możesz wyraźnie zobaczyć, jak ważne jest, aby pamiętać o wciśnięciu podmodułu, najlepiej przed wciśnięciem kontenera., Zróbmy to w koleżance i spróbujmy ponownie zaktualizować:

powinienem zauważyć, że istnieje opcja CLI, która zweryfikuje, czy aktualnie odwołane submodule wymagają wypchnięcia, a jeśli tak, to je wypchnie: jest to git push-recurse-submodules=on-demand (co prawda dość gęsty). Musi jednak mieć coś na poziomie kontenera, aby popchnąć go do pracy: tylko podmoduły tego nie wyciąną.

Co więcej, (nie ma ustawienia konfiguracji dla tego, więc trzeba by ustandaryzować procedury wokół aliasu, np. spush:) – począwszy od Git 2.7.0, jest teraz push.,recurseSubmodules konfiguracje ustawienia można zdefiniować(na żądanie lub sprawdzić).

git config --global alias.spush 'push --recurse-submodules=on-demand'

usuwanie podmodułu

są dwie sytuacje, w których chcesz „usunąć” podmoduł:

  • po prostu chcesz wyczyścić katalog roboczy (być może przed archiwizacją WD kontenera), ale chcesz zachować możliwość jego późniejszego przywrócenia (więc musi pozostać w .gitmodules i .git / modules);
  • chcesz definitywnie usunąć go z bieżącej gałęzi.

zobaczmy każdy przypadek po kolei.,

tymczasowe usuwanie podmodułu

pierwsza sytuacja jest łatwo obsługiwana przez podmoduł git deinit. Przekonajcie się sami:

nie ma to żadnego wpływu na status kontenera. Podgatunek nie jest już lokalnie znany (zniknął z .git / config), więc jego nieobecność w katalogu roboczym pozostaje niezauważona. Nadal mamy katalog vendor / plugins / demo, ale jest pusty; możemy go rozebrać bez konsekwencji.

podmoduł nie może mieć żadnych lokalnych modyfikacji, gdy to zrobisz, w przeciwnym razie musisz wymusić wywołanie.,

każdy późniejszy submodule git będzie błogi ignorował ten submodule, dopóki nie uruchomisz go ponownie, ponieważ submodule nie będzie nawet w lokalnej konfiguracji. Takie polecenia obejmują update, foreach i sync.

z drugiej strony podmoduł pozostaje zdefiniowany w .gitmodules: init, po którym następuje aktualizacja (lub pojedynczy update — init) przywróci go jako nowy:

trwałe usunięcie podmodułu

oznacza to, że chcesz pozbyć się podmodułu na dobre: zrobi to zwykły git rm, tak jak w przypadku każdej innej części katalogu roboczego., To zadziała tylko wtedy, gdy twój podmoduł używa pliku gitfile (a .git, który jest plikiem, a nie katalogiem), co ma miejsce począwszy od Git 1.7.8. W przeciwnym razie będziesz musiał poradzić sobie z tym ręcznie(powiem Ci, jak na końcu).

oprócz usunięcia podmodułu z katalogu roboczego, polecenie zaktualizuje .plik gitmodules, więc nie odwołuje się już do podmodułu. Proszę bardzo:

oczywiście, zaawansowane informacje o statusie przerzucają się tutaj, ponieważ plik gitfile dla podmodułu zniknął(właściwie cały katalog demo zniknął).,

dziwne jest jednak to, że lokalny config zachowuje informacje o podmodule, w przeciwieństwie do tego, co się dzieje, gdy deinit. Tak więc, dla kompleksowego usuwania, polecam zrobić oba, w kolejności, tak, aby skończyć prawidłowo wyczyszczone (to nie działa po naszej poprzedniej komendy, ponieważ wyczyszczone .gitmodules już):

git submodule deinit path/to/module # ensure local config cleanup
git rm path/to/module # clean WD and .gitmodules

niezależnie od Twojego podejścia, repo submodule pozostaje obecne w .git / modules/vendor/plugins / demo, ale możesz to zabić, kiedy tylko chcesz.

Jeśli kiedykolwiek będziesz potrzebował usunąć podmodule skonfigurowane przed wersją Git 1.7.,8, a zatem osadza jego .katalog git bezpośrednio w katalogu roboczym kontenera (zamiast polegać na pliku gitfile), będziesz musiał wyłamać bulldozer: poprzednie dwa polecenia muszą być poprzedzone ręcznym usunięciem folderów, np. RM-fr vendor / plugins/demo, ponieważ wspomniane polecenia zawsze odmówią usunięcia rzeczywistego repozytorium.