comparison contrib/darcs2hg.py @ 2587:fe3e87358b47

darcs2hg: pause and resume support, date extraction from commit hash, does not break on empty commits
author Sébastien Pierre <sebastien@xprima.com>
date Mon, 10 Jul 2006 12:19:37 -0400
parents 5ec2dded1bda
children 8210cf2ec19d
comparison
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