Почему git checkout FILE повторно не применяет незафиксированные изменения? - PullRequest
0 голосов
/ 28 ноября 2018

git checkout COMMIT переключает ваше текущее состояние на то, на которое указывает COMMIT, и повторно применяет любые незафиксированные изменения (оно даже не работает, если ваши незафиксированные изменения несовместимы с COMMIT, на который вы хотите переключиться.

Это работает в резком контрасте с git checkout COMMIT FILE, который просто слепо отбрасывает любые незафиксированные изменения (без какого-либо предупреждения) FILE и «заставляет» состояние FILE перейти в состояние COMMIT.

Мой вопроспочему несоответствие? (возможно, в отношении исторических / проектных решений)

Конечно, это могло быть сделано так, что git checkout COMMIT FILE попытался повторно применить незафиксированные изменения к FILE и изящно потерпел неудачу, если это не удалось. Особеннопоскольку git checkout часто позиционируется как «безопасная» операция. Во всяком случае, текущим поведением git checkout COMMIT FILE должно быть поведение git reset --hard COMMIT FILE. (Кстати, поведение git reset COMMIT и git reset FILE такжеоставляет место для улучшения согласованности ...)

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

git checkout <em>commit</em> переключает ваше текущее состояние в указанное на commit ...

Да, вроде.Это на самом деле значительно сложнее под капотом.

... и повторно применяет любые незафиксированные изменения (даже неудачно, если ваши незафиксированные изменения несовместимы с commit, который вы хотитепереключиться на).

Это действительно не делает эту первую часть (но делает вторую), и это осложнение: то, что делает git checkout, является подмножеством того, что документация git read-tree вызывает объединение двух деревьев .Если вы перейдете по этой ссылке, вы увидите, что здесь перечислены 21 отдельный случай (один, случай № 3, имеет два подслоя, и я игнорирую случай № 0, который не может произойти, поэтому, если вы подсчитаете подслотыи случай № 0, вы получите 23 разных случаев).Я не буду перечислять их всех здесь, но они как бы сводятся к этой идее: Если Git не нужно прикасаться к индексу и рабочему дереву при переключении коммитов, это не так. Вот чтосохраняет ваши незафиксированные изменения на месте: Git не повторно применяет что-либо, просто он не вырывает также ничего.Это просто мягко ходит на цыпочках вокруг незавершенных изменений.

Это, в свою очередь, в некотором роде (но не полностью) объясняет это радикально иное поведение, что некоторые, в том числе и я, могут зайти так далеко, что называют злом:

Эта функция резко контрастирует с git checkout <em>commit</em> <em>file</em>, которая просто слепо отбрасывает любые незафиксированные изменения (без какого-либо предупреждения) file и "форсирует" состояниеиз file, что в commit.

Верно, и это потому, что Git рассматривает вашу команду как запрос на замену индексная копия file с копией из commit.Обновленная индексная копия также копируется в рабочее дерево.

Я бы сказал (и имел в прошлом), что это должна быть отдельная команда Git .Он реализуется одним и тем же исходным кодом просто потому, что этот исходный код полон кода «извлечение файла из коммита, запись в индекс и рабочее дерево».Но git reset и git reset - это отдельная команда. 1 Так что в лучшем случае это оправдание формы Hysterical Raisins .

Конечно, это можно было сделать так, что git checkout <em>commit</em> <em>file</em> попытался повторно применить незафиксированные изменения к file и потерпел неудачу изящно, если это не удалось.

ТоТакже доступно в git checkout, в опции -m ... вроде.Однако, это не на основе на файл , а на основе всего коммита .То есть вы можете git checkout -m <em>commit</em>, который объединяет ваше рабочее дерево с целевым коммитом (как слияние из трех деревьев), но не git checkout -m <em>commit</em> <em>file</em>.Чтобы получить этот эффект, извлеките файл из коммита, выберите базовую версию слияния для извлечения и используйте git merge-file для трех файлов:

git show other:file > file.other
git show base:file > file.base
git merge-file file file.base file.other

1 Git'sreset также объединяет слишком много несвязанных битов функциональности в одну команду, обращенную к пользователю, если вы спросите меня, но вы этого не сделали.

(С другой стороны,поведение git reset <em>commit</em> и git reset <em>file</em> также оставляет место для улучшения согласованности ...)

ОК, возможно, вы сделали!: -)

0 голосов
/ 28 ноября 2018

Когда вы проверяете коммит, git не «повторно» применяет изменения.Он оставляет измененное рабочее дерево и индексные копии неизменными тогда и только тогда, когда извлечение не повлияет на файл, т. Е. Версия файла в проверяемом вами коммите совпадает с версией файла в заголовке предварительной проверки HEAD.совершить.Но если он вообще должен касаться файла, а индексная или рабочая копия этого файла имеет изменения, тогда извлечение отменяется.

Когда вы checkout указываете конкретный путь, этот путь блокируется без предупрежденияпотому что это определение и цель проверки конкретного пути.Вы говорите git checkout -- myfile.txt специально, потому что хотите вернуть myfile.txt, и на самом деле эту команду git status рекомендует использовать, если вы хотите вернуть myfile.txt.Имейте в виду, что для того, чтобы это соответствовало поведению "checkout commit", он все же не будет пытаться повторно применить (объединить) локальные изменения;он просто отказался бы оформить заказ в любом измененном файле.(И справедливо сказать, что это не имеет особого смысла.)

Кажущаяся несогласованность является одним из нескольких признаков неудачного выбора использовать одно и то же ключевое слово для двух совершенно разных операций.Хотя любая из этих операций может быть разумно названа «checkout», это одно из немногих решений, которые мне действительно не нравятся в git, так как оно использует это имя для обоих.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...