tangled
alpha
login
or
join now
jcs.org
/
openbsd-commitid
0
fork
atom
script to retroactively add commitids to past openbsd commits
0
fork
atom
overview
issues
pulls
pipelines
initial revision
jcs.org
11 years ago
81349ca5
+291
1 changed file
expand all
collapse all
unified
split
openbsd-commitid.rb
+291
openbsd-commitid.rb
reviewed
···
1
1
+
#!/usr/bin/ruby
2
2
+
3
3
+
require "sqlite3"
4
4
+
5
5
+
class RCSFile
6
6
+
attr_accessor :revisions
7
7
+
8
8
+
def initialize(file)
9
9
+
@revisions = {}
10
10
+
11
11
+
# rcs modified to end revs in ###
12
12
+
blocks = `rlog #{file}`.force_encoding("binary").
13
13
+
split(/^(-{28}|={77})###\n?$/).reject{|b| b.match(/^(-{28}|={77})$/) }
14
14
+
15
15
+
if !blocks.first.match(/^RCS file/)
16
16
+
raise "file #{file} didn't come out of rlog properly"
17
17
+
end
18
18
+
19
19
+
blocks.shift
20
20
+
blocks.each do |block|
21
21
+
rev = RCSRevision.new(block)
22
22
+
if @revisions[rev.revision]
23
23
+
raise "duplicate revision #{rev.revision} in #{file}"
24
24
+
end
25
25
+
@revisions[rev.revision] = rev
26
26
+
end
27
27
+
end
28
28
+
end
29
29
+
30
30
+
class RCSRevision
31
31
+
attr_accessor :revision, :date, :author, :state, :lines, :commitid, :log
32
32
+
33
33
+
# str: "revision 1.7\ndate: 1996/12/14 12:17:33; author: mickey; state: Exp; lines: +3 -3;\n-Wall'ing."
34
34
+
def initialize(str)
35
35
+
@revision = nil
36
36
+
@date = 0
37
37
+
@author = nil
38
38
+
@state = nil
39
39
+
@lines = nil
40
40
+
@commitid = nil
41
41
+
@log = nil
42
42
+
43
43
+
lines = str.gsub(/^\s*/, "").split("\n")
44
44
+
# -> [
45
45
+
# "revision 1.7",
46
46
+
# "date: 1996/12/14 12:17:33; author: mickey; state: Exp; lines: +3 -3;",
47
47
+
# "-Wall'ing."
48
48
+
# ]
49
49
+
50
50
+
# strip out possible branches line in log
51
51
+
if lines[2].to_s.match(/^branches:\s+([\d\.]+)/)
52
52
+
lines.delete_at(2)
53
53
+
end
54
54
+
55
55
+
@revision = lines.first.scan(/^revision ([\d\.]+)($|\tlocked by)/).first.first
56
56
+
# -> "1.7"
57
57
+
58
58
+
# date/author/state/lines/commitid line
59
59
+
lines[1].split(/;[ \t]*/).each do |piece|
60
60
+
kv = piece.split(": ")
61
61
+
self.send(kv[0] + "=", kv[1])
62
62
+
end
63
63
+
# -> @date = "1996/12/14 12:17:33", @author = "mickey", ...
64
64
+
65
65
+
if m = @date.match(/^\d\d\d\d\/\d\d\/\d\d \d\d:\d\d:\d\d$/)
66
66
+
@date = DateTime.parse(@date).strftime("%s").to_i
67
67
+
else
68
68
+
raise "invalid date #{@date}"
69
69
+
end
70
70
+
# -> @date = 850565853
71
71
+
72
72
+
@log = lines[2, lines.count].join("\n")
73
73
+
end
74
74
+
end
75
75
+
76
76
+
class Scanner
77
77
+
def initialize(dbf, root)
78
78
+
@db = SQLite3::Database.new dbf
79
79
+
80
80
+
@db.execute "CREATE TABLE IF NOT EXISTS changesets
81
81
+
(id integer primary key, date integer, author text, commitid text,
82
82
+
log text)"
83
83
+
@db.execute "CREATE UNIQUE INDEX IF NOT EXISTS u_commitid ON changesets
84
84
+
(commitid)"
85
85
+
86
86
+
@db.execute "CREATE TABLE IF NOT EXISTS files
87
87
+
(id integer primary key, file text)"
88
88
+
@db.execute "CREATE UNIQUE INDEX IF NOT EXISTS u_file ON files
89
89
+
(file)"
90
90
+
91
91
+
@db.execute "CREATE TABLE IF NOT EXISTS revisions
92
92
+
(id integer primary key, file_id integer, changeset_id integer,
93
93
+
date integer, version text, author text, commitid text, log text)"
94
94
+
@db.execute "CREATE UNIQUE INDEX IF NOT EXISTS u_revision ON revisions
95
95
+
(file_id, version)"
96
96
+
@db.execute "CREATE INDEX IF NOT EXISTS empty_changesets ON revisions
97
97
+
(changeset_id)"
98
98
+
@db.execute "CREATE INDEX IF NOT EXISTS cs_by_commitid ON revisions
99
99
+
(commitid, changeset_id)"
100
100
+
@db.execute "CREATE INDEX IF NOT EXISTS all_revs_by_author ON revisions
101
101
+
(author, date)"
102
102
+
103
103
+
@db.results_as_hash = true
104
104
+
105
105
+
@root = root
106
106
+
end
107
107
+
108
108
+
def recurse(dir = nil)
109
109
+
if !dir
110
110
+
dir = @root
111
111
+
end
112
112
+
113
113
+
puts "recursing into #{dir}"
114
114
+
115
115
+
Dir.glob((dir + "/*").gsub(/\/\//, "/")).each do |f|
116
116
+
if Dir.exists? f
117
117
+
recurse(f)
118
118
+
elsif f.match(/,v$/)
119
119
+
scan(f)
120
120
+
end
121
121
+
end
122
122
+
end
123
123
+
124
124
+
def scan(f)
125
125
+
canfile = f[@root.length, f.length - @root.length].gsub(/\/Attic\//, "/")
126
126
+
puts " scanning file #{canfile}"
127
127
+
128
128
+
rcs = RCSFile.new(f)
129
129
+
130
130
+
fid = @db.execute("SELECT id FROM files WHERE file = ?", [ canfile ]).first
131
131
+
if !fid
132
132
+
@db.execute("INSERT INTO files (file) VALUES (?)", [ canfile ])
133
133
+
fid = @db.execute("SELECT id FROM files WHERE file = ?",
134
134
+
[ canfile ]).first
135
135
+
end
136
136
+
raise if !fid
137
137
+
138
138
+
rcs.revisions.each do |r,rev|
139
139
+
rid = @db.execute("SELECT id FROM revisions WHERE file_id = ? AND " +
140
140
+
"version = ?", [ fid["id"], r ]).first
141
141
+
142
142
+
if !rid
143
143
+
@db.execute("INSERT INTO revisions (file_id, date, version, author, " +
144
144
+
"commitid, log) VALUES (?, ?, ?, ?, ?, ?)", [ fid["id"], rev.date,
145
145
+
rev.revision, rev.author, rev.commitid, rev.log ])
146
146
+
147
147
+
puts " inserted #{r}, authored #{rev.date} by #{rev.author}" +
148
148
+
(rev.commitid ? ", commitid #{rev.commitid}" : "")
149
149
+
end
150
150
+
end
151
151
+
end
152
152
+
153
153
+
def stray_commitids_to_changesets
154
154
+
stray_commitids = @db.execute("SELECT DISTINCT author, commitid FROM " +
155
155
+
"revisions WHERE commitid IS NOT NULL AND changeset_id IS NULL")
156
156
+
stray_commitids.each do |row|
157
157
+
csid = @db.execute("SELECT id FROM changesets WHERE commitid = ?",
158
158
+
[ row["commitid"] ]).first
159
159
+
if !csid
160
160
+
@db.execute("INSERT INTO changesets (author, commitid) VALUES (?, ?)",
161
161
+
[ row["author"], row["commitid"] ])
162
162
+
csid = @db.execute("SELECT id FROM changesets WHERE commitid = ?",
163
163
+
[ row["commitid"] ]).first
164
164
+
end
165
165
+
raise if !csid
166
166
+
167
167
+
puts "commitid #{row["commitid"]} -> changeset #{csid["id"]}"
168
168
+
169
169
+
@db.execute("UPDATE revisions SET changeset_id = ? WHERE commitid = ?",
170
170
+
[ csid["id"], row["commitid"] ])
171
171
+
end
172
172
+
end
173
173
+
174
174
+
def group_into_changesets
175
175
+
new_sets = []
176
176
+
last_row = {}
177
177
+
cur_set = []
178
178
+
179
179
+
@db.execute("SELECT * FROM revisions WHERE changeset_id IS NULL ORDER " +
180
180
+
"BY author ASC, date ASC") do |row|
181
181
+
# commits by the same author with the same log message (unless they're
182
182
+
# initial imports - 1.1.1.1) within 30 seconds of each other are grouped
183
183
+
# together
184
184
+
if last_row.any? && row["author"] == last_row["author"] &&
185
185
+
(row["log"] == last_row["log"] || row["log"] == "Initial revision" ||
186
186
+
last_row["log"] == "Initial revision") &&
187
187
+
row["date"].to_i - last_row["date"].to_i <= 30
188
188
+
cur_set.push row["id"].to_i
189
189
+
elsif !last_row.any?
190
190
+
cur_set.push row["id"].to_i
191
191
+
else
192
192
+
if cur_set.any?
193
193
+
new_sets.push cur_set
194
194
+
cur_set = []
195
195
+
end
196
196
+
cur_set.push row["id"].to_i
197
197
+
end
198
198
+
199
199
+
last_row = row
200
200
+
end
201
201
+
202
202
+
if cur_set.any?
203
203
+
new_sets.push cur_set
204
204
+
end
205
205
+
206
206
+
new_sets.each do |s|
207
207
+
puts "new set with revision ids #{s.inspect}"
208
208
+
@db.execute("INSERT INTO changesets (id) VALUES (NULL)")
209
209
+
id = @db.execute("SELECT last_insert_rowid() AS id").first["id"]
210
210
+
raise if !id
211
211
+
212
212
+
@db.execute("UPDATE revisions SET changeset_id = ? WHERE id IN (" +
213
213
+
s.map{|a| "?" }.join(",") + ")", [ id ] + s)
214
214
+
end
215
215
+
216
216
+
if @db.execute("SELECT * FROM revisions WHERE changeset_id IS NULL").any?
217
217
+
raise "still have revisions with empty changesets"
218
218
+
end
219
219
+
end
220
220
+
221
221
+
def fill_in_changeset_data
222
222
+
cses = {}
223
223
+
@db.execute("SELECT id, commitid FROM changesets WHERE date IS NULL") do |c|
224
224
+
cses[c["id"]] = c["commitid"]
225
225
+
end
226
226
+
227
227
+
cses.each do |csid,comid|
228
228
+
date = nil
229
229
+
commitid = comid
230
230
+
log = nil
231
231
+
author = nil
232
232
+
233
233
+
@db.execute("SELECT * FROM revisions WHERE changeset_id = ? ORDER BY " +
234
234
+
"date ASC", [ csid ]) do |rev|
235
235
+
if !date
236
236
+
date = rev["date"]
237
237
+
end
238
238
+
239
239
+
if rev["log"] != "Initial revision"
240
240
+
log = rev["log"]
241
241
+
end
242
242
+
243
243
+
if author && rev["author"] != author
244
244
+
raise "authors different between revs of #{csid}"
245
245
+
else
246
246
+
author = rev["author"]
247
247
+
end
248
248
+
end
249
249
+
250
250
+
if commitid.to_s == ""
251
251
+
commitid = ""
252
252
+
while commitid.length < 16
253
253
+
c = rand(75) + 48
254
254
+
if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) ||
255
255
+
(c >= 97 && c <= 122))
256
256
+
commitid << c.chr
257
257
+
end
258
258
+
end
259
259
+
end
260
260
+
261
261
+
if !date
262
262
+
raise "no date for changeset #{csid}"
263
263
+
end
264
264
+
265
265
+
puts "changeset #{csid} -> commitid #{commitid}"
266
266
+
267
267
+
@db.execute("UPDATE changesets SET date = ?, commitid = ?, log = ?, " +
268
268
+
"author = ? WHERE id = ?", [ date, commitid, log, author, csid ])
269
269
+
end
270
270
+
end
271
271
+
272
272
+
def repo_surgery(checked_out_dir)
273
273
+
Dir.chdir(checked_out_dir)
274
274
+
system("cvs", "-q", "up", "-PACd")
275
275
+
276
276
+
@db.execute("SELECT files.file, changesets.commitid, revisions.version " +
277
277
+
"FROM revisions LEFT OUTER JOIN files ON files.id = file_id " +
278
278
+
"LEFT OUTER JOIN changesets ON revisions.changeset_id = changesets.id " +
279
279
+
"WHERE revisions.commitid IS NULL ORDER BY files.id") do |rev|
280
280
+
system("cvs", "-q", "admin", "-C",
281
281
+
"#{rev["version"]}:#{rev["commitid"]}", rev["file"].gsub(/,v$/, ""))
282
282
+
end
283
283
+
end
284
284
+
end
285
285
+
286
286
+
sc = Scanner.new("openbsdv.db", "/var/cvs/src/")
287
287
+
#sc.recurse
288
288
+
sc.group_into_changesets
289
289
+
sc.stray_commitids_to_changesets
290
290
+
sc.fill_in_changeset_data
291
291
+
sc.repo_surgery("/usr/src.local")