Зачем мне нужна сцена перед коммитом в Git?

104

Я новичок в управлении версиями и понимаю, что «фиксация» - это, по сути, создание резервной копии при обновлении новой «текущей» версии того, над чем вы работаете.

Я не понимаю, для чего постановка с практической точки зрения. Постановка чего-то, что существует только по названию, или служит определенной цели? Когда вы фиксируете, он все равно все равно фиксирует, верно?

Изменить: я думаю, что могу запутать терминологию. «Поэтапный» файл - это то же самое, что «отслеживаемый» файл?

Гражданин
источник
6
Нет. Отслеживаемый файл - это файл, который известен репозиторию (обычно из предыдущей фиксации). Поэтапный файл - это файл, который был добавлен в индекс, который позже будет использоваться для фиксации.
Марк Питерс

Ответы:

83

Когда вы фиксируете, он фиксирует только изменения в индексе («поэтапные» файлы). Для этого есть много применений, но наиболее очевидным является разбиение ваших рабочих изменений на более мелкие, автономные части. Возможно, вы исправили ошибку при реализации функции. Вы можете git addпросто этот файл (или git add -pдобавить только часть файла!), А затем зафиксировать это исправление, прежде чем фиксировать все остальное. Если вы используете, git commit -aто вы просто форсируете addвсе прямо перед фиксацией. Не используйте, -aесли вы хотите использовать промежуточные файлы.

Вы также можете рассматривать подготовленные файлы как промежуточную рабочую копию с помощью --cachedмногих команд. Например, git diff --cachedон покажет вам, чем отличается этап от, HEADчтобы вы могли видеть, что собираетесь зафиксировать, не добавляя другие рабочие изменения.

Бен Джексон
источник
25
Другое действительно распространенное использование - когда некоторые из ваших изменений никогда не должны фиксироваться; например, вы можете инсценировать хорошие вещи, зафиксировать их, а затем сдуть плохие git reset --hard.
Cascabel
4
@BenJackson в вашем примере, какая разница между этапом + фиксация и выборочной фиксацией? Я не вижу разницы.
Eugenio
9
Лично я не смог получить удовлетворительного ответа на вопрос: «Какой смысл в постановке?» вопрос. Честно говоря, это просто ненужно. Я уже использую локальную ветку, поэтому нет риска сломать сборку. Я не собираюсь публиковать и выполнять пулреквест, пока не буду полностью удовлетворен своими изменениями. Я уже могу логически разбивать свои коммиты. Просто нет необходимости в промежуточном шаге. Таким образом, я никогда не использую его.
mrplainswalker 01
3
Не думаю, что вы действительно ответили на вопрос. «но наиболее очевидным является разбиение ваших рабочих изменений на более мелкие, автономные части». Зачем кому-то нужно разбивать свои изменения на более мелкие? Если вы собираетесь добавить и зафиксировать одно исправление ошибки до исправления кода, который изначально намеревались изменить, почему бы вам просто не зафиксировать это исправление ошибки, а не добавлять его, а затем фиксировать его?
kiwicomb123 01
1
@ kiwicomb123 Обычно потому, что вы обнаружили эту ошибку во время работы над чем-то другим, и хотите, чтобы это исправление было в отдельной фиксации с собственным сообщением журнала и гибкостью для слияния / вишневого выбора / перебазирования, которые исправляют где-то еще.
Бен Джексон
26
  • Промежуточная область дает возможность уменьшить размер коммита. Просто сделайте одно логическое изменение в коде, добавьте измененные файлы в промежуточную область и, наконец, если изменения плохие, выполните проверку предыдущей фиксации или иным образом зафиксируйте изменения. Это дает гибкость, чтобы разделить задачу на более мелкие задачи и зафиксировать меньшие изменения. С промежуточной областью легче сосредоточиться на небольших задачах.
  • Это также предлагает вам сделать перерыв и забыть о том, сколько работы вы сделали, прежде чем сделать перерыв. Предположим, вам нужно изменить три файла, чтобы сделать одно логическое изменение, и вы изменили первый файл, и вам нужен длительный перерыв, пока вы не начнете вносить другие изменения. В настоящий момент вы не можете совершить фиксацию и хотите отслеживать, с какими файлами вы закончили, чтобы после возвращения вам не нужно было пытаться вспомнить, сколько работы было сделано. Так что добавьте файл в область подготовки, и он сохранит вашу работу. Когда вы вернетесь, просто git diff --stagedпроверьте, какие файлы вы изменили и где, и начните вносить другие изменения.
