ХХ полезных советов для пользователей Git среднего уровня. Часть 1

    Вообще-то изначально я планировал перевести статью Энди Джеффриса (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% )
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 32
    • 0
      >git config --global alias.remotes 'remote -v' — Это сделает вывод git remote чуть более подробным, но не столь много словным, как git remote show $branch, в котором, к тому же, нужно указать конкретный бранч.

      Бранч? Может, сервер?
      • 0
        да, конечно же сервер
      • 0
        >но в процессе я отбросил бестолковые, общеизвестные или самые простые советы вроде «настройте первым делом user.name и user.email», которые явно не подходят людям, уже более-менее плотно знакомым с Git.

        Зря, по-моему — общеизвестные или самые простые советы способствуют переходу из «начинающих» в «продвинутые» или даже из «интересующихся» в «продвинутые». Но все равно спасибо, добавил в закладки, к тому времени как стану начинающим :)
        • 0
          Способствуют, не спорю. Никто не рождается сразу с багажом знаний. Но и читать каждый раз про «первым делом укажите своё имя и почту», хотя пришел искать приёмы черной магии, утомительно, согласитесь.
          • +4
            translated.by/you/git-community-book/into-ru/trans/
            Как раз вот тут мы переводим (уже больше половины перевели) Git Community Book, которая чрезвычайна полезна и начинающим.
            Помогите нам сделать свободную, качественную и красивую книжку о Git!
            Это было что-то типа рекламы. :)
            • 0
              Сначала прочитаю что уже переведено, спасибо за наводку
          • 0
            Вопрос про push в удаленный бранч.
            Можно ли его делать в не-bare репозиторий с имеющимися там изменениями?
            Я хочу таким образом выкладывать код из репы на сервер, но чтобы при этом сделанные там изменения (те же самые конфиги) не затирались, а мерджились.
            Видел какие-то вариант, но все чрезмерно замороченные. Может есть нормальный удобный способ?
            • 0
              Т.е. Вы делаете коммит origin_head+1, и хотите выложить его, даже если кто-то запушил своё вариант origin_head+1?
              Вам поможет git rebase origin/master — после этого можете пушить свои изменения.

              Я бы держал свои изменения в отдельном бранче, а в мастер держал бы синхронизированным с репо на сервере, но сути это не меняет.
              • 0
                То есть перед пушем туда мне надо сделать там rebase?

                Да, пуш в мастер на удаленную машину предполагается из мастера. Но они все равно будут рассинхронизированы в части настроек (скажем логин и пароль для БД другие). При этом хотелось бы обновить удаленный репо и не затереть его настройки. Исключать конфиг из контроля версий неправильно. Перезаписывать файл поверх после пуша тоже плохо — т.к. в настройки может добавиться что-то, что не хотелось бы руками переносить.
                • 0
                  Да, перед пушем на апстримный сервер надо сделать ребэйз, тем самым вы притворитесь, будто спушили всё, что можно, и свой коммит сделали с пылу с жару, основываясь на самом последнем коммите из апстрима.

                  Я держу пароли (точнее, конфиги с настоящими паролями) и прочие специализированные штуки в отдельном бранче. При добавлении в мастере каких-либо важных фич просто мерджу этот бранч с мастером.

                  Или я не понял исходную задачу.
                  • 0
                    Просто я хочу, чтобы бранч с конфигами лежал на удаленном серваке, и автоматически примерживался, после пуша.
                    У меня была мысль, что если его смержить один раз когда-то давно, с тем смыслом, чтобы гит «знал» про этот мердж, и учитывал его. Будет ли это иметь работать? Или такие изменения затрутся в истории гита? Возможно я спрашиваю ерунду — я еще не разобрался для себя как работает гит.
                    • 0
                      Посмотрите в сторону server-hooks. Post-commit, например
          • 0
            # я бы посоветовал еще одну комманду:

            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 и настроить его на треканье удаленного =)

            • 0
              # и да, забыл
              git push origin :heads/new-branch
              # удалить созданный бранч в удаленном репозитории
              • 0
                без ":heads/" тоже должно работать, не?
            • 0
              Git, c версии 1.6.2, кажется, пытается делать track`уемым бранч, если он был создан через -b.

              Я укажи эти команды во второй части статьи, спасибо
              • 0
                там только точку не отделил в первой комманде
                «git pull . refs/remotes/origin/master-branch»
                а вообще гит — мощная штука, и наличие например «git commit --amend» очень порадовало.
                • 0
                  git commit -a --amend -C HEAD более чем удобно, да!

                  Но, честно сказать, меня больше порадовало недавно примененная
                  $ 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 были реальными паролем и хостом ^_^
            • +1
              еще один полезный алиас из ~/.gitconfig
              here = "!echo $(git symbolic-ref HEAD | sed 's/refs\\/heads\\///g')"

              позволяет выполнять комманды типа
              git push origin `git here` # пушнуть изменения в ветку удаленного репозитория

              и подготавливает к написанию действительно страшных алиасов, типа killbranch, который будет прибивать и локальный бранч, и бранч в удаленном репозитории.

              в общем алиасов хороших можно привести много — главное не боятся их писать — тем более что в гите их делать проще простого.

              • +1
                > git push origin `git here` # пушнуть изменения в ветку удаленного репозитория

                это --track!))
              • 0
                Из недавно освоенных полезных трюков:

                git add -i # Интерактивное добавление файла в индекс
                git add -p # Интерактивное добавление файла в индекс с выбором отдельных ханков (частный случай -i)
                git rebase -i # Трансплантация ветки с редактированием состава коммитов (можно пропускать, редактировать, объединять)

                • +1
                  Про rebase -i я еще расскажу!
                  Всегда интересовало — в каких случаях интерактивно, скажем, выборочно добавлять файл?
                  • 0
                    * в каких случаях нужно
                    • +3
                      чем атомарнее коммит — там легче мёрж. так как коммит происходит на локальной машине без оверхеда работы по сети — то коммиты можно делать хоть раз в 5 минут
                      но иногда закапываешься в какую-то проблему, попутно исправляешь какие-то баги — в итоге в файле накапливаются изменения не связаные друг — с другом.
                      в таком случае лучше закоммитить по ханкам — всё что относится к одной фиче — в один коммит, что к другой — в другой коммит.
                      кроме того, очень полезная комманда: git cherry-pick. использовать можно так — есть бранч, в котором новая фича и его нельзя пока мержить в мастер. Но там попутно исправлено пару багов, а вас просят влить эти исправления в мастер. Делается в бранче сделаном от мастера
                      git cherry-pick BUGFIX_COMMIT_SHA1
                      и не надо заново вносить изменения или как-то менять историю (в этом случае вы подумаете, что «как хорошо, что я дулал коммиты атомарными и этот багфикс не тянет за собой кучу изменений связаных с новой фичей»)
                      • 0
                        Понятно
                        Никак не могу заставить себя делать по-настоящему атомарные коммиты
                      • +1
                        Я так делаю, к примеру, когда исправляю ошибку: вношу сами изменения с комментариями «для себя» или часто исправляю несколько ошибок или, к примеру, добавляю команды вывода в логи. И вот что бы сделать патчсеты с корректными названиями я и делаю git add -p. «Косметику», вроде логгирования и отладочных сообщений обычно не хочется отсылать в проекты из-за несоответствия стилей кодирования, поэтому я её потом при необходимости снова потестироваться трансплантирую наверх апстримовской ветки.
                    • 0
                      Неистово плюсую!
                      Столько новой инфы по гиту — это же просто праздник какой-то! :-)
                    • +1
                      Ну в придачу ещё две полезные комманды:
                      git rerere — позволяет записывать решение конфликтов и при повторном мерже использовать их. Помогает в случае если бранч живет долго и часто возникает необходимость мержится с другими бранчами. Включается просто: git config --global rerere.enabled 1
                      И git rebase --onto. Позволяет перенести ветку с одним основанием на другое. Когда полезно: Есть мастер в котором происходит активная разработка. В определенный момент был начат бранч с новой независимой фичей, но от текущего мастера. Фича сделана и в принципе может быть внесена в стабильную ветку, но из-за того что она была начата на от стабильной ветки, при обычном мерже потянет за собой коммиты, которые не должны попасть в стабильную версию. На выручку приходит git rebase --onto, который позволяет создать ветку которая будет начинаться от стабильной версии и содержать все коммиты, которые были сделаны в ветке от мастера.

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