Wydzielenie plików wraz z historią zmian przy pomocy git subtree

11 styczeń 2015

Często podczas rozwijania jakiegoś projektu okazuje się, że pewna jego część, biblioteka czy skrypty, powinna posiadać własne repozytorium. Oczywiście można ją w zwyczajny sposób przenieść, ale mimo wszystko byłoby miło móc zachować także historię zmian przenoszonych plików. Jednym z możliwych rozwiązań jest git subtree.

Na początek pozwolę sobie narzucić beznadziejny przykład repozytorium, na którym będę operować:

% tree
.
├── 01
├── 02
├── 03
├── 04
├── 05
├── 06
├── 07
├── 08
├── 09
└── tens
    ├── 10
    ├── 11
    ├── 12
    ├── 13
    ├── 14
    ├── 15
    ├── 16
    ├── 17
    ├── 18
    └── 19

1 directory, 19 files

Jednym z wymogów użycia subtree do wydzielenia plików do osobnego repozytorium jest umieszczenie ich w jednym katalogu. W tym przypadku przeniesione zostaną pliki z katalogu tens. Jednak zanim to nastąpi, należy utworzyć gdzieś docelowe repo. Dla uproszczenia przykładu wyodrębnię pliki do repozytorium znajdującego się w innym katalogu.

% git init --bare
Initialized empty Git repository in /tmp/tens/

Wracam teraz do oryginalnego repozytorium. Za pomocą subtree wydzielam dziesiątki do osobnej gałęzi:

% git subtree split --prefix=tens -b tens
Created branch 'tens'
6469df9c55569ba3805b875f9f664f15eade74d0

% git ls-tree tens . | awk '{print $NF}'
10
11
12
13
14
15
16
17
18
19

Pozostaje tylko wypchnąć zmiany na inny serwer:

% git push /tmp/tens tens:master
Counting objects: 15, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.26 KiB | 0 bytes/s, done.
Total 15 (delta 6), reused 0 (delta 0)
To /tmp/tens
 * [new branch]      tens -> master

Wszystkie pliki pozostały na miejscu i te niechciane można teraz swobodnie usunąć.

Warto wspomnieć, że chociaż git filter-branch to bardzo podobne rozwiązanie, to wydzielanie kodu to zaledwie czubek góry lodowej tego, co ma do zaoferowania git subtree. Poddrzewa to dobra, znacznie mniej inwazyjna alternatywa dla submodułów, co postaram się opisać w niedalekiej przyszłości na łamach bloga.