Migrating from BitKeeper
Satish Balay
balay at fastmail.fm
Thu Jan 21 18:37:51 CST 2010
We've done the migration a few years back. I had a script to convert
bk repo to a hg repo. If you decide to migtrate to mercurial - perhaps
this script will be useful.
disclaimers:
- It needs a working 'bk' - which I don't have anymore - so can't
troubleshoot it [nor do I remember how the code is supporsed to work].
- It was used with bk/hg from 2006. As it uses the comand interface
for both tools - I'm guessing it should still work.
- the process is I/O intensive - so best to do the conversion on a
local file system [i.e no nfs].
- the conversion was done on a linux box.
Satish
On Thu, 21 Jan 2010, Robert Graham wrote:
> Hi all,
>
> At work we're using BitKeeper but we're seriously considering a move. We
> haven't decided which way to jump yet, but we're interested in any advice,
> experience, or other help people can offer on moving from BitKeeper to
> mercurial.
>
> Thanks for any help,
> Robert Graham
> _______________________________________________
> Mercurial mailing list
> Mercurial at selenic.com
> http://selenic.com/mailman/listinfo/mercurial
-------------- next part --------------
#!/usr/bin/env python
#
# Usage bk2hg local-bk-repo local-hg-repo
#
# options:
# -i : create/initialize a new hg repo
# example:
# bk2hg.py -i /sandbox/petsc/petsc-dev-bk /sandbox/petsc/petsc-dev-hg
#
# local-bk-repo is a valid bk repository
# local-hg-repo is a new location
import sys
import os
import time
import shutil
import tempfile
# Get the key for the tip revision - so the bk <-> hg key mappings can be stored
def hg_get_tip_key(hg_repo):
cur_path=os.path.abspath(os.path.curdir)
os.chdir(hg_repo)
fd=os.popen('hg tip -v')
buf = fd.read()
fd.close()
os.chdir(cur_path)
return buf.splitlines()[0].split(':')[2]
def main():
createhgrepo=0
if '-i' in sys.argv:
sys.argv.remove('-i')
createhgrepo=1
arg_len = len(sys.argv)
if arg_len != 3:
print 'Error Insufficient arguments.'
print 'Usage:', sys.argv[0], '[-i] local-bk-repo local-hg-repo'
print 'Example:'
print ' bk2hg.py /sandbox/petsc/petsc-dev-bk /sandbox/petsc/petsc-dev-hg'
sys.exit()
bk_repo = sys.argv[1]
hg_repo= sys.argv[2]
# get absolute paths - this way - os.chdir() works
bk_repo = os.path.realpath(bk_repo)
hg_repo = os.path.realpath(hg_repo)
hg_repo_child = hg_repo+'-child'
hg_repo_child_merge = hg_repo+'-child-merge'
hg_rev = {}
os.environ['HGMERGE'] = '/bin/true'
# create a file with 'd' for delete - required for hg merge
fd,tmp_file = tempfile.mkstemp()
err=os.write(fd,'d\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\nd\n')
os.close(fd)
# verify if bkdir exists
if not os.path.exists(bk_repo):
print 'Error! specified path does not exist: ' + bk_repo
sys.exit()
# if createhgrepo - then create & initialize the repo [if dir exists]
# otherwise - make sure the dir exists [if not error]
if os.path.exists(hg_repo):
if createhgrepo:
print 'Warning! ignoring option -i as specified hgrepo exists: ' + hg_repo
sys.stdout.flush()
else:
if createhgrepo:
print 'Creating hgrepo: ' + hg_repo
sys.stdout.flush()
os.mkdir(hg_repo)
os.chdir(hg_repo)
if os.system("hg init"):
print 'Error during hg init!'
sys.exit()
hg_rev['1.0'] = hg_get_tip_key(hg_repo)
else:
print 'Error! specified path does not exist: ' + hg_repo
print 'If you need to create a new hgrepo, use option: -i'
sys.exit()
# verify the specified dirs are valid repositories
os.chdir(bk_repo)
if os.system("bk changes -r+ > /dev/null 2>&1"):
print 'Error! specified path is not a bk repository: ' + bk_repo
sys.exit()
os.chdir(hg_repo)
if os.system("hg tip > /dev/null 2>&1"):
print 'Error! specified path is not a hg repository: ' + hg_repo
sys.exit()
# now get the latest bk cset number
os.chdir(bk_repo)
fd=os.popen('bk changes -k -r+')
bk_cset_max=fd.read().strip()
fd.close()
# similarly get the latest hg cset number
os.chdir(hg_repo)
fd=os.popen('hg tip -v')
buf=fd.read()
fd.close()
if buf == '':
print 'ErrorNo new changesets Quitting! Last commit:', bk_cset_min
sys.exit()
elif buf.splitlines()[0].find(' -1:') >=0:
# a new repository has -1 changeset number.
bk_cset_min = '1.0'
else:
bk_cset_min =''
for line in buf.splitlines():
if line.find('|ChangeSet|') >=0:
bk_cset_min = line.strip()
break
if bk_cset_min == '':
print 'Error! bk changeset tag not found at the tip of hg repo!'
sys.exit()
if bk_cset_min == bk_cset_max:
print 'No new changesets Quitting! Last commit:', bk_cset_min
sys.exit()
log_file=os.path.join(bk_repo,'hg_log.tmp')
if os.path.isfile(log_file):
os.remove(log_file)
# find the bk-changesets that need to be exported to hg
# using -end:KEY avoids duplicate listing of TAGS [causes too much grief]
os.chdir(bk_repo)
fd=os.popen('bk changes -end:KEY: -f -r'+'"'+bk_cset_min+'".."'+bk_cset_max+'"')
buf=fd.read()
fd.close()
revs=buf.splitlines()
if revs == []:
print 'No new revisions found in bk. Perhaps some internal error!'
print 'bk_cset_min:',bk_cset_min
print 'bk_cset_max:',bk_cset_max
sys.exit()
#now process each revision [ignore the first]
for rev in revs:
# rev - basic string
# revq - rev with quotes [useable with -r]
# revn - rev number [ 1.234.4 etc..]
os.chdir(bk_repo)
revq='"'+rev+'"'
# get the rev-number
fd=os.popen('bk changes -and:I: -r'+revq)
revn = fd.read().splitlines()[0]
fd.close()
print 'Processing changeset: '+revn
sys.stdout.flush()
# get username
fd=os.popen('bk changes -and:USER:@:HOST: -r'+revq)
auth_email=fd.read().splitlines()[0].strip()
fd.close()
auth_email=auth_email.replace('.(none)','')
fd=os.popen('bk changes -and:TIME_T: -r'+revq)
gtime = fd.read().splitlines()[0].strip()
timestr = '"' + str(gtime) + ' ' +str(time.timezone) + '"'
#get comment string
fd=os.popen('bk changes -v -r'+revq)
buf=fd.read()
fd.close()
msg = 'bk-changeset-'+revn + '\n' + rev + '\n'+ buf.strip() + '\n'
fd=open(log_file,'w')
fd.write(msg)
fd.close()
# prev parent revision
# mrev merge revision
# crev current revision
if revn == '1.0':
mrev = None
prev = None
crev = '1.0'
else:
fd=os.popen('bk rset -r'+revq)
buf =fd.read()
fd.close()
tmpstr = buf.splitlines()[0].strip().split('|')[1]
if len(tmpstr.split('+')) == 2:
mrev,pstr= tmpstr.split('+')
else:
mrev = None
pstr = tmpstr
prev,crev = pstr.split('..')
print mrev,prev,crev,revn
sys.stdout.flush()
# crev should be same as revn
if crev != revn:
print 'Error! crev and revn do not match!', crev, revn
sys.exit()
# Now start the converion process
if prev:
hg_prev_val = hg_rev[prev]
else:
hg_prev_val = '000000000000'
if mrev:
hg_mrev_val = hg_rev[mrev]
else:
hg_mrev_val = '000000000000'
# merge if necessary
if mrev != None:
if os.system('hg clone -r'+str(hg_prev_val) +' '+ hg_repo +' '+ hg_repo_child + '> /dev/null 2>&1'):
print 'Error during hg clone!'
sys.exit()
if os.system('hg clone -r'+str(hg_mrev_val) +' '+ hg_repo +' '+ hg_repo_child_merge + '> /dev/null 2>&1'):
print 'Error during hg clone!'
sys.exit()
# now merge these 2 branches
os.chdir(hg_repo_child)
if os.system('hg pull -f '+hg_repo_child_merge):
print 'Error during hg pull!'
sys.exit()
# look for the tip - and tag the non-tip version [among prev and mrev]
tip_key = hg_get_tip_key(hg_repo_child)
if tip_key == hg_rev[prev]:
tagrev = mrev
elif tip_key == hg_rev[mrev]:
tagrev = prev
else:
print 'Error tip does not match either prev or mrev!'
sys.exit()
if os.system('hg tag -l -r'+str(hg_rev[tagrev]) +' '+ str(tagrev)):
print 'Error during hg tag prev'
sys.exit()
# now attempt to merge
os.system('ls -a | grep -v .hg | xargs rm -rf >/dev/null 2>&1')
if os.system('hg co -C -f'):
print 'Error during checkout'
sys.exit()
if os.system('hg merge -f -b'+str(tagrev) + '< '+ tmp_file):
print '******* Error during merge! Ignoring*********'
sys.stdout.flush()
# remove the temp clone
shutil.rmtree(hg_repo_child_merge)
else:
# clone the prev version[parent to the current change]
if os.system('hg clone -r'+str(hg_prev_val) +' '+ hg_repo +' '+ hg_repo_child + '> /dev/null 2>&1'):
print 'Error during hg clone!'
sys.exit()
# Now remove the old files - and export the new modified files
os.chdir(hg_repo_child)
os.system('ls -a | grep -v .hg | xargs rm -rf >/dev/null 2>&1')
if os.system('bk export -r'+revq+' ' + bk_repo + ' ' + hg_repo_child):
print 'Error during bk export!'
sys.exit()
# somehow add in --date as well
if os.system('hg commit --addremove --user ' + auth_email + ' --date ' + timestr + ' --logfile '+log_file + ' --exclude '+log_file):
print 'Exiting due to the previous error!'
sys.exit()
os.unlink(log_file)
# now extract the changeset id - and store
hg_rev[revn] = hg_get_tip_key(hg_repo_child)
if os.system('hg push -f ' +hg_repo):
print '********** Push returned error code! Ignoring! *************'
sys.stdout.flush()
# remove the temp clone
shutil.rmtree(hg_repo_child)
os.unlink(tmp_file)
return 0
# The classes in this file can also
# be used in other python-programs by using 'import'
if __name__ == '__main__':
main()
print '******** Done Conversion ***************'
More information about the Mercurial
mailing list