Crea un repository del sottomodulo da una cartella e mantieni la sua cronologia dei commit git


111

Ho un'applicazione web che esplora altre applicazioni web in un modo particolare. Contiene alcune demo web in una demoscartella e una delle demo dovrebbe ora avere il proprio repository. Vorrei creare un repository separato per questa applicazione demo e renderlo un filesottopackage sottomodulo dal repository principale senza perdere la cronologia di commit.

È possibile mantenere la cronologia dei commit dai file nella cartella di un repository e creare un repository da esso e utilizzarlo invece come sottomodulo ?


Ho cercato come spostare la directory 1 dal repository Git A al repository Git B. +1 per il collegamento all'articolo.
Chetabahana


Sì, questo è davvero molto simile, le soluzioni differiscono un po ', grazie per aver condiviso questo
GabLeRoux

Risposte:


191

Soluzione dettagliata

Vedi la nota alla fine di questa risposta (ultimo paragrafo) per una rapida alternativa ai sottomoduli git usando npm;)

Nella seguente risposta, saprai come estrarre una cartella da un repository e creare un repository git da esso e quindi includerlo come sottomodulo anziché come cartella.

Ispirato dall'articolo di Gerg Bayer Spostamento di file da un repository Git a un altro, preservando la storia

All'inizio abbiamo qualcosa del genere:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

Nelle fasi sotto riportate, farò riferimento questo someLibcome <directory 1>.

Alla fine, avremo qualcosa del genere:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Crea un nuovo repository git da una cartella in un altro repository

Passo 1

Ottieni una nuova copia del repository da dividere.

git clone <git repository A url>
cd <git repository A directory>

Passo 2

La cartella corrente sarà il nuovo repository, quindi rimuovi il telecomando corrente.

git remote rm origin

Passaggio 3

Estrai la cronologia della cartella desiderata e salvala

git filter-branch --subdirectory-filter <directory 1> -- --all

Ora dovresti avere un repository git con i file dalla directory 1radice del tuo repository con tutta la cronologia dei commit correlata.

Passaggio 4

Crea il tuo repository online e invia il tuo nuovo repository!

git remote add origin <git repository B url>
git push

Potrebbe essere necessario impostare il upstreamramo per il primo push

git push --set-upstream origin master

Clean <git repository A>(opzionale, vedi commenti)

Vogliamo eliminare le tracce (file e cronologia dei commit) di <git repository B>da, <git repository A>quindi la cronologia per questa cartella è presente solo una volta.

Si basa sulla rimozione dei dati sensibili da GitHub.

Vai in una nuova cartella e

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Sostituisci <directory 1>con la cartella che desideri rimuovere. -rlo farà ricorsivamente all'interno della directory specificata :). Ora spingere origin/mastercon--force

git push origin master --force

Fase Boss (vedi nota sotto)

Crea un sottomodulo da <git repository B>into<git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Verifica se tutto ha funzionato come previsto e push

git push origin master

Nota

Dopo aver fatto tutto questo, mi sono reso conto nel mio caso che era più appropriato utilizzare npm per gestire invece le mie dipendenze. Possiamo specificare gli URL e le versioni di git, vedere gli URL di git package.json come dipendenze .

Se lo si fa in questo modo, il repository che si desidera utilizzare come requisito deve essere un modulo NPM quindi deve contenere un package.jsonfile o si otterrà questo errore: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

tldr (soluzione alternativa)

Potresti trovare più facile usare npm e gestire le dipendenze con gli URL git :

  • Sposta la cartella in un nuovo repository
  • eseguito npm initall'interno di entrambi i repository
  • esegui npm install --save git://github.com/user/project.git#commit-ishdove vuoi installare le tue dipendenze

39
Il passaggio "Pulisci <git repository A>" dovrebbe essere evitato. In questo modo non è possibile ripristinare / estrarre completamente versioni precedenti / commit dalla cronologia. Dovresti semplicemente git rm la cartella e aggiungere il sottomodulo. Quindi assicurati di avere una copia completamente funzionante quando controlli i commit più vecchi.
Cybot

Non dovresti farlo cd someLibprima del passaggio 2? Dici "La cartella corrente sarà il nuovo repository" ma in realtà non lo sarà; il nuovo repository (sottomodulo) si trova all'interno di quella cartella.
Jago

1
conferma: si, funziona per più sottomoduli. Grazie mille per la risposta dettagliata. Inoltre, non è stato necessario utilizzare npm.
Breno Inojosa

2
Vorrei aggiungere informazioni su ciò refs/original/...che viene creato al passaggio 3.
Emile Bergeron

6
GitHub ha realizzato un articolo su come ottenere l'estrazione di una cartella in un nuovo repository: help.github.com/articles/…
jrobichaud

9

La soluzione di @GabLeRoux schiaccia i rami e i relativi commit.

Un modo semplice per clonare e mantenere tutti quei branch e commit extra:

1 - Assicurati di avere questo alias git

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - Clona il telecomando, estrai tutti i rami, cambia il telecomando, filtra la tua directory, premi

git clone git@github.com:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin git@github.com:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

3

La soluzione di GabLeRoux funziona bene tranne se si utilizza git lfse ha file di grandi dimensioni nella directory che si desidera scollegare. In tal caso, dopo il passaggio 3 tutti i file di grandi dimensioni rimarranno come file puntatore invece che file reali. Immagino sia probabilmente dovuto al.gitattributes file viene rimosso nel processo del ramo del filtro.

Comprendendo questo, trovo che la seguente soluzione funzioni per me:

cp .gitattributes .git/info/attributes

Copia quello .gitattributesche git lfs usa per tenere traccia dei file di grandi dimensioni nella .git/directory per evitare di essere cancellati.

Quando filter-branch è finito, non dimenticare di rimettere il .gitattributesse vuoi ancora usare git lfs per il nuovo repository:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.