【问题标题】:Run git pull over all subdirectories [duplicate]在所有子目录上运行 git pull [重复]
【发布时间】:2011-03-30 15:42:15
【问题描述】:

如何在没有cd'ing 的情况下从它们的共享父目录更新多个 git 存储库到每个 repo 的根目录?我有以下都是单独的 git 存储库(not 子模块):

/plugins/cms
/plugins/admin
/plugins/chart

我想一次更新它们,或者至少简化我当前的工作流程:

cd ~/plugins/admin
git pull origin master
cd ../chart
git pull

等等

【问题讨论】:

  • find -name .git -execdir git pull \; 有什么问题?
  • git do pull怎么样
  • answered hg mercurial 的相同问题。
  • find . -name .git -print -execdir git pull \; 可以。 -print 将回显当前目录。
  • 另请参阅,使用 Git 2.30(2020 年第四季度),[新的 git for-each-repo 命令] (stackoverflow.com/a/65766304/6309)

标签: git bash


【解决方案1】:

这应该会自动发生,只要 cms、admin 和 chart 都是存储库的一部分。

一个可能的问题是这些插件中的每一个都是一个 git 子模块。

运行git help submodule了解更多信息。

编辑

在 bash 中执行此操作:

cd plugins
for f in cms admin chart
do 
  cd $f && git pull origin master && cd ..
done

【讨论】:

  • 不,对不起,您误会了。这些目录中的每一个都是一个单独的 git 存储库。 /plugins 不是存储库
  • 啊。我的错。一分钟后会给你 bash 解决方案。
  • 你去。如果要返回父目录,之后再运行一个cd ..即可。
  • 或使用pushdpopd 或将这组命令放在一个子shell 中(当子shell 退出时,您将留在原始目录中)。 (cd dir; for ... done)
  • 出于好奇 - 为什么不使用 ssh 密钥?
【解决方案2】:

2010 年原始答案:

如果所有这些目录都是单独的 git repo,您​​应该将它们引用为 submodules

这意味着您的“来源”将是远程存储库“plugins”,它仅包含对子存储库“cms”、“admin”、“chart”的引用。

git pull 后跟 git submodule update 将实现您的需求。


2016 年 1 月更新:

使用 Git 2.8(2016 年第一季度),您将能够使用 git fetch --recurse-submodules -j2 并行(!)获取子模块。
见“How to speed up / parallelize downloads of git submodules using git clone --recursive?

【讨论】:

【解决方案3】:

从父目录运行以下命令,在本例中为plugins

find . -type d -depth 1 -exec git --git-dir={}/.git --work-tree=$PWD/{} pull origin master \;

澄清一下:

  • find .搜索当前目录
  • -type d 查找目录,而不是文件
  • -depth 1 一个子目录的最大深度
  • -exec {} \; 为每个查找运行一个自定义命令
  • git --git-dir={}/.git --work-tree=$PWD/{} pull git 拉取各个目录

要玩 find,我建议在-exec 之后使用echo 进行预览,例如:

find . -type d -depth 1 -exec echo git --git-dir={}/.git --work-tree=$PWD/{} status \;

注意:如果-depth 1 选项不可用,请尝试-mindepth 1 -maxdepth 1

【讨论】:

  • find: 警告:您在非选项参数 -type 之后指定了 -depth 选项,但选项不是位置性的(-depth 影响在它之前指定的测试以及在它之后指定的测试)。请在其他参数之前指定选项。
  • 我使用find . -maxdepth 1 -type d -print -execdir git --git-dir={}/.git --work-tree=$PWD/{} pull origin master \;在拉取之前输出文件夹的名称,以消除警告并仅对子文件夹运行拉取。
  • fetch origin master:master 替换'pull origin master' 告诉git 用origin 的master 分支显式更新你的'master' 分支。此不会进行合并,如果您这样做,任何对 master 的提交都将丢失。
  • 从 git 1.8.5 开始,可以用 -C 选项替换 --git-dir 和 --work-tree,参见this question。 -- 我正在使用find . -mindepth 1 -maxdepth 1 -type d -print -exec git -C {} pull \;
  • @Rystraum 提到的@ZsoltSzilagy,您可以使用-maxdepth 1 而不是-depth 1
【解决方案4】:

比 leo 的解决方案技术含量低一点:

for i in */.git; do ( echo $i; cd $i/..; git pull; ); done

这将更新您工作目录中的所有 Git 存储库。无需明确列出它们的名称(“cms”、“admin”、“chart”)。 “cd”命令只影响子shell(使用括号生成)。

