diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0f29b..4a0228a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.3.0 (unreleased) + +### Breaking + +- Unbranched branch will not be created if `unbranched` is not specified in the + conversion parameters file. + +### Other + +- MSRV has been bumped to 1.82. + ## 0.2.1 (2024-09-09) ### Changed diff --git a/Cargo.lock b/Cargo.lock index 429836f..a1b8c81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2230,7 +2230,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "svn2git" -version = "0.2.1" +version = "0.3.0-pre" dependencies = [ "bzip2", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 27b8364..d9c42e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "svn2git" -version = "0.2.1" +version = "0.3.0-pre" authors = ["Eduardo Sánchez Muñoz "] edition = "2021" rust-version = "1.82" @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" keywords = ["converter", "git", "repository", "subversion", "svn"] categories = ["development-tools"] exclude = ["/.github", "/book", "/ci", ".gitignore"] -publish = true +publish = false [[test]] name = "convert" diff --git a/book/src/documentation/conv-params.md b/book/src/documentation/conv-params.md index 135551b..a874815 100644 --- a/book/src/documentation/conv-params.md +++ b/book/src/documentation/conv-params.md @@ -78,10 +78,11 @@ head = "" ``` -* `unbranched-name` (default: `unbranched`) +* `unbranched-name` Specifies the name of the Git branch where everything that is not part of a - branch or a tag (as specified with `branches` or `tags`) will be placed. + branch or a tag (as specified with `branches` or `tags`) will be placed. If + not specified, these files will be discarded. Example diff --git a/convert-tests/defs.rs b/convert-tests/defs.rs index f8d4f9f..c4198b9 100644 --- a/convert-tests/defs.rs +++ b/convert-tests/defs.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] @@ -23,6 +23,8 @@ pub(crate) struct Test { pub(crate) logs: Option, #[serde(rename = "git-tags", default = "Vec::new")] pub(crate) git_tags: Vec, + #[serde(rename = "git-refs")] + pub(crate) git_refs: Option>, #[serde(rename = "git-revs", default = "Vec::new")] pub(crate) git_revs: Vec, } diff --git a/convert-tests/test.rs b/convert-tests/test.rs index af160b2..33196b2 100644 --- a/convert-tests/test.rs +++ b/convert-tests/test.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::io::Write as _; use std::path::{Path, PathBuf}; @@ -102,6 +102,29 @@ pub(crate) fn run_test(test_path: &Path) -> Result<(), String> { let git_repo = gix::open(&git_repo_path) .map_err(|e| format!("failed to open git repository {git_repo_path:?}: {e}"))?; + if let Some(ref expected_git_refs) = test_def.git_refs { + let mut actual_git_refs = BTreeSet::new(); + + let refs = git_repo + .refs + .iter() + .map_err(|e| format!("failed to get git refs: {e}"))?; + let refs_iter = refs + .all() + .map_err(|e| format!("failed to get git refs: {e}"))?; + for ref_ in refs_iter { + let ref_ = ref_.map_err(|e| format!("failed to get git refs: {e}"))?; + actual_git_refs.insert(ref_.name.to_string()); + } + + if actual_git_refs != *expected_git_refs { + return Err(format!( + "unexpected git refs:\nactual: {:?}\nexpected: {:?}", + actual_git_refs, expected_git_refs, + )); + } + } + for git_tag in test_def.git_tags.iter() { check_git_tag(&git_repo, git_tag) .map_err(|e| format!("tag {:?} check failed: {e}", git_tag.tag))?; diff --git a/convert-tests/tests/branches/branched-discard-unbranched.yaml b/convert-tests/tests/branches/branched-discard-unbranched.yaml new file mode 100644 index 0000000..e755418 --- /dev/null +++ b/convert-tests/tests/branches/branched-discard-unbranched.yaml @@ -0,0 +1,45 @@ +svn-revs: + - props: + svn:log: create trunk + nodes: + - path: trunk + kind: dir + action: add + - path: trunk/x + kind: file + action: add + text: "x\n" + - props: + svn:log: create unbranched + nodes: + - path: y + kind: file + action: add + text: "y\n" + - path: directory + kind: dir + action: add + - path: directory/z + kind: file + action: add + text: "z\n" + +conv-params: | + branches = ["trunk"] + +logs: | + D svn2git::convert::stage1: importing SVN revision 1 + D svn2git::convert::stage1: creating branch "trunk" with new directory + D svn2git::convert::stage1: importing SVN revision 2 + D svn2git::convert::stage1: committed on unbranched branch + +git-refs: + - refs/heads/trunk + +git-revs: + - rev: trunk~0 + parents: [] + tree: + x: + type: normal + data: "x\n" diff --git a/convert-tests/tests/branches/branched-unbranched.yaml b/convert-tests/tests/branches/branched-unbranched.yaml index cc3a692..87d70e5 100644 --- a/convert-tests/tests/branches/branched-unbranched.yaml +++ b/convert-tests/tests/branches/branched-unbranched.yaml @@ -26,6 +26,7 @@ svn-revs: conv-params: | branches = ["trunk"] + unbranched-name = "unbranched" logs: | D svn2git::convert::stage1: importing SVN revision 1 @@ -33,6 +34,10 @@ logs: | D svn2git::convert::stage1: importing SVN revision 2 D svn2git::convert::stage1: committed on unbranched branch +git-refs: + - refs/heads/trunk + - refs/heads/unbranched + git-revs: - rev: trunk~0 parents: [] diff --git a/convert-tests/tests/branches/copy-nonbranch-to-one.yaml b/convert-tests/tests/branches/copy-nonbranch-to-one.yaml index 258f916..98ca2c1 100644 --- a/convert-tests/tests/branches/copy-nonbranch-to-one.yaml +++ b/convert-tests/tests/branches/copy-nonbranch-to-one.yaml @@ -28,6 +28,7 @@ conv-params: | rename-branches."branches/*" = "*" head = "branches/b1" + unbranched-name = "unbranched" logs: | D svn2git::convert::stage1: importing SVN revision 3 diff --git a/convert-tests/tests/branches/copy-nonbranch-to-parent.yaml b/convert-tests/tests/branches/copy-nonbranch-to-parent.yaml index abd5aed..9e635e8 100644 --- a/convert-tests/tests/branches/copy-nonbranch-to-parent.yaml +++ b/convert-tests/tests/branches/copy-nonbranch-to-parent.yaml @@ -30,6 +30,7 @@ svn-revs: conv-params: | branches = ["branches2/*"] head = "branches2/b1" + unbranched-name = "unbranched" logs: | D svn2git::convert::stage1: importing SVN revision 2 diff --git a/convert-tests/tests/branches/copy-one-to-nonbranch.yaml b/convert-tests/tests/branches/copy-one-to-nonbranch.yaml index 7e8e6b0..6683432 100644 --- a/convert-tests/tests/branches/copy-one-to-nonbranch.yaml +++ b/convert-tests/tests/branches/copy-one-to-nonbranch.yaml @@ -26,6 +26,7 @@ svn-revs: conv-params: | branches = ["trunk"] rename-branches."trunk" = "master" + unbranched-name = "unbranched" logs: | D svn2git::convert::stage1: importing SVN revision 3 diff --git a/convert-tests/tests/branches/copy-parent-to-nonbranch.yaml b/convert-tests/tests/branches/copy-parent-to-nonbranch.yaml index a71529c..0c95905 100644 --- a/convert-tests/tests/branches/copy-parent-to-nonbranch.yaml +++ b/convert-tests/tests/branches/copy-parent-to-nonbranch.yaml @@ -30,6 +30,7 @@ svn-revs: conv-params: | branches = ["branches1/*"] head = "branches1/b1" + unbranched-name = "unbranched" logs: | D svn2git::convert::stage1: importing SVN revision 2 diff --git a/convert-tests/tests/commit-meta/custom.yaml b/convert-tests/tests/commit-meta/custom.yaml index 3d1cea1..a75e7e5 100644 --- a/convert-tests/tests/commit-meta/custom.yaml +++ b/convert-tests/tests/commit-meta/custom.yaml @@ -27,6 +27,7 @@ svn-revs: conv-params: | branches = ["trunk"] + unbranched-name = "unbranched" user-map-file = "user-map.txt" user-fallback-template = "{{ svn_author }} " commit-msg-template = """ diff --git a/convert-tests/tests/commit-meta/default.yaml b/convert-tests/tests/commit-meta/default.yaml index abff760..afe11b3 100644 --- a/convert-tests/tests/commit-meta/default.yaml +++ b/convert-tests/tests/commit-meta/default.yaml @@ -36,6 +36,7 @@ svn-revs: conv-params: | branches = ["trunk"] + unbranched-name = "unbranched" git-revs: - rev: trunk~2 diff --git a/convert-tests/tests/delete-files-unbranched.yaml b/convert-tests/tests/delete-files-unbranched.yaml index 6be14e3..f05b684 100644 --- a/convert-tests/tests/delete-files-unbranched.yaml +++ b/convert-tests/tests/delete-files-unbranched.yaml @@ -44,6 +44,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" delete-files = ["**/y"] git-revs: diff --git a/convert-tests/tests/head/unbranched.yaml b/convert-tests/tests/head/unbranched.yaml index 76353c5..443bcb8 100644 --- a/convert-tests/tests/head/unbranched.yaml +++ b/convert-tests/tests/head/unbranched.yaml @@ -16,6 +16,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: HEAD diff --git a/convert-tests/tests/ignore/disabled-unbranched.yaml b/convert-tests/tests/ignore/disabled-unbranched.yaml index ab98a6d..d24d68b 100644 --- a/convert-tests/tests/ignore/disabled-unbranched.yaml +++ b/convert-tests/tests/ignore/disabled-unbranched.yaml @@ -70,6 +70,7 @@ svn-revs: conv-params: | branches = ["trunk"] + unbranched-name = "unbranched" generate-gitignore = false git-revs: diff --git a/convert-tests/tests/ignore/unbranched.yaml b/convert-tests/tests/ignore/unbranched.yaml index eafb440..7b2085d 100644 --- a/convert-tests/tests/ignore/unbranched.yaml +++ b/convert-tests/tests/ignore/unbranched.yaml @@ -70,6 +70,7 @@ svn-revs: conv-params: | branches = ["trunk"] + unbranched-name = "unbranched" git-revs: - rev: trunk~0 diff --git a/convert-tests/tests/ops-unbranched/basic.yaml b/convert-tests/tests/ops-unbranched/basic.yaml index cc771cc..b8d570e 100644 --- a/convert-tests/tests/ops-unbranched/basic.yaml +++ b/convert-tests/tests/ops-unbranched/basic.yaml @@ -16,6 +16,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~0 diff --git a/convert-tests/tests/ops-unbranched/change-file-exec.yaml b/convert-tests/tests/ops-unbranched/change-file-exec.yaml index ccc11ee..62e2979 100644 --- a/convert-tests/tests/ops-unbranched/change-file-exec.yaml +++ b/convert-tests/tests/ops-unbranched/change-file-exec.yaml @@ -48,6 +48,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~5 diff --git a/convert-tests/tests/ops-unbranched/copy-dir.yaml b/convert-tests/tests/ops-unbranched/copy-dir.yaml index 96e9c05..4230c1a 100644 --- a/convert-tests/tests/ops-unbranched/copy-dir.yaml +++ b/convert-tests/tests/ops-unbranched/copy-dir.yaml @@ -19,6 +19,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/copy-file.yaml b/convert-tests/tests/ops-unbranched/copy-file.yaml index 29a91ba..c42778d 100644 --- a/convert-tests/tests/ops-unbranched/copy-file.yaml +++ b/convert-tests/tests/ops-unbranched/copy-file.yaml @@ -16,6 +16,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/copy-modify-file.yaml b/convert-tests/tests/ops-unbranched/copy-modify-file.yaml index 516fc37..20e4baf 100644 --- a/convert-tests/tests/ops-unbranched/copy-modify-file.yaml +++ b/convert-tests/tests/ops-unbranched/copy-modify-file.yaml @@ -17,6 +17,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/copy-replace-file.yaml b/convert-tests/tests/ops-unbranched/copy-replace-file.yaml index 5b7ab6c..3668b67 100644 --- a/convert-tests/tests/ops-unbranched/copy-replace-file.yaml +++ b/convert-tests/tests/ops-unbranched/copy-replace-file.yaml @@ -20,6 +20,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/copy-replace-modify-file.yaml b/convert-tests/tests/ops-unbranched/copy-replace-modify-file.yaml index 525dfa7..78b3fc7 100644 --- a/convert-tests/tests/ops-unbranched/copy-replace-modify-file.yaml +++ b/convert-tests/tests/ops-unbranched/copy-replace-modify-file.yaml @@ -21,6 +21,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/delete-dir.yaml b/convert-tests/tests/ops-unbranched/delete-dir.yaml index 07921ea..6a8ae87 100644 --- a/convert-tests/tests/ops-unbranched/delete-dir.yaml +++ b/convert-tests/tests/ops-unbranched/delete-dir.yaml @@ -18,6 +18,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/delete-file.yaml b/convert-tests/tests/ops-unbranched/delete-file.yaml index d521835..a07e0cf 100644 --- a/convert-tests/tests/ops-unbranched/delete-file.yaml +++ b/convert-tests/tests/ops-unbranched/delete-file.yaml @@ -18,6 +18,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/file-to-symlink.yaml b/convert-tests/tests/ops-unbranched/file-to-symlink.yaml index d9a55a2..0c99857 100644 --- a/convert-tests/tests/ops-unbranched/file-to-symlink.yaml +++ b/convert-tests/tests/ops-unbranched/file-to-symlink.yaml @@ -48,6 +48,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~4 diff --git a/convert-tests/tests/ops-unbranched/modify-file.yaml b/convert-tests/tests/ops-unbranched/modify-file.yaml index c1b750e..6ef3656 100644 --- a/convert-tests/tests/ops-unbranched/modify-file.yaml +++ b/convert-tests/tests/ops-unbranched/modify-file.yaml @@ -16,6 +16,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/ops-unbranched/replace-file.yaml b/convert-tests/tests/ops-unbranched/replace-file.yaml index 0e9bb94..e8043a5 100644 --- a/convert-tests/tests/ops-unbranched/replace-file.yaml +++ b/convert-tests/tests/ops-unbranched/replace-file.yaml @@ -16,6 +16,7 @@ svn-revs: conv-params: | head = "" + unbranched-name = "unbranched" git-revs: - rev: unbranched~1 diff --git a/convert-tests/tests/prop-delta/ignore-unbranched.yaml b/convert-tests/tests/prop-delta/ignore-unbranched.yaml index d798924..b8b01f3 100644 --- a/convert-tests/tests/prop-delta/ignore-unbranched.yaml +++ b/convert-tests/tests/prop-delta/ignore-unbranched.yaml @@ -68,6 +68,7 @@ svn-revs: conv-params: | branches = ["trunk"] + unbranched-name = "unbranched" git-revs: - rev: trunk~0 diff --git a/src/convert/options.rs b/src/convert/options.rs index 39a332a..c8e8a0e 100644 --- a/src/convert/options.rs +++ b/src/convert/options.rs @@ -7,7 +7,7 @@ pub(crate) struct InitOptions { pub(crate) keep_deleted_branches: bool, pub(crate) keep_deleted_tags: bool, pub(crate) head_path: Vec, - pub(crate) unbranched_name: String, + pub(crate) unbranched_name: Option, pub(crate) enable_merges: bool, pub(crate) merge_optional: PathPattern, pub(crate) avoid_fully_reverted_merges: bool, @@ -24,7 +24,7 @@ pub(crate) struct Options { pub(super) rename_tags: BranchRenamer, pub(super) keep_deleted_tags: bool, pub(super) head_path: Vec, - pub(super) unbranched_name: String, + pub(super) unbranched_name: Option, pub(super) enable_merges: bool, pub(super) merge_optional: PathPattern, pub(super) avoid_fully_reverted_merges: bool, @@ -278,7 +278,7 @@ mod tests { keep_deleted_branches: true, keep_deleted_tags: true, head_path: b"trunk".to_vec(), - unbranched_name: "unbranched".into(), + unbranched_name: Some("unbranched".into()), enable_merges: false, merge_optional: PathPattern::default(), avoid_fully_reverted_merges: false, diff --git a/src/convert/stage2.rs b/src/convert/stage2.rs index e16c89b..77ce862 100644 --- a/src/convert/stage2.rs +++ b/src/convert/stage2.rs @@ -96,7 +96,9 @@ impl Stage<'_> { let mut ref_name_map = Vec::new(); if !stage1_out.unbranched_rev_data.is_empty() { - ref_name_map.push((options.unbranched_name.clone(), None)); + if let Some(ref unbranched_name) = options.unbranched_name { + ref_name_map.push((unbranched_name.clone(), None)); + } } for (branch_i, branch_data) in stage1_out.branch_data.iter().enumerate() { @@ -215,18 +217,20 @@ impl Stage<'_> { fn run_inner(&mut self, reachable_revs: &BTreeSet) -> Result<(), ConvertError> { tracing::info!("Emitting unbranched commits"); - for i in 0..self.stage1_out.unbranched_rev_data.len() { - let svn_rev = self.stage1_out.root_rev_data - [self.stage1_out.unbranched_rev_data[i].root_rev] - .svn_rev; - tracing::debug!("emitting unbranched commit for SVN revision {svn_rev}"); - self.progress_print.set_progress(format!( - "emitting branch commit - {} / {} (r{svn_rev})", - i + 1, - self.stage1_out.unbranched_rev_data.len(), - )); - - self.make_unbranched_commit(i)?; + if self.unbranched_name.is_some() { + for i in 0..self.stage1_out.unbranched_rev_data.len() { + let svn_rev = self.stage1_out.root_rev_data + [self.stage1_out.unbranched_rev_data[i].root_rev] + .svn_rev; + tracing::debug!("emitting unbranched commit for SVN revision {svn_rev}"); + self.progress_print.set_progress(format!( + "emitting branch commit - {} / {} (r{svn_rev})", + i + 1, + self.stage1_out.unbranched_rev_data.len(), + )); + + self.make_unbranched_commit(i)?; + } } tracing::info!("Emitting branch commits"); diff --git a/src/params_file.rs b/src/params_file.rs index 1bf81cb..d4d4f12 100644 --- a/src/params_file.rs +++ b/src/params_file.rs @@ -18,8 +18,8 @@ pub(crate) struct ConvParams { pub(crate) keep_deleted_tags: bool, #[serde(default = "default_head")] pub(crate) head: String, - #[serde(rename = "unbranched-name", default = "default_unbranched_name")] - pub(crate) unbranched_name: String, + #[serde(rename = "unbranched-name")] + pub(crate) unbranched_name: Option, #[serde(rename = "enable-merges", default = "true_")] pub(crate) enable_merges: bool, #[serde(rename = "merge-optional", default = "Vec::new")] @@ -62,7 +62,3 @@ fn true_() -> bool { fn default_head() -> String { "trunk".into() } - -fn default_unbranched_name() -> String { - "unbranched".into() -}