Чтобы немного рассказать о ответе RomainValeri , запомните эти вещи о Git:
Git хранилищах коммитов . «Истинное имя» любого коммита - это его sh ID, эта большая уродливая строка букв и цифр, которую git log
печатает после слова «коммит». (Вы можете сокращать их, если хотите, до определенного момента.) Эти выглядят случайными, но на самом деле вовсе не случайны.
Каждый коммит содержит полный и полный снимок каждого файла. 1 Он также содержит обратную ссылку на свой родительский коммит, который мы рисуем, когда делаем эти A--B--C
виды диаграмм. Мне нравится рисовать их слева направо для StackOverflow и по вертикали с новыми коммитами к вершине - как их git log --graph
dr aws - в некоторых других ситуациях. Вы можете и должны практиковать рисование этих вещей.
Файлы, которые находятся внутри каждого коммита, хранятся в специальном, только для чтения, Git -только сжатом - иногда сильно сжатом - формат. Git сама по себе является единственной программой, которая может работать с ними. Кроме того, они доступны только для чтения: ничто, даже не вы, даже Git, не могут изменить их. Как только вы делаете коммит, этот коммит замораживается навсегда. Если этот коммит плохой - если вам нужно его заменить - вам придется вместо этого делать новый и улучшенный коммит, отбрасывая старый плохой коммит в сторону. То есть вы можете начать с:
A--B--C
, а затем решить, что C
- это плохо, и использовать git commit --amend
, чтобы исправить это. Это на самом деле не меняет C
вообще, потому что не может. Вместо этого он отбрасывает C
в сторону и делает новый и улучшенный коммит: C'
. Родитель C'
совпадает с родителем C
:
C
/
A--B--C'
Старый коммит все еще там, он просто убран, чтобы освободить место для C'
.
Как только вы отключите этот материал - который фиксирует файлы хранилища в режиме только для чтения и ссылается на предыдущие коммиты - вы поймете две вещи:
Как можно мы можем сделать какую-нибудь работу? Файлы внутри коммитов доступны только для чтения, заморожены навсегда и абсолютно нечитаемы для всего , но Git. Нам нужны обычные файлы, которые мы можем изменить .
Как нам найти вправо commits?
Ответ на вопрос № 1 заключается в том, что Git предоставляет нам место для выполнения нашей работы, которое Git вызывает рабочее дерево или рабочее дерево . (Старые версии Git раньше называли этот рабочий каталог или аналогичными именами, но это слишком легко спутать со строкой рабочего каталога, которую печатает команда pwd
, так что теперь это рабочее дерево или рабочее дерево.) Git скопирует файлы из коммитов в эту рабочую область. 2
Ответ на вопрос №2 - где приходят имена ветвей . Каждое имя ветки хранит идентификатор ha sh всего one commit. Когда мы рисуем коммиты следующим образом:
A--B--C <-- branch
или:
C <-- branch
|
B
|
A
мы рисуем то, что Git действительно имеет внутренне: имя, например branch
, , указывающее в (содержащий га sh ID) коммит, например C
. Между тем сам коммит C
имеет идентификатор ha sh - или указывает на - его родительский коммит B
и т. Д.
Вы можете в любое время создать новое имя ветки, выбрав любой существующий коммит и сказав: Сделать это новое имя ветви указанным на этот существующий коммит. Вы делаете это с помощью команды git branch
. Таким образом, чтобы указать какое-то новое имя, например hotfix
, указать на существующий коммит, вы можете выполнить:
git branch hotfix <hash>
, где <hash>
- идентификатор ha sh, полученный, возможно, путем вырезания и паста га sh ID, напечатанная git log
. Теперь у вас есть:
A--B <-- hotfix
\
C <-- branch
Когда вы запускаете git checkout <em>name</em>
, Git использует name для поиска коммита. Это коммит , который появляется в вашем рабочем дереве (и индекс; см. Сноску 2), так что вы можете работать с ним. Когда вы запускаете git commit
, Git делает * новый коммит (из того, что находится в вашем индексе на тот момент), который выбирает случайный га sh ID. 3
Это также, как вы делаете новые ветви в целом. Просто вы начинаете с:
A--B--C <-- master
и затем создаете новое имя, например develop
, которое указывает на фиксацию C
:
A--B--C <-- develop, master
Теперь нам нужно знать, какое имя ветви мы используем , поэтому на чертежах такого типа мы можем добавить специальное имя HEAD
в верхнем регистре и прикрепить его к имени ветви, например:
A--B--C <-- develop (HEAD), master
Если мы сейчас создадим новый коммит, то Git сделает запись ha sh ID нового коммита в имя, к которому прикреплен HEAD
, что даст нам:
A--B--C <-- master
\
D <-- develop (HEAD)
Если мы сделаем имя hotfix
точкой для фиксации B
, а затем сделаем git checkout hotfix
, мы получим:
A--B <-- hotfix (HEAD)
\
C <-- master
\
D <-- develop
Commit B
в нашей работе область, чтобы мы могли изменить файлы (и в нашем индексе, чтобы мы могли зафиксировать). Мы выполняем свою работу, git add
файлы (чтобы скопировать их обратно в индекс - см. Сноску 2), и git commit
, и Git делает новый коммит, который получает новый уникальный большой уродливый ha sh ID, но мы просто назовем его E
, а затем Git записывает E
ha sh ID в имя hotfix
, потому что именно там присоединено HEAD
:
A--B--E <-- hotfix (HEAD)
\
C <-- master
\
D <-- develop
Обратите внимание: что бы мы ни делали, мы всегда добавляем новые коммиты в репозиторий. Существующие коммиты не меняются. Они не могут! Мы просто добавляем новые единицы.
имена ветвей перемещаются! Они всегда указывают на последний коммит (на ветке). Фактически, это одно из определений «ветви» в Git: это последний коммит. Или иногда, когда мы говорим «ветвь», мы имеем в виду само имя , или иногда мы имеем в виду некоторый набор коммитов, ведущих к последнему коммиту и включающий его . Слово ответвление в Git является двусмысленным. Это помогает в первую очередь думать о коммитах . Добавьте имена филиалов позже. Используйте имена, чтобы найти коммитов; это то, что делает Git.
1 Технически, каждый из них содержит только снимок файлов, которые он содержит. Рассмотрим, например, что произойдет, если вы сделаете коммит C
после помещения в него совершенно нового файла, которого не было, когда вы делали коммит B
. Существующий коммит B
не может измениться, и у него нет этого файла. Таким образом, C
и последующие коммиты будут иметь файл; B
не будет иметь файл. Каждый коммит будет иметь полный и полный снимок всех файлов, которые у него есть.
Еще один способ думать об этом: коммит - это замороженная на все время копия из некоторый набор файлов. Какие копии файлов go в новый коммит? Ответ хранится в индексе Git; см. сноску 2.
2 Тогда вы можете подумать, что Git сделает новые коммиты из рабочей области, но это не так. Вместо этого Git делает новые коммиты из того, что есть в Git в index . Индекс, который Git также называет промежуточной областью или (редко в наши дни) кеш , представляет собой довольно большую топику c, и мы не будем охватывать ее должным образом Вот. Это важно понимать, чтобы эффективно использовать Git, но смотрите другие публикации или статьи об этом. Однако вы можете думать, что он содержит третью копию каждого файла. Когда вы запускаете git add
, Git копирует копию рабочего дерева файла, который вы git add
-ing, в индексную копию. Когда вы запускаете git checkout
, Git копирует замороженную копию каждого файла из существующего коммита, который вы проверяете, в индексную копию, а также в рабочее дерево. Это не на 100% правильно, но достаточно для начала.
3 Этот идентификатор ha sh зависит от:
- всего снимка;
- родитель коммит ha sh ID;
- ваше имя и адрес электронной почты и т. Д., Как записано в коммите;
- ваше сообщение журнала, как записано в коммите; и
- даже дата и время, когда вы делаете обязательство.
Если вы заранее знаете все эти вещи, вы можете предсказать, что ha sh ID вашего коммита будет. Но никто не знает, когда они собираются сделать новый коммит, пока они на самом деле не сделают это. Не стоит предварительно рассчитывать га sh, затем ждать точную правильную секунду и запускать git commit
. Просто сделайте коммит и выясните, что он получил. И если вы сделаете это снова, даже если все остальное будет таким же, на этот раз он получит другой га sh, потому что он больше не тот же время .
(Это однако, это приведет к определенному типу странностей, если вы используете программу , чтобы очень быстро выполнять одинаковые коммиты на двух разных именах веток. один и тот же родитель, они получают один и тот же идентификатор ha sh. Он не сломан или ошибка , но эти идентичные коммиты получают идентичные идентификаторы ha sh, так что имена двух ветвей указать на тот же коммит! Но именно с этого мы и начинаем, когда делаем это.)