##// END OF EJS Templates
darcs2hg: pause and resume support, date extraction from commit hash, does not break on empty commits
Sébastien Pierre -
r2587:fe3e8735 default
parent child Browse files
Show More
@@ -1,131 +1,178 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
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
18 from time import strptime, mktime
17 from time import strptime, mktime
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()
55
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)
64
56 # ------------------------------------------------------------------------------
65 # ------------------------------------------------------------------------------
57 #
66 #
58 # Darcs interface
67 # Darcs interface
59 #
68 #
60 # ------------------------------------------------------------------------------
69 # ------------------------------------------------------------------------------
61
70
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 #
101 # ------------------------------------------------------------------------------
133 # ------------------------------------------------------------------------------
102
134
103 if __name__ == "__main__":
135 if __name__ == "__main__":
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
General Comments 0
You need to be logged in to leave comments. Login now