Release 1.0.6: auto-unignore, macOS fix, --version

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mihailkudravcev
2026-07-20 16:59:55 +03:00
parent 9940ee5d31
commit ba1193a132
3 changed files with 248 additions and 40 deletions
+196 -39
View File
@@ -2,14 +2,16 @@
# ============================================================
# register-submodules.sh - Регистрация всех вложенных репозиториев как подмодулей
#
# Версия: 1.0.6
# Автор: Michael BAG <mk@p7net.ru>
# Репозиторий: https://git.p7net.ru/tools/git_man
#
# Использование:
# ./register-submodules.sh [--help] [--dry-run] [--force] [--ignore=all|dirty|untracked|none]
# ./register-submodules.sh [--help] [--version] [--dry-run] [--force] [--ignore=all|dirty|untracked|none]
#
# Опции:
# --help, -h Показать эту справку
# --version, -V Показать версию скрипта
# --dry-run Только показать, что будет сделано (без изменений)
# --force, -f Принудительно перерегистрировать подмодули
# --ignore=MODE Установить режим игнорирования для всех подмодулей
@@ -24,25 +26,33 @@
# Описание:
# Скрипт находит все вложенные репозитории (папки с .git),
# определяет их URL и регистрирует как подмодули в .gitmodules.
# Перед регистрацией снимает путь с .gitignore (если он там есть).
# Также позволяет настроить игнорирование состояния подмодулей.
# ============================================================
set -e # Остановка при ошибке
set -e # Остановка при ошибке (критичные шаги); ошибки регистрации ловятся явно
SCRIPT_VERSION="1.0.6"
# ============================================================
# Функция показа справки
# ============================================================
show_help() {
cat << 'EOF'
cat << EOF
============================================================
📦 register-submodules.sh - Регистрация вложенных репозиториев
📦 register-submodules.sh v${SCRIPT_VERSION} - Регистрация вложенных репозиториев
============================================================
ОПИСАНИЕ:
Находит вложенные репозитории (.git), регистрирует их как
подмодули. Перед регистрацией сам убирает путь из .gitignore.
ИСПОЛЬЗОВАНИЕ:
./register-submodules.sh [ОПЦИИ]
ОПЦИИ:
--help, -h Показать эту справку
--version, -V Показать версию скрипта
--dry-run Только показать, что будет сделано (без изменений)
--force, -f Принудительно перерегистрировать подмодули
(удаляет существующие и создаёт заново)
@@ -85,6 +95,11 @@ EOF
exit 0
}
show_version() {
echo "register-submodules.sh ${SCRIPT_VERSION}"
exit 0
}
# ============================================================
# Инициализация переменных
# ============================================================
@@ -104,6 +119,9 @@ while [[ $# -gt 0 ]]; do
--help|-h)
show_help
;;
--version|-V)
show_version
;;
--dry-run)
DRY_RUN=true
shift
@@ -214,6 +232,95 @@ fi
log_success "Окружение проверено"
# ============================================================
# Нормализация пути: ./foo/ -> foo
# ============================================================
normalize_path() {
local path="$1"
path="${path#./}"
path="${path%/}"
printf '%s' "$path"
}
# ============================================================
# Снять путь из .gitignore (точные правила вида /path/, path/, /path)
# ============================================================
unignore_path_from_gitignore() {
local path
path=$(normalize_path "$1")
local gitignore=".gitignore"
if [ ! -f "$gitignore" ]; then
return 0
fi
# Не игнорируется — ничего не делаем
if ! git check-ignore -q "$path" 2>/dev/null \
&& ! git check-ignore -q "$path/" 2>/dev/null \
&& ! git check-ignore -q "./$path" 2>/dev/null; then
return 0
fi
# Совпадает ли строка .gitignore с путём (с/без / и комментария в конце)
line_matches_path() {
local raw="$1"
local trimmed="${raw%%#*}"
# trim spaces
trimmed="${trimmed#"${trimmed%%[![:space:]]*}"}"
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
case "$trimmed" in
"/$path"|"/$path/"|"$path"|"$path/"|"./$path"|"./$path/")
return 0
;;
esac
return 1
}
if [ "$DRY_RUN" = true ]; then
echo " [DRY-RUN] убрать из .gitignore правила для: $path"
local lineno=0
while IFS= read -r line || [ -n "$line" ]; do
lineno=$((lineno + 1))
if line_matches_path "$line"; then
echo "$lineno:$line"
fi
done < "$gitignore"
return 0
fi
local tmp
tmp=$(mktemp)
local removed=0
while IFS= read -r line || [ -n "$line" ]; do
if line_matches_path "$line"; then
removed=$((removed + 1))
continue
fi
printf '%s\n' "$line"
done < "$gitignore" > "$tmp"
if [ "$removed" -gt 0 ]; then
mv "$tmp" "$gitignore"
log_success "Убран из .gitignore: $path ($removed правил)"
git add -- "$gitignore" 2>/dev/null || true
else
rm -f "$tmp"
# Путь всё ещё игнорируется более широким правилом — добавим исключение
if git check-ignore -q "$path" 2>/dev/null || git check-ignore -q "$path/" 2>/dev/null; then
log_warning "Широкое правило .gitignore; добавляю исключение: !/$path/"
printf '\n# auto-unignore for submodule\n!/%s/\n' "$path" >> "$gitignore"
git add -- "$gitignore" 2>/dev/null || true
fi
fi
# Если после правок путь всё ещё ignored — для submodule add нужен --force
if git check-ignore -q "$path" 2>/dev/null || git check-ignore -q "$path/" 2>/dev/null; then
return 1
fi
return 0
}
# ============================================================
# Функция установки ignore для подмодуля
# ============================================================
@@ -245,33 +352,82 @@ remove_submodule_ignore() {
return $?
}
# ============================================================
# Регистрация уже существующего вложенного репозитория
# ============================================================
register_existing_repo_as_submodule() {
local repo="$1"
local url="$2"
git config -f .gitmodules "submodule.$repo.path" "$repo"
git config -f .gitmodules "submodule.$repo.url" "$url"
git config "submodule.$repo.url" "$url"
git config "submodule.$repo.active" "true"
if ! git add -f -- .gitmodules "$repo" 2>/dev/null; then
return 1
fi
# Перенести .git вложенного репо в .git/modules родителя (если доступно)
git submodule absorbgitdirs -- "$repo" 2>/dev/null || true
return 0
}
# ============================================================
# Функция регистрации подмодуля
# ============================================================
register_submodule() {
local repo="$1"
local repo
local url="$2"
repo=$(normalize_path "$1")
local still_ignored=false
# Снять путь из .gitignore до регистрации
if ! unignore_path_from_gitignore "$repo"; then
still_ignored=true
log_debug "Путь $repo всё ещё в ignore — будет использован --force"
fi
if [ "$DRY_RUN" = true ]; then
echo " [DRY-RUN] git submodule add $url $repo"
if [ -e "$repo/.git" ]; then
echo " [DRY-RUN] зарегистрировать существующий репозиторий как подмодуль: $repo$url"
else
echo " [DRY-RUN] git submodule add --force $url $repo"
fi
return 0
fi
if [ "$FORCE" = true ]; then
git config --remove-section "submodule.$repo" 2>/dev/null || true
git config --file .gitmodules --remove-section "submodule.$repo" 2>/dev/null || true
git config -f .gitmodules --remove-section "submodule.$repo" 2>/dev/null || true
if [ -d "$repo" ] && [ -z "$(ls -A "$repo" 2>/dev/null)" ]; then
rm -rf "$repo"
fi
fi
if git config --file .gitmodules --get "submodule.$repo.url" >/dev/null 2>&1; then
if [ -f .gitmodules ] && git config -f .gitmodules --get "submodule.$repo.url" >/dev/null 2>&1; then
log_debug "Подмодуль $repo уже зарегистрирован"
return 2
fi
git submodule add "$url" "$repo" 2>/dev/null
return $?
# Каталог уже клонирован — регистрируем без повторного clone
if [ -e "$repo/.git" ]; then
if register_existing_repo_as_submodule "$repo" "$url"; then
return 0
fi
return 1
fi
# Пустой / отсутствующий путь — обычный submodule add
local add_args=()
if [ "$still_ignored" = true ] || [ "$FORCE" = true ]; then
add_args+=(--force)
fi
if git submodule add "${add_args[@]}" "$url" "$repo" 2>/dev/null; then
return 0
fi
return 1
}
# ============================================================
@@ -366,8 +522,15 @@ fi
log_step "Поиск вложенных репозиториев..."
TEMP_FILE=$(mktemp)
find . -type d -name ".git" -exec dirname {} \; 2>/dev/null | sort > "$TEMP_FILE"
sed -i '/^\.$/d' "$TEMP_FILE" 2>/dev/null || true
# Исключаем корень «.»; нормализуем ./path → path (portable, без sed -i)
find . -type d -name ".git" -exec dirname {} \; 2>/dev/null \
| while IFS= read -r p; do
[ "$p" = "." ] && continue
normalize_path "$p"
printf '\n'
done \
| grep -v '^$' \
| sort -u > "$TEMP_FILE"
if [ ! -s "$TEMP_FILE" ]; then
log_warning "Вложенные репозитории не найдены"
@@ -381,9 +544,9 @@ log_success "Найдено $TOTAL вложенных репозиториев"
if [ "$VERBOSE" = true ]; then
echo ""
echo -e "${CYAN}📋 Список найденных репозиториев:${NC}"
cat "$TEMP_FILE" | while read repo; do
while IFS= read -r repo; do
echo " 📁 $repo"
done
done < "$TEMP_FILE"
echo ""
fi
@@ -397,43 +560,38 @@ SKIPPED=0
ERRORS=0
NO_URL=0
while read repo; do
while IFS= read -r repo; do
[ -z "$repo" ] && continue
repo=$(normalize_path "$repo")
log_debug "Обработка: $repo"
if [ -d "$repo" ]; then
cd "$repo" 2>/dev/null
if [ $? -ne 0 ]; then
log_error "Не удалось перейти в папку: $repo"
ERRORS=$((ERRORS + 1))
cd - > /dev/null 2>/dev/null || true
continue
fi
url=$(git remote get-url origin 2>/dev/null)
cd - > /dev/null 2>/dev/null || true
if [ -z "$url" ]; then
if [ "$VERBOSE" = true ] || [ "$QUIET" != true ]; then
log_warning "Не удалось определить URL для: $repo (нет remote 'origin')"
fi
NO_URL=$((NO_URL + 1))
continue
fi
else
if [ ! -d "$repo" ]; then
log_warning "Папка не существует: $repo"
ERRORS=$((ERRORS + 1))
continue
fi
if ! url=$(git -C "$repo" remote get-url origin 2>/dev/null); then
url=""
fi
if [ -z "$url" ]; then
log_warning "Не удалось определить URL для: $repo (нет remote 'origin')"
NO_URL=$((NO_URL + 1))
continue
fi
if [ "$QUIET" != true ]; then
echo -e "${GREEN} Регистрация:${NC} $repo"
echo -e " ${CYAN}URL:${NC} $url"
fi
# set -e не должен прерывать цикл при ошибке одного подмодуля
set +e
register_submodule "$repo" "$url"
result=$?
set -e
case $result in
0)
@@ -442,8 +600,7 @@ while read repo; do
# Если задан режим игнорирования для всех подмодулей
if [ -n "$IGNORE_MODE" ]; then
set_submodule_ignore "$repo" "$IGNORE_MODE"
if [ $? -eq 0 ]; then
if set_submodule_ignore "$repo" "$IGNORE_MODE"; then
log_debug "Установлен ignore=$IGNORE_MODE для $repo"
fi
fi