Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  repo_lsc.rs

  Sprache: Rust
 

//! A command-line tool for creating large scale changes in the Android tree.
mod cli;
mod repo;

use crate::cli::args::{Cli, Commands, CommitArgs, Granularity, UploadArgs};
use crate::repo::runner::{
    add_all_git_files, add_git_file, command_exists, commit_git_files, find_project_for_branch,
    get_repo_status, git_checkout_new_branch, start_repo_branch, upload_repo_branch,
};
use clap::Parser;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    let cli = Cli::parse();

    if !command_exists("repo") {
        eprintln!("Error: 'repo' command not found. Please make sure it is in your PATH.");
        std::process::exit(1);
    }
    if !command_exists("git") {
        eprintln!("Error: 'git' command not found. Please make sure it is in your PATH.");
        std::process::exit(1);
    }

    match &cli.command {
        Commands::Commit(args) => handle_commit(args, cli.interactive, cli.verbose),
        Commands::Upload(args) => handle_upload(args, cli.interactive, cli.verbose),
    }
}

/// Prompts the user for confirmation to run a command.
pub fn prompt_for_confirmation(command: &str) -> bool {
    print!("About to run: `{}`. Continue? [y/N] ", command);
    io::stdout().flush().unwrap();
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");
    input.trim().eq_ignore_ascii_case("y")
}

fn handle_upload(args: &UploadArgs, interactive: bool, verbose: bool) {
    let android_build_top =
        env::var("ANDROID_BUILD_TOP").expect("ANDROID_BUILD_TOP environment variable not set.");

    if verbose {
        println!("[VERBOSE] ANDROID_BUILD_TOP is: {}", android_build_top);
    }

    if let Some(project_path) = find_project_for_branch(&args.branch, &android_build_top) {
        println!("Found branch {} in project: {}", args.branch, project_path);
        let full_project_path = Path::new(&android_build_top).join(project_path);
        upload_repo_branch(&args.topic, &args.hashtag, &full_project_path, interactive, args.yes);
    } else {
        eprintln!("Error: Branch '{}' not found in any project.", args.branch);
        std::process::exit(1);
    }
}

fn handle_commit(args: &CommitArgs, interactive: bool, verbose: bool) {
    let android_build_top =
        env::var("ANDROID_BUILD_TOP").expect("ANDROID_BUILD_TOP environment variable not set.");

    if verbose {
        println!("[VERBOSE] ANDROID_BUILD_TOP is: {}", android_build_top);
    }

    let (dirty_projects_on_branches, dirty_projects_no_branch) =
        get_dirty_projects(&android_build_top);

    if !dirty_projects_on_branches.is_empty() {
        eprintln!("Error: The following dirty projects are already on a branch. Please checkout the tip-of-tree branch before proceeding:");
        for (project, _) in dirty_projects_on_branches {
            eprintln!("  - {}", project);
        }
        std::process::exit(1);
    }

    if args.dry_run {
        println!("-- DRY RUN --\n");
        if dirty_projects_no_branch.is_empty() {
            println!("No dirty projects found.");
            return;
        }

        let mut total_commits = 0;
        let total_repos = dirty_projects_no_branch.len();
        let mut commits_per_repo = Vec::new();

        for (project_path, files) in &dirty_projects_no_branch {
            let num_commits = process_project(
                project_path,
                files,
                &android_build_top,
                args,
                "<dry-run>",
                interactive,
                true// dry_run = true
            );
            total_commits += num_commits;
            commits_per_repo.push((project_path.clone(), num_commits));
        }

        println!("\n-- SUMMARY --");
        println!("Total projects to be modified: {}", total_repos);
        println!("Total commits to be created: {}", total_commits);
        if !commits_per_repo.is_empty() {
            println!("\nCommits per project:");
            for (project_path, commits) in commits_per_repo {
                println!("  - {}: {}", project_path, commits);
            }
        }
    } else {
        let message = match &args.message {
            Some(m) => m.clone(),
            None => {
                let temp_file_path = Path::new(&android_build_top).join(".LSC_COMMIT_MSG.tmp");
                fs::write(&temp_file_path, "").expect("Unable to create temp commit message file.");

                let editor = env::var("EDITOR").unwrap_or_else(|_| "vim".to_string());
                Command::new(editor).arg(&temp_file_path).status().expect("Failed to open editor.");

                let commit_message = fs::read_to_string(&temp_file_path)
                    .expect("Unable to read commit message from file.");
                fs::remove_file(&temp_file_path)
                    .expect("Unable to remove temp commit message file.");

                if commit_message.trim().is_empty() {
                    eprintln!("Error: Commit message is empty. Aborting.");
                    std::process::exit(1);
                }
                commit_message
            }
        };

        for (project_path, files) in dirty_projects_no_branch {
            let _ = process_project(
                &project_path,
                &files,
                &android_build_top,
                args,
                &message,
                interactive,
                args.dry_run,
            );
        }
    }
}

