Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why is it so hard to see code from 5 minutes ago? (utk.edu)
523 points by azhenley on Feb 19, 2021 | hide | past | favorite | 408 comments


When making changes to a block of code or block of text, I frequently just comment out the code and rewrite it anew. I might copy-paste existing code (just to avoid stupid transcription errors). If its too large to see both what I'm writing, and the old code, at the same time, I just use two windows on the same file.

This is relevant to writing fiction or non-fiction as well.

For example, I didn't like this, so I commented out:

>though his companions jogged just behind him, he would have barely seen them at all had he look. They ran, with their heads down, attentive only to their feet slapping the muddy earth, tracking the path they followed, their breath ragged.

and wrote this, using the commented-out text as a reference:

The fog was now so dense that he could see nothing ahead of him. He ran with his head down, breath ragged, attentive only to his feet slapping the muddy earth, tracking their path. His companions followed close behind.


I do the same - both commenting out and viewing the file in parallel at different spots - vim vertical splits are great for that. In fact, one of my colleagues remarked that he never thought to view the same file alongside itself when he saw me do that when we were pairing.


As an aside, I think I wouldn't mind if a programming language would include two commenting syntaxes, one for ordinary comments, and another for commenting out. These are two very different use-cases, and distinguishing them syntactically might facilitate certain kinds of work-flows.


A common code style rule for C# is // for comments and //// for commented out code (/// is reserved for documentation generation). StyleCop analyzers can enforce this during code analysis passes.

While this isn’t a language construct (a comment is a comment after all), Visual Studio can key off these stylistic differences to show code comments vs commented code differently. I find it to be a neat trick to improve code readability.


Cool.

I've never touched C#. But that seems like an approach applicable pretty generally. I like it!


I thought //// was psuedo reserved for documentation. and then // was for general comments. I usually use block comments for commenting out code or I just use VS hotkeys which I think are //


It’s actually /// (3 slashes) that reserved for pseudo-documentation use.

There’s a StyleCop rule to enforce this as well:

https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/ma...


