Git commit фиксирует поставленный и неустановленный файл - PullRequest
0 голосов
/ 04 декабря 2018

TL; DR: когда один файл содержит поэтапные и нематериальные изменения, при фиксации будут зафиксированы обе версии с последними изменениями в файле.Зачем?Я думал, что коммит будет фиксировать только поэтапную версию, как отмечено здесь: https://stackoverflow.com/a/19892657/279516.

Допустим, у нас есть один файл в нашем рабочем каталоге.(Ранее он был передан в репо.)

$ ls
foo.txt

Содержимое файла в настоящее время представляет собой всего один символ (число 1):

$ cat foo.txt
1

Давайте изменим содержимое файлабыть "12".Теперь у нас есть это:

$ cat foo.txt
12

В нашем рабочем каталоге показано изменение (для краткости вывод выводится из git для инструкций):

$ git status
modified:   foo.txt

Теперь git add добавит этот файл в промежуточный этап.index.

$ git add foo.txt

Вы не можете видеть его здесь, но имя файла теперь зеленое, чтобы указать, что оно было подготовлено:

$ git status
modified:   foo.txt

На этом этапе мы могли бы зафиксироватьфайл, и он станет частью нашего локального хранилища.Однако давайте сначала изменим foo.txt и посмотрим, что произойдет.

$ cat foo.txt
123

И если мы проверим git status, мы увидим две версии файла foo.txt:

$ git status
On branch master
Changes to be committed:

        modified:   foo.txt

Changes not staged for commit:

        modified:   foo.txt

Первый foo.txt зеленый.Второй красный.Первый содержит «12».Второй имеет «123».Что произойдет, если мы совершим сейчас?Только поставленный foo.txt должен быть передан.Таким образом, foo.txt с "12" в качестве его тела будет в нашем локальном репо.Другая версия foo.txt все еще находится в нашем рабочем каталоге.

Передайте foo.txt нашему локальному репо, КАК ЭТО БЫЛО, КОГДА МЫ ДОБАВИЛИ ЕГО:

$ git commit -m "Added 2 to foo." foo.txt

Однако этоне то, что случилось.В нашем рабочем каталоге изменений нет.Обе версии были совершены.Почему?

$ git status
On branch master
nothing to commit, working tree clean

Ответы [ 3 ]

0 голосов
/ 04 декабря 2018

Если вы хотите зафиксировать только поэтапную версию, запустите git commit без указания каких-либо файлов.

Пример:

$ echo 2 > foo
$ git add foo
$ echo 3 > foo
$ git commit -m haha

Теперь поэтапная версия зафиксирована, а неостановленные изменения остаютсяв вашем рабочем каталоге.Это легко проверить:

$ git show HEAD:foo
2
$ git diff
--- a/foo
+++ b/foo
@@ -1 +1 @@
-2
+3

Может быть, я продемонстрирую это поведение (git commit с файлом) на другом примере:

Выполните следующие действия:

$ git init
$ echo 1 > foo
$ echo 1 > bar
$ git add foo bar
$ git commit -m 1

Теперь оба foo и bar зафиксированы

$ echo 2 > foo
$ echo 2 > bar

Теперь оба изменены, давайте сделаем foo и зафиксируем bar:

$ git add foo
$ git commit -m 2 bar
$ git status
Changes to be committed:
    modified: foo
$ git diff --name-only HEAD~ HEAD
bar

Вывидите, что foo не изменяется в коммите, но промежуточное состояние сохраняется.

0 голосов
/ 04 декабря 2018

Помимо существующих (правильных) ответов, стоит отметить, что при использовании git commit [flags] file1 file2 ... fileN вы можете поставить флаги --only или --include:

  • --onlyэто значение по умолчанию, и это означает не обращая внимания на то, что я поставил до сих пор, просто сделайте коммит с добавлением этих файлов .

  • --include означает к чемуЯ до сих пор готов, добавьте эти файлы тоже .

Это достаточно просто, но немного неправильно, потому что --only должен принять после фиксации действие также.

Для правильного понимания необходимо знать, что такое индекс Git и как git commit на самом деле фиксирует то, что в индексе , а не то, что вдерево работы.

То, как программисты Git предполагают, что вы используете Git

Индекс - довольно сложная вещь, но большинство сводится к следующему: Индекс содержит набор файлов, которые будут добавлены в ваш следующий коммит. То есть, если вы запустите git commit прямо сейчас - безut перечисляя любые файлы - новый коммит будет снимком всех файлов, которые в данный момент находятся в индексе, сохраняя содержимое, которое сейчас находится в индексе .

Что означает , так это то, что сразу после:

$ git clone <some-url>
$ cd <repository>
$ git checkout <branch>

в вашем индексе все те же файлы, с тем же содержанием , чтовы видите в своем рабочем дереве.

Таким образом, каждый коммит представляет собой полный снимок всех файлов в этом коммите, замороженных навсегда, в специальной сжатой форме только для Git.Это означает, что вы всегда можете вернуть все эти файлы в их первоначальном виде, используя Git для извлечения и распаковки, и это то, что делает git checkout: он находит последний коммит в ветви, иизвлекает и распаковывает файлы.(Это слишком упрощенно: git checkout действительно довольно необычно. Но это самое основное, что он делает.)

Файлы полезной формы находятся в вашем рабочем дереве , где вы можете видетьих и работать над ними.Это хорошо, потому что преданные заморожены и доступны только для Git, что, очевидно, было бы проблемой.: -)

