Skip to content

Commit 00f87a2

Browse files
committed
cli: untrack remote bookmarks on bookmark forget
Forgetting remote bookmarks can lead to surprising behavior since it causes the repo state to become out-of-sync with the remote until the next `jj git fetch`. Untracking the bookmarks should be a simpler and more intuitive default behavior. The old behavior is still available with the `--include-remotes` flag. I also changed the displayed number of forgotten branches. Previously when forgetting "bookmark", "bookmark@remote", and "bookmark@git" it would display `Forgot 1 bookmarks`, but I think this would be confusing with the new flag since the user might think that `--include-remotes` didn't work. Now it shows separate `Forgot N local bookmarks` and `Forgot M remote bookmarks` messages when applicable.
1 parent 4ed0e13 commit 00f87a2

File tree

10 files changed

+132
-52
lines changed

10 files changed

+132
-52
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
2525
`Commit`. All methods on `Commit` can be accessed with `commit.method()`, or
2626
`self.commit().method()`.
2727

28+
* `jj bookmark forget` now untracks any corresponding remote bookmarks instead
29+
of forgetting them, since forgetting a remote bookmark can be unintuitive.
30+
The old behavior is still available with the new `--include-remotes` flag.
31+
2832
### Deprecations
2933

3034
* This release takes the first steps to make target revision required in

