Git

Конспект по курсу на stepik.org https://stepik.org/course/3145/syllabus

Course from Stepik

Course from Stepik

2. Использование существующих репозиториев

При клонировании репозитория есть два варианта

Если клонировать через https, то и пушить гит будет через https, а при работе по https он будет спрашивать пароль, при работе через ssh он будет сразу пушить (такое поведение легко исправить через конфиг (в конфиге указывается метод взаимодействия))

Для работы по ssh нужно чтобы на сервере был добавлен Ваш ssh-ключ


При клонировании можно сразу указать новую директорию

git clone git@github.com:vandud/test.git ~/Documents/testgitrepo  

Но это сработает только с несуществующей директорией (уже существующая директория имеет в себе хардлинки (. и ..))


Конфиги есть трех видов и хранятся в трех местах

  1. Глобальные для всего сервера\компьютера - /etc/gitconfig - ключ --system
  2. Глобальные для пользователя - /home/$USER/.gitconfig или /home/$USER/config/git/config - ключ --global
  3. Конфиг для конкретного репозитория - repodir/.git/config

Конфиги применяются в описаном порядке, то есть третий конфиг перекрывает все предыдущие


После установки git сразу нужно указать имя и емайл пользователя (эта информация передается в коммитах)

git config --global user.name "Ivan Dudin"
git config --global user.email s@vandud.ru

Команда git config --list покажет текущие настройки

vandud@vandud-desktop:~/Documents/testgitrepo$ git config --list
user.name=Ivan Dudin
user.email=s@vandud.ru

Любой файл имеет два состояния

Чтобы проиндексровать файл, нужно выполнить команду

git add 

Чтобы запомнить состояние файла(ов)

git commit

Чтобы отправить информацию (коммиты) на сервер

git push
Course from Stepik

3. Обзор текущего состояния локального репозитория

Для обзора текущего состояния локального репозитория обычноиспользуют команду git status
К git status можно добавлять ключ -s, для краткого вывода

[vandud@thinkpad test]$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   a
	new file:   b
	new file:   c

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)
	modified:   a
	deleted:    b
	modified:   c

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	txt

[vandud@thinkpad test]$ git status -s
AM a
AD b
AM c
?? txt

git add

https://git-scm.com/docs/git-add

Команда git add FILE добавляет файл в индекс гита
Индекс содержит в себе снапшот контента рабочего дерева и из него будет браться контент для следующего коммита

Таким образом после изменений в рабочей директории перед коммитом нужно выполнить команду add чтобы добавить новые или модифицированные файлы в индекс

Она всего лишь добавляет контент файла на момент выполенения команды в индекс. Поэтому если после ее выполнения были сделаны еще изменения которые должны войти в один коммит с предыдущими, то нужно просто повторить команду add для нужных файлов

git status

https://git-scm.com/docs/git-status
Показывает состояние текущего расположения. Точнее показывает разницу между текущим состоянием и последним коммитом

При выводе краткого статуса возможно два варианта

XY PATH
XY ORIG_PATH -> PATH # когда файл был переименован  

Значения статусов:

git commit

Создает новый коммит содержащий в себе текущий контент индекса и переданное ему сообщение, которое описывает сделанные изменения

Коммитируемый контент может быть добавлен в коммит несколькими способами

Опция --dry-run позволит увидеть все добавленное в индекс для следующего коммита (т.е. все что будет включено в следующий коммит)

git log

Показывает историю коммитов (коммиты и некоторую инфу о них)
По умолчанию в обратно-хронологическом порядке (новее=выше)

Есть два понятия, коммитер и автор, они отличаются

git checkout

Переключение между ветвями или восстановление файлов в рабочей директории
Приводит файлы в рабочей директории к состоянию какой-либо версии из индекса

Переключение на ветвь это изменение файлов в соответствии с индексом и перевешивание указателя HEAD

HEAD – это указатель на коммит в вашем репозитории, который станет родителем следующего коммита

Course from Stepik

4. Коммит изменений

macbook:test vandud$ git rm govno 
rm 'govno'

macbook:test vandud$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    govno

macbook:test vandud$ git commit -m 'zhopa23'
[master fb6a8c6] zhopa23
 1 file changed, 1 deletion(-)
 delete mode 100644 govno
 
macbook:test vandud$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean
macbook:test vandud$ 

Выше видно как я сделал git rm (он удаляет файл и сразу же добавляет это изменение в индекс)
Далее видим что в статусе сразу видно это удаление и предлагается сделать коммит
Делаю коммит и в статусе видно что моя текущая копия впереди от ориджина на один коммит (предлагается запушить)

.gitignore

macbook:test vandud$ cat .gitignore 
*.zhopa

macbook:test vandud$ vim zhopa.zhopa

macbook:test vandud$ git status -s
?? .gitignore

macbook:test vandud$ git add zhopa.zhopa 
The following paths are ignored by one of your .gitignore files:
zhopa.zhopa
Use -f if you really want to add them.

Файл .gitignore нужен для вывода антрэкед файлов из поля зрения гита. Уже затреченые файлы он продолжит видеть не смотря на гитигнор

По формату файла можно смотреть тут - https://git-scm.com/docs/gitignore

macbook:test vandud$ git add -f zhopa.zhopa 
macbook:test vandud$ git status -s
A  zhopa.zhopa
?? .gitignore

git-add

Добавляет контент файлов в индекс
Можно перечислять файлы поименно, можно использовать ключ -A/--all, можно использовать шаблоны как в .gitignore

