代码之家  ›  专栏  ›  技术社区  ›  mark

在git中,我们如何在创建分支时签出它,前提是它还不存在?

git
  •  0
  • mark  · 技术社区  · 3 年前

    所以 git checkout -B <branch> 不符合要求,因为它会迫使当前HEAD上的,即使HEAD已经存在并指向其他地方。

    我想要一种不同的行为。如果分支存在-checkout,如果没有-在当前HEAD创建,然后checkout。

    我目前的解决方案(在Powershell中)是:

    $CreateBranch = $null
    git show-ref --verify --quiet "refs/remotes/origin/$BranchName"
    if ($LASTEXITCODE)
    {
        $CreateBranch = "-B"
    }
    git checkout $CreateBranch $BranchName
    

    有更短的路吗?

    编辑1

    我一直在思考我定义这个问题的方式,这些评论帮助我意识到我需要消除几点歧义。所以:

    1. 在以下情况下检查存储库 git fetch --force --tags --prune origin
    2. 如果 远程跟踪 分支存在,那么这就是应该检查的。
    3. 应忽略没有相应远程的本地分支。
    4. 如果本地和远程跟踪分支都存在,则必须在 远程跟踪 -它必须重新调整。

    现在我可以看到我的方法是错误的,因为它不符合这些要点。感谢所有在这些方面指出的评论者。

    0 回复  |  直到 3 年前
        1
  •  2
  •   torek    2 年前

    为了避免 git checkout (或新 git switch )使用DWIM模式 创造 分支 $b $remote/$b (对于任何可能发生这种情况的$b和$remote),请使用 --no-guess 旗帜。这个标志在Git 2.21中是新的。( git开关 它本身在Git 2.23中是新的,所以如果你有 git开关 总之,你有 --没有猜测 .)

    你仍然需要至少两个命令来获得你想要的东西,但你不再需要显式的验证 refs/heads/$b 先存在。

    如果你的Git早于2.21,请进行显式验证:

    git rev-parse --quiet --verify refs/heads/$branchname
    

    如果失败(退出代码1), $branchname 不是现有的分支名称。如果成功(退出代码0), $分行名称 是一个现有的分支名称。从那里继续。

    请注意,您的PowerShell片段表明您希望的行为与您在文本中描述的行为不同。你不在乎是否有些 地方的 分支是否存在,而是是否存在某种特定的 远程跟踪名称 存在。使用 git rev-parse 方法也是如此;只需更换 参考文献/负责人/十亿美元 具有 refs/remotes/$o/$b 哪里 $o b 被适当地设置。


    重新编辑:

    在git fetch--force--tags-prune origin之后检查存储库。

    好的。请注意,这使用了现有的 remote.origin.fetch refspecs(这可能没问题)。

    如果远程跟踪分支存在,则应检查此分支。

    这是针对某个固定的分支名称,还是一组分支名称?(不知道如何在PowerShell中编写循环 for branch in name1 name2 name3; do ...; done 在sh/bash中。)或者,您想枚举所有远程跟踪名称和/或所有本地分支名称吗?( git for-each-ref 是通用的Git名称枚举工具。)

    应忽略没有相应远程的本地分支。

    如果您正在枚举中的所有名称 refs/remotes/origin/ (全部 origin/* 远程跟踪名称),这是“免费”发生的;如果要枚举一组固定名称,或者枚举本地分支名称,当然需要进行某种测试。

    请注意,在Git存储库中(尽管在CI系统存储库中更为罕见),(本地)分支名称通常有一些 上游 集。要查找某个本地分支的上游,请使用 git rev解析 @{upstream} @{u} 后缀: git rev-parse dev@{u} 将测试是否 dev 有一个上游设置,如果没有,则出错。如果它确实有一个上游集,rev parse将继续像往常一样处理它,根据标志将该上游名称转换为哈希ID或符号名称。

    这种总体想法(有一个上游)在三角工作流中效果不佳,我们喜欢从存储库中获取新的提交 R ,但将新提交提交到不同的存储库 S 。如果它不适合你自己的任务集,就不要使用它。

    如果本地和远程跟踪分支都存在,则必须在远程跟踪处签出本地分支——必须重新对齐。

    那将是 -B -C (分别为结账或切换)动作。

    因此,假设有一组固定的名称,sh/bash变体将是:

    fatal() {
        ... insert code here to print an error message and abort ...
    }
    for name in $list_of_names; do
        # Determine whether origin/$name exists at all.  If
        # not, skip this name.  While we're at it, save the
        # hash ID since we will want it in a moment.
        hash=$(git rev-parse --quiet --verify refs/remotes/origin/$name) || continue
        # The remote-tracking name exists.  What about as a local
        # branch name?  If not, do a detached checkout of $hash, but
        # if so, do a forcible reset of the local name to $hash.
        if git rev-parse --quiet --verify refs/heads/$name >/dev/null; then
            # forcible reset:
            git switch -C $name $hash ||
               fatal "unable to locally reset $name to $hash"
        else
            # detached-HEAD check-out:
            git switch --detach $hash ||
               fatal "unable to detach HEAD to $hash"
        fi
        ... do some operation with the checked-out files ...
    done
    

    整个过程似乎有点可疑:如果我们需要对一些特定名称确定的特定提交执行一些操作,我们可以对每个这样的名称进行一个独立的HEAD检查,只要我们不需要 承诺。而且,如果我们 需要制作 提交,我们可能永远不希望任何分离的HEAD签出 除非 我们将尝试构建(毕竟这是一个CI系统) 如果成功了 ,粘贴计算机生成的名称 它(例如,一些自动构建标签)。。。这同样适用于分离式HEAD模式。

    虽然我对有问题的CI系统一无所知,但在我看来,我们要么只是想构建(然后可能会根据结果制作一个构建tarball或其他任何东西),要么看看是否已经有了一个自动化的构建 $hash 如果没有,制作一个并将其标记为“$hash的自动构建”。如果可以并行构建多个CI系统,我们可能希望添加某种并行构建协调器,以便我们知道哪些构建正在进行中,哪些构建失败并且不需要重试,等等:一种构建代理。(当然,所有这些都不在考虑范围之内 Git 确实如此。)