In short: jj rejects --at-op, --ignore-working-copy and
--no-integrate-operation, creates the directory if needed, and decides the mode. With
--git-repo, or when colocating in a directory that already has .git, it uses
that existing repository: it imports its refs, sets trunk() from
upstream/HEAD or origin/HEAD, and checks out a new commit on top of Git's
HEAD. Otherwise it creates a fresh Git repo, colocated or internal, and the working-copy
commit sits on the root commit. It ends with Initialized repo in "…".
The command is cmd_git_init, and the work is in
do_init. It shares the workspace creation with
jj git clone and the ref import with
jj git import. What a workspace and repo
consist of is on the working copy page.
1. Command-line surface
| Flag | Type | Default | Meaning |
|---|---|---|---|
DESTINATION (positional) | path | . | Where to create the workspace; created if missing, but its parent must exist (source). |
--colocate | bool | false | Colocate with Git. Has no effect unless git.colocate is false; conflicts with --git-repo (source). |
--no-colocate | bool | false | Keep the Git repo inside .jj; conflicts with --colocate (source). |
--object-hash | sha1 or sha256 | git.object-hash | Hash algorithm for a newly created Git repo; conflicts with --git-repo (source). |
--git-repo PATH | path | none | Use this existing Git repository; conflicts with --colocate and --object-hash (source). |
2. Configuration
| Key | Default | Effect |
|---|---|---|
git.colocate | true | Whether to colocate; the flags override it (default, use). |
git.object-hash | "sha1" | Hash algorithm when --object-hash isn't given (default, use). |
remotes.<name>.auto-track-bookmarks | "~*" per the schema | When importing an existing repo, which remote branches get tracked local bookmarks (use). |
Settings are read for the new workspace's path, so conditional config scoped to it applies
(source). When importing an existing repo, jj may write
revset-aliases."trunk()" to the new repo config (§6).
3. End-to-end flow
- Pre-checks. The three global flags don't make sense before a repo exists and are
refused. The destination is created with
create_or_reuse_dir, which creates one directory level only and is fine if it already exists, then canonicalized (source). - New repo. For Colocate and Internal, the workspace is created exactly as in
jj git clone, with one operation,add workspace 'default', whose working-copy commit is an empty child of the root (source). A colocated repo also gets.jj/.gitignore(maybe_add_gitignore). - Existing repo.
Workspace::init_external_gitrecords the Git repo's path, relative if it is inside the workspace so the workspace can be moved. Refs are imported first "so all the reachable commits are indexed in chronological order" (source). - Import.
init_git_refsrunsimport_refswith abandoning and synthetic predecessors turned off. If anything changed, a colocated repo also exports right away, because auto-tracking may have created local bookmarks. The transaction is committed directly asimport git refs, and jj printsDone importing changes from the underlying Git repo.(source). - HEAD. In a colocated repo,
maybe_snapshotimports Git'sHEADand puts the working-copy commit on top of it (import_git_head). Otherwise jj imports theHEADcommit itself and checks out a new commit on top, recorded asimport git head(source,import_head_commit).
4. Core architecture touchpoints
- Workspace and repo creation:
.jj/repowith its stores, and.jj/working_copy; on failure,.jjis removed (init_with_factories). - Git backend: new (bare internal or colocated) or external; see the commits page.
- View: imported bookmarks, tags and remote refs, the working-copy commit, and in a
colocated repo the recorded Git
HEAD; see the view page. - Working copy: checking out Git's
HEADupdates the files in a non-colocated workspace (Added N files); in a colocated one the files are already there and only the working-copy state is reset.
5. Transaction and operation
| Operation | When | Source |
|---|---|---|
add workspace 'default' | Always. | workspace.rs |
import git refs | Existing repo with any refs to import. | init.rs |
import git head | Existing repo whose HEAD points at a commit: by the snapshot when colocated, by do_init otherwise. | cli_util.rs, init.rs |
init_git_refs uses a bare start_repo_transaction and commits it itself, because no
workspace command helper exists yet at that point (source).
6. Algorithm and invariants
Choosing the mode
So with the default git.colocate = true, running jj git init in an existing Git
checkout adopts it as a colocated repo (test). Whether an external
repo counts as colocated is decided by opening the Git repo at the workspace root
(is_colocated_git_workspace).
Setting trunk()
For an existing repo, jj looks for refs/remotes/upstream/HEAD, then
refs/remotes/origin/HEAD. The first one that exists decides: if it is a symbolic ref to a
branch, trunk() is set to that branch at that remote in the repo config, and jj prints
Setting the revset alias `trunk()` to `…`. If it exists but can't be parsed, jj stops
without setting anything rather than falling back to origin
(maybe_set_repository_level_trunk_alias).
Untracked remote bookmarks
After an import, any local bookmark that has an untracked remote bookmark of the same name is listed,
with a ready-made jj bookmark track … command
(print_trackable_remote_bookmarks). See
jj bookmark track.
Invariants: init never creates a second Git repository next to an existing
.git. Importing an existing repo never abandons commits. A non-colocated workspace doesn't
record a Git HEAD (test).
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
--no-integrate-operation is not respected, --ignore-working-copy is not respected, --at-op is not respected (exit 2) | One of those global flags was given. | init.rs |
Failed to create workspace | The directory can't be created: the path is an existing file, or its parent doesn't exist. | init.rs, test |
Did not create a jj repo because there is an existing Git repo in this directory., hint run `jj git init --colocate` instead | Not colocating, and the directory has .git. | init.rs, test |
Warning: Initialized a new colocated jj repo inside a Git worktree., hint to use jj workspace add | Colocated with a linked Git worktree. | init.rs |
Hint: The following remote bookmarks aren't associated with the existing local bookmarks: | An imported local bookmark has an untracked remote counterpart. | init.rs |
8. Worked example
From upstream's test_git_init_external_import_trunk (non-bare case). An
existing Git repo has my-bookmark at commit e80a42cc, a remote branch
origin/trunk at the same commit, and origin/HEAD pointing at it.
jj git init repo --git-repo <path> imports the refs, sets trunk(), and checks
out a new commit on top of Git's HEAD:
Upstream's output (source):
Done importing changes from the underlying Git repo.
Setting the revset alias `trunk()` to `trunk@origin`.
Working copy (@) now at: sqpuoqvx ed6b5138 (empty) (no description set)
Parent commit (@-) : nntyzxmz e80a42cc my-bookmark trunk@origin | My commit message
Added 1 files, modified 0 files, removed 0 files
Initialized repo in "repo"
When the same kind of repo is adopted in place with jj git init --colocate, there is no
Working copy now at line: the snapshot's HEAD import puts the working-copy commit
on top of HEAD without touching the files, and the workspace records that Git
HEAD (source).