Repository: jj-vcs/jj (Rust)

Issue:
FR: point trunk() at upstream rather than origin by default
**Is your feature request related to a problem? Please describe.**
I often use git repos that have been modified by the github cli. When you clone a repo, the remote is named origin, when you use the tool to fork a repo, the origin remote is renamed upstream and your fork is now the origin.

**Describe the solution you'd like**
When initializing jj (`jj git init --colocate`), the `trunk()` alias should point at main@upstream if the remote exists rather than main@origin.

**Describe alternatives you've considered**
It's possible that this should have some configuration behind it, but this seems like it's a reasonable default. If there's push back against it being a default, I'd still like to be able to override this behavior so that what I believe to be sensible is acheivable.

**Additional context**
```
❯ jj git init --colocate    
Done importing changes from the underlying Git repo.
Setting the revset alias `trunk()` to `master@origin`
Hint: The following remote bookmarks aren't associated with the existing local bookmarks:
  master@origin
  master@upstream
Hint: Run the following command to keep local bookmarks updated on future pulls:
  jj bookmark track master@origin master@upstream
Initialized repo in "."
```


Discussion:
This is similar to https://github.com/jj-vcs/jj/issues/5928 which probably happen whenever we have first-class `jj github` support.
It's a much simpler issue than that IMO unless I'm missing some context.

By default, repos that have been modified using the gh cli have two remotes, upstream and origin. This FR is about detecting that state when setting up jj on an existing repo, not modifying that state, or even or doing the same thing, just being aware of this standard which is in place and doing the rationale thing by default.
> It's a much simpler issue than that IMO unless I'm missing some context.
> 
> By default, repos that have been modified using the gh cli have two remotes, upstream and origin. This FR is about detecting that state when setting up jj on an existing repo, not modifying that state, or even or doing the same thing, just being aware of this standard which is in place and doing the rationale thing by default.


So you basically want a reference check for `refs/remotes/upstream/` in https://github.com/jj-vcs/jj/blob/823f4fccb424e7df53928cfc7013559a77281ed6/cli/src/commands/git/init.rs#L244-L266

> So you basically want a reference check for refs/remotes/upstream/ in 

Yes. I think this should either be a sensible default, or as an option that's configurable if that default is not universally acceptable.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a16480fb310..40983980fb9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 * `jj log`, `jj evolog` and `jj op log` output can now be anonymized with the
   `builtin_log_redacted` and `builtin_op_log_redacted` templates.
 
+* `jj git init` now checks for an `upstream` remote in addition to `origin` when
+  setting the repository-level `trunk()` alias. The `upstream` remote takes
+  precedence over `origin` if both exist.
+
 * Add the `jj touch` command, which modifies a revision's metadata. This can be
   used to generate a new change-id, which may help resolve some divergences.
 
diff --git a/cli/src/commands/git/init.rs b/cli/src/commands/git/init.rs
index ae3672c3849..198cb870280 100644
--- a/cli/src/commands/git/init.rs
+++ b/cli/src/commands/git/init.rs
@@ -240,27 +240,39 @@ fn init_git_refs(
     Ok(repo)
 }
 
