web-dev-qa-db-fra.com

Fichier de copie GIT préservant l'historique

J'ai une question un peu déroutante dans GIT. Disons que j'ai un fichier dir1/A.txt commit et que git conserve un historique des commits

Maintenant, il me faut (pour certaines raisons) copier le fichier dans dir2/A.txt (ne pas déplacer mais copier). Je sais qu'il existe une commande git mv mais j'ai besoin de dir2/A.txt pour avoir le même historique de validations que dir1/A.txt, et dir1/A.txt pour y rester.

Je ne prévois pas de mettre à jour A.txt une fois la copie créée et tous les travaux futurs seront effectués sur dir2/A.txt

Je sais que cela semble déroutant. J'ajouterai que cette situation se trouve sur le module basé sur Java (projet mavenized) et que nous devons créer une nouvelle version du code afin que nos clients puissent disposer de 2 versions à l'exécution, la première version sera supprimée lorsque l'alignement sera terminé. Nous pouvons utiliser la gestion de version maven bien sûr, je suis débutant dans GIT et curieux de savoir ce que git peut fournir ici.

154
Mark Bramnik

Contrairement à Subversion, git n’a pas d’historique par fichier. Si vous examinez la structure de données de validation, celle-ci pointe uniquement sur les validations précédentes et le nouvel objet d'arborescence de cette validation. Aucune information explicite n'est stockée dans l'objet commit, quels fichiers sont modifiés par le commit; ni la nature de ces changements.

Les outils permettant d’examiner les modifications peuvent détecter les renommage sur la base de méthodes heuristiques. Par exemple. "git diff" a l'option -M qui active la détection de renommage. Ainsi, en cas de changement de nom, "git diff" peut vous montrer qu'un fichier a été supprimé et un autre créé, tandis que "git diff -M" détectera le déplacement et affichera la modification en conséquence (voir "man git diff" pour plus de détails). détails).

Donc, dans l’ensemble, ce n’est pas une question de façon dont vous validez vos modifications, mais plutôt de voir plus tard les modifications validées.

73
CliffordVienna

Tout ce que vous avez à faire est de le déplacer en parallèle vers deux emplacements différents, de le fusionner, puis de ramener une copie à l'emplacement d'origine et vous avez terminé.

Vous pouvez ensuite exécuter git blame sur l'une ou l'autre copie, sans options spéciales, et consulter les meilleures attributions d'historique pour les deux. Vous pouvez également exécuter git log sur l'original et afficher un historique complet.

En détail, supposons que vous souhaitiez copier foo/dans votre référentiel pour barrer /. Faites ceci (notez que j'utilise -n pour m'engager à éviter toute réécriture, etc. par des hooks):

git mv foo bar
git commit -n
SAVED=`git rev-parse HEAD`
git reset --hard HEAD^
git mv foo foo-magic
git commit -n
git merge $SAVED # This will generate conflicts
git commit -a -n # Trivially resolved like this
git mv foo-magic foo
git commit -n

Note: J'ai utilisé git 2.1.4 sur Linux

Pourquoi ça marche

Vous allez vous retrouver avec un historique de révision comme celui-ci:

  ORIG_HEAD
    /    \
SAVED  ALTERNATE
    \    /
    MERGED
      |
   RESTORED

Lorsque vous interrogez Git sur l'historique de 'foo', il détecte (1) le changement de nom de 'foo-magic' entre MERGED et RESTORED, (2) détecte que 'foo-magic' vient du parent ALTERNATE de MERGED, et (3) détecter le renommage de 'foo' entre ORIG_HEAD et ALTERNATE. De là, il faudra creuser dans l'histoire de "foo".

Lorsque vous interrogez git sur l'historique de 'bar', il (1) ne remarquera aucun changement entre MERGED et RESTORED, (2) détectera que 'bar' vient de la société mère SAVED de MERGED, et (3) détecte le changement de nom de ' foo 'entre ORIG_HEAD et SAVED. De là, il faudra creuser dans l'histoire de "foo".

C'est si simple. Vous avez juste besoin de forcer git dans une situation de fusion où vous pouvez accepter deux copies traçables du (des) fichier (s), et nous le faisons avec un déplacement parallèle de l'original (que nous revenons bientôt).

106
Peter Dillinger

Copiez simplement le fichier, ajoutez-le et validez-le:

cp dir1/A.txt dir2/A.txt
git add dir2/A.txt
git commit -m "Duplicated file from dir1/ to dir2/"

Ensuite, les commandes suivantes afficheront l’historique complet avant la copie:

git log --follow dir2/A.txt

Pour voir les annotations ligne par ligne héritées du fichier d'origine, utilisez ceci:

git blame -C -C -C dir2/A.txt

Git ne suit pas les copies au moment de la validation, mais les détecte lors de l'inspection de l'historique avec, par exemple. git blame et git log.

La plupart de ces informations proviennent des réponses fournies ici: Opération de copie de fichier d’enregistrement avec Git

23
Jakob Buron

Par souci d'exhaustivité, j'ajouterais que, si vous souhaitez copier un répertoire complet contenant des fichiers contrôlés ET non contrôlés, vous pouvez utiliser les éléments suivants:

git mv old new
git checkout HEAD old

Les fichiers non contrôlés seront copiés, vous devez donc les nettoyer:

git clean -fdx new
10
Hervé

J'ai légèrement modifié la réponse de Peter ici pour créer un script Shell non interactif réutilisable appelé git-split.sh:

#!/bin/sh

if [[ $# -ne 2 ]] ; then
  echo "Usage: git-split.sh original copy"
  exit 0
fi

git mv "$1" "$2"
git commit -n -m "Split history $1 to $2 - rename file to target-name"
REV=`git rev-parse HEAD`
git reset --hard HEAD^
git mv "$1" temp
git commit -n -m "Split history $1 to $2 - rename source-file to temp"
git merge $REV
git commit -a -n -m "Split history $1 to $2 - resolve conflict and keep both files"
git mv temp "$1"
git commit -n -m "Split history $1 to $2 - restore name of source-file"
7
Lukas Eder

Dans mon cas, j’ai fait le changement sur mon disque dur (coupé/collé environ 200 dossiers/fichiers d’un chemin de ma copie de travail vers un autre chemin de ma copie de travail), puis j’ai utilisé SourceTree (2.0.20.1) pour changements (un ajout, un supprimer), et tant que je mets à la fois ajouter et supprimer ensemble, il est automatiquement combiné en un seul changement avec une icône R rose (renommer je suppose).

J'ai remarqué que parce que j'avais un si grand nombre de modifications à la fois, SourceTree était un peu lent à détecter toutes les modifications. Certains de mes fichiers mis en scène ressemblent donc simplement à des ajouts (vert plus) ou à des suppressions tout simplement (rouge moins), mais je continuait d'actualiser l'état du fichier et conservait la mise en place des nouvelles modifications au fur et à mesure qu'elles apparaissaient, et au bout de quelques minutes, toute la liste était parfaite et prête pour la validation.

J'ai vérifié que l'historique est présent, aussi longtemps que je vérifie l'option "Suivre les fichiers renommés" quand je le cherche.

2
darbvin