There are just multiple different ways of working. Some ways fit some people's mental models better.
You're not going to get a definitive "jujutsu is better than git" or vice versa. You should accept that some people have no problems with what you've described using jujutsu, and likewise jujutsu users should understand that not everyone can handle jj as well as they can.
Imagine a different thread where jj users take your exact scenario, and complain about solving the problem with git. You wouldn't understand their pain, because it's not painful for you. This thread is the same, just with jj and git reversed.
Personally, I don't see the pain you have. Back when I used git a lot, if I left a branch for a few weeks, I'd forget the name of the branch and would have to list all the branches (and set an alias to sort by and list the last commit dates of each) to discover the appropriate branch name. It's really not all that different from looking at all (recent) heads. Once you get used to this, you stop naming branches - other than to share with others. And when you do share with them, you cannot push (newer) changes because only bookmarked nodes and their parents can be pushed - so just prior to pushing, you advance the bookmark. With the shells I use, it's a few keystrokes before autocomplete/fzf produces the command for me - no mental effort at all.
You definitely wouldn't advance the bookmark with each commit. Only when you need to push.
And oh man, it's so nice not to have to manage all the branches. With git, I'd routinely go and delete old branches to declutter. With jj, there's simply no need to. The same with stashes. It's really nice not having to do that labor, and simultaneously not dealing with long lists.
If this doesn't appeal to you - that's fine. You're not deficient. But understand, nor are those for whom your workflow sucks.
For example, your "not all that different from looking at all (recent) heads" implies that the number of (recent) heads isn't far off from number of (would-be-)branches (i.e. no random offshoot experiments, stashed-away debug sessions; whereas I make many of such continuously (were stashes on git (with occasional grumbles about not being able to stack stashes), regular commits now on jj (maybe with a special-format description, if I bother)));
and that you (even if subconsciously) try to ensure that the head of a branch is always identifiably-representative of the branch (i.e. don't put some random unrelated change at the tip with the idea of "I'll put this in a more proper place when I get back to this").
Effectively, using the full commit graph not as a place where anything potentially-useful can stay, but rather by itself a complete picture, with things not fitting into it going into.. idk, just being abandoned, to be found by looking through the op log? commit IDs saved in an external file? wading through evolog / scanning through `jj show -r xyz/2` etc?
If it is like you say and different people are just inherently more or less suited to different paradigms, then not everyone can be happy.
This whole thread is about working with git coworkers while using jujutsu, and it was in that context that I wrote my comment: Namely that most who prefer jujutsu happily work with their colleagues who use git in precisely the scenario the OP mentions and don't see why the OP finds it painful. The OP and others should accept that reality.
> If it is like you say and different people are just inherently more or less suited to different paradigms, then not everyone can be happy.
If you force everyone to use git, of course not everyone will be happy.
If you force everyone to use jj, of course not everyone will be happy.
Thankfully, the whole point is git and jj users can interoperate without needing to care what the other is using. So yes, not only can everyone be happy, but everyone is happy! This isn't a hypothetical - it's a reality. It's the reason so many use jujutsu at work.
wait what? how does this work?
Many of my stashes are tiny changes that need checked later - effectively, each stash entry is a quick TODO list. At some moment, for example once a major feature is done, I'll go a check every stash'ed entry and decide - maybe it's no longer relevant, maybe I should make a PR out of it, or maybe I should convert them to the branch if it's useful but needs more work. The branches are similar, but on longer scale.
The idea that you don't need to declutter your old stashes/branches seems absurd to me - it's like getting a huge box labeled "misc", and throwing every single thing in there. Sure, it's quick, but that's how you lose the important things you need to do, and find the useless junk instead of the actual thing you were looking for.
Bookmarks aren’t that bad either IMO, especially with the recent addition of `jj bookmark advance`. Curious if you can say more about the particular difficulties you found keeping them up to date?
I often work on something and then switch away to something else. it might be a week before i get back to it, and the name of the branch is a clue as to what the heck I was doing.
Other people often need to check out a branch I'm working on to help. How does anonymous branching help anyone except a solo developer?
I think I misread you, because you were talking about git vs svn in a way that made it sound like jj was a step backward from git as regards branching, and I got confused.
> I often work on something and then switch away to something else. it might be a week before i get back to it, and the name of the branch is a clue as to what the heck I was doing.
Right, but you can still name your branches with bookmarks, you’re just not required to. Personally, I tend to use commit descriptions more than bookmarks to keep track of what I’m working on, but this is a personal choice.
> Other people often need to check out a branch I'm working on to help. How does anonymous branching help anyone except a solo developer?
It’s just nicer as a local workflow, to me at least? I can create throwaway branches without having to come up with a name for them. I end up creating many, many more branches than I did in git as a result, which helps me keep my work better organized and my changes more focused when I submit them for others to review (which at least on GitHub, requires a bookmark of course). This is ultimately psychological because obviously I could just make up a name for the git branch, right? But it makes a big difference for me!
Ah, this is what the description (what git would call the commit message) is for. You can set the description even before you've made any changes.
so with jj i could use a bookmark, ok, but having to manually update that bookmark feels wrong.
In jj, it's the opposite. I start with a change, and I often describe it right away. Branches (bookmarks) come at the end.
You could, in jj, tag a new empty change with a bookmark as soon as you create it. You don't have to advance the bookmark -- that the first change in a sequence of changes is tagged with a bookmark is probably as much information as you need?
i was actually wondering about that. it might. i'll have to see it in action...
Is it easy?
Some later googling revealed `git rebase --onto origin/main theirbranch main` was probably what they needed. Which I’m sure would have come to me quicker if I hadn’t dropped the git cli for jj 2+ years ago.
Personally I’m a huge fan of this approach. If you aren’t, it’s literally just a one-liner (that is trivially made into an alias) to advance a branch name to the most recent revision. And now there’s a feature to do auto-advancing if you want it.
Why is it this way? Because jj is designed around revisions being constantly mutated.
In git, when I make a commit, I am typically signaling that that a chunk of work is complete. Not always, but usually. In most jj workflows, revisions are mutated constantly during development. A revision being made on the tip of a branch is rarely a signal that that unit of work is finished. It’s even incredibly common to have multiple revisions in a row that are works in progress. Hell, the article we’re all commenting on discusses just such a workflow. If I make five revisions on top of some branch, there is no reason to assume that any of them are ready to be shared, much less all of them.
Because of that difference, it makes sense to have an explicit act to move a branch name forward.
I like Jujutsu so much that I've been working on massive refactors to my tooling in order to support it (example: https://github.com/LoganDark/get-shit-done)
But I don't rewrite history. It's history. While I can understand people have reasons to do it, the reasons have never resonated with me. I'd rather spend my time getting new work done and not polishing work I've already done.
To me, jj has inherent value because I do a lot of things raw Git makes difficult or impossible. For example, https://github.com/LoganDark/fabric-template supports every point release of Minecraft since 1.14, and mods like https://github.com/LoganDark/debrand derive from it. I often rewrite the history of the template with configuration improvements and then rebase the histories of each mod on top of it.
This keeps my project setup consistent throughout versions, and consistent between mods, and allows all my mods, across all supported versions, to benefit trivially from all improvements I make to my global template. This workflow is either not possible with git or would require slow, ugly and fragile scripts.
I dislike this as well. I find it easier to keep track of branches with bookmarks, but my workflow still makes things cumbersome. I am usually working with the "megamerge" branches, and I usually want to add commits to my current branch instead of squashing my edits. However, adding commits means I have to add my commit, move my bookmark up to the branch tip (jj tug?), and then rebase the megamerge branch, versus doing nothing for squashing. I also find that when I mess up, I don't really love using `jj op log` to fix it. I want to not be in an environment where it's this easy to destroy history (I feel like git was on the other end of it).
That’s why I always use jj’s automatic commit identifiers. They are short and I don’t waste brain cycles naming things that are ephemeral. When I push, I let jj automatically creates, updates, and deletes remote git branches (`jj git push -c` for creation, plain `jj git push` for updates, `jj git push --deleted` for deletions). I do not ever have to think about branch names and it is great!
But when I'm picking up something someone on the team has left behind because they got pulled on to something else, or are sick, or 5 million other reasons, having a branch, with a ticket in the name, explaining what the purpose of the branch is, why it exists, what it's current state is, that all matters. I can't help but think that everyone that likes JJ isn't really doing collaboration.
> having a branch, with a ticket in the name, explaining what the purpose of the branch is, why it exists, what it's current state is, that all matters
In my view, all the above information exists not in the branch name, but either in the ticket, or in the commit message. The branch name is purely a superfluous thing that does not convey any information. Many of my colleagues already use a tool to automatically name their branches from the first line of their commit messages, and jj just makes this awkward process straightforward.
Restricted and summarized is good - easier to find/remember, less fluff in a list. And easier to recognize a short identifier from a list of the 2-3 most recent branches, than scanning through 50 commits, when trying to remember where some work last was, and which is the proper end-point instead of some failed attempt or unrelated change.
Unnamed branches are quite neat - I certainly have a lot more of such than named ones in jj - but as such named branches are, if anything, more important as a result, for separating sequences of changes striving towards a goal, from the sea of smaller experiments.
Wrong. With jj, I use `jj describe` before I start work. It is like writing out a plan for what I want to do.
> Or, if you have, you've quite potentially just wasted time writing something that will be rewritten anyway as things change.
Rewriting it is not wasted time. It is an opportunity to look at what I have written in the plan and check whether I have really executed them, and then rephrase things to be more easily understandable.
> Restricted and summarized is good - easier to find/remember, less fluff in a list.
The first line of a commit message is already a summary of the work done. And you can use actual English instead of trying to awkwardly avoid spaces in your words.
I usually don't have a plan for the end; certainly not what any specific commit would be; sure, I could make one (and either make my future self have to do extra work to figure out what commits with lies in their descriptions actually do, or continuously update the commit message marking what actually exists), but as I said that's basically a waste of time. (if you like comparing with past thoughts, sure, but that's definitely not a necessity for a workflow to be reasonable)
"is/isn't an ancestor of the bookmark" is also just a pretty damn good short-hand for denoting a separation between what's been considered the best attempt at the goal, vs things with known problems or just unrelated to the task.
At the core, this if all of course just a question of workflow; if you go into a thing with a plan, meaningful outlook of a non-vague destination, and without expecting continuous switching back&forth between a dozen other things over the time span the branch is alive, caring less about branches or branch names can perhaps work.
> The first line of a commit message is already a summary of the work done.
But you can't (sanely) use it to reference the branch in a revset, can't find it anywhere other than the full log (that's interleaved and mixed with a bunch of other things that you won't ever need to search for), and actual English just gets in the way for finding it, remembering it, and identifying it in a list.
This alone means that, even if I found interest in massively-ahead-of-time-describing commits, having a sane branch reference is still simply just necessary.
branch names are just a summarization of your commit messages
What kind of dev workflow leads to this surprising opinion?You can't possibly see a use-case for long-lived branches? Say what you will of git, at least it exposes enough knobs that it can mostly accomodate every workflow (possibly with a heavy porcelain layer to hide the plumbing). JJ seems to swing too far in the other direction, great for a "live on head" mentality but less ideal for other setups.
(The fact that all edits are automatically recorded is my personal peeve with JJ. I'm ok with lack of staging area like in Mercurial, but mercurial doesn't try to automatically amend the commit with my pending changes. Sure I can pretend that "@" is my staging commit then squash as needed, but then I also need to remember that checking out a commit is "jj new" which feels like absurd mental gymnastics to me.)
for prs, I usually start with a single commit, so `jj git push -c` will auto create a named branch based on the change id. And i have template like the following to push to the same branch if i decided to stack commits rather than rewrite:
branch-push = ["util", "exec", "--", "sh", "-c", """
name="$(jj log --no-graph --no-pager --color=never -r 'fork_point(@ | trunk())+ & fork_point(@ | trunk())..@' -T '"push-" ++ change_id.short()')"
jj bookmark set -r @- "${name}"
jj git push -b "${name}"
"""]
you could probably write a similar alias that used your workspace name as the branch name to push to.and descriptions are slightly nicer than branch names, since they can be longer.
I think the idea is that branches/bookmarks aren't as necessary as they are with git.
The un-learning curve is steep if you already know git very well.
https://github.com/jj-vcs/jj/discussions/3549 lets you enable automatically advancing bookmarks.
Review was done by having someone sit next to you and scrolling through the code telling what you had changed.
It feels like Apple vs Linux. Apple being different ... just because (it gives them an artificial moat)
I’m pretty sure `jj absorb` (and its predecessors, `git-absorb` [0] and `hg absorb`) are smarter than this, instead looking at the actual diffs.
> [absorb] splits changes in the source revision and moves each change to the closest mutable ancestor where the corresponding lines were modified last. If the destination revision cannot be determined unambiguously, the change will be left in the source revision.
I use absorb fairly often, fwiw. It's great for when I want to make a patch to a commit that will easily absorb into its right place. And I also, sometimes, prefer the more intentional approach where I decide exactly where each hunk will go.
Basically, if you don’t get into that sort of situation with commits containing parts they shouldn’t in the first place, you don’t need to do any extra work to clean them up. The tip of your branch should be the only “messy” part.
I start a new branch and begin work. When I’m ready to start organizing that work into a consistent narrative (or when bits are “finished”), I split it out into independent commits. As I keep making fixes and tweaks, I continue squashing bits from my working commit into the parent commits they belong on.
I don’t bother making any independent commits until pieces of what I’m working on are becoming fully-formed. Until then, my working commit just has everything.
A checkout is a working space after all, why can't it be (temporarily) dirty whilst you work?
From there, the simplest way is to just always use 'jj commit -i' and 'jj squash -i' to create change ids with your work. Then if you want to have your changes move around with you, just rebase your working copy which contains your "uncommitted files" to the new branch.
A different idea is to put those changes in a separate change, and then when you do work, always create your working space change as a merge change like 'jj new <uncommitted file change id> <main change id>. Then you should be able to do 'jj absorb' instead of 'jj squash' to put changes into the right change. Switching to a different branch is 'jj new <uncommitted file change id> <other change id>.
As in typing this, I'm thinking for myself and what I actually do in practice... I find moving / rebasing jj commits very easy (I have a UI tool that literally lets you drag and drop them) so I usually just commit these changes and then drag the commits around so it's not in the chain of when I want to send it out for review
> why can't it be (temporarily) dirty whilst you work?
Because that would go completely against how jj changes work.
instead of adding changes to a new commit, i split/squash them into the previous one so the current commit remains dirty
1. Squash all your commits in this branch to one
2. Move that commit to the working directory with the appropriate git reset command
3. Commit hunks as appropriate.
> Some people prefer this, as it helps git bisect work better. Debuggability versus reviewer convenience is the tradeoff, I guess.
Ideally we would have a VCS that made ergonomic to store both history-as-it-happened for some purposes, and the cleaned up, squashed and rebased history for other purposes, ensuring they match
You can revisit the original PR to see the individual commits if you really want.
In the open source context, it's limited by git here. But you can use jj with Google's monorepo, and so it itself is able to handle at least huge repositories (I don't know how big the largest files and binary files are in it, but I'm assuming that it has some of those too) well. That of course doesn't help you directly if you're not at Google, but the pathway is there.
I see there's a similar project for JJ, but it doesn't seem nearly as polished https://github.com/Cretezy/lazyjj
I just don't see the fuss about rebase. Merging just works fine.
Edit: OK, I realized later that I'm not really responding to the usual git rebase -i use case.
Have you heard people say that because of magit they started using more "advanced" git workflows, and how they emphasize having a better UI makes a difference?
It's the same idea with jujutsu. I'm much more likely to use git's power via jujutsu than directly with git. It's because jujutsu lets you do it all with a much simpler interface - fewer commands, and fewer concepts. And knowing that "jj undo" has your back.
As a sibling commenter said: Likely 99% of git users don't do "git rebase -i". But the percentage who do similar with jujutsu is high - perhaps over half[1] of jujutsu users do the equivalent of "git rebase -i" all the time. Many of them didn't when they used git.
The interface matters.
[1] If you told me 80%, it wouldn't surprise me in the least.
I'm not a git expert by any means I don't think, but there's 15 years of muscle memory there, and a few years of subversion before that...
To some of us, that's an essential structural criterion. Passing unit-level self-tests may be as well.
Just give me the PR, don't sweat the individual patches. But maybe also work on not committing your first idea as finished work.
The commits might get squashed anyways so the history on main won't necessarily match what's on the feature branch.
You can commit before you raise a pull request, I don't quite understand that point but I might just be missing something about your workflow that's different to mine.
Commits on a PR branch are usually my thinking outline, not for someone else to step through.
Is that a frequent way of reviewing? On GitHub you get shown all changes together in the review tab. You can select individual commits for closer inspection, but where is the benefit?
I started doing exactly this and it's been invaluable.
For me it's satire. There are reasons for varying effort in creating PRs or patches, but attempts like this never seem to reason about reality. If I have to review, I want to see the code, not a clever story hidden in the commit history.
I don't know about all that. All sorts of ex-post-facto automated cut-up-and-splice commits sounds to me like a recipe for an every larger mess. I say maintain git rigor, always. Now, you could say "You only say that because you know git rather than jujutsu" or "if you use git absorb more you'll get it", and theoretically you might be right, but... meh, I kind of doubt it.
sorry about that.
It tries to solve a human problem in an LLM era.
LLMs are destined to overcome humans in code merging and change versioning (already did for me).
There's little point to introducing yet another layer of indirection when LLMs just cut to the chase.
A lot of humans still don't use git too.
Many do only when they are forced.
And it's much easier for a professional to be forced to use LLMs than jj when it comes to versioning assist (not even comparable in mindshare but the obvious needs to be said sometimes).
So unfortunately I'm afraid jj is not going to achieve critical mass before 99.99% of merges are done by AI which don't need jj.
Also there are many ways to use llms. Some people control it at code review, but others control it as the VCS.
LLMs will do most of the work anyways. And they don't need jj. Like I said, jj helps solving a human problem in an LLM era.
It's hardly worth using more of the precious LLM context with jj instructions when git does the job and is mandatory anyways.
Also, remember that humans aren't going away. The touch points are changing but they still exist. The VCS is a common touch point for many and my preferred one. For others I'm sure it's online code review tools. If an agent is operating under my name in my workspace I want to manage it within the confines of my workspace. Once it becomes more agentic and operates as it's own entity I'm sure I'll change my mind but that's not the case today.
`git branch` is basically my bookmark tool. I commit for a while, then when I want to remember where I am for later, `git branch wip/topic-a-finally-compiles` or whatever. I can reset hard to it when I want to revert back, or any other topic I need. If I forget to name a branch for a commit, the reflog is right there. Nothing’s lost.
And yeah, a soft reset is basically the ideal way to just say “pretend all my changes weren’t committed yet, starting from $ref” and then I make my single commit for my PR. Easy peasy.
I do switch branch for long experiments and touch up on existing PR.
It would be great if a PR was about distributing patches and not having those automatically generated from a branch.
jj let me make changes to my git commits without fear, since version control of the git state itself let me undo those changes easily, too.
git rebase -i
# squash all the commits (e.g. in vim with ctrl-v)
git reset HEAD^
git add -p
# interactively pickup the RED hunks
git ci -m RED
The main difference to jj is that the RED commit is created later with git.Jj is not git and is not a git tool, it just (thankfully) uses git as a backend, so you can still carry on with the rest of the world.
In what way is that different from using `git rebase -i` to edit a commit?
There are no modal “sorry rebase failed, best of luck” gotchas. There are no “oops I put the wrong thing in the wrong part of the rebase and now I have to abort and start all over” gotchas.
It’s rebase, but without all the extra work, mental overhead, failure cases, and effort.
You might not think this is that big a deal, but this also means you don’t have to resolve the entire thing in one go. Plenty of times with complicated rebases in git, I’ve not been 100% certain about the path towards resolving it. But jumping around to view various commits when you’re in the rebase-conflict state is painful. In jj you can just switch to an earlier commit, tweak it there, jump to a later commit, see how it looks, etc. It removes 98% of the pain.
It also dovetails nicely with other aspects of jj. Since rebases happen automatically and constantly, they are usually tiny. If there’s a conflict, it’s caught right when you do the thing and not four hours later when that part is no longer fresh in mind. And the op log lets you restore and undo actions atomically, which makes undoing a fuck-up a no-op.
A merge conflict means that some automated tool couldn’t figure it out. But all a merge is is a commit with two parents and an accompanying diff that shows the process of combining them. A merge conflict isn’t really in any way special. It just means whatever algorithm was used couldn’t do it unambiguously without human help.
Seems straightforward, wouldn’t call it special
Yes, you can do most commit manipulations with git just like with jj. But, users of jj know they're "looking down the power continuum" (to reuse pg's terminology) when they look at git, whereas git users cannot fathom what's exactly the deal with jj. Unfortunately, the only way to get it is to spend a week with it, with an open mind. It's close to impossible to describe it except "it's really neat" and "wow it removes all git's friction I didn't know existed".
And, apparently, there's a pattern of having to try at least two times before it becomes intuitive!
It's not relevant. Don't try to convert git users--you don't need to. They're fine doing what they do, and the git backend store is acceptable. People who understand how broken git is will gravitate to jj with the lightest of prodding--give them a light poke and then don't harass them further.
The main difference is that I can teach jj (like I could teach hg) to normal people. And, because jj is based on the git store, they can operate in a Github world.
So, just teach jj to people who haven't yet broken their brain with git. Running on the git backend already gains most of the network effect, so don't proselytize the git people.
The same problem with vi for example. Which I learned, deeply and for a long time. Maybe I will be break even in a decade or two. And for example, I can code faster than anybody who I know and use vi all the time (or some vi keyset in Emacs or something), not because I type faster, but because I don't need to rewrite my code that many times than them.
The same with jj. I'm happy that it clicks better than Git for some. But I know Git from inside and out. Git was never the bottleneck for me. And really any VCS in the past. If I learn jj, then I don't expect that the effort pays off in a decade or less in the best case. My work's result would be the exact same thing, just with different commands, commands which takes almost no time compared to my other tasks.
We do get it. But have you ever thought that git inflexible nature and full control is what some people people like?
Having three different state for your work (working tree, staging, and committed) is nice for reviewing code. Picking lines and chunk give me an additional mental state to think about the design of the code.
And once upon a time, I preferred history log like the one in the article. But this days (mostly inspired by mailing list development style) I wants the commit in my main log to be either features or bug fixes. Everything else is “wip”, which I will squash. It makes it easier when rewriting history, cherry picking, or just browsing the log.
The big differences are that the jj approach gives you a commit message for the staging change, and lets you jump to some other commit without extra steps.
jj has nothing I need, so it would be changes for the sake of changes. I’m not saying it’s bad, but people do need to realize that their struggles are theirs, not the whole world.
It’s been said a million times but it is really true that jujutsu’s appeal is something you feel (or don’t) after giving it a proper go. It doesn’t survive compression into the feature list.
Actually I think that property is a much bigger obstacle to adoption than what it does or doesn’t offer to the rare true git wizard.
Git cli is tedious. But like all cli operations the goal is always to script your workflow or alias the most used commands in something shorter. I'm sure that jj may have an easier learning curve. But some people do know how git works and like the level of control over commits.
That being said, I also much prefer a UI layer (lazygit originally, now lazyjj), and personally I find the benefits of jj to be partly complementary to the UI ones.
That is, after getting used to jj, my problems with git are (1) the CLI ergonomics, and (2) the model is actually more complex than it needs to be, in a way that materially diminishes my experience. Only the first is addressed by lazygit (though maybe magit does both; not sure).
One other point: jj doesn’t actually impose a level-of-control tradeoff on the curation of commits. You can mimic the git workflow by modeling the working copy and staging area as commits (changes, in jj parlance), or you can experiment with any number of alternatives. What git gives you is the opinionated support for the working-copy-staging-area-commit approach.
BUT! You can simply ask an agent to commit every meaningful block of work. Or just ask any agent to create a JIRA ticket and start work on that named branch. Or ask it to create work trees and create a PR. Life has gotten much easier without having to fight the command line or confusing GUI UX.
My only ask--tell me how it sucks so I can make it better.
It is also true in reverse. Scopes set too broadly ("dark mode implementation," "auth flow fixes") lead to un-readable diffs no matter what tool you use for version control. Un-readable diff does not stem from commitment discipline; it is a scope problem.
That said, this fact does not diminish the usefulness of Jujutsu. There are valid use cases for the rebase and stacking operations. However, the discussion about commit granularity takes on a whole new context once the constraint of having readable commits is established at the scope setting stage.
Last time I tried jj, branches were an extremely laborious process to keep up to date. I don't see how people that aren't working alone can work with that. I have numerous branches in flight at any given time, and my colleagues do as well. The idea of manually keeping them pointed at the right commit is just nuts.
Maybe they've fixed that astonishing choice since then, and I'd give things another go if they did. But branches and worktrees are how I operate.
Regarding the article, I have no idea what is going on as I'm red-green color deficient.