【讨论】:

  • 正是我想要的
  • 这个的优点是显示它正在处理哪个存储库,当出现问题时有用(缺少分支,不可用的远程......)
  • 这很好用。我添加了一个 crontab -e 文件,每 5 分钟运行一次,它似乎完全符合我的期望。
  • 我喜欢这个解决方案,因为它只提取 git repo 的子目录,谢谢!
  • 交替使用git -C: for i in */.git; do git -C $i pull; done
【解决方案5】:
ls | xargs -I{} git -C {} pull

并行执行:

ls | xargs -P10 -I{} git -C {} pull

【讨论】:

  • 不错!我在我的 .gitconfig 中把它作为别名:all = "!f() { ls | xargs -I{} git -C {} $1; }; f" 现在我可以做git all pullgit all "checkout master" 等。
  • 清理了一下,将递归搜索所有目录以仅 git repos,并去除颜色以防你有 ls 别名 ls -R --directory --color=never */.git | sed 's/\/.git//' | xargs -P10 -I{} git -C {} pull
  • 我将一些答案拼凑在一起,为 macOS 上的 git 创建了这个,它过滤包含 .git 文件夹的文件夹,并允许您运行任意命令,例如 git all fetch --prune: git config --global alias.all '!f() { ls -R -d */.git | sed 's,\/.git,,' | xargs -P10 -I{} git -C {} $1; }; f'
  • @AWrightIV 实际上,ls -R -d */.git 正在返回当前文件夹中包含.git 目录的目录的过滤列表。这样,当我运行 git all fetch 之类的东西时,它只会针对具有 .git 文件夹的子文件夹执行。这是对原始问题的回答,但它试图通过不假设所有子目录都是 git repos 来提高效率。
  • 对 borisdiakur 命令的轻微改进,以避免在 . 上运行并打印出它在每个时刻运行的目录:git config --global alias.all '!f() { ls -R -d */.git | xargs -I{} bash -c "echo {} && git -C {}/../ $1"; }; f'
【解决方案6】:

你可以试试这个

find . -type d -name .git -exec sh -c "cd \"{}\"/../ && pwd && git pull" \;

另外,您可以通过添加一个更多的 && 参数来添加您的自定义输出。

find . -type d -name .git -exec sh -c "cd \"{}\"/../ && pwd && git pull && git status" \;

