【问题标题】:Checkout remote git branch if exists else clone master make new branch and push如果存在则检查远程 git 分支,否则克隆 master 创建新分支并推送
【发布时间】:2017-04-08 11:46:31
【问题描述】:

抱歉标题太冗长了。

fastlane,使用Ruby编程语言,我想拥有这个功能:

if remote_branch_exist
  git_clone_remote_branch
else
  git_clone_master
  git_branch
  git_push_branch_to_master
end

我已经搜索了一个可以做到这一点的单行 git 命令,但没有成功。有可能吗?

我编写的代码完全符合我的要求。但这肯定是不必要的代码量。

def git_clone_sdk_repo(path_repo: nil)
  some_branch = "some_branch"
  git_url = "git@github.com:MyComp/MyRepo.git"

  if check_if_remote_branch_exists(git_url: git_url, branch_name: some_branch)
    puts "remote branch exists"

    sh "git clone -b #{some_branch} #{git_url} #{path_repo}"
  else
    puts "no remote branch"

    sh "git clone #{git_url} #{path_repo}"
    pwd = Dir.pwd
    FileUtils.cd(path_repo)
    sh "git checkout -b #{some_branch}"
    sh "git push --set-upstream origin #{some_branch}"
    FileUtils.cd(pwd)
  end
end

def check_if_remote_branch_exists(git_url: nil, branch_name: nil)
  check_if_remote_branch_exists = "git ls-remote --heads #{git_url} #{branch_name} | wc -l | grep -o -q '1'"
  system(check_if_remote_branch_exists)
end

(上面代码块中的方法sh是用来调用CLI命令的,我觉得是fastlane的一部分。)

运行这个命令:

git clone -b <some_branch> <git_url> <path_repo>

结果:

fatal: Remote branch <some_branch> not found in upstream origin

如果远程中没有具有该名称的分支。所以这就是为什么我首先检查是否存在具有这样名称的远程分支。

我错过了什么简洁的 git 命令?