I've never seen this in C# code! How common is this? Do you have any references? Sounds like a useful convention (it's nigh impossible to google). VS only distinguishes between `///` (docblock) and all other (double, quadruple,quintuple, /*/). But maybe there's an extension.


Here’s more about commenting style:

https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/ma...

StyleCop is used for analyzing code style in C# code.

Specifically:

> If the comment is being used to comment out a line of code, begin the comment with four forward slashes rather than two.

StyleCop relies on this to tell the difference between commented code and code comments. If you use ReSharper, it can do some nifty transformation to strike these out in the IDE which is kinda cool too.

I’m a big fan of StyleCop to enforce consistent code styles. (Even if it’s not always what I may aesthetically prefer.)


Perl and Ruby have this. In Perl it's kind of an abuse of the perldoc system, like this:

    print "foo"
    =pod
    print "won't run"
    =cut
Ruby basically copied it, although I don't think these have documentation uses:

    puts "foo"
    =begin
    puts "won't run"
    =end
When I get to choose, I prefer sticking to // in C code so that I can use /* ... */ for hiding large blocks of code . . . although #if false ... #endif work too.


In C, you can wrap the 'commented' code with #if 0 ... #endif


Or indeed if rewriting a block of code:

  #if 1
    ....new code...
  #else
    ....old code...
  #endif
Esp. for tricky stuff, it's then possible to just flip the 1/0 to easily test/compare/profile the new/old code.

Some IDE's will even work out which block of code to colour and which to dim.


Why have I never thought of that?


I do the same, even in languages without a preprocessor.

You can just use a life if-else, most compilers will optimize the test-on-a-constant away. Even when the code is kept in, I doubt it will have a major performance impact during testing in most cases.


I would do a bit more by

   #if !defined(this_works_but_it_suck_memory_too_hard)
   ....
   #else
   ....
   #endif
That should give me a clue why I "disabled" it when I come back to it 5 minutes later.


I usually just do this with a comment behind the #if false. Yes, nobody is probably ever going to define that particular identifier, but comments are also less restrictive (you can write normal text).


In languages with multiline comments like /* and single-line comments like //, you can do:

    //*
    old_code();
    /*/
    new_code();
    //*/ 
And remove the first slash from the first line to switch between blocks.


Yes! Just today I was wishing that I could have syntax highlighting on commented code that I was refactoring. Just dim it a bit instead of turning it all grey.


It's not a formal distinction, but in C++ code where all the ordinary comments use //, it can be handy to use /* ... */ to comment out a block.

(Of course, if there's a stray /* ... */ comment already in the area to be commented out, this will fail. But an editor with appropriate syntax colouring can make that immediately apparent.)


I like to put a space following the // if it's written text comments, but no space if it is commented-out code.

I think the space looks nicer for text comments, but when I select a block of code and hit Ctrl+/ to comment it out, my IDE does not add a space. So it works out very conveniently for me.


I'll sometimes use:

    version (all)
    {
        ... new version ...
    }
    else
    {
        ... old version ...
    }
and change `all` to `none` to try the other branch.


Scheme has #; which comments out a single expression. This covers most but not all use-cases. (It doesn’t help for commenting-out subsequences, like if you wanted to remove the top half of a begin block.)


Sometimes I do stuff like

   // FIXME try new way mm/dd/yy
   if(1)
   {
     ... new way ...
   }
   else
   {
      ... old way ...
   }


Hrm, but it's obvious if somethingis commented out code vs an actual comment. I'm probably missing something here.


I use this workflow a lot and while I’m still figuring out how I want to implement something. I’ll often just do the whole thing in one file, commenting, and rewriting and restructuring code as I go with multiple open windows to different parts of the file. I only break it up when I’m happy with the abstractions, and I know where the natural fault lines run between functions, classes and concepts.

Doing this too early feels wrong; like I’m prematurely committing to a division of responsibility that’s going to subtly drive me to a bad design.


> one of my colleagues remarked that he never thought to view the same file alongside itself

I do this all the time in Emacs, usually to see two parts of the file that normally are too far apart to be visible at the same time. Collapsible sections can do similar, but to me it's quicker, more natural and more flexible to just split the window.


Another good trick in emacs (if using git and magit) is `magit-find-file HEAD <file>`, then you can edit in one window and see the old version in the other


It actually never occurred to me to do this. Perhaps because I tend to use command-line git, even when using an IDE. I haven't learned magit yet, and I know people swear by it. But this does look like a pretty good way to go about it, at least if your commits have a fine enough granularity.


I think this workflow comes particularly naturally in Emacs, since anytime you split the window (e.g. C-x 3) the current buffer sn already duplicated in the new window. You have to change the buffer in the new window if that is what you want. You can do this in your IDEs too (like Pycharm) but the option to do so doesn't exactly leap off the screen at you.


Vim does a similar thing to Emacs here: the `:sp` and `:vs` commands take an optional argument that, if given, loads a file in the new window. Otherwise the new window just contains the same buffer.


This is exactly what I do. Vim split of the same file, one side for editing and the other for context. Works great!


Commenting is the answer. It stays until I'm certain I no longer need it.


This is the one thing above all else which has kept me off Go. Tentatively comment out a line or two and the whole compilation process may come crashing down because now you have a declared but unused variable, and mere warnings apparently aren't Gestapo enough in this particular universe. You then find and comment out the offending declaration, only for the compilation to die in flames again because now you reference a never declared variable. It gets tiresome after a while.


Are unused variables really compiler errors in Go? That does seem very harsh, and very counter productive while developing. -Werror is all well and good, but while I’m in the middle of writing the code, it’s a bit much.


The theory is that given enough code, any antipattern that compiles will make its way into real use, and therefore the only way to make people stop using an antipattern is to refuse to compile it. Go trades inconvenience in the short term for removing an entire class of bugs and raising the readability floor of bad code.


That's an insane approach, eslint solves this just fine and is fully configurable for almost any style preference.


All Go code everywhere never has unused variables, never has unused imports, and is formatted similarly. But, when you're debugging a function, you can't leave unused variables or imports lying around.

It's a tradeoff. I don't think either side of the tradeoff is insane.


You can do underscore import and local variable to workaround this limitation and I have seen that in the code.


Sounds like a good argument for a --relax-im-just-trying-something flag for the go compiler, and taint the resulting code with it so if anyone else tries to use the code without the same flag, then the compiler/linker will complain.


They were, last time I looked. Which was admittedly a while ago.


Even worse if you include something that isn't used, that's also a fatal error.


Golang is impossible to use without an IDE like VSCode. Tried it with Sublime Text without a language server and it was a nightmare like you described. Less problematic with VSCode cause it'll add/remove imports automatically on save and have big red underlining for anything that will cause it to not compile.


My approach is similar to this, except for whole functions/classes I tend to just create a new one with a version number. e.g. DataContext => DataContext2.

When I'm satisfied that DataContext2 does everything that DataContext does, I delete old one and rename DataContext2 => DataContext.

The risk here, is that sometimes it's not possible to migrate everything to the new version and you end up with multiple old versions lying around, i.e. technical debt.


I do the same, but the opposite way, hehe - I rename e.g. "DataContext" to "DataContextOLD1", then I write the new "DataContext" (from scratch or by copying the original one and then modifying it), this way I'm sure that all calls to DataContext always use the new version.


I do this too! A big benefit is gradual migration - for example, if I'm injecting DataContext into a bunch of different places, I can implement part of the functionality of DataContext. Once I have just enough functionality implemented to support one of the places it's injected, I update that place to take DataContext2. Then I can test, and move on to the next place.

If you're using a language with strong typing and good refactoring support (right now that's C#/Visual Studio for me), the eventual rename from DataContext2 to DataContext is a non-event.


I do the same. Many people comment about commiting frequently but that doesn't work for me: delete code + commit => I don't see the code anymore and my brain magically forgets about it. I need the quick feedback loop: commented code + uncommented code (all in one screen) => new code is going to look like a mix of both. If I have to use git to see commented code then the feedback loop is broken.


Commiting frequently also has a substantial cost of needing to think of a commit message.

If you don't think of a commit message, you'll never be able to find this version again. If you think up a message every 2 minutes, you'll quickly find you spend more time thinking of commit messages than writing code.

I wish there was some kind of auto generated commit message. Things like:

"Added function xyz()" or "Adjusted constant FOO to 27" or "Made lots of changes in file a.c,, b.c and c.c".

These could be auto generated, and then commits could happen in the background every time the code is compiled.

It would be nice for git to have some kind of "commit of commits" which allows a hierarchical representation of commits. Ie. the "Add new printscreen feature" could have as subcommits "Create print renderer" and "Hook up print UI".


> I wish there was some kind of auto generated commit message. Things like:

> "Added function xyz()" or "Adjusted constant FOO to 27" or "Made lots of changes in file a.c,, b.c and c.c".

I would argue that these are not good commit messages as they do not add information not already provided by the diff itself. Commit messages should communicate the intended effect of a change that has been made, rather than being a lossy compression of a sequence of keystrokes.


In which case I want a lossy summary of the diff... And it would be great if a tool could generate those for me...


Try `git log -p <path(s)>`, which will show the sequence of diffs for only the specified set of files.


> It would be nice for git to have some kind of "commit of commits" which allows a hierarchical representation of commits. Ie. the "Add new printscreen feature" could have as subcommits "Create print renderer" and "Hook up print UI".

These are usually called feature branches.

> "Added function xyz()" or "Adjusted constant FOO to 27" or "Made lots of changes in file a.c,, b.c and c.c".

These are very poor commit messages that don't add anything of value; any diff viewer will tell you the same immediately, so you might as well leave it blank.

> Commiting frequently also has a substantial cost of needing to think of a commit message.

In larger, private feature branches I'll use loads of "asdf" commits that I will later squash together. These are my "ok, so far so good" points to make figuring out where I broke something easier.


People are getting way to hung up about their commit history in most cases. If you're just experimenting, even "asdf" can be a totally fine commit message. And it shouldn't be hard to come up with something at least a little more descriptive in <20 seconds. Remember that in some situations, what you get from git is basically a glorified backup system. And that's valuable!

You can edit the history later when it's required by somebody else working on the code.

Or you can just not care. 99% of the value of your git repo should be in the most recent commit.


Looking at other people's code I vastly prefer a feature branch with a summary name of the feature "new-checkout-flow" or whatever and then the commits as they where without squashing/rebasing. Mine tend to have quite a few commits cycling through:

  - Stub in [classes/models/etc] for checkout flow
  - Add basic test coverage
  - Fix my API for easier testing
  - Decouple foo from bar in new flow
  - New checkout flow mostly tested
  - New flow UI cleanup and add comments
  - Fix nasty [N+1/O(n^2)/etc] performance bug in new flow
  - New flow feedback from acceptance testing
  - New flow ready to merge
  - [And often enough/honestly] "WIP to share with..." or just 
 "WIP" for work in progress, "Fixing bug in" or whatever reality there was.
Going back at `git blame` etc 5 years later i can see from the branch names linked to the commit the why it was added and from the commit messages I can see something of at what point in the mental processing of designing/implementing that exact line made it it.


On the other side of the coin I hate looking at git blame to find a bunch of now irrelevant stuff, like fixes to code that were only ever in your branch. I don’t generally care about the discoveries you made along the way (at least not in commit form) unless I’m doing the review for it to go in the main branch, and even then it’s more of a curiosity than anything particularly relevant to software quality.


I wanted "squash merge" to work for me to handle this. Full history in the feature branch for detailed git blaming and a single 1000-line merge commit in main with "New Checkout Flow" but i never fully groked getting it to work when you are merging upstream branches into you feature... You end up taking credit/blame for the upstream commits on main and I feel like you could overwrite the real history of that.


I agree. I also think commit messages get more detailed the further along the process is, and the less code you change.

“Added unit tests and stub out api” (50 files) vs “fix bug when files are added too quickly” (1 file, 3 lines)


If you don't already know the commit message before you start writing the code, maybe you should wait to write the code.


What's to stop you doing all these commits locally with uninformative messages, then squashing them when you push/PR with a better message?


Putting low-effort comments into temporary commits is OK. The one can use interactive rebase (git rebase -i) to squash and edit it into a coherent and clean history, when things get closer to finished.


You don't need to think about good commit messages. Just something that works for you and then rebase before pushing. That works for me at least


I use GitKraken (or insert your favourite git GUI) and I don't have this problem. If I ever have a "wtf did I do 10 minutes ago?" moment I can just look at GitKraken real quick and feel like a stenographer reading back notes.

I typically have it open beside my IDE so I can keep an eye on the progress in the file tree view. I can drag-select a set of lines, right-click and stage them, when I'm basically locking those in, then keep going.

It also auto-fetches every minute or so, so I have great visibility on what coworkers are doing in the commit graph. Lets me react to what they do pretty quickly, and I can implicitly review what they do and I can poke them if I notice something weird.


I also use my git GUI to quickly review what changed before making the commit. That way not only I filter out irrelevant chunks/files, but I also compare the new lines to the old ones.


I get this for tracking down a bug or working on speeding a block up. But why does the unused code need to be committed? Yes it helps you with your one problem but it clutters the document with extra information for others. So I guess I phrased my question a little accusatory, commented out code is a pet-peeve of mine, but I'm still genuinely interested why the two different blocks need to be committed. Or i guess more importantly, if they need to be pushed to the mainline for others to recieve


Maybe I misread the parent, but I don't think it was stated that the commented out code is committed.

FWIW, I also sometimes work this way; however I delete the commented out block when I'm finished. This is also a pet peeve of mine, the only time commented out blocks of code are acceptable is if the obvious way to write something introduces a bug or ignores an edge case. In that case, I also leave a note explaining not to refactor this section and explain why.

But especially while refactoring or fixing certain issues, I really find it helpful to have the prior version still present to compare against. If I have to commit while that commented code is still present, invariably my last commit message is along the lines of "removed commented code" or "cleaned comments for function XYZ". Especially since for these cases I also typically end up writing a comment once finished anyway. If there was something tricky enough happening that I was comparing against older code, likely there is something confusing enough happening that whoever is in the file next could also use some additional explanation that can only really be written once finished.


> When making changes to a block of code or block of text, I frequently just comment out the code and rewrite it anew.

This is the comment I came here for. (see what I did there?)

I also sometimes keep multiple versions of the same line in comments, such as when I'm trying to a tricky regex work. For example, I might have code that looks like:

  # x = regex1 # Original line
  # x = regex2 # Doesn't work
  # x = regex3 # Nope
  # x = regex4 # Also nope
  x = regex5 # Currently testing this
That gives me the way to see which approaches I've tried while solving the problem, and ensures I don't actually use the same exact regex twice.

When I get a working solution, I comment out all but the line that works and commit that.


In this case, keeping those in the commit might be worth it, if you come back and find that the regex wasn't right after all. That way you can make sure you (or others) don't repeat your mistakes. The comment could go in the commit message instead I guess, though I tend to keep those brief.


> I frequently just comment out the code and rewrite it anew

That still doesn't give you easy access to the state of the code _halfway through the rewrite_, which is what the article mentions.


I do this, though it can get messy when I'm experimenting with multiple changes.

As an experiment recently I've taken to keeping snapshots of active work areas using rsync every minute that there are changes present - an automated full-tree version of "keeping copies of the file" that works for everything (images, word processor docs, ...) not just my code editor, avoids an accidental change when doing undo-redo "breaking" the redo buffer, and it survives explicit file closes which undo/redo buffers usually do not. There are two significant caveats: it only works at all if I save regularly while tinkering (or the editor auto-saves regularly), and it doesn't work for files mmap-ed or otherwise open-locked by the editing process. Not terribly efficient IO wise, and still wouldn't be with the improvements I know I could make but haven't got around to, but it has proven helpful at times and takes very little space for several days of snapshots most of the time (identical versions of the same file in each snapshot are hard-linked not duplicated).


Shouldn't it be "had he looked."? Also the second rendition makes doesn't include what the rest of the joggers are doing, yet the first one does. Not sure if intentional.

At first I gasped at your practice, because I never do that, but I see the point now. Mastery of language.


Yes, I had a typo. I was editing in place, and then decided that I had made some poor choices and needed to start over. I didn't bother to fix the typo.

Yes, there is a shift in point of view between the two versions. But the story is mostly written in third-person limited, and so my first version deviated from that.

But I rewrite everything every time I read it, so who knows what will happen tomorrow.


> Also the second rendition makes doesn't include what the rest of the joggers are doing ...

Something is amiss here.


Karma/backlash for my rude post. I will leave it unedited, but s/makes// in case it is not clear.


I didn't take it as rude. Should I have?


I used to work like that. Now I don't. (And I would advise against it.)

Use version control, do lots of small of commits, use diff. Have a fast edit, build, test/run cycle. If you don't have it then spend the time setting it up.


<sarcasm>My employer told me a 23-step code submission process, across five tools, that takes four days to run all the tests is fast enough. Is that fast enough?</sarcasm>


Exactly. Lots of comments. People afraid of doing changes. Or removing old unused code. Not fully understanding what is going on. All signs of a broken development setup. Spend the time and do it right. You will be paid in multiples.


Huh, I thought your first example was much more poetic. The vaguely run-on sentence does a much better job of evoking what's happening in the scene.


Thanks. I'll think about it.


For the record, I think the second version is a clear improvement.


Out of curiosity, do you do much creative writing? I agree that the second version is clearer and more grammatically sensible, but the first version bends the normal usage of syntax to evoke emotion, although a word here or there could be adjusted.

Here's an example of a similar update; would you say you like the second version more?

"Halting, stuttering, his words slurred, his eyes watery, his knees trembling, he thrust forth the knife, and with a cry the blow was struck-"

vs.

"His knees trembled, his eyes were watery, and spoke in a steady stream of unintelligible nonsense. With a cry, he thrust forth the knife, and the blow was struck."


In both, the aloneness of "him" is the focus. In the first, his companions are the subject of both sentences. In the second, The fog and "he" are the focus, with his companions only mentioned as an afterthought.


When editing lab notebooks, it's customary to finely cross a line through data or notes you wish to discount without affecting their legibility. It's also expected that any edits or annotations be circled, dated, and initialed.

The point is that data is never destroyed, and this is one of the many causes for the unflagging preference for tangible records over ELNs.


How do you comment things out in the "English fiction/non-fiction" programming language?


I'm writing in markdown. I use the quoting > character, instead of the more verbose [comment]: #(my comment) syntax. Emacs markdown mode changes the color of the text, and that is enough for me. I use the [comment]: $(my comment) syntax for actual commentary on the text.


I do the exact same i was thinking this article was silly..


Sad to see so much judgement in this thread from developers who can't fathom why others would need this. "Sounds like trial an error." "Develop a better memory."

Programming is not black and white, there is no right or wrong way to do it. OP discovered a problem they had and wrote a solution for it, believing that others might need to solve the same problem. Clearly they were correct based on plenty of others chiming in with their own anecdotes, or tools they've discovered that help alleviate the same issue in other ways.

If you've never had to UNDO/REDO a block of code to revisit a previous state before moving forward again, kudos. But suggesting those that do are somehow going about programming in the wrong way is not constructive.


> the wrong way is not constructive.

Well, open minded people can try other ways. Mine is sitting back and thinking instead of racing the keyboard to try out stuff until it works. Both ways (as I compare to my exclusively younger colleagues) reap results in about the same timespan, but I hardly type more than the actual code I need to type while my colleagues write and delete books ('iterate fast') in that time. It both works, but I have several converts who now work like me and actually enjoy it more (not to mention far less chance of RSI).

Whatever works for you I would say.

What is a bad thing though is that it also has to do with the pressure put by WFH in some companies and sites like Upwork => a lot of 'managers' (and sorry, I cannot say anything positive about these people, but their number increased rapidly during Covid in my experience) measure productivity of an employee by keystrokes, screen changes and # of commits. Even though I finish more tasks in a day than most of my colleagues in our projects, according to those metrics, especially keystrokes and screen changes, it looks like I'm doing 'nothing'. Whatever.


It sounds like you are a software engineer. My advise for you being confronted with these metrics is to look around a bit at other workplaces. I am not saying that you should quit, but it doesn't need to be this way.


Well, I don't have that issue, but I know quite a lot of people whose workplace changed to that. I would refuse to work like that, not to mention that keyboard logging etc is not allowed here in the workplace.


> productivity of an employee by keystrokes, screen changes and # of commits.

Aaargh, that's the most quality destroying metric possible.


The most easily gamed as well... I have no idea how these people thinking about introducing a metric to measure others don't realise that whenever that happens it just becomes another game.

It really isn't that hard to know that through experience and a little bit of forethought.


The other day I introduced this idea to a team member.

We have a separate budget for "development" and "hardening" - these two are actually stages - one starts only after the other ends and that moment is already precisely scheduled. No QA team so far.

So we play this little game of delivering something, anything and allow ourselves to leave some details behind.

I know of multiple problems in my code which made it past review, because there's no incentive to block merging on grounds other than code style, and maybe some glaring issues.

My gut feeling is that this approach is more expensive.


My guess is that the code that was thought through first will be better (easier to understand by the next developer) than the code generated by many iterations.


Most of the times you'd be right. But some of the times you'd be devastatingly wrong. You always need to check your assumptions about runtime, expected behavior and so on and there is only so much modeling you can do in your head. At some point - if the scope is too large to fit in your head at once - you will need to resort to divide and conquer, implement the part that you understand, regroup, then do the rest. And before you know it halfway into doing 'the rest' you will realize a better way to approach the problem.


I actually think this is part of the problem- there is excellent tooling for making programming much, much easier, but many programmers are unaware of how to use them.

When I was an undergrad, I wrote code in a text editor without syntax highlighting, I debugged using log statements, and I manually tested every change by running my program manually with different inputs.

Of course those are all fixed by pretty basic things, but you would be surprised how many programmers literally don’t know how to use the tools they have, or are unaware of them. And some tools are downright hard to grok- git is definitely one of those.


> I actually think this is part of the problem- there is excellent tooling for making programming much, much easier, but many programmers are unaware of how to use them.

Isn’t that just called “experience”? It’s understandable that junior engineers don’t know these tools exists, but most will learn them after a few years.

If you’re curious and interested enough, you’ll wonder if there are better ways to do things. Or talk to more experienced people and ask them how they do things.

I don’t think this is a “problem”, it’s just part of the process of becoming an experienced engineer.


Development often times entails a lot of trial and error, and there is nothing wrong with that. If you have fast iteration cycles, you can fail many times before you settle on an optimal solution.


That usually depends if you are writing something from scratch, or wrestling with other peoples libraries.


"Sounds like trial and error"

Isn't that what coding is?


Maybe to some extent, but most of coding in my experience is planning things out, figuring out exactly what you're trying to do and then just writing it.

Bug fixes have a bit more trial and error to them obviously, but it's usually not reading entire code blocks.

If some function becomes too gordian I start working on a refactoring branch to fix it.

I also use 2+ monitors so I experience very little of the code deletion issues referenced here.


People obviously have different preferred ways to think, and this holds for programming just like for math or any other cognitive endeavour. Compilers, type systems, REPLs and so on are specifically designed to be thinking aids, and it seems to me that using them to their full extent to augment one’s cognition is a perfectly valid way to write code.


I try to rememberf my local history view in the editor, it sometimes give a better overview.


For anyone that uses emacs, there is an excellent plugin "undo-tree" that solves this problem in a slightly different way. Everytime you undo a series of actions and then perform a new action, it creates a branch in the "tree". You can then visualize the tree and move through it quickly your entire buffer history.


Another cool thing about undo in Emacs is that it's context aware.

By default, you can select any block of text and the undo command will cycle through the changes only from that region of your buffer.


whoa this is cool

does it understand syntax boundaries like curly braces / functions? ('undo within function' would be key). or just line #?


That concern is isolated to your region selection tool, and orthogonal to the undo functionality :-) So, you could select by lines, or intelligently select by parentheses/braces, etc. And ask these are nicely composable, so you could easily set up custom shortcuts if you don’t like the sequence of keystrokes.

For eg, see https://www.johndcook.com/blog/2017/08/09/selecting-things-i...


Regions in emacs do not need to consist of whole lines.


For those that use vim, gundo.vim visualizes the "undo-tree" and allows you to preview / jump to any revision: https://github.com/sjl/gundo.vim


There's a fork called mundo which has an inline diff mode that I'm a big fan of — https://github.com/simnalamburt/vim-mundo


Came here to post this. The undo tree is actually built into Vim, but I didn't know about it until I discovered Gundo a decade or so ago. I rarely use it, but it's still installed and has saved me hours of work on a few occasions.


Even without undo-tree, Emacs undo history is "persistent". By that I mean that changes aren't overwritten, so the statement in the article that undoing stuff and then making edits loses the undone changes is not true for Emacs. You just have to undo back through the new edits, then you will start to redo the old undone edits, and so on. It can be tedious, but just the knowledge that it's all there is quite liberating.

Another vanilla feature I sometimes use to keep track of old code is the "kill ring". Just cut the old code and it will be available in there later if needed.


And to get something closer to OP there is always undo-propose.el[1].

[1] https://github.com/jackkamm/undo-propose-el#screenshot


This this this. It was the first thing I missed in Vim when I switched. You also don't lose in Vim, as it keeps it the history as a tree, but it is harder to navigate the tree than Emacs' linear history.


This was my first thought. A timeline is nice, but the problem with a linear undo history is that if you undo and then edit, you lose access to the state before you undid. A tree is clearly the right structure for undo history.


You could just append a new state to the end of the timeline when you “undo”, so you never lose history. This is how undo works in Emacs by default.


Heck it's how boring business apps handle bookkeeping data since forever. You never delete a record to correct a mistake, you create a new record to do it.


The tree structure is inherently flawed for storing undos when using features like undoing within a specific region, because the new state doesn't map to any previous node on the tree.

By default, Emacs has a linear undo structure, while still allowing you to never lose history.


Newb emacs user here. Heavily reliant on spacemacs and tutorials and copy-paste, but I use it often.

The undo-tree terrifies me.


It’s easier to mentally map that the default behaviour undo/redo for Emacs (which is not unreasonable, just complex).

The source for undo-tree contains documentation which very effectively describes the way the library works with examples and comparisons with how Emacs does things by default: https://gitlab.com/tsc25/undo-tree/-/blob/master/undo-tree.e...


I was too at first, until I learned the shortcut keys (to move to the next branch specifically), then everything was fine.


On this point, C-h m yields a complete list of keybindings for all modes active in whatever buffer you do it in.

More generally, for anyone new to Emacs and struggling as I once did, I can't recommend strongly enough that you learn how to use the help system (C-h C-h) and the built-in manual (M-x info). Emacs can teach you a great deal about itself, and these are the ways it does so.


It's easier than it looks! Poke around with the arrow keys a little. You'll get the hang of it pretty quick.


Tangentially, have you had the issue where undo-tree just uses a ton of memory and makes everything unusable?


And if you use IntelliJ/Android Studio there's Local History, which shows your git revisions but also periodic versions of your code outside of your commits


Undo in region is also pretty useful if you want to revert part of your code but not all.


It can even persist history across restarts if you set undo-tree-auto-save-history.


Came here to post this. Tho most times my undo tree are single characters.


You should work on you long-term memory for the language's keywords, or on your variable naming ;-)

EDIT: Or are you programming in APL?


guilty on the latter. tho only contributors to my packages and coworkers complain about it. I am fine with it.


I immediately thought of this, it is so good, like a source control-lite.


Something I always thought might be useful: I want to select a region of the code, and undo that, while keeping everything else unchanged.


My favorite IntelliJ feature is that it tracks the full history of the files in your project, losslessly and independently from your VCS. You can just go and pull source from Local history, from 5 minutes or 5 days ago (I'm not sure how far back it goes, I've never needed more than a week).

Not something I use often but as a last ditch effort to find code that I've spent hours or days on and subsequently lost to a git mistake, or reverted thinking I was going to use a different approach, it's worth the price of entry for the JetBrains tools alone.


Is that the stuff stored inside the .idea folder?


According to the documentation, they're stored under your user's home directory, separate from the repository: https://www.jetbrains.com/help/idea/local-history.html#locat...


Local History has saved me many times.


So does eclipse. The author seems unaware of similar solutions that have existed forever...


Personally I just use git for that. Even if I'm not 100% happy with some solution, I put it into a commit and then do subsequent improvements, or just remove it again entirely.


But then you end up having to clean up your git history before committing to the shared branches, with squashes and rebases, which is annoying.


Cleaning up your history isn't hard if you think about building a clean-up-able history from the start, where each commit is a unit. At least it is easy for me. If it's larger and complicated, I create branchname-v1, branchname-v2, etc. branches and squash. Those branches will still hang around in my fork of the repo should anyone need them.

As for my personal projects, I recently realized that using leveldb wasn't going to cut it for me right before I finished the database storage feature. I still finished it to create a commit that didn't break the build and then worked on switching to rocksdb. If in the future I decide to switch back to leveldb, it's always there.


Not as annoying as learning to navigate some IDE-specific way of doing it. And I usually end up cleaning up my commits, anyway. Also, if I've committed things, then I have commit hashes to refer to in my notes.


this is indeed useful, but what the parent comment is referring to is it also stores with local history outside of git. So you can check both git history for a selection, as well as local history that exists since your last commit.


I use my editor's history heavily, but never needed something beyond the Ctrl + Z / Ctrl + Shift + Z history to access things done immediately prior. If I'm unsure if I'll need some approach in the future, I make a commit. I've become increasingly good at this prediction.


> Want to know what we saw developers doing instead? They either duplicate code files or took screenshots of relevant code while in the middle of a change. Even I have done something similar before: I'm about to mess this up... I'll Ctrl-A and Ctrl-V this into a new tab before it gets too messy, and then I can put the window beside my editor to use as a reference. I even observed a professional developer with 20 years experience doing this!

I do this kinda stuff all the time in my digital art practice. Duplicate a layer, hide the duplicate, start making changes. Does it work better? Awesome, delete the duplicate. Did my idea not improve it after all? Cool, delete the new version and rename the duplicate back.

It's simple and reliable and builds easily on existing structures.


I made a similar comment and now I recognize that what we’re doing is branching outside version control. And I also recognize it’s still sensible to do, and that to the extent version control feels like friction this is yet another case where the interfaces to it aren’t good enough.

And I don’t just mean “git cli is a bad ux” (though I mean that also), but that the whole world of version control is poorly serving rapid prototyping and other exploratory flows.

It’s pretty likely aliases could serve some of this by wrapping a lot of fast paced idea checkpoints into a set of git actions. But it still would be disruptive for eg any intermediary file system side effects if it’s not faster than whatever watcher you have running.


if git was just ctrl+s or something similar (i.e. no more than a single keyboard shortcut sequence) then this whole problem would disappear, but because even in vscode it's "navigate to source control tab, ctrl+enter, enter enter(if something is not saved) "type some stuff" enter" it's too much to save "tiny" changes. not to mention reverts are a hassle.

because of this comment I just decided to create a keyboard shortcut for commits to be alt+cc so now it's a little easier, I still need to type a commit message and accept save and stage, but at least it's all doable from the keyboard.


Oh hey, I've built this! My version would watch code files and snapshot a diff every few seconds. It took a little work to tweak the constants to keep the watching performant, but it was super neat being able to replay code. The main problem I faced was that changes often occur in different places in the file, so the history replaying jumps around a lot. With some proper editor integration, I could see it being pretty useful.

My version is super incomplete because I kind of lost steam when I couldn't figure out a really great reason why someone should use this, but hey, maybe it's worth another try: https://github.com/nicholaslyang/codemkin


I would 100% use this! I do exactly what is described in the note. I use undo/redo a lot to traverse between two states of code and found this incredibly frustrating.

Until this point I thought that was just a bad practice on my end, so never saw the need/opportunity for something like a state slider.

Honestly never thought other people were using undo/redo like this.


I also use undo/redo like this and it has gotten me into frustrating locations on a couple of occasions... but still better than needing to break my flow to drop into making some well formed commit.

I understand how to do WIP commits etc, but its helpful to do a rewind/fast-forward that includes all the character changes in-between the logical “blocks” of diffs. It often helps me recapture approaches to problems that I’ve already attempted.


I used to use the history function in NetBeans to do this. Has been around for several years apparently. Many other editors seems to have this function too.


For me "Local History" feature in WebStorm solves the issue. https://www.jetbrains.com/help/webstorm/local-history.html


I've found Local History on these JetBrains IDEs to be really helpful too. Particularly when combined with "Put Label" command (which I assign to a keyboard shortcut). This lets you insert a descriptive label info the stream of your local history snapshots (such as before you attempt something), so you can go straight to one of those labels and diff against current. Useful when you're trying out an approach with parts that may be discarded, as it's handy for reverting lines of current or overwriting and inserting blocks back from the snapshots. You automatically get a label after certain events, like a test fail or VCS commit. I think there's a snapshot after every save too, and you can configure autosave to happen whenever the editor pane loses focus of you want to.

The only things to watch out for with local history in these IDEs is that the snapshots are deleted after five days by default, and all are wiped if you need to delete your IDE cache for any reason.


It has even saved me when I managed to delete a file not checked in yet. Just recreate the file with the same name, and then local history is available (or possibly local history on the parent folder would find it as well)


This is part of the IntelliJ IDEA platform, so all of the JetBrains IDE products have it. I don’t use it often, since I normally rely on Git, but it’s saved me several times after I accidentally deleted a file or made a mistake with Git.


Indeed it’s one of the reasons why I’m staying with JetBrains products. It’s really efficient, we can compare side by side the local history to understand where it went wrong all while keeping the git history clean. Because while I could do the same manually with git, when I’m in the flow thinking about/building/debugging a particular piece of code, moving out to select files to add to git and then filling a message for that piece of code that isn’t working and doing that every few minutes to preserve intermediate state is a nightmare for my productivity.

It can and should be done automatically by the IDE to enhance developper productivity by getting out of its mind. There is no reason why I should be thinking about that, isn’t that all the point of using IDE and tools to develop ? Seems like using git for that is still re-inventing the wheel


It has also been in stock Eclipse since at least around 2010, when I started using it.


True so did I, for web development though I prefer Webstorm. I was thinking of VSCode


Yes love this feature! I also try to get the habit to put in labels more often, it really helps to have some landmarks to navigate from.


This feature is absolutely outstanding, I use it all the time. Unfortunately they buried it in the "File" menu recently. It was so much easier to reach when it was unter "Version Control".


It's also accessible from context menu in the editor, I use it from there.


You can assign a custom shortcut to it via Settings > Keymap and then the action "Main Menu > File > Local History". I myself have assigned the shortcut control+shift+=


Thanks, did that. I decided on Alt+Z, because Ctrl+Z is Undo, so I though this makes sense.


And Ctrl+Alt+Z is revert changes. Either the whole (or selected) file, or the current block you're in. I use it all the time.


I've only recently started using JetBrains tools - and I did not know this feature existed!

Thanks for mentioning it - absolutely perfect!


This is my most used IntelliJ feature


> Why is it so hard to see code from 5 minutes ago while in the middle of a change?

In vim you can do:

  :earlier 5m
to see the code you had 5 minutes ago.

> (1) If you go to a prior state and then make a new change, you can no longer redo and all those changes are lost.

Vim makes a tree, so that doesn't happen with it. When you undo and make a new change, you're just making a new branch. u/Ctrl-r goes from leaf to trunk, but with g-/g+ or :earlier/:later you can walk the whole tree.

Emacs similarly doesn't lose changes in this scenario, but instead of making a tree, it has a ring and undo is an action that can be undone.

> (2) You can not see a side-by-side comparison of the previous version and the latest version.

It's probably not complicated to make a macro or function where you go to a previous version, copy the buffer, paste it in a new one on a new window, return the original buffer to the state you were in, and diff the windows for that side-by-side comparison.

A command like so suffices:

  :earlier 5m | %y | later 5m | diffthis | vnew | put | 1d | diffthis
> (3) There is no visual indicator of where you are are in your undo/redo history.

Not by default, no. :undolist provides some info, though, and the vimscript undotree() function probably provides all the state of the undotree. There might be plugins that somehow present this info in the interface.

> (5) I have found many actions in code editors that do not get added to the undo stack (e.g., changing a debugger option), which caused me problems in the midst of an annoying bug.

In vim, only stuff that changes the buffer is added to the undo tree. Navigation or changes of the state of the editor (e.g. editor options or vimscript variables) aren't added. Are there really editors where adding actions that aren't changes make sense?

> (6) There is no indication of what steps were "big" or how long ago they happened.

That's also true in vim it seems, at least how "big" they where. Each step does have a timestamp, though.

> (6) It is tedious to backtrack one small step at a time.

You can backtrack by however many steps you want. :earlier also supports specifying by seconds, minutes, hours, days, or file writes.

Most cool of all is that vim can persist the undo history. I don't know how common that is among editors, but in vim you can go to a file you haven't opened in years and undo it all the way to its beginning.


> > (3) There is no visual indicator of where you are are in your undo/redo history.

> There might be plugins that somehow present this info in the interface.

The undotree vim plugin [1] does this, and gives both the file at the time as well as a diff of what changed.

[1]: https://github.com/mbbill/undotree


> undotree

I was going to mention this plugin. I haven't fully mastered quickly jumping around in it, but it's one of those things that when it's useful, it's VERY useful.


Ditto. Unless I have to go back a lot of edits, I don't actually use the plugin that often. I've mapped my undo/redo keys to traverse the undo tree instead of the undo stack:

nnoremap u g-

nnoremap <C-r> g+

It takes a little bit of getting use to, but it takes care of most instance where I need a previous edit from a minute ago.


> Are there really editors where adding actions that aren't changes make sense?

Adobe Photoshop is one. Selection changes don't modify the document at all, but they are part of the undo stack. That's because selecting a part of an image isn't as trivial as selecting text.

Sometimes I wish the current selection could be automatically saved in the file too so you could start where you left off if you were halfway through making a complicated selection (I know you can manually save selections, but that's an added step).


I don’t know about Photoshop, but the selection is part of the document in GIMP.


> Most cool of all is that vim can persist the undo history. I don't know how common that is among editors, but in vim you can go to a file you haven't opened in years and undo it all the way to its beginning.

I absolutely love this feature. I regularly open a file and undo/redo to figure out where I was editing last.


NetBeans also have this but it's set to 7 days as default. Never looked into this before, just thought it was broken but you can save it for eternity if you want :-)


Wow, I have a lot of vim learning to do. Your pipeline is far beyond my level.


| in vimscript is like ; in other languages. I'm not sure pipeline is a good name to call it. It's just a list of commands run in sequence.


> In vim, only stuff that changes the buffer is added to the undo tree.

There is also a command history, and jumplist (movement cursor position history) for things that don't go in the diff tree.


> Emacs similarly doesn't lose changes in this scenario, but instead of making a tree, it has a ring and undo is an action that can be undone.

Emacs default undo system is really, really bad. Like worse than the "standard" one. But after installing the emacs undotree it becomes sane.


What is bad about it? It always goes back to the previous state, and if you want to skip previous undo states (so you don't undo an undo) you can use `M-x undo-only`.


It's been over a decade since my bad experience so take my words with a grain of salt. Iirc, the issues I had was that I made a huge history of undos-followed-by-redos-followed by-undos-etc etc and the more I navigated my history the more tangled and out of control it became. I think I also had a problem with often accidentally switching from undo to redo mode. Emacs documentation recommends using C-f (forward-character) to switch to redo mode, but in my opinion attaching side effect to movement is awful.


I agree. It's cool that it's lossless, but doing a lot of undos and redos makes the history quickly unwieldy to navigate. I much prefer vim's tree implementation.

I wouldn't say that it's worse than the standard, though. Unwieldy as it may be, I like that I can just keep on pressing C-/ and eventually get to the state I want without risk that I may have lost it because I accidentally made a change after a series of undos.

> Emacs documentation recommends using C-f (forward-character) to switch to redo mode, but in my opinion attaching side effect to movement is awful.

I use C-g (keyboard-quit) for that, which is the general let's-not-do-this-anymore keybinding. I think the documentation mentions C-f as an example keybinding that does the needful without much thought on being the "best" keybinding for it.


> doing a lot of undos and redos makes the history quickly unwieldy to navigate

Are you aware of the `undo-only` (and `undo-redo`) functions? They make it a breeze to navigate even the most complex undo history.


Actually, I wasn't. Thanks for that.

(undo-redo) doesn't seem to be defined in stock Emacs, but undo-tree defines (undo-tree-redo), which does the needful.

My comment was more about lamenting the defaults in comparison with vim's, but it's true that it doesn't take much configuration to make it much better.


undo-only doesn't have a keybind which makes it inconvient to use many times in a row. You could define one yourself I suppose, but at that point why not just get undo-tree?


The issue I have is that I want to be able to use C-f and C-g and many others without it changing anything about my history.


This seems cool. Is there an equivalent feature in emacs ?


What particular feature? :earlier 5m? I'm afraid not. Emacs' C-/ acts like :earlier/:later/g-/g+ in that you can navigate the history across undos, but there's no support for specifying by number of minutes. I don't know if there's a package for it though. Evil-mode (vim emulation in emacs) doesn't seem to have support for that either.

I can't imagine it'd be that complicated to add it though, if you really want it. I think it'd just be a matter of hooking to the undo function and maintaining a list of timestamps for each item added to the undo list, then adding a command that lets you specify the number of minutes and figures out the correct undo item it should go back to by using that list of timestamps.


This is excellent... for Vim users. A lot of developers either are not willing or haven't gotten around to learning the complexities of Vim, and use other IDEs like JetBrains or Eclipse. This workflow won't work for them.


JetBrains' various apps can do this as well with a feature called "Show Local History". I'm not sure where it is in the menus but you can hit shift twice and type it in.

It automatically commits after every change since the IDE was opened, and can easily be diffed and reverted as needed. Reverting saves another entry, so you can revert back to the future as well.


This. I've seen developers _think_ they lost code in Android Studio and spend hours trying to recreate it when it was in local history all along...


It's in the context menu for files/classes at least.


IIRC you can right-click on the column with line numbers to the left of the main editor and you'll have that option too


Eclipse has the same thing.


If you learn Vim like anything else - slowly and starting with the basics - it's not more complicated than figuring out any IDE. And once you know the basics you are fully in control how deep you want to go.

Personally I have found Vim more approachable than IDEs actually. It doesn't throw all those overloaded toolbars at you according to some one-size-fits-all principle. For me it went so far that last year I actually did a Java project in Vim instead of an IDE for the first time, because the latter had grown to be so frustrating to use.


If writing text more than 4 hours / day is what you do for a living, and you have at least 10 more years left of doing that, not learning Vim is just lazy. Vim gives you back enough time to think about what you're writing.

Also, if you go cold turkey, you'll be productive in less than a month, and arrive at a good workflow that fits you in less than six months, flow that you will improve over the years in amazing ways.


And there are a lot of people, like myself, who do use vim regularly and somehow completely missed this feature. For those people the OPs post is excellent.

Likewise, if you have any recommendations for your preferred workflow I'm sure some will find it useful. Hopefully those who don't use your preferred $EDITOR don't then rush to complain that not everyone uses $EDITOR. ;)


I am one of those "not willing" people. I encounter the same issue discussed here without even thinking about it, and solve it as such:

1. Realize I need to check how my new code looked like 5 min back. 2. Copy full file into clipboard, undo by a few steps 3. Do a temp commit on GitHub desktop 4. Paste the new code back and look at the diff on GitHub desktop, and if needed undo the temp commit from above.

Is this more clicks than the vin shortcut? Sure. But I don't have to go learn the internal tree structure representation of my text editor and spend my time in that universe. I have found a way to do what I need to with the tools I have in hand.

This is the same way how the majority of finance runs on excel when they could do better with better programing. In the end whether you get the job done in fields where you're not billed by the second is not dependent on how well you use your tools necessarily.

The other analogy is cars - for most people it's something that gets them places, for others it's a way of life if not at least a more involved proposition.


> The other analogy is cars - for most people it's something that gets them places, for others it's a way of life if not at least a more involved proposition.

But if code is your way of life, which it is for many people here, it is beneficial to learn the tools that allow you to do it easier. If someone can see how their code was 5 minutes ago in a few keystrokes, they’re going to take advantage of that significantly more frequently than someone using your cumbersome method.


As some other comments suggest, vim is the gift that keeps on giving. I'm not going to try and convince you to use it -- do whatever you want.

But for me, every little thing I learn in vim pays off... a thousand times? Macros alone have probably saved me a cumulative few hundred hours. And with every other little thing I learn, compounds with this effect. I write macros much better than I did five years ago, but I still surely have tons of stuff to learn. No IDE will ever compete because vim is just four keystrokes away at any time, no matter where my terminal is.


Not so sure about this, I think it depends on how frequently you have to do that task. If you're doing it every day or even every week you could potentially be saving a huge amount of time and effort by using a tool that does the job well. It's sort of like using a random heavy object as a hammer, sure it's fine in a pinch if you need to occasionally hang a picture but if you're working as a carpenter it's counter productive. Comparing it to the notorious abuse of excel spreadsheets in finance is not doing the argument any favours.


It's not as hard to learn as people say it is. It's just weird at first because it's modal and different than what they're used to. You can get away with using it pretty naively and still get a productivity gain.


Jetbrains IDEs have a local history feature that provides snapshots with diffs and allows partial rollbacks.

I only use it when experimenting with different approaches.

Mostly I only use the "Local Changes" tab in the Git panel that shows the diff compared to the last commit, or "Compare with branch" to see the overall changes relative to the base branch.


Local history saved my sorry butt yesterday when I thought I lost all my files due to a git-related accident. But Local History still had all my changes and restored my files.

Unclear why this isn't baked into the OS nowadays. It's not like we don't have enough disk space ;)


I just saw something similar on Twitter[1]: a script that takes a ZFS snapshot after every command. This lets you jump back to the state of the whole filesystem after every single command that you ran, giving you a sort of global history.

I figure you could adapt that to something a bit more restrained that you could use on an ongoing basis to have a "Local History"-style feature for your whole OS :).

[1]: https://twitter.com/grhmc/status/1362222849662935041


That looks amazing. I've used NetApp NFS filesystems that provided user level snapshots, where it automatically created snapshots of files. It wasn't after every command, but at regular intervals. You could access the files under `.snapshot` directory.

Searching now, it looks like this is part of NetApp ONTAP, and managed by a snapshot policy.


VSCode also has a local history plugin. One of my default installs https://marketplace.visualstudio.com/items?itemName=xyz.loca...


Thanks for sharing this. Does it have a draggable slider? Or ability to "Play" through changes?


I have the feeling JetBrains do not understand how important this feature is for some developers. It does not have a shortcut and recently got buried in the File menu, almost as if they are ashamed of it. I am using it all the time and life would be much harder without it.


In NetBeans there is two big buttons on top of every file view. Source and History. Very easy to get to and has been used a lot :-)


I came here to write this comment. I use this feature often and, like the sibling comment, it has saved my butt a ton! I love JetBrains IDE with Vim bindings


this! Indeed, JetBrains IntelliJIDEA had this feature for decades, I use it very often. It’s just a lifesaver.


I've gotten in the habbit of just commiting very frequently. I have a git alias "git cam" that just commits everything with the message "nt". Then once I've got something working, I use GitUp (which makes it very easy to merge/split/rearrange commits) to collapse all the nt commits into one or two coherent "real" commits.

Maybe a little clunky, but works well for me.


Basically same deal here, though using 'magit' and 'wip' instead of 'nt'. ('No text', one assumes?)

Doesn't help with IDE settings and such, but that's such a niche situation that it doesn't matter at all for me.


Does anyone know if there’s a good way to do this in VS Code? I often hold cmd-z until I get to some version and copy code from it. Ideally I’d like to have a separate window where I can see a previous version without affecting the current file/view.

Before anyone comments, git doesn’t cut it for this because no one commits after every -+1


Like an ongoing stream of history where I have a slider I can drag to revert on-the-fly.. That'd be cool. I mean you could easily record and do this, but that doesn't copy-paste well.

EDIT: And of course I read the article after reading the comments.. Haha, VS Code plug-in in 3.. 2..


One thing I think would help this is a dual repo setup with the local dev-only repo automatically committing on save (or if your watch is expensive committing unsaved editor state on pause). The repo could be short lived if you prefer (eg new per baseline repo branch or editing session) or a long lived series of stashes. But its history would be built into the editing experience and separate from the normal version control flow. I don’t think it would necessarily work for everyone but it could certainly fit how I work in exploratory mode without breaking my editing flow.


Yeah that is the simple and very effective solution of this whole problem. If I had to use I would use something like this.


It might not be exactly what you're looking for, but I saw this linked on Twitter recently and have been meaning to try it out, so maybe worth a look?

https://marketplace.visualstudio.com/items?itemName=Wattenbe...


This solves another problem I had, so I’ll try it, but it’s completely unrelated to edit history.


Don't know if it serves any other purpose than coloring, but if you want to just go back to the last (and next) edit locations, Visual Studio already has a shortcut for that (Ctrl+Shift+'-' / Ctrl+'-')


this looks like it does that https://github.com/zabel-xyz/local-history

not super polished nor is it actively maintained but has promise


Here's the vscode marketplace link for it https://marketplace.visualstudio.com/items?itemName=xyz.loca...

I use it with every project and it works great. Helped me out a bunch of times. Just need to remember to ignore the .history folder from git and other tools.


Commit early, Commit often!


If I have to commit my whole undo history then I’m using git very wrong. I commit (even temporarily) to save progress, but only when it makes sense to so. Sometimes I don’t know whether I’ll need this specific version again in the future.


Git doesn't mandate to only commit useful stuff.


This is correct, not sure why you’re getting downvotes. Feature branch for WIP makes sense.


But if there are more people in the team, it will be a great amount of noise they will have to go through before reaching any useful commit. Definitely not recommended.


There's a difference between committing to your local tree and pushing it to others. You can always cleanup before pushing.


`commit` doesn't mean `push`. Commit locally, rewrite history with rebase before pushing it to others.


That's still way more overhead compared to undo/redo


I take it you didn't read the article so.


In Git you don’t have to actually commit, you can use the staging area. It’s something I do rather often. I’m going to make a non-trivial change and I’m not sure if it’s the right approach, so I `git add -A` first, make my change, and then `git diff —-cached` to review it. This is especially useful when doing things like running formatting tools, so I can see what the tool just did. And it doesn’t require making any commits.

Having said that, this approach only works if I know up front that I’m going to want to compare the code. Far more often is making a change and then realizing I want to see the old version. A history timeline would be extremely useful here.


Even commits are malleable until you push them somewhere. I do this a lot - create commits of any meaningful checkpoint, then rearrange/squash/remove commits later that I don’t want , to create a well-formed commit that I push.

Git stash can handle your use cases too, but commits are more powerful as they let you compose multiple pieces together


You can also use git commit --fixup, or, to fix up a commit with message “wip something”, you can make comments with messages like “fixup! wip something” or “squash! wip something” and git rebase --autosquash --interactive will reorder them all (squash! allowing you to stack commit messages by writing in the commit bodies)


Yes. I usually maintain a hefty stack of commits where the top ~5 are experimental WIP nonsense for me to either clean up to or discard, the next ~10 might be code out for review. But in between pushes to remotes, every session of changes turns into a slew of fixups and I'm often rebasing a stack of 30+ commits, massaged into 2-3 change sets.


I have the same, but now at work we use the GitHub squash function in PRs. I still make fixup commits but don’t have to rebase to manually squash them any more.


Trivia for you: git stash is actually storing the changes as a set of stacked commits on a hidden branch. You can even do a git log or amend on your stash, although the stash abstraction is pretty complicated under the hood.


If you work on a branch you can push them to your remote repo anyway. Good for backups or if you move between hosts for any reason (I do this all the time as I prefer static workstations for focus and work/life balance.)

Branches rightly became a bit of a dirty word over the last decade. In a fast moving collaborative codebase the pattern you really want to avoid is using a long running feature branch — one that remains uncommitted to trunk for ever more uncomfortable periods of time.

Branches themselves — the VCS technology part — are incredibly useful when used fluently.


Eh, I use the dumb approach of:

    cp code.d code.old
if I'm going to make some trial changes :-/


Nothing wrong with that :)


