Содержание

githowto.com

Первоначальная настройка

# Кто автор коммитов в этом экземпляре гита - имя и адрес
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 <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   hello.html
 
no changes added to commit (use "git add" and/or "git commit -a")
# Можно откатить файл обратно
git restore hello.html
# А можно добавить его в коммит (проиндексировать)
git add hello.html
# После добавления статус показывает, что файл проиндексирован
git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   hello.html
# Можно откатить индексацию (файл вернётся к добавленному, но не индексированному состоянию)
git restore --staged hello.html
 
# Отдельный шаг индексации позволяет разделять добавленные файлы на разные коммиты,
# чтобы не связанные между собой изменения не попадали в один коммит.
# К примеру, изменены 3 файла - a.html, b.html и c.html, и файл c.html не связан с другими.
git add a.html
git add b.html
git commit -m "Changes for a and b"
git add c.html
git commit -m "Unrelated change to c"

Коммит

Если не задать параметр -m для git commit, то откроется редактор. Он задаётся в одной из настроек

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 <you@example.com>
Date:   Tue Jan 21 16:26:12 2025 +0300
 
    Added HTML header
 
commit 88622c4f43f5caa7a891542f09564c68fc362682
Author: Your Name <you@example.com>
Date:   Tue Jan 21 16:15:07 2025 +0300
 
    Added H1 tag
 
commit 1a3a27e686a3baeec77a9f44a65471b88495a3e1
Author: unknown <user@DESKTOP.workgroup>
Date:   Tue Jan 21 15:17:07 2025 +0300
 
    Initial Commit
 
# Вид настраивается, например, однострочный вид
git log --pretty=oneline
204912e8c6cf87c0743ffa5646f4c7438d6ebeaa (HEAD -> main) Added HTML header
88622c4f43f5caa7a891542f09564c68fc362682 Added H1 tag
1a3a27e686a3baeec77a9f44a65471b88495a3e1 Initial Commit
# Ещё варианты
git log --oneline --max-count=2 # 2 последних коммита
git log --oneline --since="5 minutes ago" # за последние 5 минут
git log --oneline --until="5 minutes ago" # кроме последних 5 минут
git log --oneline --author="Your Name" # фильтр по автору
git log --all --pretty=format:"%h %cd %s (%an)" --since="7 days ago" # Укороченный хэш, время, коммент, автор
204912e Tue Jan 21 16:26:12 2025 +0300 Added HTML header (Your Name)
88622c4 Tue Jan 21 16:15:07 2025 +0300 Added H1 tag (Your Name)
1a3a27e Tue Jan 21 15:17:07 2025 +0300 Initial Commit (unknown)
git log --pretty=format:"%h %ad | %s%d [%an]" --date=short # Ещё вариант
204912e 2025-01-21 | Added HTML header (HEAD -> main) [Your Name]
88622c4 2025-01-21 | Added H1 tag [Your Name]
1a3a27e 2025-01-21 | Initial Commit [unknown]
 
# Сделать последний вариант pretty format по умолчанию
git config --global format.pretty '%h %ad | %s%d [%an]'
git config --global log.date short

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 <new-branch-name>
 
Or undo this operation with:
 
  git switch -
 
Turn off this advice by setting config variable advice.detachedHead to false
 
HEAD is now at 1a3a27e Initial Commit
 
# hello.html выглядит как в самом начале
cat .\hello.html
Hello, World
# Вернуться на последнюю версию в ветке main
git switch main
# hello.html теперь опять со всеми изменениями
cat .\hello.html
<html>
  <body>
    <h1>Hello, World!</h1>
  </body>
</html>

Создание тэгов версий

Работать с хэшами коммитов неудобно. Можно задать человеческий тэг, тогда можно будет сослаться на него.

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 <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   hello.html
 
no changes added to commit (use "git add" and/or "git commit -a")
 
# Откатить изменения
git restore hello.html
 
# Теперь чисто, изменений нет
git status
On branch main
nothing to commit, working tree clean

После индексации

Отмена изменений в файле, добавленном в коммит

git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   hello.html
 
# Убрать файл из индекса (коммита). Файл при этом остался изменённым!
git restore --staged hello.html
 
# Дальше действовать как до индексации
git restore hello.html

Отмена коммитов

Есть несколько способов откатить кривой коммит.

Новый коммит, отменяющий предыдущий

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 <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    hello.html
        deleted:    style.css
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        css/
        index.html
 
no changes added to commit (use "git add" and/or "git commit -a")
 
# Перенести файлы корректно
mkdir css
git mv style.css css/style.css
git mv hello.html index.html
git status
 
# Теперь статус в порядке
git status
On branch style
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    style.css -> css/style.css
        renamed:    hello.html -> index.html
 
# Чтобы посмотреть изменения файла до переименования, добавить параметр --follow
git log css/style.css
4a6c610 2025-01-22 | Renamed hello.html; moved style.css (HEAD -> style) [Your Name]
 
git log --follow css/style.css
4a6c610 2025-01-22 | Renamed hello.html; moved style.css (HEAD -> style) [Your Name]
aa9e5d8 2025-01-22 | Add css [Your Name]

Просмотр отличающихся веток

# --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 сообщит о конфликте и попросит разрешить его вручную.

Файл index.html ветки style

<!-- Author: Alexander Shvets (alex@githowto.com) -->
<html>
  <head>
    <link type="text/css" rel="stylesheet" media="all" href="style.css" />
  </head>
  <body>
    <h1>Hello, World!</h1>
  </body>
</html>

Файл hello.html ветки main

<!-- Author: Alexander Shvets (alex@githowto.com) -->
<html>
  <head>
    <title>Hello World Page</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Let's learn Git together.</p>
  </body>
</html>

После попытки слива веток

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 выглядит так:

<!-- Author: Alexander Shvets (alex@githowto.com) -->
<html>
  <head>
<<<<<<< HEAD:index.html
    <link type="text/css" rel="stylesheet" media="all" href="style.css" />
=======
    <title>Hello World Page</title>
>>>>>>> main:hello.html
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Let's learn Git together.</p>
  </body>
</html>

Часть между <<<<<<< и >>>>>>> является конфликтом. Верхняя часть соответствует ветке style, которая является текущей веткой (или HEAD) репозитория. Нижняя часть соответствует изменениям из ветки main. Видно, что часть с <p>Let's learn Git together.</p> 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 нет графических инструментов слияния, но можно настроить сторонние.

rebase вместо merge