Android源码上传到GitLab自动化脚本

#!/bin/bash

# Android源码上传到GitLab自动化脚本
# 作者: Xupz
# 版本: 1.4
# 更新: 修复Git LFS选择逻辑,确保尊重用户选择

set -e  # 遇到错误立即退出

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# 日志函数
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step() { echo -e "${BLUE}[STEP]${NC} $1"; }
log_debug() { echo -e "${CYAN}[DEBUG]${NC} $1"; }

# 配置变量(用户可修改)
DEFAULT_GITLAB_URL="git@192.168.xx.xxx:xxxx.git" # 目标git服务地址
USER_NAME="Xupz"   # 你的用户名
USER_EMAIL="xxxx" # 你的邮箱地址
LARGE_FILE_THRESHOLD=1000M  # 大文件阈值(1000MB)

# 全局变量
PROJECT_TYPE=""  # 项目类型:android/regular
HAS_LARGE_FILES=false  # 是否包含大文件
USER_SELECTED_LFS=false  # 新增:记录用户LFS选择 [修复点1]

# 显示脚本信息
echo "================================================"
echo "  Git项目上传自动化脚本 v1.4"
echo "  修复Git LFS选择逻辑,尊重用户选择"
echo "================================================"
echo

# 函数:检查Git配置是否已设置
check_git_config() {
    log_step "检查Git配置..."
    
    local needs_config=false
    
    # 检查用户名配置
    if ! git config --global user.name &>/dev/null; then
        log_warn "全局Git用户名未配置"
        needs_config=true
    fi
    
    # 检查邮箱配置
    if ! git config --global user.email &>/dev/null; then
        log_warn "全局Git邮箱未配置"
        needs_config=true
    fi
    
    if [ "$needs_config" = false ]; then
        log_info "✅ Git配置检查通过"
        log_debug "用户名: $(git config --global user.name)"
        log_debug "邮箱: $(git config --global user.email)"
        return 0
    else
        return 1
    fi
}

# 函数:配置Git用户信息
setup_git_config() {
    log_step "配置Git用户信息..."
    
    # 只有在需要时才配置
    if ! git config --global user.name &>/dev/null; then
        git config --global user.name "$USER_NAME"
        log_info "已设置全局Git用户名: $USER_NAME"
    fi
    
    if ! git config --global user.email &>/dev/null; then
        git config --global user.email "$USER_EMAIL"
        log_info "已设置全局Git邮箱: $USER_EMAIL"
    fi
    
    # 配置大文件处理参数(始终配置)
    git config --global http.lowSpeedLimit 0
    git config --global http.lowSpeedTime 999999
    git config --global http.postBuffer 2097152000
    git config --global http.maxRequestBuffer 2097152000
    
    log_info "✅ Git配置完成"
}

# 函数:显示进度指示器(仅在分段提交阶段使用)
show_progress() {
    local pid=$1
    local message=$2
    
    echo -n "$message "
    
    # 显示进度点,直到进程结束
    while kill -0 "$pid" 2>/dev/null; do
        echo -n "."
        sleep 3
    done
    echo " ✅"
}

# 函数:检测项目类型和检查大文件
detect_project_type() {
    log_step "1. 检测项目类型和检查大文件..."
    
    local android_files=("build" "device" "frameworks" "packages" "system" "hardware")
    local found_count=0
    
    for dir in "${android_files[@]}"; do
        if [ -d "$dir" ]; then
            found_count=$((found_count + 1))
            log_debug "找到Android目录: $dir"
        fi
    done
    
    if [ $found_count -ge 3 ]; then
        PROJECT_TYPE="android"
        log_info "✅ 检测到Android源码项目 (找到 $found_count/6 个核心目录)"
    else
        PROJECT_TYPE="regular"
        log_info "📁 检测到普通项目"
    fi
    
    # 检查大文件
    check_large_files
}

