【问题标题】:Cleaning up old remote git branches清理旧的远程 git 分支
【发布时间】:2011-03-12 04:53:58
【问题描述】:

我在两台不同的计算机(A 和 B)上工作,并将一个通用的 git 远程存储在 Dropbox 目录中。

假设我有两个分支,master 和 devel。两者都在跟踪它们的远程对应对象 origin/master 和 origin/devel。

现在在计算机 A 上,我删除本地和远程分支 devel。

git push origin :heads/devel
git branch -d devel

在计算机 A 上运行 git branch -a,我得到以下分支列表。

  • 主人
  • 原点/HEAD
  • 起源/主人

在计算机B上运行git fetch,我可以用git branch -d devel删除本地devel分支,但我无法删除远程devel分支。

git push origin :heads/devel 返回以下错误消息。

错误:无法推送到不合格的目的地:heads/proxy3d
目标 refspec 既不匹配远程上的现有 ref,也不以 refs/ 开头,我们无法根据源 ref 猜测前缀。
致命:远端意外挂断

git branch -a 仍然在远程分支中列出 origin/devel。

如何清理计算机 B 的远程分支?

【问题讨论】:

  • 一位尝试过的人告诉我,Dropbox 文件夹中的 git 存储库有点脆弱(但没有其他详细信息)。
  • @ThorbjørnRavnAndersen 可能是因为您必须等待以确保它在您提交时完全同步,然后才能确定在另一台机器上使用它是安全的(即使那样也需要再次同步)。
  • 当我的公司自动添加 OneDrive 时,我也遇到了很多问题。 OneDrive 文件夹中没有 git repo!

标签: git git-branch


【解决方案1】:

我必须在这里添加一个答案,因为其他答案要么没有涵盖我的情况,要么是不必要的复杂。 我与其他开发人员一起使用 github,我只想从我的机器上一次性删除所有远程从 github PR 中(可能合并和)删除的本地分支。不,git branch -r --merged 之类的内容不涵盖未在本地合并的分支,或者根本未合并(废弃)的分支等,因此需要不同的解决方案。

无论如何,第一步我是从其他答案中得到的:

git fetch --prune

git remote prune origin 的试运行似乎在我的情况下会做同样的事情,所以我选择了最短的版本以保持简单。

现在,git branch -v 应该将其远程被删除的分支标记为[gone]。 因此,我需要做的就是:

git branch -v|grep \\[gone\\]|awk '{print $1}'|xargs -I{} git branch -D {}

就这么简单,它会删除上述场景中我想要的所有内容。

