| author | wenzelm | 
| Sat, 11 Jan 2025 21:58:47 +0100 | |
| changeset 81766 | ebf113cd6d2c | 
| parent 80197 | 36547884db60 | 
| child 81821 | 8abdf3b0074b | 
| permissions | -rw-r--r-- | 
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 1 | /* Title: Pure/General/mercurial.scala | 
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 2 | Author: Makarius | 
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 3 | |
| 64280 | 4 | Support for Mercurial repositories, with local or remote repository clone | 
| 5 | and working directory (via ssh connection). | |
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 6 | */ | 
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 7 | |
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 8 | package isabelle | 
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 9 | |
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 10 | |
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 11 | import scala.annotation.tailrec | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 12 | import scala.collection.mutable | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 13 | |
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 14 | |
| 75393 | 15 | object Mercurial {
 | 
| 66895 | 16 | type Graph = isabelle.Graph[String, Unit] | 
| 17 | ||
| 18 | ||
| 75472 | 19 | |
| 20 | /** HTTP server **/ | |
| 73611 | 21 | |
| 75393 | 22 |   object Server {
 | 
| 73611 | 23 | def apply(root: String): Server = new Server(root) | 
| 24 | ||
| 75393 | 25 |     def start(root: Path): Server = {
 | 
| 73611 | 26 | val hg = repository(root) | 
| 73609 | 27 | |
| 73611 | 28 | val server_process = Future.promise[Bash.Process] | 
| 29 | val server_root = Future.promise[String] | |
| 30 |       Isabelle_Thread.fork("hg") {
 | |
| 31 | val process = | |
| 78768 
280a228dc2f1
prefer Exn.result: avoid accidental capture of interrupts, similar to ML;
 wenzelm parents: 
78592diff
changeset | 32 |           Exn.result { Bash.process(hg.command_line("serve", options = "--port 0 --print-url")) }
 | 
| 73611 | 33 | server_process.fulfill_result(process) | 
| 34 | Exn.release(process).result(progress_stdout = | |
| 35 |           line => if (!server_root.is_finished) {
 | |
| 36 |             server_root.fulfill(Library.try_unsuffix("/", line).getOrElse(line))
 | |
| 37 | }) | |
| 38 | } | |
| 39 | server_process.join | |
| 40 | ||
| 41 |       new Server(server_root.join) {
 | |
| 42 | override def close(): Unit = server_process.join.terminate() | |
| 43 | } | |
| 44 | } | |
| 73609 | 45 | } | 
| 46 | ||
| 75393 | 47 |   class Server private(val root: String) extends AutoCloseable {
 | 
| 73609 | 48 | override def toString: String = root | 
| 49 | ||
| 73611 | 50 | def close(): Unit = () | 
| 51 | ||
| 73610 | 52 | def changeset(rev: String = "tip", raw: Boolean = false): String = | 
| 53 | root + (if (raw) "/raw-rev/" else "/rev/") + rev | |
| 54 | ||
| 55 | def file(path: Path, rev: String = "tip", raw: Boolean = false): String = | |
| 76883 | 56 | root + (if (raw) "/raw-file/" else "/file/") + rev + "/" + File.standard_path(path) | 
| 73610 | 57 | |
| 58 | def archive(rev: String = "tip"): String = | |
| 59 | root + "/archive/" + rev + ".tar.gz" | |
| 60 | ||
| 61 | def read_changeset(rev: String = "tip"): String = | |
| 62 | Url.read(changeset(rev = rev, raw = true)) | |
| 63 | ||
| 64 | def read_file(path: Path, rev: String = "tip"): String = | |
| 65 | Url.read(file(path, rev = rev, raw = true)) | |
| 66 | ||
| 67 | def download_archive(rev: String = "tip", progress: Progress = new Progress): HTTP.Content = | |
| 68 | Isabelle_System.download(archive(rev = rev), progress = progress) | |
| 69 | ||
| 75393 | 70 |     def download_dir(dir: Path, rev: String = "tip", progress: Progress = new Progress): Unit = {
 | 
| 73610 | 71 | Isabelle_System.new_directory(dir) | 
| 75394 | 72 |       Isabelle_System.with_tmp_file("rev", ext = ".tar.gz") { archive_path =>
 | 
| 73610 | 73 | val content = download_archive(rev = rev, progress = progress) | 
| 74 | Bytes.write(archive_path, content.bytes) | |
| 75 |         progress.echo("Unpacking " + rev + ".tar.gz")
 | |
| 73624 | 76 |         Isabelle_System.gnutar("-xzf " + File.bash_path(archive_path),
 | 
| 76540 
83de6e9ae983
clarified signature: prefer Scala functions instead of shell scripts;
 wenzelm parents: 
76169diff
changeset | 77 | dir = dir, original_owner = true, strip = true).check | 
| 75394 | 78 | } | 
| 73610 | 79 | } | 
| 73609 | 80 | } | 
| 81 | ||
| 82 | ||
| 75472 | 83 | |
| 84 | /** repository commands **/ | |
| 85 | ||
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 86 | /* command-line syntax */ | 
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 87 | |
| 63999 | 88 | def optional(s: String, prefix: String = ""): String = | 
| 77368 | 89 | if_proper(s, " " + prefix + " " + Bash.string(s)) | 
| 63999 | 90 | |
| 91 | def opt_flag(flag: String, b: Boolean): String = if (b) " " + flag else "" | |
| 92 | def opt_rev(s: String): String = optional(s, "--rev") | |
| 93 | def opt_template(s: String): String = optional(s, "--template") | |
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 94 | |
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 95 | |
| 73520 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 96 | /* repository archives */ | 
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 97 | |
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 98 |   private val Archive_Node = """^node: (\S{12}).*$""".r
 | 
| 73521 | 99 | private val Archive_Tag = """^tag: (\S+).*$""".r | 
| 73520 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 100 | |
| 75393 | 101 |   sealed case class Archive_Info(lines: List[String]) {
 | 
| 73520 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 102 |     def id: Option[String] = lines.collectFirst({ case Archive_Node(a) => a })
 | 
| 78592 | 103 | def tags: List[String] = for (case Archive_Tag(tag) <- lines if tag != "tip") yield tag | 
| 73520 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 104 | } | 
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 105 | |
| 75393 | 106 |   def archive_info(root: Path): Option[Archive_Info] = {
 | 
| 73520 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 107 |     val path = root + Path.explode(".hg_archival.txt")
 | 
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 108 | if (path.is_file) Some(Archive_Info(Library.trim_split_lines(File.read(path)))) else None | 
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 109 | } | 
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 110 | |
| 73521 | 111 | def archive_id(root: Path): Option[String] = | 
| 112 | archive_info(root).flatMap(_.id) | |
| 113 | ||
| 114 | def archive_tags(root: Path): Option[String] = | |
| 115 |     archive_info(root).map(info => info.tags.mkString(" "))
 | |
| 73520 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 116 | |
| 
4cba4e250c28
clarified ISABELLE_ID: distribution vs. hg archive vs. hg repos;
 wenzelm parents: 
73483diff
changeset | 117 | |
| 75511 | 118 | /* hg_sync meta data */ | 
| 119 | ||
| 75514 | 120 |   object Hg_Sync {
 | 
| 121 | val NAME = ".hg_sync" | |
| 122 | val _NAME: String = " " + NAME | |
| 123 | val PATH: Path = Path.explode(NAME) | |
| 124 |     val PATH_ID: Path = PATH + Path.explode("id")
 | |
| 125 |     val PATH_LOG: Path = PATH + Path.explode("log")
 | |
| 126 |     val PATH_DIFF: Path = PATH + Path.explode("diff")
 | |
| 127 |     val PATH_STAT: Path = PATH + Path.explode("stat")
 | |
| 128 | ||
| 80197 | 129 | def ok(root: Path, ssh: SSH.System = SSH.Local): Boolean = ssh.is_dir(root + PATH) | 
| 130 | ||
| 77785 
721b3278c8e4
more direct Hg_Sync.check_directory via SSH operations;
 wenzelm parents: 
77783diff
changeset | 131 | def check_directory(root: Path, ssh: SSH.System = SSH.Local): Unit = | 
| 80197 | 132 |       if (ssh.is_dir(root) && !ok(root, ssh = ssh) && ssh.read_dir(root).nonEmpty) {
 | 
| 77785 
721b3278c8e4
more direct Hg_Sync.check_directory via SSH operations;
 wenzelm parents: 
77783diff
changeset | 133 |         error("No .hg_sync meta data in " + ssh.rsync_path(root))
 | 
| 
721b3278c8e4
more direct Hg_Sync.check_directory via SSH operations;
 wenzelm parents: 
77783diff
changeset | 134 | } | 
| 
721b3278c8e4
more direct Hg_Sync.check_directory via SSH operations;
 wenzelm parents: 
77783diff
changeset | 135 | |
| 75514 | 136 |     def directory(root: Path, ssh: SSH.System = SSH.Local): Directory = {
 | 
| 80197 | 137 | if (ok(root, ssh = ssh)) new Directory(root, ssh) | 
| 75514 | 138 |       else error("No .hg_sync directory found in " + ssh.rsync_path(root))
 | 
| 139 | } | |
| 140 | ||
| 141 | class Directory private [Hg_Sync](val root: Path, val ssh: SSH.System) | |
| 142 |     {
 | |
| 143 | override def toString: String = ssh.rsync_path(root) | |
| 144 | ||
| 145 | def read(path: Path): String = ssh.read(root + path) | |
| 146 | lazy val id: String = read(PATH_ID) | |
| 147 | lazy val log: String = read(PATH_LOG) | |
| 148 | lazy val diff: String = read(PATH_DIFF) | |
| 149 | lazy val stat: String = read(PATH_STAT) | |
| 150 | ||
| 151 |       def changed: Boolean = id.endsWith("+")
 | |
| 152 | } | |
| 153 | } | |
| 75511 | 154 | |
| 155 | ||
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 156 | /* repository access */ | 
| 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 157 | |
| 80188 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 158 | def detect_repository(root: Path, ssh: SSH.System = SSH.Local): Option[Repository] = | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 159 |     if (ssh.is_dir(root + Path.explode(".hg"))) {
 | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 160 | val hg = new Repository(root, ssh) | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 161 |       val result = hg.command("root")
 | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 162 | if (result.ok && ssh.eq_file(root, Path.explode(result.out))) Some(hg) else None | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 163 | } | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 164 | else None | 
| 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 165 | |
| 66570 | 166 | def is_repository(root: Path, ssh: SSH.System = SSH.Local): Boolean = | 
| 80188 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 167 | detect_repository(root, ssh = ssh).isDefined | 
| 64330 | 168 | |
| 75510 | 169 | def id_repository(root: Path, ssh: SSH.System = SSH.Local, rev: String = "tip"): Option[String] = | 
| 80188 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 170 | for (hg <- detect_repository(root, ssh = ssh)) yield hg.id(rev = rev) | 
| 75510 | 171 | |
| 80188 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 172 | def repository(root: Path, ssh: SSH.System = SSH.Local): Repository = | 
| 80190 | 173 |     detect_repository(root, ssh = ssh) getOrElse error("Bad hg repository " + ssh.expand_path(root))
 | 
| 64168 | 174 | |
| 75506 | 175 | def self_repository(): Repository = repository(Path.ISABELLE_HOME) | 
| 176 | ||
| 80189 | 177 | def find_repository(start: Path, ssh: SSH.System = SSH.Local): Option[Repository] = | 
| 178 | ssh.find_path(start, detect_repository(_, ssh = ssh)) | |
| 65559 | 179 | |
| 75474 | 180 | def the_repository(start: Path, ssh: SSH.System = SSH.Local): Repository = | 
| 181 | find_repository(start, ssh = ssh) getOrElse | |
| 182 |       error("No repository found in " + start.absolute)
 | |
| 183 | ||
| 75393 | 184 | private def make_repository( | 
| 185 | root: Path, | |
| 186 | cmd: String, | |
| 187 | args: String, | |
| 188 | ssh: SSH.System = SSH.Local | |
| 189 |   ) : Repository = {
 | |
| 64230 | 190 | val hg = new Repository(root, ssh) | 
| 72375 | 191 | ssh.make_directory(hg.root.dir) | 
| 67066 | 192 | hg.command(cmd, args, repository = false).check | 
| 64168 | 193 | hg | 
| 194 | } | |
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 195 | |
| 67065 | 196 | def init_repository(root: Path, ssh: SSH.System = SSH.Local): Repository = | 
| 67066 | 197 | make_repository(root, "init", ssh.bash_path(root), ssh = ssh) | 
| 67065 | 198 | |
| 199 | def clone_repository(source: String, root: Path, | |
| 200 | rev: String = "", options: String = "", ssh: SSH.System = SSH.Local): Repository = | |
| 201 | make_repository(root, "clone", | |
| 67066 | 202 | options + " " + Bash.string(source) + " " + ssh.bash_path(root) + opt_rev(rev), ssh = ssh) | 
| 67065 | 203 | |
| 75393 | 204 |   def setup_repository(source: String, root: Path, ssh: SSH.System = SSH.Local): Repository = {
 | 
| 66570 | 205 |     if (ssh.is_dir(root)) { val hg = repository(root, ssh = ssh); hg.pull(remote = source); hg }
 | 
| 66105 
8889aad1ff92
reverted 94cad7590015: does not help much on Windows;
 wenzelm parents: 
66104diff
changeset | 206 | else clone_repository(source, root, options = "--noupdate", ssh = ssh) | 
| 64252 
e84cba30d7ff
clarified setup_repository: more uniform pull vs. clone, without update;
 wenzelm parents: 
64233diff
changeset | 207 | } | 
| 64230 | 208 | |
| 75393 | 209 |   class Repository private[Mercurial](root_path: Path, ssh: SSH.System = SSH.Local) {
 | 
| 64167 
097d122222f6
support remote repositories via ssh command execution;
 wenzelm parents: 
64162diff
changeset | 210 | hg => | 
| 
097d122222f6
support remote repositories via ssh command execution;
 wenzelm parents: 
64162diff
changeset | 211 | |
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 212 | val root: Path = ssh.expand_path(root_path) | 
| 66570 | 213 | def root_url: String = ssh.hg_url + root.implode | 
| 64230 | 214 | |
| 71562 | 215 | override def toString: String = ssh.hg_url + root.implode | 
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 216 | |
| 75393 | 217 | def command_line( | 
| 218 | name: String, | |
| 219 | args: String = "", | |
| 220 | options: String = "", | |
| 221 | repository: Boolean = true | |
| 222 |     ): String = {
 | |
| 73611 | 223 |       "export LANG=C HGPLAIN=\n\"${HG:-hg}\" --config " + Bash.string("defaults." + name + "=") +
 | 
| 224 | (if (repository) " --repository " + ssh.bash_path(root) else "") + | |
| 225 | " --noninteractive " + name + " " + options + " " + args | |
| 226 | } | |
| 227 | ||
| 228 | def command( | |
| 75393 | 229 | name: String, | 
| 230 | args: String = "", | |
| 231 | options: String = "", | |
| 232 | repository: Boolean = true | |
| 233 |     ): Process_Result = {
 | |
| 73611 | 234 | ssh.execute(command_line(name, args = args, options = options, repository = repository)) | 
| 64167 
097d122222f6
support remote repositories via ssh command execution;
 wenzelm parents: 
64162diff
changeset | 235 | } | 
| 63999 | 236 | |
| 67068 | 237 | def add(files: List[Path]): Unit = | 
| 71601 | 238 |       hg.command("add", files.map(ssh.bash_path).mkString(" "))
 | 
| 67068 | 239 | |
| 64505 | 240 | def archive(target: String, rev: String = "", options: String = ""): Unit = | 
| 241 |       hg.command("archive", opt_rev(rev) + " " + Bash.string(target), options).check
 | |
| 242 | ||
| 63999 | 243 |     def heads(template: String = "{node|short}\n", options: String = ""): List[String] =
 | 
| 64168 | 244 |       hg.command("heads", opt_template(template), options).check.out_lines
 | 
| 63999 | 245 | |
| 64232 
367d83d6030e
clarified hg.id operation, with explicit tip as default;
 wenzelm parents: 
64230diff
changeset | 246 | def identify(rev: String = "tip", options: String = ""): String = | 
| 64168 | 247 |       hg.command("id", opt_rev(rev), options).check.out_lines.headOption getOrElse ""
 | 
| 63998 | 248 | |
| 64232 
367d83d6030e
clarified hg.id operation, with explicit tip as default;
 wenzelm parents: 
64230diff
changeset | 249 | def id(rev: String = "tip"): String = identify(rev, options = "-i") | 
| 
367d83d6030e
clarified hg.id operation, with explicit tip as default;
 wenzelm parents: 
64230diff
changeset | 250 | |
| 75393 | 251 |     def tags(rev: String = "tip"): String = {
 | 
| 73524 | 252 | val result = identify(rev, options = "-t") | 
| 77218 | 253 |       space_explode(' ', result).filterNot(_ == "tip").mkString(" ")
 | 
| 73524 | 254 | } | 
| 73521 | 255 | |
| 71313 | 256 | def paths(args: String = "", options: String = ""): List[String] = | 
| 257 |       hg.command("paths", args = args, options = options).check.out_lines
 | |
| 258 | ||
| 63998 | 259 | def manifest(rev: String = "", options: String = ""): List[String] = | 
| 64168 | 260 |       hg.command("manifest", opt_rev(rev), options).check.out_lines
 | 
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 261 | |
| 64033 | 262 | def log(rev: String = "", template: String = "", options: String = ""): String = | 
| 64168 | 263 |       hg.command("log", opt_rev(rev) + opt_template(template), options).check.out
 | 
| 64027 | 264 | |
| 75514 | 265 | def diff(rev: String = "", options: String = ""): String = | 
| 266 |       hg.command("diff", opt_rev(rev), options).check.out
 | |
| 267 | ||
| 67755 | 268 |     def parent(): String = log(rev = "p1()", template = "{node|short}")
 | 
| 269 | ||
| 73340 | 270 | def push( | 
| 75393 | 271 | remote: String = "", | 
| 272 | rev: String = "", | |
| 273 | force: Boolean = false, | |
| 274 | options: String = "" | |
| 275 |     ): Unit = {
 | |
| 64348 
4c253e84ae62
clarified push/pull chain: current ISABELLE_HOME may server as source for changes that are not published on isabelle_repos_source yet (e.g. isabelle-release branch);
 wenzelm parents: 
64347diff
changeset | 276 |       hg.command("push", opt_rev(rev) + opt_flag("--force", force) + optional(remote), options).
 | 
| 64408 
50bcf976f276
clarified hg push return code: 1 means "nothing to push";
 wenzelm parents: 
64348diff
changeset | 277 | check_rc(rc => rc == 0 | rc == 1) | 
| 64348 
4c253e84ae62
clarified push/pull chain: current ISABELLE_HOME may server as source for changes that are not published on isabelle_repos_source yet (e.g. isabelle-release branch);
 wenzelm parents: 
64347diff
changeset | 278 | } | 
| 
4c253e84ae62
clarified push/pull chain: current ISABELLE_HOME may server as source for changes that are not published on isabelle_repos_source yet (e.g. isabelle-release branch);
 wenzelm parents: 
64347diff
changeset | 279 | |
| 63999 | 280 | def pull(remote: String = "", rev: String = "", options: String = ""): Unit = | 
| 64168 | 281 |       hg.command("pull", opt_rev(rev) + optional(remote), options).check
 | 
| 63999 | 282 | |
| 64168 | 283 | def update( | 
| 75393 | 284 | rev: String = "", | 
| 285 | clean: Boolean = false, | |
| 286 | check: Boolean = false, | |
| 287 | options: String = "" | |
| 288 |     ): Unit = {
 | |
| 64168 | 289 |       hg.command("update",
 | 
| 290 |         opt_rev(rev) + opt_flag("--clean", clean) + opt_flag("--check", check), options).check
 | |
| 63999 | 291 | } | 
| 65819 
ff3dc9325eaa
explore repository structure, with minimal assumptions about "hg log" output;
 wenzelm parents: 
65818diff
changeset | 292 | |
| 75470 | 293 | def status(options: String = ""): List[String] = | 
| 294 |       hg.command("status", options = options).check.out_lines
 | |
| 295 | ||
| 296 | def known_files(): List[String] = status(options = "--modified --added --clean --no-status") | |
| 65819 
ff3dc9325eaa
explore repository structure, with minimal assumptions about "hg log" output;
 wenzelm parents: 
65818diff
changeset | 297 | |
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 298 | def sync(context: Rsync.Context, target: Path, | 
| 75487 | 299 | thorough: Boolean = false, | 
| 75474 | 300 | dry_run: Boolean = false, | 
| 75477 | 301 | filter: List[String] = Nil, | 
| 75511 | 302 | contents: List[File.Content] = Nil, | 
| 75479 | 303 | rev: String = "" | 
| 75474 | 304 |     ): Unit = {
 | 
| 77782 
127d077cccfe
clarified signature: avoid object-oriented "dispatch";
 wenzelm parents: 
77518diff
changeset | 305 | require(ssh.is_local, "local repository required") | 
| 75480 | 306 | |
| 75491 | 307 |       Isabelle_System.with_tmp_dir("sync") { tmp_dir =>
 | 
| 77785 
721b3278c8e4
more direct Hg_Sync.check_directory via SSH operations;
 wenzelm parents: 
77783diff
changeset | 308 | Hg_Sync.check_directory(target, ssh = context.ssh) | 
| 
721b3278c8e4
more direct Hg_Sync.check_directory via SSH operations;
 wenzelm parents: 
77783diff
changeset | 309 | |
| 75514 | 310 | val id_content = id(rev = rev) | 
| 311 |         val is_changed = id_content.endsWith("+")
 | |
| 312 | val log_content = if (is_changed) "" else log(rev = rev, options = "-l1") | |
| 313 | val diff_content = if (is_changed) diff(rev = rev, options = "--git") else "" | |
| 314 | val stat_content = if (is_changed) diff(rev = rev, options = "--stat") else "" | |
| 315 | ||
| 77852 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 316 | val all_contents = | 
| 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 317 | File.content(Hg_Sync.PATH_ID, id_content) :: | 
| 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 318 | File.content(Hg_Sync.PATH_LOG, log_content) :: | 
| 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 319 | File.content(Hg_Sync.PATH_DIFF, diff_content) :: | 
| 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 320 | File.content(Hg_Sync.PATH_STAT, stat_content) :: contents | 
| 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 321 | |
| 
df35b5b7b6a4
more direct hg_sync init via ssh (see also 721b3278c8e4);
 wenzelm parents: 
77795diff
changeset | 322 | all_contents.foreach(_.write(target, ssh = context.ssh)) | 
| 75511 | 323 | |
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 324 | val (exclude, source) = | 
| 75479 | 325 |           if (rev.isEmpty) {
 | 
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 326 | val exclude = ".hg" :: status(options = "--unknown --ignored --no-status") | 
| 75480 | 327 | val source = File.standard_path(root) | 
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 328 | (exclude, source) | 
| 75479 | 329 | } | 
| 330 |           else {
 | |
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 331 |             val exclude = List(".hg_archival.txt")
 | 
| 75479 | 332 |             val source = File.standard_path(tmp_dir + Path.explode("archive"))
 | 
| 333 | archive(source, rev = rev) | |
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 334 | (exclude, source) | 
| 75479 | 335 | } | 
| 75511 | 336 | |
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 337 |         val exclude_path = tmp_dir + Path.explode("exclude")
 | 
| 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 338 |         File.write(exclude_path, cat_lines(exclude.map("/" + _)))
 | 
| 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 339 | |
| 75511 | 340 | val protect = | 
| 75514 | 341 | (Hg_Sync.PATH :: contents.map(_.path)) | 
| 342 | .map(path => "protect /" + File.standard_path(path)) | |
| 75526 | 343 | Rsync.exec(context, | 
| 75525 
68162e4f60a7
clarified signature: more explicit type Rsync.Context;
 wenzelm parents: 
75524diff
changeset | 344 | thorough = thorough, | 
| 75511 | 345 | dry_run = dry_run, | 
| 346 | clean = true, | |
| 347 | prune_empty_dirs = true, | |
| 348 | filter = protect ::: filter, | |
| 75519 
931c48756b88
avoid redundant meta data: exclude .hg_archival.txt;
 wenzelm parents: 
75514diff
changeset | 349 |           args = List("--exclude-from=" + exclude_path.implode, "--",
 | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 350 | Url.direct_path(source), context.target(target)) | 
| 75507 | 351 | ).check | 
| 75474 | 352 | } | 
| 353 | } | |
| 354 | ||
| 78994 | 355 | private val cache_graph = Synchronized[(List[String], Graph)]((Nil, Graph.string[Unit])) | 
| 356 | ||
| 357 | def graph(): Graph = | |
| 358 |       cache_graph.change_result({ case (old_topo, old_graph) =>
 | |
| 359 | val topo = heads(options = "--topo") | |
| 360 | val graph = | |
| 361 | if (topo == old_topo) old_graph | |
| 362 |           else {
 | |
| 363 |             val Node = """^node: (\w{12}) (\w{12}) (\w{12})""".r
 | |
| 364 | val log_result = | |
| 365 |               log(template = """node: {node|short} {p1node|short} {p2node|short}\n""")
 | |
| 366 |             split_lines(log_result).foldLeft(Graph.string[Unit]) {
 | |
| 367 | case (graph, Node(x, y, z)) => | |
| 368 | val deps = List(y, z).filterNot(s => s.forall(_ == '0')) | |
| 369 | val graph1 = (x :: deps).foldLeft(graph)(_.default_node(_, ())) | |
| 370 |                 deps.foldLeft(graph1)({ case (g, dep) => g.add_edge(dep, x) })
 | |
| 371 | case (graph, _) => graph | |
| 372 | } | |
| 373 | } | |
| 374 | (graph, (topo, graph)) | |
| 375 | }) | |
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 376 | } | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 377 | |
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 378 | |
| 75472 | 379 | |
| 380 | /** check files **/ | |
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 381 | |
| 75393 | 382 |   def check_files(files: List[Path], ssh: SSH.System = SSH.Local): (List[Path], List[Path]) = {
 | 
| 67782 
7e223a05e6d8
clarified notion of unknown files: ignore files outside of a Mercurial repository;
 wenzelm parents: 
67755diff
changeset | 383 | val outside = new mutable.ListBuffer[Path] | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 384 | val unknown = new mutable.ListBuffer[Path] | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 385 | |
| 75393 | 386 |     @tailrec def check(paths: List[Path]): Unit = {
 | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 387 |       paths match {
 | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 388 | case path :: rest => | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 389 |           find_repository(path, ssh) match {
 | 
| 67782 
7e223a05e6d8
clarified notion of unknown files: ignore files outside of a Mercurial repository;
 wenzelm parents: 
67755diff
changeset | 390 | case None => outside += path; check(rest) | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 391 | case Some(hg) => | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 392 | val known = | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 393 | hg.known_files().iterator.map(name => | 
| 65833 | 394 | (hg.root + Path.explode(name)).canonical_file).toSet | 
| 395 | if (!known(path.canonical_file)) unknown += path | |
| 396 | check(rest.filterNot(p => known(p.canonical_file))) | |
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 397 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 398 | case Nil => | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 399 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 400 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 401 | |
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 402 | check(files) | 
| 67782 
7e223a05e6d8
clarified notion of unknown files: ignore files outside of a Mercurial repository;
 wenzelm parents: 
67755diff
changeset | 403 | (outside.toList, unknown.toList) | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65825diff
changeset | 404 | } | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 405 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 406 | |
| 75472 | 407 | |
| 408 | /** hg_setup **/ | |
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 409 | |
| 75393 | 410 |   private def edit_hgrc(local_hg: Repository, path_name: String, source: String): Unit = {
 | 
| 71313 | 411 |     val hgrc = local_hg.root + Path.explode(".hg/hgrc")
 | 
| 412 |     def header(line: String): Boolean = line.startsWith("[paths]")
 | |
| 413 | val Entry = """^(\S+)\s*=\s*(.*)$""".r | |
| 414 | val new_entry = path_name + " = " + source | |
| 415 | ||
| 75393 | 416 |     def commit(lines: List[String]): Boolean = {
 | 
| 71320 | 417 | File.write(hgrc, cat_lines(lines)) | 
| 71313 | 418 | true | 
| 419 | } | |
| 420 | val edited = | |
| 421 |       hgrc.is_file && {
 | |
| 422 | val lines = split_lines(File.read(hgrc)) | |
| 71383 | 423 |         lines.count(header) == 1 && {
 | 
| 71313 | 424 |           if (local_hg.paths(options = "-q").contains(path_name)) {
 | 
| 425 | val old_source = local_hg.paths(args = path_name).head | |
| 71320 | 426 | val old_entry = path_name + ".old = " + old_source | 
| 71313 | 427 | val lines1 = | 
| 428 |               lines.map {
 | |
| 71320 | 429 | case Entry(a, b) if a == path_name && b == old_source => | 
| 430 | new_entry + "\n" + old_entry | |
| 71313 | 431 | case line => line | 
| 432 | } | |
| 433 | lines != lines1 && commit(lines1) | |
| 434 | } | |
| 435 |           else {
 | |
| 436 | val prefix = lines.takeWhile(line => !header(line)) | |
| 437 | val n = prefix.length | |
| 438 | commit(prefix ::: List(lines(n), new_entry) ::: lines.drop(n + 1)) | |
| 439 | } | |
| 440 | } | |
| 441 | } | |
| 442 | if (!edited) File.append(hgrc, "\n[paths]\n" + new_entry + "\n") | |
| 443 | } | |
| 444 | ||
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 445 | val default_path_name = "default" | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 446 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 447 | def hg_setup( | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 448 | remote: String, | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 449 | local_path: Path, | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 450 | remote_name: String = "", | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 451 | path_name: String = default_path_name, | 
| 71321 
edf3210a61a2
added option -r: support more robust consolidation of local clones with varying names;
 wenzelm parents: 
71320diff
changeset | 452 | remote_exists: Boolean = false, | 
| 75393 | 453 | progress: Progress = new Progress | 
| 454 |   ): Unit = {
 | |
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 455 | /* local repository */ | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 456 | |
| 72375 | 457 | Isabelle_System.make_directory(local_path) | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 458 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 459 | val repos_name = | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 460 | proper_string(remote_name) getOrElse local_path.absolute.base.implode | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 461 | |
| 80188 
3956e8b6a9c9
more uniform/robust detect_repository/is_repository: actually check hg root;
 wenzelm parents: 
78994diff
changeset | 462 | val local_hg = detect_repository(local_path) getOrElse init_repository(local_path) | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 463 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 464 |     progress.echo("Local repository " + local_hg.root.absolute)
 | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 465 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 466 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 467 | /* remote repository */ | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 468 | |
| 76169 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 469 |     val remote_url = {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 470 |       if (remote.startsWith("ssh://")) {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 471 | val ssh_url = remote + "/" + repos_name | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 472 | |
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 473 |         if (!remote_exists) {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 474 |           try { local_hg.command("init", ssh_url, repository = false).check }
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 475 |           catch { case ERROR(msg) => progress.echo_warning(msg) }
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 476 | } | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 477 | |
| 76169 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 478 | ssh_url | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 479 | } | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 480 |       else {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 481 | val phabricator = Phabricator.API(remote) | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 482 | |
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 483 | var repos = | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 484 |           phabricator.get_repositories().find(r => r.short_name == repos_name) getOrElse {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 485 |             if (remote_exists) {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 486 |               error("Remote repository " +
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 487 | quote(phabricator.hg_url + "/source/" + repos_name) + " expected to exist") | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 488 | } | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 489 | else phabricator.create_repository(repos_name, short_name = repos_name) | 
| 71321 
edf3210a61a2
added option -r: support more robust consolidation of local clones with varying names;
 wenzelm parents: 
71320diff
changeset | 490 | } | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 491 | |
| 76169 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 492 |         while (repos.importing) {
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 493 |           progress.echo("Awaiting remote repository ...")
 | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 494 | Time.seconds(0.5).sleep() | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 495 | repos = phabricator.the_repository(repos.phid) | 
| 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 496 | } | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 497 | |
| 76169 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 498 | repos.ssh_url | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 499 | } | 
| 76169 
a3c694039fd6
discontinued pointless SSH.Target: OpenSSH client can handle user@host directly;
 wenzelm parents: 
76136diff
changeset | 500 | } | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 501 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 502 |     progress.echo("Remote repository " + quote(remote_url))
 | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 503 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 504 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 505 | /* synchronize local and remote state */ | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 506 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 507 |     progress.echo("Synchronizing ...")
 | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 508 | |
| 71313 | 509 | edit_hgrc(local_hg, path_name, remote_url) | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 510 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 511 | local_hg.pull(options = "-u") | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 512 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 513 | local_hg.push(remote = remote_url) | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 514 | } | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 515 | |
| 75474 | 516 | val isabelle_tool1 = | 
| 72763 | 517 |     Isabelle_Tool("hg_setup", "setup remote vs. local Mercurial repository",
 | 
| 75394 | 518 | Scala_Project.here, | 
| 519 |       { args =>
 | |
| 520 | var remote_name = "" | |
| 521 | var path_name = default_path_name | |
| 522 | var remote_exists = false | |
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 523 | |
| 75394 | 524 |         val getopts = Getopts("""
 | 
| 71322 | 525 | Usage: isabelle hg_setup [OPTIONS] REMOTE LOCAL_DIR | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 526 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 527 | Options are: | 
| 71322 | 528 | -n NAME remote repository name (default: base name of LOCAL_DIR) | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 529 | -p PATH Mercurial path name (default: """ + quote(default_path_name) + """) | 
| 71321 
edf3210a61a2
added option -r: support more robust consolidation of local clones with varying names;
 wenzelm parents: 
71320diff
changeset | 530 | -r assume that remote repository already exists | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 531 | |
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 532 | Setup a remote vs. local Mercurial repository: REMOTE either refers to a | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 533 | Phabricator server "user@host" or SSH file server "ssh://user@host/path". | 
| 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 534 | """, | 
| 75394 | 535 | "n:" -> (arg => remote_name = arg), | 
| 536 | "p:" -> (arg => path_name = arg), | |
| 537 | "r" -> (_ => remote_exists = true)) | |
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 538 | |
| 75394 | 539 | val more_args = getopts(args) | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 540 | |
| 75394 | 541 | val (remote, local_path) = | 
| 542 |           more_args match {
 | |
| 543 | case List(arg1, arg2) => (arg1, Path.explode(arg2)) | |
| 544 | case _ => getopts.usage() | |
| 545 | } | |
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 546 | |
| 75394 | 547 | val progress = new Console_Progress | 
| 71312 
937328d61436
added command hg_setup: setup remote vs. local Mercurial repository;
 wenzelm parents: 
68566diff
changeset | 548 | |
| 75394 | 549 | hg_setup(remote, local_path, remote_name = remote_name, path_name = path_name, | 
| 550 | remote_exists = remote_exists, progress = progress) | |
| 551 | }) | |
| 75474 | 552 | |
| 553 | ||
| 554 | ||
| 555 | /** hg_sync **/ | |
| 556 | ||
| 557 | val isabelle_tool2 = | |
| 75479 | 558 |     Isabelle_Tool("hg_sync", "synchronize Mercurial repository with target directory",
 | 
| 75474 | 559 |       Scala_Project.here, { args =>
 | 
| 75489 | 560 | var filter: List[String] = Nil | 
| 75474 | 561 | var root: Option[Path] = None | 
| 75487 | 562 | var thorough = false | 
| 75474 | 563 | var dry_run = false | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 564 | var options = Options.init() | 
| 76136 
1bb677cceea4
let rsync re-use ssh connection via control path;
 wenzelm parents: 
76134diff
changeset | 565 | var ssh_port = 0 | 
| 75479 | 566 | var rev = "" | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 567 | var ssh_host = "" | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 568 | var ssh_user = "" | 
| 75474 | 569 | var verbose = false | 
| 570 | ||
| 571 |         val getopts = Getopts("""
 | |
| 572 | Usage: isabelle hg_sync [OPTIONS] TARGET | |
| 573 | ||
| 574 | Options are: | |
| 75511 | 575 | -F RULE add rsync filter RULE | 
| 576 | (e.g. "protect /foo" to avoid deletion) | |
| 75474 | 577 | -R ROOT explicit repository root directory | 
| 578 | (default: implicit from current directory) | |
| 75493 | 579 | -T thorough treatment of file content and directory times | 
| 75474 | 580 | -n no changes: dry-run | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 581 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 582 | -p PORT explicit SSH port | 
| 75479 | 583 | -r REV explicit revision (default: state of working directory) | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 584 | -s HOST SSH host name for remote target (default: local) | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 585 | -u USER explicit SSH user name | 
| 75474 | 586 | -v verbose | 
| 587 | ||
| 75479 | 588 | Synchronize Mercurial repository with TARGET directory, | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 589 | which can be local or remote (see options -s -p -u). | 
| 75474 | 590 | """, | 
| 75489 | 591 | "F:" -> (arg => filter = filter ::: List(arg)), | 
| 75474 | 592 | "R:" -> (arg => root = Some(Path.explode(arg))), | 
| 75487 | 593 | "T" -> (_ => thorough = true), | 
| 75474 | 594 | "n" -> (_ => dry_run = true), | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 595 | "o:" -> (arg => options = options + arg), | 
| 76136 
1bb677cceea4
let rsync re-use ssh connection via control path;
 wenzelm parents: 
76134diff
changeset | 596 | "p:" -> (arg => ssh_port = Value.Int.parse(arg)), | 
| 75479 | 597 | "r:" -> (arg => rev = arg), | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 598 | "s:" -> (arg => ssh_host = arg), | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 599 | "u:" -> (arg => ssh_user = arg), | 
| 75474 | 600 | "v" -> (_ => verbose = true)) | 
| 601 | ||
| 602 | val more_args = getopts(args) | |
| 603 | val target = | |
| 604 |           more_args match {
 | |
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 605 | case List(target) => Path.explode(target) | 
| 75474 | 606 | case _ => getopts.usage() | 
| 607 | } | |
| 608 | ||
| 77518 
fda4da0f80f4
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77368diff
changeset | 609 | val progress = new Console_Progress(verbose = verbose) | 
| 75474 | 610 | val hg = | 
| 611 |           root match {
 | |
| 612 | case Some(dir) => repository(dir) | |
| 613 | case None => the_repository(Path.current) | |
| 614 | } | |
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 615 | |
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 616 |         using(SSH.open_system(options, host = ssh_host, port = ssh_port, user = ssh_user)) { ssh =>
 | 
| 77795 | 617 | val context = Rsync.Context(progress = progress, ssh = ssh, stats = verbose) | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 618 | hg.sync(context, target, thorough = thorough, dry_run = dry_run, | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 619 | filter = filter, rev = rev) | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77782diff
changeset | 620 | } | 
| 75474 | 621 | } | 
| 622 | ) | |
| 63997 
e11ccb5aa82f
more formal Mercurial support (with the potential to upgrade to command server);
 wenzelm parents: diff
changeset | 623 | } |