我编写了以下脚本来推送工作区中的所有更改,包括子模块和超级项目。然而,听起来有点奇怪,做我想做的事情是如此复杂。有没有更好的方法,我错过了?
#!/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
答案 0 :(得分:9)
您可以使用git submodule foreach
在每个子模块上运行任何所需的命令,例如
git submodule foreach git push origin master
请参阅:man git-submodule
。
答案 1 :(得分:4)
git1.7.11([ANNOUNCE] Git 1.7.11.rc1)提到:
“git push --recurse-submodules”学会了可选地查看绑定到超级项目的子模块的历史并推送它们 进行。
所以你可以使用:
git push --recurse-submodules=on-demand
答案 2 :(得分:0)
此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);
}
}
答案 3 :(得分:0)
如果您的子模块包含子模块,则命令 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 :(得分:0)
我最近编写的 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
}
至于操作函数,原始脚本确实提交了整个子模块,但在您的情况下,您可以插入如下推送命令
push_subm() {
local path="$1"
(cd -P "$path" && git push origin; )
}
现在,我们把所有的东西都放在一起
mapchanges filter_subm push_subm
请考虑查看原始脚本以了解有关如何组织 recursion 的详细信息。