Skip to content

Commit

Permalink
templater: indicate if branch needs to be pushed to a remote
Browse files Browse the repository at this point in the history
It's useful to know when you've modified a branch that exists on a
remote. A typical case is when you have pushed a branch to a remote
and then rewritten it. This commit adds an indication in the
`branches` template keyword. A branch that needs to be pushed to a
remote now has a `*` at the end (similar to how conflicted branches
have a `?` at the end). Note that the indication only considers
remotes where the branch currently exists, so there won't be an
indication that the branch has not been pushed to a remote.

Closes #254
  • Loading branch information
martinvonz committed Nov 5, 2022
1 parent d49e2d4 commit 0d8894d
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* The new revset function `file(pattern..)` finds commits modifying the
paths specified by the `pattern..`.

* (#254) Branches that have a different target on some remote than they do
locally are now indicated by an asterisk suffix (e.g. `main*`) in `jj log`.

### Fixed bugs

* (#463) A bug in the export of branches to Git caused spurious conflicted
Expand Down
7 changes: 5 additions & 2 deletions docs/branches.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ branches from the remote will be imported as branches in your local repo.

Jujutsu also records the last seen position on each remote (just like Git's
remote-tracking branches). You can refer to these with
`<branch name>@<remote name>`, such as `jj co main@origin`. Most commands don't
`<branch name>@<remote name>`, such as `jj new main@origin`. Most commands don't
show the remote branch if it has the same target as the local branch. The local
branch (without `@<remote name>`) is considered the branch's desired target.
Consequently, if you want to update a branch on a remote, you first update the
branch locally and then push the update to the remote.
branch locally and then push the update to the remote. If a local branch also
exists on some remote but points to a different target there, `jj log` will
show the branch name with an asterisk suffix (e.g. `main*`). That is meant to
remind you that you may want to push the branch to some remote.

When you pull from a remote, any changes compared to the current record of the
remote's state will be propagated to the local branch. Let's say you run
Expand Down
6 changes: 6 additions & 0 deletions src/templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ impl TemplateProperty<Commit, String> for BranchProperty<'_> {
if local_target.has_add(context.id()) {
if local_target.is_conflict() {
names.push(format!("{}?", branch_name));
} else if branch_target
.remote_targets
.values()
.any(|remote_target| remote_target != local_target)
{
names.push(format!("{}*", branch_name));
} else {
names.push(branch_name.clone());
}
Expand Down
78 changes: 78 additions & 0 deletions tests/test_templater.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::common::TestEnvironment;

pub mod common;

#[test]
fn test_templater_branches() {
let test_env = TestEnvironment::default();

test_env.jj_cmd_success(test_env.env_root(), &["init", "--git", "origin"]);
let origin_path = test_env.env_root().join("origin");
let origin_git_repo_path = origin_path
.join(".jj")
.join("repo")
.join("store")
.join("git");

// Created some branches on the remote
test_env.jj_cmd_success(&origin_path, &["describe", "-m=description 1"]);
test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch1"]);
test_env.jj_cmd_success(&origin_path, &["new", "root", "-m=description 2"]);
test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch2"]);
test_env.jj_cmd_success(&origin_path, &["new", "root", "-m=description 3"]);
test_env.jj_cmd_success(&origin_path, &["branch", "create", "branch3"]);
test_env.jj_cmd_success(&origin_path, &["git", "export"]);
test_env.jj_cmd_success(
test_env.env_root(),
&[
"git",
"clone",
origin_git_repo_path.to_str().unwrap(),
"local",
],
);
let workspace_root = test_env.env_root().join("local");

// Rewrite branch1, move branch2 forward, create conflict in branch3, add
// new-branch
test_env.jj_cmd_success(
&workspace_root,
&["describe", "branch1", "-m", "modified branch1 commit"],
);
test_env.jj_cmd_success(&workspace_root, &["new", "branch2"]);
test_env.jj_cmd_success(&workspace_root, &["branch", "set", "branch2"]);
test_env.jj_cmd_success(&workspace_root, &["branch", "create", "new-branch"]);
test_env.jj_cmd_success(&workspace_root, &["describe", "branch3", "-m=local"]);
test_env.jj_cmd_success(&origin_path, &["describe", "branch3", "-m=origin"]);
test_env.jj_cmd_success(&origin_path, &["git", "export"]);
test_env.jj_cmd_success(&workspace_root, &["git", "fetch"]);

let output = test_env.jj_cmd_success(
&workspace_root,
&["log", "-T", r#"commit_id.short() " " branches"#],
);
insta::assert_snapshot!(output, @r###"
o 48e0b6c42296 branch3?
| @ 092b2e0283a9 branch2* new-branch
| | o f4a739b1677f branch1*
| |/
|/|
| o 752dad8b1718 branch2@origin
|/
o 000000000000
"###);
}

0 comments on commit 0d8894d

Please sign in to comment.