【讨论】:

    【解决方案7】:

    如果你有很多带有 git 存储库的子目录,你可以使用并行

    ls | parallel -I{} -j100 '
      if [ -d {}/.git ]; then
        echo Pulling {}
        git -C {} pull > /dev/null && echo "pulled" || echo "error :("
      else
         echo {} is not a .git directory
      fi
    '
    

    【讨论】:

    • 伟大的。我做了一些修改以适应我的风格。 pastebin
    【解决方案8】:

    mr 实用程序(又名myrepos)为这个问题提供了一个出色的解决方案。使用您最喜欢的包管理器安装它,或者直接获取mr 脚​​本directly from github 并将其放入$HOME/binPATH 的其他位置。然后,cd 到这些 repos 共享的父 plugins 文件夹,并创建一个基本的 .mrconfig 文件,其内容类似于以下(根据需要调整 URL):

    # File: .mrconfig
    [cms]
    checkout = git clone 'https://<username>@github.com/<username>/cms' 'cms'
    
    [admin]
    checkout = git clone 'https://<username>@github.com/<username>/admin' 'admin'
    
    [chart]
    checkout = git clone 'https://<username>@github.com/<username>/chart' 'chart'
    

    之后,您可以从顶级 plugins 文件夹运行 mr up 以从每个存储库中提取更新。 (请注意,如果目标工作副本尚不存在,这也会执行初始克隆。)您可以执行的其他命令包括 mr stmr pushmr logmr diff 等—运行 mr help看看有什么可能。有一个 mr run 命令充当传递,允许您访问不直接由 mr 本身支持的 VCS 命令(例如,mr run git tag STAGING_081220015)。您甚至可以创建自己的自定义命令来执行针对所有 repos 的任意位 shell 脚本!

    mr 是一个非常有用的处理多个 repos 的工具。由于plugins 文件夹位于您的主目录中,您可能还对vcsh 感兴趣。与mr 一起,它提供了一种强大的机制来管理所有您的配置文件。请参阅 Thomas Ferris Nicolaisen 的 blog post 了解概览。

    【讨论】:

      【解决方案9】:

      最紧凑的方法,假设所有子目录都是 git repos:

      ls | parallel git -C {} pull
      

      【讨论】:

        【解决方案10】:

        我用这个:

        find . -name ".git" -type d | sed 's/\/.git//' |  xargs -P10 -I{} git -C {} pull
        

        通用:更新当前目录下的所有 git 存储库。

        【讨论】:

        • 您可以使用--git-dir 而不是-C 直接传入.git 路径,无需替换sed! find . -name ".git" -type d | xargs -P10 -I{} git --git-dir={} pull
        【解决方案11】:

        我用这个

        for dir in $(find . -name ".git")
        do cd ${dir%/*}
            echo $PWD
            git pull
            echo ""
            cd - > /dev/null
        done
        

        Github

        【讨论】:

          【解决方案12】:

          前 5 个答案都不适合我,而且问题涉及目录。

          这行得通:

          for d in *; do pushd $d && git pull && popd; done
          

          【讨论】:

          • 对于 Windows,请在此处查看我的答案:stackoverflow.com/a/51016478/207661。和上面的很相似。
          • 接受的答案曾经对我有用,但它在去年的某个时候退出了。我终于决定寻找另一种解决方案并找到您的答案。感谢分享。效果很好。
          • 无需push和pup,只需使用git的-C选项即可。
          • 另一种选择是使用子shell:for d in *; do (cd $d &amp;&amp; git pull --ff-only); done 这比-C 具有优势,因为这种方法普遍适用于没有此类选项的程序。请注意,--ff-only 在自动更新的情况下最好使用;如果使用原生 vim 包管理,我会使用它来更新 vim 插件。
          【解决方案13】:

          其实,如果你不知道子文件夹有没有git repo,最好让find为你获取repos:

          find . -type d -name .git -exec git --git-dir={} --work-tree=$PWD/{}/.. pull origin master \;
          

          PowerShell 等效项是:

          Get-ChildItem -Recurse -Directory -Hidden -Filter .git | ForEach-Object { & git --git-dir="$($_.FullName)" --work-tree="$(Split-Path $_.FullName -Parent)" pull origin master }
          

          【讨论】:

          • 这适用于我的 macOS。
          • 确保你所有的 git repos 都在 master 上,然后再按照所写的方式执行。否则你可能会无意中将 master 合并到你当前的分支中。
          • 感谢您的 powershell 版本,仍然适用于 PS 7.10
          • 对于 PS 2.0 这工作:gci | where {$_.Attributes -match'Directory'} | foreach { write-host $_.fullname; push-location $_; &amp; git pull; &amp; cd ..}
          • 注意 - 我找不到 hidden 参数,但 Force 似乎可以工作,就像在 Get-ChildItem -Path "C:\Repos" -Recurse -Directory -Force -Filter ".git" -Depth 1 中一样
          【解决方案14】:

          gitfox 是一个在所有 subrepos 上执行命令的工具

          npm install gitfox -g
          g pull
          

          【讨论】:

          • g 是什么?不是每个人都有和你一样的别名
          • @LưuVĩnhPhúc gitfox 出于某种原因将自己安装在别名“g”下(尽管帮助消息显示“gitfox”)。就我个人而言,我不认为这是一个足够重要的命令来要求这样的捷径但是啊。不过,它可以完成这项工作。
          • @LưuVĩnhPhúc 检查源代码库的使用情况github.com/eqfox/gitfox
          【解决方案15】:

          我综合了几个cmets和答案的点:

          find . -maxdepth 1 -type d -name .git -execdir git pull \;
          

          【讨论】:

            【解决方案16】:

            我的卑微结构

            • 显示当前路径(使用python,方便又好用,见How to get full path of a file?
            • 直接查找 .git 子文件夹:在非 git 子文件夹中发出 git 命令的机会很小
            • 去掉了find的一些警告

            如下:

            find . \
                -maxdepth 2 -type d \
                -name ".git" \
                -execdir python -c 'import os; print(os.path.abspath("."))' \; \
                -execdir git pull \;
            

            当然,你可以在find 中添加其他带有-execdir 选项的git 命令,例如显示分支:

            find . \
                -maxdepth 2 -type d \
                -name ".git" \
                -execdir python -c 'import os; print(os.path.abspath("."))' \; \
                -execdir git branch \;
                -execdir git pull \;
            

            【讨论】:

            • 不知道为什么这个答案有净票数。这是最有帮助的,imo,因为我可以达到更大的最大深度,而不是继续访问非 git 存储库。我用它在我的“开发者”存储库中的所有存储库上运行 git gc。
            • 我也喜欢这个答案,因为它易于阅读并且很容易添加多个命令。
            猜你喜欢
            • 2018-05-09
            • 2020-10-11
            • 2013-01-15
            • 2011-11-29
            • 1970-01-01
            • 1970-01-01
            • 2019-07-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多