Какова цель git-mv?


287

Из того, что я понимаю, Git на самом деле не нужно отслеживать операции переименования / перемещения / копирования файлов , так какова реальная цель git mv ? Страница man не является специально описательной ...

Это устарело? Это внутренняя команда, не предназначенная для использования обычными пользователями?

Ответы:


390
git mv oldname newname

это просто сокращение для:

mv oldname newname
git add newname
git rm oldname

то есть он обновляет индекс для старых и новых путей автоматически.


38
Также в нем есть несколько встроенных
— защит

6
Спасибо @CharlesBailey - считает ли git файлы newNameFile и oldNameFile разными? Если да, что произойдет, если мы хотим объединить их? Скажем, мы разветвляем проект ant в ветви A и создаем Branch B, а затем mavenize проекты в B. Имена файлов совпадают, но размещаются по разным путям, поскольку структура проекта изменилась. Скажем, обе ветви росли некоторое время параллельно. В какой-то момент, если мы хотим объединить проекты, как git узнает, что это тот же файл, только что переименованный в его путь? (если "git mv" == "git add + git rm")
— Rose

2
@SergeyOrshanskiy Если автоопределение пойдет не так mv oldname newname; git add newname; git rm oldname, оно также пойдет не так git mv oldname newname(см. Этот ответ ).
— Ajedi32

5
Обратите внимание, что git mvэто немного отличается от того mv oldname newname; git add newname; git rm oldname, что, если вы внесли изменения в файл перед git mvего использованием, эти изменения не будут размещены, пока вы не создадите git addновый файл.
— Ajedi32

2
git mv делает что-то другое, так как обрабатывает изменения в регистре имен файлов (от foo.txt до Foo.txt), тогда как эти команды выполняются по отдельности (в OSX)
— greg.kindel

66

С официального GitFaq :

Git имеет команду переименования git mv, но это просто удобство. Эффект неотличим от удаления файла и добавления другого с другим именем и тем же содержимым


8
Так вы теряете историю файлов? Я предполагал, что переименование сохранит старую историю для этого каталога ...
— Уилл Хэнкок

17
Ну да и нет. Прочитайте приведенную выше официальную ссылку GitFaq о переименованиях, а затем прочитайте длинное электронное письмо Линуса Торвальдса о том, почему ему не нравится идея отслеживания файлов инструментом SCM: permalink.gmane.org/gmane.comp.version-control.git/ 217
— Адам Нофсингер

3
@WillHancock Теперь я использовал git немного больше и могу ответить вам более определенно: в зависимости от вашего клиента git и его параметров, вы сможете отслеживать файл после переименования, если файл изменился внутренне достаточно мало, чтобы он считал его переименовать. Если вы слишком сильно измените файл и переименуете его, git не обнаружит его - в некотором смысле он говорит: «Нет, вы можете также считать, что это совершенно другой файл!»
— Адам Нофсингер

7
@AdamNofsinger эта ссылка мертва. Вот зеркало: web.archive.org/web/20150209075907/http://…
— Карл Уолш

2
Существует ли официальная ссылка (то есть более надежная, чем FAQ), в которой говорится об эквивалентности git mvи ручном подходе? Это не очевидно из git help mv.
— Тв

40

Git просто пытается угадать для вас, что вы пытаетесь сделать. Он делает все возможное, чтобы сохранить непрерывную историю. Конечно, это не идеально. Так что git mvпозволяет вам быть явным с вашим намерением и избежать некоторых ошибок.

Рассмотрим этот пример. Начиная с пустого репо,

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
mv a c
mv b a
git status

Результат:

# On branch master
# 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)
#
#   modified:   a
#   deleted:    b
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   c
no changes added to commit (use "git add" and/or "git commit -a")