macbook:test vandud$ git add --dry-run au.zhopa 
The following paths are ignored by one of your .gitignore files:
au.zhopa
Use -f if you really want to add them.

macbook:test vandud$ git add --dry-run aou.aeou 
add 'aou.aeou'

macbook:test vandud$ git status -s # видим что ничего не добавилось
A  zhopa.zhopa
?? .gitignore
?? aou.aeou

Чтобы обновить инфу об удаленных файлах в индексе, нужно добавить их командой add

macbook:test vandud$ touch test
macbook:test vandud$ git status -s
?? test
macbook:test vandud$ git add test
macbook:test vandud$ git status -s
A  test
macbook:test vandud$ rm test
macbook:test vandud$ git status -s
AD test
macbook:test vandud$ git add test
macbook:test vandud$ git status -s
macbook:test vandud$ 

git-rm

Удаляет файл(ы) и обновляет индекс, принимает имена файлов или шаблоны

Не даст удалить проиндексированный но не закоммиченный файл
Чтобы все равно удалить, нужно использовать ключ -f/--force

macbook:test vandud$ git add aou.aeou 
macbook:test vandud$ git rm aou.aeou 
error: the following file has changes staged in the index:
    aou.aeou
(use --cached to keep the file, or -f to force removal)

macbook:test vandud$ git commit -m 'test rm'
[master cc5a978] test rm
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 aou.aeou

macbook:test vandud$ git rm aou.aeou 
rm 'aou.aeou'
macbook:test vandud$ git status -s
D  aou.aeou
?? .gitignore

А вот так с ключом -f

macbook:test vandud$ touch test
macbook:test vandud$ git status -s
D  aou.aeou
?? .gitignore
?? test

macbook:test vandud$ git add test 
macbook:test vandud$ git status -s
R  aou.aeou -> test
?? .gitignore

macbook:test vandud$ git rm test 
error: the following file has changes staged in the index:
    test
(use --cached to keep the file, or -f to force removal)

macbook:test vandud$ git rm -f test
rm 'test'
macbook:test vandud$ git status -s
D  aou.aeou
?? .gitignore
macbook:test vandud$ git status -s
A  zhopa.zhopa
?? .gitignore
?? aou.aeou

macbook:test vandud$ cat zhopa.zhopa 
santoehu

macbook:test vandud$ git rm --cached zhopa.zhopa 
rm 'zhopa.zhopa'

macbook:test vandud$ git status -s
?? .gitignore
?? aou.aeou

macbook:test vandud$ cat zhopa.zhopa 
santoehu

git-commit

"Слепок индекса"

В гит коммит можно передать имена файлов, тогда будут закоммичены только изменения из них и будет проигнорирован индекс

macbook:test vandud$ git log --oneline
cc5a978 (HEAD -> master) test rm
fb6a8c6 zhopa23
5876123 (origin/master) govno
3993fdf rm
6b356d6 double zhop commit
6cdf909 zhopnyi commit
macbook:test vandud$ git status -s
D  aou.aeou
?? .gitignore
macbook:test vandud$ git add .gitignore 
macbook:test vandud$ git status -s
A  .gitignore
D  aou.aeou
macbook:test vandud$ git commit -m '--amend test' --amend
[master 176e908] --amend test
 Date: Sun Feb 7 05:09:54 2021 +0300
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore
macbook:test vandud$ git status -s
macbook:test vandud$ git log --oneline
176e908 (HEAD -> master) --amend test
fb6a8c6 zhopa23
5876123 (origin/master) govno
3993fdf rm
6b356d6 double zhop commit
6cdf909 zhopnyi commit
Course from Stepik

5. Создание веток

aeouaeouaoeua.png

git branch - создает новую ветвь
git checkout - переключает на ветвь
git checkout -b - объединяет две пред. команды
git merge - мерджит указанную ветвь в текущую


git-checkout - позволяет переключаться между ветками (коммитами, тегами) или восстановить файлы рабочего каталога

git checkout [options] [<branch>]/<commit>/<tag>
git checkout [options] <file_name_to_restore>

-f --force - Позволяет переключиться между ветками, даже если состояние файлов отличается от состояния, обозначенного как HEAD. Может использоваться для отмены локальных изменений
-b <new_branch> - Создаёт новую ветку с именем <new_branch>; для получения дополнительной информации см. git-branch


Переход к другой ветке - git checkout master
Переход к коммиту с тегом HEAD - git checkout HEAD
Переход к коммиту, предшествующему HEAD на 4 - git checkout HEAD~4
Найти хеш коммита с использованием git log и перейти к этому коммиту:

git log --grep="Commit message fragment"`  
git checkout b20eb8bdb8daf5fcec3ceb191c6b9b25507376ca

Отменить незакоммиченные изменения в файле file.txt - git checkout -- file.txt


box@47f08793a3e6: ~/REPO $ git branch --list *5555                              
  15Qy1C1hC8BdGenCuGQthMZhaOk4l7vJ_5555

macbook:test vandud$ git branch # local
  aoeuaoeu
  master
  new_test_branch
* vandud

macbook:test vandud$ git branch --list # local
  aoeuaoeu
  master
  new_test_branch
* vandud

macbook:test vandud$ git branch --all # all
  aoeuaoeu
  master
  new_test_branch
* vandud
  remotes/origin/master
  remotes/origin/vandud
  
macbook:test vandud$ git branch -r #remote
  origin/master
  origin/vandud

Удалять ветки можно с помощью git branch -d/-D <branchname>
С сервера ветки можно удалять так git push <reponame> --delete <branchname>

macbook:test vandud$ git branch -a
* master
  remotes/origin/master
  remotes/origin/testus
  remotes/origin/vandud
  
macbook:test vandud$ git push
Everything up-to-date

macbook:test vandud$ git push origin --delete testus
remote: . Processing 1 references
remote: Processed 1 references in total
To https://gitea.vandud.ru/vandud/test.git
 - [deleted]         testus
 
macbook:test vandud$ git push origin --delete vandud
remote: . Processing 1 references
remote: Processed 1 references in total
To https://gitea.vandud.ru/vandud/test.git
 - [deleted]         vandud
 
macbook:test vandud$ git branch -a
* master
  remotes/origin/master
macbook:test vandud$ 

Интересный факт

macbook:test vandud$ git log --all --graph --oneline
* 0ba27fa (origin/master) clear working tree
* 27b74a8 (HEAD -> master) dick
*   2132219 Merge branch 'vandud'
|\  
| * 59b8d9f aoue
| * 80ba1f4 govno
...
macbook:test vandud$ git branch 59b8d9f
macbook:test vandud$ git checkout 59b8d9f
warning: refname '59b8d9f' is ambiguous. # <-- "ambiguous" - говорит о том что имя неоднозначное
D	govno
D	mazafaka
D	new.tt
Switched to branch '59b8d9f' # <-- и в итоге свитчится на бранч
Course from Stepik

6. Переход между коммитами

git log

git log - позволяет просматривать историю коммитов, производить поиск по тексту сообщений в коммитах, по измененным файлам, по датам, по авторам, по веткам и прочим параметрам

По умолчанию история коммитов выводится в обратном хронологическом порядке (выше - новее)

Также можно управлять выводом деталей в git log при помощи готовых пресетов и параметра --pretty. Доступные пресеты: oneline, short, full и fuller

Пример использования:

git log --pretty=oneline

111111 commit1
222222 commit2
333333 commit3

Кроме того, git log позволяет полностью настроить формат вывода при помощи параметра format

Пример использования format:

git log --pretty=format:"%h - %an, %ar : %s"
1111111 - Name Surname, 9 months ago : commit1
2222222 - Name Surname, 10 months ago : commit2
3333333 - Name Surname, 11 months ago : commit3

Где %h - хэш коммита, %an - имя автора, %ar - относительная дата, %s - сообщение коммита

git checkout подходит не только для переключения между ветками, но и между коммитами и тегами:

git checkout [options] [<branch>]/<commit>/<tag>
git checkout [options] <file_name_to_restore>


git log --all --grep='commit message' # позволит отыскать коммит мессадж

Пример решения задачки со степика

box@b888ffedb096: ~/REPO $ git log --all --grep='Git rules!' --pretty=format:%H 
> /home/box/answer 

git stash

https://www.git-scm.com/docs/git-stash

Эта команда нужна для сохранения изменений которые по каким-то причинам не нужно включать в коммит. Например при переключении между ветками

stash сохраняет текущее состояние репозитория без создания коммита

Сохраняются только измененные отслеживаемые файлы
Неотслеживаемые файлы в stash не включаются

Сохранить незакоммиченную информацию перед переключением ветки можно было бы с помощью squash'a, но stash подходит лучше (squash позволяет объединять коммиты в один)

git stash - эквивалентна git stash push, которая сохраняет текущее состояние в стэш

Посмотреть записи в стэше можно с помощью git stash list

Стэш существует назависимо от бранчей

WIP - work in progress


git stash pop/apply - позволяют извлечь состояние из стэша
После команды pop/apply указывается индекс записи в стэше, если не указать то будет взята последняя

Для удаления записей из стэша - git stash drop

git reset

Указатель HEAD указывает на указатель вершины ветки (например master)

macbook:test vandud$ git log --all --oneline
0ba27fa (HEAD -> master, origin/master) clear working tree
27b74a8 dick
2132219 Merge branch 'vandud'
...
macbook:test vandud$ git reset --soft HEAD~
macbook:test vandud$ git log --all --oneline
0ba27fa (origin/master) clear working tree
27b74a8 (HEAD -> master) dick
2132219 Merge branch 'vandud'
59b8d9f aoue
...

У git reset есть три важных ключа

То есть checkout переключает нас на какой-то коммит, а reset откатывает

Course from Stepik

7. git pull & git push

git fetch

Позволяет скачивать объекты и ссылки с удаленного репозитория


git fetch origin - обновить состояние всех веток в соответствии с репозиторием origin
git fetch -t - скачать все метки


Если необходимо скачать только новые файлы с удалённого репозитория, не обновляя существующие на локальном, то подходит команда git fetch

git pull

Скачать и синхронизироваться с репозиторием

git pull [opts] [<repo> [<refspec> ..]]

Слить удаленную ветку next в текущую

git pull origin next

Пример
Локально закоммитили и запушили
После этого на сервере кто-то закоммитил
Делаем изменения у себя локально без коммита
Пытаемся спуллить
Возникнет конфликт

git push


Загрузить изменения в конкретную ветку на удалённом репозитории

git push origin branch_name

Разрешение конфликтов

Конфликт - это ситуация, когда, в ходе слияния (pull/push/merge) система не может решить какие изменения нужно сохранить
Чаще всего такое бывает когда из одного коммита создано несколько веток и в один и тот же файл внесены изменения без синхронизации между ветками

Пример
Terminal_2021-02-14-22-50-30.png
Из мастера ответвились дважды и наделали независимых изменений
Далее смерджили первую ветку в мастер (все окей)
А когда попытаемся смерджить вторую ветку в мастер - будет конфликт

Внутри конфликтного файла будет такое

<<<<<<< HEAD
Changes from branch2
=======
Changes from branch1
>>>>>>> master

Упрощенное разрешение конфликтов

git checkout --ours README.md # оставляем изменения из текущей ветки (отвергаем изменения из ветки которую мерджим в текущую)  
git checkout --theirs README.md # наоборот  

(логика простая, либо оставляем НАШЕ, либо оставляем ИХ)

Далее git add, git commit и все становится хорошо


Ручное разрешение конфликтов

Ручное разрешение конфликтов подходит в ситуациях, когда необходимы изменения, добавленные во всех конфликтующих ветках. Например, если в branch1 были добавлены стили, а в branch2 исправлены ошибки текста. Для ручного разрешения конфликта необходимо проделать следующее во всех конфликтных файлах (причем конфликт может возникать в нескольких местах одного файла):

Для всех файлов с исправленным конфликтом необходимо сделать git add, после чего закоммитить и повторить операцию слияния

В данном примере, конфликт можно было спровоцировать, проделав git pull branch2 из ветки branch1 или наоборот. Алгоритм разрешения конфликтов в подобной ситуации аналогичен

Course from Stepik

8. git diff

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

Команда может быть выполнена для отдельного файла или для всего репозитория


git diff - сравнение рабочей директории и последнего коммита
git diff --cached - разница между индексом и последним коммитом
git diff HEAD - изменения в рабочем дереве с момента последнего коммита; т.е. то, что будет закоммичено, если вы запустите «git commit -a»


Сравнение с произвольными коммитами:
git diff test - Сравнение с веткой «test»
git diff HEAD -- ./file.txt - Сравнение с текущей веткой, но diff ограничен только изменениями в файле "file.txt"
git diff HEAD^ HEAD - Сравнение последнего и предпоследнего коммита

Сравнение веток:
git diff topic master - Разница веток topic и master
git diff topic...master - Изменения, произошедшие в ветке master с тех пор, как ветка topic была создана

Ограничение вывода команды git diff:
git diff --diff-filter=MRC - Показывать только изменения, переименование и копирование (modification, rename, copy), но не добавление или удаление
git diff --name-status - Показывать только названия и характер изменений, но не фактический вывод git diff git diff arch/i386 include/asm-i386 - Ограничить вывод git diff для указанных поддеревьев


Можно также делать git diff COMMIT1 COMMIT2

Gitea

Гитея развернута на phobos'e
Устанавливал по офф. доке https://docs.gitea.io/en-us/install-from-binary/

Команда для создания юзера для демона

adduser —system —shell /bin/bash —gecos 'Gitea' \
  —group —disabled-password —home /home/gitea gitea

Автозапуск сделан через systemd (в офф. доке написано как сделать)

Вот конфиг настроек
Screenshot_2020-12-19-Gitea-Git-with-a-cup-of-tea.png

Команда gitea dump -c /etc/gitea/app.ini делает дамп в котором есть все что нужно для разворачивания

Сбросить пароль

root@phobos:~# sudo -u gitea gitea admin change-password --username vandud --password ZzaoeuAOEUeee....5Qc4YP --config /etc/gitea/app.ini 
2021/01/24 03:38:55 ...dules/setting/git.go:91:newGit() [I] Git Version: 2.20.1, Wire Protocol Version 2 Enabled
vandud's password has been successfully updated!
root@phobos:~# 

Полезные ссылки

http://pr0git.blogspot.com/

Git flow | GitHub flow | GitLab Flow

http://yapro.ru/article/6172


Если не устанавливать никаких правил работы с ветками, в репозитории начинает расти энтропия:

Git Flow

aXbgitdashflow.png

Предполагает наличие основной ветки master, ветки для накопленных изменений develop, а также отдельных веток для фич, релизов и хотфиксов
Разрабатываемые изменения мержатся в develop, оттуда в релизные ветки, и в итоге попадают в master

Нередко разработчики по ошибке мержат какие-то изменения только в master, забывая про develop. А большинство графических интерфейсов к git по умолчанию считают основной веткой именно master, поэтому в них приходится каждый раз что-то переключать или настраивать
Появляется лишняя сложность из-за веток релизов и хотфиксов. Большинство команд, особенно небольших, может легко обойтись без них

Поэтому это избыточно и не удобно

GitHub Flow

github_flow.png

Самая простая модель
Здесь есть только master и feature-ветки

Мерджить в мастер и часто деплоиться - круто, но остается много других вопросов

GitLab Flow

GitLab flow: ветка production

production_branch.png

Здесь мы можем настроить автодеплой при любом изменении в ветке production
Мерджить в master можно когда угодно
А мерджить в production из master будем тогда когда нам удобно

GitLab flow: ветки для нескольких сред

environment_branches.png

Предположим, что у вас есть несколько сред: стейджинг (staging), пре-продакшен (pre-production) и продакшен (production). Код из master автоматически развёртывается на стейджинг. Как только вы готовы развернуть его на пре-продакшен, вы создаете мерж-реквест из master в pre-production. Соответственно, мерж из pre-production в production означает окончательный релиз. Такой процесс, когда все коммиты проходят через ветки в строго определенном порядке, гарантирует, что изменения прошли тестирование во всех средах

GitLab flow: релизные ветки

release_branches.png

Это удобно при предоставлении ПО внешним клиентам. Здесь каждая минорная версия хранится в отдельной ветке

Следуйте правилу "upstream first": всегда, когда это возможно, сначала делайте мерж исправлений в master, и только оттуда — cherry-pick в релизную ветку. Благодаря этому правилу вы не забудете сделать cherry-pick исправлений в master и не встретите тот же самый баг в следующем релизе

Progit

Progit

1. Введение

Основное отличие Git от любой другой СКВ (включая Subversion и её собратьев) — это подход к работе со своими данными. Концептуально, большинство других систем хранят информацию в виде списка изменений в файлах. Эти системы (CVS, Subversion, Perforce, Bazaar и т. д.) представляют хранимую информацию в виде набора файлов и изменений, сделанных в каждом файле, по времени (обычно это называют контролем версий, основанным на различиях).
Git не хранит и не обрабатывает данные таким способом. Вместо этого, подход Git к хранению данных больше похож на набор снимков миниатюрной файловой системы. Каждый раз, когда вы делаете коммит, то есть сохраняете состояние своего проекта в Git, система запоминает, как выглядит каждый файл в этот момент, и сохраняет ссылку на этот снимок. Для увеличения эффективности, если файлы не были изменены, Git не запоминает эти файлы вновь, а только создаёт ссылку на предыдущую версию идентичного файла, который уже сохранён. Git представляет свои данные как, скажем, поток снимков.
Это очень важное отличие между Git и почти любой другой СКВ. Git переосмысливает практически все аспекты контроля версий, которые были скопированы из предыдущего поколения большинством других систем. Это делает Git больше похожим на миниатюрную файловую систему с удивительно мощными утилитами, надстроенными над ней, нежели просто на СКВ.

В Git для всего вычисляется хеш-сумма, и только потом происходит сохранение. В дальнейшем обращение к сохранённым объектам происходит по этой хеш-сумме. Это значит, что невозможно изменить содержимое файла или каталога так, чтобы Git не узнал об этом. Данная функциональность встроена в Git на низком уровне и является неотъемлемой частью его философии. Вы не потеряете информацию во время её передачи и не получите повреждённый файл без ведома Git. Механизм, которым пользуется Git при вычислении хеш-сумм, называется SHA-1 хеш. Это строка длиной в 40 шестнадцатеричных символов (0-9 и a-f), она вычисляется на основе содержимого файла или структуры каталога. SHA-1 хеш выглядит примерно так: 24b9da6552252987aa493b52f8696cd6d3b00373 Вы будете постоянно встречать хеши в Git, потому что он использует их повсеместно. На самом деле, Git сохраняет все объекты в свою базу данных не по имени, а по хеш-сумме содержимого объекта.

Когда вы производите какие-либо действия в Git, практически все из них только добавляют новые данные в базу Git. Очень сложно заставить систему удалить данные либо сделать что-то, что нельзя впоследствии отменить. Как и в любой другой СКВ, вы можете потерять или испортить свои изменения, пока они не зафиксированы, но после того, как вы зафиксируете снимок в Git, будет очень сложно что-либо потерять, особенно, если вы регулярно синхронизируете свою базу с другим репозиторием.

У Git есть три основных состояния, в которых могут находиться ваши файлы: изменён (modified), индексирован (staged) и зафиксирован (committed):
К изменённым относятся файлы, которые поменялись, но ещё не были зафиксированы. • Индексированный — это изменённый файл в его текущей версии, отмеченный для включения в следующий коммит. • Зафиксированный значит, что файл уже сохранён в вашей локальной базе. Мы подошли к трём основным секциям проекта Git: рабочая копия (working tree), область индексирования (staging area) и каталог Git (Git directory).

Screenshot_2021_02_02-12_49_03-2021-10-10-at-0sth.png

Рабочая копия является снимком одной версии проекта. Эти файлы извлекаются из сжатой базы данных в каталоге Git и помещаются на диск, для того чтобы их можно было использовать или редактировать. Область индексирования — это файл, обычно находящийся в каталоге Git, в нём содержится информация о том, что попадёт в следующий коммит. Её техническое название на языке Git — «индекс», но фраза «область индексирования» также работает. Каталог Git — это то место, где Git хранит метаданные и базу объектов вашего проекта. Это самая важная часть Git и это та часть, которая копируется при клонировании репозитория с другого компьютера.

параметры могут быть сохранены в трёх местах:

  1. Файл [path]/etc/gitconfig содержит значения, общие для всех пользователей системы и для всех их репозиториев. Если при запуске git config указать параметр --system, то параметры будут читаться и сохраняться именно в этот файл. Так как этот файл является системным, то вам потребуются права суперпользователя для внесения изменений в него.
  2. Файл ~/.gitconfig или ~/.config/git/config хранит настройки конкретного пользователя. Этот файл используется при указании параметра --global и применяется ко всем репозиториям, с которыми вы работаете в текущей системе.
  3. Файл config в каталоге Git (т. е. .git/config) репозитория, который вы используете в данный момент, хранит настройки конкретного репозитория. Вы можете заставить Git читать и писать в этот файл с помощью параметра --local, но на самом деле это значение по умолчанию. Неудивительно, что вам нужно находиться где-то в репозитории Git, чтобы эта опция работала правильно. Настройки на каждом следующем уровне подменяют настройки из предыдущих уровней, то есть значения в .git/config перекрывают соответствующие значения в [path]/etc/gitconfig.

Чтобы посмотреть все установленные настройки и узнать где именно они заданы, используйте команду

git config --list --show-origin

После установки Git — укажите ваше имя и адрес электронной почты. Это важно, потому что каждый коммит в Git содержит эту информацию, и она включена в коммиты, передаваемые вами, и не может быть далее изменена:

Открыть справку по любой команде можно так

$ git help <команда>
$ git <команда> --help $ man git-<команда>
$ man git-<команда>
Progit

2. Основы

Если у вас уже есть проект в каталоге, который не находится под версионным контролем Git, то команда git init создаст в текущем каталоге новый подкаталог с именем .git, содержащий все необходимые файлы репозитория — структуру Git репозитория. На этом этапе ваш проект ещё не находится под версионным контролем. (добавляем файлы в индекс и коммитим)

Командой git clone мы получим себе копию удаленного репозитория в которой будут почти все дынные из удаленного репозитория
Это важное отличие от других скв, например в svn для получения рабочей копиии репозитория используется checkout (только рабочая копия)
Таким образом в случае поломки сервера мы сможем восстановить его из любой копии репозитория (потеряются хуки, но слепки останутся)

Screenshot_2021_02_02-12_49_03-2021-10-10-at-1lifecycleoffilesingit.png


$ git status
  On branch master
  Your branch is up-to-date with 'origin/master'.
  Untracked files:
    (use "git add <file>..." to include in what will be committed)
      README
  nothing added to commit but untracked files present (use "git add" to track)

Статус Untracked означает, что Git видит файл, которого не было в предыдущем снимке состояния (коммите)
Для добавления файла в индекс используется - git add <filename>

$ git status
  On branch master
  Your branch is up-to-date with 'origin/master'.
  Changes to be committed:
    (use "git restore --staged <file>..." to unstage)
      new file:   README

Теперь файл проиндексирован, так как он находится в секции «Changes to be committed». Если вы выполните коммит в этот момент, то версия файла, существовавшая на момент выполнения вами команды git add, будет добавлена в историю снимков состояния

git add - Это многофункциональная команда, она используется для добавления под версионный контроль новых файлов, для индексации изменений, а также для других целей, например для указания файлов с исправленным конфликтом слияния

Ситуация на скриншоте ниже наглядно демонстрирует, что Git индексирует файл в точности в том состоянии, в котором он находился, когда вы выполнили команду git add
Screenshot_2021_02_02-12_49_03-2022-01-01-git1.png


Вы можете создать файл .gitignore. с перечислением шаблонов соответствующих таким файлам. Вот пример файла .gitignore:

$ cat .gitignore
*.[oa]
*~

Первая строка предписывает Git игнорировать любые файлы заканчивающиеся на «.o» или «.a» — объектные и архивные файлы, которые могут появиться во время сборки кода. Вторая строка предписывает игнорировать все файлы заканчивающиеся на тильду (~), которая используется во многих текстовых редакторах, например Emacs, для обозначения временных файлов

Хорошая практика заключается в настройке файла .gitignore до того, как начать серьёзно работать, это защитит вас от случайного добавления в репозиторий файлов, которых вы там видеть не хотите. К шаблонам в файле .gitignore применяются следующие правила:

Glob-шаблоны представляют собой упрощённые регулярные выражения, используемые командными интерпретаторами. Символ (*) соответствует 0 или более символам; последовательность [abc] — любому символу из указанных в скобках (в данном примере a, b или c); знак вопроса (?) соответствует одному символу; и квадратные скобки, в которые заключены символы, разделённые дефисом ([0-9]), соответствуют любому символу из интервала (в данном случае от 0 до 9). Вы также можете использовать две звёздочки, чтобы указать на вложенные каталоги: a/**/z соответствует a/z, a/b/z, a/b/c/z, и так далее

У github'a есть набор шаблонов для разных языков
https://github.com/github/gitignore
Они могут быть отправной точкой для файла .gitignore в вашем проекте


git diff - покажет разницу между текущим состоянием рабочего каталога и содержимым индекса
git diff --staged - покажется разницу между последним коммитом и содержимым индекса (то есть то что войдет в коммит)

Через git difftool можно просматривать диффы через другие утилиты (какие-то более удобные консольные или даже графические)

git commit откроет редактор текста в котором будет предложено ввести commit-message, в нем уже будет вывод команды git status
Ключ -v для git commit кроме вывода git status предварительно поместит в редактор текста еще и вывод git diff
Самой первой строкой будет пустая строка в которую и предлагается ввести свое описание коммита (git commit&git diff output в сам коммит не попадут, они просто позволяют сразу видеть что именно попадает в коммит чтобы можно было написать корректное описание)


Для того чтобы удалить файл из Git, вам необходимо удалить его из отслеживаемых файлов (точнее, удалить его из вашего индекса) а затем выполнить коммит. Это позволяет сделать команда git rm, которая также удаляет файл из вашего рабочего каталога, так что в следующий раз вы не увидите его как «неотслеживаемый»

Если вы изменили файл и уже проиндексировали его, вы должны использовать принудительное удаление с помощью параметра -f. Это сделано для повышения безопасности, чтобы предотвратить ошибочное удаление данных, которые ещё не были записаны в снимок состояния и которые нельзя восстановить из Git
Чтобы удалить файл из индекса, оставив его при этом в рабочем каталоге, используйте опцию --cached
Screenshot_2021_02_02-12_49_03-2022-01-02-gitrm.png


Команда

$ git mv README.md README

Эквивалентна командам

$ mv README.md README
$ git rm README.md
$ git add README

Полезный аргумент для git log - -p или --patch, который показывает разницу (выводит патч), внесенную в каждый коммит
А если вы хотите увидеть сокращенную статистику для каждого коммита, вы можете использовать опцию --stat
Screenshot_2021_02_02-12_49_03-2022-01-02-gitlogpatchstat.png

Опция --pretty принимает на вход oneline, short, medium, full, fuller, reference, email, raw, format и некоторые другие
Подробное описание форматов можно увидеть в секции PRETTY FORMATS в git help log

Screenshot_2021_02_02-12_49_03-2022-01-02-at-03gitlogpretty.png

format принимает в себя строку из плейсхолдеров с помощью которой можно задать свой собственный формат

В примере ниже мы ищем коммиты которые затрагивают определенный файл и имеют в commit-message определенное слово
Screenshot_2021_02_02-12_49_03-2022-01-02-at-gitlogexample.png


Если вы хотите переделать коммит — внесите необходимые изменения, добавьте их в индекс и сделайте коммит ещё раз, указав параметр --amend:

  $ git commit --amend

Эта команда использует область подготовки (индекс) для внесения правок в коммит. Если вы ничего не меняли с момента последнего коммита (например, команда запущена сразу после предыдущего коммита), то снимок состояния останется в точности таким же, а всё что вы сможете изменить — это ваше сообщение к коммиту

Очень важно понимать, что когда вы вносите правки в последний коммит, вы не столько исправляете его, сколько заменяете новым, который полностью его перезаписывает. В результате всё выглядит так, будто первоначальный коммит никогда не существовал, а так же он больше не появится в истории вашего репозитория
Очевидно, смысл изменения коммитов в добавлении незначительных правок в последние коммиты и, при этом, в избежании засорения истории сообщениями вида «Ой, забыл добавить файл» или «Исправление грамматической ошибки»


Пример убирания файла из индекса
Screenshot_2021_02_02-12_49_03-2022-01-02-at-rmfromindex.png

Через git add * мы добавили лишнего, и чтобы убрать из индекса что-либо, нужно сделать git status и в его выводе будет подсказка о том как удалить файл из индекса

git checkout -- <file> - позволяет откатить изменения в working tree
В примере ниже мы удаляем добавленный в индекс файл (то есть делаем изменения неотслеженные гитом, вместо удаления можно просто отредактировать файл) и командой checkout убираем эти изменения
Screenshot_2021_02_02-12_49_03-2022-01-02-at-gitcheckout.png


git remote - покажет список доступных удаленных репозиториев (ключ -v покажет и урлы)

vandud@macbook: zabbix [0] ? git remote
origin
vandud@macbook: zabbix [0] ? git remote -v
origin	git@gitlab.i-free.com:puppet-modules/zabbix.git (fetch)
origin	git@gitlab.i-free.com:puppet-modules/zabbix.git (push)

Чтобы добавить новый удаленный репозиторий - git remote add <shorname> <url>
Screenshot_2021_02_02-12_49_03-2022-01-02-at-05gitremoteadd.png


git fetch это как git pull только другое

vandud@macbook: zabbix [0] ? git fetch origin
remote: Enumerating objects: 83, done.
remote: Counting objects: 100% (83/83), done.
remote: Compressing objects: 100% (56/56), done.
remote: Total 83 (delta 36), reused 59 (delta 27)
Unpacking objects: 100% (83/83), 33.97 KiB | 644.00 KiB/s, done.
From gitlab.i-free.com:puppet-modules/zabbix
   0439074..7007c2d  master     -> origin/master

При использовании pull, git пытается сделать всё за вас. Он сливает любые внесённые коммиты в ветку, в которой вы сейчас работаете. Команда pull автоматически сливает коммиты, не давая вам сначала просмотреть их. Если вы не пристально следите за ветками, выполнение этой команды может привести к частым конфликтам
При использовании fetch, git собирает все коммиты из целевой ветки, которых нет в текущей ветке, и сохраняет их в локальном репозитории. Однако он не сливает их в текущую ветку. Это особенно полезно, если вам нужно постоянно обновлять свой репозиторий, но вы работаете над функциональностью, неправильная реализация которой может негативно сказаться на проекте в целом. Чтобы слить коммиты в основную ветвь, нужно использовать merge
Грубо говоря, по дефолту git pull — это шоткод для последовательности двух команд: git fetch (получение изменений с сервера) и git merge (сливание в локальную копию)

Если хотите использовать поведение Git по умолчанию (простое смещение вперёд если возможно — иначе создание коммита слияния): git config --global pull.rebase "false"
Если хотите использовать перебазирование при получении изменений: git config --global pull.rebase "true"

git push <remote-name> <branch-name> - Чтобы отправить вашу ветку master на сервер origin

git remote show <remote-name> покажет информацию об удаленном репозитории

vandud@macbook: zabbix [0] ? git remote show origin
* remote origin
  Fetch URL: git@gitlab.i-free.com:puppet-modules/zabbix.git
  Push  URL: git@gitlab.i-free.com:puppet-modules/zabbix.git
  HEAD branch: master
  Remote branches:
    TSUPP-3966736    tracked
    UPEADM-6749      tracked
    master           tracked
    services_exludes tracked
    test             tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (local out of date)

Эта команда показывает какая именно локальная ветка будет отправлена на удалённый сервер по умолчанию при выполнении git push
Она также показывает, каких веток с удалённого сервера у вас ещё нет, какие ветки всё ещё есть у вас, но уже удалены на сервере
И для нескольких веток показано, какие удалённые ветки будут в них влиты при выполнении git pull

git remote rename <oldname> <newname> позволяет переименовать удаленный репозиторий
git remote remove <remote-name> удаляет

vandud@macbook: zabbix [0] ? git remote
origin
vandud-test-zabbix
vandud@macbook: zabbix [0] ? git remote rename vandud-test-zabbix vandud
vandud@macbook: zabbix [0] ? git remote
origin
vandud
vandud@macbook: zabbix [0] ? git remote remove vandud
vandud@macbook: zabbix [0] ? git remote
origin

git tag Для отображение тегов. Чтобы фильтровать их согласно шаблону требуются параметры -l или --list

Screenshot_2021_02_02-12_49_03-2022-01-02-at-gittags.png

Git использует два основных типа тегов: легковесные и аннотированные
Легковесный тег — это что-то очень похожее на ветку, которая не изменяется — просто указатель на определённый коммит
А вот аннотированные теги хранятся в базе данных Git как полноценные объекты. Они имеют контрольную сумму, содержат имя автора, его e-mail и дату создания, имеют комментарий и могут быть подписаны и проверены с помощью GNU Privacy Guard (GPG)

Ключ -a создает аннотированный тэг - git tag -a v1.4 -m "my version 1.4"
git tag без ничего создает легковесный - git tag v1.4-lw

Чтобы повесить тэг на какой-то старый коммит нужно просто указать его хэш
Было:
Screenshot_2021_02_02-12_49_03-2022-01-02-at-tagno.png
Вешаем тэг

git tag 0.0 02800787767b7e55f9f741f65503e67727fbe5a5

Стало:
Screenshot_2021_02_02-12_49_03-2022-01-02-at-tagyes.png
Как видно появился тэг на первом коммите

По умолчанию, команда git push не отправляет теги на удалённые сервера. После создания теги нужно отправлять явно на удалённый сервер. Процесс аналогичен отправке веток — достаточно выполнить команду git push origin <tagname>

Можно отправлять тэги по одиночке, а можно добавить опцию --tags и отправятся все сразу
Screenshot_2021_02_02-12_49_03-2022-01-02-at-gittagspush.png

Чтобы удалить тэг у себя локально - git tag -d <tagname>
Но с сервера он так не удалится

vandud@macbook: gittest [0] ? git tag | cat
0.0
0.1
vandud@macbook: gittest [0] ? git tag -d 0.0
Deleted tag '0.0' (was 0280078)
vandud@macbook: gittest [0] ? git tag | cat
0.1

Для удаления тэга в удаленном репозитории есть два путя:

vandud@macbook: gittest [0] ? git push origin --delete 0.1
remote: . Processing 1 references
remote: Processed 1 references in total
To https://gitea.vandud.ru/vandud/test1.git
 - [deleted]         0.1
 
vandud@macbook: gittest [0] ? git push origin :refs/tags/0.1-lw
remote: . Processing 1 references
remote: Processed 1 references in total
To https://gitea.vandud.ru/vandud/test1.git
 - [deleted]         0.1-lw

Для перехода к тэгу - git checkout v2.0.0
Но репозиторий переходит в detached HEAD state, а это опасно
Если необходимо делать изменения в старой версии, то нужно делать отдельную ветку

git checkout -b version2 v2.0.0

Можно задавать алиасы в рамках гита

vandud@macbook: gittest [0] ? git config --global alias.last 'log -1 HEAD'
vandud@macbook: gittest [0] ? git last
commit 3479140973a4c5b53b20fb0a5b0fa5beef54c012
Author: Ivan Dudin <s@vandud.ru>
Date:   Sun Jan 2 05:55:21 2022 +0300

    commit for lightweight tag

Если вы захотите выполнить внешнюю команду, а не подкоманду Git, то следует начать команду с символа !. Продемонстрируем этот случай на примере создания псевдонима git visual для запуска gitk:

$ git config --global alias.visual '!gitk'
Progit

Вопросы

  1. Где хранится контент файлов добавленных в индекс?
  2. Что по владению ветками? Если у меня на репу девелоперские права, то я не могу пушить в мастер но могу создавать самодельные ветки, а также могу коммитить и пушить в чужие самодельные ветки. Каким образом происходит разделение куда я могу пушить, а куда нет?
Progit

3. Ветвление в Git

aoeu

git status -s

Команда git status имеет ключ -s/--short который делает вывод короче

Пути до файлов в выводе этой команды, в отличие от многих других команд git, относительны

vandud@macbook: testdir [0] ? git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   testfile3
	new file:   ../testfile

Пути до файлов в выводе отображаются относительно текущей директории


Screenshot_2021_02_02-12_49_03-2022-01-02-at-0gitstatusshort.png


Короткий формат имеет две разновидности

XY PATH
XY ORIG_PATH -> PATH

ORIG_PATH - откуда был скопирован/переименован файл
XY - двухбуквенный статус-код

Двухбуквенный статус имеет три разновидности:


Index File

https://github.com/git/git/blob/master/Documentation/technical/index-format.txt


Screenshot_2021_02_02-12_49_03-2022-01-02-at-gitindex.png


Самих данных в индексе нет(
Надо разбираться и дописывать
Не догоняю где хранится контент файлов после команды git add

Tips

Change origin

git remote remove origin
git remote add origin git@git.i-free.com:monitoring/infrastructure.git
git pull origin master

Switch from master to main

git checkout master
git branch -m master main
git fetch
git branch --unset-upstream
git branch -u origin/main