Fossil Forum

Feature suggestion: file's history between two lines
Login

Feature suggestion: file's history between two lines

Feature suggestion: file's history between two lines

(1) By kasper on 2022-04-21 19:43:09 [link] [source]

Git and Mercurial have the log -L command, which shows history of a specified region of a file. Don't know about other editors, but in Emacs there is a vc-region-history command that leverages this for browsing and operating on such narrowed down diffs. This is useful: you can select a region of text in the editor and view it's history. In my opinion it would be nice to have this in Fossil. I don't know where would it fit... maybe in the timeline command?

Bonus question: is it possible to include a diff for each entry in the timeline command?

(2.1) By Stephan Beal (stephan) on 2022-04-22 10:43:46 edited from 2.0 in reply to 1 [source]

... which shows history of a specified region of a file.

That's what the annotate/blame feature does, but lists the whole file. It "might" (not certain) be feasible to restrict annotate to a specific range of lines, noting that that's not really meaningful because lines shift around all the time, and line 17 in one version might be line 2321 in another, and those won't line (haha) up in any meaningful sense in such a feature.

is it possible to include a diff for each entry in the timeline command?

Not currently, no, and i cannot imagine how that might be even remotely useful unless the output is limited to a single entry (in which case it's identical to the output of the /info/CHECKIN page, making it redundant as a feature). Diffs can be arbitrarily large, e.g. src:/info/b3c5a48926cf15af, and having that much output between each timeline entry would make it effectively unreadable (as a timeline).

The /info pages, where diffs are shown, also show the previous/next checkins, making it easy to browse diffs up and down the history. Collecting separate diffs for multiple versions in a single page would, IMO, not be terribly useful (in the usability sense, though the data themselves may occasionally be useful).

That said... one of the primary targets for libfossil is special-case applications like a timeline/diff combo. It has all of the features needed for implementing such a thing, the one potential exception being that the HTML-format diff has not been ported over (only because of lack of need - the library doesn't have any HTML-generating client apps yet).

(3) By kasper on 2022-04-23 06:59:44 in reply to 2.1 [link] [source]

lines shift around all the time

Yes! But the log -L command of Git and HG takes this into account, so the region moves around according to lines added/deleted in each consecutive diff.

how that might be even remotely useful unless the output is limited to a single entry

Yeah, it seems to only make sense when combined with the log -L thing. Otherwise the diffs can be just too big and it's redundant.

Collecting separate diffs for multiple versions in a single page would, IMO, not be terribly useful

It can be useful, for example, you can browse the history of a chosen function. Without this, you would have to open multiple windows next to each other, each containing different revisions, and navigate to the right line in them.

With a small region, say 3 lines, it's common to squeeze in even 3 different revisions. Personally, I can think faster this way, because I can compare things directly next to each other. It's also cheaper to set up and tear down, with a single shortcut, instead of having to create a lot of windows/browser tabs.

So basically /finfo is great for browsing single file history, but to me it's too coarse-grained for browsing the history of individual functions, and this is where this feature comes in.

libfossil

Hmm yes, maybe creating something like f-linediff would be appropriate here.

But, no idea what the algorithm for such a thing would be. Maybe going through the diff line by line, looking for +/- as the first character and moving the initial range accordingly. And I can't just copy or even look at HG code, because I couldn't share it with Fossil due to the GPL :-)

(4) By Stephan Beal (stephan) on 2022-04-23 10:58:00 in reply to 3 [link] [source]

It can be useful, for example, you can browse the history of a chosen function. Without this, you would have to open multiple windows next to each other, each containing different revisions, and navigate to the right line in them.

That's easier to use than having a single window with 10000 lines of output across multiple revisions to scroll back and forth between. At least multiple windows can be placed side by side. The output of a diff can be arbitrarily large and we currently have no way of saying "just show the diff for lines 10-20."

We won't ever have a way of saying "show me the diff of function X" because fossil does not know what a function is. It strives to be mostly content-agnostic, with some minor exceptions involving indentation-only changes and the like, and feature suggestions which are language-specific have, to date, all been rejected. Function blocks, by definition, are language-specific.

So basically /finfo is great for browsing single file history, but to me it's too coarse-grained for browsing the history of individual functions, and this is where this feature comes in.

But, no idea what the algorithm for such a thing would be.

That's the real question. Though the underlying diff algo is way above my proverbial pay grade, i feel somewhat comfortable saying that it's not currently suited to do the level of tracking you're interested in, in particular because fossil's internals cannot distinguish a C function from a paragraph of HTML-format text.

(5) By Warren Young (wyoung) on 2022-04-23 11:25:00 in reply to 4 [link] [source]

fossil does not know what a function is.

But it could be taught by teaching it how to read a ctags or cscope file. That gets you a line range, which feeds back into this thread's topic.

(6) By Stephan Beal (stephan) on 2022-04-23 13:05:32 in reply to 5 [link] [source]

But it could be taught by teaching it how to read a ctags or cscope file. That gets you a line range, which feeds back into this thread's topic.

For that to be useful the ctags/cscope file would have to be checked in with the source code, fossil would have to know how to identify such files, and only versions checked in with correct/up-to-date tag files could be diffed that way. Any given developer working in an environment without ctags/etc could check in changes which don't include an updated tag file, invalidating any diffs done with the most-recently-checked-in tag file. There's a whole shelf full of cans of worms there.

"Patches thoughtfully" and all that ;).

