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 |
|