Reputation: 1489
If you want to move the HEAD
to the parent of the current HEAD
, that's easy:
git reset --hard HEAD^
But is there any simple way to do the exact opposite of this operation, that is, set the head to the current head's first child commit?
Right now, I use gitk as a workaround (alt-tab, up-arrow, alt-tab, middle-click), but I would like a more elegant solution, one that can also be used when gitk is not available.
Upvotes: 51
Views: 16811
Reputation: 5203
This post shows a neat way of doing it if you can create a well-defined tag at the end of your commit stack. Essentially
git config --global alias.next '!git checkout `git rev-list HEAD..demo-end | tail -1`'
where "demo-end" is the last tag.
Upvotes: 0
Reputation: 571
AttishOculus's method using git rev-list --all
considers all available commits, which can be a lot and is often not necessary. If the interesting child commits are reachable from some branch, the number of commits that a script interested in child commits needs to process can be reduced:
branches=$(git branch --contains $commit | grep -v '[*] (' | sed -e 's+^..++')
will determine the set of branches that $commit is an ancestor of.
With a modern Git, at least version 2.21+, this should do the same without the need for sed
(untested):
branches=$(git branch --format='%(refname:short)' --contains $commit | grep -v '[*] (')
Using this set, git rev-list --parents ^$commit $branches
should yield exactly the set of all parent-child relationships between $commit and all branch heads that it is an ancestor of.
Upvotes: 13
Reputation: 1219
This is not an absolute answer but it is very useful for quick research.
git log
from the beginning--oneline
--graph
for multiple ancestors.|
--before-context=n
(same as -B n
) of grep
searching an abbrev commit (7 characters)git log --oneline HEAD | grep -B 3 <an-abbrev-commit>
Upvotes: 2
Reputation: 1489
Very probably not the fastest possible solution, but it does what I need:
#!/bin/bash
REV=$1
if [[ -z "$REV" ]]; then
echo "Usage: git-get-child <refspec> [<child-number>]"
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"
The git rev-list --all --parents
does exactly what I need: it iterates over all reachable commits, and prints the following line for each:
SHA1_commit SHA1_parent1 SHA1_parent2
etc.
The space in the grep expression ensures that only those lines are found where the SHA1 in question is a parent. Then we get the nth line for the nth child and get the child's SHA1.
Upvotes: 18
Reputation: 46963
To just move HEAD (as asked - this doesn't update the index or working tree), use:
git reset --soft $(git child)
You'll need to use the configuration listed below.
Based on @Michael's answer, I hacked up the child
alias in my .gitconfig
.
It works as expected in the default case, and is also versatile.
# 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' -"
It defaults to giving the child of HEAD (unless another commit-ish argument is given) by following the ancestry one step toward the tip of the current branch (unless another commit-ish is given as second argument).
Use %h
instead of %H
if you want the short hash form.
With a detached head, there is no branch, but getting the first child can still be achieved with this alias:
# 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' -"
Change the $1
to $*
to print all the children
Upvotes: 4
Reputation: 9382
Based partly on Paul Wagland's answer and partly on his source, I am using the following:
git log --ancestry-path --format=%H ${commit}..master | tail -1
I found that Paul's answer gave me the wrong output for older commits (possibly due to merging?), where the primary difference is the --ancestry-path
flag.
Upvotes: 6
Reputation: 1327304
You can use the gist of the creator for Hudson (now Jenkins) Kohsuke Kawaguchi (November 2013):
kohsuke / git-children-of
:
Given a commit, find immediate children of that commit.
#!/bin/bash -e
# given a commit, find immediate children of that commit.
for arg in "$@"; 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
Put that script in a folder referenced by your $PATH
, and simply type:
git children-of <a-commit>
Upvotes: 2
Reputation: 29129
Based on the answer given in How do I find the next commit in git?, I have another solution that works for me.
Assuming that you want to find the next revision on the "master" branch, then you can do:
git log --reverse ${commit}..master | sed 's/commit //; q'
This also assumes that there is one next revision, but that is kind of assumed by the question anyway.
Upvotes: 4
Reputation: 91010
It depends on what you're asking. There could be an infinite number of children of the current head in an infinite number of branches, some local, some remote, and many that have been rebased away and are in your repository, but not part of a history you intend to publish.
For a simple case, if you have just done a reset to HEAD^
, you can get back the child you just threw away as HEAD@{1}
.
Upvotes: 2
Reputation: 49813
It is strictly not possible to give a good answer -- since git is distributed, most of the children of the commit you ask about might be in repositories that you don't have on your local machine! That's of course a silly answer, but something to think about. Git rarely implements operations that it can't implement correctly.
Upvotes: -2
Reputation: 53944
You can use gitk
... since there can be more than one child there is probably no easy way like HEAD^
.
If you want to undo your whole operation you can use the reflog, too. Use git reflog
to find your commit’s pointer, which you can use for the reset
command. See here.
Upvotes: 4