====== githowto.com ======
===== Первоначальная настройка =====
Let's learn Git together. Let's learn Git together.
# Кто автор коммитов в этом экземпляре гита - имя и адрес
git config --global user.name "Your Name"
git config --global user.email "your_email@whatever.com"
# Задать main веткой по умолчанию
git config --global init.defaultBranch main
# Корректно обрабатывать окончания строк (для Windows)
git config --global core.autocrlf true
git config --global core.safecrlf warn
===== Создание проекта =====
# Создать новый репозиторий (можно уже после создания файлов проекта в каталоге)
git init # создастся подкаталог .git
# Добавить файл и закоммитить с комментарием
git add hello.html
git commit -m "Initial Commit"
# Проверить состояние репозитория
git status
On branch main # Сейчас на ветке main
nothing to commit, working tree clean # Нет файлов, ожидающих записи, репозиторий равен текущему состоянию каталога
# Переименовать master в main, если гит старый и команда, делающая ветку main основной, не сработала
git branch -m master main
===== Внесение изменений =====
# Если что-то добавить в файл и посмотреть состояние репозитория:
git status
On branch main
Changes not staged for commit:
(use "git add
===== Коммит =====
Если не задать параметр -m для git commit, то откроется редактор. Он задаётся в одной из настроек
* переменная среды GIT_EDITOR
* параметр конфигурации core.editor
* переменная среды VISUAL
* переменная среды EDITOR
git config --global core.editor emacs # emacs
git config --global core.editor "code --wait" # VSCode
Гит фокусируется на изменениях в файле, а не на самом файле. К примеру, если одно изменение в файле было проиндексировано, а второе - нет, то
при коммите добавится только проиндексированное изменение. Второе нужно будет сначала проиндексировать и потом коммитить.
===== История проекта =====
git log
git log
commit 204912e8c6cf87c0743ffa5646f4c7438d6ebeaa (HEAD -> main)
Author: Your Name
https://git-scm.com/docs/git-log
===== Откат на старые версии =====
# Откатиться на самый первый коммит (достаточно первых 7 символов хэша как указатель)
git checkout 1a3a27e
Note: switching to '1a3a27e'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c
===== Создание тэгов версий =====
Работать с хэшами коммитов неудобно. Можно задать человеческий тэг, тогда можно будет сослаться на него.
Hello, World!
git tag v1 # задать тэг текущей версии
git log -n 1
204912e 2025-01-21 | Added HTML header (HEAD -> main, tag: v1) [Your Name]
### Задать тэг предыдущей версии
# Переключиться на предыдущую версию
git checkout v1^ # или git checkout v1~1
git tag v1-beta
git log -n 1
88622c4 2025-01-21 | Added H1 tag (HEAD, tag: v1-beta) [Your Name]
# Теперь можно переключаться по тэгам
git checkout v1
Previous HEAD position was 88622c4 Added H1 tag
HEAD is now at 204912e Added HTML header
git checkout v1-beta
Previous HEAD position was 204912e Added HTML header
HEAD is now at 88622c4 Added H1 tag
# Список тэгов
git tag
v1
v1-beta
# Тэги в логе
git log main --all
204912e 2025-01-21 | Added HTML header (tag: v1, main) [Your Name]
88622c4 2025-01-21 | Added H1 tag (HEAD, tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
===== Отмена локальных изменений =====
==== До индексации ====
Чтобы откатить изменения в файле, ещё не добавленном в коммит:
# Вот есть изменения, которые надо отменить
git status
On branch main
Changes not staged for commit:
(use "git add
==== После индексации ====
Отмена изменений в файле, добавленном в коммит
git status
On branch main
Changes to be committed:
(use "git restore --staged
===== Отмена коммитов =====
Есть несколько способов откатить кривой коммит.
==== Новый коммит, отменяющий предыдущий ====
''revert'' - история коммитов сохраняется: есть и кривой коммит, и отменяющий его.
git revert HEAD
[main 20dea26] Revert "Wrong commit"
1 file changed, 1 deletion(-)
PS C:\temp\githowto\repositories\work> git log
20dea26 2025-01-21 | Revert "Wrong commit" (HEAD -> main) [Your Name]
c90b848 2025-01-21 | Wrong commit [Your Name]
204912e 2025-01-21 | Added HTML header (tag: v1) [Your Name]
88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
==== Удаление коммита из истории ====
''reset''
* Изменит текущую ветку, чтобы она указывала на выбранный коммит.
* Опционально сбросит область подготовки до соответствия с указанным коммитом.
* Опционально сбросит рабочую директорию до соответствия с указанным коммитом.
# Сбросит ветку до коммита v1 (здесь: бесследно удалится 2 последних коммита, откатятся изменения в файлах)
git reset --hard v1
# Лог чист
git log
204912e 2025-01-21 | Added HTML header (HEAD -> main, tag: v1) [Your Name]
88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
# Тем не менее, коммиты ещё есть в списке всех, просто их нет в ветке main
git log --all
20dea26 2025-01-21 | Revert "Wrong commit" (tag: wrong) [Your Name]
c90b848 2025-01-21 | Wrong commit [Your Name]
204912e 2025-01-21 | Added HTML header (HEAD -> main, tag: v1) [Your Name]
88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
На удалённые коммиты можно сослаться либо по хэшу, либо по тэгу, если он есть. Удалённые коммиты, на которые нет ссылок или тэгов, существуют до запуска сборщика мусора.
Здесь у одного из удалённых коммитов есть тэг, его надо удалить, чтобы сборщик мусора смог на нём отработать.
git tag -d wrong
Deleted tag 'wrong' (was 20dea26)
# Теперь в списке всех коммитов также нет ошибочных
git log --all
204912e 2025-01-21 | Added HTML header (HEAD -> main, tag: v1) [Your Name]
88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
git commit -m "Add copyright"
[main 3d91d05] Add copyright
1 file changed, 1 insertion(+)
git log
3d91d05 2025-01-22 | Add copyright (HEAD -> main) [Your Name]
204912e 2025-01-21 | Added HTML header (tag: v1) [Your Name]
88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
git add .\hello.html
git commit --amend -m "Add copyright with email"
[main b0d54f0] Add copyright with email
Date: Wed Jan 22 13:41:15 2025 +0300
1 file changed, 1 insertion(+)
# Последний коммит заменён на новый
git log
b0d54f0 2025-01-22 | Add copyright with email (HEAD -> main) [Your Name]
204912e 2025-01-21 | Added HTML header (tag: v1) [Your Name]
88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
Этого же результата можно достичь путем сброса последнего коммита в ветке и повторного коммита новых изменений.
===== Создание веток и переключение между ними =====
# Создать ветку style
git switch -c style
Switched to a new branch 'style'
# Старый способ (не рекомендуется)
git checkout -b style
git status
On branch style
nothing to commit, working tree clean
# Переключение между ветками (hello.html будет разным после коммитов в ветку style)
git switch main
cat hello.html
git switch style
cat hello.html
===== Перемещение файлов =====
Если тупо переименовать или перенести файлы в репозитории, то они будут помечены как удалённые и созданные заново. Связи между этими файлами нет и в истории гита нельзя будет отследить, что откуда взялось. Гит иногда догадывается, что файл был переименован, но рассчитывать на это нельзя.
# После обычного переименования/перемещения файлов руками
git status
On branch style
Changes not staged for commit:
(use "git add/rm
===== Просмотр отличающихся веток =====
# --all - показать все ветки (по умолчанию показывается текущая ветка)
# --graph - дерево в виде простых текстовых линий
git log --all --graph
* b022e6a 2025-01-22 | Add README (HEAD -> main) [Your Name]
| * 4a6c610 2025-01-22 | Renamed hello.html; moved style.css (style) [Your Name]
| * 632e4e7 2025-01-22 | Include css into hello.html [Your Name]
| * aa9e5d8 2025-01-22 | Add css [Your Name]
|/
* b0d54f0 2025-01-22 | Add copyright with email [Your Name]
* 204912e 2025-01-21 | Added HTML header (tag: v1) [Your Name]
* 88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
* 1a3a27e 2025-01-21 | Initial Commit [unknown]
===== Слияние веток =====
Слить ветку main в style
# Перейти в ветку style
git switch style
Switched to branch 'style'
# Слить ветки
git merge main
Merge made by the 'ort' strategy.
README | 1 +
1 file changed, 1 insertion(+)
create mode 100644 README
# График коммитов
git log --all --graph
* 98277e2 2025-01-22 | Merge branch 'main' into style (HEAD -> style) [Your Name]
|\
| * b022e6a 2025-01-22 | Add README (main) [Your Name]
* | 4a6c610 2025-01-22 | Renamed hello.html; moved style.css [Your Name]
* | 632e4e7 2025-01-22 | Include css into hello.html [Your Name]
* | aa9e5d8 2025-01-22 | Add css [Your Name]
|/
* b0d54f0 2025-01-22 | Add copyright with email [Your Name]
* 204912e 2025-01-21 | Added HTML header (tag: v1) [Your Name]
* 88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
* 1a3a27e 2025-01-21 | Initial Commit [unknown]
Путем периодического слияния ветки main с веткой style происходит перенос изменений из main и поддерживается совместимость изменений style с изменениями в основной ветке. Однако, это делает графики коммитов уродливыми. Альтернатива слиянию - перебазирование (rebase).
===== Конфликтующие изменения в ветках и разрешение конфликта =====
Если в обеих ветках была изменена одна и та же часть файла, Git может не справиться с автоматическим слиянием изменений. В этом случае Git сообщит о конфликте и попросит разрешить его вручную.
Hello, World!
Hello, World!
git switch style
Switched to branch 'style'
git merge main
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Файл index.html выглядит так:
<<<<<<< HEAD:index.html
=======
Часть между ''%%<<<<<<<%%'' и ''%%>>>>>>>%%'' является конфликтом. Верхняя часть соответствует ветке style, которая является текущей веткой (или HEAD) репозитория. Нижняя часть соответствует изменениям из ветки main. Видно, что часть с ''%%Hello, World!
Let's learn Git together.
%%'' git сделал сам.
# Можно отменить слияние веток
git merge --abort
# А можно отредактировать index.html и закоммитить его
git add index.html
git commit -m "Resolved merge conflict"
git status
On branch style
nothing to commit, working tree clean
git log --all --graph
* e71d981 2025-01-22 | Resolved merge conflict (HEAD -> style) [Your Name]
|\
| * 9e1309b 2025-01-22 | Added meta title (main) [Your Name]
* | 98277e2 2025-01-22 | Merge branch 'main' into style [Your Name]
|\|
| * b022e6a 2025-01-22 | Add README [Your Name]
* | 4a6c610 2025-01-22 | Renamed hello.html; moved style.css [Your Name]
* | 632e4e7 2025-01-22 | Include css into hello.html [Your Name]
* | aa9e5d8 2025-01-22 | Add css [Your Name]
|/
* b0d54f0 2025-01-22 | Add copyright with email [Your Name]
* 204912e 2025-01-21 | Added HTML header (tag: v1) [Your Name]
* 88622c4 2025-01-21 | Added H1 tag (tag: v1-beta) [Your Name]
* 1a3a27e 2025-01-21 | Initial Commit [unknown]
У Git нет графических инструментов слияния, но [[https://stackoverflow.com/questions/137102/whats-the-best-visual-merge-tool-for-git|можно настроить сторонние]].
===== rebase вместо merge =====