【问题讨论】:

    标签: ruby git shell version-control fastlane


    【解决方案1】:

    让我用 Git 术语而不是 Ruby 代码重新表达这个任务。

    您希望:

    1. 从某个 URL 克隆存储库。然后,我们将该 URL 保存在通常的“远程”名称下,origin

    2. 给定分支名称,例如 foo,检查该特定分支(以便当前提交是该分支的提示提交)。

      如果分支可以从远程跟踪分支派生,例如(例如)对于通常从 origin/master 派生的master 通常是正确的——您希望 Git 在本地创建此分支,并使用相应的远程跟踪分支设置为其上游,准备对其进行处理。因此,如果分支foo 存在于origin 上的Git 存储库中,那么origin/foo 将存在于本地存储库中,您希望创建本地分支foo,并将origin/foo 作为其上游。

      但是,如果没有——如果没有相应的上游名称,那么此时,该分支将成为一个新分支——您希望创建该新分支,使其指向与 @987654330 相同的提交@ 将指向。在这种情况下,您还想立即(或尽可能快地)请求origin 上的 Git 创建这个分支名称,指向同一个提交,并在成功时,将foo 设置为将origin/foo 作为其上游。理想情况下,这个过程的最终结果是本地分支foo 存在并且有origin/foo 作为其上游。

    您已经观察到,如果foo 存在于远程,git clone -b foo &lt;url&gt; &lt;directory&gt; 会在一个干净的步骤中完成任务(尽管作为副作用,本地克隆不会master分支呢!)。但是,如果foo 确实 存在于遥控器上,则克隆失败。

    不幸的是,没有一个 Git 命令可以做到这一切。此外,这里还有一个原子性问题(“原子性”在数据库或并行编程术语中具有其通常含义):在克隆步骤期间foo 不存在这一事实并不意味着@987654341 @ 在您要求上游存储库创建它时将不存在。

    所有这一切的“最佳”答案取决于您对这个原子性问题的关心程度(解决它通常只是将原子性问题移到后面的推送步骤,因为到那时可以在服务器上删除分支foo,或者已经获得了额外的提交,或者被重绕和重写,或者其他)。但最终你必须使用多个 Git 命令。

    方法一

    使用最少网络流量的序列是在没有-b的情况下进行克隆。在这种情况下,您的克隆将自行检查某个分支 — 通常是 master,但实际选择的分支将取决于将存储在远程的 URL 中 Git 的 HEAD 条目中的内容.然后,您的克隆将照常保存遥控器的 URL,名称为 origin(或您提供的任何 -o 参数)。

    现在您可以简单地尝试git checkout foofoo 已经是当前分支(因为它在远程的HEAD 中),所以这是一个成功的空操作;或 foo 不是当前分支。如果foo 不是当前分支,当且仅当origin/foo 存在时,thish 将创建foo 作为本地分支,并将origin/foo 设置为其上游。这个origin/foo 将反过来存在当且仅当一个名为foo 的分支存在于远程在您进行克隆时(参见“原子性”)。

    如果git checkout 失败,您可以假设origin/foo 不存在。 (唯一的另一种可能性是事情发生了非常严重的错误,例如,您的磁盘空间不足或存储设备出现故障,或者 Git 中存在错误:在这两种情况下,所有赌注都没有。)你可以在这个指向您的“创建foo 指向与origin/master 相同的提交并使用git push -u 要求在origin 上创建它”路径,并验证这一切是否有效。与git push 一样,您现在正在与创建foo 的任何人else 竞争。另请注意,如果在您进行克隆时另一个 Git 上没有 master,那么您自己的存储库中可能没有origin/master

    方法二

    您可以像现在一样使用git ls-remote,它对远程执行一个完整的往返操作(当前通过URL,因为还没有本地克隆,因此没有名为origin 的远程到store 该 URL)以确定它具有的引用集。如果该存储库中不存在foo,您可以要求 Git 创建它。如果您愿意,您可以稍微不同地执行此操作,在一个新的存储库中使用一系列本地 Git 操作,但该存储库中还没有任何内容:

    mkdir <directory>
    cd <directory>
    git init
    git remote add origin <url>
    

    此时您可以运行git ls-remote origin,因为现在一个名为origin 的遥控器。但是,根本没有本地分支机构。现在我们遇到了通常的原子性问题,“下一步做什么”再次取决于您希望如何解决它们。但如果我不使用方法 1 或它的一些轻微变体,这就是我接下来要做的:

    # assumes $branch is set to "foo" as needed, and that
    # function "die" prints an error message and exits with failure
    
    git fetch origin  # bring over all commits and origin/* branches
    if branchrev=$(git rev-parse -q --verify origin/$branch); then
        # origin/$branch exists, so we want to act like "git clone -b $branch"
        git checkout $branch ||
            die "unable to check out $branch, cannot proceed"
    else
        # origin/$branch does not exist: ask to create it pointing to
        # origin/master
        rev=$(git rev-parse -q --verify origin/master) ||
            die "no origin/master exists, cannot proceed"
        git checkout -b $branch $rev ||
            die "failed to create $branch"
        git push -u origin "$branch:refs/heads/$branch" ||
            die "failed to create $branch on origin"
    fi
    

    git checkout -b 在本地存储库中创建分支,并将其设置为当前分支。由于初始提交 ID 由原始提交哈希给出(由于 $rev 包含来自 git rev-parse 的结果),它将没有上游。您可以改为使用git checkout -b $branch origin/master,但这会将新分支的上游设置为origin/master,如果git push -u 由于某种原因(例如网络故障)失败,则会为粗心的人留下一个陷阱。你可以使用git checkout --no-track -b $branch origin/master,但考虑到测试以确保origin/master 是一个有效的名称,我们不妨将哈希ID 保存在$rev 中并使用它。

    同样的shell脚本——如果你愿意,你可以用Ruby重写——可以在一个普通的旧git clone之后使用,而不是使用有点晦涩的git init; git remote add ...; git fetch序列来完成git clone会做的所有事情除了远程HEAD所指示的分支的初始git checkout

    (换句话说,在实践中,我会先运行git clone——没有棘手的-b部分——然后在上面的shell脚本部分中执行所有操作除了@987654391 @ step,通常在 clone step 之后是不必要的。如果克隆需要很长时间,额外的 git fetch 可能仍然有用,因为这会缩小原子性竞赛,代价是多一个往返于origin 的服务器。不过,没有什么可以完全结束比赛。)

    【讨论】:

    • 您提供了一个非常彻底和教学性的答案!对我来说,原子性不是问题。不过谢谢你。
    • 所以答案基本上是“不可能”。如果我们 - 不考虑原子性 - 将你的 shell 脚本与我的 ruby​​ 代码进行比较,你的解决方案比我的有什么优势吗?
    • @Sajjon:如果您进行一个克隆(或 fetch-equivalent),然后使用本地信息(“是否存在 origin/foo”等)执行剩余操作,您将最大限度地减少转移次数网络。这是不使用git ls-remote 的唯一原因。
    猜你喜欢
    • 2015-08-14
    • 2012-07-25
    • 2014-02-04
    • 2016-05-02
    • 2013-06-27
    • 2020-09-16
    • 2016-10-10
    • 2021-09-17
    • 2011-09-10
    相关资源
    最近更新 更多