2011-09-11 16 views
23

La mia situazione era, ho un repository git convertito da SVN a HG in GIT, e volevo estrarre solo un file sorgente. Avevo anche strani personaggi come aÌ (un errore di codifica che corrompeva Unicode ä) e spazi nei nomi dei file.Come estrarre un file con cronologia del commit da un repository git con index-filter & co

Sembra che non sia particolarmente facile, e questo è il motivo per cui risponderò alla mia stessa domanda nonostante molte domande simili riguardo a git [index-filter | subdirectory-filter | filter-tree], dato che avevo bisogno di usare tutto il precedente Per realizzare questo!

Quindi la domanda è: "Come posso estrarre un file da un repository e inserirlo nella root del nuovo repository?"

+0

Perché si desidera estrarre un singolo file dal repo? – svick

+1

È tutto ciò di cui ho bisogno. E btw, http://stackoverflow.com/questions/5998987/splitting-a-set-of-files-within-a-git-repo-into-their-own-repository-preserving non è un clone di alcuna sottodirectory- domanda di filtro. L'estrazione dei file richiede sia il passaggio del filtro --subdirectory sia un filtro -index o --tree-filter. – peterhil

+1

O meglio quello che voglio, perché creerò un pacchetto del singolo file che fornisce un trie. Voglio usarlo anche in altri progetti, e pubblicare in Github e ho del codice nel repository che non voglio rendere open source (almeno ancora). – peterhil

risposta

10

In primo luogo una breve nota, che anche un incantesimo come in un commento sul Splitting a set of files within a git repo into their own repository, preserving relevant history

SPELL='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "trie.lisp" | tr "\n" "\0" | xargs -0 git rm --cached -r --ignore-unmatch' 
git filter-branch --prune-empty --index-filter "$SPELL" -- --all 

non aiuterà con file denominati come imaging/DrinkkejaI<0300>$'\302\210'.txt_74x2032.gif. La parte aI<0300>$'\302\210' una volta era una singola lettera: ä.

Quindi, al fine di estrarre un singolo file, oltre a filtrare ramo Ho anche bisogno di fare:

git filter-branch -f --subdirectory-filter lisp/source/model HEAD 

In alternativa, è possibile utilizzare --tree-filtro: (è necessaria la prova, perché il file era in un'altra directory in precedenza, vedi: How can I move a directory in a Git repo for all commits?)

MV_FILTER='test -f source/model/trie.lisp && mv ./source/model/trie.lisp . || echo "Nothing to do."' 
git filter-branch --tree-filter $MV_FILTER HEAD --all 

Per visualizzare tutti i nomi di un file hanno avuto, utilizzare:

git log --pretty=oneline --follow --name-only git-path/to/file | grep -v ' ' | sort -u 

Come descritto in http://whileimautomaton.net/2010/04/03012432

seguire anche le fasi di seguito:

$ git reset --hard 
$ git gc --aggressive 
$ git prune 
$ git remote rm origin # Otherwise changes will be pushed to where the repo was cloned from 
+3

Non sono sicuro di come seguire queste istruzioni, il testo di questa risposta sembra porre diversi possibili percorsi. Non vedo alcuna procedura. – ThorSummoner

+0

Forse dovresti vedere la documentazione git sul comando filtro-ramo e sulla storia della riscrittura: - http://git-scm.com/docs/git-filter-branch - http://git-scm.com/ libro/it/V2/GIT-Tools-Rewriting History- – peterhil

8

Si noti che le cose si fanno molto più facile se si combina questo con l'ulteriore passo di spostare il file desiderato (s) in un nuova directory.

Questo potrebbe essere un caso d'uso abbastanza comune (ad esempio, spostare il singolo file desiderato nella directory principale).
I Did It (usando git 1.9) come questo (prima lo spostamento del file (s), quindi l'eliminazione del vecchio albero):

git filter-branch -f --tree-filter 'mkdir -p new_path && git mv -k -f old_path/to/file new_path/' 
git filter-branch -f --prune-empty --index-filter 'git rm -r --cached --ignore-unmatch old_path' 

si può anche facilmente utilizzare i caratteri jolly per i file desiderati (senza fare in giro con grep -v).

Penso che questo ('mv' e 'rm') potrebbe anche essere fatto in un ramo filtro ma non ha funzionato per me.

Non l'ho provato con caratteri strani ma spero che questo aiuti comunque. Rendere le cose più semplici sembra essere sempre una buona idea per me.

Suggerimento:
Questa operazione richiede molto tempo su repository di grandi dimensioni. Quindi, se vuoi fare diverse azioni (come ottenere un mucchio di file e poi riorganizzarli in 'new_path/sottodirectory') è una buona idea fare la parte 'rm' il prima possibile per ottenere un albero più piccolo e più veloce.

+0

ho anche provato su Ubuntu 12.04 e git 1.7.x con i seguenti risultati: * il problema di permessi-negato appare anche su Ubuntu * git 1.7.x didn' fare bene con i comandi che ho menzionato sopra (dato che solo 1 file corrisponde è stato rinominato nella directory in cui dovrebbe essere spostato. Quindi raccomando git 1.9.x che sto correndo sul mio computer windows – Roman

+0

rielaborato il mio post i miei problemi sembrano essere causati dalle mie inesistenti abilità bash -> usare '&&' invece di '|' per combinare i comandi ora – Roman

+0

Il primo passo non preoccupa k per me in git 2.2.1. Non c'è alcun cambiamento al repository. – xixixao

21

Un veloce e più facile da capire filtro che compie la stessa cosa:

git filter-branch --index-filter ' 
         git read-tree --empty 
         git reset $GIT_COMMIT -- $your $files $here 
       ' \ 
     -- --all -- $your $files $here 
+0

Questo è il migliore – podarok

+0

Questo ha funzionato perfettamente per me. Ho aggiunto un argomento '--prune-empty' per rimuovere qualsiasi commit vuoto. –

+0

@AaronJensen Il '--all - $ your $ files $ here' sull'ultima riga viene passato al' git rev-list' che 'filter-branch' viene eseguito, quindi le commits filter-branch vede sono già state potate . Questo è molto più veloce di fare il branch-branch caricare inutilmente l'indice ed eseguire il filtro e creare nuovi alberi e un commit prima di buttarlo via tutto per i commit che non hanno toccato quei file. Tuttavia, non fa male aggiungerlo. – jthill

Problemi correlati