contrib/darcs2hg.py
changeset 2587 fe3e87358b47
parent 2585 5ec2dded1bda
child 2588 8210cf2ec19d
equal deleted inserted replaced
2585:5ec2dded1bda 2587:fe3e87358b47
     2 # Encoding: iso-8859-1
     2 # Encoding: iso-8859-1
     3 # vim: tw=80 ts=4 sw=4 noet
     3 # vim: tw=80 ts=4 sw=4 noet
     4 # -----------------------------------------------------------------------------
     4 # -----------------------------------------------------------------------------
     5 # Project   : Basic Darcs to Mercurial conversion script
     5 # Project   : Basic Darcs to Mercurial conversion script
     6 # -----------------------------------------------------------------------------
     6 # -----------------------------------------------------------------------------
     7 # Author    : Sebastien Pierre <sebastien@xprima.com>
     7 # Authors   : Sebastien Pierre                           <sebastien@xprima.com>
       
     8 #             TK Soh                                      <teekaysoh@gmail.com>
       
     9 # -----------------------------------------------------------------------------
     8 # Creation  : 24-May-2006
    10 # Creation  : 24-May-2006
     9 # Last mod  : 26-May-2006
    11 # Last mod  : 05-Jun-2006
    10 # History   :
       
    11 #             26-May-2006 - Updated
       
    12 #             24-May-2006 - First implementation
       
    13 # -----------------------------------------------------------------------------
    12 # -----------------------------------------------------------------------------
    14 
    13 
    15 import os, sys
    14 import os, sys
    16 import tempfile
    15 import tempfile
    17 import xml.dom.minidom as xml_dom
    16 import xml.dom.minidom as xml_dom
    19 
    18 
    20 DARCS_REPO = None
    19 DARCS_REPO = None
    21 HG_REPO    = None
    20 HG_REPO    = None
    22 
    21 
    23 USAGE = """\
    22 USAGE = """\
    24 %s DARCSREPO HGREPO
    23 %s DARCSREPO HGREPO [SKIP]
    25 
    24 
    26     Converts the given Darcs repository to a new Mercurial repository. The given
    25     Converts the given Darcs repository to a new Mercurial repository. The given
    27     HGREPO must not exist, as it will be created and filled up (this will avoid
    26     HGREPO must not exist, as it will be created and filled up (this will avoid
    28     overwriting valuable data.
    27     overwriting valuable data.
    29 
    28 
       
    29     In case an error occurs within the process, you can resume the process by
       
    30     giving the last successfuly applied change number.
    30 """ % (os.path.basename(sys.argv[0]))
    31 """ % (os.path.basename(sys.argv[0]))
    31 
    32 
    32 # ------------------------------------------------------------------------------
    33 # ------------------------------------------------------------------------------
    33 #
    34 #
    34 # Utilities
    35 # Utilities
    35 #
    36 #
    36 # ------------------------------------------------------------------------------
    37 # ------------------------------------------------------------------------------
    37 
    38 
    38 def cmd(text, path=None):
    39 def cmd(text, path=None, silent=False):
    39 	"""Executes a command, in the given directory (if any), and returns the
    40 	"""Executes a command, in the given directory (if any), and returns the
    40 	command result as a string."""
    41 	command result as a string."""
    41 	cwd = None
    42 	cwd = None
    42 	if path:
    43 	if path:
    43 		path = os.path.abspath(path)
    44 		path = os.path.abspath(path)
    44 		cwd  = os.getcwd()
    45 		cwd  = os.getcwd()
    45 		os.chdir(path)
    46 		os.chdir(path)
    46 	print text
    47 	if not silent: print "> ", text
    47 	res = os.popen(text).read()
    48 	res = os.popen(text).read()
    48 	if path:
    49 	if path:
    49 		os.chdir(cwd)
    50 		os.chdir(cwd)
    50 	return res
    51 	return res
    51 
    52 
    52 def writefile(path, data):
    53 def writefile(path, data):
    53 	"""Writes the given data into the given file."""
    54 	"""Writes the given data into the given file."""
    54 	f = file(path, "w") ; f.write(data)  ; f.close()
    55 	f = file(path, "w") ; f.write(data)  ; f.close()
       
    56 
       
    57 def error( *args ):
       
    58 	sys.stderr.write("ERROR: ")
       
    59 	for a in args: sys.stderr.write(str(a))
       
    60 	sys.stderr.write("\n")
       
    61 	sys.stderr.write("You can make manual fixes if necessary and then resume by"
       
    62 	" giving the last changeset number")
       
    63 	sys.exit(-1)
    55 
    64 
    56 # ------------------------------------------------------------------------------
    65 # ------------------------------------------------------------------------------
    57 #
    66 #
    58 # Darcs interface
    67 # Darcs interface
    59 #
    68 #
    62 def darcs_changes(darcsRepo):
    71 def darcs_changes(darcsRepo):
    63 	"""Gets the changes list from the given darcs repository. This returns the
    72 	"""Gets the changes list from the given darcs repository. This returns the
    64 	chronological list of changes as (change name, change summary)."""
    73 	chronological list of changes as (change name, change summary)."""
    65 	changes    = cmd("darcs changes --reverse --xml-output", darcsRepo)
    74 	changes    = cmd("darcs changes --reverse --xml-output", darcsRepo)
    66 	doc        = xml_dom.parseString(changes)
    75 	doc        = xml_dom.parseString(changes)
    67 	res        = []
       
    68 	for patch_node in doc.childNodes[0].childNodes:
    76 	for patch_node in doc.childNodes[0].childNodes:
    69 		name = filter(lambda n:n.nodeName == "name", patch_node.childNodes)
    77 		name = filter(lambda n:n.nodeName == "name", patch_node.childNodes)
    70 		comm = filter(lambda n:n.nodeName == "comment", patch_node.childNodes)
    78 		comm = filter(lambda n:n.nodeName == "comment", patch_node.childNodes)
    71 		if not name:continue
    79 		if not name:continue
    72 		else: name = name[0].childNodes[0].data
    80 		else: name = name[0].childNodes[0].data
    73 		if not comm: comm = ""
    81 		if not comm: comm = ""
    74 		else: comm = comm[0].childNodes[0].data
    82 		else: comm = comm[0].childNodes[0].data
    75 		author = patch_node.getAttribute("author")
    83 		author = patch_node.getAttribute("author")
    76 		date = patch_node.getAttribute("date")
    84 		date   = patch_node.getAttribute("date")
    77         hash = patch_node.getAttribute("hash")
    85 		chash  = os.path.splitext(patch_node.getAttribute("hash"))[0]
    78         yield hash, author, date, name, comm
    86 		yield author, date, name, chash, comm
    79 
    87 
    80 def darcs_pull(hg_repo, darcs_repo, change):
    88 def darcs_tip(darcs_repo):
    81 	cmd("darcs pull '%s' --all --patches='%s'" % (darcs_repo, change), hg_repo)
    89 	changes = cmd("darcs changes",darcs_repo,silent=True)
       
    90 	changes = filter(lambda l:l.strip().startswith("* "), changes.split("\n"))
       
    91 	return len(changes)
       
    92 
       
    93 def darcs_pull(hg_repo, darcs_repo, chash):
       
    94 	old_tip = darcs_tip(darcs_repo)
       
    95 	res     = cmd("darcs pull '%s' --all --match='hash %s'" % (darcs_repo, chash), hg_repo)
       
    96 	print res
       
    97 	new_tip = darcs_tip(darcs_repo)
       
    98 	if not new_tip != old_tip + 1:
       
    99 		error("Darcs pull did not work as expected: " + res)
    82 
   100 
    83 # ------------------------------------------------------------------------------
   101 # ------------------------------------------------------------------------------
    84 #
   102 #
    85 # Mercurial interface
   103 # Mercurial interface
    86 #
   104 #
    87 # ------------------------------------------------------------------------------
   105 # ------------------------------------------------------------------------------
    88 
   106 
    89 def hg_commit( hg_repo, text, author, date ):
   107 def hg_commit( hg_repo, text, author, date ):
    90 	fd, tmpfile = tempfile.mkstemp(prefix="darcs2hg_")
   108 	fd, tmpfile = tempfile.mkstemp(prefix="darcs2hg_")
    91 	writefile(tmpfile, text)
   109 	writefile(tmpfile, text)
       
   110 	old_tip = hg_tip(hg_repo)
    92 	cmd("hg add -X _darcs", hg_repo)
   111 	cmd("hg add -X _darcs", hg_repo)
    93 	cmd("hg remove -X _darcs --after", hg_repo)
   112 	cmd("hg remove -X _darcs --after", hg_repo)
    94 	cmd("hg commit -l %s -u '%s' -d '%s 0'"  % (tmpfile, author, date), hg_repo)
   113 	res = cmd("hg commit -l %s -u '%s' -d '%s 0'"  % (tmpfile, author, date), hg_repo)
    95 	os.unlink(tmpfile)
   114 	os.unlink(tmpfile)
       
   115 	new_tip = hg_tip(hg_repo)
       
   116 	if not new_tip == old_tip + 1:
       
   117 		# Sometimes we may have empty commits, we simply skip them
       
   118 		if res.strip().lower().find("nothing changed") != -1:
       
   119 			pass
       
   120 		else:
       
   121 			error("Mercurial commit did not work as expected: " + res)
       
   122 
       
   123 def hg_tip( hg_repo ):
       
   124 	"""Returns the latest local revision number in the given repository."""
       
   125 	tip = cmd("hg tip", hg_repo, silent=True)
       
   126 	tip = tip.split("\n")[0].split(":")[1].strip()
       
   127 	return int(tip)
    96 
   128 
    97 # ------------------------------------------------------------------------------
   129 # ------------------------------------------------------------------------------
    98 #
   130 #
    99 # Main
   131 # Main
   100 #
   132 #
   104 	args = sys.argv[1:]
   136 	args = sys.argv[1:]
   105 	# We parse the arguments
   137 	# We parse the arguments
   106 	if len(args)   == 2:
   138 	if len(args)   == 2:
   107 		darcs_repo = os.path.abspath(args[0])
   139 		darcs_repo = os.path.abspath(args[0])
   108 		hg_repo    = os.path.abspath(args[1])
   140 		hg_repo    = os.path.abspath(args[1])
       
   141 		skip       = None
       
   142 	elif len(args) == 3:
       
   143 		darcs_repo = os.path.abspath(args[0])
       
   144 		hg_repo    = os.path.abspath(args[1])
       
   145 		skip       = int(args[2])
   109 	else:
   146 	else:
   110 		print USAGE
   147 		print USAGE
   111 		sys.exit(-1)
   148 		sys.exit(-1)
   112 	# Initializes the target repo
   149 	# Initializes the target repo
   113 	if not os.path.isdir(darcs_repo + "/_darcs"):
   150 	if not os.path.isdir(darcs_repo + "/_darcs"):
   114 		print "No darcs directory found at: " + darc_repo
   151 		print "No darcs directory found at: " + darcs_repo
   115 		sys.exit(-1)
   152 		sys.exit(-1)
   116 	if not os.path.isdir(hg_repo):
   153 	if not os.path.isdir(hg_repo):
   117 		os.mkdir(hg_repo)
   154 		os.mkdir(hg_repo)
   118 	else:
   155 	elif skip == None:
   119 		print "Given HG repository must not exist. It will be created"
   156 		print "Given HG repository must not exist when no SKIP is specified."
   120 		sys.exit(-1)
   157 		sys.exit(-1)
   121 	cmd("hg init '%s'" % (hg_repo))
   158 	if skip == None:
   122 	cmd("darcs initialize", hg_repo)
   159 		cmd("hg init '%s'" % (hg_repo))
       
   160 		cmd("darcs initialize", hg_repo)
   123 	# Get the changes from the Darcs repository
   161 	# Get the changes from the Darcs repository
   124 	for hash, author, date, summary, description in darcs_changes(darcs_repo):
   162 	change_number = 0
   125 		text = summary + "\n" + description
   163 	for author, date, summary, chash, description in darcs_changes(darcs_repo):
   126 		darcs_pull(hg_repo, darcs_repo, hash)
   164 		print "== changeset", change_number,
   127 		epoch = int(mktime(strptime(date, '%Y%m%d%H%M%S')))
   165 		if skip != None and change_number <= skip:
   128 		hg_commit(hg_repo, text, author, epoch)
   166 			print "(skipping)"
       
   167 		else:
       
   168 			text = summary + "\n" + description
       
   169 			darcs_pull(hg_repo, darcs_repo, chash)
       
   170 			# The commit hash has a date like 20021020201112
       
   171 			# --------------------------------YYYYMMDDHHMMSS
       
   172 			date = chash.split("-")[0]
       
   173 			epoch = int(mktime(strptime(date, '%Y%m%d%H%M%S')))
       
   174 			hg_commit(hg_repo, text, author, epoch)
       
   175 		change_number += 1
       
   176 	print "Darcs repository (_darcs) was not deleted. You can keep or remove it."
   129 
   177 
   130 # EOF
   178 # EOF
   131