Are you suggesting that people make commits before reviewing the changes? I never thought of that...


No commit is involved at this point, only the staging area.


In CoCalc, one of the foundational ideas is that it should be very easy to see code and Jupyter notebooks from 5 minutes ago. Hence every editor in CoCalc includes a TimeTravel button, which you click on to get a side-by-side view of your file (or notebook) and a slider that lets you go back and forth in time at high resolution. I designed CoCalc this way because when I taught data science classes, I would walk around a classroom while the students were doing an in-class exercise and they would often say to me "I had this part figured out 5 minutes ago, but I messed it up." Some people (including me!) who get used to working this way get very addicted to it, and can hardly imagine working any other way. This is vastly superior to depending on undo/redo, and I use it all the time when doing deep coding projects. It's also useful for collaboration, since it makes it easier to figure out what somebody else did with a file or notebook.


Reading the comments, I'm stunned and disillusioned.

Are most modern developers struggling to write code without this? If so, there is a fundamental problem that has nothing to do with levels of undo.


I am more stunned by the tone your response. Why are you implying there's a "struggle" or "fundamental problem"? Sorry but your language is very condescending.

Sometimes it's easiest to hit Cmd+Z a few times to check out how something looked at an earlier stage of a change. And then it's easy to Redo everything back to the current state. Rewinding changes back can even jog your memory as to why you did something. It's fast, simple, and effective. What's so bad about that?