# 函数:检查大文件 [修复点2:完善LFS选择逻辑]
check_large_files() {
    log_step "检查大文件..."
    
    log_info "扫描大于 $LARGE_FILE_THRESHOLD 的文件..."
    
    # 使用find命令查找大文件
    local large_files=$(find . -type f -size +$LARGE_FILE_THRESHOLD 2>/dev/null | head -10)
    local large_file_count=$(find . -type f -size +$LARGE_FILE_THRESHOLD 2>/dev/null | wc -l)
    
    if [ "$large_file_count" -gt 0 ]; then
        HAS_LARGE_FILES=true
        log_warn "⚠️  发现 $large_file_count 个大文件(> $LARGE_FILE_THRESHOLD)"
        
        # 显示前10个大文件
        if [ -n "$large_files" ]; then
            log_info "大文件列表:"
            echo "$large_files" | while read file; do
                local size=$(du -h "$file" 2>/dev/null | cut -f1)
                log_warn "  $file ($size)"
            done
        fi
        
        # 询问用户确认
        echo
        read -p "是否继续上传?大文件可能导致上传缓慢或失败 (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            log_info "操作已取消"
            exit 0
        fi
        
        # 询问是否使用Git LFS [修复点3:正确记录用户选择]
        read -p "是否设置Git LFS来管理大文件?(Y/n): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]] || [ -z "$REPLY" ]; then
            USER_SELECTED_LFS=true
            log_info "用户选择使用Git LFS管理大文件"
        else
            USER_SELECTED_LFS=false
            log_info "用户选择不使用Git LFS,大文件将通过普通Git管理"
        fi
    else
        log_info "✅ 未发现大文件"
        HAS_LARGE_FILES=false
        USER_SELECTED_LFS=false
    fi
}

# 函数:设置Git LFS
setup_git_lfs() {
    log_step "设置Git LFS..."
    
    if command -v git-lfs >/dev/null 2>&1; then
        log_info "安装Git LFS..."
        git lfs install
        
        # 添加常见的大文件类型到LFS
        git lfs track "*.zip"
        git lfs track "*.tar.gz"
        git lfs track "*.7z"
        git lfs track "*.rar"
        git lfs track "*.bin"
        git lfs track "*.img"
        git lfs track "*.iso"
        git lfs track "*.apk"
        
        log_info "✅ Git LFS配置完成"
    else
        log_warn "Git LFS未安装,跳过LFS设置"
        log_info "你可以手动安装: sudo apt-get install git-lfs"
        return 1
    fi
}

# 函数:获取用户配置
get_user_config() {
    log_step "2. 获取配置信息..."
    
    # 显示项目类型
    if [ "$PROJECT_TYPE" = "android" ]; then
        log_info "项目类型: Android源码"
    else
        log_info "项目类型: 普通项目"
    fi
    
    # 显示LFS选择状态 [修复点4:增加状态提示]
    if [ "$HAS_LARGE_FILES" = true ]; then
        if [ "$USER_SELECTED_LFS" = true ]; then
            log_info "Git LFS: 已启用"
        else
            log_info "Git LFS: 已禁用"
        fi
    fi
    
    # GitLab地址
    read -p "请输入GitLab仓库地址 [默认: $DEFAULT_GITLAB_URL]: " gitlab_url
    GITLAB_URL=${gitlab_url:-$DEFAULT_GITLAB_URL}
    
    # 用户名(仅在需要时询问)
    if ! check_git_config; then
        read -p "请输入Git用户名 [默认: $USER_NAME]: " user_name
        USER_NAME=${user_name:-$USER_NAME}
    else
        log_info "使用现有Git配置"
    fi
    
    # 分支名称
    read -p "请输入分支名称 [默认: main]: " branch_name
    BRANCH_NAME=${branch_name:-"main"}
    
    # 提交信息
    if [ "$PROJECT_TYPE" = "android" ]; then
        read -p "请输入初始提交信息 [默认: 初始提交: Android 13.0源码]: " commit_msg
        COMMIT_MSG=${commit_msg:-"初始提交: Android 13.0源码"}
    else
        read -p "请输入初始提交信息 [默认: 初始提交]: " commit_msg
        COMMIT_MSG=${commit_msg:-"初始提交"}
    fi
    
    log_info "配置完成:"
    log_info "  GitLab地址: $GITLAB_URL"
    log_info "  分支: $BRANCH_NAME"
    log_info "  项目类型: $PROJECT_TYPE"
    if [ "$HAS_LARGE_FILES" = true ]; then
        log_info "  Git LFS: $([ "$USER_SELECTED_LFS" = true ] && echo "启用" || echo "禁用")"
    fi
    echo
}