Тапаши Табассум Урми
источник
13

Практическая цель постановки - логическое разделение коммитов файлов.

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

Предположим , у вас есть 4 файла fileA.html, fileB.html, fileC.htmlи fileD.html. Вы делаете изменения для всех 4 -х файлов и готовы совершить , но изменения в fileA.htmlи fileB.htmlлогически связаны между собой (например, тем же новой реализация функции в обеих файлах) , а изменения fileC.htmlи fileD.htmlявляются самостоятельными и логически связаны с предыдущими файлами. Вы можете первый этап файлы fileA.htmlи fileB.htmlи зафиксируйте их.

git add fileA.html
git add fileB.html
git commit -m "Implemented new feature XYZ"

Затем на следующем этапе вы вносите изменения в оставшиеся два файла и фиксируете их.

git add fileC.html
git add fileD.html
git commit -m "Implemented another feature EFG"
DarthWader
источник
7
В этом примере я не уверен, действительно ли требуется постановка. После редактирования всех 4 файлов, если я просто хочу зафиксировать fileA.html и fileB.html, я все равно могу зафиксировать без постановки. Команда: git commit -m "Implemented new feature XYZ" fileA.html fileB.html будет работать нормально и без команд git add. Я пришел из мира подрывной деятельности, где постановка не является концепцией, поэтому я не уверен в полезности постановки с помощью git
Паван
5

Легче понять использование команд git, addи commitесли вы представите, что файл журнала поддерживается в вашем репозитории на Github. Типичный файл журнала проекта для меня может выглядеть так:

---------------- Day 1 --------------------
Message: Complete Task A
Index of files changed: File1, File2

Message: Complete Task B
Index of files changed: File2, File3
-------------------------------------------

---------------- Day 2 --------------------
Message: Correct typos
Index of files changed: File3, File1
-------------------------------------------
...
...
...and so on

Обычно я начинаю свой день с git pullпросьбы и заканчиваю его git pushпросьбой. Итак, все внутри дневной записи соответствует тому, что происходит между ними. В течение дня я выполняю одну или несколько логических задач , требующих изменения нескольких файлов. Файлы, отредактированные во время этой задачи, перечислены в указателе.

Каждая из этих подзадач (здесь задача A и задача B) - это отдельные коммиты. Команда git addдобавляет файлы в список «Индекс измененных файлов». Этот процесс также называют постановкой. Команда git commitзаписывает / завершает изменения и соответствующий список индексов вместе с настраиваемым сообщением.

Помните, что вы по-прежнему изменяете только локальную копию своего репозитория, а не копию на Github. После этого, только когда вы выполните «git push», все эти записанные изменения вместе с вашими индексными файлами для каждого коммита будут зарегистрированы в основном репозитории (на Github).

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

git pull
# Make changes to these files
git add File3 File4
# Verify changes, run tests etc..
git commit -m 'Correct typos'
git push

В двух словах, git addи git commitпозволяет вам разбить изменения в основном репозитории на систематические логические подмены. Как указывалось в других ответах и ​​комментариях, их, конечно же, можно использовать гораздо больше. Однако это одно из наиболее распространенных применений и движущий принцип, лежащий в основе Git как многоступенчатой ​​системы контроля версий, в отличие от других популярных систем, таких как Svn.

Сибин Джозеф
источник
3

Чтобы расширить ответ Бена Джексона , и это нормально, давайте внимательно рассмотрим исходный вопрос. (Смотрите его ответ, чтобы узнать, зачем беспокоить типовые вопросы; это больше о том, что происходит .)

Я новичок в управлении версиями и понимаю, что «фиксация» - это, по сути, создание резервной копии при обновлении новой «текущей» версии того, над чем вы работаете.