不太常见的xargs 语法是这样的,除了Linux 之外,它还可以在Mac 和BSD 上运行。 小心,这个命令不是空运行,所以它会强制删除所有标记为[gone] 的分支。显然,这是 git nothing 永远消失了,如果你看到删除了你记得想要保留的分支,你可以随时取消删除它们(上面的命令将列出它们删除时的哈希,所以一个简单的git checkout -b <branch> <hash>

编辑:只需将此别名添加到您的 .bashrc/.bash_profile 中,这两个命令合二为一,我更新了第二个以适用于所有 shell:

alias old_branch_delete='git fetch -p && git branch -vv | awk "/: gone]/{print \$1}" | xargs git branch -D'

【讨论】:

    【解决方案2】:
    # First use prune --dry-run to filter+delete the local branches
    git remote prune origin --dry-run \
      | grep origin/ \
      | sed 's,.*origin/,,g' \
      | xargs git branch -D
    
    # Second delete the remote refs without --dry-run
    git remote prune origin
    

    从本地和远程引用中修剪相同的分支(在我的示例中来自origin)。

    【讨论】:

    • 组合成一个shell别名alias git-prune-origin="git remote prune origin --dry-run | grep origin/ | sed 's,.*origin/,,g' | xargs git branch -D; git remote prune origin"
    【解决方案3】:

    首先,git branch -a 在机器 B 上的结果是什么?

    其次,你已经在origin上删除了heads/devel,所以你不能从机器B上删除它。

    试试

    git branch -r -d origin/devel
    

    git remote prune origin
    

    git fetch origin --prune
    

    并随时将--dry-run 添加到您的git 语句的末尾,以查看在不实际运行的情况下运行它的结果。

    git remote prunegit branch 的文档。

    【讨论】:

    • git remote prune origin 为我工作 => * [pruned] origin/my-old-branch
    • git remote prune origin --dry-run 向您展示了哪些内容将被删除而无需实际执行。
    • git fetch origin --prune 非常适合删除已删除的分支
    • 如果你有一个本地分支跟踪一个已经消失的远程,这不会删除任何东西。对于那些,它出现git branch -vv,然后是git branch -D branchname,最后修剪是最好的方法。
    • 您可以通过设置以下选项将修剪配置为在 pull/fetch 时自动发生:git config remote.origin.prune true
    【解决方案4】:

    如果git branch -r 显示了很多您不感兴趣的远程跟踪分支,并且您只想从本地删除它们,请使用以下命令:

    git branch -r | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd
    

    更安全的版本是只删除合并的:

    git branch -r --merged | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd
    

    这对于大型项目可能很有用,在这些项目中您不需要其他队友的功能分支,但在初始克隆时会获取大量远程跟踪分支。

    但是,仅此步骤是不够的,因为那些已删除的远程跟踪分支会在下一个 git fetch 再次出现。

    要停止获取那些远程跟踪分支,您需要在 .git/config 中明确指定要获取的引用:

    [remote "origin"]
      # fetch = +refs/heads/*:refs/remotes/origin/*    ### don't fetch everything
      fetch = +refs/heads/master:refs/remotes/origin/master
      fetch = +refs/heads/develop:refs/remotes/origin/develop
      fetch = +refs/heads/release/*:refs/remotes/origin/release/*
    

    在上面的例子中,我们只获取masterdeveloprelease分支,你可以随意调整。

    【讨论】:

    • 谢谢,第一个命令对我有用。您也可以使用git fetch origin branchname 仅获取一个分支,或者创建一个别名,如ft = "!f() { git fetch origin $(git rev-parse --abbrev-ref HEAD);}; f" 以仅获取当前分支(我个人最喜欢的)。干杯。
    • 也许是 OT 但-r 标志是什么意思?我看到xargs 有一个-R 参数,但没有找到关于-r 的任何信息。我的意思是,理论上如果我没记错xargs 是如何工作的,即使没有-r 它也应该可以工作。
    • 我喜欢这个答案。几个注释(确实很明显)。通过删除最后一个管道和最后一个命令| xargs git branch -d,可以“预览”哪些分支将被删除。此外,通过从git branch -d 中删除-r 选项(--remotes),我们只清理本地分支(考虑到默认情况下git branch 无论如何都不显示远程分支,这可能就足够了)。
    • 谢谢!这正是我所需要的。其他解决方案建议将修剪的本地分支推送到远程以将它们也删除,但是当远程已被删除或不再存在时,这无济于事。这种方法删除了对我不再有权访问的远程分支的本地引用。
    【解决方案5】:

    此命令将“试运行”删除所有远程 (origin) 合并分支,master 除外。您可以更改它,或者在 master 之后添加其他分支:grep -v for-example-your-branch-here |

    git branch -r --merged | 
      grep origin | 
      grep -v '>' | 
      grep -v master | 
      xargs -L1 | 
      awk '{sub(/origin\//,"");print}'| 
      xargs git push origin --delete --dry-run
    

    如果看起来不错,请删除 --dry-run。此外,您可能想先在 fork 上进行测试。

    【讨论】:

    • 不错。我使用 perl 而不是第一个 xargs + awk 进行调整: git branch -r --merged | grep 起源 | grep -v '>' | grep -v 大师 | perl -lpe '($junk, $_) = split(/\//, $_,2)' | xargs git push origin --delete
    【解决方案6】:

    删除总是一项具有挑战性的任务,而且可能很危险!!!因此,首先执行以下命令,看看会发生什么:

    git push --all --prune --dry-run
    

    通过像上面那样做,git 将为您提供一个列表,如果执行以下命令会发生什么。

    然后运行以下命令,从远程 repo 中删除所有不在本地 repo 中的分支:

    git push --all --prune
    

    【讨论】:

    • 这个命令似乎很危险......它设法删除了我想要的东西(并且不能用上面的至少四个答案做)。但它也删除了其他四个开发分支。 Git 真的很烂……
    • 那些分支一定不在您的本地。然而,一切都没有丢失。 Git 提交,Git reflog,然后 git reset --hard 。你可以用这个字面上的任何提交(又名保存)和其他人。分支只是一个标签,删除标签不会删除保存...它将永远有一个校验和。让我知道是否可以提供帮助
    • 当然,你会想很快恢复这些,因为 git 的垃圾收集器最终会删除分支未引用的提交。
    • 最好将-n(试运行)添加到此命令中,这样您就可以预览更改,然后如果一切正常,请在没有-n 的情况下再次调用它。
    • 同意@GuiWeinmann - 这太危险了,尤其是在没有特定标注的情况下,至少要先在试运行中运行它。
    【解决方案7】:

    考虑运行:

    git fetch --prune
    

    定期在每个 repo 中删除本地分支,这些分支一直在跟踪已删除的远程分支(远程 GIT repo 中不再存在)。

    这可以进一步简化

    git config remote.origin.prune true
    

    这是一个per-repo 设置,它将使任何未来的git fetch or git pull 自动修剪

    要为您的用户进行设置,您还可以编辑全局 .gitconfig 并添加

    [fetch]
        prune = true
    

    但是,建议使用以下命令来完成:

    git config --global fetch.prune true
    

    或在系统范围内应用它(不仅仅是为用户)

    git config --system fetch.prune true
    

    【讨论】:

      【解决方案8】:

      以下是使用 SourceTree (v2.3.1) 的方法:
      1. 点击获取
      2.选中“修剪跟踪分支...”
      3. 按确定
      4.?

      【讨论】:

        【解决方案9】:

        这是可以为您完成的 bash 脚本。它是http://snippets.freerobby.com/post/491644841/remove-merged-branches-in-git 脚本的修改版本。我的修改使它能够支持不同的远程位置。

        #!/bin/bash
        
        current_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
        if [ "$current_branch" != "master" ]; then
          echo "WARNING: You are on branch $current_branch, NOT master."
        fi
        echo -e "Fetching merged branches...\n"
        
        git remote update --prune
        remote_branches=$(git branch -r --merged | grep -v '/master$' | grep -v "/$current_branch$")
        local_branches=$(git branch --merged | grep -v 'master$' | grep -v "$current_branch$")
        if [ -z "$remote_branches" ] && [ -z "$local_branches" ]; then
          echo "No existing branches have been merged into $current_branch."
        else
          echo "This will remove the following branches:"
          if [ -n "$remote_branches" ]; then
        echo "$remote_branches"
          fi
          if [ -n "$local_branches" ]; then
        echo "$local_branches"
          fi
          read -p "Continue? (y/n): " -n 1 choice
          echo
          if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then
            remotes=`echo "$remote_branches" | sed 's/\(.*\)\/\(.*\)/\1/g' | sort -u`
        # Remove remote branches
        for remote in $remotes
        do
                branches=`echo "$remote_branches" | grep "$remote/" | sed 's/\(.*\)\/\(.*\)/:\2 /g' | tr -d '\n'`
                git push $remote $branches 
        done
        
        # Remove local branches
        git branch -d `git branch --merged | grep -v 'master$' | grep -v "$current_branch$" | sed 's/origin\///g' | tr -d '\n'`
          else
        echo "No branches removed."
          fi
        fi
        

        【讨论】:

        • 这发生在一个名为“origin/feature/mybranch”的分支上,我不知道为什么。
        • 问题出在两个seds。将sed 's/\(.*\)\/\(.*\)/\1/g' 替换为sed 's/\([^/]*\)\/\(.*\)/\1/g'
        猜你喜欢
        • 2012-06-24
        • 2013-07-02
        • 1970-01-01
        • 2012-10-22
        • 2013-07-07
        • 2013-03-25
        • 2018-11-19
        • 1970-01-01
        相关资源
        最近更新 更多