Ссылка на дочерний элемент фиксации в Git

Если вы хотите переместить HEAD в родительский элемент текущего HEAD, это легко:

git reset --hard HEAD^

Но существует ли какой-либо простой способ сделать точную противоположность этой операции, то есть установить головку в текущую головку с первым потомком?

В настоящее время я использую gitk в качестве обходного пути (alt-tab, стрелка вверх, alt-tab, средний щелчок), но мне хотелось бы более элегантное решение, которое также можно использовать, когда gitk недоступен.

Ответ 1

Очень вероятно не самое быстрое решение, но оно делает то, что мне нужно:

#!/bin/bash

REV=$1

if [[ -z "$REV" ]]; then
    echo "Usage: git-get-child  []"
    exit
fi

HASH=$(git rev-parse $REV)

NUM=$2

if [[ -z "$NUM" ]]; then
    NUM=1
fi

git rev-list --all --parents | grep " $HASH" | sed -n "${NUM}s/\([^ ]*\) .*$/\\1/p"

git rev-list --all --parents делает именно то, что мне нужно: он перебирает все достижимые коммиты и печатает следующую строку для каждого:

SHA1_commit SHA1_parent1 SHA1_parent2 и т.д.

Пробел в выражении grep гарантирует, что будут найдены только те строки, где рассматриваемый SHA1 является родителем. Затем мы получаем n-ю строку для n-го дочернего элемента и получаем дочерний SHA1.

Ответ 2

Приведенный выше метод с использованием git rev-list --all учитывает все доступные коммиты, что может быть много и часто не требуется. Если интересующие дочерние коммиты доступны из некоторой ветки, количество коммитов, которые необходимо обработать скриптом, который заинтересован в дочерних коммитах, может быть уменьшено:

branches=$(git branch --contains $commit| grep -v '[*] ('| sed -e 's+^..++')

определит набор ветвей, предком которых является $ commit. С современным git, по крайней мере, версией 2. 21+ это должно сделать то же самое без необходимости в sed (не проверено):

branches=$(git branch --format='%(refname:short)' --contains $commit| grep -v '[*] (')

Используя этот набор, git rev-list --parents ^$commit $branches должны в точности дать набор всех родительско-дочерних отношений между $ commit и всеми git rev-list --parents ^$commit $branches предком которых он является.

Ответ 3

Отчасти на основе Пол Вагленд ответ и частично на его источник, я используя следующее:

git log --ancestry-path --format=%H ${commit}..master | tail -1

Я обнаружил, что ответ Павла дал мне неправильный результат для более старых коммитов (возможно, из-за слияния?), где основное различие - это флаг --ancestry-path.

Ответ 4

Вы можете использовать gitk... так как может быть более одного ребенка, вероятно, нет простого способа, как HEAD^.

Если вы хотите отменить всю операцию, вы также можете использовать reflog. Используйте git reflog, чтобы найти указатель Commits, который вы можете использовать для команды reset. См. здесь.

Ответ 5

Чтобы просто переместить HEAD (как задано - это не обновляет индекс или рабочее дерево), используйте:

git reset --soft $(git child)

Вам нужно будет использовать конфигурацию, указанную ниже.

Объяснение

Основываясь на @Michael answer, я взломал псевдоним 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 на $*, чтобы напечатать все дочерние элементы

Ответ 6

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

Для простого случая, если вы только что сделали reset до HEAD^, вы можете вернуть ребенка, который вы просто выбросили, как [email protected]{1}.

Ответ 7

Основываясь на ответе, приведенном в Как найти следующую фиксацию в git?, у меня есть другое решение, которое работает для меня.

Предполагая, что вы хотите найти следующую ревизию в ветке "master", вы можете сделать:

git log --reverse ${commit}..master | sed 's/commit //; q'

Это также предполагает, что есть одна следующая ревизия, но в любом случае это предположение принято вопросом.

Ответ 8

Вы можете использовать сущность создателя для Хадсона (теперь Дженкинс) Kohsuke Kawaguchi (ноябрь 2013):
kohsuke / git-children-of:

Учитывая фиксацию, найдите немедленных дочерних элементов этой фиксации.

#!/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

Поместите этот script в папку, на которую ссылается ваш $PATH, и просто введите:

git children-of <a-commit>

Ответ 9

Невозможно дать хороший ответ - поскольку распределяется git, большинство дочерних элементов комманды, о которых вы спрашиваете, могут быть в репозиториях, которые у вас нет на вашей локальной машине! Это, конечно, глупый ответ, но о чем подумать. git редко реализует операции, которые он не может реализовать правильно.

Ответ 10

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