Как уже отмечалось в нескольких ответах и комментариях, незафиксированная работа просто незафиксирована: она еще не «ни на одной» ветви. Ментальная модель, которая вам нужна, чтобы понять, почему это происходит и что происходит, относительно проста, но не так проста, как могла бы быть, если бы Git не было Git. : -)
Помните обо всех этих вещах:
В хранилище, содержащемся в ветвях, находятся коммиты . Пока что-то не будет совершено , оно вообще не будет в хранилище.
Каждый коммит - это отдельная вещь, в которой вы можете выбрать один и работать с ним. , В каждом коммите хранится полный снимок всех его файлов, а также дополнительная информация, например, о том, кто его сделал, - то, что отображается в git log
. Другими словами, Git не хранит изменений , а скорее всего снимков .
Когда Git показывает вам коммит как изменения , то, что он действительно делает, сравнивает коммит с тем, который предшествует ему - его родительский коммит. Git извлекает и родителя, и ребенка во временную область (в памяти), а затем сравнивает их. Для всех одинаковых файлов Git ничего не говорит. Для каждого файла, который отличается, Git вычисляет рецепт изменения: внесите эти изменения в родительскую копию файла, и вы получите дочернюю копию файла.
Git может сравнивать любые два коммита - например, дать git diff
любые два коммита, например, sh ID, - но он также может сравнивать некоторые другие вещи, которые не совсем коммиты, как мы увидим ниже.
Git находит коммиты по их идентификаторам ha sh. Это большие уродливые строки букв и цифр. Каждый коммит имеет уникальный. Они выглядят случайными (но на самом деле полностью детерминированы c). Люди плохо умеют с ними работать, поэтому мы используем имена ветвей , во-первых.
Каждый коммит полностью, полностью, на 100% читаем. только когда это сделано. Ничто не может изменить какую-либо часть любого коммита. Файлы внутри каждого коммита хранятся в специальном, только для чтения, Git -сжатом сжатом формате, который может использовать только Git. Они заморожены навсегда и продолжают существовать в сохраненной форме, пока сам коммит продолжает существовать.
Эта последняя часть великолепна, потому что это означает, что вы всегда можете go вернуться к любой версии, которую вы сохранили. Но это также ужасно, потому что это означает, что вы на самом деле не можете выполнить какую-либо работу с файлами внутри коммита.
Из-за этого Git must извлечение коммит. То есть он должен скопировать все замороженные Git -только файлы из коммита и превратить их в обычные повседневные файлы, которые может использовать остальная часть вашего компьютера. Таким образом, есть команда Git, которая означает: Извлечь замороженные файлы из коммита и сделать их обычными файлами, которые я могу видеть и работать с ними. Эта команда git checkout
(и в Git 2.23 и новее, git switch
также).
Вынув файлы из коммита, теперь вы можете просматривать их и работать с ними. Вот где вы вносите изменения. Эта рабочая область ваша для работы: это не Git. Git просто перезаписывает эти файлы новыми (и / или удаляет некоторые из этих файлов), когда вы говорите это сделать. Таким образом, любые сделанные вами изменения на самом деле нигде не хранятся в Git. Они просто находятся в вашей рабочей области - место, которое Git называет вашим рабочим деревом или рабочим деревом .
Способ, которым вы работаете, состоит в том, что вы выбираете commit - по номеру ha sh или по имени ветви; в любом случае вы выбрали один конкретный коммит - и дали Git, у которого sh идентификатор или имя, с git checkout
или git switch
. Git затем упорядочивает вещи так, чтобы в вашем рабочем дереве были копии файлов этого коммита. Этот коммит теперь ваш текущий коммит . Если вы дали Git имя ветви, то это имя вашей ветви текущая ветка . (Если вы дали Git необработанный идентификатор * ha 1302 *, вы не находитесь на какой-либо ветви сейчас.)
И текущее имя ветви, и текущий коммит можно найти по специальному названию HEAD
. Git может ответить на два разных вопроса о HEAD
:
- Какое название ветви, если таковое имеется, имеет место? (Если у него нет имени ветви, Git называет это detached HEAD .)
- Какой коммит ha sh ID дает имя?
Команда git status
обычно сообщает вам о имени ветки , так как это более полезно для людей, но вы также увидите sh идентификаторы во многих местах, таких как git log
или git branch -v
output.
Здесь важно знать еще одну вещь. Когда Git сначала копирует все файлы из некоторого коммита, он копирует их - или, по крайней мере, информацию о них, а также о вашем рабочем дереве - в область Git только для формата, которая объединяет Git с вашим рабочим деревом. Эта дополнительная копия помещается во что-то, что Git вызывает, по-разному, index или область подготовки .
В результате существует третий копия каждого файла, 1 как бы между подтвержденной копией и пригодной для использования копией, например:
HEAD index work-tree
--------- --------- ---------
README.md README.md README.md
main.py main.py main.py
например, если у вас есть два файла в коммите. Копия каждого файла в HEAD
доступна только для чтения и Git -только, и вы не можете видеть его напрямую - вам нужно Git извлечь его. Копия в вашем рабочем дереве - это обычный файл, который вы можете использовать по своему усмотрению.
Копия каждого файла в index - Git -только в том же формате как зафиксированный файл - но он не только для чтения: вы можете заменить его в любое время. Команда git add
эффективно берет все, что у вас есть в рабочем дереве, и копирует это в индекс. Поэтому после того, как вы изменили файл в своем рабочем дереве, вам нужно использовать git add
для замены копии в индексе Git.
Пока вы не обновите git add
файлов, копии, которые находящиеся в индексе совпадают с копиями, которые находятся в коммите HEAD
. То есть вы начали с того, что git checkout
или git switch
выбрали коммит. Git скопировал файлы коммита в индекс, затем скопировал файлы индекса в ваше рабочее дерево. Так что HEAD
и индекс совпадают.
Когда вы делаете новый коммит, Git просто замораживает копию индекса в новый коммит. Новый коммит добавляется к текущему имени ветки (используя "какое имя ветки содержится в HEAD
вопросе), а затем новый коммит становится текущим коммитом (обновление которого sh ID - текущий коммит.) Теперь, когда Git сделал новый коммит из любых копий файлов, которые были в индексе, HEAD
и индекс снова совпадает.
Обратите внимание, как копии файлы в вашем рабочем дереве на самом деле не имеют значения, Git. Git делает новые коммиты из индекса Git. Они не находятся ни в одной ветви: только коммиты могут содержаться в ветвях .
Когда вы используете git status
, он фактически запускает для вас два git diff
с:
Сначала он печатает имя текущей ветви, а некоторые другая полезная информация, если это уместно.
Затем он сравнивает - т. е. использует git diff
- HEAD
коммит с индексом. Для каждого файла, который одинаков, он ничего не говорит. Для каждого файла, который отличается, git diff
покажет вам рецепт для изменения левой си де (HEAD
) копировать в правую (индексную) копию; git status
обрезает это до только имени файла и статуса (новый, измененный или удаленный). Это изменения, сделанные для коммита .
Затем он сравнивает, т. Е. Использует git diff
, индекс с вашим рабочим деревом. Для каждого файла, который одинаков, он ничего не говорит. Для каждого файла, который отличается, git status
перечисляет имя и статус файла. Это изменения, не подготовленные для фиксации .
Наконец, поскольку ваше рабочее дерево принадлежит вам, вы можете создавать новые файлы, которые вообще не включены в индекс. Эти файлы не вышли из предыдущего коммита. Они еще не включены в индекс Git, пока вы не используете git add
для их копирования в него. Это ваши неотслеживаемые файлы . Не отслеживаемый файл - это просто файл, который находится в вашем рабочем дереве, но отсутствует в индексе.
Если вы перечислите конкретный неотслеживаемый файл в .gitignore
, git status
не будет упомяните этот файл, и git add
не скопирует этот файл в индекс. Это работает только для неотслеживаемых файлов: если файл уже находится в индексе Git, его перечисление в .gitignore
не влияет на него.
Обратите внимание, что индекс загружается из коммита, который вы git checkout
, и разные коммиты могут содержать разные файлы, поэтому то, что находится в индексе Git, зависит - по крайней мере, на начальном этапе - от выбранного вами c коммита. После этого вы можете использовать git add
и git rm
, чтобы помещать новые файлы в индекс Git и полностью извлекать файлы из индекса Git, прежде чем делать новый коммит, поэтому набор файлов в индекс также не заморожен. Только файлы в commitits заморожены.
Если учесть все вышеперечисленное, будет иметь смысл гораздо больше о Git.
1 Технически, индекс содержит ссылку на содержимое файла, а не на собственно файл. Это различие имеет значение только в том случае, если вы начнете использовать git ls-files --stage
и git update-index
для непосредственной работы с индексом. В основном вам следует использовать git add
и аналогичные команды, которые скрывают все эти детали, так что вам не нужно заботиться об этих незначительных различиях.
Заключение
A Коммит может быть в нескольких ветках. Все эти ветви содержат , которые фиксируют. Полезно установить отношения между коммитами: некоторые являются родителями других, некоторые являются детьми других, некоторые являются братьями и сестрами и так далее. Это очень похоже на семейное древо.
В коммите есть снимок, но, сравнивая снимок в parent коммита с моментальным снимком в коммите, вы можете увидеть коммит как изменения .
Вы можете иметь коммиты, которых нет в любой ветке , как работает git stash
. Мы не рассмотрели, как здесь работают имена ветвей, но на самом деле они содержат идентификатор ha sh * last commit в этой ветке. Все остальное в самих коммитах. коммиты имеют отношения (родитель / потомок), но имена ветвей не имеют.
коммиты имеют значение здесь. Имена ветвей просто помогают вам (и Git) находят коммиты.
Фиксирует снимки магазина, а не изменения. Так же как и индекс Git - снимок в индексе действует как предлагаемый следующий коммит . Так что, в этом отношении, ваше рабочее дерево: но ваше рабочее дерево ваше , как вы хотите. Git просто создает и удаляет файлы в нем, когда вы указываете Git сделать это (git checkout
, git switch
и удалить один указанный c файл или набор файлов из обоих индексов и work-tree, git rm
).
Мы не рассмотрели это здесь, но можно изменить копии файлов в вашем рабочем дереве (и даже в индексе Git тоже) , а затем все еще переключиться на другую ветку. Но это только иногда возможно. Когда и почему вы можете или не можете переключаться таким образом, становится сложно, хотя есть фундаментальный аспект безопасности: вы можете переключать ветви, если это не разрушит ваши изменения; Вы не можете, если это будет. Подробные сведения см. В Извлечение другой ветки при наличии незафиксированных изменений в текущей ветке .
Незавершенная работа существует только в вашем рабочем дереве и, возможно, также в индексе Git, если вы использовали git add
, чтобы скопировать его туда. Его нет ни в одном коммите, и, следовательно, нет ни в одной ветви.