TL; DR
Если файл равен в каком-то коммите, вы можете получить его из этого коммита с помощью:
git checkout <commit-specifier> IgnoreMyChanges.cs
, после чего вам, возможно, следует run:
git rm --cached IgnoreMyChanges.cs
commit-specifier
почти наверняка не будет HEAD
.
Если у вас есть Git 2.23 или позже вы можете использовать:
git restore --source=<commit-specifier> -w IgnoreMyChanges.cs
, и вам не понадобится следующий git rm --cached
. В качестве альтернативы вы можете, как ElpieKay предложил в комментарии , использовать:
git show <commit-specifier>:IgnoreMyChanges.cs > IgnoreMyChanges.cs
, который использует перенаправление вашей оболочки для записи файла в ваше рабочее дерево. Имейте в виду, что в некоторых случаях использование git show
как это обходит обработку окончания строки CRLF.
Long
Сначала несколько общих советов:
Вы всегда должны писать HEAD
в верхнем регистре. Орфография в нижнем регистре иногда работает, а иногда нет. Не делай этого. Если вам не нравится набирать такое количество прописных букв, попробуйте использовать @
вместо HEAD
.
Если ваша версия Git - 2.23 или более поздняя, рассмотрите возможность использования двух команды git switch
и git restore
для выполнения двух отдельных заданий, которые git checkout
складываются в одну команду.
Учитывая, что вы не упомянули git restore
, я буду придерживаться с git checkout
ниже, но обратите внимание, что есть два вида git checkout
:
Один вид проверки проверяет всю фиксацию . Вот что делает git switch
, когда вы используете новые команды. Этот вид проверки также меняет, к какой фиксации и ветке относится имя HEAD
(или @
). (Помните, что «настоящее имя» любого коммита - это его необработанный ha sh ID. HEAD
- это сокращение от текущего имени ветки и текущего коммита ha sh ID .) Этот вид проверки является безопасным, поскольку сначала проверяется, не повредит ли ваши файлы.
Другой вид проверки проверяет некоторые c файлы. Это то, что делает git restore
, когда вы используете новые команды. Этот вид проверки вообще не меняет HEAD
, и этот вид проверки небезопасен : он уничтожит несохраненную работу, если вы попросите об этом.
Оба вида оформления заказа обозначаются git checkout
с использованием команд до 2.23. Иногда бывает сложно сказать наверняка, какой именно вид вы получите. В Git 2.23 или более поздних версиях, если команда git checkout
могла выполнить любой из них, git checkout
заставит вас выбрать один, вместо того, чтобы просто сделать неправильный. К сожалению, в версии до 2.23 Git, git checkout
иногда выбирает уничтожить вашу несохраненную работу вариант, когда вы этого не ожидали.
Использование:
git checkout <commit-specifier> -- <file>
всегда вызывает второй тип git checkout
- вид, который теперь более непосредственно обрабатывается git restore
. Часть commit-specifier
может быть чем угодно, что указывает конкретную c фиксацию, включая:
- необработанный ha sh ID;
- ветвь название; или
- специальное имя
HEAD
.
Двойной дефис отделяет эту часть от части имени файла и является необязательным. Он вам понадобится, если имя файла напоминает параметр git checkout
, например, если файл называется --force
или -m
или что-то в этом роде.
Вы можете заключите имя файла в двойные кавычки, чтобы защитить его от интерпретатора командной строки. Когда и нужно ли вам это делать, зависит не от Git, а от вашего интерпретатора командной строки.
С учетом этого давайте посмотрим, что входит в коммит и что делает .gitignore
Теперь файлы в Git никогда не игнорируются. На самом деле слово «игнорировать» - неправильный глагол. Мы используем это слово, потому что в большинстве случаев правильный набор слов слишком длинный, чтобы его трудно было сказать. Настоящая уловка здесь состоит в том, чтобы правильно различать guish между файлами, которые являются в Git, и файлами, которые не в Git.
Каждая фиксация - обнаруженная по идентификатору ha sh или по другому имени, приемлемому в соответствии с документацией gitrevisions, - содержит набор файлов в виде снимка. Таким образом, любой заданный файл либо находится в некотором коммите, либо не входит в этого коммита.
Если:
git checkout HEAD -- IgnoreMyChanges.cs
говорит:
IgnoreMyChanges.cs did not match any file(s) known to git.
, то этого файла нет в этом коммите. Если вы хотите узнать, что это за коммит - у которого, в частности, есть sh ID - вы можете запустить:
git rev-parse HEAD
, который задает Git вопрос: What ha sh ID делает специальное имя HEAD
означает?
Тот факт, что IgnoreMyChanges.cs
отсутствует в , что коммит, не говорит вам, находится ли файл IgnoreMyChanges.cs
в любом другой коммит. Хотя каждая фиксация является полным и полным снимком всех ваших файлов, это снимок всех файлов, которые вы - или кто-то другой - сказал Git поместить в него в то время, когда вы (или кто-то еще) сделал это. После создания этот снимок содержит эту версию из этих файлов навсегда ... или, по крайней мере, до тех пор, пока этот коммит продолжает существовать. Если вы можете передать ha sh ID в Git, и эта фиксация находится в большой базе данных Git всех- Git -объектов, Git может получить эти версии эти файлы назад.
Эти файлы в Git. Они навсегда заморожены в коммитах. Каждая фиксация имеет полный снимок каждого файла с действующим дедупликацией. Поскольку большинство коммитов в основном содержат те же файлы , что и некоторые другие коммиты, это дедупликация означает, что коллекция замороженных файлов коммитов не очень быстро разрастается. Но поскольку они заморожены и имеют этот особый, странный формат Git, ничто другое на вашем компьютере не может их использовать .
Для используйте эти файлы, вы должны иметь Git взять их из фиксации. Вот о чем идет речь в первом типе git checkout
: мы выбираем фиксацию - часто по имени ветки - и получаем Git извлечение каждого файла, который в ней . Эти файлы копируются в рабочую область, где они теперь являются обычными повседневными файлами. Рабочая область называется вашим рабочим деревом или рабочим деревом . Эти повседневные файлы в вашем рабочем дереве - это ваши , а не Git.
Важно всегда помнить об этом. Ваши файлы рабочего дерева вообще отсутствуют в Git. Они просто находятся в вашем рабочем дереве; они принадлежат вам, чтобы использовать их по своему усмотрению; и это не файлы, которые будут go в новую фиксацию, либо .
* индекс 1493 *
Когда у вас есть Git извлечение некоторой фиксации —Как в git switch
или первом виде git checkout
- Git на самом деле first копирует файлы замороженного формата в Git index . Этот индекс, который настолько важен и / или плохо назван, что имеет еще два имени - он также называется промежуточной областью или иногда (сейчас редко) cache - содержит файлы, которые являются своего рода «половиной» Git.
Извлечение полной фиксации:
- копирует все файлы в индекс Git, 1 где они имеют замороженный формат , но больше не заморожены;
- затем копирует все файлы из индекса Git в ваше рабочее дерево, перезаписывая файлы рабочего дерева.
Это стиль git switch
git checkout
, и он обычно гарантирует, что ваши файлы рабочего дерева не будут уничтожены, убедившись, что все они сохранены в текущем коммите - том, который является текущим перед переключением, то есть.
Теперь, когда все целевые (переключаемые) файлы находятся в индексе Git, Git может обновить ваше рабочее дерево: Git будет удалить любой файл, который не в целевом коммите, который раньше был в индексе и вашем рабочем дереве, чтобы чтобы убрать это с дороги. Git заменит любой файл, который равен в целевой фиксации, но отличается. Файлы, которые совпадают как в ранее текущем, так и в целевом коммите, можно оставить в покое, и Git сделает это. Таким образом, переключение с одной фиксации на другую только обновляет или удаляет файлы рабочего дерева, которые нуждаются в для обновления или удаления.
Конечным результатом является то, что ваше рабочее дерево и * Индекс 1247 * Git теперь соответствует выбранной вами фиксации. 2 Это важно, потому что, когда вы делаете новую фиксацию, Git действительно останавливается в этот новый коммит, именно те файлы, которые находятся в индексе прямо тогда .
Следовательно, что вы делаете при подготовке нового коммита:
- отрегулируйте ваши файлы в вашем рабочем дереве, как вам нравится, используя любую команду , которая вам нравится; но затем
- используйте
git add
(и / или git rm
) для обновления Git индекса , потому что индекс содержит предлагаемую следующую фиксацию.
Когда у вас есть файлы индекса - поэтапные копии - организованные так, как вам нужно, вы запускаете git commit
, а Git создает новый моментальный снимок файлов, находящихся в индексе или промежуточной области. Поскольку индекс соответствовал commit , который вы извлекли, фактически изменяется только то, что вы скопировали обратно в индекс .
1 Технически, то, что находится в индексе, не является полной копией файла, а скорее его именем и режимом, а также blob ha sh ID . Вы можете увидеть разницу, если и когда вы разбиваете индекс на отдельные записи, используя git ls-files --stage
или git update-index
. Но когда вы не используете эти низкоуровневые подкоманды build-a-new- Git -command, вам не нужно беспокоиться об этом: вы можете просто думать об индексе как о хранении копии файла. , готовы к go следующей фиксации.
2 Существует особый случай, который возникает, когда вы переключаетесь с одной фиксации на другую при сохранении некоторых изменений незафиксированными. Вы не всегда можете это сделать, и это немного сложно описать, когда вы можете это сделать, но это связано с тем, может ли Git переключать коммиты без перезаписи каких-либо измененных данных в обоих Git индекс и ваше рабочее дерево. Для более полной картины этого особого случая см. Оформить заказ в другой ветке, если в текущей ветке есть незафиксированные изменения .
Отслеживаемые и неотслеживаемые
Файлы в ваше дерево работ либо отслеживается , либо не отслеживается . Отслеживаемый файл - это, очень просто, файл, который находится в вашем рабочем дереве и в индексе Git прямо сейчас . неотслеживаемый файл - это файл, который находится в вашем рабочем дереве, но не в индексе Git прямо сейчас . И это действительно все, что есть, но это очень много.
Как только вы действительно поймете, как Git использует индекс Git, концепция отслеживаемого файла обретет смысл. Отслеживаемый файл будет в следующей фиксации. Не отслеживаемый файл не будет. Git строит новый фиксируется из файлов, находящихся в индексе, во время запуска git commit
. Если файл там, то он в новом коммите. Если файла там нет, значит, его нет в новой фиксации.
Файлы, которые находятся в индексе, как я сказал ранее, как бы «половина в» Git: они готовы быть замороженными в фиксации, и как только они заморожены в фиксацию, они безопасно хранятся. Теперь они точно находятся в Git.
Файлы в вашем рабочем дереве вообще не находятся в Git. Их можно скопировать в индекс, и тогда они будут наполовину введены, и фиксация сделает их определенно полностью загруженными, но до тех пор это просто обычные файлы, которых нет в Git.
.gitignore
Добавление файла в список .gitignore
делает три вещи:
Он сообщает Git: , если этот файл сейчас нет в индексе (т.е. не отслеживается), не жалуйтесь на это, когда я запускаю git status
.
Команда git status
выводит некоторые общие полезные данные, а затем выполняет два сравнения:
Первое сравнение сравнивает текущую фиксацию (HEAD
) и Git индекс. Для всех одинаковых файлов git status
ничего не говорит. Для каждого файла, который отличается каким-либо образом - измененным, удаленным или новым - git status
перечисляет имя файла в разделе изменения, подготовленные для фиксации .
Второе сравнение сравнивает индекс Git с вашим рабочим деревом. Для всех одинаковых файлов git status
ничего не говорит. Для файлов, которые отличаются (изменены или удалены), git status
перечисляет имя файла в разделе изменения, не поставленные для фиксации .
Обратите внимание, что неотслеживаемые файлы еще не перечислены во втором сравнении: файлы, которых нет в индексе, но находятся в рабочем дереве, о них еще ничего не сказано. Перечислив промежуточные и неустановленные файлы, если таковые имеются, теперь Git будет жаловаться на эти неотслеживаемые файлы ... , если вы не перечислили их в .gitignore
.
Следовательно, перечисление файла в .gitignore
сообщает Git: , если файл не отслеживается, молчите. Но это не влияет на отслеживаемый файл как на отслеживаемый файл будет go в новую фиксацию.
Git имеет массовые git add
операции, такие как git add *
или git add .
. Внесение файла в список .gitignore
сообщает Git, что если файл не отслеживается, массовое add
должно не копировать его в индекс.
Обычно это неотслеживаемый файл - один не в индексе - будет просто скопирован в индекс, и теперь это будет отслеживаемый файл с копией индекса, соответствующей копии рабочего дерева на момент запуска git add
на нем. Но, вероятно, вы указали файл с .gitignore
по , чтобы предотвратить его фиксацию . Поскольку Git фиксирует все, что находится в индексе Git, вы не хотите, чтобы это добавлялось в индекс Git. Эта цель достигается путем пропуска git add
неотслеживаемого файла.
И снова, это не влияет на отслеживаемый файл . Отслеживаемый файл уже находится в индексе Git.
Последний - и обычно невидимый, но иногда это вызывает проблемы - включение файла в .gitignore
дает Git дополнительное разрешение чтобы разрушить содержимое файла. Обычно Git сообщает вам, что какой-то файл будет перезаписан или удален с помощью git checkout
или git merge
, но его включение в .gitignore
дает Git разрешение на уничтожение (или удаление) файла в некоторых случаях.
Так что вместо того, чтобы называть этот файл .gitignore
, Git, возможно, следовало назвать его .git-ignore-these-files-when-they-are-untracked-and-also-do-not-automatically-add-them-with-en-masse-add-operations-unless-they-are-already-tracked
. (Он может даже упомянуть аспект иногда-clobber-them.) Но это нелепо для ввода, поэтому .gitignore
это так.
Заключение
Важно знать из того, что на самом деле означает отслежено против неотслеживаемое : в индексе или нет. Важно знать, что Git делает новые коммиты из индекса, а не из вашего рабочего дерева, и что ни один из ваших файлов рабочего дерева на самом деле не в Git у всех. И важно знать, что перечисление файла в .gitignore
не имеет никакого эффекта при отслеживании файла.
Обычно, если файла нет в текущем (HEAD
) коммите, он также не сейчас не отслеживается. Это потому, что вы попали в это состояние, запустив git checkout
или git switch
, чтобы полностью заполнить индекс Git и ваше рабочее дерево из коммита HEAD
. Таким образом, если его не было и нет в HEAD
, он не был помещен в индекс (или даже был удален из него) и поэтому не отслеживается. Но поскольку вы можете намеренно вынуть что-то из индекса:
git rm --cache somefile
или поместить в индекс:
git add somefile
, это не жесткая и быстрая гарантия .
Если какой-либо файл не отслеживается, его включение в .gitignore
имеет значение. Но если он отслеживается, это все, что действительно имеет значение: он отслеживается , а будет в следующем коммите.