Вообще-то изначально я планировал перевести статью Энди Джеффриса (Andy Jeffries)
25 Tips for Intermediate Git Users, но в процессе я отбросил бестолковые, общеизвестные или самые простые советы вроде «настройте первым делом user.name и user.email», которые явно не подходят людям, уже более-менее плотно знакомым с Git.
Взамен я дополню статью моментами из личной практики («Своя практика»! Звучит здорово, будто я частный врач или адвокат! :-] )
1. Самое начало.
Поскольку user.{name,email,signingkey} настроены, можно посмотреть в сторону мелких удобств, вроде $ git config --global help.autocorrect 1 — в случае опечаток, гит попробует угадать что же вы имели в виду
[solar@hasher couchdb]$ git statu
WARNING: You called a Git command named 'statu', which does not exist.
Continuing under the assumption that you meant 'status'
in 0.1 seconds automatically…
# On branch 0.10.x-alt
nothing to commit (working directory clean)
Значение 1 здесь показывает десятые доли секунды.
Отрицательное значение приведёт к выполнению предполагаемой команды немедленно, а нулевое — просто к показу подсказки (по умолчанию).
Из альясов я добавляю только
git config --global alias.logp 'log --pretty=format:"%h — %an: %s"' — 10 последних коммитов в формате «SHA — Author: Commit message»
git config --global alias.unstage 'reset HEAD'
git config --global alias.remotes 'remote -v' — Это сделает вывод git remote чуть более подробным, но не столь много словным, как git remote show $branch, в котором, к тому же, нужно указать конкретный сервер.
2. Конфликты при мердже
При имеющем место конфликте можно посмотреть предмет раздора в нормальном стандартного diff -u:
$ git diff --merge
diff --cc dummy.rb
index 5175dde,0c65895..4a00477
--- a/dummy.rb
+++ b/dummy.rb
@@@ -1,5 -1,5 +1,5 @@@
class MyFoo
def say
- puts "Bonjour"
- puts "Hello world"
++ puts "Annyong Haseyo"
end
end
Если нужно получить файл из одного из мерджащихся бранчей, можно сделать git checkout —ours flash/foo.fla либо git checkout —theirs flash/foo.fla — это избавит от необходимости помнить какие бранчи сливаются.
Так же всегда можно посмотреть различия перед объединением — $ git diff branch1 branch2
3. Тэги.
Чтобы удобно определять эволюционные стадии кода, надо использовать тэги.
$ git tag -m '$tag_description' $tag_name
Если из вашего гита содержимое отправляется на сборку, все можно будет собрать конкретную указанную версию. Кстати, скорее всего, нужно будет создавать тэги, подписанные Вашим gpg-ключём (git config --global user.singingkey $key_ID)
4. Новые бранчи
Как всем должно быть известно, в новый бранч можно попасть либо создаев его и переключившись ($git branch new_branch; git checkout new_branch), либо сразу — $ git checkout -b new_branch.
Кстати, переименовать текущий бранч можно с помощью git branch -m new_name, а сторонний — git branch -m current_name new_name.
Не самая часто используемая возможность, но иногда приходится и так получается всяко быстрее чем checkout -b и последующее ручное удаление старого бранча.
5. Объединение бранчей. Merge и rebase.
Чтобы поместить в один бранч изменения из другого бранча, можно использовать либо merge — m создаёт новый коммит, в котором находятся необходимые правки, либо rebase — r же переписывает историю. С одной стороны, после rebase история получается более аккуратной, с другой — это уже другая история, поэтому в случае опубликованных общеступных бранчей лучше пользоваться merge. Тем более, что за количество коммитов никто денег не берёт.
Можно посмотреть бранчи, изменения в которых еще не перенесены в текущий бранч — это можно сделать через git branch --no-merged. Такие бранчи лучше не дропать, хехе.
6. Удалённые бранчи
Удалённые — в смысле, remote :-]
Можно пушить в удалённый бранч напрямую — git push origin branch_name, если имена локального и удалённого бранчей совпадают, а можно и с помощью полной формы — git push origin localBranch:refs/heads/Remote_Branch_Name
Кстати, чтобы не указывать каждый раз имя сервера и бранч, можно создавать отслеживаемые бранчи с помощью git checkout -b myfeature origin/myfeature. Теперь, находясь в этом бранче, можно пушить/фетчить/пуллить без конкретных имен.
7. Stash
Если кто-то еще не пользуется git stash, советую обратить на эту команду пристальное внимание. Более чем удобно, занимаясь одним делом, «отложить» текущую работу в сторону и отвлечься, скажем, на срочное исправление бага, даже если он находится в другом бранче. После исправления и коммита можно преспокойно вернуться к начатому.
i. hack-hack-hack
ii. git stash
iii. fix-fix-fix
iv. git commit -a -m 'bugfix #31337'
v. git stash pop
Те же, кто знает про git stash, посмотрите на последнюю строку — её отличие от apply в том, что откладываемые результаты не остаются во временном хранилище (посмотрите git stash list после нескольких примений stash!)
Если изменение было фактически законченным, можно оформить коммит не отходят от кассы — git stash save 'commit msg'
8. Поиск в истории
Если про коммит известно хоть что-то, можно попробовать его найти!
$ git log --grep=«Something in the message» — поиск по описанию коммита
$ git log -S"{% include \«header.tmpl\» %}" — поиск коммита по строке в файлах бранча. Обратите внимание на отсутствие пробела после -S
$ git log templates/header.tmpl — поиск коммитов, в которых затрагивался указанный файл.
Также можно уточнить поиск при помощи опций вроде --since=X.months.ago --until=Y.day.ago --author=andy -S«something»
Опция --all-match комбинирует параметры с помощью AND, а не дефолтного OR.
Дальше я опишу отмену и манипуляции с изменениями, интерактивный rebase, изменение файлов по всей истории (лично мне пригодилось, когда я заметил, что держу боевые пароли в основном бранче, хехе) и всякие другие мелочи ^_^
P.S.: Спасибо за такой нужный +1, %username% )
комментарии (32)
Бранч? Может, сервер?
Зря, по-моему — общеизвестные или самые простые советы способствуют переходу из «начинающих» в «продвинутые» или даже из «интересующихся» в «продвинутые». Но все равно спасибо, добавил в закладки, к тому времени как стану начинающим :)
Как раз вот тут мы переводим (уже больше половины перевели) Git Community Book, которая чрезвычайна полезна и начинающим.
Помогите нам сделать свободную, качественную и красивую книжку о Git!
Это было что-то типа рекламы. :)
Можно ли его делать в не-bare репозиторий с имеющимися там изменениями?
Я хочу таким образом выкладывать код из репы на сервер, но чтобы при этом сделанные там изменения (те же самые конфиги) не затирались, а мерджились.
Видел какие-то вариант, но все чрезмерно замороченные. Может есть нормальный удобный способ?
Вам поможет git rebase origin/master — после этого можете пушить свои изменения.
Я бы держал свои изменения в отдельном бранче, а в мастер держал бы синхронизированным с репо на сервере, но сути это не меняет.
Да, пуш в мастер на удаленную машину предполагается из мастера. Но они все равно будут рассинхронизированы в части настроек (скажем логин и пароль для БД другие). При этом хотелось бы обновить удаленный репо и не затереть его настройки. Исключать конфиг из контроля версий неправильно. Перезаписывать файл поверх после пуша тоже плохо — т.к. в настройки может добавиться что-то, что не хотелось бы руками переносить.
Я держу пароли (точнее, конфиги с настоящими паролями) и прочие специализированные штуки в отдельном бранче. При добавлении в мастере каких-либо важных фич просто мерджу этот бранч с мастером.
Или я не понял исходную задачу.
У меня была мысль, что если его смержить один раз когда-то давно, с тем смыслом, чтобы гит «знал» про этот мердж, и учитывал его. Будет ли это иметь работать? Или такие изменения затрутся в истории гита? Возможно я спрашиваю ерунду — я еще не разобрался для себя как работает гит.
git pull. refs/remotes/origin/master-branch
# смержить удаленный бранч к себе
# ну и обратить внимание на такую последовательность:
git push origin origin/source-branch:refs/heads/new-branch
# создает на удаленном сервере бранч new-branch c кодом из source-branch
git checkout -b new-branch --track origin/new-branch
# создаль локальный new-branch и настроить его на треканье удаленного =)
git push origin :heads/new-branch
# удалить созданный бранч в удаленном репозитории
Я укажи эти команды во второй части статьи, спасибо
«git pull . refs/remotes/origin/master-branch»
а вообще гит — мощная штука, и наличие например «git commit --amend» очень порадовало.
Но, честно сказать, меня больше порадовало недавно примененная
$ git filter-branch --tree-filter «sed -e 's#my_secret_pass#dbpassword#' -e 's#my_db_hostdbhost.tld#' -i settings.py» HEAD
где my_secret_pass и my_db_host были реальными паролем и хостом ^_^
here = "!echo $(git symbolic-ref HEAD | sed 's/refs\\/heads\\///g')"
позволяет выполнять комманды типа
git push origin `git here` # пушнуть изменения в ветку удаленного репозитория
и подготавливает к написанию действительно страшных алиасов, типа killbranch, который будет прибивать и локальный бранч, и бранч в удаленном репозитории.
в общем алиасов хороших можно привести много — главное не боятся их писать — тем более что в гите их делать проще простого.
это --track!))
git add -i # Интерактивное добавление файла в индекс
git add -p # Интерактивное добавление файла в индекс с выбором отдельных ханков (частный случай -i)
git rebase -i # Трансплантация ветки с редактированием состава коммитов (можно пропускать, редактировать, объединять)
Всегда интересовало — в каких случаях интерактивно, скажем, выборочно добавлять файл?
но иногда закапываешься в какую-то проблему, попутно исправляешь какие-то баги — в итоге в файле накапливаются изменения не связаные друг — с другом.
в таком случае лучше закоммитить по ханкам — всё что относится к одной фиче — в один коммит, что к другой — в другой коммит.
кроме того, очень полезная комманда: git cherry-pick. использовать можно так — есть бранч, в котором новая фича и его нельзя пока мержить в мастер. Но там попутно исправлено пару багов, а вас просят влить эти исправления в мастер. Делается в бранче сделаном от мастера
git cherry-pick BUGFIX_COMMIT_SHA1
и не надо заново вносить изменения или как-то менять историю (в этом случае вы подумаете, что «как хорошо, что я дулал коммиты атомарными и этот багфикс не тянет за собой кучу изменений связаных с новой фичей»)
Никак не могу заставить себя делать по-настоящему атомарные коммиты
Столько новой инфы по гиту — это же просто праздник какой-то! :-)
git rerere — позволяет записывать решение конфликтов и при повторном мерже использовать их. Помогает в случае если бранч живет долго и часто возникает необходимость мержится с другими бранчами. Включается просто: git config --global rerere.enabled 1
И git rebase --onto. Позволяет перенести ветку с одним основанием на другое. Когда полезно: Есть мастер в котором происходит активная разработка. В определенный момент был начат бранч с новой независимой фичей, но от текущего мастера. Фича сделана и в принципе может быть внесена в стабильную ветку, но из-за того что она была начата на от стабильной ветки, при обычном мерже потянет за собой коммиты, которые не должны попасть в стабильную версию. На выручку приходит git rebase --onto, который позволяет создать ветку которая будет начинаться от стабильной версии и содержать все коммиты, которые были сделаны в ветке от мастера.