GIT F.A.Q

Категория: GIT

Статья-сборник примеров решения частых задач при использовании GIT - работа с историей изменений, создание, удаление, публикация веток, удаленные (remote) репозитории git, сокрытие и отмена локальных изменений, просмотр информации о коммитах и истории...

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

  • Визуальный справочник: http://marklodato.github.io/visual-git-guide/index-ru.html

F.A.Q по GIT дополняется..

Рекомендую пройти хороший интерактивный курс по использованию GIT из консоли - http://githowto.com/

Стратегии pull/push

При запуске pull/push без указания веток вы можете выбрать желаемую стратегию (поведение Git). Стратегия определяет что будет делать Git при запросе на получение или отправку изменений без явного указания веток.

Установить стратегию для pull/push:

git config pull.default simple          # локальный конфиг
git config --global pull.default simple # глобальный конфиг
git config --global push.default simple

Доступные стратегии:

simple   Текущая ветка (при условии что имя удаленной ветки такое же)
matching Все ветки, имена которых совпадают
<alias>  Явно указать имя ссылки на удаленный репозиторий
current  Deprecated! Переименован в simple со 2й версии Git. При current обновляются ВСЕ ветки!
Поведение git pull

После какого-то обновления Git, вы можете наблюдать сообщение запуске pull без явного указания веток: If you wish to set tracking information for this branch you can do so with: git branch --set-upstream-to=origin/stage-api-fast-filters. Это означает, что вам необходимо связать текущую ветку с удаленной.
Мы знаем, что в локальном git хранилище могут быть ссылки на несколько удаленных репозиториев, которые имеют ветки с одинаковым названием. При этом, сходство имен локальной ветки и ветки в каком-нибудь удаленном репозитории - никак не могут однозначно указывать на родство этих веток. Поэтому, вам нужно явно указать связь локальной ветки с удаленной:

git branch --set-upstream-to=origin/their_branch my_branch

В дальнейшем, при создании веток, сразу связывайте новую ветку с удаленной:

git branch my_branch origin/their_branch
## или
git checkout -b my_branch origin/their_branc
Поведение git push

При попытке отправить изменения на сервер без явного указания веток, вы можете видеть сообщение: warning: push.default is unset (не указана стратегия или ссылка на репозиторий по умолчанию). Вам необходимо задать желаемое поведение при запуске git push без параметров в конфиге с помощью опции  push.default:

git config --global push.default simple

Индекс

Отменить индексацию файла (сам файл не будет удален из каталога):

git rm --cached <file>
git rm -r --cached <some/directory/> # или каталога

Игнорировать (не отслеживать) последующие изменения файла:

git update-index --assume-unchanged app/database/production.sqlite

Для отмены игнора:

git update-index --no-assume-unchanged app/database/production.sqlite

Смотрим список файлов изменения в которых игнорируются git'ом:

git ls-files -v | grep '^h'

Удалить из индекса все удаленные файлы:

git status --porcelain | grep ' D' | awk '$1 == "D" {print $2}' | xargs git rm --cached  

История коммитов

Показать историю коммитов со статистикой по файлу (список файлов и количество измененных строк):
git log --stat <some/file_or_dir>
Краткая история коммитов определенного пользователя в формате hash comment:
git log --author=author@onedev.net --pretty=oneline
Последние 10 коммитов автора с комментариями:
git log --author=author@onedev.net --pretty=oneline -10
Список коммитов, которые не отправлены в удаленный репозиторий:
git log origin/имя_ветки..
git log --branches --decorate --not --remotes=origin # во всех ветках
Компактная история коммитов (хеш дата <автор> | коммент):
git log --pretty=format:"%h %ad <%an> %s" --date=short
http://git-scm.com/book/ru/Основы-Git-Просмотр-истории-коммитов 
Одно строчный формат истории коммитов с информацией об авторах:
git log --pretty=format:"%h %ad %an <%ae>" --date=short

История изменений

Показать изменения не внесенные в индекс:

git diff file_name

Показать изменения внесенные в индекс:

git diff --cached file_name

Показать внесенные вами изменения с последнего коммита:

git diff HEAD

Список измененных файлов от указанного коммита до текущего состояния:

git diff --name-only <HASH>