fn get_dirty_projects(
    android_build_top: &str,
) -> (Vec<(String, Vec<String>)>, Vec<(String, Vec<String>)>) {
    let output_str = get_repo_status(android_build_top);
    let mut dirty_projects_on_branches = Vec::new();
    let mut dirty_projects_no_branch = Vec::new();
    let mut current_project: Option<(String, bool, Vec<String>)> = None; // (path, has_branch, files)

    for line in output_str.lines() {
        if line.starts_with("project ") {
            if let Some((path, has_branch, files)) = current_project.take() {
                if !files.is_empty() {
                    if has_branch {
                        dirty_projects_on_branches.push((path, files));
                    } else {
                        dirty_projects_no_branch.push((path, files));
                    }
                }
            }
            let parts: Vec<&str> = line.split_whitespace().collect();
            let path = parts[1].to_string();
            let has_branch = parts.len() > 2 && parts[2] == "(branch";
            current_project = Some((path, has_branch, Vec::new()));
        } else if line.trim().starts_with('-') {
            if let Some((_, _, files)) = &mut current_project {
                let file_path = line.split_whitespace().last().unwrap_or("").to_string();
                files.push(file_path);
            }
        }
    }
    if let Some((path, has_branch, files)) = current_project.take() {
        if !files.is_empty() {
            if has_branch {
                dirty_projects_on_branches.push((path, files));
            } else {
                dirty_projects_no_branch.push((path, files));
            }
        }
    }
    (dirty_projects_on_branches, dirty_projects_no_branch)
}

