-
Notifications
You must be signed in to change notification settings - Fork 365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: docs/design: git branch mode #4670
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# Branch mode | ||
|
||
This is a proposed configuration option to replace | ||
`experimental-advance-branches`. It is not intended to replace or obviate | ||
topics. | ||
|
||
The design below assumes that the configuration option is enabled. There should | ||
be no change from current behavior if the option is not enabled. | ||
|
||
**"Non-headless"** or **"branch"** mode: a git-colocated repository with the | ||
new configuration option enabled is considered to be in this mode whenever | ||
`.git/HEAD` (hereafter `HEAD`) is populated with a valid `jj` bookmark, and the | ||
bookmark is currently at `@` or `@-`. In this design, the bookmark will be | ||
Comment on lines
+12
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, we currently don't allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting; I assumed For a merge commit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably the first parent, as far as I can remember. So, pretty close to being arbitrary. |
||
referred to as `b(HEAD)`. | ||
|
||
**Note:** The only part of this design that is specific to `git` colocation is | ||
the use of `.git/HEAD`. The design could be implemented for any backend that | ||
has a concept of a "current" branch. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that if we make this work, it should work on non-colocated repos as well. To me, this looks like a proposal of "what if we added the notion of a 'current branch' to This makes more sense to me than the experimental advance-branches feature in some ways, but it would also be a much larger change to implement. E.g., you'd need to adjust the log template to show which branch is current, store this information in the repo, etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's also something to be said for fixing the advance-branches feature, if it's possible without storing extra info (which I'm not at all sure about). I think your original idea was to store the extra info in the Git repo only (by storing a branch in the |
||
|
||
## Invariants | ||
|
||
The configuration option will never affect any of these `jj` behaviors: | ||
|
||
* how `@` is updated by each `jj` command | ||
* the way commit topology or contents (ignoring bookmarks) evolve with each | ||
command | ||
|
||
## Diagrams | ||
|
||
These Excalidraw diagrams show the effect of various `jj` commands when in | ||
branch mode: | ||
[final rendering TBD; see PR-description for updated link] | ||
|
||
In these diagrams, a colored arrow is a `jj` bookmark, and a bold and colored | ||
commit-graph node is `b(HEAD)`. | ||
|
||
## Entering branch mode | ||
|
||
These commands always enter branch mode, by setting `.git/HEAD` appropriately: | ||
|
||
``` | ||
git init | ||
git clone | ||
bookmark set <name> @ | ||
bookmark create <name> @ | ||
bookmark set <name> @- | ||
bookmark create <name> @- | ||
``` | ||
|
||
See also "Changing branches." | ||
|
||
## Staying on the same Git branch | ||
|
||
When in branch mode, these operations preserve branch mode, and do not change | ||
which bookmark `HEAD` points to: | ||
|
||
``` | ||
rebase | ||
abandon | ||
commit | ||
new [without specifying revisions] | ||
duplicate | ||
backout | ||
``` | ||
Comment on lines
+54
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None of these commands currently do anything special w.r.t. branches - it's all generic behavior that's implemented at a lower level. I'd really like to keep it that way. That ensures that the behavior is consistent between commands, including for our custom commands at Google, and in operations implemented by various UIs that use the library. So, can you instead describe the behavior in a way that doesn't refer to a specific command (other than as an example)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll try to rephrase, but the short version is that these commits aren't doing anything "special." If you've got an active branch, then that branch stays active, and the corresponding jj bookmark moves appropriately, i.e. it moves together with |
||
|
||
... as well as any commands that don't change the change topology, such as | ||
`status`, `describe`, `diff`, etc. | ||
|
||
In branch mode, each of the above commands acts on `b(HEAD)` the same way it | ||
acts on `@`: | ||
* when `@` is `b(HEAD)`, after the command, `b(HEAD)` is set to `@`. | ||
* when `@-` is `b(HEAD)`, after the command, `b(HEAD)` is set to `@-`, unless | ||
that would be ambiguous (for instance, with `jj abandon @-` if the original | ||
`@-` has multiple parents); in case of ambiguity, `b(HEAD)` is set to `@`. | ||
|
||
Note: I haven't thought of any cases where this would occur, but it may be that | ||
there is an "obvious" resolution to some otherwise-ambiguous `@-` cases, in | ||
which case we would not need to set `b(HEAD)` to `@`; for instance, if only one | ||
commit in `@-` is a viable candidate for `jj branch set b(HEAD) <rev>` without | ||
`--allow-backwards`, then we would pick that revision. | ||
|
||
## Changing branches | ||
|
||
These operations may either enter branch mode or change which branch is in | ||
`HEAD`: | ||
|
||
``` | ||
edit [bookmark] | ||
new [bookmark] | ||
``` | ||
|
||
With a revision argument that is *not* a bookmark (including `<branch>@git` or | ||
`<branch>@origin`), these commands will always exit branch mode (i.e. leave git | ||
in the "headless" state). | ||
|
||
## Merges: | ||
|
||
``` | ||
new [bookmark1] [bookmark2] ... | ||
``` | ||
|
||
If none of the bookmarks is `b(HEAD)`, this will simply enter headless mode. | ||
|
||
As long as any of the bookmarks is `b(HEAD)`, this will remain in branch mode, | ||
but will *not* update the bookmark to the new commit (even if `@` is initially | ||
`b(HEAD)`). That is, after this operation, `b(HEAD)` will be a member of `@-`. | ||
|
||
This will ensure that the next `commit` or `new` (without a revision argument) | ||
will behave as described above: only `b(HEAD)` will be advanced. This means | ||
that, in Git terminology, to merge changes "from" branch (bookmark) A "into" | ||
branch (bookmark) B, the user would first checkout `B` and then use `jj new A B | ||
; jj new`. B would be advanced to the merge commit, while A would not. | ||
|
||
## Diffing: | ||
|
||
In non-headless mode, `jj diff` (with no revset) would default to `jj diff @ | ||
--from b(HEAD)`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would help me if this document started with a problem statement. Can you list some scenarios that you think we should improve? Here's one I can think of where it should be easy to make jj behave better:
I.e. we left Git in a "detached HEAD" state where we could have left
main
checked out.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since
describe
doesn't move@
(it changes the commit ID but not the change ID), it also wouldn't change.git/HEAD
in "active branch" mode, since branch mode means that.git/HEAD
contains a branch reference, not a commit sha.(As before, I will add this to the doc, I just wanted to briefly answer your question now.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the Git branch that HEAD@git points to always points to a commit hash, not a change_id, so it does need updating. Or, in terms of bookmarks, the JJ bookmark might still point to the same change_id, but as a newly created commit is associated with the change_id from the bookmark, and the old commit becomes hidden, JJ effectively needs to run
jj git export
to update the commit hash that git sees for the branch that the bookmark is tracking.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposal is currently only for colocated repos. In colocated repos, the export already happens automatically:
I'll need to learn a little more about how non-colocated repos currently behave before adding them to the design.