Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • mercurial/mercurial-devel
1 result
Show changes
Commits on Source (3)
  • Mitchell Kember's avatar
    rust-config: add username parsing · 879029f03324
    Mitchell Kember authored
    This adds Config::username which returns HGUSER, ui.username, or EMAIL in that
    order, similar to ui.username() in Python.
    
    I considered following the pattern of EDITOR, VISUAL, PAGER, etc. and using
    add_for_environment_variable, but it's not possible to get the same precendence
    as in Python that way (in particular HGUSER coming after the repo .hg/hgrc), at
    least not without significant changes.
    
    This will be used for 'rhg annotate -r wdir() -u' to annotate the username on
    lines that were changed in the working directory.
    879029f03324
  • Mitchell Kember's avatar
    rust-revlog: add RevisionOrWdir · 1a99e0c2d6d5
    Mitchell Kember authored
    This type represents either a checked Revision or the wdir() pseudo-revision
    (revision 0x7fffffff, node ffffffffffffffffffffffffffffffffffffffff).
    
    You construct it with revision.into() or Revision::wdir(), and destructure it
    with rev.exclude_wdir() which returns Option<Revision>.
    
    I considered something like `enum RevisionOrWdir { Wdir, Revision(Revision) }`,
    but decided on `struct RevisionOrWdir(BaseRevision)` for a few reasons:
    
    - It's more ergonomic for the ways it actually gets used, in my opinion.
    - It also avoids the possibility of an invalid value Revision(0x7fffffff).
    - It remains 4 bytes rather than 8.
    - It maintains the ordering: wdir is greater than all other revisions.
    
    I'm planning to use this for 'rhg annotate -r wdir()'.
    1a99e0c2d6d5
  • Mitchell Kember's avatar
    rust-revset: support resolving wdir() · bb30c89f1ffb
    Mitchell Kember authored
    This makes revset::resolve_single return RevisionOrWdir. Previously, it returned
    RevlogError::WDirUnsupported (leading to abort, not fallback) for 2147483647 and
    ffffffffffffffffffffffffffffffffffffffff. It did not recognize 'wdir()' itself,
    so that would lead to Python fallback. Now, it treats all 3 cases the same: it
    returns RevisionOrWdir::wdir() and lets the caller decide what to do.
    
    I changed rhg cat, files, and annotate to return HgError::unsupported in this
    case, since wdir is valid. I made `rhg status --change wdir()` behave the same
    as `rhg status`, conforming to the test in test-status.t.
    bb30c89f1ffb
