Mercurial > core / rust/lib/obj/src/config/repo/hg.rs
changeset 67: |
0ccbbd142694 |
parent: |
c7165d93a9eb
|
author: |
ellis <ellis@rwest.io> |
date: |
Sat, 02 Dec 2023 16:26:08 -0500 |
permissions: |
-rw-r--r-- |
description: |
rust cleanup, added rocksdb bindings |
4 file_content, FileType, ManifestEntryDetails, MercurialRepository, Revision, 7 use logger::log::{error, info, trace}; 8 use serde::{Deserialize, Serialize}; 12 io::{LineWriter, Read, Write}, 14 path::{Path, PathBuf}, 18 /// Mercurial configuration type -- corresponds to .hgrc file 20 /// TODO set 'default' and 'default_push' paths 21 #[derive(Serialize, Deserialize, Debug, PartialEq)] 22 pub struct MercurialConfig { 23 pub ui: HashMap<String, String>, 24 pub extensions: Option<HashMap<String, Option<String>>>, 25 pub paths: Option<HashMap<String, String>>, 29 impl MercurialConfig { 30 pub fn handle<P: AsRef<Path>>(path: P) -> MercurialRepository { 31 MercurialRepository::open(path).unwrap() 35 /// Mercurial '.hgsub' file handle, which is just a list of PATH=SOURCE pairs. 36 pub struct HgSubFile { 37 pub path: PathBuf, // path to the .hgsub file 38 pub subrepos: Vec<SubRepo>, // Vec containing `SubRepo` handles 42 /// Create a new '.hgsub' file handle 43 pub fn new() -> Self { 45 path: PathBuf::from(".hgsub"), 49 /// ensure that the path is in a hg repo. 50 fn parent_is_hg(&self) -> bool { 51 if self.path.parent().unwrap().join(".hg").exists() { 57 /// insert a subrepo into this HgSubFile. does not clone the source 58 /// or ensure that path exists. Takes an optional argument of 'hg' 59 /// or 'git' to indicate the subrepo-type. Value can be ommitted to 67 // set prefix based on vcs (repo type) 68 if let Some(i) = vcs { 70 "hg" => prefix = "hg", 71 "git" => prefix = "git", 73 error!("failed to recognize repo type") 78 let source = format!("{}:{}", prefix, source); 80 let subrepo = SubRepo { 81 vcs: vcs.unwrap().to_string(), 82 origin: source.to_string(), 83 path: path.to_string(), 86 self.subrepos.push(subrepo); 90 /// Save subs to `.hgsub` file specified in the `path` field of 91 /// this struct. This will overwrite any existing file at that path. 92 pub fn save(self) -> Result<()> { 93 match self.parent_is_hg() { 95 let mut file = File::open(self.path).unwrap(); 96 for i in self.subrepos.iter() { 97 write!(file, "{} = {}", i.path, i.origin)?; 101 error!("Parent is not a Mercurial repo!") 107 /// Sort the full contents of a .hgsub file alphabetically. 108 pub fn sort(self) -> Result<Self> { 109 let mut fd = File::open(&self.path)?; 110 let len = fd.metadata().unwrap().len() as usize; 111 let mut bufr = String::with_capacity(len); 112 fd.read_to_string(&mut bufr).unwrap(); // This reads the entire file into memory. 113 drop(fd); // drop the old readonly file descriptor 115 let mut subs = Vec::new(); 117 trace!("starting sort of {:?} lines", &self.path.canonicalize()?); 118 for line in bufr.lines() { 119 subs.push(line.as_bytes()); 121 subs.sort_unstable(); 122 trace!("lines have been sorted"); 124 let file = File::create(&self.path)?; 125 let mut file = LineWriter::new(file); 126 for sub in subs.iter() { 127 file.write_all(sub)?; 128 file.write_all(b"\n")?; 132 info!("sorted {:?}", &self.path.canonicalize()?); 138 impl Default for HgSubFile { 139 fn default() -> Self { 141 path: PathBuf::from(".hgsub"), 147 /// Hgweb configuration type 149 /// Based on the configuration file for 'hgweb' scripts. 151 /// We don't store the file path in a field because all HgwebConfig 152 /// values are relative to env::current_dir() 153 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 154 pub struct HgwebConfig { 157 pub description: String, 158 pub extensions: Vec<String>, 159 pub socket: SocketAddr, 160 pub paths: HashMap<PathBuf, PathBuf>, 163 impl Default for HgwebConfig { 164 fn default() -> Self { 166 name: "".to_string(), 167 contact: "".to_string(), 168 description: "".to_string(), 172 .expect("could not parse hgweb socketaddr"), 173 paths: HashMap::new(), 178 /// from hg_parser crate docs - given a mercurial repo as a path, 179 /// exports to git fast-import format (to stdout) 180 pub fn export_hg_git<P: AsRef<Path>>(path: P) -> Result<()> { 181 let start = Instant::now(); 182 let repo = MercurialRepository::open(path).expect("could not open repo path"); 184 let stdout = std::io::stdout(); 185 let mut writer = stdout.lock(); 187 for changeset in &repo { 188 let revision = changeset.revision; 189 eprintln!("rev: {:?}", revision); 191 let header = &changeset.header; 192 let mut branch = None; 193 let mut closed = false; 194 for (key, value) in &header.extra { 195 if key == b"branch" { 196 branch = Some(value.as_slice()); 199 if key == b"close" && value == b"1" { 204 let mut branch: Vec<_> = branch.unwrap_or_else(|| b"master").into(); 205 for b in branch.iter_mut() { 211 let user = String::from_utf8_lossy(&header.user); 212 let desc = String::from_utf8_lossy(&header.comment); 214 let time = header.time.timestamp_secs(); 215 let timezone = header.time.tz_offset_secs(); 217 format!("{:+03}{:02}", -timezone / 3600, ((-timezone % 3600) / 60)); 219 write!(writer, "reset refs/heads/")?; 220 writer.write_all(&mut branch)?; 221 write!(writer, "\ncommit refs/heads/")?; 222 writer.write_all(&mut branch)?; 223 writeln!(writer, "\nmark :{}", mark(revision))?; 225 writeln!(writer, "author {} {} {}", user, time, tz)?; 226 writeln!(writer, "committer {} {} {}", user, time, tz)?; 227 writeln!(writer, "data {}", desc.len() + 1)?; 228 writeln!(writer, "{}\n", desc)?; 230 match (header.p1, header.p2) { 231 (Some(p1), Some(p2)) => { 232 writeln!(writer, "from :{}", mark(p1))?; 233 writeln!(writer, "merge :{}", mark(p2))?; 235 (Some(p), None) | (None, Some(p)) => { 236 writeln!(writer, "from :{}", mark(p))?; 241 for mut file in changeset.files { 242 match (file.data, file.manifest_entry) { 244 write!(writer, "D ")?; 245 writer.write_all(&mut file.path)?; 248 (Some(data), Some(manifest_entry)) => { 252 match manifest_entry.details { 253 ManifestEntryDetails::File(FileType::Symlink) => "120000", 254 ManifestEntryDetails::File(FileType::Executable) => "100755", 255 ManifestEntryDetails::Tree 256 | ManifestEntryDetails::File(FileType::Regular) => "100644", 259 writer.write_all(&mut file.path)?; 260 let data = file_content(&data); 261 writeln!(writer, "\ndata {}", data.len())?; 262 writer.write_all(&data[..])?; 264 _ => panic!("Wrong file data!"), 269 write!(writer, "reset refs/tags/archive/")?; 270 writer.write_all(&mut branch)?; 271 writeln!(writer, "\nfrom :{}\n", mark(revision))?; 273 write!(writer, "reset refs/heads/")?; 274 writer.write_all(&mut branch)?; 275 writeln!(writer, "\nfrom 0000000000000000000000000000000000000000\n")?; 279 for (rev, tag) in repo.tags().unwrap() { 280 eprintln!("export tag {}", tag.name); 281 writeln!(writer, "reset refs/tags/{}", tag.name).unwrap(); 282 writeln!(writer, "from :{}", mark(rev)).unwrap(); 283 writeln!(writer).unwrap(); 286 eprintln!("Done. Elapsed: {:?}", start.elapsed()); 290 fn mark<R: Into<Revision>>(rev: R) -> usize { 291 (rev.into() + 1).0 as usize