Как я использую git / Хабрахабр

Основам git мне пришлось научиться на своем первом месте работы (около трех лет назад).

С тех пор я считал, что для полноценной работы нужно запомнить всего-лишь несколько команд:

  • git add <path>
  • git commit
  • git checkout <path/branch>
  • git checkout -b <new branch>

И дополнительно:

  • git push/pull
  • git merge <branch>
  • git rebase master (а что, можно еще и на другие ветки ребейзить? О_о)

В принципе, я и сейчас во многом так считаю, но со временем волей-неволей начинаешь узнавать интересные трюки.

Вообще, имеет смысл подробнее разузнать о понятиях гита. Лучше подробнее ознакомиться с концепцией коммитов, что такое ветка, что такое тег и пр.

Автодополнение

Удивительно, но не у всех оно есть.

Отправляемся в гугл по запросу “git_completion”, скачиваем скрипт и действуем по инструкции к нему.

Выводим текущую ветку в строке bash

Данный код нужно добавить в .bashrc. Он со мной с некоторыми изменениями путешествует еще с того самого первого места работы.

function git-current-branch {
    git branch --no-color 2> /dev/null | grep * | colrm 1 2
}

function set_prompt_line {
    local        BLUE="[�33[0;34m]"

    # OPTIONAL - if you want to use any of these other colors:
    local         RED="[�33[0;31m]"
    local   LIGHT_RED="[�33[1;31m]"
    local       GREEN="[�33[0;32m]"
    local LIGHT_GREEN="[�33[1;32m]"
    local       WHITE="[�33[1;37m]"
    local  LIGHT_GRAY="[�33[0;37m]"
    # END OPTIONAL
    local     DEFAULT="[�33[0m]"
    export PS1="$BLUEw $LIGHT_RED[$(git-current-branch)]$DEFAULT $ "
}

set_prompt_line

Для справки: за внешний вид командной строки баша отвечает переменная PS1. Have fun.

Алиасы

Вообще-то, у гита есть свои алиасы, но я понятия не имею, как их добавлять, т.к. мне лень изучать вопрос. Я пользуюсь башем:

#
# Git
#
alias current-branch='git-current-branch'
alias git-uncommit='git reset --soft $(git log --format=%H -2 | tail -1)'
alias gst='git status'
alias glog='git log'
alias gcheck='git checkout'
alias gamend='git commit --amend'
__git_complete gcheck _git_checkout
alias gcom='git commit'
__git_complete gcom _git_commit
alias gdiff='git diff'
__git_complete gdiff _git_diff
alias gadd='git add'
__git_complete gadd _git_add

Обратите внимание на __git_complete <something> <another>. Эта команда включает гитовое автодополнение для алиаса.

Редактируем сообщения к коммитам в своем любимом текстовом редакторе

Для начала небольшая страшилка, основанная на реальных событиях:

Как-то раз молодой неопытный программист хотел впервые закоммитить код, а гит открыл ему vim!

Да, история произошла со мной. Через несколько часов я смог его закрыть и начал коммитить только с однострочными комментариями через git commit -m.

Git, как и некоторые другие утилиты (crontab, например) проверяют наличие переменной EDITOR.

В конфиге баша (~/.bashrc) можно добавить вот такую строчку:

export EDITOR=<команда, открывающая ваш текстовый редактор>

У меня это emacsclient, раньше был subl (Sublime Text). Я не проверял, но я полагаю, что очень важно, чтобы команда не возвращала управление терминалу, пока текстовый файл не будет закрыт.

Иногда можно просто сменить ветку, но иногда возникают конфликты. Я знаю два варианта:

1) Сделать временный коммит

2) git stash, сменить ветку, …, вернуть ветку, git stash pop

Первый вариант надежнее, второй удобнее (имхо).

git diff

Показывает ваши изменения относительно текущего коммита + stage (важное уточнение). Замечание: в дифф не попадают новые файлы

Посмотреть, что я добавил в stage

git diff --cached

Замечание: сюда новые файлы попадают.

Т.е. файлы, которые не относятся к репозиторию

git clean -df

  • -d — удаляет еще и директории
  • -f — обязательная опция, без нее гит попросту откажется что-либо удалять (уж не знаю, зачем она)

У меня на это дело есть alias в баше:

alias git-uncommit='git reset --soft $(git log --format=%H -2 | tail -1)'

git reset --soft <commit/branch/tag> переносит ветку на коммит, но код не меняет. Разница заносится в stage.

