Cette réponse fournit des commandes intéressantes basées sur git am
et présentées à l'aide d'exemples, étape par étape.
Objectif
- Vous souhaitez déplacer certains ou tous les fichiers d'un référentiel vers un autre.
- Vous voulez garder leur histoire.
- Mais vous ne vous souciez pas de conserver les balises et les branches.
- Vous acceptez un historique limité pour les fichiers renommés (et les fichiers dans des répertoires renommés).
Procédure
- Extraire l'historique au format e-mail à l'aide
git log --pretty=email -p --reverse --full-index --binary
- Réorganiser l'arborescence des fichiers et mettre à jour le changement de nom de fichier dans l'historique [facultatif]
- Appliquer un nouvel historique à l'aide
git am
1. Extraire l'historique au format e-mail
Exemple: l' histoire Extrait du file3
, file4
etfile5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
Nettoyer la destination du répertoire temporaire
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning
Nettoyez votre source 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
Extraire l'historique de chaque fichier au format email
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"' {} ';'
Malheureusement option --follow
ou --find-copies-harder
ne peut pas être combinée avec --reverse
. C'est pourquoi l'historique est coupé lorsque le fichier est renommé (ou lorsqu'un répertoire parent est renommé).
Après: historique temporaire au format e-mail
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
2. Réorganiser l'arborescence des fichiers et mettre à jour le changement de nom de fichier dans l'historique [facultatif]
Supposons que vous souhaitiez déplacer ces trois fichiers dans cet autre référentiel (il peut s'agir du même référentiel).
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
Réorganisez donc vos fichiers:
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
Votre historique temporaire est maintenant:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
Modifiez également les noms de fichiers dans l'historique:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
Remarque: Cela réécrit l'historique pour refléter le changement de chemin d'accès et de nom de fichier.
(c'est-à-dire le changement du nouvel emplacement / nom dans le nouveau référentiel)
3. Appliquer une nouvelle histoire
Votre autre dépôt est:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
Appliquer des validations à partir de fichiers d'historique temporaires:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am
Votre autre dépôt est maintenant:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB ^
│ ├── dirB1 | New files
│ │ ├── file33 | with
│ │ └── file44 | history
│ └── dirB2 | kept
│ └── file5 v
└── dirH
└── file77
Utilisez git status
pour voir le nombre de commits prêts à être poussés :-)
Remarque: Comme l'historique a été réécrit pour refléter le changement de chemin et de nom de fichier:
(c'est-à-dire par rapport à l'emplacement / nom dans le dépôt précédent)
- Pas besoin de
git mv
changer l'emplacement / nom de fichier.
- Pas besoin d'
git log --follow
accéder à l'historique complet.
Astuce supplémentaire: Détectez les fichiers renommés / déplacés dans votre référentiel
Pour répertorier les fichiers renommés:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Plus de personnalisations: vous pouvez exécuter la commande à l' git log
aide des options --find-copies-harder
ou --reverse
. Vous pouvez également supprimer les deux premières colonnes en utilisant cut -f3-
et en accueillant le motif complet '{. * =>. *}'.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'