Likewise. This whole thread is totally bizarre.

The closest imaginable thing I might do is wrapping some block of code in an if(false) {..} or #ifdef it out, when I'm refactoring and feeling unsure about my changes. But is that even the same thing that we're talking about here?


Hello from the bizarro universe of people who do this all the time and can't imagine the opposite. Do you never rewrite some code only to realize it didn't precisely capture all the edge cases of the thing you rewrote? In those cases I use the git gutter in VSCode to view what the old code looked like and compare and contrast to the new version to see what I missed. It seems to me that that's the same as what these engineers are doing, just with a bit more primitive tooling.

Can't imagine how anyone doesn't do this - unless there's just a race of God-programmers I've never encountered that write everything perfect the first go. But I don't think so. :-)


Exactly. Sometimes you start heading down a change path, and as you discover more, you realize there is a better solution. You want to save some of your concepts, but discard others. That's what this helps solve.

Git stash can be helpful. Stash your "wrong direction" change, then apply just the good chunks from your stash.


TDD (test driven development) really help in solidifying interfaces ahead of time and also to capture regressions.

Yes, git diffs help, but once I’ve passed a parameterized suite of unit tests, never needed to undo/redo. Maybe not perfect the first go, but only need one/two tries to working code. Then refactor for readability.


TDD is definitely helpful, but I don't ever use it as a metric for code fitness. At best it proves a limited number of inputs produce a limited number of expected outputs.

