如果想要将 HEAD
移动到当前 HEAD
的父级,那很容易:
git reset --hard HEAD^
但有没有一种简单的方式来执行与此操作相反的操作,即将头设置为当前头的第一个子提交?
现在,我使用 gitk 作为解决方法(alt-tab、up-arrow、alt-tab、middle-click),但我想要更优雅的解决方案,一个也可以在没有可用gitk时使用的方案。
如果想要将 HEAD
移动到当前 HEAD
的父级,那很容易:
git reset --hard HEAD^
但有没有一种简单的方式来执行与此操作相反的操作,即将头设置为当前头的第一个子提交?
现在,我使用 gitk 作为解决方法(alt-tab、up-arrow、alt-tab、middle-click),但我想要更优雅的解决方案,一个也可以在没有可用gitk时使用的方案。
很可能不是最快的解决方案,但它满足我的需求:
#!/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"
git rev-list --all --parents
能够完美满足我的需求:它会迭代所有可访问的提交,对于每个提交,它会打印如下所示的一行:
SHA1_commit SHA1_parent1 SHA1_parent2
等。
grep表达式中的空格确保只找到那些涉及到当前SHA1的父节点。然后我们获取第n个子节点的第n行,并得到该子节点的SHA1。
git rev-list --all --parents | grep -m 1 -B $(($NUM-1)) " $HASH" | head -1 | sed 's/ .*//'
,否则当 $NUM != 1 时它不太起作用。 - Schwerngit reset --hard HEAD^
通常会使HEAD提交不可访问(除非它也在一个分支上)。因此,这可能无法找到所有提交。要查找所有提交,必须使用git rev-list --walk-reflog
和/或git fsck --unreachable
。 - sleskegit rev-parse
而不是 git-rev-parse
,无法编辑帖子。 - ben.snape使用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 和所有其祖先的分支头之间的所有父子关系集合。
--format='%(refname: short)
来简化 git branch
的输出(以去除前导空格和 *) - Joshua Goldbergbranches=($(git branch --contains ... ))
。 - Joshua Goldberggrep
调用以摆脱这些行,以及像*(HEAD detached at $SHA1)
这样的行。@Joshua Goldberg:添加了sed
以去除前导的*
,如果有的话。在我的LTS系统上不可用--format
,但很好知道。对于那些有幸拥有它的人,我添加了你的建议。关于zsh vs. bash,我更喜欢将我的脚本限制在POSIX sh所提供的内容上,感谢您为zsh用户提供解决方案。感谢您将“上面”的答案链接化。 - Rainer Blome基于Paul Wagland的答案和他的来源,我正在使用以下内容:
git log --ancestry-path --format=%H ${commit}..master | tail -1
我发现Paul的答案给我旧版本提交记录的错误输出(可能是由于合并导致的),其中主要差异在于--ancestry-path
标志。
--reverse
并使用 head -1
进行轻微加速。 - Tom Halegitk
...因为可能有多个子级,所以可能没有像HEAD^
这样的简单方法。git reflog
查找提交的指针,您可以将其用于reset
命令。请参见此处。根据How do I find the next commit in git?中的答案,我有一个适合自己的解决方案。
假设您想要在“主”分支上找到下一个修订版,则可以执行以下操作:
git log --reverse ${commit}..master | sed 's/commit //; q'
如果只是想移动 HEAD(不更新索引或工作树),可以使用以下命令:
git reset --soft $(git child)
您需要使用下面的配置。
基于@Michael的答案,我在我的.gitconfig
中修改了child
别名。
它在默认情况下按预期工作,并且也很灵活。
# 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的子代作为结果(除非指定另一个提交参数)。
如果要使用短哈希表单,请使用%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
替换为$*
以打印所有子元素。children
别名可以正常工作,但对于具有多个子提交的提交,它无法打印出更多的提交。 - TamaMcGlinn这取决于你所问的内容。在无数个分支中,有当前引用的无限数量的子节点,有些是本地的,有些是远程的,还有许多已经被重新定义并在你的仓库中,但不是你打算发布的历史的一部分。
对于一个简单的情况,如果你刚刚执行了重置到HEAD^
,你可以通过HEAD@{1}
获取回你刚才丢掉的子节点。
您可以使用Hudson的创建者(现在是Jenkins)Kohsuke Kawaguchi(2013年11月)的要点:
Kohsuke Kawaguchi的
kohsuke / git-children-of
:
给定一个提交,找到该提交的直接子级。
#!/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
$PATH
引用的文件夹中,然后只需输入以下命令即可:git children-of <a-commit>
这个答案不是一个绝对的答案,但它对于快速研究非常有用。
git log
从一开始--oneline
--graph
的选项,以获得多个祖先。|
--before-context=n
(也可以使用-B n
)向后追溯n(此处为3)个祖先,并在其中搜索缩写提交(7个字符)grep
git log --oneline HEAD | grep -B 3 <an-abbrev-commit>
这篇文章(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 children-of
! - VonC