Это не совсем так . Резервное копирование и контроль версий, безусловно, связаны - насколько сильно зависит от некоторых вещей, которые в какой-то степени являются предметом мнения, - но, безусловно, есть некоторые различия, хотя бы по назначению: резервные копии обычно предназначены для аварийного восстановления (сбой машины, пожар уничтожает все здание, включая все носители информации и т. д.). Контроль версий обычно предназначен для более детального взаимодействия и предлагает функции, которых нет в резервных копиях. Резервные копии обычно хранятся в течение некоторого времени, а затем отбрасываются как «слишком старые»: все, что имеет значение, - это более свежие резервные копии. Контроль версий обычно сохраняет каждую подтвержденную версию навсегда.

Я не понимаю, для чего постановка с практической точки зрения. Постановка чего-то, что существует только по названию, или служит определенной цели? Когда вы фиксируете, он все равно все равно фиксирует, верно?

Да и нет. Дизайн Git здесь несколько своеобразный. Существуют системы контроля версий, которые не требуют отдельного этапа подготовки. Например, Mercurial, который в остальном очень похож на Git с точки зрения использования, не требует отдельного hg addшага, кроме самого первого, который вводит полностью новый файл. В Mercurial вы используете hgкоманду, которая выбирает какую-то фиксацию, затем вы делаете свою работу, затем запускаете hg commit, и все готово. В Git вы используете git checkout, 1, затем выполняете свою работу, затем запускаете git add, а затем git commit. Почему лишняя git addступенька?

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

Изменить: я думаю, что могу запутать терминологию. «Поэтапный» файл - это то же самое, что «отслеживаемый» файл?

Нет, но они связаны. Отслеживаются файл один , который существует в индексе Git и . Чтобы правильно понять индекс, хорошо начать с понимания коммитов.


Начиная с версии Git 2.23, вы можете использовать git switchвместо git checkout. В данном конкретном случае эти две команды делают одно и то же. Новая команда существует потому, что git checkoutона перегружена множеством вещей; они были разделены на две отдельные команды git switchи git restore, чтобы было проще и безопаснее использовать Git.


Совершает

В Git коммит сохраняет полный снимок каждого файла, о котором знает Git. (О каких файлах знает Git? Мы увидим это в следующем разделе.) Эти снимки хранятся в специальной, доступной только для чтения, только Git, сжатой и дедуплицированной форме, которую, как правило, может читать только сам Git. . (В каждом коммите есть больше вещей, чем просто этот снимок, но это все, что мы здесь рассмотрим.)

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

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

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

  • В текущем коммите Git имеет замороженную на все время копию Git-ified . Вы не можете изменить эту копию (хотя вы, конечно, можете выбрать другую фиксацию или сделать новую фиксацию).

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

Другие системы контроля версий (включая Mercurial, упомянутые выше) останавливаются на этих двух копиях. Вы просто изменяете свою копию рабочего дерева, а затем фиксируете. Гит ... нет.

Индекс

Между этими двумя копиями Git хранит третью копию 2 каждого файла. Эта третья копия находится в замороженном формате , но, в отличие от замороженной копии в фиксации, вы можете изменить ее. Чтобы изменить это, вы используете git add.

Команда git addозначает, что индексная копия файла должна соответствовать копии рабочего дерева . То есть вы говорите Git: замените замороженный формат, дедуплицированную копию, которая сейчас находится в индексе, сжав мою обновленную копию рабочего дерева, дедуплицируя ее и подготовив ее к замораживанию в новом коммите. Если вы не используете git add, индекс по-прежнему содержит копию в замороженном формате из текущего коммита.

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

Это также объясняет, что такое неотслеживаемые файлы . Неотслеживаемый файл представляет собой файл , который находится в рабочем дереве , но не в индексе Git и сейчас . Неважно, как файл оказался в таком состоянии. Может быть, вы скопировали его из другого места на своем компьютере в свое рабочее дерево. Может быть, вы создали его здесь свежим. Возможно, в индексе Git была копия, но вы удалили эту копию с помощью git rm --cached. Так или иначе, в вашем рабочем дереве есть копия, но ее нет в индексе Git. Если вы сделаете новую фиксацию сейчас, этого файла не будет в новой фиксации.

