История: log -p, show, blame
Репозиторий /etc/demo с тремя коммитами двух авторов:
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 — только
последний, с -- <файл> — только коммиты этого файла:
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 подписывает каждую строку файла
коммитом, который её последним менял:
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 делает коммит, обратный выбранному, — история сохраняется, и
видно, кто и когда отменил:
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:
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
ему не видны. Поэтому их задают в самом репозитории:
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:
git init --bare -b main /srv/git/site.git
git clone /srv/git/site.git ~/site
git remote -v
git status сравнивает ветку с тем, что git знал об origin при
последнем обмене. Новый коммит коллеги он увидит после git fetch:
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 отказывается:
! [rejected] main -> main (fetch first)
error: failed to push some refs to '/srv/git/site.git'
Сначала git pull, потом снова git push. Если и у тебя, и в origin
есть новые коммиты, ветки разошлись, и git pull спрашивает, как их
сводить, — git.
git stash убирает незакоммиченные правки в отдельное хранилище и
возвращает файлы к последнему коммиту — так можно забрать чужие
коммиты, когда pull отказывается из-за локальной правки:
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 не знает, какую версию оставить. Он пишет в файл обе:
<<<<<<< Updated upstream
v2 colleague
=======
v1 local edit
>>>>>>> Stashed changes
Между <<<<<<< и ======= — версия, которая уже в ветке, между
======= и >>>>>>> — та, что вливается. После stash pop метки
называются Updated upstream и Stashed changes, после pull —
HEAD и хэш коммита. git status показывает такие файлы как
both modified.
Разбор:
- Открой файл, оставь нужное, убери все три метки.
git add <файл> — конфликт разобран.
- После
pull — git commit (сообщение слияния уже готово); после
stash pop коммит не нужен, правка просто вернулась в рабочие файлы.
Передумал сливать — git merge --abort: файлы вернутся к состоянию до
pull.