Как мне найти следующий коммит в git? (ребенок/дети реф.)

ref^ относится к фиксации до ref, как насчет получения фиксации после ref?

Например, если я git checkout 12345, как я могу проверить следующую фиксацию?

Спасибо.

PS Да, git a DAG node указатель struct tree any. Как найти коммит после этого?

Ответ 1

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

git log --reverse --ancestry-path 894e8b4e93d8f3^..master

где 894e8b4e93d8f3 - это первая фиксация, которую вы хотите показать.

Ответ 2

Создатель для Хадсона (теперь Дженкинс) 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: Первым родителем является ветка, в которой вы были включены, когда вы слились, а вторая - фиксация на ветке, в которую вы слились.

Ответ 3

я нашел

git rev-list --ancestry-path commit1..commit2

где я устанавливаю commit1 в качестве текущей фиксации и commit2 текущей голове. Это возвращает мне список всех коммитов, которые строят путь между commit1 и commit2.

Последней строкой вывода является дочерний элемент commit1 (по пути к commit2).

Ответ 4

Я знаю, что вы имеете в виду. Это разочаровывает наличие большого синтаксиса для перехода к предыдущим фиксациям, но ни один из них не подходит к следующим. В сложной истории проблема "что является следующей фиксацией" становится довольно сложной, но тогда в сложном слиянии такая же твердость возникает и с "предыдущими" фиксациями. В простом случае внутри одной ветки с линейной историей (даже локально для некоторого ограниченного количества коммитов) было бы неплохо и иметь смысл идти вперед и назад.

Реальная проблема с этим, однако, заключается в том, что дети не фиксируются, это только список, связанный с обратной связью. Поиск child commit принимает поиск, что не так уж плохо, но, вероятно, не что-то git хочет поместить в логику refspec.

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

Выберите фиксацию перед тем, где вы находитесь. Вероятно, это может быть ветка. Если вы находитесь в ветке ~ 10, тогда "git ветвь проверки ~ 9" затем "git checkout branch ~ 8", чтобы получить следующее после этого, затем "git ветвь проверки ~ 7" и т.д.

Уменьшение числа должно быть действительно простым в script, если вам это нужно. Гораздо проще, чем разбор git rev-list.

Ответ 5

Два практических ответа:

Один ребенок

Основываясь на ответе @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 на коммит, чтобы отображать только потомков, которые являются предками этого коммита, другими словами, отображать только потомков "в направлении" данного коммита. Это может помочь вам сузить вывод от многих детей до одного.

Ответ 6

Нет уникальной "следующей фиксации". Поскольку история в Git является DAG, а не строкой, многие коммиты могут иметь общий родительский элемент (ветки), а commits может иметь более одного родителя (слияния).

Если у вас есть конкретная ветка, вы можете посмотреть ее журнал и посмотреть, что коммит перечисляет в качестве родителя.

Ответ 7

В том случае, если вы не имеете в виду конкретный "целевой" коммит, но вместо этого хотите увидеть дочерние коммиты, которые могут быть в любой ветки, вы можете использовать эту команду:

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) коммита, чьи дочерние элементы вам интересны.

Ответ 8

У меня есть этот псевдоним в ~/.gitconfig

first-child = "!f() { git log  --reverse --ancestry-path --pretty=%H $1..${2:-HEAD} | head -1; }; f"

Ответ 9

Я пробовал много разных решений, и никто не работал у меня. Пришлось придумать свои собственные.

найти следующую фиксацию

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
}

Ответ 10

Если ребенок совершает все действия на какой-либо ветки, вы можете использовать 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' создаст строку, содержащую все дочерние элементы этого коммита, предположительно независимо от того, существует ли ветвь.

Ответ 11

Мне удалось найти следующего ребенка следующим образом:

git log --reverse --children -n1 HEAD (where 'n' is the number of children to show)

Ответ 12

(Это началось как ответ на дубликат вопроса. Я немного отредактировал его, чтобы очистить его.)

Все внутренние стрелки 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).

Ответ 13

Каждая фиксация сохраняет указатель на ее родительский элемент (родители, в случае слияния (стандартного)).

Таким образом, нет способа указывать на дочерний фиксатор (если он есть) из родителя.

Ответ 14

Этот пост (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" - последний тег.

Ответ 15

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

В моем случае, коммит, который я искал, не был в git rev-list --all так как ни одна ветка не содержала его. В итоге я просмотрел gitk --reflog вручную.

Если вы не можете найти свой коммит даже в reflog, попробуйте git fsck --full чтобы получить список висячих git fsck --full (т. git fsck --full Не в любой ветке), или git fsck --lost-found чтобы сделать ссылки и применить методы в других ответах.