company_banner

Малоизвестные Git-команды

https://hackernoon.com/lesser-known-git-commands-151a1918a60#.l48pn1p8i
  • Перевод


У Git есть строгие обязательства по обратной совместимости: многие продвинутые возможности скрыты за разнообразными опциями, а не применяются как поведение по умолчанию. К счастью, Git также поддерживает и алиасы, так что вы можете создавать свои собственные команды, которые делают всю характерную для Git магию. Под катом — подборка полезных (или как минимум забавных) алиасов, определённых в моём .gitconfig.

git please


$ git config --global alias.please 'push --force-with-lease'

Каждому разработчику приходилось хотя бы раз общаться со своим тимлидом на тему принудительного пуша (force pushing) в общую ветку (не делайте этого). Ребейз (rebasing), внесение правок и squash — всё это забавно до тех пор, пока вы не перезапишете часть общей истории и не раскидаете дублирующиеся коммиты по всему репозиторию. К счастью, Git не позволит вам невольно перезаписать историю на сервере. Вам придётся явным образом передать в git push опцию --force, чтобы доказать серьёзность своих намерений. Но принудительный пуш — это грубый подход: вы затаптываете локальной версией вышерасположенную ветку, и все изменения, которые вы к тому моменту не подтянули (fetch), будут стёрты из истории.



Git-опция --force-with-lease действует гораздо аккуратнее: она проверяет, чтобы ваша локальная копия ref’а была самой свежей, прежде чем накатить её. Это означает, что вы как минимум подтянули все изменения, которые собираетесь затоптать. Но чтобы не писать каждый раз git push --force-with-lease, я сделал для этой строки алиас: git please

git commend


$ git config --global alias.commend 'commit --amend --no-edit'

Бывало так, что вы закоммитили и тут же сообразили, что забыли проиндексировать (stage) файл? Больше не нужно об этом беспокоиться! Алиас git commend тихо прикрепляет к последнему созданному вами коммиту все проиндексированные файлы, повторно используя уже имеющееся сообщение о коммите.

$ git add Dockerfile
$ git commit -m ‘Update Bitbucket pipeline with new Docker image’
# (facepalm)
$ git add bitbucket-pipelines.yml
$ git commend

git it


$ git config --global alias.it \
'!git init && git commit -m “root” --allow-empty'

Первому коммиту в репозитории нельзя сделать ребейз, как обычному. Поэтому рекомендуется в качестве корневого создавать пустой коммит. Алиас git it инициализирует ваш репозиторий и за одну операцию создаёт пустой корневой коммит. И когда вы в следующий раз запустите проект, то не надо просто добавлять его в систему управления версиями: выполните git it!

$ cd shiny-new-thing
$ git it
Initialized empty Git repository in /shiny-new-thing/.git/
[master (root-commit) efc9119] root

git staaash


$ git config --global alias.stsh 'stash --keep-index'
$ git config --global alias.staash 'stash --include-untracked'
$ git config --global alias.staaash 'stash --all'

git stash — одна из самых восхитительных и полезных Git-команд. Она регистрирует все изменения, вносимые в отслеживаемый файл в вашем рабочем дереве, и скрывает их для последующего использования, а вам показывает чистое дерево, чтобы вы могли спокойно работать с другой его частью. Но если вы создали новые файлы и ещё не проиндексировали их, то по умолчанию git stash их не тронет, поэтому у вас будет неопрятное рабочее дерево. Соответственно, по умолчанию не скрывается и содержимое неотслеживаемых или игнорируемых файлов.

Я сделал несколько удобных алиасов для разных вариантов git stash, в зависимости от того, какие биты вашего рабочего дерева нужно скрыть:

git stsh      # скрывает только непроиндексированные изменения в отслеживаемых файлах
git stash     # скрывает все изменения в отслеживаемых файлах 
git staash    # скрывает неотслеживаемые и отслеживаемые файлы
git staaash   # скрывает игнорируемые, неотслеживаемые и отслеживаемые файлы

Если сомневаетесь в выборе, то самый длинный алиас (git staaash) всегда сможет восстановить рабочее дерево состояния свежего клона вашего репозитория.

git shorty


$ git config --global alias.shorty 'status --short --branch'

Я запускаю git status чаще любой другой Git-команды. Встроенная помощь в Git за последние годы стала куда удобнее, что очень хорошо для начинающих, но для более опытных пользователей информация слишком многословна. Например, git status объясняет мне в 12 строках, что у меня пара индексированных, неиндексированных и неотслеживаемых изменений:

$ git status
On branch master
Changes to be committed:
  (use “git reset HEAD <file>…” to unstage)
    modified: package.json
Changes not staged for commit:
  (use “git add <file>…” to update what will be committed)
  (use “git checkout -- <file>…” to discard changes)
    modified: package.json
Untracked files:
  (use “git add <file>…” to include in what will be committed)
    index.js

Всё то же самое git shorty говорит мне тремя строками:

$ git shorty
## master
AM test
?? .gitignore

Для краткости я сделал это в виде алиаса git st, не смог остановиться.

git merc


$ git config --global alias.merc 'merge --no-ff'