Consider, for example, a bug recently introduced during a refactor at my work:

The programmer optimized a conditional based on a regex by transforming it into a simple string compare. All the tests passed, code/branch coverage was good. Except that he missed that the regular expression tested case-insensitively, and our test suite didn't test upper and lower case scenarios.

This simple mistake outlines a few flaws:

- coverage was not robust, because it didn't take into consideration the branches inside the regex (which one could interpret as a form of macro expansion)

- the limited number of inputs used to test failed to capture the broader domain of possible input

- the test did not reflect a successful refactor

Obviously this is a fairly complicated example despite a pretty simple change, and several pieces had to fail in order for this change to fail. But it affirms that even basic changes to code aren't necessarily adequately covered by TDD.

Yes, the developer who forgot to test different letter cases made a mistake. Yes, regular expressions bring their own problems. But fundamentally, the result was that passing the tests did not affirm fitness of the change. Rather, it only proved a limited subset of conditions were error-free.

Effectively, tests are loaded with false negatives, so trusting them to identify problems should be done with a massive grain of salt.

If the developer had simply copy/pasted the code he changed and compared the two, then it's exceedingly more likely that he would have noticed that his code didn't capture the full breadth of conditions in the previous code.

A bug existed in the test suite, to start, and then a bug was introduced into the codebase. A human being looking at the two lines of code as it was rewritten probably would have noticed the regression. But even a code review missed it because it was a fundamentally small change among the other, more "make sure this looks good" code.

