【问题标题】:Pushing all submodules recursively递归推送所有子模块
【发布时间】:2014-07-11 09:31:08
【问题描述】:

我编写了以下脚本来推送工作区中的所有更改,包括子模块和超级项目。然而,这听起来有点奇怪,做我想做的事就是这么复杂。有没有更好的方法,我错过了?

#!/bin/bash

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
    exit
fi

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

cd "${SCRIPT_DIR}/../submodule1" 
git status
git add -A
git commit -m "$1"
git push origin master

cd "${SCRIPT_DIR}/../submodule2" 
git status
git add -A
git commit -m "$1"
git push origin master

cd "${SCRIPT_DIR}/../submodule3" 
git status
git add -A
git commit -m "$1"
git push origin master

printf "\n\nUpdating Super Project\n\n" 
cd .. 
git status
git add -A
git commit -m "All Submodules Updated - $1"
git push origin master

【问题讨论】:

    标签: git git-submodules


    【解决方案1】:

    您可以使用git submodule foreach 在每个子模块上运行任何所需的命令,例如

    git submodule foreach git push origin master
    

    请参阅:man git-submodule

    【讨论】:

      【解决方案2】:

      git1.7.11 ([ANNOUNCE] Git 1.7.11.rc1) 提到:

      “git push --recurse-submodules”学会了选择性地查看绑定到超级项目的子模块的历史并推送它们 出去。

      所以你可以使用:

      git push --recurse-submodules=on-demand
      

      【讨论】:

        【解决方案3】:

        如果您的子模块包含子模块,则命令git push --recurse-submodules=on-demand 不起作用。 (git 版本:2.20.1)

        在您的 ~/.gitconfig 文件中添加以下别名:

        [alias]
            push-all = "! find . -depth -name .git -exec dirname {} \\; 2> /dev/null | sort -n -r | xargs -I{} bash -c \"cd {}; git status | grep ahead > /dev/null && { echo '* Pushing: {}'; git push; }\""
        

        然后在你的父 git 文件夹中发出 git push-all

        说明

        • !:我们正在发出一个非 git 命令。
        • find . -depth -name .git -exec dirname {} \\; 2> /dev/null :查找所有子模块(和 git 存储库,这不会造成伤害)
        • | sort -n -r:按路径深度排序,最深的优先。
        • | xargs -I{} bash -c \":将目录传递给以下命令:
          • cd {};: cd 到目标目录。
          • git status | grep ahead > /dev/null:测试是否需要推送这个仓库。
          • && { echo '* Pushing: {}'; git push; }\"":通知推送。

        【讨论】:

          【解决方案4】:

          我最近编写的git-powercommit 脚本确实递归地推送子模块作为其工作流程的一部分。在底层,有一个map-like iterator,它使用git-status --porcelain=v2 来迭代存储库对象。这是它的定义:

          mapchanges() {(
            set -e -x
            local filter="$1"
            local commiter="$2"
            git status --no-renames --porcelain=v2 | \
            while read N XY sub mH mI mW hH hI path ; do
              $filter $N $XY $sub $mH $mI $mW $hH $hI "$path"
            done | \
            sort -u --reverse | \
            while read path; do
              $commiter "$path"
            done
          )}
          

          为了迭代子模块,您需要为其提供过滤器和操作回调函数。在您的情况下,过滤器功能可能是:

          filter_subm() {
            # Inputs are according to `git status --porcelain=v2` spec. The function
            # filters submodules which has changed commits.
            local sub="$3"; shift 8; local path="$1"
            case "$sub" in
              SC??) echo "$path" ;;
              *) ;;
            esac
          }
          

          至于动作功能,origina 脚本确实提交了整个子模块,但在您的情况下,您可以插入如下推送命令

          push_subm() {
            local path="$1"
            (cd -P "$path" && git push origin; )
          }
          

          现在,我们把所有东西都用这条线放在一起

          mapchanges filter_subm push_subm
          

          请考虑查看原始脚本以了解有关如何组织recursion 的详细信息。

          【讨论】:

            【解决方案5】:

            这个 c# 控制台应用程序会提示您更改每个子模块,并允许您轻松添加/提交/推送或运行自定义 git 命令:

            using System;
            using System.Collections.Generic;
            using System.Diagnostics;
            using System.IO;
            
            static class Program
            {
                static readonly Process Proc = new Process();
            
            static void Main(string[] args)
            {
                Console.WindowHeight = (int) (Console.LargestWindowHeight * .8f);
                var dir = Path.GetFullPath(args.Length == 1 ? args[0] : Directory.GetCurrentDirectory());
            
                foreach (var module in GetModules(dir))
                {
                    Console.WriteLine("Processing: " + module);
                    PrintDiff(module, out var untracked, out var mods);
            
                    if (!untracked && !mods)
                        continue;
            
                    while (true)
                    {
                        Console.WriteLine("Use \"c <msg>\" to commit and push" + (untracked ? ", \"a <msg>\" to also add untracked files" : "") + ", r to refresh.\nTo fix issues use git as normal, or cmd to enter prompt (exit to leave)" + ":");
            
                        var cmd = Console.ReadLine();
            
                        if (cmd.StartsWith("a "))
                            Run("git", "add .", module);
            
                        if (cmd.StartsWith("a ") || cmd.StartsWith("c "))
                        {
                            Run("git", "commit -a -m \"" + cmd.Substring(2) + "\"", module);
                            Run("git", "push", module);
                            break;
                        }
            
                        if (cmd.Trim() == "r")
                        {
                            PrintDiff(module, out untracked, out mods);
                            if (!untracked && !mods)
                                break;
                            continue;
                        }
            
                        try
                        {
                            var parts = cmd.Split(' ');
                            Run(parts[0], cmd.Substring(parts[0].Length), module);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                            Console.WriteLine();
                        }
                    }
            
                    Console.WriteLine();
                }
            
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
            }
            
            static void PrintDiff(string module, out bool untracked, out bool mods)
            {
                var u = false;
            
                Run("git", "ls-files . --exclude-standard --others", module, s =>
                {
                    u = true;
                    Console.WriteLine("Untracked file: " + s);
                });
            
                var m = false;
                Run("git", "diff --staged", module, t =>
                {
                    m = true;
                    if (t.StartsWith("diff --git"))
                        Console.WriteLine();
                    Console.WriteLine(t);
                });
            
                Run("git", "diff", module, t =>
                {
                    m = true;
                    if (t.StartsWith("diff --git"))
                        Console.WriteLine();
                    Console.WriteLine(t);
                });
            
                if (m)
                    Console.WriteLine();
            
                if (!u && !m)
                {
                    Console.WriteLine("No changes");
                    Console.WriteLine();
                }
            
                untracked = u;
                mods = m;
            }
            
            static List<string> GetModules(string dir)
            {
                var l = new List<string>();
                var paths = new Queue<string>();
                var modules = new List<string>();
                paths.Enqueue("");
            
                while (paths.Count > 0)
                {
                    var d = Path.Combine(dir, paths.Dequeue());
                    if (!File.Exists(Path.Combine(d, ".gitmodules")))
                        continue;
            
                    Run("git", "config --file .gitmodules -l", d, t =>
                    {
                        var parts = t.Split('=');
                        if (parts[0].EndsWith(".path"))
                            l.Add(Path.Combine(d, parts[1]));
                    });
            
                    foreach (var s in l)
                        paths.Enqueue(s);
                    modules.AddRange(l);
                    l.Clear();
                }
            
                modules.Reverse();
                return modules;
            }
            
            static void Run(string fileName, string arguments, string directory)
            {
                Run(fileName, arguments, directory, Console.WriteLine);
                Console.WriteLine();
            }
            
            static void Run(string fileName, string arguments, string directory, Action<string> a)
            {
                Proc.StartInfo = new ProcessStartInfo
                {
                    FileName = fileName,
                    Arguments = arguments,
                    RedirectStandardOutput = true,
                    WorkingDirectory = directory,
                    UseShellExecute = false
                };
                Proc.Start();
                string t;
                while ((t = Proc.StandardOutput.ReadLine()) != null)
                    a(t);
            }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-07-30
              • 2022-01-20
              • 1970-01-01
              • 2018-05-29
              • 2012-04-27
              • 1970-01-01
              • 2014-07-31
              • 1970-01-01
              相关资源
              最近更新 更多