$(<whatever>) — баш выполняет содержимое скобочек и подставляет результат выполнения вместо всего выражения. Например, cat $(ls | tail -1) выдаст содержимое последнего файла из ls.

git log --format=%H -2 выдаст хеши двух последних коммитов.

В общем, вся команда сводится к тому, что текущая ветка переносится на один коммит назад, а изменения, внесенные коммитом, попадают в stage

Когда я работаю на своей ветке, периодически я делаю несколько коммитов, которые совсем не имеют смысла по отдельности (а делаю я это просто для того, чтобы коммитить почаще и не терять мысль), поэтому перед вливанием их в мастер имеет смысл их объединить

Решение:

Интерактивный rebase!

git rebase -i master

Это откроет текстовый редактор, в котором списком будут указаны коммиты.

Вы можете:

  • Менять порядок их применения (очень часто пригождается)
  • “Сквошить” — объединять несколько коммитов в один
  • редактировать — гит будет останавливаться, чтобы вы могли делать изменения с помощью –amend
  • менять сообщение — в общем-то, частный случай редактирования
  • не применять коммит в принципе

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

Вообще я после каждой фичи делаю интерактивный ребейз и смотрю, какие коммиты я хочу объединить, какие переставить для красоты, какие поправить. Это позволяет сохранять красоту в версионировании.

git add <forgotten changes>
git commit --amend

Еще стоит упомянуть:

git commit --amend --no-edit # Не редактировать сообщение
git commit --amend -m 'my commit message' # работает так, как вы ожидаете 

Ситуация:

3 коммита назад допустил опечатку, не хочу, чтобы это позорище кто-то увидел отдельным коммитом.

Решение:

Интерактивный rebase!

git rebase -i HEAD~3

Лично я в указанной ситуации (а у меня она часто возникает) делаю так: создаю коммит, где в сообщении добавляю префикс[to_squash], заканчиваю работу над веткой, делаю полный ребейз ветки на мастер (git rebase -i master) и переношу этот коммит под тот, к которому данная правка относится, с пометкой s (squash).

Коммиты желательно делать максимально простыми (антоним слову “сложными”).

Хочу вот я на гитхабе посмотреть, какая история у файла hello_world.rb, смотрю историю, а там среди прочих коммит “create super-booper feature”, в котором в файле hello_world.rb у одной переменной изменено имя, хотя она к фиче совсем отношения не имеет. Лучше было бы наличие коммита “rename variable x to y in hello_world.rb”.

Собственно, например, у меня есть код:

def kvadrat(x)
  x * x
end

puts kvadrat(n)

Мне нужно добавить фичу: выводить удвоенное n. Изи!

Но пока я пишу фичу, на автомате меняю некрасивое имя функции

Пишем:

def square(x)
  x * x
end

def double(x)
  x + x
end

puts square(n)
puts double(n)

Как теперь коммитить?

Можно быстро вернуть старое название, закоммитить новый функционал, а потом уже переименовать, но это не всегда уместно, т.к. изменения могут быть достаточно крупными.

Можно честно признать, что коммит сложный и написать сообщение в духе “добавил фичу + переименовал метод”, но мы ведь стараемся делать коммиты простыми, верно?

Но у гита есть отличная команда:

git add -p

Она интерактивная. Поочередно берет изменения кусками (hunk) и спрашивает, что с данным куском делать: игнорировать, добавить, изменить и добавить. Третий вариант достаточно мощный, можно по отдельности добавлять изменения даже в рамках одной строчки (kvadrat(x) + kub(x) => square(x) + cube(x) в 2 коммита).

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

  • git reflog — меня это спасло, когда я случайно удалил ветку, не смерджив и не запушив ее
  • git rebase -i — в посте указан лишь частный случай применения.
  • git log --graph — просто он забавный. Не знаю, есть ли практическое применение.
  • Дополните?

Я указал здесь всего-лишь парочку “трюков” работы с git, но их я использую на ежедневной основе.

Смысл данного поста (помимо того, чтобы ублажить свое ЧСВ и оставить заметку для себя самого) в том, чтобы еще раз подчеркнуть известную (относительно) фразу: Know your tools!.

В гите (да и в принципе во всех ваших инструментах для работы) есть множество деталей, которые могут сделать вашу жизнь проще. Например, я видел репозитории, в которых стояли валидации для коммитов: нельзя было ничего закоммитить, если не проходили тесты или тесты не покрывают изменения.

Источник