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