Git и владение им — неотъемлемая часть профессионального программирования. К старту курса по Fullstack-разработке на Python делимся самыми важными деталями о новом релизе Git прямо из блога его разработчиков.


Проект Git с открытым исходным кодом только что выпустил Git 2.35, содержащий функции и исправления ошибок от более чем 93 участников, 35 из которых — новые. В последний раз мы рассказывали вам о новинках Git, выпустив версию 2.34. Чтобы отпраздновать этот последний выпуск, предлагаем вам мнение GitHub о самых интересных функциях и изменениях в версии 2.35.

При работе над сложным изменением может быть полезно временно отбросить части своей работы, чтобы заняться ими отдельно. Для этого мы используем git stash. Этот инструмент сохраняет все изменения файлов, отслеживаемые в вашем репозитории Git. Использование git stash таким образом позволяет очень легко хранить все накопленные изменения, чтобы использовать их позже.

Но что, если вы хотите хранить в stash только часть изменений? Можно воспользоваться git stash -p и интерактивно выбирать куски для хранения или сохранения. Но что, если вы уже сделали это с помощью предыдущего git add -p?

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

 Новый режим git stash --staged позволяет легко спрятать то, что у вас уже есть в области хранения, и ничего больше. Вы можете думать об этом как о git commit, записывающем только поэтапные изменения, но вместо создания нового коммита он прячет новую запись в stash. Затем, когда вы будете готовы, восстановите свои изменения командой git stash pop и продолжите работу.

 [код]

git log имеет богатый набор опций --format, которые вы можете использовать для настройки вывода git log. Они могут быть полезны при доработке терминала, но особенно полезны они для облегчения работы с выводом git log.

В нашем посте, посвящённом Git 2.33, мы рассказали о новом спецификаторе --format под названием %(describe). Он позволил включить вывод git describe вместе с выводом git log. Сразу после выпуска функции вы могли передавать дополнительные опции через спецификатор %(describe), например сопоставление или исключение определённых тегов, написав --format=%(describe:match=<foo>,exclude=<bar>).

В версии 2.35 Git включает несколько новых способов настройки вывода git describe. Теперь вы можете контролировать, использовать ли теги lightweight tags и сколько шестнадцатеричных символов использовать при сокращении идентификатора объекта.

Опробовать их можно с помощью %(describe:tags=<bool>) и %(describe:abbrev=<n>), соответственно. Вот глупый пример, который даёт мне вывод git describe для последних 8 коммитов в моей копии git.git, используя только теги, не относящиеся к релизу, а также для сокращения их хешей используя 13 символов:

$ git log -8 --format='%(describe:exclude=*-rc*,abbrev=13)'
v2.34.1-646-gaf4e5f569bc89
v2.34.1-644-g0330edb239c24
v2.33.1-641-g15f002812f858
v2.34.1-643-g2b95d94b056ab
v2.34.1-642-gb56bd95bbc8f7
v2.34.1-203-gffb9f2980902d
v2.34.1-640-gdf3c41adeb212
v2.34.1-639-g36b65715a4132

Что гораздо чище этого способа объединения git log и git describe:

$ git log -8 --format='%H' | xargs git describe --exclude='*-rc*' --abbrev=13

[код]

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

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

Теперь предположим, что один из тех, с кем вы работали, ротировал ключ. Что вы можете сделать? Обновить запись в файле, чтобы она указывала на новый ключ, но это сделает невозможной проверку объектов, подписанных старым ключом. Можно хранить оба ключа, но это может означать, что вы допустите подпись новых объектов старым ключом.

Git 2.35 даёт несколько преимуществ директив OpenSSH valid-before и valid-after, гарантируя, что объект, который вы проверяете, подписан действительной на момент создания подписью. Это позволяет людям ротировать ключи, отслеживая валидность каждого ключа без ошибок с объектами, которые подписаны старым ключом.

Git 2.35 также поддерживает новые типы ключей в конфигурации user.signingKey, когда вы включаете ключ дословно, а не храните пути к файлу, содержащему ключ подписи). Ранее правило интерпретации user.signingKey заключалось в том, чтобы рассматривать его значение как литеральный SSH-ключ, если оно начиналось с «ssh-», и рассматривать его как filepath в противном случае. Теперь вы можете указывать литеральные SSH-ключи с типами ключей, которые не начинаются с «ssh-» (например, ключи ECDSA).