(7) By Warren Young (wyoung) on 2022-04-23 13:23:17 in reply to 6 [link] [source]

would have to be checked in

Why? Surely you're running this from an active checkout, where recreation of those files can be part of the normal build process.

It's true that without a checked-in tags file, you can't do this from Fossil UI, but is that a deal-killer?

fossil would have to know how to identify such files,

They have well-known names because they're used by other tools like programmer's text editors. (e.g. Vim's Ctrl-] feature.)

correct/up-to-date tag files

If one is using ctags or cscope, one already has that incentive.

I'm getting the impression from these objections that you don't use any sort of tags file. If so, you're missing out. Even when you know the codebase intimately, being able to hit Ctrl-] to jump from the call site to the function definition is indispensable. cscope adds more, such as "show me all the places that call this function."

Try "make tags" in Fossil's own source tree.

"Patches thoughtfully" and all that ;).

I don't especially want the feature. I'm just pushing back against the "it can't be done" mindset. It can. Someone just has to want it bad enough to put in the work.

(8) By Stephan Beal (stephan) on 2022-04-23 13:55:54 in reply to 7 [link] [source]

It's true that without a checked-in tags file, you can't do this from Fossil UI, but is that a deal-killer?

You'd be comparing multiple versions, so if the tags files aren't versioned, or (ugh!) recreated every time a diff is run, they'd be useless.

I'm getting the impression from these objections that you don't use any sort of tags file.

i use etags but would never in my life consider checking them in. They're environment-/toolchain-specific and of no use to me in versioning - they can be/are recreated on demand when a version is checked out. Having two versions of a tags file at a single time would only cause grief unless they were encapsulated for file-chunk-level diffing purposes or the like.

I don't especially want the feature.

Nor i!

I'm just pushing back against the "it can't be done" mindset.

i'm not saying "can't be done," but something more akin to "cans of worms abound to the point where implementing properly/completely would require unappealing amounts of code and patience." But...

Someone just has to want it bad enough to put in the work.

They're welcome to, with no pushback from me :)! Even so, it's a language(s)-specific feature which would be unlikely (if history's a good basis for comparison) to be accepted for inclusion into fossil but would still be well within the wheelhouse of libfossil for getting at and diffing the SCM-side data. If someone wants to take that approach i'd be happy to support them with that side of the equation but am not volunteering to crawl down the neighboring rabbit holes.

(9) By Warren Young (wyoung) on 2022-04-23 15:17:27 in reply to 8 [link] [source]

if the tags files aren't versioned, or (ugh!) recreated every time a diff is run, they'd be useless.

You should be able to start with the unversioned checkout data and update it from the diff data without needing historical tags files.

Cases:

  1. Diff says 10 lines were removed before the first line of the range being traced? Subtract 10 from the start and end values of the line range.

  2. Diff says lines were added after the line range? Doesn't matter; keep using the same line range on this version.

  3. Diff says lines were added/removed within the range? Adjust accordingly.

(12) By kasper on 2022-05-24 17:06:26 in reply to 9 [link] [source]

I stepped into the rabbit hole and converged to a similiar algorithm, which seems to work. But it only does for check-ins that are part of the primary parent lineage of the most recent one. This is because the range differences are calculated between direct parent and child, and with branches you suddenly step out of that nice linear history.

Of course you could just print the changes introduced by merges to trunk, but that skips the more fine grained information, which is not satisfying. In fact, Git doesn't even print merge commits with the equivalent command, but prints the particular ones correctly.

To have it work across branches I used this approach:

Pre-calculate regions for primary parents of the most recent check-in (down to the oldest check-in)

For each check-in, starting again from the most recent:
  If the region is not already known:
     Find the first check-in with a known region by walking down primary parents
     Fill the regions back up (the same path)
  Print diff of check-in region to its primary parent region

I wrote the code for this: (link to diff) - plus the corresponding code for vc-fossil (link to diff). I'm using it now, which makes me very happy because that was the one big thing that I missed in Fossil. Maybe someone will make use of it as well :-).

PS: I also learned in the process that IntelliJ supports this feature for Git, Mercurial and even Subversion. So there might even be a generic algorithm, where functions like "get first parent" and "get file content" are parameters - since I don't think Subversion has this natively.

(10) By Martin Gagnon (mgagnon) on 2022-04-24 13:05:12 in reply to 1 [link] [source]

Bonus question: is it possible to include a diff for each entry in the timeline command?

If by "diff for each entry" you mean "show what file change":

fossil timeline -v will list the modified files.

On the web ui /timeline, you can click on Advanced on top and enable the Files checkbox.

(11) By kasper on 2022-04-24 21:57:55 in reply to 10 [link] [source]

Thanks, I didn't know about the -v flag. Though I was after output similiar to git log --patch.