cli/src/commands/bookmark/delete.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ use crate::ui::Ui;
3030
/// revisions as well as bookmarks, use `jj abandon`. For example, `jj abandon
3131
/// main..<bookmark>` will abandon revisions belonging to the `<bookmark>`
3232
/// branch (relative to the `main` branch.)
33+
///
34+
/// If you don't want the deletion of the local bookmark to propagate to any
35+
/// tracked remote bookmarks, use `jj bookmark forget` instead.
3336
#[derive(clap::Args, Clone, Debug)]
3437
pub struct BookmarkDeleteArgs {
3538
/// The bookmarks to delete

cli/src/commands/bookmark/forget.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,22 @@ use crate::command_error::CommandError;
2626
use crate::complete;
2727
use crate::ui::Ui;
2828

29-
/// Forget everything about a bookmark, including its local and remote
30-
/// targets
29+
/// Forget a bookmark without marking it as a deletion to be pushed
3130
///
32-
/// A forgotten bookmark will not impact remotes on future pushes. It will be
33-
/// recreated on future pulls if it still exists in the remote.
31+
/// If a local bookmark is forgotten, any corresponding remote bookmarks will
32+
/// become untracked to ensure that the forgotten bookmark will not impact
33+
/// remotes on future pushes.
3434
#[derive(clap::Args, Clone, Debug)]
3535
pub struct BookmarkForgetArgs {
36+
/// When forgetting a local bookmark, also forget any corresponding remote
37+
/// bookmarks
38+
///
39+
/// A forgotten remote bookmark will not impact remotes on future pushes. It
40+
/// will be recreated on future fetches if it still exists on the remote. If
41+
/// there is a corresponding Git-tracking remote bookmark, it will also be
42+
/// forgotten.
43+
#[arg(long)]
44+
include_remotes: bool,
3645
/// The bookmarks to forget
3746
///
3847
/// By default, the specified name matches exactly. Use `glob:` prefix to
@@ -57,22 +66,36 @@ pub fn cmd_bookmark_forget(
5766
let repo = workspace_command.repo().clone();
5867
let matched_bookmarks = find_forgettable_bookmarks(repo.view(), &args.names)?;
5968
let mut tx = workspace_command.start_transaction();
69+
let mut forgotten_remote: usize = 0;
6070
for (name, bookmark_target) in &matched_bookmarks {
6171
tx.repo_mut()
6272
.set_local_bookmark_target(name, RefTarget::absent());
6373
for (remote_name, _) in &bookmark_target.remote_refs {
64-
tx.repo_mut()
65-
.set_remote_bookmark(name, remote_name, RemoteRef::absent());
74+
// If `--include-remotes` is specified, we forget the corresponding remote
75+
// bookmarks instead of untracking them
76+
if args.include_remotes {
77+
tx.repo_mut()
78+
.set_remote_bookmark(name, remote_name, RemoteRef::absent());
79+
forgotten_remote += 1;
80+
continue;
81+
}
82+
// Git-tracking remote bookmarks cannot be untracked currently, so skip them
83+
if jj_lib::git::is_special_git_remote(remote_name) {
84+
continue;
85+
}
86+
tx.repo_mut().untrack_remote_bookmark(name, remote_name);
6687
}
6788
}
68-
writeln!(ui.status(), "Forgot {} bookmarks.", matched_bookmarks.len())?;
69-
tx.finish(
70-
ui,
71-
format!(
72-
"forget bookmark {}",
73-
matched_bookmarks.iter().map(|(name, _)| name).join(", ")
74-
),
89+
writeln!(
90+
ui.status(),
91+
"Forgot {} local bookmarks.",
92+
matched_bookmarks.len()
7593
)?;
94+
if forgotten_remote != 0 {
95+
writeln!(ui.status(), "Forgot {forgotten_remote} remote bookmarks.")?;
96+
}
97+
let forgotten_bookmarks = matched_bookmarks.iter().map(|(name, _)| name).join(", ");
98+
tx.finish(ui, format!("forget bookmark {forgotten_bookmarks}"))?;
7699
Ok(())
77100
}
78101

cli/src/commands/bookmark/untrack.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ use crate::ui::Ui;
2626
///
2727
/// A non-tracking remote bookmark is just a pointer to the last-fetched remote
2828
/// bookmark. It won't be imported as a local bookmark on future pulls.
29+
///
30+
/// If you want to forget a local bookmark while also untracking the
31+
/// corresponding remote bookmarks, use `jj bookmark forget` instead.
2932
#[derive(clap::Args, Clone, Debug)]
3033
pub struct BookmarkUntrackArgs {
3134
/// Remote bookmarks to untrack

cli/tests/[email protected]

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ See the [bookmark documentation] for more information.
290290

291291
* `create` — Create a new bookmark
292292
* `delete` — Delete an existing bookmark and propagate the deletion to remotes on the next push
293-
* `forget` — Forget everything about a bookmark, including its local and remote targets
293+
* `forget` — Forget a bookmark without marking it as a deletion to be pushed
294294
* `list` — List bookmarks and their targets
295295
* `move` — Move existing bookmarks to target revision
296296
* `rename` — Rename `old` bookmark name to `new` bookmark name
@@ -322,6 +322,8 @@ Delete an existing bookmark and propagate the deletion to remotes on the next pu
322322

323323
Revisions referred to by the deleted bookmarks are not abandoned. To delete revisions as well as bookmarks, use `jj abandon`. For example, `jj abandon main..<bookmark>` will abandon revisions belonging to the `<bookmark>` branch (relative to the `main` branch.)
324324

325+
If you don't want the deletion of the local bookmark to propagate to any tracked remote bookmarks, use `jj bookmark forget` instead.
326+
325327
**Usage:** `jj bookmark delete <NAMES>...`
326328

327329
###### **Arguments:**
@@ -336,11 +338,11 @@ Revisions referred to by the deleted bookmarks are not abandoned. To delete revi
336338

337339
## `jj bookmark forget`
338340

339-
Forget everything about a bookmark, including its local and remote targets
341+
Forget a bookmark without marking it as a deletion to be pushed
340342

341-
A forgotten bookmark will not impact remotes on future pushes. It will be recreated on future pulls if it still exists in the remote.
343+
If a local bookmark is forgotten, any corresponding remote bookmarks will become untracked to ensure that the forgotten bookmark will not impact remotes on future pushes.
342344

343-
**Usage:** `jj bookmark forget <NAMES>...`
345+
**Usage:** `jj bookmark forget [OPTIONS] <NAMES>...`
344346

345347
###### **Arguments:**
346348

@@ -350,6 +352,12 @@ A forgotten bookmark will not impact remotes on future pushes. It will be recrea
350352

351353
[wildcard pattern]: https://jj-vcs.github.io/jj/latest/revsets/#string-patterns
352354

355+
###### **Options:**
356+
357+
* `--include-remotes` — When forgetting a local bookmark, also forget any corresponding remote bookmarks
358+
359+
A forgotten remote bookmark will not impact remotes on future pushes. It will be recreated on future fetches if it still exists on the remote. If there is a corresponding Git-tracking remote bookmark, it will also be forgotten.
360+
353361

354362

355363
## `jj bookmark list`
@@ -485,6 +493,8 @@ Stop tracking given remote bookmarks
485493

486494
A non-tracking remote bookmark is just a pointer to the last-fetched remote bookmark. It won't be imported as a local bookmark on future pulls.
487495

496+
If you want to forget a local bookmark while also untracking the corresponding remote bookmarks, use `jj bookmark forget` instead.
497+
488498
**Usage:** `jj bookmark untrack <BOOKMARK@REMOTE>...`
489499

490500
###### **Arguments:**

cli/tests/test_bookmark_command.rs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -512,16 +512,12 @@ fn test_bookmark_forget_glob() {
512512
let (stdout, stderr) =
513513
test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "glob:foo-[1-3]"]);
514514
insta::assert_snapshot!(stdout, @"");
515-
insta::assert_snapshot!(stderr, @r###"
516-
Forgot 2 bookmarks.
517-
"###);
515+
insta::assert_snapshot!(stderr, @"Forgot 2 local bookmarks.");
518516
test_env.jj_cmd_ok(&repo_path, &["undo"]);
519517
let (stdout, stderr) =
520518
test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "glob:foo-[1-3]"]);
521519
insta::assert_snapshot!(stdout, @"");
522-
insta::assert_snapshot!(stderr, @r###"
523-
Forgot 2 bookmarks.
524-
"###);
520+
insta::assert_snapshot!(stderr, @"Forgot 2 local bookmarks.");
525521
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
526522
@ bar-2 foo-4 230dd059e1b0
527523
◆ 000000000000
@@ -534,9 +530,7 @@ fn test_bookmark_forget_glob() {
534530
&["bookmark", "forget", "foo-4", "glob:foo-*", "glob:foo-*"],
535531
);
536532
insta::assert_snapshot!(stdout, @"");
537-
insta::assert_snapshot!(stderr, @r###"
538-
Forgot 1 bookmarks.
539-
"###);
533+
insta::assert_snapshot!(stderr, @"Forgot 1 local bookmarks.");
540534
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
541535
@ bar-2 230dd059e1b0
542536
◆ 000000000000
@@ -705,13 +699,17 @@ fn test_bookmark_forget_export() {
705699
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
706700
insta::assert_snapshot!(stdout, @"");
707701
insta::assert_snapshot!(stderr, @"");
708-
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "foo"]);
702+
let (stdout, stderr) = test_env.jj_cmd_ok(
703+
&repo_path,
704+
&["bookmark", "forget", "--include-remotes", "foo"],
705+
);
709706
insta::assert_snapshot!(stdout, @"");
710-
insta::assert_snapshot!(stderr, @r###"
711-
Forgot 1 bookmarks.
712-
"###);
713-
// Forgetting a bookmark deletes local and remote-tracking bookmarks including
714-
// the corresponding git-tracking bookmark.
707+
insta::assert_snapshot!(stderr, @r#"
708+
Forgot 1 local bookmarks.
709+
Forgot 1 remote bookmarks.
710+
"#);
711+
// Forgetting a bookmark with --include-remotes deletes local and
712+
// remote-tracking bookmarks including the corresponding git-tracking bookmark.
715713
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
716714
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r=foo", "--no-graph"]);
717715
insta::assert_snapshot!(stderr, @"Error: Revision `foo` doesn't exist");
@@ -763,8 +761,11 @@ fn test_bookmark_forget_fetched_bookmark() {
763761
");
764762

765763
// TEST 1: with export-import
766-
// Forget the bookmark
767-
test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "feature1"]);
764+
// Forget the bookmark with --include-remotes
765+
test_env.jj_cmd_ok(
766+
&repo_path,
767+
&["bookmark", "forget", "--include-remotes", "feature1"],
768+
);
768769
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
769770

770771
// At this point `jj git export && jj git import` does *not* recreate the
@@ -797,7 +798,10 @@ fn test_bookmark_forget_fetched_bookmark() {
797798
");
798799

799800
// TEST 2: No export/import (otherwise the same as test 1)
800-
test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "feature1"]);
801+
test_env.jj_cmd_ok(
802+
&repo_path,
803+
&["bookmark", "forget", "--include-remotes", "feature1"],
804+
);
801805
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
802806
// Fetch works even without the export-import
803807
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]);
@@ -810,7 +814,7 @@ fn test_bookmark_forget_fetched_bookmark() {
810814
@origin: qomsplrm ebeb70d8 message
811815
");
812816

813-
// TEST 3: fetch bookmark that was moved & forgotten
817+
// TEST 3: fetch bookmark that was moved & forgotten with --include-remotes
814818

815819
// Move the bookmark in the git repo.
816820
git::write_commit(
@@ -820,11 +824,15 @@ fn test_bookmark_forget_fetched_bookmark() {
820824
"another message",
821825
&[first_git_repo_commit],
822826
);
823-
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "feature1"]);
827+
let (stdout, stderr) = test_env.jj_cmd_ok(
828+
&repo_path,
829+
&["bookmark", "forget", "--include-remotes", "feature1"],
830+
);
824831
insta::assert_snapshot!(stdout, @"");
825-
insta::assert_snapshot!(stderr, @r###"
826-
Forgot 1 bookmarks.
827-
"###);
832+
insta::assert_snapshot!(stderr, @r#"
833+
Forgot 1 local bookmarks.
834+
Forgot 1 remote bookmarks.
835+
"#);
828836

829837
// Fetching a moved bookmark does not create a conflict
830838
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]);
@@ -836,6 +844,15 @@ fn test_bookmark_forget_fetched_bookmark() {
836844
feature1: tyvxnvqr 9175cb32 (empty) another message
837845
@origin: tyvxnvqr 9175cb32 (empty) another message
838846
");
847+
848+
// TEST 4: If `--include-remotes` isn't used, remote bookmarks are untracked
849+
test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "feature1"]);
850+
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"feature1@origin: tyvxnvqr 9175cb32 (empty) another message");
851+
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]);
852+
insta::assert_snapshot!(stdout, @"");
853+
// There should be no output here since the remote bookmark wasn't forgotten
854+
insta::assert_snapshot!(stderr, @"Nothing changed.");
855+
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"feature1@origin: tyvxnvqr 9175cb32 (empty) another message");
839856
}
840857

