7.1 Инструменты Git – Выбор ревизии (коммита)
К этому моменту вы уже изучили большинство повседневных команд и способов организации рабочего процесса, которые необходимы для управления Git репозиторием, используемым для управления вашим исходным кодом. Вы выполнили основные задания по отслеживанию и сохранению файлов в Git, вооружились мощью области подготовленных изменений (индекса), легковесного ветвления и слияния.
Теперь настало время познакомиться с некоторыми очень мощными возможностями Git, которые при повседневной работе вам, наверное, не потребуются, но в какой-то момент могут оказаться полезными.
Git позволяет различными способами указать коммиты или их диапазоны. Эти способы не всегда очевидны, но их полезно знать.
Одиночные ревизии
Конечно, вы можете ссылаться на коммит по его хешу SHA-1, но существуют более удобные для человека способы. В данном разделе описываются различные способы обращения к одному коммиту.
Сокращённый SHA-1
Git достаточно умен, чтобы понять какой коммит имеется ввиду по нескольким первым символам его хеша, если указанная часть SHA-1 имеет в длину, по крайней мере, четыре символа и однозначна – то есть в текущем репозитории существует только один объект с таким частичным SHA-1.
Например, предположим, чтобы найти некоторый коммит, вы выполнили команду git log и нашли коммит, в котором добавили определённую функциональность:
Предположим, что в нашем примере это коммит 1c002dd… . Если вы хотите выполнить для него git show , то следующие команды эквиваленты (предполагается, что сокращения однозначны):
Git может вычислить уникальные сокращения для ваших значений SHA-1. Если вы передадите опцию —abbrev-commit команде git log , в выводе будут использоваться сокращённые значения, сохраняющие уникальность; по умолчанию используется семь символов, но для сохранения уникальности SHA-1 могут использоваться и более длинные значения.
Обычно от восьми до десяти символов более чем достаточно для сохранения уникальности значений в проекте.
Например, в ядре Linux, который является довольно большим проектом с более чем 450 тыс. коммитов и 3.6 млн. объектов, отсутствуют объекты, чьи SHA-1 совпадают более, чем в 11 первых символах.
Примечание
Небольшое замечание о SHA-1
Большинство людей в этом месте начинают беспокоиться о том, что будет, если у них в репозитории случайно появятся два объекта с одинаковыми значениями SHA-1. Что тогда?
Если вы вдруг зафиксируете объект, который имеет такое же значение SHA-1, как и предыдущий объект в вашем репозитории, Git увидит этот предыдущий объект в своей базе и посчитает, что он уже был записан. Если вы позже попытаетесь переключиться на этот объект, то вы всегда будете получать данные первого объекта.
Однако, вы должны осознавать, насколько маловероятен такой сценарий. Длина SHA-1 составляет 20 байт или 160 бит. Количество случайно хешированных объектов, необходимых для достижения 50% вероятности возникновения коллизии, равно примерно 2 80 (формула для определения вероятности возникновения коллизии p = (n(n-1)/2) * (1/2^160)). 2 80 – это 1.2 × 10 24 , или 1 миллион миллиардов миллиардов, что в 1200 раз больше количества песчинок на земле.
Приведём пример, чтобы дать вам представление, чего будет стоить получение коллизии SHA-1. Если бы все 6.5 миллиардов человек на Земле были программистами, и ежесекундно каждый из них производил количество кода, эквивалентное всей истории ядра Linux (3.6 миллиона Git-объектов), и отправлял его в один огромный Git репозиторий, то потребовалось бы около 2 лет, пока этот репозиторий накопил бы количество объектов, достаточное для 50% вероятности возникновения коллизии SHA-1. Более вероятно, что каждый член вашей команды в одну и туже ночь будет атакован и убит волками в несвязанных друг с другом происшествиях.
Если выделить на это несколько тысяч долларов вычислительной мощности, можно будет синтезировать два файла с одним и тем же хешем, что было доказано проектом https://shattered.io/ в феврале 2017 года. Git движется к использованию SHA256 в качестве алгоритма хеширования по умолчанию, который намного более устойчив к атакам с коллизиями и имеет код, помогающий смягчить эту атаку (хотя он не может полностью ее устранить).
Ссылки на ветки
Для наиболее простого способа указать коммит требуется существование ветки, указывающей на этот коммит. Тогда вы можете использовать имя ветки в любой команде Git, которая ожидает коммит или значение SHA-1. Например, если вы хотите просмотреть последний коммит в ветке, то следующие команды эквивалентны (предполагается, что ветка topic1 указывает на коммит ca82a6d ):
Если вы хотите узнать SHA-1 объекта, на который указывает ветка, или увидеть, к чему сводятся все примеры в терминах SHA-1, то вы можете воспользоваться служебной командой Git rev-parse . Служебные команды подробно рассмотрены в главе «Git изнутри»; в основном, команда rev-parse предназначена для низкоуровневых операций, а не для ежедневного использования. Однако она может быть полезна, когда вам нужно увидеть, что происходит в действительности. Теперь вы можете выполнить rev-parse для вашей ветки.
Сокращения журнала ссылок
Одна из вещей, которую Git делает в фоновом режиме, является ведение журнала ссылок, в котором сохраняется то, куда указывали HEAD и ветки за последние несколько месяцев.
Для просмотра этого журнала используется команда git reflog :
Каждый раз, когда по каким-то причинам изменяется вершина вашей ветки, Git сохраняет информацию об этом в эту временную историю. И вы можете указывать старые коммиты, используя эти данные. Например, чтобы посмотреть, куда ссылался указатель HEAD пять шагов назад, используйте ссылку @ <5>, которую можно увидеть в выводимых данных команды reflog :
Этот синтаксис используется и в случае, когда требуется посмотреть, в каком состоянии пребывала ветка некоторое время назад. В частности, чтобы увидеть, где была ветка master вчера, следует написать:
Вы увидите, что было на вершине ветки вчера. Такой способ работает только для данных, которые всё ещё содержатся в вашем журнале ссылок, поэтому вы не можете использовать её для коммитов, которые старше нескольких месяцев.
Для просмотра журнала ссылок в формате, похожем на вывод git log , вы можете выполнить git log -g :
Важно отметить, что информация в журнале ссылок строго локальная – это лог того, что вы делали в вашем репозитории. Ссылки не будут такими же в других копиях репозитория; а сразу после первоначального клонирования репозитория, у вас будет пустой журнал ссылок, так как никаких действий в вашем репозитории пока не производилось. Команда git show HEAD@ <2.months.ago>будет работать, только если вы клонировали проект, по крайней мере, два месяца назад – если вы клонировали его пять минут назад, то не получите никаких результатов.
Подсказка
Воспринимайте reflog в Git как историю командной строки
Если у вас есть опыт работы с UNIX или Linux, можете думать о reflog как об истории командной строки Git, которая подчеркивает, что то, что там есть, явно актуально только для вас и вашего «сеанса» и не имеет ничего общего с кем-либо еще, кто может работать на той же машине.
Примечание
Экранирование фигурных скобок в PowerShell
При использовании PowerShell фигурные скобки, такие как < и >, являются специальными символами и должны быть экранированы. Вы можете экранировать их с помощью апострофа ` или поместить ссылку на коммит в кавычки:
Ссылки на предков
Ещё один популярный способ указать коммит – это использовать её родословную. Если вы поместите ^ в конце ссылки, Git поймёт, что нужно использовать родителя этого коммита. Предположим, история вашего проекта выглядит следующим образом:
Для просмотра предыдущего коммита достаточно написать HEAD^ , что означает «родитель HEAD »:
Примечание
Экранирование символа карета в Windows
В командной строке Windows (cmd.exe) ^ является специальным символом и требует другого обращения. Вы можете либо удвоить его, либо поместить ссылку на коммит в кавычки:
Также вы можете указать число после ^ – например, d921970^2 означает «второй родитель коммита d921970 ». Такой синтаксис полезен только для коммитов слияния, которые имеют больше одного родителя. Первым родителем является ветка, в которую вы выполняли слияние, а вторым – коммит в ветке, которую вы сливали:
Второе важное обозначение для указания предков – это символ тильда
. Он также соответствует ссылке на первого родителя, поэтому HEAD
и HEAD^ эквивалентны. Различия становятся заметными, когда вы указываете число. HEAD
2 означает «первый родитель первого родителя» или «дедушка» – при этом происходит переход от заданного предка вглубь указанное число раз. К примеру, для показанной ранее истории, коммитом HEAD
То же самое можно записать как HEAD
, что также является первым родителем первого родителя первого родителя:
Вы также можете совмещать эти обозначения – можно получить второго родителя предыдущей ссылки (предполагается, что это коммит слияния) используя запись HEAD
3^2 , и так далее.
Диапазоны коммитов
Теперь вы умеете указывать отдельные коммиты; давайте посмотрим, как указывать диапазоны коммитов. Это в частности полезно для управления вашими ветками – если у вас есть множество веток, вы можете использовать указание диапазонов коммитов для ответа на вопрос «Что было сделано в этой ветке, что я ещё не слил в основную ветку?»
Две точки
Наиболее часто для указания диапазона коммитов используется синтаксис с двумя точками. Таким образом, вы, по сути, просите Git включить в диапазон коммитов только те, которые достижимы из одной, но не достижимы из другой. Для примера предположим, что ваша история выглядит, как представлено на рисунке 136.
Рисунок 136 – Пример истории для выбора диапазонов коммитов
Вы хотите посмотреть, что находится в вашей экспериментальной ветке, которая ещё не была слита в основную. Вы можете попросить Git отобразить в логе только такие коммиты, используя запись master..experiment – она означает «все коммиты, которые доступны из ветки experiment , но не доступны из ветки master ». Для краткости и наглядности в этих примерах вместо настоящего вывода лога мы будем использовать для коммитов их буквенные обозначения из диаграммы, располагая их в должном порядке:
И напротив, если вы хотите наоборот увидеть все коммиты ветки master , которых нет в ветке experiment , вы можете поменять местами имена веток в команде. При использовании записи experiment..master будут отображены все коммиты ветки master , недоступные из ветки experiment :
Это полезно, если вы хотите сохранить ветку experiment в актуальном состоянии и просмотреть, какие изменения нужно в нее слить. Другое частое использование такого синтаксиса – просмотр того, что будет отправлено в удалённый репозиторий.
Такая команда покажет вам все коммиты вашей текущей ветки, которые отсутствуют в ветке master удалённого репозитория origin . Если вы выполните git push , находясь на ветке, отслеживающей origin/master , то коммиты, отображённые командой git log origin/master..HEAD , будут теми коммитами, которые отправятся на сервер. Вы также можете опустить одну из частей в такой записи, Git будет считать её равной HEAD . Например, вы можете получить такой же результат как в предыдущем примере, выполнив git log origin/master.. , – Git подставит HEAD , если одна часть отсутствует.
Множественная выборка
Запись с двумя точками полезна как сокращение, но, возможно, вы захотите использовать более двух веток для указания нужной ревизии, например, для того, чтобы узнать какие коммиты присутствуют в любой из нескольких веток, но отсутствуют в ветке, в которой вы сейчас находитесь. Git позволяет сделать это, используя символ ^ или опцию —not перед любой ссылкой, доступные коммиты из которой вы не хотите видеть. Таким образом, следующие три команды эквивалентны:
Этот синтаксис удобен, так как позволяет указывать в запросе более двух ссылок, чего не позволяет сделать синтаксис с двумя точками. Например, если вы хотите увидеть все коммиты, доступные из refA и refB , но не доступные из refC , вы можете использовать одну из следующих команд:
Это делает систему запросов ревизий более мощной и должно помочь вам лучше понять, что содержится в вашей ветке.
Три точки
Последний основной способ выбора ревизий – это синтаксис с тремя точками, который обозначает все коммиты, доступные хотя бы из одной ссылки, но не из обеих сразу. Вспомните пример истории коммитов на рисунке 136. Если вы хотите узнать, какие коммиты есть либо в ветке master , либо в experiment , но не в обеих сразу, вы можете выполнить:
Эта команда снова выводит обычный журнал коммитов, но в нем содержится информация только об этих четырёх коммитах, обычно отсортированных по дате.
В таких случаях с командой log часто используют опцию —left-right , которая отображает сторону диапазона, с которой был сделан каждый из коммитов. Это делает данную информацию более полезной:
С помощью этих инструментов, вам будет намного проще указать Git, какой коммит или коммиты вы хотите изучить.
git-rev-parse
Многие фарфоровые команды Git принимают сочетание флагов (т.е. параметров, начинающихся с тире — ) и параметров, предназначенных для базовой команды git rev-list ,которую они используют внутри, а также флагов и параметров для других команд, которые они используют ниже по течению от git rev-list . Эта команда используется, чтобы различать их.
Options
Operation Modes
Каждая из этих опций должна появиться первой в командной строке.
Используйте git rev-parse в режиме анализа параметров (см. Раздел PARSEOPT ниже).
Используйте git rev-parse в режиме цитирования оболочки (см. Раздел SQ-QUOTE ниже). В отличие от параметра —sq ниже, этот режим выполняет только цитирование. Больше ничего не делается для ввода команд.
Опции для -партнёрства
—parseopt смысл только в режиме —parseopt . Сообщает параметр анализатор эхо из первого — встретился вместо того , чтобы пропускать его.
—parseopt смысл только в режиме —parseopt . Позволяет синтаксическому анализатору параметров останавливаться на первом аргументе, не являющемся параметром. Это можно использовать для анализа подкоманд, которые сами принимают параметры.
—parseopt смысл только в режиме —parseopt . Выведите варианты в их развернутой форме, если они доступны, и с закрепленными аргументами.
Опции для фильтрации
Не выводить флаги и параметры, не предназначенные для команды git rev-list .
Не выводить флаги и параметры, предназначенные для команды git rev-list .
Не выводите не флаговые параметры.
Не выводите параметры флага.
Опции для выхода
Если пользователь не задан параметром, используйте вместо него <arg> .
Вести себя так, как если бы git rev-parse был вызван из подкаталога <arg> рабочего дерева. Любые относительные имена файлов разрешаются так, как если бы они начинались с префикса <arg> , и будут напечатаны в этой форме.
Это может быть использовано для преобразования аргументов в команду,запущенную в подкаталоге,чтобы их можно было использовать после перехода на верхний уровень репозитория.Например:
Убедитесь,что предоставлен ровно один параметр,и что его можно превратить в необработанный 20-байтовый SHA-1,который можно использовать для доступа к объектной базе данных.Если да,то излучите его в стандартный выходной сигнал;в противном случае ошибка будет устранена.
Если вы хотите убедиться, что выходные данные действительно именуют объект в вашей базе данных объектов и / или могут использоваться в качестве объекта определенного типа, который вам нужен, вы можете добавить к параметру оператор отслаивания ^
Обратите внимание: если вы проверяете имя из ненадежного источника, разумно использовать —end-of-options , чтобы аргумент имени не был ошибочно принят за другой параметр.
Имеет смысл только в режиме —verify . Не выводите сообщение об ошибке, если первый аргумент не является допустимым именем объекта; вместо этого выйдите с ненулевым статусом молча. SHA-1 для допустимых имен объектов выводятся на стандартный вывод в случае успеха.
Обычно вывод осуществляется по одной строке на каждый флаг и параметр. Эта опция делает вывод одной строкой, правильно цитируемой для использования оболочкой. Полезно, когда вы ожидаете, что ваш параметр будет содержать пробелы и символы новой строки (например, при использовании git diff-* -S с git diff- * ). В отличие от —sq-quote , ввод команды по-прежнему интерпретируется как обычно.
То же, что и —verify , но сокращает имя объекта до уникального префикса, содержащего не менее length символов. Минимальная длина — 4, по умолчанию — действующее значение конфигурационной переменной core.abbrev (см. Git-config [1] ).
При отображении имен объектов добавляйте к ним префикс ^ и удаляйте префикс ^ из имен объектов, у которых он уже есть.
Недвусмысленное короткое название объектов.Для выбора режима строгой аббревиатуры используется опция core.warnAmbiguousRefs.
Обычно имена объектов выводятся в форме SHA-1 (с возможным префиксом ^ ); эта опция заставляет их выводить в форме, максимально приближенной к исходной.
Это похоже на —символику,но при этом опускаются входные данные,которые не являются ссылками (т.е.имена ветвей или тегов;или более явная форма «head/master»,когда вы хотите назвать «master» ветвь,когда есть,к сожалению,названный тег «master»),и показывать их в виде полных имен (например,»refs/heads/master»).
Опции для объектов
Показать все ссылки, найденные в refs/ .
—branches[=pattern] —tags[=pattern] —remotes[=pattern]
Показать все ветки, теги или ветки удаленного отслеживания, соответственно (т.е. ссылки, найденные в refs/heads , refs/tags или refs/remotes , соответственно).
Если задан pattern , отображаются только ссылки, соответствующие данному глобу оболочки. Если в шаблоне нет символа подстановки ( ? , * Или [ ), он преобразуется в совпадение по префиксу путем добавления /* .
Показать все ссылки, соответствующие шаблону pattern оболочки . Если шаблон не начинается с refs/ , он добавляется автоматически. Если в шаблоне нет символа подстановки ( ? , * Или [ ), он преобразуется в совпадение по префиксу путем добавления /* .
Не включайте ссылки, соответствующие <glob-pattern> , которые в противном случае —glob бы следующие —all , —branches , —tags , —remotes или —glob . Повторения этой опции накапливают шаблоны исключения до следующего параметра —all , —branches , —tags , —remotes или —glob (другие параметры или аргументы не очищают накопленные шаблоны).
Приведенные шаблоны не должны начинаться с refs/heads , refs/tags или refs/remotes при применении к —branches , —tags или —remotes соответственно, и они должны начинаться с refs/ при применении к —glob или —all . Если предполагается завершающий /* , он должен быть указан явно.
Показать каждый объект, имя которого начинается с данного префикса. <Префикс> должен состоять как минимум из 4 шестнадцатеричных цифр, чтобы избежать ошибочного перечисления всех без исключения объектов в репозитории.
Опции для файлов
Перечислите переменные окружения GIT_*,которые являются локальными для репозитория (например,GIT_DIR или GIT_WORK_TREE,но не GIT_EDITOR).Перечисляются только имена переменных,а не их значения,даже если они установлены.
Управляет поведением некоторых других опций.Если указано значение absolute,то пути,выводимые этими опциями,будут абсолютными и каноническими.Если указано relative,то пути будут относительными к текущему рабочему каталогу,если это возможно.По умолчанию используется опция relative.
Этот параметр может быть указан несколько раз и влияет только на те аргументы,которые следуют за ним в командной строке,либо до конца командной строки,либо до следующего экземпляра этого параметра.
Следующие параметры изменяются параметром —path-format :
Показать $GIT_DIR если он определен. В противном случае покажите путь к каталогу .git. Показанный путь, если он относительный, относится к текущему рабочему каталогу.
Если $GIT_DIR не определен и текущий каталог не определен как лежащий в репозитории Git или рабочем дереве, распечатайте сообщение в stderr и выйдите с ненулевым статусом.
Показать $GIT_COMMON_DIR если он определен, иначе $GIT_DIR .
Проверьте, является ли <path> допустимым репозиторием или gitfile, указывающим на действительный репозиторий, и распечатайте местоположение репозитория. Если <path> является gitfile, то печатается разрешенный путь к реальному репозиторию.
Разрешить «$GIT_DIR/<путь>» и принять во внимание другие переменные перемещения пути, такие как $GIT_OBJECT_DIRECTORY, $GIT_INDEX_FILE…. Например, если $GIT_OBJECT_DIRECTORY имеет значение /foo/bar, то «git rev-parse —git-path objects/abc» возвращает /foo/bar/abc.
Показать (по умолчанию абсолютный)путь к каталогу верхнего уровня рабочего дерева.Если рабочего дерева нет,сообщите об ошибке.
Показать абсолютный путь к корню рабочего дерева суперпроекта (если он существует),который использует текущий репозиторий в качестве своего подмодуля.Не выводит ничего,если текущий репозиторий не используется в качестве подмодуля ни одним проектом.
Показывать путь к файлу с общим индексом в режиме разделенного индекса,или пустой,если не в режиме разделенного индекса.
Следующие параметры не зависят от —path-format :
Подобно —git-dir , но его вывод всегда является канонизированным абсолютным путем.
Когда текущий рабочий каталог находится ниже директории хранилища,выведите «true»,в противном случае «false».
При нахождении текущей рабочей директории внутри рабочего дерева хранилища выведите «true»,иначе «false».
Когда в репозитории есть голая печать «true»,в противном случае «false».
Если в репозитории выведено мелкое «true»,в противном случае «false».
При вызове команды из подкаталога,покажите путь к каталогу верхнего уровня относительно текущего каталога (обычно это последовательность «../»,или пустая строка).
Когда команда вызывается из подкаталога,покажите путь текущей директории относительно директории верхнего уровня.
Покажите формат объекта (алгоритм хеширования), используемый репозиторием для хранения внутри каталога .git , ввода или вывода. Для ввода можно напечатать несколько алгоритмов, разделенных пробелами. Если не указано, по умолчанию используется «хранилище».
Other Options
Разберите строку даты и выведите соответствующий параметр —max-age = для git rev-list .
Анализируйте строку даты и выведите соответствующий параметр —min-age = для git rev-list .
Name already in use
git / Documentation / git-rev-parse.txt
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
What does git rev-parse do?
I have read the man page but it raised more questions than answers. Things like:
Pick out and massage parameters
Massage? What does that mean?
I’m using as a resolver (to SHA1) of revision specifiers, like
Is this the command’s purpose? If not, is even correct to use it to achieve this?
4 Answers 4
git rev-parse is an ancillary plumbing command primarily used for manipulation.
One common usage of git rev-parse is to print the SHA1 hashes given a revision specifier. In addition, it has various options to format this output such as —short for printing a shorter unique SHA1.
There are other use cases as well (in scripts and other tools built on top of git) that I’ve used for:
- —verify to verify that the specified object is a valid git object.
- —git-dir for displaying the abs/relative path of the .git directory.
- Checking if you’re currently within a repository using —is-inside-git-dir or within a work-tree using —is-inside-work-tree
- Checking if the repo is a bare using —is-bare-repository
- Printing SHA1 hashes of branches ( —branches ), tags ( —tags ) and the refs can also be filtered based on the remote (using —remote )
- —parse-opt to normalize arguments in a script (kind of similar to getopt ) and print an output string that can be used with eval
Massage just implies that it is possible to convert the info from one form into another i.e. a transformation command. These are some quick examples I can think of:
- a branch or tag name into the commit’s SHA1 it is pointing to so that it can be passed to a plumbing command which only accepts SHA1 values for the commit.
- a revision range A..B for git log or git diff into the equivalent arguments for the underlying plumbing command as B ^A
![]()
Just to elaborate on the etymology of the command name rev-parse , Git consistently uses the term rev in plumbing commands as short for «revision» and generally meaning the 40-character SHA1 hash for a commit. The command rev-list for example prints a list of 40-char commit hashes for a branch or whatever.
In this case the name might be expanded to parse-a-commitish-to-a-full-SHA1-hash . While the command has the several ancillary functions mentioned in Tuxdude’s answer, its namesake appears to be the use case of transforming a user-friendly reference like a branch name or abbreviated hash into the unambiguous 40-character SHA1 hash most useful for many programming/plumbing purposes.
I know I was thinking it was «reverse-parse» something for quite a while before I figured it out and had the same trouble making sense of the terms «massaging» and «manipulation» 🙂
Anyway, I find this «parse-to-a-revision» notion a satisfying way to think of it, and a reliable concept for bringing this command to mind when I need that sort of thing. Frequently in scripting Git you take a user-friendly commit reference as user input and generally want to get it resolved to a validated and unambiguous working reference as soon after receiving it as possible. Otherwise input translation and validation tends to proliferate through the script.