С чего начать Первые шаги трек · 15 ступеней
Linux Labs

git: история правок, откат и удалённый репозиторий

13 мин git 2.x

Система контроля версий: кто и что поменял в файлах, как вернуть рабочую версию и обменяться правками.

Шпаргалка

12 команд Версия для печати
  • git status что изменено, на какой ветке
  • git diff незакоммиченные правки
  • git add -A ; git commit -m "msg" зафиксировать
  • git log --oneline -10 история кратко
  • git log -p -- site.conf история файла с правками
  • git blame site.conf кто и когда менял каждую строку
  • git restore site.conf выбросить незакоммиченную правку
  • git revert --no-edit <коммит> отменить коммит новым коммитом
  • git fetch ; git status что нового в удалённом
  • git pull забрать и влить
  • git push отправить свои коммиты
  • git stash ; git stash pop отложить правку и вернуть

Обзор

git — система контроля версий. Репозиторий — каталог .git рядом с рабочими файлами: в нём вся история. Коммит — снимок файлов с автором, датой, сообщением и хэшем (c9ab4a3). Админу git нужен для конфигов: каталог вроде /etc/nginx под git показывает, кто и что поменял, и возвращает рабочую версию одной командой.

Цикл работы: правка → git add → git commit → git push.

bash
git init -b main                  # новый репозиторий, ветка main
git clone /srv/git/site.git       # копия удалённого репозитория
git add site.conf                 # правку — в следующий коммит
git commit -m "site: listen 8080" # зафиксировать

Без -b main git 2.43 называет первую ветку master и печатает длинную подсказку hint: Using 'master' as the name for the initial branch.

Ключи

20

Ниже — основные подкоманды git; полный список — man git, git help <команда>.

Ключ Что делает Пример
status Что изменено, что в индексе, на какой ветке. git status
add Добавить изменения в индекс (staged) перед коммитом. git add -A
commit Зафиксировать изменения из индекса как новый коммит. git commit -m "message"
push Отправить локальные коммиты в удалённый репозиторий. git push
pull Забрать и влить изменения из удалённого репозитория. git pull
log Показать историю коммитов. git log --oneline -10
diff Что изменено в рабочих файлах с последнего коммита; с двумя коммитами — разница между ними. git diff HEAD origin/main
fetch Забрать новые коммиты из удалённого репозитория, не трогая рабочие файлы. git fetch
clone Копия репозитория с рабочими файлами; удалённый получает имя origin. git clone /srv/git/site.git
switch -c <name> Переключиться на ветку; с -c — создать и сразу переключиться. git switch -c feature
merge Влить указанную ветку в текущую. git merge feature
restore Отменить правки файла, вернув его как в HEAD (или индексе с --staged). git restore file
revert Создать новый коммит, отменяющий указанный, — безопасно для общей истории. git revert <commit>
stash Отложить незакоммиченные изменения, чтобы вернуть их позже. git stash
reflog Журнал перемещений HEAD — помогает найти «потерянные» коммиты. git reflog
show Один коммит с его правкой; <коммит>:<файл> — файл в том виде, каким он был в коммите. git show HEAD~2:site.conf
blame Кто и в каком коммите последним менял каждую строку файла. git blame site.conf
init Новый репозиторий; -b main — имя первой ветки, --bare — без рабочих файлов. git init --bare -b main /srv/git/site.git
reset --hard|--soft Переставить HEAD; --hard затирает незакоммиченные изменения без возврата. опасно git reset --hard origin/main
clean -fd Удалить неотслеживаемые файлы и каталоги без возможности восстановления. опасно git clean -fd

Ничего не найдено. Попробуй запрос короче.

Рецепты

7

История: log -p, show, blame

Репозиторий /etc/demo с тремя коммитами двух авторов:

text
student@node1:/etc/demo$ sudo git log --format='%h %an %ad %s' \
> --date=short
c9ab4a3 Boris Ops 2026-10-09 site: tune listen
4c69b7d Boris Ops 2026-10-05 site: move root to /srv/www
f7de9e0 Anna Admin 2026-10-01 site: initial config

git log -p добавляет к каждому коммиту его правку; с -1 — только последний, с -- <файл> — только коммиты этого файла:

text
student@node1:/etc/demo$ sudo git log -p -1 -- site.conf
commit c9ab4a3032a1cf6f1f4b1fee565958cfaf2f67c4
Author: Boris Ops <boris@lab.local>
Date:   Fri Oct 9 09:15:00 2026 +0000

    site: tune listen

diff --git a/site.conf b/site.conf
index d988f4e..f671dff 100644
--- a/site.conf
+++ b/site.conf
@@ -1,4 +1,4 @@
 server {
-    listen 80;
+    listen 8O;
     root /srv/www;
 }

- — строка до коммита, + — после: вместо 80 записано 8O с буквой O. git show <коммит> печатает один коммит так же, с --stat — только список файлов. git blame подписывает каждую строку файла коммитом, который её последним менял:

text
student@node1:/etc/demo$ sudo git blame site.conf
^f7de9e0 (Anna Admin 2026-10-01 10:00:00 +0000 1) server {
c9ab4a30 (Boris Ops  2026-10-09 09:15:00 +0000 2)     listen 8O;
4c69b7d7 (Boris Ops  2026-10-05 14:30:00 +0000 3)     root /srv/www;
^f7de9e0 (Anna Admin 2026-10-01 10:00:00 +0000 4) }

^ у хэша — первый коммит репозитория. Незакоммиченная правка видна в git status (modified: site.conf) и git diff.

Отменить правку и коммит: restore, revert, reset

Что отменить Команда История
незакоммиченную правку файла git restore site.conf не меняется
один коммит git revert --no-edit <коммит> новый коммит поверх
файл — к версии из старого коммита git restore --source=<коммит> site.conf не меняется, правка — в рабочем файле
ветку — к старому коммиту git reset --hard <коммит> коммиты после него уходят из ветки

revert делает коммит, обратный выбранному, — история сохраняется, и видно, кто и когда отменил:

text
student@node1:/etc/demo$ sudo git revert --no-edit HEAD
[main 107f37c] Revert "site: tune listen"
 Date: Wed Oct 21 11:54:18 2026 +0000
 1 file changed, 1 insertion(+), 1 deletion(-)

В сообщении такого коммита — This reverts commit c9ab4a30…. Отменить можно и старый коммит (git revert --no-edit HEAD~2): git снимает только его правку и оставляет более поздние. Посмотреть файл из старого коммита, ничего не меняя, — git show HEAD~3:site.conf.

reset --hard переписывает ветку: коммиты после выбранного пропадают из неё, а незакоммиченные правки — совсем. Ушедшие коммиты помнит git reflog:

text
student@node1:/etc/demo$ sudo git reflog | head -3
107f37c HEAD@{0}: reset: moving to HEAD~1
d01f2cc HEAD@{1}: revert: Revert "site: move root to /srv/www"
107f37c HEAD@{2}: revert: Revert "site: tune listen"

git reset --hard 'HEAD@{1}' возвращает ветку туда, где она была до reset. В общем репозитории, откуда другие делают pull, историю не переписывают — там отменяют revert'ом.

Репозиторий root'а: sudo git

Файлы в /etc принадлежат root, и репозиторий в /etc/nginx — тоже. Команды git там идут через sudo, как и правка конфига. Имя и почту автора sudo git берёт из настроек root — твои git config --global ему не видны. Поэтому их задают в самом репозитории:

bash
sudo git -C /etc/nginx config user.name 'Lab Admin'
sudo git -C /etc/nginx config user.email 'admin@lab.local'

Без этого sudo git commit и sudo git revert отвечают Author identity unknown — git. git без sudo в чужом репозитории отказывается работать — git.

Удалённый репозиторий: fetch, pull, push

Bare-репозиторий — только история, без рабочих файлов: в него отправляют push с разных машин. Рабочая копия на сервере — клон, у неё удалённый репозиторий называется origin:

bash
git init --bare -b main /srv/git/site.git   # общий репозиторий
git clone /srv/git/site.git ~/site           # рабочая копия
git remote -v                                # куда смотрит origin

git status сравнивает ветку с тем, что git знал об origin при последнем обмене. Новый коммит коллеги он увидит после git fetch:

text
student@node1:~/w1$ git fetch
From /srv/git/site
   b1ad7d1..8396b40  main       -> origin/main
student@node1:~/w1$ git status | head -2
On branch main
Your branch is behind 'origin/main' by 1 commit, and can be
  fast-forwarded.

git log --oneline HEAD..origin/main — какие коммиты придут, git diff HEAD origin/main — что они поменяют. git pull = fetch и слияние; если своих коммитов нет, ветка просто сдвигается вперёд (Fast-forward).

git push отправляет свои коммиты. Если в origin есть коммиты, которых у тебя нет, push отказывается:

text
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to '/srv/git/site.git'

Сначала git pull, потом снова git push. Если и у тебя, и в origin есть новые коммиты, ветки разошлись, и git pull спрашивает, как их сводить, — git.

Отложить правку: stash

git stash убирает незакоммиченные правки в отдельное хранилище и возвращает файлы к последнему коммиту — так можно забрать чужие коммиты, когда pull отказывается из-за локальной правки:

text
student@node1:~/w1$ git stash
Saved working directory and index state WIP on main: b1ad7d1 site: v1
student@node1:~/w1$ git pull
Updating b1ad7d1..8396b40
Fast-forward
 index.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
student@node1:~/w1$ git stash pop
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
...
The stash entry is kept in case you need it again.

git stash list — что отложено (stash@{0}: WIP on main: …), git stash pop — вернуть правку и убрать её из хранилища. Здесь вернувшаяся правка задела ту же строку, что пришла с pull, — конфликт (git), и запись осталась в хранилище. Убрать её после разбора — git stash drop.

Конфликт

Конфликт — обе стороны поменяли одни и те же строки, и git не знает, какую версию оставить. Он пишет в файл обе:

text
<<<<<<< Updated upstream
v2 colleague
=======
v1 local edit
>>>>>>> Stashed changes

Между <<<<<<< и ======= — версия, которая уже в ветке, между ======= и >>>>>>> — та, что вливается. После stash pop метки называются Updated upstream и Stashed changes, после pull — HEAD и хэш коммита. git status показывает такие файлы как both modified.

Разбор:

  1. Открой файл, оставь нужное, убери все три метки.
  2. git add <файл> — конфликт разобран.
  3. После pull — git commit (сообщение слияния уже готово); после stash pop коммит не нужен, правка просто вернулась в рабочие файлы.

Передумал сливать — git merge --abort: файлы вернутся к состоянию до pull.

Ветки

bash
git branch                        # список веток
git switch -c feature             # создать и перейти
git switch main                   # переключиться
git merge feature                 # влить ветку в текущую
git branch -d feature             # удалить влитую ветку

Неполадки

8

detected dubious ownership

text
student@node1:~$ git -C /etc/demo status
fatal: detected dubious ownership in repository at '/etc/demo'
To add an exception for this directory, call:

	git config --global --add safe.directory /etc/demo

Репозиторий принадлежит другому пользователю (здесь root), и git не работает в нём от твоего имени: чужие настройки в .git/config могли бы запустить чужие команды. Для репозитория root'а нужен sudo git. Исключение safe.directory даст только читать: писать в .git root'а student всё равно не может — fatal: Unable to create '/etc/demo/.git/index.lock': Permission denied.

Author identity unknown

text
Author identity unknown

*** Please tell me who you are.
...
fatal: unable to auto-detect email address (got 'root@node1.(none)')

У пользователя, от которого идёт коммит, нет имени и почты. В скобках видно, кто это: здесь root — из-за sudo. Задай их в репозитории (git config user.name, git config user.email) — git.

Your local changes … would be overwritten by merge

text
error: Your local changes to the following files would be overwritten
  by merge:
	index.html
Please commit your changes or stash them before you merge.
Aborting

В рабочей копии есть незакоммиченная правка файла, который меняют пришедшие коммиты. Либо закоммить её и делай pull, либо отложи git stash, сделай pull и верни git stash pop — git.

Need to specify how to reconcile divergent branches

text
hint: You have divergent branches and need to specify how to
  reconcile them.
...
fatal: Need to specify how to reconcile divergent branches.

И у тебя, и в origin есть коммиты, которых нет у другой стороны (git status: have diverged). Слить их коммитом слияния — git pull --no-rebase; в терминале git откроет редактор с готовым сообщением, --no-edit возьмёт его без редактора. Выбор на будущее — git config pull.rebase false. После слияния — git push.

rejected … (fetch first)

В origin есть чужие коммиты. git pull, разбери конфликт, если будет, и снова git push — git.

refusing to update checked out branch

push в обычный репозиторий с рабочими файлами: git не меняет ветку, которая там сейчас выписана, иначе её файлы разошлись бы с историей. Общий репозиторий делают bare — git init --bare.

Permission denied (publickey) при push

SSH-ключ не настроен или не добавлен на сервер — см. ssh-keys. Проверь git remote -v: адрес по SSH или по HTTPS.

Случайно сделал reset --hard

git reflog помнит, где был HEAD, — можно вернуться: git reset --hard <hash> (git).

Опасно

2

git reset --hard и git clean -fd

reset --hard затирает незакоммиченные изменения в отслеживаемых файлах без подтверждения; git reflog спасает только то, что было закоммичено — незакоммиченные правки после reset --hard не восстановить. git clean -fd удаляет неотслеживаемые файлы и каталоги (в том числе те, что ты забыл добавить в git) — тоже без корзины. Перед clean прогони git clean -nd (пробный запуск: только покажет, что удалится).

git push --force

Перезаписывает историю удалённой ветки: коммиты, которые уже забрали себе коллеги, «пропадают» из ветки — у них останутся расходящиеся копии, слияние превратится в путаницу. Для общей ветки безопаснее git push --force-with-lease (откажется, если на сервере появились чужие коммиты, о которых ты не знал).

Термины

4
репозиторий git git repository
Каталог .git рядом с рабочими файлами: в нём вся история — коммиты, ветки, настройки. git status и git log работают внутри каталога с таким .git.
коммит commit
Снимок файлов репозитория с автором, датой и сообщением. У каждого коммита свой хэш (c9ab4a3), по нему коммит находят git show, git revert и git blame. Ещё встречается в: GPG
bare-репозиторий bare repository
Репозиторий git без рабочих файлов — только история. В него отправляют push с разных машин; создаётся git init --bare, по обычаю с именем на .git.
конфликт слияния merge conflict
Обе стороны поменяли одни и те же строки, и git не может слить их сам: он пишет в файл обе версии между метками <<<<<<<, ======= и >>>>>>> и ждёт правки и git add.

См. также

  • diff-patch — как читать вывод git diff и git log -p.
  • ssh-keys — ключ для push по SSH.
  • sudo — команды от root.
  • files-basics — навигация и файлы.
Загрузка…