......@@ -23,6 +23,7 @@
use self::layer::ConfigValue;
use crate::errors::HgError;
use crate::errors::{HgResultExt, IoResultExt};
use crate::exit_codes;
use crate::utils::files::get_bytes_from_os_str;
use format_bytes::{write_bytes, DisplayBytes};
use std::collections::HashSet;
......@@ -841,6 +842,32 @@
_ => None,
}
}
/// Returns the default username to be used in commits. Like ui.username()
/// in Python with acceptempty=False, but aborts rather than prompting for
/// input or falling back to the OS username and hostname.
pub fn username(&self) -> Result<Vec<u8>, HgError> {
if let Some(value) = env::var_os("HGUSER") {
if !value.is_empty() {
return Ok(value.into_encoded_bytes());
}
}
if let Some(value) = self.get_str(b"ui", b"username")? {
if !value.is_empty() {
return Ok(value.as_bytes().to_vec());
}
}
if let Some(value) = env::var_os("EMAIL") {
if !value.is_empty() {
return Ok(value.into_encoded_bytes());
}
}
Err(HgError::abort(
"no username supplied",
exit_codes::ABORT,
Some("use 'hg config --edit' to set your username".to_string()),
))
}
}
/// Corresponds to `usage.resources[.<dimension>]`.
......
......@@ -83,7 +83,11 @@
revset: &str,
mut files: Vec<&'a HgPath>,
) -> Result<CatOutput<'a>, RevlogError> {
let rev = crate::revset::resolve_single(revset, repo)?;
let Some(rev) =
crate::revset::resolve_single(revset, repo)?.exclude_wdir()
else {
return Err(HgError::unsupported("cat wdir not implemented").into());
};
let manifest = repo.manifest_for_rev(rev.into())?;
let mut results: Vec<(&'a HgPath, Vec<u8>)> = vec![];
let node = *repo.changelog()?.node_from_rev(rev);
......
......@@ -22,8 +22,12 @@
revset: &str,
narrow_matcher: Box<dyn Matcher + Sync>,
) -> Result<FilesForRev, RevlogError> {
let rev = crate::revset::resolve_single(revset, repo)?;
list_rev_tracked_files(repo, rev.into(), narrow_matcher)
match crate::revset::resolve_single(revset, repo)?.exclude_wdir() {
Some(rev) => list_rev_tracked_files(repo, rev.into(), narrow_matcher),
None => {
Err(HgError::unsupported("list wdir files not implemented").into())
}
}
}
/// List files under Mercurial control at a given revision.
......
......@@ -126,6 +126,39 @@
pub const WORKING_DIRECTORY_HEX: &str =
"ffffffffffffffffffffffffffffffffffffffff";
/// Either a checked revision or the working directory.
/// Note that [`Revision`] will never hold [`WORKING_DIRECTORY_REVISION`]
/// because that is not a valid revision in any revlog.
#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct RevisionOrWdir(BaseRevision);
impl From<Revision> for RevisionOrWdir {
fn from(value: Revision) -> Self {
RevisionOrWdir(value.0)
}
}
impl RevisionOrWdir {
/// Creates a [`RevisionOrWdir`] representing the working directory.
pub fn wdir() -> Self {
RevisionOrWdir(WORKING_DIRECTORY_REVISION.0)
}
/// Returns the revision, or `None` if this is the working directory.
pub fn exclude_wdir(self) -> Option<Revision> {
if self.0 == WORKING_DIRECTORY_REVISION.0 {
None
} else {
Some(Revision(self.0))
}
}
/// Returns true if this is the working directory.
pub fn is_wdir(&self) -> bool {
*self == Self::wdir()
}
}
/// The simplest expression of what we need of Mercurial DAGs.
pub trait Graph {
/// Return the two parents of the given `Revision`.
......@@ -974,4 +1007,10 @@
}
};
}
#[test]
fn test_revision_or_wdir_ord() {
let highest: RevisionOrWdir = Revision(i32::MAX - 1).into();
assert!(highest < RevisionOrWdir::wdir());
}
}
......@@ -4,6 +4,6 @@
use crate::errors::HgError;
use crate::repo::Repo;
use crate::revlog::NodePrefix;
use crate::revlog::{NodePrefix, RevisionOrWdir};
use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
use crate::revlog::{Revlog, RevlogError};
......@@ -8,6 +8,6 @@
use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
use crate::revlog::{Revlog, RevlogError};
use crate::Node;
use crate::{Node, WORKING_DIRECTORY_REVISION};
/// Resolve a query string into a single revision.
///
......@@ -15,9 +15,9 @@
pub fn resolve_single(
input: &str,
repo: &Repo,
) -> Result<Revision, RevlogError> {
) -> Result<RevisionOrWdir, RevlogError> {
let changelog = repo.changelog()?;
match input {
"." => {
let p1 = repo.dirstate_parents()?.p1;
......@@ -19,7 +19,7 @@
let changelog = repo.changelog()?;
match input {
"." => {
let p1 = repo.dirstate_parents()?.p1;
return changelog.revlog.rev_from_node(p1.into());
return Ok(changelog.revlog.rev_from_node(p1.into())?.into());
}
......@@ -25,5 +25,6 @@
}
"null" => return Ok(NULL_REVISION),
"null" => return Ok(NULL_REVISION.into()),
"wdir()" => return Ok(RevisionOrWdir::wdir()),
_ => {}
}
......@@ -27,7 +28,7 @@
_ => {}
}
match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
match resolve(input, &changelog.revlog) {
Err(RevlogError::InvalidRevision(revision)) => {
// TODO: support for the rest of the language here.
let msg = format!("cannot parse revset '{}'", revision);
......@@ -41,9 +42,8 @@
/// the changelog, such as in `hg debugdata --manifest` CLI argument.
///
/// * A non-negative decimal integer for a revision number, or
/// * An hexadecimal string, for the unique node ID that starts with this
/// prefix
/// * A hexadecimal string, for the unique node ID that starts with this prefix
pub fn resolve_rev_number_or_hex_prefix(
input: &str,
revlog: &Revlog,
) -> Result<Revision, RevlogError> {
......@@ -46,6 +46,16 @@
pub fn resolve_rev_number_or_hex_prefix(
input: &str,
revlog: &Revlog,
) -> Result<Revision, RevlogError> {
match resolve(input, revlog)?.exclude_wdir() {
Some(rev) => Ok(rev),
None => Err(RevlogError::WDirUnsupported),
}
}
fn resolve(
input: &str,
revlog: &Revlog,
) -> Result<RevisionOrWdir, RevlogError> {
// The Python equivalent of this is part of `revsymbol` in
// `mercurial/scmutil.py`
......@@ -50,4 +60,3 @@
// The Python equivalent of this is part of `revsymbol` in
// `mercurial/scmutil.py`
if let Ok(integer) = input.parse::<i32>() {
......@@ -53,11 +62,13 @@
if let Ok(integer) = input.parse::<i32>() {
if integer.to_string() == input
&& integer >= 0
&& revlog.has_rev(integer.into())
{
// This is fine because we've just checked that the revision is
// valid for the given revlog.
return Ok(Revision(integer));
if integer.to_string() == input && integer >= 0 {
if integer == WORKING_DIRECTORY_REVISION.0 {
return Ok(RevisionOrWdir::wdir());
}
if revlog.has_rev(integer.into()) {
// This is fine because we've just checked that the revision is
// valid for the given revlog.
return Ok(Revision(integer).into());
}
}
}
if let Ok(prefix) = NodePrefix::from_hex(input) {
......@@ -61,7 +72,8 @@
}
}
if let Ok(prefix) = NodePrefix::from_hex(input) {
if prefix.is_prefix_of(&Node::from_hex(WORKING_DIRECTORY_HEX).unwrap())
{
return Err(RevlogError::WDirUnsupported);
let wdir_node =
Node::from_hex(WORKING_DIRECTORY_HEX).expect("wdir hex is valid");
if prefix.is_prefix_of(&wdir_node) {
return Ok(RevisionOrWdir::wdir());
}
......@@ -67,5 +79,5 @@
}
return revlog.rev_from_node(prefix);
return Ok(revlog.rev_from_node(prefix)?.into());
}
Err(RevlogError::InvalidRevision(input.to_string()))
}
......@@ -151,6 +151,11 @@
let rev = args.get_one::<String>("rev").expect("rev has a default");
let rev = hg::revset::resolve_single(rev, repo)?;
let Some(rev) = rev.exclude_wdir() else {
return Err(CommandError::unsupported(
"annotate wdir not implemented",
));
};
let files = match args.get_many::<OsString>("files") {
None => vec![],
......
......@@ -25,7 +25,7 @@
use hg::repo::Repo;
use hg::revlog::manifest::Manifest;
use hg::revlog::options::{default_revlog_options, RevlogOpenOptions};
use hg::revlog::RevlogType;
use hg::revlog::{RevisionOrWdir, RevlogError, RevlogType};
use hg::utils::debug::debug_wait_for_file;
use hg::utils::files::{
get_bytes_from_os_str, get_bytes_from_os_string, get_path_from_bytes,
......@@ -171,5 +171,11 @@
let Some(revs) = revs else {
return Ok(None);
};
let resolve = |input| match hg::revset::resolve_single(input, repo)?
.exclude_wdir()
{
Some(rev) => Ok(rev),
None => Err(RevlogError::WDirUnsupported),
};
match revs.as_slice() {
[] => Ok(None),
......@@ -174,9 +180,6 @@
match revs.as_slice() {
[] => Ok(None),
[rev1, rev2] => Ok(Some((
hg::revset::resolve_single(rev1, repo)?,
hg::revset::resolve_single(rev2, repo)?,
))),
[rev1, rev2] => Ok(Some((resolve(rev1)?, resolve(rev2)?))),
_ => Err(CommandError::unsupported("expected 0 or 2 --rev flags")),
}
}
......@@ -312,6 +315,8 @@
let change = change
.map(|rev| hg::revset::resolve_single(rev, repo))
.transpose()?;
// Treat `rhg status --change wdir()` the same as `rhg status`.
let change = change.and_then(RevisionOrWdir::exclude_wdir);
if verbose && has_unfinished_state(repo)? {
return Err(CommandError::unsupported(
......