Обратите внимание, что git checkoutизначально индекс Git заполняется из проверенной фиксации. Итак, индекс начинает соответствовать фиксации. Git также заполняет ваше рабочее дерево из того же источника. Итак, изначально все три совпадают. Когда вы меняете файлы в своем рабочем дереве и git addих, ну, теперь индекс и ваше рабочее дерево совпадают. Затем вы запускаете, git commitи Git делает новую фиксацию из индекса, и теперь все три снова совпадают.

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

Вот и все, но это все еще довольно сложно! Это особенно сложно, потому что нет простого способа точно увидеть, что находится в индексе Git. 3 Но это команда Git , которая говорит вам , что происходит, таким образом , что это очень полезно, и что команда git status.


2 Технически это вообще не копия . Вместо этого это ссылка на файл Git-ified, предварительно дедуплицированный и все такое. Здесь также есть больше вещей, таких как режим, имя файла, номер стадии и некоторые данные кеша, чтобы Git работал быстрее. Но если вы не начнете работать с некоторыми низкоуровневыми командами Git - git ls-files --stageи git update-indexв частности - вы можете просто думать об этом как о копии.

3 Команда git ls-files --stageпокажет вам имена и промежуточные номера каждого файла в индексе Git, но обычно это все равно не очень полезно.


git status

На git statusсамом деле команда работает, выполняя git diffза вас две отдельные команды (а также делая некоторые другие полезные вещи, например, сообщая вам, в какой ветке вы находитесь).

Первый git diffсравнивает текущую фиксацию - которая, помните, заморожена на все время - с тем, что есть в индексе Git. Для одинаковых файлов Git вообще ничего не скажет. Для разных файлов Git сообщит вам, что этот файл подготовлен для фиксации . Это включает в себя все новые файлы, если обязательство не sub.pyв нем, но индекс действительно есть sub.pyв нем, то добавляется, и этот файл любые удаленные файлы, которые были (и есть) в фиксации , но не в индекса уже нет ( git rm, возможно).

Второй git diffсравнивает все файлы в индексе Git с файлами в вашем рабочем дереве. Для одинаковых файлов Git вообще ничего не говорит. Для разных файлов Git сообщит вам, что этот файл не предназначен для фиксации . В отличие от первого сравнения, этот конкретный список не включает файлы, которые являются полностью новыми: если файл untrackedсуществует в вашем рабочем дереве, но не в индексе Git, Git просто добавляет его в список неотслеживаемых файлов . 4

В конце, собрав эти неотслеживаемые файлы в списке, также git statusбудут объявлены имена этих файлов, но есть специальное исключение: если имя файла указано в .gitignoreфайле, этот последний список будет подавлен. Обратите внимание, что перечисление отслеживаемого файла - того, что находится в индексе Git - .gitignoreздесь не имеет никакого эффекта : файл находится в индексе, поэтому он сравнивается и фиксируется, даже если он указан в .gitignore. Файл игнорирования подавляет только жалобы на "неотслеживаемый файл". 5


4 При использовании краткой версии git status- git status -s- неотслеживаемые файлы не отделены друг от друга, но принцип тот же. git statusПодобное накопление файлов также позволяет суммировать имена нескольких неотслеживаемых файлов, иногда просто печатая имя каталога. Чтобы получить полный список, используйте git status -uallили git status -u.

5 Листинг файла также заставляет массово добавлять множество файловых операций, таких как неотслеживаемый файл git add .или git add *пропускать его. Эта часть становится немного сложнее, так как вы можете использовать git add --forceдля добавления файла, который обычно пропускается. Есть несколько других обычно незначительных особых случаев, все из которых складываются в это: файл .gitignoreможет называться более правильно .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themили что-то столь же громоздкое. Но это слишком смешно, так .gitignoreоно и есть.


git add -u, git commit -aи т. д.

