hg_git_fast_import/
single.rs

1use std::{path::Path, time::Instant};
2
3use indicatif::{HumanDuration, ProgressBar, ProgressStyle};
4use tracing::{debug, info};
5
6use crate::error::ErrorKind;
7
8use super::{config, env, MercurialRepo, RepositorySavedState, TargetRepository};
9
10pub fn hg2git<P: AsRef<Path>>(
11    repourl: P,
12    verify: bool,
13    git_active_branches: Option<usize>,
14    ignore_unknown_requirements: bool,
15    target: &mut dyn TargetRepository,
16    env: &env::Environment,
17    repository_config: &config::RepositoryConfig,
18) -> Result<(), ErrorKind> {
19    debug!("Config: {:?}", repository_config);
20    debug!("Environment: {:?}", env);
21
22    let repo = MercurialRepo::open_with_pull(
23        repourl.as_ref(),
24        repository_config,
25        ignore_unknown_requirements,
26        env,
27    )?;
28
29    if !repo.verify_heads(repository_config.allow_unnamed_heads)? {
30        return Err(ErrorKind::VerifyFailure("Verify heads failed".into()));
31    };
32
33    let tip = repo.changelog_len()?;
34
35    let to = if let Some(limit_high) = repository_config.limit_high {
36        tip.min(limit_high)
37    } else {
38        tip
39    };
40
41    debug!("Checking saved state...");
42    let mut brmap = repository_config.branches.clone().unwrap_or_default();
43    let mut counter: usize = 0;
44    let offset = repository_config.offset.unwrap_or(0);
45
46    let mut errors = None;
47    let from_tag = {
48        let (output, saved_state, default_branch) =
49            target.start_import(git_active_branches, repository_config.default_branch())?;
50
51        let (from, from_tag) = if let Some(saved_state) = saved_state.as_ref() {
52            match saved_state {
53                RepositorySavedState::OffsetedRevision(rev, from_tag) => {
54                    (rev - offset, from_tag - offset)
55                }
56            }
57        } else {
58            (0, 0)
59        };
60
61        info!("Exporting commits from {}", from);
62
63        let show_progress_bar = !env.cron;
64
65        let start = Instant::now();
66        let progress_bar = ProgressBar::new((to - from) as u64);
67        if show_progress_bar {
68            progress_bar.set_style(ProgressStyle::default_bar().template(
69                "{spinner:.green}[{elapsed_precise}] [{wide_bar:.cyan/blue}] {msg} ({eta})",
70            )?);
71        }
72        for mut changeset in repo.range(from..to) {
73            if show_progress_bar {
74                progress_bar.inc(1);
75                progress_bar.set_message(format!("{:6}/{}", changeset.revision.0, to));
76            }
77
78            match repo.export_commit(&mut changeset, counter, &mut brmap, output, &default_branch) {
79                Ok(progress) => counter = progress,
80                x => {
81                    errors = Some((x, changeset.revision.0));
82                    break;
83                }
84            }
85        }
86
87        if errors.is_none() {
88            if show_progress_bar {
89                progress_bar.finish_with_message(format!(
90                    "Repository {} [{from};{to}). Elapsed: {}",
91                    repourl.as_ref().to_str().unwrap(),
92                    HumanDuration(start.elapsed())
93                ));
94            }
95
96            counter = repo.export_tags(from_tag..to, counter, output)?;
97        }
98
99        from_tag
100    };
101
102    if let Some((error, at)) = errors {
103        if at > 0 {
104            let at = at as usize;
105            eprintln!("Import failed at {}", at);
106            info!("Saving last success state at {}...", at);
107            target.save_state(RepositorySavedState::OffsetedRevision(
108                at + offset,
109                from_tag + offset,
110            ))?;
111        }
112        error?;
113    }
114
115    info!("Issued {} commands", counter);
116    info!("Saving state...");
117    target.save_state(RepositorySavedState::OffsetedRevision(
118        to + offset,
119        to + offset,
120    ))?;
121
122    target.finish()?;
123
124    if verify {
125        target.verify(
126            repourl.as_ref().to_str().unwrap(),
127            repository_config.path_prefix.as_ref().map(|x| &x[..]),
128        )?;
129    }
130
131    Ok(())
132}