Список изменений из одного определенного коммита:

git diff <HASH>^ <HASH>

Список изменений от определенного коммита до последнего (текущего состояния):

git diff <HASH>^ HEAD

Расширенный поиск

Как найти ветку по коммиту:

git branch --contains <commit>

Как найти самый первый коммит по определенному тексту (может занять много времени):

git grep 'some text' $(git rev-list --all) | tail -n 1 | cut -c -10

Как найти самый первый коммит, в котором добавили файл:

git log --diff-filter=A -- path/file.php

Удаленные репозитории

Информация об удаленных репозиториях:

git remote -v          # показать алиасы и URL удаленных репозиториев
git remote show origin # детальная инфа об удаленном репозитории

Добавление и удаление удаленных репозиториев:

git remote add <алиас> <URL>
git remote rm <алиас>
Пример
git remote add origin git@github.com:vendor/project-name.git

Ветки / branch

Посмотреть все ветки репозитория:

git branch -a
git branch -r # ветки на сервере

Создать удаленную ветку на сервере:

git push origin origin:refs/heads/new_branch

Создать/получить локальную ветку my_branch на основе удаленной their_branch:

git checkout -b my_branch origin/their_branch # создать новую ветку
git checkout --track origin/their_branch      # cвязать текущую ветку с репом

Опубликовать локальную ветку в удаленном репозитории:

git push origin some_branch:some_branch
git push -u origin my_branch ## опубликовать текущую ветку и связать эти ветки
Примечание

При первой публикации ветки необходимо указывать имя удаленной ветки для того, чтобы связать локальную и удаленные ветки. При последующих pull'ах имя удаленной ветки можно опускать.

Переименовать текущую ветку:

git branch -m new-branch-name

Удалить ветку в удаленном репозитории:

git push origin :some_branch

Удалить локальную ветку:

git branch -d some_branch

Сокрытие изменений

Скрыть рабочие изменения (изменения вносятся в стек):

git stash

Будут скрыты только изменения файлов, которые находятся в индексе. Новые файлы не будут скрыты (перемещены в стек/тайник) - заначка.

Посмотреть список тайников (стек спрятанных изменений):

git stash list

Применить (вернуть из тайника) последнюю заначку:

git stash apply

Применить одну из старых заначек:

git stash apply stash@{2}

Показать все содержимое тайника:

git stash show

Удалить все тайники:

git stash clear

Отмена изменений

Отменить последний коммит:
git reset --soft HEAD^
Примечание
Внесенные изменения будут сохранены. Это сработает только в том случае, если вы еще не опубликовали свои изменения!

Отменить все изменения в файле до последнего коммита:
Внимание!
Вы НЕ сможете восстановить изменения после этой команды! Убедитесь, что вы отменяете изменения в нужном файле.
git checkout -- Controller/ContentController.php

Отменить конфликтный merge:

git reset --hard HEAD

Получить версию файла из определенной ветки:

git checkout some-branch -- file.txt

Восстановить файл из указанного коммита:
git log --pretty=oneline # смотрим список коммитов, выбираем нужный и ...
git checkout 129c39e9b93a19c app/controllers/Controller.php # Возвращаем нужный нам файл
Извлечь определенную версию файла из истории GIT в указанный путь
git show 129c39e9b93a19c:app/controllers/Controller.php > ~/Controller.php

Отменить последний коммит, который ушел на сервер:

git revert HASH
git revert REVERT-HASH # вернуть коммит ("отменить отмену" коммита)

* Команда revert собирает patch отражающий нужный коммит, разворачивает его и добавляет новый коммит для отката (коммит, конечно же, будет видет виден в истории). Таким образом, при merge и push нет необходимости использовать --force. 

Отменить 3 последних коммита, которые уже ушли на сервер:

git reset --hard HEAD^^
git push --force origin origin some-branch
Внимание!

Не используйте без критической на то необходимости --force:

git push --force origin origin some-branch

Это единственная команда Git, которая делает необратимые изменения!

Вообще, делать reset --hard или rebase в опубликованной истории крайне не желательно. Лучше создать новую ветку, собрать в ней нужное состояние и слить ее с вашей веткой.

Откатить коммиты и оставить изменения в виде не проиндексированных:

git reset --mixed HEAD^^

Конфликты

Слить ветки предпочитая чужие изменения (-X--strategy-option):

git merge -X theirs some_branch

Оставить локальную версию конфликтного файла (из текущей локальной ветки) или принять изменения из другой ветки (или ветки удаленного репозитория):

git checkout --ours file.php   # оставить файл текущей (локальной) ветки
git checkout --theirs file.php # принять изменения из вливаемой ветки

Конфликтные участки кода:

<<<<ours
 Наши изменения
========
 Чужие изменения
theirs>>

Изменение истории коммитов

cherry-pick

Влить определенный коммит в текущую ветку (наложить патч, из любой ветки):

git cherry-pick <hash>

rebase

Источник (спасибо автору за разъяснение): http://tonyganch.com/git/rebase/

Внимание!

git rebase меняет hash коммитов! Поэтому, git rebase можно делать только в локальном репозитории, и только до публикации коммитов (push)!

Слияние коммитов

Начать процесс интерактивного слияния 2-х последних коммитов:

git rebase -i HEAD~2
Примечание

После запуска rebase - откроется текстовый редактор. Сверху идут старые коммиты, внизу более свежие.

Доступные действия с коммитами в текстовом редакторе:

pick, p
Указать как основной коммит-приемник при слиянии.
fixup, f
Слить коммиты, используя комментарий основного pick-коммита.
squash, s
Слить коммиты вместе с их комментариями.
reword, r
Изменить комментарий коммита. Укажите только опцию r, а имя коммита вы укажете при выходе из интерактивного режима.

Для удаления коммита - просто удалите строку в интерактивном редакторе коммитов.

Управление процессом rebase

Отменить незавершенный rebase:

git rebase --abort

Откатить успешный rebase можно только с помощью reset --hard:

git reflog                 # ищем состояние с нужным head коммитом
git reset --hard HEAD@{5}  # сбрасываем текущую ветку к этому коммиту
* git log HEAD@{5}         # можно проверить историю для старого head коммита

Продолжить выполнение команды rebase:

git rebase --continue

Пропустить наложение коммита и перейти к следующему:

git rebase --skip

Пример слияния коммитов

Начать процесс интерактивного слияния коммитов в пределах 6-ти последних:

git rebase -i HEAD~6
Примечание

При слиянии коммита, Git вливает его в предыдущий в списке. Вы можете изменить порядок коммитов перед слиянием. Например:

pick  <hash>  Finally Fix #4
f     <hash>  Two fix #4
f     <hash>  One fix #4
pick  <hash>  Fix #12
s     <hash>  New comment #12
git log --pretty=%s --graph
* Fix #12
* Finally Fix #4

Автоматизация слияния коммитов

Подготовить коммиты к слиянию:

git commit --fixup=g73di4w2mf # Git создаст коммит с сообщением `fixup fixup! Fixed #4`
git commit --squash=5e67t89q0 # Git создаст коммит с сообщением `squash squash! Fixed #9`

Состояние коммитов:

pick Fixed #4
fixup fixup! Fixed #4
pick Fixed #9
squash squash! Fixed #9

Выполнить сливание в автоматическом режиме:

git rebase -i --autosquash HEAD~4

Изменение комментариев

Изменить комментарий к последнему коммиту:

git commit --amend -m 'Обновить комментарий к последнему коммиту'

Изменить комментарии в диапазоне последних 3-х коммитов:

git rebase -i HEAD~3
r New commit comment ~1
pick Original commit ~2
r New commit comment ~3

Сдвинуть коммиты

Принять изменения и сдвинуть свои незапушенные коммиты вверх (как будто вы сделали pull, до того как закоммитили изменения):

git pull --rebase

Наложить коммиты из ветки my-fix на последний коммит ветки stage:

git rebase stage my-fix
Внимание!

Обратите внимание, что при разрешении конфликтов, theirs - это изменения из ветки, в которую мы влили коммиты и сдвинули наш коммит на верх, а ours - это изменения, которые мы получили из чужих новых коммитов. После разрешения конфликтов - вызовите:

git rebase --continue

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

Удалить последний коммит:

git reset --hard HEAD~
git reset --hard HEAD~2 # последние 3 коммита

#cvs, #git

категория: GIT