[код, код]

Если вы когда-либо имели дело с конфликтом слияния, вы знаете, что точное разрешение конфликтов требует тщательного обдумывания. Возможно, вы не слышали о параметре merge.conflictStyle в Git, который намного упрощает разрешение конфликтов.

Значение по умолчанию для этой конфигурации — «merge», что создаёт маркеры конфликтов слияния, с которыми вы, вероятно, знакомы. Но есть и другой режим, «diff3», который показывает базу слияния в дополнение к изменениям по обе стороны.

В Git 2.35 появился новый режим «zdiff3», который ревностно перемещает любые общие строки в начале или конце конфликта вне конфликтной области, что делает разрешаемый вами конфликт немного меньше.

Например, у меня есть список с комментарием-заполнителем, и я объединяю две ветви, каждая из которых добавляет разное содержимое для заполнения заполнителя. Обычный конфликт слияния может выглядеть примерно так:

1,
foo,
bar,
<<<<<<< HEAD
=======
quux,
woot,
>>>>>>> side
baz,
3,

Повторная попытка с маркерами конфликтов в стиле diff3 показывает мне базу слияния (обнаруживая комментарий, о котором я не знал) вместе с полным содержимым обеих сторон:

1,
<<<<<<< HEAD
foo,
bar,
baz,
||||||| 60c6bd0
# add more here
=======
foo,
bar,
quux,
woot,
baz,
>>>>>>> side
3,

Приведённый выше пример даёт нам больше деталей, но обратите внимание, что обе стороны добавляют «foo» и «bar» в начале и «baz» в конце. Последняя попытка с маркерами конфликтов в стиле zdiff3 выводит «foo» и «bar» вообще за пределы конфликтующей области. Результат получается как более точным (поскольку включает базу слияния), так и более кратким (поскольку он обрабатывает избыточные части конфликта за нас).

1,
foo,
bar,
<<<<<<< HEAD
||||||| 60c6bd0
# add more here
=======
quux,
woot,
>>>>>>> side
baz,
3,

[код]

Вы можете знать (а можете и не знать!), что Git поддерживает несколько различных алгоритмов генерации diff. Обычный алгоритм (и тот, с которым вы, скорее всего, уже знакомы) — это алгоритм Myers diff. Другим является алгоритм --patience diff, а также его кузен --histogram.

Часто это может привести к более читаемым для человека отличиям (например, избегая распространённой проблемы, когда добавление новой функции начинает diff, добавляя закрывающую скобку к функции, непосредственно предшествующей новой).

В Git 2.35 --histogram получил хороший прирост производительности, что должно сделать его быстрее во многих случаях. Подробности слишком сложны, чтобы приводить их здесь полностью, но вы можете ознакомиться со ссылкой ниже и увидеть все улучшения и сочные цифры производительности.

[код]

Если вы любитель улучшений производительности (и опций diff!), то вот ещё один вариант, который может вам понравиться. Возможно, вы слышали об опции git diff --color-moved (если не слышали, мы рассказывали о ней в нашей статье Highlights from Git 2.17). Возможно, вы не слышали о связанном с ним --color-moved-ws, который управляет тем, как пробельные символы игнорируются или не игнорируются при раскрашивании диффов.

Представьте это как другие опции выравнивания пробелов (например, --ignore-space-at-eol, --ignore-space-change или --ignore-all-space), но специально для тех случаев, когда вы запускаете diff в режиме --color-moved.

Git 2.35 также включает различные улучшения производительности для --color-moved-ws. Если вы ещё не пробовали --color-moved, попробуйте! Если вы уже используете его в своём рабочем процессе, он должен стать быстрее после простого обновления до Git 2.35.

[код]

Ещё в Основных моментах Git 2.19 мы рассказывали о том, как новая функция в git grep позволила аддону git jump заполнить ваш редактор точными местоположениями совпадений git grep.