fn process_project(
    project_path: &str,
    files: &[String],
    android_build_top: &str,
    args: &CommitArgs,
    message: &str,
    interactive: bool,
    dry_run: bool,
) -> usize {
    if dry_run {
        println!("-- Project: {} --", project_path);
    } else {
        println!("Processing dirty project: {}", project_path);
    }

    let full_project_path = Path::new(android_build_top).join(project_path);
    let mut is_first_commit = true;
    let num_commits;

    match args.granularity {
        Granularity::Repo => {
            num_commits = 1;
            if dry_run {
                println!("Branch: {}", args.branch);
                println!("  Files to be committed together:");
                for file in files {
                    println!("    - {}", file);
                }
            } else {
                start_repo_branch(&args.branch, &full_project_path, interactive);
                add_all_git_files(&full_project_path, interactive);
                commit_git_files(message, &full_project_path, interactive);
                if !args.no_upload {
                    upload_repo_branch(
                        &args.topic,
                        &args.hashtag,
                        &full_project_path,
                        interactive,
                        args.yes,
                    );
                }
            }
        }
        Granularity::File => {
            num_commits = files.len();
            for file in files {
                let branch_name = format!("{}-{}", args.branch, file.replace('/'"-"));
                if dry_run {
                    println!("Branch: {}", branch_name);
                    println!("  File to be committed:");
                    println!("    - {}", file);
                } else {
                    if args.stack && !is_first_commit {
                        git_checkout_new_branch(&branch_name, &full_project_path, interactive);
                    } else {
                        start_repo_branch(&branch_name, &full_project_path, interactive);
                        is_first_commit = false;
                    }
                    add_git_file(file, &full_project_path, interactive);
                    commit_git_files(message, &full_project_path, interactive);
                    if !args.no_upload {
                        upload_repo_branch(
                            &args.topic,
                            &args.hashtag,
                            &full_project_path,
                            interactive,
                            args.yes,
                        );
                    }
                }
            }
        }
        Granularity::Dir => {
            let mut files_by_dir: HashMap<PathBuf, Vec<String>> = HashMap::new();
            for file in files {
                let parent_dir = Path::new(file).parent().unwrap_or_else(|| Path::new(""));
                files_by_dir.entry(parent_dir.to_path_buf()).or_default().push(file.clone());
            }

            num_commits = files_by_dir.len();
            for (dir, dir_files) in files_by_dir {
                let branch_name =
                    format!("{}-{}", args.branch, dir.to_string_lossy().replace('/'"-"));
                if dry_run {
                    println!("Branch: {}", branch_name);
                    println!(
                        "  Files to be committed together from directory '{}':",
                        dir.to_string_lossy()
                    );
                    for file in &dir_files {
                        println!("    - {}", file);
                    }
                } else {
                    if args.stack && !is_first_commit {
                        git_checkout_new_branch(&branch_name, &full_project_path, interactive);
                    } else {
                        start_repo_branch(&branch_name, &full_project_path, interactive);
                        is_first_commit = false;
                    }
                    for file in dir_files {
                        add_git_file(&file, &full_project_path, interactive);
                    }
                    commit_git_files(message, &full_project_path, interactive);
                    if !args.no_upload {
                        upload_repo_branch(
                            &args.topic,
                            &args.hashtag,
                            &full_project_path,
                            interactive,
                            args.yes,
                        );
                    }
                }
            }
        }
        Granularity::Package => {
            let mut files_by_package: HashMap<PathBuf, Vec<String>> = HashMap::new();
            for file in files {
                let full_file_path = full_project_path.join(file);
                if let Some(package_root) = find_package_root(&full_file_path, &full_project_path) {
                    let relative_package_root =
                        package_root.strip_prefix(&full_project_path).unwrap().to_path_buf();
                    files_by_package.entry(relative_package_root).or_default().push(file.clone());
                } else {
                    files_by_package.entry(PathBuf::new()).or_default().push(file.clone());
                }
            }

            num_commits = files_by_package.len();
            for (package, package_files) in files_by_package {
                let branch_name = if package.as_os_str().is_empty() {
                    format!("{}-root", args.branch)
                } else {
                    format!("{}-{}", args.branch, package.to_string_lossy().replace('/'"-"))
                };

                if dry_run {
                    println!("Branch: {}", branch_name);
                    let package_name = if package.as_os_str().is_empty() {
                        "<no package>".to_string()
                    } else {
                        format!("package '{}'", package.to_string_lossy())
                    };
                    println!("  Files to be committed together from {}:", package_name);
                    for file in &package_files {
                        println!("    - {}", file);
                    }
                } else {
                    if args.stack && !is_first_commit {
                        git_checkout_new_branch(&branch_name, &full_project_path, interactive);
                    } else {
                        start_repo_branch(&branch_name, &full_project_path, interactive);
                        is_first_commit = false;
                    }
                    for file in package_files {
                        add_git_file(&file, &full_project_path, interactive);
                    }
                    commit_git_files(message, &full_project_path, interactive);
                    if !args.no_upload {
                        upload_repo_branch(
                            &args.topic,
                            &args.hashtag,
                            &full_project_path,
                            interactive,
                            args.yes,
                        );
                    }
                }
            }
        }
    }
    num_commits
}

fn find_package_root(file_path: &Path, project_root: &Path) -> Option<PathBuf> {
    let mut current_path = file_path.parent()?;
    while current_path.starts_with(project_root) {
        if current_path.join("Android.bp").exists() || current_path.join("Android.mk").exists() {
            return Some(current_path.to_path_buf());
        }
        if current_path == project_root {
            break;
        }
        current_path = current_path.parent()?;
    }
    None
}

Messung V0.5 in Prozent
C=97 H=96 G=96

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-06-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik