Вернуть файл к состоянию последнего коммита (аналог svn revert
) можно командой git checkout файл
.
Уточнение: файл будет возвращен к зафиксированному состоянию, если после коммита была явно дана команда git add файл
. То есть, откатываются изменения, сделанные после git commit
или git add
.
Если файл был удален, то нужно указать версию; например git checkout HEAD файл
.
Так можно откатить и всю папку (в т.ч. рабочий каталог): git checkout .
.
Для отмены изменений на уровне рабочей копии служит команда git reset
. Она "отматывает" историю изменений назад до указанного момента (в том числе отменяет коммиты), не трогая физические файлы:
git reset файл
С ключом --hard
переписываются и файлы.
git reset --hard HEAD^
- отменить последний коммитgit reset --hard HEAD~2
- отменить два последних коммита
Отменять коммиты можно, только если они не опубликованы в общий репозиторий (не было git push).
Отменить добавление нового файла в индекс можно командой git rm --cached файл
.
- история изменений, по одной строке на каждый коммит -
git log --oneline
- вариант сжатого формата истории коммитов:
--pretty=format:"%h %cd %s"
- то же с выделением цветом:
--pretty=format:"%C(auto)%h %C(green)%cd%C(reset) %s"
(опция%C(auto)
включает использование цветов по умолчанию для стандартных частей отформатированной строки, эти цвета недоступны через прямое указание) - метка:
--pretty=format:"%(describe:tags=true)"
- формат даты задается отдельным аргументом:
--date=format:"%d.%m.%Y %H:%M"
- всё вышеперечисленное:
git log --pretty=format:" %C(yellow)%h %Cgreen%cd%Creset %s %C(red) %(describe:tags=true)%C(reset)" --date=format:"%d.%m.%Y %H:%M"
Полный список меток можно посмотреть командой git help log
в секции pretty.
- распечатать сообщение указанного коммита:
git log (коммит) --format=%B -n 1
в качестве идентификатора коммита достаточно первых пяти символов его хэш-суммы либо специального имени типаHEAD
- дополнить последний коммит в одну команду с сохранением сообщения:
git commit --amend --no-edit
- фильтрация по строке в тексте коммита:
git log --grep="шаблон"
(можно указывать опцию несколько раз, результат будет объединением; при--all-match=1
- пересечением) - вывод истории только для локальной ветки, отпочковавшейся от
master
:
git log master..
(обратить внимание на две точки в конце!)
подробнее см. https://stackoverflow.com/a/4649377/
Без клонирования репозитория к себе не обойтись, но это можно сделать в сокращенном виде (см. https://stackoverflow.com/a/60952814/589600):
git clone --filter=blob:none --no-checkout --single-branch --branch master git://some.repo.git .
git log
git log --all --full-history -- путь/файл
или, если не известен путь:
git log --all --full-history -- "**/файл"
--all
- предписывает команде охватить все коммиты всех веток--full-history
- (выясняется)--
- разделитель аргументов
Источник: stackoverflow.com.
Посмотреть последнюю версию файла можно с помощью команды
git show HEAD^:путь-к-файлу
Первую строку нужно отделять от остального текста двойным переводом строки. Тогда в короткий формат git log
войдет только она. В противном случае туда попадет сообщение полностью, что снизит удобочитаемость списка.
Чтобы редактор vi
открывался сразу в режиме ввода, файл ~/.gitconfig
должен содержать секцию [core]
:
[core]
editor = 'vim' -c 'startinsert'
Добавить сокращение: git config alias.<имя> '<команда>'
. Лушче давать команду с ключом --global
, чтобы иметь к нему доступ из-под своего пользователя ОС в любом месте.
Например:
git config --global alias.st 'status -s'
git config --global alias.lg 'log --pretty=format:" %C(auto)%h %Cgreen%cd%Creset %s" --date=format:"%d.%m.%Y %H:%M"'
git config --global alias.ci 'commit --no-status'
Посмотреть список сокращений: git config --get-regexp alias
. Без уточнения области видимости типа --system
или --global
будут показаны сокращения всех уровней. Область видимости нужно указывать до alias
.
Удалить сокращение можно командой git config --unset alias.имя
.
- откатить рабочий каталог к ревизии № rev. -
git checkout (rev.)
(допустим четырехзначный формат);git checkout (rev.) -- [файл]
- откатить конкретный файл - посмотреть старую версию файла (в т.ч. удалённого):
git show коммит:path/to/file
- убрать файл из-под контроля git, оставив его при этом на диске:
git rm --cached файл
; очень важна при этом опция--cached
- без неё команда удалит файл и с диска git commit --no-status
- начать с пустого сообщения для коммита в редакторе, безgit diff
git ls-files -o
- просмотр списка неотслеживаемых файлов;--exclude-standard
- исключить из списка игнорируемые,--directory
- не разворачивать содержимое неотслеживаемых каталогов (в список тогда попадут также пустые каталоги - они никогда не отслеживаются)git status --ignored
- показывать полный список игнорируемых файлов
- клонировать репозиторий в непустой каталог (
git clone ...
) нельзя; инициализировать в непустом каталоге (git init
) — можно - конфликты при
content merge
без прямого пересечения бывают, если изменения в разных ветках сделаны в соседних строках; если между зонами изменений есть хотя бы одна строка, слияние проходит автоматически - безусловно принять свою версию при конфликте можно, дав команду
git checkout --ours файл
, а затем -git add файл
- при назначении вышестоящего репозитория (куда будет отправляться
git pull
) не важно, был ли он создан раньше дочернего; если дочерний был создан как отдельно стоящий (не клонированием), то зависимость устанавливается двумя командами:git remote add origin (путь к файлу .git)
- при первом
git push
нужно явно указать название репозитория (origin
), ветку (master
), и аргумент-u
(--set-upstream
):
git push -u origin master
- чтобы назначить вышестоящий репозиторий, необязательно делать
push
; специально для этого можно дать командуbranch
с тем же аргументом:git branch -u origin master
, однако для этого вышестоящий репозиторий уже должен иметь коммиты, с пустым такое не получится
- поменять upstream-ветку можно с помощью
git remote set-url (ветка) (url)
, например:
git remote set-url origin ssh://...
- выявить причину неполадок при соединении по SSH поможет просмотр опции
git config core.sshCommand
и установка её в"ssh -v"
(как было, например, в в этом случае) - включить цветной вывод (если уже не включен):
git config --global color.ui true
- отправить локальную ветку в вышестоящий репозиторий:
git push origin ветка
- получить id самого первого коммита:
git rev-list --max-parents=0 HEAD
(см. stackoverflow № 1007545); потом можно, например, просмотреть его сообщение -git log <id>
- чтобы переключиться на ветку из вышестоящего репозитория, которой еще нет в локальном, нужно сначала выполнить
git fetch origin ветка
и уже потом на переключиться на неё -git checkout ветка
Потому что возникают ошибки из-за разных идентификаторов пользователя ОС, которые на каждой машине уникальны, даже если имена пользователей совпадают (видимо, идентификатор присваивается самим git):
fatal: detected dubious ownership in repository at 'D:/s/portliss/1/vendor/one234ru/html-dynamic'
'D:/s/portliss/1/vendor/one234ru/html-dynamic' is owned by:
'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxx'
but the current user is:
'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxx'
To add an exception for this directory, call:
git config --global --add safe.directory D:/s/portliss/1/vendor/one234ru/html-dynamic
От этих ошибок можно избавиться, выполнив команду из сообщения.
Если используется Composer, и были подключены пакеты в виде git-репозиториев, то эти ошибки возникнут для каждого из них.
Избавиться от ошибок во всех репозиториях сразу можно с помощью варианта команды для всех каталогов: --add safe.directory "*"
. Но лучше вообще избегать этой ситуации.
git checkout master
git merge --squash dev
git checkout --theirs .
git commit -a
Полезно, когда при разработке было сделано много промежуточных коммитов, которые по отдельности интереса не представляют, и все изменения просто нужно перенести в основную ветку в виде единственного коммита, чтобы не засорять историю изменений. При этом dev-ветка должна быть впереди master!
Для этого используется опция --squash
команды git merge
.
Если полученные изменения относятся к существующим файлам, они будут помечены как конфликт. Нужно скомандовать git безусловно принять вновь пришедшие изменения - git checkout .
с опцией --theirs
.
Частный случай: пустая ветка master (точнее, ее отсутствие). Такое бывает в начале разработки.
Когда пора делать первый коммит в основную ветку, нужно дать команду git checkout --orphan master
. Она добавит все файлы из рабочего каталога текущей ветки в новую, останется только выполнить git commit
.
git pull origin master
Это намного удобней, чем давать последовательность команд
git checkout master
git pull
git checkout branch_name
git merge master
В том числе потому, что некоторые редакторы (например, PhpStorm) незамедлительно реагируют на физическое наличие на диске открытых файлов, и если файл удаляется с диска (что может происходить при переключении на master-ветку, если файл добавлен в ветке branch_name), то вкладки с ними автоматически закрываются и их приходится потом открывать заново.
-
Получить суммарные изменения между двумя коммитами:
git diff коммит_1 коммит_2 каталог
-
Получить только имена изменённых файлов позволяет опция
--name-only
. Вот так, например, выглядит команда для получения такого списка относительно предпоследнего коммита:git diff --name-only HEAD~1
При желании можно, например, добавить маски файлов:
git diff {...} *.tpl *.css *.js
.Вывести не только имена и статусы - команда
--name-status
. -
Получить изменения по дате:
git diff HEAD 'HEAD@{2020-10-01 12:00:00}' -- файл
Вместо точного времени можно указывать интервал относительно текущего момента - 'HEAD@{3 weeks ago}'
, а также указывать дату без временной части.
Источник - stackoverflow.com (a/9658178 и a/41303758)
-
Вывести список файлов, в которых есть конфликты слияния:
git diff --diff-filter=U
U
- unmerged.Файлы остаются помеченными как имеющие конфликт, даже если если метки конфликтов из них удалены (вручную или с помощью
git checkout --theirs
). Пометка исчезает, только когда файл добавляется в индекс вручную. Впрочем, для выполнения коммита это необязательно. -
Исключить файлы из вывода -
'!:маска'
, например:git diff --name-only HEAD~1 ':!*.scss'
Посмотреть список всех локальных настроек: git config --list
Посмотреть список всех возможных настроек: git help config
, секция Variables
Есть три уровня действия настроек: системный, текущего пользователя и конкретного репозитория (подробнее см. здесь).
Файлы .gitconfig
имеют структуру:
[раздел]
переменная = значение
которая соответствует команде
git config раздел.переменная = значение
Выполнение команды git config
и редактирование файла .gitconfig
вручную имеют одинаковый эффект.
Для более удобной работы в оболочке bash
(в т.ч. под Windows) существуют специальные shell-скрипты. Они размещены в публичном репозитории git (см. каталог contrib/completion).
Чтобы использовать заложенные в них возможности, нужно скачать их из репозитория куда-нибудь на рабочий компьютер (например, в свой же домашный каталог) и подключить в файле .bashrc
.
Например, автодополнение команд по нажатию Tab (позволяет не печатать команды полностью; действует как на подкоманды, так и на их опции):
curl --output-dir ~ -O https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
# -O - использовать имя файла из ссылки
. ~/git-completion.bash
Для включения в системное приглашение информации о репозитории git нужно подключать файл git-prompt.sh
и вызывать функцию __git_ps1
:
export PROMPT_COMMAND='PS1="$(__git_ps1)"'
# даст приглашение типа
# (название ветки)
То, что возвращает __git_ps1
, определяется значениями переменных типа GIT_PS1_*
(например, GIT_PS1_SHOWDIRTYSTATE
- показывать ли звёздочку в случае, если есть неотслеживаемые файлы), полный список и описание которых можно найти в самом sh-файле.
export PROMPT_COMMAND='PS1="$(__git_ps1)"'
GIT_PS1_SHOWDIRTYSTATE=1
Также перечислены в статье.
Если git-часть приглашения требуется окружить пробелами или другими символами, нужно передать функции аргумент с меткой %s
:
git_command='__git_ps1 "%s "'
# даст приглашение типа
# (название ветки) >
Проверить работу функции можно непосредственно из командной строки, находясь в каталоге репозитория:
echo __git_ps1
Хуки - это скрипты, которые могут выполняться при наступлении различных событий: при создании коммита, при получении коммита (на сервере) и пр. Подробнее - тут (en).
Пример - хуки для Composer.
При обычном клонировании связанные репозитории запрошены не будут. Запросить их можно, указав команде ключ --recurse-submodules
:
git clone --recurse-submodules (repository) (local folder)
Рекомендуется всегда выполнять команду с этим ключом, т.к. подмодули, как правило, содержат файлы, необходимые для работы всей системы. (Вообще странно, почему эта опция не включена по умолчанию.)
Если требуется внести в подчиненный репозиторий изменения, рекомендуется проверить, что указатель HEAD
установлен на ветку master
(бывает, что репозиторий имеет отсоединенный HEAD
), просто посмотрев на результат команды git branch
.
Подмодули также нужно инициализировать в upstream-репозитории, если туда была отправлена локальная ветка (git push origin ветка
):
Работа над подмодулем должна быть локализована в каком-то подкаталоге. Перед тем, как инициализировать подмодуль в проекте, его нужно опубликовать на github или каком-то другом хранилище, после чего выполнить команду
git submodule add https://...
Докальный каталог, где велась разработка подмодуля до его публикации, нужно переименовать или вообще удалить, чтобы он не мешал исполнению команды, которая попытается создать каталог с тем же именем.
После того, как подмодуль таким образом зарегистрирован, нужно сделать коммит в основной проект и отправить его в вышестоящий репозиторий. После этого там появится каталог подмодуля, но он будет пуст (весьма странно, что git так работает, потому что при такой схеме можно отправить в вышестоящий репозиторий проект в нерабочем состоянии). Подмодуль нужно вручную инициализировать. Для этого в каталоге проекта в вышестоящем репозитории нужно дать команду
git submodule init (путь к каталогу подмодуля)
git submodule update (путь к каталогу подмодуля)
Команда вида git submodule init (локальный каталог)
не сработает, т.к. она ожидает пути к централизованному хранилищу, откуда потом можно будет забирать изменения.
Можно, однако, сначала перейти в каталог подмодуля, а потом последовательно выполнить команды git submodule init
и git submodule update
без указания каталога, как это сделано в примере из документации.
git submodule update --recursive --init подмодуль
При внесении изменений в подмодуль в вышестоящем репозитории git pull
из него не обновляет подмодуль, вместо этого его каталог помечается как измененный. Чтобы изменения вступили в силу, нужно выполнить команду git submodule update подмодуль
.
В конце работы рекомендуется просмотреть содержимое файла .gitmodules
и исправить имена модулей с автоматически сгенерированных из путей на более удобные и понятные.
Обновление модулей не происходит автоматически при отправке изменений в вышестоящий репозиторий, это нужно делать отдельной командой, вручную или через хуки.
Если вышестоящий репозиторий при этом является production-копией, как это имеет место в схеме push-to-deploy, обновление модулей должно следовать сразу же за командой push
, т.к. сразу после push
система может оказаться в нерабочем состоянии: код проекта обновился, а подчиненные модули - нет.
Если планируется не только чтение, но и внесение изменений, нужно соединяться не по https, а по ssh, причем *обязательно от имени пользователя git: [email protected]
.
Чтобы это сработало, нужно добавить свой публичный SSH-ключ через настройки учетной записи. Подробная инструкция здесь.
Проверить работу ключа можно, выполнив команду ssh -vT [email protected]
(см. docs.github.com).
Поменять URL репозитория можно так:
git remote set-url origin [email protected]:<user>/<repository>.git
При первой отправке изменений в пустой, только что созданный, репозиторий нужно явно установить (обычно - продублировать) название будущей ветки. Делается это с помощью ключа -u
/--set-upstream
:
git push --set-upstream origin master
или, в общем случае
git push --set-upstream origin ветка
Такая процедура связана с тем, что в пустом репозитории на сервере github веток ещё нет. Подробнее см. https://stackoverflow.com/q/17096311/
Делать это можно только при наличии коммитов в локальной ветке. При пустой ветке команда не сработает.
Также нельзя обойтись одной только командой git push
без создания репозитория через веб-интерфейс Github.
Если прямое соединение с помощью ssh
проходит успешно, а команды git
не проходят, нужно посмотреть вывод git config core.sshCommand
- не указано ли там имя пользователя явно (как было в этом случае).
Технически любой gist - это просто репозиторий на Github, процедура работы с которым упрощена:
- названием является хэш типа
f3cef177af2a86fbf0a8deb46c515b3b
- при внесении правок через веб-интерфейс не требуется заполнять сообщения commit (что видно в
git log
)
Метки (tags)
Часто применяются для указания номеров версий, которые потом используются в т.ч. Composer'ом.
git tag -l
- просмотреть список меток
Создать метку без подписи:
git tag название
# например, git tag v1.0
Создать метку с подписью (аннотированную):
git tag -a v1.0 -m "Подпись"
Метка будет приписана к последнему коммиту.
Можно назначать метку задним числом для прошлых коммитов. Для этого контрольную сумму коммита или ее часть нужно указать в конце команды:
git tag -a v1.0 abcde01
Метки сами не отправляются в вышестоящий репозиторий. Нужно либо отправлять каждую поименнно:
git push origin имя_метки
либо отправить их все сразу:
git push origin --tags
Ключевой момент: открываем composer.json
и вручную добавляем туда запись в раздел repositories
:
"repositories": [
{
"type": "git",
"url": "ssh://user@host:port/path"
}
]
Также обязательно нужен раздел autoload типа
{
"autoload": {
"psr-4": {"Namespace\\": "./"}
}
}
После этого нужно дать команду composer require
. При этом надо иметь в виду, что если проекту не назначаются версии в виде меток git (tags), команде нужно явно указать ветку в виде названия с префиксом dev-
. Например, для ветки master
команда будет выглядить так:
composer require vendor/package:dev-master
В противном случае обычная команда выдаст ошибку:
Could not find a version of package vendor/package matching your minimum-stability (stable). Require it with an explicit version constraint allowing its desired stability.