确定合并是否将通过快进解决

Determine if a merge will resolve via fast-forward

我想知道在 运行 执行合并命令之前,特定合并是否会通过 "fast-forward" 解决。

我知道我可以通过 "fast-forward"(使用 --no-ff 选项)明确请求不解决合并问题。或者我可以尝试仅通过快进(使用 --ff 选项)来解决合并问题。

但有时我想知道某个特定的合并是否会在 运行 之前通过快进解决。我意识到理论上我可以通过挖掘历史树来解决这个问题。而且我也意识到我可以 运行 合并并看看会发生什么,但是如果我随后决定我更愿意以其他方式解决合并,这就会成为问题,因为我必须撤消合并(通过重新指向参考日志中的分支标签)并再做一次。

注意:--dry-run 问题 (Is there a git-merge --dry-run option?) 更多是关于查看合并中可能存在哪些合并冲突,而不是关于可以通过快进解决的合并。

这里有一个类似但不完全相同的问题:Is there a git-merge --dry-run option?

第一个答案看起来可能就是您要找的。

具体来说,一个带有 --no-commit 标志的 merge 并在您已经看够并想返回并进行实际合并时使用 --abort

您可以测试 git merge-base <branch1> <branch2> 是否等于 git rev-parse <branch1>。如果相等,则在 运行 git merge <branch1> <branch2> 时进行 ff 合并或已经更新。如果不是,则进行非 ff 合并。

function isff(){
a=$(git merge-base  )
b=$(git rev-parse )
c=$(git rev-parse )
if [[ "$b"  == "$c" ]] || [[ "$a" == "$c" ]];then
    echo merge dry run: already up-to-date
    return
fi
if [ "$a" == "$b" ];then
    echo merge dry run: a fast forward merge
else
    echo merge dry run: a non fast forward merge
fi
}

isff master topic

查看@Ashwin-nair 引用的问题,我找到了我认为是答案的答案。

为了

git checkout MY_BRANCH 
git merge OTHER_BRANCH 

如果

的输出
git branch --contains MY_BRANCH

包含OTHER_BRANCH,然后可以通过快进解决与OTHER_BRANCH的合并。

总结:git merge-base --is-ancestor 测试一个提交是否是另一个提交的祖先(其中提交被认为是他们自己的祖先,这是一种特别奇怪的乱伦形式,也许:-))。由于当当前分支(HEAD)指向作为另一个提交的祖先的提交时,分支标签只能通过 git merge 快进,我们可以使用它来确定是否 git merge 可以进行快进操作。

您似乎希望将此作为答案发布,因此我已将其转换为有效的 git 别名,您可以将其放入全局 git 配置中。别名有点长且复杂,最好将其剪切并粘贴到您的 git 配置别名部分:

canff = "!f() { if [ $# -gt 0 ]; then b=\"\"; git rev-parse -q --verify \"$b^{commit}\" >/dev/null || { printf \"%s: not a valid commit specifier\n\" \"$b\"; return 1; } else b=$(git rev-parse --symbolic-full-name --abbrev-ref @{u}) || return $?; fi; if git merge-base --is-ancestor HEAD \"$b\"; then echo \"merge with $b can fast-forward\"; else echo \"merge with $b cannot fast-forward\"; fi; }; f"

下面是作为 shell 脚本编写的相同内容,以更具可读性的方式和一些评论:

#! /bin/sh
#
# canff - test whether it is possible to fast-forward to
# a given commit (which may be a branch name).  If given
# no arguments, find the upstream of the current (HEAD) branch.

# First, define a small function to print the upstream name
# of the current branch.  If no upstream is set, this prints a
# message to stderr and returns with failure (nonzero).
upstream_name() {
    git rev-parse --symbolic-full-name --abbrev-ref @{u}
}

# Now define a function to detect fast-forward-ability.
canff() {
    local b # branch name or commit ID

    if [ $# -gt 0 ]; then  # at least 1 argument given
        b=""
        # make sure it is or can be converted to a commit ID.
        git rev-parse -q --verify "$b^{commit}" >/dev/null || {
            printf "%s: not a valid commit specifier\n" "$b"
            return 1
        }
    else
        # no arguments: find upstream, or bail out
        b=$(upstream_name) || return $?
    fi
    # now test whether git merge --ff-only could succeed on $b
    if git merge-base --is-ancestor HEAD "$b"; then
         echo "merge with $b can fast-forward"
    else
         echo "merge with $b cannot fast-forward"
    fi
}

shell脚本只需要一个主要部分来驱动它,就是别名后对f的调用。别名本身只是将 canffupstream_name 中的所有内容铲到一行中。 Git 的配置文件规则要求整个别名用双引号引起来,这又要求将所有内部双引号转换为反斜杠双引号序列。

(我还删除了 local b 语句,因为作为别名,它每次都会触发 shell 的一个新实例,因此变量名卫生变得不重要。)

(实际上可以将别名写成多行。只需在每个换行符前加上反斜杠即可。但是,这个别名太复杂了,这样看起来也很难看,所以我最后只留下了一大行。 )