Pull to refresh

Алиасы в bash для быстрого набора команд Git

Reading time6 min
Views15K

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

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

Формат задания алиаса имеет следующий вид:

alias name="<выполняемый код>"

Основной проблемой алиасов является то, что они не подхватываются автоматически механизмом автодополнения кода. Их написание отличается от тех команд, которые зарегистрированы в системе автодополнения, поэтому приходится дополнительно регистрировать алиасы, назначая им соответствующие спецификации автодополнения (compspec) через встроенную в bash команду complete. В случае с Git над complete существует обёртка __git_complete, в которую необходимо передавать предопределённые функции-обёртки, начинающиеся с префикса _git_ и оканчивающиеся названием подкоманды git. В Ubuntu весь связанный с этим код можно посмотреть в файле /usr/share/bash-completion/completions/git.

Алиас

Действие

f

Получить изменения с сервера

s

Просмотреть текущее состояние локального репозитория

ss

Просмотреть текущее состояние локального репозитория и количество изменённых строк

d

Просмотреть изменения, сделанные в файлах и ожидающие подтверждения, в стандартном diff-формате

dd

Просмотреть изменения, сделанные в файлах и ожидающие подтверждения, с выделением одним только цветом

l

Список последних коммитов в кратком формате.

ll

Список последних коммитов с развёрнутой информацией о них.

go или gg

Переход на другую ветку или коммит, создание новой ветки с опцией -b.

pull

Получить изменения с сервера и влить их в текущую ветку.

commit

Сделать коммит (зафиксировать изменения).

add

Добавить файлы или изменения в будущий коммит.

reset

Отменить добавление изменений в будущий коммит.

push

Отправить новые коммиты на сервер.

push-fix

Отправить коммиты на сервер с перезаписью их истории, если другие люди не успели отправить туда свои коммиты.

merge

Влить заданную ветку в текущую.

amend

Добавить изменения в уже сделанный коммит или изменить его комментарий.

rebase

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

branch

Переименовывание или удаление локальной ветки

pick

Применить в текущей ветке отдельные существующие коммиты.

stash

Сохранить свои текущие изменения во временное хранилище.

apply

Применить свои текущие изменения из временного хранилища, но не удалять и оттуда.

pop

Забрать свои изменения из временного хранилища и применить их.

Ниже приведено примерное содержимое файла ~/.bash_aliases с основными алиасами Git и кодом их автоматической регистрации в механизме автодополнения командой строки.

#!/bin/bash

alias f="git fetch --tags"
alias s="git status"
alias ss="git status && git diff --shortstat"
alias d="git diff"
alias dd="git diff --word-diff=color"
alias l="git log -n 20 --graph --pretty=format:'%Cred%h%Creset %an: %s / %Cgreen%cr%Creset %Creset %C(yellow)%d%Creset' --abbrev-commit --date=relative"
alias ll="git log"
alias go="git checkout"
#или alias gg="git checkout"
alias pull="git pull"
alias commit="git commit"
alias add="git add"
alias reset="git reset"
alias push="git push"
alias push-fix="git push --force-with-lease"
alias merge="git merge"
alias amend="git commit --amend"
alias rebase="git rebase"
alias branch="git branch"
alias pick="git cherry-pick"
alias stash="git stash"
alias apply="git stash apply"
alias pop="git stash pop"