Если вы не знакомы с git jump, вот краткая справка. git jump заполняет список быстрых исправлений Vim местами конфликтов слияния, совпадений grep или ошибок diff (при выполнении git jump merge, git jump grep или git jump diff соответственно).

В Git 2.35 git jump merge научился сужать набор конфликтов слияния с помощью pathspec. Так что, если вы работаете над разрешением большого конфликта слияния, но хотите поработать только над определённым разделом, вы можете запустить его:

$ git jump merge -- foo

чтобы сосредоточиться только на конфликтах в каталоге foo. В качестве альтернативы, если вы хотите пропустить конфликты в определённом каталоге, то можете использовать специальный отрицательный pathspec:

# Skip any conflicts in the Documentation directory for now.
$ git jump merge -- ':^Documentation'

[код]

Возможно, вы слышали о фильтрах Git clean и smudge, которые позволяют пользователям указывать, как «чистить» файлы при постановке на хранение или «размазывать» их при заполнении рабочей копии. 

Git LFS широко использует эти фильтры для представления больших файлов с помощью «указателей». Большие файлы преобразуются в указатели при постановке на хранение с помощью фильтра clean, а затем обратно в большие файлы при заполнении рабочей копии с помощью фильтра smudge.

Git исторически использует типы size_t и unsigned long относительно взаимозаменяемо. Это понятно, поскольку Git изначально написан на Linux, где эти два типа имеют одинаковую ширину (и, следовательно, одинаковый представляемый диапазон значений).

Но в Windows, которая использует модель данных LLP64, тип unsigned long имеет ширину только 4 байта, тогда как size_t имеет ширину 8 байт. Поскольку фильтры clean и smudge ранее использовали unsigned long, это означало, что они не могли обрабатывать файлы размером более 4 Гб на платформах, соответствующих LLP64.

В Git 2.35 для представления длины объекта продолжаются усилия по стандартизации правильного типа size_t, что позволяет фильтрам обрабатывать файлы размером более 4 Гб даже на платформах LLP64, таких как Windows1.

[код]

Если вы не использовали Git в рабочем процессе на основе патчей, где они отправляются по электронной почте, то можете не знать о команде git am, которая извлекает патчи из почтового ящика и применяет их к вашему репозиторию.

Раньше, если вы пытались отправить через git am письмо, не содержащее патча, вы попадали в состояние, подобное этому:

$ git am /path/to/mailbox
Applying: [...]
Patch is empty.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

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

В Git 2.35 вы можете указать, как будет вести себя git am, если он встретит пустой коммит с помощью --empty=<stop|drop|keep>. Эти опции предписывают am либо полностью прекратить применение патчей, либо отменить все пустые патчи, либо применять их как есть (создавая пустой коммит, но сохраняя сообщение в логе). Если вы забыли указать поведение --empty, но попытались применить пустой патч, вы можете выполнить git am --allow-empty, чтобы применить текущий патч как есть и продолжить.

[код]

Читатели блога, возможно, помнят наше обсуждение sparse index, функции Git, которая улучшает производительность в хранилищах, использующих sparse-checkout. Вышеупомянутая ссылка подробно описывает эту функцию, но суть заключается в том, что она хранит сжатую форму индекса, который растёт вместе с размером вашей проверки, а не с размером вашего репозитория.

В версии 2.34 разреженный индекс был интегрирован в несколько команд, включая git status, git add и git commit. В версии 2.35 поддержка команд для разреженного индекса расширилась и теперь содержит интеграции с git reset, git diff, git blame, git fetch, git pull, а также новый режим git ls-files.

[код, код, код]

Говоря о разреженном контроле, встроенный модуль git sparse-checkout отказался от подкоманды git sparse-checkout init в пользу git sparse-checkout set. Все опции, ранее доступные в подкоманде init, доступны в подкоманде set. Например, вы можете включить cone-режим sparse-checkout и включить каталог foo с помощью этой команды:

$ git sparse-checkout set --cone foo

[код]

Git хранит ссылки (такие как ветки и теги) в вашем репозитории одним из двух способов: либо «свободным», в виде файла внутри .git/refs (как .git/refs/heads/main), либо «упакованным» — записью внутри файла по адресу .git/packed_refs.

