Scollega molte sottodirectory in un nuovo repository Git separato

Questa domanda è basata sulla sottodirectory di Detach in un repository Git separato

Invece di staccare una singola sottodirectory, voglio staccare una coppia. Ad esempio, il mio albero di directory corrente si presenta così:

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

E mi piacerebbe questo invece:

 /apps /AAA /libs /XXX 

L’argomento --subdirectory-filter di git filter-branch non funzionerà perché elimina la totalità di tutto tranne la directory data la prima volta che viene eseguita. Ho pensato che usare l’argomento --index-filter per tutti i file indesiderati avrebbe funzionato (anche se noioso), ma se provo a eseguirlo più di una volta, ottengo il seguente messaggio:

 Cannot create a new backup. A previous backup already exists in refs/original/ Force overwriting the backup with -f 

Qualche idea? TIA

Invece di dover gestire una subshell e usare ext glob (come suggerito da kynan), prova questo approccio molto più semplice:

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

Passi manuali con semplici comandi git

Il piano è quello di dividere singole directory nei propri repository, quindi unirli tra loro. I seguenti passaggi manuali non hanno utilizzato script geek-to-use ma comandi di facile comprensione e potrebbero aiutare a unire sottocartelle N aggiuntive in un altro repository singolo.

Dividere

Supponiamo che il tuo repository originale sia: original_repo

1 – Dividi app:

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

2 – Dividi le librerie

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

Continua se hai più di 2 cartelle. Ora avrai due repository git nuovi e temporanei.

Conquista combinando app e librerie

3 – Preparare il nuovo repository:

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

E dovrai fare almeno un commit. Se le seguenti tre righe devono essere saltate, il tuo primo repository apparirà immediatamente sotto la radice del repository:

 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" 

Con il file temp commesso, il comando di merge nella sezione successiva si interromperà come previsto.

Prendendo in considerazione il feedback dell’utente, invece di aggiungere un file casuale come a_file_and_make_a_commit , puoi scegliere di aggiungere un .gitignore , o README.md ecc.

4 – Unisci le app repo per prime:

 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" 

Ora dovresti vedere la directory app all’interno del tuo nuovo repository. git log dovrebbe mostrare tutti i messaggi di commit storici rilevanti.

Nota: come Chris ha annotato sotto nei commenti, per la versione più recente (> = 2.9) di git, è necessario specificare --allow-unrelated-histories con git merge

5 – Unisci libs repo avanti allo stesso modo:

 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" 

Continua se hai più di 2 repository da unire.

Riferimento: unisci una sottodirectory di un altro repository con git

Perché vorresti eseguire il filter-branch più di una volta? Puoi fare tutto in una volta, quindi non c’è bisogno di forzarlo (nota che hai bisogno di extglob abilitato nella tua shell affinché funzioni):

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

Questo dovrebbe sbarazzarsi di tutte le modifiche nelle sottodirectory indesiderate e mantenere tutti i tuoi rami e commit (a meno che non riguardino solo i file nelle sottodirectory --prune-empty , in virtù di --prune-empty ) – nessun problema con commit duplicati ecc.

Dopo questa operazione le directory indesiderate verranno elencate come non tracciate dallo git status .

Il $(ls ...) è necessario st l’ extglob viene valutato dalla shell invece del filtro dell’indice, che usa l’ eval incorporato sh (dove extglob non è disponibile). Vedi Come abilitare le opzioni della shell in git? per ulteriori dettagli su questo.

Rispondere alla mia domanda qui … dopo un sacco di prove ed errori.

Sono riuscito a farlo usando una combinazione di git subtree e git-stitch-repo . Queste istruzioni sono basate su:

Innanzitutto, ho estratto le directory che volevo conservare nel proprio repository separato:

 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 

Ho quindi creato un nuovo repository vuoto e ho importato / cucito gli ultimi due in esso:

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

Questo crea due rami, master-A e master-B , ognuno dei quali contiene il contenuto di uno dei punti di vendita cuciti. Per combinarli e ripulire:

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

Ora non sono abbastanza sicuro di come / quando ciò accadrà, ma dopo il primo checkoutcheckout e il pull , il codice si fonde magicamente con il ramo master (qualsiasi apprezzamento su ciò che sta accadendo qui è apprezzato!)

Tutto sembra aver funzionato come previsto, tranne che se guardo la newRepo commit di newRepo , ci sono dei duplicati quando il changeset ha riguardato sia apps/AAA che libs/XXX . Se c’è un modo per rimuovere i duplicati, sarebbe perfetto.

Ho scritto un filtro git per risolvere esattamente questo problema. Ha il nome fantastico di git_filter e si trova su github qui:

https://github.com/slobobaby/git_filter

È basato sull’eccellente libgit2.

Avevo bisogno di dividere un repository di grandi dimensioni con molti commit (~ 100000) e le soluzioni basate su git filter-branch impiegavano diversi giorni per essere eseguite. git_filter richiede un minuto per fare la stessa cosa

Usa l’estensione git ‘git splits’

git splits è uno script di bash che è un wrapper attorno a git branch-filter che ho creato come estensione git, basato sulla soluzione di jkeating .

E ‘stato fatto esattamente per questa situazione. Per il tuo errore, prova a utilizzare l’opzione git splits -f per forzare la rimozione del backup. Poiché git splits opera su un nuovo ramo, non riscriverà il ramo attuale, quindi il backup è estraneo. Vedi il readme per maggiori dettagli e assicurati di usarlo su una copia / clone del tuo repository (per ogni evenienza!) .

  1. installa git splits .
  2. Dividere le directory in un ramo locale #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
    #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. Crea un repository vuoto da qualche parte. Supponiamo di aver creato un repository vuoto chiamato xyz su GitHub che ha percorso: git@github.com:simpliwp/xyz.git

  4. Spingi al nuovo repository. #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz git@github.com:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. Clona il repository remoto appena creato in una nuova directory locale
    #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 git@github.com:simpliwp/xyz.git

Si. Forza la sovrascrittura del backup utilizzando il flag -f sulle chiamate successive al filter-branch per sovrascrivere tale avviso. 🙂 Altrimenti penso che tu abbia la soluzione (cioè, eliminare una directory indesiderata alla volta con filter-branch ).

 git clone git@example.com: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 git@example.com:newthing.git git push --all 

Elimina il backup presente sotto la directory .git in ref / originale come suggerisce il messaggio. La directory è nascosta.