view rust/rhg/src/utils/path_utils.rs @ 48454:473af5cbc209

rhg: Add support for `rhg status --copies` Copy sources are collected during `status()` rather than after the fact like in Python, because `status()` takes a `&mut` exclusive reference to the dirstate map (in order to potentially mutate it for directory mtimes) and returns `Cow<'_, HgPath>` that borrow the dirstate map. Even though with `Cow` only some shared borrows remain, the still extend the same lifetime of the initial `&mut` so the dirstate map cannot be borrowed again to access copy sources after the fact: https://doc.rust-lang.org/nomicon/lifetime-mismatch.html#limits-of-lifetimes Additionally, collecting copy sources during the dirstate tree traversal that `status()` already does avoids the cost of another traversal or other lookups (though I haven’t benchmarked that cost). Differential Revision: https://phab.mercurial-scm.org/D11899
author Simon Sapin <simon.sapin@octobus.net>
date Fri, 10 Dec 2021 16:18:58 +0100
parents 9b0e1f64656f
children 58074252db3c
line wrap: on
line source

// path utils module
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.

use hg::errors::HgError;
use hg::repo::Repo;
use hg::utils::current_dir;
use hg::utils::files::{get_bytes_from_path, relativize_path};
use hg::utils::hg_path::HgPath;
use hg::utils::hg_path::HgPathBuf;
use std::borrow::Cow;

pub struct RelativizePaths {
    repo_root: HgPathBuf,
    cwd: HgPathBuf,
    outside_repo: bool,
}

impl RelativizePaths {
    pub fn new(repo: &Repo) -> Result<Self, HgError> {
        let cwd = current_dir()?;
        let repo_root = repo.working_directory_path();
        let repo_root = cwd.join(repo_root); // Make it absolute
        let repo_root_hgpath =
            HgPathBuf::from(get_bytes_from_path(repo_root.to_owned()));

        if let Ok(cwd_relative_to_repo) = cwd.strip_prefix(&repo_root) {
            // The current directory is inside the repo, so we can work with
            // relative paths
            Ok(Self {
                repo_root: repo_root_hgpath,
                cwd: HgPathBuf::from(get_bytes_from_path(
                    cwd_relative_to_repo,
                )),
                outside_repo: false,
            })
        } else {
            Ok(Self {
                repo_root: repo_root_hgpath,
                cwd: HgPathBuf::from(get_bytes_from_path(cwd)),
                outside_repo: true,
            })
        }
    }

    pub fn relativize<'a>(&self, path: &'a HgPath) -> Cow<'a, [u8]> {
        if self.outside_repo {
            let joined = self.repo_root.join(path);
            Cow::Owned(relativize_path(&joined, &self.cwd).into_owned())
        } else {
            relativize_path(path, &self.cwd)
        }
    }
}