Comment by WhyNotHugo

3 months ago

The main things that drives me crazy about jj is that all changes are always staged implicitly. This is what SVN did back in the day, and git was a huge improvement by staging changes explicitly.

I almost always have more changes in my repository that those which I want to include in the next commit. With git, I just add the changes I want. With jj (and svn), there’s not obvious way around it—you have to manually copy-paste changes outside of the repository before committing.

> The main things that drives me crazy about jj is that all changes are always staged implicitly.

That's one way to look at it, but I would encourage you to think about it a bit differently.

JJ does not have a concept of "staging", it only has changes and commits. Yes, it automatically snapshots the workspace commit, but I wouldn't use the workspace commit as your staging area. If you want to do explicit staging use the parent commit (@-) as your staging area. You can move changes from the workspace commit (@) to the staging area (@-) explicitly, just like in Git. And you can "commit" (Git terminology) your staging area by starting a new staging area.

The difference here really is "only" that the workspace, the index, and committed changes are modeled with the same concept. And that is very powerful. Admittedly you have to make an informed decision on how to map your workflows onto the model, but that is what comes with the powerful flexibility that it gives you.

I'm torn on this - I don't know if there's a simple solution.

Having to stage things every time was always a real pain for me (coming from Mercurial).

Having it autocommit is what I need well over 90% of the time. But then there are always those pesky files that for various dumb reasons I'm not allowed to put in .gitignore.

I could disable autoadding files, but life will be worse that way.

Damned if you do, damned if you don't.

  • > I could disable autoadding files, but life will be worse that way.

    ``` [snapshot] auto-track = 'none()' ```

    This is what I do, and I don't think it is worse. I prefer not having all my ignored files auto-tracked when I accidentally go back to a commit without them in the .gitignore

    Some other solutions (which aren't simple at all): - Remove specific files from auto-track - Have a private commit with changes to the .gitignore and work on top of a merge commit - Like last one, have a private commit with the files you don't want to push (+ merge)

    I have it setup where any commit with a description that starts with "IGNORE:" is private.

    snippet from my config: ``` [git] private-commits = ''' description(regex:"(?x) # (?x) enables the x flag (verbose mode) allowing comments and ignores whitespace # see: https://docs.rs/regex/latest/regex/#grouping-and-flags

      # Ignores commits starting with:
      # (case-insensitive) PRIV: or PRIVATE:, or IGNORE:
      ^(?i:PRIV(ATE)?):
      | ^IGNORE:
     ")

    ''' ```

I think the workflow in JJ is a bit different in this case, you can try to have a base commit `jj new -m 'base'`, then create an anonymous commit on top `jj new`, then make some changes in the anonymous commit and when you are ready to send out a PR/MR you squash `jj squash` or split `jj split` and squash what you need in the base commit.

`jj commit -i` (or a lot of commands `-i`) and maybe `snapshot.auto-track="none()"` in the config, to a certain extent is what I use. I used to do the same with mercurial. In practice, I also use `absorb` a lot, that leaves unrelated files and chunks alone.

This is actually the main reason it exists in the first place, so given that fact, you should probably not use it?

jj is actaully great at this since it natively tracks branches which aren't heads correctly, so all rebases and merges work without stashing.