ref^
относится к фиксации до ref
, как насчет получения фиксации после ref
?
Например, если я git checkout 12345
, как я могу проверить следующую фиксацию?
Спасибо.
PS Да, git a DAG node указатель struct tree any. Как найти коммит после этого?
ref^
относится к фиксации до ref
, как насчет получения фиксации после ref
?
Например, если я git checkout 12345
, как я могу проверить следующую фиксацию?
Спасибо.
PS Да, git a DAG node указатель struct tree any. Как найти коммит после этого?
Чтобы перечислить все коммиты, начиная с текущего, а затем с его дочернего элемента и т.д. - в основном стандартный git журнал, но идя по другому пути, используйте что-то вроде
git log --reverse --ancestry-path 894e8b4e93d8f3^..master
где 894e8b4e93d8f3 - это первая фиксация, которую вы хотите показать.
Создатель для Хадсона (теперь Дженкинс) Kohsuke Kawaguchi только что опубликовал (ноябрь 2013):
kohsuke/ git -children из:
Учитывая фиксацию, найдите немедленных дочерних элементов этой фиксации.
#!/bin/bash -e
# given a commit, find immediate children of that commit.
for arg in "[email protected]"; do
for commit in $(git rev-parse $arg^0); do
for child in $(git log --format='%H %P' --all | grep -F " $commit" | cut -f1 -d' '); do
git describe $child
done
done
done
Как показано этот поток, в VCS на основе истории, представленной DAG (Направленный ациклический график), нет "одного родителя" или "одного ребенка".
C1 -> C2 -> C3
/ \
A -> B E -> F
\ /
D1 -> D2 ----/
Порядок коммитов выполняется с помощью "topo-order" или "date-order" (см. GitPro book)
Но так как Git1.6.0, вы можете перечислить дочерние элементы фиксации.
git rev-list --children
git log --children
Примечание: для parent Commits, у вас есть та же проблема с суффиксом ^
с параметром ревизии, означающим первого родителя этого передать объект. ^<n>
означает, что родитель <n>
th (т.е. rev^
эквивалентен rev^1
).
Если вы находитесь на ветке foo
и выходите "git merge bar
", тогда foo
будет первым родителем.
I.e: Первым родителем является ветка, в которой вы были включены, когда вы слились, а вторая - фиксация на ветке, в которую вы слились.
я нашел
git rev-list --ancestry-path commit1..commit2
где я устанавливаю commit1
в качестве текущей фиксации и commit2
текущей голове. Это возвращает мне список всех коммитов, которые строят путь между commit1
и commit2
.
Последней строкой вывода является дочерний элемент commit1 (по пути к commit2).
Я знаю, что вы имеете в виду. Это разочаровывает наличие большого синтаксиса для перехода к предыдущим фиксациям, но ни один из них не подходит к следующим. В сложной истории проблема "что является следующей фиксацией" становится довольно сложной, но тогда в сложном слиянии такая же твердость возникает и с "предыдущими" фиксациями. В простом случае внутри одной ветки с линейной историей (даже локально для некоторого ограниченного количества коммитов) было бы неплохо и иметь смысл идти вперед и назад.
Реальная проблема с этим, однако, заключается в том, что дети не фиксируются, это только список, связанный с обратной связью. Поиск child commit принимает поиск, что не так уж плохо, но, вероятно, не что-то git хочет поместить в логику refspec.
Во всяком случае, я столкнулся с этим вопросом, потому что я просто хочу сделать шаг вперед в истории, которую вы совершаете за один раз, выполняя тесты, а иногда вам приходится двигаться вперед, а не назад. Ну, с некоторыми соображениями я придумал это решение:
Выберите фиксацию перед тем, где вы находитесь. Вероятно, это может быть ветка. Если вы находитесь в ветке ~ 10, тогда "git ветвь проверки ~ 9" затем "git checkout branch ~ 8", чтобы получить следующее после этого, затем "git ветвь проверки ~ 7" и т.д.
Уменьшение числа должно быть действительно простым в script, если вам это нужно. Гораздо проще, чем разбор git rev-list.
Основываясь на ответе @Michael, я взломал псевдоним child
в моем .gitconfig
.
Он работает как положено в случае по умолчанию, а также является универсальным.
# Get the child commit of the current commit.
# Use $1 instead of 'HEAD' if given. Use $2 instead of curent branch if given.
child = "!bash -c 'git log --format=%H --reverse --ancestry-path ${1:-HEAD}..${2:\"$(git rev-parse --abbrev-ref HEAD)\"} | head -1' -"
По умолчанию он задает дочерний элемент HEAD (если не задан другой аргумент commit-ish), следуя шагу предка до конца текущей ветки (если только в качестве второго аргумента не указан другой commit-ish).
Используйте %h
вместо %H
если вам нужна короткая форма хеша.
С отсоединенной ГОЛОВКОЙ (нет ветки) или чтобы получить всех детей независимо от ветки:
# For the current (or specified) commit-ish, get the all children, print the first child
children = "!bash -c 'c=${1:-HEAD}; set -- $(git rev-list --all --not \"$c\"^@ --children | grep $(git rev-parse \"$c\") ); shift; echo $1' -"
Измените $1
на $*
чтобы напечатать всех детей.
Вы также можете изменить --all
на коммит, чтобы отображать только потомков, которые являются предками этого коммита, другими словами, отображать только потомков "в направлении" данного коммита. Это может помочь вам сузить вывод от многих детей до одного.
Нет уникальной "следующей фиксации". Поскольку история в Git является DAG, а не строкой, многие коммиты могут иметь общий родительский элемент (ветки), а commits может иметь более одного родителя (слияния).
Если у вас есть конкретная ветка, вы можете посмотреть ее журнал и посмотреть, что коммит перечисляет в качестве родителя.
В том случае, если вы не имеете в виду конкретный "целевой" коммит, но вместо этого хотите увидеть дочерние коммиты, которые могут быть в любой ветки, вы можете использовать эту команду:
git rev-list --children --all | grep ^${COMMIT}
Если вы хотите увидеть всех детей и внуков, вы должны рекурсивно использовать rev-list --children
, например:
git rev-list --children --all | \
egrep ^\($(git rev-list --children --all | \
grep ^${COMMIT} | \
sed 's/ /|/g')\)
(Версия, которая дает только внукам, будет использовать более сложный sed
и/или cut
.)
Наконец, вы можете log --graph
команду log --graph
чтобы увидеть древовидную структуру, вот так:
git log --graph --oneline --decorate \
\^${COMMIT}^@ \
$(git rev-list --children --all | \
egrep ^\($(git rev-list --children --all | \
grep ^${COMMIT} | \
sed 's/ /|/g')\))
Примечание: все вышеприведенные команды предполагают, что вы установили переменную оболочки ${COMMIT}
для некоторой ссылки (branch, tag, sha1) коммита, чьи дочерние элементы вам интересны.
У меня есть этот псевдоним в ~/.gitconfig
first-child = "!f() { git log --reverse --ancestry-path --pretty=%H $1..${2:-HEAD} | head -1; }; f"
Я пробовал много разных решений, и никто не работал у меня. Пришлось придумать свои собственные.
найти следующую фиксацию
function n() {
git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}
найти предыдущую фиксацию
function p() {
git checkout HEAD^1
}
Если ребенок совершает все действия на какой-либо ветки, вы можете использовать gitk --all commit^..
, где "commit" - это то, что идентифицирует фиксацию. Например, если сокращенный SHA-1 с фиксацией является c6661c5, введите gitk --all c6661c5^..
Вам, вероятно, потребуется ввести полный SHA-1 в ячейку "SHA1 ID:". Вам понадобится полный SHA-1, который для этого примера можно получить с помощью git rev-parse c6661c5
В качестве альтернативы, git rev-list --all --children | grep '^c6661c5883bb53d400ce160a5897610ecedbdc9d'
создаст строку, содержащую все дочерние элементы этого коммита, предположительно независимо от того, существует ли ветвь.
Мне удалось найти следующего ребенка следующим образом:
git log --reverse --children -n1 HEAD (where 'n' is the number of children to show)
(Это началось как ответ на дубликат вопроса. Я немного отредактировал его, чтобы очистить его.)
Все внутренние стрелки Git односторонние и направлены назад. Поэтому нет короткого удобного синтаксиса для продвижения вперед: это просто невозможно.
Можно "двигаться против стрелок", но способ сделать это удивителен, если вы не видели этого раньше, а потом и после. Допустим, у нас есть:
A <-B <-C <-D <-E <-- last
^
|
\--------- middle
Использование middle~2
следует за стрелками дважды от C
обратно до A
. Итак, как нам перейти от C
к D
? Ответ таков: мы начинаем с E
, используя имя last
, и работаем в обратном направлении, пока не доберемся до middle
, записывая точки, которые мы посетим по пути. Затем мы просто двигаемся так далеко, как мы хотим, в направлении last
: переходим на один шаг к D
или на два к E
.
Это особенно важно, когда у нас есть ветки:
D--E <-- feature1
/
...--B--C <-- master
\
F--G <-- feature2
Какой коммит находится на шаг после C
? Там нет правильного ответа, пока вы не добавите к вопросу: в направлении признака ___ (заполните пробел).
Чтобы перечислить коммиты между C
(исключая C
) и, скажем, G
, мы используем:
git rev-list --topo-order --ancestry-path master..feature2
--topo-order
гарантирует, что даже при наличии сложных ветвлений и слияний коммиты выходят в топологически отсортированном порядке. Это требуется только в том случае, если цепочка не является линейной. Ограничение --ancestry-path
означает, что когда мы работаем в обратном направлении от feature2
, мы перечисляем только те коммиты, у которых коммит C
был одним из их собственных предков. То есть, если график - или его часть в любом случае - на самом деле выглядит так:
A--B--C <-- master
\ \
\ F--G--J <-- feature2
\ /
H-------I <-- feature3
простой запрос вида feature2..master
перечисляет коммиты J
, G
и I
, а также F
и H
в некотором порядке. С помощью --ancestry-path
мы выбиваем H
и I
: они не являются потомками C
, только A
. С помощью --topo-order
мы гарантируем, что фактический порядок перечисления будет J
, затем G
, затем F
.
Команда git rev-list
выдает эти хэш-идентификаторы на свой стандартный вывод, по одному на строку. Чтобы сделать шаг вперед в направлении feature2
, нам просто нужна последняя строка.
Можно (и заманчиво, и полезно) добавить --reverse
, чтобы git rev-list
печатал коммиты в обратном порядке после их генерации. Это работает, но если вы используете его в конвейере, как это:
git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1
чтобы просто получить "следующий коммит в направлении id2", и есть очень длинный список коммитов, команда git rev-list
может получить разорванный канал, когда она пытается записать в head
, который прекратил чтение его вход и выход. Поскольку ошибки в сломанной pipeе обычно игнорируются оболочкой, это в основном работает. Просто убедитесь, что они игнорируются при вашем использовании.
Также заманчиво добавить -n 1
к команде git rev-list
вместе с --reverse
. Не делай этого! Это заставляет git rev-list
останавливаться после шага назад, а затем переворачивать (один вход) список посещенных коммитов. Так что это просто производит <id2>
каждый раз.
Обратите внимание, что с фрагментами графа "алмаз" или "бензольное кольцо":
I--J
/ \
...--H M--... <-- last
\ /
K--L
перемещение одного коммита "вперед" из H
в сторону last
даст вам либо I
, либо K
. С этим ничего не поделаешь: оба коммита на один шаг вперед! Если вы затем начнете с полученного коммита и сделаете еще один шаг, теперь вы будете придерживаться того пути, с которого начинали.
Лекарство от этого состоит в том, чтобы не двигаться по одному шагу за раз и не зацикливаться на зависимых от пути цепях. Вместо этого, если вы планируете посетить всю цепочку путей предков, прежде чем делать что-либо еще, составьте полный список всех коммитов в цепочке:
git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits
Затем, посетите каждый коммит в этом списке, по одному, и вы получите всю цепочку. --topo-order
позаботится о том, чтобы вы нажали I
-and- J
в этом порядке и K
-and- L
в этом порядке (хотя нет простого способа предсказать, будете ли вы выполнять IJ пара до или после пары KL).
Каждая фиксация сохраняет указатель на ее родительский элемент (родители, в случае слияния (стандартного)).
Таким образом, нет способа указывать на дочерний фиксатор (если он есть) из родителя.
Этот пост (http://www.jayway.com/2015/03/30/using-git-commits-to-drive-a-live-coding-session/#comment-282667) показывает аккуратный способ, если вы делаете это, если вы можете создать хорошо определенный тег в конце вашей фиксации стек. по существу
git config --global alias.next '!git checkout `git rev-list HEAD..demo-end | tail -1`'
где "demo-end" - последний тег.
Существующие ответы предполагают, что у вас есть ветка (или ссылка), содержащая коммит, который вы ищете.
В моем случае, коммит, который я искал, не был в git rev-list --all
так как ни одна ветка не содержала его. В итоге я просмотрел gitk --reflog
вручную.
Если вы не можете найти свой коммит даже в reflog, попробуйте git fsck --full
чтобы получить список висячих git fsck --full
(т. git fsck --full
Не в любой ветке), или git fsck --lost-found
чтобы сделать ссылки и применить методы в других ответах.