-// Set repository level `trunk()` alias to the default branch for "origin".
+// Set repository level `trunk()` alias to the default branch.
+// Checks "upstream" first, then "origin" as fallback.
 pub fn maybe_set_repository_level_trunk_alias(
     ui: &Ui,
     workspace_command: &WorkspaceCommandHelper,
 ) -> Result<(), CommandError> {
     let git_repo = git::get_git_repo(workspace_command.repo().store())?;
-    if let Some(reference) = git_repo
-        .try_find_reference("refs/remotes/origin/HEAD")
-        .map_err(internal_error)?
-    {
-        if let Some(reference_name) = reference.target().try_name() {
-            if let Some((GitRefKind::Bookmark, symbol)) = str::from_utf8(reference_name.as_bstr())
-                .ok()
-                .and_then(|name| parse_git_ref(name.as_ref()))
-            {
-                // TODO: Can we assume the symbolic target points to the same remote?
-                let symbol = symbol.name.to_remote_symbol("origin".as_ref());
-                write_repository_level_trunk_alias(ui, workspace_command.repo_path(), symbol)?;
+
+    // Try "upstream" first, then fall back to "origin"
+    for remote in ["upstream", "origin"] {
+        let ref_name = format!("refs/remotes/{remote}/HEAD");
+        if let Some(reference) = git_repo
+            .try_find_reference(&ref_name)
+            .map_err(internal_error)?
+        {
+            // Found a HEAD reference for this remote. Even if we can't parse it,
+            // we should stop here and not try other remotes because it doesn't
+            // really make sense if "origin" were to be set as the default if we
+            // know "upstream" exists.
+            if let Some(reference_name) = reference.target().try_name() {
+                if let Some((GitRefKind::Bookmark, symbol)) =
+                    str::from_utf8(reference_name.as_bstr())
+                        .ok()
+                        .and_then(|name| parse_git_ref(name.as_ref()))
+                {
+                    // TODO: Can we assume the symbolic target points to the same remote?
+                    let symbol = symbol.name.to_remote_symbol(remote.as_ref());
+                    write_repository_level_trunk_alias(ui, workspace_command.repo_path(), symbol)?;
+                }
             }
-        };
-    };
+            return Ok(());
+        }
+    }
 
     Ok(())
 }


Test patch:
diff --git a/cli/tests/test_git_init.rs b/cli/tests/test_git_init.rs
index 433a61a3fda..ca55e24f8db 100644
--- a/cli/tests/test_git_init.rs
+++ b/cli/tests/test_git_init.rs
@@ -234,6 +234,84 @@ fn test_git_init_external_import_trunk(bare: bool) {
     }
 }
 
+#[test]
+fn test_git_init_external_import_trunk_upstream_takes_precedence() {
+    let test_env = TestEnvironment::default();
+    let git_repo_path = test_env.env_root().join("git-repo");
+    let git_repo = init_git_repo(&git_repo_path, false);
+
+    let oid = git_repo
+        .find_reference("refs/heads/my-bookmark")
+        .unwrap()
+        .id();
+
+    // Add both upstream and origin remotes with different default branches
+    // upstream has "develop" as default
+    git_repo
+        .reference(
+            "refs/remotes/upstream/develop",
+            oid.detach(),
+            gix::refs::transaction::PreviousValue::MustNotExist,
+            "create upstream remote ref",
+        )
+        .unwrap();
+
+    git::set_symbolic_reference(
+        &git_repo,
+        "refs/remotes/upstream/HEAD",
+        "refs/remotes/upstream/develop",
+    );
+
+    // origin has "trunk" as default
+    git_repo
+        .reference(
+            "refs/remotes/origin/trunk",
+            oid.detach(),
+            gix::refs::transaction::PreviousValue::MustNotExist,
+            "create origin remote ref",
+        )
+        .unwrap();
+
+    git::set_symbolic_reference(
+        &git_repo,
+        "refs/remotes/origin/HEAD",
+        "refs/remotes/origin/trunk",
+    );
+
+    let output = test_env.run_jj_in(
+        ".",
+        [
+            "git",
+            "init",
+            "repo",
+            "--git-repo",
+            git_repo_path.to_str().unwrap(),
+        ],
+    );
+    insta::allow_duplicates! {
+    insta::assert_snapshot!(output, @r#"
+    ------- stderr -------
+    Done importing changes from the underlying Git repo.
+    Setting the revset alias `trunk()` to `develop@upstream`
+    Working copy  (@) now at: sqpuoqvx ed6b5138 (empty) (no description set)
+    Parent commit (@-)      : nntyzxmz e80a42cc develop@upstream my-bookmark trunk@origin | My commit message
+    Added 1 files, modified 0 files, removed 0 files
+    Initialized repo in "repo"
+    [EOF]
+    "#);
+    }
+
+    // "trunk()" alias should be set to "upstream"'s default, not "origin"'s
+    let work_dir = test_env.work_dir("repo");
+    let output = work_dir.run_jj(["config", "list", "--repo", "revset-aliases.\"trunk()\""]);
+    insta::allow_duplicates! {
+        insta::assert_snapshot!(output, @r#"
+        revset-aliases."trunk()" = "develop@upstream"
+        [EOF]
+        "#);
+    }
+}
+
 #[test]
 fn test_git_init_external_ignore_working_copy() {
     let test_env = TestEnvironment::default();