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

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

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

Ответ 1

git mv oldname newname

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

mv oldname newname
git add newname
git rm oldname

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

Ответ 2

Из официального GitFaq:

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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

С открытием git add -p (git добавить режим исправления, см. 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 работать как обычно.

Ответ 6

Есть нишевый случай, когда 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