Если вы используете обычный рабочий процесс ветвления без ребейза, то будет не лучшим решением запускать стандартный git merge для слияния веток с фичами с мастер-веткой. Если не добавить к этой команде опции, то по умолчанию станет использоваться стратегия слияния --ff, при которой новый коммит слияния будет создан только в том случае, если в мастер-ветке нет новых изменений. В противном случае мастер-ветка просто «перемотается» до места последнего коммита в вашей ветке. Лишь иногда, создавая коммит слияния, при просмотре Git-истории бывает непросто сказать, какой код был разработан в какой ветке.



git merc использует стратегию --no-ff, при которой всегда создаётся коммит слияния.



Между прочим, --no-ff всегда используется по умолчанию в ходе слияния pull request’ов в Bitbucket.

git grog


$ git config --global alias.grog 'log --graph --abbrev-commit --decorate --all --format=format:"%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(dim white) - %an%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n %C(white)%s%C(reset)"'

Мой алиас git grog (или graphical log) в последние годы разросся настолько, что я больше не уверен, будто точно знаю, что он делает. Но выглядит красиво:



Для сравнения, вот стандартный git log:



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

Для поклонников GUI


Если вы поклонник Git GUI и работаете под Mac или Windows, то, возможно, вы используете наш бесплатный Git-клиент Atlassian SourceTree. Если да, то примените описанные в этой статье алиасы, создав новое кастомное действие — можно назначить комбинацию клавиш — в настройках SourceTree:



Это действие запускается с помощью меню (Actions → Custom Actions) или клавиатурной комбинации:



Приятного алиасинга!
Метки:
Mail.Ru Group 1 001,51
Строим Интернет
Поделиться публикацией
Похожие публикации
Комментарии 41
  • +9

    очередная кучка алиасов
    нет бы в апстрим протащить хотя бы какой-нибудь (хотя бы git it в качестве поведения по умолчанию)


    inb4: я понимаю, что это сложно

    • 0
      Прошу прощения, но я не вижу никакого смысла протаскивать это в апстрим, если все это можно реализовать в виде алиасов. Не стоит плодить сущности без особой нужды на то.
      • +2
        все это можно реализовать в виде алиасов

        своих собственных алиасов у каждого второго пользователя

    • +57
      Название вводит в заблуждение. Это не подборка команд, а подборка алиасов, которые отдельно взятой группе пользователей кажутся удобными.
      • +26

        Действительно, ожидал увидеть мясо про rev-parse, for-each-ref, cat-file, rebase --onto, fetch +, update-ref, show-ref… А тут просто пачка алиасов.

        • +1
          Можете порекомендовать статьи, в которых более-менее доступно разжевано это самое «мясо»?
          • 0

            К сожалению нет. Небольшая часть этого есть в pro git. Оставшаяся же часть осваивается посредством долгого и вдумчивого ковыряния в git на практике.

        • +7
          Это еще не самое страшное. Хуже, когда такой любитель алиасов начинает их втыкать в продуктовые и CI скрипты, потому что «так удобнее».
          • +3

            К слову, я в скриптах пользуюсь исключительно длинными ключами команд (git commit --message="$ABYRVALG" вместо git commit -m "$ABYRVALG")

            • +2
              Разумеется. Скрипт пишется один раз, а читается несколько, да еще и через пол года-год.
        • –2
          немного полезных алисасов от проекта oh-my-zsh: https://github.com/robbyrussell/oh-my-zsh/wiki/Cheatsheet
          • +3
            Самый нужный алиас это «кто первым добавил эту строку», пока что-то всё что попадалось как-то криво работало или не работало
            • –14
              Использую git-flow и уже давно забыл о боли.
              • 0

                Хмм… На Хабре не любят git-flow?

                • +2

                  На хабре не любят бессмысленные, да ещё и не относящиеся к статье комментарии. Вот написал бы inook, какое отношение git‐flow имеет к статье и почему он «забыл о боли» — минусов было бы меньше.

                  • +1

                    Думаю, потому что мерж там по умолчанию --no-ff и не нужно вспоминать, с какой опцией -d или -D нужно удалять слитые ветки. Ну и в принципе, стандартизированный flow и инструменты для него освобождают мозг для более важных задач.

                    • 0

                      staaash, grog, commend, shorty, please? Насколько мне известно, git‐flow заменяет только merc и it. Кроме того, совет «используйте git‐flow» подходит только для новых проектов или проектов, где он уже используется.

              • +2
                А я использую пока лишь TortoiseGit в самом примитивном виде, просто для себя лично, локально. Без веток, только фиксацию изменений, чтобы не потерять историю изменений и чтобы было куда откатиться если чего поломаю. На предыдущей работе был svn, на новой ничего — а без контроля версий как-то уже некомфортно :)
                • +4

                  А вы изучите лучше все возможности гита — даже для ведения репозитория одним человеком ветки крайне полезны. Для чего:
                  чтобы вносить изменения в код на какую-то другую тему в то время как имеются незаконченные изменения, для фиксации в общей истории только значащих изменений, в то время как в рабочей ветке могут быть зафиксированы незавершенные состояния кода, для более простого ревью фич, для откладывания изменений в коде и применении их, когда основа для изменения уже другая.

                  • +1
                    Плюсую, схема «по ветке для фичи/фикса» работает.
                  • 0
                    на новой ничего

                    Т.е. вы один что-то разрабатываете на новой работе, или все-таки у вас там команда и вы живете без системы контроля версий?
                  • +1
                    добавлю от себя еще две полезных команды

                    показать ветки в которые вошел данный коммит:
                    git branch -a --contains $SHA
                    (можно добавить в sourceTree customAction и дергать мышкой из гуи)

                    показать разницу в коммитах между двумя бранчами
                    git log --oneline release/release_1.23 ^release/release_1.22
                    • +1

                      А можно как-то проверить коммиты, которые были перенесены в другую ветку с помощью cherry-pick? sha у них отличаются, конечно. Можно попробовать patch-id, но это несколько затратно перебирать много коммитов.

                      • 0
                        Если в коммитах проставляется герритовский ChangeId, то поиском по коммитам с тем же ChangeId. Это можно сделать например с помощью git grep. Но это если вы знаете копии чего вы хотите искать.

                        В общем случае видимо только скриптить перебор.
                        • +1
                          Есть команда cherry, который делает обратное — показывает коммиты, которых еще нет в другой ветке, может быть она подойдет? Правда, для определения коммитов используется тот же patch-id, подозреваю, который вы сочли дорогим.
                          • 0

                            Эта команда, как минимум, уже готова. Спасибо, попробую.

                      • –4
                        эх, а я так и не добрался до Git :(
                        • +1

                          Зря. Я начал использовать vcs ещё когда работал на заводе, где никому ничего не надо было и всякий работал как хотел. Тогда это была cvs. Я даже ветки не использовал, но уже забыл про "ё-моё, что ж я наделал-то", "откуда это здесь" и "что я имел в виду". Теперь однозначно git, ветки, упрощённый git flow — даже при том, что владею кодом единолично. Зато нет проблем разрабатывать несколько функциональных едиц, радикально переделывать код, но при этом не терять возможности выпустить хотфикс менее, чем за полчаса. Это реально стоит времени на обучение.

                          • +2
                            Да, спасибо за информацию, думаю в скором будущем начну обучение.
                            • +1
                              Начните прямо сейчас! А то в ближайшем будущем пожалеете, что тянули время. Буквально пяток команд достаточно изучить для начала работы.
                        • +1

                          Я очень старался, но так и не понял отрывок про git shorty. В полном выводе git status есть информация про package.json и index.js, а в выводе git shorty только какой-то test и .gitignore. Может кто-нибудь пояснить, что имел в виду автор?

                          • +1

                            Автор опечатался, скорее всего, и сделал копипасту с разных репозиториев.


                            % git status
                            On branch master
                            Changes to be committed:
                              (use "git reset HEAD <file>..." to unstage)
                            
                                deleted:    a
                            
                            Changes not staged for commit:
                              (use "git add <file>..." to update what will be committed)
                              (use "git checkout -- <file>..." to discard changes in working directory)
                            
                                modified:   b
                            
                            Untracked files:
                              (use "git add <file>..." to include in what will be committed)
                            
                                c
                            
                            % git status --short --branch
                            ## master
                            D  a
                             M b
                            ?? c
                          • +4
                            я вижу ересь! вся суть гита в том и состоит, чтобы глумиться над теми, кто не смог пройти по минному полю системы его команд.
                            • +5
                              Первому коммиту в репозитории нельзя сделать ребейз, как обычному.

                              git rebase -i --root

                              • 0

                                soft reset последнего коммита с выводом статуса


                                git config --global alias.rs1 '!git reset --soft HEAD~1 && git status --short --branch'
                                • 0
                                  git status --short --branch
                                  

                                  =
                                  git status -sb
                                  


                                  можно спокойно обойтись и без алиаса
                                  • +2
                                    git status — это длинно.
                                    git st — наше всё! (тяжкое наследие svn, я понимаю).
                                    • 0
                                      Я еще больше ленив видимо. У меня git status -sb реализовано в виде алиаса gs
                                      git log -> gl
                                      gd -> git diff
                                      gp -> git push ну и так далее, что умещается в две буквы
                                  • 0

                                    Как же приятно узнавать что-то новое о старом добром, казалось бы, вдоль и поперёк известном инструменте!

                                    • +1
                                      С алиасов гита перешел на алиасы из oh-my-zsh https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/git/git.plugin.zsh — очень рекомендую, хорошо продуманы. Заменили мне все комманды гита, которые я постоянно использую в работе, избавив меня от головной боли с продумыванием имен и подбором параметров для команд, когда я пытался сам сделать хорошую систему под себя.
                                      • +3
                                        Сегодня добавил себе ещё алиас git branches:

                                        git config --global alias.branches "for-each-ref --sort=committerdate refs/heads/ --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(color:red)%(objectname:short)%(color:reset) - %(contents:subject) - %(authorname) (%(color:green)%(committerdate:relative)%(color:reset))'"
                                        


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

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

                                        Самое читаемое