Не удалось автоопределение :( Или сделал?

$ git add *
$ git commit -m "change"
$ git log c

commit 0c5425be1121c20cc45df04734398dfbac689c39
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

а потом

$ git log --follow c

Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

commit 50c2a4604a27be2a1f4b95399d5e0f96c3dbf70a
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:45 2013 -0400

    initial commit

Теперь попробуйте вместо этого (не забудьте удалить .gitпапку при экспериментировании):

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
git mv a c
git status

Все идет нормально:

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


git mv b a
git status

Теперь никто не идеален

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a
#   deleted:    b
#   new file:   c
#

В самом деле? Но конечно...

git add *
git commit -m "change"
git log c
git log --follow c

... и результат тот же, что и выше: --followпоказывает только полную историю.


Теперь будьте осторожны с переименованием, так как любой из этих вариантов может по-прежнему вызывать странные эффекты . Пример:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git commit -m "first move"
git mv b a
git commit -m "second move"

git log --follow a

commit 81b80f5690deec1864ebff294f875980216a059d
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:35:58 2013 -0400

    second move

commit f284fba9dc8455295b1abdaae9cc6ee941b66e7f
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:34:54 2013 -0400

    initial b

Сравните это с:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git mv b a
git commit -m "both moves at the same time"

git log --follow a

Результат:

commit 84bf29b01f32ea6b746857e0d8401654c4413ecd
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:37:13 2013 -0400

    both moves at the same time

commit ec0de3c5358758ffda462913f6e6294731400455
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:36:52 2013 -0400

    initial a

Упс ... Теперь история возвращается к начальной а вместо начальной b , что неверно. Поэтому, когда мы делали два хода за раз, Git запутался и не отслеживал изменения должным образом. Кстати, в моих экспериментах то же самое происходило, когда я удалял / создавал файлы вместо использования git mv. Действовать осторожно; вы были предупреждены ...


5
+1 за подробное объяснение. Я искал проблемы, которые могут возникнуть в истории журналов, если файлы будут перемещены в git, ваш ответ был действительно интересным. Спасибо! Кстати, вы знаете какие-либо другие подводные камни, которые мы должны избегать при перемещении файлов в git? (или любое упоминание, на которое вы могли бы указать .... не очень
— удачно поискать

1
Ну, мои примеры пессимистичны. Когда файлы пусты, гораздо сложнее правильно интерпретировать изменения. Я полагаю, что если вы просто делаете коммит после каждого переименования, у вас все будет хорошо.
— Ос

27

Как говорит @Charles, git mvэто сокращение.

Реальный вопрос здесь: «Другие системы контроля версий (например, Subversion и Perforce) обрабатывают переименования файлов специально. Почему не Git?»

Линус объясняет на http://permalink.gmane.org/gmane.comp.version-control.git/217 с характерным тактом:

Пожалуйста, прекратите это дерьмо "отслеживать файлы". Git отслеживает именно то , что имеет значение, а именно «коллекции файлов». Ничто другое не имеет отношения, и даже мысль о том, что это важно, только ограничивает ваше мировоззрение. Обратите внимание, что понятие «аннотировать» в CVS всегда неизбежно ограничивает то, как люди его используют. Я думаю, что это абсолютно бесполезный кусок дерьма, и я описал кое-что, что, на мой взгляд, в миллион раз полезнее, и все вышло именно потому, что я не ограничиваю свое мышление неправильной моделью мира.


9

Есть еще одно использование, которое я git mvне упомянул выше.

С момента обнаружения git add -p(режим патча git add; см. Http://git-scm.com/docs/git-add ) я люблю использовать его для просмотра изменений при добавлении их в индекс. Таким образом, мой рабочий процесс становится (1) работой над кодом, (2) просмотром и добавлением в индекс, (3) фиксацией.

Как git mvвписывается? Если файл перемещается напрямую, то с помощью git rmи git addвсе изменения добавляются в индекс, а использование git diff для просмотра изменений не так просто (перед фиксацией). Использование git mv, однако, добавляет новый путь к индексу, но не вносит изменения в файл, что позволяет git diffи git add -pработать как обычно.


5

Есть нишевый случай, который git mvостается очень полезным: когда вы хотите изменить регистр имени файла в файловой системе без учета регистра. APFS (mac) и NTFS (windows) по умолчанию нечувствительны к регистру (но сохраняют регистр).

greg.kindel упоминает об этом в комментарии к ответу CB Bailey.

Предположим, вы работаете на Mac и у вас есть файл, Mytest.txtуправляемый git. Вы хотите изменить имя файла на MyTest.txt.

Вы можете попробовать:

$ mv Mytest.txt MyTest.txt
overwrite MyTest.txt? (y/n [n]) y
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

О, Боже. Git не признает, что в файле произошли какие-либо изменения.

Вы можете обойти это, переименовав файл полностью, а затем переименовав его обратно:

$ mv Mytest.txt temp.txt
$ git rm Mytest.txt
rm 'Mytest.txt'
$ mv temp.txt MyTest.txt
$ git add MyTest.txt 
$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

    renamed:    Mytest.txt -> MyTest.txt

Ура!

Или вы можете сэкономить на этом, используя git mv:

$ git mv Mytest.txt MyTest.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

    renamed:    Mytest.txt -> MyTest.txt
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.