Но, чтобы сделать новый коммит, вместо повторного сжатия всех файлов рабочего дерева - что во многих случаях займет много времени - что делает Git сохранить (не замороженные, но все еще сжатые и только для Git) файлы в этой вещи, называемой, по-разному, index , область подготовки или кэш .Поэтому, если у вас есть README.txt и main.py в вашем рабочем дереве, они также есть - в форме только для Git - в вашем текущем коммите (где они заморожены) и в вашем индексе (где они оттаивают, но все еще только в Git).

Запуск git add README.txt указывает Git перезаписать индексную копию с копией рабочего дерева: возьмите файл обычного формата и перезапустите- сжать его до формата Git-only, заменив README.txt в индексе на значение из рабочего дерева.Он еще не заморожен - вы можете перезаписать его еще раз перед фиксацией - но он готов сейчас.

Запуск git commit без указания каких-либо файлов, говорит Git: Упакуйте все, что есть в индексе прямо сейчас и сделайте новый коммит из этого. Это происходит очень быстро, поскольку файлы уже находятся в правильной форме: Git просто нужно заморозить их вновый коммит.(Конечно, пока вы не сделаете файлы в индексе отличными от файлов в текущем коммите, в этом нет никакого смысла.)

Обратите внимание, что после git commitделает новый коммит из индекса, вы обычно возвращаетесь к нормальной ситуации, когда индекс и текущий коммит совпадают.Если вы git add добавили все файлы, которые вы изменили, все три копии каждого файла - HEAD, индекс и рабочее дерево - совпадают.

Представляем --only и--include

Бег git commit с некоторые перечисленные файлы немного отличаются, и именно здесь приходит --only против --include. Если вы используете --include, Git по существу делает git add для перечисленных файлов.Это просто сокращение для git add <those files> && git commit, более или менее.

Но, если вы используете --only - или не указываете, что означает --only - то, что делает Git, этоуберите регулярный индекс с пути и вместо этого создайте новый временный индекс из того, что находится в замороженном коммите.Для этого нового временного индекса Git будет git add каждый из перечисленных вами файлов.Затем Git сделает новый коммит из этого other index.Но теперь есть проблема, потому что Git теперь нужно вернуться к нормальному индексу, и вот здесь он становится немного хитрым.

Сделав new коммит из временного индекса, GitТеперь необходимо обновить индекс real таким же образом.По сути, после фиксации из временного индекса Git повторно добавляет тот же набор файлов, который вы перечислили, в индекс real , чтобы они снова соответствовали.

Давайте снова используем двухфайловый пример с README.txt и main.py.На этот раз давайте добавим номер версии после каждого файла.Он не является частью имени файла, он просто помогает нам запомнить:

    HEAD           index          work-tree
-------------   -------------   -------------
README.txt(1)   README.txt(1)   README.txt(1)
main.py(1)      main.py(1)      main.py(1)

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

Теперь вы редактируете оба файла в рабочем дереве:

    HEAD           index          work-tree
-------------   -------------   -------------
README.txt(1)   README.txt(1)   README.txt(2)
main.py(1)      main.py(1)      main.py(2)

Допустим, вы сейчас делаете git add main.py, чтобы скопировать версию рабочего дерева.в индекс:

    HEAD           index          work-tree
-------------   -------------   -------------
README.txt(1)   README.txt(1)   README.txt(2)
main.py(1)      main.py(2)      main.py(2)

Если вы запустите простой git commit прямо сейчас, новый HEAD будет иметь старый README.txt, потому что index имеет старый README.txt.Но вместо этого давайте запустим git commit --only README.txt.Это делает временный индекс, так что мы имеем:

    HEAD         temp-index       work-tree
-------------   -------------   -------------
README.txt(1)   README.txt(2)   README.txt(2)
main.py(1)      main.py(1)      main.py(2)

Далее, это делает новый коммит из временного индекса:

    HEAD         temp-index       work-tree
-------------   -------------   -------------
README.txt(2)   README.txt(2)   README.txt(2)
main.py(1)      main.py(1)      main.py(2)

Между тем, индекс real еще не изменился.Прокрутите немного вверх и посмотрите на это: какая версия main.py есть в нем?Какая версия README.txt есть в ней?

Если Git просто переключился обратно на реальный индекс сейчас, сохраняя только что сделанный вами коммит, это то, что вы бы получили:

    HEAD         ugly-index       work-tree
-------------   -------------   -------------
README.txt(2)   README.txt(1)   README.txt(2)
main.py(1)      main.py(2)      main.py(2)

То есть ваше дерево работ - это все последние файлы.Ваш коммит имеет обновленный README.txt.Но это уродливое состояние означает, что следующий коммит будет использовать старая / неправильная версия из README.txt!Вот почему Git теперь повторно добавляет README.txt к реальному индексу, так что вы получите:

    HEAD            index         work-tree
-------------   -------------   -------------
README.txt(2)   README.txt(2)   README.txt(2)
main.py(1)      main.py(2)      main.py(2)

Теперь вы готовы сделать второй коммит с обновленным main.py, если хотите.

0 голосов
/ 04 декабря 2018

Поскольку вы используете git commit [...] foo.txt:

https://git -scm.com / docs / git-commit

git commit [...] [<file>…​]

<file>…​ Когда файлы передаются в командной строке, команда фиксирует содержимое именованных файлов без записи уже внесенных изменений.Содержимое этих файлов также подготовлено для следующего коммита поверх того, что было подготовлено ранее.

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