Mercurial > hg > sonic-visualiser
comparison vext.sml @ 1752:716e13004b19
Update Vext and subrepos
author | Chris Cannam |
---|---|
date | Thu, 09 Nov 2017 15:59:18 +0000 |
parents | ffe59b457557 |
children | edaa018a3731 |
comparison
equal
deleted
inserted
replaced
1751:5cbe4eb3d189 | 1752:716e13004b19 |
---|---|
36 shall not be used in advertising or otherwise to promote the sale, | 36 shall not be used in advertising or otherwise to promote the sale, |
37 use or other dealings in this Software without prior written | 37 use or other dealings in this Software without prior written |
38 authorization. | 38 authorization. |
39 *) | 39 *) |
40 | 40 |
41 val vext_version = "0.9.9" | 41 val vext_version = "0.9.91" |
42 | 42 |
43 | 43 |
44 datatype vcs = | 44 datatype vcs = |
45 HG | | 45 HG | |
46 GIT | 46 GIT |
156 | 156 |
157 (** Test whether the library is at the newest revision for the | 157 (** Test whether the library is at the newest revision for the |
158 given branch. False may indicate that the branch has advanced | 158 given branch. False may indicate that the branch has advanced |
159 or that the library is not on the branch at all. This function | 159 or that the library is not on the branch at all. This function |
160 may use the network to check for new revisions *) | 160 may use the network to check for new revisions *) |
161 val is_newest : context -> libname * branch -> bool result | 161 val is_newest : context -> libname * source * branch -> bool result |
162 | 162 |
163 (** Test whether the library is at the newest revision available | 163 (** Test whether the library is at the newest revision available |
164 locally for the given branch. False may indicate that the | 164 locally for the given branch. False may indicate that the |
165 branch has advanced or that the library is not on the branch | 165 branch has advanced or that the library is not on the branch |
166 at all. This function must not use the network *) | 166 at all. This function must not use the network *) |
173 (** Check out, i.e. clone a fresh copy of, the repo for the given | 173 (** Check out, i.e. clone a fresh copy of, the repo for the given |
174 library on the given branch *) | 174 library on the given branch *) |
175 val checkout : context -> libname * source * branch -> unit result | 175 val checkout : context -> libname * source * branch -> unit result |
176 | 176 |
177 (** Update the library to the given branch tip *) | 177 (** Update the library to the given branch tip *) |
178 val update : context -> libname * branch -> id_or_tag result | 178 val update : context -> libname * source * branch -> id_or_tag result |
179 | 179 |
180 (** Update the library to the given specific id or tag *) | 180 (** Update the library to the given specific id or tag *) |
181 val update_to : context -> libname * id_or_tag -> id_or_tag result | 181 val update_to : context -> libname * source * id_or_tag -> id_or_tag result |
182 end | 182 end |
183 | 183 |
184 signature LIB_CONTROL = sig | 184 signature LIB_CONTROL = sig |
185 val review : context -> libspec -> (libstate * localstate) result | 185 val review : context -> libspec -> (libstate * localstate) result |
186 val status : context -> libspec -> (libstate * localstate) result | 186 val status : context -> libspec -> (libstate * localstate) result |
208 case OS.Process.getEnv "VEXT_VERBOSE" of | 208 case OS.Process.getEnv "VEXT_VERBOSE" of |
209 SOME "0" => false | 209 SOME "0" => false |
210 | SOME _ => true | 210 | SOME _ => true |
211 | NONE => false | 211 | NONE => false |
212 | 212 |
213 fun split_relative path desc = | |
214 case OS.Path.fromString path of | |
215 { isAbs = true, ... } => raise Fail (desc ^ " may not be absolute") | |
216 | { arcs, ... } => arcs | |
217 | |
213 fun extpath ({ rootpath, extdir, ... } : context) = | 218 fun extpath ({ rootpath, extdir, ... } : context) = |
214 let val { isAbs, vol, arcs } = OS.Path.fromString rootpath | 219 let val { isAbs, vol, arcs } = OS.Path.fromString rootpath |
215 in OS.Path.toString { | 220 in OS.Path.toString { |
216 isAbs = isAbs, | 221 isAbs = isAbs, |
217 vol = vol, | 222 vol = vol, |
218 arcs = arcs @ [ extdir ] | 223 arcs = arcs @ |
224 split_relative extdir "extdir" | |
219 } | 225 } |
220 end | 226 end |
221 | 227 |
222 fun subpath ({ rootpath, extdir, ... } : context) libname remainder = | 228 fun subpath ({ rootpath, extdir, ... } : context) libname remainder = |
223 (* NB libname is allowed to be a path fragment, e.g. foo/bar *) | 229 (* NB libname is allowed to be a path fragment, e.g. foo/bar *) |
224 let val { isAbs, vol, arcs } = OS.Path.fromString rootpath | 230 let val { isAbs, vol, arcs } = OS.Path.fromString rootpath |
225 val split = String.fields (fn c => c = #"/") | |
226 in OS.Path.toString { | 231 in OS.Path.toString { |
227 isAbs = isAbs, | 232 isAbs = isAbs, |
228 vol = vol, | 233 vol = vol, |
229 arcs = arcs @ [ extdir ] @ split libname @ split remainder | 234 arcs = arcs @ |
235 split_relative extdir "extdir" @ | |
236 split_relative libname "library path" @ | |
237 split_relative remainder "subpath" | |
230 } | 238 } |
231 end | 239 end |
232 | 240 |
233 fun libpath context "" = | 241 fun libpath context "" = |
234 extpath context | 242 extpath context |
383 | OK () => ((OS.FileSys.mkDir path; OK ()) | 391 | OK () => ((OS.FileSys.mkDir path; OK ()) |
384 handle OS.SysErr (e, _) => | 392 handle OS.SysErr (e, _) => |
385 ERROR ("Directory creation failed: " ^ e)) | 393 ERROR ("Directory creation failed: " ^ e)) |
386 | 394 |
387 fun mkpath path = | 395 fun mkpath path = |
388 (* strip any trailing '/', something isDir doesn't always handle *) | 396 mkpath' (OS.Path.mkCanonical path) |
389 case rev (explode path) of | 397 |
390 #"/"::rest => mkpath (implode (rev rest)) | 398 fun rmpath' path = |
391 | _ => mkpath' path | |
392 | |
393 fun rmpath path = | |
394 let open OS | 399 let open OS |
395 fun files_from dirstream = | 400 fun files_from dirstream = |
396 case FileSys.readDir dirstream of | 401 case FileSys.readDir dirstream of |
397 NONE => [] | 402 NONE => [] |
398 | SOME file => | 403 | SOME file => |
417 else FileSys.remove path | 422 else FileSys.remove path |
418 in | 423 in |
419 (remove path; OK ()) | 424 (remove path; OK ()) |
420 handle SysErr (e, _) => ERROR ("Path removal failed: " ^ e) | 425 handle SysErr (e, _) => ERROR ("Path removal failed: " ^ e) |
421 end | 426 end |
427 | |
428 fun rmpath path = | |
429 rmpath' (OS.Path.mkCanonical path) | |
430 | |
422 end | 431 end |
423 | 432 |
424 functor LibControlFn (V: VCS_CONTROL) :> LIB_CONTROL = struct | 433 functor LibControlFn (V: VCS_CONTROL) :> LIB_CONTROL = struct |
425 | 434 |
426 (* Valid states for unpinned libraries: | 435 (* Valid states for unpinned libraries: |
447 | 456 |
448 - ABSENT: Repo doesn't exist here at all. | 457 - ABSENT: Repo doesn't exist here at all. |
449 *) | 458 *) |
450 | 459 |
451 fun check with_network context | 460 fun check with_network context |
452 ({ libname, branch, project_pin, lock_pin, ... } : libspec) = | 461 ({ libname, source, branch, |
462 project_pin, lock_pin, ... } : libspec) = | |
453 let fun check_unpinned () = | 463 let fun check_unpinned () = |
454 let val is_newest = if with_network | 464 let val newest = |
455 then V.is_newest | 465 if with_network |
456 else V.is_newest_locally | 466 then V.is_newest context (libname, source, branch) |
467 else V.is_newest_locally context (libname, branch) | |
457 in | 468 in |
458 case is_newest context (libname, branch) of | 469 case newest of |
459 ERROR e => ERROR e | 470 ERROR e => ERROR e |
460 | OK true => OK CORRECT | 471 | OK true => OK CORRECT |
461 | OK false => | 472 | OK false => |
462 case V.is_on_branch context (libname, branch) of | 473 case V.is_on_branch context (libname, branch) of |
463 ERROR e => ERROR e | 474 ERROR e => ERROR e |
501 | 512 |
502 fun update context | 513 fun update context |
503 ({ libname, source, branch, | 514 ({ libname, source, branch, |
504 project_pin, lock_pin, ... } : libspec) = | 515 project_pin, lock_pin, ... } : libspec) = |
505 let fun update_unpinned () = | 516 let fun update_unpinned () = |
506 case V.is_newest context (libname, branch) of | 517 case V.is_newest context (libname, source, branch) of |
507 ERROR e => ERROR e | 518 ERROR e => ERROR e |
508 | OK true => V.id_of context libname | 519 | OK true => V.id_of context libname |
509 | OK false => V.update context (libname, branch) | 520 | OK false => V.update context (libname, source, branch) |
510 fun update_pinned target = | 521 fun update_pinned target = |
511 case V.is_at context (libname, target) of | 522 case V.is_at context (libname, target) of |
512 ERROR e => ERROR e | 523 ERROR e => ERROR e |
513 | OK true => OK target | 524 | OK true => OK target |
514 | OK false => V.update_to context (libname, target) | 525 | OK false => V.update_to context (libname, source, target) |
515 fun update' () = | 526 fun update' () = |
516 case lock_pin of | 527 case lock_pin of |
517 PINNED target => update_pinned target | 528 PINNED target => update_pinned target |
518 | UNPINNED => | 529 | UNPINNED => |
519 case project_pin of | 530 case project_pin of |
1154 (login_for context service) | 1165 (login_for context service) |
1155 (#providers context) | 1166 (#providers context) |
1156 end | 1167 end |
1157 | 1168 |
1158 structure HgControl :> VCS_CONTROL = struct | 1169 structure HgControl :> VCS_CONTROL = struct |
1159 | 1170 |
1171 (* Pulls always use an explicit URL, never just the default | |
1172 remote, in order to ensure we update properly if the location | |
1173 given in the project file changes. *) | |
1174 | |
1160 type vcsstate = { id: string, modified: bool, | 1175 type vcsstate = { id: string, modified: bool, |
1161 branch: string, tags: string list } | 1176 branch: string, tags: string list } |
1162 | 1177 |
1163 val hg_args = [ "--config", "ui.interactive=true" ] | 1178 val hg_args = [ "--config", "ui.interactive=true" ] |
1164 | 1179 |
1231 fun is_newest_locally context (libname, branch) = | 1246 fun is_newest_locally context (libname, branch) = |
1232 case hg_command_output context libname | 1247 case hg_command_output context libname |
1233 ["log", "-l1", | 1248 ["log", "-l1", |
1234 "-b", branch_name branch, | 1249 "-b", branch_name branch, |
1235 "--template", "{node}"] of | 1250 "--template", "{node}"] of |
1236 ERROR e => ERROR e | 1251 ERROR e => OK false (* desired branch does not exist *) |
1237 | OK newest_in_repo => is_at context (libname, newest_in_repo) | 1252 | OK newest_in_repo => is_at context (libname, newest_in_repo) |
1238 | 1253 |
1239 fun pull context libname = | 1254 fun pull context (libname, source) = |
1240 hg_command context libname | 1255 let val url = remote_for context (libname, source) |
1241 (if FileBits.verbose () | 1256 in |
1242 then ["pull"] | 1257 hg_command context libname |
1243 else ["pull", "-q"]) | 1258 (if FileBits.verbose () |
1244 | 1259 then ["pull", url] |
1245 fun is_newest context (libname, branch) = | 1260 else ["pull", "-q", url]) |
1261 end | |
1262 | |
1263 fun is_newest context (libname, source, branch) = | |
1246 case is_newest_locally context (libname, branch) of | 1264 case is_newest_locally context (libname, branch) of |
1247 ERROR e => ERROR e | 1265 ERROR e => ERROR e |
1248 | OK false => OK false | 1266 | OK false => OK false |
1249 | OK true => | 1267 | OK true => |
1250 case pull context libname of | 1268 case pull context (libname, source) of |
1251 ERROR e => ERROR e | 1269 ERROR e => ERROR e |
1252 | _ => is_newest_locally context (libname, branch) | 1270 | _ => is_newest_locally context (libname, branch) |
1253 | 1271 |
1254 fun is_modified_locally context libname = | 1272 fun is_modified_locally context libname = |
1255 case current_state context libname of | 1273 case current_state context libname of |
1267 | _ => hg_command context "" | 1285 | _ => hg_command context "" |
1268 ["clone", "-u", branch_name branch, | 1286 ["clone", "-u", branch_name branch, |
1269 url, libname] | 1287 url, libname] |
1270 end | 1288 end |
1271 | 1289 |
1272 fun update context (libname, branch) = | 1290 fun update context (libname, source, branch) = |
1273 let val pull_result = pull context libname | 1291 let val pull_result = pull context (libname, source) |
1274 in | 1292 in |
1275 case hg_command context libname ["update", branch_name branch] of | 1293 case hg_command context libname ["update", branch_name branch] of |
1276 ERROR e => ERROR e | 1294 ERROR e => ERROR e |
1277 | _ => | 1295 | _ => |
1278 case pull_result of | 1296 case pull_result of |
1279 ERROR e => ERROR e | 1297 ERROR e => ERROR e |
1280 | _ => id_of context libname | 1298 | _ => id_of context libname |
1281 end | 1299 end |
1282 | 1300 |
1283 fun update_to context (libname, "") = | 1301 fun update_to context (libname, _, "") = |
1284 ERROR "Non-empty id (tag or revision id) required for update_to" | 1302 ERROR "Non-empty id (tag or revision id) required for update_to" |
1285 | update_to context (libname, id) = | 1303 | update_to context (libname, source, id) = |
1286 let val pull_result = pull context libname | 1304 let val pull_result = pull context (libname, source) |
1287 in | 1305 in |
1288 case hg_command context libname ["update", "-r", id] of | 1306 case hg_command context libname ["update", "-r", id] of |
1289 OK _ => id_of context libname | 1307 OK _ => id_of context libname |
1290 | ERROR e => | 1308 | ERROR e => |
1291 case pull_result of | 1309 case pull_result of |
1296 end | 1314 end |
1297 | 1315 |
1298 structure GitControl :> VCS_CONTROL = struct | 1316 structure GitControl :> VCS_CONTROL = struct |
1299 | 1317 |
1300 (* With Git repos we always operate in detached HEAD state. Even | 1318 (* With Git repos we always operate in detached HEAD state. Even |
1301 the master branch is checked out using the remote reference, | 1319 the master branch is checked out using a remote reference |
1302 origin/master. *) | 1320 (vext/master). The remote we use is always named vext, and we |
1321 update it to the expected URL each time we fetch, in order to | |
1322 ensure we update properly if the location given in the project | |
1323 file changes. The origin remote is unused. *) | |
1303 | 1324 |
1304 fun git_command context libname args = | 1325 fun git_command context libname args = |
1305 FileBits.command context libname ("git" :: args) | 1326 FileBits.command context libname ("git" :: args) |
1306 | 1327 |
1307 fun git_command_output context libname args = | 1328 fun git_command_output context libname args = |
1317 fun branch_name branch = case branch of | 1338 fun branch_name branch = case branch of |
1318 DEFAULT_BRANCH => "master" | 1339 DEFAULT_BRANCH => "master" |
1319 | BRANCH "" => "master" | 1340 | BRANCH "" => "master" |
1320 | BRANCH b => b | 1341 | BRANCH b => b |
1321 | 1342 |
1322 fun remote_branch_name branch = "origin/" ^ branch_name branch | 1343 val our_remote = "vext" |
1344 | |
1345 fun remote_branch_name branch = our_remote ^ "/" ^ branch_name branch | |
1323 | 1346 |
1324 fun checkout context (libname, source, branch) = | 1347 fun checkout context (libname, source, branch) = |
1325 let val url = remote_for context (libname, source) | 1348 let val url = remote_for context (libname, source) |
1326 in | 1349 in |
1327 (* make the lib dir rather than just the ext dir, since | 1350 (* make the lib dir rather than just the ext dir, since |
1328 the lib dir might be nested and git will happily check | 1351 the lib dir might be nested and git will happily check |
1329 out into an existing empty dir anyway *) | 1352 out into an existing empty dir anyway *) |
1330 case FileBits.mkpath (FileBits.libpath context libname) of | 1353 case FileBits.mkpath (FileBits.libpath context libname) of |
1331 OK () => git_command context "" | 1354 OK () => git_command context "" |
1332 ["clone", "-b", | 1355 ["clone", "--origin", our_remote, |
1333 branch_name branch, | 1356 "--branch", branch_name branch, |
1334 url, libname] | 1357 url, libname] |
1335 | ERROR e => ERROR e | 1358 | ERROR e => ERROR e |
1359 end | |
1360 | |
1361 fun add_our_remote context (libname, source) = | |
1362 (* When we do the checkout ourselves (above), we add the | |
1363 remote at the same time. But if the repo was cloned by | |
1364 someone else, we'll need to do it after the fact. Git | |
1365 doesn't seem to have a means to add a remote or change its | |
1366 url if it already exists; seems we have to do this: *) | |
1367 let val url = remote_for context (libname, source) | |
1368 in | |
1369 case git_command context libname | |
1370 ["remote", "set-url", our_remote, url] of | |
1371 OK () => OK () | |
1372 | ERROR e => git_command context libname | |
1373 ["remote", "add", "-f", our_remote, url] | |
1336 end | 1374 end |
1337 | 1375 |
1338 (* NB git rev-parse HEAD shows revision id of current checkout; | 1376 (* NB git rev-parse HEAD shows revision id of current checkout; |
1339 git rev-list -1 <tag> shows revision id of revision with that tag *) | 1377 git rev-list -1 <tag> shows revision id of revision with that tag *) |
1340 | 1378 |
1341 fun id_of context libname = | 1379 fun id_of context libname = |
1342 git_command_output context libname ["rev-parse", "HEAD"] | 1380 git_command_output context libname ["rev-parse", "HEAD"] |
1343 | 1381 |
1344 fun is_at context (libname, id_or_tag) = | 1382 fun is_at context (libname, id_or_tag) = |
1345 case id_of context libname of | 1383 case id_of context libname of |
1346 ERROR e => ERROR e | 1384 ERROR e => OK false (* HEAD nonexistent, expected in empty repo *) |
1347 | OK id => | 1385 | OK id => |
1348 if String.isPrefix id_or_tag id orelse | 1386 if String.isPrefix id_or_tag id orelse |
1349 String.isPrefix id id_or_tag | 1387 String.isPrefix id id_or_tag |
1350 then OK true | 1388 then OK true |
1351 else | 1389 else |
1352 case git_command_output context libname | 1390 case git_command_output context libname |
1353 ["show-ref", | 1391 ["show-ref", |
1354 "refs/tags/" ^ id_or_tag] of | 1392 "refs/tags/" ^ id_or_tag, |
1393 "--"] of | |
1355 OK "" => OK false | 1394 OK "" => OK false |
1356 | ERROR _ => OK false | 1395 | ERROR _ => OK false |
1357 | OK s => OK (id = hd (String.tokens (fn c => c = #" ") s)) | 1396 | OK s => OK (id = hd (String.tokens (fn c => c = #" ") s)) |
1358 | 1397 |
1359 fun branch_tip context (libname, branch) = | 1398 fun branch_tip context (libname, branch) = |
1399 (* We don't have access to the source info or the network | |
1400 here, as this is used by status (e.g. via is_on_branch) as | |
1401 well as review. It's possible the remote branch won't exist, | |
1402 e.g. if the repo was checked out by something other than | |
1403 Vext, and if that's the case, we can't add it here; we'll | |
1404 just have to fail, since checking against local branches | |
1405 instead could produce the wrong result. *) | |
1360 git_command_output context libname | 1406 git_command_output context libname |
1361 ["rev-list", "-1", | 1407 ["rev-list", "-1", |
1362 remote_branch_name branch] | 1408 remote_branch_name branch, "--"] |
1363 | 1409 |
1364 fun is_newest_locally context (libname, branch) = | 1410 fun is_newest_locally context (libname, branch) = |
1365 case branch_tip context (libname, branch) of | 1411 case branch_tip context (libname, branch) of |
1366 ERROR e => ERROR e | 1412 ERROR e => OK false |
1367 | OK rev => is_at context (libname, rev) | 1413 | OK rev => is_at context (libname, rev) |
1368 | 1414 |
1369 fun is_on_branch context (libname, branch) = | 1415 fun is_on_branch context (libname, branch) = |
1370 case branch_tip context (libname, branch) of | 1416 case branch_tip context (libname, branch) of |
1371 ERROR e => ERROR e | 1417 ERROR e => OK false |
1372 | OK rev => | 1418 | OK rev => |
1373 case is_at context (libname, rev) of | 1419 case is_at context (libname, rev) of |
1374 ERROR e => ERROR e | 1420 ERROR e => ERROR e |
1375 | OK true => OK true | 1421 | OK true => OK true |
1376 | OK false => | 1422 | OK false => |
1378 ["merge-base", "--is-ancestor", | 1424 ["merge-base", "--is-ancestor", |
1379 "HEAD", remote_branch_name branch] of | 1425 "HEAD", remote_branch_name branch] of |
1380 ERROR e => OK false (* cmd returns non-zero for no *) | 1426 ERROR e => OK false (* cmd returns non-zero for no *) |
1381 | _ => OK true | 1427 | _ => OK true |
1382 | 1428 |
1383 fun is_newest context (libname, branch) = | 1429 fun fetch context (libname, source) = |
1384 case is_newest_locally context (libname, branch) of | 1430 case add_our_remote context (libname, source) of |
1385 ERROR e => ERROR e | 1431 ERROR e => ERROR e |
1386 | OK false => OK false | 1432 | _ => git_command context libname ["fetch", our_remote] |
1387 | OK true => | 1433 |
1388 case git_command context libname ["fetch"] of | 1434 fun is_newest context (libname, source, branch) = |
1435 case add_our_remote context (libname, source) of | |
1436 ERROR e => ERROR e | |
1437 | OK () => | |
1438 case is_newest_locally context (libname, branch) of | |
1389 ERROR e => ERROR e | 1439 ERROR e => ERROR e |
1390 | _ => is_newest_locally context (libname, branch) | 1440 | OK false => OK false |
1441 | OK true => | |
1442 case fetch context (libname, source) of | |
1443 ERROR e => ERROR e | |
1444 | _ => is_newest_locally context (libname, branch) | |
1391 | 1445 |
1392 fun is_modified_locally context libname = | 1446 fun is_modified_locally context libname = |
1393 case git_command_output context libname ["status", "--porcelain"] of | 1447 case git_command_output context libname ["status", "--porcelain"] of |
1394 ERROR e => ERROR e | 1448 ERROR e => ERROR e |
1395 | OK "" => OK false | 1449 | OK "" => OK false |
1400 branch, as that will succeed even if the branch isn't up to | 1454 branch, as that will succeed even if the branch isn't up to |
1401 date. We could checkout the branch and then fetch and merge, | 1455 date. We could checkout the branch and then fetch and merge, |
1402 but it's perhaps cleaner not to maintain a local branch at all, | 1456 but it's perhaps cleaner not to maintain a local branch at all, |
1403 but instead checkout the remote branch as a detached head. *) | 1457 but instead checkout the remote branch as a detached head. *) |
1404 | 1458 |
1405 fun update context (libname, branch) = | 1459 fun update context (libname, source, branch) = |
1406 case git_command context libname ["fetch"] of | 1460 case fetch context (libname, source) of |
1407 ERROR e => ERROR e | 1461 ERROR e => ERROR e |
1408 | _ => | 1462 | _ => |
1409 case git_command context libname ["checkout", "--detach", | 1463 case git_command context libname ["checkout", "--detach", |
1410 remote_branch_name branch] of | 1464 remote_branch_name branch] of |
1411 ERROR e => ERROR e | 1465 ERROR e => ERROR e |
1417 attempt the fetch first, though, purely in order to avoid ugly | 1471 attempt the fetch first, though, purely in order to avoid ugly |
1418 error messages in the common case where we're being asked to | 1472 error messages in the common case where we're being asked to |
1419 update to a new pin (from the lock file) that hasn't been | 1473 update to a new pin (from the lock file) that hasn't been |
1420 fetched yet. *) | 1474 fetched yet. *) |
1421 | 1475 |
1422 fun update_to context (libname, "") = | 1476 fun update_to context (libname, _, "") = |
1423 ERROR "Non-empty id (tag or revision id) required for update_to" | 1477 ERROR "Non-empty id (tag or revision id) required for update_to" |
1424 | update_to context (libname, id) = | 1478 | update_to context (libname, source, id) = |
1425 let val fetch_result = git_command context libname ["fetch"] | 1479 let val fetch_result = fetch context (libname, source) |
1426 in | 1480 in |
1427 case git_command context libname ["checkout", "--detach", id] of | 1481 case git_command context libname ["checkout", "--detach", id] of |
1428 OK _ => id_of context libname | 1482 OK _ => id_of context libname |
1429 | ERROR e => | 1483 | ERROR e => |
1430 case fetch_result of | 1484 case fetch_result of |