diff --git a/hg-fast-export.py b/hg-fast-export.py index 9405468..c5f6f5d 100755 --- a/hg-fast-export.py +++ b/hg-fast-export.py @@ -284,7 +284,7 @@ def strip_leading_slash(filename): def export_commit(ui,repo,revision,old_marks,max,count,authors, branchesmap,sob,brmap,hgtags,encoding='',fn_encoding='', - plugins={}): + first_commit_hash="",plugins={}): def get_branchname(name): if name in brmap: return brmap[name] @@ -332,6 +332,9 @@ def export_commit(ui,repo,revision,old_marks,max,count,authors, if not parents: type='full' + if revision == 0 and first_commit_hash: + wr(b'from %s' % first_commit_hash.encode()) + type='simple delta' else: wr(b'from %s' % revnum_to_revref(parents[0], old_marks)) if len(parents) == 1: @@ -526,7 +529,8 @@ def verify_heads(ui,repo,cache,force,ignore_unnamed_heads,branchesmap): def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile, authors={},branchesmap={},tagsmap={}, - sob=False,force=False,ignore_unnamed_heads=False,hgtags=False,notes=False,encoding='',fn_encoding='', + sob=False,force=False,ignore_unnamed_heads=False,hgtags=False, + notes=False,encoding='',fn_encoding='',first_commit_hash='', plugins={}): def check_cache(filename, contents): if len(contents) == 0: @@ -582,7 +586,7 @@ def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile, brmap={} for rev in range(min,max): c=export_commit(ui,repo,rev,old_marks,max,c,authors,branchesmap, - sob,brmap,hgtags,encoding,fn_encoding, + sob,brmap,hgtags,encoding,fn_encoding,first_commit_hash, plugins) if notes: for rev in range(min,max): @@ -656,6 +660,8 @@ if __name__=='__main__': help="Add a plugin with the given init string ") parser.add_option("--subrepo-map", type="string", dest="subrepo_map", help="Provide a mapping file between the subrepository name and the submodule name") + parser.add_option("--first-commit-hash", type="string", dest="first_commit_hash", + help="Allow importing into an existing git repository by specifying the hash of the first commit") (options,args)=parser.parse_args() @@ -735,4 +741,5 @@ if __name__=='__main__': ignore_unnamed_heads=options.ignore_unnamed_heads, hgtags=options.hgtags, notes=options.notes,encoding=encoding,fn_encoding=fn_encoding, + first_commit_hash=options.first_commit_hash, plugins=plugins_dict)) diff --git a/hg-fast-export.sh b/hg-fast-export.sh index 7d6d7b7..30e8ee1 100755 --- a/hg-fast-export.sh +++ b/hg-fast-export.sh @@ -87,6 +87,8 @@ Options: with as arguments --plugin Add a plugin with the given init string (repeatable) --plugin-path Add an additional plugin lookup path + --first-commit-hash Use the given git commit hash as the + first commit's parent (for grafting) " case "$1" in -h|--help) diff --git a/t/first_commit_hash_option.t b/t/first_commit_hash_option.t new file mode 100755 index 0000000..8df44d0 --- /dev/null +++ b/t/first_commit_hash_option.t @@ -0,0 +1,117 @@ +#!/bin/bash +# +# Copyright (c) 2025 +# + +test_description='git_lfs_importer plugin integration tests' + +. "${SHARNESS_TEST_SRCDIR-$(dirname "$0")/sharness}"/sharness.sh || exit 1 + +setup() { + cat > "$HOME"/.hgrc <<-EOF + [ui] + username = Test User + EOF + + # Git config for the destination repo commits + git config --global user.email "test@example.com" + git config --global user.name "Test User" +} + +setup + +test_expect_success 'Mercurial history is imported over the provided commit' ' + test_when_finished "rm -rf hgrepo gitrepo lfs-patterns.txt" && + + # 1. Create source Mercurial repository with binary files + ( + hg init hgrepo && + cd hgrepo && + echo "regular text file" > readme.txt && + hg add readme.txt && + hg commit -m "initial commit" + ) && + + # 2. Prepare destination git repo with LFS setup + mkdir gitrepo && + ( + cd gitrepo && + git init -q && + git config core.ignoreCase false && + git lfs install --local && + git switch --create master && + + cat > .gitattributes <<-EOF && + * -text + EOF + + git add .gitattributes && + git commit -q -m "Initialize Git configuration" + ) && + + FIRST_HASH=$(git -C gitrepo rev-parse HEAD) && + + # 3. Run hg-fast-export + ( + cd gitrepo && + hg-fast-export.sh \ + -r "../hgrepo" \ + --first-commit-hash "$FIRST_HASH" --force \ + -M master + ) && + + # 4. Verify git file is still present + git -C gitrepo show HEAD:.gitattributes > gitattributes_check.txt && + test "$(cat gitattributes_check.txt)" = "* -text" && + + # 5. Verify hg file is imported + git -C gitrepo show HEAD:readme.txt > readme_check.txt && + test "$(cat readme_check.txt)" = "regular text file" +' + +test_expect_success 'Mercurial history has priority over git' ' + test_when_finished "rm -rf hgrepo gitrepo lfs-patterns.txt" && + + # 1. Create source Mercurial repository with binary files + ( + hg init hgrepo && + cd hgrepo && + echo "hg readme file" > readme.txt && + hg add readme.txt && + hg commit -m "initial commit" + ) && + + # 2. Prepare destination git repo with LFS setup + mkdir gitrepo && + ( + cd gitrepo && + git init -q && + git config core.ignoreCase false && + git lfs install --local && + git switch --create master && + + cat > readme.txt <<-EOF && + git readme file + EOF + + git add readme.txt && + git commit -q -m "Initialize Git readme file" + ) && + + FIRST_HASH=$(git -C gitrepo rev-parse HEAD) && + + # 3. Run hg-fast-export + ( + cd gitrepo && + hg-fast-export.sh \ + -r "../hgrepo" \ + --first-commit-hash "$FIRST_HASH" --force \ + -M master + ) && + + # 5. Verify hg file is imported + git -C gitrepo show HEAD:readme.txt > readme_check.txt && + test "$(cat readme_check.txt)" = "hg readme file" +' + +test_done