Git — зачем нужны двойные тире при запуске команды в удаленном файле?

Рассмотрим репозиторий git, из которого однажды был удален файл.

git rm path/to/file
git commit -a -m"testing"

Хорошо, теперь я хочу увидеть git log для файла, но получаю классическое сообщение об ошибке:

git log path/to/file
fatal: ambiguous argument 'path/to/file': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions

Решение простое — добавляем --:

git log -- path/to/file

Но почему? Зачем это нужно? В чем здесь обоснование? Разве git не может сделать обоснованное предположение, что когда-то это мог быть файл? Я понимаю проблему «неоднозначности», но тега с таким именем никогда не было. Если файл был когда-то удален, а тег отсутствует, то выбор «интерпретации файла» всегда является хорошим выбором.

С другой стороны, можно иметь тег с тем же именем, что и файл, с которым git log справляется довольно хорошо:

fatal: ambiguous argument 'path/to/file': both revision and filename
Use '--' to separate filenames from revisions

Такое поведение кажется непоследовательным. Кто-нибудь может объяснить, что имели в виду разработчики git?


person Liosan    schedule 12.04.2013    source источник


Ответы (3)


git log можно использовать как для файлов, так и для веток, тегов и т. д.

Предположим, у вас есть папка с именем a/b/c, вы получите коммиты для этой папки, используя

git log a/b/c

Это нормально.

Вы также можете иметь ветку под названием d/e/f. Вы получите коммиты для этой ветви, используя

git log d/e/f

Это тоже хорошо.

Ситуация начинает усложняться, если элемент, над которым должен работать git log, не может быть четко определен. Если вы глупы и тоже называете свою ветвь a/b/c, git понятия не имеет, чей журнал должен быть напечатан: журнал ветки a/b/c или журнал вашего каталога a/b/c? Поэтому вам нужно немного подробнее рассказать о той информации, которую вы хотите получить:

  • показать журнал ветки a/b/c:
    git log a/b/c --
  • показать журнал папки a/b/c в текущей ветке:
    git log -- a/b/c
  • показать журнал папки a/b/c в ветке a/b/c:
    git log a/b/c -- a/b/c

С удаленным файлом аналогичная проблема: в рабочей копии нет ни файла с именем path/to/file, ни ветки с именем path/to/file. Это причина, почему вы должны указать, что вы хотите.

Конечно, git может знать, что существовал файл с именем path/to/file 20 000 ревизий назад, но для этого потребуется (в худшем случае) выполнить поиск по всей истории вашего проекта независимо от того, существовал ли такой файл или нет.

Явно указав путь к файлу после --, вы сообщаете git:

искать этот файл усерднее, даже если это займет несколько часов


Вывод (отвечая на ваш вопрос):
в вашем случае -- нужен, потому что иначе git log в целом работал бы медленнее.

person eckes    schedule 12.04.2013

Какая разница, когда файл существует, а когда его нет? Давайте рассмотрим случаи:

  1. И ревизия, и имя файла существуют. Явно двусмысленно.
  2. Ревизия существует, а имя файла — нет. Явно не двусмысленный.
  3. Ревизия не существует, а имя файла существует. Явно не двусмысленный.
  4. Редакции не существует, как и имени файла. Двусмысленный?

Это действительно так.

Чтобы узнать, существовал ли когда-либо файл по какому-то пути, нужно просмотреть историю, открыть каждую фиксацию, открыть каждое дерево и пройтись по дереву, чтобы увидеть, существовал ли этот путь в то время. В большом репозитории с большим количеством коммитов и длинным путем... это может дорого обойтись, особенно на медленном диске.

Но! Ты говоришь. Разве это не именно то, что делает git log filename? И ответ - да, это так. Разница в том, что когда я запускаю git log filename и известно, что файл существует, тогда git-log знает, что я хочу потратить время на получение этой истории.

Если вместо этого я запускаю git log foo, а foo не является ревизией или файлом, который в настоящее время существует, тогда ему потребуется потратить время на просмотр всего графа только для того, чтобы сказать мне, что foo был неоднозначным.

Ой.

Поэтому вы можете сказать git log -- filename, чтобы сообщить git, что вы действительно хотите, чтобы он пошел по графику. В противном случае он будет снижаться.

Примечание: git просто stat(2)s аргумент, который вы указываете в командной строке, чтобы определить, существует ли файл. Он не просматривает индекс и не открывает дерево HEAD. Он мог бы, конечно, делать те вещи, которые позволили бы вам использовать git log filename, где filename было удалением, не подготовленным для фиксации. Это кажется очень разумным изменением.

person Edward Thomson    schedule 12.04.2013
comment
Это почти то, о чем я спрашиваю :) Я понимаю необходимость -- при написании инструментов командной строки. Но если файл был когда-то удален, а тега нет, то выбор интерпретации файла всегда будет хорошим выбором. - person Liosan; 12.04.2013
comment
@Liosan: ах, кажется, я неправильно понял твой вопрос...! - person Edward Thomson; 12.04.2013

тега с таким именем никогда не было.

Почему ты это сказал? git tag path/to/file работает нормально.

Это действительно так просто, как кажется: git rm принимает только пути; git log принимает ссылочные имена и имена путей, сначала ссылочные имена, и все, что может быть путевым именем, также может быть ссылочным именем — это не столько правило для того, что может быть ссылочным именем, сколько определение.

В небольших проектах git log может легко решить, что если он вообще действителен, то в какой-то момент это должен быть путь к исходному файлу, но на этом этапе вы должны принять решение, уравновешивая вероятность и стоимость. из всех возможных ошибок здесь. Вероятнее ли, что вы запрашиваете журналы для файла, которого больше не существует, или что вы запутались в существующем имени пути или существующей ссылке? git log remote/ref встречается очень часто. Я думаю, что git просто предполагает, что имя, которое не соответствует ничему текущему, скорее всего, будет опечаткой.

person jthill    schedule 12.04.2013
comment
Я думаю, вы меня неправильно поняли. Я знаю, что могу создать тег, но тега с таким именем нет. Его не существует. Я знаю, что потенциально может, но это не так. Если я попытаюсь сделать git show path/to/file -- (без использования git tag, как вы показали), то я получу fatal: bad revision 'path/to/file'. - person Liosan; 12.04.2013