Questa risposta fornisce comandi interessanti basati git am
e presentati usando esempi, passo dopo passo.
Obbiettivo
- Si desidera spostare alcuni o tutti i file da un repository all'altro.
- Vuoi mantenere la loro storia.
- Ma non ti interessa conservare tag e rami.
- Accetti una cronologia limitata per i file rinominati (e i file nelle directory rinominate).
Procedura
- Estrarre la cronologia in formato e-mail utilizzando
git log --pretty=email -p --reverse --full-index --binary
- Riorganizza l'albero dei file e aggiorna la modifica del nome file nella cronologia [opzionale]
- Applica nuova cronologia utilizzando
git am
1. Estrarre la cronologia in formato e-mail
Esempio: la storia di estratto file3
, file4
efile5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
Pulisci la destinazione della directory temporanea
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning
Pulisci la tua fonte repo
git commit ... # Commit your working files
rm .gitignore # Disable gitignore
git clean -n # Simulate removal
git clean -f # Remove untracked file
git checkout .gitignore # Restore gitignore
Estrai la cronologia di ogni file in formato e-mail
cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'
Purtroppo l'opzione --follow
o --find-copies-harder
non può essere combinata con --reverse
. Questo è il motivo per cui la cronologia viene tagliata quando il file viene rinominato (o quando viene rinominata una directory padre).
Dopo: Cronologia temporanea in formato e-mail
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
2. Riorganizza l'albero dei file e aggiorna la modifica del nome file nella cronologia [opzionale]
Supponiamo di voler spostare questi tre file in questo altro repository (può essere lo stesso repository).
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # was subdir
│ │ ├── file33 # was file3
│ │ └── file44 # was file4
│ └── dirB2 # new dir
│ └── file5 # = file5
└── dirH
└── file77
Quindi riorganizza i tuoi file:
cd /tmp/mail/dir
mkdir dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir dirB/dirB2
mv file5 dirB/dirB2
La tua cronologia temporanea è ora:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
Cambia anche i nomi dei file nella cronologia:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
Nota: questo riscrive la cronologia per riflettere il cambio di percorso e nome file.
(ovvero la modifica della nuova posizione / nome all'interno del nuovo repository)
3. Applica nuova cronologia
L'altro repository è:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
Applica commit da file cronologici temporanei:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am
L'altro repository è ora:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB ^
│ ├── dirB1 | New files
│ │ ├── file33 | with
│ │ └── file44 | history
│ └── dirB2 | kept
│ └── file5 v
└── dirH
└── file77
Utilizzare git status
per visualizzare la quantità di commit pronti per essere inviati :-)
Nota: poiché la cronologia è stata riscritta per riflettere il percorso e la modifica del nome file:
(cioè rispetto alla posizione / nome all'interno del repository precedente)
- Non è necessario
git mv
modificare la posizione / il nome file.
- Non è necessario
git log --follow
accedere alla cronologia completa.
Trucco aggiuntivo: rileva i file rinominati / spostati nel tuo repository
Per elencare i file che sono stati rinominati:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Altre personalizzazioni: è possibile completare il comando git log
utilizzando le opzioni --find-copies-harder
o --reverse
. Puoi anche rimuovere le prime due colonne usando cut -f3-
e grepping il modello completo '{. * =>. *}'.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'