mirror of
https://github.com/frej/fast-export.git
synced 2026-02-08 14:16:46 +01:00
Merge branch 'PR/236-v2' into master
Implement a plugin converting unnamed heads to branches
This commit is contained in:
@@ -167,7 +167,7 @@ defined filter methods in the [dos2unix](./plugins/dos2unix) and
|
||||
[branch_name_in_commit](./plugins/branch_name_in_commit) plugins.
|
||||
|
||||
```
|
||||
commit_data = {'branch': branch, 'parents': parents, 'author': author, 'desc': desc}
|
||||
commit_data = {'branch': branch, 'parents': parents, 'author': author, 'desc': desc, 'revision': revision, 'hg_hash': hg_hash}
|
||||
|
||||
def commit_message_filter(self,commit_data):
|
||||
```
|
||||
@@ -201,6 +201,8 @@ exactly one head each. Otherwise commits to the tip of these heads
|
||||
within the branch will get flattened into merge commits. Chris J
|
||||
Billington's [hg-export-tool] can help you to handle branches with
|
||||
duplicate heads.
|
||||
Alternatively, you can use the [head2branch plugin](./plugins/head2branch)
|
||||
to create a new named branch from an unnamed head.
|
||||
|
||||
hg-fast-export will ignore any files or directories tracked by mercurial
|
||||
called `.git`, and will print a warning if it encounters one. Git cannot
|
||||
|
||||
@@ -302,9 +302,10 @@ def export_commit(ui,repo,revision,old_marks,max,count,authors,
|
||||
|
||||
parents = [p for p in repo.changelog.parentrevs(revision) if p >= 0]
|
||||
author = get_author(desc,user,authors)
|
||||
hg_hash=revsymbol(repo,b"%d" % revision).hex()
|
||||
|
||||
if plugins and plugins['commit_message_filters']:
|
||||
commit_data = {'branch': branch, 'parents': parents, 'author': author, 'desc': desc}
|
||||
commit_data = {'branch': branch, 'parents': parents, 'author': author, 'desc': desc, 'revision': revision, 'hg_hash': hg_hash}
|
||||
for filter in plugins['commit_message_filters']:
|
||||
filter(commit_data)
|
||||
branch = commit_data['branch']
|
||||
@@ -475,7 +476,7 @@ def branchtip(repo, heads):
|
||||
break
|
||||
return tip
|
||||
|
||||
def verify_heads(ui,repo,cache,force,branchesmap):
|
||||
def verify_heads(ui,repo,cache,force,ignore_unnamed_heads,branchesmap):
|
||||
branches={}
|
||||
for bn, heads in repo.branchmap().iteritems():
|
||||
branches[bn] = branchtip(repo, heads)
|
||||
@@ -497,21 +498,23 @@ def verify_heads(ui,repo,cache,force,branchesmap):
|
||||
|
||||
# verify that branch has exactly one head
|
||||
t={}
|
||||
unnamed_heads=False
|
||||
for h in repo.filtered(b'visible').heads():
|
||||
(_,_,_,_,_,_,branch,_)=get_changeset(ui,repo,h)
|
||||
if t.get(branch,False):
|
||||
stderr_buffer.write(
|
||||
b'Error: repository has at least one unnamed head: hg r%d\n'
|
||||
b'Error: repository has an unnamed head: hg r%d\n'
|
||||
% repo.changelog.rev(h)
|
||||
)
|
||||
if not force: return False
|
||||
unnamed_heads=True
|
||||
if not force and not ignore_unnamed_heads: return False
|
||||
t[branch]=True
|
||||
|
||||
if unnamed_heads and not force and not ignore_unnamed_heads: return False
|
||||
return True
|
||||
|
||||
def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile,
|
||||
authors={},branchesmap={},tagsmap={},
|
||||
sob=False,force=False,hgtags=False,notes=False,encoding='',fn_encoding='',
|
||||
sob=False,force=False,ignore_unnamed_heads=False,hgtags=False,notes=False,encoding='',fn_encoding='',
|
||||
plugins={}):
|
||||
def check_cache(filename, contents):
|
||||
if len(contents) == 0:
|
||||
@@ -532,7 +535,7 @@ def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile,
|
||||
|
||||
ui,repo=setup_repo(repourl)
|
||||
|
||||
if not verify_heads(ui,repo,heads_cache,force,branchesmap):
|
||||
if not verify_heads(ui,repo,heads_cache,force,ignore_unnamed_heads,branchesmap):
|
||||
return 1
|
||||
|
||||
try:
|
||||
@@ -618,7 +621,9 @@ if __name__=='__main__':
|
||||
parser.add_option("-T","--tags",dest="tagsfile",
|
||||
help="Read tags map from TAGSFILE")
|
||||
parser.add_option("-f","--force",action="store_true",dest="force",
|
||||
default=False,help="Ignore validation errors by force")
|
||||
default=False,help="Ignore validation errors by force, implies --ignore-unnamed-heads")
|
||||
parser.add_option("--ignore-unnamed-heads",action="store_true",dest="ignore_unnamed_heads",
|
||||
default=False,help="Ignore unnamed head errors")
|
||||
parser.add_option("-M","--default-branch",dest="default_branch",
|
||||
help="Set the default branch")
|
||||
parser.add_option("-o","--origin",dest="origin_name",
|
||||
@@ -714,6 +719,8 @@ if __name__=='__main__':
|
||||
sys.exit(hg2git(options.repourl,m,options.marksfile,options.mappingfile,
|
||||
options.headsfile, options.statusfile,
|
||||
authors=a,branchesmap=b,tagsmap=t,
|
||||
sob=options.sob,force=options.force,hgtags=options.hgtags,
|
||||
sob=options.sob,force=options.force,
|
||||
ignore_unnamed_heads=options.ignore_unnamed_heads,
|
||||
hgtags=options.hgtags,
|
||||
notes=options.notes,encoding=encoding,fn_encoding=fn_encoding,
|
||||
plugins=plugins_dict))
|
||||
|
||||
@@ -45,7 +45,7 @@ if [ -z "${PYTHON}" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USAGE="[--quiet] [-r <repo>] [--force] [-m <max>] [-s] [--hgtags] [-A <file>] [-B <file>] [-T <file>] [-M <name>] [-o <name>] [--hg-hash] [-e <encoding>]"
|
||||
USAGE="[--quiet] [-r <repo>] [--force] [--ignore-unnamed-heads] [-m <max>] [-s] [--hgtags] [-A <file>] [-B <file>] [-T <file>] [-M <name>] [-o <name>] [--hg-hash] [-e <encoding>]"
|
||||
LONG_USAGE="Import hg repository <repo> up to either tip or <max>
|
||||
If <repo> is omitted, use last hg repository as obtained from state file,
|
||||
GIT_DIR/$PFX-$SFX_STATE by default.
|
||||
|
||||
13
plugins/head2branch/README.md
Normal file
13
plugins/head2branch/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## Convert Head to Branch
|
||||
|
||||
`fast-export` can only handle one head per branch. This plugin makes it possible
|
||||
to create a new branch from a head by specifying the new branch name and
|
||||
the first divergent commit for that head.
|
||||
|
||||
Note: the hg hash must be in the full form, 40 hexadecimal characters.
|
||||
|
||||
Note: you must run `fast-export` with `--ignore-unnamed-heads` option,
|
||||
otherwise, the conversion will fail.
|
||||
|
||||
To use the plugin, add the command line flag `--plugin head2branch=name,<hg_hash>`.
|
||||
The flag can be given multiple times to name more than one head.
|
||||
24
plugins/head2branch/__init__.py
Normal file
24
plugins/head2branch/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import sys
|
||||
|
||||
def build_filter(args):
|
||||
return Filter(args)
|
||||
|
||||
class Filter:
|
||||
|
||||
def __init__(self, args):
|
||||
args = args.split(',')
|
||||
self.branch_name = args[0].encode('ascii', 'replace')
|
||||
self.starting_commit_hash = args[1].encode('ascii', 'strict')
|
||||
self.branch_parents = set()
|
||||
|
||||
def commit_message_filter(self, commit_data):
|
||||
hg_hash = commit_data['hg_hash']
|
||||
rev = commit_data['revision']
|
||||
rev_parents = commit_data['parents']
|
||||
if (hg_hash == self.starting_commit_hash
|
||||
or any(rp in self.branch_parents for rp in rev_parents)
|
||||
):
|
||||
self.branch_parents.add(rev)
|
||||
commit_data['branch'] = self.branch_name
|
||||
sys.stderr.write('\nchanging r%s to branch %r\n' % (rev, self.branch_name))
|
||||
sys.stderr.flush()
|
||||
Reference in New Issue
Block a user