diff --git a/README.md b/README.md index 67819bb8..6bf2878d 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,20 @@ The default protocol is https, and the port can be omitted. - `hg::tags:` becomes `hg://:tags` +Compatibility: +-------------- + +As of version 0.7, some corner cases in Mercurial repositories will generate +different git commits than with prior versions of git-cinnabar. This means +a fresh clone might have different git SHA-1s than existing clones, but this +doesn't impact the use of existing clones with newer versions of git-cinnabar. + +Most repositories should remain non-affected by the change. + +You can set the `cinnabar.compat` git configuration to `0.6` to keep the +previous behavior. + + Experimental features: ---------------------- diff --git a/src/main.rs b/src/main.rs index 8ac5f7dd..5448efe0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4705,6 +4705,31 @@ pub fn check_enabled(checks: Checks) -> bool { CHECKS.contains(checks) } +bitflags! { + #[derive(Debug)] + pub struct Compat: i32 { + const CHANGESET_CONFLICT_NUL = 0x1; + + const CINNABAR_0_6 = Compat::CHANGESET_CONFLICT_NUL.bits(); + } +} + +static COMPAT: Lazy = + Lazy::new( + || match get_config("compat").as_ref().map(|x| x.as_bytes()) { + Some(b"0.6") => Compat::CINNABAR_0_6, + Some(x) => { + warn!(target: "root", "Ignoring invalid value for compat: {}", x.as_bstr()); + Compat::empty() + } + None => Compat::empty(), + }, + ); + +pub fn has_compat(c: Compat) -> bool { + COMPAT.contains(c) +} + pub struct Experiments { merge: bool, similarity: CString, diff --git a/src/store.rs b/src/store.rs index ab801e26..fd86c09d 100644 --- a/src/store.rs +++ b/src/store.rs @@ -55,7 +55,7 @@ use crate::util::{ Transpose, }; use crate::xdiff::{apply, textdiff, PatchInfo}; -use crate::{check_enabled, Checks}; +use crate::{check_enabled, has_compat, Checks, Compat}; pub const REFS_PREFIX: &str = "refs/cinnabar/"; pub const REPLACE_REFS_PREFIX: &str = "refs/cinnabar/replace/"; @@ -1526,7 +1526,11 @@ fn store_changeset( // we find a commit that doesn't map to another changeset. match GitChangesetId::from_unchecked(commit_id).to_hg(store) { Some(existing_hg_id) if existing_hg_id != changeset_id => { - raw_commit.push(b'\0'); + if has_compat(Compat::CHANGESET_CONFLICT_NUL) { + raw_commit.push(b'\0'); + } else { + raw_commit.push(b'\n'); + } } _ => { break commit_id; diff --git a/tests/conflicts.t b/tests/conflicts.t index 873abdb1..454c7260 100644 --- a/tests/conflicts.t +++ b/tests/conflicts.t @@ -60,7 +60,7 @@ changesets properly. author nobody <> 1 +0000 committer nobody <> 1 +0000 - b\x00 (no-eol) (esc) + b $ git clone -q --mirror repo-git repo-mirror $ git -C repo-mirror cinnabar rollback @@ -68,7 +68,6 @@ changesets properly. 3 $ git -C repo-mirror gc --prune=now $ git -C repo-mirror fsck - warning in commit 36376d9e996376d02d00f72a8c7903cef9d6ea45: nulInCommit: NUL byte in the commit object body Check that this round-trips properly. @@ -90,6 +89,52 @@ Check that this round-trips properly. o f92470d7f6966a39dfbced6a525fe81ebf5c37b9 default a +Trying again with git-cinnabar 0.6 compat. + + $ git -c cinnabar.compat=0.6 -c fetch.prune=true clone -q hg::$REPO repo-git-0.6 + $ git -C repo-git-0.6 cat-file -p $(git -C repo-git-0.6 cinnabar hg2git 636e60525868096cbdc961870493510558f41d2f) + tree 3683f870be446c7cc05ffaef9fa06415276e1828 + parent 8b86a58578d5270969543e287634e3a2f122a338 + author nobody <> 1 +0000 + committer nobody <> 1 +0000 + + b (no-eol) + $ git -C repo-git-0.6 cat-file -p $(git -C repo-git-0.6 cinnabar hg2git 97b815fb8d45129120112766f8c69db8e93fbe8f) + tree 3683f870be446c7cc05ffaef9fa06415276e1828 + parent 8b86a58578d5270969543e287634e3a2f122a338 + author nobody <> 1 +0000 + committer nobody <> 1 +0000 + + b\x00 (no-eol) (esc) + + $ git clone -q --mirror repo-git-0.6 repo-mirror-0.6 + $ git -C repo-mirror-0.6 cinnabar rollback + $ git -C repo-mirror-0.6 rev-list --count --all + 3 + $ git -C repo-mirror-0.6 gc --prune=now + $ git -C repo-mirror-0.6 fsck + warning in commit 36376d9e996376d02d00f72a8c7903cef9d6ea45: nulInCommit: NUL byte in the commit object body + +Check that this round-trips properly. + + $ hg init ${REPO}2-0.6 + $ git -C repo-git-0.6 push hg::${REPO}2-0.6 "refs/remotes/origin/branches/foo/tip:refs/heads/branches/foo/tip" "refs/remotes/origin/branches/default/tip:refs/heads/branches/default/tip" + remote: adding changesets + remote: adding manifests + remote: adding file changes + remote: added 3 changesets with 2 changes to 2 files (+1 heads) + To hg::.*/conflicts.t/repo2-0.6 (re) + * [new branch] origin/branches/foo/tip -> branches/foo/tip + * [new branch] origin/branches/default/tip -> branches/default/tip + + $ hg -R ${REPO}2-0.6 log -G --template '{node} {branch} {desc}' + o 97b815fb8d45129120112766f8c69db8e93fbe8f foo b + | + | o 636e60525868096cbdc961870493510558f41d2f default b + |/ + o f92470d7f6966a39dfbced6a525fe81ebf5c37b9 default a + + The obvious consequence is that without initial metadata, pushing this to a mercurial repo will create a different changeset for the one in branch foo. TODO: But we don't support creating new branches anyway, so we can't really