Странное поведение `git mv`

В прошлом месяце я начал вносить вклад в репозиторий GitHub, разветвив соответствующий репозиторий, создав функциональную ветвь и затем отправив запрос на извлечение. Повторяя этот процесс в течение нескольких дней, я столкнулся со странной проблемой при переименовании файлов с помощью предустановленной команды Linux mv а также с командой Git git mv,

Проблема в том, что в зависимости от того, когда вы перемещаете / переименовываете файл с git mv, когда ты git add это и в какой момент вы редактируете переименованный файл, вы либо получаете:

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

        renamed:    somethingelse -> something

Или это:

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

        new file:   something
        deleted:    somethingelse

Чтобы продемонстрировать это, я написал тест:

#!/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"

По некоторым причинам, это в основном влияет на "новые файлы", а не те, которые уже существуют в репозитории. Если вы клонируете мою ветвь хранилища Spoon-Knife с git clone https://github.com/christianheinrichs/Spoon-Knife.git Например, а затем примените рабочий процесс связанного тестового сценария, вы увидите, что в большинстве случаев вы сможете переименовать файл README.md, например, в README, отредактировать его, и он все равно будет считаться переименованным вместо новый файл / удаленный сплит.

Хотя я мог воспроизвести новый файл / удаленное поведение на клонированном репо-ветке Spoon-Knife, я не совсем уверен, как я это сделал, и поверьте мне, когда скажу, что пытался это выяснить.

Так что именно здесь происходит, что я не понимаю?

См.: https://gist.github.com/christianheinrichs/e50bfdd5eec70a606fa6ce4a88c5951b

1 ответ

Решение

git не держит флаг, говорящий "это newname файл изначально назывался oldname файл":

git mv oldname newname

# is exactly equivalent to :  

mv oldname newname
git rm oldname
git add newname

При отображении статуса файла, git пытается угадать, если это было rename или delete + add сравнивая содержимое файлов и видя, насколько они похожи.

Итак, если вы начнете с git mv файл, а затем отредактируйте файл, в зависимости от степени изменения файла, git может или не может увидеть, что все началось с mv,

Смотрите также ответ на этот вопрос: как Git узнает, что файл был переименован?

Другие вопросы по тегам