Но для репозиториев с действительно гигантским количеством ссылок может оказаться неэффективным хранить их все вместе в одном файле. Предложение reftable описывает альтернативный способ, которым JGit хранит ссылки блочно-ориентированно. JGit использует reftable уже много лет, но у Git собственной реализации не было.

Reftable обещает улучшить производительность чтения и записи для хранилищ с большим количеством ссылок. Работа по внедрению reftable в Git ведётся уже довольно давно, и Git 2.35 поставляется с начальным импортом бэкенда reftable. Этот новый бэкенд ещё не интегрирован в refs, поэтому вы пока не можете начать использовать reftable, но мы будем держать вас в курсе любых новых разработок.

[код]

Невидимая часть айсберга

Это лишь пример изменений из последнего выпуска. Для получения дополнительной информации ознакомьтесь с примечаниями к выпуску 2.35 или любой предыдущей версии в репозитории Git.


  1. Обратите внимание, что эти исправления появились в Git для Windows в выпуске 2.34, так что технически это совсем не новость! Но мы всё равно упомянем об этом. 

Продолжить работу с Git вы сможете на наших курсах:

Узнайте подробности здесь.

Другие профессии и курсы

Комментарии (14)


  1. ymishta
    26.01.2022 22:16
    +5

    Интересно, когда появится возможность найти самый первый коммит во всем дереве, где в любом из его файлов есть нужный текст?


    1. emaxx
      27.01.2022 00:45
      +2

      git log -S? или вам нужно что-то другое?


    1. imadbazzal
      27.01.2022 11:37

      Возможно вам поможет git bisect?


  1. Nacreous1991
    27.01.2022 01:58

    Простите за оффтоп. А есть какой-то курс или ещё что-то чтобы сдать git-джедаем? А то вроде бы использую основные команды, но есть понимание что можно и нужно больше


    1. Megadeth77
      27.01.2022 02:28
      +2

      https://git-scm.com/book/ru/v2 же для начала проштудировать


      1. Veber
        27.01.2022 04:24

        А после? Вряд ли после этой книги можно именно джедаем стать.


        1. Deosis
          27.01.2022 07:06
          +2

          Более детальное описание здесь: https://github.com/git/git


    1. QuAzI
      27.01.2022 09:05
      -1

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

      [pretty]
          time-hash-author-branch = %C(auto,brightyellow)%ad %C(auto,yellow)%h %C(auto,green)%<(22,trunc)%cn %C(auto,reset)%w(0,0,54)%s %C(auto,brightred)%d%C(auto,reset)
      [alias]
          changes = log --no-merges --decorate=short --pretty=time-hash-author-branch --date=format:'%Y-%m-%d %H:%M:%S'


      1. ApeCoder
        27.01.2022 12:09
        +1

        Чем stackoverflow не катит?


        1. Ritan
          27.01.2022 15:10

          Слишком формально. И их система кармы совсем не поощряет вопросы, которые вообще возникают только из-за недопонимания темы


          1. Usul
            28.01.2022 11:54

            Да, есть такое. StackOverflow побуждает пользователей сначала подумать, потом поискать ответ на вопрос в различных источниках (в том числе и на самом SO), и уже только потом задавать вопрос. Система не идеальна, но во многих ситуациях она работает. Особенно, когда речь идет о широко распространенных инструментах, типа git, и о типовых случаях их использования.

            Например, по вопросу@QuAzl выше:

            https://stackoverflow.com/questions/18384808/which-branch-was-this-commit-in?noredirect=1&lq=1

            git doesn't track branches as first class citizens: they might get renamed, deleted, recreated; so the answer might not be as meaningful as you would like it to be, e.g. it doesn't necessarily give you the branch that on which the commit was originally authored - instead you get the (local) branches of which the commit is currently part of


    1. RC_Cat
      27.01.2022 09:06

      Отписал в личку, чтобы не реклама.


  1. acmnu
    27.01.2022 08:27

    GitHub вроде как обещает поддержку ssh ключей в ближайшие месяцы. Собственно именно диапазонов дат им не хватало.

    https://github.com/github/feedback/discussions/7744#discussioncomment-1794438


  1. ysadyev
    27.01.2022 11:37
    +2

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