Submitter | Boris Feld |
---|---|
Date | July 6, 2017, 9:50 p.m. |
Message ID | <e18d8e61b7260e246a82.1499377817@FB> |
Download | mbox | patch |
Permalink | /patch/22052/ |
State | Changes Requested, archived |
Headers | show |
Comments
Boris Feld <boris.feld@octobus.net> writes: > # HG changeset patch > # User Boris Feld <boris.feld@octobus.net> > # Date 1499085172 -7200 > # Mon Jul 03 14:32:52 2017 +0200 > # Node ID e18d8e61b7260e246a82727c8cde01f936692cff > # Parent 098585d4fbc88dc54513e12fa306d0e52ea2b323 > # EXP-Topic obsfatetemplate > template: use template-engine for obsfate > > Try to replace the obsfateprinter part by "sub-templates". > > The code is hacky, I've tried to use at maximum the template engine but the > raw data-structure doesn't seems well supported: > > [{'markers': [{}, {}, ...], 'successors': [A, ...], 'verb': '', ...}, ...] > > I've put this changeset at the end so the beginning of the serie might be > accepted without it. Even without this changeset, we already have good tests > and the right structure for the computation of obsfate data. I like the general sentiment of this series, so thanks for that! But by the end of reading the series I found myself want to customize (e.g. not include some of the info; colorize differently, etc.) the string. I think an easy approach for now (unless I'm missing something) would be to make {obsfate} a meta-like template that is just an alias for finer grain templates: {obsfate_user} {obsfate_succesors} (or whatever name you prefer). I have the same critique for 'hg show' as well (*cough* Greg *cough*).
On Thu, 2017-07-06 at 18:05 -0700, Sean Farley wrote: > Boris Feld <boris.feld@octobus.net> writes: > > > # HG changeset patch > > # User Boris Feld <boris.feld@octobus.net> > > # Date 1499085172 -7200 > > # Mon Jul 03 14:32:52 2017 +0200 > > # Node ID e18d8e61b7260e246a82727c8cde01f936692cff > > # Parent 098585d4fbc88dc54513e12fa306d0e52ea2b323 > > # EXP-Topic obsfatetemplate > > template: use template-engine for obsfate > > > > Try to replace the obsfateprinter part by "sub-templates". > > > > The code is hacky, I've tried to use at maximum the template engine > > but the > > raw data-structure doesn't seems well supported: > > > > [{'markers': [{}, {}, ...], 'successors': [A, ...], 'verb': '', > > ...}, ...] > > > > I've put this changeset at the end so the beginning of the serie > > might be > > accepted without it. Even without this changeset, we already have > > good tests > > and the right structure for the computation of obsfate data. > > I like the general sentiment of this series, so thanks for that! But > by > the end of reading the series I found myself want to customize (e.g. > not > include some of the info; colorize differently, etc.) the string. > > I think an easy approach for now (unless I'm missing something) would > be > to make {obsfate} a meta-like template that is just an alias for > finer > grain templates: {obsfate_user} {obsfate_succesors} (or whatever name > you prefer). Interesting, I didn't think about your use-cases much. The main twist in obsfate is that the value is a list. The list could contains 1 element in case of a simple evolution or several elements in case of a divergence. We could have a top-level template {obsfate_users} being a list of list, but it would be hard to combine with another top-level template {obsfate_verb} for example. Also, I just found out that it's possible to access the data with the template engine. For example if you only want the verb and the users: hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} by {get(obsfate, "users")}\n"}' Which would returns "obsolete: rewritten by test" in simple cases. Unfortunately it fails with multiple users "obsolete: rewritten by test1test2" I tried changing the template into: hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} by {join(get(obsfate, "users"), ', ')}\n"}' Unfortunately I only get a blank output, maybe a template engine experts could help me identify what am I doing wrong. Apart from this problem, do you think is it customizable enough for your needs? We can even access the markers with: $ hg log -T '{obsfate % "obsolete: {ge t(obsfate, "markers")|json}\n"}' obsolete: [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["user", "test"]], [0.0, 0], null]] obsolete: [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["user", "test"]], [0.0, 0], null], ["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["user", "test"]], [0.0, 0], null]] However, I'm not sure what would be the use-cases to accessing the markers as obsfate is designed to show a summary of the evolution of a changeset. There could be multiple successorsets in case of divergence and several markers for each successorsets in case the changeset has been rewritten several times. Cheers, > > I have the same critique for 'hg show' as well (*cough* Greg > *cough*). > _______________________________________________ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
On Thu, 06 Jul 2017 23:50:17 +0200, Boris Feld wrote: > # HG changeset patch > # User Boris Feld <boris.feld@octobus.net> > # Date 1499085172 -7200 > # Mon Jul 03 14:32:52 2017 +0200 > # Node ID e18d8e61b7260e246a82727c8cde01f936692cff > # Parent 098585d4fbc88dc54513e12fa306d0e52ea2b323 > # EXP-Topic obsfatetemplate > template: use template-engine for obsfate > > Try to replace the obsfateprinter part by "sub-templates". > > The code is hacky, I've tried to use at maximum the template engine but the > raw data-structure doesn't seems well supported: > > [{'markers': [{}, {}, ...], 'successors': [A, ...], 'verb': '', ...}, ...] > > I've put this changeset at the end so the beginning of the serie might be > accepted without it. Even without this changeset, we already have good tests > and the right structure for the computation of obsfate data. > > diff -r 098585d4fbc8 -r e18d8e61b726 mercurial/templatekw.py > --- a/mercurial/templatekw.py Mon Jul 03 17:38:56 2017 +0200 > +++ b/mercurial/templatekw.py Mon Jul 03 14:32:52 2017 +0200 > @@ -699,6 +699,32 @@ > > return "; ".join(lines) > > +def obsfatedefaulttempl(ui): > + """ Returns a dict with the default templates for obs fate > + """ > + # Prepare templates > + verbtempl = '{verb}' > + usertempl = '{if(users, " by {join(users, ", ")}")}' > + succtempl = '{if(successors, " as ")}{successors}' # Bypass if limitation > + datetempleq = ' (at {min_date|isodate})' > + datetemplnoteq = ' (between {min_date|isodate} and {max_date|isodate})' > + > + datetempl = '{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' > + datetempl = datetempl % (datetempleq, datetemplnoteq) > + > + optionalusertempl = usertempl > + username = _getusername(ui) > + if username is not None: > + optionalusertempl = ('{ifeq(join(users, "\0"), "%s", "", "%s")}' > + % (username, usertempl)) > + > + # Assemble them > + return { > + 'obsfate_quiet': verbtempl + succtempl, > + 'obsfate': verbtempl + optionalusertempl + succtempl, > + 'obsfate_verbose': verbtempl + usertempl + succtempl + datetempl, > + } This makes me feel you're doing things in wrong layer. In principle, template keywords provide primitive data, which are pretty-printed by using user/stock templates (e.g. templates/map-cmdline.default.)
On Fri, 2017-07-07 at 21:09 +0900, Yuya Nishihara wrote: > On Thu, 06 Jul 2017 23:50:17 +0200, Boris Feld wrote: > > # HG changeset patch > > # User Boris Feld <boris.feld@octobus.net> > > # Date 1499085172 -7200 > > # Mon Jul 03 14:32:52 2017 +0200 > > # Node ID e18d8e61b7260e246a82727c8cde01f936692cff > > # Parent 098585d4fbc88dc54513e12fa306d0e52ea2b323 > > # EXP-Topic obsfatetemplate > > template: use template-engine for obsfate > > > > Try to replace the obsfateprinter part by "sub-templates". > > > > The code is hacky, I've tried to use at maximum the template engine > > but the > > raw data-structure doesn't seems well supported: > > > > [{'markers': [{}, {}, ...], 'successors': [A, ...], 'verb': '', > > ...}, ...] > > > > I've put this changeset at the end so the beginning of the serie > > might be > > accepted without it. Even without this changeset, we already have > > good tests > > and the right structure for the computation of obsfate data. > > > > diff -r 098585d4fbc8 -r e18d8e61b726 mercurial/templatekw.py > > --- a/mercurial/templatekw.py Mon Jul 03 17:38:56 2017 +0200 > > +++ b/mercurial/templatekw.py Mon Jul 03 14:32:52 2017 +0200 > > @@ -699,6 +699,32 @@ > > > > return "; ".join(lines) > > > > +def obsfatedefaulttempl(ui): > > + """ Returns a dict with the default templates for obs fate > > + """ > > + # Prepare templates > > + verbtempl = '{verb}' > > + usertempl = '{if(users, " by {join(users, ", ")}")}' > > + succtempl = '{if(successors, " as ")}{successors}' # Bypass if > > limitation > > + datetempleq = ' (at {min_date|isodate})' > > + datetemplnoteq = ' (between {min_date|isodate} and > > {max_date|isodate})' > > + > > + datetempl = '{if(max_date, "{ifeq(min_date, max_date, "%s", > > "%s")}")}' > > + datetempl = datetempl % (datetempleq, datetemplnoteq) > > + > > + optionalusertempl = usertempl > > + username = _getusername(ui) > > + if username is not None: > > + optionalusertempl = ('{ifeq(join(users, "\0"), "%s", "", > > "%s")}' > > + % (username, usertempl)) > > + > > + # Assemble them > > + return { > > + 'obsfate_quiet': verbtempl + succtempl, > > + 'obsfate': verbtempl + optionalusertempl + succtempl, > > + 'obsfate_verbose': verbtempl + usertempl + succtempl + > > datetempl, > > + } > > This makes me feel you're doing things in wrong layer. In principle, > template > keywords provide primitive data, which are pretty-printed by using > user/stock > templates (e.g. templates/map-cmdline.default.) Definitely, I should have made it more explicit that this one patch is in RFC state. Obsfate has a difficult task: summarize the obsolescence history (potentially spanning multiple obs-markers), aggregating the different values and all of this in multiple dimensions when there's a divergence. This template seems quite complex, it felt complex during implementation using templates. I tried finding an existing template that was close to this complexity, successorssets was close but obsfate adds one more layer of nesting, so I didn't find a good example to mimic. I'm pretty sure that implementing obsfate cleanly with the template engine can be done, but after spending several days, I'm afraid I won't be able to do it on my own. For example, I wasn't able to successfully format the users list using templates, I tried doing this: hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} by {join(get(obsfate, "users"), ', ')}\n"}' Lately, I was thinking about sending a V2 that, instead of returning the formatted string, would returns an _hybrid object: - return _obsfateprinter(values, repo, repo.ui) + gen = _obsfateprinter(values, repo, repo.ui) + return _hybrid(gen, values, None, None) This way people would be able to start customizing it (with template function "get") and we would be able to improve the implementation with potential syntactic sugar addition in the template engine. What do you think? Could you provide me with some direction to move forward? Cheers,
On Tue, 11 Jul 2017 11:16:03 +0200, Boris Feld wrote: > Obsfate has a difficult task: summarize the obsolescence history > (potentially spanning multiple obs-markers), aggregating the different > values and all of this in multiple dimensions when there's a > divergence. > > This template seems quite complex, it felt complex during > implementation using templates. I tried finding an existing template > that was close to this complexity, successorssets was close but obsfate > adds one more layer of nesting, so I didn't find a good example to > mimic. > > I'm pretty sure that implementing obsfate cleanly with the template > engine can be done, but after spending several days, I'm afraid I won't > be able to do it on my own. For example, I wasn't able to successfully > format the users list using templates, I tried doing this: > > hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} by > {join(get(obsfate, "users"), ', ')}\n"}' > > Lately, I was thinking about sending a V2 that, instead of returning > the formatted string, would returns an _hybrid object: > > - return _obsfateprinter(values, repo, repo.ui) > + gen = _obsfateprinter(values, repo, repo.ui) > + return _hybrid(gen, values, None, None) > > This way people would be able to start customizing it (with template > function "get") and we would be able to improve the implementation with > potential syntactic sugar addition in the template engine. Well, I don't have expertise in the obsolete thingy, though I'm (unfortunately) a template expert. Guessing from the PATCH 4, which has the following functions, obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] maybe we'll want a template function which converts 'succs' to 'markers' ? Let's call it 'relatedmarkers' here. relatedmarkers: succs => [marker, ...] (where marker is a _hybrid dict) Then, a part of {obsfate} could probably be written as: {successorsets % "{relatedmarkers(successorset) % "{get(marker, "verb")} ..."}"} I think that's similar to what Jun suggested.
Excerpts from Yuya Nishihara's message of 2017-07-12 00:06:13 +0900: > Well, I don't have expertise in the obsolete thingy, though I'm (unfortunately) > a template expert. > > Guessing from the PATCH 4, which has the following functions, > > obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] > > maybe we'll want a template function which converts 'succs' to 'markers' ? > Let's call it 'relatedmarkers' here. > > relatedmarkers: succs => [marker, ...] (where marker is a _hybrid dict) > > Then, a part of {obsfate} could probably be written as: > > {successorsets % "{relatedmarkers(successorset) > % "{get(marker, "verb")} ..."}"} Is "get" necessary? It seems "%" is changing the "binding" here, so I wonder if it's possible to do something like: "{successorgroups % "{markers % "{user} did {operation} on {predecessors} and got {successors}"}"}" > I think that's similar to what Jun suggested.
On Tue, 11 Jul 2017 12:44:52 -0700, Jun Wu wrote: > Excerpts from Yuya Nishihara's message of 2017-07-12 00:06:13 +0900: > > Well, I don't have expertise in the obsolete thingy, though I'm (unfortunately) > > a template expert. > > > > Guessing from the PATCH 4, which has the following functions, > > > > obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] > > > > maybe we'll want a template function which converts 'succs' to 'markers' ? > > Let's call it 'relatedmarkers' here. > > > > relatedmarkers: succs => [marker, ...] (where marker is a _hybrid dict) > > > > Then, a part of {obsfate} could probably be written as: > > > > {successorsets % "{relatedmarkers(successorset) > > % "{get(marker, "verb")} ..."}"} > > Is "get" necessary? Maybe no. Good catch. > It seems "%" is changing the "binding" here, so I wonder > if it's possible to do something like: > > "{successorgroups % > "{markers % > "{user} did {operation} on {predecessors} and got {successors}"}"}" I'm not sure if we need markers(node) or markers([node, ...]). markers(node) can be a template keyword bound to the current ctx, {successorgroups % "{groups % "{markers % ...}"}"} but markers([node, ...]) can't because there's no concept of the current set of nodes. {successorgroups % "{markers(groups) % ...}"}
On Wed, 2017-07-12 at 00:06 +0900, Yuya Nishihara wrote: > On Tue, 11 Jul 2017 11:16:03 +0200, Boris Feld wrote: > > Obsfate has a difficult task: summarize the obsolescence history > > (potentially spanning multiple obs-markers), aggregating the > > different > > values and all of this in multiple dimensions when there's a > > divergence. > > > > This template seems quite complex, it felt complex during > > implementation using templates. I tried finding an existing > > template > > that was close to this complexity, successorssets was close but > > obsfate > > adds one more layer of nesting, so I didn't find a good example to > > mimic. > > > > I'm pretty sure that implementing obsfate cleanly with the template > > engine can be done, but after spending several days, I'm afraid I > > won't > > be able to do it on my own. For example, I wasn't able to > > successfully > > format the users list using templates, I tried doing this: > > > > hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} by > > {join(get(obsfate, "users"), ', ')}\n"}' > > > > Lately, I was thinking about sending a V2 that, instead of > > returning > > the formatted string, would returns an _hybrid object: > > > > - return _obsfateprinter(values, repo, repo.ui) > > + gen = _obsfateprinter(values, repo, repo.ui) > > + return _hybrid(gen, values, None, None) > > > > This way people would be able to start customizing it (with > > template > > function "get") and we would be able to improve the implementation > > with > > potential syntactic sugar addition in the template engine. > > Well, I don't have expertise in the obsolete thingy, though I'm > (unfortunately) > a template expert. > > Guessing from the PATCH 4, which has the following functions, > > obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] > > maybe we'll want a template function which converts 'succs' to > 'markers' ? > Let's call it 'relatedmarkers' here. > > relatedmarkers: succs => [marker, ...] (where marker is a _hybrid > dict) > > Then, a part of {obsfate} could probably be written as: > > {successorsets % "{relatedmarkers(successorset) > % "{get(marker, "verb")} ..."}"} I hadn't thought about splitting the template into several templates functions, it's a good idea!. One small, but important detail: the verb, users list and dates are computed from the markers list. Something like this might work, what do you think? {successorsets % "{obsfateverb(successorset)} by {obsfateusers(successorset)} as {join(get(successorset, 'successors'), ', ')}"} Would it be possible to keep the current {obsfate} template? It is easy to use for users who are OK with the default obsfate output format (which could be updated of course). Also I've almost successfully reproduce the obsfate output "by hand" with: $ hg log -r 34177 --hidden -v -T "{obsfate}" rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 (at 2017-07-06 23:47 +0200) $ hg log -r 34177 --hidden -T '{obsfate % "{get(obsfate, "verb")} by {join(get(obsfate, "users"), ", ")} as {get(obsfate, "successors")} (at {get(obsfate, "min_date")|isodate})\n"}' rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 (at 2017-07-06 23:47 +0200) > > I think that's similar to what Jun suggested.
On Wed, 12 Jul 2017 15:44:13 +0200, Boris Feld wrote: > On Wed, 2017-07-12 at 00:06 +0900, Yuya Nishihara wrote: > > Guessing from the PATCH 4, which has the following functions, > > > > obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] > > > > maybe we'll want a template function which converts 'succs' to > > 'markers' ? > > Let's call it 'relatedmarkers' here. > > > > relatedmarkers: succs => [marker, ...] (where marker is a _hybrid > > dict) > > > > Then, a part of {obsfate} could probably be written as: > > > > {successorsets % "{relatedmarkers(successorset) > > % "{get(marker, "verb")} ..."}"} > > I hadn't thought about splitting the template into several templates > functions, it's a good idea!. > > One small, but important detail: the verb, users list and dates are > computed from the markers list. Something like this might work, what do > you think? > > {successorsets % "{obsfateverb(successorset)} by > {obsfateusers(successorset)} as {join(get(successorset, 'successors'), > ', ')}"} That will work. Maybe obsfatexxx() could be a single function which summarizes markers. {successorsets % "{obsxxx(markers(successorset))} % "{verb} {user}... or {successorsets % "{obsxxx(successorset)} % "{verb} {user}... > Would it be possible to keep the current {obsfate} template? It is easy > to use for users who are OK with the default obsfate output format > (which could be updated of course). > > Also I've almost successfully reproduce the obsfate output "by hand" > with: > > $ hg log -r 34177 --hidden -v -T "{obsfate}" > rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 (at > 2017-07-06 23:47 +0200) > > $ hg log -r 34177 --hidden -T '{obsfate % "{get(obsfate, "verb")} by > {join(get(obsfate, "users"), ", ")} as {get(obsfate, "successors")} (at > {get(obsfate, "min_date")|isodate})\n"}' > rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 (at > 2017-07-06 23:47 +0200) I don't think a template keyword should generate a long descriptive output. You could instead add it to map-cmdline.default. I know it isn't always useful, but that is another issue.
As previously discussed in the "operation" thread [1]. I think we want to use "operation" metadata instead of introducing flags, "obsfate" and/or "verb" as new concepts. Practically "rebased as X" / "histedited as X" is more friendly to end-user than just "rewritten as X". With "operation" concept available, template could be just raw fields of an obsmarker like user, data, successors, operation etc without needing another layer of indirection (obsfate). I think that's simpler for users to understand. [1]: https://www.mercurial-scm.org/pipermail/mercurial-devel/2017-May/097678.html Excerpts from Boris Feld's message of 2017-07-12 15:44:13 +0200: > On Wed, 2017-07-12 at 00:06 +0900, Yuya Nishihara wrote: > > On Tue, 11 Jul 2017 11:16:03 +0200, Boris Feld wrote: > > > Obsfate has a difficult task: summarize the obsolescence history > > > (potentially spanning multiple obs-markers), aggregating the > > > different > > > values and all of this in multiple dimensions when there's a > > > divergence. > > > > > > This template seems quite complex, it felt complex during > > > implementation using templates. I tried finding an existing > > > template > > > that was close to this complexity, successorssets was close but > > > obsfate > > > adds one more layer of nesting, so I didn't find a good example to > > > mimic. > > > > > > I'm pretty sure that implementing obsfate cleanly with the template > > > engine can be done, but after spending several days, I'm afraid I > > > won't > > > be able to do it on my own. For example, I wasn't able to > > > successfully > > > format the users list using templates, I tried doing this: > > > > > > hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} by > > > {join(get(obsfate, "users"), ', ')}\n"}' > > > > > > Lately, I was thinking about sending a V2 that, instead of > > > returning > > > the formatted string, would returns an _hybrid object: > > > > > > - return _obsfateprinter(values, repo, repo.ui) > > > + gen = _obsfateprinter(values, repo, repo.ui) > > > + return _hybrid(gen, values, None, None) > > > > > > This way people would be able to start customizing it (with > > > template > > > function "get") and we would be able to improve the implementation > > > with > > > potential syntactic sugar addition in the template engine. > > > > Well, I don't have expertise in the obsolete thingy, though I'm > > (unfortunately) > > a template expert. > > > > Guessing from the PATCH 4, which has the following functions, > > > > obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] > > > > maybe we'll want a template function which converts 'succs' to > > 'markers' ? > > Let's call it 'relatedmarkers' here. > > > > relatedmarkers: succs => [marker, ...] (where marker is a _hybrid > > dict) > > > > Then, a part of {obsfate} could probably be written as: > > > > {successorsets % "{relatedmarkers(successorset) > > % "{get(marker, "verb")} ..."}"} > > I hadn't thought about splitting the template into several templates > functions, it's a good idea!. > > One small, but important detail: the verb, users list and dates are > computed from the markers list. Something like this might work, what do > you think? > > {successorsets % "{obsfateverb(successorset)} by > {obsfateusers(successorset)} as {join(get(successorset, 'successors'), > ', ')}"} > > Would it be possible to keep the current {obsfate} template? It is easy > to use for users who are OK with the default obsfate output format > (which could be updated of course). > > Also I've almost successfully reproduce the obsfate output "by hand" > with: > > $ hg log -r 34177 --hidden -v -T "{obsfate}" > rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 (at > 2017-07-06 23:47 +0200) > > $ hg log -r 34177 --hidden -T '{obsfate % "{get(obsfate, "verb")} by > {join(get(obsfate, "users"), ", ")} as {get(obsfate, "successors")} (at > {get(obsfate, "min_date")|isodate})\n"}' > rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 (at > 2017-07-06 23:47 +0200) > > > > I think that's similar to what Jun suggested.
I think there was a misunderstanding about obsfate that I will try to clarify, this email is a bit long, sorry. TL;DR: - hg log is a changeset-centric command - obsfate template summarize the obs-history between a changeset and its successors, history that can span several obs-markers - hg obslog from evolve extensions is a obs-marker centric command that could gain support for operation Given this repository: @ changeset: 2:256782ab8caa | tag: tip | parent: 0:ea207398892e | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: B | | o changeset: 1:2a34000d3544 |/ user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: A | o changeset: 0:ea207398892e user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: ROOT If you rebase 1 on top of 2, amend it a first time, then amend it a second time, you will get: @ changeset: 5:022e174ded42 | tag: tip | parent: 2:256782ab8caa | user: test2 | date: Thu Jan 01 00:00:00 1970 +0000 | summary: A" | o changeset: 2:256782ab8caa | parent: 0:ea207398892e | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: B | o changeset: 0:ea207398892e user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: ROOT Now for any reason if changeset 1 should be shown (if it's the current repository parent for example), the output would be: o changeset: 5:022e174ded42 | tag: tip | parent: 2:256782ab8caa | user: test2 | date: Thu Jan 01 00:00:00 1970 +0000 | files: A | description: | A" | | o changeset: 2:256782ab8caa | parent: 0:ea207398892e | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | files: B | description: | B | | | @ changeset: 1:2a34000d3544 |/ user: test | date: Thu Jan 01 00:00:00 1970 +0000 | obsolete: rewritten by test as 022e174ded42 (at 1970-01-01 00:00 +0000) | files: A | description: | A | | o changeset: 0:ea207398892e user: test date: Thu Jan 01 00:00:00 1970 +0000 files: ROOT description: ROOT Obsfate was designed to summarize the obsolescence history for users. As 'hg log' is a changeset centric command, obsfate tries to output informations about visible changesets only. That's why obsfate for 2a34000d3544 is "rewritten as 022e174ded42". We have intermediary changesets but they are not visible here so it would be disturbing to say to the user "rewritten as 1737d8285b4d". One side-effect of this summarization is that obsfate needs to aggregate all the obs-markers from 2a34000d3544 to 022e174ded42, which in this case is "1->3" (from the rebase), "3->4" (from the first amend) and "4->5" (from the second amend). Here the range of obs-markers is short but it could be much more big. That's why obsfate aggregate a couple of fields to show a human-redable summary of the obs-history: - Remove users duplicate. - Compute the range of date, the smaller date of the obs-marker range and the bigger one. - Try to compute a verb based on the markers. All theses computations are quite naive for the moment but the code has been designed to be easily updated, wrapped by a command or extended. This example is also quite simple but a real-life example: obsolete: split by Boris Feld <boris.feld@octobus.net>,Matthieu Laneuville <matthieu.laneuville@octobus.net> as 008f7cd1fcbe, b6e50897b94e (between 2017-06-26 17:17 +0200 and 2017-07-02 15:08 +0200) In this case, obsfate is summarizing 19 obs-markers. Correct me if I'm wrong, but it looks like you're thinking about a more obs-marker centric way of displaying history. We have a command like that in the evolve extension: "obslog": while hg log shows changesets and parents/descendants, hg obslog shows changesets and successors/predecessors. For example, this would be the output of "hg obslog -r 5": @ 022e174ded42 (5) A" | x 15900262089a (4) A" | rewritten(user) by test (Thu Jan 01 00:00:00 1970 +0000) as 022e174ded42 | x 1737d8285b4d (3) A | rewritten(description) by test (Thu Jan 01 00:00:00 1970 +0000) as 15900262089a | x 2a34000d3544 (1) A rewritten(parent) by test (Thu Jan 01 00:00:00 1970 +0000) as 1737d8285b4d With this command, one marker is displayed by line, not an aggregate so it would make sense to add support for operation there. It would be a nice improvement to use the operation metadata to display a more accurate verb in this function. On Wed, 2017-07-12 at 09:28 -0700, Jun Wu wrote: > As previously discussed in the "operation" thread [1]. I think we > want to > use "operation" metadata instead of introducing flags, "obsfate" > and/or > "verb" as new concepts. Practically "rebased as X" / "histedited as > X" is > more friendly to end-user than just "rewritten as X". > > With "operation" concept available, template could be just raw fields > of an > obsmarker like user, data, successors, operation etc without needing > another > layer of indirection (obsfate). I think that's simpler for users to > understand. > > [1]: https://www.mercurial-scm.org/pipermail/mercurial-devel/2017-May > /097678.html > > Excerpts from Boris Feld's message of 2017-07-12 15:44:13 +0200: > > On Wed, 2017-07-12 at 00:06 +0900, Yuya Nishihara wrote: > > > On Tue, 11 Jul 2017 11:16:03 +0200, Boris Feld wrote: > > > > Obsfate has a difficult task: summarize the obsolescence > > > > history > > > > (potentially spanning multiple obs-markers), aggregating the > > > > different > > > > values and all of this in multiple dimensions when there's a > > > > divergence. > > > > > > > > This template seems quite complex, it felt complex during > > > > implementation using templates. I tried finding an existing > > > > template > > > > that was close to this complexity, successorssets was close but > > > > obsfate > > > > adds one more layer of nesting, so I didn't find a good example > > > > to > > > > mimic. > > > > > > > > I'm pretty sure that implementing obsfate cleanly with the > > > > template > > > > engine can be done, but after spending several days, I'm afraid > > > > I > > > > won't > > > > be able to do it on my own. For example, I wasn't able to > > > > successfully > > > > format the users list using templates, I tried doing this: > > > > > > > > hg log -G -T '{obsfate % "obsolete: {get(obsfate, "verb")} > > > > by > > > > {join(get(obsfate, "users"), ', ')}\n"}' > > > > > > > > Lately, I was thinking about sending a V2 that, instead of > > > > returning > > > > the formatted string, would returns an _hybrid object: > > > > > > > > - return _obsfateprinter(values, repo, repo.ui) > > > > + gen = _obsfateprinter(values, repo, repo.ui) > > > > + return _hybrid(gen, values, None, None) > > > > > > > > This way people would be able to start customizing it (with > > > > template > > > > function "get") and we would be able to improve the > > > > implementation > > > > with > > > > potential syntactic sugar addition in the template engine. > > > > > > Well, I don't have expertise in the obsolete thingy, though I'm > > > (unfortunately) > > > a template expert. > > > > > > Guessing from the PATCH 4, which has the following functions, > > > > > > obsfatedata: ctx => [succs, ...] => [(succs, markers), ...] > > > > > > maybe we'll want a template function which converts 'succs' to > > > 'markers' ? > > > Let's call it 'relatedmarkers' here. > > > > > > relatedmarkers: succs => [marker, ...] (where marker is a > > > _hybrid > > > dict) > > > > > > Then, a part of {obsfate} could probably be written as: > > > > > > {successorsets % "{relatedmarkers(successorset) > > > % "{get(marker, "verb")} ..."}"} > > > > I hadn't thought about splitting the template into several > > templates > > functions, it's a good idea!. > > > > One small, but important detail: the verb, users list and dates are > > computed from the markers list. Something like this might work, > > what do > > you think? > > > > {successorsets % "{obsfateverb(successorset)} by > > {obsfateusers(successorset)} as {join(get(successorset, > > 'successors'), > > ', ')}"} > > > > Would it be possible to keep the current {obsfate} template? It is > > easy > > to use for users who are OK with the default obsfate output format > > (which could be updated of course). > > > > Also I've almost successfully reproduce the obsfate output "by > > hand" > > with: > > > > $ hg log -r 34177 --hidden -v -T "{obsfate}" > > rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 > > (at > > 2017-07-06 23:47 +0200) > > > > $ hg log -r 34177 --hidden -T '{obsfate % "{get(obsfate, "verb")} > > by > > {join(get(obsfate, "users"), ", ")} as {get(obsfate, "successors")} > > (at > > {get(obsfate, "min_date")|isodate})\n"}' > > rewritten by Boris Feld <boris.feld@octobus.net> as e18d8e61b726 > > (at > > 2017-07-06 23:47 +0200) > > > > > > I think that's similar to what Jun suggested.
Excerpts from Boris Feld's message of 2017-07-13 12:32:39 +0200: > I think there was a misunderstanding about obsfate that I will try to > clarify, this email is a bit long, sorry. > > TL;DR: > - hg log is a changeset-centric command > - obsfate template summarize the obs-history between a changeset and > its successors, history that can span several obs-markers I agree a function is needed to work with multiple obsmarkers. But I don't think the current obsfate implementation is a good design. I think that function's input is (src, [dest=(not hidden())]), output is multiple obsmarkers. The user could choose what to do with a list of ordered obsmarkers. Like, if they want to display the entire chain of markers, they should be able to do it. If they want to summarize things, we could provide "dedup", "if_unique" helper function to allow them to do so. > - hg obslog from evolve extensions is a obs-marker centric command that > could gain support for operation It still prints changesets, but uses a different DAG. It could be a --dag flag of "hg log" command. We might also want to add other DAG types, like a linear (ignore p2) graph [1]. [1]: https://www.mercurial-scm.org/pipermail/mercurial-devel/2017-July/101148.html > [...]
Excerpts from Jun Wu's message of 2017-07-13 08:42:11 -0700: > Excerpts from Boris Feld's message of 2017-07-13 12:32:39 +0200: > > I think there was a misunderstanding about obsfate that I will try to > > clarify, this email is a bit long, sorry. > > > > TL;DR: > > - hg log is a changeset-centric command > > - obsfate template summarize the obs-history between a changeset and > > its successors, history that can span several obs-markers > > I agree a function is needed to work with multiple obsmarkers. But I don't > think the current obsfate implementation is a good design. > > I think that function's input is (src, [dest=(not hidden())]), output is > multiple obsmarkers. The user could choose what to do with a list of ordered > obsmarkers. Like, if they want to display the entire chain of markers, they > should be able to do it. If they want to summarize things, we could provide > "dedup", "if_unique" helper function to allow them to do so. To minimize the work needed, I think we can: 1. Add something to return a list of markers that can be used in templates directly. (Maybe a list of list if we want to want to deal with the divergence case well) 2. Make obsfate accepts a list of markers (the output of the above function), and returns summarized results that can also be used in templates. So "obsfate" is the "helper function" that summarize things, while the user still have the flexibility of rendering markers in details. > [...]
Patch
diff -r 098585d4fbc8 -r e18d8e61b726 mercurial/templatekw.py --- a/mercurial/templatekw.py Mon Jul 03 17:38:56 2017 +0200 +++ b/mercurial/templatekw.py Mon Jul 03 14:32:52 2017 +0200 @@ -699,6 +699,32 @@ return "; ".join(lines) +def obsfatedefaulttempl(ui): + """ Returns a dict with the default templates for obs fate + """ + # Prepare templates + verbtempl = '{verb}' + usertempl = '{if(users, " by {join(users, ", ")}")}' + succtempl = '{if(successors, " as ")}{successors}' # Bypass if limitation + datetempleq = ' (at {min_date|isodate})' + datetemplnoteq = ' (between {min_date|isodate} and {max_date|isodate})' + + datetempl = '{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' + datetempl = datetempl % (datetempleq, datetemplnoteq) + + optionalusertempl = usertempl + username = _getusername(ui) + if username is not None: + optionalusertempl = ('{ifeq(join(users, "\0"), "%s", "", "%s")}' + % (username, usertempl)) + + # Assemble them + return { + 'obsfate_quiet': verbtempl + succtempl, + 'obsfate': verbtempl + optionalusertempl + succtempl, + 'obsfate_verbose': verbtempl + usertempl + succtempl + datetempl, + } + @templatekeyword("obsfate") def showobsfate(repo, ctx, **args): """Returns a string describing how an obsolete changeset has evolved in a @@ -717,7 +743,38 @@ if values is None: return '' - return _obsfateprinter(values, repo, repo.ui) + # Format each successorset successors list + for raw in values: + # As we can't do something like + # "{join(map(nodeshort, successors), ', '}" in template, manually + # create a correct textual representation + gen = ', '.join(_formatrevnode(repo[n]) for n in raw['successors']) + + makemap = lambda x: {'successor': x} + joinfmt = lambda d: "%s" % d['successor'] + raw['successors'] = _hybrid(gen, raw['successors'], makemap, + joinfmt) + + # And then format them + # Insert default obsfate templates + args['templ'].cache.update(obsfatedefaulttempl(repo.ui)) + + if repo.ui.quiet: + name = "obsfate_quiet" + elif repo.ui.verbose: + name = "obsfate_verbose" + elif repo.ui.debugflag: + name = "obsfate_debug" + else: + name = "obsfate" + + # Format a single value using template + def fmt(d): + nargs = args.copy() + nargs.update(d[name]) + return args['templ'](name, **nargs) + + return _hybrid(None, values, lambda x: {name: x}, fmt) @templatekeyword('p1rev') def showp1rev(repo, ctx, templ, **args): diff -r 098585d4fbc8 -r e18d8e61b726 tests/test-obsmarker-template.t --- a/tests/test-obsmarker-template.t Mon Jul 03 17:38:56 2017 +0200 +++ b/tests/test-obsmarker-template.t Mon Jul 03 14:32:52 2017 +0200 @@ -20,7 +20,8 @@ > {if(successorssets, "\n Successors: {successorssets}")}\ > {if(successorssets, "\n multi-line: {join(successorssets, "\n multi-line: ")}")}\ > {if(successorssets, "\n json: {successorssets|json}")}\n' - > fatelog = log -G -T '{node|short}\n{if(obsfate, " Obsfate: {obsfate}\n")}' + > fatelog = log -G -T '{node|short}\n{if(obsfate, " Obsfate: {join(obsfate, "; ")}\n")}' + > fatelogjson = log -G -T '{node|short} {obsfate|json}\n' > EOF Test templates on amended commit @@ -212,6 +213,19 @@ |/ Obsfate: rewritten by test1 as 3:a468dc9b3633 (at 2009-02-13 23:31 +0000) o ea207398892e + + $ hg fatelogjson --hidden + @ d004c8f274b9 "" + | + | x a468dc9b3633 [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["user", "test2"]], [987654321.0, 0], null]], "max_date": [987654321.0, 0], "min_date": [987654321.0, 0], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], "users": ["test2"], "verb": "rewritten"}] + |/ + | x f137d23bb3e1 [{"markers": [["f137d23bb3e11dc1daeb6264fac9cb2433782e15", [], 0, [["user", "test1"]], [1234567890.0, 0], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]], "max_date": [1234567890.0, 0], "min_date": [1234567890.0, 0], "successors": [], "users": ["test1"], "verb": "pruned"}] + | | + | x 471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["user", "test1"]], [1234567890.0, 0], null]], "max_date": [1234567890.0, 0], "min_date": [1234567890.0, 0], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], "users": ["test1"], "verb": "rewritten"}] + |/ + o ea207398892e "" + + Test templates with splitted commit =================================== @@ -345,6 +359,15 @@ |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a o ea207398892e + $ hg fatelogjson --hidden + @ f257fde29c7a "" + | + o 337fec4d2edc "" + | + | x 471597cad322 [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], "users": ["test"], "verb": "split"}] + |/ + o ea207398892e "" + Test templates with folded commit ================================= @@ -504,6 +527,17 @@ |/ Obsfate: rewritten as 3:eb5a0daa2192 o ea207398892e + + $ hg fatelogjson --hidden + @ eb5a0daa2192 "" + | + | x 0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] + | | + | x 471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] + |/ + o ea207398892e "" + + Test templates with divergence ============================== @@ -690,6 +724,19 @@ |/ Obsfate: rewritten as 2:fdf9bde5129a; rewritten as 3:65b757b745b9 o ea207398892e + + $ hg fatelogjson --hidden + o 019fadeab383 "" + | + | x 65b757b745b9 [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], "users": ["test"], "verb": "rewritten"}] + |/ + | @ fdf9bde5129a "" + |/ + | x 471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], "users": ["test"], "verb": "rewritten"}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"], "users": ["test"], "verb": "rewritten"}] + |/ + o ea207398892e "" + + Test templates with amended + folded commit =========================================== @@ -906,6 +953,19 @@ |/ Obsfate: rewritten as 4:eb5a0daa2192 o ea207398892e + + $ hg fatelogjson --hidden + @ eb5a0daa2192 "" + | + | x b7ea6d14e664 [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] + | | + | | x 0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], "users": ["test"], "verb": "rewritten"}] + | |/ + | x 471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], "users": ["test"], "verb": "rewritten"}] + |/ + o ea207398892e "" + + Test template with pushed and pulled obs markers ================================================ @@ -1467,6 +1527,27 @@ |/ Obsfate: rewritten as 2:0dec01379d3b o ea207398892e + $ hg fatelogjson --hidden + @ 0b997eb7ceee "" + | + | o b18bc8331526 "" + |/ + | o ba2ed02b0c9a "" + | | + | x 4a004186e638 [{"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["b18bc8331526a22cbb1801022bd1555bf291c48b"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["b18bc8331526a22cbb1801022bd1555bf291c48b"], "users": ["test"], "verb": "rewritten"}, {"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["0b997eb7ceeee06200a02f8aab185979092d514e"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["0b997eb7ceeee06200a02f8aab185979092d514e"], "users": ["test"], "verb": "rewritten"}] + |/ + o dd800401bd8c "" + | + | x 9bd10a0775e4 [{"markers": [["9bd10a0775e478708cada5f176ec6de654359ce7", ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f", "dd800401bd8c79d815329277739e433e883f784e"], "users": ["test"], "verb": "split"}] + |/ + o f897c6137566 "" + | + | x 0dec01379d3b [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["f897c6137566320b081514b4c7227ecc3d384b39"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["f897c6137566320b081514b4c7227ecc3d384b39"], "users": ["test"], "verb": "rewritten"}, {"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["471f378eab4c5e25f6c77f785b27c936efb22874"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["471f378eab4c5e25f6c77f785b27c936efb22874"], "users": ["test"], "verb": "rewritten"}] + | | + | x 471f378eab4c [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"], 0, [["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"], "users": ["test"], "verb": "rewritten"}] + |/ + o ea207398892e "" + $ hg up --hidden 4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg rebase -r 7 -d 8 --config extensions.rebase=