Odłączenie wielu podkatalogów do nowego, oddzielnego repozytorium Git

To pytanie opiera się na odłączeniu podkatalogu do osobnego repozytorium Git

Zamiast odłączać pojedynczy podkatalog, chcę odłączyć kilka. Na przykład moje obecne drzewo katalogów wygląda tak:

/apps
  /AAA
  /BBB
  /CCC
/libs
  /XXX
  /YYY
  /ZZZ

A ja wolałabym zamiast tego:

/apps
  /AAA
/libs
  /XXX

Argument --subdirectory-filter do git filter-branch nie zadziała, ponieważ pozbywa się wszystkiego poza podanym katalogiem przy pierwszym uruchomieniu. Myślałem, że używając argumentu --index-filter dla wszystkich niechcianych pliki będą działać (choć żmudne), ale jeśli spróbuję uruchomić go więcej niż raz, dostaję następujący komunikat:

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f
Jakieś pomysły? TIA
Author: Community, 2010-06-05

10 answers

Zamiast zajmować się subshellem i używać ext glob (jak zasugerował kynan), Spróbuj tego znacznie prostszego podejścia:

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- apps/AAA libs/XXX' --prune-empty -- --all

Jak wspomniano przez void.komentarz pointera , usunie wszystko oprócz apps/AAA i libs/XXX z bieżącego repozytorium.

Prune empty merge commits

To pozostawia wiele pustych połączeń. Można je usunąć za pomocą innego przejścia opisanego przez raphinesse w jego odpowiedź :

git filter-branch --prune-empty --parent-filter \
'sed "s/-p //g" | xargs -r git show-branch --independent | sed "s/\</-p /g"'

⚠️ Uwaga : powyższe musi używać wersji GNU sed i xargs w przeciwnym razie usunie wszystkie commity jako xargs nie powiedzie się. brew install gnu-sed findutils a następnie użyj gsed i gxargs:

git filter-branch --prune-empty --parent-filter \
'gsed "s/-p //g" | gxargs git show-branch --independent | gsed "s/\</-p /g"' 
 166
Author: David Smiley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-06 17:09:52

Manual steps with simple Git commands

Plan polega na podzieleniu poszczególnych katalogów na własne repozytoria, a następnie połączeniu ich ze sobą. Poniższe kroki ręczne nie wykorzystywały geek-to-use skryptów, ale łatwe do zrozumienia polecenia i może pomóc scalić dodatkowe N podfolderów do innego pojedynczego repozytorium.

Divide

Załóżmy, że twój oryginalny repo to: original_repo

1 - Podział aplikacji:

git clone original_repo apps-repo
cd apps-repo
git filter-branch --prune-empty --subdirectory-filter apps master

2 - Split libs

git clone original_repo libs-repo
cd libs-repo
git filter-branch --prune-empty --subdirectory-filter libs master

Kontynuuj, jeśli masz więcej niż 2 foldery. Teraz będziesz mieć dwa nowe i tymczasowe repozytorium git.

Conquer poprzez łączenie aplikacji i bibliotek

3 - przygotuj nowy repo:

mkdir my-desired-repo
cd my-desired-repo
git init

I będziesz musiał zrobić co najmniej jeden commit. Jeśli następujące trzy linie powinny zostać pominięte, twój pierwszy repo pojawi się natychmiast pod rootem twojego repo:

touch a_file_and_make_a_commit # see user's feedback
git add a_file_and_make_a_commit
git commit -am "at least one commit is needed for it to work"

Z uruchomionym plikiem tymczasowym, merge komenda w dalszej sekcji zatrzyma się jako oczekiwane.

Biorąc pod uwagę opinie użytkowników, zamiast dodawać losowy plik jak a_file_and_make_a_commit, możesz dodać .gitignore, lub README.md itp.

4 - Merge apps repo first:

git remote add apps-repo ../apps-repo
git fetch apps-repo
git merge -s ours --no-commit apps-repo/master # see below note.
git read-tree --prefix=apps -u apps-repo/master
git commit -m "import apps"

Teraz powinieneś zobaczyć katalog apps w nowym repozytorium. git log powinien pokazywać wszystkie istotne historyczne komunikaty commit.

Uwaga: Jak zauważył Chris poniżej w komentarzach, dla nowszej wersji(>=2.9) git, musisz podać --allow-unrelated-histories za pomocą git merge

5-Merge libs repo następny w ten sam sposób:

git remote add libs-repo ../libs-repo
git fetch libs-repo
git merge -s ours --no-commit libs-repo/master # see above note.
git read-tree --prefix=libs -u libs-repo/master
git commit -m "import libs"

Kontynuuj, jeśli masz więcej niż 2 repo do scalenia.

Referencja: Połącz podkatalog innego repozytorium z git

 39
Author: chfw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 20:11:55

Dlaczego chcesz uciekać filter-branch więcej niż raz? Możesz to zrobić za jednym zamachem, więc nie musisz tego wymuszać (zauważ, że musisz włączyć extglob w swojej powłoce, aby to działało): {]}

git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch $(ls -xd apps/!(AAA) libs/!(XXX))" --prune-empty -- --all

To powinno pozbyć się wszystkich zmian w niechcianych podkatalogach i zachować wszystkie gałęzie i commity (chyba że wpływają one tylko na pliki w podkatalogach przycinanych, na mocy --prune-empty) - nie ma problemu z duplikatami commitów itp.

Po tej operacji niechciane katalogi będą notowane jako untracked przez git status.

