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
+27
View File
@@ -7,6 +7,32 @@
--- ---
## [1.0.6] - 2026-07-20
### Добавлено
- 🧹 **Автоснятие пути из `.gitignore`** перед регистрацией подмодуля
- Удаляются точные правила вида `/path/`, `path/`, `/path`
- Если срабатывает более широкое правило — добавляется исключение `!/path/`
- 📦 Регистрация **уже существующих** вложенных клонов без повторного `clone`
(`gitlink` + `git submodule absorbgitdirs`)
- 🏷️ Константа версии `SCRIPT_VERSION` и опции `--version` / `-V`
### Исправлено
- 🍎 Совместимость с **macOS/BSD `sed`**: корень `.` больше не попадал в список
(раньше `sed -i` без аргумента backup падал тихо, скрипт пытался
зарегистрировать `.` и обрывался из‑за `set -e`)
- Ошибка одного подмодуля больше не останавливает весь цикл регистрации
- Нормализация путей `./foo``foo`
### Документация
- 📖 Обновлён **README.md**
- 📝 Обновлён **CHANGELOG.md**
---
## [1.1.0] - 2025-07-19 ## [1.1.0] - 2025-07-19
### Добавлено ### Добавлено
@@ -107,6 +133,7 @@
| Версия | Дата | Изменения | | Версия | Дата | Изменения |
|--------|------|-----------| |--------|------|-----------|
| **1.0.6** | 2026-07-20 | Автоснятие из `.gitignore`, fix macOS sed, `--version` |
| **1.1.0** | 2025-07-19 | Добавлена настройка игнорирования подмодулей | | **1.1.0** | 2025-07-19 | Добавлена настройка игнорирования подмодулей |
| **1.0.0** | 2025-07-19 | Первый стабильный релиз | | **1.0.0** | 2025-07-19 | Первый стабильный релиз |
| **0.1.0** | 2025-07-19 | Предварительная версия (прототип) | | **0.1.0** | 2025-07-19 | Предварительная версия (прототип) |
+25 -1
View File
@@ -1,5 +1,7 @@
# Git Management Tools (`git_man`) # Git Management Tools (`git_man`)
**Версия:** 1.0.6
Набор скриптов и утилит для управления Git-репозиториями, работы с подмодулями и автоматизации повседневных задач. Набор скриптов и утилит для управления Git-репозиториями, работы с подмодулями и автоматизации повседневных задач.
--- ---
@@ -12,6 +14,7 @@
- [Установка](#установка) - [Установка](#установка)
- [Использование](#использование) - [Использование](#использование)
- [Требования](#требования) - [Требования](#требования)
- [История версий](#история-версий)
- [Автор](#автор) - [Автор](#автор)
- [Лицензия](#лицензия) - [Лицензия](#лицензия)
@@ -23,6 +26,7 @@
Основные задачи: Основные задачи:
- Автоматическая регистрация вложенных репозиториев как подмодулей - Автоматическая регистрация вложенных репозиториев как подмодулей
- Снятие путей из `.gitignore` при регистрации
- Централизованное управление множеством репозиториев - Централизованное управление множеством репозиториев
- Настройка игнорирования состояния подмодулей в основном репозитории - Настройка игнорирования состояния подмодулей в основном репозитории
- Автоматизация рутинных операций - Автоматизация рутинных операций
@@ -34,7 +38,7 @@
### `register-submodules.sh` ### `register-submodules.sh`
**Описание:** **Описание:**
Автоматически находит все вложенные Git-репозитории в текущей папке и регистрирует их как подмодули в `.gitmodules`. Поддерживает настройку игнорирования состояния подмодулей. Автоматически находит все вложенные Git-репозитории в текущей папке и регистрирует их как подмодули в `.gitmodules`. Перед регистрацией снимает путь из `.gitignore` (точные правила или исключение `!/path/`). Уже существующие клоны регистрируются без повторного clone. Поддерживает настройку игнорирования состояния подмодулей.
**Использование:** **Использование:**
```bash ```bash
@@ -46,6 +50,7 @@
| Опция | Описание | | Опция | Описание |
|-------|----------| |-------|----------|
| `--help`, `-h` | Показать справку | | `--help`, `-h` | Показать справку |
| `--version`, `-V` | Показать версию скрипта (`1.0.6`) |
| `--dry-run` | Только показать, что будет сделано (без изменений) | | `--dry-run` | Только показать, что будет сделано (без изменений) |
| `--force`, `-f` | Принудительная перерегистрация подмодулей | | `--force`, `-f` | Принудительная перерегистрация подмодулей |
| `--ignore=MODE` | Установить режим игнорирования для ВСЕХ подмодулей | | `--ignore=MODE` | Установить режим игнорирования для ВСЕХ подмодулей |
@@ -176,6 +181,25 @@ git submodule add https://git.p7net.ru/tools/git_man.git tools/git_man
--- ---
## 📌 История версий
Текущая версия: **1.0.6** (2026-07-20).
Кратко по релизам — см. [CHANGELOG.md](./CHANGELOG.md).
| Версия | Дата | Суть |
|--------|------|------|
| **1.0.6** | 2026-07-20 | Автоснятие из `.gitignore`, fix macOS, `--version` |
| **1.1.0** | 2025-07-19 | Настройка `ignore` для подмодулей |
| **1.0.0** | 2025-07-19 | Первый стабильный релиз |
Проверка версии:
```bash
./register-submodules.sh --version
```
---
## 👤 Автор ## 👤 Автор
**Michael BAG** **Michael BAG**
+196 -39
View File
@@ -2,14 +2,16 @@
# ============================================================ # ============================================================
# register-submodules.sh - Регистрация всех вложенных репозиториев как подмодулей # register-submodules.sh - Регистрация всех вложенных репозиториев как подмодулей
# #
# Версия: 1.0.6
# Автор: Michael BAG <mk@p7net.ru> # Автор: Michael BAG <mk@p7net.ru>
# Репозиторий: https://git.p7net.ru/tools/git_man # Репозиторий: 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 Показать эту справку # --help, -h Показать эту справку
# --version, -V Показать версию скрипта
# --dry-run Только показать, что будет сделано (без изменений) # --dry-run Только показать, что будет сделано (без изменений)
# --force, -f Принудительно перерегистрировать подмодули # --force, -f Принудительно перерегистрировать подмодули
# --ignore=MODE Установить режим игнорирования для всех подмодулей # --ignore=MODE Установить режим игнорирования для всех подмодулей
@@ -24,25 +26,33 @@
# Описание: # Описание:
# Скрипт находит все вложенные репозитории (папки с .git), # Скрипт находит все вложенные репозитории (папки с .git),
# определяет их URL и регистрирует как подмодули в .gitmodules. # определяет их URL и регистрирует как подмодули в .gitmodules.
# Перед регистрацией снимает путь с .gitignore (если он там есть).
# Также позволяет настроить игнорирование состояния подмодулей. # Также позволяет настроить игнорирование состояния подмодулей.
# ============================================================ # ============================================================
set -e # Остановка при ошибке set -e # Остановка при ошибке (критичные шаги); ошибки регистрации ловятся явно
SCRIPT_VERSION="1.0.6"
# ============================================================ # ============================================================
# Функция показа справки # Функция показа справки
# ============================================================ # ============================================================
show_help() { show_help() {
cat << 'EOF' cat << EOF
============================================================ ============================================================
📦 register-submodules.sh - Регистрация вложенных репозиториев 📦 register-submodules.sh v${SCRIPT_VERSION} - Регистрация вложенных репозиториев
============================================================ ============================================================
ОПИСАНИЕ:
Находит вложенные репозитории (.git), регистрирует их как
подмодули. Перед регистрацией сам убирает путь из .gitignore.
ИСПОЛЬЗОВАНИЕ: ИСПОЛЬЗОВАНИЕ:
./register-submodules.sh [ОПЦИИ] ./register-submodules.sh [ОПЦИИ]
ОПЦИИ: ОПЦИИ:
--help, -h Показать эту справку --help, -h Показать эту справку
--version, -V Показать версию скрипта
--dry-run Только показать, что будет сделано (без изменений) --dry-run Только показать, что будет сделано (без изменений)
--force, -f Принудительно перерегистрировать подмодули --force, -f Принудительно перерегистрировать подмодули
(удаляет существующие и создаёт заново) (удаляет существующие и создаёт заново)
@@ -85,6 +95,11 @@ EOF
exit 0 exit 0
} }
show_version() {
echo "register-submodules.sh ${SCRIPT_VERSION}"
exit 0
}
# ============================================================ # ============================================================
# Инициализация переменных # Инициализация переменных
# ============================================================ # ============================================================
@@ -104,6 +119,9 @@ while [[ $# -gt 0 ]]; do
--help|-h) --help|-h)
show_help show_help
;; ;;
--version|-V)
show_version
;;
--dry-run) --dry-run)
DRY_RUN=true DRY_RUN=true
shift shift
@@ -214,6 +232,95 @@ fi
log_success "Окружение проверено" 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 для подмодуля # Функция установки ignore для подмодуля
# ============================================================ # ============================================================
@@ -245,33 +352,82 @@ remove_submodule_ignore() {
return $? 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() { register_submodule() {
local repo="$1" local repo
local url="$2" 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 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 return 0
fi fi
if [ "$FORCE" = true ]; then if [ "$FORCE" = true ]; then
git config --remove-section "submodule.$repo" 2>/dev/null || true 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 if [ -d "$repo" ] && [ -z "$(ls -A "$repo" 2>/dev/null)" ]; then
rm -rf "$repo" rm -rf "$repo"
fi fi
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 уже зарегистрирован" log_debug "Подмодуль $repo уже зарегистрирован"
return 2 return 2
fi fi
git submodule add "$url" "$repo" 2>/dev/null # Каталог уже клонирован — регистрируем без повторного clone
return $? 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 "Поиск вложенных репозиториев..." log_step "Поиск вложенных репозиториев..."
TEMP_FILE=$(mktemp) TEMP_FILE=$(mktemp)
find . -type d -name ".git" -exec dirname {} \; 2>/dev/null | sort > "$TEMP_FILE" # Исключаем корень «.»; нормализуем ./path → path (portable, без sed -i)
sed -i '/^\.$/d' "$TEMP_FILE" 2>/dev/null || true 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 if [ ! -s "$TEMP_FILE" ]; then
log_warning "Вложенные репозитории не найдены" log_warning "Вложенные репозитории не найдены"
@@ -381,9 +544,9 @@ log_success "Найдено $TOTAL вложенных репозиториев"
if [ "$VERBOSE" = true ]; then if [ "$VERBOSE" = true ]; then
echo "" echo ""
echo -e "${CYAN}📋 Список найденных репозиториев:${NC}" echo -e "${CYAN}📋 Список найденных репозиториев:${NC}"
cat "$TEMP_FILE" | while read repo; do while IFS= read -r repo; do
echo " 📁 $repo" echo " 📁 $repo"
done done < "$TEMP_FILE"
echo "" echo ""
fi fi
@@ -397,43 +560,38 @@ SKIPPED=0
ERRORS=0 ERRORS=0
NO_URL=0 NO_URL=0
while read repo; do while IFS= read -r repo; do
[ -z "$repo" ] && continue [ -z "$repo" ] && continue
repo=$(normalize_path "$repo")
log_debug "Обработка: $repo" log_debug "Обработка: $repo"
if [ -d "$repo" ]; then 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
log_warning "Папка не существует: $repo" log_warning "Папка не существует: $repo"
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
continue continue
fi 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 if [ "$QUIET" != true ]; then
echo -e "${GREEN} Регистрация:${NC} $repo" echo -e "${GREEN} Регистрация:${NC} $repo"
echo -e " ${CYAN}URL:${NC} $url" echo -e " ${CYAN}URL:${NC} $url"
fi fi
# set -e не должен прерывать цикл при ошибке одного подмодуля
set +e
register_submodule "$repo" "$url" register_submodule "$repo" "$url"
result=$? result=$?
set -e
case $result in case $result in
0) 0)
@@ -442,8 +600,7 @@ while read repo; do
# Если задан режим игнорирования для всех подмодулей # Если задан режим игнорирования для всех подмодулей
if [ -n "$IGNORE_MODE" ]; then if [ -n "$IGNORE_MODE" ]; then
set_submodule_ignore "$repo" "$IGNORE_MODE" if set_submodule_ignore "$repo" "$IGNORE_MODE"; then
if [ $? -eq 0 ]; then
log_debug "Установлен ignore=$IGNORE_MODE для $repo" log_debug "Установлен ignore=$IGNORE_MODE для $repo"
fi fi
fi fi