Personally, I very much value copy/paste/compare changes and never treat the test suite for anything other than "well we haven't broken anything in any exceedingly obvious ways." Maybe you're a superhuman programmer, but I'd lean more towards "you've probably added more bugs than you realize".


There’s def. ways to write really good tests as well to avoid the limited input issue - write the test in a property-based test manner, like haskell’s quickcheck, and the test can catch entire classes of bugs.

The caps vs. no caps issue would be easily caught using randomString() as the test case.


YES! TDD has saved me from this, listen to our friend fung. I put off learning about it for so long out of laziness but the payoff is huge.


Yeah, sure it happens. Then I type “git diff”. But never have I unraveled the entire undo buffer stack only to peek at it and re-push the entire contents. That’s super weird to me.


What’s weird about it? For some - to do git diff might take more keystrokes than hitting undo 5 times.

I do all of these. I’d use undo/redo when I want to look at something I /just/ did, meaning while in the process of writing something. The git gutter when I stumble upon some change I want to see what was there before. Or Sourcetree for a full diff.


you cant do it on a first go. i write a draft of what has to happen. often i draw drafts of the solution on a4 sheets of paper(my own blackboard, the best tool i know to make software). i do research and update the drafts to a point where i type in the code. code is the last thing i do. i laught that i make coloring books and not coding :) i am quite fast and i have little problems with switching contexts. the code is much better as i "see" the whole solution. i have much better abstractions. in a recent task i have found with this way of coding, many places with bad code and made them better as they didnt fit the cleanliness of the soulution. they just stand out like the eifel tower in paris.


If there’s a specific implementation that’s especially hairy, I’ll just copy that block and comment it out before continuing. Instant reference. And if I’m rewriting more than 10 or so lines at a time of complex business logic, something is wrong.

But also, `git diff`.


Thought the same, copy-pasting valuable blocks as a reference into a comment worked fine. If it exceeds a one block rewrite, make small dirty commits to keep track of things.

On one hand it's nice that there are tools to support devs who get lost in their undo-redo history, on the other I feel like it's a matter of good habits to not even have this problem.

of course changing habits is hard, so maybe tooling is justified in this case. I'm just happy I don't have nother "history" type mental model to deal with.


I suspect that is exactly the method most developers would uses in that situation.

All versions controls have a way to compare, merge and revert historical changes in and out of the current working copy.


It’s amazing how many developers don’t have a working knowledge of fundamental tools - like version control and debuggers. I honestly don’t know how you could get hired without this basic knowledge.


Undo/redo have nothing to do with this though. Undo/redo is for looking at changes you did while in the middle of writing something. Before committing.

This is from an example I did just yesterday.

Let’s say you’re moving a piece of code by cut n paste. You cut it but in the middle of changing you copied something else, maybe accidentally. Undo and cut it again. Redo and it’s now back in your clipboard.

You could copy from the diff but you’d get extra tokens to clean up.


Indeed, I find this to be terrifying.


What's the problem? I do this often.


> I'm stunned and disillusioned.

I think you simply don't understand the workflow... when and why people do this.

Generally, the idea of referring to an older version of code while working on a later version is good and useful, is it not?

This is just one way of doing this.


I'm so confused as to why anyone would do this.


I write a lot of frontend JS with hot reloading

make change => see result => cmd-z 1 time to fix result OR commit and keep coding

oops, it wasn't really fixed. cmd-z 3 times and see if that works

I know it's wrong, but what's right?


> I know it's wrong, but what's right?

Nah, it's not wrong. You're testing your code changes live.


no it's webpack HMR

