branching: merge with default
We merge with the current children of the bad merge (37b52b938579)
--- a/mercurial/cmdutil.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/cmdutil.py Fri Dec 15 11:10:24 2023 +0100
@@ -2381,8 +2381,19 @@
full=False,
)
):
+ entry = dirstate.get_entry(f)
+ # We don't want to even attmpt to add back files that have been removed
+ # It would lead to a misleading message saying we're adding the path,
+ # and can also lead to file/dir conflicts when attempting to add it.
+ removed = entry and entry.removed
exact = match.exact(f)
- if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
+ if (
+ exact
+ or not explicitonly
+ and f not in wctx
+ and repo.wvfs.lexists(f)
+ and not removed
+ ):
if cca:
cca(f)
names.append(f)
--- a/mercurial/commands.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/commands.py Fri Dec 15 11:10:24 2023 +0100
@@ -29,6 +29,7 @@
copies,
debugcommands as debugcommandsmod,
destutil,
+ diffutil,
discovery,
encoding,
error,
@@ -2655,7 +2656,7 @@
if change:
repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
ctx2 = logcmdutil.revsingle(repo, change, None)
- ctx1 = logcmdutil.diff_parent(ctx2)
+ ctx1 = diffutil.diff_parent(ctx2)
elif from_rev or to_rev:
repo = scmutil.unhidehashlikerevs(
repo, [from_rev] + [to_rev], b'nowarn'
--- a/mercurial/diffutil.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/diffutil.py Fri Dec 15 11:10:24 2023 +0100
@@ -16,6 +16,7 @@
)
from .i18n import _
+from .node import nullrev
from . import (
mdiff,
@@ -155,3 +156,35 @@
)
return mdiff.diffopts(**pycompat.strkwargs(buildopts))
+
+
+def diff_parent(ctx):
+ """get the context object to use as parent when diffing
+
+
+ If diff.merge is enabled, an overlayworkingctx of the auto-merged parents will be returned.
+ """
+ repo = ctx.repo()
+ if repo.ui.configbool(b"diff", b"merge") and ctx.p2().rev() != nullrev:
+ # avoid circular import
+ from . import (
+ context,
+ merge,
+ )
+
+ wctx = context.overlayworkingctx(repo)
+ wctx.setbase(ctx.p1())
+ with repo.ui.configoverride(
+ {
+ (
+ b"ui",
+ b"forcemerge",
+ ): b"internal:merge3-lie-about-conflicts",
+ },
+ b"merge-diff",
+ ):
+ with repo.ui.silent():
+ merge.merge(ctx.p2(), wc=wctx)
+ return wctx
+ else:
+ return ctx.p1()
--- a/mercurial/dirstate.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/dirstate.py Fri Dec 15 11:10:24 2023 +0100
@@ -408,16 +408,6 @@
"""
return self._changing_level > 0
- def pendingparentchange(self):
- return self.is_changing_parent()
-
- def is_changing_parent(self):
- """Returns true if the dirstate is in the middle of a set of changes
- that modify the dirstate parent.
- """
- self._ui.deprecwarn(b"dirstate.is_changing_parents", b"6.5")
- return self.is_changing_parents
-
@property
def is_changing_parents(self):
"""Returns true if the dirstate is in the middle of a set of changes
--- a/mercurial/extensions.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/extensions.py Fri Dec 15 11:10:24 2023 +0100
@@ -625,9 +625,8 @@
def __init__(self, container, funcname, wrapper):
assert callable(wrapper)
if not isinstance(funcname, str):
- msg = b"pass wrappedfunction target name as `str`, not `bytes`"
- util.nouideprecwarn(msg, b"6.6", stacklevel=2)
- funcname = pycompat.sysstr(funcname)
+ msg = b"wrappedfunction target name should be `str`, not `bytes`"
+ raise TypeError(msg)
self._container = container
self._funcname = funcname
self._wrapper = wrapper
@@ -675,9 +674,8 @@
assert callable(wrapper)
if not isinstance(funcname, str):
- msg = b"pass wrapfunction target name as `str`, not `bytes`"
- util.nouideprecwarn(msg, b"6.6", stacklevel=2)
- funcname = pycompat.sysstr(funcname)
+ msg = b"wrapfunction target name should be `str`, not `bytes`"
+ raise TypeError(msg)
origfn = getattr(container, funcname)
assert callable(origfn)
--- a/mercurial/localrepo.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/localrepo.py Fri Dec 15 11:10:24 2023 +0100
@@ -2921,17 +2921,7 @@
unfi = self.unfiltered()
- if full:
- msg = (
- "`full` argument for `repo.updatecaches` is deprecated\n"
- "(use `caches=repository.CACHE_ALL` instead)"
- )
- self.ui.deprecwarn(msg, b"5.9")
- caches = repository.CACHES_ALL
- if full == b"post-clone":
- caches = repository.CACHES_POST_CLONE
- caches = repository.CACHES_ALL
- elif caches is None:
+ if caches is None:
caches = repository.CACHES_DEFAULT
if repository.CACHE_BRANCHMAP_SERVED in caches:
--- a/mercurial/logcmdutil.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/logcmdutil.py Fri Dec 15 11:10:24 2023 +0100
@@ -11,18 +11,18 @@
import posixpath
from .i18n import _
-from .node import nullrev, wdirrev
+from .node import wdirrev
from .thirdparty import attr
from . import (
dagop,
+ diffutil,
error,
formatter,
graphmod,
match as matchmod,
mdiff,
- merge,
patch,
pathutil,
pycompat,
@@ -69,36 +69,7 @@
return limit
-def diff_parent(ctx):
- """get the context object to use as parent when diffing
-
-
- If diff.merge is enabled, an overlayworkingctx of the auto-merged parents will be returned.
- """
- repo = ctx.repo()
- if repo.ui.configbool(b"diff", b"merge") and ctx.p2().rev() != nullrev:
- # avoid cycle context -> subrepo -> cmdutil -> logcmdutil
- from . import context
-
- wctx = context.overlayworkingctx(repo)
- wctx.setbase(ctx.p1())
- with repo.ui.configoverride(
- {
- (
- b"ui",
- b"forcemerge",
- ): b"internal:merge3-lie-about-conflicts",
- },
- b"merge-diff",
- ):
- with repo.ui.silent():
- merge.merge(ctx.p2(), wc=wctx)
- return wctx
- else:
- return ctx.p1()
-
-
-def diffordiffstat(
+def get_diff_chunks(
ui,
repo,
diffopts,
@@ -107,14 +78,10 @@
match,
changes=None,
stat=False,
- fp=None,
- graphwidth=0,
prefix=b'',
root=b'',
- listsubrepos=False,
hunksfilterfn=None,
):
- '''show diff or diffstat.'''
if root:
relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
else:
@@ -159,14 +126,11 @@
if stat:
diffopts = diffopts.copy(context=0, noprefix=False)
- width = 80
- if not ui.plain():
- width = ui.termwidth() - graphwidth
# If an explicit --root was given, don't respect ui.relative-paths
if not relroot:
pathfn = compose(scmutil.getuipathfn(repo), pathfn)
- chunks = ctx2.diff(
+ return ctx2.diff(
ctx1,
match,
changes,
@@ -176,6 +140,45 @@
hunksfilterfn=hunksfilterfn,
)
+
+def diffordiffstat(
+ ui,
+ repo,
+ diffopts,
+ ctx1,
+ ctx2,
+ match,
+ changes=None,
+ stat=False,
+ fp=None,
+ graphwidth=0,
+ prefix=b'',
+ root=b'',
+ listsubrepos=False,
+ hunksfilterfn=None,
+):
+ '''show diff or diffstat.'''
+
+ chunks = get_diff_chunks(
+ ui,
+ repo,
+ diffopts,
+ ctx1,
+ ctx2,
+ match,
+ changes=changes,
+ stat=stat,
+ prefix=prefix,
+ root=root,
+ hunksfilterfn=hunksfilterfn,
+ )
+
+ if stat:
+ diffopts = diffopts.copy(context=0, noprefix=False)
+ width = 80
+ if not ui.plain():
+ width = ui.termwidth() - graphwidth
+
if fp is not None or ui.canwritewithoutlabels():
out = fp or ui
if stat:
@@ -241,7 +244,7 @@
ui,
ctx.repo(),
diffopts,
- diff_parent(ctx),
+ diffutil.diff_parent(ctx),
ctx,
match=self._makefilematcher(ctx),
stat=stat,
@@ -249,6 +252,33 @@
hunksfilterfn=self._makehunksfilter(ctx),
)
+ def getdiffstats(self, ui, ctx, diffopts, stat=False):
+ chunks = get_diff_chunks(
+ ui,
+ ctx.repo(),
+ diffopts,
+ diffutil.diff_parent(ctx),
+ ctx,
+ match=self._makefilematcher(ctx),
+ stat=stat,
+ hunksfilterfn=self._makehunksfilter(ctx),
+ )
+
+ diffdata = []
+ for filename, additions, removals, binary in patch.diffstatdata(
+ util.iterlines(chunks)
+ ):
+ diffdata.append(
+ {
+ b"name": filename,
+ b"additions": additions,
+ b"removals": removals,
+ b"binary": binary,
+ }
+ )
+
+ return diffdata
+
def changesetlabels(ctx):
labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
@@ -525,9 +555,10 @@
)
if self._includestat or b'diffstat' in datahint:
- self.ui.pushbuffer()
- self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
- fm.data(diffstat=self.ui.popbuffer())
+ data = self._differ.getdiffstats(
+ self.ui, ctx, self._diffopts, stat=True
+ )
+ fm.data(diffstat=fm.formatlist(data, name=b'diffstat'))
if self._includediff or b'diff' in datahint:
self.ui.pushbuffer()
self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
--- a/mercurial/pycompat.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/pycompat.py Fri Dec 15 11:10:24 2023 +0100
@@ -12,7 +12,6 @@
import builtins
import codecs
import concurrent.futures as futures
-import functools
import getopt
import http.client as httplib
import http.cookiejar as cookielib
@@ -352,26 +351,11 @@
return sysbytes(doc)
-def _wrapattrfunc(f):
- @functools.wraps(f)
- def w(object, name, *args):
- if isinstance(name, bytes):
- from . import util
-
- msg = b'function "%s" take `str` as argument, not `bytes`'
- fname = f.__name__.encode('ascii')
- msg %= fname
- util.nouideprecwarn(msg, b"6.6", stacklevel=2)
- return f(object, sysstr(name), *args)
-
- return w
-
-
# these wrappers are automagically imported by hgloader
-delattr = _wrapattrfunc(builtins.delattr)
-getattr = _wrapattrfunc(builtins.getattr)
-hasattr = _wrapattrfunc(builtins.hasattr)
-setattr = _wrapattrfunc(builtins.setattr)
+delattr = builtins.delattr
+getattr = builtins.getattr
+hasattr = builtins.hasattr
+setattr = builtins.setattr
xrange = builtins.range
unicode = str
@@ -386,7 +370,7 @@
return builtins.open(name, sysstr(mode), buffering, encoding)
-safehasattr = _wrapattrfunc(builtins.hasattr)
+safehasattr = builtins.hasattr
def _getoptbwrapper(
--- a/mercurial/statprof.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/statprof.py Fri Dec 15 11:10:24 2023 +0100
@@ -384,7 +384,7 @@
time = sample.time
stack = sample.stack
sites = [
- b'\1'.join([s.path, b'%d' % s.lineno, s.function])
+ b'\1'.join([s.path, b'%d' % s.lineno or -1, s.function])
for s in stack
]
file.write(b"%d\0%s\n" % (time, b'\0'.join(sites)))
@@ -663,7 +663,7 @@
count / relevant_samples * 100,
pycompat.fsencode(parent.filename()),
pycompat.sysbytes(parent.function),
- parent.lineno,
+ parent.lineno or -1,
pycompat.sysbytes(parent.getsource(50)),
)
)
@@ -705,7 +705,7 @@
b' %6.2f%% line %s: %s\n'
% (
count / relevant_samples * 100,
- child.lineno,
+ child.lineno or -1,
pycompat.sysbytes(child.getsource(50)),
)
)
@@ -865,7 +865,7 @@
stack.append(
(
pycompat.sysstr(frame.path),
- frame.lineno,
+ frame.lineno or -1,
pycompat.sysstr(frame.function),
)
)
@@ -954,7 +954,10 @@
(
(
'%s:%d'
- % (simplifypath(pycompat.sysstr(frame.path)), frame.lineno),
+ % (
+ simplifypath(pycompat.sysstr(frame.path)),
+ frame.lineno or -1,
+ ),
pycompat.sysstr(frame.function),
)
for frame in sample.stack
--- a/mercurial/templatekw.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/templatekw.py Fri Dec 15 11:10:24 2023 +0100
@@ -270,7 +270,7 @@
ui = context.resource(mapping, b'ui')
ctx = context.resource(mapping, b'ctx')
diffopts = diffutil.diffallopts(ui, {b'noprefix': False})
- diff = ctx.diff(opts=diffopts)
+ diff = ctx.diff(diffutil.diff_parent(ctx), opts=diffopts)
stats = patch.diffstatdata(util.iterlines(diff))
maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
return b'%d: +%d/-%d' % (len(stats), adds, removes)
--- a/mercurial/upgrade_utils/actions.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/upgrade_utils/actions.py Fri Dec 15 11:10:24 2023 +0100
@@ -5,6 +5,7 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
+import random
from ..i18n import _
from .. import (
@@ -409,9 +410,17 @@
def fromrepo(repo):
# Mercurial 4.0 changed changelogs to not use delta chains. Search for
# changelogs with deltas.
- cl = repo.changelog
+ cl = repo.unfiltered().changelog
+ if len(cl) <= 1000:
+ some_rev = list(cl)
+ else:
+ # do a random sampling to speeds things up Scanning the whole
+ # repository can get really slow on bigger repo.
+ some_rev = sorted(
+ {random.randint(0, len(cl) - 1) for x in range(1000)}
+ )
chainbase = cl.chainbase
- return all(rev == chainbase(rev) for rev in cl)
+ return all(rev == chainbase(rev) for rev in some_rev)
@staticmethod
def fromconfig(repo):
--- a/mercurial/utils/urlutil.py Thu Dec 07 02:07:16 2023 +0100
+++ b/mercurial/utils/urlutil.py Fri Dec 15 11:10:24 2023 +0100
@@ -14,7 +14,6 @@
error,
pycompat,
urllibcompat,
- util,
)
from . import (
@@ -680,8 +679,7 @@
"""
if isinstance(attr, bytes):
msg = b'pathsuboption take `str` as "attr" argument, not `bytes`'
- util.nouideprecwarn(msg, b"6.6", stacklevel=2)
- attr = attr.decode('ascii')
+ raise TypeError(msg)
def register(func):
_pathsuboptions[option] = (attr, func)
@@ -923,14 +921,6 @@
new._setup_url(self._pushloc)
return new
- def pushloc(self):
- """compatibility layer for the deprecated attributes"""
- from .. import util # avoid a cycle
-
- msg = "don't use path.pushloc, use path.get_push_variant()"
- util.nouideprecwarn(msg, b"6.5")
- return self._pushloc
-
def _validate_path(self):
# When given a raw location but not a symbolic name, validate the
# location is valid.
--- a/rust/hg-core/src/operations/mod.rs Thu Dec 07 02:07:16 2023 +0100
+++ b/rust/hg-core/src/operations/mod.rs Fri Dec 15 11:10:24 2023 +0100
@@ -5,6 +5,8 @@
mod cat;
mod debugdata;
mod list_tracked_files;
+mod status_rev_rev;
pub use cat::{cat, CatOutput};
pub use debugdata::{debug_data, DebugDataKind};
pub use list_tracked_files::{list_rev_tracked_files, FilesForRev};
+pub use status_rev_rev::{status_rev_rev_no_copies, DiffStatus, StatusRevRev};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/operations/status_rev_rev.rs Fri Dec 15 11:10:24 2023 +0100
@@ -0,0 +1,89 @@
+use crate::errors::HgError;
+use crate::matchers::Matcher;
+use crate::repo::Repo;
+use crate::revlog::manifest::Manifest;
+use crate::utils::filter_map_results;
+use crate::utils::hg_path::HgPath;
+use crate::utils::merge_join_results_by;
+
+use crate::Revision;
+
+use itertools::EitherOrBoth;
+
+#[derive(Debug, Copy, Clone)]
+pub enum DiffStatus {
+ Removed,
+ Added,
+ Matching,
+ Modified,
+}
+
+pub struct StatusRevRev {
+ manifest1: Manifest,
+ manifest2: Manifest,
+ narrow_matcher: Box<dyn Matcher>,
+}
+
+fn manifest_for_rev(repo: &Repo, rev: Revision) -> Result<Manifest, HgError> {
+ repo.manifest_for_rev(rev.into()).map_err(|e| {
+ HgError::corrupted(format!(
+ "manifest lookup failed for revision {}: {}",
+ rev, e
+ ))
+ })
+}
+
+pub fn status_rev_rev_no_copies(
+ repo: &Repo,
+ rev1: Revision,
+ rev2: Revision,
+ narrow_matcher: Box<dyn Matcher>,
+) -> Result<StatusRevRev, HgError> {
+ let manifest1 = manifest_for_rev(repo, rev1)?;
+ let manifest2 = manifest_for_rev(repo, rev2)?;
+ Ok(StatusRevRev {
+ manifest1,
+ manifest2,
+ narrow_matcher,
+ })
+}
+
+impl StatusRevRev {
+ pub fn iter(
+ &self,
+ ) -> impl Iterator<Item = Result<(&HgPath, DiffStatus), HgError>> {
+ let iter1 = self.manifest1.iter();
+ let iter2 = self.manifest2.iter();
+
+ let merged =
+ merge_join_results_by(iter1, iter2, |i1, i2| i1.path.cmp(i2.path));
+
+ filter_map_results(merged, |entry| {
+ let (path, status) = match entry {
+ EitherOrBoth::Left(entry) => {
+ let path = entry.path;
+ (path, DiffStatus::Removed)
+ }
+ EitherOrBoth::Right(entry) => {
+ let path = entry.path;
+ (path, DiffStatus::Added)
+ }
+ EitherOrBoth::Both(entry1, entry2) => {
+ let path = entry1.path;
+ if entry1.node_id().unwrap() == entry2.node_id().unwrap()
+ && entry1.flags == entry2.flags
+ {
+ (path, DiffStatus::Matching)
+ } else {
+ (path, DiffStatus::Modified)
+ }
+ }
+ };
+ Ok(if self.narrow_matcher.matches(path) {
+ Some((path, status))
+ } else {
+ None
+ })
+ })
+ }
+}
--- a/rust/hg-core/src/utils.rs Thu Dec 07 02:07:16 2023 +0100
+++ b/rust/hg-core/src/utils.rs Fri Dec 15 11:10:24 2023 +0100
@@ -11,7 +11,10 @@
use crate::utils::hg_path::HgPath;
use im_rc::ordmap::DiffItem;
use im_rc::ordmap::OrdMap;
+use itertools::EitherOrBoth;
+use itertools::Itertools;
use std::cell::Cell;
+use std::cmp::Ordering;
use std::fmt;
use std::{io::Write, ops::Deref};
@@ -499,6 +502,43 @@
})
}
+/// Like `itertools::merge_join_by`, but merges fallible iterators.
+///
+/// The callback is only used for Ok values. Errors are passed through as-is.
+/// Errors compare less than Ok values, which makes the error handling
+/// conservative.
+pub fn merge_join_results_by<'a, I1, I2, F, A, B, E>(
+ iter1: I1,
+ iter2: I2,
+ f: F,
+) -> impl Iterator<Item = Result<EitherOrBoth<A, B>, E>> + 'a
+where
+ I1: Iterator<Item = Result<A, E>> + 'a,
+ I2: Iterator<Item = Result<B, E>> + 'a,
+ F: FnMut(&A, &B) -> Ordering + 'a,
+{
+ let mut g = f;
+ iter1
+ .merge_join_by(iter2, move |i1, i2| match i1 {
+ Err(_) => Ordering::Less,
+ Ok(i1) => match i2 {
+ Err(_) => Ordering::Greater,
+ Ok(i2) => g(i1, i2),
+ },
+ })
+ .map(|result| match result {
+ EitherOrBoth::Left(Err(e)) => Err(e),
+ EitherOrBoth::Right(Err(e)) => Err(e),
+ EitherOrBoth::Both(Err(e), _) => Err(e),
+ EitherOrBoth::Both(_, Err(e)) => Err(e),
+ EitherOrBoth::Left(Ok(v)) => Ok(EitherOrBoth::Left(v)),
+ EitherOrBoth::Right(Ok(v)) => Ok(EitherOrBoth::Right(v)),
+ EitherOrBoth::Both(Ok(v1), Ok(v2)) => {
+ Ok(EitherOrBoth::Both(v1, v2))
+ }
+ })
+}
+
/// Force the global rayon threadpool to not exceed 16 concurrent threads
/// unless the user has specified a value.
/// This is a stop-gap measure until we figure out why using more than 16
--- a/rust/rhg/src/commands/status.rs Thu Dec 07 02:07:16 2023 +0100
+++ b/rust/rhg/src/commands/status.rs Fri Dec 15 11:10:24 2023 +0100
@@ -30,12 +30,15 @@
use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
use hg::DirstateStatus;
use hg::PatternFileWarning;
+use hg::Revision;
use hg::StatusError;
use hg::StatusOptions;
use hg::{self, narrow, sparse};
use log::info;
use rayon::prelude::*;
+use std::borrow::Cow;
use std::io;
+use std::mem::take;
use std::path::PathBuf;
pub const HELP_TEXT: &str = "
@@ -140,6 +143,38 @@
.action(clap::ArgAction::SetTrue)
.long("verbose"),
)
+ .arg(
+ Arg::new("rev")
+ .help("show difference from/to revision")
+ .long("rev")
+ .num_args(1)
+ .action(clap::ArgAction::Append)
+ .value_name("REV"),
+ )
+}
+
+fn parse_revpair(
+ repo: &Repo,
+ revs: Option<Vec<String>>,
+) -> Result<Option<(Revision, Revision)>, CommandError> {
+ let revs = match revs {
+ None => return Ok(None),
+ Some(revs) => revs,
+ };
+ if revs.is_empty() {
+ return Ok(None);
+ }
+ if revs.len() != 2 {
+ return Err(CommandError::unsupported("expected 0 or 2 --rev flags"));
+ }
+
+ let rev1 = &revs[0];
+ let rev2 = &revs[1];
+ let rev1 = hg::revset::resolve_single(rev1, repo)
+ .map_err(|e| (e, rev1.as_str()))?;
+ let rev2 = hg::revset::resolve_single(rev2, repo)
+ .map_err(|e| (e, rev2.as_str()))?;
+ Ok(Some((rev1, rev2)))
}
/// Pure data type allowing the caller to specify file states to display
@@ -229,6 +264,7 @@
let config = invocation.config;
let args = invocation.subcommand_args;
+ let revs = args.get_many::<String>("rev");
let print0 = args.get_flag("print0");
let verbose = args.get_flag("verbose")
|| config.get_bool(b"ui", b"verbose")?
@@ -262,6 +298,7 @@
|| config.get_bool(b"ui", b"statuscopies")?;
let repo = invocation.repo?;
+ let revpair = parse_revpair(repo, revs.map(|i| i.cloned().collect()))?;
if verbose && has_unfinished_state(repo)? {
return Err(CommandError::unsupported(
@@ -285,13 +322,37 @@
type StatusResult<'a> =
Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>;
+ let relative_status = config
+ .get_option(b"commands", b"status.relative")?
+ .expect("commands.status.relative should have a default value");
+
+ let relativize_paths = relative_status || {
+ // See in Python code with `getuipathfn` usage in `commands.py`.
+ let legacy_relative_behavior = args.contains_id("file");
+ match relative_paths(invocation.config)? {
+ RelativePaths::Legacy => legacy_relative_behavior,
+ RelativePaths::Bool(v) => v,
+ }
+ };
+
+ let mut output = DisplayStatusPaths {
+ ui,
+ no_status,
+ relativize: if relativize_paths {
+ Some(RelativizePaths::new(repo)?)
+ } else {
+ None
+ },
+ print0,
+ };
+
let after_status = |res: StatusResult| -> Result<_, CommandError> {
let (mut ds_status, pattern_warnings) = res?;
for warning in pattern_warnings {
ui.write_stderr(&format_pattern_file_warning(&warning, repo))?;
}
- for (path, error) in ds_status.bad {
+ for (path, error) in take(&mut ds_status.bad) {
let error = match error {
hg::BadMatch::OsError(code) => {
std::io::Error::from_raw_os_error(code).to_string()
@@ -322,8 +383,7 @@
})?;
let working_directory_vfs = repo.working_directory_vfs();
let store_vfs = repo.store_vfs();
- let res: Vec<_> = ds_status
- .unsure
+ let res: Vec<_> = take(&mut ds_status.unsure)
.into_par_iter()
.map(|to_check| {
// The compiler seems to get a bit confused with complex
@@ -370,55 +430,12 @@
}
}
- let relative_status = config
- .get_option(b"commands", b"status.relative")?
- .expect("commands.status.relative should have a default value");
-
- let relativize_paths = relative_status || {
- // See in Python code with `getuipathfn` usage in `commands.py`.
- let legacy_relative_behavior = args.contains_id("file");
- match relative_paths(invocation.config)? {
- RelativePaths::Legacy => legacy_relative_behavior,
- RelativePaths::Bool(v) => v,
- }
- };
-
- let output = DisplayStatusPaths {
- ui,
- no_status,
- relativize: if relativize_paths {
- Some(RelativizePaths::new(repo)?)
- } else {
- None
- },
- print0,
- };
- if display_states.modified {
- output.display(b"M ", "status.modified", ds_status.modified)?;
- }
- if display_states.added {
- output.display(b"A ", "status.added", ds_status.added)?;
- }
- if display_states.removed {
- output.display(b"R ", "status.removed", ds_status.removed)?;
- }
- if display_states.deleted {
- output.display(b"! ", "status.deleted", ds_status.deleted)?;
- }
- if display_states.unknown {
- output.display(b"? ", "status.unknown", ds_status.unknown)?;
- }
- if display_states.ignored {
- output.display(b"I ", "status.ignored", ds_status.ignored)?;
- }
- if display_states.clean {
- output.display(b"C ", "status.clean", ds_status.clean)?;
- }
-
let dirstate_write_needed = ds_status.dirty;
let filesystem_time_at_status_start =
ds_status.filesystem_time_at_status_start;
+ output.output(display_states, ds_status)?;
+
Ok((
fixup,
dirstate_write_needed,
@@ -426,6 +443,57 @@
))
};
let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?;
+
+ match revpair {
+ Some((rev1, rev2)) => {
+ let mut ds_status = DirstateStatus::default();
+ if list_copies {
+ return Err(CommandError::unsupported(
+ "status --rev --rev with copy information is not implemented yet",
+ ));
+ }
+
+ let stat = hg::operations::status_rev_rev_no_copies(
+ repo,
+ rev1,
+ rev2,
+ narrow_matcher,
+ )?;
+ for entry in stat.iter() {
+ let (path, status) = entry?;
+ let path = StatusPath {
+ path: Cow::Borrowed(path),
+ copy_source: None,
+ };
+ match status {
+ hg::operations::DiffStatus::Removed => {
+ if display_states.removed {
+ ds_status.removed.push(path)
+ }
+ }
+ hg::operations::DiffStatus::Added => {
+ if display_states.added {
+ ds_status.added.push(path)
+ }
+ }
+ hg::operations::DiffStatus::Modified => {
+ if display_states.modified {
+ ds_status.modified.push(path)
+ }
+ }
+ hg::operations::DiffStatus::Matching => {
+ if display_states.clean {
+ ds_status.clean.push(path)
+ }
+ }
+ }
+ }
+ output.output(display_states, ds_status)?;
+ return Ok(());
+ }
+ None => (),
+ }
+
let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?;
let matcher = match (repo.has_narrow(), repo.has_sparse()) {
(true, true) => {
@@ -628,6 +696,35 @@
}
Ok(())
}
+
+ fn output(
+ &mut self,
+ display_states: DisplayStates,
+ ds_status: DirstateStatus,
+ ) -> Result<(), CommandError> {
+ if display_states.modified {
+ self.display(b"M ", "status.modified", ds_status.modified)?;
+ }
+ if display_states.added {
+ self.display(b"A ", "status.added", ds_status.added)?;
+ }
+ if display_states.removed {
+ self.display(b"R ", "status.removed", ds_status.removed)?;
+ }
+ if display_states.deleted {
+ self.display(b"! ", "status.deleted", ds_status.deleted)?;
+ }
+ if display_states.unknown {
+ self.display(b"? ", "status.unknown", ds_status.unknown)?;
+ }
+ if display_states.ignored {
+ self.display(b"I ", "status.ignored", ds_status.ignored)?;
+ }
+ if display_states.clean {
+ self.display(b"C ", "status.clean", ds_status.clean)?;
+ }
+ Ok(())
+ }
}
/// Outcome of the additional check for an ambiguous tracked file
--- a/rust/rhg/src/main.rs Thu Dec 07 02:07:16 2023 +0100
+++ b/rust/rhg/src/main.rs Fri Dec 15 11:10:24 2023 +0100
@@ -524,13 +524,20 @@
std::process::exit(exit_code(&result, use_detailed_exit_code))
}
+mod commands {
+ pub mod cat;
+ pub mod config;
+ pub mod debugdata;
+ pub mod debugignorerhg;
+ pub mod debugrequirements;
+ pub mod debugrhgsparse;
+ pub mod files;
+ pub mod root;
+ pub mod status;
+}
+
macro_rules! subcommands {
($( $command: ident )+) => {
- mod commands {
- $(
- pub mod $command;
- )+
- }
fn add_subcommand_args(app: clap::Command) -> clap::Command {
app
--- a/setup.py Thu Dec 07 02:07:16 2023 +0100
+++ b/setup.py Fri Dec 15 11:10:24 2023 +0100
@@ -221,6 +221,9 @@
self.cmd = cmd
self.env = env
+ def __repr__(self):
+ return f"<hgcommand cmd={self.cmd} env={self.env}>"
+
def run(self, args):
cmd = self.cmd + args
returncode, out, err = runcmd(cmd, self.env)
@@ -295,9 +298,15 @@
if attempt(hgcmd + check_cmd, hgenv):
return hgcommand(hgcmd, hgenv)
- # Fall back to trying the local hg installation.
+ # Fall back to trying the local hg installation (pure python)
+ repo_hg = os.path.join(os.path.dirname(__file__), 'hg')
hgenv = localhgenv()
- hgcmd = [sys.executable, 'hg']
+ hgcmd = [sys.executable, repo_hg]
+ if attempt(hgcmd + check_cmd, hgenv):
+ return hgcommand(hgcmd, hgenv)
+ # Fall back to trying the local hg installation (whatever we can)
+ hgenv = localhgenv(pure_python=False)
+ hgcmd = [sys.executable, repo_hg]
if attempt(hgcmd + check_cmd, hgenv):
return hgcommand(hgcmd, hgenv)
@@ -319,17 +328,18 @@
return None
-def localhgenv():
+def localhgenv(pure_python=True):
"""Get an environment dictionary to use for invoking or importing
mercurial from the local repository."""
# Execute hg out of this directory with a custom environment which takes
# care to not use any hgrc files and do no localization.
env = {
- 'HGMODULEPOLICY': 'py',
'HGRCPATH': '',
'LANGUAGE': 'C',
'PATH': '',
} # make pypi modules that use os.environ['PATH'] happy
+ if pure_python:
+ env['HGMODULEPOLICY'] = 'py'
if 'LD_LIBRARY_PATH' in os.environ:
env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
if 'SystemRoot' in os.environ:
@@ -1821,5 +1831,5 @@
'welcome': 'contrib/packaging/macosx/Welcome.html',
},
},
- **extra
+ **extra,
)
--- a/tests/test-log.t Thu Dec 07 02:07:16 2023 +0100
+++ b/tests/test-log.t Fri Dec 15 11:10:24 2023 +0100
@@ -2001,6 +2001,8 @@
@@ -0,0 +1,1 @@
+b
+ $ hg log -r 3 -T'{diffstat}\n'
+ 2: +2/-1
Test that diff.merge is respected (file b was added on one side and
and therefore merged cleanly)
@@ -2021,6 +2023,9 @@
-b
+c
+ $ hg log -r 3 -T'{diffstat}\n' --config diff.merge=yes
+ 1: +1/-1
+
$ cd ..
'hg log -r rev fn' when last(filelog(fn)) != rev
--- a/tests/test-status-rev.t Thu Dec 07 02:07:16 2023 +0100
+++ b/tests/test-status-rev.t Fri Dec 15 11:10:24 2023 +0100
@@ -88,6 +88,33 @@
Status between first and second commit. Should ignore dirstate status.
+ $ hg status -marc --rev 0 --rev 1 --config rhg.on-unsupported=abort
+ M content1_content2_content1-tracked
+ M content1_content2_content1-untracked
+ M content1_content2_content2-tracked
+ M content1_content2_content2-untracked
+ M content1_content2_content3-tracked
+ M content1_content2_content3-untracked
+ M content1_content2_missing-tracked
+ M content1_content2_missing-untracked
+ A missing_content2_content2-tracked
+ A missing_content2_content2-untracked
+ A missing_content2_content3-tracked
+ A missing_content2_content3-untracked
+ A missing_content2_missing-tracked
+ A missing_content2_missing-untracked
+ R content1_missing_content1-tracked
+ R content1_missing_content1-untracked
+ R content1_missing_content3-tracked
+ R content1_missing_content3-untracked
+ R content1_missing_missing-tracked
+ R content1_missing_missing-untracked
+ C content1_content1_content1-tracked
+ C content1_content1_content1-untracked
+ C content1_content1_content3-tracked
+ C content1_content1_content3-untracked
+ C content1_content1_missing-tracked
+ C content1_content1_missing-untracked
$ hg status -A --rev 0:1 'glob:content1_content2_*'
M content1_content2_content1-tracked
M content1_content2_content1-untracked
--- a/tests/test-symlinks.t Thu Dec 07 02:07:16 2023 +0100
+++ b/tests/test-symlinks.t Fri Dec 15 11:10:24 2023 +0100
@@ -188,6 +188,35 @@
$ cd ..
+== symlinks and add with --include ==
+
+directory moved and symlinked
+
+ $ hg init add-include
+ $ cd add-include
+ $ mkdir foo
+ $ touch foo/a
+ $ hg ci -Ama
+ adding foo/a
+ $ hg mv foo bar
+ moving foo/a to bar/a
+ $ ln -s bar foo
+ $ hg status
+ A bar/a
+ R foo/a
+ ? foo
+
+can add with --include
+
+ $ hg add -I foo
+ adding foo
+ $ hg status
+ A bar/a
+ A foo
+ R foo/a
+
+ $ cd ..
+
== root of repository is symlinked ==
$ hg init root
--- a/tests/test-template-map.t Thu Dec 07 02:07:16 2023 +0100
+++ b/tests/test-template-map.t Fri Dec 15 11:10:24 2023 +0100
@@ -766,7 +766,26 @@
],
'desc': 'third',
'diff': 'diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n',
- 'diffstat': ' fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n',
+ 'diffstat': [
+ {
+ 'additions': 1,
+ 'binary': False,
+ 'name': 'fourth',
+ 'removals': 0
+ },
+ {
+ 'additions': 0,
+ 'binary': False,
+ 'name': 'second',
+ 'removals': 1
+ },
+ {
+ 'additions': 1,
+ 'binary': False,
+ 'name': 'third',
+ 'removals': 0
+ }
+ ],
'files': [
'fourth',
'second',
@@ -820,7 +839,7 @@
"date": [1577872860, 0],
"desc": "third",
"diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n",
- "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
+ "diffstat": [{"additions": 1, "binary": false, "name": "fourth", "removals": 0}, {"additions": 0, "binary": false, "name": "second", "removals": 1}, {"additions": 1, "binary": false, "name": "third", "removals": 0}],
"files": ["fourth", "second", "third"],
"node": "95c24699272ef57d062b8bccc32c878bf841784a",
"parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
@@ -1180,7 +1199,7 @@
$ hg log -r. -T'json(diffstat)'
[
- {"diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n"}
+ {"diffstat": [{"additions": 1, "binary": false, "name": "fourth", "removals": 0}, {"additions": 0, "binary": false, "name": "second", "removals": 1}, {"additions": 1, "binary": false, "name": "third", "removals": 0}]}
]
$ hg log -r. -T'json(manifest)'