2017-09-15 1 views
1

Le mois dernier, j'ai commencé à contribuer à un dépôt GitHub en annulant le repo correspondant, en créant une branche de fonctionnalité et en soumettant une requête pull. En répétant ce processus pendant quelques jours, j'ai rencontré un problème étrange en renommant les fichiers avec la commande Linux pré-installée mv et aussi avec la commande Git git mv.Comportement Odd `git mv`

Le problème réel est que selon le moment où vous déplacer/renommer un fichier avec git mv, lorsque vous git add et à quel point vous modifiez le fichier renommé, soit vous obtenez:

On branch master 
Changes to be committed: 
    (use "git reset HEAD <file>..." to unstage) 

     renamed: somethingelse -> something 

Ou ceci:

On branch master 
Changes to be committed: 
    (use "git reset HEAD <file>..." to unstage) 

     new file: something 
     deleted: somethingelse 

Pour le démontrer, j'ai écrit un test:

#!/bin/bash 

# To my knowledge, this “problem” only occurs with new files in a Git repo 
printf "COMMAND: mkdir -v gitrepo\n\n" 
mkdir -v gitrepo 

printf "\nCOMMAND: cd gitrepo\n\n" 
cd gitrepo 

printf "\nCOMMAND: git init\n\n" 
git init 

printf "\nCOMMAND: git status\n\n" 
git status 

printf "\nCOMMAND: touch something\n\n" 
touch something 

printf "\nCOMMAND: git status\n\n" 
git status 

printf "\nCOMMAND: git add something\n\n" 
git add something 

printf "\nCOMMAND: git status\n\n" 
git status 

printf '\nCOMMAND: git commit -m "Added something"\n\n' 
git commit -m "Added something" 

printf "\nCOMMAND: git status\n\n" 
git status 

printf "\nCOMMAND: git mv something somethingelse\n\n" 
git mv something somethingelse 

printf "\nCOMMAND: git status\n\n" 
git status 

# Type in the following on line 1: First line of code 
printf "\nCOMMAND: vim somethingelse\n\n" 
vim somethingelse 

printf "\nCOMMAND: git status\n\n" 
git status 

printf "\nCOMMAND: git add somethingelse\n\n" 
git add somethingelse 

printf "\nCOMMAND: git status\n\n" 
git status 

printf '\nCOMMAND: git commit -m "Renamed something to somethingelse and edited somethingelse"\n\n' 
git commit -m "Renamed something to somethingelse and edited somethingelse" 

printf "\nCOMMAND: git status\n\n" 
git status 

printf "\nCOMMAND: git mv somethingelse something\n\n" 
git mv somethingelse something 

printf "\nCOMMAND: git status\n\n" 
git status 

# If you add something to the first line, the rename will not be detected by Git 
# However, if you instead create 2 newlines and fill line 3 with new code, 
# the rename gets detected for whatever reason 
printf "\nCOMMAND: vim something\n\n" 
vim something 

printf "\nCOMMAND: git status\n\n" 
git status 

printf "\nCOMMAND: git add something\n\n" 
git add something 

printf "\nCOMMAND: git status\n\n" 
git status 

printf '\nCOMMAND: git commit -m "Renamed somethingelse to something and edited something"\n\n' 
git commit -m "Renamed somethingelse to something and edited something" 

printf "\nCOMMAND: git status\n\n" 
git status 

cd .. && rm -fr gitrepo && printf "\nREMOVED gitrepo folder\n" 
printf "\nDONE.\n" 

Pour une raison quelconque, cela affecte principalement les «nouveaux fichiers» et non ceux qui existent déjà dans un référentiel. Si vous clonez ma branche du Spoon-Knife repository par exemple avec git clone https://github.com/christianheinrichs/Spoon-Knife.git puis que vous appliquez le flux de travail du script de test lié, vous verrez que dans la plupart des cas, vous pourrez renommer le fichier README.md en README par exemple, le modifier et il sera toujours considéré comme un changement de nom au lieu d'un nouveau fichier/segment supprimé. Bien que je puisse reproduire le nouveau comportement de fichier/supprimé sur le repo de fourchette Spoon-Knife cloné, je ne suis pas sûr exactement comment je l'ai fait et croyez-moi quand je dis que j'ai essayé de le comprendre.

Alors que se passe-t-il exactement ici que je ne comprends pas?

Voir: https://gist.github.com/christianheinrichs/e50bfdd5eec70a606fa6ce4a88c5951b#file-git_mv-test-sh-L65

Répondre

4

git ne garde pas un drapeau en disant "ce fichier newname a été initialement appelé fichier oldname":

git mv oldname newname 

# is exactly equivalent to : 

mv oldname newname 
git rm oldname 
git add newname 

Lors de l'affichage de l'état d'un fichier, git essaie de deviner si elle était un rename ou un delete + add en comparant le contenu des fichiers, et de voir à quel point ils sont similaires. Donc, si vous commencez par git mv un fichier, puis modifiez le fichier, en fonction de combien le fichier est modifié, git peut ou ne peut pas voir que tout a commencé avec un mv.

Voir aussi la réponse à cette question: How does Git know that file was renamed?

+1

Notez également que lorsque vous exécutez 'git diff' vous pouvez activer ou désactiver la détection renommer, et définir le nombre « seuil de similarité ». Lorsque 'git status' exécute' git diff' pour vous, dans ce cas, il définit le seuil de similarité sur 50%: la détection du renommage est toujours activée et le seuil est fixé. – torek

+0

@torek Je suppose que vous parlez de 'git diff -M [], --find-renames [= ]'? C'est une caractéristique intéressante que je ne connaissais pas et qui pourrait partiellement expliquer le problème dans ma deuxième question. –

+0

@LeGEC Merci pour votre réponse. Cependant, avant de l'accepter, pouvez-vous expliquer le comportement de Git dans la deuxième question que j'ai jointe? 'Aussi, pourquoi Git ne détecte-t-il pas le renommer si vous ajoutez quelque chose à la première ligne, mais si vous créez 2 lignes et remplissez la ligne 3 avec le nouveau code?' –