(I know you're joking)


Hmm... I actually wasn't. By "live" I mean in the developer's own browser.


Ohhh, I thought it was a riff on "I don't always test my code, but when I do, I do it in production."


Agreed. I never do this. Why do people do this?


I too am struggling to understand this use of undo.

If I'm reading this correctly it means applying a large number of undos to get to a previous editor state, followed by an equally large number of redos to then get back to where you started.

I can't recall every doing something like this.

The only time I seem to use undo is when I genuinely make some sort of editing mistake.

One reason I know I don't subconsciously do this is because I never struggle to execute an undo action.

However, on the rare occasions I need to do a redo I tend to struggle with the short cut key and usually go to the menu instead.

That tells me I use undo much more than redo.


In Vim you can achieve something quite similar with gundo plugin. Vim tracks all your changes in a tree by default and you can also use the "earlier" and "later" commands with a time delta to go back and forward a specified amount of time. So in Vim, to see the state of the file 5 minutes ago, you type ":earlier 5m" . Gundo is a visualisation on top of the builtin undo. I'm sure Emacs has something similar and probably more powerful, but I'm not familiar with it. I'm not aware of other editors having tree undo.


Emacs does indeed have a plugin for this called "undo-tree". I think it is missing the ability to specify a time to go back by, but you can view the timestamps in the tree visualization.


This is honestly the feature keeping me on PyCharm over VSCode the most. The "local history" feature of JetBrains IDEs is amazing. It saves on every blur, and has a diff viewer when comparing with history.


> The "local history" feature of JetBrains IDEs is amazing.

NetBeans has the same feature. I cranked it up to keep it for 30 days. Very handy. It's essentially an "automatic git" on every save.


I'm quickly reminded of Photoshop's "History" window and how it was one of my first adds to the standard environment setup to make it visually prominent in the top right of the workspace. You could quickly click up/down it and view the steps, but the functionality is in the end the same as undo/redo, where you lose the others once you make a change after walking backwards.

I can't imagine it would be too crazy for IDE's to begin to add the same, per file at first but maybe also per project and then with saving some of those trees.


The actions can be embedded in the files as well. Pretty handy if you need to recreate something that's not been touched in a year.


I am very surprised by this. I seldom use the control-z shortcut and I've been writing code for several decades...


Indeed. This sounds more like trial-and-error programming.


What's the alternative? What should I be doing instead of cmd-z?

> I'll Ctrl-A and Ctrl-V this into a new tab before it gets too messy

I did this about 400 times today


Yes, and I've always believed that short-term memory is a very important mental training for a coder.


My short term memory is much reduced compared to my younger self. Consequently, I work in ways which allow me to reduce cognitive load and still accomplish the task. FP is a godsend for me, even if I just adopt some of its principles.


Likewise. I have ADHD and even with treatment, I feel more like a GPU than CPU. Small functions, lots of static typing, IDE for autocomplete, and lots of FP style patterns.

When I get thrown into an un-typed python codebase, my productivity grinds to a crawl, and I start having to do things like have a scratch pad with various versions to copy paste.


I've just started using ZBrush recently, and one of my favorite ideas in this software is their timeline scrubber. You can just drag through undo/redo history to get back to the point in time that you want very quickly.

I could see this being similarly useful in code.


Same. Perhaps a different time scale but I’ve considered doing this for files within a source code repository to better familiarize myself with the history.

One interesting feature might be to be able to select any two commits such that you could diff them in isolation.


I don’t think I've ever used this sort of time jumping with code. If I'm refactoring or testing a different segment I just comment out the original for visual memory reference and refactor based on the commented segment. Once I'm done, I chuck the commented section. It's nice to have the reference so I don't have to commit anything to memory.


I do this too. Sometimes I'll duplicate the entire code and comment out the lower one as I write a new version or parts.


In Sublime Text I actually use a quick Undo-then-Redo to navigate, of all things.

I'll be typing some code and realise I need to check whether the imports at the top of the file are correct.

So I'll Cmd+Up (go to the top of the file) and see that yes, they are correct. Now I have to get back to where I was.

A quick Cmd+Z,Cmd+Shift+Z does the trick - Undo (takes me back to the code I was editing, and undoes some of it) and Redo to put it back to how it was. Works well, but would mess up this researcher's statistics.

(I absolutely do also use the undo stack for looking at the last few minutes of history too, that's really valuable)


> Now I have to get back to where I was.

In Emacs, you can often use C-x C-x for this, since many movement commands which moves far away, like beginning-of-buffer, sets the mark at where you moved from. So C-x C-x moves you back. If this does not help, i.e. you have moved several times, you can almost always get back by repeatedly doing C-u C-Space until you find yourself at the correct place again.


Funny, I've been using the same trick in VSCode without reflecting on it.


I do a similar thing, but I use "soft_undo" as it seems to include navigation jumps in it's stack so it's only one step. Think the default keybind is Cmd+U.


There is a navigational back command, that moves your cursor back in time, just like a webbrowsers back button.

cmd+-, if I remember correctly.


`ctrl + -` is a good short-cut.

But, often times I have jumped around many times, or only scrolled. It doesn't work in these situations. Whereas `undo` `redo` reliably brings me back to the place of last input.


What ever works for you. To return to scroll position I click up then down or left then right.

You can use `ctr + -` to also jump between documents. So if you’ve jumped to a function definition in another document the back shortcut can take you there.


so..... the data this is based on is from https://sci-hub.se/10.1109/vlhcc.2014.6883030 which is 21 students programming.

Seems a bit suspect the whole premise really. I don't really end up doing what this article outlines. I find a lot of programming studies are often done on what is immediately available... students, and I think this tends to not really be indicator of what experienced programmers do.


If I remember correctly, Eclipse used to have (still has I guess) a feature were it would store code changes in a local history independent from your version control.


Yes, definitely Eclipse local history.

At least 10 years using it. What's interesting though is that such a feature would be off the radar for other editors and even the OP article.


jetbrains IDEs have a local history feature that seems to do this


When I looked, it had the same problem that Dropbox has with this: a textual listing with only timestamps. I need semantic info! Show me the differences. Don't make me choose from this arbitrary list. I want to scan through the versions very, very quickly.


Not sure what you're looking at, but IntelliJ 2020.3 gives me a list w/ timestamps in 1/3 of the modal, then a diff viewer in the other 2/3. I can down-arrow through the versions and the diff updates.


Fantastic, downloading the latest version now!


That has been in for quite some time. I don’t remember since when, but at least in the 2019 version.


That's the way it's been for years.


I use view local history of selected so I can blame myself and view git history of selected to I secretly seethe at some one who left the company 3 years ago.


Yep, it's quite a handy feature at times. I don't use it a ton, but it is very helpful when I want it. One very nice aspect is that you can scope it to a specific section within a file (the same can be done with git history as well).


Came here to say this. It is a really old feature.


It is, and yes I've found it useful. However, the idea of a slider that you can just yank around sounds really appealing.


Yeah and it’s actually decent.

That being said I don’t mind committing often. If I have a slew of small commits I squash them.

Since I will need to commit at some point, I rather do it incrementally.


Committing removes the diff view in PyCharm, and I can't lose that.

By "diff view" I mean I can see all the changes I've done from the main branch.


Personally, when I'm ready to submit a pull request, I usually do a soft reset to the point where I branched off from, then redo my commits and force push to the branch, for exactly that reason. I can't remember exactly which lines I've changed over the past few days unless I see all the diffs together.


Interesting idea...

I suppose I can stash my current code, reset to where I started somehow, and then unstash.

I've also toyed with the idea of having two repos on my disk.


My goofy lil' homemade editor does it this way: If you go back in the past and change it, the future becomes the past. This might sound confusing, but effectively it means that the undo stack contains the actual history of "what happened". This means you can hop to any past or future state linearly with undo & redo alone. I just find this easier than navigating tree GUIs and things that aren't worth the trouble to me. It can still lead to a bit of mental gymnastics in extreme cases.

https://github.com/zaboople/klonk

Note that in theory this can cause an exponential growth of the undo/redo stacks because it makes an upside-down copy of the future when you change the past; but in practice it's never been an issue.

I was thinking of adding a feature to navigate back to "last change-the-past", which wouldn't be hard.

It at least makes more sense than most science fiction time-travel plots...


I'm fairly used to using basic undo/redo as a timeline already. I spam "undo" often in vim just to yank something into a scratch window. I might accidentally make a change that breaks my redo path back to the present, but thankfully that's a pretty rare occurrence for me (and I can always recover with "g-"). Good enough for me!


Vim is the only editor I’ve used that has a branching undo model. I know there’s a UX problem there but I don’t understand why more editors don’t support this.


[Works if source is versioned].

I usually use these 3 approaches in IntelliJ (IJ):

1. When a line is changed, IJ will show some marker in the left gutter. If I hover my mouse, I can see the diff.

2. Sometimes I peek Local History.

3. Sometimes I commit with random name e.g. "bla" so I can refer to that commit from time to time. When I'm done, I squash all the blas into 1 proper commit.


I just comment out the old code while working on the new version. Surprised this isn't the obvious answer.


I seldom even do that. I just keep the code there, write the replacement, and then delete it.


Forget writing code, I do the mass-undo, mass-redo thing all the time when I'm writing prose. Even sometimes when I'm writing Hacker News comments.


Not exactly the same, but as a voice actor the DAW is my "IDE", and configuring it save a time-stamped backup of the project file every five minutes has really saved my bacon several times, especially when the undo tree won't let me undo back to the state of 5 minutes ago.


I didn't even realize I did this until reading this article.

I think the issue is that the undo/redo concept is inadequate. The author's solution of a persistent, read-only history seems like a step in the right direction, but that seems like it's much too slow of a workflow to beat out CTRL+Z/Y/Shift+Z, which is muscle memory for most.

Maybe we need something like a hyper lightweight git that can be controlled via keystrokes.

It's really disappointing how so many interfaces have been stagnant for so long. It sometimes feels like the only software difference between a computer from 2021 and 2001 is that the graphics and animations look nicer, but otherwise is just more of the same stuff.


Undo/Redo shortcuts and a button to see the history in a timeline, my biggest problem is writing while in a undo state overrides the redo stack. Jetbrains has a Local History functionality, by it has a linear timeline instead of branching like the author plugin


That's what IDEA has integrated as "local history" and I use it every day for this purpose.


Netbeans has had this feature for about a decade and I use it all the time for exactly the reasons suggested. I was pleased to see that IntelliJ has the same feature. The resolution doesn't need to be more than a couple of minutes. Netbeans will keep a couple of weeks of local history and prunes the old history down to one or two snapshots a day. It can get a little confusing if you are checking out vastly different versions of the same file from different branches since it doesn't record what branch you were working on at the time, but it nonetheless very useful.


> It can get a little confusing if you are checking out vastly different versions of the same file from different branches

I see that as a feature, not a bug. I’ve been saved countless times when I accidentally messed up my git or local state.


if you use vim, you can do things like ':earlier 5m' to see code from 5min ago. in fact, vim keeps a whole undo tree available, and some plugins will let you visualize it. https://github.com/mbbill/undotree


I have had this same idea and would love to collaborate. I can see tgis as either an extension to be added to existing IDEs or an enhancement to the Monaco Editor.

I see it as a product named Multiverse Editing. You start off coding in the prime universe. Each Undo places a node on the current universe's timeline. If you continue editing you are still on the prime timeline. But you can click on any node in the timeline to go back, and editing from there branches to a new universe with its own unique timeline. Each node represents either complete before and after code for the current timeline and a pointer to the root of one or more new universes. you can select any two or three nodes and perform a merge which results in another new universe branching from the source (left) node of the merge. A database will exist for each source file, each representing their own multiverse. Source control events would become nodes on the timelines such as branches becoming a new universe and merges collapsing universes together. Ideally the database would be in the SCP and not the editor, though that might get slow, nut that would preserve the complete edit history for a file. Each node would have metadata where the developer could add notes in MD format, etc.


Because you forget what you can’t see.

This is the same way that whitespace and long variable names make code harder to read and reason about: simply because you can see less of it.


Pycharm has a local file history that even allows showing history for a selection. I'm not sure what triggers the periodic records, but it has saved me multiple times.


> I'll walk through some reasons why version control does not save the day here. While a developer is making changes to code, they may not realize that they want some intermediate version from a few minutes ago until they are well into making the change and become stuck. We saw this repeatedly in our studies. This introduces a problem of premature commitment3, which forces the developer to decide to save an intermediate version (or not to save it) before they have the information needed to make the decision (whether they will need it or not). Unless you commit code to your git repository every few minutes, working or not working, then version control won't help you here.

Presuming DVCS here; if you’re still on a centralized VCS this is surely Not Good:

So, you need an editor utility that automatically commits every save (and possibly autosaves and commiys every completed undo-list item)on a special temporary branch that gets deleted when you do a real commit, and if you go back to prior state on that branch it automatically does the same but starts a new temporary branch with the next item, deleting the whole temp tree on a real commit. Or maybe leave the temp branches around until manually cleaned up, but provide a UI to do that in an all-at-once sweep.

The obvious challenges seem to be managing temp-branch vs. “real” current branch state in a way that doesn't mess up or get messed up by other tools interacting with the repo (including ha sling things like pulls and other inbound changes to your “real” active branch), and generating non-noise automated commit messages. Advantages would be all the tools you have for VCS history can then be deployed to your edit history without any additional friction.


> The obvious challenges seem to be managing temp-branch vs. “real” current branch state in a way that doesn't mess up or get messed up by other tools interacting with the repo (including ha sling things like pulls and other inbound changes to your “real” active branch), and generating non-noise automated commit messages.

Low-level plumbing commands, if the VCS has them, can help with that, e. g. `git commit-tree`.


Where would you want to use `git commit-tree`?


It’s a convenient shortcut to take an existing commit and append to it a new commit that changes the file tree to any state you want. For example, if you want that new commit to result in the exact same file tree as another existing commit in the same repository, you can tell git commit-tree to make exactly that happen.

It will also just print the new commit hash without moving `HEAD`, which is why it may also be useful in dragonwriter’s use case.

Raymond Chen explains it way better than I do: https://devblogs.microsoft.com/oldnewthing/20190506-00/?p=10...

List of related blog entries: https://news.ycombinator.com/item?id=20620441


Thanks.


Vim has time-based undo which is great, but I always forget about it and need to look up how to use it the few times that I want it.

Having a slider/timeline does sound like an excellent idea. Also just being able to non-destructively go back to a previous state and have a peak without changing the files I'm working on right now would would really well for me I think.

Of course git is an answer to this, but I don't want to think about making a checkpoint.


I have simply enabled "Local History" in NetBeans and can go back (with diff) to any time until 30 days ago (in addition to what Git gives me).


My school’s JS IDE [1](“JavaScript without the bad parts”) has a history feature that is just a scrolling list of thumbnails of the code at every change. The IDE doesn’t have many features overall, but the history is actually very nice and natural and feels like it should be standard in any IDE.

[1] https://www.ocelot-ide.org/


I'm curious, does it actually use the local machine to compile files? Or is the compilation done on some remote instance?


It’s built off of Stopify[1], which compiles “from JS to JS” and runs on the client side.

[1] https://www.stopify.org/


Please don't make me use a slider. IntelliJ has local history, and I have that instinct to Cmd-S after every change. Problem already solved.


What’s funny is that Ctrl+S does absolutely nothing in IntelliJ-based IDEs - all changes are saved automatically there, but we all still press it, because old habits die hard - and IntelliJ creators know this and don’t bind Ctrl+S to anything by default.


I remember using eclipse and a plugin for cfml years ago. If I help undo for a couple seconds to see old version and then redo for a couple seconds there was a 50/50 chance it would corrupt the code. If you haven't saved your latest changes or accidentally saving the corrupt code and have to compare to repo it gets old quick. Switched to Sublime.


I routinely use copypasta and a quick rename as basically a stash/throwaway commit without breaking editing flow. It doesn’t stick around til a real commit because my tools yell about unused references. But it lets me keep and cross-reference a previous approach without undo history or going into version control mode when I’m exploring options.



In emacs, it's called magit-wip-mode.


I learned very early in to "Commit early, commit often!"

That being said, I don't think the reasons viven are honest: > When asked why they did this, they revealed they were trying to view some intermediate state of the code in the middle of a change.

Looking at some GitHub repos, with one single commit, I believe many devs are afraid someone could see all their trial and error attempts and judge them.

I oersonally am too lazy for the undo redo nonsense. Many of my final commits are mostly cleaning up the code. E.g. Refactoring so my code is like reading a story.

I don't have an issue with people thinking I have OCD or something similar. My bigger issue is if I ever have to touch my code again, to waste time on trying to understand it. Or even worse, someone else not understanding it and asking me for help.


> I believe many devs are afraid someone could see all their trial and error attempts and judge them

This is certainly part of the reason I do it. Sometimes I'll start repos anew, without the "mistake" commits and branches, and add that version to my Github before I send the link to a potential employer.


https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History

https://git-scm.com/docs/git-merge

C-f squash

You can compress a series of commits into one with squash in git.


I want to add one tool that (looks like) still has no mention there: clipboard manager.

Clipboard manager allow you to store all clipboard history.

Thats not the best solution, but at least for me with it I able to copy some original and intermediate states of code few times and return to it anytime in the future.


Advantages to this approach to referring to an older version of code that alternatives may not have:

* works well entirely through keyboard shortcuts (especially useful when editing code)

* the shortcuts are standard so you already know them and have them committed to memory

* works across editors

* high granularity of control over exactly which version of the old code to refer to

Undo trees seem like the best alternative, since it can be done strictly as an extension to convention undo/redo... a little HUD can let you see your current location in the tree and the normal keys can be used to navigate it, with the addition of one more shortcut to change branch.

The "separate window with panning control" suggested in the article seems awful to me.


Before Microsoft bought GitHub and killed Atom, they were working on Xray[1] & Memo[2]:

> Memo is an operation-based version control system that tracks changes at the level of individual keystrokes and synchronizes branches in real time.

[1] https://github.com/atom-archive/xray [2] https://github.com/atom-archive/xray/tree/master/memo_core

So, it would fix this problem, and also enable real-time collaborative editing, had it been completed.


M$ killed Atom??

It ostensibly seems fine, updated today. https://github.com/atom/atom


This sounds like it could be an incredibly useful feature in IDEs. It reminds me of the timeline in Fusion 360 [0] which allows you to scroll back through time, make a change to a feature in ‘past’, and then jump back to the present state of the project.

[0]: https://www.autodesk.com/products/fusion-360/blog/wp-content...


Tangential to this. Does anyone know of an app/service like etherpad, but for handwritten notes?

Would love to be able to replay notes that I write with the pencil on the iPad.

I also feel it would enable a cool way of storytelling in which you can play with the way you write so readers get to experience the text in different ways. Imagine that as you are reading something all of a sudden the letters start getting bigger, or thicker, or star changing color. Anyway, just replaying handwritten notes would be nice.


https://github.com/lovasoa/whitebophir and it can be embedded in a pad for both text and hand written notes.


Thank you, looks great. Does it have a replay feature? Didn’t see a reference to it on the readme or the wiki.


I’ve never heard someone talk about this before, I’m sure developing good development techniques and relying on editor auto complete would alleviate most of those listed issues.


Undo/redo doesn’t affect the clipboard, so I find myself often undoing back to some state, using cmd-c to grab something I wanted, then redoing to the present.


Does anyone know of something similar that lets me watch data "flow" through my code at a human readable pace?

I dont use ctr-z, Its surprising to me that people code this way, but I do tend to spend a lot of time thinking about how my data would flow through my current code set. It would be great if I could easily and almost cartoonishly visualize it. Anything like that out there? gdb is just to abrubt an harsh to use.


NetBeans have something very similar to this. You can get a diff on all changes to a file, both from your commits and from just a timeline. You can click to transfer old code to the new version blockwise as needed. No need for copy-pasting.

It's set to delete history after 7 days but can be changed to never. Haven't realy used it much last few years though so don't know if it's still there.


The same feature is available in IntelliJ IDEA and likely in the other JetBrains IDEs.


so how do you handle the "undo make a change and no longer can redo" conundrum? Just have the branches in the tree of edits laid out flat in chronological order so that in order to bring changes back from the past you go to an earlier time, copy and then paste the changes to the alternative branch in the present?

Idk I do think version control is probably a much better tool to sanely manage rapid workflow when working at scale on huge projects, I just think the problem is that there too many features and far to few people who actually take the time to learn how to use them effectively.

To prove my point, the awk tool is super powerful and solves many "quality of life" problems developers begrudgingly deal with on a daily basis over and over again.

Now lets see a raise of hands of people in here that can actually write an awk script one liner without referring to the manual?

We have all these wonderful tools but most of us are too spoiled by having a mouse and powerful editors that we don't ever learn how to use them to improve our performance as developers.


a workflow i'm pretty hooked on is:

using vim w/ git-gutters plugin, which shows the state of a line (+-~), and so you can stage and unstage these hunks with mappings.

so maybe you unstage a whole hunk to see what its original state was, but then just do an undo to return it. this is technically many undos in one

there's some more conveniences here, but this is one that stands out regarding "many undos ago"


My primary editor is VSCode, but I've got Sublime open as well. When making changes part way, I just cmd+x -> cmd+v to Sublime. This way if something doesn't work out I don't need to undo to whatever it was that was working.

If operating on multiple files I can have the snippets in the same tab, or different tabs in Sublime.


Check this out then. It’s like git-timemachine for Emacs but for VS Code

https://marketplace.visualstudio.com/items?itemName=bee.git-...


It's not so hard, the author is just using the wrong OS. VMS saves new files versions for every disk write and now that it runs on x86 [1], there is little excuse to not use it. :D

[1] https://news.ycombinator.com/item?id=26169179


I don't have this much at all.. But I do stage and commit pretty aggressively (with the occasional file-backup when I 'm lazy to branch and know I'm going to frewheel a bit) and look at local git diffs when I want to look back. Of course rebasing/squashing before pushing to a PR or merging.


I used to work on Cloud9 IDE and it had a feature like this. It's now probably still in AWS Cloud9. It's tremendously helpful and magical when you have it. Having a

We had considered ways to expand on this feature for educational purposes by adding audio to create a tutorial but never could allot resources for this.


While it might get out of hand from normal undo/redo code flows, Drama (a design prototyping app similar to Principle) has an awesome history UI with branching.

https://www.drama.app/documentation/history


Oh, this is a neat idea. I can easily do this for everything using hooks. Just a minimal GUI, target window to hook into (handle to it) and then every keystroke into its own. Combine this with a multiple view/diff and you have a good version control on the fly.

Be right back, I'll go implement it right away.


For this I use:

a) git add and git diff --cache

b) :w and git diff (or git diff HEAD to compare a+b)