# Регистрируем алиасы в системе автодополнения
git_completions="/usr/share/bash-completion/completions/git"
if [ -f "${git_completions}" ]; then
    source "${git_completions}"

    # Получаем массивы алиасов и соответствующих им команд git
    cmds=( $(alias | grep -v ss | grep -Po '(?<=git )[\w-]+' | sed -r 's/[ -]+/_/g') )
    aliases=( $(alias | grep -v ss | grep git | grep -Po '(?<=alias )\w+') )

    # Регистрируем каждый алиас
    for (( i = 0; i < ${#aliases[@]}; i++ )); do
        a="${aliases[$i]}"
        cmd="${cmds[$i]}"
        __git_complete "${a}" _git_"${cmd}"
    done
fi

Для того, чтобы файл с алиасами применялся при входе в систему, необходимо добавить его подключение в файл ~/.bashrc с помощью команды source (или .). Файл ~/.bashrc запускается на исполнение всегда при старте командного интерпретатора bash, поэтому если файл редактировался из консоли, потребуется её заново открыть или запустить вложенный командный интерпретатор bash.

if [ -f ~/.bash_aliases ]; then
  . ~/.bash_aliases
fi

Алиасы s, d и l простые, дают необходимый минимум информации. Задумка двухбуквенных в том, чтобы дать слегка больше информации, если это требуется. С помощью f можно получить последние изменения в репозитории и влить их в текущую ветку с помощью pull или pull -r. Через l можно посмотреть, что за новые коммиты прилетели в ветку, или что было последним из изменений, а через ll — подробно изучить коммиты. Переключиться на другую ветку можно через go, создать новую ветку можно через go -b. При создании коммита, в общем случае, сначала вводится s, проверяется на какой мы ветке, какие файлы требуется добавить, затем d, чтобы проверить свои изменения. Через add добавляются файлы (или add -i для выбора отдельных изменений в файлах), через commit -m создаётся новый коммит. Если вдруг в тексте сообщения коммита оказалась опечатка, можно быстро всё поправить через amend. Если в main-ветку успели прилететь новые коммиты, можно сделать rebase main. И потом сделать push. Если ветка своя собственная, то можно делать rebase -i с исправлениям прошлых коммитов и отправить модифицированные коммиты через push-fix.

Если шла работа, но срочно понадобилось переключиться на другую ветку, можно временно убрать свои изменения через stash, переключиться на другую ветку через go. Потом можно вернуть изменения с помощью apply или pop. Отдельные коммиты из других веток можно применять с помощью pick, также это может быть полезным, если случайно сделать изменения в отделённом HEAD (например, в подмодуле).

Алиасы могут совпадать с существующими командами. Если такие команды обычно не используются, то это проблемой не является. Если же команды понадобятся, то в случае исполняемых файлов всегда можно указать полный путь к файлу. Не будет проблем и с командами, которые запускаются от имени суперпользователя, поскольку алиасы прописаны лишь для непривилегированного пользователя. Если используется язык Go, то с использованием алиаса go могут возникнуть проблемы. В качестве решения можно изменить алиас на gg или gc, либо же сделать алиас golang для команды go. Здесь кому как удобнее.

В Ubuntu 21.10 при использовании алиаса над git checkout выводятся сообщения об ошибках. Происходит это из-за того, что одна из переменных в целочисленных сравнениях оказывается пустой. Вариантом подавления вывода ошибок является замена в файле /usr/share/bash-completion/completions/git сравнения через квадратные скобки на арифметическое сравнение через двойные скобки, например, (( var1 < var2 )), в которых можно использовать названия переменных без предшествующего знака $. Такое исправление работает и в zsh, на который автодополнение тоже рассчитано.

Патч, решающий проблему
--- /usr/share/bash-completion/completions/git  2021-08-09 15:29:27.000000000 +0300
+++ ./git       2022-04-08 23:32:38.501198226 +0300
@@ -1011,7 +1011,7 @@
        if [ "$cmd" = "remote" ]; then
                ((c++))
        fi
-       while [ $c -lt $cword ]; do
+       while (( c < cword )); do
                i="${words[c]}"
                case "$i" in
                --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
@@ -1187,7 +1187,7 @@
        done
        local wordlist="$1"
 
-       while [ $c -lt $cword ]; do
+       while (( c < cword )); do
                for word in $wordlist; do
                        if [ "$word" = "${words[c]}" ]; then
                                if [ -n "${show_idx-}" ]; then
@@ -1221,7 +1221,7 @@
        done
        local wordlist="$1"
 
-       while [ $c -gt "$__git_cmd_idx" ]; do
+       while (( c > __git_cmd_idx )); do
                ((c--))
                for word in $wordlist; do
                        if [ "$word" = "${words[c]}" ]; then
@@ -1283,7 +1283,7 @@
 __git_has_doubledash ()
 {
        local c=1
-       while [ $c -lt $cword ]; do
+       while (( c < cword )); do
                if [ "--" = "${words[c]}" ]; then
                        return 0
                fi
@@ -1449,7 +1449,7 @@
 {
        local i c="$__git_cmd_idx" only_local_ref="n" has_r="n"
 
-       while [ $c -lt $cword ]; do
+       while (( c < cword )); do
                i="${words[c]}"
                case "$i" in
                -d|-D|--delete|-m|-M|--move|-c|-C|--copy)
@@ -2479,7 +2479,7 @@
 __git_config_get_set_variables ()
 {
        local prevword word config_file= c=$cword
-       while [ $c -gt "$__git_cmd_idx" ]; do
+       while (( c > __git_cmd_idx )); do
                word="${words[c]}"
                case "$word" in
                --system|--global|--local|--file=*)
@@ -3213,7 +3213,7 @@
 _git_tag ()
 {
        local i c="$__git_cmd_idx" f=0
-       while [ $c -lt $cword ]; do
+       while (( c < cword )); do
                i="${words[c]}"
                case "$i" in
                -d|--delete|-v|--verify)
@@ -3388,7 +3388,7 @@
        local __git_C_args C_args_count=0
        local __git_cmd_idx
 
-       while [ $c -lt $cword ]; do
+       while (( c < cword )); do
                i="${words[c]}"
                case "$i" in
                --git-dir=*)

На практике я использую несколько меньший набор алиасов, в списке же привёл все, какие могут пригодиться в повседневной работе. Надеюсь, этот список кому-то окажется полезным, удачного использования!

Tags:
Hubs:
Total votes 12: ↑5 and ↓7-2
Comments35

Articles