Mercurial > core / rust/lib/obj/src/config/repo/hg.rs
changeset 17: |
c7165d93a9eb |
child: |
0ccbbd142694 |
author: |
ellis <ellis@rwest.io> |
date: |
Sun, 22 Oct 2023 23:03:15 -0400 |
permissions: |
-rw-r--r-- |
description: |
add obj and net src |
3 use hg_parser::{file_content, FileType, ManifestEntryDetails, MercurialRepository, Revision}; 5 use logger::log::{error, info, trace}; 6 use serde::{Deserialize, Serialize}; 10 io::{LineWriter, Read, Write}, 12 path::{Path, PathBuf}, 16 /// Mercurial configuration type -- corresponds to .hgrc file 18 /// TODO set 'default' and 'default_push' paths 19 #[derive(Serialize, Deserialize, Debug, PartialEq)] 20 pub struct MercurialConfig { 21 pub ui: HashMap<String, String>, 22 pub extensions: Option<HashMap<String, Option<String>>>, 23 pub paths: Option<HashMap<String, String>>, 27 impl MercurialConfig { 28 pub fn handle<P: AsRef<Path>>(path: P) -> MercurialRepository { 29 MercurialRepository::open(path).unwrap() 33 /// Mercurial '.hgsub' file handle, which is just a list of PATH=SOURCE pairs. 34 pub struct HgSubFile { 35 pub path: PathBuf, // path to the .hgsub file 36 pub subrepos: Vec<SubRepo>, // Vec containing `SubRepo` handles 40 /// Create a new '.hgsub' file handle 41 pub fn new() -> Self { 43 path: PathBuf::from(".hgsub"), 47 /// ensure that the path is in a hg repo. 48 fn parent_is_hg(&self) -> bool { 49 if self.path.parent().unwrap().join(".hg").exists() { 55 /// insert a subrepo into this HgSubFile. does not clone the source 56 /// or ensure that path exists. Takes an optional argument of 'hg' 57 /// or 'git' to indicate the subrepo-type. Value can be ommitted to 58 pub fn insert(&mut self, path: &str, source: &str, vcs: Option<&str>) -> Result<()> { 60 // set prefix based on vcs (repo type) 61 if let Some(i) = vcs { 63 "hg" => prefix = "hg", 64 "git" => prefix = "git", 66 error!("failed to recognize repo type") 71 let source = format!("{}:{}", prefix, source); 73 let subrepo = SubRepo { 74 vcs: vcs.unwrap().to_string(), 75 origin: source.to_string(), 76 path: path.to_string(), 79 self.subrepos.push(subrepo); 83 /// Save subs to `.hgsub` file specified in the `path` field of 84 /// this struct. This will overwrite any existing file at that path. 85 pub fn save(self) -> Result<()> { 86 match self.parent_is_hg() { 88 let mut file = File::open(self.path).unwrap(); 89 for i in self.subrepos.iter() { 90 write!(file, "{} = {}", i.path, i.origin)?; 94 error!("Parent is not a Mercurial repo!") 100 /// Sort the full contents of a .hgsub file alphabetically. 101 pub fn sort(self) -> Result<Self> { 102 let mut fd = File::open(&self.path)?; 103 let len = fd.metadata().unwrap().len() as usize; 104 let mut bufr = String::with_capacity(len); 105 fd.read_to_string(&mut bufr).unwrap(); // This reads the entire file into memory. 106 drop(fd); // drop the old readonly file descriptor 108 let mut subs = Vec::new(); 110 trace!("starting sort of {:?} lines", &self.path.canonicalize()?); 111 for line in bufr.lines() { 112 subs.push(line.as_bytes()); 114 subs.sort_unstable(); 115 trace!("lines have been sorted"); 117 let file = File::create(&self.path)?; 118 let mut file = LineWriter::new(file); 119 for sub in subs.iter() { 120 file.write_all(sub)?; 121 file.write_all(b"\n")?; 125 info!("sorted {:?}", &self.path.canonicalize()?); 131 impl Default for HgSubFile { 132 fn default() -> Self { 134 path: PathBuf::from(".hgsub"), 140 /// Hgweb configuration type 142 /// Based on the configuration file for 'hgweb' scripts. 144 /// We don't store the file path in a field because all HgwebConfig 145 /// values are relative to env::current_dir() 146 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 147 pub struct HgwebConfig { 150 pub description: String, 151 pub extensions: Vec<String>, 152 pub socket: SocketAddr, 153 pub paths: HashMap<PathBuf, PathBuf>, 156 impl Default for HgwebConfig { 157 fn default() -> Self { 159 name: "".to_string(), 160 contact: "".to_string(), 161 description: "".to_string(), 165 .expect("could not parse hgweb socketaddr"), 166 paths: HashMap::new(), 171 /// from hg_parser crate docs - given a mercurial repo as a path, 172 /// exports to git fast-import format (to stdout) 173 pub fn export_hg_git<P: AsRef<Path>>(path: P) -> Result<()> { 174 let start = Instant::now(); 175 let repo = MercurialRepository::open(path).expect("could not open repo path"); 177 let stdout = std::io::stdout(); 178 let mut writer = stdout.lock(); 180 for changeset in &repo { 181 let revision = changeset.revision; 182 eprintln!("rev: {:?}", revision); 184 let header = &changeset.header; 185 let mut branch = None; 186 let mut closed = false; 187 for (key, value) in &header.extra { 188 if key == b"branch" { 189 branch = Some(value.as_slice()); 192 if key == b"close" && value == b"1" { 197 let mut branch: Vec<_> = branch.unwrap_or_else(|| b"master").into(); 198 for b in branch.iter_mut() { 204 let user = String::from_utf8_lossy(&header.user); 205 let desc = String::from_utf8_lossy(&header.comment); 207 let time = header.time.timestamp_secs(); 208 let timezone = header.time.tz_offset_secs(); 209 let tz = format!("{:+03}{:02}", -timezone / 3600, ((-timezone % 3600) / 60)); 211 write!(writer, "reset refs/heads/")?; 212 writer.write_all(&mut branch)?; 213 write!(writer, "\ncommit refs/heads/")?; 214 writer.write_all(&mut branch)?; 215 writeln!(writer, "\nmark :{}", mark(revision))?; 217 writeln!(writer, "author {} {} {}", user, time, tz)?; 218 writeln!(writer, "committer {} {} {}", user, time, tz)?; 219 writeln!(writer, "data {}", desc.len() + 1)?; 220 writeln!(writer, "{}\n", desc)?; 222 match (header.p1, header.p2) { 223 (Some(p1), Some(p2)) => { 224 writeln!(writer, "from :{}", mark(p1))?; 225 writeln!(writer, "merge :{}", mark(p2))?; 227 (Some(p), None) | (None, Some(p)) => { 228 writeln!(writer, "from :{}", mark(p))?; 233 for mut file in changeset.files { 234 match (file.data, file.manifest_entry) { 236 write!(writer, "D ")?; 237 writer.write_all(&mut file.path)?; 240 (Some(data), Some(manifest_entry)) => { 244 match manifest_entry.details { 245 ManifestEntryDetails::File(FileType::Symlink) => "120000", 246 ManifestEntryDetails::File(FileType::Executable) => "100755", 247 ManifestEntryDetails::Tree | ManifestEntryDetails::File(FileType::Regular) => 251 writer.write_all(&mut file.path)?; 252 let data = file_content(&data); 253 writeln!(writer, "\ndata {}", data.len())?; 254 writer.write_all(&data[..])?; 256 _ => panic!("Wrong file data!"), 261 write!(writer, "reset refs/tags/archive/")?; 262 writer.write_all(&mut branch)?; 263 writeln!(writer, "\nfrom :{}\n", mark(revision))?; 265 write!(writer, "reset refs/heads/")?; 266 writer.write_all(&mut branch)?; 267 writeln!(writer, "\nfrom 0000000000000000000000000000000000000000\n")?; 271 for (rev, tag) in repo.tags().unwrap() { 272 eprintln!("export tag {}", tag.name); 273 writeln!(writer, "reset refs/tags/{}", tag.name).unwrap(); 274 writeln!(writer, "from :{}", mark(rev)).unwrap(); 275 writeln!(writer).unwrap(); 278 eprintln!("Done. Elapsed: {:?}", start.elapsed()); 282 fn mark<R: Into<Revision>>(rev: R) -> usize { 283 (rev.into() + 1).0 as usize