| ▲ | tom_alexander 11 hours ago |
| I'm giving jj a try but one aspect of it I dislike is edits to files are automatically committed, so you need to defensively create empty new commits for your changes. As in, want to browse the repo from a commit 2 weeks ago? Well if you just checkout that commit and then edit a file, you've automatically changed that commit in your repo and rebased everything after it on top of your new changes. So instead you create a new branch off of the old commit and add an empty commit to that branch so any file changes don't end up rewriting the past 2 weeks of history. git is much nicer in that I can do whatever I want to the files and it won't change the repo until _I tell it to_. |
|
| ▲ | smackmybishop 11 hours ago | parent | next [-] |
| Just don't ever use `edit`, use `new` instead; then your changes are tracked without making a mess. I think that's much nicer than juggling stashes in git. |
| |
| ▲ | embedding-shape 10 hours ago | parent | next [-] | | > Just don't ever use `edit`, use `new` instead As a git-ist (?), if I'd ever move away from git, it would be to avoid tooling that has idioms like this (like git too has), if `jj` just gonna surface a bunch of new "bad ideas" (together with what seems like really good ideas), kind of makes it feel like it isn't worth picking up unless you don't already know git. | | |
| ▲ | rtpg 35 minutes ago | parent | next [-] | | "jj new" is like "I'm going to make some changes", then you do "jj squash" to say "OK these look good enough to commit". If you work this way you're "always" in a WIP state. And if you switch to another spot you won't lose your work, cuz it's persisted. The end result if you work like this is you don't need stashing, since you get "free" stashing in your commit tree, which is more likely what people want (and if it's not... rebasing is easy so just move the node/`jj duplicate` it!) `jj edit` exists but I think it's just not what people want in the default case at all. In exchange: rebasing "just works", stashing is not a thing to think about, and you don't lose your work | |
| ▲ | saghm 9 hours ago | parent | prev | next [-] | | The idiom here is use `edit` if you want to edit a commit, and use `new` if you want to make a new commit. This works identically whether you specify the commit via branch name or commit id. I'm not sure why people are saying not to use `edit` ever. It's basically just a shorthand for staging and amending changes in an existing commit, and there's still a use case for that; it's just not "I want to see the changes on this old branch". | | |
| ▲ | embedding-shape 9 hours ago | parent | next [-] | | > Just don't ever use `edit`, > The idiom here is use `edit` if you want to edit a commit You know, you guys have fun with that, I'll continue using git which (probably) has the same amount of warts, but I already know them. I'll continue to refer new VCS users to jj, seems a lot easier to learn, but really don't have the interest to re-learn a bunch of ever-changing idioms. | | |
| ▲ | saghm 8 hours ago | parent | next [-] | | I disagree with the people saying "never use edit". There are plenty of people saying conflicting things about git too, and I'd argue that understanding edit versus new isn't anywhere close to the level of wart that having to get people to agree on merging versus rebasing. Like you said though, have fun with that! | |
| ▲ | sswatson 6 hours ago | parent | prev [-] | | No system is perfect, but there's nothing wrong with `jj edit` and `jj new`. Both commands are completely reasonable and do what you think they would do. |
| |
| ▲ | joshuamorton 9 hours ago | parent | prev [-] | | I think it's because it's easy to make annoying mistakes (still easy to fix with undo) with edit. And it gains relatively little over new+squash. Edit is a useful power-feature, but I think for a novice, "never use it, only use the more well understood workflow of new+squash" is a good heuristic. |
| |
| ▲ | dzaima 10 hours ago | parent | prev | next [-] | | `edit` is still useful; just, for ..editing (!) something, instead of viewing it. If you have some unfinished changes at the tip and want to temporarily checkout something 2 weeks ago, you `jj new` to there (similar to `git stash; git switch whatever`), and then later `jj edit your-old-tip` to go back (equivalent to `git switch main; git stash pop`; I think `jj edit` being an extended replacement for stash-popping things is a reasonable way to think about it). (and if you don't have any uncommitted changes, you always `jj new`) jj also has a concept of immutable commits (defaulting to include tagged commits, and trunk at origin, which it'll disallow editing as a layer of defense) | |
| ▲ | surajrmal 10 hours ago | parent | prev [-] | | jj edit has good use cases, but it's not the default command you need. For instance, say you were working on some changes but had to change branches for a few minutes to do something. If you didn't manage to create a commit and want to go back to the previous staging area, you would use the jj edit command rather than jj new. It's very intuitive in my experience, something I can't say is true for managing git commits (unless you've spent years forcing it into muscle memory). I never need to run jj help. I run help commands with git all the time. |
| |
| ▲ | VMG 11 hours ago | parent | prev [-] | | ... unless you actually want to edit a change! | | |
| ▲ | throawayonthe 11 hours ago | parent | next [-] | | well, you can do jj new <revision>, make your edit, and then do jj squash which will add the changes to the prev revision i do this for example when i want to see a specific edit highlighted in my editor, it's a nice workflow i think | | |
| ▲ | Aeolun 11 hours ago | parent [-] | | This is exactly how someone explained Git to me 12 years ago or so, and I’ve finally wrapped my head around it. Not changing now. | | |
| ▲ | mh- 7 hours ago | parent | next [-] | | If I'm understanding the thread correctly, I have a git alias to `git commit --amend --no-edit`, for exactly this workflow. When I'm hacking on something locally and want to just keep amending a commit. I only ever do this if it's HEAD though. | | |
| ▲ | steveklabnik 6 hours ago | parent [-] | | Yes, one way to think about jj in a sort of low-level way is that every jj command does the equivalent of that, every time. (You can also set up watchman and have that happen on every file change...) |
| |
| ▲ | hacker161 8 hours ago | parent | prev [-] | | [flagged] |
|
| |
| ▲ | BeetleB 10 hours ago | parent | prev | next [-] | | I go back and forth between the two approaches, but because of the whole "accidentally made some temporary changes and now it's a pain to separate/undo them because not all changes were temporary", I also usually do a jj new and then jj squash. | |
| ▲ | arccy 11 hours ago | parent | prev | next [-] | | still use new, and then squash your changes in. that way you can actually see what changes you made | |
| ▲ | incognito124 11 hours ago | parent | prev [-] | | then you `new` & `squash` :) |
|
|
|
| ▲ | smweber 11 hours ago | parent | prev | next [-] |
| jj edit is the biggest jj footgun I can think of, as other comments said just use jj new. But also if you do accidentally edit or change something jj undo works surprisingly well. I found when using jj it worked best for me when I stopped thinking in commits (which jj treats as very cheap “snapshots” of your code) and instead focus on the “changes”. Felt weird for me at first, but I realized when I was rebasing with git that’s how I viewed the logical changes I made anyway, jj just makes it explicit. jj auto-rebasing doesn’t matter until you push changes, and once you do it marks them immutable, preventing you from accidentally rebasing changes that have been shared. |
| |
| ▲ | saghm 10 hours ago | parent | next [-] | | > jj edit is the biggest jj footgun I can think of Honestly, this is only because `git checkout` is so convoluted that we've collectively changed our expectations around the UX. "checkout" can mean switching to another branch (and creating it if you specify a flag but erroring if you don't), looking at a commit (in which case you have "detached HEAD" and can't actually make changes until you make a branch) or resetting a file to the current state of HEAD (and mercy on your soul if you happen to name a branch the same as one of your files). Instead of having potentially wildly different behavior based on the "type" of the thing you pass to it, `jj edit` only accepts one type: the commit you want to edit. A branch (or "bookmark", as jj seems to call it now) is another way of specifying the commit you want to edit, but it's still saying "edit the commit" and not "edit the bookmark". Unfortunately, the expectation for a lot of people seems to be that "edit" should have the same convoluted behavior as git, and I'm not sure how to bridge that gap without giving up part of what makes jj nice in the first place. | | |
| ▲ | nightski 9 hours ago | parent [-] | | It's not "wildly" different behavior based on the thing it's pointing to. In all 3 cases, the command is pointed at a commit and the behavior is the same. Once you know that branches/HEAD are just named pointers to commits, then it becomes obvious you are always just working on commits and branches/ids/HEAD etc are just ways of referencing them. | | |
| ▲ | MrJohz 5 hours ago | parent | next [-] | | But branches are not just named pointers to a commit. If they were, then checking out the pointer would be the same as checking out the commit itself. But I can check out a commit and I can check out a branch and depending on which I've done, I'm in two different states. Either I'm in branch state, where making a commit bumps the branch pointer and means the commit will be visible in the default log output, or I'm in "detached head" mode, and making a commit will just create a new commit somewhere that by default is hidden into I learn what a reflog is. And the kicker is: these two states look completely identical - I can have exactly the same files in my repository, and exactly the same parent commit checked out, but the hidden mode changes how git will respond to my commands. In fairness, none of this is so difficult that you can't eventually figure it out and learn it. But it's not intuitive. This is the sort of weirdness that junior developers stumble over regularly where they accidentally do the wrong kind of checkout, make a bunch of changes, and then suddenly seem to have lost all their work. This is one of the ways that I think the JJ model is so much clearer. You always checkout a commit. Any argument you pass to `jj new` will get resolved to a commit and that commit will be checked out. The disadvantage is that you need to manually bump the branch pointer, but the advantage is that you don't necessarily need branch pointers unless you want to share a particular branch with other people, or give it a certain name. Creating new commits on anonymous branches is perfectly normal and you'll never struggle to find commits by accidentally checking out the wrong thing. | | |
| ▲ | fragmede 5 hours ago | parent [-] | | > these two states look completely identical No they don't. As you noted, one state is "detached head" and any competently set up shell PS1 will tell you that, or that you're on a branch by displaying the name of the branch vs the commit. > Creating new commits on anonymous branches is perfectly normal Sorry, that that's an example of more intuitive behavior on jj's partc, you've lost me. I've done that intentionally with git, but I know what I'm doing in that case. For someone new to version control, committing to an unnamed branch doesn't seem like a desired operation no matter which system you're using. What's wrong with requiring branches to be named? | | |
| ▲ | steveklabnik 5 hours ago | parent | next [-] | | > For someone new to version control, committing to an unnamed branch doesn't seem like a desired operation no matter which system you're using. We have data on this! I can't cite anything public, but companies like Meta have to train people who are used to git to use tools like sapling, which does not require named branches. In my understanding, at first, people tend to name their branches, but because they don't have to, they quickly end up moving towards not naming. > What's wrong with requiring branches to be named? Because it's not necessary. It's an extra step that doesn't bring any real benefits, so why bother? Now, in some cases, a name is useful. For example, knowing which branch is trunk. But for normal development and submitting changes? It's just extra work to name the branch, and it's going to go away anyway. | | |
| ▲ | fragmede 3 hours ago | parent [-] | | Fascinating. The benefit it brings is you can map the branch to its name. Of the, say, 10 branches you've got checked out, how do you know which branch maps to jira-123 and which one maps to jira-234, or if you're using names, which anonymous branch maps to addFeatureA or fixBugB? More to the point though, what tooling is there on top of raw jj/git? Specifically, there's a jira cli (well, multiple) as well as a gh cli for github as well as gitlab has one as well. When you call the script that submits the branch to jira/github/gitlab, how does it get the ticket name to submit the code to the system under? Hopefully no one's actually opening up jira/github/gitlab by hand and having to click a bunch of buttons! So I'll be totally transparent about my bias here in that my tooling relies on the branch being named jira-123 so it submits it to jira and github from the command line and uses the branch name as part of the automated PR creation and jira ticket modification. | | |
| ▲ | steveklabnik 2 hours ago | parent | next [-] | | > Of the, say, 10 branches you've got checked out, how do you know which branch maps to jira-123 and which one maps to jira-234, or if you're using names, which anonymous branch maps to addFeatureA or fixBugB? The descriptions of the changes. I shared some jj log output in another comment, here it is with more realistic messages, taken from a project of mine: @ vvxvznow
│ (empty) (no description set)
│ ○ uuowqquz
├─╯ Fix compiler panic in error rendering for anonymous struct methods (rue-fwi9)
│ ○ uvlpytpm
├─╯ Stabilize anonymous struct methods feature
◆ lwywpyls trunk
│ Fix array return type unification in type inference
That (rue-fwi9) is the equivalent of jira-123, if I super care about it being obvious, I might put it in the message. But also, I might not, as you can see with the other two. You could also pass flags to see more verbose output, if the first line isn't clear enough, but in general, the convention for git as well is to have that short summary that explains your change, so if it's confusing, you probably need to do better on that.> Specifically, there's a jira cli (well, multiple) as well as a gh cli for github as well as gitlab has one as well. These systems do require branches in order to open a pull request. In these cases, I use `jj git push -c <change id>`, which will create a branch name for me, and push it up. This is configured to produce a branch name like steveklabnik/push-mrzwmwmvkowx for a change with the id mrzwmwmv, and ultimately, it's still easier to name locally with m or mr depending on if the prefix is ambiguous. That said, from there I do usually just click the button and then "open pull request" on GitHub, but like, all of these tools (gh is the only one I've used, but I can't imagine that the others do not work, since ultimately, it's a git repo) just work if you want to use them. Other systems do not even require a branch to submit, and so you don't even need to do this. I would say "submit mr" and it would return me the URL for the created change request. Gerrit does this on top of plain old git. > how does it get the ticket name to submit the code to the system under? I haven't worked with Jira in a long time, but with GitHub, if I make a change that fixes issue 5, I put "Fixes #5" in my description, and when the PR is created, it updates ticket #5 to link the PR to that change automatically, no other process needed. | |
| ▲ | Zizizizz 2 hours ago | parent | prev [-] | | You can name branches in JJ too, they're just called bookmarks. git checkout main
git pull
git switch -c jira-234
...
git commit
git push -u origin main jj git fetch
jj new main
...
jj commit
jj b(ookmark) c(reate) jira-234 -r @-
jj b(ookmark) t(rack) jira-234@origin
jj git push | | |
| ▲ | steveklabnik 2 hours ago | parent [-] | | Right, this is a good point: you can if you want to, or if you're working with a system that requires them. Just in practice, anonymous branches end up feeling very natural, especially during development, and especially if your code review tooling doesn't require names. |
|
|
| |
| ▲ | MrJohz 4 hours ago | parent | prev | next [-] | | They look identical to people who don't know what to look for, and who don't realise that these two states are different, which is the key thing. You can also distinguish them by running `git status`, but that's kind of the point: there's some magic state living in .git/ that changes how a bunch of commands you run work, and you need to understand how that state works in order to correctly use git. Why not just remove that state entirely, and make all checkouts behave identically to each other, the only difference being which files are present in the filesystem, and what the parent commit was? What's wrong with unnamed branches? I mean, in git the main issue is that they're not surfaced very clearly (although they exist). But if you can design an interface where unnamed branches are the default, where they're always visible, and where you can clearly see what they're doing, what's wrong with avoiding naming your branches until you really need to? I think this is the key thing that makes jj so exciting to me: it's consistently a simpler mental model. You don't need to understand the different states a checkout can be in, because there aren't any - a checkout is a checkout is a checkout. You don't need to have a separate concept of a branch, because branches are just chains of commits, and the default jj log commands is very good at showing chains of commits. | | |
| ▲ | fragmede 3 hours ago | parent [-] | | My command looks like either: fragmede@laptop:(abranch)~/projects/project-foo$
or
fragmede@laptop:(abcdef)~/projects/project-foo$Depending on if abranch is checked out, or abcdef which may be HEAD of abranch is checked out. If you're having to run `git status` by hand to figure out which of the two states you're in, something's gone wrong. (That something being your PS1 config.) If people are having trouble with that, I can see why switching to a system that doesn't have that problem, it just that it doesn't seem like it should even be problem to begin with. (It's not that it's not useful to have unnamed branches and to commit to them, just that it's not a intro-to-git level skill. Throwing people into the deep end of the git pool and being surprised when some people sink, isn't a good recipe for getting people to like using git.) > What's wrong with unnamed branches?
As you point out, those commits kinda just go into the ether, and must be dug out via reflog, so operationally, why would you do that to yourself. Separate from that though, do you "cd" into the project directory, and then just randomly start writing code, or is there some idea of what you're working on. Either a (Jira) ticket name/number, or at least some idea of the bug or feature you wanna work on. Or am I crazy (which I am open to the possibilty) and that people do just "cd" into some code and just start writing stuff? VCS aside, nothing worse than opening Google docs/a document folder and seeing a list of 50 "Untitled document" files an my habit of naming branches comes from that. Even though I'm capable of digging random commits out of reflog, if all of those commits are on unnamed branches, and have helpful commit messages like "wip" or "poop", figuring out the right commit is gonna be an exercise in frustration. As long as you've got something that works for you though, to each their own. I've been using too long for me to change. | | |
| ▲ | MrJohz 18 minutes ago | parent [-] | | The only thing that changed in the two things you wrote was `ranch` -> `cdef`. Every other part of that PS1 output was the same. Now put yourself in the shoes of a git novice and ask yourself if you'd always notice the difference. At least from my experience, they often don't, especially if they're concentrating on something else, it if they're using an IDE and the visual information about which branch/commit is checked out. I don't think you're crazy, I think you're just too used to this sort of stuff to remember what it was like to still be learning git. When I say people make these sorts of mistakes, I'm thinking about real colleagues of mine who have made exactly these mistakes and then panicked that commits suddenly had disappeared. Similarly, I think to you, unnamed branches feel like something complicated because in git that are. Git makes it very easy for commits to seemingly disappear into the ether, even though they are still there. But in jj, they don't disappear - they remain very visible, and the log UI shows them in a way that makes it clear where they come from. The default log UI is something like git's --graph output, which means you see how the different commits interact with each other. I really recommend having a look at the output of `jj log`, because I think then it'll be a lot clearer what I mean when I say that it's not hard to figure out what the right commit is. |
|
| |
| ▲ | tom_alexander 4 hours ago | parent | prev [-] | | > any competently set up shell PS1 will tell you that I certainly hope your shell is not running `git` commands automatically for you. If so, that is a RCE vulnerability since you could extract a tarball/zip that you don't expect to be a git repository but it contains a `.git` folder with a `fsmonitor` configured to execute a malicious script: https://github.com/califio/publications/blob/main/MADBugs/vi... | | |
|
| |
| ▲ | saghm 8 hours ago | parent | prev [-] | | > In all 3 cases, the command is pointed at a commit and the behavior is the same echo "something" >> foo.txt
git checkout foo.txt
What's the name of the branch this is pointed at? If I have to run another git command to find out, then it's not "pointed" at it. | | |
| ▲ | webstrand 8 hours ago | parent [-] | | If you don't provide it a <tree-ish> it reads from the index (staged files). So you're right its not really pointed anywhere, since the index isn't a ref. | | |
| ▲ | saghm 7 hours ago | parent [-] | | That's my overall point: the argument itself (with respect to the current state of the repo) is what determines the behavior. I don't think this is anywhere close to as intuitive as commands that only ever accept one "type" of argument (and erroring if it's different). | | |
| ▲ | nightski 6 hours ago | parent [-] | | I stand corrected by this one scenario, but I’ve been using git for over a decade and never found that useful. Just don’t use checkout on a file path, there is no need. | | |
| ▲ | saghm an hour ago | parent | next [-] | | "Just don't accidentally do things wrong" is also the way to avoid null pointer errors, type mismatches in dynamically typed languages, UB in C/C++. It works, until it doesn't, and in practice that happens pretty quickly. Personally, I like things that have proper safety checks. | |
| ▲ | sswatson 6 hours ago | parent | prev | next [-] | | I find this kind of advice to be a more scathing indictment of an interface than a critic could ever muster: asking users to forego available functionality so that some sense of order can be imposed. | | |
| ▲ | tom_alexander 5 hours ago | parent [-] | | < glances around at all the people telling me to never use `jj edit` > | | |
| ▲ | dwattttt 2 hours ago | parent | next [-] | | That goes in the same bucket as rebase. Until you know what it does, you'll be fine avoiding it. Since people are sharing their experiences and my recent one is relevant to edit, I'll go: Working on a feature recently, I ended up making 3 changes ("commits") on top of each other and hopping between them via jj edit. The first change wasn't feature specific, it was extending the base project in preparation. The second change just added a doc describing all the changes needed for the feature. The third change removed the doc as parts were implemented, bit by bit. As I progressed on the third change & found stuff I'd missed at the start of this process, I jumped back to edit the first change (maybe I had a bug in that base project extension) and the second change (oh hey, I found something else that needed to be done for the feature). It sounds crazy compared to a git workflow, but at the end of the process I have 3 changes, all tested & working. If I was doing this with git, I'd have to rebase/squash to get the final changes into a neat clear history. | |
| ▲ | smackmybishop an hour ago | parent | prev | next [-] | | I suggested that since you seemed really concerned about editing the commit that you just told it to edit. Use 'edit' all you want if your goal is to edit commits, otherwise 'new' does what it seems like you're expecting... | |
| ▲ | baq 3 hours ago | parent | prev [-] | | edit is useful and there are good reasons to use it, 'never use edit' is like 'never use goto' i.e. false - but if you're just starting out, jj new/jj squash is the way to go indeed. (my particular favorite reasons to use jj edit are git-native tools which expect to work with uncommitted files e.g. autoformatters, linters, etc. which have been scripted in CI/dev workflows such that they cannot accept a list of files as params) |
|
| |
| ▲ | hollowcelery 4 hours ago | parent | prev | next [-] | | Interesting - I use git checkout constantly, whenever I have a file in another branch or commit that I want to drag into this one wholesale. | | |
| ▲ | saghm an hour ago | parent [-] | | It's a useful thing to be able to do! It just fundamentally shouldn't be under one command. To its credit, git did add `switch` (with `-c` for creating a new branch and `-d` for specifying detached HEAD), but after two decades I can't imagine they'll ever get rid of checkout entirely because it was so fundamental for so long, and as long as its there, it's a loaded footgun with the safety off. |
| |
| ▲ | tom_alexander 5 hours ago | parent | prev [-] | | If you don't run checkout on file paths, how do you undo changes to specific files that you haven't committed yet? Like you've edited but not committed <foo>, <bar>, and <baz>. You realize your edits to <bar> are a mistake. I'd just run `git checkout <bar>` to revert those changes, what do you do? It is also really useful when you realize you want <bar> to be the version from a commit two weeks ago. I guess you could always switch to the branch 2 weeks ago, copy the file to /tmp/, switch back, and copy the file into place, but `git checkout c23a99b -- <bar>` is so quick and easy. Or does this example not fall under the "dont run checkout on a path" since it is taking a treeish first before the path? |
|
|
|
|
|
| |
| ▲ | nomel 10 hours ago | parent | prev [-] | | > preventing you from accidentally rebasing changes that have been shared. I think this ruins it for me then. I push my in-progress work, to my in-progress branches (then git-squash or whatever later, if needed). It makes switching between (lab) computers, dead or not, trivial. Is there some "live remote" feature that could work for me, that just constantly force pushes to enabled branches? | | |
| ▲ | aseipp 9 hours ago | parent | next [-] | | Yes, almost all JJ users do this constantly. Just "track" the particular branch. JJ has an idea that only some commits are immutable, the set of "immutable heads", and the default logic is something like "The main branch is always immutable, remote branches are immutable, 'tracked' remote branches are mutable." In other words, tracking a remote branch removes it from the set of immutable heads. So just run: jj bookmark track myname/somecoolfeature --remote origin
and the default settings will Do What You Want. This is intended as a kind of safeguard so that you do not accidentally update someone else's work.Some people configure the set of immutable heads to be the empty set so they can go wild. | | | |
| ▲ | saghm 9 hours ago | parent | prev [-] | | Nothing stops you from doing the equivalent of `git push --force` in `jj`. The flag is just named differently: `--ignore-immutable`. This is a global flag though, so it's available to all commands, and `jj` requires it whenever you're making changes to immutable commits, even locally. I'd argue that this is one of the killer features of `jj`, since by comparison `git rebase` treats everything the same whether you're squashing your own local commits on a feature branch or messing with the history of `main` in a way that would break things for everyone. |
|
|
|
| ▲ | baq 8 hours ago | parent | prev | next [-] |
| > edits to files are automatically committed this is a core feature and it makes jj possible - you're supposed to get used to jj new and jj squash into the previous bookmarked commit, which you map to the git branch head/PR. IOW you're supposed to work on a detached git head and jj makes this easy and pleasant. |
|
| ▲ | saghm 10 hours ago | parent | prev | next [-] |
| How are you "checking out" the old commit? It sounds like you're using `jj edit`, which I'd argue does what it says on the tin. Switch to using `jj new <branch>` and your problem goes away. |
| |
| ▲ | tom_alexander 9 hours ago | parent [-] | | That avoids the problem for the specific workflow of checking out an old revision (and it was what I was describing with checking out a new branch off the old commit and adding a blank commit to that branch), but another way this design bites me: At work I am constantly jumping around numerous repos because I might be working on repo <foo> but then someone on my team will ask for help with repo <bar>. So I'll turn on screen sharing, open up repo <bar> and I'll type out psuedo-code into <bar> as I'm explaining things to them. So if the last thing I did on <bar> was finish some work by making a new commit, then writing some changes, and then giving it a commit message with `jj desc`, then I am now polluting that commit with the unrelated explanatory psuedo-code. So when switching to a repo I'm not actively working in, I need to defensively remember to check the current `jj status` before typing in any files to make sure I am on an empty commit. With git, I can jump around repos and make explanatory edits willy-nilly, confident that my changes are distinct from real meaningful commits. I guess one way to describe it is: we want to make it easy to make good commits and hard to make bad commits. jj seems to be prioritizing the former to the detriment of the latter. My personality prioritizes rigorous safety / lack of surprises. | | |
| ▲ | kps 8 hours ago | parent | next [-] | | I think you have somehow picked up an overcomplicated workflow, and this is case is actually something that `jj` is much better at. If I'm in the middle of working on <foo> and someone asks about <bar>: `jj new <bar>`. When I'm done (and do whatever I want with those new changes in <bar>, including deferring deciding what to do), I just `jj edit <foo>` and I'm back exactly where I left off. It's a bit like `git stash` without having to remember to stash in advance, and using regular commit navigation rather than being bolted on the side. | |
| ▲ | MrJohz 4 hours ago | parent | prev | next [-] | | I think the right intuition to have with jj is that `jj st` should show an empty change unless you are actively working on something. `jj commit`, as mentioned below, is a good example of this - it automatically creates a new change and checks it out. The "squash flow" also does this well - you use the branch tip as a staging area and squash work into other changes on the branch as you go along. Either way, once the work is finished, there's an empty change at the tip of the branch. This is also supported by jj implicitly - whenever you check out a different commit, if the change you were on is empty, has no description, and is the tip of a branch, it's automatically deleted to clean things up for you. | |
| ▲ | icorbrey 9 hours ago | parent | prev | next [-] | | Fwiw I generally solve this by using `jj commit` instead of `jj desc` unless I'm specifically targeting something that isn't my working copy. Technically it violates the "we want commands to be orthogonal" guideline we use to write Jujutsu (otherwise this would indeed be `jj desc; jj new`) but as a habit it's never let me down | | |
| ▲ | tom_alexander 9 hours ago | parent [-] | | Ah, thanks! That's a command I haven't learned yet, so I'll have to check it out. I learned jj from the tutorial that was posted and I don't think it covered `jj commit` at all. | | |
| ▲ | steveklabnik 8 hours ago | parent [-] | | I didn't cover it for various reasons, but I think it's good to teach now that I've had more time to consider this sort of thing, so the next iteration will likely start by beginning with jj commit. | | |
| ▲ | saghm 7 hours ago | parent [-] | | In a pure `jj` model, commit might not even be necessary as it's own subcommand (since you could easily define an alias for `desc` followed by `new`). We're still living in a world where most people who would consider adopting `jj` are git users currently, so I wonder if starting with `commit` and then following it up with an explanation of "here's how you can change the commit message without needing to make a new commit" and "here's how you can make a new commit without changing the name of the current one" would end up fitting people's expectations better. | | |
| ▲ | steveklabnik 6 hours ago | parent [-] | | Yes, I do think that the latter is correct now. I tend to learn "bottom-up", so I like the new + describe as a way of learning, but people want to jump in and get going with tools, so commit fits that expectation better. | | |
| ▲ | saghm an hour ago | parent [-] | | I'm the same way. I've learned over the years that this ends up being somewhat uncommon though, and one of the harder but more rewarding parts of helping people learn is figuring out where they're coming from and meeting them there. (I'm positive this is something you've been well aware of for a while though, probably longer than me!) |
|
|
|
|
| |
| ▲ | ersatz-matty 9 hours ago | parent | prev [-] | | From your "polluted" snapshot, you can run `jj commit -i` and use the TUI to select only what you want. | | |
|
|
|
| ▲ | alunchbox 9 hours ago | parent | prev | next [-] |
| if you loose an edit jj op log is incredible, I've saved a ton of work more-so now with AI's making mistakes. Also workspaces are super fast compared to git worktree's - same concept, different implementation. I agree, that was a bit of an interesting approach but more-so than not it's been better in DX even though you have to 'unlearn' long term it's been a benefit IMO, but a soft one, not something you can measure easily. |
|
| ▲ | csmantle 11 hours ago | parent | prev | next [-] |
| `jj new` works like `git checkout` most by creating an empty revision on the top. `jj edit` on the other hand resembles `git checkout; [edits...]; git add -A; git commit --amend --no-edit`. |
|
| ▲ | arianvanp 11 hours ago | parent | prev | next [-] |
| You can disable the auto staging of new files since recently which removed the main grype for me |
| |
| ▲ | tom_alexander 5 hours ago | parent [-] | | ooo that will be a nice improvement. So many times I've run `jj status`, then saw a file I wanted gitignored, so I'll edit my gitignore, but the file has already been added to the repo so I have to `mv <file> /tmp/ && jj status && mv /tmp/<file> .` to get the file out of the repo. | | |
|
|
| ▲ | jdkoeck 11 hours ago | parent | prev | next [-] |
| Wow, that’s a total deal breaker to me. Using git may require a complex mental model, but at least it’s not doing anything I didn’t ask for. |
| |
| ▲ | Diggsey 11 hours ago | parent | next [-] | | You would have had to run `jj edit` in order for this to happen, so I think it's a stretch to say you didn't ask for the edit? This is the main difference though: in git files can be `staged`, `unstaged` or `committed`, so at any one time there are 3 entire snapshots of the repo "active". In `jj` there is only one kind of snapshot (a change) and only one is "active" (the current working directory). When you make changes to the working directory you are modifying that "change". As others have mentioned, the equivalent to `git checkout` would be `jj new`, which ensures a new empty change exists above the one you are checking out, so that any changes you make go into that new change rather than affecting the existing one. | | |
| ▲ | jdkoeck 2 hours ago | parent [-] | | Thanks for the explanation! I wish I could edit my comment to reflect the truth. |
| |
| ▲ | saghm 9 hours ago | parent | prev | next [-] | | Using `jj edit` will edit a commit you specify, and `jj new` will make a new empty commit after the one you specify. These work exactly the same whether you specify a commit by branch or by the hash. I'd argue that you're getting exactly what you ask for with these commands, and by comparison, what "checkout" is asking for is much less obvious (and depends on context). We've just internalized the bad behavior of git for so long that it's become normalized. | |
| ▲ | stouset 8 hours ago | parent | prev | next [-] | | `jj edit` is quite literally asking for that. GP is holding it wrong. If you don’t want to edit a commit, don’t ask to edit it. Use `jj new`. | |
| ▲ | 11 hours ago | parent | prev [-] | | [deleted] |
|
|
| ▲ | Jenk 11 hours ago | parent | prev | next [-] |
| This is literally jj's schtick and reason for existing, so I wouldn't be surprised if you decide it is not the tool for you. |
| |
| ▲ | tom_alexander 9 hours ago | parent [-] | | Yeah, that's a very real possibility. On the bright side, jj is git-compatible so at least the two camps can live together in harmony. |
|
|
| ▲ | et1337 9 hours ago | parent | prev [-] |
| Jujutsu has a concept of mutable vs immutable commits to solve this. Usually everything in a remote branch is immutable. To work on a branch, I track it and that makes it mutable. |