841858
#[test]
@@ -876,7 +893,10 @@ fn test_bookmark_forget_deleted_or_nonexistent_bookmark() {
876893
// ============ End of test setup ============
877894

878895
// We can forget a deleted bookmark
879-
test_env.jj_cmd_ok(&repo_path, &["bookmark", "forget", "feature1"]);
896+
test_env.jj_cmd_ok(
897+
&repo_path,
898+
&["bookmark", "forget", "--include-remotes", "feature1"],
899+
);
880900
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
881901

882902
// Can't forget a non-existent bookmark

cli/tests/test_git_clone.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -793,16 +793,20 @@ fn test_git_clone_trunk_deleted(subprocess: bool) {
793793
"#);
794794
}
795795

796-
let (stdout, stderr) = test_env.jj_cmd_ok(&clone_path, &["bookmark", "forget", "main"]);
796+
let (stdout, stderr) = test_env.jj_cmd_ok(
797+
&clone_path,
798+
&["bookmark", "forget", "--include-remotes", "main"],
799+
);
797800
insta::allow_duplicates! {
798801
insta::assert_snapshot!(stdout, @"");
799802
}
800803
insta::allow_duplicates! {
801-
insta::assert_snapshot!(stderr, @r"
802-
Forgot 1 bookmarks.
804+
insta::assert_snapshot!(stderr, @r#"
805+
Forgot 1 local bookmarks.
806+
Forgot 1 remote bookmarks.
803807
Warning: Failed to resolve `revset-aliases.trunk()`: Revision `main@origin` doesn't exist
804808
Hint: Use `jj config edit --repo` to adjust the `trunk()` alias.
805-
");
809+
"#);
806810
}
807811

808812
let (stdout, stderr) = test_env.jj_cmd_ok(&clone_path, &["log"]);

cli/tests/test_git_colocated.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,15 @@ fn test_git_colocated_bookmark_forget() {
384384
@git: rlvkpnrz 65b6b74e (empty) (no description set)
385385
"###);
386386

387-
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["bookmark", "forget", "foo"]);
387+
let (stdout, stderr) = test_env.jj_cmd_ok(
388+
&workspace_root,
389+
&["bookmark", "forget", "--include-remotes", "foo"],
390+
);
388391
insta::assert_snapshot!(stdout, @"");
389-
insta::assert_snapshot!(stderr, @r###"
390-
Forgot 1 bookmarks.
391-
"###);
392+
insta::assert_snapshot!(stderr, @r#"
393+
Forgot 1 local bookmarks.
394+
Forgot 1 remote bookmarks.
395+
"#);
392396
// A forgotten bookmark is deleted in the git repo. For a detailed demo
393397
// explaining this, see `test_bookmark_forget_export` in
394398
// `test_bookmark_command.rs`.

cli/tests/test_git_fetch.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,10 @@ fn test_git_fetch_removed_bookmark(subprocess: bool) {
15901590
}
15911591

15921592
// Remove a2 bookmark in origin
1593-
test_env.jj_cmd_ok(&source_git_repo_path, &["bookmark", "forget", "a2"]);
1593+
test_env.jj_cmd_ok(
1594+
&source_git_repo_path,
1595+
&["bookmark", "forget", "--include-remotes", "a2"],
1596+
);
15941597

15951598
// Fetch bookmark a1 from origin and check that a2 is still there
15961599
let (stdout, stderr) =
@@ -1708,7 +1711,10 @@ fn test_git_fetch_removed_parent_bookmark(subprocess: bool) {
17081711
}
17091712

17101713
// Remove all bookmarks in origin.
1711-
test_env.jj_cmd_ok(&source_git_repo_path, &["bookmark", "forget", "glob:*"]);
1714+
test_env.jj_cmd_ok(
1715+
&source_git_repo_path,
1716+
&["bookmark", "forget", "--include-remotes", "glob:*"],
1717+
);
17121718

17131719
// Fetch bookmarks master, trunk1 and a1 from origin and check that only those
17141720
// bookmarks have been removed and that others were not rebased because of

cli/tests/test_git_push.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,10 @@ fn test_git_push_creation_unexpectedly_already_exists(subprocess: bool) {
542542
}
543543

544544
// Forget bookmark1 locally
545-
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "forget", "bookmark1"]);
545+
test_env.jj_cmd_ok(
546+
&workspace_root,
547+
&["bookmark", "forget", "--include-remotes", "bookmark1"],
548+
);
546549

547550
// Create a new branh1
548551
test_env.jj_cmd_ok(&workspace_root, &["new", "root()", "-m=new bookmark1"]);

0 commit comments

Comments
 (0)