Most uses of templates requires a trailing newline to get vertical output.
Graphlog with a template without trailing newline did however not just create
horisontal output like other commands would but truncated the output at the
last \n. Template values without any \n were ignored completely.
Graphlog will now only eat one trailing newline before it lets the flow of the
graph add the necessary vertical space.
Many tests didn't change back from subdirectories at the end of the tests ...
and they don't have to. The missing 'cd ..' could always be added when another
test case is added to the test file.
This change do that tests (99.5%) consistently end up in $TESTDIR where they
started, thus making it simpler to extend them or move them around.
The situation is complicated because filelog() revset uses a match object in
relpath mode while follow() revset interprets the filename as a manifest entry.
This solves a similar problem than the previous --follow/--rev patch. This time
we need changelog.ancestors()/descendants() filtering on first parent.
Duplicating the code looked better than introducing keyword arguments. Besides,
the ancestors() version was already implemented in follow() revset.
The previous behaviour of --follow was really a subset of what is really
happening in log command:
- If --rev is not passed, default to '.:0'
- Resolve --rev into a revision list "revs"
- Set the starting revision to revs[0]
- If revs[1] > revs[0] keep descendants(revs[0]) in revs, otherwise keep
ancestors.
When --follow and --rev are passed, --follow actual behaviour depends on the
input revision sequence defined by --rev. If --rev is not passed, the default
revision sequence depends on the presence of --follow. It means the revision
sequence generation is part of log logic and must be wrapped. The issue
described above is fixed in following patches.
--rev options cannot be merged into a single revset because we do not know if
they are valid revset or old-style revision specifications, like 'foo-bar'
tags. Instead, a base revision set is generated with scmutil.revrange() then
filtered with the revset built from log options. It also fixes incorrect or
hostile expressions passed in --rev.
When passing --patch/--stat, file filters have to be applied to generate the
correct diff or stat output:
- Without --follow, the static match object can be reused
- With --follow, the files displayed at revision X are the ancestors of
selected files at parent revision. To do this, we reproduce the ancestry
calculations done by --follow, lazily.
test-glog.t changes show that --patch output is not satisfying because renames
are reported as copies. This can probably be fixed by:
- Without --follow: compute files to display, look for renames sources and
extend the matcher to include them.
- With --follow: detect .path() transitions between parent/child filectx,
filter them using the linked changectx .removed() field and extend fcache
with them.
"hg log --removed FILE" does not return changesets where FILE was removed, but
ones where FILE was changed and possibly removed. The flag is really here to
disable walkchangerevs() fast path, which cannot see file removals by scanning
filelogs.
Matt suggested this on IRC, I do not think the choice is obvious, but this one
makes things simpler because while filesets are turned into a list of files
into the match objects, it would more be difficult to tell invalid files passed
in pats from those expanded from filesets.
This subtlety is not documented yet but:
- pats/--include/--exclude filesets are evaluated against the working directory
- --rev filesets are reevaluated against every revisions
log --graph --follow-first FILE cannot be compared with the regular version
because it never worked: --follow-first is not taken in account in
walkchangerevs() fast path and is explicitely bypassed in FILE case in
walkchangerevs() nested iterate() function.
On platforms not supporting shell expansion, scmutil.match() performs glob
expansion on 'pats' arguments. But _matchfiles() revset calls match.match()
directly, bypassing this behaviour. To avoid duplicating scmutil.match(), a
secondary scmutil.matchandpats() is introduced returning both the match object
and the expanded inputs. Note the expanded pats are also needed in the fast
path, and will be used by --follow code path.
The filtering logic of match objects cannot be reproduced with the existing
revsets as it operates at changeset files level. A changeset touching "a" and
"b" is matched by "-I a -X b" but not by "file(a) and not file(b)".
To solve this, a new internal "_matchfiles(...)" revset is introduced. It works
like "file(x)" but accepts more than one argument and its arguments are
prefixed with "p:", "i:" and "x:" to be used as patterns, include patterns or
exclude patterns respectively.
The _matchfiles revset is kept private for now:
- There are probably smarter ways to pass the arguments in a user-friendly way
- A "rev:" argument is likely appear at some point to emulate log command
behaviour with regard to filesets: they are evaluated for the parent revision
and applied everywhere instead of being reevaluated for each revision.
This will let use override the "join" value (and/or) depending on the option
considered. The option revset arity is now deduced from the revset and the
option value type, to simplify opt2revset definition.
Using "hg log -G --print-revset" prints the revset generated by graphlog and
exits. This helps debugging and writing shorter tests.
It has been suggested to handle these tests with doctests. I think the
extension approach is better because:
- It tests the actual parameter set passed to graphlog.revset(), not what we
expect it to be. 'branch' and 'only-branch' are currently distinct options
but nothing prevents fancyopts to grow a notion of option aliasing one day,
where both options would be merged before reaching the command.
- It can be used as debug output interleaved with real log calls.
v2:
- Use a test extension instead of a global deprecated new option
The grapher cannot really handled revisions if they are not emitted in
topological order. The previous 'reverse()' revset was not enough to achieve
that and was replaced by an explicit sort call for simplicity. The --limit
option is now also handled as usual with cmdutil.loglimit() instead of a
'limit' revset.
The grandparent() function was returning only the closest predecessor of a
missing parent while it must return all of them to display a correct ancestry
graph.
follow() revset really means '::.' while we want something based on the passed
argument. Also, ancestors() revset does not include the parent revisions.
Thanks for the idea and most of the implementation to Klaus Koch
Backs revisions() and filerevs() with DAG walker which can iterate through
arbitrary list of revisions instead of strict one by one iteration from start to
stop. When a gap occurs in a revisions (i.e. in file log), the next topological
parent within the revset is searched and the connection to it is printed in the
ascii graph.
File graph can draw sometimes more connections than previous version, because
graph is produced according to the revset, not according to a file's filelog.
In case the graph contains several branches where the left parent is null, the
graphs for each are printed sequentially, not in parallel as it was a case
earlier (see for example the graph for README in hg-dev).
While this situation should never under normal use, some real
life repos sometimes contain such changesets (older hg versions,
broken rebases, etc...)
hgweb was displaying an "Internal error" in this case, and graphlog
displayed a redundant branch all the way to null: it does not cost us
much to just ignore this extra parent when constructing the DAG.
When issuing `hg pull -r REV` in a repo with no common ancestor with the
remote repo, the message 'requesting all changes' is printed, even though only
the changese that are ancestors of REV are actually requested. This can be
confusing for users (see
http://www.selenic.com/pipermail/mercurial/2010-October/035508.html).
This silences the message if (and only if) the '-r' option was passed.
The glog command didn't emit header and footer from the style, as demonstrated
by "hg glog --style xml". Asciiart combined with xml markup hardly makes sense,
but header and footer might however be useful for adding for example html pre
tags around the graph.