c) nmap <leader>d :w !diff -u % -<CR>

so that’s three diffs / four different stages.

If I need more I also don’t refrain to use sequences of temporary commits I rework with git reset master + git add -p and/or git rebase -i

So overall there’s no fear of early commitment to be had.


I found myself more and more often push a Pull Request just to have better visualization of the changes.


I thought this was just a thing I did because I'm not a rockstar coder. I guess I'm average.


> ... introduces a problem of premature commitment, which forces the developer to decide to save an intermediate version (or not to save it) before they have the information needed to make the decision

Why is that a problem? Just commit, as many times as you want, to a throwaway branch.


Auto History for Visual Studio is excellent for this use case.

https://devblogs.microsoft.com/visualstudio/auto-history-ext...


Code smell, poorly factored. I almost never do this, but when I do, it's almost always because the code is already messy.

This can vary by problem domain and language. I see some examples of regex below and I can agree with that, because regex is difficult to read.


> Why is it so hard to see code from 5 minutes ago?

Because you aren't using Vim: ":earlier 5m"


I would love to see this in my WebStorm! I often did premature commitment and then I used undo and redo. Then I learnt that it is hard to code. Now I am slower in decisions which does not quite help me be productive but calms down premature commitment.


> I'll Ctrl-A and Ctrl-V this into a new tab before it gets too messy, and then I can put the window beside my editor to use as a reference. I even observed a professional developer with 20 years experience doing this!

This seems... fine?


iTerm2 has an Instant Replay feature in case you want to see exactly what you were doing on screen in the past.

https://iterm2.com/features.html


I always think about that thing John carmack said that when working on quake he could rewrite the whole thing from memory. My memory is terrible. I think to be really good takes an excellent memory. :(


This would actually work for text editing in general. When I’m writing essays, sometimes things don’t come out right and it helps to compare them with newer attempts.


You can just use emacs and undo-tree. You can also save the undo history in a file and you will have all the file history forever.


I just searched and couldn't find a plugin for Atom called Yestercode. If there was one, I'd definitely use it.


It isn’t public [yet]. You should check out our plug-in CodeRibbon though! Available for Atom.

https://utk-se.github.io/CodeRibbon/


> It isn’t public [yet].

Ah hah. I'd like to use the code history slider plugin too. You've done well to identify a common problem many of us face.


Why is this a problem worth solving? I don't recall this being a bottleneck in my recent work.


wow, so my assumptions that coders dont design and draft solutions, but type in furiosly code to make it work where true. i recently begin to believe the teaching how to create software is fundamentaly flawed. now i have more evidence. wow.


This is awesome, I would use it. Maybe make it a visual studio code plugin?


I came here to say this also.


Eclipse keeps local history and you can see changes as you made them.


Jetbrains IDEs have a local history which helps for that.


vim has a very versatile undo function, including undo the last n minutes/hours/days of edits. it also has undo branches. e.g. `:earlier 5m`


I've not seen it mentioned yet so I'd be remiss not to link to undo tree [1] for vim. I tend to put my list of plug-ins on a diet over time, but undotree is one I always kept! Having a visual of the history (like in the article) make it much easier to do a quick back and forth through time.

Also for fun, consider having your scroll wheel traverse time instead of space [2].

[1] https://github.com/mbbill/undotree [2] https://xkcd.com/1806/


vim users just can't stop winning


I refuse to believe anyone worthy of calling himself a developer takes screenshots of the screen to save intermediate code changes


IDEAs local history to the rescue


intellij does this quite well. Gives a browsable timeline of diffs, even.


in vim: :earlier 10m, or short :ea 10m and :later 2h, or short :lat 2h


Piazza represent


If looking at a source browser of a previous version of your code is not adequate to understanding how it worked, such that you wish to "rewind" to that version and run it, then your code is very poorly written.

It is indeed difficult sometimes to understand our own code some time after it is written, but by following good practices (ideally functional, with mostly pure functions), it should be readable.

I will venture a (biased) guess that one of the biggest challenges to understanding the code was the mutation of objects in the process. This is the #1 reason why functional programming is superior. With FP, you can grok what a function does and then comfortably forget about how it does it. It takes input, it produces output. Period. As long as you agree with the transformation algorithm, you can reduce that complexity to a one-line description.

Even in non-FP-first languages, you can usually write pure functions. Some scenarios have enough performance demands that you must mutate things, but probably most situations can be approached with FP styling.

All this rewinding and undoing is a distraction from the real problem. The real problem is that "it smells". It smells like a level of complexity that should not be.


Are you certain the requirements can always be met without complexity?

Have you ever had to build an os kernel, a database, an mmorpg server, a realtime 3d game engine, a production compiler, jit, or garbage collector?


I never said there was no complexity. The idea is to manage it well.

And sure, it may be long ago, but I have some code in a AAA game. Even C++ with hundreds (*edit) of files, we got along just fine without rewind.


Nope. Sometimes I’m rewriting something and I just want to see what the old code looked like before I changed it. There’s nothing smelly about that.


That's what a git browser is for. It's not like your code is gone forever, unless you are undisciplined.


I certainly don’t commit things every five minutes, so this doesn’t help.


In my 30 years of professional programming, and even with my admittedly declining short term memory, I have never felt lost without access to versions of code from 5-15 minutes ago.

Not to be harsh, but I really think that this indicates some fundamental approach issue rather than a tooling problem.

If it cannot be written on paper (or tablet note with pen) as rough pseudocode... or even drawing boxes and arrows for the visually-inclined, then the problem is not understood. Take what is understood and codify it, pushing the complexity and the unknowns to the edges. At least that reduces the complexity.

One aid I do lean on heavily though is a REPL. Load the code and do some interactive work with it. Inspect the data, test some transformations, and then write code to do what works. That's my current approach to situations that I cannot immediately and accurately write in one pass.


> One aid I do lean on heavily though is a REPL. Load the code and do some interactive work with it. Inspect the data, test some transformations, and then write code to do what works. That's my current approach to situations that I cannot immediately and accurately write in one pass.

Honestly, this sounds mostly like what you're chastising, except you're doing it outside of the main workflow.


(Ok, looking at your profile and your background, I must be missing something. You surely don't need 5 or 10 minute history of code to know what you are doing.) Please help me understand what I am missing from this conversation!


You know what? Most of these ‘disagreements’ about coding workflow and tool choice in which one person is telling another person that they’re Doing It Wrong boil down to different strokes for different folks.

This seems to be a fine example of that.

(Though I must admit that consternation about how often someone presses Ctrl-Z is a bit lower level than I’m used to seeing.)


A common example from my work: I’m doing layout code, so I need to clean up some constraints that are broken. Often this a fairly manual process where I change some code and run it to see how it does, and then adjust the code as needed. Sometimes I need to change my strategy (instead of manually pinning this view, I can put it in a stack view…) and this requires me to change a couple lines of code. Then I run it and it breaks in a way that makes me realize that this direction was a dead end, so now I want to go back to where I was five minutes ago. Or, sometimes this is the way I wanted to go but in the process of rewriting it I forgot to carry over a constraint, so I want to see how I was doing it before and pull out a line or two so I can include it in the new code.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: