Как сделать так, чтобы git пометил удаленный и новый файл как файл перемещенный?
505
Я переместил файл вручную, а затем изменил его. По словам Git, это новый файл и удаленный файл. Есть ли способ заставить Git рассматривать его как перемещение файла?
Для данного файла old_file.txt, то git mv old_file.txt new_file.txtэто эквивалентно git rm --cached old_file.txt, mv old_file.txt new_file.txt, git add new_file.txt.
Ярл
3
Ярл: нет, это не так. Если в файле также есть изменения, git mvони не будут добавлены в кеш, но git addбудут. Я предпочитаю переместить файл назад, чтобы я мог использовать его git mv, а затем git add -pпросмотреть свой набор изменений.
Git автоматически обнаружит движение / переименование, если ваша модификация не слишком серьезна. Просто git addновый файл и git rmстарый файл. git statusзатем покажет, обнаружил ли он переименование.
Кроме того, для перемещения по каталогам вам может понадобиться:
перейдите к вершине этой структуры каталогов.
Запустить git add -A .
Запустите, git statusчтобы убедиться, что «новый файл» теперь является «переименованным» файлом
Если в git status по-прежнему отображается «новый файл», а не «переименован», вам нужно следовать совету Хэнка Гея и выполнять перемещение и модификацию в двух отдельных коммитах.
Пояснение: «не слишком серьезный» означает, что новый файл и старый файл> 50% «похожи» на основании некоторых индексов сходства, которые использует git.
pjz 19.10.10
151
Что-то, что повесило меня на несколько минут: если переименованные файлы и удаленные файлы не подготовлены для фиксации, они будут отображаться как удаление и новый файл. Как только вы добавите их в промежуточный индекс, он распознает его как переименование.
Марчич
19
Стоит упомянуть, что, говоря о « Git, автоматически обнаружит перемещение / переименование ». Это происходит в то время, когда вы используете git status, git logили git diff, не в то время, когда вы делаете git add, git mvили git rm. Далее, говоря об обнаружении переименования, это имеет смысл только для промежуточных файлов. Таким образом, git mvпоследующее изменение файла может выглядеть так, git statusкак будто оно рассматривает его как файл rename, но когда вы используете git stage(так же, как git add) файл, становится ясно, что изменение слишком велико, чтобы его можно было обнаружить как переименованное.
Ярл
5
Реальный ключ, кажется, заключается в добавлении как новых, так и старых локаций в одно и то же git add, что, как предполагает @ jrhorn424, должно быть всего лишь целым репо.
Брианари
5
Это не безошибочно, особенно если файл изменился и имеет небольшой размер. Есть ли способ, чтобы конкретно рассказать git о переезде?
Ребс
112
Делать движение и модифицировать в отдельных коммитах.
Я понимаю, что Git может обрабатывать ходы и модификации одновременно. При программировании на Java и использовании IDE переименование класса является одновременно и модификацией, и движением. Я понимаю, что Git даже должен иметь возможность автоматически выяснить это, когда есть ход (из удаления и создания).
Пупено
1
Perl тоже требует этого, и у меня никогда не было, чтобы Git обнаруживал движение / переименование.
jrockway
10
@ jrockway, я имел. Происходит легко с небольшими файлами, я думаю, они «становятся слишком разными», чтобы означать движение ».
Ноябрь 15-10
2
Есть ли способ сделать это автоматизированным? У меня много файлов в индексе, я хочу сначала зафиксировать перемещение, а затем изменение, но это трудно сделать вручную
ReDetection
5
Стоит отметить, что если git diffне распознавать переименование через один коммит, он не распознает его через два коммита. Вам нужно будет использовать -Mака, --find-renamesчтобы сделать это. Поэтому, если мотивация, которая привела вас к этому вопросу, состоит в том, чтобы увидеть переименование в запросе на извлечение (если судить по опыту), разделение его на два коммита не приведет вас к этой цели.
Mattliu
44
Это все воспринимаемые вещи. Git обычно довольно хорошо распознает ходы, потому что GIT - это трекер контента.
Все, что действительно зависит, - это то, как ваш «стат» отображает это. Единственная разница здесь - флаг -M.
Извините, если это кажется немного педантичным, но «Git, как правило, довольно хорошо распознает ходы, потому что GIT - это трекер контента», мне кажется, не является следствием. Да, это трекер контента, и, возможно, он хорошо обнаруживает ходы, но одно утверждение на самом деле не следует из другого. Только потому, что это трекер контента, обнаружение перемещения не обязательно хорошо. На самом деле, у трекера контента вообще не может быть обнаружения движения.
Лоуренс Гонсалвес
1
@WarrenSeine, когда вы переименовываете каталог, файлы SHA1 в этом каталоге не изменяются. Все, что у вас есть, это новый объект TREE с теми же SHA1. Нет причин, по которым переименование каталога может привести к значительным изменениям данных.
Кент Фредрик
1
@KentFredric Вы показали, что с помощью -M с «git log» мы можем проверить, переименован ли файл, и я попробовал его, и он работает нормально, но когда я отправляю свой код для проверки и вижу этот файл в gerrit ( gerritcodereview.com ) там он показывает, что файл был добавлен, а предыдущий был удален. Так есть ли опция в "git commit", используя которую я делаю commit и gerrit показывает это правильно.
Патрик
1
Я этого не показывал. Я показал, что Git способен притворяться, что add + delete - это переименование из-за одинакового содержимого. Там нет никакого способа узнать, что произошло, и это не волнует. Все, что git помнит, это «добавлено» и «удалено». «Переименованный» никогда не записывается.
Кент Фредрик
1
Например, в последнее время я много делал «Копировать + Редактировать» в репо. Большинство способов его просмотра видят только «новый файл», но если вы пропустите git log -M1 -C1 -B1 -D --find-copies-harder, git может «обнаружить», что новый файл мог быть скопирован первым. Иногда он делает это правильно, иногда он находит совершенно не связанные файлы, которые имеют идентичное содержимое.
Кент Фредрик
36
git diff -Mили git log -Mдолжны автоматически обнаруживать такие изменения как переименование с незначительными изменениями, если они действительно есть. Если ваши незначительные изменения не являются незначительными, вы можете уменьшить порог сходства, например
$ git log -M20 -p --stat
чтобы уменьшить его от значения по умолчанию 50% до 20%.
Вот быстрое и грязное решение для одного или нескольких переименованных и измененных файлов, которые не были переданы.
Допустим, файл был назван fooи теперь он называется bar:
Переименуйте barво временное имя:
mv bar side
Оформить заказ foo:
git checkout HEAD foo
Переименуйте fooв barGit:
git mv foo bar
Теперь переименуйте ваш временный файл обратно в bar.
mv side bar
Этот последний шаг - то, что возвращает ваш измененный контент обратно в файл.
Хотя это может сработать, если перемещенный файл слишком отличается по содержанию от исходного git, он посчитает более эффективным определить, что это новый объект. Позвольте мне продемонстрировать:
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
renamed: README -> README.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: README.md
modified: work.js
$ git add README.md work.js # why are the changes unstaged, let's add them.
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
deleted: README
new file: README.md
modified: work.js
$ git stash # what? let's go back a bit
Saved working directory and index state WIP on dir: f7a8685 update
HEAD is now at f7a8685 update
$ git status
On branch workit
Untracked files:
(use "git add <file>..." to include in what will be committed)
.idea/
nothing added to commit but untracked files present (use "git add" to track)
$ git stash pop
Removing README
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
new file: README.md
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: README
modified: work.js
Dropped refs/stash@{0} (1ebca3b02e454a400b9fb834ed473c912a00cd2f)
$ git add work.js
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
new file: README.md
modified: work.js
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: README
$ git add README # hang on, I want it removed
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
deleted: README
new file: README.md
modified: work.js
$ mv README.md Rmd # Still? Try the answer I found.
$ git checkout README
error: pathspec 'README' did not match any file(s) known to git.
$ git checkout HEAD README # Ok the answer needed fixing.
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
new file: README.md
modified: work.js
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: README.md
modified: work.js
Untracked files:
(use "git add <file>..." to include in what will be committed)
Rmd
$ git mv README README.md
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
renamed: README -> README.md
modified: work.js
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: work.js
Untracked files:
(use "git add <file>..." to include in what will be committed)
Rmd
$ mv Rmd README.md
$ git status
On branch workit
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitignore
renamed: README -> README.md
modified: work.js
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: README.md
modified: work.js
$ # actually that's half of what I wanted; \
# and the js being modified twice? Git prefers it in this case.
Этот процесс не имеет смысла. мерзавец не добавляет метаданные для переименований, git mvпросто удобство для git rm/ git addпары. Если вы уже выполнили «mv bar foo», то все, что вам нужно сделать, - это убедиться, что вы git add fooи git rm barдо совершения коммита. Это может быть сделано как одна git add -Aкоманда или, возможно, git add foo; git commit -aпоследовательность.
CB Bailey
17
Все, что я знаю, это то, что до того, как я это сделал, Git не распознал это как ход. После того, как я это сделал, Git признал это ходом.
8
Это признает это как движение. Но это также имеет изменение как неустановленное изменение сейчас. Как только вы addснова загрузите файл, git разбивает перемещение / изменение на удаленное / добавленное снова.
Майкл Пифель
4
Этот процесс будет работать, если вы git commitпосле шага 3, в противном случае это неверно. Кроме того, @CharlesBailey правильно, вы можете с легкостью выполнить нормальное действие mv blah fooна шаге 3 с последующей фиксацией и получить те же результаты.
DaFlame
5
«Этот процесс не имеет смысла». - Он служит цели, когда Git запутался и думает, что файл был удален, а другой файл был добавлен. Это устраняет путаницу в Git и помечает файл как перемещенный явно, без необходимости делать промежуточные коммиты.
Данак
20
Если вы говорите git statusне показывать переименования, попробуйте git commit --dry-run -aвместо
Если вы используете TortoiseGit, важно отметить, что автоматическое обнаружение переименования в Git происходит во время коммита, но тот факт, что это произойдет, не всегда отображается программным обеспечением заранее. Я переместил два файла в другой каталог и выполнил несколько небольших изменений. Я использую TortoiseGit в качестве инструмента фиксации, и в списке «Изменения» были показаны файлы, которые были удалены и добавлены, а не перемещены. Запуск git status из командной строки показал похожую ситуацию. Однако после фиксации файлов они оказались переименованы в журнале. Таким образом, ответ на ваш вопрос таков: пока вы не сделали ничего слишком радикального, Git должен автоматически подобрать переименование.
Редактировать: очевидно, если вы добавляете новые файлы, а затем делаете состояние git из командной строки, переименование должно отображаться перед фиксацией.
Редактировать 2: Кроме того, в TortoiseGit добавьте новые файлы в диалоге фиксации, но не фиксируйте их. Затем, если вы войдете в команду Show Log и посмотрите на рабочий каталог, вы увидите, обнаружил ли Git переименование перед фиксацией.
Тот же вопрос был поднят здесь: https://tortoisegit.org/issue/1389 и был зарегистрирован как ошибка, чтобы исправить здесь: https://tortoisegit.org/issue/1440 Оказывается, это проблема отображения с коммитом TortoiseGit диалог, а также вид существует в состоянии git, если вы не добавили новые файлы.
Вы правы, даже если TortoiseGit показывает delete + add, даже если git status показывает delete + add, даже если git commit --dry-run показывает delete + add, после git commit я вижу переименование, а не delete + add.
Томас Кубес
1
Я уверен, что автоматическое обнаружение переименования происходит во время поиска истории ; в коммите это всегда добавить + удалить. Это также объясняет шизофреническое поведение, которое вы описываете. Отсюда и варианты здесь: stackoverflow.com/a/434078/155892
Марк Совул
10
Или вы можете попробовать ответить на этот вопрос здесь от Амбер ! Чтобы процитировать это снова:
Сначала отмените поэтапное добавление для перемещенного вручную файла:
Обратите внимание, что git mvкоманда существует только в Git версий 1.8.5 и выше. Поэтому вам, возможно, придется обновить ваш Git, чтобы использовать эту команду.
Для меня это сработало, чтобы сохранить все изменения перед фиксацией и вытащить их снова. Это заставило git повторно проанализировать добавленные / удаленные файлы и правильно пометить их как перемещенные.
Существует, вероятно, лучший способ «командной строки» сделать это, и я знаю, что это взлом, но я так и не смог найти хорошего решения.
Использование TortoiseGIT: Если у вас есть коммит GIT, в котором некоторые операции перемещения файлов отображаются в виде загрузки / удаления, а не переименования, даже если файлы имеют небольшие изменения, сделайте следующее:
Проверьте, что вы сделали локально
Проверить мини-однострочное изменение во 2-м коммите
Перейти в GIT войти в черепаху Git
Выберите два коммита, щелкните правой кнопкой мыши и выберите «объединить в один коммит»
Новый коммит теперь будет правильно отображать переименования файлов ..., что поможет поддерживать правильную историю файлов.
Когда я редактирую, переименовываю и перемещаю файл одновременно, ни одно из этих решений не работает. Решение состоит в том, чтобы сделать это в двух коммитах (редактировать и переименовать / переместить по отдельности), а затем fixupчерез второй коммит git rebase -iсделать это в одном коммите.
Как я понимаю этот вопрос: «Как заставить git распознавать удаление старого файла и создание нового файла при перемещении файла».
Да, в рабочем каталоге, как только вы удалите старый файл и вставите старый файл, git statusпроизойдет " deleted: old_file" и " Untracked files: ... new_file"
Но в промежуточном индексе / уровне, когда вы добавляете и удаляете файл с помощью git, он будет распознаваться как перемещение файла. Для этого, если вы выполнили удаление и создание с помощью своей операционной системы, введите следующие команды:
git add new_file
git rm old_file
Если содержимое файла на 50% или более одинаковое, при выполнении git statusкоманды вы должны получить:
old_file.txt
, тоgit mv old_file.txt new_file.txt
это эквивалентноgit rm --cached old_file.txt
,mv old_file.txt new_file.txt
,git add new_file.txt
.git mv
они не будут добавлены в кеш, ноgit add
будут. Я предпочитаю переместить файл назад, чтобы я мог использовать егоgit mv
, а затемgit add -p
просмотреть свой набор изменений.Ответы:
Git автоматически обнаружит движение / переименование, если ваша модификация не слишком серьезна. Просто
git add
новый файл иgit rm
старый файл.git status
затем покажет, обнаружил ли он переименование.Кроме того, для перемещения по каталогам вам может понадобиться:
git add -A .
git status
чтобы убедиться, что «новый файл» теперь является «переименованным» файломЕсли в git status по-прежнему отображается «новый файл», а не «переименован», вам нужно следовать совету Хэнка Гея и выполнять перемещение и модификацию в двух отдельных коммитах.
источник
git status
,git log
илиgit diff
, не в то время, когда вы делаетеgit add
,git mv
илиgit rm
. Далее, говоря об обнаружении переименования, это имеет смысл только для промежуточных файлов. Таким образом,git mv
последующее изменение файла может выглядеть так,git status
как будто оно рассматривает его как файлrename
, но когда вы используетеgit stage
(так же, какgit add
) файл, становится ясно, что изменение слишком велико, чтобы его можно было обнаружить как переименованное.git add
, что, как предполагает @ jrhorn424, должно быть всего лишь целым репо.Делать движение и модифицировать в отдельных коммитах.
источник
git diff
не распознавать переименование через один коммит, он не распознает его через два коммита. Вам нужно будет использовать-M
ака,--find-renames
чтобы сделать это. Поэтому, если мотивация, которая привела вас к этому вопросу, состоит в том, чтобы увидеть переименование в запросе на извлечение (если судить по опыту), разделение его на два коммита не приведет вас к этой цели.Это все воспринимаемые вещи. Git обычно довольно хорошо распознает ходы, потому что GIT - это трекер контента.
Все, что действительно зависит, - это то, как ваш «стат» отображает это. Единственная разница здесь - флаг -M.
git log --stat -M
git log --stat
git help log
источник
git log -M1 -C1 -B1 -D --find-copies-harder
, git может «обнаружить», что новый файл мог быть скопирован первым. Иногда он делает это правильно, иногда он находит совершенно не связанные файлы, которые имеют идентичное содержимое.git diff -M
илиgit log -M
должны автоматически обнаруживать такие изменения как переименование с незначительными изменениями, если они действительно есть. Если ваши незначительные изменения не являются незначительными, вы можете уменьшить порог сходства, напримерчтобы уменьшить его от значения по умолчанию 50% до 20%.
источник
Вот быстрое и грязное решение для одного или нескольких переименованных и измененных файлов, которые не были переданы.
Допустим, файл был назван
foo
и теперь он называетсяbar
:Переименуйте
bar
во временное имя:Оформить заказ
foo
:Переименуйте
foo
вbar
Git:Теперь переименуйте ваш временный файл обратно в
bar
.Этот последний шаг - то, что возвращает ваш измененный контент обратно в файл.
Хотя это может сработать, если перемещенный файл слишком отличается по содержанию от исходного git, он посчитает более эффективным определить, что это новый объект. Позвольте мне продемонстрировать:
источник
git mv
просто удобство дляgit rm
/git add
пары. Если вы уже выполнили «mv bar foo», то все, что вам нужно сделать, - это убедиться, что выgit add foo
иgit rm bar
до совершения коммита. Это может быть сделано как однаgit add -A
команда или, возможно,git add foo; git commit -a
последовательность.add
снова загрузите файл, git разбивает перемещение / изменение на удаленное / добавленное снова.git commit
после шага 3, в противном случае это неверно. Кроме того, @CharlesBailey правильно, вы можете с легкостью выполнить нормальное действиеmv blah foo
на шаге 3 с последующей фиксацией и получить те же результаты.Если вы говорите
git status
не показывать переименования, попробуйтеgit commit --dry-run -a
вместоисточник
Если вы используете TortoiseGit, важно отметить, что автоматическое обнаружение переименования в Git происходит во время коммита, но тот факт, что это произойдет, не всегда отображается программным обеспечением заранее. Я переместил два файла в другой каталог и выполнил несколько небольших изменений. Я использую TortoiseGit в качестве инструмента фиксации, и в списке «Изменения» были показаны файлы, которые были удалены и добавлены, а не перемещены. Запуск git status из командной строки показал похожую ситуацию. Однако после фиксации файлов они оказались переименованы в журнале. Таким образом, ответ на ваш вопрос таков: пока вы не сделали ничего слишком радикального, Git должен автоматически подобрать переименование.
Редактировать: очевидно, если вы добавляете новые файлы, а затем делаете состояние git из командной строки, переименование должно отображаться перед фиксацией.
Редактировать 2: Кроме того, в TortoiseGit добавьте новые файлы в диалоге фиксации, но не фиксируйте их. Затем, если вы войдете в команду Show Log и посмотрите на рабочий каталог, вы увидите, обнаружил ли Git переименование перед фиксацией.
Тот же вопрос был поднят здесь: https://tortoisegit.org/issue/1389 и был зарегистрирован как ошибка, чтобы исправить здесь: https://tortoisegit.org/issue/1440 Оказывается, это проблема отображения с коммитом TortoiseGit диалог, а также вид существует в состоянии git, если вы не добавили новые файлы.
источник
Или вы можете попробовать ответить на этот вопрос здесь от Амбер ! Чтобы процитировать это снова:
Сначала отмените поэтапное добавление для перемещенного вручную файла:
Затем используйте Git для перемещения файла:
Конечно, если вы уже совершили ручное перемещение, вы можете вместо этого сбросить ревизию до перемещения, а затем просто выполнить команду git mv.
источник
Используйте
git mv
команду для перемещения файлов вместо команд перемещения ОС: https://git-scm.com/docs/git-mvОбратите внимание, что
git mv
команда существует только в Git версий 1.8.5 и выше. Поэтому вам, возможно, придется обновить ваш Git, чтобы использовать эту команду.источник
У меня была эта проблема недавно, при перемещении (но не изменении) некоторых файлов.
Проблема в том, что Git изменил некоторые окончания строк, когда я переместил файлы, а затем не смог сказать, что файлы были одинаковыми.
Использование
git mv
решило проблему, но она работает только с отдельными файлами / каталогами, и у меня было много файлов в корне хранилища.Один из способов исправить это - использовать магию bash / batch.
Другой способ заключается в следующем
git commit
. Это обновляет окончания строки.git commit --amend
git commit --amend
. В этот раз нет изменений в окончаниях строк, поэтому Git счастливисточник
Для меня это сработало, чтобы сохранить все изменения перед фиксацией и вытащить их снова. Это заставило git повторно проанализировать добавленные / удаленные файлы и правильно пометить их как перемещенные.
источник
Существует, вероятно, лучший способ «командной строки» сделать это, и я знаю, что это взлом, но я так и не смог найти хорошего решения.
Использование TortoiseGIT: Если у вас есть коммит GIT, в котором некоторые операции перемещения файлов отображаются в виде загрузки / удаления, а не переименования, даже если файлы имеют небольшие изменения, сделайте следующее:
Новый коммит теперь будет правильно отображать переименования файлов ..., что поможет поддерживать правильную историю файлов.
источник
Когда я редактирую, переименовываю и перемещаю файл одновременно, ни одно из этих решений не работает. Решение состоит в том, чтобы сделать это в двух коммитах (редактировать и переименовать / переместить по отдельности), а затем
fixup
через второй коммитgit rebase -i
сделать это в одном коммите.источник
Как я понимаю этот вопрос: «Как заставить git распознавать удаление старого файла и создание нового файла при перемещении файла».
Да, в рабочем каталоге, как только вы удалите старый файл и вставите старый файл,
git status
произойдет "deleted: old_file
" и "Untracked files: ... new_file
"Но в промежуточном индексе / уровне, когда вы добавляете и удаляете файл с помощью git, он будет распознаваться как перемещение файла. Для этого, если вы выполнили удаление и создание с помощью своей операционной системы, введите следующие команды:
Если содержимое файла на 50% или более одинаковое, при выполнении
git status
команды вы должны получить:источник
git status
скажет "deleted: old_file
" и "Untracked files: ... new_file
": не начиная с Git 2.18: stackoverflow.com/a/50573107/6309