$(ls ...) jest konieczne s. t. extglob jest oceniana przez powłokę zamiast filtra indeksu, który używa wbudowanego sh eval (gdzie extglob nie jest dostępna). Zobacz Jak włączyć opcje powłoki w git? aby uzyskać więcej informacji na ten temat.

 28
Author: kynan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:05

Odpowiadam na moje pytanie... po wielu próbach i błędach.

Udało mi się to zrobić używając kombinacji git subtree i git-stitch-repo. Instrukcje te są oparte na:

Najpierw wyciągnąłem katalogi, które chciałem zachować w osobnym repozytorium:

cd origRepo
git subtree split -P apps/AAA -b aaa
git subtree split -P libs/XXX -b xxx

cd ..
mkdir aaaRepo
cd aaaRepo
git init
git fetch ../origRepo aaa
git checkout -b master FETCH_HEAD

cd ..
mkdir xxxRepo
cd xxxRepo
git init
git fetch ../origRepo xxx
git checkout -b master FETCH_HEAD

Następnie utworzyłem nowe puste repozytorium i zaimportowałem/zszyłem do niego dwa ostatnie:

cd ..
mkdir newRepo
cd newRepo
git init
git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import

Tworzy to dwie gałęzie, master-A i master-B, z których każda zawiera zawartość jednego z szytych repo. Aby je połączyć i posprzątać:

git checkout master-A
git pull . master-B
git checkout master
git branch -d master-A 
git branch -d master-B

Teraz nie jestem do końca pewien jak/kiedy dzieje się tak, ale po pierwszym checkout i pull kod magicznie łączy się z gałęzią master (każdy wgląd w to, co się tutaj dzieje, jest doceniany!)

Wszystko wydaje się działać zgodnie z oczekiwaniami, z wyjątkiem tego, że jeśli przejrzę historię zatwierdzeń newRepo, pojawią się duplikaty, gdy zestaw zmian wpłynie zarówno na apps/AAA, jak i libs/XXX. Jeśli istnieje sposób na usunięcie duplikatów, byłoby to idealne.

 20
Author: prisonerjohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-20 18:40:19

Proste rozwiązanie: git-filter-repo

Miałem podobny problem i po przejrzeniu różnych podejść wymienionych tutaj, odkryłem git-filter-repo. Jest to zalecane jako alternatywa dla Git-filter-branch w oficjalnej dokumentacji git tutaj .

Aby utworzyć nowe repozytorium z podzbioru katalogów w istniejącym repozytorium, możesz użyć polecenia:

git filter-repo --path <file_to_keep>

Filtruj wiele plików / folderów, łącząc je w łańcuch:

git filter-repo --path keepthisfile --path keepthisfolder/

Więc do odpowiedz oryginalne pytanie, w git-filter-repo potrzebowałbyś następującego polecenia:

git filter-repo --path apps/AAA/ --path libs/XXX/
 17
Author: elmo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-19 17:40:06

Napisałem filtr Gita, aby rozwiązać dokładnie ten problem. Ma fantastyczną nazwę git_filter i znajduje się na github tutaj:

Https://github.com/slobobaby/git_filter

Opiera się na znakomitym libgit2.

Musiałem podzielić duże repozytorium z wieloma commitami (~100000), a uruchomienie rozwiązań opartych na git filter-branch trwało kilka dni. git_filter zajmuje minutę, aby zrobić to samo.

 7
Author: slobobaby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-17 21:06:03

Use' Git splits ' Git extension

git splits jest skryptem bash, który jest opakowaniem wokół git branch-filter, który stworzyłem jako rozszerzenie git, bazując na jkeating ' s solution .

Został stworzony dokładnie do tej sytuacji. W przypadku błędu spróbuj użyć opcji git splits -f, aby wymusić usunięcie kopii zapasowej. Ponieważ git splits działa na nowej gałęzi, nie przepisuje bieżącej gałęzi, więc kopia zapasowa jest zbędna. Zobacz readme po więcej szczegółów i upewnij się, że używasz go na Kopia / Klon repo (na wszelki wypadek!).

  1. zainstaluj git splits.
  2. Podziel katalogi na lokalną gałąź #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ apps/AAA libs/ZZZ

  3. Utwórz gdzieś pusty repo. Założymy, że stworzyliśmy puste repo o nazwie xyz na Githubie, które ma path : [email protected]:simpliwp/xyz.git

  4. / align = "left" / #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz [email protected]:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. Sklonuj nowo utworzony zdalny repo do nowego katalogu lokalnego
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone [email protected]:simpliwp/xyz.git

 7
Author: AndrewD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:29
git clone [email protected]:thing.git
cd thing
git fetch
for originBranch in `git branch -r | grep -v master`; do
    branch=${originBranch:7:${#originBranch}}
    git checkout $branch
done
git checkout master

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- dir1 dir2 .gitignore' --prune-empty -- --all

git remote set-url origin [email protected]:newthing.git
git push --all
 6
Author: Richard Barraclough,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 13:37:51

Tak. Wymuś nadpisanie kopii zapasowej, używając znacznika -f przy kolejnych wywołaniach filter-branch, aby nadpisać to Ostrzeżenie. :) W przeciwnym razie myślę, że masz rozwiązanie (czyli wyeliminowanie niechcianego katalogu na raz za pomocą filter-branch).

 3
Author: Jakob Borg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-05 20:59:57

Usuń kopię zapasową obecną pod .katalog git w refs / original, jak sugeruje wiadomość. Katalog jest ukryty.

 -5
Author: user5200576,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-07 04:07:54