Оглавление
Всякие первоначальные настройки репозитория
-
Задание юзернейма и адреса электронной почты:
git config --global user.name "poppyfanboy" git config --global user.email "whatever@email.com"
-
Достать текущие конфигурации:
git config --get user.name git config --get user.email # вот такая штука ещё выводит фаел, из которого берутся конкретные конфиги git config --list --show-origin
У конфигов есть уровни (system, global, local), каждый следующий переопределяет предыдущий:
system
— конфиги для всех пользователей, хранятся в/etc/gitconfig
global
— конфиги для текущего пользователя, хранятся в домашней папке (например, в~/.gitconfig
)local
— конфиги для текущего репозитория, хранятся в./.git/config
-
Задать дефолтный текстовый редактор:
git config --global core.editor vim
Для других текстовых редакторов там может быть всё сложнее и вообще неочевидно, придётся гуглить отдельно. Но для таких целей вима будет более, чем достаточно.
-
Создание SSH-ключа:
ssh-keygen -t rsa -b 4096 -C "whatever@email.com"
-
Как создать репозиторий и сделать push в удалённый репозиторий на гитхабе:
git init # *создать фаелы проекта* git add . git commit -m "Initial commit" # или можно https://github.com/poppyfanboy/tetris-game.git git remote add origin git@github.com:poppyfanboy/tetris-game.git git push origin master
About Version Control
Система контроля версий (version control system, VCS) — это штука, которая запоминает изменения, сделанные в наборе файлов, запоминает, кто и когда сделал изменения, так что разные версии проекта можно было сравнить, вернуться к предыдущим версиям, или смерджить некоторые типы файлов.
В частности эта штука полезна в командной работе, например, когда кто-то занимается исправлением багов, а кто-то одновременно с этим добавляет какие-то новые фичи, так что нужно поддерживать две версии одного проекта. Ну и ещё можно ограничивать возможность изменения каких-то файлов проекта.
Виды VCS:
- Local VCS — все изменения сохраняются локально.
- Centralized VCS — есть отдельный сервер под VCS. Это упрощает работу в команде, позволяет ввести контроль над тем, кто что может делать в проекте. Минусы в том, что если сервер упадёт, то вся работа остановится, и ещё вроде как ветвление сложнее, потому что серверу придётся постоянно поддерживать кучу веток, а в DVCS ветки изначально могут быть локальными и потом они сливаются с мастером.
- Distributed VCS — каждый клиент делает полную копию проекта и работает с ней. Плюс в том, что все изменения локальны, можно работать без подключения к серверу (в том числе доступна вся история изменений проекта) и потом запушить свои изменения, нет задержки при работе с удалёнными файлами. Минус в том, что если, например, в проекте много больших бинарных файлов, которые плохо сжимаются, то потребуется много места для хранения проекта локально.
Обычно VCS хранят изменения между файлами, а не сами файлы разных версий, чтобы не надо было хранить несколько одинаковых копий одного файла. Такое называется delta-based version control. А когда для каждой версии хранится отдельный полный набор фаелов (кроме тех, что повторяются полностью, повторяющиеся хранятся только в одном месте и следующие снапшоты просто ссылаются на них), то это называется snapshot-based version control.
Последовательность изменений можно представить в виде направленного ацикличного дерева: узлы — это отдельные revisions проекта, они упорядочены в порядке внесения изменений. Выделяется trunk или mainline дерева, который соответствует мастеру в гите, от него могут отходить ветки, которые потом могут сливаться с главной веткой. HEAD — это текущий конец главного ствола дерева. Baseline, tag, label — это всё одно и то же, это способ пометить какие-то отдельные снапшоты как наиболее важные (какой-то стабильный релиз или ещё что-то такое). Check out — создание рабочей копии, check in — наоборот, то же самое, что и коммит, внесение изменений.
Файлы в гите идентифицируются во SHA-1 хешу, с помощью него же обнаруживаются все внесённые изменения. Файлы в гит-проекте могут находиться в одном из трёх состояний (modified, staged, commited):
- modified — фаел был изменён, но изменения ещё не были закоммичены
- staged — это просто означает, что файл был помечен для того, чтобы войти в следующий коммит. Совокупность всех таких файлов — это staging area.
- commited — это уже, когда изменения были внесены в локальную базу данных
Git Basics
git clone. Я не знаю, почему, но у меня эта штука сначала, когда я использовал HTTPS URL, попросила авторизоваться с юзернеймом и паролем, и не дала склонировать чужой репозиторий. А когда я попробовал SSH URL, то всё спокойно склонировалось и с HTTPS URL проблем больше тоже не было. Так что, видимо, SSH URL — это предпочтительный вариант тут.
Эта команда тупо всё копирует, все снапшоты, всё-всё.
git clone git@github.com:username/repository.git
Помимо modified/staged/commited состояний файлы ещё могут быть tracked и untracked. Untracked — это фаелы, которые не были включены в последний коммит, то есть те, которые были созданы вот прямо сейчас. Tracked файлы — соответственно, фаелы, которые в последнем коммите есть. Из состояния untracked файлы сразу попадают в staged, после чего в commited.
Short status. Оказывается, в дурацком и громоздком git status
есть флаг -s
или --short
, который выводит всё в более кратком виде:
git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
# ? - untracked
# A - added to the staging area (из untracked в staging area)
# M - modified
# R - renamed
При этом состояния файлов выводятся в два столбца: в первом — состояние файла в staging area, во втором — состояние в working directory. То естьMM
означает, что файл изменили, добавили в некст коммит, а потом снова изменили. Или, например, AM
— файл создали, добавили в коммит, а потом изменили.
Нормальный git diff. Кстати, оказывается, ещё помимо тупого неудобного git diff
есть
git difftool [--staged] --tool=vimdiff
Который красиво всё выводит side-by-side в виме. А простой git diff
всё выводит тупо и непонятно вообще. И я ещё не знал, что git diff
выводит всё, что ещё не было staged, а git diff --staged
наоборот — всё, что было staged.
Skipping staging area. Можно тупо добавить флаг -a
при коммите, чтобы все изменения сами автоматически добавились. При этом он добавляет в коммит только те файлы, которые изначально были tracked, untracked файлы в коммит не попадают.
Ignoring Files
Ну, понятно, что это всё делается в .gitignore
, но я не знал, что можно иметь дополнительные .gitignore
в поддиректориях, которые переписывают правила, определённые в глобальном .gitignore
в корне репозитория. А ещё можно использовать вот такие штуки:
- Если в начале указать
/
, то он не будет игнорировать рекурсивно то, что идёт после слеша, а только то, что видно из текущей директории. Ну, то есть, если написатьbin/
, то он будет игнорить все папки с таким названием, а если/bin/
, то только одну папку в корне. - Если в начале указать
!
, то это добавит паттерн в исключение. Так что можно, например, заигнорить все jpg-картинки, кроме какой-то одной. - А ещё тут работают glob patterns — это такие упрощённые регулярные выражения специально для названий файлов и папок. Так что тут можно писать штуки вроде
[abc]
,*?
,[a-zA-Z][0-9]
. При этом*
никак не взаимодействует со штуками в квадратных скобках, это просто нисколько или сколько-то произвольных символов. - А ещё есть
**
— это любые поддиректории. То есть вот такоеa/**/*.pdf
заигнорит все pdf-файлы в любых поддиректориях папкиa
.
Removing Files
Удаление вручную vs git rm
. Если просто вручную удалить файл из working tree, то в следующем коммите он всё равно останется, нужно будет дополнительно после удаления сделать git rm %DELETED%
или git add %DELETED%
. И, я так понимаю, вообще так лучше не делать, потому что можно по ошибке что-то удалить и вернуть обратно будет нельзя.
git rm
. Так что луче использовать git rm %FILENAME%
, который добавляет факт удаления файла в следующий коммит и убирает его из working tree, если надо полностью удалить файл. Или же git rm --cached %FILENAME%
, если надо оставить файл в working tree, а только удалить из БД (например, если забыл добавить какие-то файлы в gitignore и случайно закоммитил их).
Forced rm. Ещё один повод использовать эту штуку — это то, что он немного безопаснее, потому что не удаляет так просто файлы уже попавшие в staging area или просто изменённые, потому что все изменения будут потеряны. Нужно указать флаг -f
, если хочешь удалить такие файлы.
Escaping glob patterns. Оказывается, вот эти штуки работают в том числе и в command line гит командах, но надо эскейпить звёздочки (или засовывать аргумент в кавычки), чтобы использовались glob patterns, определённые в гите, а не те, которые в командной строке. В большинстве случаев, правда, это не играет роли, но:
- Glob patterns в командной строке вроде как не рекурсивны в отличие от гитовских. Но у меня вроде как рекурсивны и те, и те почему-то.
- А ещё в командной строке по умолчанию glob patterns будет работать только для файлов, которые она видит сама. То есть, если, например, сначала сделать
rm src/*.class
, а потомgit rm src/*.class
, то вторая команда в теории должна зафейлиться, потому что в папкеsrc/
физически файлов больше нет. Так что правильно было бы заэскейпить:git rm src/\*.class
. Но у меня опять же, почему-то работает и без этого.
Moving (renaming) Files
Тут основное — это то, что если сделать mv %FROM% %TO%
, то в git status
он распишет это как удаление файла %FROM%
и создание файла %TO%
, так что нужно будет сделать вот это в итоге, чтобы переименовать файл:
mv FROM TO
git rm FROM
git add TO
Но при этом после всего этого при коммите гит, видимо, проверяет хеши файлов и определяет, что это один и тот же файл, который просто переместили, и помечает это всё как переименование.
Можно ещё явно сразу переместить с помощью одной команды вместо трёх: git mv FROM TO
.
Viewing the Commit History
git log --oneline
— просто отображает все коммиты в хронологическом порядке от последнего к самому первому, каждый коммит в одну строку (сокращённый хеш и commit message).
git log -p -<N>
— для N последних коммитов отображает все измения, которые были сделаны (короче, просто diff
для последних N коммитов). Ну и, видимо, для любых видов git log
можно ограничить количество коммитов.
git log --stat
— показывает, какие файлы были изменены и сколько там строк удалено или добавлено.
git log --pretty=format:"%h - %an, %ar : %s"
— можно самому задать формат вывода коммитов. Там есть список всяких опций для вывода. Вроде как это для того, чтобы можно было вывестти всю эту информацию независимо от версии гита (может, дефолтный формат вывода мог поменяться) или просто, чтобы было легче распарсить это.
git log --oneline --graph
— выводит историю коммитов с визуализацией веток. При этом он дополнительно всё немного упрощает: если параллельно с ответвлением в мастер не было коммитов, то это ответвление не будет отображено, всё будет выведено в одну линию.
Фильтры. Также там есть куча опций для того, чтобы отфильтровать вывод git log
. Самое полезное — это опции для дат: --since/after/before/until=<date>
и фильтр по наличию каких-то ключевых слов в commit messages: --grep/invert-grep=<pattern>
(ещё один повод придерживаться каких-то соглашений при коммитах — чтобы потом можно было отфильтровать, скажем, по слову “add”). Ну и там ещё есть всякие опции, которые влияют на то, как будут обрабатываться паттерны. Ну, короче, там уже нет смысла без необходимости что-то понимать. Ну и ещё можно ограничить вывод коммитов только для какой-то директории, для этого просто в конце надо дописать git log ... -- <path>
.
Git pickaxe. Ещё одна, видимо, полезная (потому что я не очень понимаю, как её на практике использовать) штука — это вывод тех коммитов, которые изменили то, насколько часто встречается какая-то строка в source файлах: git log -S <string>
. Видимо, можно найти те коммиты, в которых добавился/убрался новый вызов какой-то функции. Хм, ну или можно определить, в какой момент была написана какая-то строка кода. Можно скомбинировать с флагом -p
, чтобы ещё сразу увидеть код в контексте которого указанный кусок кода был добавлен/удалён.