# 函数:优化清理原有的Git/Repo信息
cleanup_existing_git() {
    log_step "3. 彻底清理原有的Git和Repo信息..."
    
    # 1. 删除所有.git目录(包括根目录)
    log_info "清理所有.git目录..."
    find . -name ".git" -type d -exec rm -rf {} + 2>/dev/null
    local remaining_git=$(find . -name ".git" -type d 2>/dev/null | wc -l)
    if [ $remaining_git -eq 0 ]; then
        log_info "✅ 所有的.git目录已被删除。"
    else
        log_warn "⚠️  仍有 $remaining_git 个.git目录未能删除"
    fi
    
    # 2. 删除.repo目录(仅Android项目需要)
    if [ "$PROJECT_TYPE" = "android" ]; then
        log_info "清理.repo目录..."
        find . -name ".repo" -type d -exec rm -rf {} + 2>/dev/null
        local remaining_repo=$(find . -name ".repo" -type d 2>/dev/null | wc -l)
        if [ $remaining_repo -eq 0 ]; then
            log_info "✅ 所有的.repo目录已被删除。"
        else
            log_warn "⚠️  仍有 $remaining_repo 个.repo目录未能删除"
        fi
    fi
    
    # 3. 删除Git LFS配置文件和对象
    log_info "清理Git LFS配置..."
    find . -wholename "*/.git/lfs" -type d -exec rm -rf {} + 2>/dev/null
    local remaining_lfs=$(find . -wholename "*/.git/lfs" -type d 2>/dev/null | wc -l)
    if [ $remaining_lfs -eq 0 ]; then
        log_info "✅ 所有的Git LFS对象和配置已被删除。"
    else
        log_warn "⚠️  仍有 $remaining_lfs 个Git LFS目录未能删除"
    fi
    
    log_info "✅ 清理完成,已清理Git/Repo相关项目"
}

# 函数:初始化Git仓库(使用验证过的命令)[修复点5:修复LFS自动设置问题]
init_git_repo() {
    log_step "4. 初始化Git仓库..."
    
    # 删除现有的.git目录(如果有)
    if [ -d ".git" ]; then
        log_info "删除现有.git目录..."
        rm -rf .git
    fi
    
    # 初始化新仓库
    log_info "执行git init..."
    if git init; then
        log_info "✅ Git仓库初始化成功"
    else
        log_error "Git仓库初始化失败"
        exit 1
    fi
    
    # 配置Git用户信息(智能检查)
    setup_git_config
    
    # 修复:只有在用户明确选择使用LFS时才设置Git LFS
    if [ "$HAS_LARGE_FILES" = true ] && [ "$USER_SELECTED_LFS" = true ] && command -v git-lfs >/dev/null 2>&1; then
        log_info "应用用户选择的Git LFS设置..."
        setup_git_lfs
    elif [ "$HAS_LARGE_FILES" = true ] && [ "$USER_SELECTED_LFS" = true ] && ! command -v git-lfs >/dev/null 2>&1; then
        log_warn "Git LFS未安装,但用户选择了使用LFS,将跳过LFS设置"
    elif [ "$HAS_LARGE_FILES" = true ] && [ "$USER_SELECTED_LFS" = false ]; then
        log_info "用户选择不使用Git LFS,大文件将通过普通Git管理"
    fi
    
    # 添加远程仓库
    log_info "添加远程仓库: $GITLAB_URL"
    if git remote add origin "$GITLAB_URL"; then
        log_info "✅ 远程仓库配置成功"
    else
        log_error "远程仓库配置失败"
        exit 1
    fi
}

# 函数:Android项目的分阶段提交
android_staged_commit() {
    log_step "5. 分阶段提交Android源码..."
    
    echo "开始分阶段提交Android源码..."
    echo "此过程可能较长时间,请耐心等待..."
    echo
    
    # 第一阶段:核心框架文件
    log_info "第一阶段:提交核心框架文件..."
    (
        local added_files=false
        local stage1_dirs=("build" "device" "frameworks" "system")
        
        for dir in "${stage1_dirs[@]}"; do
            if [ -d "$dir" ]; then
                git add "$dir" 2>/dev/null && added_files=true
            fi
        done
        
        # 添加构建文件
        for pattern in "*.mk" "*.bp" "Android.mk" "Android.bp" "Makefile"; do
            if ls $pattern 1> /dev/null 2>&1; then
                git add $pattern 2>/dev/null && added_files=true
            fi
        done
        
        if [ "$added_files" = true ]; then
            git commit -m "${COMMIT_MSG} - 核心框架" >/dev/null 2>&1
        else
            exit 1
        fi
    ) &
    local stage1_pid=$!
    
    show_progress $stage1_pid "处理核心框架"
    
    if wait $stage1_pid; then
        log_info "✅ 第一阶段提交完成"
    else
        log_warn "第一阶段无文件可提交,跳过"
    fi
    
    # 第二阶段:系统组件
    log_info "第二阶段:提交系统组件..."
    (
        local added_files=false
        local stage2_dirs=("bionic" "bootable" "development" "hardware" "packages")
        
        for dir in "${stage2_dirs[@]}"; do
            if [ -d "$dir" ]; then
                git add "$dir" 2>/dev/null && added_files=true
            fi
        done
        
        if [ "$added_files" = true ]; then
            git commit -m "${COMMIT_MSG} - 系统组件" >/dev/null 2>&1
        else
            exit 1
        fi
    ) &
    local stage2_pid=$!
    
    show_progress $stage2_pid "处理系统组件"
    
    if wait $stage2_pid; then
        log_info "✅ 第二阶段提交完成"
    else
        log_warn "第二阶段无文件可提交,跳过"
    fi
    
    # 第三阶段:其他组件
    log_info "第三阶段:提交其他组件..."
    (
        local added_files=false
        local stage3_dirs=("art" "cts" "dalvik" "external" "libcore" "prebuilts" "sdk" "tools" "vendor")
        
        for dir in "${stage3_dirs[@]}"; do
            if [ -d "$dir" ]; then
                git add "$dir" 2>/dev/null && added_files=true
            fi
        done
        
        if [ "$added_files" = true ]; then
            git commit -m "${COMMIT_MSG} - 其他组件" >/dev/null 2>&1
        else
            exit 1
        fi
    ) &
    local stage3_pid=$!
    
    show_progress $stage3_pid "处理其他组件"
    
    if wait $stage3_pid; then
        log_info "✅ 第三阶段提交完成"
    else
        log_warn "第三阶段无文件可提交,跳过"
    fi
    
    # 最终阶段:剩余所有文件
    log_info "最终阶段:提交剩余文件..."
    log_warn "⚠️  此阶段可能需时较长,正在处理所有剩余文件..."
    
    # 显示文件计数
    local file_count=$(git status --porcelain 2>/dev/null | grep -c "??" || echo "0")
    if [ "$file_count" -gt 0 ]; then
        log_info "发现 $file_count 个未跟踪文件,正在添加..."
        
        (
            git add -A
            git commit -m "${COMMIT_MSG} - 剩余文件" >/dev/null 2>&1
        ) &
        local final_stage_pid=$!
        
        show_progress $final_stage_pid "处理剩余文件"
        
        if wait $final_stage_pid; then
            log_info "✅ 最终阶段提交完成"
        else
            log_warn "最终阶段提交失败"
        fi
    else
        log_warn "无剩余文件需要提交"
    fi
    
    # 显示提交统计
    local commit_count=$(git log --oneline 2>/dev/null | wc -l || echo "0")
    log_info "提交完成,共创建了 $commit_count 个提交"
}

# 函数:普通项目的一次性提交
regular_project_commit() {
    log_step "5. 提交普通项目..."
    
    echo "开始提交普通项目文件..."
    echo "此过程可能较长时间,请耐心等待..."
    echo
    
    # 显示文件统计
    local total_files=$(find . -type f -not -path "./.git/*" | wc -l)
    local total_size=$(du -sh . | cut -f1)
    
    log_info "项目统计:"
    log_info "  文件数量: $total_files"
    log_info "  总大小: $total_size"
    echo
    
    # 一次性添加所有文件
    log_info "添加所有文件到Git..."
    
    (
        git add -A
        git commit -m "$COMMIT_MSG" >/dev/null 2>&1
    ) &
    local commit_pid=$!
    
    show_progress $commit_pid "提交文件"
    
    if wait $commit_pid; then
        log_info "✅ 提交完成"
        
        # 显示提交信息
        local commit_hash=$(git rev-parse --short HEAD 2>/dev/null)
        local file_count=$(git ls-files | wc -l)
        log_info "提交哈希: $commit_hash"
        log_info "跟踪文件数: $file_count"
    else
        log_error "提交失败"
        exit 1
    fi
}

# 函数:推送到远程仓库
push_to_remote() {
    log_step "6. 推送到远程GitLab仓库..."
    
    # 重命名分支
    git branch -M "$BRANCH_NAME"
    
    echo "开始推送代码到远程仓库..."
    echo "这可能需要较长时间,请确保网络连接稳定..."
    echo
    
    # 显示推送估计信息
    local total_commits=$(git log --oneline | wc -l)
    local total_files=$(git ls-files | wc -l)
    
    log_info "统计信息:"
    log_info "  提交数量: $total_commits"
    log_info "  文件数量: $total_files"
    log_info "  项目类型: $PROJECT_TYPE"
    if [ "$HAS_LARGE_FILES" = true ]; then
        log_info "  Git LFS: $([ "$USER_SELECTED_LFS" = true ] && echo "启用" || echo "禁用")"
    fi
    echo
    
    # 执行推送(使用Git自带的进度显示)
    log_info "开始推送过程..."
    
    # 根据项目类型显示不同的提示
    if [ "$PROJECT_TYPE" = "android" ]; then
        log_warn "⚠️  Android项目较大,推送可能需时较长..."
    elif [ "$HAS_LARGE_FILES" = true ]; then
        if [ "$USER_SELECTED_LFS" = true ]; then
            log_warn "⚠️  项目包含大文件,已启用Git LFS管理..."
        else
            log_warn "⚠️  项目包含大文件,但未启用Git LFS,推送可能较慢..."
        fi
    fi
    
    # 直接使用Git的进度显示,不添加额外的进度指示
    if git push -u origin "$BRANCH_NAME" --progress; then
        log_info "✅ 代码推送成功!"
    else
        log_error "推送失败,请检查网络连接和仓库权限"
        exit 1
    fi
}

# 函数:验证推送结果
verify_push() {
    log_step "7. 验证推送结果..."
    
    # 获取最新状态
    if git fetch origin 2>/dev/null; then
        LOCAL_COMMIT=$(git rev-parse "$BRANCH_NAME" 2>/dev/null)
        REMOTE_COMMIT=$(git rev-parse "origin/$BRANCH_NAME" 2>/dev/null)
        
        if [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ] && [ -n "$LOCAL_COMMIT" ]; then
            log_info "✅ 验证成功:本地和远程代码一致"
        else
            log_warn "⚠️  本地和远程代码不一致或无法获取远程状态"
            log_info "本地提交: ${LOCAL_COMMIT:0:8}"
            log_info "远程提交: ${REMOTE_COMMIT:0:8}"
        fi
    else
        log_warn "无法获取远程状态,但推送可能已成功"
    fi
    
    # 显示最终仓库信息
    echo
    log_info "最终仓库状态:"
    git remote -v
    echo
    git branch -av
}

# 主执行函数
main() {
    log_step "开始项目上传流程..."
    
    # 设置异常处理
    trap 'log_error "脚本执行中断"; exit 1' INT TERM
    
    # 检测项目类型
    detect_project_type
    
    # 显示磁盘使用情况
    local dir_size=$(du -sh . | cut -f1)
    log_info "当前目录大小: $dir_size"
    
    # 确认操作
    read -p "是否继续上传流程?(y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        log_info "操作已取消"
        exit 0
    fi
    
    # 执行各个步骤
    get_user_config
    cleanup_existing_git
    init_git_repo
    
    # 根据项目类型选择提交策略
    if [ "$PROJECT_TYPE" = "android" ]; then
        android_staged_commit
    else
        regular_project_commit
    fi
    
    push_to_remote
    verify_push
    
    # 完成提示
    echo
    echo "================================================"
    log_info "🎉 项目上传完成!"
    log_info "仓库地址: $GITLAB_URL"
    log_info "分支: $BRANCH_NAME"
    log_info "项目类型: $PROJECT_TYPE"
    if [ "$HAS_LARGE_FILES" = true ]; then
        log_info "大文件: 是 (Git LFS: $([ "$USER_SELECTED_LFS" = true ] && echo "启用" || echo "禁用"))"
    else
        log_info "大文件: 否"
    fi
    log_info "总耗时: ${SECONDS} 秒"
    echo "================================================"
}

# 运行主函数
main "$@"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容