Здесь есть несколько удобных ярлыков:

  • git add .добавит все обновленные файлы в текущий каталог и любой подкаталог. Это соблюдается .gitignore, поэтому, если файл, который в настоящее время не отслеживается, не получил жалобу git status, он не будет добавлен автоматически.

  • git add -uавтоматически добавит все обновленные файлы в любое место вашего рабочего дерева . 6 Это влияет только на отслеживаемые файлы. Обратите внимание, что если вы удалили копию рабочего дерева, это также удалит копию индекса ( git addэто как часть его соответствия индексу с деревом работы ).

  • git add -Aэто похоже на бег git add .с верхнего уровня вашего рабочего дерева (но см. сноску 6).

Помимо этого, вы можете бегать git commit -a, что примерно эквивалентно 7 бегу git add -uи затем git commit. То есть это дает вам то же поведение, которое удобно в Mercurial.

Я обычно не рекомендую использовать этот git commit -aшаблон: я считаю, что его лучше использовать git statusпочаще, внимательно посмотрите на вывод и, если статус не соответствует вашим ожиданиям, выясните, почему это так. При git commit -aиспользовании слишком легко случайно изменить файл и зафиксировать изменение, которое вы не собирались фиксировать. Но это в основном вопрос вкуса / мнения.


6 Если ваша версия Git предшествует Git 2.0, будьте осторожны: git add -uработает только с текущим каталогом и подкаталогами, поэтому вы должны сначала подняться на верхний уровень своего рабочего дерева. У git add -Aварианта есть аналогичная проблема.

7 Я говорю примерно эквивалентно, потому что на git commit -aсамом деле работает путем создания дополнительного индекса и использования этого другого индекса для фиксации. Если фиксация работает , вы получите тот же эффект, что и выполнение git add -u && git commit. Если фиксация не работает - если вы заставляете Git пропускать фиксацию любым из множества способов, которыми вы можете это сделать, - после этого никакие файлы не обрабатываются git add, потому что Git выбрасывает временный дополнительный индекс и возвращается к использованию основного индекса .

Если вы воспользуетесь git commit --onlyздесь , возникнут дополнительные сложности . В этом случае Git создает третий индекс, и все становится очень сложно, особенно если вы используете перехватчики перед фиксацией. Это еще одна причина использовать отдельные git addоперации.

торек
источник
2

Промежуточная область помогает нам создавать коммиты с большей гибкостью. Под обработкой я подразумеваю разбиение коммитов на логические единицы. Это очень важно, если вы хотите поддерживать программное обеспечение. Самый очевидный способ добиться этого:

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

Вы можете найти больше примеров здесь: Использование индекса

И что самое приятное, преимущества не ограничиваются этим списком рабочих процессов. Если все же появится уникальный рабочий процесс, вы можете быть почти уверены, что промежуточная область вам поможет.

Андрей Несин
источник
1

Я вижу смысл в использовании stage для уменьшения размера коммитов, о чем упоминали @Ben Jackson и @Tapashee Tabassum Urmi, и иногда я использую его для этой цели, но в основном я использую его для увеличения размера моих коммитов! вот моя точка зрения:

Скажем, я хочу добавить небольшую функцию, которая требует нескольких небольших шагов. Я не вижу смысла в том, чтобы делать отдельные коммиты для небольших шагов и заполнять мою временную шкалу. Однако я хочу сохранить каждый шаг и вернуться при необходимости,

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

Я вижу другие способы сделать это (упрощая историю git), которые вы можете использовать в зависимости от ваших предпочтений:

  1. git alternate (который изменяет вашу последнюю фиксацию), чего вы не хотите для этой конкретной цели (я вижу это в основном как плохую фиксацию, а затем ее исправление)
  2. git rebase, о котором думают позже и могут вызвать серьезные проблемы для вас и других, кто использует ваш репозиторий.
  3. создать временную ветку, объединить, а затем удалить ее (что также является хорошим вариантом, требует больше шагов, но дает вам больше контроля)
Ali80
источник
0

Это похоже на флажок, который дает возможность выбирать, какие файлы фиксировать.

например, если я редактировал fileA.txtи. fileB.txtНо я хочу зафиксировать изменения fileA.txtтолько. потому что я еще не закончил fileB.txt.

Я могу просто использовать git add fileA.txtи зафиксировать использование git commit -m "changed fileA.txt"и продолжить работу сfileB.txt а после завершения я могу fileB.txtлегко фиксировать

Рамун
источник